From e56ae590815030d7790b91df0ae3135ead1ba4e7 Mon Sep 17 00:00:00 2001 From: Bryan Ricker <978899+bricker@users.noreply.github.com> Date: Sun, 19 Nov 2023 05:02:21 -0800 Subject: [PATCH] Add extensions support to federation.field (#3239) * Add extensions support to federation.field * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update tests/schema/extensions/test_input_mutation_federation.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Patrick Arminio --- RELEASE.md | 9 ++ strawberry/federation/field.py | 6 + .../test_input_mutation_federation.py | 103 ++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 RELEASE.md create mode 100644 tests/schema/extensions/test_input_mutation_federation.py diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000000..1755130736 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,9 @@ +Release type: minor + +Adds an optional `extensions` parameter to `strawberry.federation.field`, with default value `None`. The key is passed through to `strawberry.field`, so the functionality is exactly as described [here](https://strawberry.rocks/docs/guides/field-extensions). + +Example: + +```python +strawberry.federation.field(extensions=[InputMutationExtension()]) +``` diff --git a/strawberry/federation/field.py b/strawberry/federation/field.py index 7bbe002cde..806e6ae8bb 100644 --- a/strawberry/federation/field.py +++ b/strawberry/federation/field.py @@ -21,6 +21,7 @@ if TYPE_CHECKING: from typing_extensions import Literal + from strawberry.extensions.field_extension import FieldExtension from strawberry.field import _RESOLVER_TYPE, StrawberryField from strawberry.permission import BasePermission @@ -48,6 +49,7 @@ def field( default: Any = UNSET, default_factory: Union[Callable[..., object], object] = UNSET, directives: Sequence[object] = (), + extensions: Optional[List[FieldExtension]] = None, graphql_type: Optional[Any] = None, ) -> T: ... @@ -72,6 +74,7 @@ def field( default: Any = UNSET, default_factory: Union[Callable[..., object], object] = UNSET, directives: Sequence[object] = (), + extensions: Optional[List[FieldExtension]] = None, graphql_type: Optional[Any] = None, ) -> Any: ... @@ -96,6 +99,7 @@ def field( default: Any = UNSET, default_factory: Union[Callable[..., object], object] = UNSET, directives: Sequence[object] = (), + extensions: Optional[List[FieldExtension]] = None, graphql_type: Optional[Any] = None, ) -> StrawberryField: ... @@ -119,6 +123,7 @@ def field( default: Any = dataclasses.MISSING, default_factory: Union[Callable[..., object], object] = dataclasses.MISSING, directives: Sequence[object] = (), + extensions: Optional[List[FieldExtension]] = None, graphql_type: Optional[Any] = None, # This init parameter is used by PyRight to determine whether this field # is added in the constructor or not. It is not used to change @@ -169,5 +174,6 @@ def field( default_factory=default_factory, init=init, # type: ignore directives=directives, + extensions=extensions, graphql_type=graphql_type, ) diff --git a/tests/schema/extensions/test_input_mutation_federation.py b/tests/schema/extensions/test_input_mutation_federation.py new file mode 100644 index 0000000000..3aaa84fb98 --- /dev/null +++ b/tests/schema/extensions/test_input_mutation_federation.py @@ -0,0 +1,103 @@ +import textwrap +from typing_extensions import Annotated + +import strawberry +from strawberry.field_extensions import InputMutationExtension + + +@strawberry.federation.type +class Fruit: + name: str + color: str + + +@strawberry.federation.type +class Query: + @strawberry.field + def hello(self) -> str: # pragma: no cover + return "hi" + + +@strawberry.federation.type +class Mutation: + @strawberry.federation.mutation(extensions=[InputMutationExtension()]) + def create_fruit( + self, + name: str, + color: Annotated[ + str, + strawberry.federation.argument( + description="The color of the fruit", + ), + ], + ) -> Fruit: + return Fruit( + name=name, + color=color, + ) + + +schema = strawberry.federation.Schema(query=Query, mutation=Mutation) + + +def test_schema(): + expected = ''' + input CreateFruitInput { + name: String! + + """The color of the fruit""" + color: String! + } + + type Fruit { + name: String! + color: String! + } + + type Mutation { + createFruit( + """Input data for `createFruit` mutation""" + input: CreateFruitInput! + ): Fruit! + } + + type Query { + _service: _Service! + hello: String! + } + + scalar _Any + + type _Service { + sdl: String! + } + ''' + assert str(schema).strip() == textwrap.dedent(expected).strip() + + +def test_input_mutation(): + result = schema.execute_sync( + """ + mutation TestQuery ($input: CreateFruitInput!) { + createFruit (input: $input) { + ... on Fruit { + name + color + } + } + } + """, + variable_values={ + "input": { + "name": "Dragonfruit", + "color": "red", + } + }, + ) + assert result.errors is None + assert result.data == { + "createFruit": { + "name": "Dragonfruit", + "color": "red", + }, + }