Added support for graphql 0.5.0

This commit is contained in:
Syrus Akbary 2016-05-10 00:40:35 -07:00
parent 76ea7d7637
commit c738d3db28
23 changed files with 87 additions and 51 deletions

View File

@ -25,6 +25,8 @@ install:
if [ "$TEST_TYPE" = build ]; then if [ "$TEST_TYPE" = build ]; then
pip install --download-cache $HOME/.cache/pip/ pytest pytest-cov coveralls six pytest-django django-filter sqlalchemy_utils pip install --download-cache $HOME/.cache/pip/ pytest pytest-cov coveralls six pytest-django django-filter sqlalchemy_utils
pip install --download-cache $HOME/.cache/pip psycopg2 > /dev/null 2>&1 pip install --download-cache $HOME/.cache/pip psycopg2 > /dev/null 2>&1
pip install https://github.com/graphql-python/graphql-core/archive/master.zip
pip install https://github.com/graphql-python/graphql-relay-py/archive/master.zip
pip install --download-cache $HOME/.cache/pip/ -e .[django] pip install --download-cache $HOME/.cache/pip/ -e .[django]
pip install --download-cache $HOME/.cache/pip/ -e .[sqlalchemy] pip install --download-cache $HOME/.cache/pip/ -e .[sqlalchemy]
pip install django==$DJANGO_VERSION pip install django==$DJANGO_VERSION

View File

@ -33,7 +33,8 @@ from graphene.core.fields import (
) )
from graphene.utils import ( from graphene.utils import (
resolve_only_args resolve_only_args,
with_context
) )
__all__ = [ __all__ = [

View File

@ -72,8 +72,8 @@ class DjangoDebugPlugin(Plugin):
@contextmanager @contextmanager
def context_execution(self, executor): def context_execution(self, executor):
executor['root'] = WrappedRoot(root=executor['root']) executor['root_value'] = WrappedRoot(root=executor.get('root_value'))
executor['schema'] = self.wrap_schema(executor['schema']) executor['schema'] = self.wrap_schema(executor['schema'])
self.enable_instrumentation(executor['root']) self.enable_instrumentation(executor['root_value'])
yield executor yield executor
self.disable_instrumentation() self.disable_instrumentation()

View File

@ -149,6 +149,7 @@ def test_should_query_connection():
schema = graphene.Schema(query=Query, plugins=[DjangoDebugPlugin()]) schema = graphene.Schema(query=Query, plugins=[DjangoDebugPlugin()])
result = schema.execute(query) result = schema.execute(query)
assert not result.errors assert not result.errors
print result.data['__debug']['sql']
assert result.data['allReporters'] == expected['allReporters'] assert result.data['allReporters'] == expected['allReporters']
assert 'COUNT' in result.data['__debug']['sql'][0]['rawSql'] assert 'COUNT' in result.data['__debug']['sql'][0]['rawSql']
query = str(Reporter.objects.all()[:1].query) query = str(Reporter.objects.all()[:1].query)

View File

@ -14,15 +14,15 @@ class GlobalIDFilter(Filter):
field_class = GlobalIDFormField field_class = GlobalIDFormField
def filter(self, qs, value): def filter(self, qs, value):
gid = from_global_id(value) _type, _id = from_global_id(value)
return super(GlobalIDFilter, self).filter(qs, gid.id) return super(GlobalIDFilter, self).filter(qs, _id)
class GlobalIDMultipleChoiceFilter(MultipleChoiceFilter): class GlobalIDMultipleChoiceFilter(MultipleChoiceFilter):
field_class = GlobalIDMultipleChoiceField field_class = GlobalIDMultipleChoiceField
def filter(self, qs, value): def filter(self, qs, value):
gids = [from_global_id(v).id for v in value] gids = [from_global_id(v)[1] for v in value]
return super(GlobalIDMultipleChoiceFilter, self).filter(qs, gids) return super(GlobalIDMultipleChoiceFilter, self).filter(qs, gids)

View File

@ -16,13 +16,13 @@ class GlobalIDFormField(Field):
return None return None
try: try:
gid = from_global_id(value) _type, _id = from_global_id(value)
except (TypeError, ValueError, UnicodeDecodeError, binascii.Error): except (TypeError, ValueError, UnicodeDecodeError, binascii.Error):
raise ValidationError(self.error_messages['invalid']) raise ValidationError(self.error_messages['invalid'])
try: try:
IntegerField().clean(gid.id) IntegerField().clean(_id)
CharField().clean(gid.type) CharField().clean(_type)
except ValidationError: except ValidationError:
raise ValidationError(self.error_messages['invalid']) raise ValidationError(self.error_messages['invalid'])

View File

@ -11,6 +11,3 @@ class GraphQLView(BaseGraphQLView):
executor=schema.executor, executor=schema.executor,
**kwargs **kwargs
) )
def execute(self, *args, **kwargs):
return self.graphene_schema.execute(*args, **kwargs)

View File

@ -25,4 +25,4 @@ def test_uniontype():
assert isinstance(object_type, GraphQLUnionType) assert isinstance(object_type, GraphQLUnionType)
assert Thing._meta.type_name == 'Thing' assert Thing._meta.type_name == 'Thing'
assert object_type.description == 'Thing union description' assert object_type.description == 'Thing union description'
assert object_type.get_possible_types() == [schema.T(Human), schema.T(Pet)] assert object_type.get_types() == [schema.T(Human), schema.T(Pet)]

View File

@ -1,7 +1,6 @@
import inspect import inspect
from collections import OrderedDict
from graphql.execution.executors.sync import SyncExecutor from graphql import graphql
from graphql.type import GraphQLSchema as _GraphQLSchema from graphql.type import GraphQLSchema as _GraphQLSchema
from graphql.utils.introspection_query import introspection_query from graphql.utils.introspection_query import introspection_query
from graphql.utils.schema_printer import print_schema from graphql.utils.schema_printer import print_schema
@ -116,10 +115,19 @@ class Schema(object):
def types(self): def types(self):
return self._types_names return self._types_names
def execute(self, request='', root=None, args=None, **kwargs): def execute(self, request_string='', root_value=None, variable_values=None,
kwargs = dict(kwargs, request=request, root=root, args=args, schema=self.schema) context_value=None, operation_name=None, executor=None):
kwargs = dict(
schema=self.schema,
request_string=request_string,
root_value=root_value,
context_value=context_value,
variable_values=variable_values,
operation_name=operation_name,
executor=executor or self._executor
)
with self.plugins.context_execution(**kwargs) as execute_kwargs: with self.plugins.context_execution(**kwargs) as execute_kwargs:
return self.executor.execute(**execute_kwargs) return graphql(**execute_kwargs)
def introspect(self): def introspect(self):
return self.execute(introspection_query).data return graphql(self.schema, introspection_query).data

View File

@ -58,6 +58,6 @@ def test_execute_mutations():
'result': '5', 'result': '5',
} }
} }
result = schema.execute(query, root=object()) result = schema.execute(query, root_value=object())
assert not result.errors assert not result.errors
assert result.data == expected assert result.data == expected

View File

@ -94,7 +94,7 @@ def test_field_resolve():
f = StringField(required=True, resolve=lambda *args: 'RESOLVED').as_field() f = StringField(required=True, resolve=lambda *args: 'RESOLVED').as_field()
f.contribute_to_class(MyOt, 'field_name') f.contribute_to_class(MyOt, 'field_name')
field_type = schema.T(f) field_type = schema.T(f)
assert 'RESOLVED' == field_type.resolver(MyOt, None, None) assert 'RESOLVED' == field_type.resolver(MyOt, None, None, None)
def test_field_resolve_type_custom(): def test_field_resolve_type_custom():

View File

@ -40,7 +40,7 @@ Human_type = schema.T(Human)
def test_type(): def test_type():
assert Human._meta.fields_map['name'].resolver( assert Human._meta.fields_map['name'].resolver(
Human(object()), {}, None) == 'Peter' Human(object()), {}, None, None) == 'Peter'
def test_query(): def test_query():
@ -59,6 +59,6 @@ def test_query():
'type': 'Dog' 'type': 'Dog'
} }
} }
result = graphql(schema, query, root=Human(object())) result = graphql(schema, query, root_value=Human(object()))
assert not result.errors assert not result.errors
assert result.data == expected assert result.data == expected

View File

@ -63,7 +63,7 @@ def test_query_schema_graphql():
'type': 'Dog' 'type': 'Dog'
} }
} }
result = graphql(schema.schema, query, root=Human(object())) result = graphql(schema.schema, query, root_value=Human(object()))
assert not result.errors assert not result.errors
assert result.data == expected assert result.data == expected
@ -84,7 +84,7 @@ def test_query_schema_execute():
'type': 'Dog' 'type': 'Dog'
} }
} }
result = schema.execute(query, root=object()) result = schema.execute(query, root_value=object())
assert not result.errors assert not result.errors
assert result.data == expected assert result.data == expected
@ -93,8 +93,8 @@ def test_schema_get_type_map():
assert_equal_lists( assert_equal_lists(
schema.schema.get_type_map().keys(), schema.schema.get_type_map().keys(),
['__Field', 'String', 'Pet', 'Character', '__InputValue', ['__Field', 'String', 'Pet', 'Character', '__InputValue',
'__Directive', '__TypeKind', '__Schema', '__Type', 'Human', '__Directive', '__DirectiveLocation', '__TypeKind', '__Schema',
'__EnumValue', 'Boolean']) '__Type', 'Human', '__EnumValue', 'Boolean'])
def test_schema_no_query(): def test_schema_no_query():
@ -156,6 +156,10 @@ def test_lazytype():
def test_schema_str(): def test_schema_str():
expected = """ expected = """
schema {
query: Human
}
interface Character { interface Character {
name: String name: String
} }

View File

@ -57,7 +57,7 @@ def to_arguments(*args, **kwargs):
def snake_case_args(resolver): def snake_case_args(resolver):
@wraps(resolver) @wraps(resolver)
def wrapped_resolver(instance, args, info): def wrapped_resolver(instance, args, context, info):
return resolver(instance, ProxySnakeDict(args), info) return resolver(instance, ProxySnakeDict(args), context, info)
return wrapped_resolver return wrapped_resolver

View File

@ -5,6 +5,7 @@ import six
from graphql.type import GraphQLField, GraphQLInputObjectField from graphql.type import GraphQLField, GraphQLInputObjectField
from ...utils import maybe_func from ...utils import maybe_func
from ...utils.wrap_resolver_function import wrap_resolver_function
from ..classtypes.base import FieldsClassType from ..classtypes.base import FieldsClassType
from ..classtypes.inputobjecttype import InputObjectType from ..classtypes.inputobjecttype import InputObjectType
from ..classtypes.mutation import Mutation from ..classtypes.mutation import Mutation
@ -105,14 +106,15 @@ class Field(NamedType, OrderedType):
assert len(arguments) == 0 assert len(arguments) == 0
arguments = type_objecttype.get_arguments() arguments = type_objecttype.get_arguments()
resolver = getattr(type_objecttype, 'mutate') resolver = getattr(type_objecttype, 'mutate')
resolver = wrap_resolver_function(resolver)
else: else:
my_resolver = resolver my_resolver = wrap_resolver_function(resolver)
@wraps(my_resolver) @wraps(my_resolver)
def wrapped_func(instance, args, info): def wrapped_func(instance, args, context, info):
if not isinstance(instance, self.object_type): if not isinstance(instance, self.object_type):
instance = self.object_type(_root=instance) instance = self.object_type(_root=instance)
return my_resolver(instance, args, info) return my_resolver(instance, args, context, info)
resolver = wrapped_func resolver = wrapped_func
assert type, 'Internal type for field %s is None' % str(self) assert type, 'Internal type for field %s is None' % str(self)

View File

@ -48,7 +48,7 @@ def test_to_arguments_wrong_type():
def test_snake_case_args(): def test_snake_case_args():
def resolver(instance, args, info): def resolver(instance, args, context, info):
return args['my_arg']['inner_arg'] return args['my_arg']['inner_arg']
r = snake_case_args(resolver) r = snake_case_args(resolver)
assert r(None, {'myArg': {'innerArg': 3}}, None) == 3 assert r(None, {'myArg': {'innerArg': 3}}, None, None) == 3

View File

@ -25,7 +25,7 @@ def test_field_internal_type():
assert field.attname == 'my_field' assert field.attname == 'my_field'
assert isinstance(type, GraphQLField) assert isinstance(type, GraphQLField)
assert type.description == 'My argument' assert type.description == 'My argument'
assert type.resolver(None, {}, None) == 'RESOLVED' assert type.resolver(None, {}, None, None) == 'RESOLVED'
assert type.type == GraphQLString assert type.type == GraphQLString
@ -44,7 +44,7 @@ def test_field_objectype_resolver():
type = schema.T(field) type = schema.T(field)
assert isinstance(type, GraphQLField) assert isinstance(type, GraphQLField)
assert type.description == 'Custom description' assert type.description == 'Custom description'
assert type.resolver(Query(), {}, None) == 'RESOLVED' assert type.resolver(Query(), {}, None, None) == 'RESOLVED'
def test_field_custom_name(): def test_field_custom_name():
@ -143,7 +143,7 @@ def test_field_resolve_argument():
schema = Schema(query=Query) schema = Schema(query=Query)
type = schema.T(field) type = schema.T(field)
assert type.resolver(None, {'firstName': 'Peter'}, None) == 'Peter' assert type.resolver(None, {'firstName': 'Peter'}, None, None) == 'Peter'
def test_field_resolve_vars(): def test_field_resolve_vars():
@ -160,7 +160,7 @@ def test_field_resolve_vars():
{ {
hello(firstName:$firstName) hello(firstName:$firstName)
} }
""", args={"firstName": "Serkan"}) """, variable_values={"firstName": "Serkan"})
expected = { expected = {
'hello': 'Hello Serkan' 'hello': 'Hello Serkan'

View File

@ -4,7 +4,7 @@ from graphql_relay.node.node import from_global_id
from ..core.fields import Field from ..core.fields import Field
from ..core.types.definitions import NonNull from ..core.types.definitions import NonNull
from ..core.types.scalars import ID, Int, String from ..core.types.scalars import ID, Int, String
from ..utils import with_context
class ConnectionField(Field): class ConnectionField(Field):
@ -65,14 +65,13 @@ class NodeField(Field):
object_type or Node, id=id, *args, **kwargs) object_type or Node, id=id, *args, **kwargs)
self.field_object_type = object_type self.field_object_type = object_type
def id_fetcher(self, global_id, info): def id_fetcher(self, global_id, context, info):
from graphene.relay.utils import is_node from graphene.relay.utils import is_node
schema = info.schema.graphene_schema schema = info.schema.graphene_schema
try: try:
resolved_global_id = from_global_id(global_id) _type, _id = from_global_id(global_id)
except: except:
return None return None
_type, _id = resolved_global_id.type, resolved_global_id.id
object_type = schema.get_type(_type) object_type = schema.get_type(_type)
if isinstance(self.field_object_type, six.string_types): if isinstance(self.field_object_type, six.string_types):
field_object_type = schema.get_type(self.field_object_type) field_object_type = schema.get_type(self.field_object_type)
@ -83,9 +82,10 @@ class NodeField(Field):
return object_type.get_node(_id, info) return object_type.get_node(_id, info)
def resolver(self, instance, args, info): @with_context
def resolver(self, instance, args, context, info):
global_id = args.get('id') global_id = args.get('id')
return self.id_fetcher(global_id, info) return self.id_fetcher(global_id, context, info)
class GlobalIDField(Field): class GlobalIDField(Field):

View File

@ -76,6 +76,6 @@ def test_execute_mutations():
'result': '5', 'result': '5',
} }
} }
result = schema.execute(query, root=object()) result = schema.execute(query, root_value=object())
assert not result.errors assert not result.errors
assert result.data == expected assert result.data == expected

View File

@ -2,7 +2,7 @@ import pytest
from graphql.type import GraphQLID, GraphQLNonNull from graphql.type import GraphQLID, GraphQLNonNull
import graphene import graphene
from graphene import relay from graphene import relay, with_context
schema = graphene.Schema() schema = graphene.Schema()
@ -16,7 +16,7 @@ class MyNode(relay.Node):
name = graphene.String() name = graphene.String()
@classmethod @classmethod
def get_node(cls, id, info): def get_node(cls, id, context, info):
return MyNode(id=id, name='mo') return MyNode(id=id, name='mo')
@ -24,8 +24,9 @@ class SpecialNode(relay.Node):
value = graphene.String() value = graphene.String()
@classmethod @classmethod
def get_node(cls, id, info): @with_context
value = "!!!" if info.request_context.get('is_special') else "???" def get_node(cls, id, context, info):
value = "!!!" if context.get('is_special') else "???"
return SpecialNode(id=id, value=value) return SpecialNode(id=id, value=value)
@ -108,7 +109,7 @@ def test_get_node_info(specialness, value):
'value': value, 'value': value,
}, },
} }
result = schema.execute(query, request_context={'is_special': specialness}) result = schema.execute(query, context_value={'is_special': specialness})
assert not result.errors assert not result.errors
assert result.data == expected assert result.data == expected

View File

@ -14,6 +14,7 @@ from ..core.types import Boolean, Field, List, String
from ..core.types.argument import ArgumentsGroup from ..core.types.argument import ArgumentsGroup
from ..core.types.definitions import NonNull from ..core.types.definitions import NonNull
from ..utils import memoize from ..utils import memoize
from ..utils.wrap_resolver_function import wrap_resolver_function
from .fields import GlobalIDField from .fields import GlobalIDField

View File

@ -5,8 +5,10 @@ from .maybe_func import maybe_func
from .misc import enum_to_graphql_enum from .misc import enum_to_graphql_enum
from .resolve_only_args import resolve_only_args from .resolve_only_args import resolve_only_args
from .lazylist import LazyList from .lazylist import LazyList
from .wrap_resolver_function import with_context, wrap_resolver_function
__all__ = ['to_camel_case', 'to_snake_case', 'to_const', 'ProxySnakeDict', __all__ = ['to_camel_case', 'to_snake_case', 'to_const', 'ProxySnakeDict',
'cached_property', 'memoize', 'maybe_func', 'enum_to_graphql_enum', 'cached_property', 'memoize', 'maybe_func', 'enum_to_graphql_enum',
'resolve_only_args', 'LazyList'] 'resolve_only_args', 'LazyList', 'with_context',
'wrap_resolver_function']

View File

@ -0,0 +1,17 @@
from functools import wraps
def with_context(func):
setattr(func, 'with_context', 'context')
return func
def wrap_resolver_function(func):
@wraps(func)
def inner(self, args, context, info):
with_context = getattr(func, 'with_context', None)
if with_context:
return func(self, args, context, info)
# For old compatibility
return func(self, args, info)
return inner