diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9cc136a..4522ba5 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -10,17 +10,18 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v3 + - uses: actions/checkout@v5 + - name: Set up Python + uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.14' - name: Build wheel and source tarball run: | - pip install wheel - python setup.py sdist bdist_wheel + python -m pip install --upgrade pip + pip install build + python -m build - name: Publish a Python distribution to PyPI - uses: pypa/gh-action-pypi-publish@v1.1.0 + uses: pypa/gh-action-pypi-publish@v1.8.14 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 86cbe2f..3dfd309 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Build environment run: make integration-build - name: Run Integration Tests diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9aa03f8..5c70c04 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,11 +7,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 with: - python-version: 3.10.5 + python-version: 3.14 - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3d3770d..d0ee373 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,12 +8,12 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/Dockerfile b/Dockerfile index ae10274..3e0ebda 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.10-alpine +FROM python:3-alpine # Disable Python buffering in order to see the logs immediatly ENV PYTHONUNBUFFERED=1 diff --git a/Makefile b/Makefile index a2216dd..ec309be 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,11 @@ # ------------------------- integration-build: ## Build environment for integration tests - cd integration_tests && docker-compose build + cd integration_tests && docker compose build .PHONY: integration-build integration-tests: ## Run integration tests - cd integration_tests && docker-compose down && docker-compose run --rm tests + cd integration_tests && docker compose down && docker compose run --rm tests .PHONY: integration-test # ------------------------- @@ -15,19 +15,19 @@ integration-tests: ## Run integration tests # ------------------------- dev-setup: ## Install development dependencies - docker-compose up --build -d + docker compose up --build -d .PHONY: dev-setup tests: ## Run unit tests - docker-compose run graphene_federation py.test tests --cov=graphene_federation -vv + docker compose run --rm --remove-orphans graphene_federation py.test tests --cov=graphene_federation -vv .PHONY: tests check-style: ## Run linting - docker-compose run graphene_federation black graphene_federation --check + docker compose run --rm graphene_federation black graphene_federation --check .PHONY: check-style check-types: ## Run typing check - docker-compose run graphene_federation mypy graphene_federation + docker compose run --rm graphene_federation mypy graphene_federation .PHONY: check-types # ------------------------- diff --git a/README.md b/README.md index 0bf6504..2250c10 100644 --- a/README.md +++ b/README.md @@ -38,13 +38,17 @@ If you need to use a version compatible with `graphene` v2 I recommend using the - [x] v2.3 - [x] v2.4 - [x] v2.5 -- [x] v2.6 `STABLE_VERSION` . Rover dev supports only upto v2.6 -- [x] v2.7 `LATEST_VERSION` +- [x] v2.6 +- [x] v2.7 +- [x] v2.8 +- [x] v2.9 +- [x] v2.10 +- [x] v2.11 `STABLE_VERSION`,`LATEST_VERSION` All directives could be easily integrated with the help of [graphene-directives](https://github.com/strollby/graphene-directives). Now every directive's values are validated at run time itself by [graphene-directives](https://github.com/strollby/graphene-directives). -### Directives (v2.7) +### Directives (v2.11) ```graphql directive @composeDirective(name: String!) repeatable on SCHEMA @@ -84,21 +88,38 @@ directive @authenticated on | INTERFACE | SCALAR | ENUM -directive @requiresScopes(scopes: [[federation__Scope!]!]!) on +directive @requiresScopes(scopes: [[Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM -directive @policy(policies: [[federation__Policy!]!]!) on +directive @policy(policies: [[Policy!]!]!) on | FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM +directive @context(name: String!) on OBJECT | INTERFACE | UNION +directive @fromContext(field: ContextFieldValue) on ARGUMENT_DEFINITION +directive @listSize( + assumedSize: Int + slicingArguments: [String!] + sizedFields: [String!] + requireOneSlicingArgument: Boolean = true +) on FIELD_DEFINITION +directive @cost(weight: Int!) on + ARGUMENT_DEFINITION + | ENUM + | FIELD_DEFINITION + | INPUT_FIELD_DEFINITION + | OBJECT + | SCALAR + | ENUM scalar federation__Policy scalar federation__Scope scalar FieldSet +scalar federation__ContextFieldValue ``` Read about directives in [official documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives) @@ -430,10 +451,6 @@ schema = build_schema(query=Query, federation_version=LATEST_VERSION) # auto_cam ------------------------ -## Known Issues - -- Using `@composeDirective` with `@link` in Federation `v2.6` shows error in rover, rover cli only supports upto `v2.5` as of 16/01/2024 - ## Contributing * You can run the unit tests by doing: `make tests`. diff --git a/docker-compose.yml b/docker-compose.yml index 2f44d90..374141f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,10 +1,7 @@ -version: '3.5' - services: - graphene_federation: container_name: graphene_federation build: context: . volumes: - - ./:/workdir + - ./:/workdir \ No newline at end of file diff --git a/federation_spec/federation-v2.10.graphql b/federation_spec/federation-v2.10.graphql new file mode 100644 index 0000000..e9f8450 --- /dev/null +++ b/federation_spec/federation-v2.10.graphql @@ -0,0 +1,69 @@ +directive @composeDirective(name: String!) repeatable on SCHEMA +directive @extends on OBJECT | INTERFACE +directive @external on OBJECT | FIELD_DEFINITION +directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE +directive @inaccessible on + | FIELD_DEFINITION + | OBJECT + | INTERFACE + | UNION + | ENUM + | ENUM_VALUE + | SCALAR + | INPUT_OBJECT + | INPUT_FIELD_DEFINITION + | ARGUMENT_DEFINITION +directive @interfaceObject on OBJECT +directive @override(from: String!, label: String) on FIELD_DEFINITION +directive @provides(fields: FieldSet!) on FIELD_DEFINITION +directive @requires(fields: FieldSet!) on FIELD_DEFINITION +directive @shareable repeatable on FIELD_DEFINITION | OBJECT +directive @tag(name: String!) repeatable on + | FIELD_DEFINITION + | INTERFACE + | OBJECT + | UNION + | ARGUMENT_DEFINITION + | SCALAR + | ENUM + | ENUM_VALUE + | INPUT_OBJECT + | INPUT_FIELD_DEFINITION +directive @authenticated on + FIELD_DEFINITION + | OBJECT + | INTERFACE + | SCALAR + | ENUM +directive @requiresScopes(scopes: [[federation__Scope!]!]!) on + FIELD_DEFINITION + | OBJECT + | INTERFACE + | SCALAR + | ENUM +directive @policy(policies: [[federation__Policy!]!]!) on + | FIELD_DEFINITION + | OBJECT + | INTERFACE + | SCALAR + | ENUM +directive @context(name: String!) on OBJECT | INTERFACE | UNION +directive @fromContext(field: federation__ContextFieldValue) on ARGUMENT_DEFINITION +directive @listSize( + assumedSize: Int + slicingArguments: [String!] + sizedFields: [String!] + requireOneSlicingArgument: Boolean = true +) on FIELD_DEFINITION +directive @cost(weight: Int!) on + ARGUMENT_DEFINITION + | ENUM + | FIELD_DEFINITION + | INPUT_FIELD_DEFINITION + | OBJECT + | SCALAR + | ENUM +scalar federation__Policy +scalar federation__Scope +scalar FieldSet +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/federation_spec/federation-v2.11.graphql b/federation_spec/federation-v2.11.graphql new file mode 100644 index 0000000..e9f8450 --- /dev/null +++ b/federation_spec/federation-v2.11.graphql @@ -0,0 +1,69 @@ +directive @composeDirective(name: String!) repeatable on SCHEMA +directive @extends on OBJECT | INTERFACE +directive @external on OBJECT | FIELD_DEFINITION +directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE +directive @inaccessible on + | FIELD_DEFINITION + | OBJECT + | INTERFACE + | UNION + | ENUM + | ENUM_VALUE + | SCALAR + | INPUT_OBJECT + | INPUT_FIELD_DEFINITION + | ARGUMENT_DEFINITION +directive @interfaceObject on OBJECT +directive @override(from: String!, label: String) on FIELD_DEFINITION +directive @provides(fields: FieldSet!) on FIELD_DEFINITION +directive @requires(fields: FieldSet!) on FIELD_DEFINITION +directive @shareable repeatable on FIELD_DEFINITION | OBJECT +directive @tag(name: String!) repeatable on + | FIELD_DEFINITION + | INTERFACE + | OBJECT + | UNION + | ARGUMENT_DEFINITION + | SCALAR + | ENUM + | ENUM_VALUE + | INPUT_OBJECT + | INPUT_FIELD_DEFINITION +directive @authenticated on + FIELD_DEFINITION + | OBJECT + | INTERFACE + | SCALAR + | ENUM +directive @requiresScopes(scopes: [[federation__Scope!]!]!) on + FIELD_DEFINITION + | OBJECT + | INTERFACE + | SCALAR + | ENUM +directive @policy(policies: [[federation__Policy!]!]!) on + | FIELD_DEFINITION + | OBJECT + | INTERFACE + | SCALAR + | ENUM +directive @context(name: String!) on OBJECT | INTERFACE | UNION +directive @fromContext(field: federation__ContextFieldValue) on ARGUMENT_DEFINITION +directive @listSize( + assumedSize: Int + slicingArguments: [String!] + sizedFields: [String!] + requireOneSlicingArgument: Boolean = true +) on FIELD_DEFINITION +directive @cost(weight: Int!) on + ARGUMENT_DEFINITION + | ENUM + | FIELD_DEFINITION + | INPUT_FIELD_DEFINITION + | OBJECT + | SCALAR + | ENUM +scalar federation__Policy +scalar federation__Scope +scalar FieldSet +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/federation_spec/federation-v2.8.graphql b/federation_spec/federation-v2.8.graphql new file mode 100644 index 0000000..07119ca --- /dev/null +++ b/federation_spec/federation-v2.8.graphql @@ -0,0 +1,55 @@ +directive @composeDirective(name: String!) repeatable on SCHEMA +directive @extends on OBJECT | INTERFACE +directive @external on OBJECT | FIELD_DEFINITION +directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE +directive @inaccessible on + | FIELD_DEFINITION + | OBJECT + | INTERFACE + | UNION + | ENUM + | ENUM_VALUE + | SCALAR + | INPUT_OBJECT + | INPUT_FIELD_DEFINITION + | ARGUMENT_DEFINITION +directive @interfaceObject on OBJECT +directive @override(from: String!, label: String) on FIELD_DEFINITION +directive @provides(fields: FieldSet!) on FIELD_DEFINITION +directive @requires(fields: FieldSet!) on FIELD_DEFINITION +directive @shareable repeatable on FIELD_DEFINITION | OBJECT +directive @tag(name: String!) repeatable on + | FIELD_DEFINITION + | INTERFACE + | OBJECT + | UNION + | ARGUMENT_DEFINITION + | SCALAR + | ENUM + | ENUM_VALUE + | INPUT_OBJECT + | INPUT_FIELD_DEFINITION +directive @authenticated on + FIELD_DEFINITION + | OBJECT + | INTERFACE + | SCALAR + | ENUM +directive @requiresScopes(scopes: [[federation__Scope!]!]!) on + FIELD_DEFINITION + | OBJECT + | INTERFACE + | SCALAR + | ENUM +directive @policy(policies: [[federation__Policy!]!]!) on + | FIELD_DEFINITION + | OBJECT + | INTERFACE + | SCALAR + | ENUM +directive @context(name: String!) on OBJECT | INTERFACE | UNION +directive @fromContext(field: federation__ContextFieldValue) on ARGUMENT_DEFINITION +scalar federation__Policy +scalar federation__Scope +scalar FieldSet +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/federation_spec/federation-v2.9.graphql b/federation_spec/federation-v2.9.graphql new file mode 100644 index 0000000..5b52db7 --- /dev/null +++ b/federation_spec/federation-v2.9.graphql @@ -0,0 +1,68 @@ +directive @composeDirective(name: String!) repeatable on SCHEMA +directive @extends on OBJECT | INTERFACE +directive @external on OBJECT | FIELD_DEFINITION +directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE +directive @inaccessible on + | FIELD_DEFINITION + | OBJECT + | INTERFACE + | UNION + | ENUM + | ENUM_VALUE + | SCALAR + | INPUT_OBJECT + | INPUT_FIELD_DEFINITION + | ARGUMENT_DEFINITION +directive @interfaceObject on OBJECT +directive @override(from: String!, label: String) on FIELD_DEFINITION +directive @provides(fields: FieldSet!) on FIELD_DEFINITION +directive @requires(fields: FieldSet!) on FIELD_DEFINITION +directive @shareable repeatable on FIELD_DEFINITION | OBJECT +directive @tag(name: String!) repeatable on + | FIELD_DEFINITION + | INTERFACE + | OBJECT + | UNION + | ARGUMENT_DEFINITION + | SCALAR + | ENUM + | ENUM_VALUE + | INPUT_OBJECT + | INPUT_FIELD_DEFINITION +directive @authenticated on + FIELD_DEFINITION + | OBJECT + | INTERFACE + | SCALAR + | ENUM +directive @requiresScopes(scopes: [[federation__Scope!]!]!) on + FIELD_DEFINITION + | OBJECT + | INTERFACE + | SCALAR + | ENUM +directive @policy(policies: [[federation__Policy!]!]!) on + | FIELD_DEFINITION + | OBJECT + | INTERFACE + | SCALAR + | ENUM +directive @context(name: String!) on OBJECT | INTERFACE | UNION +directive @fromContext(field: federation__ContextFieldValue) on ARGUMENT_DEFINITION +directive @listSize( + assumedSize: Int + slicingArguments: [String!] + sizedFields: [String!] + requireOneSlicingArgument: Boolean = true +) on FIELD_DEFINITION +directive @cost(weight: Int!) on + ARGUMENT_DEFINITION + | FIELD_DEFINITION + | INPUT_FIELD_DEFINITION + | OBJECT + | SCALAR + | ENUM +scalar federation__Policy +scalar federation__Scope +scalar FieldSet +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/graphene_federation/__init__.py b/graphene_federation/__init__.py index 5ba433d..35accda 100644 --- a/graphene_federation/__init__.py +++ b/graphene_federation/__init__.py @@ -4,11 +4,15 @@ from .composable_directive import ComposableDirective from .directives import ( authenticated, + context, + cost, extends, external, + from_context, inaccessible, interface_object, key, + list_size, override, policy, provides, @@ -29,11 +33,15 @@ "ComposableDirective", "DirectiveLocation", "authenticated", + "context", + "cost", "extends", "external", + "from_context", "inaccessible", "interface_object", "key", + "list_size", "override", "provides", "policy", diff --git a/graphene_federation/apollo_versions/__init__.py b/graphene_federation/apollo_versions/__init__.py index 46e398f..fec2211 100644 --- a/graphene_federation/apollo_versions/__init__.py +++ b/graphene_federation/apollo_versions/__init__.py @@ -9,12 +9,16 @@ from .v2_5 import get_directives as get_directives_v2_5 from .v2_6 import get_directives as get_directives_v2_6 from .v2_7 import get_directives as get_directives_v2_7 +from .v2_8 import get_directives as get_directives_v2_8 +from .v2_9 import get_directives as get_directives_v2_9 +from .v2_10 import get_directives as get_directives_v2_10 +from .v2_11 import get_directives as get_directives_v2_11 from .version import FederationVersion -LATEST_VERSION = FederationVersion.VERSION_2_7 +LATEST_VERSION = FederationVersion.VERSION_2_11 # Stable version is determined with the latest version that rover cli supports -STABLE_VERSION = FederationVersion.VERSION_2_6 +STABLE_VERSION = FederationVersion.VERSION_2_11 def get_directives_based_on_version( @@ -43,8 +47,16 @@ def get_directives_based_on_version( return get_directives_v2_6() if federation_version == FederationVersion.VERSION_2_7: return get_directives_v2_7() + if federation_version == FederationVersion.VERSION_2_8: + return get_directives_v2_8() + if federation_version == FederationVersion.VERSION_2_9: + return get_directives_v2_9() + if federation_version == FederationVersion.VERSION_2_10: + return get_directives_v2_10() + if federation_version == FederationVersion.VERSION_2_11: + return get_directives_v2_11() - return get_directives_v2_7() + return get_directives_v2_11() def get_directive_from_name( diff --git a/graphene_federation/apollo_versions/v1_0.py b/graphene_federation/apollo_versions/v1_0.py index 672924e..23b9836 100644 --- a/graphene_federation/apollo_versions/v1_0.py +++ b/graphene_federation/apollo_versions/v1_0.py @@ -2,7 +2,7 @@ from graphql import GraphQLArgument, GraphQLDirective, GraphQLNonNull from graphene_federation.scalars import _FieldSet -from graphene_federation.transform import field_set_case_transform +from graphene_federation.transform import fields_set_case_transform from graphene_federation.validators import ( validate_key, validate_provides, @@ -20,7 +20,7 @@ is_repeatable=True, add_definition_to_schema=False, non_field_validator=validate_key, - input_transform=field_set_case_transform, + input_transform=fields_set_case_transform, ) requires_directive = CustomDirective( @@ -32,7 +32,7 @@ description="Federation @requires directive", add_definition_to_schema=False, field_validator=validate_requires, - input_transform=field_set_case_transform, + input_transform=fields_set_case_transform, ) @@ -45,7 +45,7 @@ description="Federation @provides directive", add_definition_to_schema=False, field_validator=validate_provides, - input_transform=field_set_case_transform, + input_transform=fields_set_case_transform, ) external_directive = CustomDirective( diff --git a/graphene_federation/apollo_versions/v2_0.py b/graphene_federation/apollo_versions/v2_0.py index 827d953..7f41cf0 100644 --- a/graphene_federation/apollo_versions/v2_0.py +++ b/graphene_federation/apollo_versions/v2_0.py @@ -8,7 +8,7 @@ ) from graphene_federation.scalars import FieldSet -from graphene_federation.transform import field_set_case_transform +from graphene_federation.transform import fields_set_case_transform from graphene_federation.validators import ( validate_key, validate_provides, @@ -31,7 +31,7 @@ is_repeatable=True, add_definition_to_schema=False, non_field_validator=validate_key, - input_transform=field_set_case_transform, + input_transform=fields_set_case_transform, ) requires_directive = CustomDirective( @@ -45,7 +45,7 @@ description="Federation @requires directive", add_definition_to_schema=False, field_validator=validate_requires, - input_transform=field_set_case_transform, + input_transform=fields_set_case_transform, ) @@ -60,7 +60,7 @@ description="Federation @provides directive", add_definition_to_schema=False, field_validator=validate_provides, - input_transform=field_set_case_transform, + input_transform=fields_set_case_transform, ) diff --git a/graphene_federation/apollo_versions/v2_10.py b/graphene_federation/apollo_versions/v2_10.py new file mode 100644 index 0000000..e9d22dc --- /dev/null +++ b/graphene_federation/apollo_versions/v2_10.py @@ -0,0 +1,8 @@ +from graphql import GraphQLDirective + +from .v2_9 import get_directives as get_directives_v2_9 + + +# No Change +def get_directives() -> dict[str, GraphQLDirective]: + return get_directives_v2_9() diff --git a/graphene_federation/apollo_versions/v2_11.py b/graphene_federation/apollo_versions/v2_11.py new file mode 100644 index 0000000..b41f1e6 --- /dev/null +++ b/graphene_federation/apollo_versions/v2_11.py @@ -0,0 +1,8 @@ +from graphql import GraphQLDirective + +from .v2_10 import get_directives as get_directives_v2_10 + + +# No Change +def get_directives() -> dict[str, GraphQLDirective]: + return get_directives_v2_10() diff --git a/graphene_federation/apollo_versions/v2_5.py b/graphene_federation/apollo_versions/v2_5.py index 190e7de..d569284 100644 --- a/graphene_federation/apollo_versions/v2_5.py +++ b/graphene_federation/apollo_versions/v2_5.py @@ -2,7 +2,7 @@ from graphql import GraphQLArgument, GraphQLDirective, GraphQLList, GraphQLNonNull from .v2_4 import get_directives as get_directives_v2_4 -from graphene_federation.scalars import FederationScope +from graphene_federation.scalars import Scope authenticated_directive = CustomDirective( name="authenticated", @@ -29,9 +29,7 @@ args={ "scopes": GraphQLArgument( GraphQLNonNull( - GraphQLList( - GraphQLNonNull(GraphQLList(GraphQLNonNull(FederationScope))) - ) + GraphQLList(GraphQLNonNull(GraphQLList(GraphQLNonNull(Scope)))) ) ), }, diff --git a/graphene_federation/apollo_versions/v2_6.py b/graphene_federation/apollo_versions/v2_6.py index bf62d36..87c6efb 100644 --- a/graphene_federation/apollo_versions/v2_6.py +++ b/graphene_federation/apollo_versions/v2_6.py @@ -2,7 +2,7 @@ from graphql import GraphQLArgument, GraphQLDirective, GraphQLList, GraphQLNonNull from .v2_5 import get_directives as get_directives_v2_5 -from graphene_federation.scalars import FederationPolicy +from graphene_federation.scalars import Policy policy_directive = CustomDirective( name="policy", @@ -16,9 +16,7 @@ args={ "policies": GraphQLArgument( GraphQLNonNull( - GraphQLList( - GraphQLNonNull(GraphQLList(GraphQLNonNull(FederationPolicy))) - ) + GraphQLList(GraphQLNonNull(GraphQLList(GraphQLNonNull(Policy)))) ) ), }, diff --git a/graphene_federation/apollo_versions/v2_8.py b/graphene_federation/apollo_versions/v2_8.py new file mode 100644 index 0000000..9472e5a --- /dev/null +++ b/graphene_federation/apollo_versions/v2_8.py @@ -0,0 +1,46 @@ +from graphene_directives import CustomDirective, DirectiveLocation +from graphql import GraphQLArgument, GraphQLDirective, GraphQLNonNull, GraphQLString + +from graphene_federation.scalars import ContextFieldValue +from graphene_federation.transform import context_field_set_case_transform +from .v2_7 import get_directives as get_directives_v2_7 + +context_directive = CustomDirective( + name="context", + locations=[ + DirectiveLocation.OBJECT, + DirectiveLocation.INTERFACE, + DirectiveLocation.UNION, + ], + args={ + "name": GraphQLArgument(GraphQLNonNull(GraphQLString)), + }, + description="Federation @context directive", + add_definition_to_schema=False, + is_repeatable=True, +) + +from_context_directive = CustomDirective( + name="from_context", + locations=[ + DirectiveLocation.ARGUMENT_DEFINITION, + ], + args={ + "field": GraphQLArgument(ContextFieldValue), + }, + description="Federation @fromContext directive", + add_definition_to_schema=False, + input_transform=context_field_set_case_transform, +) + + +# Added directives @context, @from_context +def get_directives() -> dict[str, GraphQLDirective]: + directives = get_directives_v2_7() + directives.update( + { + directive.name: directive + for directive in [context_directive, from_context_directive] + } + ) + return directives diff --git a/graphene_federation/apollo_versions/v2_9.py b/graphene_federation/apollo_versions/v2_9.py new file mode 100644 index 0000000..f926bca --- /dev/null +++ b/graphene_federation/apollo_versions/v2_9.py @@ -0,0 +1,58 @@ +from graphene_directives import CustomDirective, DirectiveLocation +from graphql import ( + GraphQLArgument, + GraphQLBoolean, + GraphQLDirective, + GraphQLInt, + GraphQLList, + GraphQLNonNull, + GraphQLString, +) + +from .v2_8 import get_directives as get_directives_v2_8 + +list_size_directive = CustomDirective( + name="listSize", + locations=[ + DirectiveLocation.FIELD_DEFINITION, + ], + args={ + "assumed_size": GraphQLArgument(GraphQLInt), + "slicing_arguments": GraphQLArgument( + GraphQLList(GraphQLNonNull(GraphQLString)) + ), + "sized_fields": GraphQLArgument(GraphQLList(GraphQLNonNull(GraphQLString))), + "require_one_slicing_argument": GraphQLArgument(GraphQLBoolean), + }, + description="Federation @listSize directive", + add_definition_to_schema=False, +) + +requires_scope_directive = CustomDirective( + name="cost", + locations=[ + DirectiveLocation.ARGUMENT_DEFINITION, + DirectiveLocation.FIELD_DEFINITION, + DirectiveLocation.INPUT_FIELD_DEFINITION, + DirectiveLocation.OBJECT, + DirectiveLocation.SCALAR, + DirectiveLocation.ENUM, + ], + args={ + "weight": GraphQLArgument(GraphQLNonNull(GraphQLInt)), + }, + description="Federation @cost directive", + add_definition_to_schema=False, +) + + +# Added @listSize, @cost +def get_directives() -> dict[str, GraphQLDirective]: + directives = get_directives_v2_8() + directives.update( + { + directive.name: directive + for directive in [list_size_directive, requires_scope_directive] + } + ) + return directives diff --git a/graphene_federation/apollo_versions/version.py b/graphene_federation/apollo_versions/version.py index fc01edb..c074619 100644 --- a/graphene_federation/apollo_versions/version.py +++ b/graphene_federation/apollo_versions/version.py @@ -11,3 +11,7 @@ class FederationVersion(Enum): VERSION_2_5 = "2.5" VERSION_2_6 = "2.6" VERSION_2_7 = "2.7" + VERSION_2_8 = "2.8" + VERSION_2_9 = "2.9" + VERSION_2_10 = "2.10" + VERSION_2_11 = "2.11" diff --git a/graphene_federation/composable_directive.py b/graphene_federation/composable_directive.py index 1055a1c..7d2c1fd 100644 --- a/graphene_federation/composable_directive.py +++ b/graphene_federation/composable_directive.py @@ -18,7 +18,7 @@ def __init__( description: Optional[str] = None, extensions: Optional[Dict[str, Any]] = None, ast_node: Optional[DirectiveDefinitionNode] = None, - spec_url: str = None, + spec_url: Optional[str] = None, add_to_schema_directives: bool = True, ) -> None: """ diff --git a/graphene_federation/directives/__init__.py b/graphene_federation/directives/__init__.py index c07e952..ca31062 100644 --- a/graphene_federation/directives/__init__.py +++ b/graphene_federation/directives/__init__.py @@ -1,9 +1,13 @@ from .authenticated import authenticated +from .context import context +from .cost import cost from .extends import extends from .external import external +from .from_context import from_context from .inaccessible import inaccessible from .interface_object import interface_object from .key import key +from .list_size import list_size from .override import override from .policy import policy from .provides import provides diff --git a/graphene_federation/directives/context.py b/graphene_federation/directives/context.py new file mode 100644 index 0000000..232545c --- /dev/null +++ b/graphene_federation/directives/context.py @@ -0,0 +1,51 @@ +from typing import Any + +from graphene_directives import directive_decorator + +from graphene_federation.apollo_versions import ( + FederationVersion, + LATEST_VERSION, + get_directive_from_name, +) +from .utils import is_non_field + + +def context( + name: str, + *, + federation_version: FederationVersion = LATEST_VERSION, +) -> Any: + """ + The @context directive defines a named context from which a field of the annotated type can be passed to a receiver + of the context. The receiver must be a field annotated with the @fromContext directive. + + Reference: + https://www.apollographql.com/docs/graphos/schema-design/federated-schemas/reference/directives#context + + https://www.apollographql.com/docs/graphos/schema-design/federated-schemas/entities/use-contexts + """ + directive = get_directive_from_name("context", federation_version) + decorator = directive_decorator(directive) + + if "_" in name: + raise ValueError( + f"Invalid name for @context directive. Name must not contain '_' character." + ) + + def wrapper(field_or_type): + if is_non_field(field_or_type): + return decorator(field=None, name=name)(field_or_type) + raise TypeError( + "\n".join( + [ + f"\nInvalid Usage of {directive}.", + "Must be applied on a class of ObjectType|InterfaceType|UnionType", + "Example:", + f"{directive}", + "class Product(graphene.ObjectType)", + "\t...", + ] + ) + ) + + return wrapper diff --git a/graphene_federation/directives/cost.py b/graphene_federation/directives/cost.py new file mode 100644 index 0000000..003b1cf --- /dev/null +++ b/graphene_federation/directives/cost.py @@ -0,0 +1,43 @@ +from typing import Callable + +from graphene_directives import directive_decorator + +from graphene_federation.apollo_versions import ( + FederationVersion, + LATEST_VERSION, + get_directive_from_name, +) +from .utils import is_non_field + + +def cost( + graphene_type=None, + *, + weight: int, + federation_version: FederationVersion = LATEST_VERSION, +) -> Callable: + """ + The @cost directive defines a custom weight for a schema location. + For GraphOS Router, it customizes the operation cost calculation of the demand control feature. + + If @cost is not specified for a field, a default value is used: + - Scalars and enums have default cost of 0 + - Composite input and output types have default cost of 1 + + Regardless of whether @cost is specified on a field, the field cost for that field also accounts for its arguments + and selections. + + Reference: https://www.apollographql.com/docs/graphos/schema-design/federated-schemas/reference/directives#cost + """ + directive = get_directive_from_name("cost", federation_version=federation_version) + decorator = directive_decorator(directive) + + def wrapper(field_or_type): + if is_non_field(field_or_type): + return decorator(field=None, weight=weight)(field_or_type) + return decorator(field=field_or_type, weight=weight) + + if graphene_type: + return wrapper(graphene_type) + + return wrapper diff --git a/graphene_federation/directives/from_context.py b/graphene_federation/directives/from_context.py new file mode 100644 index 0000000..85c3fd3 --- /dev/null +++ b/graphene_federation/directives/from_context.py @@ -0,0 +1,73 @@ +from typing import Callable + +from graphene_directives import directive_decorator + +from graphene_federation.apollo_versions import ( + FederationVersion, + LATEST_VERSION, + get_directive_from_name, +) +from graphene_federation.validators import ( + InternalNamespace, + ast_to_str, + build_ast, + validate_from_context_field_str, +) +from .utils import is_non_field + + +def from_context( + graphene_type, + field: str, + *, + auto_case: bool = True, + federation_version: FederationVersion = LATEST_VERSION, +) -> Callable: + """ + A `@fromContext` directive must be used as an argument on a field. + Its field value—the `ContextFieldValue` scalar—must contain the name of a defined context and + a selection of a field from the context's type. + + - The first element must be the name of a context defined by `@context` and prefixed with `$`. + For example, `$myContext`. This is the only context that can be referenced by the annotated field. + - The second element must be a selection set that resolves to a single field. + - Top-level type conditions must not overlap with one another so that the field can be resolved to a single value. + - All fields referenced in the ContextFieldValue must be expressed within the current subgraph. + If the fields are referenced across multiple subgraphs, they must be annotated with @external. + + Reference: https://www.apollographql.com/docs/graphos/schema-design/federated-schemas/reference/directives#fromcontext + """ + directive = get_directive_from_name("from_context", federation_version) + decorator = directive_decorator(directive) + + field = validate_from_context_field_str(directive, field) + + if not auto_case: + field = f"{InternalNamespace.NO_AUTO_CASE.value} {field}" + + def wrapper(field_or_type): + if is_non_field(field_or_type): + raise TypeError( + "\n".join( + [ + f"\nInvalid Usage of {directive}.", + "Must be applied on a field argument level", + "Example:", + "class Query(graphene.ObjectType)", + """\tproductQuery = graphene.Field( + ProductType, + category=from_context( + graphene.Argument(graphene.Int), + field="$context1 { some_int_field }" + ), + ) + """, + ] + ) + ) + return decorator(field=field_or_type, args_via_dict={"field": field}) + + if graphene_type: + return wrapper(graphene_type) + + return wrapper diff --git a/graphene_federation/directives/key.py b/graphene_federation/directives/key.py index 1f7b812..de5a2ae 100644 --- a/graphene_federation/directives/key.py +++ b/graphene_federation/directives/key.py @@ -1,4 +1,4 @@ -from typing import Any, Union +from typing import Any, Optional, Union from graphene_directives import directive_decorator @@ -13,9 +13,9 @@ def key( fields: Union[str, list[str]], - resolvable: bool = None, + resolvable: Optional[bool] = None, *, - auto_case: bool = True, + auto_case: Optional[bool] = True, federation_version: FederationVersion = LATEST_VERSION, ) -> Any: """ diff --git a/graphene_federation/directives/list_size.py b/graphene_federation/directives/list_size.py new file mode 100644 index 0000000..8e5e4a0 --- /dev/null +++ b/graphene_federation/directives/list_size.py @@ -0,0 +1,58 @@ +from typing import Callable, Optional + +from graphene_directives import directive_decorator + +from graphene_federation.apollo_versions import ( + FederationVersion, + LATEST_VERSION, + get_directive_from_name, +) +from .utils import is_non_field + + +def list_size( + graphene_type, + assumed_size: Optional[int] = None, + slicing_arguments: Optional[list[str]] = None, + sized_fields: Optional[list[str]] = None, + require_one_slicing_argument: Optional[bool] = None, + *, + federation_version: FederationVersion = LATEST_VERSION, +) -> Callable: + """ + The @listSize directive is used to customize the cost calculation of the demand control feature of GraphOS Router. + + Reference: https://www.apollographql.com/docs/graphos/schema-design/federated-schemas/reference/directives#listField + """ + directive = get_directive_from_name( + "listSize", federation_version=federation_version + ) + decorator = directive_decorator(directive) + + def wrapper(field_or_type): + if is_non_field(field_or_type): + raise TypeError( + "\n".join( + [ + f"\nInvalid Usage of {directive}.", + "Must be applied on a field level", + "Example:", + "class Query(graphene.ObjectType)", + "\tproductsQuery = list_field(graphene.List(ProductType), assumed_size=10)", + ] + ) + ) + return decorator( + field=field_or_type, + **{ + "assumed_size": assumed_size, + "slicing_arguments": slicing_arguments, + "sized_fields": sized_fields, + "require_one_slicing_argument": require_one_slicing_argument, + }, + ) + + if graphene_type: + return wrapper(graphene_type) + + return wrapper diff --git a/graphene_federation/directives/override.py b/graphene_federation/directives/override.py index ce33447..004413d 100644 --- a/graphene_federation/directives/override.py +++ b/graphene_federation/directives/override.py @@ -1,4 +1,4 @@ -from typing import Callable +from typing import Callable, Optional from graphene_directives import directive_decorator @@ -13,7 +13,7 @@ def override( graphene_type, from_: str, - label: str = None, + label: Optional[str] = None, *, federation_version: FederationVersion = LATEST_VERSION, ) -> Callable: diff --git a/graphene_federation/scalars/__init__.py b/graphene_federation/scalars/__init__.py index 3bb899a..b170941 100644 --- a/graphene_federation/scalars/__init__.py +++ b/graphene_federation/scalars/__init__.py @@ -1,6 +1,7 @@ from ._any import _Any -from .federation_policy import FederationPolicy -from .federation_scope import FederationScope +from .federation_context_field_value import ContextFieldValue +from .federation_policy import Policy +from .federation_scope import Scope from .field_set_v1 import _FieldSet from .field_set_v2 import FieldSet from .link_import import link_import diff --git a/graphene_federation/scalars/federation_context_field_value.py b/graphene_federation/scalars/federation_context_field_value.py new file mode 100644 index 0000000..ae76ba8 --- /dev/null +++ b/graphene_federation/scalars/federation_context_field_value.py @@ -0,0 +1,13 @@ +from graphql import GraphQLScalarType + +# Reference: +# https://www.apollographql.com/docs/graphos/schema-design/federated-schemas/reference/directives#fromcontext +# https://www.apollographql.com/docs/graphos/schema-design/federated-schemas/entities/use-contexts +ContextFieldValue = GraphQLScalarType( + name="federation__ContextFieldValue", + description=" ".join( + ( + "Contains the name of a defined context and a selection of a field from the context's type", + ) + ), +) diff --git a/graphene_federation/scalars/federation_policy.py b/graphene_federation/scalars/federation_policy.py index d4934ed..af09cc5 100644 --- a/graphene_federation/scalars/federation_policy.py +++ b/graphene_federation/scalars/federation_policy.py @@ -44,7 +44,7 @@ def _parse_string_literal(value_node: ValueNode, _variables: Any = None) -> str: # Reference: https://www.apollographql.com/docs/federation/subgraph-spec/ -FederationPolicy = GraphQLScalarType( +Policy = GraphQLScalarType( name="federation__Policy", description="This string-serialized scalar represents an authorization policy.", serialize=_serialize_string, diff --git a/graphene_federation/scalars/federation_scope.py b/graphene_federation/scalars/federation_scope.py index 0ce7c9c..28a9a9b 100644 --- a/graphene_federation/scalars/federation_scope.py +++ b/graphene_federation/scalars/federation_scope.py @@ -40,7 +40,7 @@ def _parse_string_literal(value_node: ValueNode, _variables: Any = None) -> str: # Reference: https://www.apollographql.com/docs/federation/subgraph-spec/ -FederationScope = GraphQLScalarType( +Scope = GraphQLScalarType( name="federation__Scope", description="This string-serialized scalar represents a JWT scope", serialize=_serialize_string, diff --git a/graphene_federation/schema.py b/graphene_federation/schema.py index 62c96fb..91423f6 100644 --- a/graphene_federation/schema.py +++ b/graphene_federation/schema.py @@ -64,15 +64,15 @@ def _add_sharable_to_page_info_type( def build_schema( - query: Union[ObjectType, Type[ObjectType]] = None, - mutation: Union[ObjectType, Type[ObjectType]] = None, - subscription: Union[ObjectType, Type[ObjectType]] = None, - types: Collection[Union[ObjectType, Type[ObjectType]]] = None, - directives: Union[Collection[ComposableDirective], None] = None, - include_graphql_spec_directives: bool = True, - schema_directives: Collection[SchemaDirective] = None, + query: Optional[Union[ObjectType, Type[ObjectType]]] = None, + mutation: Optional[Union[ObjectType, Type[ObjectType]]] = None, + subscription: Optional[Union[ObjectType, Type[ObjectType]]] = None, + types: Optional[Collection[Union[ObjectType, Type[ObjectType]]]] = None, + directives: Optional[Union[Collection[ComposableDirective], None]] = None, + include_graphql_spec_directives: Optional[bool] = True, + schema_directives: Optional[Collection[SchemaDirective]] = None, auto_camelcase: bool = True, - federation_version: FederationVersion = None, + federation_version: Optional[FederationVersion] = None, ) -> Schema: """ Build Schema. diff --git a/graphene_federation/transform/__init__.py b/graphene_federation/transform/__init__.py index b03eece..7a31230 100644 --- a/graphene_federation/transform/__init__.py +++ b/graphene_federation/transform/__init__.py @@ -1 +1,2 @@ -from .field_set_case_transform import field_set_case_transform +from .context_field_set_case_transform import context_field_set_case_transform +from .fields_set_case_transform import fields_set_case_transform diff --git a/graphene_federation/transform/context_field_set_case_transform.py b/graphene_federation/transform/context_field_set_case_transform.py new file mode 100644 index 0000000..df70633 --- /dev/null +++ b/graphene_federation/transform/context_field_set_case_transform.py @@ -0,0 +1,23 @@ +from graphene_directives import Schema + +from graphene_federation.validators import InternalNamespace, to_case + + +def context_field_set_case_transform(inputs: dict, schema: Schema) -> dict: + """ + Transform the fields from internal representation to schema representation + + Internal representation uses + __union__ for representing ... on + __arg__ for representing (arg1: value1, arg2: value2) + """ + field = inputs.get("field") + auto_case = InternalNamespace.NO_AUTO_CASE.value not in inputs.get("field", ()) + if field: + inputs["field"] = ( + to_case(field, schema, auto_case) + .replace(InternalNamespace.UNION.value, "... on") + .replace(InternalNamespace.ARG.value, "") + .replace(InternalNamespace.NO_AUTO_CASE.value, "") + ) + return inputs diff --git a/graphene_federation/transform/field_set_case_transform.py b/graphene_federation/transform/fields_set_case_transform.py similarity index 91% rename from graphene_federation/transform/field_set_case_transform.py rename to graphene_federation/transform/fields_set_case_transform.py index c2f3416..9775088 100644 --- a/graphene_federation/transform/field_set_case_transform.py +++ b/graphene_federation/transform/fields_set_case_transform.py @@ -3,7 +3,7 @@ from graphene_federation.validators import InternalNamespace, to_case -def field_set_case_transform(inputs: dict, schema: Schema) -> dict: +def fields_set_case_transform(inputs: dict, schema: Schema) -> dict: """ Transform the fields from internal representation to schema representation diff --git a/graphene_federation/validators/__init__.py b/graphene_federation/validators/__init__.py index 9f13202..75551c8 100644 --- a/graphene_federation/validators/__init__.py +++ b/graphene_federation/validators/__init__.py @@ -1,3 +1,4 @@ +from .from_context import validate_from_context_field_str from .key import validate_key from .provides import validate_provides from .requires import validate_requires diff --git a/graphene_federation/validators/from_context.py b/graphene_federation/validators/from_context.py new file mode 100644 index 0000000..189e27e --- /dev/null +++ b/graphene_federation/validators/from_context.py @@ -0,0 +1,39 @@ +from graphql import GraphQLDirective + +from .utils import ast_to_str, build_ast + + +def validate_from_context_field_str(directive: GraphQLDirective, field: str) -> str: + """ + Used to validate the context field str + + Raises: + ValueError + + Returns validated and parsed context field str + """ + ast_node = build_ast( + fields=field if isinstance(field, str) else " ".join(field), + directive_name=str(directive), + additional_valid_special_characters={"$"}, + ) + + errors = [] + found_context = False + context_has_underscore = False + for token in ast_node: + if token.startswith("$"): + found_context = True + if "_" in token: + context_has_underscore = True + break + + if not found_context: + errors.append("One context must be specified") + if context_has_underscore: + errors.append('Context name cannot contain "_"') + + if errors: + raise ValueError("\n".join(errors)) + + return ast_to_str(ast_node) diff --git a/graphene_federation/validators/utils.py b/graphene_federation/validators/utils.py index a812fae..03e3543 100644 --- a/graphene_federation/validators/utils.py +++ b/graphene_federation/validators/utils.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import Union +from typing import Optional, Union from graphene import Field, Interface, NonNull, ObjectType from graphene.types.definitions import ( @@ -139,10 +139,13 @@ def get_type_for_field( AST FUNCTIONS For @key, @provides, @requires FieldSet Parsing +For @fromContext Parsing """ -def _tokenize_field_set(fields: str, directive_name: str) -> list[str]: +def _tokenize_field_set( + fields: str, directive_name: str, additional_valid_special_characters: set[str] +) -> list[str]: """ Splits the fields string to tokens """ @@ -217,6 +220,8 @@ def _tokenize_field_set(fields: str, directive_name: str) -> list[str]: elif current_token: tokens.append(current_token) current_token = "" + elif char in additional_valid_special_characters: + current_token += char else: if current_token: tokens.append(current_token) @@ -293,14 +298,24 @@ def evaluate_ast( ) -def build_ast(fields: str, directive_name: str) -> dict: +def build_ast( + fields: str, + directive_name: str, + additional_valid_special_characters: Optional[set[str]] = None, +) -> dict: """ Converts the fields string to an AST tree :param fields: string fields :param directive_name: name of the directive + :param additional_valid_special_characters: additional valid special characters """ - cleaned_fields = _tokenize_field_set(fields, directive_name) + if additional_valid_special_characters is None: + additional_valid_special_characters = set() + + cleaned_fields = _tokenize_field_set( + fields, directive_name, additional_valid_special_characters + ) parent: dict[str, dict] = {} field_stack: list[str] = [] diff --git a/integration_tests/docker-compose.yml b/integration_tests/docker-compose.yml index 853d37c..18d205c 100644 --- a/integration_tests/docker-compose.yml +++ b/integration_tests/docker-compose.yml @@ -1,4 +1,3 @@ -version: '2.1' services: service_a: build: service_a/. diff --git a/setup.py b/setup.py index ae99c3a..56ce62d 100644 --- a/setup.py +++ b/setup.py @@ -7,17 +7,17 @@ def read(*rnames): return open(os.path.join(os.path.dirname(__file__), *rnames)).read() -version = "3.2.0" +version = "3.3.0" tests_require = [ - "pytest==7.1.2", + "pytest==8.4.2", "pytest-cov", ] dev_require = [ "black==23.12.1", - "flake8==4.0.1", - "mypy==0.961", + "flake8==7.3.0", + "mypy==1.18.2", ] + tests_require setup( @@ -36,19 +36,18 @@ def read(*rnames): install_requires=[ "graphene>=3.1", "graphql-core>=3.1", - "graphene-directives>=0.4.6", + "graphene-directives>=0.5.0", ], classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ], extras_require={ "test": tests_require, diff --git a/tests/gql/test_annotation_corner_cases/test_annotate_object_with_meta_name_1.graphql b/tests/gql/test_annotation_corner_cases/test_annotate_object_with_meta_name_1.graphql index b89ff4a..fb4b41a 100644 --- a/tests/gql/test_annotation_corner_cases/test_annotate_object_with_meta_name_1.graphql +++ b/tests/gql/test_annotation_corner_cases/test_annotate_object_with_meta_name_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key"]) type Query { a: Banana @@ -33,4 +33,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_annotation_corner_cases/test_annotate_object_with_meta_name_2.graphql b/tests/gql/test_annotation_corner_cases/test_annotate_object_with_meta_name_2.graphql index 562cbf8..07f61ee 100644 --- a/tests/gql/test_annotation_corner_cases/test_annotate_object_with_meta_name_2.graphql +++ b/tests/gql/test_annotation_corner_cases/test_annotate_object_with_meta_name_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key"]) type Query { a: Banana @@ -23,4 +23,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_annotation_corner_cases/test_annotated_field_also_used_in_filter_1.graphql b/tests/gql/test_annotation_corner_cases/test_annotated_field_also_used_in_filter_1.graphql index b1c768d..3bf2a67 100644 --- a/tests/gql/test_annotation_corner_cases/test_annotated_field_also_used_in_filter_1.graphql +++ b/tests/gql/test_annotation_corner_cases/test_annotated_field_also_used_in_filter_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key"]) type Query { a: A @@ -33,4 +33,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_annotation_corner_cases/test_annotated_field_also_used_in_filter_2.graphql b/tests/gql/test_annotation_corner_cases/test_annotated_field_also_used_in_filter_2.graphql index a19de00..1c9d7e6 100644 --- a/tests/gql/test_annotation_corner_cases/test_annotated_field_also_used_in_filter_2.graphql +++ b/tests/gql/test_annotation_corner_cases/test_annotated_field_also_used_in_filter_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key"]) type Query { a: A @@ -23,4 +23,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_1.graphql b/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_1.graphql index 53b2a74..5f0b712 100644 --- a/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_1.graphql +++ b/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key", "@requires"]) type Query { camel: Camel @@ -31,4 +31,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_2.graphql b/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_2.graphql index f57b03e..c3c984f 100644 --- a/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_2.graphql +++ b/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key", "@requires"]) type Query { camel: Camel @@ -21,4 +21,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_without_auto_camelcase_1.graphql b/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_without_auto_camelcase_1.graphql index 45dddc2..28e25f7 100644 --- a/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_without_auto_camelcase_1.graphql +++ b/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_without_auto_camelcase_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@requires"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@requires"]) type Query { camel: Camel @@ -31,4 +31,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_without_auto_camelcase_2.graphql b/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_without_auto_camelcase_2.graphql index 8edc83c..4f0cbb4 100644 --- a/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_without_auto_camelcase_2.graphql +++ b/tests/gql/test_annotation_corner_cases/test_camel_case_field_name_without_auto_camelcase_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@requires"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@requires"]) type Query { camel: Camel @@ -21,4 +21,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_annotation_corner_cases/test_similar_field_name_1.graphql b/tests/gql/test_annotation_corner_cases/test_similar_field_name_1.graphql index c02d8d3..a5fae1d 100644 --- a/tests/gql/test_annotation_corner_cases/test_similar_field_name_1.graphql +++ b/tests/gql/test_annotation_corner_cases/test_similar_field_name_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key"]) schema { query: ChatQuery @@ -41,4 +41,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_annotation_corner_cases/test_similar_field_name_2.graphql b/tests/gql/test_annotation_corner_cases/test_similar_field_name_2.graphql index 0bc8d56..c1fd4a7 100644 --- a/tests/gql/test_annotation_corner_cases/test_similar_field_name_2.graphql +++ b/tests/gql/test_annotation_corner_cases/test_similar_field_name_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key"]) schema { query: ChatQuery @@ -31,4 +31,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_custom_enum/test_custom_enum_1.graphql b/tests/gql/test_custom_enum/test_custom_enum_1.graphql index dc39872..ad5a096 100644 --- a/tests/gql/test_custom_enum/test_custom_enum_1.graphql +++ b/tests/gql/test_custom_enum/test_custom_enum_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible", "@shareable"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@inaccessible", "@shareable"]) type TestCustomEnum @shareable { testShareableScalar: Episode @shareable @@ -31,4 +31,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_custom_enum/test_custom_enum_2.graphql b/tests/gql/test_custom_enum/test_custom_enum_2.graphql index e9f3d2e..9cfb00f 100644 --- a/tests/gql/test_custom_enum/test_custom_enum_2.graphql +++ b/tests/gql/test_custom_enum/test_custom_enum_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible", "@shareable"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@inaccessible", "@shareable"]) type TestCustomEnum @shareable { testShareableScalar: Episode @shareable @@ -26,4 +26,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_from_context/test_from_context_with_input_1.graphql b/tests/gql/test_from_context/test_from_context_with_input_1.graphql new file mode 100644 index 0000000..b7b9033 --- /dev/null +++ b/tests/gql/test_from_context/test_from_context_with_input_1.graphql @@ -0,0 +1,33 @@ +extend schema + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@from_context"]) + +type Query { + acme: Acme + _service: _Service! +} + +type Acme { + id: ID! + age: Int + foo(someInput: String @from_context(field: "$context1 ... on A { someField } ... on B { someField } ... on C { someOtherField }")): String +} + +type _Service { + sdl: String +} + +""" +A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides +""" +scalar FieldSet + +"""This string-serialized scalar represents a JWT scope""" +scalar federation__Scope + +"""This string-serialized scalar represents an authorization policy.""" +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_from_context/test_from_context_with_input_2.graphql b/tests/gql/test_from_context/test_from_context_with_input_2.graphql new file mode 100644 index 0000000..21c514b --- /dev/null +++ b/tests/gql/test_from_context/test_from_context_with_input_2.graphql @@ -0,0 +1,28 @@ +extend schema + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@from_context"]) + +type Query { + acme: Acme +} + +type Acme { + id: ID! + age: Int + foo(someInput: String @from_context(field: "$context1 ... on A { someField } ... on B { someField } ... on C { someOtherField }")): String +} + +""" +A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides +""" +scalar FieldSet + +"""This string-serialized scalar represents a JWT scope""" +scalar federation__Scope + +"""This string-serialized scalar represents an authorization policy.""" +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_inaccessible/test_inaccessible_1.graphql b/tests/gql/test_inaccessible/test_inaccessible_1.graphql index adbf745..ae5e166 100644 --- a/tests/gql/test_inaccessible/test_inaccessible_1.graphql +++ b/tests/gql/test_inaccessible/test_inaccessible_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@inaccessible"]) type Position @inaccessible { x: Int! @@ -24,4 +24,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_inaccessible/test_inaccessible_2.graphql b/tests/gql/test_inaccessible/test_inaccessible_2.graphql index 9bd20e8..574f372 100644 --- a/tests/gql/test_inaccessible/test_inaccessible_2.graphql +++ b/tests/gql/test_inaccessible/test_inaccessible_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@inaccessible"]) type Position @inaccessible { x: Int! @@ -19,4 +19,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_inaccessible/test_inaccessible_union_1.graphql b/tests/gql/test_inaccessible/test_inaccessible_union_1.graphql index eb0927d..2d798c9 100644 --- a/tests/gql/test_inaccessible/test_inaccessible_union_1.graphql +++ b/tests/gql/test_inaccessible/test_inaccessible_union_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@inaccessible"]) union SearchResult @inaccessible = Human | Droid | Starship @@ -36,4 +36,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_inaccessible/test_inaccessible_union_2.graphql b/tests/gql/test_inaccessible/test_inaccessible_union_2.graphql index a9403a5..e7bca91 100644 --- a/tests/gql/test_inaccessible/test_inaccessible_union_2.graphql +++ b/tests/gql/test_inaccessible/test_inaccessible_union_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@inaccessible"]) union SearchResult @inaccessible = Human | Droid | Starship @@ -31,4 +31,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_key/test_compound_primary_key_1.graphql b/tests/gql/test_key/test_compound_primary_key_1.graphql index 3bcc601..22ca0e8 100644 --- a/tests/gql/test_key/test_compound_primary_key_1.graphql +++ b/tests/gql/test_key/test_compound_primary_key_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@key"]) type Query { user: User @@ -33,4 +33,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_key/test_compound_primary_key_2.graphql b/tests/gql/test_key/test_compound_primary_key_2.graphql index 3c48134..c6bbe84 100644 --- a/tests/gql/test_key/test_compound_primary_key_2.graphql +++ b/tests/gql/test_key/test_compound_primary_key_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@key"]) type Query { user: User @@ -23,4 +23,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_key/test_compound_primary_key_with_depth_1.graphql b/tests/gql/test_key/test_compound_primary_key_with_depth_1.graphql index c8dfebd..04a105d 100644 --- a/tests/gql/test_key/test_compound_primary_key_with_depth_1.graphql +++ b/tests/gql/test_key/test_compound_primary_key_with_depth_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@key"]) type Query { user: User @@ -39,4 +39,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_key/test_compound_primary_key_with_depth_2.graphql b/tests/gql/test_key/test_compound_primary_key_with_depth_2.graphql index 54430d3..76b1b3c 100644 --- a/tests/gql/test_key/test_compound_primary_key_with_depth_2.graphql +++ b/tests/gql/test_key/test_compound_primary_key_with_depth_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@key"]) type Query { user: User @@ -29,4 +29,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_key/test_multiple_keys_1.graphql b/tests/gql/test_key/test_multiple_keys_1.graphql index 9e1d066..c50482f 100644 --- a/tests/gql/test_key/test_multiple_keys_1.graphql +++ b/tests/gql/test_key/test_multiple_keys_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@key"]) type Query { user: User @@ -29,4 +29,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_key/test_multiple_keys_2.graphql b/tests/gql/test_key/test_multiple_keys_2.graphql index 815b80c..3518310 100644 --- a/tests/gql/test_key/test_multiple_keys_2.graphql +++ b/tests/gql/test_key/test_multiple_keys_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@key"]) type Query { user: User @@ -19,4 +19,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_override/test_override_1.graphql b/tests/gql/test_override/test_override_1.graphql index 86554c8..484f842 100644 --- a/tests/gql/test_override/test_override_1.graphql +++ b/tests/gql/test_override/test_override_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@override"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@override"]) type Query { product: Product @@ -25,4 +25,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_override/test_override_2.graphql b/tests/gql/test_override/test_override_2.graphql index 3d717b6..791fa70 100644 --- a/tests/gql/test_override/test_override_2.graphql +++ b/tests/gql/test_override/test_override_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@override"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@override"]) type Query { product: Product @@ -20,4 +20,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_provides/test_provides_1.graphql b/tests/gql/test_provides/test_provides_1.graphql index aa03470..503b4e5 100644 --- a/tests/gql/test_provides/test_provides_1.graphql +++ b/tests/gql/test_provides/test_provides_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@external", "@key", "@provides"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@external", "@key", "@provides"]) type Query { inStockCount: InStockCount @@ -35,4 +35,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_provides/test_provides_2.graphql b/tests/gql/test_provides/test_provides_2.graphql index a18d895..757aef4 100644 --- a/tests/gql/test_provides/test_provides_2.graphql +++ b/tests/gql/test_provides/test_provides_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@external", "@key", "@provides"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@external", "@key", "@provides"]) type Query { inStockCount: InStockCount @@ -25,4 +25,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_provides/test_provides_multiple_fields_1.graphql b/tests/gql/test_provides/test_provides_multiple_fields_1.graphql index 85dad3c..0c2cf43 100644 --- a/tests/gql/test_provides/test_provides_multiple_fields_1.graphql +++ b/tests/gql/test_provides/test_provides_multiple_fields_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@external", "@key", "@provides"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@external", "@key", "@provides"]) type Query { inStockCount: InStockCount @@ -35,4 +35,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_provides/test_provides_multiple_fields_2.graphql b/tests/gql/test_provides/test_provides_multiple_fields_2.graphql index a5f1511..c477a7a 100644 --- a/tests/gql/test_provides/test_provides_multiple_fields_2.graphql +++ b/tests/gql/test_provides/test_provides_multiple_fields_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@external", "@key", "@provides"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@external", "@key", "@provides"]) type Query { inStockCount: InStockCount @@ -25,4 +25,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_provides/test_provides_multiple_fields_as_list_1.graphql b/tests/gql/test_provides/test_provides_multiple_fields_as_list_1.graphql index 83672e2..53924bc 100644 --- a/tests/gql/test_provides/test_provides_multiple_fields_as_list_1.graphql +++ b/tests/gql/test_provides/test_provides_multiple_fields_as_list_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@provides"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key", "@provides"]) type Query { inStockCount: InStockCount @@ -35,4 +35,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_provides/test_provides_multiple_fields_as_list_2.graphql b/tests/gql/test_provides/test_provides_multiple_fields_as_list_2.graphql index ff687af..1bbb9ba 100644 --- a/tests/gql/test_provides/test_provides_multiple_fields_as_list_2.graphql +++ b/tests/gql/test_provides/test_provides_multiple_fields_as_list_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@provides"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key", "@provides"]) type Query { inStockCount: InStockCount @@ -25,4 +25,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_requires/test_requires_multiple_fields_1.graphql b/tests/gql/test_requires/test_requires_multiple_fields_1.graphql index d408da1..a626dbe 100644 --- a/tests/gql/test_requires/test_requires_multiple_fields_1.graphql +++ b/tests/gql/test_requires/test_requires_multiple_fields_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key", "@requires"]) type Query { product: Product @@ -31,4 +31,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_requires/test_requires_multiple_fields_2.graphql b/tests/gql/test_requires/test_requires_multiple_fields_2.graphql index 7ff53e0..98afe42 100644 --- a/tests/gql/test_requires/test_requires_multiple_fields_2.graphql +++ b/tests/gql/test_requires/test_requires_multiple_fields_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key", "@requires"]) type Query { product: Product @@ -21,4 +21,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_requires/test_requires_multiple_fields_as_list_1.graphql b/tests/gql/test_requires/test_requires_multiple_fields_as_list_1.graphql index d408da1..a626dbe 100644 --- a/tests/gql/test_requires/test_requires_multiple_fields_as_list_1.graphql +++ b/tests/gql/test_requires/test_requires_multiple_fields_as_list_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key", "@requires"]) type Query { product: Product @@ -31,4 +31,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_requires/test_requires_multiple_fields_as_list_2.graphql b/tests/gql/test_requires/test_requires_multiple_fields_as_list_2.graphql index 7ff53e0..98afe42 100644 --- a/tests/gql/test_requires/test_requires_multiple_fields_as_list_2.graphql +++ b/tests/gql/test_requires/test_requires_multiple_fields_as_list_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key", "@requires"]) type Query { product: Product @@ -21,4 +21,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_requires/test_requires_with_input_1.graphql b/tests/gql/test_requires/test_requires_with_input_1.graphql index a898ef2..e80ef8b 100644 --- a/tests/gql/test_requires/test_requires_with_input_1.graphql +++ b/tests/gql/test_requires/test_requires_with_input_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key", "@requires"]) type Query { acme: Acme @@ -30,4 +30,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_requires/test_requires_with_input_2.graphql b/tests/gql/test_requires/test_requires_with_input_2.graphql index 2f4d703..ae14657 100644 --- a/tests/gql/test_requires/test_requires_with_input_2.graphql +++ b/tests/gql/test_requires/test_requires_with_input_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key", "@requires"]) type Query { acme: Acme @@ -20,4 +20,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_scalar/test_custom_scalar_1.graphql b/tests/gql/test_scalar/test_custom_scalar_1.graphql index e1607ae..e6ff4bb 100644 --- a/tests/gql/test_scalar/test_custom_scalar_1.graphql +++ b/tests/gql/test_scalar/test_custom_scalar_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible", "@shareable"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@inaccessible", "@shareable"]) type TestScalar @shareable { testShareableScalar(x: AddressScalar): String @shareable @@ -27,4 +27,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_scalar/test_custom_scalar_2.graphql b/tests/gql/test_scalar/test_custom_scalar_2.graphql index da23193..4303041 100644 --- a/tests/gql/test_scalar/test_custom_scalar_2.graphql +++ b/tests/gql/test_scalar/test_custom_scalar_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible", "@shareable"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@inaccessible", "@shareable"]) type TestScalar @shareable { testShareableScalar(x: AddressScalar): String @shareable @@ -22,4 +22,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_schema_annotation/test_chat_schema_1.graphql b/tests/gql/test_schema_annotation/test_chat_schema_1.graphql index c1ba222..cd4d73a 100644 --- a/tests/gql/test_schema_annotation/test_chat_schema_1.graphql +++ b/tests/gql/test_schema_annotation/test_chat_schema_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key"]) schema { query: ChatQuery @@ -39,4 +39,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_schema_annotation/test_chat_schema_2.graphql b/tests/gql/test_schema_annotation/test_chat_schema_2.graphql index 2ea0c63..2075b21 100644 --- a/tests/gql/test_schema_annotation/test_chat_schema_2.graphql +++ b/tests/gql/test_schema_annotation/test_chat_schema_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@extends", "@external", "@key"]) schema { query: ChatQuery @@ -29,4 +29,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_schema_annotation/test_user_schema_1.graphql b/tests/gql/test_schema_annotation/test_user_schema_1.graphql index 3f80e75..d4857ab 100644 --- a/tests/gql/test_schema_annotation/test_user_schema_1.graphql +++ b/tests/gql/test_schema_annotation/test_user_schema_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@key"]) schema { query: UserQuery @@ -34,4 +34,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_schema_annotation/test_user_schema_2.graphql b/tests/gql/test_schema_annotation/test_user_schema_2.graphql index e20f2cc..3cfcf37 100644 --- a/tests/gql/test_schema_annotation/test_user_schema_2.graphql +++ b/tests/gql/test_schema_annotation/test_user_schema_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@key"]) schema { query: UserQuery @@ -24,4 +24,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_shareable/test_shareable_1.graphql b/tests/gql/test_shareable/test_shareable_1.graphql index 417c965..b7f969e 100644 --- a/tests/gql/test_shareable/test_shareable_1.graphql +++ b/tests/gql/test_shareable/test_shareable_1.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@shareable"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@shareable"]) type Position @shareable { x: Int! @@ -24,4 +24,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/gql/test_shareable/test_shareable_2.graphql b/tests/gql/test_shareable/test_shareable_2.graphql index 5e585f5..b663f46 100644 --- a/tests/gql/test_shareable/test_shareable_2.graphql +++ b/tests/gql/test_shareable/test_shareable_2.graphql @@ -1,5 +1,5 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@shareable"]) + @link(url: "https://specs.apollo.dev/federation/v2.11", import: ["@shareable"]) type Position @shareable { x: Int! @@ -19,4 +19,9 @@ scalar FieldSet scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" -scalar federation__Policy \ No newline at end of file +scalar federation__Policy + +""" +Contains the name of a defined context and a selection of a field from the context's type +""" +scalar federation__ContextFieldValue \ No newline at end of file diff --git a/tests/test_from_context.py b/tests/test_from_context.py new file mode 100644 index 0000000..f59c3fc --- /dev/null +++ b/tests/test_from_context.py @@ -0,0 +1,90 @@ +from pathlib import Path + +import graphene +from graphene import Field, ID, ObjectType, String +from graphene import Int +import pytest + +from graphene_federation import LATEST_VERSION, build_schema +from graphene_federation.directives import from_context +from tests.util import file_handlers, sdl_query + +save_file, open_file = file_handlers(Path(__file__)) + + +def test_from_context_with_wrong_input(): + """ + Test checking that the issue https://github.com/preply/graphene-federation/pull/47 is resolved. + """ + + # Context cannot contain _ + with pytest.raises(ValueError) as err: + + class Acme(ObjectType): + id = ID(required=True) + age = Int() + foo = Field( + String, + someInput=from_context( + graphene.Argument(String), + field="$con_text1{ someField }", + auto_case=False, + ), + ) + + class Query(ObjectType): + acme = Field(Acme) + + build_schema(query=Query, federation_version=LATEST_VERSION) + + assert str(err.value) == 'Context name cannot contain "_"' + + # No Context + with pytest.raises(ValueError) as err: + + class Acme(ObjectType): + id = ID(required=True) + age = Int() + foo = Field( + String, + someInput=from_context( + graphene.Argument(String), + field="abc { someField }", + auto_case=False, + ), + ) + + class Query(ObjectType): + acme = Field(Acme) + + build_schema(query=Query, federation_version=LATEST_VERSION) + + assert str(err.value) == "One context must be specified" + + +def test_from_context_with_input(): + """ + Test checking that the issue https://github.com/preply/graphene-federation/pull/47 is resolved. + """ + + class Acme(ObjectType): + id = ID(required=True) + age = Int() + foo = Field( + String, + someInput=from_context( + graphene.Argument(String), + field="$context1 ... on A { someField } ... on B { someField } ... on C { someOtherField }", + ), + ) + + class Query(ObjectType): + acme = Field(Acme) + + schema = build_schema(query=Query, federation_version=LATEST_VERSION) + + # save_file(str(schema), "1") + # save_file(sdl_query(schema), "2") + + assert open_file("1") == str(schema) + assert open_file("2") == sdl_query(schema)