strawberry-graphql 0.277.1__py3-none-any.whl → 0.278.1__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.
@@ -8,14 +8,16 @@ from typing import (
8
8
  Any,
9
9
  Callable,
10
10
  Generic,
11
+ Literal,
11
12
  Optional,
12
13
  Union,
13
14
  cast,
14
15
  overload,
15
16
  )
16
- from typing_extensions import Literal, TypeGuard
17
+ from typing_extensions import TypeGuard
17
18
 
18
19
  from graphql import GraphQLError
20
+ from lia import AsyncHTTPRequestAdapter, HTTPException
19
21
 
20
22
  from strawberry.exceptions import MissingQueryError
21
23
  from strawberry.file_uploads.utils import replace_placeholders_with_files
@@ -43,9 +45,7 @@ from strawberry.types.graphql import OperationType
43
45
  from strawberry.types.unset import UNSET, UnsetType
44
46
 
45
47
  from .base import BaseView
46
- from .exceptions import HTTPException
47
48
  from .parse_content_type import parse_content_type
48
- from .types import FormData, HTTPMethod, QueryParams
49
49
  from .typevars import (
50
50
  Context,
51
51
  Request,
@@ -57,30 +57,6 @@ from .typevars import (
57
57
  )
58
58
 
59
59
 
60
- class AsyncHTTPRequestAdapter(abc.ABC):
61
- @property
62
- @abc.abstractmethod
63
- def query_params(self) -> QueryParams: ...
64
-
65
- @property
66
- @abc.abstractmethod
67
- def method(self) -> HTTPMethod: ...
68
-
69
- @property
70
- @abc.abstractmethod
71
- def headers(self) -> Mapping[str, str]: ...
72
-
73
- @property
74
- @abc.abstractmethod
75
- def content_type(self) -> Optional[str]: ...
76
-
77
- @abc.abstractmethod
78
- async def get_body(self) -> Union[str, bytes]: ...
79
-
80
- @abc.abstractmethod
81
- async def get_form_data(self) -> FormData: ...
82
-
83
-
84
60
  class AsyncWebSocketAdapter(abc.ABC):
85
61
  def __init__(self, view: "AsyncBaseHTTPView") -> None:
86
62
  self.view = view
@@ -153,7 +129,9 @@ class AsyncBaseHTTPView(
153
129
 
154
130
  @abc.abstractmethod
155
131
  def create_response(
156
- self, response_data: GraphQLHTTPResponse, sub_response: SubResponse
132
+ self,
133
+ response_data: Union[GraphQLHTTPResponse, list[GraphQLHTTPResponse]],
134
+ sub_response: SubResponse,
157
135
  ) -> Response: ...
158
136
 
159
137
  @abc.abstractmethod
@@ -184,8 +162,12 @@ class AsyncBaseHTTPView(
184
162
  ) -> WebSocketResponse: ...
185
163
 
186
164
  async def execute_operation(
187
- self, request: Request, context: Context, root_value: Optional[RootValue]
188
- ) -> Union[ExecutionResult, SubscriptionExecutionResult]:
165
+ self,
166
+ request: Request,
167
+ context: Context,
168
+ root_value: Optional[RootValue],
169
+ sub_response: SubResponse,
170
+ ) -> Union[ExecutionResult, list[ExecutionResult], SubscriptionExecutionResult]:
189
171
  request_adapter = self.request_adapter_class(request)
190
172
 
191
173
  try:
@@ -201,6 +183,22 @@ class AsyncBaseHTTPView(
201
183
  if not self.allow_queries_via_get and request_adapter.method == "GET":
202
184
  allowed_operation_types = allowed_operation_types - {OperationType.QUERY}
203
185
 
186
+ if isinstance(request_data, list):
187
+ # batch GraphQL requests
188
+ return await asyncio.gather(
189
+ *[
190
+ self.execute_single(
191
+ request=request,
192
+ request_adapter=request_adapter,
193
+ sub_response=sub_response,
194
+ context=context,
195
+ root_value=root_value,
196
+ request_data=data,
197
+ )
198
+ for data in request_data
199
+ ]
200
+ )
201
+
204
202
  if request_data.protocol == "multipart-subscription":
205
203
  return await self.schema.subscribe(
206
204
  request_data.query, # type: ignore
@@ -211,24 +209,59 @@ class AsyncBaseHTTPView(
211
209
  operation_extensions=request_data.extensions,
212
210
  )
213
211
 
214
- return await self.schema.execute(
215
- request_data.query,
212
+ return await self.execute_single(
213
+ request=request,
214
+ request_adapter=request_adapter,
215
+ sub_response=sub_response,
216
+ context=context,
216
217
  root_value=root_value,
217
- variable_values=request_data.variables,
218
- context_value=context,
219
- operation_name=request_data.operation_name,
220
- allowed_operation_types=allowed_operation_types,
221
- operation_extensions=request_data.extensions,
218
+ request_data=request_data,
222
219
  )
223
220
 
221
+ async def execute_single(
222
+ self,
223
+ request: Request,
224
+ request_adapter: AsyncHTTPRequestAdapter,
225
+ sub_response: SubResponse,
226
+ context: Context,
227
+ root_value: Optional[RootValue],
228
+ request_data: GraphQLRequestData,
229
+ ) -> ExecutionResult:
230
+ allowed_operation_types = OperationType.from_http(request_adapter.method)
231
+
232
+ if not self.allow_queries_via_get and request_adapter.method == "GET":
233
+ allowed_operation_types = allowed_operation_types - {OperationType.QUERY}
234
+
235
+ try:
236
+ result = await self.schema.execute(
237
+ request_data.query,
238
+ root_value=root_value,
239
+ variable_values=request_data.variables,
240
+ context_value=context,
241
+ operation_name=request_data.operation_name,
242
+ allowed_operation_types=allowed_operation_types,
243
+ operation_extensions=request_data.extensions,
244
+ )
245
+ except CannotGetOperationTypeError as e:
246
+ raise HTTPException(400, e.as_http_error_reason()) from e
247
+ except InvalidOperationTypeError as e:
248
+ raise HTTPException(
249
+ 400, e.as_http_error_reason(request_adapter.method)
250
+ ) from e
251
+ except MissingQueryError as e:
252
+ raise HTTPException(400, "No GraphQL query found in the request") from e
253
+
254
+ return result
255
+
224
256
  async def parse_multipart(self, request: AsyncHTTPRequestAdapter) -> dict[str, str]:
225
257
  try:
226
258
  form_data = await request.get_form_data()
227
259
  except ValueError as e:
228
260
  raise HTTPException(400, "Unable to parse the multipart body") from e
229
261
 
230
- operations = form_data["form"].get("operations", "{}")
231
- files_map = form_data["form"].get("map", "{}")
262
+ operations = form_data.form.get("operations", "{}")
263
+ files_map = form_data.form.get("map", "{}")
264
+ files = form_data.files
232
265
 
233
266
  if isinstance(operations, (bytes, str)):
234
267
  operations = self.parse_json(operations)
@@ -237,9 +270,7 @@ class AsyncBaseHTTPView(
237
270
  files_map = self.parse_json(files_map)
238
271
 
239
272
  try:
240
- return replace_placeholders_with_files(
241
- operations, files_map, form_data["files"]
242
- )
273
+ return replace_placeholders_with_files(operations, files_map, files)
243
274
  except KeyError as e:
244
275
  raise HTTPException(400, "File(s) missing in form data") from e
245
276
 
@@ -330,18 +361,12 @@ class AsyncBaseHTTPView(
330
361
  return await self.render_graphql_ide(request)
331
362
  raise HTTPException(404, "Not Found")
332
363
 
333
- try:
334
- result = await self.execute_operation(
335
- request=request, context=context, root_value=root_value
336
- )
337
- except CannotGetOperationTypeError as e:
338
- raise HTTPException(400, e.as_http_error_reason()) from e
339
- except InvalidOperationTypeError as e:
340
- raise HTTPException(
341
- 400, e.as_http_error_reason(request_adapter.method)
342
- ) from e
343
- except MissingQueryError as e:
344
- raise HTTPException(400, "No GraphQL query found in the request") from e
364
+ result = await self.execute_operation(
365
+ request=request,
366
+ context=context,
367
+ root_value=root_value,
368
+ sub_response=sub_response,
369
+ )
345
370
 
346
371
  if isinstance(result, SubscriptionExecutionResult):
347
372
  stream = self._get_stream(request, result)
@@ -425,10 +450,22 @@ class AsyncBaseHTTPView(
425
450
  },
426
451
  )
427
452
 
428
- response_data = await self.process_result(request=request, result=result)
453
+ response_data: Union[GraphQLHTTPResponse, list[GraphQLHTTPResponse]]
454
+
455
+ if isinstance(result, list):
456
+ response_data = []
457
+ for execution_result in result:
458
+ processed_result = await self.process_result(
459
+ request=request, result=execution_result
460
+ )
461
+ if execution_result.errors:
462
+ self._handle_errors(execution_result.errors, processed_result)
463
+ response_data.append(processed_result)
464
+ else:
465
+ response_data = await self.process_result(request=request, result=result)
429
466
 
430
- if result.errors:
431
- self._handle_errors(result.errors, response_data)
467
+ if result.errors:
468
+ self._handle_errors(result.errors, response_data)
432
469
 
433
470
  return self.create_response(
434
471
  response_data=response_data, sub_response=sub_response
@@ -584,15 +621,16 @@ class AsyncBaseHTTPView(
584
621
 
585
622
  async def parse_http_body(
586
623
  self, request: AsyncHTTPRequestAdapter
587
- ) -> GraphQLRequestData:
624
+ ) -> Union[GraphQLRequestData, list[GraphQLRequestData]]:
588
625
  headers = {key.lower(): value for key, value in request.headers.items()}
589
626
  content_type, _ = parse_content_type(request.content_type or "")
590
627
  accept = headers.get("accept", "")
591
628
 
592
- protocol: Literal["http", "multipart-subscription"] = "http"
593
-
594
- if self._is_multipart_subscriptions(*parse_content_type(accept)):
595
- protocol = "multipart-subscription"
629
+ protocol: Literal["http", "multipart-subscription"] = (
630
+ "multipart-subscription"
631
+ if self._is_multipart_subscriptions(*parse_content_type(accept))
632
+ else "http"
633
+ )
596
634
 
597
635
  if request.method == "GET":
598
636
  data = self.parse_query_params(request.query_params)
@@ -603,6 +641,19 @@ class AsyncBaseHTTPView(
603
641
  else:
604
642
  raise HTTPException(400, "Unsupported content type")
605
643
 
644
+ if isinstance(data, list):
645
+ self._validate_batch_request(data, protocol=protocol)
646
+ return [
647
+ GraphQLRequestData(
648
+ query=item.get("query"),
649
+ variables=item.get("variables"),
650
+ operation_name=item.get("operationName"),
651
+ extensions=item.get("extensions"),
652
+ protocol=protocol,
653
+ )
654
+ for item in data
655
+ ]
656
+
606
657
  query = data.get("query")
607
658
  if not isinstance(query, (str, type(None))):
608
659
  raise HTTPException(
strawberry/http/base.py CHANGED
@@ -3,10 +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
+
8
+ from strawberry.http import GraphQLRequestData
6
9
  from strawberry.http.ides import GraphQL_IDE, get_graphql_ide_html
7
10
  from strawberry.http.types import HTTPMethod, QueryParams
11
+ from strawberry.schema.base import BaseSchema
8
12
 
9
- from .exceptions import HTTPException
10
13
  from .typevars import Request
11
14
 
12
15
 
@@ -24,6 +27,7 @@ class BaseRequestProtocol(Protocol):
24
27
  class BaseView(Generic[Request]):
25
28
  graphql_ide: Optional[GraphQL_IDE]
26
29
  multipart_uploads_enabled: bool = False
30
+ schema: BaseSchema
27
31
 
28
32
  def should_render_graphql_ide(self, request: BaseRequestProtocol) -> bool:
29
33
  return (
@@ -82,5 +86,19 @@ class BaseView(Generic[Request]):
82
86
 
83
87
  return params.get("subscriptionspec", "").startswith("1.0")
84
88
 
89
+ def _validate_batch_request(
90
+ self, request_data: list[GraphQLRequestData], protocol: str
91
+ ) -> None:
92
+ if self.schema.config.batching_config is None:
93
+ raise HTTPException(400, "Batching is not enabled")
94
+
95
+ if protocol == "multipart-subscription":
96
+ raise HTTPException(
97
+ 400, "Batching is not supported for multipart subscriptions"
98
+ )
99
+
100
+ if len(request_data) > self.schema.config.batching_config["max_operations"]:
101
+ raise HTTPException(400, "Too many operations")
102
+
85
103
 
86
104
  __all__ = ["BaseView"]
@@ -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__ = ["HTTPException"]
13
+ __all__ = [
14
+ "NonJsonMessageReceived",
15
+ "NonTextMessageReceived",
16
+ "WebSocketDisconnected",
17
+ ]
@@ -1,15 +1,15 @@
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,
6
+ Literal,
8
7
  Optional,
9
8
  Union,
10
9
  )
11
10
 
12
11
  from graphql import GraphQLError
12
+ from lia import HTTPException, SyncHTTPRequestAdapter
13
13
 
14
14
  from strawberry.exceptions import MissingQueryError
15
15
  from strawberry.file_uploads.utils import replace_placeholders_with_files
@@ -29,42 +29,10 @@ from strawberry.types.graphql import OperationType
29
29
  from strawberry.types.unset import UNSET
30
30
 
31
31
  from .base import BaseView
32
- from .exceptions import HTTPException
33
32
  from .parse_content_type import parse_content_type
34
- from .types import HTTPMethod, QueryParams
35
33
  from .typevars import Context, Request, Response, RootValue, SubResponse
36
34
 
37
35
 
38
- class SyncHTTPRequestAdapter(abc.ABC):
39
- @property
40
- @abc.abstractmethod
41
- def query_params(self) -> QueryParams: ...
42
-
43
- @property
44
- @abc.abstractmethod
45
- def body(self) -> Union[str, bytes]: ...
46
-
47
- @property
48
- @abc.abstractmethod
49
- def method(self) -> HTTPMethod: ...
50
-
51
- @property
52
- @abc.abstractmethod
53
- def headers(self) -> Mapping[str, str]: ...
54
-
55
- @property
56
- @abc.abstractmethod
57
- def content_type(self) -> Optional[str]: ...
58
-
59
- @property
60
- @abc.abstractmethod
61
- def post_data(self) -> Mapping[str, Union[str, bytes]]: ...
62
-
63
- @property
64
- @abc.abstractmethod
65
- def files(self) -> Mapping[str, Any]: ...
66
-
67
-
68
36
  class SyncBaseHTTPView(
69
37
  abc.ABC,
70
38
  BaseView[Request],
@@ -92,15 +60,21 @@ class SyncBaseHTTPView(
92
60
 
93
61
  @abc.abstractmethod
94
62
  def create_response(
95
- self, response_data: GraphQLHTTPResponse, sub_response: SubResponse
63
+ self,
64
+ response_data: Union[GraphQLHTTPResponse, list[GraphQLHTTPResponse]],
65
+ sub_response: SubResponse,
96
66
  ) -> Response: ...
97
67
 
98
68
  @abc.abstractmethod
99
69
  def render_graphql_ide(self, request: Request) -> Response: ...
100
70
 
101
71
  def execute_operation(
102
- self, request: Request, context: Context, root_value: Optional[RootValue]
103
- ) -> ExecutionResult:
72
+ self,
73
+ request: Request,
74
+ context: Context,
75
+ root_value: Optional[RootValue],
76
+ sub_response: SubResponse,
77
+ ) -> Union[ExecutionResult, list[ExecutionResult]]:
104
78
  request_adapter = self.request_adapter_class(request)
105
79
 
106
80
  try:
@@ -116,16 +90,64 @@ class SyncBaseHTTPView(
116
90
  if not self.allow_queries_via_get and request_adapter.method == "GET":
117
91
  allowed_operation_types = allowed_operation_types - {OperationType.QUERY}
118
92
 
119
- return self.schema.execute_sync(
120
- request_data.query,
93
+ if isinstance(request_data, list):
94
+ # batch GraphQL requests
95
+ return [
96
+ self.execute_single(
97
+ request=request,
98
+ request_adapter=request_adapter,
99
+ sub_response=sub_response,
100
+ context=context,
101
+ root_value=root_value,
102
+ request_data=data,
103
+ )
104
+ for data in request_data
105
+ ]
106
+
107
+ return self.execute_single(
108
+ request=request,
109
+ request_adapter=request_adapter,
110
+ sub_response=sub_response,
111
+ context=context,
121
112
  root_value=root_value,
122
- variable_values=request_data.variables,
123
- context_value=context,
124
- operation_name=request_data.operation_name,
125
- allowed_operation_types=allowed_operation_types,
126
- operation_extensions=request_data.extensions,
113
+ request_data=request_data,
127
114
  )
128
115
 
116
+ def execute_single(
117
+ self,
118
+ request: Request,
119
+ request_adapter: SyncHTTPRequestAdapter,
120
+ sub_response: SubResponse,
121
+ context: Context,
122
+ root_value: Optional[RootValue],
123
+ request_data: GraphQLRequestData,
124
+ ) -> ExecutionResult:
125
+ allowed_operation_types = OperationType.from_http(request_adapter.method)
126
+
127
+ if not self.allow_queries_via_get and request_adapter.method == "GET":
128
+ allowed_operation_types = allowed_operation_types - {OperationType.QUERY}
129
+
130
+ try:
131
+ result = self.schema.execute_sync(
132
+ request_data.query,
133
+ root_value=root_value,
134
+ variable_values=request_data.variables,
135
+ context_value=context,
136
+ operation_name=request_data.operation_name,
137
+ allowed_operation_types=allowed_operation_types,
138
+ operation_extensions=request_data.extensions,
139
+ )
140
+ except CannotGetOperationTypeError as e:
141
+ raise HTTPException(400, e.as_http_error_reason()) from e
142
+ except InvalidOperationTypeError as e:
143
+ raise HTTPException(
144
+ 400, e.as_http_error_reason(request_adapter.method)
145
+ ) from e
146
+ except MissingQueryError as e:
147
+ raise HTTPException(400, "No GraphQL query found in the request") from e
148
+
149
+ return result
150
+
129
151
  def parse_multipart(self, request: SyncHTTPRequestAdapter) -> dict[str, str]:
130
152
  operations = self.parse_json(request.post_data.get("operations", "{}"))
131
153
  files_map = self.parse_json(request.post_data.get("map", "{}"))
@@ -135,8 +157,18 @@ class SyncBaseHTTPView(
135
157
  except KeyError as e:
136
158
  raise HTTPException(400, "File(s) missing in form data") from e
137
159
 
138
- def parse_http_body(self, request: SyncHTTPRequestAdapter) -> GraphQLRequestData:
160
+ def parse_http_body(
161
+ self, request: SyncHTTPRequestAdapter
162
+ ) -> Union[GraphQLRequestData, list[GraphQLRequestData]]:
163
+ headers = {key.lower(): value for key, value in request.headers.items()}
139
164
  content_type, params = parse_content_type(request.content_type or "")
165
+ accept = headers.get("accept", "")
166
+
167
+ protocol: Literal["http", "multipart-subscription"] = (
168
+ "multipart-subscription"
169
+ if self._is_multipart_subscriptions(*parse_content_type(accept))
170
+ else "http"
171
+ )
140
172
 
141
173
  if request.method == "GET":
142
174
  data = self.parse_query_params(request.query_params)
@@ -152,6 +184,18 @@ class SyncBaseHTTPView(
152
184
  else:
153
185
  raise HTTPException(400, "Unsupported content type")
154
186
 
187
+ if isinstance(data, list):
188
+ self._validate_batch_request(data, protocol=protocol)
189
+ return [
190
+ GraphQLRequestData(
191
+ query=item.get("query"),
192
+ variables=item.get("variables"),
193
+ operation_name=item.get("operationName"),
194
+ extensions=item.get("extensions"),
195
+ )
196
+ for item in data
197
+ ]
198
+
155
199
  query = data.get("query")
156
200
  if not isinstance(query, (str, type(None))):
157
201
  raise HTTPException(
@@ -209,25 +253,29 @@ class SyncBaseHTTPView(
209
253
  )
210
254
  root_value = self.get_root_value(request) if root_value is UNSET else root_value
211
255
 
212
- try:
213
- result = self.execute_operation(
214
- request=request,
215
- context=context,
216
- root_value=root_value,
217
- )
218
- except CannotGetOperationTypeError as e:
219
- raise HTTPException(400, e.as_http_error_reason()) from e
220
- except InvalidOperationTypeError as e:
221
- raise HTTPException(
222
- 400, e.as_http_error_reason(request_adapter.method)
223
- ) from e
224
- except MissingQueryError as e:
225
- raise HTTPException(400, "No GraphQL query found in the request") from e
256
+ result = self.execute_operation(
257
+ request=request,
258
+ context=context,
259
+ root_value=root_value,
260
+ sub_response=sub_response,
261
+ )
226
262
 
227
- response_data = self.process_result(request=request, result=result)
263
+ response_data: Union[GraphQLHTTPResponse, list[GraphQLHTTPResponse]]
264
+
265
+ if isinstance(result, list):
266
+ response_data = []
267
+ for execution_result in result:
268
+ processed_result = self.process_result(
269
+ request=request, result=execution_result
270
+ )
271
+ if execution_result.errors:
272
+ self._handle_errors(execution_result.errors, processed_result)
273
+ response_data.append(processed_result)
274
+ else:
275
+ response_data = self.process_result(request=request, result=result)
228
276
 
229
- if result.errors:
230
- self._handle_errors(result.errors, response_data)
277
+ if result.errors:
278
+ self._handle_errors(result.errors, response_data)
231
279
 
232
280
  return self.create_response(
233
281
  response_data=response_data, sub_response=sub_response
@@ -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
@@ -302,7 +264,9 @@ class GraphQLController(
302
264
  return Response(self.graphql_ide_html, media_type=MediaType.HTML)
303
265
 
304
266
  def create_response(
305
- self, response_data: GraphQLHTTPResponse, sub_response: Response[bytes]
267
+ self,
268
+ response_data: Union[GraphQLHTTPResponse, list[GraphQLHTTPResponse]],
269
+ sub_response: Response[bytes],
306
270
  ) -> Response[bytes]:
307
271
  response = Response(
308
272
  self.encode_json(response_data).encode(),