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.
@@ -2,6 +2,5 @@ from strawberry.aiohttp.handlers.graphql_transport_ws_handler import (
2
2
  GraphQLTransportWSHandler,
3
3
  )
4
4
  from strawberry.aiohttp.handlers.graphql_ws_handler import GraphQLWSHandler
5
- from strawberry.aiohttp.handlers.http_handler import HTTPHandler
6
5
 
7
- __all__ = ["GraphQLTransportWSHandler", "GraphQLWSHandler", "HTTPHandler"]
6
+ __all__ = ["GraphQLTransportWSHandler", "GraphQLWSHandler"]
@@ -1,33 +1,89 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
- import json
5
4
  from datetime import timedelta
6
- from typing import TYPE_CHECKING, Iterable
5
+ from io import BytesIO
6
+ from typing import (
7
+ TYPE_CHECKING,
8
+ Any,
9
+ Dict,
10
+ Iterable,
11
+ Mapping,
12
+ Optional,
13
+ cast,
14
+ )
7
15
 
8
16
  from aiohttp import web
9
17
  from strawberry.aiohttp.handlers import (
10
18
  GraphQLTransportWSHandler,
11
19
  GraphQLWSHandler,
12
- HTTPHandler,
13
20
  )
14
- from strawberry.http import process_result
21
+ from strawberry.http.async_base_view import AsyncBaseHTTPView, AsyncHTTPRequestAdapter
22
+ from strawberry.http.exceptions import HTTPException
23
+ from strawberry.http.types import FormData, HTTPMethod, QueryParams
24
+ from strawberry.http.typevars import (
25
+ Context,
26
+ RootValue,
27
+ )
15
28
  from strawberry.subscriptions import GRAPHQL_TRANSPORT_WS_PROTOCOL, GRAPHQL_WS_PROTOCOL
29
+ from strawberry.utils.graphiql import get_graphiql_html
16
30
 
17
31
  if TYPE_CHECKING:
18
32
  from strawberry.http import GraphQLHTTPResponse
19
33
  from strawberry.schema import BaseSchema
20
- from strawberry.types import ExecutionResult
21
34
 
22
35
 
23
- class GraphQLView:
36
+ class AioHTTPRequestAdapter(AsyncHTTPRequestAdapter):
37
+ def __init__(self, request: web.Request):
38
+ self.request = request
39
+
40
+ @property
41
+ def query_params(self) -> QueryParams:
42
+ return self.request.query.copy()
43
+
44
+ async def get_body(self) -> str:
45
+ return (await self.request.content.read()).decode()
46
+
47
+ @property
48
+ def method(self) -> HTTPMethod:
49
+ return cast(HTTPMethod, self.request.method.upper())
50
+
51
+ @property
52
+ def headers(self) -> Mapping[str, str]:
53
+ return self.request.headers
54
+
55
+ async def get_form_data(self) -> FormData:
56
+ reader = await self.request.multipart()
57
+
58
+ data: Dict[str, Any] = {}
59
+ files: Dict[str, Any] = {}
60
+
61
+ async for field in reader:
62
+ assert field.name
63
+
64
+ if field.filename:
65
+ files[field.name] = BytesIO(await field.read(decode=False))
66
+ else:
67
+ data[field.name] = await field.text()
68
+
69
+ return FormData(files=files, form=data)
70
+
71
+ @property
72
+ def content_type(self) -> Optional[str]:
73
+ return self.request.content_type
74
+
75
+
76
+ class GraphQLView(
77
+ AsyncBaseHTTPView[web.Request, web.Response, web.Response, Context, RootValue]
78
+ ):
24
79
  # Mark the view as coroutine so that AIOHTTP does not confuse it with a deprecated
25
80
  # bare handler function.
26
81
  _is_coroutine = asyncio.coroutines._is_coroutine # type: ignore[attr-defined]
27
82
 
28
83
  graphql_transport_ws_handler_class = GraphQLTransportWSHandler
29
84
  graphql_ws_handler_class = GraphQLWSHandler
30
- http_handler_class = HTTPHandler
85
+ allow_queries_via_get = True
86
+ request_adapter_class = AioHTTPRequestAdapter
31
87
 
32
88
  def __init__(
33
89
  self,
@@ -52,58 +108,64 @@ class GraphQLView:
52
108
  self.subscription_protocols = subscription_protocols
53
109
  self.connection_init_wait_timeout = connection_init_wait_timeout
54
110
 
111
+ def render_graphiql(self, request: web.Request) -> web.Response:
112
+ # TODO: get_graphiql_html should be on self
113
+ html_string = get_graphiql_html()
114
+
115
+ return web.Response(text=html_string, content_type="text/html")
116
+
117
+ async def get_sub_response(self, request: web.Request) -> web.Response:
118
+ return web.Response()
119
+
55
120
  async def __call__(self, request: web.Request) -> web.StreamResponse:
56
121
  ws = web.WebSocketResponse(protocols=self.subscription_protocols)
57
122
  ws_test = ws.can_prepare(request)
58
123
 
59
- if ws_test.ok:
60
- if ws_test.protocol == GRAPHQL_TRANSPORT_WS_PROTOCOL:
61
- return await self.graphql_transport_ws_handler_class(
62
- schema=self.schema,
63
- debug=self.debug,
64
- connection_init_wait_timeout=self.connection_init_wait_timeout,
65
- get_context=self.get_context, # type: ignore
66
- get_root_value=self.get_root_value,
67
- request=request,
68
- ).handle()
69
- elif ws_test.protocol == GRAPHQL_WS_PROTOCOL:
70
- return await self.graphql_ws_handler_class(
71
- schema=self.schema,
72
- debug=self.debug,
73
- keep_alive=self.keep_alive,
74
- keep_alive_interval=self.keep_alive_interval,
75
- get_context=self.get_context,
76
- get_root_value=self.get_root_value,
77
- request=request,
78
- ).handle()
79
- else:
80
- await ws.prepare(request)
81
- await ws.close(code=4406, message=b"Subprotocol not acceptable")
82
- return ws
83
- else:
84
- return await self.http_handler_class(
124
+ if not ws_test.ok:
125
+ try:
126
+ return await self.run(request=request)
127
+ except HTTPException as e:
128
+ return web.Response(
129
+ body=e.reason,
130
+ status=e.status_code,
131
+ )
132
+
133
+ if ws_test.protocol == GRAPHQL_TRANSPORT_WS_PROTOCOL:
134
+ return await self.graphql_transport_ws_handler_class(
135
+ schema=self.schema,
136
+ debug=self.debug,
137
+ connection_init_wait_timeout=self.connection_init_wait_timeout,
138
+ get_context=self.get_context, # type: ignore
139
+ get_root_value=self.get_root_value,
140
+ request=request,
141
+ ).handle()
142
+ elif ws_test.protocol == GRAPHQL_WS_PROTOCOL:
143
+ return await self.graphql_ws_handler_class(
85
144
  schema=self.schema,
86
- graphiql=self.graphiql,
87
- allow_queries_via_get=self.allow_queries_via_get,
145
+ debug=self.debug,
146
+ keep_alive=self.keep_alive,
147
+ keep_alive_interval=self.keep_alive_interval,
88
148
  get_context=self.get_context,
89
149
  get_root_value=self.get_root_value,
90
- encode_json=self.encode_json,
91
- process_result=self.process_result,
92
150
  request=request,
93
151
  ).handle()
152
+ else:
153
+ await ws.prepare(request)
154
+ await ws.close(code=4406, message=b"Subprotocol not acceptable")
155
+ return ws
94
156
 
95
- async def get_root_value(self, request: web.Request) -> object:
157
+ async def get_root_value(self, request: web.Request) -> Optional[RootValue]:
96
158
  return None
97
159
 
98
160
  async def get_context(
99
- self, request: web.Request, response: web.StreamResponse
100
- ) -> object:
101
- return {"request": request, "response": response}
161
+ self, request: web.Request, response: web.Response
162
+ ) -> Context:
163
+ return {"request": request, "response": response} # type: ignore
102
164
 
103
- async def process_result(
104
- self, request: web.Request, result: ExecutionResult
105
- ) -> GraphQLHTTPResponse:
106
- return process_result(result)
165
+ def create_response(
166
+ self, response_data: GraphQLHTTPResponse, sub_response: web.Response
167
+ ) -> web.Response:
168
+ sub_response.text = self.encode_json(response_data)
169
+ sub_response.content_type = "application/json"
107
170
 
108
- def encode_json(self, response_data: GraphQLHTTPResponse) -> str:
109
- return json.dumps(response_data)
171
+ return sub_response
@@ -1,33 +1,87 @@
1
1
  from __future__ import annotations
2
2
 
3
- import json
4
3
  from datetime import timedelta
5
- from typing import TYPE_CHECKING, Any, Optional, Union
4
+ from typing import (
5
+ TYPE_CHECKING,
6
+ Any,
7
+ Mapping,
8
+ Optional,
9
+ Sequence,
10
+ Union,
11
+ cast,
12
+ )
6
13
 
14
+ from starlette import status
15
+ from starlette.requests import Request
16
+ from starlette.responses import HTMLResponse, PlainTextResponse, Response
7
17
  from starlette.websockets import WebSocket
8
18
 
9
19
  from strawberry.asgi.handlers import (
10
20
  GraphQLTransportWSHandler,
11
21
  GraphQLWSHandler,
12
- HTTPHandler,
13
22
  )
14
- from strawberry.http import process_result
23
+ from strawberry.http.async_base_view import AsyncBaseHTTPView, AsyncHTTPRequestAdapter
24
+ from strawberry.http.exceptions import HTTPException
25
+ from strawberry.http.types import FormData, HTTPMethod, QueryParams
26
+ from strawberry.http.typevars import (
27
+ Context,
28
+ RootValue,
29
+ )
15
30
  from strawberry.subscriptions import GRAPHQL_TRANSPORT_WS_PROTOCOL, GRAPHQL_WS_PROTOCOL
31
+ from strawberry.utils.graphiql import get_graphiql_html
16
32
 
17
33
  if TYPE_CHECKING:
18
- from starlette.requests import Request
19
- from starlette.responses import Response
20
34
  from starlette.types import Receive, Scope, Send
21
35
 
22
36
  from strawberry.http import GraphQLHTTPResponse
23
37
  from strawberry.schema import BaseSchema
24
- from strawberry.types import ExecutionResult
25
38
 
26
39
 
27
- class GraphQL:
40
+ class ASGIRequestAdapter(AsyncHTTPRequestAdapter):
41
+ def __init__(self, request: Request) -> None:
42
+ self.request = request
43
+
44
+ @property
45
+ def query_params(self) -> QueryParams:
46
+ return dict(self.request.query_params)
47
+
48
+ @property
49
+ def method(self) -> HTTPMethod:
50
+ return cast(HTTPMethod, self.request.method.upper())
51
+
52
+ @property
53
+ def headers(self) -> Mapping[str, str]:
54
+ return self.request.headers
55
+
56
+ @property
57
+ def content_type(self) -> Optional[str]:
58
+ return self.request.headers.get("content-type")
59
+
60
+ async def get_body(self) -> bytes:
61
+ return await self.request.body()
62
+
63
+ async def get_form_data(self) -> FormData:
64
+ multipart_data = await self.request.form()
65
+
66
+ return FormData(
67
+ files=multipart_data,
68
+ form=multipart_data,
69
+ )
70
+
71
+
72
+ class GraphQL(
73
+ AsyncBaseHTTPView[
74
+ Union[Request, WebSocket],
75
+ Response,
76
+ Response,
77
+ Context,
78
+ RootValue,
79
+ ]
80
+ ):
28
81
  graphql_transport_ws_handler_class = GraphQLTransportWSHandler
29
82
  graphql_ws_handler_class = GraphQLWSHandler
30
- http_handler_class = HTTPHandler
83
+ allow_queries_via_get = True
84
+ request_adapter_class = ASGIRequestAdapter # pyright: ignore
31
85
 
32
86
  def __init__(
33
87
  self,
@@ -37,7 +91,10 @@ class GraphQL:
37
91
  keep_alive: bool = False,
38
92
  keep_alive_interval: float = 1,
39
93
  debug: bool = False,
40
- subscription_protocols=(GRAPHQL_TRANSPORT_WS_PROTOCOL, GRAPHQL_WS_PROTOCOL),
94
+ subscription_protocols: Sequence[str] = (
95
+ GRAPHQL_TRANSPORT_WS_PROTOCOL,
96
+ GRAPHQL_WS_PROTOCOL,
97
+ ),
41
98
  connection_init_wait_timeout: timedelta = timedelta(minutes=1),
42
99
  ) -> None:
43
100
  self.schema = schema
@@ -51,16 +108,7 @@ class GraphQL:
51
108
 
52
109
  async def __call__(self, scope: Scope, receive: Receive, send: Send):
53
110
  if scope["type"] == "http":
54
- await self.http_handler_class(
55
- schema=self.schema,
56
- graphiql=self.graphiql,
57
- allow_queries_via_get=self.allow_queries_via_get,
58
- debug=self.debug,
59
- get_context=self.get_context,
60
- get_root_value=self.get_root_value,
61
- process_result=self.process_result,
62
- encode_json=self.encode_json,
63
- ).handle(scope=scope, receive=receive, send=send)
111
+ return await self.handle_http(scope, receive, send)
64
112
 
65
113
  elif scope["type"] == "websocket":
66
114
  ws = WebSocket(scope=scope, receive=receive, send=send)
@@ -102,16 +150,57 @@ class GraphQL:
102
150
  return None
103
151
 
104
152
  async def get_context(
153
+ self, request: Union[Request, WebSocket], response: Response
154
+ ) -> Context:
155
+ return {"request": request, "response": response} # type: ignore
156
+
157
+ async def get_sub_response(
105
158
  self,
106
159
  request: Union[Request, WebSocket],
107
- response: Optional[Response] = None,
108
- ) -> Optional[Any]:
109
- return {"request": request, "response": response}
160
+ ) -> Response:
161
+ sub_response = Response()
162
+ sub_response.status_code = None # type: ignore
163
+ del sub_response.headers["content-length"]
164
+
165
+ return sub_response
166
+
167
+ async def handle_http(
168
+ self,
169
+ scope: Scope,
170
+ receive: Receive,
171
+ send: Send,
172
+ ) -> None:
173
+ request = Request(scope=scope, receive=receive)
174
+
175
+ try:
176
+ response = await self.run(request)
177
+ except HTTPException as e:
178
+ response = PlainTextResponse(
179
+ e.reason, status_code=e.status_code
180
+ ) # pyright: ignore
181
+
182
+ await response(scope, receive, send)
183
+
184
+ def render_graphiql(self, request: Union[Request, WebSocket]) -> Response:
185
+ html = get_graphiql_html()
186
+
187
+ return HTMLResponse(html)
188
+
189
+ def create_response(
190
+ self, response_data: GraphQLHTTPResponse, sub_response: Response
191
+ ) -> Response:
192
+ response = Response(
193
+ self.encode_json(response_data),
194
+ status_code=status.HTTP_200_OK,
195
+ media_type="application/json",
196
+ )
197
+
198
+ response.headers.raw.extend(sub_response.headers.raw)
199
+
200
+ if sub_response.background:
201
+ response.background = sub_response.background
110
202
 
111
- async def process_result(
112
- self, request: Request, result: ExecutionResult
113
- ) -> GraphQLHTTPResponse:
114
- return process_result(result)
203
+ if sub_response.status_code:
204
+ response.status_code = sub_response.status_code
115
205
 
116
- def encode_json(self, response_data: GraphQLHTTPResponse) -> str:
117
- return json.dumps(response_data)
206
+ return response
@@ -2,6 +2,5 @@ from strawberry.asgi.handlers.graphql_transport_ws_handler import (
2
2
  GraphQLTransportWSHandler,
3
3
  )
4
4
  from strawberry.asgi.handlers.graphql_ws_handler import GraphQLWSHandler
5
- from strawberry.asgi.handlers.http_handler import HTTPHandler
6
5
 
7
- __all__ = ["GraphQLTransportWSHandler", "GraphQLWSHandler", "HTTPHandler"]
6
+ __all__ = ["GraphQLTransportWSHandler", "GraphQLWSHandler"]