strawberry-graphql 0.247.2__py3-none-any.whl → 0.248.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.
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import dataclasses
4
3
  import uuid
5
4
  from typing import (
6
5
  TYPE_CHECKING,
@@ -26,10 +25,14 @@ from strawberry.subscriptions.protocols.graphql_transport_ws.types import (
26
25
  SubscribeMessage,
27
26
  SubscribeMessagePayload,
28
27
  )
29
- from strawberry.subscriptions.protocols.graphql_ws import (
30
- GQL_CONNECTION_ACK,
31
- GQL_CONNECTION_INIT,
32
- GQL_START,
28
+ from strawberry.subscriptions.protocols.graphql_ws.types import (
29
+ ConnectionAckMessage as GraphQLWSConnectionAckMessage,
30
+ )
31
+ from strawberry.subscriptions.protocols.graphql_ws.types import (
32
+ ConnectionInitMessage as GraphQLWSConnectionInitMessage,
33
+ )
34
+ from strawberry.subscriptions.protocols.graphql_ws.types import (
35
+ StartMessage as GraphQLWSStartMessage,
33
36
  )
34
37
  from strawberry.types import ExecutionResult
35
38
 
@@ -112,9 +115,11 @@ class GraphQLWebsocketCommunicator(WebsocketCommunicator):
112
115
  assert response == ConnectionAckMessage().as_dict()
113
116
  else:
114
117
  assert res == (True, GRAPHQL_WS_PROTOCOL)
115
- await self.send_json_to({"type": GQL_CONNECTION_INIT})
116
- response = await self.receive_json_from()
117
- assert response["type"] == GQL_CONNECTION_ACK
118
+ await self.send_json_to(
119
+ GraphQLWSConnectionInitMessage({"type": "connection_init"})
120
+ )
121
+ response: GraphQLWSConnectionAckMessage = await self.receive_json_from()
122
+ assert response["type"] == "connection_ack"
118
123
 
119
124
  # Actual `ExecutionResult`` objects are not available client-side, since they
120
125
  # get transformed into `FormattedExecutionResult` on the wire, but we attempt
@@ -123,22 +128,28 @@ class GraphQLWebsocketCommunicator(WebsocketCommunicator):
123
128
  self, query: str, variables: Optional[Dict] = None
124
129
  ) -> Union[ExecutionResult, AsyncIterator[ExecutionResult]]:
125
130
  id_ = uuid.uuid4().hex
126
- sub_payload = SubscribeMessagePayload(query=query, variables=variables)
131
+
127
132
  if self.protocol == GRAPHQL_TRANSPORT_WS_PROTOCOL:
128
133
  await self.send_json_to(
129
134
  SubscribeMessage(
130
135
  id=id_,
131
- payload=sub_payload,
136
+ payload=SubscribeMessagePayload(query=query, variables=variables),
132
137
  ).as_dict()
133
138
  )
134
139
  else:
135
- await self.send_json_to(
136
- {
137
- "type": GQL_START,
138
- "id": id_,
139
- "payload": dataclasses.asdict(sub_payload),
140
- }
141
- )
140
+ start_message: GraphQLWSStartMessage = {
141
+ "type": "start",
142
+ "id": id_,
143
+ "payload": {
144
+ "query": query,
145
+ },
146
+ }
147
+
148
+ if variables is not None:
149
+ start_message["payload"]["variables"] = variables
150
+
151
+ await self.send_json_to(start_message)
152
+
142
153
  while True:
143
154
  response = await self.receive_json_from(timeout=5)
144
155
  message_type = response["type"]
@@ -1,24 +0,0 @@
1
- GQL_CONNECTION_INIT = "connection_init"
2
- GQL_CONNECTION_ACK = "connection_ack"
3
- GQL_CONNECTION_ERROR = "connection_error"
4
- GQL_CONNECTION_TERMINATE = "connection_terminate"
5
- GQL_CONNECTION_KEEP_ALIVE = "ka"
6
- GQL_START = "start"
7
- GQL_DATA = "data"
8
- GQL_ERROR = "error"
9
- GQL_COMPLETE = "complete"
10
- GQL_STOP = "stop"
11
-
12
-
13
- __all__ = [
14
- "GQL_CONNECTION_INIT",
15
- "GQL_CONNECTION_ACK",
16
- "GQL_CONNECTION_ERROR",
17
- "GQL_CONNECTION_TERMINATE",
18
- "GQL_CONNECTION_KEEP_ALIVE",
19
- "GQL_START",
20
- "GQL_DATA",
21
- "GQL_ERROR",
22
- "GQL_COMPLETE",
23
- "GQL_STOP",
24
- ]
@@ -11,24 +11,18 @@ from typing import (
11
11
  )
12
12
 
13
13
  from strawberry.http.exceptions import NonTextMessageReceived, WebSocketDisconnected
14
- from strawberry.subscriptions.protocols.graphql_ws import (
15
- GQL_COMPLETE,
16
- GQL_CONNECTION_ACK,
17
- GQL_CONNECTION_ERROR,
18
- GQL_CONNECTION_INIT,
19
- GQL_CONNECTION_KEEP_ALIVE,
20
- GQL_CONNECTION_TERMINATE,
21
- GQL_DATA,
22
- GQL_ERROR,
23
- GQL_START,
24
- GQL_STOP,
25
- )
26
14
  from strawberry.subscriptions.protocols.graphql_ws.types import (
27
- ConnectionInitPayload,
28
- DataPayload,
15
+ CompleteMessage,
16
+ ConnectionAckMessage,
17
+ ConnectionErrorMessage,
18
+ ConnectionInitMessage,
19
+ ConnectionKeepAliveMessage,
20
+ ConnectionTerminateMessage,
21
+ DataMessage,
22
+ ErrorMessage,
29
23
  OperationMessage,
30
- OperationMessagePayload,
31
- StartPayload,
24
+ StartMessage,
25
+ StopMessage,
32
26
  )
33
27
  from strawberry.types.execution import ExecutionResult, PreExecutionError
34
28
  from strawberry.utils.debug import pretty_print_graphql_operation
@@ -59,7 +53,7 @@ class BaseGraphQLWSHandler:
59
53
  self.keep_alive_task: Optional[asyncio.Task] = None
60
54
  self.subscriptions: Dict[str, AsyncGenerator] = {}
61
55
  self.tasks: Dict[str, asyncio.Task] = {}
62
- self.connection_params: Optional[ConnectionInitPayload] = None
56
+ self.connection_params: Optional[Dict[str, object]] = None
63
57
 
64
58
  async def handle(self) -> None:
65
59
  try:
@@ -87,41 +81,40 @@ class BaseGraphQLWSHandler:
87
81
  self,
88
82
  message: OperationMessage,
89
83
  ) -> None:
90
- message_type = message["type"]
91
-
92
- if message_type == GQL_CONNECTION_INIT:
84
+ if message["type"] == "connection_init":
93
85
  await self.handle_connection_init(message)
94
- elif message_type == GQL_CONNECTION_TERMINATE:
86
+ elif message["type"] == "connection_terminate":
95
87
  await self.handle_connection_terminate(message)
96
- elif message_type == GQL_START:
88
+ elif message["type"] == "start":
97
89
  await self.handle_start(message)
98
- elif message_type == GQL_STOP:
90
+ elif message["type"] == "stop":
99
91
  await self.handle_stop(message)
100
92
 
101
- async def handle_connection_init(self, message: OperationMessage) -> None:
93
+ async def handle_connection_init(self, message: ConnectionInitMessage) -> None:
102
94
  payload = message.get("payload")
103
95
  if payload is not None and not isinstance(payload, dict):
104
- error_message: OperationMessage = {"type": GQL_CONNECTION_ERROR}
96
+ error_message: ConnectionErrorMessage = {"type": "connection_error"}
105
97
  await self.websocket.send_json(error_message)
106
98
  await self.websocket.close(code=1000, reason="")
107
99
  return
108
100
 
109
- payload = cast(Optional["ConnectionInitPayload"], payload)
110
101
  self.connection_params = payload
111
102
 
112
- acknowledge_message: OperationMessage = {"type": GQL_CONNECTION_ACK}
113
- await self.websocket.send_json(acknowledge_message)
103
+ connection_ack_message: ConnectionAckMessage = {"type": "connection_ack"}
104
+ await self.websocket.send_json(connection_ack_message)
114
105
 
115
106
  if self.keep_alive:
116
107
  keep_alive_handler = self.handle_keep_alive()
117
108
  self.keep_alive_task = asyncio.create_task(keep_alive_handler)
118
109
 
119
- async def handle_connection_terminate(self, message: OperationMessage) -> None:
110
+ async def handle_connection_terminate(
111
+ self, message: ConnectionTerminateMessage
112
+ ) -> None:
120
113
  await self.websocket.close(code=1000, reason="")
121
114
 
122
- async def handle_start(self, message: OperationMessage) -> None:
115
+ async def handle_start(self, message: StartMessage) -> None:
123
116
  operation_id = message["id"]
124
- payload = cast("StartPayload", message["payload"])
117
+ payload = message["payload"]
125
118
  query = payload["query"]
126
119
  operation_name = payload.get("operationName")
127
120
  variables = payload.get("variables")
@@ -139,14 +132,14 @@ class BaseGraphQLWSHandler:
139
132
  )
140
133
  self.tasks[operation_id] = asyncio.create_task(result_handler)
141
134
 
142
- async def handle_stop(self, message: OperationMessage) -> None:
135
+ async def handle_stop(self, message: StopMessage) -> None:
143
136
  operation_id = message["id"]
144
137
  await self.cleanup_operation(operation_id)
145
138
 
146
139
  async def handle_keep_alive(self) -> None:
147
140
  assert self.keep_alive_interval
148
141
  while True:
149
- data: OperationMessage = {"type": GQL_CONNECTION_KEEP_ALIVE}
142
+ data: ConnectionKeepAliveMessage = {"type": "ka"}
150
143
  await self.websocket.send_json(data)
151
144
  await asyncio.sleep(self.keep_alive_interval)
152
145
 
@@ -168,14 +161,24 @@ class BaseGraphQLWSHandler:
168
161
  if isinstance(agen_or_err, PreExecutionError):
169
162
  assert agen_or_err.errors
170
163
  error_payload = agen_or_err.errors[0].formatted
171
- await self.send_message(GQL_ERROR, operation_id, error_payload)
164
+ error_message: ErrorMessage = {
165
+ "type": "error",
166
+ "id": operation_id,
167
+ "payload": error_payload,
168
+ }
169
+ await self.websocket.send_json(error_message)
172
170
  else:
173
171
  self.subscriptions[operation_id] = agen_or_err
174
172
  async for result in agen_or_err:
175
173
  await self.send_data(result, operation_id)
176
- await self.send_message(GQL_COMPLETE, operation_id, None)
174
+ complete_message: CompleteMessage = {
175
+ "type": "complete",
176
+ "id": operation_id,
177
+ }
178
+ await self.websocket.send_json(complete_message)
177
179
  except asyncio.CancelledError:
178
- await self.send_message(GQL_COMPLETE, operation_id, None)
180
+ complete_message: CompleteMessage = {"type": "complete", "id": operation_id}
181
+ await self.websocket.send_json(complete_message)
179
182
 
180
183
  async def cleanup_operation(self, operation_id: str) -> None:
181
184
  if operation_id in self.subscriptions:
@@ -188,26 +191,24 @@ class BaseGraphQLWSHandler:
188
191
  await self.tasks[operation_id]
189
192
  del self.tasks[operation_id]
190
193
 
191
- async def send_message(
192
- self,
193
- type_: str,
194
- operation_id: str,
195
- payload: Optional[OperationMessagePayload] = None,
196
- ) -> None:
197
- data: OperationMessage = {"type": type_, "id": operation_id}
198
- if payload is not None:
199
- data["payload"] = payload
200
- await self.websocket.send_json(data)
201
-
202
194
  async def send_data(
203
195
  self, execution_result: ExecutionResult, operation_id: str
204
196
  ) -> None:
205
- payload: DataPayload = {"data": execution_result.data}
197
+ data_message: DataMessage = {
198
+ "type": "data",
199
+ "id": operation_id,
200
+ "payload": {"data": execution_result.data},
201
+ }
202
+
206
203
  if execution_result.errors:
207
- payload["errors"] = [err.formatted for err in execution_result.errors]
204
+ data_message["payload"]["errors"] = [
205
+ err.formatted for err in execution_result.errors
206
+ ]
207
+
208
208
  if execution_result.extensions:
209
- payload["extensions"] = execution_result.extensions
210
- await self.send_message(GQL_DATA, operation_id, payload)
209
+ data_message["payload"]["extensions"] = execution_result.extensions
210
+
211
+ await self.websocket.send_json(data_message)
211
212
 
212
213
 
213
214
  __all__ = ["BaseGraphQLWSHandler"]
@@ -1,52 +1,96 @@
1
- from typing import Any, Dict, List, Optional, Union
2
- from typing_extensions import TypedDict
1
+ from typing import Dict, List, TypedDict, Union
2
+ from typing_extensions import Literal, NotRequired
3
3
 
4
4
  from graphql import GraphQLFormattedError
5
5
 
6
- ConnectionInitPayload = Dict[str, Any]
7
6
 
7
+ class ConnectionInitMessage(TypedDict):
8
+ type: Literal["connection_init"]
9
+ payload: NotRequired[Dict[str, object]]
8
10
 
9
- ConnectionErrorPayload = Dict[str, Any]
10
11
 
11
-
12
- class StartPayload(TypedDict, total=False):
12
+ class StartMessagePayload(TypedDict):
13
13
  query: str
14
- variables: Optional[Dict[str, Any]]
15
- operationName: Optional[str]
14
+ variables: NotRequired[Dict[str, object]]
15
+ operationName: NotRequired[str]
16
+
17
+
18
+ class StartMessage(TypedDict):
19
+ type: Literal["start"]
20
+ id: str
21
+ payload: StartMessagePayload
16
22
 
17
23
 
18
- class DataPayload(TypedDict, total=False):
19
- data: Any
24
+ class StopMessage(TypedDict):
25
+ type: Literal["stop"]
26
+ id: str
20
27
 
21
- # Optional list of formatted graphql.GraphQLError objects
22
- errors: Optional[List[GraphQLFormattedError]]
23
- extensions: Optional[Dict[str, Any]]
24
28
 
29
+ class ConnectionTerminateMessage(TypedDict):
30
+ type: Literal["connection_terminate"]
25
31
 
26
- ErrorPayload = GraphQLFormattedError
27
32
 
33
+ class ConnectionErrorMessage(TypedDict):
34
+ type: Literal["connection_error"]
35
+ payload: NotRequired[Dict[str, object]]
28
36
 
29
- OperationMessagePayload = Union[
30
- ConnectionInitPayload,
31
- ConnectionErrorPayload,
32
- StartPayload,
33
- DataPayload,
34
- ErrorPayload,
35
- ]
36
37
 
38
+ class ConnectionAckMessage(TypedDict):
39
+ type: Literal["connection_ack"]
37
40
 
38
- class OperationMessage(TypedDict, total=False):
39
- type: str
41
+
42
+ class DataMessagePayload(TypedDict):
43
+ data: object
44
+ errors: NotRequired[List[GraphQLFormattedError]]
45
+
46
+ # Non-standard field:
47
+ extensions: NotRequired[Dict[str, object]]
48
+
49
+
50
+ class DataMessage(TypedDict):
51
+ type: Literal["data"]
40
52
  id: str
41
- payload: OperationMessagePayload
53
+ payload: DataMessagePayload
54
+
55
+
56
+ class ErrorMessage(TypedDict):
57
+ type: Literal["error"]
58
+ id: str
59
+ payload: GraphQLFormattedError
60
+
61
+
62
+ class CompleteMessage(TypedDict):
63
+ type: Literal["complete"]
64
+ id: str
65
+
66
+
67
+ class ConnectionKeepAliveMessage(TypedDict):
68
+ type: Literal["ka"]
69
+
70
+
71
+ OperationMessage = Union[
72
+ ConnectionInitMessage,
73
+ StartMessage,
74
+ StopMessage,
75
+ ConnectionTerminateMessage,
76
+ ConnectionErrorMessage,
77
+ ConnectionAckMessage,
78
+ DataMessage,
79
+ ErrorMessage,
80
+ CompleteMessage,
81
+ ]
42
82
 
43
83
 
44
84
  __all__ = [
45
- "ConnectionInitPayload",
46
- "ConnectionErrorPayload",
47
- "StartPayload",
48
- "DataPayload",
49
- "ErrorPayload",
50
- "OperationMessagePayload",
85
+ "ConnectionInitMessage",
86
+ "StartMessage",
87
+ "StopMessage",
88
+ "ConnectionTerminateMessage",
89
+ "ConnectionErrorMessage",
90
+ "ConnectionAckMessage",
91
+ "DataMessage",
92
+ "ErrorMessage",
93
+ "CompleteMessage",
94
+ "ConnectionKeepAliveMessage",
51
95
  "OperationMessage",
52
96
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: strawberry-graphql
3
- Version: 0.247.2
3
+ Version: 0.248.0
4
4
  Summary: A library for creating GraphQL APIs
5
5
  Home-page: https://strawberry.rocks/
6
6
  License: MIT
@@ -16,7 +16,7 @@ strawberry/channels/handlers/base.py,sha256=KV4KA0eF5NRtikV9m4ssoPI5pmCZvDuRkfoT
16
16
  strawberry/channels/handlers/http_handler.py,sha256=fnQcPwdUz2CboVlnI3s1urQ_ZnZyNZvRx7SM_xPLLEA,11577
17
17
  strawberry/channels/handlers/ws_handler.py,sha256=k9xax8S1g0tfEFSe76UOHkheHqIrnYjEioYlLm4UPLo,6052
18
18
  strawberry/channels/router.py,sha256=DKIbl4zuRBhfvViUVpyu0Rf_WRT41E6uZC-Yic9Ltvo,2024
19
- strawberry/channels/testing.py,sha256=GZqYu_rhrT1gLHmdI219L1fctVDmrv7AMHs0bwhXitc,6166
19
+ strawberry/channels/testing.py,sha256=Ae2G8v4bKdkw8Se6itQ-gkaHpZREhrHWPPpF5dbhroY,6592
20
20
  strawberry/cli/__init__.py,sha256=byS5VrEiTJatAH6YS4V1Kd4SOwMRAQO2M1oJdIddivg,585
21
21
  strawberry/cli/app.py,sha256=tTMBV1pdWqMcwjWO2yn-8oLDhMhfJvUzyQtWs75LWJ0,54
22
22
  strawberry/cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -189,9 +189,9 @@ strawberry/subscriptions/protocols/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQe
189
189
  strawberry/subscriptions/protocols/graphql_transport_ws/__init__.py,sha256=wN6dkMu6WiaIZTE19PGoN9xXpIN_RdDE_q7F7ZgjCxk,138
190
190
  strawberry/subscriptions/protocols/graphql_transport_ws/handlers.py,sha256=_h-xNf_ZRtjn8PGbxZk3u9qTR-NNNCevdgaFF0uXciw,14728
191
191
  strawberry/subscriptions/protocols/graphql_transport_ws/types.py,sha256=udYxzGtwjETYvY5f23org0t-aY4cimTjEGFYUR3idaY,2596
192
- strawberry/subscriptions/protocols/graphql_ws/__init__.py,sha256=ijn1A1O0Fzv5p9n-jw3T5H7M3oxbN4gbxRaepN7HyJk,553
193
- strawberry/subscriptions/protocols/graphql_ws/handlers.py,sha256=tQmQjoA-x4yXLHbjmq7r7fFnNPluIcc7EiYgQSFbiZ0,7740
194
- strawberry/subscriptions/protocols/graphql_ws/types.py,sha256=CKm4Hy95p6H9u_-QyWdxipwzNeeDIWtilP3RRp8vjPw,1050
192
+ strawberry/subscriptions/protocols/graphql_ws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
193
+ strawberry/subscriptions/protocols/graphql_ws/handlers.py,sha256=IQsOqh_9uWZMuBPmo05ThEzE9cC98WiRFt2K-2GWymk,7703
194
+ strawberry/subscriptions/protocols/graphql_ws/types.py,sha256=diZ36w56Nb_YmgfWXe6uXGiQOKmWIVjNEkcM-PkjaSs,1939
195
195
  strawberry/test/__init__.py,sha256=U3B5Ng7C_H8GpCpfvgZZcfADMw6cor5hm78gS3nDdMI,115
196
196
  strawberry/test/client.py,sha256=Va7J1tIjZ6PxbOqPl57jSp5lNLOZSueHPmrUuUx5sRY,6462
197
197
  strawberry/tools/__init__.py,sha256=pdGpZx8wpq03VfUZJyF9JtYxZhGqzzxCiipsalWxJX4,127
@@ -230,8 +230,8 @@ strawberry/utils/logging.py,sha256=U1cseHGquN09YFhFmRkiphfASKCyK0HUZREImPgVb0c,7
230
230
  strawberry/utils/operation.py,sha256=SSXxN-vMqdHO6W2OZtip-1z7y4_A-eTVFdhDvhKeLCk,1193
231
231
  strawberry/utils/str_converters.py,sha256=KGd7QH90RevaJjH6SQEkiVVsb8KuhJr_wv5AsI7UzQk,897
232
232
  strawberry/utils/typing.py,sha256=3xws5kxSQGsp8BnYyUwClvxXNzZakMAuOPoq1rjHRuk,14252
233
- strawberry_graphql-0.247.2.dist-info/LICENSE,sha256=m-XnIVUKqlG_AWnfi9NReh9JfKhYOB-gJfKE45WM1W8,1072
234
- strawberry_graphql-0.247.2.dist-info/METADATA,sha256=UNSXeMuxxVcoA8hC-8-CP-I7jeo-4R8Syg8W-kIA-ZE,7758
235
- strawberry_graphql-0.247.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
236
- strawberry_graphql-0.247.2.dist-info/entry_points.txt,sha256=Nk7-aT3_uEwCgyqtHESV9H6Mc31cK-VAvhnQNTzTb4k,49
237
- strawberry_graphql-0.247.2.dist-info/RECORD,,
233
+ strawberry_graphql-0.248.0.dist-info/LICENSE,sha256=m-XnIVUKqlG_AWnfi9NReh9JfKhYOB-gJfKE45WM1W8,1072
234
+ strawberry_graphql-0.248.0.dist-info/METADATA,sha256=aKYBf7nSLOqh8q-WwFvOugbyH8ZlZBDokIS6Y5HfoMw,7758
235
+ strawberry_graphql-0.248.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
236
+ strawberry_graphql-0.248.0.dist-info/entry_points.txt,sha256=Nk7-aT3_uEwCgyqtHESV9H6Mc31cK-VAvhnQNTzTb4k,49
237
+ strawberry_graphql-0.248.0.dist-info/RECORD,,