From b9f08606a04a205fa872a20c2682fc610332409d Mon Sep 17 00:00:00 2001 From: Markus Padourek Date: Fri, 19 Aug 2016 15:18:01 +0100 Subject: [PATCH 1/4] Fix return promise from ConnectionField resolver --- graphene/relay/fields.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/graphene/relay/fields.py b/graphene/relay/fields.py index d93cdcf8..eefc3a0e 100644 --- a/graphene/relay/fields.py +++ b/graphene/relay/fields.py @@ -1,4 +1,5 @@ import six +import functools from graphql_relay.node.node import from_global_id @@ -8,6 +9,8 @@ from ..core.types.scalars import ID, Int, String from ..utils.wrap_resolver_function import has_context, with_context from .connection import Connection, Edge +def _is_thenable(obj): + return callable(getattr(obj, "then", None)) class ConnectionField(Field): @@ -26,6 +29,11 @@ class ConnectionField(Field): **kwargs) self.connection_type = connection_type or Connection self.edge_type = edge_type or Edge + + def _get_connection_type(self, connection_type, args, context, info, resolved): + if isinstance(resolved, self.connection_type): + return resolved + return self.from_list(connection_type, resolved, args, context, info) @with_context def resolver(self, instance, args, context, info): @@ -38,9 +46,12 @@ class ConnectionField(Field): else: resolved = super(ConnectionField, self).resolver(instance, args, info) - if isinstance(resolved, self.connection_type): - return resolved - return self.from_list(connection_type, resolved, args, context, info) + get_connection_type = functools.partial(self._get_connection_type, connection_type, args, context, info) + + if _is_thenable(resolved): + return resolved.then(get_connection_type) + + return get_connection_type(resolved) def from_list(self, connection_type, resolved, args, context, info): return connection_type.from_list(resolved, args, context, info) From 85928036e0a57b10f0c34a308d12ad2fe09c4fd6 Mon Sep 17 00:00:00 2001 From: Markus Padourek Date: Fri, 19 Aug 2016 15:36:23 +0100 Subject: [PATCH 2/4] added test for returning promise --- graphene/relay/tests/test_query.py | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/graphene/relay/tests/test_query.py b/graphene/relay/tests/test_query.py index 1603709e..c598ab9e 100644 --- a/graphene/relay/tests/test_query.py +++ b/graphene/relay/tests/test_query.py @@ -51,6 +51,9 @@ class Query(graphene.ObjectType): connection_type_nodes = relay.ConnectionField( MyNode, connection_type=MyConnection) + + promise_connection_type = relay.ConnectionField( + MyNode, connection_type=MyConnection) all_my_objects = relay.ConnectionField( MyObject, connection_type=MyConnection) @@ -75,6 +78,9 @@ class Query(graphene.ObjectType): def resolve_all_my_objects(self, args, info): return [MyObject(name='my_object')] + + def resolve_promise_connection_type(self, args, info): + return Promise.resolve('async name').then(lambda name: [MyNode(id='1', name=name)]) schema.query = Query @@ -228,6 +234,32 @@ def test_connectionfield_resolve_returning_objects(): assert result.data == expected +def test_connectionfield_resolve_returning_promise(): + query = ''' + query RebelsShipsQuery { + promiseConnectionType { + edges { + node { + name + } + } + } + } + ''' + expected = { + 'promiseConnectionType': { + 'edges': [{ + 'node': { + 'name': 'async name' + } + }] + } + } + result = schema.execute(query) + assert not result.errors + assert result.data == expected + + @pytest.mark.parametrize('specialness,value', [(True, '!!!'), (False, '???')]) def test_get_node_info(specialness, value): query = ''' From 9d5e428cd24f1d570ac3d42db4097908e37a86b8 Mon Sep 17 00:00:00 2001 From: Markus Padourek Date: Fri, 19 Aug 2016 15:37:21 +0100 Subject: [PATCH 3/4] Fixed linting --- graphene/relay/fields.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/graphene/relay/fields.py b/graphene/relay/fields.py index eefc3a0e..2942a3a9 100644 --- a/graphene/relay/fields.py +++ b/graphene/relay/fields.py @@ -9,9 +9,11 @@ from ..core.types.scalars import ID, Int, String from ..utils.wrap_resolver_function import has_context, with_context from .connection import Connection, Edge + def _is_thenable(obj): return callable(getattr(obj, "then", None)) + class ConnectionField(Field): def __init__(self, type, resolver=None, description='', @@ -29,7 +31,7 @@ class ConnectionField(Field): **kwargs) self.connection_type = connection_type or Connection self.edge_type = edge_type or Edge - + def _get_connection_type(self, connection_type, args, context, info, resolved): if isinstance(resolved, self.connection_type): return resolved From 1e1dc4c83a1b457974962f3298a8f89c510a7f31 Mon Sep 17 00:00:00 2001 From: Markus Padourek Date: Mon, 22 Aug 2016 07:35:40 +0100 Subject: [PATCH 4/4] Added python to test setup. --- graphene/relay/tests/test_query.py | 6 ++++-- setup.py | 1 + tox.ini | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/graphene/relay/tests/test_query.py b/graphene/relay/tests/test_query.py index c598ab9e..af6e188a 100644 --- a/graphene/relay/tests/test_query.py +++ b/graphene/relay/tests/test_query.py @@ -4,6 +4,8 @@ from graphql.type import GraphQLID, GraphQLNonNull import graphene from graphene import relay, with_context +from promise import Promise + schema = graphene.Schema() @@ -51,7 +53,7 @@ class Query(graphene.ObjectType): connection_type_nodes = relay.ConnectionField( MyNode, connection_type=MyConnection) - + promise_connection_type = relay.ConnectionField( MyNode, connection_type=MyConnection) @@ -78,7 +80,7 @@ class Query(graphene.ObjectType): def resolve_all_my_objects(self, args, info): return [MyObject(name='my_object')] - + def resolve_promise_connection_type(self, args, info): return Promise.resolve('async name').then(lambda name: [MyNode(id='1', name=name)]) diff --git a/setup.py b/setup.py index 27246df5..8f1016a4 100644 --- a/setup.py +++ b/setup.py @@ -66,6 +66,7 @@ setup( 'sqlalchemy', 'sqlalchemy_utils', 'mock', + 'promse', # Required for Django postgres fields testing 'psycopg2', ], diff --git a/tox.ini b/tox.ini index 05b37dfd..255e3959 100644 --- a/tox.ini +++ b/tox.ini @@ -14,6 +14,7 @@ deps= blinker singledispatch mock + promise setenv = PYTHONPATH = .:{envdir} commands=