strawberry-graphql 0.227.0__py3-none-any.whl → 0.227.0.dev1713475585__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/ext/mypy_plugin.py +1 -24
- strawberry/federation/object_type.py +9 -0
- strawberry/object_type.py +8 -0
- strawberry/schema/execute.py +5 -0
- strawberry/schema/schema.py +11 -0
- strawberry/schema/schema_converter.py +33 -0
- strawberry/schema/validation_rules/__init__.py +0 -0
- strawberry/schema/validation_rules/one_of.py +80 -0
- strawberry/schema_codegen/__init__.py +87 -96
- strawberry/schema_directives.py +9 -0
- strawberry/type.py +4 -0
- strawberry/types/types.py +11 -0
- {strawberry_graphql-0.227.0.dist-info → strawberry_graphql-0.227.0.dev1713475585.dist-info}/METADATA +1 -2
- {strawberry_graphql-0.227.0.dist-info → strawberry_graphql-0.227.0.dev1713475585.dist-info}/RECORD +17 -14
- {strawberry_graphql-0.227.0.dist-info → strawberry_graphql-0.227.0.dev1713475585.dist-info}/LICENSE +0 -0
- {strawberry_graphql-0.227.0.dist-info → strawberry_graphql-0.227.0.dev1713475585.dist-info}/WHEEL +0 -0
- {strawberry_graphql-0.227.0.dist-info → strawberry_graphql-0.227.0.dev1713475585.dist-info}/entry_points.txt +0 -0
strawberry/ext/mypy_plugin.py
CHANGED
@@ -11,7 +11,6 @@ from typing import (
|
|
11
11
|
List,
|
12
12
|
Optional,
|
13
13
|
Set,
|
14
|
-
Tuple,
|
15
14
|
Union,
|
16
15
|
cast,
|
17
16
|
)
|
@@ -60,16 +59,11 @@ try:
|
|
60
59
|
except ImportError:
|
61
60
|
TypeVarDef = TypeVarType
|
62
61
|
|
63
|
-
PYDANTIC_VERSION: Optional[Tuple[int, ...]] = None
|
64
|
-
|
65
62
|
# To be compatible with user who don't use pydantic
|
66
63
|
try:
|
67
|
-
import pydantic
|
68
64
|
from pydantic.mypy import METADATA_KEY as PYDANTIC_METADATA_KEY
|
69
65
|
from pydantic.mypy import PydanticModelField
|
70
66
|
|
71
|
-
PYDANTIC_VERSION = tuple(map(int, pydantic.__version__.split(".")))
|
72
|
-
|
73
67
|
from strawberry.experimental.pydantic._compat import IS_PYDANTIC_V1
|
74
68
|
except ImportError:
|
75
69
|
PYDANTIC_METADATA_KEY = ""
|
@@ -470,11 +464,6 @@ def strawberry_pydantic_class_callback(ctx: ClassDefContext) -> None:
|
|
470
464
|
return_type=model_type,
|
471
465
|
)
|
472
466
|
else:
|
473
|
-
extra = {}
|
474
|
-
|
475
|
-
if PYDANTIC_VERSION and PYDANTIC_VERSION >= (2, 7, 0):
|
476
|
-
extra["api"] = ctx.api
|
477
|
-
|
478
467
|
add_method(
|
479
468
|
ctx,
|
480
469
|
"to_pydantic",
|
@@ -485,7 +474,6 @@ def strawberry_pydantic_class_callback(ctx: ClassDefContext) -> None:
|
|
485
474
|
typed=True,
|
486
475
|
force_optional=False,
|
487
476
|
use_alias=True,
|
488
|
-
**extra,
|
489
477
|
)
|
490
478
|
for f in missing_pydantic_fields
|
491
479
|
],
|
@@ -499,23 +487,12 @@ def strawberry_pydantic_class_callback(ctx: ClassDefContext) -> None:
|
|
499
487
|
initializer=None,
|
500
488
|
kind=ARG_OPT,
|
501
489
|
)
|
502
|
-
extra_type = ctx.api.named_type(
|
503
|
-
"builtins.dict",
|
504
|
-
[ctx.api.named_type("builtins.str"), AnyType(TypeOfAny.explicit)],
|
505
|
-
)
|
506
|
-
|
507
|
-
extra_argument = Argument(
|
508
|
-
variable=Var(name="extra", type=UnionType([NoneType(), extra_type])),
|
509
|
-
type_annotation=UnionType([NoneType(), extra_type]),
|
510
|
-
initializer=None,
|
511
|
-
kind=ARG_OPT,
|
512
|
-
)
|
513
490
|
|
514
491
|
add_static_method_to_class(
|
515
492
|
ctx.api,
|
516
493
|
ctx.cls,
|
517
494
|
name="from_pydantic",
|
518
|
-
args=[model_argument
|
495
|
+
args=[model_argument],
|
519
496
|
return_type=fill_typevars(ctx.cls.info),
|
520
497
|
)
|
521
498
|
|
@@ -31,6 +31,7 @@ def _impl_type(
|
|
31
31
|
*,
|
32
32
|
name: Optional[str] = None,
|
33
33
|
description: Optional[str] = None,
|
34
|
+
one_of: Optional[bool] = None,
|
34
35
|
directives: Iterable[object] = (),
|
35
36
|
authenticated: bool = False,
|
36
37
|
keys: Iterable[Union["Key", str]] = (),
|
@@ -54,6 +55,7 @@ def _impl_type(
|
|
54
55
|
Shareable,
|
55
56
|
Tag,
|
56
57
|
)
|
58
|
+
from strawberry.schema_directives import OneOf
|
57
59
|
|
58
60
|
directives = list(directives)
|
59
61
|
|
@@ -83,6 +85,9 @@ def _impl_type(
|
|
83
85
|
if is_interface_object:
|
84
86
|
directives.append(InterfaceObject())
|
85
87
|
|
88
|
+
if one_of:
|
89
|
+
directives.append(OneOf())
|
90
|
+
|
86
91
|
return base_type( # type: ignore
|
87
92
|
cls,
|
88
93
|
name=name,
|
@@ -182,6 +187,7 @@ def input(
|
|
182
187
|
cls: T,
|
183
188
|
*,
|
184
189
|
name: Optional[str] = None,
|
190
|
+
one_of: Optional[bool] = None,
|
185
191
|
description: Optional[str] = None,
|
186
192
|
directives: Sequence[object] = (),
|
187
193
|
inaccessible: bool = UNSET,
|
@@ -200,6 +206,7 @@ def input(
|
|
200
206
|
*,
|
201
207
|
name: Optional[str] = None,
|
202
208
|
description: Optional[str] = None,
|
209
|
+
one_of: Optional[bool] = None,
|
203
210
|
directives: Sequence[object] = (),
|
204
211
|
inaccessible: bool = UNSET,
|
205
212
|
tags: Iterable[str] = (),
|
@@ -211,6 +218,7 @@ def input(
|
|
211
218
|
cls: Optional[T] = None,
|
212
219
|
*,
|
213
220
|
name: Optional[str] = None,
|
221
|
+
one_of: Optional[bool] = None,
|
214
222
|
description: Optional[str] = None,
|
215
223
|
directives: Sequence[object] = (),
|
216
224
|
inaccessible: bool = UNSET,
|
@@ -223,6 +231,7 @@ def input(
|
|
223
231
|
directives=directives,
|
224
232
|
inaccessible=inaccessible,
|
225
233
|
is_input=True,
|
234
|
+
one_of=one_of,
|
226
235
|
tags=tags,
|
227
236
|
)
|
228
237
|
|
strawberry/object_type.py
CHANGED
@@ -272,6 +272,7 @@ def input(
|
|
272
272
|
cls: T,
|
273
273
|
*,
|
274
274
|
name: Optional[str] = None,
|
275
|
+
one_of: Optional[bool] = None,
|
275
276
|
description: Optional[str] = None,
|
276
277
|
directives: Optional[Sequence[object]] = (),
|
277
278
|
) -> T:
|
@@ -285,6 +286,7 @@ def input(
|
|
285
286
|
def input(
|
286
287
|
*,
|
287
288
|
name: Optional[str] = None,
|
289
|
+
one_of: Optional[bool] = None,
|
288
290
|
description: Optional[str] = None,
|
289
291
|
directives: Optional[Sequence[object]] = (),
|
290
292
|
) -> Callable[[T], T]:
|
@@ -295,6 +297,7 @@ def input(
|
|
295
297
|
cls: Optional[T] = None,
|
296
298
|
*,
|
297
299
|
name: Optional[str] = None,
|
300
|
+
one_of: Optional[bool] = None,
|
298
301
|
description: Optional[str] = None,
|
299
302
|
directives: Optional[Sequence[object]] = (),
|
300
303
|
):
|
@@ -305,6 +308,11 @@ def input(
|
|
305
308
|
>>> field_abc: str = "ABC"
|
306
309
|
"""
|
307
310
|
|
311
|
+
from strawberry.schema_directives import OneOf
|
312
|
+
|
313
|
+
if one_of:
|
314
|
+
directives = (*(directives or ()), OneOf())
|
315
|
+
|
308
316
|
return type( # type: ignore # not sure why mypy complains here
|
309
317
|
cls,
|
310
318
|
name=name,
|
strawberry/schema/execute.py
CHANGED
@@ -23,6 +23,7 @@ from graphql.validation import validate
|
|
23
23
|
|
24
24
|
from strawberry.exceptions import MissingQueryError
|
25
25
|
from strawberry.extensions.runner import SchemaExtensionsRunner
|
26
|
+
from strawberry.schema.validation_rules.one_of import OneOfInputValidationRule
|
26
27
|
from strawberry.types import ExecutionResult
|
27
28
|
|
28
29
|
from .exceptions import InvalidOperationTypeError
|
@@ -55,6 +56,10 @@ def validate_document(
|
|
55
56
|
document: DocumentNode,
|
56
57
|
validation_rules: Tuple[Type[ASTValidationRule], ...],
|
57
58
|
) -> List[GraphQLError]:
|
59
|
+
validation_rules = (
|
60
|
+
*validation_rules,
|
61
|
+
OneOfInputValidationRule,
|
62
|
+
)
|
58
63
|
return validate(
|
59
64
|
schema,
|
60
65
|
document,
|
strawberry/schema/schema.py
CHANGED
@@ -16,6 +16,8 @@ from typing import (
|
|
16
16
|
)
|
17
17
|
|
18
18
|
from graphql import (
|
19
|
+
GraphQLBoolean,
|
20
|
+
GraphQLField,
|
19
21
|
GraphQLNamedType,
|
20
22
|
GraphQLNonNull,
|
21
23
|
GraphQLSchema,
|
@@ -168,6 +170,7 @@ class Schema(BaseSchema):
|
|
168
170
|
|
169
171
|
self._warn_for_federation_directives()
|
170
172
|
self._resolve_node_ids()
|
173
|
+
self._extend_introspection()
|
171
174
|
|
172
175
|
# Validate schema early because we want developers to know about
|
173
176
|
# possible issues as soon as possible
|
@@ -375,6 +378,14 @@ class Schema(BaseSchema):
|
|
375
378
|
stacklevel=3,
|
376
379
|
)
|
377
380
|
|
381
|
+
def _extend_introspection(self):
|
382
|
+
def _resolve_is_one_of(obj: Any, info: Any) -> bool:
|
383
|
+
return obj.extensions["strawberry-definition"].is_one_of
|
384
|
+
|
385
|
+
instrospection_type = self._schema.type_map["__Type"]
|
386
|
+
instrospection_type.fields["isOneOf"] = GraphQLField(GraphQLBoolean) # type: ignore[attr-defined]
|
387
|
+
instrospection_type.fields["isOneOf"].resolve = _resolve_is_one_of # type: ignore[attr-defined]
|
388
|
+
|
378
389
|
def as_str(self) -> str:
|
379
390
|
return print_schema(self)
|
380
391
|
|
@@ -26,6 +26,7 @@ from graphql import (
|
|
26
26
|
GraphQLDirective,
|
27
27
|
GraphQLEnumType,
|
28
28
|
GraphQLEnumValue,
|
29
|
+
GraphQLError,
|
29
30
|
GraphQLField,
|
30
31
|
GraphQLInputField,
|
31
32
|
GraphQLInputObjectType,
|
@@ -410,6 +411,37 @@ class GraphQLCoreConverter:
|
|
410
411
|
assert isinstance(graphql_object_type, GraphQLInputObjectType) # For mypy
|
411
412
|
return graphql_object_type
|
412
413
|
|
414
|
+
def check_one_of(value: dict[str, Any]) -> dict[str, Any]:
|
415
|
+
if len(value) != 1:
|
416
|
+
raise GraphQLError(
|
417
|
+
f"OneOf Input Object '{type_name}' must specify exactly one key."
|
418
|
+
)
|
419
|
+
|
420
|
+
first_key, first_value = next(iter(value.items()))
|
421
|
+
|
422
|
+
if first_value is None or first_value is UNSET:
|
423
|
+
raise GraphQLError(
|
424
|
+
f"Value for member field '{first_key}' must be non-null"
|
425
|
+
)
|
426
|
+
|
427
|
+
# We are populating all missing keys with `None`, so users
|
428
|
+
# don't have to set an explicit default for the input type
|
429
|
+
# The alternative is to tell them to use `UNSET`, but it looks
|
430
|
+
# a bit unfriendly to use
|
431
|
+
|
432
|
+
for field in type_definition.fields:
|
433
|
+
name = self.config.name_converter.from_field(field)
|
434
|
+
if name not in value:
|
435
|
+
value[name] = None
|
436
|
+
|
437
|
+
return value
|
438
|
+
|
439
|
+
out_type = (
|
440
|
+
check_one_of
|
441
|
+
if type_definition.is_input and type_definition.is_one_of
|
442
|
+
else None
|
443
|
+
)
|
444
|
+
|
413
445
|
graphql_object_type = GraphQLInputObjectType(
|
414
446
|
name=type_name,
|
415
447
|
fields=lambda: self.get_graphql_input_fields(type_definition),
|
@@ -417,6 +449,7 @@ class GraphQLCoreConverter:
|
|
417
449
|
extensions={
|
418
450
|
GraphQLCoreConverter.DEFINITION_BACKREF: type_definition,
|
419
451
|
},
|
452
|
+
out_type=out_type,
|
420
453
|
)
|
421
454
|
|
422
455
|
self.type_map[type_name] = ConcreteType(
|
File without changes
|
@@ -0,0 +1,80 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from graphql import (
|
4
|
+
ExecutableDefinitionNode,
|
5
|
+
GraphQLError,
|
6
|
+
GraphQLNamedType,
|
7
|
+
ObjectValueNode,
|
8
|
+
ValidationContext,
|
9
|
+
ValidationRule,
|
10
|
+
VariableDefinitionNode,
|
11
|
+
get_named_type,
|
12
|
+
)
|
13
|
+
|
14
|
+
|
15
|
+
class OneOfInputValidationRule(ValidationRule):
|
16
|
+
def __init__(self, validation_context: ValidationContext) -> None:
|
17
|
+
super().__init__(validation_context)
|
18
|
+
|
19
|
+
def enter_operation_definition(
|
20
|
+
self, node: ExecutableDefinitionNode, *_args: Any
|
21
|
+
) -> None:
|
22
|
+
self.variable_definitions: dict[str, VariableDefinitionNode] = {}
|
23
|
+
|
24
|
+
def enter_variable_definition(
|
25
|
+
self, node: VariableDefinitionNode, *_args: Any
|
26
|
+
) -> None:
|
27
|
+
self.variable_definitions[node.variable.name.value] = node
|
28
|
+
|
29
|
+
def enter_object_value(self, node: ObjectValueNode, *_args: Any) -> None:
|
30
|
+
type_ = get_named_type(self.context.get_input_type())
|
31
|
+
|
32
|
+
if not type_:
|
33
|
+
return
|
34
|
+
|
35
|
+
strawberry_type = type_.extensions.get("strawberry-definition")
|
36
|
+
|
37
|
+
if strawberry_type and strawberry_type.is_one_of:
|
38
|
+
self.validate_one_of(node, type_)
|
39
|
+
|
40
|
+
def validate_one_of(self, node: ObjectValueNode, type: GraphQLNamedType) -> None:
|
41
|
+
field_node_map = {field.name.value: field for field in node.fields}
|
42
|
+
keys = list(field_node_map.keys())
|
43
|
+
is_not_exactly_one_field = len(keys) != 1
|
44
|
+
|
45
|
+
if is_not_exactly_one_field:
|
46
|
+
self.report_error(
|
47
|
+
GraphQLError(
|
48
|
+
f"OneOf Input Object '{type.name}' must specify exactly one key.",
|
49
|
+
nodes=[node],
|
50
|
+
)
|
51
|
+
)
|
52
|
+
|
53
|
+
return
|
54
|
+
|
55
|
+
value = field_node_map[keys[0]].value
|
56
|
+
is_null_literal = not value or value.kind == "null_value"
|
57
|
+
is_variable = value.kind == "variable"
|
58
|
+
|
59
|
+
if is_null_literal:
|
60
|
+
self.report_error(
|
61
|
+
GraphQLError(
|
62
|
+
f"Field '{type.name}.{keys[0]}' must be non-null.",
|
63
|
+
nodes=[node],
|
64
|
+
)
|
65
|
+
)
|
66
|
+
|
67
|
+
return
|
68
|
+
|
69
|
+
if is_variable:
|
70
|
+
variable_name = value.name.value # type: ignore
|
71
|
+
definition = self.variable_definitions[variable_name]
|
72
|
+
is_nullable_variable = definition.type.kind != "non_null_type"
|
73
|
+
|
74
|
+
if is_nullable_variable:
|
75
|
+
self.report_error(
|
76
|
+
GraphQLError(
|
77
|
+
f"Variable '{variable_name}' must be non-nullable to be used for OneOf Input Object '{type.name}'.",
|
78
|
+
nodes=[node],
|
79
|
+
)
|
80
|
+
)
|
@@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, List, Tuple, Union
|
|
7
7
|
from typing_extensions import Protocol, TypeAlias
|
8
8
|
|
9
9
|
import libcst as cst
|
10
|
-
from graphlib import TopologicalSorter
|
11
10
|
from graphql import (
|
12
11
|
EnumTypeDefinitionNode,
|
13
12
|
EnumValueDefinitionNode,
|
@@ -178,6 +177,19 @@ def _get_argument(name: str, value: ArgumentValue) -> cst.Arg:
|
|
178
177
|
)
|
179
178
|
|
180
179
|
|
180
|
+
# TODO: this might be removed now
|
181
|
+
def _get_argument_list(name: str, values: list[ArgumentValue]) -> cst.Arg:
|
182
|
+
value = cst.List(
|
183
|
+
elements=[cst.Element(value=_sanitize_argument(value)) for value in values],
|
184
|
+
)
|
185
|
+
|
186
|
+
return cst.Arg(
|
187
|
+
value=value,
|
188
|
+
keyword=cst.Name(name),
|
189
|
+
equal=cst.AssignEqual(cst.SimpleWhitespace(""), cst.SimpleWhitespace("")),
|
190
|
+
)
|
191
|
+
|
192
|
+
|
181
193
|
def _get_field_value(
|
182
194
|
field: FieldDefinitionNode | InputValueDefinitionNode,
|
183
195
|
alias: str | None,
|
@@ -429,11 +441,11 @@ def _get_class_definition(
|
|
429
441
|
| InputObjectTypeDefinitionNode,
|
430
442
|
is_apollo_federation: bool,
|
431
443
|
imports: set[Import],
|
432
|
-
) ->
|
444
|
+
) -> cst.ClassDef:
|
433
445
|
decorator = _get_strawberry_decorator(definition, is_apollo_federation, imports)
|
434
446
|
|
435
|
-
|
436
|
-
[interface.name.value for interface in definition.interfaces]
|
447
|
+
bases = (
|
448
|
+
[cst.Arg(cst.Name(interface.name.value)) for interface in definition.interfaces]
|
437
449
|
if isinstance(
|
438
450
|
definition, (ObjectTypeDefinitionNode, InterfaceTypeDefinitionNode)
|
439
451
|
)
|
@@ -441,24 +453,21 @@ def _get_class_definition(
|
|
441
453
|
else []
|
442
454
|
)
|
443
455
|
|
444
|
-
|
456
|
+
return cst.ClassDef(
|
445
457
|
name=cst.Name(definition.name.value),
|
458
|
+
bases=bases,
|
446
459
|
body=cst.IndentedBlock(
|
447
460
|
body=[
|
448
461
|
_get_field(field, is_apollo_federation, imports)
|
449
462
|
for field in definition.fields
|
450
463
|
]
|
451
464
|
),
|
452
|
-
bases=[cst.Arg(cst.Name(interface)) for interface in interfaces],
|
453
465
|
decorators=[decorator],
|
454
466
|
)
|
455
467
|
|
456
|
-
return Definition(class_definition, interfaces, definition.name.value)
|
457
|
-
|
458
468
|
|
459
469
|
def _get_enum_value(enum_value: EnumValueDefinitionNode) -> cst.SimpleStatementLine:
|
460
470
|
name = enum_value.name.value
|
461
|
-
|
462
471
|
return cst.SimpleStatementLine(
|
463
472
|
body=[
|
464
473
|
cst.Assign(
|
@@ -469,7 +478,7 @@ def _get_enum_value(enum_value: EnumValueDefinitionNode) -> cst.SimpleStatementL
|
|
469
478
|
)
|
470
479
|
|
471
480
|
|
472
|
-
def _get_enum_definition(definition: EnumTypeDefinitionNode) ->
|
481
|
+
def _get_enum_definition(definition: EnumTypeDefinitionNode) -> cst.ClassDef:
|
473
482
|
decorator = cst.Decorator(
|
474
483
|
decorator=cst.Attribute(
|
475
484
|
value=cst.Name("strawberry"),
|
@@ -477,7 +486,7 @@ def _get_enum_definition(definition: EnumTypeDefinitionNode) -> Definition:
|
|
477
486
|
),
|
478
487
|
)
|
479
488
|
|
480
|
-
|
489
|
+
return cst.ClassDef(
|
481
490
|
name=cst.Name(definition.name.value),
|
482
491
|
bases=[cst.Arg(cst.Name("Enum"))],
|
483
492
|
body=cst.IndentedBlock(
|
@@ -486,12 +495,6 @@ def _get_enum_definition(definition: EnumTypeDefinitionNode) -> Definition:
|
|
486
495
|
decorators=[decorator],
|
487
496
|
)
|
488
497
|
|
489
|
-
return Definition(
|
490
|
-
class_definition,
|
491
|
-
[],
|
492
|
-
definition.name.value,
|
493
|
-
)
|
494
|
-
|
495
498
|
|
496
499
|
def _get_schema_definition(
|
497
500
|
root_query_name: str | None,
|
@@ -559,54 +562,38 @@ def _get_schema_definition(
|
|
559
562
|
)
|
560
563
|
|
561
564
|
|
562
|
-
|
563
|
-
class Definition:
|
564
|
-
code: cst.CSTNode
|
565
|
-
dependencies: list[str]
|
566
|
-
name: str
|
567
|
-
|
568
|
-
|
569
|
-
def _get_union_definition(definition: UnionTypeDefinitionNode) -> Definition:
|
565
|
+
def _get_union_definition(definition: UnionTypeDefinitionNode) -> cst.Assign:
|
570
566
|
name = definition.name.value
|
571
567
|
|
572
568
|
types = cst.parse_expression(
|
573
569
|
" | ".join([type_.name.value for type_ in definition.types])
|
574
570
|
)
|
575
571
|
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
cst.
|
585
|
-
|
586
|
-
cst.
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
)
|
593
|
-
)
|
594
|
-
),
|
595
|
-
],
|
572
|
+
return cst.Assign(
|
573
|
+
targets=[cst.AssignTarget(cst.Name(name))],
|
574
|
+
value=cst.Subscript(
|
575
|
+
value=cst.Name("Annotated"),
|
576
|
+
slice=[
|
577
|
+
cst.SubscriptElement(slice=cst.Index(types)),
|
578
|
+
cst.SubscriptElement(
|
579
|
+
slice=cst.Index(
|
580
|
+
cst.Call(
|
581
|
+
cst.Attribute(
|
582
|
+
value=cst.Name("strawberry"),
|
583
|
+
attr=cst.Name("union"),
|
584
|
+
),
|
585
|
+
args=[_get_argument("name", name)],
|
586
|
+
)
|
587
|
+
)
|
596
588
|
),
|
597
|
-
|
598
|
-
|
599
|
-
)
|
600
|
-
return Definition(
|
601
|
-
simple_statement,
|
602
|
-
[],
|
603
|
-
definition.name.value,
|
589
|
+
],
|
590
|
+
),
|
604
591
|
)
|
605
592
|
|
606
593
|
|
607
594
|
def _get_scalar_definition(
|
608
595
|
definition: ScalarTypeDefinitionNode, imports: set[Import]
|
609
|
-
) ->
|
596
|
+
) -> cst.SimpleStatementLine | None:
|
610
597
|
name = definition.name.value
|
611
598
|
|
612
599
|
if name == "Date":
|
@@ -665,7 +652,7 @@ def _get_scalar_definition(
|
|
665
652
|
),
|
666
653
|
]
|
667
654
|
|
668
|
-
|
655
|
+
return cst.SimpleStatementLine(
|
669
656
|
body=[
|
670
657
|
cst.Assign(
|
671
658
|
targets=[cst.AssignTarget(cst.Name(name))],
|
@@ -690,13 +677,12 @@ def _get_scalar_definition(
|
|
690
677
|
)
|
691
678
|
]
|
692
679
|
)
|
693
|
-
return Definition(statement_definition, [], name=definition.name.value)
|
694
680
|
|
695
681
|
|
696
682
|
def codegen(schema: str) -> str:
|
697
683
|
document = parse(schema)
|
698
684
|
|
699
|
-
definitions:
|
685
|
+
definitions: list[cst.CSTNode] = []
|
700
686
|
|
701
687
|
root_query_name: str | None = None
|
702
688
|
root_mutation_name: str | None = None
|
@@ -706,17 +692,17 @@ def codegen(schema: str) -> str:
|
|
706
692
|
Import(module=None, imports=("strawberry",)),
|
707
693
|
}
|
708
694
|
|
695
|
+
object_types: dict[str, cst.ClassDef] = {}
|
696
|
+
|
709
697
|
# when we encounter a extend schema @link ..., we check if is an apollo federation schema
|
710
698
|
# and we use this variable to keep track of it, but at the moment the assumption is that
|
711
699
|
# the schema extension is always done at the top, this might not be the case all the
|
712
700
|
# time
|
713
701
|
is_apollo_federation = False
|
714
702
|
|
715
|
-
for
|
716
|
-
definition: Definition | None = None
|
717
|
-
|
703
|
+
for definition in document.definitions:
|
718
704
|
if isinstance(
|
719
|
-
|
705
|
+
definition,
|
720
706
|
(
|
721
707
|
ObjectTypeDefinitionNode,
|
722
708
|
InterfaceTypeDefinitionNode,
|
@@ -724,17 +710,23 @@ def codegen(schema: str) -> str:
|
|
724
710
|
ObjectTypeExtensionNode,
|
725
711
|
),
|
726
712
|
):
|
727
|
-
|
728
|
-
|
713
|
+
class_definition = _get_class_definition(
|
714
|
+
definition, is_apollo_federation, imports
|
729
715
|
)
|
730
716
|
|
731
|
-
|
717
|
+
object_types[definition.name.value] = class_definition
|
718
|
+
|
719
|
+
definitions.append(cst.EmptyLine())
|
720
|
+
definitions.append(class_definition)
|
721
|
+
|
722
|
+
elif isinstance(definition, EnumTypeDefinitionNode):
|
732
723
|
imports.add(Import(module="enum", imports=("Enum",)))
|
733
724
|
|
734
|
-
|
725
|
+
definitions.append(cst.EmptyLine())
|
726
|
+
definitions.append(_get_enum_definition(definition))
|
735
727
|
|
736
|
-
elif isinstance(
|
737
|
-
for operation_type_definition in
|
728
|
+
elif isinstance(definition, SchemaDefinitionNode):
|
729
|
+
for operation_type_definition in definition.operation_types:
|
738
730
|
if operation_type_definition.operation == OperationType.QUERY:
|
739
731
|
root_query_name = operation_type_definition.type.name.value
|
740
732
|
elif operation_type_definition.operation == OperationType.MUTATION:
|
@@ -745,33 +737,36 @@ def codegen(schema: str) -> str:
|
|
745
737
|
raise NotImplementedError(
|
746
738
|
f"Unknown operation {operation_type_definition.operation}"
|
747
739
|
)
|
748
|
-
elif isinstance(
|
740
|
+
elif isinstance(definition, UnionTypeDefinitionNode):
|
749
741
|
imports.add(Import(module="typing", imports=("Annotated",)))
|
750
742
|
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
743
|
+
definitions.append(cst.EmptyLine())
|
744
|
+
definitions.append(_get_union_definition(definition))
|
745
|
+
definitions.append(cst.EmptyLine())
|
746
|
+
elif isinstance(definition, ScalarTypeDefinitionNode):
|
747
|
+
scalar_definition = _get_scalar_definition(definition, imports)
|
748
|
+
|
749
|
+
if scalar_definition is not None:
|
750
|
+
definitions.append(cst.EmptyLine())
|
751
|
+
definitions.append(scalar_definition)
|
752
|
+
definitions.append(cst.EmptyLine())
|
753
|
+
elif isinstance(definition, SchemaExtensionNode):
|
756
754
|
is_apollo_federation = any(
|
757
755
|
_is_federation_link_directive(directive)
|
758
|
-
for directive in
|
756
|
+
for directive in definition.directives
|
759
757
|
)
|
760
758
|
else:
|
761
759
|
raise NotImplementedError(f"Unknown definition {definition}")
|
762
760
|
|
763
|
-
if definition is not None:
|
764
|
-
definitions[definition.name] = definition
|
765
|
-
|
766
761
|
if root_query_name is None:
|
767
|
-
root_query_name = "Query" if "Query" in
|
762
|
+
root_query_name = "Query" if "Query" in object_types else None
|
768
763
|
|
769
764
|
if root_mutation_name is None:
|
770
|
-
root_mutation_name = "Mutation" if "Mutation" in
|
765
|
+
root_mutation_name = "Mutation" if "Mutation" in object_types else None
|
771
766
|
|
772
767
|
if root_subscription_name is None:
|
773
768
|
root_subscription_name = (
|
774
|
-
"Subscription" if "Subscription" in
|
769
|
+
"Subscription" if "Subscription" in object_types else None
|
775
770
|
)
|
776
771
|
|
777
772
|
schema_definition = _get_schema_definition(
|
@@ -782,23 +777,19 @@ def codegen(schema: str) -> str:
|
|
782
777
|
)
|
783
778
|
|
784
779
|
if schema_definition:
|
785
|
-
definitions
|
786
|
-
|
787
|
-
body: list[cst.CSTNode] = [
|
788
|
-
cst.SimpleStatementLine(body=[import_.to_cst()])
|
789
|
-
for import_ in sorted(imports, key=lambda i: (i.module or "", i.imports))
|
790
|
-
]
|
791
|
-
|
792
|
-
# DAG to sort definitions based on dependencies
|
793
|
-
graph = {name: definition.dependencies for name, definition in definitions.items()}
|
794
|
-
ts = TopologicalSorter(graph)
|
795
|
-
|
796
|
-
for definition_name in tuple(ts.static_order()):
|
797
|
-
definition = definitions[definition_name]
|
798
|
-
|
799
|
-
body.append(cst.EmptyLine())
|
800
|
-
body.append(definition.code)
|
780
|
+
definitions.append(cst.EmptyLine())
|
781
|
+
definitions.append(schema_definition)
|
801
782
|
|
802
|
-
module = cst.Module(
|
783
|
+
module = cst.Module(
|
784
|
+
body=[
|
785
|
+
*[
|
786
|
+
cst.SimpleStatementLine(body=[import_.to_cst()])
|
787
|
+
for import_ in sorted(
|
788
|
+
imports, key=lambda i: (i.module or "", i.imports)
|
789
|
+
)
|
790
|
+
],
|
791
|
+
*definitions, # type: ignore
|
792
|
+
]
|
793
|
+
)
|
803
794
|
|
804
795
|
return module.code
|
strawberry/type.py
CHANGED
strawberry/types/types.py
CHANGED
@@ -197,6 +197,17 @@ class StrawberryObjectDefinition(StrawberryType):
|
|
197
197
|
# All field mappings succeeded. This is a match
|
198
198
|
return True
|
199
199
|
|
200
|
+
@property
|
201
|
+
def is_one_of(self) -> bool:
|
202
|
+
from strawberry.schema_directives import OneOf
|
203
|
+
|
204
|
+
if not self.is_input or not self.directives:
|
205
|
+
return False
|
206
|
+
|
207
|
+
return any(
|
208
|
+
directive for directive in self.directives if isinstance(directive, OneOf)
|
209
|
+
)
|
210
|
+
|
200
211
|
|
201
212
|
# TODO: remove when deprecating _type_definition
|
202
213
|
if TYPE_CHECKING:
|
{strawberry_graphql-0.227.0.dist-info → strawberry_graphql-0.227.0.dev1713475585.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: strawberry-graphql
|
3
|
-
Version: 0.227.0
|
3
|
+
Version: 0.227.0.dev1713475585
|
4
4
|
Summary: A library for creating GraphQL APIs
|
5
5
|
Home-page: https://strawberry.rocks/
|
6
6
|
License: MIT
|
@@ -44,7 +44,6 @@ Requires-Dist: chalice (>=1.22,<2.0) ; extra == "chalice"
|
|
44
44
|
Requires-Dist: channels (>=3.0.5) ; extra == "channels"
|
45
45
|
Requires-Dist: fastapi (>=0.65.2) ; extra == "fastapi"
|
46
46
|
Requires-Dist: flask (>=1.1) ; extra == "flask"
|
47
|
-
Requires-Dist: graphlib_backport ; (python_version < "3.9") and (extra == "cli")
|
48
47
|
Requires-Dist: graphql-core (>=3.2.0,<3.3.0)
|
49
48
|
Requires-Dist: libcst (>=0.4.7) ; extra == "debug" or extra == "debug-server" or extra == "cli"
|
50
49
|
Requires-Dist: litestar (>=2) ; (python_version >= "3.8") and (extra == "litestar")
|
{strawberry_graphql-0.227.0.dist-info → strawberry_graphql-0.227.0.dev1713475585.dist-info}/RECORD
RENAMED
@@ -96,7 +96,7 @@ strawberry/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
96
96
|
strawberry/ext/dataclasses/LICENSE,sha256=WZgm35K_3NJwLqxpEHJJi7CWxVrwTumEz5D3Dtd7WnA,13925
|
97
97
|
strawberry/ext/dataclasses/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
98
98
|
strawberry/ext/dataclasses/dataclasses.py,sha256=Le32f96qmahuenVsMW5xjIr-EPnO7WKljbOPLpU800Q,2254
|
99
|
-
strawberry/ext/mypy_plugin.py,sha256=
|
99
|
+
strawberry/ext/mypy_plugin.py,sha256=AGWu3xTguWMetqQP7bnaLRJBdLplPOtZ_eRGYt0Gfg8,18956
|
100
100
|
strawberry/extensions/__init__.py,sha256=SZ-YEMnxAzoDZFo-uHXMHOaY_PPkYm1muPpK4ccJ3Xk,1248
|
101
101
|
strawberry/extensions/add_validation_rules.py,sha256=l1cCl6VGQ8c2EELK3cIjmn6RftonHD1jKoWQ5-hMTf0,1366
|
102
102
|
strawberry/extensions/base_extension.py,sha256=XvYKIsSaxfTCVWdphOFll-Fo0wJd5Ib4QdKuYule6S0,2000
|
@@ -130,7 +130,7 @@ strawberry/federation/argument.py,sha256=5qyJYlQGEZd6iXWseQ7dnnejCYj5HyglfK10jOC
|
|
130
130
|
strawberry/federation/enum.py,sha256=wpM1z2NOkoBCqTVyD6vJJXoNlcnrNIERt2YpB4R9ybU,2977
|
131
131
|
strawberry/federation/field.py,sha256=HHOs8FL52_jxuYxPiR2EwJsXtk7LyapJeBUPqvJ6pCg,6184
|
132
132
|
strawberry/federation/mutation.py,sha256=0lV5HJwgw4HYR_59pwxWqnPs342HwakTNMc98w5Hb-c,43
|
133
|
-
strawberry/federation/object_type.py,sha256=
|
133
|
+
strawberry/federation/object_type.py,sha256=moxIUYxs7n0WdjtEWhoTesmZ63ymIr_U1qIuaiNrvhY,9302
|
134
134
|
strawberry/federation/scalar.py,sha256=jrSvaqfGEnj_Vl7b-LYMdq_NDMwfdu0t5Pfzlw5WAmU,3807
|
135
135
|
strawberry/federation/schema.py,sha256=9g7jp6eUTTP3atW81dLMtaqeY0tQB4YGdR8beKZ-JX8,13715
|
136
136
|
strawberry/federation/schema_directive.py,sha256=TpqoVeN3-iE-acndIRAVyU4LIXh6FTHz-Pv2kI8zGu0,1719
|
@@ -160,7 +160,7 @@ strawberry/litestar/controller.py,sha256=CPVnXA36O3OQb9NnHdD3ycox21rarjrS-I2HiOO
|
|
160
160
|
strawberry/litestar/handlers/graphql_transport_ws_handler.py,sha256=q_erlgzPsxarohRQXGp1yK0mjKyS8vsWntMYEmrQx4s,2008
|
161
161
|
strawberry/litestar/handlers/graphql_ws_handler.py,sha256=vVpjd5rJOldF8aQWEGjmmNd60WE1p6q6hFmB_DtCzDU,2250
|
162
162
|
strawberry/mutation.py,sha256=NROPvHJU1BBxZB7Wj4Okxw4hDIYM59MCpukAGEmSNYA,255
|
163
|
-
strawberry/object_type.py,sha256=
|
163
|
+
strawberry/object_type.py,sha256=90KgHnOnAY_rlbqMs_89gurmLQmRH9X7PIuh12z6YGs,11623
|
164
164
|
strawberry/parent.py,sha256=mCnJcLBQKwtokXcGxkm5HqGg7Wkst97rn61ViuaotrM,829
|
165
165
|
strawberry/permission.py,sha256=dcKx4Zlg4ZhcxEDBOSWzz0CUN4WPkcc_kJUVuvLLs6w,5925
|
166
166
|
strawberry/printer/__init__.py,sha256=DmepjmgtkdF5RxK_7yC6qUyRWn56U-9qeZMbkztYB9w,62
|
@@ -186,16 +186,19 @@ strawberry/schema/base.py,sha256=lQBJyzG2ZhKc544oLbXEbpYOPOjaXBop3lxp68h_lfI,297
|
|
186
186
|
strawberry/schema/compat.py,sha256=n0r3UPUcGemMqK8vklgtCkkuCA1p6tWAYbc6Vl4iNOw,1684
|
187
187
|
strawberry/schema/config.py,sha256=XkWwmxEsmecscH29o4qU0iSe-hgwJJ2X0DPBVlka2OE,640
|
188
188
|
strawberry/schema/exceptions.py,sha256=T-DsvBtjx9svkegIm1YrVPGPswpVEpMTFc0_7flLEkM,542
|
189
|
-
strawberry/schema/execute.py,sha256=
|
189
|
+
strawberry/schema/execute.py,sha256=dwxMrJrR2Qdd1nPGcIS9-Viq7h5qtPfsD6qdujALoMw,11153
|
190
190
|
strawberry/schema/name_converter.py,sha256=UdNyd-QtqF2HsDCQK-nsOcLGxDTj4hJwYFNvMtZnpq4,6533
|
191
|
-
strawberry/schema/schema.py,sha256=
|
192
|
-
strawberry/schema/schema_converter.py,sha256=
|
191
|
+
strawberry/schema/schema.py,sha256=bfQdLmFXR_BQd80IGO8qa0431cQMUDr22XLNnBL7Tu0,14252
|
192
|
+
strawberry/schema/schema_converter.py,sha256=_DfYbWLhbtjBR-w596O5xUwTd3OvHS0P7e5G52JaKXI,35956
|
193
193
|
strawberry/schema/types/__init__.py,sha256=oHO3COWhL3L1KLYCJNY1XFf5xt2GGtHiMC-UaYbFfnA,68
|
194
194
|
strawberry/schema/types/base_scalars.py,sha256=Z_BmgwLicNexLipGyw6MmZ7OBnkGJU3ySgaY9SwBWrw,1837
|
195
195
|
strawberry/schema/types/concrete_type.py,sha256=HB30G1hMUuuvjAvfSe6ADS35iI_T_wKO-EprVOWTMSs,746
|
196
196
|
strawberry/schema/types/scalar.py,sha256=SVJ8HiKncCvOw2xwABI5xYaHcC7KkGHG-tx2WDtSoCA,2802
|
197
|
-
strawberry/
|
197
|
+
strawberry/schema/validation_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
198
|
+
strawberry/schema/validation_rules/one_of.py,sha256=hO0Re1fQQWT9zIqo_7BHLM2bdSsmNJDbKia8AUTSXQQ,2588
|
199
|
+
strawberry/schema_codegen/__init__.py,sha256=eHHr59CLzIlqb9b5h4PLK6RNPPvUHIJCQ0sAJa2l4aw,24059
|
198
200
|
strawberry/schema_directive.py,sha256=GxiOedFB-RJAflpQNUZv00C5Z6gavR-AYdsvoCA_0jc,1963
|
201
|
+
strawberry/schema_directives.py,sha256=ZcaTeFfxBp5RsmVu2NDPP_hOL2WpPomQIQSmNp9BO3s,179
|
199
202
|
strawberry/starlite/__init__.py,sha256=v209swT8H9MljVL-npvANhEO1zz3__PSfxb_Ix-NoeE,134
|
200
203
|
strawberry/starlite/controller.py,sha256=oyGkcdhIKvY5BCxVqPhS-pn7Ae8SZKXnI5W5fk4p0zw,11916
|
201
204
|
strawberry/starlite/handlers/graphql_transport_ws_handler.py,sha256=WhfFVWdjRSk4A48MaBLGWqZdi2OnHajxdQlA_Gc4XBE,1981
|
@@ -216,7 +219,7 @@ strawberry/test/client.py,sha256=N_AkxLp-kWVTgQDcL8FkruJpAd6NpkChIDptzneu_uM,575
|
|
216
219
|
strawberry/tools/__init__.py,sha256=pdGpZx8wpq03VfUZJyF9JtYxZhGqzzxCiipsalWxJX4,127
|
217
220
|
strawberry/tools/create_type.py,sha256=QxLRT9-DrSgo6vsu_L818wtlbO3wRtI0dOos0gmn1xk,1593
|
218
221
|
strawberry/tools/merge_types.py,sha256=YZ2GSMoDD82bfuvCT0COpN6SLFVRZpTXFFm9LHUyi10,1014
|
219
|
-
strawberry/type.py,sha256=
|
222
|
+
strawberry/type.py,sha256=jQh75TjgaKx1mP-AF6VjWacDAqbHABQuW06vrnsArnM,6603
|
220
223
|
strawberry/types/__init__.py,sha256=APb1Cjy6bxqFxIfDfempP6eb9NE3LYDwQ3gX7r07lXI,139
|
221
224
|
strawberry/types/execution.py,sha256=Dz4Y_M1ysKz3UxnBK_-h103DjvP6NwLElXXOEpQCkgw,2783
|
222
225
|
strawberry/types/fields/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -225,7 +228,7 @@ strawberry/types/graphql.py,sha256=3SWZEsa0Zy1eVW6vy75BnB7t9_lJVi6TBV3_1j3RNBs,6
|
|
225
228
|
strawberry/types/info.py,sha256=b1ZWW_wUop6XrGNcGHKBQeUYjlX-y8u3s2Wm_XhKPYI,3412
|
226
229
|
strawberry/types/nodes.py,sha256=5tTYmxGpVDshbydicHTTBWEiUe8A7p7mdiaSV8Ry80Y,5027
|
227
230
|
strawberry/types/type_resolver.py,sha256=F0z_geS4VEun8EhD571LaTgI8ypjCeLfp910gF0Q3MY,6280
|
228
|
-
strawberry/types/types.py,sha256=
|
231
|
+
strawberry/types/types.py,sha256=2iUj5ohnpXYTIEvwk1i-4LVasO0IuLv9GUJgd_jp7GM,7447
|
229
232
|
strawberry/union.py,sha256=IyC1emthtgoEGDOrEOpicC7QyQ0sUexXMx4BT6tFIF8,9951
|
230
233
|
strawberry/unset.py,sha256=4zYRN8vUD7lHQLLpulBFqEPfyvzpx8fl7ZDBUyfMqqk,1112
|
231
234
|
strawberry/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -241,8 +244,8 @@ strawberry/utils/logging.py,sha256=flS7hV0JiIOEdXcrIjda4WyIWix86cpHHFNJL8gl1y4,7
|
|
241
244
|
strawberry/utils/operation.py,sha256=Um-tBCPl3_bVFN2Ph7o1mnrxfxBes4HFCj6T0x4kZxE,1135
|
242
245
|
strawberry/utils/str_converters.py,sha256=avIgPVLg98vZH9mA2lhzVdyyjqzLsK2NdBw9mJQ02Xk,813
|
243
246
|
strawberry/utils/typing.py,sha256=Qxz1LwyVsNGV7LQW1dFsaUbsswj5LHBOdKLMom5eyEA,13491
|
244
|
-
strawberry_graphql-0.227.0.dist-info/LICENSE,sha256=m-XnIVUKqlG_AWnfi9NReh9JfKhYOB-gJfKE45WM1W8,1072
|
245
|
-
strawberry_graphql-0.227.0.dist-info/METADATA,sha256=
|
246
|
-
strawberry_graphql-0.227.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
247
|
-
strawberry_graphql-0.227.0.dist-info/entry_points.txt,sha256=Nk7-aT3_uEwCgyqtHESV9H6Mc31cK-VAvhnQNTzTb4k,49
|
248
|
-
strawberry_graphql-0.227.0.dist-info/RECORD,,
|
247
|
+
strawberry_graphql-0.227.0.dev1713475585.dist-info/LICENSE,sha256=m-XnIVUKqlG_AWnfi9NReh9JfKhYOB-gJfKE45WM1W8,1072
|
248
|
+
strawberry_graphql-0.227.0.dev1713475585.dist-info/METADATA,sha256=Hl3n4V8EBrYh2QrTGqWT7F08HZDuWGKUZNB7JchFx_s,7754
|
249
|
+
strawberry_graphql-0.227.0.dev1713475585.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
250
|
+
strawberry_graphql-0.227.0.dev1713475585.dist-info/entry_points.txt,sha256=Nk7-aT3_uEwCgyqtHESV9H6Mc31cK-VAvhnQNTzTb4k,49
|
251
|
+
strawberry_graphql-0.227.0.dev1713475585.dist-info/RECORD,,
|
{strawberry_graphql-0.227.0.dist-info → strawberry_graphql-0.227.0.dev1713475585.dist-info}/LICENSE
RENAMED
File without changes
|
{strawberry_graphql-0.227.0.dist-info → strawberry_graphql-0.227.0.dev1713475585.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|