strawberry-graphql 0.248.1__py3-none-any.whl → 0.250.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.
@@ -17,23 +17,10 @@ from graphql import GraphQLError, GraphQLFormattedError
17
17
 
18
18
  from channels.testing.websocket import WebsocketCommunicator
19
19
  from strawberry.subscriptions import GRAPHQL_TRANSPORT_WS_PROTOCOL, GRAPHQL_WS_PROTOCOL
20
- from strawberry.subscriptions.protocols.graphql_transport_ws.types import (
21
- ConnectionAckMessage,
22
- ConnectionInitMessage,
23
- ErrorMessage,
24
- NextMessage,
25
- SubscribeMessage,
26
- SubscribeMessagePayload,
27
- )
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,
20
+ from strawberry.subscriptions.protocols.graphql_transport_ws import (
21
+ types as transport_ws_types,
36
22
  )
23
+ from strawberry.subscriptions.protocols.graphql_ws import types as ws_types
37
24
  from strawberry.types import ExecutionResult
38
25
 
39
26
  if TYPE_CHECKING:
@@ -109,19 +96,21 @@ class GraphQLWebsocketCommunicator(WebsocketCommunicator):
109
96
  if self.protocol == GRAPHQL_TRANSPORT_WS_PROTOCOL:
110
97
  assert res == (True, GRAPHQL_TRANSPORT_WS_PROTOCOL)
111
98
  await self.send_json_to(
112
- ConnectionInitMessage(payload=self.connection_params).as_dict()
99
+ transport_ws_types.ConnectionInitMessage(
100
+ {"type": "connection_init", "payload": self.connection_params}
101
+ )
113
102
  )
114
- graphql_transport_ws_response = await self.receive_json_from()
115
- assert graphql_transport_ws_response == ConnectionAckMessage().as_dict()
103
+ transport_ws_connection_ack_message: transport_ws_types.ConnectionAckMessage = await self.receive_json_from()
104
+ assert transport_ws_connection_ack_message == {"type": "connection_ack"}
116
105
  else:
117
106
  assert res == (True, GRAPHQL_WS_PROTOCOL)
118
107
  await self.send_json_to(
119
- GraphQLWSConnectionInitMessage({"type": "connection_init"})
108
+ ws_types.ConnectionInitMessage({"type": "connection_init"})
120
109
  )
121
- graphql_ws_response: GraphQLWSConnectionAckMessage = (
110
+ ws_connection_ack_message: ws_types.ConnectionAckMessage = (
122
111
  await self.receive_json_from()
123
112
  )
124
- assert graphql_ws_response["type"] == "connection_ack"
113
+ assert ws_connection_ack_message["type"] == "connection_ack"
125
114
 
126
115
  # Actual `ExecutionResult`` objects are not available client-side, since they
127
116
  # get transformed into `FormattedExecutionResult` on the wire, but we attempt
@@ -133,13 +122,16 @@ class GraphQLWebsocketCommunicator(WebsocketCommunicator):
133
122
 
134
123
  if self.protocol == GRAPHQL_TRANSPORT_WS_PROTOCOL:
135
124
  await self.send_json_to(
136
- SubscribeMessage(
137
- id=id_,
138
- payload=SubscribeMessagePayload(query=query, variables=variables),
139
- ).as_dict()
125
+ transport_ws_types.SubscribeMessage(
126
+ {
127
+ "id": id_,
128
+ "type": "subscribe",
129
+ "payload": {"query": query, "variables": variables},
130
+ }
131
+ )
140
132
  )
141
133
  else:
142
- start_message: GraphQLWSStartMessage = {
134
+ start_message: ws_types.StartMessage = {
143
135
  "type": "start",
144
136
  "id": id_,
145
137
  "payload": {
@@ -153,17 +145,18 @@ class GraphQLWebsocketCommunicator(WebsocketCommunicator):
153
145
  await self.send_json_to(start_message)
154
146
 
155
147
  while True:
156
- response = await self.receive_json_from(timeout=5)
157
- message_type = response["type"]
158
- if message_type == NextMessage.type:
159
- payload = NextMessage(**response).payload
148
+ message: transport_ws_types.Message = await self.receive_json_from(
149
+ timeout=5
150
+ )
151
+ if message["type"] == "next":
152
+ payload = message["payload"]
160
153
  ret = ExecutionResult(payload.get("data"), None)
161
154
  if "errors" in payload:
162
155
  ret.errors = self.process_errors(payload.get("errors") or [])
163
156
  ret.extensions = payload.get("extensions", None)
164
157
  yield ret
165
- elif message_type == ErrorMessage.type:
166
- error_payload = ErrorMessage(**response).payload
158
+ elif message["type"] == "error":
159
+ error_payload = message["payload"]
167
160
  yield ExecutionResult(
168
161
  data=None, errors=self.process_errors(error_payload)
169
162
  )
@@ -8,7 +8,6 @@ if TYPE_CHECKING:
8
8
  OpenTelemetryExtension,
9
9
  OpenTelemetryExtensionSync,
10
10
  )
11
- from .sentry import SentryTracingExtension, SentryTracingExtensionSync
12
11
 
13
12
  __all__ = [
14
13
  "ApolloTracingExtension",
@@ -17,8 +16,6 @@ __all__ = [
17
16
  "DatadogTracingExtensionSync",
18
17
  "OpenTelemetryExtension",
19
18
  "OpenTelemetryExtensionSync",
20
- "SentryTracingExtension",
21
- "SentryTracingExtensionSync",
22
19
  ]
23
20
 
24
21
 
@@ -32,7 +29,4 @@ def __getattr__(name: str) -> Any:
32
29
  if name in {"OpenTelemetryExtension", "OpenTelemetryExtensionSync"}:
33
30
  return getattr(importlib.import_module(".opentelemetry", __name__), name)
34
31
 
35
- if name in {"SentryTracingExtension", "SentryTracingExtensionSync"}:
36
- return getattr(importlib.import_module(".sentry", __name__), name)
37
-
38
32
  raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
strawberry/permission.py CHANGED
@@ -177,8 +177,7 @@ class PermissionExtension(FieldExtension):
177
177
  elif isinstance(field.type, StrawberryList):
178
178
  self.return_empty_list = True
179
179
  else:
180
- errror = PermissionFailSilentlyRequiresOptionalError(field)
181
- raise errror
180
+ raise PermissionFailSilentlyRequiresOptionalError(field)
182
181
 
183
182
  def _on_unauthorized(self, permission: BasePermission) -> Any:
184
183
  if self.fail_silently:
@@ -5,11 +5,11 @@ import logging
5
5
  from contextlib import suppress
6
6
  from typing import (
7
7
  TYPE_CHECKING,
8
- Any,
9
8
  Awaitable,
10
9
  Dict,
11
10
  List,
12
11
  Optional,
12
+ cast,
13
13
  )
14
14
 
15
15
  from graphql import GraphQLError, GraphQLSyntaxError, parse
@@ -21,20 +21,16 @@ from strawberry.http.exceptions import (
21
21
  )
22
22
  from strawberry.subscriptions.protocols.graphql_transport_ws.types import (
23
23
  CompleteMessage,
24
- ConnectionAckMessage,
25
24
  ConnectionInitMessage,
26
- ErrorMessage,
27
- NextMessage,
28
- NextPayload,
25
+ Message,
26
+ NextMessagePayload,
29
27
  PingMessage,
30
28
  PongMessage,
31
29
  SubscribeMessage,
32
- SubscribeMessagePayload,
33
30
  )
34
31
  from strawberry.types import ExecutionResult
35
32
  from strawberry.types.execution import PreExecutionError
36
33
  from strawberry.types.graphql import OperationType
37
- from strawberry.types.unset import UNSET
38
34
  from strawberry.utils.debug import pretty_print_graphql_operation
39
35
  from strawberry.utils.operation import get_operation_type
40
36
 
@@ -44,9 +40,6 @@ if TYPE_CHECKING:
44
40
  from strawberry.http.async_base_view import AsyncWebSocketAdapter
45
41
  from strawberry.schema import BaseSchema
46
42
  from strawberry.schema.subscribe import SubscriptionResult
47
- from strawberry.subscriptions.protocols.graphql_transport_ws.types import (
48
- GraphQLTransportMessage,
49
- )
50
43
 
51
44
 
52
45
  class BaseGraphQLTransportWSHandler:
@@ -73,15 +66,15 @@ class BaseGraphQLTransportWSHandler:
73
66
  self.connection_timed_out = False
74
67
  self.operations: Dict[str, Operation] = {}
75
68
  self.completed_tasks: List[asyncio.Task] = []
76
- self.connection_params: Optional[Dict[str, Any]] = None
69
+ self.connection_params: Optional[Dict[str, object]] = None
77
70
 
78
- async def handle(self) -> Any:
71
+ async def handle(self) -> None:
79
72
  self.on_request_accepted()
80
73
 
81
74
  try:
82
75
  try:
83
76
  async for message in self.websocket.iter_json():
84
- await self.handle_message(message)
77
+ await self.handle_message(cast(Message, message))
85
78
  except NonTextMessageReceived:
86
79
  await self.handle_invalid_message("WebSocket message type must be text")
87
80
  except NonJsonMessageReceived:
@@ -134,39 +127,28 @@ class BaseGraphQLTransportWSHandler:
134
127
  async def handle_task_exception(self, error: Exception) -> None: # pragma: no cover
135
128
  self.task_logger.exception("Exception in worker task", exc_info=error)
136
129
 
137
- async def handle_message(self, message: dict) -> None:
130
+ async def handle_message(self, message: Message) -> None:
138
131
  try:
139
- message_type = message.pop("type")
132
+ if message["type"] == "connection_init":
133
+ await self.handle_connection_init(message)
140
134
 
141
- if message_type == ConnectionInitMessage.type:
142
- await self.handle_connection_init(ConnectionInitMessage(**message))
135
+ elif message["type"] == "ping":
136
+ await self.handle_ping(message)
143
137
 
144
- elif message_type == PingMessage.type:
145
- await self.handle_ping(PingMessage(**message))
138
+ elif message["type"] == "pong":
139
+ await self.handle_pong(message)
146
140
 
147
- elif message_type == PongMessage.type:
148
- await self.handle_pong(PongMessage(**message))
141
+ elif message["type"] == "subscribe":
142
+ await self.handle_subscribe(message)
149
143
 
150
- elif message_type == SubscribeMessage.type:
151
- payload_args = message.pop("payload")
152
- payload = SubscribeMessagePayload(
153
- query=payload_args["query"],
154
- operationName=payload_args.get("operationName"),
155
- variables=payload_args.get("variables"),
156
- extensions=payload_args.get("extensions"),
157
- )
158
- await self.handle_subscribe(
159
- SubscribeMessage(payload=payload, **message)
160
- )
161
-
162
- elif message_type == CompleteMessage.type:
163
- await self.handle_complete(CompleteMessage(**message))
144
+ elif message["type"] == "complete":
145
+ await self.handle_complete(message)
164
146
 
165
147
  else:
166
- error_message = f"Unknown message type: {message_type}"
148
+ error_message = f"Unknown message type: {message['type']}"
167
149
  await self.handle_invalid_message(error_message)
168
150
 
169
- except (KeyError, TypeError):
151
+ except KeyError:
170
152
  await self.handle_invalid_message("Failed to parse message")
171
153
  finally:
172
154
  await self.reap_completed_tasks()
@@ -175,14 +157,11 @@ class BaseGraphQLTransportWSHandler:
175
157
  if self.connection_timed_out:
176
158
  # No way to reliably excercise this case during testing
177
159
  return # pragma: no cover
160
+
178
161
  if self.connection_init_timeout_task:
179
162
  self.connection_init_timeout_task.cancel()
180
163
 
181
- payload = (
182
- message.payload
183
- if message.payload is not None and message.payload is not UNSET
184
- else {}
185
- )
164
+ payload = message.get("payload", {})
186
165
 
187
166
  if not isinstance(payload, dict):
188
167
  await self.websocket.close(
@@ -198,11 +177,11 @@ class BaseGraphQLTransportWSHandler:
198
177
  return
199
178
 
200
179
  self.connection_init_received = True
201
- await self.send_message(ConnectionAckMessage())
180
+ await self.send_message({"type": "connection_ack"})
202
181
  self.connection_acknowledged = True
203
182
 
204
183
  async def handle_ping(self, message: PingMessage) -> None:
205
- await self.send_message(PongMessage())
184
+ await self.send_message({"type": "pong"})
206
185
 
207
186
  async def handle_pong(self, message: PongMessage) -> None:
208
187
  pass
@@ -213,14 +192,14 @@ class BaseGraphQLTransportWSHandler:
213
192
  return
214
193
 
215
194
  try:
216
- graphql_document = parse(message.payload.query)
195
+ graphql_document = parse(message["payload"]["query"])
217
196
  except GraphQLSyntaxError as exc:
218
197
  await self.websocket.close(code=4400, reason=exc.message)
219
198
  return
220
199
 
221
200
  try:
222
201
  operation_type = get_operation_type(
223
- graphql_document, message.payload.operationName
202
+ graphql_document, message["payload"].get("operationName")
224
203
  )
225
204
  except RuntimeError:
226
205
  await self.websocket.close(
@@ -228,16 +207,16 @@ class BaseGraphQLTransportWSHandler:
228
207
  )
229
208
  return
230
209
 
231
- if message.id in self.operations:
232
- reason = f"Subscriber for {message.id} already exists"
210
+ if message["id"] in self.operations:
211
+ reason = f"Subscriber for {message['id']} already exists"
233
212
  await self.websocket.close(code=4409, reason=reason)
234
213
  return
235
214
 
236
215
  if self.debug: # pragma: no cover
237
216
  pretty_print_graphql_operation(
238
- message.payload.operationName,
239
- message.payload.query,
240
- message.payload.variables,
217
+ message["payload"].get("operationName"),
218
+ message["payload"]["query"],
219
+ message["payload"].get("variables"),
241
220
  )
242
221
 
243
222
  if isinstance(self.context, dict):
@@ -247,15 +226,15 @@ class BaseGraphQLTransportWSHandler:
247
226
 
248
227
  operation = Operation(
249
228
  self,
250
- message.id,
229
+ message["id"],
251
230
  operation_type,
252
- message.payload.query,
253
- message.payload.variables,
254
- message.payload.operationName,
231
+ message["payload"]["query"],
232
+ message["payload"].get("variables"),
233
+ message["payload"].get("operationName"),
255
234
  )
256
235
 
257
236
  operation.task = asyncio.create_task(self.run_operation(operation))
258
- self.operations[message.id] = operation
237
+ self.operations[message["id"]] = operation
259
238
 
260
239
  async def run_operation(self, operation: Operation) -> None:
261
240
  """The operation task's top level method. Cleans-up and de-registers the operation once it is done."""
@@ -291,11 +270,15 @@ class BaseGraphQLTransportWSHandler:
291
270
  # that's a mutation / query result
292
271
  elif isinstance(first_res_or_agen, ExecutionResult):
293
272
  await operation.send_next(first_res_or_agen)
294
- await operation.send_message(CompleteMessage(id=operation.id))
273
+ await operation.send_operation_message(
274
+ {"id": operation.id, "type": "complete"}
275
+ )
295
276
  else:
296
277
  async for result in first_res_or_agen:
297
278
  await operation.send_next(result)
298
- await operation.send_message(CompleteMessage(id=operation.id))
279
+ await operation.send_operation_message(
280
+ {"id": operation.id, "type": "complete"}
281
+ )
299
282
 
300
283
  except BaseException as e: # pragma: no cover
301
284
  self.operations.pop(operation.id, None)
@@ -312,14 +295,13 @@ class BaseGraphQLTransportWSHandler:
312
295
  del self.operations[id]
313
296
 
314
297
  async def handle_complete(self, message: CompleteMessage) -> None:
315
- await self.cleanup_operation(operation_id=message.id)
298
+ await self.cleanup_operation(operation_id=message["id"])
316
299
 
317
300
  async def handle_invalid_message(self, error_message: str) -> None:
318
301
  await self.websocket.close(code=4400, reason=error_message)
319
302
 
320
- async def send_message(self, message: GraphQLTransportMessage) -> None:
321
- data = message.as_dict()
322
- await self.websocket.send_json(data)
303
+ async def send_message(self, message: Message) -> None:
304
+ await self.websocket.send_json(message)
323
305
 
324
306
  async def cleanup_operation(self, operation_id: str) -> None:
325
307
  if operation_id not in self.operations:
@@ -358,7 +340,7 @@ class Operation:
358
340
  id: str,
359
341
  operation_type: OperationType,
360
342
  query: str,
361
- variables: Optional[Dict[str, Any]],
343
+ variables: Optional[Dict[str, object]],
362
344
  operation_name: Optional[str],
363
345
  ) -> None:
364
346
  self.handler = handler
@@ -370,10 +352,10 @@ class Operation:
370
352
  self.completed = False
371
353
  self.task: Optional[asyncio.Task] = None
372
354
 
373
- async def send_message(self, message: GraphQLTransportMessage) -> None:
355
+ async def send_operation_message(self, message: Message) -> None:
374
356
  if self.completed:
375
357
  return
376
- if isinstance(message, (CompleteMessage, ErrorMessage)):
358
+ if message["type"] == "complete" or message["type"] == "error":
377
359
  self.completed = True
378
360
  # de-register the operation _before_ sending the final message
379
361
  self.handler.forget_id(self.id)
@@ -383,17 +365,26 @@ class Operation:
383
365
  # Initial errors see https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md#error
384
366
  # "This can occur before execution starts,
385
367
  # usually due to validation errors, or during the execution of the request"
386
- await self.send_message(
387
- ErrorMessage(id=self.id, payload=[err.formatted for err in errors])
368
+ await self.send_operation_message(
369
+ {
370
+ "id": self.id,
371
+ "type": "error",
372
+ "payload": [err.formatted for err in errors],
373
+ }
388
374
  )
389
375
 
390
376
  async def send_next(self, execution_result: ExecutionResult) -> None:
391
- next_payload: NextPayload = {"data": execution_result.data}
377
+ next_payload: NextMessagePayload = {"data": execution_result.data}
378
+
392
379
  if execution_result.errors:
393
380
  next_payload["errors"] = [err.formatted for err in execution_result.errors]
381
+
394
382
  if execution_result.extensions:
395
383
  next_payload["extensions"] = execution_result.extensions
396
- await self.send_message(NextMessage(id=self.id, payload=next_payload))
384
+
385
+ await self.send_operation_message(
386
+ {"id": self.id, "type": "next", "payload": next_payload}
387
+ )
397
388
 
398
389
 
399
390
  __all__ = ["BaseGraphQLTransportWSHandler", "Operation"]
@@ -1,108 +1,91 @@
1
- from __future__ import annotations
1
+ from typing import Dict, List, TypedDict, Union
2
+ from typing_extensions import Literal, NotRequired
2
3
 
3
- from dataclasses import asdict, dataclass
4
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, TypedDict
4
+ from graphql import GraphQLFormattedError
5
5
 
6
- from strawberry.types.unset import UNSET
7
6
 
8
- if TYPE_CHECKING:
9
- from graphql import GraphQLFormattedError
10
-
11
-
12
- @dataclass
13
- class GraphQLTransportMessage:
14
- def as_dict(self) -> dict:
15
- data = asdict(self)
16
- if getattr(self, "payload", None) is UNSET:
17
- # Unset fields must have a JSON value of "undefined" not "null"
18
- data.pop("payload")
19
- return data
20
-
21
-
22
- @dataclass
23
- class ConnectionInitMessage(GraphQLTransportMessage):
7
+ class ConnectionInitMessage(TypedDict):
24
8
  """Direction: Client -> Server."""
25
9
 
26
- payload: Optional[Dict[str, Any]] = UNSET
27
- type: str = "connection_init"
10
+ type: Literal["connection_init"]
11
+ payload: NotRequired[Union[Dict[str, object], None]]
28
12
 
29
13
 
30
- @dataclass
31
- class ConnectionAckMessage(GraphQLTransportMessage):
14
+ class ConnectionAckMessage(TypedDict):
32
15
  """Direction: Server -> Client."""
33
16
 
34
- payload: Optional[Dict[str, Any]] = UNSET
35
- type: str = "connection_ack"
17
+ type: Literal["connection_ack"]
18
+ payload: NotRequired[Union[Dict[str, object], None]]
36
19
 
37
20
 
38
- @dataclass
39
- class PingMessage(GraphQLTransportMessage):
21
+ class PingMessage(TypedDict):
40
22
  """Direction: bidirectional."""
41
23
 
42
- payload: Optional[Dict[str, Any]] = UNSET
43
- type: str = "ping"
24
+ type: Literal["ping"]
25
+ payload: NotRequired[Union[Dict[str, object], None]]
44
26
 
45
27
 
46
- @dataclass
47
- class PongMessage(GraphQLTransportMessage):
28
+ class PongMessage(TypedDict):
48
29
  """Direction: bidirectional."""
49
30
 
50
- payload: Optional[Dict[str, Any]] = UNSET
51
- type: str = "pong"
31
+ type: Literal["pong"]
32
+ payload: NotRequired[Union[Dict[str, object], None]]
52
33
 
53
34
 
54
- @dataclass
55
- class SubscribeMessagePayload:
35
+ class SubscribeMessagePayload(TypedDict):
36
+ operationName: NotRequired[Union[str, None]]
56
37
  query: str
57
- operationName: Optional[str] = None
58
- variables: Optional[Dict[str, Any]] = None
59
- extensions: Optional[Dict[str, Any]] = None
38
+ variables: NotRequired[Union[Dict[str, object], None]]
39
+ extensions: NotRequired[Union[Dict[str, object], None]]
60
40
 
61
41
 
62
- @dataclass
63
- class SubscribeMessage(GraphQLTransportMessage):
42
+ class SubscribeMessage(TypedDict):
64
43
  """Direction: Client -> Server."""
65
44
 
66
45
  id: str
46
+ type: Literal["subscribe"]
67
47
  payload: SubscribeMessagePayload
68
- type: str = "subscribe"
69
48
 
70
49
 
71
- class NextPayload(TypedDict, total=False):
72
- data: Any
50
+ class NextMessagePayload(TypedDict):
51
+ errors: NotRequired[List[GraphQLFormattedError]]
52
+ data: NotRequired[Union[Dict[str, object], None]]
53
+ extensions: NotRequired[Dict[str, object]]
73
54
 
74
- # Optional list of formatted graphql.GraphQLError objects
75
- errors: Optional[List[GraphQLFormattedError]]
76
- extensions: Optional[Dict[str, Any]]
77
55
 
78
-
79
- @dataclass
80
- class NextMessage(GraphQLTransportMessage):
56
+ class NextMessage(TypedDict):
81
57
  """Direction: Server -> Client."""
82
58
 
83
59
  id: str
84
- payload: NextPayload
85
- type: str = "next"
86
-
87
- def as_dict(self) -> dict:
88
- return {"id": self.id, "payload": self.payload, "type": self.type}
60
+ type: Literal["next"]
61
+ payload: NextMessagePayload
89
62
 
90
63
 
91
- @dataclass
92
- class ErrorMessage(GraphQLTransportMessage):
64
+ class ErrorMessage(TypedDict):
93
65
  """Direction: Server -> Client."""
94
66
 
95
67
  id: str
68
+ type: Literal["error"]
96
69
  payload: List[GraphQLFormattedError]
97
- type: str = "error"
98
70
 
99
71
 
100
- @dataclass
101
- class CompleteMessage(GraphQLTransportMessage):
72
+ class CompleteMessage(TypedDict):
102
73
  """Direction: bidirectional."""
103
74
 
104
75
  id: str
105
- type: str = "complete"
76
+ type: Literal["complete"]
77
+
78
+
79
+ Message = Union[
80
+ ConnectionInitMessage,
81
+ ConnectionAckMessage,
82
+ PingMessage,
83
+ PongMessage,
84
+ SubscribeMessage,
85
+ NextMessage,
86
+ ErrorMessage,
87
+ CompleteMessage,
88
+ ]
106
89
 
107
90
 
108
91
  __all__ = [
@@ -114,4 +97,5 @@ __all__ = [
114
97
  "NextMessage",
115
98
  "ErrorMessage",
116
99
  "CompleteMessage",
100
+ "Message",
117
101
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: strawberry-graphql
3
- Version: 0.248.1
3
+ Version: 0.250.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=2cZvF9S4ofYLLRh2G8iyWZTphfQi7XrUcFpRqGjUmPQ,6688
19
+ strawberry/channels/testing.py,sha256=0q7XQi3uOa-WbqXTkZKWwsLH2B8IHfP3JAXF-b-1qM4,6490
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
@@ -101,11 +101,10 @@ strawberry/extensions/parser_cache.py,sha256=IvDbkVpKC_2lDXLZyrvj0VleZghbtkD6l2S
101
101
  strawberry/extensions/pyinstrument.py,sha256=dy2NPagLDW4mU2jTfaHek3H1SBVZCjwYKf0zPMxTYp8,712
102
102
  strawberry/extensions/query_depth_limiter.py,sha256=Jtg-HSmEux97Z09Y5G5nhTbJu56rBiGmMG6lB1_ejz8,9882
103
103
  strawberry/extensions/runner.py,sha256=cVsBzNMBDjD4Pg_0jHNzYHv2cJUIVrNL_SzPr6rd7gk,1885
104
- strawberry/extensions/tracing/__init__.py,sha256=wx8_EAroGhNrP4HiGYMgKo8jnCsfde5ib6lO4OvcLV0,1400
104
+ strawberry/extensions/tracing/__init__.py,sha256=igoDJBlfh7vGhytJ5njx1qQzpxZOUmdfIaH4j5Kmt3E,1112
105
105
  strawberry/extensions/tracing/apollo.py,sha256=XlI88NzSZBSmBHEJ9iitDU9br2-9CdjdtHE_eiTJCIw,5880
106
106
  strawberry/extensions/tracing/datadog.py,sha256=khxvY4_WTjYaeJUb_dn6mLvmk9TCS4tIodnC3G-9pTo,5541
107
107
  strawberry/extensions/tracing/opentelemetry.py,sha256=MH2j71denfLmzInl6zcTzxzckqjcwOzm_F305z8-vYw,7281
108
- strawberry/extensions/tracing/sentry.py,sha256=r_U1OeiDDq-Mf6v30-aEuHhrio2Pckxfrcrdu_OAnWM,5027
109
108
  strawberry/extensions/tracing/utils.py,sha256=tXZNyqfct6YNSWi3GRj4GU1fKQGvSce8ZESfoVeys7U,654
110
109
  strawberry/extensions/utils.py,sha256=YPiacKNLQXvpYj-HI6fR5ORFdM9RrcdabGeM7F8vBGg,1001
111
110
  strawberry/extensions/validation_cache.py,sha256=CZ-brPYA1grL_lW38Rq4TxEVKlHM2n1DC6q9BHlSF4g,1398
@@ -144,7 +143,7 @@ strawberry/http/typevars.py,sha256=8hK5PfNPZXb2EhZmqlobYyfwJJcO2Wb96T91MlLEVJs,4
144
143
  strawberry/litestar/__init__.py,sha256=zsXzg-mglCGUVO9iNXLm-yadoDSCK7k-zuyRqyvAh1w,237
145
144
  strawberry/litestar/controller.py,sha256=yA8f59NuC6ZJgpG2p4HoILko3FdWiNx5AWdkQGti_6U,13992
146
145
  strawberry/parent.py,sha256=sXURm0lauSpjUADsmfNGY-Zl7kHs0A67BFcWuWKzRxw,771
147
- strawberry/permission.py,sha256=HusiB46yZANdpZM3AdzFVZB6JkCu7dcvoZ3QP2E01jM,7575
146
+ strawberry/permission.py,sha256=rCJLK21cRNDQ6N9eSqcEiIUZiuCvF3FVOK3onXGai9E,7543
148
147
  strawberry/printer/__init__.py,sha256=DmepjmgtkdF5RxK_7yC6qUyRWn56U-9qeZMbkztYB9w,62
149
148
  strawberry/printer/ast_from_value.py,sha256=LgM5g2qvBOnAIf9znbiMEcRX0PGSQohR3Vr3QYfU604,4983
150
149
  strawberry/printer/printer.py,sha256=GntTBivg3fb_zPM41Q8DtWMiRmkmM9xwTF-aFWvnqTg,17524
@@ -187,8 +186,8 @@ strawberry/static/pathfinder.html,sha256=0DPx9AmJ2C_sJstFXnWOz9k5tVQHeHaK7qdVY4l
187
186
  strawberry/subscriptions/__init__.py,sha256=1VGmiCzFepqRFyCikagkUoHHdoTG3XYlFu9GafoQMws,170
188
187
  strawberry/subscriptions/protocols/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
189
188
  strawberry/subscriptions/protocols/graphql_transport_ws/__init__.py,sha256=wN6dkMu6WiaIZTE19PGoN9xXpIN_RdDE_q7F7ZgjCxk,138
190
- strawberry/subscriptions/protocols/graphql_transport_ws/handlers.py,sha256=_h-xNf_ZRtjn8PGbxZk3u9qTR-NNNCevdgaFF0uXciw,14728
191
- strawberry/subscriptions/protocols/graphql_transport_ws/types.py,sha256=udYxzGtwjETYvY5f23org0t-aY4cimTjEGFYUR3idaY,2596
189
+ strawberry/subscriptions/protocols/graphql_transport_ws/handlers.py,sha256=R9QCGHJ_lf0gmNf_wzt4Ow-HFA5ulFsGUiNo_K3CHkI,14104
190
+ strawberry/subscriptions/protocols/graphql_transport_ws/types.py,sha256=AtKPEyFuNIKgnIIBD-MC4kAYFQ6uhXeq3xyRF6Dh-hg,2181
192
191
  strawberry/subscriptions/protocols/graphql_ws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
193
192
  strawberry/subscriptions/protocols/graphql_ws/handlers.py,sha256=oM0o85zC24LpBjtVq8VakKx2hBGL0kb3Q9yl0GyFLS8,7614
194
193
  strawberry/subscriptions/protocols/graphql_ws/types.py,sha256=diZ36w56Nb_YmgfWXe6uXGiQOKmWIVjNEkcM-PkjaSs,1939
@@ -230,8 +229,8 @@ strawberry/utils/logging.py,sha256=U1cseHGquN09YFhFmRkiphfASKCyK0HUZREImPgVb0c,7
230
229
  strawberry/utils/operation.py,sha256=SSXxN-vMqdHO6W2OZtip-1z7y4_A-eTVFdhDvhKeLCk,1193
231
230
  strawberry/utils/str_converters.py,sha256=KGd7QH90RevaJjH6SQEkiVVsb8KuhJr_wv5AsI7UzQk,897
232
231
  strawberry/utils/typing.py,sha256=G6k2wWD1TDQ9WFk-Togekj_hTVFqHV-g7Phf2Wu41ms,14380
233
- strawberry_graphql-0.248.1.dist-info/LICENSE,sha256=m-XnIVUKqlG_AWnfi9NReh9JfKhYOB-gJfKE45WM1W8,1072
234
- strawberry_graphql-0.248.1.dist-info/METADATA,sha256=4QSuJdbk6cKZOzgkTHqSSja1u2TpyxIH1XdfBysw-5E,7758
235
- strawberry_graphql-0.248.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
236
- strawberry_graphql-0.248.1.dist-info/entry_points.txt,sha256=Nk7-aT3_uEwCgyqtHESV9H6Mc31cK-VAvhnQNTzTb4k,49
237
- strawberry_graphql-0.248.1.dist-info/RECORD,,
232
+ strawberry_graphql-0.250.0.dist-info/LICENSE,sha256=m-XnIVUKqlG_AWnfi9NReh9JfKhYOB-gJfKE45WM1W8,1072
233
+ strawberry_graphql-0.250.0.dist-info/METADATA,sha256=4VaLyKu8LPZoMQwYXdWBxpVCoVtEM5JVKH6bST7gY3U,7758
234
+ strawberry_graphql-0.250.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
235
+ strawberry_graphql-0.250.0.dist-info/entry_points.txt,sha256=Nk7-aT3_uEwCgyqtHESV9H6Mc31cK-VAvhnQNTzTb4k,49
236
+ strawberry_graphql-0.250.0.dist-info/RECORD,,
@@ -1,161 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import hashlib
4
- import warnings
5
- from functools import cached_property
6
- from inspect import isawaitable
7
- from typing import TYPE_CHECKING, Any, Callable, Generator, Optional
8
-
9
- from sentry_sdk import configure_scope, start_span
10
-
11
- from strawberry.extensions import SchemaExtension
12
- from strawberry.extensions.tracing.utils import should_skip_tracing
13
-
14
- if TYPE_CHECKING:
15
- from graphql import GraphQLResolveInfo
16
-
17
- from strawberry.types.execution import ExecutionContext
18
-
19
-
20
- class SentryTracingExtension(SchemaExtension):
21
- def __init__(
22
- self,
23
- *,
24
- execution_context: Optional[ExecutionContext] = None,
25
- ) -> None:
26
- warnings.warn(
27
- "The Sentry tracing extension is deprecated, please update to sentry-sdk>=1.32.0",
28
- DeprecationWarning,
29
- stacklevel=2,
30
- )
31
-
32
- if execution_context:
33
- self.execution_context = execution_context
34
-
35
- @cached_property
36
- def _resource_name(self) -> str:
37
- assert self.execution_context.query
38
-
39
- query_hash = self.hash_query(self.execution_context.query)
40
-
41
- if self.execution_context.operation_name:
42
- return f"{self.execution_context.operation_name}:{query_hash}"
43
-
44
- return query_hash
45
-
46
- def hash_query(self, query: str) -> str:
47
- return hashlib.md5(query.encode("utf-8")).hexdigest()
48
-
49
- def on_operation(self) -> Generator[None, None, None]:
50
- self._operation_name = self.execution_context.operation_name
51
- name = f"{self._operation_name}" if self._operation_name else "Anonymous Query"
52
-
53
- with configure_scope() as scope:
54
- if scope.span:
55
- self.gql_span = scope.span.start_child(
56
- op="gql",
57
- description=name,
58
- )
59
- else:
60
- self.gql_span = start_span(
61
- op="gql",
62
- )
63
-
64
- operation_type = "query"
65
-
66
- assert self.execution_context.query
67
-
68
- if self.execution_context.query.strip().startswith("mutation"):
69
- operation_type = "mutation"
70
- if self.execution_context.query.strip().startswith("subscription"):
71
- operation_type = "subscription"
72
-
73
- self.gql_span.set_tag("graphql.operation_type", operation_type)
74
- self.gql_span.set_tag("graphql.resource_name", self._resource_name)
75
- self.gql_span.set_data("graphql.query", self.execution_context.query)
76
-
77
- yield
78
-
79
- self.gql_span.finish()
80
-
81
- def on_validate(self) -> Generator[None, None, None]:
82
- self.validation_span = self.gql_span.start_child(
83
- op="validation", description="Validation"
84
- )
85
-
86
- yield
87
-
88
- self.validation_span.finish()
89
-
90
- def on_parse(self) -> Generator[None, None, None]:
91
- self.parsing_span = self.gql_span.start_child(
92
- op="parsing", description="Parsing"
93
- )
94
-
95
- yield
96
-
97
- self.parsing_span.finish()
98
-
99
- def should_skip_tracing(self, _next: Callable, info: GraphQLResolveInfo) -> bool:
100
- return should_skip_tracing(_next, info)
101
-
102
- async def resolve(
103
- self,
104
- _next: Callable,
105
- root: Any,
106
- info: GraphQLResolveInfo,
107
- *args: str,
108
- **kwargs: Any,
109
- ) -> Any:
110
- if self.should_skip_tracing(_next, info):
111
- result = _next(root, info, *args, **kwargs)
112
-
113
- if isawaitable(result): # pragma: no cover
114
- result = await result
115
-
116
- return result
117
-
118
- field_path = f"{info.parent_type}.{info.field_name}"
119
-
120
- with self.gql_span.start_child(
121
- op="resolve", description=f"Resolving: {field_path}"
122
- ) as span:
123
- span.set_tag("graphql.field_name", info.field_name)
124
- span.set_tag("graphql.parent_type", info.parent_type.name)
125
- span.set_tag("graphql.field_path", field_path)
126
- span.set_tag("graphql.path", ".".join(map(str, info.path.as_list())))
127
-
128
- result = _next(root, info, *args, **kwargs)
129
-
130
- if isawaitable(result):
131
- result = await result
132
-
133
- return result
134
-
135
-
136
- class SentryTracingExtensionSync(SentryTracingExtension):
137
- def resolve(
138
- self,
139
- _next: Callable,
140
- root: Any,
141
- info: GraphQLResolveInfo,
142
- *args: str,
143
- **kwargs: Any,
144
- ) -> Any:
145
- if self.should_skip_tracing(_next, info):
146
- return _next(root, info, *args, **kwargs)
147
-
148
- field_path = f"{info.parent_type}.{info.field_name}"
149
-
150
- with self.gql_span.start_child(
151
- op="resolve", description=f"Resolving: {field_path}"
152
- ) as span:
153
- span.set_tag("graphql.field_name", info.field_name)
154
- span.set_tag("graphql.parent_type", info.parent_type.name)
155
- span.set_tag("graphql.field_path", field_path)
156
- span.set_tag("graphql.path", ".".join(map(str, info.path.as_list())))
157
-
158
- return _next(root, info, *args, **kwargs)
159
-
160
-
161
- __all__ = ["SentryTracingExtension", "SentryTracingExtensionSync"]