strawberry-graphql 0.168.2__py3-none-any.whl → 0.170.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/aiohttp/handlers/__init__.py +1 -2
- strawberry/aiohttp/views.py +109 -47
- strawberry/asgi/__init__.py +118 -29
- strawberry/asgi/handlers/__init__.py +1 -2
- strawberry/chalice/views.py +81 -158
- strawberry/cli/debug_server.py +2 -1
- strawberry/django/views.py +138 -200
- strawberry/fastapi/router.py +101 -164
- strawberry/file_uploads/utils.py +2 -2
- strawberry/flask/views.py +117 -178
- strawberry/http/async_base_view.py +215 -0
- strawberry/http/base.py +63 -0
- strawberry/http/exceptions.py +4 -0
- strawberry/http/sync_base_view.py +210 -0
- strawberry/http/temporal_response.py +3 -1
- strawberry/http/types.py +13 -0
- strawberry/http/typevars.py +7 -0
- strawberry/sanic/utils.py +9 -2
- strawberry/sanic/views.py +86 -136
- strawberry/schema/name_converter.py +4 -1
- strawberry/schema/schema_converter.py +6 -1
- strawberry/starlite/controller.py +119 -177
- strawberry/types/graphql.py +5 -2
- {strawberry_graphql-0.168.2.dist-info → strawberry_graphql-0.170.0.dist-info}/METADATA +1 -1
- {strawberry_graphql-0.168.2.dist-info → strawberry_graphql-0.170.0.dist-info}/RECORD +28 -24
- strawberry/aiohttp/handlers/http_handler.py +0 -163
- strawberry/asgi/handlers/http_handler.py +0 -214
- {strawberry_graphql-0.168.2.dist-info → strawberry_graphql-0.170.0.dist-info}/LICENSE +0 -0
- {strawberry_graphql-0.168.2.dist-info → strawberry_graphql-0.170.0.dist-info}/WHEEL +0 -0
- {strawberry_graphql-0.168.2.dist-info → strawberry_graphql-0.170.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,210 @@
|
|
1
|
+
import abc
|
2
|
+
import json
|
3
|
+
from typing import (
|
4
|
+
Any,
|
5
|
+
Callable,
|
6
|
+
Dict,
|
7
|
+
Generic,
|
8
|
+
Mapping,
|
9
|
+
Optional,
|
10
|
+
Union,
|
11
|
+
)
|
12
|
+
|
13
|
+
from strawberry import UNSET
|
14
|
+
from strawberry.exceptions import MissingQueryError
|
15
|
+
from strawberry.file_uploads.utils import replace_placeholders_with_files
|
16
|
+
from strawberry.http import GraphQLHTTPResponse, GraphQLRequestData, process_result
|
17
|
+
from strawberry.schema import BaseSchema
|
18
|
+
from strawberry.schema.exceptions import InvalidOperationTypeError
|
19
|
+
from strawberry.types import ExecutionResult
|
20
|
+
from strawberry.types.graphql import OperationType
|
21
|
+
|
22
|
+
from .base import BaseView
|
23
|
+
from .exceptions import HTTPException
|
24
|
+
from .types import HTTPMethod, QueryParams
|
25
|
+
from .typevars import Context, Request, Response, RootValue, SubResponse
|
26
|
+
|
27
|
+
|
28
|
+
class SyncHTTPRequestAdapter(abc.ABC):
|
29
|
+
@property
|
30
|
+
@abc.abstractmethod
|
31
|
+
def query_params(self) -> QueryParams:
|
32
|
+
...
|
33
|
+
|
34
|
+
@property
|
35
|
+
@abc.abstractmethod
|
36
|
+
def body(self) -> Union[str, bytes]:
|
37
|
+
...
|
38
|
+
|
39
|
+
@property
|
40
|
+
@abc.abstractmethod
|
41
|
+
def method(self) -> HTTPMethod:
|
42
|
+
...
|
43
|
+
|
44
|
+
@property
|
45
|
+
@abc.abstractmethod
|
46
|
+
def headers(self) -> Mapping[str, str]:
|
47
|
+
...
|
48
|
+
|
49
|
+
@property
|
50
|
+
@abc.abstractmethod
|
51
|
+
def content_type(self) -> Optional[str]:
|
52
|
+
...
|
53
|
+
|
54
|
+
@property
|
55
|
+
@abc.abstractmethod
|
56
|
+
def post_data(self) -> Mapping[str, Union[str, bytes]]:
|
57
|
+
...
|
58
|
+
|
59
|
+
@property
|
60
|
+
@abc.abstractmethod
|
61
|
+
def files(self) -> Mapping[str, Any]:
|
62
|
+
...
|
63
|
+
|
64
|
+
|
65
|
+
class SyncBaseHTTPView(
|
66
|
+
abc.ABC,
|
67
|
+
BaseView[Request],
|
68
|
+
Generic[Request, Response, SubResponse, Context, RootValue],
|
69
|
+
):
|
70
|
+
schema: BaseSchema
|
71
|
+
graphiql: bool
|
72
|
+
request_adapter_class: Callable[[Request], SyncHTTPRequestAdapter]
|
73
|
+
|
74
|
+
# Methods that need to be implemented by individual frameworks
|
75
|
+
|
76
|
+
@property
|
77
|
+
@abc.abstractmethod
|
78
|
+
def allow_queries_via_get(self) -> bool:
|
79
|
+
...
|
80
|
+
|
81
|
+
@abc.abstractmethod
|
82
|
+
def get_sub_response(self, request: Request) -> SubResponse:
|
83
|
+
...
|
84
|
+
|
85
|
+
@abc.abstractmethod
|
86
|
+
def get_context(self, request: Request, response: SubResponse) -> Context:
|
87
|
+
...
|
88
|
+
|
89
|
+
@abc.abstractmethod
|
90
|
+
def get_root_value(self, request: Request) -> Optional[RootValue]:
|
91
|
+
...
|
92
|
+
|
93
|
+
@abc.abstractmethod
|
94
|
+
def create_response(
|
95
|
+
self, response_data: GraphQLHTTPResponse, sub_response: SubResponse
|
96
|
+
) -> Response:
|
97
|
+
...
|
98
|
+
|
99
|
+
@abc.abstractmethod
|
100
|
+
def render_graphiql(self, request: Request) -> Response:
|
101
|
+
# TODO: this could be non abstract
|
102
|
+
# maybe add a get template function?
|
103
|
+
...
|
104
|
+
|
105
|
+
def execute_operation(
|
106
|
+
self, request: Request, context: Context, root_value: Optional[RootValue]
|
107
|
+
) -> ExecutionResult:
|
108
|
+
request_adapter = self.request_adapter_class(request)
|
109
|
+
|
110
|
+
try:
|
111
|
+
request_data = self.parse_http_body(request_adapter)
|
112
|
+
except json.decoder.JSONDecodeError as e:
|
113
|
+
raise HTTPException(400, "Unable to parse request body as JSON") from e
|
114
|
+
# DO this only when doing files
|
115
|
+
except KeyError as e:
|
116
|
+
raise HTTPException(400, "File(s) missing in form data") from e
|
117
|
+
|
118
|
+
allowed_operation_types = OperationType.from_http(request_adapter.method)
|
119
|
+
|
120
|
+
if not self.allow_queries_via_get and request_adapter.method == "GET":
|
121
|
+
allowed_operation_types = allowed_operation_types - {OperationType.QUERY}
|
122
|
+
|
123
|
+
assert self.schema
|
124
|
+
|
125
|
+
return self.schema.execute_sync(
|
126
|
+
request_data.query,
|
127
|
+
root_value=root_value,
|
128
|
+
variable_values=request_data.variables,
|
129
|
+
context_value=context,
|
130
|
+
operation_name=request_data.operation_name,
|
131
|
+
allowed_operation_types=allowed_operation_types,
|
132
|
+
)
|
133
|
+
|
134
|
+
def parse_multipart(self, request: SyncHTTPRequestAdapter) -> Dict[str, str]:
|
135
|
+
operations = self.parse_json(request.post_data.get("operations", "{}"))
|
136
|
+
files_map = self.parse_json(request.post_data.get("map", "{}"))
|
137
|
+
|
138
|
+
try:
|
139
|
+
return replace_placeholders_with_files(operations, files_map, request.files)
|
140
|
+
except KeyError as e:
|
141
|
+
raise HTTPException(400, "File(s) missing in form data") from e
|
142
|
+
|
143
|
+
def parse_http_body(self, request: SyncHTTPRequestAdapter) -> GraphQLRequestData:
|
144
|
+
content_type = request.content_type or ""
|
145
|
+
|
146
|
+
if "application/json" in content_type:
|
147
|
+
data = self.parse_json(request.body)
|
148
|
+
elif content_type.startswith("multipart/form-data"):
|
149
|
+
data = self.parse_multipart(request)
|
150
|
+
elif request.method == "GET":
|
151
|
+
data = self.parse_query_params(request.query_params)
|
152
|
+
else:
|
153
|
+
raise HTTPException(400, "Unsupported content type")
|
154
|
+
|
155
|
+
return GraphQLRequestData(
|
156
|
+
query=data.get("query"),
|
157
|
+
variables=data.get("variables"), # type: ignore
|
158
|
+
operation_name=data.get("operationName"),
|
159
|
+
)
|
160
|
+
|
161
|
+
def run(
|
162
|
+
self,
|
163
|
+
request: Request,
|
164
|
+
context: Optional[Context] = UNSET,
|
165
|
+
root_value: Optional[RootValue] = UNSET,
|
166
|
+
) -> Response:
|
167
|
+
request_adapter = self.request_adapter_class(request)
|
168
|
+
|
169
|
+
if not self.is_request_allowed(request_adapter):
|
170
|
+
raise HTTPException(405, "GraphQL only supports GET and POST requests.")
|
171
|
+
|
172
|
+
if self.should_render_graphiql(request_adapter):
|
173
|
+
if self.graphiql:
|
174
|
+
return self.render_graphiql(request)
|
175
|
+
else:
|
176
|
+
raise HTTPException(404, "Not Found")
|
177
|
+
|
178
|
+
sub_response = self.get_sub_response(request)
|
179
|
+
context = (
|
180
|
+
self.get_context(request, response=sub_response)
|
181
|
+
if context is UNSET
|
182
|
+
else context
|
183
|
+
)
|
184
|
+
root_value = self.get_root_value(request) if root_value is UNSET else root_value
|
185
|
+
|
186
|
+
assert context
|
187
|
+
|
188
|
+
try:
|
189
|
+
result = self.execute_operation(
|
190
|
+
request=request,
|
191
|
+
context=context,
|
192
|
+
root_value=root_value,
|
193
|
+
)
|
194
|
+
except InvalidOperationTypeError as e:
|
195
|
+
raise HTTPException(
|
196
|
+
400, e.as_http_error_reason(request_adapter.method)
|
197
|
+
) from e
|
198
|
+
except MissingQueryError as e:
|
199
|
+
raise HTTPException(400, "No GraphQL query found in the request") from e
|
200
|
+
|
201
|
+
response_data = self.process_result(request=request, result=result)
|
202
|
+
|
203
|
+
return self.create_response(
|
204
|
+
response_data=response_data, sub_response=sub_response
|
205
|
+
)
|
206
|
+
|
207
|
+
def process_result(
|
208
|
+
self, request: Request, result: ExecutionResult
|
209
|
+
) -> GraphQLHTTPResponse:
|
210
|
+
return process_result(result)
|
strawberry/http/types.py
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
from typing import Any, List, Mapping, Optional, Union
|
2
|
+
from typing_extensions import Literal, TypedDict
|
3
|
+
|
4
|
+
HTTPMethod = Literal[
|
5
|
+
"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"
|
6
|
+
]
|
7
|
+
|
8
|
+
QueryParams = Mapping[str, Optional[Union[str, List[str]]]]
|
9
|
+
|
10
|
+
|
11
|
+
class FormData(TypedDict):
|
12
|
+
files: Mapping[str, Any]
|
13
|
+
form: Mapping[str, Any]
|
strawberry/sanic/utils.py
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
from io import BytesIO
|
4
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Union
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast
|
5
|
+
|
6
|
+
from sanic.request import File
|
5
7
|
|
6
8
|
if TYPE_CHECKING:
|
7
9
|
from sanic.request import Request
|
@@ -23,11 +25,16 @@ def convert_request_to_files_dict(request: Request) -> Dict[str, Any]:
|
|
23
25
|
|
24
26
|
Note that the dictionary entries are lists.
|
25
27
|
"""
|
26
|
-
request_files = request.files
|
28
|
+
request_files = cast(Optional[Dict[str, List[File]]], request.files)
|
29
|
+
|
30
|
+
if not request_files:
|
31
|
+
return {}
|
32
|
+
|
27
33
|
files_dict: Dict[str, Union[BytesIO, List[BytesIO]]] = {}
|
28
34
|
|
29
35
|
for field_name, file_list in request_files.items():
|
30
36
|
assert len(file_list) == 1
|
37
|
+
|
31
38
|
files_dict[field_name] = BytesIO(file_list[0].body)
|
32
39
|
|
33
40
|
return files_dict
|
strawberry/sanic/views.py
CHANGED
@@ -2,37 +2,82 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import json
|
4
4
|
import warnings
|
5
|
-
from typing import
|
5
|
+
from typing import (
|
6
|
+
TYPE_CHECKING,
|
7
|
+
Any,
|
8
|
+
Dict,
|
9
|
+
List,
|
10
|
+
Mapping,
|
11
|
+
Optional,
|
12
|
+
Type,
|
13
|
+
cast,
|
14
|
+
)
|
6
15
|
|
7
|
-
from sanic.
|
16
|
+
from sanic.request import Request
|
8
17
|
from sanic.response import HTTPResponse, html
|
9
18
|
from sanic.views import HTTPMethodView
|
10
|
-
from strawberry.
|
11
|
-
from strawberry.
|
12
|
-
from strawberry.http import (
|
13
|
-
parse_query_params,
|
14
|
-
parse_request_data,
|
15
|
-
process_result,
|
16
|
-
)
|
19
|
+
from strawberry.http.async_base_view import AsyncBaseHTTPView, AsyncHTTPRequestAdapter
|
20
|
+
from strawberry.http.exceptions import HTTPException
|
17
21
|
from strawberry.http.temporal_response import TemporalResponse
|
18
|
-
from strawberry.
|
22
|
+
from strawberry.http.types import FormData, HTTPMethod, QueryParams
|
23
|
+
from strawberry.http.typevars import (
|
24
|
+
Context,
|
25
|
+
RootValue,
|
26
|
+
)
|
19
27
|
from strawberry.sanic.utils import convert_request_to_files_dict
|
20
|
-
from strawberry.schema.exceptions import InvalidOperationTypeError
|
21
|
-
from strawberry.types.graphql import OperationType
|
22
28
|
from strawberry.utils.graphiql import get_graphiql_html
|
23
29
|
|
24
30
|
if TYPE_CHECKING:
|
25
|
-
from
|
26
|
-
|
27
|
-
from sanic.request import Request
|
28
|
-
from strawberry.http import GraphQLHTTPResponse, GraphQLRequestData
|
31
|
+
from strawberry.http import GraphQLHTTPResponse
|
29
32
|
from strawberry.schema import BaseSchema
|
30
|
-
from strawberry.types import ExecutionResult
|
31
33
|
|
32
|
-
from .context import StrawberrySanicContext
|
33
34
|
|
35
|
+
class SanicHTTPRequestAdapter(AsyncHTTPRequestAdapter):
|
36
|
+
def __init__(self, request: Request):
|
37
|
+
self.request = request
|
38
|
+
|
39
|
+
@property
|
40
|
+
def query_params(self) -> QueryParams:
|
41
|
+
# Just a heads up, Sanic's request.args uses urllib.parse.parse_qs
|
42
|
+
# to parse query string parameters. This returns a dictionary where
|
43
|
+
# the keys are the unique variable names and the values are lists
|
44
|
+
# of values for each variable name. To ensure consistency, we're
|
45
|
+
# enforcing the use of the first value in each list.
|
46
|
+
|
47
|
+
args = cast(
|
48
|
+
Dict[str, Optional[List[str]]],
|
49
|
+
self.request.get_args(keep_blank_values=True),
|
50
|
+
)
|
51
|
+
|
52
|
+
return {k: args.get(k, None) for k in args}
|
53
|
+
|
54
|
+
@property
|
55
|
+
def method(self) -> HTTPMethod:
|
56
|
+
return cast(HTTPMethod, self.request.method.upper())
|
57
|
+
|
58
|
+
@property
|
59
|
+
def headers(self) -> Mapping[str, str]:
|
60
|
+
return self.request.headers
|
61
|
+
|
62
|
+
@property
|
63
|
+
def content_type(self) -> Optional[str]:
|
64
|
+
return self.request.content_type
|
34
65
|
|
35
|
-
|
66
|
+
async def get_body(self) -> str:
|
67
|
+
return self.request.body.decode()
|
68
|
+
|
69
|
+
async def get_form_data(self) -> FormData:
|
70
|
+
assert self.request.form is not None
|
71
|
+
|
72
|
+
files = convert_request_to_files_dict(self.request)
|
73
|
+
|
74
|
+
return FormData(form=self.request.form, files=files)
|
75
|
+
|
76
|
+
|
77
|
+
class GraphQLView(
|
78
|
+
AsyncBaseHTTPView[Request, HTTPResponse, TemporalResponse, Context, RootValue],
|
79
|
+
HTTPMethodView,
|
80
|
+
):
|
36
81
|
"""
|
37
82
|
Class based view to handle GraphQL HTTP Requests
|
38
83
|
|
@@ -51,6 +96,9 @@ class GraphQLView(HTTPMethodView):
|
|
51
96
|
)
|
52
97
|
"""
|
53
98
|
|
99
|
+
allow_queries_via_get = True
|
100
|
+
request_adapter_class = SanicHTTPRequestAdapter
|
101
|
+
|
54
102
|
def __init__(
|
55
103
|
self,
|
56
104
|
schema: BaseSchema,
|
@@ -81,57 +129,26 @@ class GraphQLView(HTTPMethodView):
|
|
81
129
|
|
82
130
|
self.json_encoder = json.JSONEncoder
|
83
131
|
|
84
|
-
def get_root_value(self) ->
|
132
|
+
async def get_root_value(self, request: Request) -> Optional[RootValue]:
|
85
133
|
return None
|
86
134
|
|
87
135
|
async def get_context(
|
88
136
|
self, request: Request, response: TemporalResponse
|
89
|
-
) ->
|
90
|
-
return {"request": request, "response": response}
|
91
|
-
|
92
|
-
def render_template(self, template: str) -> HTTPResponse:
|
93
|
-
return html(template)
|
137
|
+
) -> Context:
|
138
|
+
return {"request": request, "response": response} # type: ignore
|
94
139
|
|
95
|
-
|
96
|
-
|
97
|
-
) -> GraphQLHTTPResponse:
|
98
|
-
return process_result(result)
|
140
|
+
def render_graphiql(self, request: Request) -> HTTPResponse:
|
141
|
+
template = get_graphiql_html()
|
99
142
|
|
100
|
-
|
101
|
-
if request.args:
|
102
|
-
# Sanic request.args uses urllib.parse.parse_qs
|
103
|
-
# returns a dictionary where the keys are the unique variable names
|
104
|
-
# and the values are a list of values for each variable name
|
105
|
-
# Enforcing using the first value
|
106
|
-
query_data = {
|
107
|
-
variable_name: value[0] for variable_name, value in request.args.items()
|
108
|
-
}
|
109
|
-
try:
|
110
|
-
data = parse_query_params(query_data)
|
111
|
-
except json.JSONDecodeError:
|
112
|
-
raise ServerError(
|
113
|
-
"Unable to parse request body as JSON", status_code=400
|
114
|
-
)
|
115
|
-
|
116
|
-
request_data = parse_request_data(data)
|
117
|
-
|
118
|
-
return await self.execute_request(
|
119
|
-
request=request, request_data=request_data, method="GET"
|
120
|
-
)
|
121
|
-
|
122
|
-
elif should_render_graphiql(self.graphiql, request):
|
123
|
-
template = get_graphiql_html(False)
|
124
|
-
return self.render_template(template=template)
|
143
|
+
return html(template)
|
125
144
|
|
126
|
-
|
145
|
+
async def get_sub_response(self, request: Request) -> TemporalResponse:
|
146
|
+
return TemporalResponse()
|
127
147
|
|
128
|
-
|
129
|
-
self, response_data: GraphQLHTTPResponse,
|
148
|
+
def create_response(
|
149
|
+
self, response_data: GraphQLHTTPResponse, sub_response: TemporalResponse
|
130
150
|
) -> HTTPResponse:
|
131
|
-
status_code =
|
132
|
-
|
133
|
-
if "response" in context and context["response"]:
|
134
|
-
status_code = context["response"].status_code
|
151
|
+
status_code = sub_response.status_code
|
135
152
|
|
136
153
|
data = self.encode_json(response_data)
|
137
154
|
|
@@ -139,84 +156,17 @@ class GraphQLView(HTTPMethodView):
|
|
139
156
|
data,
|
140
157
|
status=status_code,
|
141
158
|
content_type="application/json",
|
159
|
+
headers=sub_response.headers,
|
142
160
|
)
|
143
161
|
|
144
|
-
def encode_json(self, response_data: GraphQLHTTPResponse) -> str:
|
145
|
-
if self.json_dumps_params:
|
146
|
-
assert self.json_encoder
|
147
|
-
|
148
|
-
return json.dumps(
|
149
|
-
response_data, cls=self.json_encoder, **self.json_dumps_params
|
150
|
-
)
|
151
|
-
|
152
|
-
if self.json_encoder:
|
153
|
-
return json.dumps(response_data, cls=self.json_encoder)
|
154
|
-
|
155
|
-
return json.dumps(response_data)
|
156
|
-
|
157
162
|
async def post(self, request: Request) -> HTTPResponse:
|
158
|
-
request_data = self.get_request_data(request)
|
159
|
-
|
160
|
-
return await self.execute_request(
|
161
|
-
request=request, request_data=request_data, method="POST"
|
162
|
-
)
|
163
|
-
|
164
|
-
async def execute_request(
|
165
|
-
self,
|
166
|
-
request: Request,
|
167
|
-
request_data: GraphQLRequestData,
|
168
|
-
method: Union[Literal["GET"], Literal["POST"]],
|
169
|
-
) -> HTTPResponse:
|
170
|
-
context = await self.get_context(request, TemporalResponse())
|
171
|
-
root_value = self.get_root_value()
|
172
|
-
|
173
|
-
allowed_operation_types = OperationType.from_http(method)
|
174
|
-
|
175
|
-
if not self.allow_queries_via_get and method == "GET":
|
176
|
-
allowed_operation_types = allowed_operation_types - {OperationType.QUERY}
|
177
|
-
|
178
163
|
try:
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
context_value=context,
|
183
|
-
root_value=root_value,
|
184
|
-
operation_name=request_data.operation_name,
|
185
|
-
allowed_operation_types=allowed_operation_types,
|
186
|
-
)
|
187
|
-
except InvalidOperationTypeError as e:
|
188
|
-
raise ServerError(
|
189
|
-
e.as_http_error_reason(method=method), status_code=400
|
190
|
-
) from e
|
191
|
-
except MissingQueryError:
|
192
|
-
raise ServerError("No GraphQL query found in the request", status_code=400)
|
193
|
-
|
194
|
-
response_data = await self.process_result(request, result)
|
164
|
+
return await self.run(request)
|
165
|
+
except HTTPException as e:
|
166
|
+
return HTTPResponse(e.reason, status=e.status_code)
|
195
167
|
|
196
|
-
|
197
|
-
|
198
|
-
def get_request_data(self, request: Request) -> GraphQLRequestData:
|
168
|
+
async def get(self, request: Request) -> HTTPResponse:
|
199
169
|
try:
|
200
|
-
|
201
|
-
except
|
202
|
-
|
203
|
-
|
204
|
-
return parse_request_data(data)
|
205
|
-
|
206
|
-
def parse_request(self, request: Request) -> Dict[str, Any]:
|
207
|
-
content_type = request.content_type or ""
|
208
|
-
|
209
|
-
if "application/json" in content_type:
|
210
|
-
return json.loads(request.body)
|
211
|
-
elif content_type.startswith("multipart/form-data"):
|
212
|
-
files = convert_request_to_files_dict(request)
|
213
|
-
operations = json.loads(request.form.get("operations", "{}"))
|
214
|
-
files_map = json.loads(request.form.get("map", "{}"))
|
215
|
-
try:
|
216
|
-
return replace_placeholders_with_files(operations, files_map, files)
|
217
|
-
except KeyError:
|
218
|
-
raise SanicException(
|
219
|
-
status_code=400, message="File(s) missing in form data"
|
220
|
-
)
|
221
|
-
|
222
|
-
raise ServerError("Unsupported Media Type", status_code=415)
|
170
|
+
return await self.run(request)
|
171
|
+
except HTTPException as e:
|
172
|
+
return HTTPResponse(e.reason, status=e.status_code)
|
@@ -5,7 +5,7 @@ from typing_extensions import Protocol
|
|
5
5
|
|
6
6
|
from strawberry.custom_scalar import ScalarDefinition
|
7
7
|
from strawberry.directive import StrawberryDirective
|
8
|
-
from strawberry.enum import EnumDefinition
|
8
|
+
from strawberry.enum import EnumDefinition, EnumValue
|
9
9
|
from strawberry.lazy_type import LazyType
|
10
10
|
from strawberry.schema_directive import StrawberrySchemaDirective
|
11
11
|
from strawberry.type import StrawberryList, StrawberryOptional
|
@@ -76,6 +76,9 @@ class NameConverter:
|
|
76
76
|
def from_enum(self, enum: EnumDefinition) -> str:
|
77
77
|
return enum.name
|
78
78
|
|
79
|
+
def from_enum_value(self, enum: EnumDefinition, enum_value: EnumValue) -> str:
|
80
|
+
return enum_value.name
|
81
|
+
|
79
82
|
def from_directive(
|
80
83
|
self, directive: Union[StrawberryDirective, StrawberrySchemaDirective]
|
81
84
|
) -> str:
|
@@ -150,7 +150,12 @@ class GraphQLCoreConverter:
|
|
150
150
|
graphql_enum = CustomGraphQLEnumType(
|
151
151
|
enum=enum,
|
152
152
|
name=enum_name,
|
153
|
-
values={
|
153
|
+
values={
|
154
|
+
self.config.name_converter.from_enum_value(
|
155
|
+
enum, item
|
156
|
+
): self.from_enum_value(item)
|
157
|
+
for item in enum.values
|
158
|
+
},
|
154
159
|
description=enum.description,
|
155
160
|
extensions={
|
156
161
|
GraphQLCoreConverter.DEFINITION_BACKREF: enum,
|