strawberry-graphql 0.235.1.dev1719337273__py3-none-any.whl → 0.236.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.
- strawberry/__init__.py +17 -11
- strawberry/aiohttp/handlers/graphql_transport_ws_handler.py +3 -0
- strawberry/aiohttp/handlers/graphql_ws_handler.py +3 -0
- strawberry/aiohttp/test/client.py +3 -0
- strawberry/aiohttp/views.py +3 -0
- strawberry/annotation.py +19 -22
- strawberry/asgi/__init__.py +3 -3
- strawberry/asgi/handlers/graphql_transport_ws_handler.py +3 -0
- strawberry/asgi/handlers/graphql_ws_handler.py +3 -0
- strawberry/asgi/test/client.py +3 -0
- strawberry/chalice/views.py +12 -3
- strawberry/channels/handlers/__init__.py +0 -0
- strawberry/channels/handlers/base.py +5 -5
- strawberry/channels/handlers/graphql_transport_ws_handler.py +3 -0
- strawberry/channels/handlers/graphql_ws_handler.py +3 -0
- strawberry/channels/handlers/http_handler.py +5 -2
- strawberry/channels/handlers/ws_handler.py +4 -1
- strawberry/channels/router.py +9 -5
- strawberry/channels/testing.py +11 -4
- strawberry/cli/commands/upgrade/__init__.py +13 -5
- strawberry/cli/commands/upgrade/_fake_progress.py +2 -1
- strawberry/cli/commands/upgrade/_run_codemod.py +18 -1
- strawberry/codegen/exceptions.py +8 -0
- strawberry/codegen/query_codegen.py +16 -7
- strawberry/codegen/types.py +32 -1
- strawberry/codemods/update_imports.py +136 -0
- strawberry/dataloader.py +13 -0
- strawberry/directive.py +52 -4
- strawberry/django/context.py +4 -1
- strawberry/django/test/client.py +3 -0
- strawberry/django/views.py +3 -0
- strawberry/exceptions/__init__.py +5 -5
- strawberry/exceptions/duplicated_type_name.py +1 -1
- strawberry/exceptions/invalid_argument_type.py +3 -3
- strawberry/exceptions/invalid_union_type.py +5 -6
- strawberry/exceptions/missing_arguments_annotations.py +1 -1
- strawberry/exceptions/missing_dependencies.py +10 -2
- strawberry/exceptions/missing_return_annotation.py +1 -1
- strawberry/exceptions/permission_fail_silently_requires_optional.py +3 -3
- strawberry/exceptions/scalar_already_registered.py +1 -1
- strawberry/exceptions/unresolved_field_type.py +2 -2
- strawberry/exceptions/utils/source_finder.py +5 -2
- strawberry/experimental/pydantic/conversion.py +5 -5
- strawberry/experimental/pydantic/conversion_types.py +4 -2
- strawberry/experimental/pydantic/error_type.py +2 -2
- strawberry/experimental/pydantic/fields.py +2 -2
- strawberry/experimental/pydantic/object_type.py +11 -7
- strawberry/experimental/pydantic/utils.py +4 -5
- strawberry/ext/dataclasses/dataclasses.py +2 -1
- strawberry/ext/mypy_plugin.py +10 -8
- strawberry/extensions/add_validation_rules.py +27 -23
- strawberry/extensions/base_extension.py +6 -4
- strawberry/extensions/directives.py +4 -1
- strawberry/extensions/disable_validation.py +15 -12
- strawberry/extensions/field_extension.py +11 -5
- strawberry/extensions/mask_errors.py +3 -0
- strawberry/extensions/max_aliases.py +21 -19
- strawberry/extensions/max_tokens.py +14 -16
- strawberry/extensions/parser_cache.py +22 -19
- strawberry/extensions/pyinstrument.py +4 -8
- strawberry/extensions/query_depth_limiter.py +22 -23
- strawberry/extensions/runner.py +3 -0
- strawberry/extensions/tracing/apollo.py +3 -0
- strawberry/extensions/tracing/datadog.py +7 -2
- strawberry/extensions/tracing/opentelemetry.py +3 -0
- strawberry/extensions/tracing/sentry.py +3 -0
- strawberry/extensions/tracing/utils.py +3 -0
- strawberry/extensions/utils.py +3 -0
- strawberry/extensions/validation_cache.py +23 -20
- strawberry/fastapi/context.py +3 -0
- strawberry/fastapi/handlers/graphql_transport_ws_handler.py +3 -0
- strawberry/fastapi/handlers/graphql_ws_handler.py +3 -0
- strawberry/fastapi/router.py +3 -0
- strawberry/federation/argument.py +4 -1
- strawberry/federation/enum.py +5 -3
- strawberry/federation/field.py +6 -3
- strawberry/federation/mutation.py +2 -0
- strawberry/federation/object_type.py +7 -4
- strawberry/federation/scalar.py +43 -20
- strawberry/federation/schema.py +12 -9
- strawberry/federation/schema_directive.py +2 -2
- strawberry/federation/schema_directives.py +19 -1
- strawberry/federation/types.py +5 -2
- strawberry/federation/union.py +27 -8
- strawberry/field_extensions/input_mutation.py +5 -2
- strawberry/file_uploads/scalars.py +3 -1
- strawberry/file_uploads/utils.py +3 -0
- strawberry/flask/views.py +8 -2
- strawberry/http/__init__.py +9 -0
- strawberry/http/async_base_view.py +4 -3
- strawberry/http/base.py +5 -7
- strawberry/http/exceptions.py +3 -0
- strawberry/http/ides.py +3 -0
- strawberry/http/sync_base_view.py +4 -3
- strawberry/http/temporal_response.py +3 -0
- strawberry/http/types.py +5 -2
- strawberry/http/typevars.py +3 -0
- strawberry/litestar/controller.py +6 -0
- strawberry/litestar/handlers/__init__.py +0 -0
- strawberry/litestar/handlers/graphql_transport_ws_handler.py +3 -0
- strawberry/litestar/handlers/graphql_ws_handler.py +3 -0
- strawberry/parent.py +27 -21
- strawberry/permission.py +70 -27
- strawberry/printer/ast_from_value.py +4 -1
- strawberry/printer/printer.py +8 -5
- strawberry/quart/views.py +3 -0
- strawberry/relay/exceptions.py +7 -0
- strawberry/relay/fields.py +70 -45
- strawberry/relay/types.py +78 -78
- strawberry/relay/utils.py +10 -1
- strawberry/resolvers.py +3 -0
- strawberry/sanic/context.py +3 -0
- strawberry/sanic/utils.py +10 -8
- strawberry/sanic/views.py +5 -9
- strawberry/scalars.py +6 -2
- strawberry/schema/base.py +7 -4
- strawberry/schema/compat.py +12 -2
- strawberry/schema/config.py +3 -0
- strawberry/schema/exceptions.py +3 -0
- strawberry/schema/execute.py +3 -0
- strawberry/schema/name_converter.py +12 -9
- strawberry/schema/schema.py +46 -9
- strawberry/schema/schema_converter.py +16 -14
- strawberry/schema/types/base_scalars.py +3 -1
- strawberry/schema/types/concrete_type.py +4 -4
- strawberry/schema/types/scalar.py +8 -1
- strawberry/schema/validation_rules/one_of.py +3 -0
- strawberry/schema_codegen/__init__.py +3 -0
- strawberry/schema_directive.py +2 -2
- strawberry/starlite/controller.py +3 -0
- strawberry/starlite/handlers/__init__.py +0 -0
- strawberry/starlite/handlers/graphql_transport_ws_handler.py +3 -0
- strawberry/starlite/handlers/graphql_ws_handler.py +3 -0
- strawberry/subscriptions/__init__.py +6 -0
- strawberry/subscriptions/protocols/graphql_transport_ws/__init__.py +5 -0
- strawberry/subscriptions/protocols/graphql_transport_ws/handlers.py +12 -17
- strawberry/subscriptions/protocols/graphql_transport_ws/types.py +21 -25
- strawberry/subscriptions/protocols/graphql_ws/__init__.py +14 -0
- strawberry/subscriptions/protocols/graphql_ws/handlers.py +8 -5
- strawberry/subscriptions/protocols/graphql_ws/types.py +11 -0
- strawberry/test/client.py +44 -29
- strawberry/tools/create_type.py +27 -8
- strawberry/tools/merge_types.py +5 -3
- strawberry/types/__init__.py +8 -1
- strawberry/{arguments.py → types/arguments.py} +44 -13
- strawberry/{auto.py → types/auto.py} +21 -3
- strawberry/types/{types.py → base.py} +234 -10
- strawberry/{enum.py → types/enum.py} +69 -9
- strawberry/types/execution.py +3 -0
- strawberry/{field.py → types/field.py} +46 -23
- strawberry/types/fields/resolver.py +2 -2
- strawberry/types/graphql.py +3 -0
- strawberry/types/info.py +50 -7
- strawberry/{lazy_type.py → types/lazy_type.py} +50 -0
- strawberry/types/mutation.py +351 -0
- strawberry/types/nodes.py +4 -2
- strawberry/{object_type.py → types/object_type.py} +108 -29
- strawberry/{private.py → types/private.py} +13 -6
- strawberry/{custom_scalar.py → types/scalar.py} +39 -23
- strawberry/types/type_resolver.py +21 -16
- strawberry/{union.py → types/union.py} +24 -9
- strawberry/{unset.py → types/unset.py} +20 -0
- strawberry/utils/aio.py +8 -0
- strawberry/utils/await_maybe.py +3 -0
- strawberry/utils/dataclasses.py +3 -0
- strawberry/utils/debug.py +5 -2
- strawberry/utils/deprecations.py +3 -0
- strawberry/utils/graphql_lexer.py +3 -0
- strawberry/utils/importer.py +3 -0
- strawberry/utils/inspect.py +39 -30
- strawberry/utils/logging.py +3 -0
- strawberry/utils/operation.py +3 -0
- strawberry/utils/str_converters.py +3 -0
- strawberry/utils/typing.py +33 -16
- {strawberry_graphql-0.235.1.dev1719337273.dist-info → strawberry_graphql-0.236.0.dist-info}/METADATA +1 -1
- strawberry_graphql-0.236.0.dist-info/RECORD +255 -0
- strawberry/mutation.py +0 -8
- strawberry/type.py +0 -232
- strawberry_graphql-0.235.1.dev1719337273.dist-info/RECORD +0 -252
- {strawberry_graphql-0.235.1.dev1719337273.dist-info → strawberry_graphql-0.236.0.dist-info}/LICENSE +0 -0
- {strawberry_graphql-0.235.1.dev1719337273.dist-info → strawberry_graphql-0.236.0.dist-info}/WHEEL +0 -0
- {strawberry_graphql-0.235.1.dev1719337273.dist-info → strawberry_graphql-0.236.0.dist-info}/entry_points.txt +0 -0
strawberry/parent.py
CHANGED
@@ -8,31 +8,37 @@ class StrawberryParent: ...
|
|
8
8
|
T = TypeVar("T")
|
9
9
|
|
10
10
|
Parent = Annotated[T, StrawberryParent()]
|
11
|
-
|
11
|
+
"""Represents a parameter holding the parent resolver's value.
|
12
12
|
|
13
13
|
This can be used when defining a resolver on a type when the parent isn't expected
|
14
14
|
to return the type itself.
|
15
15
|
|
16
16
|
Example:
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
18
|
+
```python
|
19
|
+
import strawberry
|
20
|
+
from dataclasses import dataclass
|
21
|
+
|
22
|
+
|
23
|
+
@dataclass
|
24
|
+
class UserRow:
|
25
|
+
id_: str
|
26
|
+
|
27
|
+
|
28
|
+
@strawberry.type
|
29
|
+
class User:
|
30
|
+
@strawberry.field
|
31
|
+
@staticmethod
|
32
|
+
async def name(parent: strawberry.Parent[UserRow]) -> str:
|
33
|
+
return f"User Number {parent.id_}"
|
34
|
+
|
35
|
+
|
36
|
+
@strawberry.type
|
37
|
+
class Query:
|
38
|
+
@strawberry.field
|
39
|
+
def user(self) -> User:
|
40
|
+
return UserRow(id_="1234")
|
41
|
+
```
|
38
42
|
"""
|
43
|
+
|
44
|
+
__all__ = ["Parent"]
|
strawberry/permission.py
CHANGED
@@ -21,7 +21,7 @@ from strawberry.exceptions.permission_fail_silently_requires_optional import (
|
|
21
21
|
)
|
22
22
|
from strawberry.extensions import FieldExtension
|
23
23
|
from strawberry.schema_directive import Location, StrawberrySchemaDirective
|
24
|
-
from strawberry.
|
24
|
+
from strawberry.types.base import StrawberryList, StrawberryOptional
|
25
25
|
from strawberry.utils.await_maybe import await_maybe
|
26
26
|
|
27
27
|
if TYPE_CHECKING:
|
@@ -31,13 +31,25 @@ if TYPE_CHECKING:
|
|
31
31
|
AsyncExtensionResolver,
|
32
32
|
SyncExtensionResolver,
|
33
33
|
)
|
34
|
-
from strawberry.field import StrawberryField
|
35
34
|
from strawberry.types import Info
|
35
|
+
from strawberry.types.field import StrawberryField
|
36
36
|
|
37
37
|
|
38
38
|
class BasePermission(abc.ABC):
|
39
|
-
"""
|
40
|
-
|
39
|
+
"""Base class for permissions. All permissions should inherit from this class.
|
40
|
+
|
41
|
+
Example:
|
42
|
+
|
43
|
+
```python
|
44
|
+
from strawberry.permission import BasePermission
|
45
|
+
|
46
|
+
|
47
|
+
class IsAuthenticated(BasePermission):
|
48
|
+
message = "User is not authenticated"
|
49
|
+
|
50
|
+
def has_permission(self, source, info, **kwargs):
|
51
|
+
return info.context["user"].is_authenticated
|
52
|
+
```
|
41
53
|
"""
|
42
54
|
|
43
55
|
message: Optional[str] = None
|
@@ -52,16 +64,39 @@ class BasePermission(abc.ABC):
|
|
52
64
|
def has_permission(
|
53
65
|
self, source: Any, info: Info, **kwargs: Any
|
54
66
|
) -> Union[bool, Awaitable[bool]]:
|
67
|
+
"""Check if the permission should be accepted.
|
68
|
+
|
69
|
+
This method should be overridden by the subclasses.
|
70
|
+
"""
|
55
71
|
raise NotImplementedError(
|
56
72
|
"Permission classes should override has_permission method"
|
57
73
|
)
|
58
74
|
|
59
75
|
def on_unauthorized(self) -> None:
|
60
|
-
"""
|
61
|
-
|
62
|
-
This can be overridden to customize the
|
63
|
-
|
76
|
+
"""Default error raising for permissions.
|
77
|
+
|
78
|
+
This method can be overridden to customize the error raised when the permission is not granted.
|
79
|
+
|
80
|
+
Example:
|
81
|
+
|
82
|
+
```python
|
83
|
+
from strawberry.permission import BasePermission
|
84
|
+
|
85
|
+
|
86
|
+
class CustomPermissionError(PermissionError):
|
87
|
+
pass
|
88
|
+
|
64
89
|
|
90
|
+
class IsAuthenticated(BasePermission):
|
91
|
+
message = "User is not authenticated"
|
92
|
+
|
93
|
+
def has_permission(self, source, info, **kwargs):
|
94
|
+
return info.context["user"].is_authenticated
|
95
|
+
|
96
|
+
def on_unauthorized(self) -> None:
|
97
|
+
raise CustomPermissionError(self.message)
|
98
|
+
```
|
99
|
+
"""
|
65
100
|
# Instantiate error class
|
66
101
|
error = self.error_class(self.message or "")
|
67
102
|
|
@@ -91,17 +126,14 @@ class BasePermission(abc.ABC):
|
|
91
126
|
|
92
127
|
|
93
128
|
class PermissionExtension(FieldExtension):
|
94
|
-
"""
|
95
|
-
Handles permissions for a field
|
96
|
-
Instantiate this as a field extension with all of the permissions you want to apply
|
129
|
+
"""Handles permissions for a field.
|
97
130
|
|
98
|
-
|
99
|
-
instead of raising an exception. This is only valid for optional or list fields.
|
131
|
+
Instantiate this as a field extension with all of the permissions you want to apply.
|
100
132
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
This is deprecated
|
133
|
+
Note:
|
134
|
+
Currently, this is automatically added to the field, when using field.permission_classes
|
135
|
+
|
136
|
+
This is deprecated behaviour, please manually add the extension to field.extensions
|
105
137
|
"""
|
106
138
|
|
107
139
|
def __init__(
|
@@ -110,16 +142,21 @@ class PermissionExtension(FieldExtension):
|
|
110
142
|
use_directives: bool = True,
|
111
143
|
fail_silently: bool = False,
|
112
144
|
) -> None:
|
145
|
+
"""Initialize the permission extension.
|
146
|
+
|
147
|
+
Args:
|
148
|
+
permissions: List of permissions to apply.
|
149
|
+
fail_silently: If True, return None or [] instead of raising an exception.
|
150
|
+
This is only valid for optional or list fields.
|
151
|
+
use_directives: If True, add schema directives to the field.
|
152
|
+
"""
|
113
153
|
self.permissions = permissions
|
114
154
|
self.fail_silently = fail_silently
|
115
155
|
self.return_empty_list = False
|
116
156
|
self.use_directives = use_directives
|
117
157
|
|
118
158
|
def apply(self, field: StrawberryField) -> None:
|
119
|
-
"""
|
120
|
-
Applies all of the permission directives to the schema
|
121
|
-
and sets up silent permissions
|
122
|
-
"""
|
159
|
+
"""Applies all of the permission directives to the schema and sets up silent permissions."""
|
123
160
|
if self.use_directives:
|
124
161
|
field.directives.extend(
|
125
162
|
p.schema_directive for p in self.permissions if p.schema_directive
|
@@ -147,10 +184,7 @@ class PermissionExtension(FieldExtension):
|
|
147
184
|
info: Info,
|
148
185
|
**kwargs: Dict[str, Any],
|
149
186
|
) -> Any:
|
150
|
-
"""
|
151
|
-
Checks if the permission should be accepted and
|
152
|
-
raises an exception if not
|
153
|
-
"""
|
187
|
+
"""Checks if the permission should be accepted and raises an exception if not."""
|
154
188
|
for permission in self.permissions:
|
155
189
|
if not permission.has_permission(source, info, **kwargs):
|
156
190
|
return self._on_unauthorized(permission)
|
@@ -177,11 +211,20 @@ class PermissionExtension(FieldExtension):
|
|
177
211
|
|
178
212
|
@cached_property
|
179
213
|
def supports_sync(self) -> bool:
|
180
|
-
"""
|
181
|
-
|
214
|
+
"""Whether this extension can be resolved synchronously or not.
|
215
|
+
|
216
|
+
The Permission extension always supports async checking using await_maybe,
|
217
|
+
but only supports sync checking if there are no async permissions.
|
218
|
+
"""
|
182
219
|
async_permissions = [
|
183
220
|
True
|
184
221
|
for permission in self.permissions
|
185
222
|
if iscoroutinefunction(permission.has_permission)
|
186
223
|
]
|
187
224
|
return len(async_permissions) == 0
|
225
|
+
|
226
|
+
|
227
|
+
__all__ = [
|
228
|
+
"BasePermission",
|
229
|
+
"PermissionExtension",
|
230
|
+
]
|
@@ -27,7 +27,7 @@ from graphql.type import (
|
|
27
27
|
)
|
28
28
|
|
29
29
|
import strawberry
|
30
|
-
from strawberry.
|
30
|
+
from strawberry.types.base import has_object_definition
|
31
31
|
|
32
32
|
if TYPE_CHECKING:
|
33
33
|
from graphql.language import ValueNode
|
@@ -149,3 +149,6 @@ def ast_from_value(value: Any, type_: GraphQLInputType) -> Optional[ValueNode]:
|
|
149
149
|
|
150
150
|
# Not reachable. All possible input types have been considered.
|
151
151
|
raise TypeError(f"Unexpected input type: {inspect(type_)}.") # pragma: no cover
|
152
|
+
|
153
|
+
|
154
|
+
__all__ = ["ast_from_value"]
|
strawberry/printer/printer.py
CHANGED
@@ -37,11 +37,11 @@ from graphql.utilities.print_schema import (
|
|
37
37
|
)
|
38
38
|
from graphql.utilities.print_schema import print_type as original_print_type
|
39
39
|
|
40
|
-
from strawberry.custom_scalar import ScalarWrapper
|
41
|
-
from strawberry.enum import EnumDefinition
|
42
40
|
from strawberry.schema_directive import Location, StrawberrySchemaDirective
|
43
|
-
from strawberry.
|
44
|
-
from strawberry.
|
41
|
+
from strawberry.types.base import StrawberryContainer, has_object_definition
|
42
|
+
from strawberry.types.enum import EnumDefinition
|
43
|
+
from strawberry.types.scalar import ScalarWrapper
|
44
|
+
from strawberry.types.unset import UNSET
|
45
45
|
|
46
46
|
from .ast_from_value import ast_from_value
|
47
47
|
|
@@ -55,8 +55,8 @@ if TYPE_CHECKING:
|
|
55
55
|
)
|
56
56
|
from graphql.type.directives import GraphQLDirective
|
57
57
|
|
58
|
-
from strawberry.field import StrawberryField
|
59
58
|
from strawberry.schema import BaseSchema
|
59
|
+
from strawberry.types.field import StrawberryField
|
60
60
|
|
61
61
|
|
62
62
|
_T = TypeVar("_T")
|
@@ -588,3 +588,6 @@ def print_schema(schema: BaseSchema) -> str:
|
|
588
588
|
),
|
589
589
|
)
|
590
590
|
)
|
591
|
+
|
592
|
+
|
593
|
+
__all__ = ["print_schema"]
|
strawberry/quart/views.py
CHANGED
strawberry/relay/exceptions.py
CHANGED
@@ -102,3 +102,10 @@ class RelayWrongResolverAnnotationError(StrawberryException):
|
|
102
102
|
|
103
103
|
source_finder = SourceFinder()
|
104
104
|
return source_finder.find_function_from_object(cast(Callable, self.function))
|
105
|
+
|
106
|
+
|
107
|
+
__all__ = [
|
108
|
+
"NodeIDAnnotationError",
|
109
|
+
"RelayWrongAnnotationError",
|
110
|
+
"RelayWrongResolverAnnotationError",
|
111
|
+
]
|
strawberry/relay/fields.py
CHANGED
@@ -29,20 +29,20 @@ from typing import (
|
|
29
29
|
from typing_extensions import Annotated, get_origin
|
30
30
|
|
31
31
|
from strawberry.annotation import StrawberryAnnotation
|
32
|
-
from strawberry.arguments import StrawberryArgument, argument
|
33
32
|
from strawberry.extensions.field_extension import (
|
34
33
|
AsyncExtensionResolver,
|
35
34
|
FieldExtension,
|
36
35
|
SyncExtensionResolver,
|
37
36
|
)
|
38
|
-
from strawberry.field import _RESOLVER_TYPE, StrawberryField, field
|
39
|
-
from strawberry.lazy_type import LazyType
|
40
37
|
from strawberry.relay.exceptions import (
|
41
38
|
RelayWrongAnnotationError,
|
42
39
|
RelayWrongResolverAnnotationError,
|
43
40
|
)
|
44
|
-
from strawberry.
|
41
|
+
from strawberry.types.arguments import StrawberryArgument, argument
|
42
|
+
from strawberry.types.base import StrawberryList, StrawberryOptional
|
43
|
+
from strawberry.types.field import _RESOLVER_TYPE, StrawberryField, field
|
45
44
|
from strawberry.types.fields.resolver import StrawberryResolver
|
45
|
+
from strawberry.types.lazy_type import LazyType
|
46
46
|
from strawberry.utils.aio import asyncgen_to_list
|
47
47
|
from strawberry.utils.typing import eval_type, is_generic_alias
|
48
48
|
|
@@ -62,7 +62,7 @@ class NodeExtension(FieldExtension):
|
|
62
62
|
if isinstance(field.type, StrawberryList):
|
63
63
|
resolver = self.get_node_list_resolver(field)
|
64
64
|
else:
|
65
|
-
resolver = self.get_node_resolver(field)
|
65
|
+
resolver = self.get_node_resolver(field) # type: ignore
|
66
66
|
|
67
67
|
field.base_resolver = StrawberryResolver(resolver, type_override=field.type)
|
68
68
|
|
@@ -81,7 +81,9 @@ class NodeExtension(FieldExtension):
|
|
81
81
|
# async extensions.
|
82
82
|
return await retval if inspect.isawaitable(retval) else retval
|
83
83
|
|
84
|
-
def get_node_resolver(
|
84
|
+
def get_node_resolver(
|
85
|
+
self, field: StrawberryField
|
86
|
+
) -> Callable[[Info, GlobalID], Union[Node, None, Awaitable[Union[Node, None]]]]:
|
85
87
|
type_ = field.type
|
86
88
|
is_optional = isinstance(type_, StrawberryOptional)
|
87
89
|
|
@@ -97,7 +99,9 @@ class NodeExtension(FieldExtension):
|
|
97
99
|
|
98
100
|
return resolver
|
99
101
|
|
100
|
-
def get_node_list_resolver(
|
102
|
+
def get_node_list_resolver(
|
103
|
+
self, field: StrawberryField
|
104
|
+
) -> Callable[[Info, List[GlobalID]], Union[List[Node], Awaitable[List[Node]]]]:
|
101
105
|
type_ = field.type
|
102
106
|
assert isinstance(type_, StrawberryList)
|
103
107
|
is_optional = isinstance(type_.of_type, StrawberryOptional)
|
@@ -390,48 +394,66 @@ def connection(
|
|
390
394
|
case for this is to provide a filtered iterable of nodes by using some custom
|
391
395
|
filter arguments.
|
392
396
|
|
397
|
+
Args:
|
398
|
+
graphql_type: The type of the nodes in the connection. This is used to
|
399
|
+
determine the type of the edges and the node field in the connection.
|
400
|
+
resolver: The resolver for the connection. This is expected to return an
|
401
|
+
iterable of the expected node type.
|
402
|
+
name: The GraphQL name of the field.
|
403
|
+
is_subscription: Whether the field is a subscription.
|
404
|
+
description: The GraphQL description of the field.
|
405
|
+
permission_classes: The permission classes to apply to the field.
|
406
|
+
deprecation_reason: The deprecation reason of the field.
|
407
|
+
default: The default value of the field.
|
408
|
+
default_factory: The default factory of the field.
|
409
|
+
metadata: The metadata of the field.
|
410
|
+
directives: The directives to apply to the field.
|
411
|
+
extensions: The extensions to apply to the field.
|
412
|
+
init: Used only for type checking purposes.
|
413
|
+
|
393
414
|
Examples:
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
...
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
415
|
+
Annotating something like this:
|
416
|
+
|
417
|
+
```python
|
418
|
+
@strawberry.type
|
419
|
+
class X:
|
420
|
+
some_node: relay.Connection[SomeType] = relay.connection(
|
421
|
+
resolver=get_some_nodes,
|
422
|
+
description="ABC",
|
423
|
+
)
|
424
|
+
|
425
|
+
@relay.connection(relay.Connection[SomeType], description="ABC")
|
426
|
+
def get_some_nodes(self, age: int) -> Iterable[SomeType]: ...
|
427
|
+
```
|
428
|
+
|
429
|
+
Will produce a query like this:
|
430
|
+
|
431
|
+
```graphql
|
432
|
+
query {
|
433
|
+
someNode (
|
434
|
+
before: String
|
435
|
+
after: String
|
436
|
+
first: String
|
437
|
+
after: String
|
438
|
+
age: Int
|
439
|
+
) {
|
440
|
+
totalCount
|
441
|
+
pageInfo {
|
442
|
+
hasNextPage
|
443
|
+
hasPreviousPage
|
444
|
+
startCursor
|
445
|
+
endCursor
|
446
|
+
}
|
447
|
+
edges {
|
448
|
+
cursor
|
449
|
+
node {
|
450
|
+
id
|
451
|
+
...
|
431
452
|
}
|
432
453
|
}
|
433
454
|
}
|
434
|
-
|
455
|
+
}
|
456
|
+
```
|
435
457
|
|
436
458
|
.. _Relay connections:
|
437
459
|
https://relay.dev/graphql/connections.htm
|
@@ -454,3 +476,6 @@ def connection(
|
|
454
476
|
if resolver is not None:
|
455
477
|
f = f(resolver)
|
456
478
|
return f
|
479
|
+
|
480
|
+
|
481
|
+
__all__ = ["node", "connection"]
|