mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-22 09:36:44 +03:00
Improved tests and schema definition.
This commit is contained in:
parent
750bbfbc2c
commit
eafc9a102e
|
@ -6,6 +6,21 @@ from graphql.core.type import (
|
|||
GraphQLID as ID
|
||||
)
|
||||
|
||||
from graphene import signals
|
||||
|
||||
from graphene.core.schema import (
|
||||
Schema
|
||||
)
|
||||
|
||||
from graphene.env import (
|
||||
get_global_schema
|
||||
)
|
||||
|
||||
from graphene.core.types import (
|
||||
ObjectType,
|
||||
Interface
|
||||
)
|
||||
|
||||
from graphene.core.fields import (
|
||||
Field,
|
||||
StringField,
|
||||
|
@ -16,12 +31,6 @@ from graphene.core.fields import (
|
|||
NonNullField,
|
||||
)
|
||||
|
||||
from graphene.core.types import (
|
||||
ObjectType,
|
||||
Interface,
|
||||
Schema
|
||||
)
|
||||
|
||||
from graphene.decorators import (
|
||||
resolve_only_args
|
||||
)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import inspect
|
||||
from graphql.core.type import (
|
||||
GraphQLField,
|
||||
GraphQLList,
|
||||
|
@ -9,7 +10,7 @@ from graphql.core.type import (
|
|||
GraphQLArgument,
|
||||
)
|
||||
from graphene.utils import cached_property
|
||||
from graphene.core.utils import get_object_type
|
||||
from graphene.core.types import ObjectType
|
||||
|
||||
class Field(object):
|
||||
def __init__(self, field_type, resolve=None, null=True, args=None, description='', **extra_args):
|
||||
|
@ -25,6 +26,7 @@ class Field(object):
|
|||
def contribute_to_class(self, cls, name):
|
||||
self.field_name = name
|
||||
self.object_type = cls
|
||||
self.schema = cls._meta.schema
|
||||
if isinstance(self.field_type, Field) and not self.field_type.object_type:
|
||||
self.field_type.contribute_to_class(cls, name)
|
||||
cls._meta.add_field(self)
|
||||
|
@ -42,12 +44,27 @@ class Field(object):
|
|||
resolve_fn = lambda root, args, info: root.resolve(self.field_name, args, info)
|
||||
return resolve_fn(instance, args, info)
|
||||
|
||||
def get_object_type(self):
|
||||
field_type = self.field_type
|
||||
_is_class = inspect.isclass(field_type)
|
||||
if _is_class and issubclass(field_type, ObjectType):
|
||||
return field_type
|
||||
elif isinstance(field_type, basestring):
|
||||
if field_type == 'self':
|
||||
return self.object_type
|
||||
elif self.schema:
|
||||
return self.schema.get_type(field_type)
|
||||
|
||||
@cached_property
|
||||
def type(self):
|
||||
if isinstance(self.field_type, Field):
|
||||
field_type = self.field_type
|
||||
if isinstance(field_type, Field):
|
||||
field_type = self.field_type.type
|
||||
else:
|
||||
field_type = get_object_type(self.field_type, self.object_type)
|
||||
object_type = self.get_object_type()
|
||||
if object_type:
|
||||
field_type = object_type._meta.type
|
||||
|
||||
field_type = self.type_wrapper(field_type)
|
||||
return field_type
|
||||
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
from graphene.env import get_global_schema
|
||||
from graphene.utils import cached_property
|
||||
|
||||
DEFAULT_NAMES = ('app_label', 'description', 'name', 'interface',
|
||||
DEFAULT_NAMES = ('description', 'name', 'interface', 'schema',
|
||||
'type_name', 'interfaces', 'proxy')
|
||||
|
||||
|
||||
class Options(object):
|
||||
def __init__(self, meta=None, app_label=None):
|
||||
def __init__(self, meta=None, schema=None):
|
||||
self.meta = meta
|
||||
self.local_fields = []
|
||||
self.interface = False
|
||||
self.proxy = False
|
||||
self.schema = schema or get_global_schema()
|
||||
self.interfaces = []
|
||||
self.parents = []
|
||||
self.app_label = app_label
|
||||
|
||||
def contribute_to_class(self, cls, name):
|
||||
cls._meta = self
|
||||
|
|
73
graphene/core/schema.py
Normal file
73
graphene/core/schema.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
from graphql.core import graphql
|
||||
from graphql.core.type import (
|
||||
GraphQLSchema
|
||||
)
|
||||
from graphene import signals
|
||||
from graphene.utils import cached_property
|
||||
# from graphene.relay.nodes import create_node_definitions
|
||||
|
||||
class Schema(object):
|
||||
_query = None
|
||||
|
||||
def __init__(self, query=None, mutation=None, name='Schema'):
|
||||
self.mutation = mutation
|
||||
self.query = query
|
||||
self.name = name
|
||||
self._types = {}
|
||||
|
||||
def __repr__(self):
|
||||
return '<Schema: %s>' % str(self.name)
|
||||
|
||||
# @cachedproperty
|
||||
# def node_definitions(self):
|
||||
# return [object, object]
|
||||
# # from graphene.relay import create_node_definitions
|
||||
# # return create_node_definitions(schema=self)
|
||||
|
||||
# @property
|
||||
# def Node(self):
|
||||
# return self.node_definitions[0]
|
||||
|
||||
# @property
|
||||
# def NodeField(self):
|
||||
# return self.node_definitions[1]
|
||||
|
||||
@property
|
||||
def query(self):
|
||||
return self._query
|
||||
@query.setter
|
||||
def query(self, query):
|
||||
if not query:
|
||||
return
|
||||
self._query = query
|
||||
self._query_type = query._meta.type
|
||||
self._schema = GraphQLSchema(query=self._query_type, mutation=self.mutation)
|
||||
|
||||
def register_type(self, type):
|
||||
type_name = type._meta.type_name
|
||||
if type_name in self._types:
|
||||
raise Exception('Type name %s already registered in %r' % (type_name, self))
|
||||
self._types[type_name] = type
|
||||
|
||||
def get_type(self, type_name):
|
||||
if type_name not in self._types:
|
||||
raise Exception('Type %s not found in %r' % (type_name, self))
|
||||
return self._types[type_name]
|
||||
|
||||
def execute(self, request='', root=None, vars=None, operation_name=None):
|
||||
return graphql(
|
||||
self._schema,
|
||||
request=request,
|
||||
root=root or self.query(),
|
||||
vars=vars,
|
||||
operation_name=operation_name
|
||||
)
|
||||
|
||||
|
||||
@signals.class_prepared.connect
|
||||
def object_type_created(object_type):
|
||||
schema = object_type._meta.schema
|
||||
if schema:
|
||||
schema.register_type(object_type)
|
||||
|
||||
from graphene.env import get_global_schema
|
|
@ -3,10 +3,8 @@ import six
|
|||
|
||||
from graphql.core.type import (
|
||||
GraphQLObjectType,
|
||||
GraphQLInterfaceType,
|
||||
GraphQLSchema
|
||||
GraphQLInterfaceType
|
||||
)
|
||||
from graphql.core import graphql
|
||||
|
||||
from graphene import signals
|
||||
from graphene.core.options import Options
|
||||
|
@ -33,12 +31,9 @@ class ObjectTypeMeta(type):
|
|||
meta = attr_meta
|
||||
base_meta = getattr(new_class, '_meta', None)
|
||||
|
||||
if '.' in module:
|
||||
app_label, _ = module.rsplit('.', 1)
|
||||
else:
|
||||
app_label = module
|
||||
schema = (base_meta and base_meta.schema)
|
||||
|
||||
new_class.add_to_class('_meta', Options(meta, app_label))
|
||||
new_class.add_to_class('_meta', Options(meta, schema))
|
||||
if base_meta and base_meta.proxy:
|
||||
new_class._meta.interface = base_meta.interface
|
||||
# Add all attributes to the class.
|
||||
|
@ -54,6 +49,8 @@ class ObjectTypeMeta(type):
|
|||
# Things without _meta aren't functional models, so they're
|
||||
# uninteresting parents.
|
||||
continue
|
||||
if base._meta.schema != new_class._meta.schema:
|
||||
raise Exception('The parent schema is not the same')
|
||||
|
||||
parent_fields = base._meta.local_fields
|
||||
# Check for clashes between locally declared fields and those
|
||||
|
@ -138,19 +135,3 @@ class Interface(ObjectType):
|
|||
class Meta:
|
||||
interface = True
|
||||
proxy = True
|
||||
|
||||
|
||||
class Schema(object):
|
||||
def __init__(self, query, mutation=None):
|
||||
self.query = query
|
||||
self.query_type = query._meta.type
|
||||
self._schema = GraphQLSchema(query=self.query_type, mutation=mutation)
|
||||
|
||||
def execute(self, request='', root=None, vars=None, operation_name=None):
|
||||
return graphql(
|
||||
self._schema,
|
||||
request=request,
|
||||
root=root or self.query(),
|
||||
vars=vars,
|
||||
operation_name=operation_name
|
||||
)
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
import inspect
|
||||
|
||||
from graphene.core.types import ObjectType
|
||||
from graphene import signals
|
||||
|
||||
registered_object_types = []
|
||||
|
||||
|
||||
def get_object_type(field_type, object_type=None):
|
||||
native_type = get_type(field_type, object_type)
|
||||
if native_type:
|
||||
field_type = native_type._meta.type
|
||||
return field_type
|
||||
|
||||
|
||||
def get_type(field_type, object_type=None):
|
||||
_is_class = inspect.isclass(field_type)
|
||||
if _is_class and issubclass(field_type, ObjectType):
|
||||
return field_type
|
||||
elif isinstance(field_type, basestring):
|
||||
if field_type == 'self':
|
||||
return object_type
|
||||
else:
|
||||
object_type = get_registered_object_type(field_type, object_type)
|
||||
return object_type
|
||||
return None
|
||||
|
||||
def get_registered_object_type(name, object_type=None):
|
||||
app_label = None
|
||||
object_type_name = name
|
||||
|
||||
if '.' in name:
|
||||
app_label, object_type_name = name.rsplit('.', 1)
|
||||
elif object_type:
|
||||
app_label = object_type._meta.app_label
|
||||
|
||||
# Filter all registered object types which have the same name
|
||||
ots = [ot for ot in registered_object_types if ot._meta.type_name == object_type_name]
|
||||
# If the list have more than one object type with the name, filter by
|
||||
# the app_label
|
||||
if len(ots)>1 and app_label:
|
||||
ots = [ot for ot in ots if ot._meta.app_label == app_label]
|
||||
|
||||
if len(ots)>1:
|
||||
raise Exception('Multiple ObjectTypes returned with the name %s' % name)
|
||||
if not ots:
|
||||
raise Exception('No ObjectType found with name %s' % name)
|
||||
|
||||
return ots[0]
|
||||
|
||||
|
||||
@signals.class_prepared.connect
|
||||
def object_type_created(sender):
|
||||
registered_object_types.append(sender)
|
9
graphene/env.py
Normal file
9
graphene/env.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from graphene.core.schema import Schema
|
||||
|
||||
_global_schema = None
|
||||
|
||||
def get_global_schema():
|
||||
global _global_schema
|
||||
if not _global_schema:
|
||||
_global_schema = Schema(name='Global Schema')
|
||||
return _global_schema
|
|
@ -1,85 +1,5 @@
|
|||
import collections
|
||||
|
||||
from graphene import signals
|
||||
from graphene.core.fields import Field, NativeField
|
||||
from graphene.core.types import Interface
|
||||
from graphene.core.utils import get_type
|
||||
from graphene.utils import cached_property
|
||||
|
||||
from graphql_relay.node.node import (
|
||||
nodeDefinitions,
|
||||
globalIdField,
|
||||
fromGlobalId
|
||||
)
|
||||
from graphql_relay.connection.arrayconnection import (
|
||||
connectionFromArray
|
||||
)
|
||||
from graphql_relay.connection.connection import (
|
||||
connectionArgs,
|
||||
connectionDefinitions
|
||||
from graphene.relay.nodes import (
|
||||
create_node_definitions
|
||||
)
|
||||
|
||||
registered_nodes = {}
|
||||
|
||||
|
||||
def getNode(globalId, *args):
|
||||
resolvedGlobalId = fromGlobalId(globalId)
|
||||
_type, _id = resolvedGlobalId.type, resolvedGlobalId.id
|
||||
if _type in registered_nodes:
|
||||
object_type = registered_nodes[_type]
|
||||
return object_type.get_node(_id)
|
||||
|
||||
|
||||
def getNodeType(obj):
|
||||
return obj._meta.type
|
||||
|
||||
|
||||
_nodeDefinitions = nodeDefinitions(getNode, getNodeType)
|
||||
|
||||
|
||||
class Node(Interface):
|
||||
@classmethod
|
||||
def get_graphql_type(cls):
|
||||
if cls is Node:
|
||||
# Return only nodeInterface when is the Node Inerface
|
||||
return _nodeDefinitions.nodeInterface
|
||||
return super(Node, cls).get_graphql_type()
|
||||
|
||||
|
||||
class NodeField(NativeField):
|
||||
field = _nodeDefinitions.nodeField
|
||||
|
||||
|
||||
class ConnectionField(Field):
|
||||
def __init__(self, field_type, resolve=None, description=''):
|
||||
super(ConnectionField, self).__init__(field_type, resolve=resolve,
|
||||
args=connectionArgs, description=description)
|
||||
|
||||
def resolve(self, instance, args, info):
|
||||
resolved = super(ConnectionField, self).resolve(instance, args, info)
|
||||
if resolved:
|
||||
assert isinstance(resolved, collections.Iterable), 'Resolved value from the connection field have to be iterable'
|
||||
return connectionFromArray(resolved, args)
|
||||
|
||||
@cached_property
|
||||
def type(self):
|
||||
object_type = get_type(self.field_type, self.object_type)
|
||||
assert issubclass(object_type, Node), 'Only nodes have connections.'
|
||||
return object_type.connection
|
||||
|
||||
|
||||
@signals.class_prepared.connect
|
||||
def object_type_created(object_type):
|
||||
if issubclass(object_type, Node):
|
||||
type_name = object_type._meta.type_name
|
||||
assert type_name not in registered_nodes, 'Two nodes with the same type_name: %s' % type_name
|
||||
registered_nodes[type_name] = object_type
|
||||
# def getId(*args, **kwargs):
|
||||
# print '**GET ID', args, kwargs
|
||||
# return 2
|
||||
field = NativeField(globalIdField(type_name))
|
||||
object_type.add_to_class('id', field)
|
||||
assert hasattr(object_type, 'get_node'), 'get_node classmethod not found in %s Node' % type_name
|
||||
|
||||
connection = connectionDefinitions(type_name, object_type._meta.type).connectionType
|
||||
object_type.add_to_class('connection', connection)
|
||||
from graphene.relay.relay import *
|
||||
|
|
37
graphene/relay/nodes.py
Normal file
37
graphene/relay/nodes.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
from graphql_relay.node.node import (
|
||||
nodeDefinitions,
|
||||
fromGlobalId
|
||||
)
|
||||
|
||||
def create_node_definitions(getNode=None, getNodeType=None, schema=None):
|
||||
from graphene.core.types import Interface
|
||||
from graphene.core.fields import Field, NativeField
|
||||
if not getNode:
|
||||
def getNode(globalId, *args):
|
||||
from graphene.env import get_global_schema
|
||||
_schema = schema or get_global_schema()
|
||||
resolvedGlobalId = fromGlobalId(globalId)
|
||||
_type, _id = resolvedGlobalId.type, resolvedGlobalId.id
|
||||
object_type = _schema.get_type(_type)
|
||||
return object_type.get_node(_id)
|
||||
|
||||
if not getNodeType:
|
||||
def getNodeType(obj):
|
||||
return obj._meta.type
|
||||
|
||||
_nodeDefinitions = nodeDefinitions(getNode, getNodeType)
|
||||
|
||||
|
||||
class Node(Interface):
|
||||
@classmethod
|
||||
def get_graphql_type(cls):
|
||||
if cls is Node:
|
||||
# Return only nodeInterface when is the Node Inerface
|
||||
return _nodeDefinitions.nodeInterface
|
||||
return super(Node, cls).get_graphql_type()
|
||||
|
||||
|
||||
class NodeField(NativeField):
|
||||
field = _nodeDefinitions.nodeField
|
||||
|
||||
return Node, NodeField
|
51
graphene/relay/relay.py
Normal file
51
graphene/relay/relay.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
import collections
|
||||
|
||||
from graphene import signals
|
||||
from graphene.utils import cached_property
|
||||
|
||||
from graphql_relay.node.node import (
|
||||
globalIdField
|
||||
)
|
||||
from graphql_relay.connection.arrayconnection import (
|
||||
connectionFromArray
|
||||
)
|
||||
from graphql_relay.connection.connection import (
|
||||
connectionArgs,
|
||||
connectionDefinitions
|
||||
)
|
||||
from graphene.relay.nodes import create_node_definitions
|
||||
from graphene.core.fields import Field, NativeField
|
||||
|
||||
Node, NodeField = create_node_definitions()
|
||||
|
||||
class ConnectionField(Field):
|
||||
def __init__(self, field_type, resolve=None, description=''):
|
||||
super(ConnectionField, self).__init__(field_type, resolve=resolve,
|
||||
args=connectionArgs, description=description)
|
||||
|
||||
def resolve(self, instance, args, info):
|
||||
resolved = super(ConnectionField, self).resolve(instance, args, info)
|
||||
if resolved:
|
||||
assert isinstance(resolved, collections.Iterable), 'Resolved value from the connection field have to be iterable'
|
||||
return connectionFromArray(resolved, args)
|
||||
|
||||
@cached_property
|
||||
def type(self):
|
||||
object_type = self.get_object_type()
|
||||
assert issubclass(object_type, Node), 'Only nodes have connections.'
|
||||
return object_type.connection
|
||||
|
||||
|
||||
@signals.class_prepared.connect
|
||||
def object_type_created(object_type):
|
||||
if issubclass(object_type, Node):
|
||||
type_name = object_type._meta.type_name
|
||||
# def getId(*args, **kwargs):
|
||||
# print '**GET ID', args, kwargs
|
||||
# return 2
|
||||
field = NativeField(globalIdField(type_name))
|
||||
object_type.add_to_class('id', field)
|
||||
assert hasattr(object_type, 'get_node'), 'get_node classmethod not found in %s Node' % type_name
|
||||
|
||||
connection = connectionDefinitions(type_name, object_type._meta.type).connectionType
|
||||
object_type.add_to_class('connection', connection)
|
|
@ -18,15 +18,18 @@ from graphene.core.types import (
|
|||
class Character(Interface):
|
||||
'''Character description'''
|
||||
name = StringField()
|
||||
|
||||
class Meta:
|
||||
type_name = 'core.Character'
|
||||
class Human(Character):
|
||||
'''Human description'''
|
||||
friends = StringField()
|
||||
class Meta:
|
||||
type_name = 'core.Human'
|
||||
|
||||
def test_interface():
|
||||
object_type = Character._meta.type
|
||||
assert Character._meta.interface == True
|
||||
assert Character._meta.type_name == 'Character'
|
||||
assert Character._meta.type_name == 'core.Character'
|
||||
assert isinstance(object_type, GraphQLInterfaceType)
|
||||
assert object_type.description == 'Character description'
|
||||
assert object_type.get_fields() == {'name': Character._meta.fields_map['name'].field}
|
||||
|
@ -34,7 +37,7 @@ def test_interface():
|
|||
def test_object_type():
|
||||
object_type = Human._meta.type
|
||||
assert Human._meta.interface == False
|
||||
assert Human._meta.type_name == 'Human'
|
||||
assert Human._meta.type_name == 'core.Human'
|
||||
assert isinstance(object_type, GraphQLObjectType)
|
||||
assert object_type.description == 'Human description'
|
||||
assert object_type.get_fields() == {'name': Character._meta.fields_map['name'].field, 'friends': Human._meta.fields_map['friends'].field}
|
||||
|
|
|
@ -3,6 +3,7 @@ from pytest import raises
|
|||
import graphene
|
||||
from graphene import relay
|
||||
|
||||
schema = graphene.Schema()
|
||||
|
||||
class OtherNode(relay.Node):
|
||||
name = graphene.StringField()
|
||||
|
@ -28,14 +29,19 @@ def test_node_should_have_id_field():
|
|||
assert 'id' in OtherNode._meta.fields_map
|
||||
|
||||
|
||||
def test_field_no_contributed_raises_error():
|
||||
with raises(Exception) as excinfo:
|
||||
class Ship(graphene.ObjectType):
|
||||
name = graphene.StringField()
|
||||
# def test_field_no_contributed_raises_error():
|
||||
# with raises(Exception) as excinfo:
|
||||
# class Ship(graphene.ObjectType):
|
||||
# name = graphene.StringField()
|
||||
# class Meta:
|
||||
# schema = schema
|
||||
|
||||
|
||||
class Faction(relay.Node):
|
||||
name = graphene.StringField()
|
||||
ships = relay.ConnectionField(Ship)
|
||||
|
||||
assert 'same type_name' in str(excinfo.value)
|
||||
# class Faction(relay.Node):
|
||||
# name = graphene.StringField()
|
||||
# ships = relay.ConnectionField(Ship)
|
||||
# @classmethod
|
||||
# def get_node(cls):
|
||||
# pass
|
||||
# class Meta:
|
||||
# schema = schema
|
||||
# assert 'same type_name' in str(excinfo.value)
|
||||
|
|
|
@ -46,6 +46,9 @@ class Query(graphene.ObjectType):
|
|||
id = graphene.Argument(graphene.String)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
type_name = 'core.Query'
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_hero(self, episode):
|
||||
return wrap_character(getHero(episode))
|
||||
|
|
|
@ -8,6 +8,7 @@ from .data import (
|
|||
getEmpire,
|
||||
)
|
||||
|
||||
schema = graphene.Schema()
|
||||
|
||||
class Ship(relay.Node):
|
||||
'''A ship in the Star Wars saga'''
|
||||
|
@ -19,6 +20,8 @@ class Ship(relay.Node):
|
|||
if ship:
|
||||
return Ship(ship)
|
||||
|
||||
# class Meta:
|
||||
# schema = schema
|
||||
|
||||
class Faction(relay.Node):
|
||||
'''A faction in the Star Wars saga'''
|
||||
|
@ -35,6 +38,9 @@ class Faction(relay.Node):
|
|||
if faction:
|
||||
return Faction(faction)
|
||||
|
||||
# class Meta:
|
||||
# schema = schema
|
||||
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
rebels = graphene.Field(Faction)
|
||||
|
@ -50,4 +56,10 @@ class Query(graphene.ObjectType):
|
|||
return Faction(getEmpire())
|
||||
|
||||
|
||||
Schema = graphene.Schema(query=Query)
|
||||
# class Meta:
|
||||
# schema = schema
|
||||
|
||||
print '*CACA', schema._types
|
||||
|
||||
schema.query = Query
|
||||
Schema = schema
|
||||
|
|
Loading…
Reference in New Issue
Block a user