Skip to content
This repository has been archived by the owner on Oct 21, 2022. It is now read-only.

add @requires support #22

Merged
merged 2 commits into from
Nov 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ Supports now:
```
* extend # extend remote types
* external # mark field as external
* requires # mark that field resolver requires other fields to be pre-fetched

Todo implement:
* @requires
* @provides


Expand Down
2 changes: 1 addition & 1 deletion graphene_federation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .main import key, build_schema
from .extend import extend, external
from .extend import extend, external, requires
5 changes: 5 additions & 0 deletions graphene_federation/extend.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,8 @@ def decorator(Type):
def external(field):
field._external = True
return field


def requires(field, fields: str):
field._requires = fields
return field
23 changes: 20 additions & 3 deletions graphene_federation/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,36 @@
from .entity import custom_entities


def _mark_external(entity_name, entity, schema, auto_camelcase):
def _mark_field(
entity_name, entity, schema: str, mark_attr_name: str,
decorator_resolver: callable, auto_camelcase: bool
):
for field_name in dir(entity):
field = getattr(entity, field_name, None)
if field is not None and getattr(field, '_external', False):
if field is not None and getattr(field, mark_attr_name, None):
# todo write tests on regexp
schema_field_name = to_camel_case(field_name) if auto_camelcase else field_name
pattern = re.compile(
r"(\s%s\s[^\{]*\{[^\}]*\s%s[\s]*:[\s]*[^\s]+)(\s)" % (
entity_name, schema_field_name))
schema = pattern.sub(r'\g<1> @external ', schema)
schema = pattern.sub(
rf'\g<1> {decorator_resolver(getattr(field, mark_attr_name))} ', schema)

return schema


def _mark_external(entity_name, entity, schema, auto_camelcase):
return _mark_field(
entity_name, entity, schema, '_external', lambda _: '@external', auto_camelcase)


def _mark_requires(entity_name, entity, schema, auto_camelcase):
return _mark_field(
entity_name, entity, schema, '_requires', lambda fields: f'@requires(fields: "{fields}")',
auto_camelcase
)


def get_sdl(schema, custom_entities):
string_schema = str(schema)
string_schema = string_schema.replace("\n", " ")
Expand All @@ -37,6 +53,7 @@ def get_sdl(schema, custom_entities):

for entity_name, entity in extended_types.items():
string_schema = _mark_external(entity_name, entity, string_schema, schema.auto_camelcase)
string_schema = _mark_requires(entity_name, entity, string_schema, schema.auto_camelcase)

type_def_re = r"type %s ([^\{]*)" % entity_name
type_def = r"type %s " % entity_name
Expand Down
7 changes: 6 additions & 1 deletion integration_tests/service_c/src/schema.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
from graphene import ObjectType, String, Int, List, NonNull, Field
from graphene_federation import build_schema, extend, external
from graphene_federation import build_schema, extend, external, requires


@extend(fields='id')
class User(ObjectType):
id = external(Int(required=True))
primary_email = external(String())
uppercase_email = requires(String(), fields='primaryEmail')

def resolve_uppercase_email(self, info):
return self.primary_email.upper() if self.primary_email else self.primary_email


class Article(ObjectType):
Expand Down
27 changes: 27 additions & 0 deletions integration_tests/tests/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,30 @@ def test_multiple_key_decorators_apply_multiple_key_annotations():
def test_avoid_duplication_of_key_decorator():
sdl = fetch_sdl('service_a')
assert 'extend type FileNode @key(fields: \"id\") {' in sdl


def test_requires():
query = {
'query': """
query {
articles {
id
text
author {
uppercaseEmail
}
}
}
""",
'variables': {}
}
response = requests.post(
'http://federation:3000/graphql/',
json=query,
)
assert response.status_code == 200
data = json.loads(response.content)['data']
articles = data['articles']

assert articles == [
{'id': 1, 'text': 'some text', 'author': {'uppercaseEmail': 'NAME_5@GMAIL.COM'}}]