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
@@ -1,163 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import json
|
4
|
-
from io import BytesIO
|
5
|
-
from typing import TYPE_CHECKING, Any, Dict, Union
|
6
|
-
|
7
|
-
from aiohttp import web
|
8
|
-
from strawberry.exceptions import MissingQueryError
|
9
|
-
from strawberry.file_uploads.utils import replace_placeholders_with_files
|
10
|
-
from strawberry.http import parse_query_params, parse_request_data
|
11
|
-
from strawberry.schema.exceptions import InvalidOperationTypeError
|
12
|
-
from strawberry.types.graphql import OperationType
|
13
|
-
from strawberry.utils.graphiql import get_graphiql_html
|
14
|
-
|
15
|
-
if TYPE_CHECKING:
|
16
|
-
from typing_extensions import Literal
|
17
|
-
|
18
|
-
from strawberry.http import GraphQLRequestData
|
19
|
-
from strawberry.schema import BaseSchema
|
20
|
-
|
21
|
-
|
22
|
-
class HTTPHandler:
|
23
|
-
def __init__(
|
24
|
-
self,
|
25
|
-
schema: BaseSchema,
|
26
|
-
graphiql: bool,
|
27
|
-
allow_queries_via_get: bool,
|
28
|
-
get_context,
|
29
|
-
get_root_value,
|
30
|
-
encode_json,
|
31
|
-
process_result,
|
32
|
-
request: web.Request,
|
33
|
-
):
|
34
|
-
self.schema = schema
|
35
|
-
self.graphiql = graphiql
|
36
|
-
self.allow_queries_via_get = allow_queries_via_get
|
37
|
-
self.get_context = get_context
|
38
|
-
self.get_root_value = get_root_value
|
39
|
-
self.encode_json = encode_json
|
40
|
-
self.process_result = process_result
|
41
|
-
self.request = request
|
42
|
-
|
43
|
-
async def handle(self) -> web.StreamResponse:
|
44
|
-
if self.request.method == "GET":
|
45
|
-
return await self.get(self.request)
|
46
|
-
if self.request.method == "POST":
|
47
|
-
return await self.post(self.request)
|
48
|
-
raise web.HTTPMethodNotAllowed(self.request.method, ["GET", "POST"])
|
49
|
-
|
50
|
-
async def get(self, request: web.Request) -> web.StreamResponse:
|
51
|
-
if request.query:
|
52
|
-
try:
|
53
|
-
query_params = {
|
54
|
-
key: request.query.getone(key) for key in set(request.query.keys())
|
55
|
-
}
|
56
|
-
query_data = parse_query_params(query_params)
|
57
|
-
request_data = parse_request_data(query_data)
|
58
|
-
except json.JSONDecodeError:
|
59
|
-
raise web.HTTPBadRequest(reason="Unable to parse request body as JSON")
|
60
|
-
|
61
|
-
return await self.execute_request(
|
62
|
-
request=request, request_data=request_data, method="GET"
|
63
|
-
)
|
64
|
-
|
65
|
-
elif self.should_render_graphiql(request):
|
66
|
-
return self.render_graphiql()
|
67
|
-
raise web.HTTPNotFound()
|
68
|
-
|
69
|
-
async def post(self, request: web.Request) -> web.StreamResponse:
|
70
|
-
request_data = await self.get_request_data(request)
|
71
|
-
|
72
|
-
return await self.execute_request(
|
73
|
-
request=request, request_data=request_data, method="POST"
|
74
|
-
)
|
75
|
-
|
76
|
-
async def execute_request(
|
77
|
-
self,
|
78
|
-
request: web.Request,
|
79
|
-
request_data: GraphQLRequestData,
|
80
|
-
method: Union[Literal["GET"], Literal["POST"]],
|
81
|
-
) -> web.StreamResponse:
|
82
|
-
response = web.Response()
|
83
|
-
|
84
|
-
context = await self.get_context(request, response)
|
85
|
-
root_value = await self.get_root_value(request)
|
86
|
-
|
87
|
-
allowed_operation_types = OperationType.from_http(method)
|
88
|
-
|
89
|
-
if not self.allow_queries_via_get and method == "GET":
|
90
|
-
allowed_operation_types = allowed_operation_types - {OperationType.QUERY}
|
91
|
-
|
92
|
-
try:
|
93
|
-
result = await self.schema.execute(
|
94
|
-
query=request_data.query,
|
95
|
-
root_value=root_value,
|
96
|
-
variable_values=request_data.variables,
|
97
|
-
context_value=context,
|
98
|
-
operation_name=request_data.operation_name,
|
99
|
-
allowed_operation_types=allowed_operation_types,
|
100
|
-
)
|
101
|
-
except InvalidOperationTypeError as e:
|
102
|
-
raise web.HTTPBadRequest(
|
103
|
-
reason=e.as_http_error_reason(method=method)
|
104
|
-
) from e
|
105
|
-
except MissingQueryError:
|
106
|
-
raise web.HTTPBadRequest(reason="No GraphQL query found in the request")
|
107
|
-
|
108
|
-
response_data = await self.process_result(request, result)
|
109
|
-
|
110
|
-
response.text = self.encode_json(response_data)
|
111
|
-
response.content_type = "application/json"
|
112
|
-
|
113
|
-
return response
|
114
|
-
|
115
|
-
async def get_request_data(self, request: web.Request) -> GraphQLRequestData:
|
116
|
-
data = await self.parse_body(request)
|
117
|
-
return parse_request_data(data)
|
118
|
-
|
119
|
-
async def parse_body(self, request: web.Request) -> dict:
|
120
|
-
if request.content_type.startswith("multipart/form-data"):
|
121
|
-
return await self.parse_multipart_body(request)
|
122
|
-
try:
|
123
|
-
return await request.json()
|
124
|
-
except json.JSONDecodeError as e:
|
125
|
-
raise web.HTTPBadRequest(
|
126
|
-
reason="Unable to parse request body as JSON"
|
127
|
-
) from e
|
128
|
-
|
129
|
-
async def parse_multipart_body(self, request: web.Request) -> dict:
|
130
|
-
reader = await request.multipart()
|
131
|
-
operations: Dict[str, Any] = {}
|
132
|
-
files_map: Dict[str, Any] = {}
|
133
|
-
files: Dict[str, Any] = {}
|
134
|
-
try:
|
135
|
-
async for field in reader:
|
136
|
-
if field.name == "operations":
|
137
|
-
operations = (await field.json()) or {}
|
138
|
-
elif field.name == "map":
|
139
|
-
files_map = (await field.json()) or {}
|
140
|
-
elif field.filename:
|
141
|
-
assert field.name
|
142
|
-
|
143
|
-
files[field.name] = BytesIO(await field.read(decode=False))
|
144
|
-
except ValueError:
|
145
|
-
raise web.HTTPBadRequest(reason="Unable to parse the multipart body")
|
146
|
-
try:
|
147
|
-
return replace_placeholders_with_files(operations, files_map, files)
|
148
|
-
except KeyError:
|
149
|
-
raise web.HTTPBadRequest(reason="File(s) missing in form data")
|
150
|
-
|
151
|
-
def render_graphiql(self) -> web.StreamResponse:
|
152
|
-
html_string = get_graphiql_html()
|
153
|
-
|
154
|
-
return web.Response(text=html_string, content_type="text/html")
|
155
|
-
|
156
|
-
def should_render_graphiql(self, request: web.Request) -> bool:
|
157
|
-
if not self.graphiql:
|
158
|
-
return False
|
159
|
-
|
160
|
-
return any(
|
161
|
-
supported_header in request.headers.get("Accept", "")
|
162
|
-
for supported_header in ("text/html", "*/*")
|
163
|
-
)
|
@@ -1,214 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import json
|
4
|
-
from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, Optional
|
5
|
-
|
6
|
-
from starlette import status
|
7
|
-
from starlette.requests import Request
|
8
|
-
from starlette.responses import HTMLResponse, PlainTextResponse, Response
|
9
|
-
|
10
|
-
from strawberry.exceptions import MissingQueryError
|
11
|
-
from strawberry.file_uploads.utils import replace_placeholders_with_files
|
12
|
-
from strawberry.http import parse_query_params, parse_request_data
|
13
|
-
from strawberry.schema.exceptions import InvalidOperationTypeError
|
14
|
-
from strawberry.types.graphql import OperationType
|
15
|
-
from strawberry.utils.debug import pretty_print_graphql_operation
|
16
|
-
from strawberry.utils.graphiql import get_graphiql_html
|
17
|
-
|
18
|
-
if TYPE_CHECKING:
|
19
|
-
from starlette.types import Receive, Scope, Send
|
20
|
-
|
21
|
-
from strawberry.schema import BaseSchema
|
22
|
-
from strawberry.types.execution import ExecutionResult
|
23
|
-
|
24
|
-
|
25
|
-
class HTTPHandler:
|
26
|
-
def __init__(
|
27
|
-
self,
|
28
|
-
schema: BaseSchema,
|
29
|
-
graphiql: bool,
|
30
|
-
allow_queries_via_get: bool,
|
31
|
-
debug: bool,
|
32
|
-
get_context,
|
33
|
-
get_root_value,
|
34
|
-
process_result,
|
35
|
-
encode_json,
|
36
|
-
):
|
37
|
-
self.schema = schema
|
38
|
-
self.graphiql = graphiql
|
39
|
-
self.allow_queries_via_get = allow_queries_via_get
|
40
|
-
self.debug = debug
|
41
|
-
self.get_context = get_context
|
42
|
-
self.get_root_value = get_root_value
|
43
|
-
self.process_result = process_result
|
44
|
-
self.encode_json = encode_json
|
45
|
-
|
46
|
-
async def handle(self, scope: Scope, receive: Receive, send: Send) -> None:
|
47
|
-
request = Request(scope=scope, receive=receive)
|
48
|
-
root_value = await self.get_root_value(request)
|
49
|
-
|
50
|
-
sub_response = Response()
|
51
|
-
sub_response.status_code = None # type: ignore
|
52
|
-
del sub_response.headers["content-length"]
|
53
|
-
|
54
|
-
context = await self.get_context(request=request, response=sub_response)
|
55
|
-
|
56
|
-
response = await self.get_http_response(
|
57
|
-
request=request,
|
58
|
-
execute=self.execute,
|
59
|
-
process_result=self.process_result,
|
60
|
-
root_value=root_value,
|
61
|
-
context=context,
|
62
|
-
)
|
63
|
-
|
64
|
-
response.headers.raw.extend(sub_response.headers.raw)
|
65
|
-
|
66
|
-
if sub_response.background:
|
67
|
-
response.background = sub_response.background
|
68
|
-
|
69
|
-
if sub_response.status_code:
|
70
|
-
response.status_code = sub_response.status_code
|
71
|
-
|
72
|
-
await response(scope, receive, send)
|
73
|
-
|
74
|
-
async def get_http_response(
|
75
|
-
self,
|
76
|
-
request: Request,
|
77
|
-
execute: Callable,
|
78
|
-
process_result: Callable,
|
79
|
-
root_value: Optional[Any],
|
80
|
-
context: Optional[Any],
|
81
|
-
) -> Response:
|
82
|
-
method = request.method
|
83
|
-
|
84
|
-
if method == "GET":
|
85
|
-
if request.query_params:
|
86
|
-
try:
|
87
|
-
data = parse_query_params(request.query_params._dict)
|
88
|
-
except json.JSONDecodeError:
|
89
|
-
return PlainTextResponse(
|
90
|
-
"Unable to parse request body as JSON",
|
91
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
92
|
-
)
|
93
|
-
|
94
|
-
elif self.should_render_graphiql(request):
|
95
|
-
return self.get_graphiql_response()
|
96
|
-
else:
|
97
|
-
return HTMLResponse(status_code=status.HTTP_404_NOT_FOUND)
|
98
|
-
elif method == "POST":
|
99
|
-
content_type = request.headers.get("Content-Type", "")
|
100
|
-
if "application/json" in content_type:
|
101
|
-
try:
|
102
|
-
data = await request.json()
|
103
|
-
except json.JSONDecodeError:
|
104
|
-
return PlainTextResponse(
|
105
|
-
"Unable to parse request body as JSON",
|
106
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
107
|
-
)
|
108
|
-
elif content_type.startswith("multipart/form-data"):
|
109
|
-
multipart_data = await request.form()
|
110
|
-
try:
|
111
|
-
operations_text = multipart_data.get("operations", "{}")
|
112
|
-
operations = json.loads(operations_text) # type: ignore
|
113
|
-
files_map = json.loads(multipart_data.get("map", "{}")) # type: ignore # noqa: E501
|
114
|
-
except json.JSONDecodeError:
|
115
|
-
return PlainTextResponse(
|
116
|
-
"Unable to parse request body as JSON",
|
117
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
118
|
-
)
|
119
|
-
|
120
|
-
try:
|
121
|
-
data = replace_placeholders_with_files(
|
122
|
-
operations, files_map, multipart_data
|
123
|
-
)
|
124
|
-
except KeyError:
|
125
|
-
return PlainTextResponse(
|
126
|
-
"File(s) missing in form data",
|
127
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
128
|
-
)
|
129
|
-
else:
|
130
|
-
return PlainTextResponse(
|
131
|
-
"Unsupported Media Type",
|
132
|
-
status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
|
133
|
-
)
|
134
|
-
else:
|
135
|
-
return PlainTextResponse(
|
136
|
-
"Method Not Allowed",
|
137
|
-
status_code=status.HTTP_405_METHOD_NOT_ALLOWED,
|
138
|
-
)
|
139
|
-
|
140
|
-
try:
|
141
|
-
request_data = parse_request_data(data)
|
142
|
-
except json.JSONDecodeError:
|
143
|
-
return PlainTextResponse(
|
144
|
-
"Unable to parse request body as JSON",
|
145
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
146
|
-
)
|
147
|
-
|
148
|
-
allowed_operation_types = OperationType.from_http(method)
|
149
|
-
|
150
|
-
if not self.allow_queries_via_get and method == "GET":
|
151
|
-
allowed_operation_types = allowed_operation_types - {OperationType.QUERY}
|
152
|
-
|
153
|
-
try:
|
154
|
-
result = await execute(
|
155
|
-
request_data.query,
|
156
|
-
variables=request_data.variables,
|
157
|
-
context=context,
|
158
|
-
operation_name=request_data.operation_name,
|
159
|
-
root_value=root_value,
|
160
|
-
allowed_operation_types=allowed_operation_types,
|
161
|
-
)
|
162
|
-
except InvalidOperationTypeError as e:
|
163
|
-
return PlainTextResponse(
|
164
|
-
e.as_http_error_reason(method),
|
165
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
166
|
-
)
|
167
|
-
except MissingQueryError:
|
168
|
-
return PlainTextResponse(
|
169
|
-
"No GraphQL query found in the request",
|
170
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
171
|
-
)
|
172
|
-
|
173
|
-
response_data = await process_result(request=request, result=result)
|
174
|
-
|
175
|
-
return Response(
|
176
|
-
self.encode_json(response_data),
|
177
|
-
status_code=status.HTTP_200_OK,
|
178
|
-
media_type="application/json",
|
179
|
-
)
|
180
|
-
|
181
|
-
def should_render_graphiql(self, request: Request) -> bool:
|
182
|
-
if not self.graphiql:
|
183
|
-
return False
|
184
|
-
|
185
|
-
return any(
|
186
|
-
supported_header in request.headers.get("accept", "")
|
187
|
-
for supported_header in ("text/html", "*/*")
|
188
|
-
)
|
189
|
-
|
190
|
-
def get_graphiql_response(self) -> HTMLResponse:
|
191
|
-
html = get_graphiql_html()
|
192
|
-
|
193
|
-
return HTMLResponse(html)
|
194
|
-
|
195
|
-
async def execute(
|
196
|
-
self,
|
197
|
-
query: str,
|
198
|
-
variables: Optional[Dict[str, Any]] = None,
|
199
|
-
context: Any = None,
|
200
|
-
operation_name: Optional[str] = None,
|
201
|
-
root_value: Any = None,
|
202
|
-
allowed_operation_types: Optional[Iterable[OperationType]] = None,
|
203
|
-
) -> ExecutionResult:
|
204
|
-
if self.debug:
|
205
|
-
pretty_print_graphql_operation(operation_name, query, variables)
|
206
|
-
|
207
|
-
return await self.schema.execute(
|
208
|
-
query,
|
209
|
-
root_value=root_value,
|
210
|
-
variable_values=variables,
|
211
|
-
operation_name=operation_name,
|
212
|
-
context_value=context,
|
213
|
-
allowed_operation_types=allowed_operation_types,
|
214
|
-
)
|
File without changes
|
File without changes
|
{strawberry_graphql-0.168.2.dist-info → strawberry_graphql-0.170.0.dist-info}/entry_points.txt
RENAMED
File without changes
|