strawberry-graphql 0.279.0.dev1754159379__py3-none-any.whl → 0.280.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- strawberry/__init__.py +1 -2
- strawberry/aiohttp/views.py +2 -48
- strawberry/asgi/__init__.py +2 -37
- strawberry/chalice/views.py +7 -75
- strawberry/channels/handlers/http_handler.py +30 -6
- strawberry/cli/commands/upgrade/__init__.py +2 -0
- strawberry/codemods/__init__.py +9 -0
- strawberry/codemods/maybe_optional.py +118 -0
- strawberry/django/views.py +4 -73
- strawberry/experimental/pydantic/_compat.py +1 -0
- strawberry/experimental/pydantic/error_type.py +1 -0
- strawberry/experimental/pydantic/fields.py +1 -0
- strawberry/experimental/pydantic/utils.py +1 -0
- strawberry/fastapi/router.py +8 -4
- strawberry/flask/views.py +4 -74
- strawberry/http/async_base_view.py +5 -31
- strawberry/http/base.py +2 -1
- strawberry/http/exceptions.py +5 -7
- strawberry/http/sync_base_view.py +1 -34
- strawberry/litestar/controller.py +1 -39
- strawberry/quart/views.py +3 -33
- strawberry/sanic/views.py +4 -43
- strawberry/schema/schema.py +2 -0
- strawberry/schema/schema_converter.py +15 -38
- strawberry/schema/validation_rules/maybe_null.py +136 -0
- strawberry/types/arguments.py +16 -2
- strawberry/types/maybe.py +1 -1
- {strawberry_graphql-0.279.0.dev1754159379.dist-info → strawberry_graphql-0.280.0.dist-info}/METADATA +2 -1
- {strawberry_graphql-0.279.0.dev1754159379.dist-info → strawberry_graphql-0.280.0.dist-info}/RECORD +32 -34
- strawberry/pydantic/__init__.py +0 -22
- strawberry/pydantic/error.py +0 -51
- strawberry/pydantic/fields.py +0 -202
- strawberry/pydantic/object_type.py +0 -348
- {strawberry_graphql-0.279.0.dev1754159379.dist-info → strawberry_graphql-0.280.0.dist-info}/LICENSE +0 -0
- {strawberry_graphql-0.279.0.dev1754159379.dist-info → strawberry_graphql-0.280.0.dist-info}/WHEEL +0 -0
- {strawberry_graphql-0.279.0.dev1754159379.dist-info → strawberry_graphql-0.280.0.dist-info}/entry_points.txt +0 -0
strawberry/flask/views.py
CHANGED
|
@@ -3,67 +3,27 @@ from __future__ import annotations
|
|
|
3
3
|
import warnings
|
|
4
4
|
from typing import (
|
|
5
5
|
TYPE_CHECKING,
|
|
6
|
-
Any,
|
|
7
6
|
ClassVar,
|
|
8
7
|
Optional,
|
|
9
8
|
Union,
|
|
10
|
-
cast,
|
|
11
9
|
)
|
|
12
10
|
from typing_extensions import TypeGuard
|
|
13
11
|
|
|
12
|
+
from lia import AsyncFlaskHTTPRequestAdapter, FlaskHTTPRequestAdapter, HTTPException
|
|
13
|
+
|
|
14
14
|
from flask import Request, Response, render_template_string, request
|
|
15
15
|
from flask.views import View
|
|
16
|
-
from strawberry.http.async_base_view import AsyncBaseHTTPView
|
|
17
|
-
from strawberry.http.
|
|
18
|
-
from strawberry.http.sync_base_view import (
|
|
19
|
-
SyncBaseHTTPView,
|
|
20
|
-
SyncHTTPRequestAdapter,
|
|
21
|
-
)
|
|
22
|
-
from strawberry.http.types import FormData, HTTPMethod, QueryParams
|
|
16
|
+
from strawberry.http.async_base_view import AsyncBaseHTTPView
|
|
17
|
+
from strawberry.http.sync_base_view import SyncBaseHTTPView
|
|
23
18
|
from strawberry.http.typevars import Context, RootValue
|
|
24
19
|
|
|
25
20
|
if TYPE_CHECKING:
|
|
26
|
-
from collections.abc import Mapping
|
|
27
|
-
|
|
28
21
|
from flask.typing import ResponseReturnValue
|
|
29
22
|
from strawberry.http import GraphQLHTTPResponse
|
|
30
23
|
from strawberry.http.ides import GraphQL_IDE
|
|
31
24
|
from strawberry.schema.base import BaseSchema
|
|
32
25
|
|
|
33
26
|
|
|
34
|
-
class FlaskHTTPRequestAdapter(SyncHTTPRequestAdapter):
|
|
35
|
-
def __init__(self, request: Request) -> None:
|
|
36
|
-
self.request = request
|
|
37
|
-
|
|
38
|
-
@property
|
|
39
|
-
def query_params(self) -> QueryParams:
|
|
40
|
-
return self.request.args.to_dict()
|
|
41
|
-
|
|
42
|
-
@property
|
|
43
|
-
def body(self) -> Union[str, bytes]:
|
|
44
|
-
return self.request.data.decode()
|
|
45
|
-
|
|
46
|
-
@property
|
|
47
|
-
def method(self) -> HTTPMethod:
|
|
48
|
-
return cast("HTTPMethod", self.request.method.upper())
|
|
49
|
-
|
|
50
|
-
@property
|
|
51
|
-
def headers(self) -> Mapping[str, str]:
|
|
52
|
-
return self.request.headers # type: ignore
|
|
53
|
-
|
|
54
|
-
@property
|
|
55
|
-
def post_data(self) -> Mapping[str, Union[str, bytes]]:
|
|
56
|
-
return self.request.form
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def files(self) -> Mapping[str, Any]:
|
|
60
|
-
return self.request.files
|
|
61
|
-
|
|
62
|
-
@property
|
|
63
|
-
def content_type(self) -> Optional[str]:
|
|
64
|
-
return self.request.content_type
|
|
65
|
-
|
|
66
|
-
|
|
67
27
|
class BaseGraphQLView:
|
|
68
28
|
graphql_ide: Optional[GraphQL_IDE]
|
|
69
29
|
|
|
@@ -131,36 +91,6 @@ class GraphQLView(
|
|
|
131
91
|
return render_template_string(self.graphql_ide_html) # type: ignore
|
|
132
92
|
|
|
133
93
|
|
|
134
|
-
class AsyncFlaskHTTPRequestAdapter(AsyncHTTPRequestAdapter):
|
|
135
|
-
def __init__(self, request: Request) -> None:
|
|
136
|
-
self.request = request
|
|
137
|
-
|
|
138
|
-
@property
|
|
139
|
-
def query_params(self) -> QueryParams:
|
|
140
|
-
return self.request.args.to_dict()
|
|
141
|
-
|
|
142
|
-
@property
|
|
143
|
-
def method(self) -> HTTPMethod:
|
|
144
|
-
return cast("HTTPMethod", self.request.method.upper())
|
|
145
|
-
|
|
146
|
-
@property
|
|
147
|
-
def content_type(self) -> Optional[str]:
|
|
148
|
-
return self.request.content_type
|
|
149
|
-
|
|
150
|
-
@property
|
|
151
|
-
def headers(self) -> Mapping[str, str]:
|
|
152
|
-
return self.request.headers # type: ignore
|
|
153
|
-
|
|
154
|
-
async def get_body(self) -> str:
|
|
155
|
-
return self.request.data.decode()
|
|
156
|
-
|
|
157
|
-
async def get_form_data(self) -> FormData:
|
|
158
|
-
return FormData(
|
|
159
|
-
files=self.request.files,
|
|
160
|
-
form=self.request.form,
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
|
|
164
94
|
class AsyncGraphQLView(
|
|
165
95
|
BaseGraphQLView,
|
|
166
96
|
AsyncBaseHTTPView[
|
|
@@ -17,6 +17,7 @@ from typing import (
|
|
|
17
17
|
from typing_extensions import TypeGuard
|
|
18
18
|
|
|
19
19
|
from graphql import GraphQLError
|
|
20
|
+
from lia import AsyncHTTPRequestAdapter, HTTPException
|
|
20
21
|
|
|
21
22
|
from strawberry.exceptions import MissingQueryError
|
|
22
23
|
from strawberry.file_uploads.utils import replace_placeholders_with_files
|
|
@@ -44,9 +45,7 @@ from strawberry.types.graphql import OperationType
|
|
|
44
45
|
from strawberry.types.unset import UNSET, UnsetType
|
|
45
46
|
|
|
46
47
|
from .base import BaseView
|
|
47
|
-
from .exceptions import HTTPException
|
|
48
48
|
from .parse_content_type import parse_content_type
|
|
49
|
-
from .types import FormData, HTTPMethod, QueryParams
|
|
50
49
|
from .typevars import (
|
|
51
50
|
Context,
|
|
52
51
|
Request,
|
|
@@ -58,30 +57,6 @@ from .typevars import (
|
|
|
58
57
|
)
|
|
59
58
|
|
|
60
59
|
|
|
61
|
-
class AsyncHTTPRequestAdapter(abc.ABC):
|
|
62
|
-
@property
|
|
63
|
-
@abc.abstractmethod
|
|
64
|
-
def query_params(self) -> QueryParams: ...
|
|
65
|
-
|
|
66
|
-
@property
|
|
67
|
-
@abc.abstractmethod
|
|
68
|
-
def method(self) -> HTTPMethod: ...
|
|
69
|
-
|
|
70
|
-
@property
|
|
71
|
-
@abc.abstractmethod
|
|
72
|
-
def headers(self) -> Mapping[str, str]: ...
|
|
73
|
-
|
|
74
|
-
@property
|
|
75
|
-
@abc.abstractmethod
|
|
76
|
-
def content_type(self) -> Optional[str]: ...
|
|
77
|
-
|
|
78
|
-
@abc.abstractmethod
|
|
79
|
-
async def get_body(self) -> Union[str, bytes]: ...
|
|
80
|
-
|
|
81
|
-
@abc.abstractmethod
|
|
82
|
-
async def get_form_data(self) -> FormData: ...
|
|
83
|
-
|
|
84
|
-
|
|
85
60
|
class AsyncWebSocketAdapter(abc.ABC):
|
|
86
61
|
def __init__(self, view: "AsyncBaseHTTPView") -> None:
|
|
87
62
|
self.view = view
|
|
@@ -284,8 +259,9 @@ class AsyncBaseHTTPView(
|
|
|
284
259
|
except ValueError as e:
|
|
285
260
|
raise HTTPException(400, "Unable to parse the multipart body") from e
|
|
286
261
|
|
|
287
|
-
operations = form_data
|
|
288
|
-
files_map = form_data
|
|
262
|
+
operations = form_data.form.get("operations", "{}")
|
|
263
|
+
files_map = form_data.form.get("map", "{}")
|
|
264
|
+
files = form_data.files
|
|
289
265
|
|
|
290
266
|
if isinstance(operations, (bytes, str)):
|
|
291
267
|
operations = self.parse_json(operations)
|
|
@@ -294,9 +270,7 @@ class AsyncBaseHTTPView(
|
|
|
294
270
|
files_map = self.parse_json(files_map)
|
|
295
271
|
|
|
296
272
|
try:
|
|
297
|
-
return replace_placeholders_with_files(
|
|
298
|
-
operations, files_map, form_data["files"]
|
|
299
|
-
)
|
|
273
|
+
return replace_placeholders_with_files(operations, files_map, files)
|
|
300
274
|
except KeyError as e:
|
|
301
275
|
raise HTTPException(400, "File(s) missing in form data") from e
|
|
302
276
|
|
strawberry/http/base.py
CHANGED
|
@@ -3,12 +3,13 @@ from collections.abc import Mapping
|
|
|
3
3
|
from typing import Any, Generic, Optional, Union
|
|
4
4
|
from typing_extensions import Protocol
|
|
5
5
|
|
|
6
|
+
from lia import HTTPException
|
|
7
|
+
|
|
6
8
|
from strawberry.http import GraphQLRequestData
|
|
7
9
|
from strawberry.http.ides import GraphQL_IDE, get_graphql_ide_html
|
|
8
10
|
from strawberry.http.types import HTTPMethod, QueryParams
|
|
9
11
|
from strawberry.schema.base import BaseSchema
|
|
10
12
|
|
|
11
|
-
from .exceptions import HTTPException
|
|
12
13
|
from .typevars import Request
|
|
13
14
|
|
|
14
15
|
|
strawberry/http/exceptions.py
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
class HTTPException(Exception):
|
|
2
|
-
def __init__(self, status_code: int, reason: str) -> None:
|
|
3
|
-
self.status_code = status_code
|
|
4
|
-
self.reason = reason
|
|
5
|
-
|
|
6
|
-
|
|
7
1
|
class NonTextMessageReceived(Exception):
|
|
8
2
|
pass
|
|
9
3
|
|
|
@@ -16,4 +10,8 @@ class WebSocketDisconnected(Exception):
|
|
|
16
10
|
pass
|
|
17
11
|
|
|
18
12
|
|
|
19
|
-
__all__ = [
|
|
13
|
+
__all__ = [
|
|
14
|
+
"NonJsonMessageReceived",
|
|
15
|
+
"NonTextMessageReceived",
|
|
16
|
+
"WebSocketDisconnected",
|
|
17
|
+
]
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import json
|
|
3
|
-
from collections.abc import Mapping
|
|
4
3
|
from typing import (
|
|
5
|
-
Any,
|
|
6
4
|
Callable,
|
|
7
5
|
Generic,
|
|
8
6
|
Literal,
|
|
@@ -11,6 +9,7 @@ from typing import (
|
|
|
11
9
|
)
|
|
12
10
|
|
|
13
11
|
from graphql import GraphQLError
|
|
12
|
+
from lia import HTTPException, SyncHTTPRequestAdapter
|
|
14
13
|
|
|
15
14
|
from strawberry.exceptions import MissingQueryError
|
|
16
15
|
from strawberry.file_uploads.utils import replace_placeholders_with_files
|
|
@@ -30,42 +29,10 @@ from strawberry.types.graphql import OperationType
|
|
|
30
29
|
from strawberry.types.unset import UNSET
|
|
31
30
|
|
|
32
31
|
from .base import BaseView
|
|
33
|
-
from .exceptions import HTTPException
|
|
34
32
|
from .parse_content_type import parse_content_type
|
|
35
|
-
from .types import HTTPMethod, QueryParams
|
|
36
33
|
from .typevars import Context, Request, Response, RootValue, SubResponse
|
|
37
34
|
|
|
38
35
|
|
|
39
|
-
class SyncHTTPRequestAdapter(abc.ABC):
|
|
40
|
-
@property
|
|
41
|
-
@abc.abstractmethod
|
|
42
|
-
def query_params(self) -> QueryParams: ...
|
|
43
|
-
|
|
44
|
-
@property
|
|
45
|
-
@abc.abstractmethod
|
|
46
|
-
def body(self) -> Union[str, bytes]: ...
|
|
47
|
-
|
|
48
|
-
@property
|
|
49
|
-
@abc.abstractmethod
|
|
50
|
-
def method(self) -> HTTPMethod: ...
|
|
51
|
-
|
|
52
|
-
@property
|
|
53
|
-
@abc.abstractmethod
|
|
54
|
-
def headers(self) -> Mapping[str, str]: ...
|
|
55
|
-
|
|
56
|
-
@property
|
|
57
|
-
@abc.abstractmethod
|
|
58
|
-
def content_type(self) -> Optional[str]: ...
|
|
59
|
-
|
|
60
|
-
@property
|
|
61
|
-
@abc.abstractmethod
|
|
62
|
-
def post_data(self) -> Mapping[str, Union[str, bytes]]: ...
|
|
63
|
-
|
|
64
|
-
@property
|
|
65
|
-
@abc.abstractmethod
|
|
66
|
-
def files(self) -> Mapping[str, Any]: ...
|
|
67
|
-
|
|
68
|
-
|
|
69
36
|
class SyncBaseHTTPView(
|
|
70
37
|
abc.ABC,
|
|
71
38
|
BaseView[Request],
|
|
@@ -13,10 +13,10 @@ from typing import (
|
|
|
13
13
|
Optional,
|
|
14
14
|
TypedDict,
|
|
15
15
|
Union,
|
|
16
|
-
cast,
|
|
17
16
|
)
|
|
18
17
|
from typing_extensions import TypeGuard
|
|
19
18
|
|
|
19
|
+
from lia import HTTPException, LitestarRequestAdapter
|
|
20
20
|
from msgspec import Struct
|
|
21
21
|
|
|
22
22
|
from litestar import (
|
|
@@ -41,16 +41,13 @@ from litestar.status_codes import HTTP_200_OK
|
|
|
41
41
|
from strawberry.exceptions import InvalidCustomContext
|
|
42
42
|
from strawberry.http.async_base_view import (
|
|
43
43
|
AsyncBaseHTTPView,
|
|
44
|
-
AsyncHTTPRequestAdapter,
|
|
45
44
|
AsyncWebSocketAdapter,
|
|
46
45
|
)
|
|
47
46
|
from strawberry.http.exceptions import (
|
|
48
|
-
HTTPException,
|
|
49
47
|
NonJsonMessageReceived,
|
|
50
48
|
NonTextMessageReceived,
|
|
51
49
|
WebSocketDisconnected,
|
|
52
50
|
)
|
|
53
|
-
from strawberry.http.types import FormData, HTTPMethod, QueryParams
|
|
54
51
|
from strawberry.http.typevars import Context, RootValue
|
|
55
52
|
from strawberry.subscriptions import GRAPHQL_TRANSPORT_WS_PROTOCOL, GRAPHQL_WS_PROTOCOL
|
|
56
53
|
|
|
@@ -153,41 +150,6 @@ class GraphQLResource(Struct):
|
|
|
153
150
|
extensions: Optional[dict[str, object]]
|
|
154
151
|
|
|
155
152
|
|
|
156
|
-
class LitestarRequestAdapter(AsyncHTTPRequestAdapter):
|
|
157
|
-
def __init__(self, request: Request[Any, Any, Any]) -> None:
|
|
158
|
-
self.request = request
|
|
159
|
-
|
|
160
|
-
@property
|
|
161
|
-
def query_params(self) -> QueryParams:
|
|
162
|
-
return self.request.query_params
|
|
163
|
-
|
|
164
|
-
@property
|
|
165
|
-
def method(self) -> HTTPMethod:
|
|
166
|
-
return cast("HTTPMethod", self.request.method.upper())
|
|
167
|
-
|
|
168
|
-
@property
|
|
169
|
-
def headers(self) -> Mapping[str, str]:
|
|
170
|
-
return self.request.headers
|
|
171
|
-
|
|
172
|
-
@property
|
|
173
|
-
def content_type(self) -> Optional[str]:
|
|
174
|
-
content_type, params = self.request.content_type
|
|
175
|
-
|
|
176
|
-
# combine content type and params
|
|
177
|
-
if params:
|
|
178
|
-
content_type += "; " + "; ".join(f"{k}={v}" for k, v in params.items())
|
|
179
|
-
|
|
180
|
-
return content_type
|
|
181
|
-
|
|
182
|
-
async def get_body(self) -> bytes:
|
|
183
|
-
return await self.request.body()
|
|
184
|
-
|
|
185
|
-
async def get_form_data(self) -> FormData:
|
|
186
|
-
multipart_data = await self.request.form()
|
|
187
|
-
|
|
188
|
-
return FormData(form=multipart_data, files=multipart_data)
|
|
189
|
-
|
|
190
|
-
|
|
191
153
|
class LitestarWebSocketAdapter(AsyncWebSocketAdapter):
|
|
192
154
|
def __init__(
|
|
193
155
|
self, view: AsyncBaseHTTPView, request: WebSocket, response: WebSocket
|
strawberry/quart/views.py
CHANGED
|
@@ -3,25 +3,24 @@ import warnings
|
|
|
3
3
|
from collections.abc import AsyncGenerator, Mapping, Sequence
|
|
4
4
|
from datetime import timedelta
|
|
5
5
|
from json.decoder import JSONDecodeError
|
|
6
|
-
from typing import TYPE_CHECKING, Callable, ClassVar, Optional, Union
|
|
6
|
+
from typing import TYPE_CHECKING, Callable, ClassVar, Optional, Union
|
|
7
7
|
from typing_extensions import TypeGuard
|
|
8
8
|
|
|
9
|
+
from lia import HTTPException, QuartHTTPRequestAdapter
|
|
10
|
+
|
|
9
11
|
from quart import Request, Response, Websocket, request, websocket
|
|
10
12
|
from quart.ctx import has_websocket_context
|
|
11
13
|
from quart.views import View
|
|
12
14
|
from strawberry.http.async_base_view import (
|
|
13
15
|
AsyncBaseHTTPView,
|
|
14
|
-
AsyncHTTPRequestAdapter,
|
|
15
16
|
AsyncWebSocketAdapter,
|
|
16
17
|
)
|
|
17
18
|
from strawberry.http.exceptions import (
|
|
18
|
-
HTTPException,
|
|
19
19
|
NonJsonMessageReceived,
|
|
20
20
|
NonTextMessageReceived,
|
|
21
21
|
WebSocketDisconnected,
|
|
22
22
|
)
|
|
23
23
|
from strawberry.http.ides import GraphQL_IDE
|
|
24
|
-
from strawberry.http.types import FormData, HTTPMethod, QueryParams
|
|
25
24
|
from strawberry.http.typevars import Context, RootValue
|
|
26
25
|
from strawberry.subscriptions import GRAPHQL_TRANSPORT_WS_PROTOCOL, GRAPHQL_WS_PROTOCOL
|
|
27
26
|
|
|
@@ -31,35 +30,6 @@ if TYPE_CHECKING:
|
|
|
31
30
|
from strawberry.schema.base import BaseSchema
|
|
32
31
|
|
|
33
32
|
|
|
34
|
-
class QuartHTTPRequestAdapter(AsyncHTTPRequestAdapter):
|
|
35
|
-
def __init__(self, request: Request) -> None:
|
|
36
|
-
self.request = request
|
|
37
|
-
|
|
38
|
-
@property
|
|
39
|
-
def query_params(self) -> QueryParams:
|
|
40
|
-
return self.request.args.to_dict()
|
|
41
|
-
|
|
42
|
-
@property
|
|
43
|
-
def method(self) -> HTTPMethod:
|
|
44
|
-
return cast("HTTPMethod", self.request.method.upper())
|
|
45
|
-
|
|
46
|
-
@property
|
|
47
|
-
def content_type(self) -> Optional[str]:
|
|
48
|
-
return self.request.content_type
|
|
49
|
-
|
|
50
|
-
@property
|
|
51
|
-
def headers(self) -> Mapping[str, str]:
|
|
52
|
-
return self.request.headers # type: ignore
|
|
53
|
-
|
|
54
|
-
async def get_body(self) -> str:
|
|
55
|
-
return (await self.request.data).decode()
|
|
56
|
-
|
|
57
|
-
async def get_form_data(self) -> FormData:
|
|
58
|
-
files = await self.request.files
|
|
59
|
-
form = await self.request.form
|
|
60
|
-
return FormData(files=files, form=form)
|
|
61
|
-
|
|
62
|
-
|
|
63
33
|
class QuartWebSocketAdapter(AsyncWebSocketAdapter):
|
|
64
34
|
def __init__(
|
|
65
35
|
self, view: AsyncBaseHTTPView, request: Websocket, response: Response
|
strawberry/sanic/views.py
CHANGED
|
@@ -8,68 +8,29 @@ from typing import (
|
|
|
8
8
|
Callable,
|
|
9
9
|
Optional,
|
|
10
10
|
Union,
|
|
11
|
-
cast,
|
|
12
11
|
)
|
|
13
12
|
from typing_extensions import TypeGuard
|
|
14
13
|
|
|
14
|
+
from lia import HTTPException, SanicHTTPRequestAdapter
|
|
15
|
+
|
|
15
16
|
from sanic.request import Request
|
|
16
17
|
from sanic.response import HTTPResponse, html
|
|
17
18
|
from sanic.views import HTTPMethodView
|
|
18
|
-
from strawberry.http.async_base_view import AsyncBaseHTTPView
|
|
19
|
-
from strawberry.http.exceptions import HTTPException
|
|
19
|
+
from strawberry.http.async_base_view import AsyncBaseHTTPView
|
|
20
20
|
from strawberry.http.temporal_response import TemporalResponse
|
|
21
|
-
from strawberry.http.types import FormData, HTTPMethod, QueryParams
|
|
22
21
|
from strawberry.http.typevars import (
|
|
23
22
|
Context,
|
|
24
23
|
RootValue,
|
|
25
24
|
)
|
|
26
|
-
from strawberry.sanic.utils import convert_request_to_files_dict
|
|
27
25
|
|
|
28
26
|
if TYPE_CHECKING:
|
|
29
|
-
from collections.abc import AsyncGenerator
|
|
27
|
+
from collections.abc import AsyncGenerator
|
|
30
28
|
|
|
31
29
|
from strawberry.http import GraphQLHTTPResponse
|
|
32
30
|
from strawberry.http.ides import GraphQL_IDE
|
|
33
31
|
from strawberry.schema import BaseSchema
|
|
34
32
|
|
|
35
33
|
|
|
36
|
-
class SanicHTTPRequestAdapter(AsyncHTTPRequestAdapter):
|
|
37
|
-
def __init__(self, request: Request) -> None:
|
|
38
|
-
self.request = request
|
|
39
|
-
|
|
40
|
-
@property
|
|
41
|
-
def query_params(self) -> QueryParams:
|
|
42
|
-
# Just a heads up, Sanic's request.args uses urllib.parse.parse_qs
|
|
43
|
-
# to parse query string parameters. This returns a dictionary where
|
|
44
|
-
# the keys are the unique variable names and the values are lists
|
|
45
|
-
# of values for each variable name. To ensure consistency, we're
|
|
46
|
-
# enforcing the use of the first value in each list.
|
|
47
|
-
args = self.request.get_args(keep_blank_values=True)
|
|
48
|
-
return {k: args.get(k, None) for k in args}
|
|
49
|
-
|
|
50
|
-
@property
|
|
51
|
-
def method(self) -> HTTPMethod:
|
|
52
|
-
return cast("HTTPMethod", self.request.method.upper())
|
|
53
|
-
|
|
54
|
-
@property
|
|
55
|
-
def headers(self) -> Mapping[str, str]:
|
|
56
|
-
return self.request.headers
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def content_type(self) -> Optional[str]:
|
|
60
|
-
return self.request.content_type
|
|
61
|
-
|
|
62
|
-
async def get_body(self) -> str:
|
|
63
|
-
return self.request.body.decode()
|
|
64
|
-
|
|
65
|
-
async def get_form_data(self) -> FormData:
|
|
66
|
-
assert self.request.form is not None
|
|
67
|
-
|
|
68
|
-
files = convert_request_to_files_dict(self.request)
|
|
69
|
-
|
|
70
|
-
return FormData(form=self.request.form, files=files)
|
|
71
|
-
|
|
72
|
-
|
|
73
34
|
class GraphQLView(
|
|
74
35
|
AsyncBaseHTTPView[
|
|
75
36
|
Request,
|
strawberry/schema/schema.py
CHANGED
|
@@ -50,6 +50,7 @@ from strawberry.extensions.directives import (
|
|
|
50
50
|
from strawberry.extensions.runner import SchemaExtensionsRunner
|
|
51
51
|
from strawberry.printer import print_schema
|
|
52
52
|
from strawberry.schema.schema_converter import GraphQLCoreConverter
|
|
53
|
+
from strawberry.schema.validation_rules.maybe_null import MaybeNullValidationRule
|
|
53
54
|
from strawberry.schema.validation_rules.one_of import OneOfInputValidationRule
|
|
54
55
|
from strawberry.types.base import (
|
|
55
56
|
StrawberryObjectDefinition,
|
|
@@ -124,6 +125,7 @@ def validate_document(
|
|
|
124
125
|
) -> list[GraphQLError]:
|
|
125
126
|
validation_rules = (
|
|
126
127
|
*validation_rules,
|
|
128
|
+
MaybeNullValidationRule,
|
|
127
129
|
OneOfInputValidationRule,
|
|
128
130
|
)
|
|
129
131
|
return validate(
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import contextlib
|
|
4
3
|
import dataclasses
|
|
5
4
|
import sys
|
|
6
5
|
from functools import partial, reduce
|
|
@@ -732,27 +731,14 @@ class GraphQLCoreConverter:
|
|
|
732
731
|
) -> Any:
|
|
733
732
|
# parse field arguments into Strawberry input types and convert
|
|
734
733
|
# field names to Python equivalents
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
)
|
|
744
|
-
except Exception as exc:
|
|
745
|
-
# Try to import Pydantic ValidationError
|
|
746
|
-
with contextlib.suppress(ImportError):
|
|
747
|
-
from pydantic import ValidationError
|
|
748
|
-
|
|
749
|
-
if isinstance(
|
|
750
|
-
exc, ValidationError
|
|
751
|
-
) and self._should_convert_validation_error(field):
|
|
752
|
-
from strawberry.pydantic import Error
|
|
753
|
-
|
|
754
|
-
return Error.from_validation_error(exc)
|
|
755
|
-
raise
|
|
734
|
+
field_args, field_kwargs = get_arguments(
|
|
735
|
+
field=field,
|
|
736
|
+
source=_source,
|
|
737
|
+
info=info,
|
|
738
|
+
kwargs=kwargs,
|
|
739
|
+
config=self.config,
|
|
740
|
+
scalar_registry=self.scalar_registry,
|
|
741
|
+
)
|
|
756
742
|
|
|
757
743
|
resolver_requested_info = False
|
|
758
744
|
if "info" in field_kwargs:
|
|
@@ -813,22 +799,6 @@ class GraphQLCoreConverter:
|
|
|
813
799
|
_resolver._is_default = not field.base_resolver # type: ignore
|
|
814
800
|
return _resolver
|
|
815
801
|
|
|
816
|
-
def _should_convert_validation_error(self, field: StrawberryField) -> bool:
|
|
817
|
-
"""Check if field return type is a Union containing strawberry.pydantic.Error."""
|
|
818
|
-
from strawberry.types.union import StrawberryUnion
|
|
819
|
-
|
|
820
|
-
field_type = field.type
|
|
821
|
-
if isinstance(field_type, StrawberryUnion):
|
|
822
|
-
# Import Error dynamically to avoid circular imports
|
|
823
|
-
try:
|
|
824
|
-
from strawberry.pydantic import Error
|
|
825
|
-
|
|
826
|
-
return any(union_type is Error for union_type in field_type.types)
|
|
827
|
-
except ImportError:
|
|
828
|
-
# If strawberry.pydantic doesn't exist or Error isn't available
|
|
829
|
-
return False
|
|
830
|
-
return False
|
|
831
|
-
|
|
832
802
|
def from_scalar(self, scalar: type) -> GraphQLScalarType:
|
|
833
803
|
from strawberry.relay.types import GlobalID
|
|
834
804
|
|
|
@@ -884,6 +854,13 @@ class GraphQLCoreConverter:
|
|
|
884
854
|
NoneType = type(None)
|
|
885
855
|
if type_ is None or type_ is NoneType:
|
|
886
856
|
return self.from_type(type_)
|
|
857
|
+
if isinstance(type_, StrawberryMaybe):
|
|
858
|
+
# StrawberryMaybe should always generate optional types
|
|
859
|
+
# because Maybe[T] = Union[Some[T], None] (field can be absent)
|
|
860
|
+
# But we need to handle the case where of_type is itself optional
|
|
861
|
+
if isinstance(type_.of_type, StrawberryOptional):
|
|
862
|
+
return self.from_type(type_.of_type.of_type)
|
|
863
|
+
return self.from_type(type_.of_type)
|
|
887
864
|
if isinstance(type_, StrawberryOptional):
|
|
888
865
|
return self.from_type(type_.of_type)
|
|
889
866
|
return GraphQLNonNull(self.from_type(type_))
|