Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Federation v2 support added #4

Merged
merged 96 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
dba69d0
Federation v2 support added
arun-sureshkumar Oct 4, 2022
308be36
Minor fix in link
arun-sureshkumar Oct 4, 2022
c61f145
Minor fix in @provides
arun-sureshkumar Oct 4, 2022
5bb34d7
Minor fix in @keys
arun-sureshkumar Oct 4, 2022
c469282
Revert patch in @keys
arun-sureshkumar Oct 4, 2022
97f6fdc
Remove Print
arun-sureshkumar Oct 4, 2022
1489d96
add override @override to @link
arun-sureshkumar Oct 4, 2022
8e5d39f
fix @link
arun-sureshkumar Oct 4, 2022
d8d8240
fix @link
arun-sureshkumar Oct 4, 2022
963e0a2
fix @link
arun-sureshkumar Oct 4, 2022
f1ea7b8
Test case passed
arun-sureshkumar Oct 4, 2022
70b9476
Add @key (multi) support
arun-sureshkumar Oct 4, 2022
c7aa475
Fix Test Cases
arun-sureshkumar Oct 4, 2022
783bfb7
Fix Test Cases
arun-sureshkumar Oct 4, 2022
2438b3d
Fix Lint Error
arun-sureshkumar Oct 4, 2022
f0c397b
Update README.md
arun-sureshkumar Oct 5, 2022
c4616fb
Fix Lint Error
arun-sureshkumar Oct 5, 2022
e625658
Multi fields support @extend
arun-sureshkumar Oct 6, 2022
4d054fe
Federation v2 support added
arunsureshkumar Oct 7, 2022
3bc7d30
Minor fix in link
arunsureshkumar Oct 7, 2022
0aa55fc
Minor fix in @provides
arunsureshkumar Oct 7, 2022
530c66e
Minor fix in @keys
arunsureshkumar Oct 7, 2022
223dffb
Revert patch in @keys
arunsureshkumar Oct 7, 2022
69035d2
Remove Print
arunsureshkumar Oct 7, 2022
593ec2e
add override @override to @link
arunsureshkumar Oct 7, 2022
3c486f3
fix @link
arunsureshkumar Oct 7, 2022
c136bac
fix @link
arunsureshkumar Oct 7, 2022
f78bdb5
fix @link
arunsureshkumar Oct 7, 2022
e5f13fd
Test case passed
arunsureshkumar Oct 7, 2022
436821c
Add @key (multi) support
arunsureshkumar Oct 7, 2022
7c9b9f8
Fix Test Cases
arunsureshkumar Oct 7, 2022
26ca6a9
Fix Test Cases
arunsureshkumar Oct 7, 2022
95022a6
Fix Lint Error
arunsureshkumar Oct 7, 2022
f8359b2
Update README.md
arunsureshkumar Oct 7, 2022
f4da95f
Fix Lint Error
arunsureshkumar Oct 7, 2022
39c6857
Merge pull request #1 from strollby/support-federation-v2
arunsureshkumar Oct 7, 2022
9e4fc93
Multi fields support @extend
arunsureshkumar Oct 7, 2022
26c78c9
Merge remote-tracking branch 'origin/main'
arunsureshkumar Oct 7, 2022
f3ecfb5
Fix Lint error
arunsureshkumar Oct 7, 2022
e865642
mark PageInfo @shareable
arunsureshkumar Oct 7, 2022
4cd4c82
Refactor: @override directive argument renamed to from_
adarshdigievo Oct 30, 2022
cab47c8
Add: resolvable argument to @key directive
adarshdigievo Oct 30, 2022
e5cf8fb
WIP: Toggle for federation v2
adarshdigievo Oct 30, 2022
601edb1
Fix: correct resolvable attribute of entity
adarshdigievo Oct 30, 2022
8a13881
Fix: correct entity set building
adarshdigievo Oct 30, 2022
ee88d7e
Fix: resolvable argument addition to schema
adarshdigievo Oct 30, 2022
d87328a
Refactor: Correct docstrings
adarshdigievo Oct 30, 2022
55de8fc
Add: enable_federation_2 to examples
adarshdigievo Oct 30, 2022
b797e30
Add: test case for compound keys
adarshdigievo Oct 30, 2022
9ccad5e
Fix: Lint issues
adarshdigievo Oct 30, 2022
053e0e7
Merge pull request #2 from adarshdigievo/federation-v2-fixes
arun-sureshkumar Oct 31, 2022
14de45b
Merge pull request #3 from strollby/main
arunsureshkumar Oct 31, 2022
3ba0870
Merge branch 'main' into support-federation-v2
arunsureshkumar Oct 31, 2022
37fc480
Merge remote-tracking branch 'gql-py-origin/main' into federation-v2-…
adarshdigievo Oct 31, 2022
be43692
Fix: Remove duplicated definition of User type in test_key
adarshdigievo Oct 31, 2022
2483d96
Add: Compound key validation
adarshdigievo Oct 31, 2022
586c51f
Add: test for compound keys
adarshdigievo Oct 31, 2022
9b4d7be
Docs: Correct known issues in readme as compound keys are now working
adarshdigievo Oct 31, 2022
7cbb7f5
Fix: lint
adarshdigievo Oct 31, 2022
55dfa5f
Merge branch 'strollby:main' into federation-v2-fixes
adarshdigievo Oct 31, 2022
30456bb
Merge pull request #4 from adarshdigievo/federation-v2-fixes
adarshdigievo Oct 31, 2022
f8950c4
Merge remote-tracking branch 'stby-gql-fed/support-federation-v2' int…
adarshdigievo Oct 31, 2022
01cc712
Merge pull request #5 from strollby/main
adarshdigievo Oct 31, 2022
dd09fbb
Update graphene_federation/shareable.py
arunsureshkumar Nov 17, 2022
2e83652
Update graphene_federation/inaccessible.py
arunsureshkumar Nov 17, 2022
177b7fc
Fix: Correct output comments in examples
adarshdigievo Nov 25, 2022
e8c591d
Fix: Use graphene_type._meta.fields for getting valid fields in type
adarshdigievo Nov 25, 2022
0db19eb
Fix: Add resolvable argument only for federation v2
adarshdigievo Nov 26, 2022
da3fd9c
Refactor: Rename variable Type to type_
adarshdigievo Nov 26, 2022
9282a62
Doc: Correct comment
adarshdigievo Nov 26, 2022
de5fc41
Remove: unused _shareable list in shareable.py
adarshdigievo Nov 26, 2022
5c6d36c
Lint: service.py
adarshdigievo Nov 26, 2022
6b6e915
Add: utility function get_attributed_fields
adarshdigievo Nov 26, 2022
bf7e95f
Add: tests for federation v1
adarshdigievo Nov 26, 2022
e1d1e97
LInt: fix lint error
adarshdigievo Nov 26, 2022
bc71b3b
Merge pull request #6 from adarshdigievo/federation-v2-fixes
adarshdigievo Nov 26, 2022
bbafe96
Fix: optimise imports
adarshdigievo Nov 26, 2022
e24be38
Change: implementation of utility function is_valid_compound_key opti…
adarshdigievo Nov 26, 2022
c16cc10
Merge pull request #7 from adarshdigievo/federation-v2-fixes
adarshdigievo Nov 26, 2022
7b84274
Change: combined logic for adding _inaccessible attribute to fields a…
adarshdigievo Dec 1, 2022
fdc3c89
Merge remote-tracking branch 'gql-py-origin/main' into federation-v2-…
adarshdigievo Dec 6, 2022
2a0885a
Change: Compound key validation logic using graphql core parse()
adarshdigievo Dec 6, 2022
f9b1686
Fix: Auto camelcase field names if enabled in schema
adarshdigievo Dec 6, 2022
cafaeec
Add: Advanced test cases for Compound keys
adarshdigievo Dec 6, 2022
eb1e39a
Lint test_key.py
adarshdigievo Dec 6, 2022
5257ccc
Merge pull request #8 from adarshdigievo/federation-v2-fixes
adarshdigievo Dec 6, 2022
6644a6d
Merge branch 'graphql-python:main' into support-federation-v2
adarshdigievo Jan 15, 2023
f7ecfbe
Merge remote-tracking branch 'origin/support-federation-v2' into supp…
arunsureshkumar Mar 14, 2023
3ebf1f9
Support @inaccessible on graphene Interface
arunsureshkumar Mar 14, 2023
72e0d14
Merge remote-tracking branch 'original/main' into support-federation-v2
arunsureshkumar Mar 14, 2023
f32186d
Fix: Lint Error
arunsureshkumar Mar 14, 2023
9c1e6ed
Fix: Gateway Dockerfile build error
arunsureshkumar Mar 14, 2023
1fb0ffe
Add Support: @inaccessible & @shareable to graphene.Union
arunsureshkumar Mar 14, 2023
a479fc5
Update: Dependencies, Dockerfile
arunsureshkumar Mar 14, 2023
01b9f9a
Add: Test case for @shareable & @inaccessible
arunsureshkumar Mar 14, 2023
e6d7fe0
Fix: Lint Error
arunsureshkumar Mar 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ There is also a cool [example](https://github.com/preply/graphene-federation/iss
## Known issues

1. decorators will not work properly on fields with custom names for example `some_field = String(name='another_name')`
1. `@key` decorator will not work on [compound primary key](https://www.apollographql.com/docs/federation/entities-advanced/#compound-keys)

------------------------

Expand Down
4 changes: 2 additions & 2 deletions examples/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def resolve_file(self, **kwargs):
'''
result = schema.execute(query)
print(result.data)
# OrderedDict([('_service', OrderedDict([('sdl', ' type File @key(fields: "id") { id: Int! name: String } extend type Query { hello: String file: File } ')]))])
# {'_service': {'sdl': 'type Query {\n file: File\n}\n\ntype File @key(fields: "id") {\n id: Int!\n name: String\n}'}}

query ='''
query entities($_representations: [_Any!]!) {
Expand All @@ -62,4 +62,4 @@ def resolve_file(self, **kwargs):
]
})
print(result.data)
# OrderedDict([('_entities', [OrderedDict([('id', 1), ('name', 'test_name')])])])
# {'_entities': [{'id': 1, 'name': 'test_name'}]}
2 changes: 1 addition & 1 deletion examples/extend.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ def resolve_file(self, **kwargs):
'''
result = schema.execute(query)
print(result.data)
# OrderedDict([('_service', OrderedDict([('sdl', ' extend type Message @key(fields: "id") { id: Int! @external } type Query { message: Message } ')]))])
# {'sdl': 'type Query {\n message: Message\n}\n\nextend type Message @key(fields: "id") {\n id: Int! @external\n}'}}
70 changes: 70 additions & 0 deletions examples/inaccessible.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import graphene

from graphene_federation import inaccessible, external, provides, key, override, shareable

from graphene_federation import build_schema


@key(fields="x")
class Position(graphene.ObjectType):
x = graphene.Int(required=True)
y = external(graphene.Int(required=True))
z = inaccessible(graphene.Int(required=True))
a = provides(graphene.Int(), fields="x")
b = override(graphene.Int(required=True), from_="h")


@inaccessible
class ReviewInterface(graphene.Interface):
interfaced_body = graphene.String(required=True)


@inaccessible
class Review(graphene.ObjectType):
class Meta:
interfaces = (ReviewInterface,)

id = graphene.Int(required=True)
body = graphene.String(required=True)


@inaccessible
class Human(graphene.ObjectType):
name = graphene.String()
born_in = graphene.String()


@inaccessible
class Droid(graphene.ObjectType):
name = graphene.String()
primary_function = graphene.String()


@inaccessible
class Starship(graphene.ObjectType):
name = graphene.String()
length = graphene.Int()


@inaccessible
class SearchResult(graphene.Union):
class Meta:
types = (Human, Droid, Starship)


class Query(graphene.ObjectType):
position = graphene.Field(Position)


schema = build_schema(Query, enable_federation_2=True, types=(ReviewInterface, SearchResult, Review))

query = '''
query getSDL {
_service {
sdl
}
}
'''
result = schema.execute(query)
print(result.data)
# {'_service': {'sdl': 'extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@external", "@key", "@override", "@provides", "@inaccessible"])\ntype Query {\n position: Position\n}\n\ntype Position @key(fields: "x") {\n x: Int!\n y: Int! @external\n z: Int! @inaccessible\n a: Int @provides(fields: "x")\n b: Int! @override(from: "h")\n}'}}
28 changes: 28 additions & 0 deletions examples/override.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import graphene

from graphene_federation import build_schema, shareable, external, key, override, inaccessible


@key(fields="id")
class Product(graphene.ObjectType):
id = graphene.ID(required=True)
in_stock = override(graphene.Boolean(required=True), "Products")
out_stock = inaccessible(graphene.Boolean(required=True))


class Query(graphene.ObjectType):
position = graphene.Field(Product)


schema = build_schema(Query, enable_federation_2=True)

query = '''
query getSDL {
_service {
sdl
}
}
'''
result = schema.execute(query)
print(result.data)
# {'_service': {'sdl': 'extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@override", "@inaccessible"])\ntype Query {\n position: Product\n}\n\ntype Product @key(fields: "id") {\n id: ID!\n inStock: Boolean! @override(from: "Products")\n outStock: Boolean! @inaccessible\n}'}}
54 changes: 54 additions & 0 deletions examples/shareable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import graphene
from graphene import Interface

from graphene_federation.shareable import shareable

from graphene_federation import build_schema


@shareable
class Position(graphene.ObjectType):
x = graphene.Int(required=True)
y = shareable(graphene.Int(required=True))


@shareable
class Human(graphene.ObjectType):
name = graphene.String()
born_in = graphene.String()


@shareable
class Droid(graphene.ObjectType):
name = shareable(graphene.String())
primary_function = graphene.String()


@shareable
class Starship(graphene.ObjectType):
name = graphene.String()
length = shareable(graphene.Int())


@shareable
class SearchResult(graphene.Union):
class Meta:
types = (Human, Droid, Starship)


class Query(graphene.ObjectType):
position = graphene.Field(Position)


schema = build_schema(Query, enable_federation_2=True, types=(SearchResult,))

query = '''
query getSDL {
_service {
sdl
}
}
'''
result = schema.execute(query)
print(result.data)
# {'_service': {'sdl': 'extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@shareable"])\ntype Query {\n position: Position\n}\n\ntype Position @shareable {\n x: Int!\n y: Int! @shareable\n}'}}
29 changes: 29 additions & 0 deletions examples/tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import graphene

from graphene_federation import build_schema, key, inaccessible, shareable
from graphene_federation.tag import tag


class Product(graphene.ObjectType):
id = graphene.ID(required=True)
in_stock = tag(graphene.Boolean(required=True), "Products")
out_stock = shareable(graphene.Boolean(required=True))
is_listed = inaccessible(graphene.Boolean(required=True))


class Query(graphene.ObjectType):
position = graphene.Field(Product)


schema = build_schema(Query, enable_federation_2=True)

query = '''
query getSDL {
_service {
sdl
}
}
'''
result = schema.execute(query)
print(result.data)
# {'_service': {'sdl': 'extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@inaccessible", "@shareable", "@tag"])\ntype Query {\n position: Product\n}\n\ntype Product {\n id: ID!\n inStock: Boolean! @tag(name: "Products")\n outStock: Boolean! @shareable\n isListed: Boolean! @inaccessible\n}'}}
7 changes: 6 additions & 1 deletion graphene_federation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
from .main import build_schema
from .entity import key
from .extend import extend, external, requires
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
57 changes: 42 additions & 15 deletions graphene_federation/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,22 @@
from graphene.types.schema import TypeMap

from .types import _Any
from .utils import field_name_to_type_attribute
from .utils import (
field_name_to_type_attribute,
check_fields_exist_on_type,
is_valid_compound_key,
)

import collections.abc


def update(d, u):
for k, v in u.items():
if isinstance(v, collections.abc.Mapping):
d[k] = update(d.get(k, {}), v)
else:
d[k] = v
return d


def get_entities(schema: Schema) -> Dict[str, Any]:
Expand All @@ -24,6 +39,14 @@ def get_entities(schema: Schema) -> Dict[str, Any]:
continue
if getattr(type_.graphene_type, "_keys", None):
entities[type_name] = type_.graphene_type

# Validation for compound keys
key_str = " ".join(type_.graphene_type._keys)
type_name = type_.graphene_type._meta.name
if "{" in key_str: # checking for subselection to identify compound key
assert is_valid_compound_key(
type_name, key_str, schema
), f'Invalid compound key definition for type "{type_name}"'
return entities


Expand Down Expand Up @@ -83,27 +106,31 @@ def resolve_entities(self, info, representations):
return EntityQuery


def key(fields: str) -> Callable:
def key(fields: str, resolvable: bool = True) -> Callable:
"""
Take as input a field that should be used as key for that entity.
See specification: https://www.apollographql.com/docs/federation/federation-spec/#key

If the input contains a space it means it's a [compound primary key](https://www.apollographql.com/docs/federation/entities/#defining-a-compound-primary-key)
which is not yet supported.
"""
if " " in fields:
raise NotImplementedError("Compound primary keys are not supported.")

def decorator(Type):
def decorator(type_):
# Check the provided fields actually exist on the Type.
assert (
fields in Type._meta.fields
), f'Field "{fields}" does not exist on type "{Type._meta.name}"'

keys = getattr(Type, "_keys", [])
if " " not in fields:
assert (
fields in type_._meta.fields
), f'Field "{fields}" does not exist on type "{type_._meta.name}"'
if "{" not in fields:
# Skip valid fields check if the key is a compound key. The validation for compound keys
# is done on calling get_entities()
fields_set = set(fields.replace(" ", "").split(","))
assert check_fields_exist_on_type(
fields=fields_set, type_=type_
), f'Field "{fields}" does not exist on type "{type_._meta.name}"'

keys = getattr(type_, "_keys", [])
keys.append(fields)
setattr(Type, "_keys", keys)
setattr(type_, "_keys", keys)
setattr(type_, "_resolvable", resolvable)

return Type
return type_

return decorator
Loading