From c15ce935427099d59de4a773805e3cbda42e2dea Mon Sep 17 00:00:00 2001 From: Markus Padourek Date: Mon, 12 Sep 2016 09:25:38 +0100 Subject: [PATCH 1/6] Export union type directly. --- graphene/__init__.py | 4 +++- graphene/types/__init__.py | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/graphene/__init__.py b/graphene/__init__.py index 089d4765..5cbd2ab9 100644 --- a/graphene/__init__.py +++ b/graphene/__init__.py @@ -30,7 +30,8 @@ if not __SETUP__: List, NonNull, Enum, Argument, - Dynamic + Dynamic, + Union, ) from .relay import ( Node, @@ -62,6 +63,7 @@ if not __SETUP__: 'NonNull', 'Argument', 'Dynamic', + 'Union', 'resolve_only_args', 'Node', 'is_node', diff --git a/graphene/types/__init__.py b/graphene/types/__init__.py index c7411ff9..179c6f6d 100644 --- a/graphene/types/__init__.py +++ b/graphene/types/__init__.py @@ -13,6 +13,7 @@ from .inputfield import InputField from .argument import Argument from .inputobjecttype import InputObjectType from .dynamic import Dynamic +from .union import Union __all__ = [ @@ -33,5 +34,6 @@ __all__ = [ 'Boolean', 'List', 'NonNull', - 'Argument' - 'Dynamic'] + 'Argument', + 'Dynamic', +] From 3888307f89ec9e3bcb576fd528a6f828e0bd762a Mon Sep 17 00:00:00 2001 From: Markus Padourek Date: Mon, 12 Sep 2016 09:31:22 +0100 Subject: [PATCH 2/6] Added GlobalID as well. --- graphene/__init__.py | 2 ++ graphene/relay/__init__.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/graphene/__init__.py b/graphene/__init__.py index 5cbd2ab9..5afc4f97 100644 --- a/graphene/__init__.py +++ b/graphene/__init__.py @@ -36,6 +36,7 @@ if not __SETUP__: from .relay import ( Node, is_node, + GlobalID, ClientIDMutation, Connection, ConnectionField, @@ -67,6 +68,7 @@ if not __SETUP__: 'resolve_only_args', 'Node', 'is_node', + 'GlobalID', 'ClientIDMutation', 'Connection', 'ConnectionField', diff --git a/graphene/relay/__init__.py b/graphene/relay/__init__.py index 353cbf1f..8cc26ebf 100644 --- a/graphene/relay/__init__.py +++ b/graphene/relay/__init__.py @@ -1,10 +1,11 @@ -from .node import Node, is_node +from .node import Node, is_node, GlobalID from .mutation import ClientIDMutation from .connection import Connection, ConnectionField, PageInfo __all__ = [ 'Node', 'is_node', + 'GlobalID', 'ClientIDMutation', 'Connection', 'ConnectionField', From f232e433e6d4ad4f7c54a6d119c27a04743acee8 Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Mon, 12 Sep 2016 10:06:35 -0400 Subject: [PATCH 3/6] Make assertion message use same var as test Ensure that the assertion message correctly shows the value used by the assertion test. --- graphene/types/argument.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphene/types/argument.py b/graphene/types/argument.py index 471102d4..07dc1d1d 100644 --- a/graphene/types/argument.py +++ b/graphene/types/argument.py @@ -32,7 +32,7 @@ def to_arguments(args, extra_args): raise ValueError('Unknown argument "{}".'.format(default_name)) arg_name = default_name or arg.name - assert arg_name not in arguments, 'More than one Argument have same name "{}".'.format(arg.name) + assert arg_name not in arguments, 'More than one Argument have same name "{}".'.format(arg_name) arguments[arg_name] = arg return arguments From 73945fb569af8e0859567c248ccc3fa203e86986 Mon Sep 17 00:00:00 2001 From: Markus Padourek Date: Mon, 12 Sep 2016 15:28:13 +0100 Subject: [PATCH 4/6] Added test for node connection edge. --- graphene/relay/tests/test_connection.py | 10 +++++++++ graphene/relay/tests/test_mutation.py | 28 +++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/graphene/relay/tests/test_connection.py b/graphene/relay/tests/test_connection.py index 01680bf6..2f1441ac 100644 --- a/graphene/relay/tests/test_connection.py +++ b/graphene/relay/tests/test_connection.py @@ -92,6 +92,16 @@ def test_edge_with_bases(): assert edge_fields['other'].type == String +def test_edge_on_node(): + Edge = MyObject.Connection.Edge + assert Edge._meta.name == 'MyObjectEdge' + edge_fields = Edge._meta.fields + assert list(edge_fields.keys()) == ['node', 'cursor'] + + assert isinstance(edge_fields['node'], Field) + assert edge_fields['node'].type == MyObject + + def test_pageinfo(): assert PageInfo._meta.name == 'PageInfo' fields = PageInfo._meta.fields diff --git a/graphene/relay/tests/test_mutation.py b/graphene/relay/tests/test_mutation.py index fab91f98..246790b0 100644 --- a/graphene/relay/tests/test_mutation.py +++ b/graphene/relay/tests/test_mutation.py @@ -1,15 +1,26 @@ +from collections import OrderedDict import pytest from ...types import (Argument, Field, InputField, InputObjectType, ObjectType, Schema, AbstractType, NonNull) from ...types.scalars import String +from ..connection import Connection from ..mutation import ClientIDMutation +from ..node import Node class SharedFields(AbstractType): shared = String() +class MyNode(ObjectType): + + class Meta: + interfaces = (Node, ) + + name = String() + + class SaySomething(ClientIDMutation): class Input: @@ -28,12 +39,16 @@ class OtherMutation(ClientIDMutation): additional_field = String() name = String() + my_node_edge = Field(MyNode.Connection.Edge) @classmethod def mutate_and_get_payload(cls, args, context, info): shared = args.get('shared', '') additionalField = args.get('additionalField', '') - return SaySomething(name=shared + additionalField) + edge_type = MyNode.Connection.Edge + return OtherMutation(name=shared + additionalField, + my_node_edge=edge_type( + cursor='1', node=MyNode(name='name'))) class RootQuery(ObjectType): @@ -81,7 +96,7 @@ def test_mutation_input(): def test_subclassed_mutation(): fields = OtherMutation._meta.fields - assert list(fields.keys()) == ['name'] + assert list(fields.keys()) == ['name', 'my_node_edge'] assert isinstance(fields['name'], Field) field = OtherMutation.Field() assert field.type == OtherMutation @@ -110,3 +125,12 @@ def test_subclassed_mutation_input(): # ) # assert not executed.errors # assert executed.data == {'say': {'phrase': 'hello'}} + +def test_edge_query(): + executed = schema.execute( + 'mutation a { other(input: {clientMutationId:"1"}) { myNodeEdge { cursor node { name }} } }' + ) + assert not executed.errors + assert executed.data == OrderedDict( + {'other': OrderedDict({'myNodeEdge': OrderedDict({'cursor': '1', 'node': OrderedDict({'name': 'name'})})})} + ) From aa94fe157eaeb05ccde9cce1eec61ce8860d119a Mon Sep 17 00:00:00 2001 From: Markus Padourek Date: Mon, 12 Sep 2016 15:36:09 +0100 Subject: [PATCH 5/6] compare as dict for py3.5 compat. --- graphene/relay/tests/test_mutation.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/graphene/relay/tests/test_mutation.py b/graphene/relay/tests/test_mutation.py index 246790b0..f8502df0 100644 --- a/graphene/relay/tests/test_mutation.py +++ b/graphene/relay/tests/test_mutation.py @@ -131,6 +131,4 @@ def test_edge_query(): 'mutation a { other(input: {clientMutationId:"1"}) { myNodeEdge { cursor node { name }} } }' ) assert not executed.errors - assert executed.data == OrderedDict( - {'other': OrderedDict({'myNodeEdge': OrderedDict({'cursor': '1', 'node': OrderedDict({'name': 'name'})})})} - ) + assert dict(executed.data) == {'other': {'myNodeEdge': {'cursor': '1', 'node': {'name': 'name'}}}} From 2975e1fc42709cfd8c2331a57dd91091f6ad2ef4 Mon Sep 17 00:00:00 2001 From: Markus Padourek Date: Mon, 12 Sep 2016 15:51:51 +0100 Subject: [PATCH 6/6] Allow initialising ObjectType with private state. --- graphene/types/objecttype.py | 2 +- graphene/types/tests/test_objecttype.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/graphene/types/objecttype.py b/graphene/types/objecttype.py index 273049ba..91b74c0f 100644 --- a/graphene/types/objecttype.py +++ b/graphene/types/objecttype.py @@ -95,7 +95,7 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta)): if kwargs: for prop in list(kwargs): try: - if isinstance(getattr(self.__class__, prop), property): + if isinstance(getattr(self.__class__, prop), property) or prop.startswith('_'): setattr(self, prop, kwargs.pop(prop)) except AttributeError: pass diff --git a/graphene/types/tests/test_objecttype.py b/graphene/types/tests/test_objecttype.py index a6196b1f..eb297917 100644 --- a/graphene/types/tests/test_objecttype.py +++ b/graphene/types/tests/test_objecttype.py @@ -64,6 +64,20 @@ def test_generate_objecttype_with_fields(): assert 'field' in MyObjectType._meta.fields +def test_generate_objecttype_with_private_attributes(): + class MyObjectType(ObjectType): + _private_state = None + + assert '_private_state' not in MyObjectType._meta.fields + assert hasattr(MyObjectType, '_private_state') + + m = MyObjectType(_private_state='custom') + assert m._private_state == 'custom' + + with pytest.raises(TypeError): + MyObjectType(_other_private_state='Wrong') + + def test_ordered_fields_in_objecttype(): class MyObjectType(ObjectType): b = Field(MyType)