Skip to content

Commit

Permalink
add: support upto 2.6 spec
Browse files Browse the repository at this point in the history
  • Loading branch information
mak626 committed Jan 15, 2024
1 parent 9210be6 commit 1cd59b8
Show file tree
Hide file tree
Showing 152 changed files with 3,131 additions and 2,307 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
strategy:
max-parallel: 4
matrix:
python-version: ["3.9", "3.10"]
python-version: ["3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v1
Expand All @@ -21,7 +21,7 @@ jobs:
python -m pip install --upgrade pip
pip install -e ".[test]"
- name: Run Unit Tests
run: py.test graphene_federation --cov=graphene_federation -vv
run: py.test tests --cov=graphene_federation -vv
- name: Upload Coverage
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ integration-tests: ## Run integration tests
# -------------------------

dev-setup: ## Install development dependencies
docker-compose up -d && docker-compose exec graphene_federation bash
docker-compose up -d && docker-compose exec graphene_federation pip install -e ".[dev]"
.PHONY: dev-setup

tests: ## Run unit tests
docker-compose run graphene_federation py.test graphene_federation --cov=graphene_federation -vv
docker-compose run graphene_federation py.test tests --cov=graphene_federation -vv
.PHONY: tests

check-style: ## Run linting
Expand Down
24 changes: 14 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ At the moment it supports:

* `sdl` (`_service` on field): enable to add schema in federation (as is)
* `@key` decorator (entity support): enable to perform queries across service boundaries (you can have more than one key per type)
* `@extend`: extend remote types
* `@extends`: extend remote types
* `external()`: mark a field as external
* `requires()`: mark that field resolver requires other fields to be pre-fetched
* `provides()`/`@provides`: annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway.

Each type which is decorated with `@key` or `@extend` is added to the `_Entity` union.
Each type which is decorated with `@key` or `@extends` is added to the `_Entity` union.
The [`__resolve_reference` method](https://www.apollographql.com/docs/federation/api/apollo-federation/#__resolvereference) can be defined for each type that is an entity.
Note that since the notation with double underscores can be problematic in Python for model inheritance this resolver method can also be named `_resolve_reference` (the `__resolve_reference` method will take precedence if both are declared).

Expand All @@ -57,7 +57,7 @@ It implements a federation schema for a basic e-commerce application over three
First add an account service that expose a `User` type that can then be referenced in other services by its `id` field:

```python
from graphene import Field, ID, ObjectType, String
from graphene import Field, Int, ObjectType, String
from graphene_federation import build_schema, key

@key("id")
Expand All @@ -81,7 +81,7 @@ schema = build_schema(query=Query)
The product service exposes a `Product` type that can be used by other services via the `upc` field:

```python
from graphene import Argument, ID, Int, List, ObjectType, String
from graphene import Argument, Int, List, ObjectType, String
from graphene_federation import build_schema, key

@key("upc")
Expand All @@ -108,10 +108,12 @@ It also has the ability to provide the username of the `User`.
On top of that it adds to the `User`/`Product` types (that are both defined in other services) the ability to get their reviews.

```python
from graphene import Field, ID, Int, List, ObjectType, String
from graphene_federation import build_schema, extend, external, provides
from graphene import Field, Int, List, ObjectType, String

@extend("id")
from graphene_federation import build_schema, extends, external, key, provides


@key("id")
class User(ObjectType):
id = external(Int(required=True))
reviews = List(lambda: Review)
Expand All @@ -122,21 +124,23 @@ class User(ObjectType):
"""
return []

@extend("upc")

@key("upc")
class Product(ObjectType):
upc = external(String(required=True))
reviews = List(lambda: Review)

# Note that both the base type and the field need to be decorated with `provides` (on the field itself you need to specify which fields get provided).
@provides

class Review(ObjectType):
body = String()
author = provides(Field(User), fields="username")
product = Field(Product)


class Query(ObjectType):
review = Field(Review)


schema = build_schema(query=Query)
```

Expand Down
8 changes: 5 additions & 3 deletions examples/extend.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import graphene
from graphene_federation import build_schema, extend, external

from graphene_federation import build_schema, extends, external, key

@extend(fields="id")

@key("id")
@extends
class Message(graphene.ObjectType):
id = external(graphene.Int(required=True))

Expand All @@ -28,4 +30,4 @@ def resolve_file(self, **kwargs):
"""
result = schema.execute(query)
print(result.data)
# {'sdl': 'type Query {\n message: Message\n}\n\nextend type Message @key(fields: "id") {\n id: Int! @external\n}'}}
# {'sdl': 'type Query {\n message: Message\n}\n\n type Message @key(fields: "id") @extends {\n id: Int! @external\n}'}}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
32 changes: 32 additions & 0 deletions federation_spec/federation-v2.4.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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!) 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
scalar FieldSet
45 changes: 45 additions & 0 deletions federation_spec/federation-v2.5.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
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!) 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: [[Scope!]!]!) on
FIELD_DEFINITION
| OBJECT
| INTERFACE
| SCALAR
| ENUM
scalar Scope
scalar FieldSet
52 changes: 52 additions & 0 deletions federation_spec/federation-v2.6.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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!) 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: [[Scope!]!]!) on
FIELD_DEFINITION
| OBJECT
| INTERFACE
| SCALAR
| ENUM
directive @policy(policies: [[federation__Policy!]!]!) on
| FIELD_DEFINITION
| OBJECT
| INTERFACE
| SCALAR
| ENUM
scalar federation__Policy
scalar Scope
scalar FieldSet
8 changes: 7 additions & 1 deletion graphene_federation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
from graphene_directives import DirectiveLocation

from .appolo_versions import FederationVersion, LATEST_VERSION
from .apollo_versions import FederationVersion, LATEST_VERSION
from .directives import (
authenticated,
extends,
external,
inaccessible,
interface_object,
key,
override,
policy,
provides,
requires,
requires_scope,
shareable,
tag,
)
Expand All @@ -24,14 +27,17 @@
"build_schema",
"FederationDirective",
"DirectiveLocation",
"authenticated",
"extends",
"external",
"inaccessible",
"interface_object",
"key",
"override",
"provides",
"policy",
"requires",
"requires_scope",
"shareable",
"tag",
"compose_directive",
Expand Down
49 changes: 49 additions & 0 deletions graphene_federation/apollo_versions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from graphql import GraphQLDirective

from .v1_0 import get_directives as get_directives_v1_0
from .v2_0 import get_directives as get_directives_v2_0
from .v2_1 import get_directives as get_directives_v2_1
from .v2_2 import get_directives as get_directives_v2_2
from .v2_3 import get_directives as get_directives_v2_3
from .v2_4 import get_directives as get_directives_v2_4
from .v2_5 import get_directives as get_directives_v2_5
from .v2_6 import get_directives as get_directives_v2_6
from .version import FederationVersion

LATEST_VERSION = FederationVersion.VERSION_2_6


def get_directives_based_on_version(
federation_version: FederationVersion,
) -> dict[str, GraphQLDirective]:
if federation_version == FederationVersion.VERSION_1_0:
return get_directives_v1_0()
if federation_version == FederationVersion.VERSION_2_0:
return get_directives_v2_0()
if federation_version == FederationVersion.VERSION_2_1:
return get_directives_v2_1()
if federation_version == FederationVersion.VERSION_2_2:
return get_directives_v2_2()
if federation_version == FederationVersion.VERSION_2_3:
return get_directives_v2_3()
if federation_version == FederationVersion.VERSION_2_4:
return get_directives_v2_4()
if federation_version == FederationVersion.VERSION_2_5:
return get_directives_v2_5()
if federation_version == FederationVersion.VERSION_2_6:
return get_directives_v2_6()

return get_directives_v2_6()


def get_directive_from_name(
directive_name: str, federation_version: FederationVersion
) -> GraphQLDirective:
directive = get_directives_based_on_version(federation_version).get(
directive_name, None
)
if directive is None:
raise ValueError(
f"@{directive_name} not supported on federation version {federation_version}"
)
return directive
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
],
args={"fields": GraphQLArgument(GraphQLNonNull(_FieldSet))},
description="Federation @requires directive",
is_repeatable=True,
add_definition_to_schema=False,
field_validator=validate_requires,
input_transform=field_set_case_transform,
Expand All @@ -41,6 +40,7 @@
args={"fields": GraphQLArgument(GraphQLNonNull(_FieldSet))},
description="Federation @provides directive",
add_definition_to_schema=False,
input_transform=field_set_case_transform,
)

external_directive = CustomDirective(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
],
args={"fields": GraphQLArgument(GraphQLNonNull(FieldSet))},
description="Federation @requires directive",
is_repeatable=True,
add_definition_to_schema=False,
field_validator=validate_requires,
input_transform=field_set_case_transform,
Expand All @@ -52,6 +51,7 @@
args={"fields": GraphQLArgument(GraphQLNonNull(FieldSet))},
description="Federation @provides directive",
add_definition_to_schema=False,
input_transform=field_set_case_transform,
)


Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
8 changes: 8 additions & 0 deletions graphene_federation/apollo_versions/v2_4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from graphql import GraphQLDirective

from .v2_3 import get_directives as get_directives_v2_3


# No Change, Added Subscription Support
def get_directives() -> dict[str, GraphQLDirective]:
return get_directives_v2_3()
Loading

0 comments on commit 1cd59b8

Please sign in to comment.