diff --git a/UPGRADE-v1.0.md b/UPGRADE-v1.0.md index 45676715..98c80c82 100644 --- a/UPGRADE-v1.0.md +++ b/UPGRADE-v1.0.md @@ -25,7 +25,7 @@ def resolve_xxx(self, args, context, info): # ... ``` -* `ObjectType and `Interface` no longer accept the `abstract` option in the `Meta`. +* `ObjectType` and `Interface` no longer accept the `abstract` option in the `Meta`. Inheriting fields should be now achieved using `AbstractType` inheritance. Before: diff --git a/graphene/__init__.py b/graphene/__init__.py index 089d4765..5afc4f97 100644 --- a/graphene/__init__.py +++ b/graphene/__init__.py @@ -30,11 +30,13 @@ if not __SETUP__: List, NonNull, Enum, Argument, - Dynamic + Dynamic, + Union, ) from .relay import ( Node, is_node, + GlobalID, ClientIDMutation, Connection, ConnectionField, @@ -62,9 +64,11 @@ if not __SETUP__: 'NonNull', 'Argument', 'Dynamic', + 'Union', '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', 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..f8502df0 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,10 @@ 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 dict(executed.data) == {'other': {'myNodeEdge': {'cursor': '1', 'node': {'name': 'name'}}}} 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', +] 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 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)