Improved Mutation with custom Field and output

This commit is contained in:
Syrus Akbary 2017-06-29 22:18:04 -07:00
parent 078230ad49
commit f22504c2fc
3 changed files with 49 additions and 20 deletions

View File

@ -5,14 +5,14 @@ import six
from promise import Promise from promise import Promise
from ..types import AbstractType, Argument, Field, InputObjectType, String from ..types import Field, AbstractType, Argument, InputObjectType, String
from ..types.objecttype import ObjectType, ObjectTypeMeta from ..types.mutation import Mutation, MutationMeta
from ..types.objecttype import ObjectTypeMeta
from ..utils.is_base_type import is_base_type from ..utils.is_base_type import is_base_type
from ..utils.props import props from ..utils.props import props
class ClientIDMutationMeta(ObjectTypeMeta): class ClientIDMutationMeta(MutationMeta):
def __new__(cls, name, bases, attrs): def __new__(cls, name, bases, attrs):
# Also ensure initialization is only performed for subclasses of # Also ensure initialization is only performed for subclasses of
# Mutation # Mutation
@ -23,13 +23,13 @@ class ClientIDMutationMeta(ObjectTypeMeta):
base_name = re.sub('Payload$', '', name) base_name = re.sub('Payload$', '', name)
if 'client_mutation_id' not in attrs: if 'client_mutation_id' not in attrs:
attrs['client_mutation_id'] = String(name='clientMutationId') attrs['client_mutation_id'] = String(name='clientMutationId')
cls = ObjectTypeMeta.__new__(cls, '{}Payload'.format(base_name), bases, attrs) cls = ObjectTypeMeta.__new__(cls, '{}Payload'.format(base_name), bases,
attrs)
mutate_and_get_payload = getattr(cls, 'mutate_and_get_payload', None) mutate_and_get_payload = getattr(cls, 'mutate_and_get_payload', None)
if cls.mutate and cls.mutate.__func__ == ClientIDMutation.mutate.__func__: if cls.mutate and cls.mutate.__func__ == ClientIDMutation.mutate.__func__:
assert mutate_and_get_payload, ( assert mutate_and_get_payload, (
"{}.mutate_and_get_payload method is required" "{}.mutate_and_get_payload method is required"
" in a ClientIDMutation." " in a ClientIDMutation.").format(name)
).format(name)
input_attrs = {} input_attrs = {}
bases = () bases = ()
if not input_class: if not input_class:
@ -39,13 +39,18 @@ class ClientIDMutationMeta(ObjectTypeMeta):
else: else:
bases += (input_class, ) bases += (input_class, )
input_attrs['client_mutation_id'] = String(name='clientMutationId') input_attrs['client_mutation_id'] = String(name='clientMutationId')
cls.Input = type('{}Input'.format(base_name), bases + (InputObjectType,), input_attrs) cls.Input = type('{}Input'.format(base_name),
cls.Field = partial(Field, cls, resolver=cls.mutate, input=Argument(cls.Input, required=True)) bases + (InputObjectType, ), input_attrs)
output_class = getattr(cls, 'Output', cls)
cls.Field = partial(
Field,
output_class,
resolver=cls.mutate,
input=Argument(cls.Input, required=True))
return cls return cls
class ClientIDMutation(six.with_metaclass(ClientIDMutationMeta, ObjectType)): class ClientIDMutation(six.with_metaclass(ClientIDMutationMeta, Mutation)):
@classmethod @classmethod
def mutate(cls, root, args, context, info): def mutate(cls, root, args, context, info):
input = args.get('input') input = args.get('input')
@ -54,11 +59,10 @@ class ClientIDMutation(six.with_metaclass(ClientIDMutationMeta, ObjectType)):
try: try:
payload.client_mutation_id = input.get('clientMutationId') payload.client_mutation_id = input.get('clientMutationId')
except: except:
raise Exception(( raise Exception(
'Cannot set client_mutation_id in the payload object {}' ('Cannot set client_mutation_id in the payload object {}'
).format(repr(payload))) ).format(repr(payload)))
return payload return payload
return Promise.resolve( return Promise.resolve(
cls.mutate_and_get_payload(input, context, info) cls.mutate_and_get_payload(input, context, info)).then(on_resolve)
).then(on_resolve)

View File

@ -10,7 +10,6 @@ from .objecttype import ObjectType, ObjectTypeMeta
class MutationMeta(ObjectTypeMeta): class MutationMeta(ObjectTypeMeta):
def __new__(cls, name, bases, attrs): def __new__(cls, name, bases, attrs):
# Also ensure initialization is only performed for subclasses of # Also ensure initialization is only performed for subclasses of
# Mutation # Mutation
@ -21,10 +20,12 @@ class MutationMeta(ObjectTypeMeta):
cls = ObjectTypeMeta.__new__(cls, name, bases, attrs) cls = ObjectTypeMeta.__new__(cls, name, bases, attrs)
field_args = props(input_class) if input_class else {} field_args = props(input_class) if input_class else {}
output_class = getattr(cls, 'Output', cls)
resolver = getattr(cls, 'mutate', None) resolver = getattr(cls, 'mutate', None)
assert resolver, 'All mutations must define a mutate method in it' assert resolver, 'All mutations must define a mutate method in it'
resolver = get_unbound_function(resolver) resolver = get_unbound_function(resolver)
cls.Field = partial(Field, cls, args=field_args, resolver=resolver) cls.Field = partial(
Field, output_class, args=field_args, resolver=resolver)
return cls return cls

View File

@ -3,6 +3,7 @@ import pytest
from ..mutation import Mutation from ..mutation import Mutation
from ..objecttype import ObjectType from ..objecttype import ObjectType
from ..schema import Schema from ..schema import Schema
from ..argument import Argument
from ..scalars import String from ..scalars import String
from ..dynamic import Dynamic from ..dynamic import Dynamic
@ -10,6 +11,7 @@ from ..dynamic import Dynamic
def test_generate_mutation_no_args(): def test_generate_mutation_no_args():
class MyMutation(Mutation): class MyMutation(Mutation):
'''Documentation''' '''Documentation'''
@classmethod @classmethod
def mutate(cls, *args, **kwargs): def mutate(cls, *args, **kwargs):
pass pass
@ -22,7 +24,6 @@ def test_generate_mutation_no_args():
def test_generate_mutation_with_meta(): def test_generate_mutation_with_meta():
class MyMutation(Mutation): class MyMutation(Mutation):
class Meta: class Meta:
name = 'MyOtherMutation' name = 'MyOtherMutation'
description = 'Documentation' description = 'Documentation'
@ -38,10 +39,33 @@ def test_generate_mutation_with_meta():
def test_mutation_raises_exception_if_no_mutate(): def test_mutation_raises_exception_if_no_mutate():
with pytest.raises(AssertionError) as excinfo: with pytest.raises(AssertionError) as excinfo:
class MyMutation(Mutation): class MyMutation(Mutation):
pass pass
assert "All mutations must define a mutate method in it" == str(excinfo.value) assert "All mutations must define a mutate method in it" == str(
excinfo.value)
def test_mutation_custom_output_type():
class User(ObjectType):
name = String()
class CreateUser(Mutation):
class Input:
name = String()
Output = User
@classmethod
def mutate(cls, args, context, info):
name = args.get('name')
return User(name=name)
field = CreateUser.Field()
assert field.type == User
assert field.args == {'name': Argument(String)}
assert field.resolver == CreateUser.mutate
def test_mutation_execution(): def test_mutation_execution():