Skip to content

Commit

Permalink
feat: add support for federation-v2.3 using graphene-directives
Browse files Browse the repository at this point in the history
  • Loading branch information
mak626 committed Jan 13, 2024
1 parent a25e2a3 commit 5f3aa58
Show file tree
Hide file tree
Showing 53 changed files with 1,222 additions and 879 deletions.
40 changes: 31 additions & 9 deletions graphene_federation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
from .directives import (
extends,
external,
inaccessible,
interface_object,
key,
override,
provides,
requires,
shareable,
tag,
)
from .main import build_schema
from .entity import key
from .extend import extend
from .external import external
from .requires import requires
from .shareable import shareable
from .inaccessible import inaccessible
from .provides import provides
from .override import override
from .compose_directive import mark_composable, is_composable
from .schema_directives import compose_directive, link_directive
from .service import get_sdl

__all__ = [
"build_schema",
"extends",
"external",
"inaccessible",
"interface_object",
"key",
"override",
"provides",
"requires",
"shareable",
"tag",
"compose_directive",
"link_directive",
"get_sdl",
]
35 changes: 35 additions & 0 deletions graphene_federation/appolo_versions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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

LATEST_VERSION = "2.3"


def get_directives_based_on_version(version: str) -> dict[str, GraphQLDirective]:
if version < "2.0.0":
return get_directives_v1_0()
elif version <= "2.0.0":
return get_directives_v2_0()
elif version <= "2.1.0":
return get_directives_v2_1()
elif version <= "2.2.0":
return get_directives_v2_2()
else:
return get_directives_v2_3()


def get_directive_from_name(
directive_name: str, federation_version: str
) -> GraphQLDirective:
directive = get_directives_based_on_version(federation_version).get(
directive_name, None
)
if directive is None:
raise ValueError(
f"@{directive_name} not found on federation version {federation_version}"
)
return directive
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
directive @key(fields: _FieldSet!) repeatable on OBJECT | INTERFACE
directive @requires(fields: _FieldSet!) on FIELD_DEFINITION
directive @provides(fields: _FieldSet!) on FIELD_DEFINITION
directive @external on FIELD_DEFINITION
scalar _FieldSet

# this is an optional directive
# used in frameworks that don't natively support GraphQL extend syntax
directive @extends on OBJECT | INTERFACE
30 changes: 30 additions & 0 deletions graphene_federation/appolo_versions/spec/federation-v2.0.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
directive @requires(fields: FieldSet!) on FIELD_DEFINITION
directive @provides(fields: FieldSet!) on FIELD_DEFINITION
directive @external on OBJECT | FIELD_DEFINITION
directive @shareable on FIELD_DEFINITION | OBJECT
directive @extends on OBJECT | INTERFACE
directive @override(from: String!) on FIELD_DEFINITION
directive @inaccessible on
| FIELD_DEFINITION
| OBJECT
| INTERFACE
| UNION
| ENUM
| ENUM_VALUE
| SCALAR
| INPUT_OBJECT
| INPUT_FIELD_DEFINITION
| ARGUMENT_DEFINITION
directive @tag(name: String!) repeatable on
| FIELD_DEFINITION
| INTERFACE
| OBJECT
| UNION
| ARGUMENT_DEFINITION
| SCALAR
| ENUM
| ENUM_VALUE
| INPUT_OBJECT
| INPUT_FIELD_DEFINITION
scalar FieldSet
31 changes: 31 additions & 0 deletions graphene_federation/appolo_versions/spec/federation-v2.1.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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 @override(from: String!) on FIELD_DEFINITION
directive @provides(fields: FieldSet!) on FIELD_DEFINITION
directive @requires(fields: FieldSet!) on FIELD_DEFINITION
directive @shareable 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
31 changes: 31 additions & 0 deletions graphene_federation/appolo_versions/spec/federation-v2.2.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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 @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
32 changes: 32 additions & 0 deletions graphene_federation/appolo_versions/spec/federation-v2.3.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
73 changes: 73 additions & 0 deletions graphene_federation/appolo_versions/v1_0.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from graphene_directives import CustomDirective, DirectiveLocation
from graphql import GraphQLArgument, GraphQLDirective, GraphQLNonNull

from ..scalars import _FieldSet
from ..validators import validate_key, validate_requires

key_directive = CustomDirective(
name="key",
locations=[
DirectiveLocation.OBJECT,
DirectiveLocation.INTERFACE,
],
args={"fields": GraphQLArgument(GraphQLNonNull(_FieldSet))},
description="Federation @key directive",
is_repeatable=True,
add_definition_to_schema=False,
non_field_validator=validate_key,
)

requires_directive = CustomDirective(
name="requires",
locations=[
DirectiveLocation.FIELD_DEFINITION,
],
args={"fields": GraphQLArgument(GraphQLNonNull(_FieldSet))},
description="Federation @requires directive",
is_repeatable=True,
add_definition_to_schema=False,
field_validator=validate_requires,
)


provides_directive = CustomDirective(
name="provides",
locations=[
DirectiveLocation.FIELD_DEFINITION,
],
args={"fields": GraphQLArgument(GraphQLNonNull(_FieldSet))},
description="Federation @provides directive",
add_definition_to_schema=False,
)

external_directive = CustomDirective(
name="external",
locations=[
DirectiveLocation.FIELD_DEFINITION,
],
description="Federation @external directive",
add_definition_to_schema=False,
)

extends_directive = CustomDirective(
name="extends",
locations=[
DirectiveLocation.OBJECT,
DirectiveLocation.INTERFACE,
],
description="Federation @extends directive",
add_definition_to_schema=False,
)


def get_directives() -> dict[str, GraphQLDirective]:
return {
directive.name: directive
for directive in [
key_directive,
requires_directive,
provides_directive,
external_directive,
extends_directive,
]
}
Loading

0 comments on commit 5f3aa58

Please sign in to comment.