From 41648b5a946a06c847575f080fe7d9d5c48ea286 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Tue, 10 Nov 2015 22:57:22 -0800 Subject: [PATCH] Improved relay integration --- graphene/core/types/field.py | 5 +++-- graphene/core/types/objecttype.py | 28 +++++++++++++++------------- graphene/relay/fields.py | 20 ++++++++++---------- graphene/relay/types.py | 26 ++++++++++++++------------ tests/relay/test_relay_mutations.py | 26 +++++++++++++------------- tests/relay/test_relayfields.py | 7 ++++--- 6 files changed, 59 insertions(+), 53 deletions(-) diff --git a/graphene/core/types/field.py b/graphene/core/types/field.py index 56f172f5..6ea48d86 100644 --- a/graphene/core/types/field.py +++ b/graphene/core/types/field.py @@ -21,7 +21,8 @@ class Field(OrderedType): self.name = name if isinstance(type, six.string_types): type = LazyType(type) - if required: + self.required = required + if self.required: type = NonNull(type) self.type = type self.description = description @@ -68,7 +69,7 @@ class Field(OrderedType): type_objecttype = schema.objecttype(type) if type_objecttype and type_objecttype._meta.is_mutation: assert len(arguments) == 0 - arguments = type_objecttype.arguments + arguments = type_objecttype.get_arguments() resolver = getattr(type_objecttype, 'mutate') assert type, 'Internal type for field %s is None' % str(self) diff --git a/graphene/core/types/objecttype.py b/graphene/core/types/objecttype.py index a6c73602..8399c75d 100644 --- a/graphene/core/types/objecttype.py +++ b/graphene/core/types/objecttype.py @@ -52,10 +52,6 @@ class ObjectTypeMeta(type): assert not ( new_class._meta.is_interface and new_class._meta.is_mutation) - input_class = None - if new_class._meta.is_mutation: - input_class = attrs.pop('Input', None) - # Add all attributes to the class. for obj_name, obj in attrs.items(): new_class.add_to_class(obj_name, obj) @@ -64,14 +60,6 @@ class ObjectTypeMeta(type): assert hasattr( new_class, 'mutate'), "All mutations must implement mutate method" - if input_class: - items = dict(input_class.__dict__) - items.pop('__dict__', None) - items.pop('__doc__', None) - items.pop('__module__', None) - arguments = ArgumentsGroup(**items) - new_class.add_to_class('arguments', arguments) - new_class.add_extra_fields() new_fields = new_class._meta.local_fields @@ -215,7 +203,21 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta, BaseObjectType)): class Mutation(six.with_metaclass(ObjectTypeMeta, BaseObjectType)): - pass + @classmethod + def _prepare_class(cls): + input_class = getattr(cls, 'Input', None) + if input_class: + items = dict(input_class.__dict__) + items.pop('__dict__', None) + items.pop('__doc__', None) + items.pop('__module__', None) + arguments = ArgumentsGroup(**items) + cls.add_to_class('arguments', arguments) + delattr(cls, 'Input') + + @classmethod + def get_arguments(cls): + return cls.arguments class InputObjectType(ObjectType): diff --git a/graphene/relay/fields.py b/graphene/relay/fields.py index 7efa30d8..78ef0cc6 100644 --- a/graphene/relay/fields.py +++ b/graphene/relay/fields.py @@ -1,18 +1,21 @@ from collections import Iterable from graphene.core.fields import Field, IDField +from graphene.core.types.scalars import String, ID from graphql.core.type import GraphQLArgument, GraphQLID, GraphQLNonNull from graphql_relay.connection.arrayconnection import connection_from_list -from graphql_relay.connection.connection import connection_args from graphql_relay.node.node import from_global_id class ConnectionField(Field): - def __init__(self, field_type, resolve=None, description='', + def __init__(self, field_type, resolver=None, description='', connection_type=None, edge_type=None, **kwargs): - super(ConnectionField, self).__init__(field_type, resolve=resolve, - args=connection_args, + super(ConnectionField, self).__init__(field_type, resolver=resolver, + before=String(), + after=String(), + first=String(), + last=String(), description=description, **kwargs) self.connection_type = connection_type self.edge_type = edge_type @@ -60,12 +63,9 @@ class NodeField(Field): def __init__(self, object_type=None, *args, **kwargs): from graphene.relay.types import Node + kwargs['id'] = ID(description='The ID of an object') super(NodeField, self).__init__(object_type or Node, *args, **kwargs) self.field_object_type = object_type - self.args['id'] = GraphQLArgument( - GraphQLNonNull(GraphQLID), - description='The ID of an object' - ) def id_fetcher(self, global_id, info): from graphene.relay.utils import is_node @@ -88,11 +88,11 @@ class GlobalIDField(IDField): '''The ID of an object''' required = True - def contribute_to_class(self, cls, name, add=True): + def contribute_to_class(self, cls, name): from graphene.relay.utils import is_node, is_node_type in_node = is_node(cls) or is_node_type(cls) assert in_node, 'GlobalIDField could only be inside a Node, but got %r' % cls - super(GlobalIDField, self).contribute_to_class(cls, name, add) + super(GlobalIDField, self).contribute_to_class(cls, name) def resolve(self, instance, args, info): return self.object_type.to_global_id(instance, args, info) diff --git a/graphene/relay/types.py b/graphene/relay/types.py index 748db640..dfbd51c1 100644 --- a/graphene/relay/types.py +++ b/graphene/relay/types.py @@ -1,6 +1,8 @@ from graphene.core.fields import BooleanField, Field, ListField, StringField from graphene.core.types import (InputObjectType, Interface, Mutation, ObjectType) +from graphene.core.types.argument import ArgumentsGroup +from graphene.core.types.definitions import NonNull from graphene.relay.fields import GlobalIDField from graphene.utils import memoize from graphql_relay.node.node import to_global_id @@ -90,7 +92,7 @@ class BaseNode(object): class Node(BaseNode, Interface): '''An object with an ID''' - id = GlobalIDField() + id = GlobalIDField(required=True) class MutationInputType(InputObjectType): @@ -102,19 +104,19 @@ class ClientIDMutation(Mutation): @classmethod def _prepare_class(cls): - input_type = getattr(cls, 'input_type', None) - if input_type: + Input = getattr(cls, 'Input', None) + if Input: assert hasattr( cls, 'mutate_and_get_payload'), 'You have to implement mutate_and_get_payload' - new_input_inner_type = type('{}InnerInput'.format( - cls._meta.type_name), (MutationInputType, input_type, ), {}) - items = { - 'input': Field(new_input_inner_type) - } - assert issubclass(new_input_inner_type, InputObjectType) - input_type = type('{}Input'.format( - cls._meta.type_name), (ObjectType, ), items) - setattr(cls, 'input_type', input_type) + + items = dict(Input.__dict__) + items.pop('__dict__', None) + new_input_type = type('{}Input'.format( + cls._meta.type_name), (MutationInputType, ), items) + cls.add_to_class('input_type', new_input_type) + arguments = ArgumentsGroup(input=NonNull(new_input_type)) + cls.add_to_class('arguments', arguments) + delattr(cls, 'Input') @classmethod def mutate(cls, instance, args, info): diff --git a/tests/relay/test_relay_mutations.py b/tests/relay/test_relay_mutations.py index de206d70..5f3a91a3 100644 --- a/tests/relay/test_relay_mutations.py +++ b/tests/relay/test_relay_mutations.py @@ -32,19 +32,19 @@ class MyResultMutation(graphene.ObjectType): schema = Schema(query=Query, mutation=MyResultMutation) -def test_mutation_input(): - assert ChangeNumber.input_type - assert ChangeNumber.input_type._meta.type_name == 'ChangeNumberInput' - assert list(ChangeNumber.input_type._meta.fields_map.keys()) == ['input'] - _input = ChangeNumber.input_type._meta.fields_map['input'] - inner_type = _input.get_object_type(schema) - client_mutation_id_field = inner_type._meta.fields_map[ - 'client_mutation_id'] - assert issubclass(inner_type, InputObjectType) - assert isinstance(client_mutation_id_field, graphene.StringField) - assert client_mutation_id_field.object_type == inner_type - assert isinstance(client_mutation_id_field.internal_field( - schema), GraphQLInputObjectField) +def test_mutation_arguments(): + assert ChangeNumber.arguments + assert list(ChangeNumber.arguments) == ['input'] + _input = ChangeNumber.arguments['input'] + + # inner_type = _input.get_object_type(schema) + # client_mutation_id_field = inner_type._meta.fields_map[ + # 'client_mutation_id'] + # assert issubclass(inner_type, InputObjectType) + # assert isinstance(client_mutation_id_field, graphene.StringField) + # assert client_mutation_id_field.object_type == inner_type + # assert isinstance(client_mutation_id_field.internal_field( + # schema), GraphQLInputObjectField) def test_execute_mutations(): diff --git a/tests/relay/test_relayfields.py b/tests/relay/test_relayfields.py index 34e6d305..44313daf 100644 --- a/tests/relay/test_relayfields.py +++ b/tests/relay/test_relayfields.py @@ -21,7 +21,7 @@ class MyNode(relay.Node): class Query(graphene.ObjectType): my_node = relay.NodeField(MyNode) all_my_nodes = relay.ConnectionField( - MyNode, connection_type=MyConnection, customArg=graphene.Argument(graphene.String)) + MyNode, connection_type=MyConnection, customArg=graphene.String()) def resolve_all_my_nodes(self, args, info): custom_arg = args.get('customArg') @@ -73,5 +73,6 @@ def test_nodefield_query(): def test_nodeidfield(): id_field = MyNode._meta.fields_map['id'] - assert isinstance(id_field.internal_field(schema).type, GraphQLNonNull) - assert id_field.internal_field(schema).type.of_type == GraphQLID + id_field_type = schema.T(id_field) + assert isinstance(id_field_type.type, GraphQLNonNull) + assert id_field_type.type.of_type == GraphQLID