pgjones / quart-schema

Quart-Schema is a Quart extension that provides schema validation and auto-generated API documentation.
MIT License
78 stars 23 forks source link

validation results in 400 no matter what #80

Closed bandwiches closed 4 months ago

bandwiches commented 4 months ago

OS: Windows & Mac Python Version: 3.12.3 Packages:

Issue:

Any time I use the method POST or PUT it results in a 400. I have been searching for 2 days for any way to gather more information or get past this, but I am officially stuck now.

Removing the wrappers and argument definitions works like normal.

Test

Post

curl -v -H "content-type: application/json" -d '{"due":"2020-12-08T11:19:35.818445","task":"Build an example"}' localhost:5000/

Put

curl -v -H "content-type: application/json" -d '{"due":"2020-12-08T11:19:35.818445","task":"Build an example"}' -X PUT localhost:5000/

Get

curl -v localhost:5000/ping

Client Logs

POST

*   Trying [::1]:5000...
*   Trying 127.0.0.1:5000...
* Connected to localhost (127.0.0.1) port 5000
> POST / HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/8.4.0
> Accept: */*
> content-type: application/json
> Content-Length: 62
>
< HTTP/1.1 400
< content-type: text/html; charset=utf-8
< content-length: 167
< date: Mon, 20 May 2024 17:53:49 GMT
< server: hypercorn-h11
<
<!doctype html>
<html lang=en>
<title>400 Bad Request</title>
<h1>Bad Request</h1>
<p>The browser (or proxy) sent a request that this server could not understand.</p>
* Connection #0 to host localhost left intact

PUT

*   Trying [::1]:5000...
*   Trying 127.0.0.1:5000...
* Connected to localhost (127.0.0.1) port 5000
> PUT /test HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/8.4.0
> Accept: */*
> content-type: application/json
> Content-Length: 62
>
< HTTP/1.1 400
< content-type: text/html; charset=utf-8
< content-length: 167
< date: Mon, 20 May 2024 17:56:08 GMT
< server: hypercorn-h11
<
<!doctype html>
<html lang=en>
<title>400 Bad Request</title>
<h1>Bad Request</h1>
<p>The browser (or proxy) sent a request that this server could not understand.</p>
* Connection #0 to host localhost left intact

Get

*   Trying [::1]:5000...
*   Trying 127.0.0.1:5000...
* Connected to localhost (127.0.0.1) port 5000
> GET /ping HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 200
< content-type: application/json
< content-length: 16
< date: Mon, 20 May 2024 17:57:11 GMT
< server: hypercorn-h11
<
{"ping":"pong"}
* Connection #0 to host localhost left intact`

Server Logs

export QUART_APP="schema:app" && quart run

 * Serving Quart app 'schema'
 * Debug mode: False
 * Please use an ASGI server (e.g. Hypercorn) directly in production
 * Running on http://127.0.0.1:5000 (CTRL + C to quit)
[2024-05-20 12:45:47 -0500] [29736] [INFO] Running on http://127.0.0.1:5000 (CTRL + C to quit)
[2024-05-20 12:45:56 -0500] [29736] [INFO] 127.0.0.1:57824 PUT /test 1.1 400 167 3010
[2024-05-20 12:45:56 -0500] [29736] [INFO] 127.0.0.1:57824 PUT /test 1.1 - - 3999
[2024-05-20 12:49:48 -0500] [16884] [INFO] 127.0.0.1:57865 POST / 1.1 400 167 3145
[2024-05-20 12:49:48 -0500] [16884] [INFO] 127.0.0.1:57865 POST / 1.1 - - 3145
[2024-05-20 12:51:11 -0500] [15108] [INFO] 127.0.0.1:57892 GET /ping 1.1 200 16 2973
[2024-05-20 12:51:11 -0500] [15108] [INFO] 127.0.0.1:57892 GET /ping 1.1 - - 2973

Recreation

Shell

pip install quart quart-schema msgspec

Code

schema.py

from dataclasses import dataclass
from datetime import datetime
from typing import Optional

from quart import Quart, websocket
from quart_schema import QuartSchema, validate_request, validate_response

app = Quart(__name__)
QuartSchema(app)

@dataclass
class Todo:
    task: str
    due: Optional[datetime]

@app.post("/")
@validate_request(Todo)
@validate_response(Todo, 201)
async def create_todo(data: Todo) -> tuple[Todo, int]:
    print(data)
    return data, 201

@app.put("/test")
@validate_request(Todo)
@validate_response(Todo, 201)
async def test(data: Todo) -> tuple[Todo, int]:
    print(data)
    return data, 201

@app.websocket("/ws")
async def ws() -> None:
    while True:
        data = await websocket.receive_as(Todo)
        print(data)
        await websocket.send_as(data, Todo)

@app.get("/ping")
async def pong() -> dict:
    return {"ping": "pong"}
bandwiches commented 4 months ago

Some additional information...

openapi.json results

image

[2024-05-20 13:11:19,136] ERROR in app: Exception on request GET /openapi.json
Traceback (most recent call last):
  File "...\Lib\site-packages\quart\app.py", line 1403, in handle_request
    return await self.full_dispatch_request(request_context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\quart\app.py", line 1441, in full_dispatch_request
    result = await self.handle_user_exception(error)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\quart\app.py", line 1029, in handle_user_exception
    raise error
  File "...\Lib\site-packages\quart\app.py", line 1439, in full_dispatch_request
    result = await self.dispatch_request(request_context)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\quart\app.py", line 1535, in dispatch_request
    return await self.ensure_async(handler)(**request_.view_args)  # type: ignore
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\quart_schema\extension.py", line 321, in openapi
    return jsonify(_build_openapi_schema(current_app, self))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\quart_schema\extension.py", line 657, in _build_openapi_schema
    built_paths, components = _build_path(func, rule, app)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\quart_schema\extension.py", line 490, in _build_path
    schema = model_schema(
             ^^^^^^^^^^^^^
  File "...\Lib\site-packages\quart_schema\conversion.py", line 209, in model_schema
    return TypeAdapter(model_class).json_schema(ref_template=PYDANTIC_REF_TEMPLATE)
           ^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: TypeAdapter() takes no arguments
[2024-05-20 13:11:19 -0500] [15108] [INFO] 127.0.0.1:58139 GET /openapi.json 1.1 500 265 2018
[2024-05-20 13:11:19 -0500] [15108] [INFO] 127.0.0.1:58139 GET /openapi.json 1.1 - - 2018

docs results

Can't upload another image for some reason. This loads an actual page, but the results are a Failed to load API definition error because it gets a 500 when fetching /openapi.json

[2024-05-20 13:14:29 -0500] [15108] [INFO] 127.0.0.1:58178 GET /docs 1.1 200 660 0
[2024-05-20 13:14:29 -0500] [15108] [INFO] 127.0.0.1:58178 GET /docs 1.1 - - 0
[2024-05-20 13:14:29,153] ERROR in app: Exception on request GET /openapi.json
Traceback (most recent call last):
  File "...\Lib\site-packages\quart\app.py", line 1403, in handle_request
    return await self.full_dispatch_request(request_context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\quart\app.py", line 1441, in full_dispatch_request
    result = await self.handle_user_exception(error)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\quart\app.py", line 1029, in handle_user_exception
    raise error
  File "...\Lib\site-packages\quart\app.py", line 1439, in full_dispatch_request
    result = await self.dispatch_request(request_context)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\quart\app.py", line 1535, in dispatch_request
    return await self.ensure_async(handler)(**request_.view_args)  # type: ignore
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\quart_schema\extension.py", line 321, in openapi
    return jsonify(_build_openapi_schema(current_app, self))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\quart_schema\extension.py", line 657, in _build_openapi_schema
    built_paths, components = _build_path(func, rule, app)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\quart_schema\extension.py", line 490, in _build_path
    schema = model_schema(
             ^^^^^^^^^^^^^
  File "...\Lib\site-packages\quart_schema\conversion.py", line 209, in model_schema
    return TypeAdapter(model_class).json_schema(ref_template=PYDANTIC_REF_TEMPLATE)
           ^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: TypeAdapter() takes no arguments
[2024-05-20 13:14:29 -0500] [15108] [INFO] 127.0.0.1:58178 GET /openapi.json 1.1 500 265 1999
[2024-05-20 13:14:29 -0500] [15108] [INFO] 127.0.0.1:58178 GET /openapi.json 1.1 - - 3001
bandwiches commented 4 months ago

Using quart-schema[msgspec] didn't make a difference.

bandwiches commented 4 months ago

Figured out the issue on the windows side, will confirm if I did the same thing on mac.

pgjones commented 4 months ago

Should be fixed in 00a596b698e86473d0266b7674e13ea78332620d (a temporary fix is to install attrs). Now released in 0.20.0

bandwiches commented 4 months ago

Should be fixed in 00a596b (a temporary fix is to install attrs). Now released in 0.20.0

Hell yeah, that did it. Thanks!