strawberry-graphql 0.275.7__py3-none-any.whl → 0.284.3__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.
Potentially problematic release.
This version of strawberry-graphql might be problematic. Click here for more details.
- strawberry/__init__.py +2 -0
- strawberry/aiohttp/test/client.py +8 -15
- strawberry/aiohttp/views.py +15 -64
- strawberry/annotation.py +70 -25
- strawberry/asgi/__init__.py +22 -56
- strawberry/asgi/test/client.py +6 -6
- strawberry/chalice/views.py +13 -79
- strawberry/channels/handlers/base.py +7 -8
- strawberry/channels/handlers/http_handler.py +50 -32
- strawberry/channels/handlers/ws_handler.py +12 -14
- strawberry/channels/router.py +3 -4
- strawberry/channels/testing.py +7 -9
- strawberry/cli/__init__.py +7 -6
- strawberry/cli/commands/codegen.py +7 -7
- strawberry/cli/commands/dev.py +72 -0
- strawberry/cli/commands/schema_codegen.py +1 -2
- strawberry/cli/commands/server.py +3 -44
- strawberry/cli/commands/upgrade/__init__.py +3 -3
- strawberry/cli/commands/upgrade/_run_codemod.py +2 -2
- strawberry/cli/constants.py +1 -2
- strawberry/cli/{debug_server.py → dev_server.py} +3 -7
- strawberry/codegen/plugins/print_operation.py +2 -2
- strawberry/codegen/plugins/python.py +2 -2
- strawberry/codegen/query_codegen.py +20 -30
- strawberry/codegen/types.py +32 -32
- strawberry/codemods/__init__.py +9 -0
- strawberry/codemods/annotated_unions.py +2 -2
- strawberry/codemods/maybe_optional.py +118 -0
- strawberry/dataloader.py +28 -24
- strawberry/directive.py +6 -7
- strawberry/django/test/client.py +3 -3
- strawberry/django/views.py +21 -91
- strawberry/exceptions/__init__.py +4 -4
- strawberry/exceptions/conflicting_arguments.py +2 -2
- strawberry/exceptions/duplicated_type_name.py +4 -4
- strawberry/exceptions/exception.py +3 -3
- strawberry/exceptions/handler.py +8 -7
- strawberry/exceptions/invalid_argument_type.py +2 -2
- strawberry/exceptions/invalid_superclass_interface.py +2 -2
- strawberry/exceptions/invalid_union_type.py +4 -4
- strawberry/exceptions/missing_arguments_annotations.py +2 -2
- strawberry/exceptions/missing_dependencies.py +2 -4
- strawberry/exceptions/missing_field_annotation.py +2 -2
- strawberry/exceptions/missing_return_annotation.py +2 -2
- strawberry/exceptions/object_is_not_a_class.py +2 -2
- strawberry/exceptions/object_is_not_an_enum.py +2 -2
- strawberry/exceptions/permission_fail_silently_requires_optional.py +2 -2
- strawberry/exceptions/private_strawberry_field.py +2 -2
- strawberry/exceptions/scalar_already_registered.py +2 -2
- strawberry/exceptions/syntax.py +3 -3
- strawberry/exceptions/unresolved_field_type.py +2 -2
- strawberry/exceptions/utils/source_finder.py +25 -25
- strawberry/experimental/pydantic/_compat.py +8 -7
- strawberry/experimental/pydantic/conversion.py +2 -2
- strawberry/experimental/pydantic/conversion_types.py +2 -2
- strawberry/experimental/pydantic/error_type.py +10 -12
- strawberry/experimental/pydantic/fields.py +9 -15
- strawberry/experimental/pydantic/object_type.py +17 -25
- strawberry/experimental/pydantic/utils.py +1 -2
- strawberry/ext/mypy_plugin.py +12 -14
- strawberry/extensions/base_extension.py +2 -1
- strawberry/extensions/context.py +13 -18
- strawberry/extensions/directives.py +9 -3
- strawberry/extensions/field_extension.py +4 -4
- strawberry/extensions/mask_errors.py +24 -13
- strawberry/extensions/max_aliases.py +1 -3
- strawberry/extensions/parser_cache.py +1 -2
- strawberry/extensions/query_depth_limiter.py +18 -14
- strawberry/extensions/runner.py +2 -2
- strawberry/extensions/tracing/apollo.py +3 -3
- strawberry/extensions/tracing/datadog.py +3 -3
- strawberry/extensions/tracing/opentelemetry.py +6 -8
- strawberry/extensions/tracing/utils.py +3 -1
- strawberry/extensions/utils.py +2 -2
- strawberry/extensions/validation_cache.py +2 -3
- strawberry/fastapi/context.py +6 -6
- strawberry/fastapi/router.py +43 -42
- strawberry/federation/argument.py +4 -5
- strawberry/federation/enum.py +18 -21
- strawberry/federation/field.py +94 -97
- strawberry/federation/object_type.py +56 -58
- strawberry/federation/scalar.py +27 -35
- strawberry/federation/schema.py +15 -16
- strawberry/federation/schema_directive.py +7 -6
- strawberry/federation/schema_directives.py +11 -11
- strawberry/federation/union.py +4 -4
- strawberry/flask/views.py +16 -85
- strawberry/http/__init__.py +30 -20
- strawberry/http/async_base_view.py +208 -89
- strawberry/http/base.py +28 -11
- strawberry/http/exceptions.py +5 -7
- strawberry/http/ides.py +2 -3
- strawberry/http/sync_base_view.py +115 -69
- strawberry/http/types.py +3 -3
- strawberry/litestar/controller.py +43 -77
- strawberry/permission.py +4 -6
- strawberry/printer/ast_from_value.py +3 -5
- strawberry/printer/printer.py +18 -15
- strawberry/quart/views.py +16 -48
- strawberry/relay/exceptions.py +4 -4
- strawberry/relay/fields.py +33 -32
- strawberry/relay/types.py +32 -35
- strawberry/relay/utils.py +11 -23
- strawberry/resolvers.py +2 -1
- strawberry/sanic/context.py +1 -0
- strawberry/sanic/utils.py +3 -3
- strawberry/sanic/views.py +15 -54
- strawberry/scalars.py +2 -2
- strawberry/schema/_graphql_core.py +55 -0
- strawberry/schema/base.py +32 -33
- strawberry/schema/compat.py +9 -9
- strawberry/schema/config.py +10 -1
- strawberry/schema/exceptions.py +1 -3
- strawberry/schema/name_converter.py +9 -8
- strawberry/schema/schema.py +133 -100
- strawberry/schema/schema_converter.py +96 -58
- strawberry/schema/types/base_scalars.py +1 -1
- strawberry/schema/types/concrete_type.py +5 -5
- strawberry/schema/validation_rules/maybe_null.py +136 -0
- strawberry/schema_codegen/__init__.py +3 -3
- strawberry/schema_directive.py +7 -6
- strawberry/static/graphiql.html +5 -5
- strawberry/streamable.py +35 -0
- strawberry/subscriptions/protocols/graphql_transport_ws/handlers.py +5 -16
- strawberry/subscriptions/protocols/graphql_transport_ws/types.py +20 -20
- strawberry/subscriptions/protocols/graphql_ws/handlers.py +5 -12
- strawberry/subscriptions/protocols/graphql_ws/types.py +14 -14
- strawberry/test/client.py +18 -18
- strawberry/tools/create_type.py +2 -3
- strawberry/types/arguments.py +41 -28
- strawberry/types/auto.py +3 -4
- strawberry/types/base.py +25 -27
- strawberry/types/enum.py +22 -25
- strawberry/types/execution.py +21 -16
- strawberry/types/field.py +109 -130
- strawberry/types/fields/resolver.py +19 -21
- strawberry/types/info.py +5 -11
- strawberry/types/lazy_type.py +2 -3
- strawberry/types/maybe.py +12 -3
- strawberry/types/mutation.py +115 -118
- strawberry/types/nodes.py +2 -2
- strawberry/types/object_type.py +43 -63
- strawberry/types/scalar.py +37 -43
- strawberry/types/union.py +12 -14
- strawberry/utils/aio.py +12 -9
- strawberry/utils/await_maybe.py +3 -3
- strawberry/utils/deprecations.py +2 -2
- strawberry/utils/importer.py +1 -2
- strawberry/utils/inspect.py +4 -6
- strawberry/utils/logging.py +2 -2
- strawberry/utils/operation.py +4 -4
- strawberry/utils/typing.py +18 -83
- {strawberry_graphql-0.275.7.dist-info → strawberry_graphql-0.284.3.dist-info}/METADATA +14 -8
- strawberry_graphql-0.284.3.dist-info/RECORD +243 -0
- {strawberry_graphql-0.275.7.dist-info → strawberry_graphql-0.284.3.dist-info}/WHEEL +1 -1
- strawberry/utils/dataclasses.py +0 -37
- strawberry/utils/debug.py +0 -46
- strawberry/utils/graphql_lexer.py +0 -35
- strawberry_graphql-0.275.7.dist-info/RECORD +0 -241
- {strawberry_graphql-0.275.7.dist-info → strawberry_graphql-0.284.3.dist-info}/entry_points.txt +0 -0
- {strawberry_graphql-0.275.7.dist-info → strawberry_graphql-0.284.3.dist-info/licenses}/LICENSE +0 -0
strawberry/chalice/views.py
CHANGED
|
@@ -1,56 +1,21 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import warnings
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
6
|
from chalice.app import Request, Response
|
|
7
|
-
from
|
|
8
|
-
|
|
7
|
+
from lia import ChaliceHTTPRequestAdapter, HTTPException
|
|
8
|
+
|
|
9
|
+
from strawberry.http.sync_base_view import SyncBaseHTTPView
|
|
9
10
|
from strawberry.http.temporal_response import TemporalResponse
|
|
10
11
|
from strawberry.http.typevars import Context, RootValue
|
|
11
12
|
|
|
12
13
|
if TYPE_CHECKING:
|
|
13
|
-
from collections.abc import Mapping
|
|
14
|
-
|
|
15
14
|
from strawberry.http import GraphQLHTTPResponse
|
|
16
15
|
from strawberry.http.ides import GraphQL_IDE
|
|
17
|
-
from strawberry.http.types import HTTPMethod, QueryParams
|
|
18
16
|
from strawberry.schema import BaseSchema
|
|
19
17
|
|
|
20
18
|
|
|
21
|
-
class ChaliceHTTPRequestAdapter(SyncHTTPRequestAdapter):
|
|
22
|
-
def __init__(self, request: Request) -> None:
|
|
23
|
-
self.request = request
|
|
24
|
-
|
|
25
|
-
@property
|
|
26
|
-
def query_params(self) -> QueryParams:
|
|
27
|
-
return self.request.query_params or {}
|
|
28
|
-
|
|
29
|
-
@property
|
|
30
|
-
def body(self) -> Union[str, bytes]:
|
|
31
|
-
return self.request.raw_body
|
|
32
|
-
|
|
33
|
-
@property
|
|
34
|
-
def method(self) -> HTTPMethod:
|
|
35
|
-
return cast("HTTPMethod", self.request.method.upper())
|
|
36
|
-
|
|
37
|
-
@property
|
|
38
|
-
def headers(self) -> Mapping[str, str]:
|
|
39
|
-
return self.request.headers
|
|
40
|
-
|
|
41
|
-
@property
|
|
42
|
-
def post_data(self) -> Mapping[str, Union[str, bytes]]:
|
|
43
|
-
raise NotImplementedError
|
|
44
|
-
|
|
45
|
-
@property
|
|
46
|
-
def files(self) -> Mapping[str, Any]:
|
|
47
|
-
raise NotImplementedError
|
|
48
|
-
|
|
49
|
-
@property
|
|
50
|
-
def content_type(self) -> Optional[str]:
|
|
51
|
-
return self.request.headers.get("Content-Type", None)
|
|
52
|
-
|
|
53
|
-
|
|
54
19
|
class GraphQLView(
|
|
55
20
|
SyncBaseHTTPView[Request, Response, TemporalResponse, Context, RootValue]
|
|
56
21
|
):
|
|
@@ -60,8 +25,8 @@ class GraphQLView(
|
|
|
60
25
|
def __init__(
|
|
61
26
|
self,
|
|
62
27
|
schema: BaseSchema,
|
|
63
|
-
graphiql:
|
|
64
|
-
graphql_ide:
|
|
28
|
+
graphiql: bool | None = None,
|
|
29
|
+
graphql_ide: GraphQL_IDE | None = "graphiql",
|
|
65
30
|
allow_queries_via_get: bool = True,
|
|
66
31
|
) -> None:
|
|
67
32
|
self.allow_queries_via_get = allow_queries_via_get
|
|
@@ -76,7 +41,7 @@ class GraphQLView(
|
|
|
76
41
|
else:
|
|
77
42
|
self.graphql_ide = graphql_ide
|
|
78
43
|
|
|
79
|
-
def get_root_value(self, request: Request) ->
|
|
44
|
+
def get_root_value(self, request: Request) -> RootValue | None:
|
|
80
45
|
return None
|
|
81
46
|
|
|
82
47
|
def render_graphql_ide(self, request: Request) -> Response:
|
|
@@ -88,33 +53,13 @@ class GraphQLView(
|
|
|
88
53
|
def get_sub_response(self, request: Request) -> TemporalResponse:
|
|
89
54
|
return TemporalResponse()
|
|
90
55
|
|
|
91
|
-
@staticmethod
|
|
92
|
-
def error_response(
|
|
93
|
-
message: str,
|
|
94
|
-
error_code: str,
|
|
95
|
-
http_status_code: int,
|
|
96
|
-
headers: Optional[dict[str, str | list[str]]] = None,
|
|
97
|
-
) -> Response:
|
|
98
|
-
"""A wrapper for error responses.
|
|
99
|
-
|
|
100
|
-
Args:
|
|
101
|
-
message: The error message.
|
|
102
|
-
error_code: The error code.
|
|
103
|
-
http_status_code: The HTTP status code.
|
|
104
|
-
headers: The headers to include in the response.
|
|
105
|
-
|
|
106
|
-
Returns:
|
|
107
|
-
An errors response.
|
|
108
|
-
"""
|
|
109
|
-
body = {"Code": error_code, "Message": message}
|
|
110
|
-
|
|
111
|
-
return Response(body=body, status_code=http_status_code, headers=headers)
|
|
112
|
-
|
|
113
56
|
def get_context(self, request: Request, response: TemporalResponse) -> Context:
|
|
114
57
|
return {"request": request, "response": response} # type: ignore
|
|
115
58
|
|
|
116
59
|
def create_response(
|
|
117
|
-
self,
|
|
60
|
+
self,
|
|
61
|
+
response_data: GraphQLHTTPResponse | list[GraphQLHTTPResponse],
|
|
62
|
+
sub_response: TemporalResponse,
|
|
118
63
|
) -> Response:
|
|
119
64
|
status_code = 200
|
|
120
65
|
|
|
@@ -134,20 +79,9 @@ class GraphQLView(
|
|
|
134
79
|
try:
|
|
135
80
|
return self.run(request=request)
|
|
136
81
|
except HTTPException as e:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
403: "ForbiddenError",
|
|
141
|
-
404: "NotFoundError",
|
|
142
|
-
409: "ConflictError",
|
|
143
|
-
429: "TooManyRequestsError",
|
|
144
|
-
500: "ChaliceViewError",
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return self.error_response(
|
|
148
|
-
error_code=error_code_map.get(e.status_code, "ChaliceViewError"),
|
|
149
|
-
message=e.reason,
|
|
150
|
-
http_status_code=e.status_code,
|
|
82
|
+
return Response(
|
|
83
|
+
body=e.reason,
|
|
84
|
+
status_code=e.status_code,
|
|
151
85
|
)
|
|
152
86
|
|
|
153
87
|
|
|
@@ -2,13 +2,12 @@ import asyncio
|
|
|
2
2
|
import contextlib
|
|
3
3
|
import warnings
|
|
4
4
|
from collections import defaultdict
|
|
5
|
-
from collections.abc import AsyncGenerator, Awaitable, Sequence
|
|
5
|
+
from collections.abc import AsyncGenerator, Awaitable, Callable, Sequence
|
|
6
6
|
from typing import (
|
|
7
7
|
Any,
|
|
8
|
-
|
|
9
|
-
Optional,
|
|
8
|
+
Literal,
|
|
10
9
|
)
|
|
11
|
-
from typing_extensions import
|
|
10
|
+
from typing_extensions import Protocol, TypedDict
|
|
12
11
|
from weakref import WeakSet
|
|
13
12
|
|
|
14
13
|
from channels.consumer import AsyncConsumer
|
|
@@ -54,7 +53,7 @@ class ChannelsConsumer(AsyncConsumer):
|
|
|
54
53
|
"""Base channels async consumer."""
|
|
55
54
|
|
|
56
55
|
channel_name: str
|
|
57
|
-
channel_layer:
|
|
56
|
+
channel_layer: ChannelsLayer | None
|
|
58
57
|
channel_receive: Callable[[], Awaitable[dict]]
|
|
59
58
|
|
|
60
59
|
def __init__(self, *args: str, **kwargs: Any) -> None:
|
|
@@ -80,7 +79,7 @@ class ChannelsConsumer(AsyncConsumer):
|
|
|
80
79
|
self,
|
|
81
80
|
type: str,
|
|
82
81
|
*,
|
|
83
|
-
timeout:
|
|
82
|
+
timeout: float | None = None,
|
|
84
83
|
groups: Sequence[str] = (),
|
|
85
84
|
) -> AsyncGenerator[Any, None]:
|
|
86
85
|
"""Listen for messages sent to this consumer.
|
|
@@ -139,7 +138,7 @@ class ChannelsConsumer(AsyncConsumer):
|
|
|
139
138
|
self,
|
|
140
139
|
type: str,
|
|
141
140
|
*,
|
|
142
|
-
timeout:
|
|
141
|
+
timeout: float | None = None,
|
|
143
142
|
groups: Sequence[str] = (),
|
|
144
143
|
) -> AsyncGenerator[Any, None]:
|
|
145
144
|
"""Listen for messages sent to this consumer.
|
|
@@ -188,7 +187,7 @@ class ChannelsConsumer(AsyncConsumer):
|
|
|
188
187
|
await self.channel_layer.group_discard(group, self.channel_name)
|
|
189
188
|
|
|
190
189
|
async def _listen_to_channel_generator(
|
|
191
|
-
self, queue: asyncio.Queue, timeout:
|
|
190
|
+
self, queue: asyncio.Queue, timeout: float | None
|
|
192
191
|
) -> AsyncGenerator[Any, None]:
|
|
193
192
|
"""Generator for listen_to_channel method.
|
|
194
193
|
|
|
@@ -5,34 +5,27 @@ import json
|
|
|
5
5
|
import warnings
|
|
6
6
|
from functools import cached_property
|
|
7
7
|
from io import BytesIO
|
|
8
|
-
from typing import
|
|
9
|
-
|
|
10
|
-
Any,
|
|
11
|
-
Callable,
|
|
12
|
-
Optional,
|
|
13
|
-
Union,
|
|
14
|
-
)
|
|
15
|
-
from typing_extensions import TypeGuard, assert_never
|
|
8
|
+
from typing import TYPE_CHECKING, Any, TypeGuard
|
|
9
|
+
from typing_extensions import assert_never
|
|
16
10
|
from urllib.parse import parse_qs
|
|
17
11
|
|
|
12
|
+
from channels.db import database_sync_to_async
|
|
13
|
+
from channels.generic.http import AsyncHttpConsumer
|
|
18
14
|
from django.conf import settings
|
|
19
15
|
from django.core.files import uploadhandler
|
|
20
16
|
from django.http.multipartparser import MultiPartParser
|
|
17
|
+
from lia import AsyncHTTPRequestAdapter, FormData, HTTPException, SyncHTTPRequestAdapter
|
|
21
18
|
|
|
22
|
-
from
|
|
23
|
-
from
|
|
24
|
-
from strawberry.http.async_base_view import AsyncBaseHTTPView, AsyncHTTPRequestAdapter
|
|
25
|
-
from strawberry.http.exceptions import HTTPException
|
|
26
|
-
from strawberry.http.sync_base_view import SyncBaseHTTPView, SyncHTTPRequestAdapter
|
|
19
|
+
from strawberry.http.async_base_view import AsyncBaseHTTPView
|
|
20
|
+
from strawberry.http.sync_base_view import SyncBaseHTTPView
|
|
27
21
|
from strawberry.http.temporal_response import TemporalResponse
|
|
28
|
-
from strawberry.http.types import FormData
|
|
29
22
|
from strawberry.http.typevars import Context, RootValue
|
|
30
23
|
from strawberry.types.unset import UNSET
|
|
31
24
|
|
|
32
25
|
from .base import ChannelsConsumer
|
|
33
26
|
|
|
34
27
|
if TYPE_CHECKING:
|
|
35
|
-
from collections.abc import AsyncGenerator, Mapping
|
|
28
|
+
from collections.abc import AsyncGenerator, Callable, Mapping
|
|
36
29
|
|
|
37
30
|
from strawberry.http import GraphQLHTTPResponse
|
|
38
31
|
from strawberry.http.ides import GraphQL_IDE
|
|
@@ -84,7 +77,7 @@ class ChannelsRequest:
|
|
|
84
77
|
return self.consumer.scope["method"].upper()
|
|
85
78
|
|
|
86
79
|
@property
|
|
87
|
-
def content_type(self) ->
|
|
80
|
+
def content_type(self) -> str | None:
|
|
88
81
|
return self.headers.get("content-type", None)
|
|
89
82
|
|
|
90
83
|
@cached_property
|
|
@@ -130,9 +123,31 @@ class BaseChannelsRequestAdapter:
|
|
|
130
123
|
return self.request.headers
|
|
131
124
|
|
|
132
125
|
@property
|
|
133
|
-
def content_type(self) ->
|
|
126
|
+
def content_type(self) -> str | None:
|
|
134
127
|
return self.request.content_type
|
|
135
128
|
|
|
129
|
+
@property
|
|
130
|
+
def url(self) -> str:
|
|
131
|
+
scheme = self.request.consumer.scope["scheme"]
|
|
132
|
+
host = self.headers.get("host", "localhost")
|
|
133
|
+
path = self.request.consumer.scope["path"]
|
|
134
|
+
query_string = self.request.consumer.scope["query_string"]
|
|
135
|
+
url = f"{scheme}://{host}{path}"
|
|
136
|
+
if query_string:
|
|
137
|
+
url += f"?{query_string.decode()}"
|
|
138
|
+
return url
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def cookies(self) -> Mapping[str, str]:
|
|
142
|
+
cookie_header = self.headers.get("cookie", "")
|
|
143
|
+
cookies = {}
|
|
144
|
+
if cookie_header:
|
|
145
|
+
for cookie in cookie_header.split(";"):
|
|
146
|
+
if "=" in cookie:
|
|
147
|
+
key, value = cookie.split("=", 1)
|
|
148
|
+
cookies[key.strip()] = value.strip()
|
|
149
|
+
return cookies
|
|
150
|
+
|
|
136
151
|
|
|
137
152
|
class ChannelsRequestAdapter(BaseChannelsRequestAdapter, AsyncHTTPRequestAdapter):
|
|
138
153
|
async def get_body(self) -> bytes:
|
|
@@ -148,23 +163,26 @@ class SyncChannelsRequestAdapter(BaseChannelsRequestAdapter, SyncHTTPRequestAdap
|
|
|
148
163
|
return self.request.body
|
|
149
164
|
|
|
150
165
|
@property
|
|
151
|
-
def post_data(self) -> Mapping[str,
|
|
152
|
-
return self.request.form_data
|
|
166
|
+
def post_data(self) -> Mapping[str, str | bytes]:
|
|
167
|
+
return self.request.form_data.form
|
|
153
168
|
|
|
154
169
|
@property
|
|
155
170
|
def files(self) -> Mapping[str, Any]:
|
|
156
|
-
return self.request.form_data
|
|
171
|
+
return self.request.form_data.files
|
|
172
|
+
|
|
173
|
+
def get_form_data(self) -> FormData:
|
|
174
|
+
return self.request.form_data
|
|
157
175
|
|
|
158
176
|
|
|
159
177
|
class BaseGraphQLHTTPConsumer(ChannelsConsumer, AsyncHttpConsumer):
|
|
160
178
|
graphql_ide_html: str
|
|
161
|
-
graphql_ide:
|
|
179
|
+
graphql_ide: GraphQL_IDE | None = "graphiql"
|
|
162
180
|
|
|
163
181
|
def __init__(
|
|
164
182
|
self,
|
|
165
183
|
schema: BaseSchema,
|
|
166
|
-
graphiql:
|
|
167
|
-
graphql_ide:
|
|
184
|
+
graphiql: bool | None = None,
|
|
185
|
+
graphql_ide: GraphQL_IDE | None = "graphiql",
|
|
168
186
|
allow_queries_via_get: bool = True,
|
|
169
187
|
multipart_uploads_enabled: bool = False,
|
|
170
188
|
**kwargs: Any,
|
|
@@ -186,7 +204,9 @@ class BaseGraphQLHTTPConsumer(ChannelsConsumer, AsyncHttpConsumer):
|
|
|
186
204
|
super().__init__(**kwargs)
|
|
187
205
|
|
|
188
206
|
def create_response(
|
|
189
|
-
self,
|
|
207
|
+
self,
|
|
208
|
+
response_data: GraphQLHTTPResponse | list[GraphQLHTTPResponse],
|
|
209
|
+
sub_response: TemporalResponse,
|
|
190
210
|
) -> ChannelsResponse:
|
|
191
211
|
return ChannelsResponse(
|
|
192
212
|
content=json.dumps(response_data).encode(),
|
|
@@ -227,7 +247,7 @@ class GraphQLHTTPConsumer(
|
|
|
227
247
|
BaseGraphQLHTTPConsumer,
|
|
228
248
|
AsyncBaseHTTPView[
|
|
229
249
|
ChannelsRequest,
|
|
230
|
-
|
|
250
|
+
ChannelsResponse | MultipartChannelsResponse,
|
|
231
251
|
TemporalResponse,
|
|
232
252
|
ChannelsRequest,
|
|
233
253
|
TemporalResponse,
|
|
@@ -259,7 +279,7 @@ class GraphQLHTTPConsumer(
|
|
|
259
279
|
allow_queries_via_get: bool = True
|
|
260
280
|
request_adapter_class = ChannelsRequestAdapter
|
|
261
281
|
|
|
262
|
-
async def get_root_value(self, request: ChannelsRequest) ->
|
|
282
|
+
async def get_root_value(self, request: ChannelsRequest) -> RootValue | None:
|
|
263
283
|
return None # pragma: no cover
|
|
264
284
|
|
|
265
285
|
async def get_context(
|
|
@@ -302,13 +322,11 @@ class GraphQLHTTPConsumer(
|
|
|
302
322
|
) -> TypeGuard[ChannelsRequest]:
|
|
303
323
|
return False
|
|
304
324
|
|
|
305
|
-
async def pick_websocket_subprotocol(
|
|
306
|
-
self, request: ChannelsRequest
|
|
307
|
-
) -> Optional[str]:
|
|
325
|
+
async def pick_websocket_subprotocol(self, request: ChannelsRequest) -> str | None:
|
|
308
326
|
return None
|
|
309
327
|
|
|
310
328
|
async def create_websocket_response(
|
|
311
|
-
self, request: ChannelsRequest, subprotocol:
|
|
329
|
+
self, request: ChannelsRequest, subprotocol: str | None
|
|
312
330
|
) -> TemporalResponse:
|
|
313
331
|
raise NotImplementedError
|
|
314
332
|
|
|
@@ -333,7 +351,7 @@ class SyncGraphQLHTTPConsumer(
|
|
|
333
351
|
allow_queries_via_get: bool = True
|
|
334
352
|
request_adapter_class = SyncChannelsRequestAdapter
|
|
335
353
|
|
|
336
|
-
def get_root_value(self, request: ChannelsRequest) ->
|
|
354
|
+
def get_root_value(self, request: ChannelsRequest) -> RootValue | None:
|
|
337
355
|
return None # pragma: no cover
|
|
338
356
|
|
|
339
357
|
def get_context(
|
|
@@ -361,7 +379,7 @@ class SyncGraphQLHTTPConsumer(
|
|
|
361
379
|
self,
|
|
362
380
|
request: ChannelsRequest,
|
|
363
381
|
context: Context = UNSET,
|
|
364
|
-
root_value:
|
|
382
|
+
root_value: RootValue | None = UNSET,
|
|
365
383
|
) -> ChannelsResponse | MultipartChannelsResponse:
|
|
366
384
|
return super().run(request, context, root_value)
|
|
367
385
|
|
|
@@ -5,11 +5,9 @@ import datetime
|
|
|
5
5
|
import json
|
|
6
6
|
from typing import (
|
|
7
7
|
TYPE_CHECKING,
|
|
8
|
-
Optional,
|
|
9
8
|
TypedDict,
|
|
10
|
-
|
|
9
|
+
TypeGuard,
|
|
11
10
|
)
|
|
12
|
-
from typing_extensions import TypeGuard
|
|
13
11
|
|
|
14
12
|
from strawberry.http.async_base_view import AsyncBaseHTTPView, AsyncWebSocketAdapter
|
|
15
13
|
from strawberry.http.exceptions import NonJsonMessageReceived, NonTextMessageReceived
|
|
@@ -62,7 +60,7 @@ class ChannelsWebSocketAdapter(AsyncWebSocketAdapter):
|
|
|
62
60
|
|
|
63
61
|
|
|
64
62
|
class MessageQueueData(TypedDict):
|
|
65
|
-
message:
|
|
63
|
+
message: str | None
|
|
66
64
|
disconnected: bool
|
|
67
65
|
|
|
68
66
|
|
|
@@ -102,19 +100,18 @@ class GraphQLWSConsumer(
|
|
|
102
100
|
```
|
|
103
101
|
"""
|
|
104
102
|
|
|
105
|
-
websocket_adapter_class = ChannelsWebSocketAdapter
|
|
103
|
+
websocket_adapter_class = ChannelsWebSocketAdapter # type: ignore
|
|
106
104
|
|
|
107
105
|
def __init__(
|
|
108
106
|
self,
|
|
109
107
|
schema: BaseSchema,
|
|
110
108
|
keep_alive: bool = False,
|
|
111
109
|
keep_alive_interval: float = 1,
|
|
112
|
-
debug: bool = False,
|
|
113
110
|
subscription_protocols: Sequence[str] = (
|
|
114
111
|
GRAPHQL_TRANSPORT_WS_PROTOCOL,
|
|
115
112
|
GRAPHQL_WS_PROTOCOL,
|
|
116
113
|
),
|
|
117
|
-
connection_init_wait_timeout:
|
|
114
|
+
connection_init_wait_timeout: datetime.timedelta | None = None,
|
|
118
115
|
) -> None:
|
|
119
116
|
if connection_init_wait_timeout is None:
|
|
120
117
|
connection_init_wait_timeout = datetime.timedelta(minutes=1)
|
|
@@ -122,10 +119,9 @@ class GraphQLWSConsumer(
|
|
|
122
119
|
self.schema = schema
|
|
123
120
|
self.keep_alive = keep_alive
|
|
124
121
|
self.keep_alive_interval = keep_alive_interval
|
|
125
|
-
self.debug = debug
|
|
126
122
|
self.protocols = subscription_protocols
|
|
127
123
|
self.message_queue: asyncio.Queue[MessageQueueData] = asyncio.Queue()
|
|
128
|
-
self.run_task:
|
|
124
|
+
self.run_task: asyncio.Task | None = None
|
|
129
125
|
|
|
130
126
|
super().__init__()
|
|
131
127
|
|
|
@@ -133,7 +129,7 @@ class GraphQLWSConsumer(
|
|
|
133
129
|
self.run_task = asyncio.create_task(self.run(self))
|
|
134
130
|
|
|
135
131
|
async def receive(
|
|
136
|
-
self, text_data:
|
|
132
|
+
self, text_data: str | None = None, bytes_data: bytes | None = None
|
|
137
133
|
) -> None:
|
|
138
134
|
if text_data:
|
|
139
135
|
self.message_queue.put_nowait({"message": text_data, "disconnected": False})
|
|
@@ -145,7 +141,7 @@ class GraphQLWSConsumer(
|
|
|
145
141
|
assert self.run_task
|
|
146
142
|
await self.run_task
|
|
147
143
|
|
|
148
|
-
async def get_root_value(self, request: GraphQLWSConsumer) ->
|
|
144
|
+
async def get_root_value(self, request: GraphQLWSConsumer) -> RootValue | None:
|
|
149
145
|
return None
|
|
150
146
|
|
|
151
147
|
async def get_context(
|
|
@@ -164,7 +160,9 @@ class GraphQLWSConsumer(
|
|
|
164
160
|
raise NotImplementedError
|
|
165
161
|
|
|
166
162
|
def create_response(
|
|
167
|
-
self,
|
|
163
|
+
self,
|
|
164
|
+
response_data: GraphQLHTTPResponse | list[GraphQLHTTPResponse],
|
|
165
|
+
sub_response: GraphQLWSConsumer,
|
|
168
166
|
) -> GraphQLWSConsumer:
|
|
169
167
|
raise NotImplementedError
|
|
170
168
|
|
|
@@ -178,14 +176,14 @@ class GraphQLWSConsumer(
|
|
|
178
176
|
|
|
179
177
|
async def pick_websocket_subprotocol(
|
|
180
178
|
self, request: GraphQLWSConsumer
|
|
181
|
-
) ->
|
|
179
|
+
) -> str | None:
|
|
182
180
|
protocols = request.scope["subprotocols"]
|
|
183
181
|
intersection = set(protocols) & set(self.protocols)
|
|
184
182
|
sorted_intersection = sorted(intersection, key=protocols.index)
|
|
185
183
|
return next(iter(sorted_intersection), None)
|
|
186
184
|
|
|
187
185
|
async def create_websocket_response(
|
|
188
|
-
self, request: GraphQLWSConsumer, subprotocol:
|
|
186
|
+
self, request: GraphQLWSConsumer, subprotocol: str | None
|
|
189
187
|
) -> GraphQLWSConsumer:
|
|
190
188
|
await request.accept(subprotocol=subprotocol)
|
|
191
189
|
return request
|
strawberry/channels/router.py
CHANGED
|
@@ -7,11 +7,10 @@ on preferences and client support. Then it hands off to the appropriate consumer
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
from typing import TYPE_CHECKING
|
|
11
|
-
|
|
12
|
-
from django.urls import re_path
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
13
11
|
|
|
14
12
|
from channels.routing import ProtocolTypeRouter, URLRouter
|
|
13
|
+
from django.urls import re_path
|
|
15
14
|
|
|
16
15
|
from .handlers.http_handler import GraphQLHTTPConsumer
|
|
17
16
|
from .handlers.ws_handler import GraphQLWSConsumer
|
|
@@ -47,7 +46,7 @@ class GraphQLProtocolTypeRouter(ProtocolTypeRouter):
|
|
|
47
46
|
def __init__(
|
|
48
47
|
self,
|
|
49
48
|
schema: BaseSchema,
|
|
50
|
-
django_application:
|
|
49
|
+
django_application: str | None = None,
|
|
51
50
|
url_pattern: str = "^graphql",
|
|
52
51
|
) -> None:
|
|
53
52
|
http_urls = [re_path(url_pattern, GraphQLHTTPConsumer.as_asgi(schema=schema))]
|
strawberry/channels/testing.py
CHANGED
|
@@ -4,13 +4,11 @@ import uuid
|
|
|
4
4
|
from typing import (
|
|
5
5
|
TYPE_CHECKING,
|
|
6
6
|
Any,
|
|
7
|
-
Optional,
|
|
8
|
-
Union,
|
|
9
7
|
)
|
|
10
8
|
|
|
9
|
+
from channels.testing.websocket import WebsocketCommunicator
|
|
11
10
|
from graphql import GraphQLError, GraphQLFormattedError
|
|
12
11
|
|
|
13
|
-
from channels.testing.websocket import WebsocketCommunicator
|
|
14
12
|
from strawberry.subscriptions import GRAPHQL_TRANSPORT_WS_PROTOCOL, GRAPHQL_WS_PROTOCOL
|
|
15
13
|
from strawberry.subscriptions.protocols.graphql_transport_ws import (
|
|
16
14
|
types as transport_ws_types,
|
|
@@ -53,7 +51,7 @@ class GraphQLWebsocketCommunicator(WebsocketCommunicator):
|
|
|
53
51
|
self,
|
|
54
52
|
application: ASGIApplication,
|
|
55
53
|
path: str,
|
|
56
|
-
headers:
|
|
54
|
+
headers: list[tuple[bytes, bytes]] | None = None,
|
|
57
55
|
protocol: str = GRAPHQL_TRANSPORT_WS_PROTOCOL,
|
|
58
56
|
connection_params: dict | None = None,
|
|
59
57
|
**kwargs: Any,
|
|
@@ -83,9 +81,9 @@ class GraphQLWebsocketCommunicator(WebsocketCommunicator):
|
|
|
83
81
|
|
|
84
82
|
async def __aexit__(
|
|
85
83
|
self,
|
|
86
|
-
exc_type:
|
|
87
|
-
exc_val:
|
|
88
|
-
exc_tb:
|
|
84
|
+
exc_type: type[BaseException] | None,
|
|
85
|
+
exc_val: BaseException | None,
|
|
86
|
+
exc_tb: TracebackType | None,
|
|
89
87
|
) -> None:
|
|
90
88
|
await self.disconnect()
|
|
91
89
|
|
|
@@ -114,8 +112,8 @@ class GraphQLWebsocketCommunicator(WebsocketCommunicator):
|
|
|
114
112
|
# get transformed into `FormattedExecutionResult` on the wire, but we attempt
|
|
115
113
|
# to do a limited representation of them here, to make testing simpler.
|
|
116
114
|
async def subscribe(
|
|
117
|
-
self, query: str, variables:
|
|
118
|
-
) ->
|
|
115
|
+
self, query: str, variables: dict | None = None
|
|
116
|
+
) -> ExecutionResult | AsyncIterator[ExecutionResult]:
|
|
119
117
|
id_ = uuid.uuid4().hex
|
|
120
118
|
|
|
121
119
|
if self.protocol == GRAPHQL_TRANSPORT_WS_PROTOCOL:
|
strawberry/cli/__init__.py
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
try:
|
|
2
2
|
from .app import app
|
|
3
|
-
from .commands.codegen import codegen as codegen
|
|
4
|
-
from .commands.
|
|
3
|
+
from .commands.codegen import codegen as codegen
|
|
4
|
+
from .commands.dev import dev as dev
|
|
5
|
+
from .commands.export_schema import export_schema as export_schema
|
|
5
6
|
from .commands.locate_definition import (
|
|
6
|
-
locate_definition as locate_definition,
|
|
7
|
+
locate_definition as locate_definition,
|
|
7
8
|
)
|
|
8
9
|
from .commands.schema_codegen import (
|
|
9
|
-
schema_codegen as schema_codegen,
|
|
10
|
+
schema_codegen as schema_codegen,
|
|
10
11
|
)
|
|
11
|
-
from .commands.server import server as server
|
|
12
|
-
from .commands.upgrade import upgrade as upgrade
|
|
12
|
+
from .commands.server import server as server
|
|
13
|
+
from .commands.upgrade import upgrade as upgrade
|
|
13
14
|
|
|
14
15
|
def run() -> None:
|
|
15
16
|
app()
|
|
@@ -4,7 +4,7 @@ import functools
|
|
|
4
4
|
import importlib
|
|
5
5
|
import inspect
|
|
6
6
|
from pathlib import Path # noqa: TC003
|
|
7
|
-
from typing import
|
|
7
|
+
from typing import cast
|
|
8
8
|
|
|
9
9
|
import rich
|
|
10
10
|
import typer
|
|
@@ -22,9 +22,9 @@ def _is_codegen_plugin(obj: object) -> bool:
|
|
|
22
22
|
)
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def _import_plugin(plugin: str) ->
|
|
25
|
+
def _import_plugin(plugin: str) -> type[QueryCodegenPlugin] | None:
|
|
26
26
|
module_name = plugin
|
|
27
|
-
symbol_name:
|
|
27
|
+
symbol_name: str | None = None
|
|
28
28
|
|
|
29
29
|
if ":" in plugin:
|
|
30
30
|
module_name, symbol_name = plugin.split(":", 1)
|
|
@@ -61,7 +61,7 @@ def _import_plugin(plugin: str) -> Optional[type[QueryCodegenPlugin]]:
|
|
|
61
61
|
@functools.lru_cache
|
|
62
62
|
def _load_plugin(
|
|
63
63
|
plugin_path: str,
|
|
64
|
-
) -> type[
|
|
64
|
+
) -> type[QueryCodegenPlugin | ConsolePlugin]:
|
|
65
65
|
# try to import plugin_name from current folder
|
|
66
66
|
# then try to import from strawberry.codegen.plugins
|
|
67
67
|
|
|
@@ -79,7 +79,7 @@ def _load_plugin(
|
|
|
79
79
|
|
|
80
80
|
def _load_plugins(
|
|
81
81
|
plugin_ids: list[str], query: Path
|
|
82
|
-
) -> list[
|
|
82
|
+
) -> list[QueryCodegenPlugin | ConsolePlugin]:
|
|
83
83
|
plugins = []
|
|
84
84
|
for ptype_id in plugin_ids:
|
|
85
85
|
ptype = _load_plugin(ptype_id)
|
|
@@ -91,7 +91,7 @@ def _load_plugins(
|
|
|
91
91
|
|
|
92
92
|
@app.command(help="Generate code from a query")
|
|
93
93
|
def codegen(
|
|
94
|
-
query:
|
|
94
|
+
query: list[Path] | None = typer.Argument(
|
|
95
95
|
default=None, exists=True, dir_okay=False
|
|
96
96
|
),
|
|
97
97
|
schema: str = typer.Option(..., help="Python path to the schema file"),
|
|
@@ -120,7 +120,7 @@ def codegen(
|
|
|
120
120
|
"-p",
|
|
121
121
|
"--plugins",
|
|
122
122
|
),
|
|
123
|
-
cli_plugin:
|
|
123
|
+
cli_plugin: str | None = None,
|
|
124
124
|
) -> None:
|
|
125
125
|
if not query:
|
|
126
126
|
return
|