strawberry-graphql 0.283.0__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/aiohttp/test/client.py +8 -15
- strawberry/aiohttp/views.py +12 -15
- strawberry/annotation.py +19 -23
- strawberry/asgi/__init__.py +18 -17
- strawberry/asgi/test/client.py +6 -6
- strawberry/chalice/views.py +6 -6
- strawberry/channels/handlers/base.py +7 -8
- strawberry/channels/handlers/http_handler.py +18 -20
- strawberry/channels/handlers/ws_handler.py +10 -12
- strawberry/channels/router.py +3 -4
- strawberry/channels/testing.py +7 -9
- strawberry/cli/commands/codegen.py +7 -7
- strawberry/cli/commands/schema_codegen.py +1 -2
- strawberry/cli/commands/upgrade/__init__.py +1 -3
- strawberry/cli/commands/upgrade/_run_codemod.py +2 -2
- 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/annotated_unions.py +2 -2
- strawberry/dataloader.py +28 -24
- strawberry/directive.py +6 -7
- strawberry/django/test/client.py +3 -3
- strawberry/django/views.py +16 -19
- 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 +15 -23
- 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 +3 -1
- strawberry/extensions/field_extension.py +4 -4
- 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 +1 -2
- strawberry/fastapi/context.py +6 -6
- strawberry/fastapi/router.py +33 -36
- 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 +10 -11
- strawberry/http/__init__.py +14 -14
- strawberry/http/async_base_view.py +23 -28
- strawberry/http/base.py +11 -12
- strawberry/http/ides.py +2 -3
- strawberry/http/sync_base_view.py +11 -13
- strawberry/http/types.py +3 -3
- strawberry/litestar/controller.py +40 -35
- strawberry/permission.py +4 -6
- strawberry/printer/ast_from_value.py +3 -5
- strawberry/printer/printer.py +8 -13
- strawberry/quart/views.py +12 -14
- strawberry/relay/exceptions.py +4 -4
- strawberry/relay/fields.py +33 -32
- strawberry/relay/types.py +31 -34
- strawberry/relay/utils.py +2 -2
- strawberry/resolvers.py +2 -1
- strawberry/sanic/context.py +1 -0
- strawberry/sanic/utils.py +3 -3
- strawberry/sanic/views.py +11 -14
- strawberry/scalars.py +2 -2
- strawberry/schema/_graphql_core.py +5 -5
- strawberry/schema/base.py +32 -33
- strawberry/schema/compat.py +9 -9
- strawberry/schema/config.py +5 -2
- strawberry/schema/exceptions.py +1 -3
- strawberry/schema/name_converter.py +6 -6
- strawberry/schema/schema.py +55 -60
- strawberry/schema/schema_converter.py +27 -22
- strawberry/schema/types/base_scalars.py +1 -1
- strawberry/schema/types/concrete_type.py +5 -5
- strawberry/schema_codegen/__init__.py +3 -3
- strawberry/schema_directive.py +7 -6
- strawberry/subscriptions/protocols/graphql_transport_ws/handlers.py +5 -6
- strawberry/subscriptions/protocols/graphql_transport_ws/types.py +20 -20
- strawberry/subscriptions/protocols/graphql_ws/handlers.py +5 -6
- 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 +25 -26
- strawberry/types/auto.py +3 -4
- strawberry/types/base.py +25 -27
- strawberry/types/enum.py +22 -25
- strawberry/types/execution.py +14 -15
- strawberry/types/field.py +108 -108
- 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.283.0.dist-info → strawberry_graphql-0.284.3.dist-info}/METADATA +2 -2
- strawberry_graphql-0.284.3.dist-info/RECORD +243 -0
- strawberry/utils/dataclasses.py +0 -37
- strawberry_graphql-0.283.0.dist-info/RECORD +0 -244
- {strawberry_graphql-0.283.0.dist-info → strawberry_graphql-0.284.3.dist-info}/WHEEL +0 -0
- {strawberry_graphql-0.283.0.dist-info → strawberry_graphql-0.284.3.dist-info}/entry_points.txt +0 -0
- {strawberry_graphql-0.283.0.dist-info → strawberry_graphql-0.284.3.dist-info}/licenses/LICENSE +0 -0
strawberry/federation/scalar.py
CHANGED
|
@@ -1,22 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
from collections.abc import Iterable
|
|
1
|
+
from collections.abc import Callable, Iterable
|
|
3
2
|
from typing import (
|
|
4
3
|
Any,
|
|
5
|
-
Callable,
|
|
6
4
|
NewType,
|
|
7
|
-
Optional,
|
|
8
5
|
TypeVar,
|
|
9
|
-
Union,
|
|
10
6
|
overload,
|
|
11
7
|
)
|
|
12
8
|
|
|
13
9
|
from strawberry.types.scalar import ScalarWrapper, _process_scalar
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
if sys.version_info >= (3, 10):
|
|
17
|
-
_T = TypeVar("_T", bound=Union[type, NewType])
|
|
18
|
-
else:
|
|
19
|
-
_T = TypeVar("_T", bound=type)
|
|
11
|
+
_T = TypeVar("_T", bound=type | NewType)
|
|
20
12
|
|
|
21
13
|
|
|
22
14
|
def identity(x: _T) -> _T: # pragma: no cover
|
|
@@ -26,18 +18,18 @@ def identity(x: _T) -> _T: # pragma: no cover
|
|
|
26
18
|
@overload
|
|
27
19
|
def scalar(
|
|
28
20
|
*,
|
|
29
|
-
name:
|
|
30
|
-
description:
|
|
31
|
-
specified_by_url:
|
|
21
|
+
name: str | None = None,
|
|
22
|
+
description: str | None = None,
|
|
23
|
+
specified_by_url: str | None = None,
|
|
32
24
|
serialize: Callable = identity,
|
|
33
|
-
parse_value:
|
|
34
|
-
parse_literal:
|
|
25
|
+
parse_value: Callable | None = None,
|
|
26
|
+
parse_literal: Callable | None = None,
|
|
35
27
|
directives: Iterable[object] = (),
|
|
36
28
|
authenticated: bool = False,
|
|
37
29
|
inaccessible: bool = False,
|
|
38
|
-
policy:
|
|
39
|
-
requires_scopes:
|
|
40
|
-
tags:
|
|
30
|
+
policy: list[list[str]] | None = None,
|
|
31
|
+
requires_scopes: list[list[str]] | None = None,
|
|
32
|
+
tags: Iterable[str] | None = (),
|
|
41
33
|
) -> Callable[[_T], _T]: ...
|
|
42
34
|
|
|
43
35
|
|
|
@@ -45,36 +37,36 @@ def scalar(
|
|
|
45
37
|
def scalar(
|
|
46
38
|
cls: _T,
|
|
47
39
|
*,
|
|
48
|
-
name:
|
|
49
|
-
description:
|
|
50
|
-
specified_by_url:
|
|
40
|
+
name: str | None = None,
|
|
41
|
+
description: str | None = None,
|
|
42
|
+
specified_by_url: str | None = None,
|
|
51
43
|
serialize: Callable = identity,
|
|
52
|
-
parse_value:
|
|
53
|
-
parse_literal:
|
|
44
|
+
parse_value: Callable | None = None,
|
|
45
|
+
parse_literal: Callable | None = None,
|
|
54
46
|
directives: Iterable[object] = (),
|
|
55
47
|
authenticated: bool = False,
|
|
56
48
|
inaccessible: bool = False,
|
|
57
|
-
policy:
|
|
58
|
-
requires_scopes:
|
|
59
|
-
tags:
|
|
49
|
+
policy: list[list[str]] | None = None,
|
|
50
|
+
requires_scopes: list[list[str]] | None = None,
|
|
51
|
+
tags: Iterable[str] | None = (),
|
|
60
52
|
) -> _T: ...
|
|
61
53
|
|
|
62
54
|
|
|
63
55
|
def scalar(
|
|
64
|
-
cls:
|
|
56
|
+
cls: _T | None = None,
|
|
65
57
|
*,
|
|
66
|
-
name:
|
|
67
|
-
description:
|
|
68
|
-
specified_by_url:
|
|
58
|
+
name: str | None = None,
|
|
59
|
+
description: str | None = None,
|
|
60
|
+
specified_by_url: str | None = None,
|
|
69
61
|
serialize: Callable = identity,
|
|
70
|
-
parse_value:
|
|
71
|
-
parse_literal:
|
|
62
|
+
parse_value: Callable | None = None,
|
|
63
|
+
parse_literal: Callable | None = None,
|
|
72
64
|
directives: Iterable[object] = (),
|
|
73
65
|
authenticated: bool = False,
|
|
74
66
|
inaccessible: bool = False,
|
|
75
|
-
policy:
|
|
76
|
-
requires_scopes:
|
|
77
|
-
tags:
|
|
67
|
+
policy: list[list[str]] | None = None,
|
|
68
|
+
requires_scopes: list[list[str]] | None = None,
|
|
69
|
+
tags: Iterable[str] | None = (),
|
|
78
70
|
) -> Any:
|
|
79
71
|
"""Annotates a class or type as a GraphQL custom scalar.
|
|
80
72
|
|
strawberry/federation/schema.py
CHANGED
|
@@ -44,18 +44,17 @@ FederationAny = scalar(NewType("_Any", object), name="_Any") # type: ignore
|
|
|
44
44
|
class Schema(BaseSchema):
|
|
45
45
|
def __init__(
|
|
46
46
|
self,
|
|
47
|
-
query:
|
|
48
|
-
mutation:
|
|
49
|
-
subscription:
|
|
47
|
+
query: type | None = None,
|
|
48
|
+
mutation: type | None = None,
|
|
49
|
+
subscription: type | None = None,
|
|
50
50
|
# TODO: we should update directives' type in the main schema
|
|
51
51
|
directives: Iterable[type] = (),
|
|
52
52
|
types: Iterable[type] = (),
|
|
53
53
|
extensions: Iterable[Union[type["SchemaExtension"], "SchemaExtension"]] = (),
|
|
54
|
-
execution_context_class:
|
|
54
|
+
execution_context_class: type["GraphQLExecutionContext"] | None = None,
|
|
55
55
|
config: Optional["StrawberryConfig"] = None,
|
|
56
|
-
scalar_overrides:
|
|
57
|
-
|
|
58
|
-
] = None,
|
|
56
|
+
scalar_overrides: dict[object, Union[type, "ScalarWrapper", "ScalarDefinition"]]
|
|
57
|
+
| None = None,
|
|
59
58
|
schema_directives: Iterable[object] = (),
|
|
60
59
|
enable_federation_2: bool = False,
|
|
61
60
|
) -> None:
|
|
@@ -85,9 +84,9 @@ class Schema(BaseSchema):
|
|
|
85
84
|
|
|
86
85
|
def _get_federation_query_type(
|
|
87
86
|
self,
|
|
88
|
-
query:
|
|
89
|
-
mutation:
|
|
90
|
-
subscription:
|
|
87
|
+
query: type[WithStrawberryObjectDefinition] | None,
|
|
88
|
+
mutation: type[WithStrawberryObjectDefinition] | None,
|
|
89
|
+
subscription: type[WithStrawberryObjectDefinition] | None,
|
|
91
90
|
additional_types: Iterable[type[WithStrawberryObjectDefinition]],
|
|
92
91
|
) -> type:
|
|
93
92
|
"""Returns a new query type that includes the _service field.
|
|
@@ -124,7 +123,7 @@ class Schema(BaseSchema):
|
|
|
124
123
|
|
|
125
124
|
if entity_type:
|
|
126
125
|
self.entities_resolver.__annotations__["return"] = list[
|
|
127
|
-
|
|
126
|
+
entity_type | None # type: ignore
|
|
128
127
|
]
|
|
129
128
|
|
|
130
129
|
entities_field = strawberry.field(
|
|
@@ -250,7 +249,7 @@ class Schema(BaseSchema):
|
|
|
250
249
|
directive_by_url[import_url].add(f"@{name}")
|
|
251
250
|
|
|
252
251
|
def _add_link_directives(
|
|
253
|
-
self, additional_directives:
|
|
252
|
+
self, additional_directives: list[object] | None = None
|
|
254
253
|
) -> None:
|
|
255
254
|
from .schema_directives import FederationDirective, Link
|
|
256
255
|
|
|
@@ -312,11 +311,11 @@ class Schema(BaseSchema):
|
|
|
312
311
|
|
|
313
312
|
|
|
314
313
|
def _get_entity_type(
|
|
315
|
-
query:
|
|
316
|
-
mutation:
|
|
317
|
-
subscription:
|
|
314
|
+
query: type[WithStrawberryObjectDefinition] | None,
|
|
315
|
+
mutation: type[WithStrawberryObjectDefinition] | None,
|
|
316
|
+
subscription: type[WithStrawberryObjectDefinition] | None,
|
|
318
317
|
additional_types: Iterable[type[WithStrawberryObjectDefinition]],
|
|
319
|
-
) ->
|
|
318
|
+
) -> StrawberryUnion | None:
|
|
320
319
|
# recursively iterate over the schema to find all types annotated with @key
|
|
321
320
|
# if no types are annotated with @key, then the _Entity union and Query._entities
|
|
322
321
|
# field should not be added to the schema
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import dataclasses
|
|
2
|
-
from
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from typing import TypeVar
|
|
3
4
|
from typing_extensions import dataclass_transform
|
|
4
5
|
|
|
5
6
|
from strawberry.directive import directive_field
|
|
@@ -11,12 +12,12 @@ from strawberry.types.type_resolver import _get_fields
|
|
|
11
12
|
|
|
12
13
|
@dataclasses.dataclass
|
|
13
14
|
class ComposeOptions:
|
|
14
|
-
import_url:
|
|
15
|
+
import_url: str | None
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
@dataclasses.dataclass
|
|
18
19
|
class StrawberryFederationSchemaDirective(StrawberrySchemaDirective):
|
|
19
|
-
compose_options:
|
|
20
|
+
compose_options: ComposeOptions | None = None
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
T = TypeVar("T", bound=type)
|
|
@@ -30,12 +31,12 @@ T = TypeVar("T", bound=type)
|
|
|
30
31
|
def schema_directive(
|
|
31
32
|
*,
|
|
32
33
|
locations: list[Location],
|
|
33
|
-
description:
|
|
34
|
-
name:
|
|
34
|
+
description: str | None = None,
|
|
35
|
+
name: str | None = None,
|
|
35
36
|
repeatable: bool = False,
|
|
36
37
|
print_definition: bool = True,
|
|
37
38
|
compose: bool = False,
|
|
38
|
-
import_url:
|
|
39
|
+
import_url: str | None = None,
|
|
39
40
|
) -> Callable[[T], T]:
|
|
40
41
|
def _wrap(cls: T) -> T:
|
|
41
42
|
cls = _wrap_dataclass(cls) # type: ignore
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import ClassVar
|
|
2
|
+
from typing import ClassVar
|
|
3
3
|
|
|
4
4
|
from strawberry import directive_field
|
|
5
5
|
from strawberry.schema_directive import Location, schema_directive
|
|
@@ -59,7 +59,7 @@ class Provides(FederationDirective):
|
|
|
59
59
|
)
|
|
60
60
|
class Key(FederationDirective):
|
|
61
61
|
fields: FieldSet
|
|
62
|
-
resolvable:
|
|
62
|
+
resolvable: bool | None = True
|
|
63
63
|
imported_from: ClassVar[ImportedFrom] = ImportedFrom(
|
|
64
64
|
name="key", url="https://specs.apollo.dev/federation/v2.7"
|
|
65
65
|
)
|
|
@@ -81,17 +81,17 @@ class Shareable(FederationDirective):
|
|
|
81
81
|
locations=[Location.SCHEMA], name="link", repeatable=True, print_definition=False
|
|
82
82
|
)
|
|
83
83
|
class Link:
|
|
84
|
-
url:
|
|
85
|
-
as_:
|
|
86
|
-
for_:
|
|
87
|
-
import_:
|
|
84
|
+
url: str | None
|
|
85
|
+
as_: str | None = directive_field(name="as")
|
|
86
|
+
for_: LinkPurpose | None = directive_field(name="for")
|
|
87
|
+
import_: list[LinkImport | None] | None = directive_field(name="import")
|
|
88
88
|
|
|
89
89
|
def __init__(
|
|
90
90
|
self,
|
|
91
|
-
url:
|
|
92
|
-
as_:
|
|
93
|
-
for_:
|
|
94
|
-
import_:
|
|
91
|
+
url: str | None = UNSET,
|
|
92
|
+
as_: str | None = UNSET,
|
|
93
|
+
for_: LinkPurpose | None = UNSET,
|
|
94
|
+
import_: list[LinkImport | None] | None = UNSET,
|
|
95
95
|
) -> None:
|
|
96
96
|
self.url = url
|
|
97
97
|
self.as_ = as_
|
|
@@ -128,7 +128,7 @@ class Tag(FederationDirective):
|
|
|
128
128
|
)
|
|
129
129
|
class Override(FederationDirective):
|
|
130
130
|
override_from: str = directive_field(name="from")
|
|
131
|
-
label:
|
|
131
|
+
label: str | None = UNSET
|
|
132
132
|
imported_from: ClassVar[ImportedFrom] = ImportedFrom(
|
|
133
133
|
name="override", url="https://specs.apollo.dev/federation/v2.7"
|
|
134
134
|
)
|
strawberry/federation/union.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from collections.abc import Collection, Iterable
|
|
2
|
-
from typing import Any
|
|
2
|
+
from typing import Any
|
|
3
3
|
|
|
4
4
|
from strawberry.types.union import StrawberryUnion
|
|
5
5
|
from strawberry.types.union import union as base_union
|
|
@@ -7,12 +7,12 @@ from strawberry.types.union import union as base_union
|
|
|
7
7
|
|
|
8
8
|
def union(
|
|
9
9
|
name: str,
|
|
10
|
-
types:
|
|
10
|
+
types: Collection[type[Any]] | None = None,
|
|
11
11
|
*,
|
|
12
|
-
description:
|
|
12
|
+
description: str | None = None,
|
|
13
13
|
directives: Iterable[object] = (),
|
|
14
14
|
inaccessible: bool = False,
|
|
15
|
-
tags:
|
|
15
|
+
tags: Iterable[str] | None = (),
|
|
16
16
|
) -> StrawberryUnion:
|
|
17
17
|
"""Creates a new named Union type.
|
|
18
18
|
|
strawberry/flask/views.py
CHANGED
|
@@ -4,10 +4,8 @@ import warnings
|
|
|
4
4
|
from typing import (
|
|
5
5
|
TYPE_CHECKING,
|
|
6
6
|
ClassVar,
|
|
7
|
-
|
|
8
|
-
Union,
|
|
7
|
+
TypeGuard,
|
|
9
8
|
)
|
|
10
|
-
from typing_extensions import TypeGuard
|
|
11
9
|
|
|
12
10
|
from lia import AsyncFlaskHTTPRequestAdapter, FlaskHTTPRequestAdapter, HTTPException
|
|
13
11
|
|
|
@@ -19,19 +17,20 @@ from strawberry.http.typevars import Context, RootValue
|
|
|
19
17
|
|
|
20
18
|
if TYPE_CHECKING:
|
|
21
19
|
from flask.typing import ResponseReturnValue
|
|
20
|
+
|
|
22
21
|
from strawberry.http import GraphQLHTTPResponse
|
|
23
22
|
from strawberry.http.ides import GraphQL_IDE
|
|
24
23
|
from strawberry.schema.base import BaseSchema
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
class BaseGraphQLView:
|
|
28
|
-
graphql_ide:
|
|
27
|
+
graphql_ide: GraphQL_IDE | None
|
|
29
28
|
|
|
30
29
|
def __init__(
|
|
31
30
|
self,
|
|
32
31
|
schema: BaseSchema,
|
|
33
|
-
graphiql:
|
|
34
|
-
graphql_ide:
|
|
32
|
+
graphiql: bool | None = None,
|
|
33
|
+
graphql_ide: GraphQL_IDE | None = "graphiql",
|
|
35
34
|
allow_queries_via_get: bool = True,
|
|
36
35
|
multipart_uploads_enabled: bool = False,
|
|
37
36
|
) -> None:
|
|
@@ -52,7 +51,7 @@ class BaseGraphQLView:
|
|
|
52
51
|
|
|
53
52
|
def create_response(
|
|
54
53
|
self,
|
|
55
|
-
response_data:
|
|
54
|
+
response_data: GraphQLHTTPResponse | list[GraphQLHTTPResponse],
|
|
56
55
|
sub_response: Response,
|
|
57
56
|
) -> Response:
|
|
58
57
|
sub_response.set_data(self.encode_json(response_data)) # type: ignore
|
|
@@ -72,7 +71,7 @@ class GraphQLView(
|
|
|
72
71
|
def get_context(self, request: Request, response: Response) -> Context:
|
|
73
72
|
return {"request": request, "response": response} # type: ignore
|
|
74
73
|
|
|
75
|
-
def get_root_value(self, request: Request) ->
|
|
74
|
+
def get_root_value(self, request: Request) -> RootValue | None:
|
|
76
75
|
return None
|
|
77
76
|
|
|
78
77
|
def get_sub_response(self, request: Request) -> Response:
|
|
@@ -105,7 +104,7 @@ class AsyncGraphQLView(
|
|
|
105
104
|
async def get_context(self, request: Request, response: Response) -> Context:
|
|
106
105
|
return {"request": request, "response": response} # type: ignore
|
|
107
106
|
|
|
108
|
-
async def get_root_value(self, request: Request) ->
|
|
107
|
+
async def get_root_value(self, request: Request) -> RootValue | None:
|
|
109
108
|
return None
|
|
110
109
|
|
|
111
110
|
async def get_sub_response(self, request: Request) -> Response:
|
|
@@ -127,11 +126,11 @@ class AsyncGraphQLView(
|
|
|
127
126
|
def is_websocket_request(self, request: Request) -> TypeGuard[Request]:
|
|
128
127
|
return False
|
|
129
128
|
|
|
130
|
-
async def pick_websocket_subprotocol(self, request: Request) ->
|
|
129
|
+
async def pick_websocket_subprotocol(self, request: Request) -> str | None:
|
|
131
130
|
raise NotImplementedError
|
|
132
131
|
|
|
133
132
|
async def create_websocket_response(
|
|
134
|
-
self, request: Request, subprotocol:
|
|
133
|
+
self, request: Request, subprotocol: str | None
|
|
135
134
|
) -> Response:
|
|
136
135
|
raise NotImplementedError
|
|
137
136
|
|
strawberry/http/__init__.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import Any,
|
|
5
|
-
from typing_extensions import
|
|
4
|
+
from typing import Any, Literal
|
|
5
|
+
from typing_extensions import TypedDict
|
|
6
6
|
|
|
7
7
|
from strawberry.schema._graphql_core import (
|
|
8
8
|
GraphQLIncrementalExecutionResults,
|
|
@@ -11,14 +11,14 @@ from strawberry.schema._graphql_core import (
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class GraphQLHTTPResponse(TypedDict, total=False):
|
|
14
|
-
data:
|
|
15
|
-
errors:
|
|
16
|
-
extensions:
|
|
17
|
-
hasNext:
|
|
18
|
-
completed:
|
|
19
|
-
pending:
|
|
20
|
-
initial:
|
|
21
|
-
incremental:
|
|
14
|
+
data: dict[str, object] | None
|
|
15
|
+
errors: list[object] | None
|
|
16
|
+
extensions: dict[str, object] | None
|
|
17
|
+
hasNext: bool | None
|
|
18
|
+
completed: list[Any] | None
|
|
19
|
+
pending: list[Any] | None
|
|
20
|
+
initial: list[Any] | None
|
|
21
|
+
incremental: list[Any] | None
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def process_result(result: ResultType) -> GraphQLHTTPResponse:
|
|
@@ -39,10 +39,10 @@ def process_result(result: ResultType) -> GraphQLHTTPResponse:
|
|
|
39
39
|
class GraphQLRequestData:
|
|
40
40
|
# query is optional here as it can be added by an extensions
|
|
41
41
|
# (for example an extension for persisted queries)
|
|
42
|
-
query:
|
|
43
|
-
variables:
|
|
44
|
-
operation_name:
|
|
45
|
-
extensions:
|
|
42
|
+
query: str | None
|
|
43
|
+
variables: dict[str, Any] | None
|
|
44
|
+
operation_name: str | None
|
|
45
|
+
extensions: dict[str, Any] | None
|
|
46
46
|
protocol: Literal["http", "multipart-subscription"] = "http"
|
|
47
47
|
|
|
48
48
|
|
|
@@ -2,19 +2,16 @@ import abc
|
|
|
2
2
|
import asyncio
|
|
3
3
|
import contextlib
|
|
4
4
|
import json
|
|
5
|
-
from collections.abc import AsyncGenerator, Mapping
|
|
5
|
+
from collections.abc import AsyncGenerator, Callable, Mapping
|
|
6
6
|
from datetime import timedelta
|
|
7
7
|
from typing import (
|
|
8
8
|
Any,
|
|
9
|
-
Callable,
|
|
10
9
|
Generic,
|
|
11
10
|
Literal,
|
|
12
|
-
|
|
13
|
-
Union,
|
|
11
|
+
TypeGuard,
|
|
14
12
|
cast,
|
|
15
13
|
overload,
|
|
16
14
|
)
|
|
17
|
-
from typing_extensions import TypeGuard
|
|
18
15
|
|
|
19
16
|
from graphql import GraphQLError
|
|
20
17
|
from lia import AsyncHTTPRequestAdapter, HTTPException
|
|
@@ -87,9 +84,9 @@ class AsyncBaseHTTPView(
|
|
|
87
84
|
],
|
|
88
85
|
):
|
|
89
86
|
schema: BaseSchema
|
|
90
|
-
graphql_ide:
|
|
87
|
+
graphql_ide: GraphQL_IDE | None
|
|
91
88
|
keep_alive = False
|
|
92
|
-
keep_alive_interval:
|
|
89
|
+
keep_alive_interval: float | None = None
|
|
93
90
|
connection_init_wait_timeout: timedelta = timedelta(minutes=1)
|
|
94
91
|
request_adapter_class: Callable[[Request], AsyncHTTPRequestAdapter]
|
|
95
92
|
websocket_adapter_class: Callable[
|
|
@@ -117,19 +114,19 @@ class AsyncBaseHTTPView(
|
|
|
117
114
|
@abc.abstractmethod
|
|
118
115
|
async def get_context(
|
|
119
116
|
self,
|
|
120
|
-
request:
|
|
121
|
-
response:
|
|
117
|
+
request: Request | WebSocketRequest,
|
|
118
|
+
response: SubResponse | WebSocketResponse,
|
|
122
119
|
) -> Context: ...
|
|
123
120
|
|
|
124
121
|
@abc.abstractmethod
|
|
125
122
|
async def get_root_value(
|
|
126
|
-
self, request:
|
|
127
|
-
) ->
|
|
123
|
+
self, request: Request | WebSocketRequest
|
|
124
|
+
) -> RootValue | None: ...
|
|
128
125
|
|
|
129
126
|
@abc.abstractmethod
|
|
130
127
|
def create_response(
|
|
131
128
|
self,
|
|
132
|
-
response_data:
|
|
129
|
+
response_data: GraphQLHTTPResponse | list[GraphQLHTTPResponse],
|
|
133
130
|
sub_response: SubResponse,
|
|
134
131
|
) -> Response: ...
|
|
135
132
|
|
|
@@ -147,26 +144,26 @@ class AsyncBaseHTTPView(
|
|
|
147
144
|
|
|
148
145
|
@abc.abstractmethod
|
|
149
146
|
def is_websocket_request(
|
|
150
|
-
self, request:
|
|
147
|
+
self, request: Request | WebSocketRequest
|
|
151
148
|
) -> TypeGuard[WebSocketRequest]: ...
|
|
152
149
|
|
|
153
150
|
@abc.abstractmethod
|
|
154
151
|
async def pick_websocket_subprotocol(
|
|
155
152
|
self, request: WebSocketRequest
|
|
156
|
-
) ->
|
|
153
|
+
) -> str | None: ...
|
|
157
154
|
|
|
158
155
|
@abc.abstractmethod
|
|
159
156
|
async def create_websocket_response(
|
|
160
|
-
self, request: WebSocketRequest, subprotocol:
|
|
157
|
+
self, request: WebSocketRequest, subprotocol: str | None
|
|
161
158
|
) -> WebSocketResponse: ...
|
|
162
159
|
|
|
163
160
|
async def execute_operation(
|
|
164
161
|
self,
|
|
165
162
|
request: Request,
|
|
166
163
|
context: Context,
|
|
167
|
-
root_value:
|
|
164
|
+
root_value: RootValue | None,
|
|
168
165
|
sub_response: SubResponse,
|
|
169
|
-
) ->
|
|
166
|
+
) -> ExecutionResult | list[ExecutionResult] | SubscriptionExecutionResult:
|
|
170
167
|
request_adapter = self.request_adapter_class(request)
|
|
171
168
|
|
|
172
169
|
try:
|
|
@@ -223,7 +220,7 @@ class AsyncBaseHTTPView(
|
|
|
223
220
|
request_adapter: AsyncHTTPRequestAdapter,
|
|
224
221
|
sub_response: SubResponse,
|
|
225
222
|
context: Context,
|
|
226
|
-
root_value:
|
|
223
|
+
root_value: RootValue | None,
|
|
227
224
|
request_data: GraphQLRequestData,
|
|
228
225
|
) -> ExecutionResult:
|
|
229
226
|
allowed_operation_types = OperationType.from_http(request_adapter.method)
|
|
@@ -283,7 +280,7 @@ class AsyncBaseHTTPView(
|
|
|
283
280
|
self,
|
|
284
281
|
request: Request,
|
|
285
282
|
context: Context = UNSET,
|
|
286
|
-
root_value:
|
|
283
|
+
root_value: RootValue | None = UNSET,
|
|
287
284
|
) -> Response: ...
|
|
288
285
|
|
|
289
286
|
@overload
|
|
@@ -291,15 +288,15 @@ class AsyncBaseHTTPView(
|
|
|
291
288
|
self,
|
|
292
289
|
request: WebSocketRequest,
|
|
293
290
|
context: Context = UNSET,
|
|
294
|
-
root_value:
|
|
291
|
+
root_value: RootValue | None = UNSET,
|
|
295
292
|
) -> WebSocketResponse: ...
|
|
296
293
|
|
|
297
294
|
async def run(
|
|
298
295
|
self,
|
|
299
|
-
request:
|
|
296
|
+
request: Request | WebSocketRequest,
|
|
300
297
|
context: Context = UNSET,
|
|
301
|
-
root_value:
|
|
302
|
-
) ->
|
|
298
|
+
root_value: RootValue | None = UNSET,
|
|
299
|
+
) -> Response | WebSocketResponse:
|
|
303
300
|
root_value = (
|
|
304
301
|
await self.get_root_value(request) if root_value is UNSET else root_value
|
|
305
302
|
)
|
|
@@ -373,7 +370,6 @@ class AsyncBaseHTTPView(
|
|
|
373
370
|
stream,
|
|
374
371
|
sub_response,
|
|
375
372
|
headers={
|
|
376
|
-
"Transfer-Encoding": "chunked",
|
|
377
373
|
"Content-Type": "multipart/mixed;boundary=graphql;subscriptionSpec=1.0,application/json",
|
|
378
374
|
},
|
|
379
375
|
)
|
|
@@ -442,12 +438,11 @@ class AsyncBaseHTTPView(
|
|
|
442
438
|
stream,
|
|
443
439
|
sub_response,
|
|
444
440
|
headers={
|
|
445
|
-
"Transfer-Encoding": "chunked",
|
|
446
441
|
"Content-Type": 'multipart/mixed; boundary="-"',
|
|
447
442
|
},
|
|
448
443
|
)
|
|
449
444
|
|
|
450
|
-
response_data:
|
|
445
|
+
response_data: GraphQLHTTPResponse | list[GraphQLHTTPResponse]
|
|
451
446
|
|
|
452
447
|
if isinstance(result, list):
|
|
453
448
|
response_data = []
|
|
@@ -618,7 +613,7 @@ class AsyncBaseHTTPView(
|
|
|
618
613
|
|
|
619
614
|
async def parse_http_body(
|
|
620
615
|
self, request: AsyncHTTPRequestAdapter
|
|
621
|
-
) ->
|
|
616
|
+
) -> GraphQLRequestData | list[GraphQLRequestData]:
|
|
622
617
|
headers = {key.lower(): value for key, value in request.headers.items()}
|
|
623
618
|
content_type, _ = parse_content_type(request.content_type or "")
|
|
624
619
|
accept = headers.get("accept", "")
|
|
@@ -687,7 +682,7 @@ class AsyncBaseHTTPView(
|
|
|
687
682
|
|
|
688
683
|
async def on_ws_connect(
|
|
689
684
|
self, context: Context
|
|
690
|
-
) ->
|
|
685
|
+
) -> UnsetType | None | dict[str, object]:
|
|
691
686
|
return UNSET
|
|
692
687
|
|
|
693
688
|
|
strawberry/http/base.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from collections.abc import Mapping
|
|
3
|
-
from typing import Any, Generic
|
|
3
|
+
from typing import Any, Generic
|
|
4
4
|
from typing_extensions import Protocol
|
|
5
5
|
|
|
6
6
|
from lia import HTTPException
|
|
@@ -15,7 +15,7 @@ from .typevars import Request
|
|
|
15
15
|
|
|
16
16
|
class BaseRequestProtocol(Protocol):
|
|
17
17
|
@property
|
|
18
|
-
def query_params(self) -> Mapping[str,
|
|
18
|
+
def query_params(self) -> Mapping[str, str | list[str] | None]: ...
|
|
19
19
|
|
|
20
20
|
@property
|
|
21
21
|
def method(self) -> HTTPMethod: ...
|
|
@@ -25,7 +25,7 @@ class BaseRequestProtocol(Protocol):
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
class BaseView(Generic[Request]):
|
|
28
|
-
graphql_ide:
|
|
28
|
+
graphql_ide: GraphQL_IDE | None
|
|
29
29
|
multipart_uploads_enabled: bool = False
|
|
30
30
|
schema: BaseSchema
|
|
31
31
|
|
|
@@ -42,13 +42,13 @@ class BaseView(Generic[Request]):
|
|
|
42
42
|
def is_request_allowed(self, request: BaseRequestProtocol) -> bool:
|
|
43
43
|
return request.method in ("GET", "POST")
|
|
44
44
|
|
|
45
|
-
def parse_json(self, data:
|
|
45
|
+
def parse_json(self, data: str | bytes) -> Any:
|
|
46
46
|
try:
|
|
47
47
|
return self.decode_json(data)
|
|
48
48
|
except json.JSONDecodeError as e:
|
|
49
49
|
raise HTTPException(400, "Unable to parse request body as JSON") from e
|
|
50
50
|
|
|
51
|
-
def decode_json(self, data:
|
|
51
|
+
def decode_json(self, data: str | bytes) -> object:
|
|
52
52
|
return json.loads(data)
|
|
53
53
|
|
|
54
54
|
def encode_json(self, data: object) -> str:
|
|
@@ -78,13 +78,12 @@ class BaseView(Generic[Request]):
|
|
|
78
78
|
def _is_multipart_subscriptions(
|
|
79
79
|
self, content_type: str, params: dict[str, str]
|
|
80
80
|
) -> bool:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return params.get("subscriptionspec", "").startswith("1.0")
|
|
81
|
+
subscription_spec = params.get("subscriptionspec", "").strip("'\"")
|
|
82
|
+
return (
|
|
83
|
+
content_type == "multipart/mixed"
|
|
84
|
+
and ("boundary" not in params or params["boundary"] == "graphql")
|
|
85
|
+
and subscription_spec.startswith("1.0")
|
|
86
|
+
)
|
|
88
87
|
|
|
89
88
|
def _validate_batch_request(
|
|
90
89
|
self, request_data: list[GraphQLRequestData], protocol: str
|
strawberry/http/ides.py
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import pathlib
|
|
2
|
-
from typing import
|
|
3
|
-
from typing_extensions import Literal
|
|
2
|
+
from typing import Literal
|
|
4
3
|
|
|
5
4
|
GraphQL_IDE = Literal["graphiql", "apollo-sandbox", "pathfinder"]
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
def get_graphql_ide_html(
|
|
9
|
-
graphql_ide:
|
|
8
|
+
graphql_ide: GraphQL_IDE | None = "graphiql",
|
|
10
9
|
) -> str:
|
|
11
10
|
here = pathlib.Path(__file__).parents[1]
|
|
12
11
|
|