mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-22 09:36:44 +03:00
Use latest graphql-core 3.1.0b1 instead of 3.0.3
Adapt Schema, because there is no type reducer in core 3.1 any more.
This commit is contained in:
parent
ffb7701466
commit
5e6f68957e
|
@ -1,2 +1,2 @@
|
|||
[settings]
|
||||
known_third_party = aniso8601,graphql,graphql_relay,promise,pytest,pytz,pyutils,setuptools,six,snapshottest,sphinx_graphene_theme
|
||||
known_third_party = aniso8601,graphql,graphql_relay,promise,pytest,pytz,pyutils,setuptools,snapshottest,sphinx_graphene_theme
|
||||
|
|
|
@ -31,7 +31,16 @@ snapshots["test_correctly_refetches_xwing 1"] = {
|
|||
|
||||
snapshots[
|
||||
"test_str_schema 1"
|
||||
] = '''"""A faction in the Star Wars saga"""
|
||||
] = '''type Query {
|
||||
rebels: Faction
|
||||
empire: Faction
|
||||
node(
|
||||
"""The ID of the object"""
|
||||
id: ID!
|
||||
): Node
|
||||
}
|
||||
|
||||
"""A faction in the Star Wars saga"""
|
||||
type Faction implements Node {
|
||||
"""The ID of the object"""
|
||||
id: ID!
|
||||
|
@ -43,28 +52,20 @@ type Faction implements Node {
|
|||
ships(before: String = null, after: String = null, first: Int = null, last: Int = null): ShipConnection
|
||||
}
|
||||
|
||||
input IntroduceShipInput {
|
||||
shipName: String!
|
||||
factionId: String!
|
||||
clientMutationId: String
|
||||
}
|
||||
|
||||
type IntroduceShipPayload {
|
||||
ship: Ship
|
||||
faction: Faction
|
||||
clientMutationId: String
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
introduceShip(input: IntroduceShipInput!): IntroduceShipPayload
|
||||
}
|
||||
|
||||
"""An object with an ID"""
|
||||
interface Node {
|
||||
"""The ID of the object"""
|
||||
id: ID!
|
||||
}
|
||||
|
||||
type ShipConnection {
|
||||
"""Pagination data for this connection."""
|
||||
pageInfo: PageInfo!
|
||||
|
||||
"""Contains the nodes in this connection."""
|
||||
edges: [ShipEdge]!
|
||||
}
|
||||
|
||||
"""
|
||||
The Relay compliant `PageInfo` type, containing data necessary to paginate this connection.
|
||||
"""
|
||||
|
@ -82,13 +83,13 @@ type PageInfo {
|
|||
endCursor: String
|
||||
}
|
||||
|
||||
type Query {
|
||||
rebels: Faction
|
||||
empire: Faction
|
||||
node(
|
||||
"""The ID of the object"""
|
||||
id: ID!
|
||||
): Node
|
||||
"""A Relay edge containing a `Ship` and its cursor."""
|
||||
type ShipEdge {
|
||||
"""The item at the end of the edge"""
|
||||
node: Ship
|
||||
|
||||
"""A cursor for use in pagination"""
|
||||
cursor: String!
|
||||
}
|
||||
|
||||
"""A ship in the Star Wars saga"""
|
||||
|
@ -100,20 +101,19 @@ type Ship implements Node {
|
|||
name: String
|
||||
}
|
||||
|
||||
type ShipConnection {
|
||||
"""Pagination data for this connection."""
|
||||
pageInfo: PageInfo!
|
||||
|
||||
"""Contains the nodes in this connection."""
|
||||
edges: [ShipEdge]!
|
||||
type Mutation {
|
||||
introduceShip(input: IntroduceShipInput!): IntroduceShipPayload
|
||||
}
|
||||
|
||||
"""A Relay edge containing a `Ship` and its cursor."""
|
||||
type ShipEdge {
|
||||
"""The item at the end of the edge"""
|
||||
node: Ship
|
||||
type IntroduceShipPayload {
|
||||
ship: Ship
|
||||
faction: Faction
|
||||
clientMutationId: String
|
||||
}
|
||||
|
||||
"""A cursor for use in pagination"""
|
||||
cursor: String!
|
||||
input IntroduceShipInput {
|
||||
shipName: String!
|
||||
factionId: String!
|
||||
clientMutationId: String
|
||||
}
|
||||
'''
|
||||
|
|
|
@ -42,7 +42,7 @@ from .utils.resolve_only_args import resolve_only_args
|
|||
from .utils.module_loading import lazy_import
|
||||
|
||||
|
||||
VERSION = (3, 0, 0, "alpha", 1)
|
||||
VERSION = (3, 0, 0, "beta", 0)
|
||||
|
||||
|
||||
__version__ = get_version(VERSION)
|
||||
|
|
|
@ -183,6 +183,12 @@ def test_str_schema():
|
|||
name: String
|
||||
}
|
||||
|
||||
"""An object with an ID"""
|
||||
interface Node {
|
||||
"""The ID of the object"""
|
||||
id: ID!
|
||||
}
|
||||
|
||||
type MyOtherNode implements Node {
|
||||
"""The ID of the object"""
|
||||
id: ID!
|
||||
|
@ -191,12 +197,6 @@ def test_str_schema():
|
|||
extraField: String
|
||||
}
|
||||
|
||||
"""An object with an ID"""
|
||||
interface Node {
|
||||
"""The ID of the object"""
|
||||
id: ID!
|
||||
}
|
||||
|
||||
type RootQuery {
|
||||
first: String
|
||||
node(
|
||||
|
|
|
@ -59,9 +59,12 @@ def test_str_schema_correct():
|
|||
query: RootQuery
|
||||
}
|
||||
|
||||
interface BasePhoto {
|
||||
"""The width of the photo in pixels"""
|
||||
width: Int
|
||||
type User implements Node {
|
||||
"""The ID of the object"""
|
||||
id: ID!
|
||||
|
||||
"""The full name of the user"""
|
||||
name: String
|
||||
}
|
||||
|
||||
interface Node {
|
||||
|
@ -77,20 +80,17 @@ def test_str_schema_correct():
|
|||
width: Int
|
||||
}
|
||||
|
||||
interface BasePhoto {
|
||||
"""The width of the photo in pixels"""
|
||||
width: Int
|
||||
}
|
||||
|
||||
type RootQuery {
|
||||
node(
|
||||
"""The ID of the object"""
|
||||
id: ID!
|
||||
): Node
|
||||
}
|
||||
|
||||
type User implements Node {
|
||||
"""The ID of the object"""
|
||||
id: ID!
|
||||
|
||||
"""The full name of the user"""
|
||||
name: String
|
||||
}
|
||||
'''
|
||||
)
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ def test_issue():
|
|||
graphene.Schema(query=Query)
|
||||
|
||||
assert str(exc_info.value) == (
|
||||
"Query fields cannot be resolved:"
|
||||
"Query fields cannot be resolved."
|
||||
" IterableConnectionField type has to be a subclass of Connection."
|
||||
' Received "MyUnion".'
|
||||
)
|
||||
|
|
|
@ -3,8 +3,8 @@ from __future__ import absolute_import
|
|||
import datetime
|
||||
|
||||
from aniso8601 import parse_date, parse_datetime, parse_time
|
||||
from graphql import Undefined
|
||||
from graphql.language import StringValueNode
|
||||
from graphql.error import GraphQLError
|
||||
from graphql.language import StringValueNode, print_ast
|
||||
|
||||
from .scalars import Scalar
|
||||
|
||||
|
@ -20,25 +20,30 @@ class Date(Scalar):
|
|||
def serialize(date):
|
||||
if isinstance(date, datetime.datetime):
|
||||
date = date.date()
|
||||
assert isinstance(
|
||||
date, datetime.date
|
||||
), 'Received not compatible date "{}"'.format(repr(date))
|
||||
if not isinstance(date, datetime.date):
|
||||
raise GraphQLError("Date cannot represent value: {}".format(repr(date)))
|
||||
return date.isoformat()
|
||||
|
||||
@classmethod
|
||||
def parse_literal(cls, node):
|
||||
if isinstance(node, StringValueNode):
|
||||
if not isinstance(node, StringValueNode):
|
||||
raise GraphQLError(
|
||||
"Date cannot represent non-string value: {}".format(print_ast(node))
|
||||
)
|
||||
return cls.parse_value(node.value)
|
||||
|
||||
@staticmethod
|
||||
def parse_value(value):
|
||||
try:
|
||||
if isinstance(value, datetime.date):
|
||||
return value
|
||||
elif isinstance(value, str):
|
||||
if not isinstance(value, str):
|
||||
raise GraphQLError(
|
||||
"Date cannot represent non-string value: {}".format(repr(value))
|
||||
)
|
||||
try:
|
||||
return parse_date(value)
|
||||
except ValueError:
|
||||
return Undefined
|
||||
raise GraphQLError("Date cannot represent value: {}".format(repr(value)))
|
||||
|
||||
|
||||
class DateTime(Scalar):
|
||||
|
@ -50,25 +55,32 @@ class DateTime(Scalar):
|
|||
|
||||
@staticmethod
|
||||
def serialize(dt):
|
||||
assert isinstance(
|
||||
dt, (datetime.datetime, datetime.date)
|
||||
), 'Received not compatible datetime "{}"'.format(repr(dt))
|
||||
if not isinstance(dt, (datetime.datetime, datetime.date)):
|
||||
raise GraphQLError("DateTime cannot represent value: {}".format(repr(dt)))
|
||||
return dt.isoformat()
|
||||
|
||||
@classmethod
|
||||
def parse_literal(cls, node):
|
||||
if isinstance(node, StringValueNode):
|
||||
if not isinstance(node, StringValueNode):
|
||||
raise GraphQLError(
|
||||
"DateTime cannot represent non-string value: {}".format(print_ast(node))
|
||||
)
|
||||
return cls.parse_value(node.value)
|
||||
|
||||
@staticmethod
|
||||
def parse_value(value):
|
||||
try:
|
||||
if isinstance(value, datetime.datetime):
|
||||
return value
|
||||
elif isinstance(value, str):
|
||||
if not isinstance(value, str):
|
||||
raise GraphQLError(
|
||||
"DateTime cannot represent non-string value: {}".format(repr(value))
|
||||
)
|
||||
try:
|
||||
return parse_datetime(value)
|
||||
except ValueError:
|
||||
return Undefined
|
||||
raise GraphQLError(
|
||||
"DateTime cannot represent value: {}".format(repr(value))
|
||||
)
|
||||
|
||||
|
||||
class Time(Scalar):
|
||||
|
@ -80,22 +92,27 @@ class Time(Scalar):
|
|||
|
||||
@staticmethod
|
||||
def serialize(time):
|
||||
assert isinstance(
|
||||
time, datetime.time
|
||||
), 'Received not compatible time "{}"'.format(repr(time))
|
||||
if not isinstance(time, datetime.time):
|
||||
raise GraphQLError("Time cannot represent value: {}".format(repr(time)))
|
||||
return time.isoformat()
|
||||
|
||||
@classmethod
|
||||
def parse_literal(cls, node):
|
||||
if isinstance(node, StringValueNode):
|
||||
if not isinstance(node, StringValueNode):
|
||||
raise GraphQLError(
|
||||
"Time cannot represent non-string value: {}".format(print_ast(node))
|
||||
)
|
||||
return cls.parse_value(node.value)
|
||||
|
||||
@classmethod
|
||||
def parse_value(cls, value):
|
||||
try:
|
||||
if isinstance(value, datetime.time):
|
||||
return value
|
||||
elif isinstance(value, str):
|
||||
if not isinstance(value, str):
|
||||
raise GraphQLError(
|
||||
"Time cannot represent non-string value: {}".format(repr(value))
|
||||
)
|
||||
try:
|
||||
return parse_time(value)
|
||||
except ValueError:
|
||||
return Undefined
|
||||
raise GraphQLError("Time cannot represent value: {}".format(repr(value)))
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from graphql import Undefined
|
||||
|
||||
from .mountedtype import MountedType
|
||||
from .structures import NonNull
|
||||
from .utils import get_type
|
||||
|
|
|
@ -7,7 +7,6 @@ from graphql import (
|
|||
graphql,
|
||||
graphql_sync,
|
||||
introspection_types,
|
||||
is_type,
|
||||
print_schema,
|
||||
GraphQLArgument,
|
||||
GraphQLBoolean,
|
||||
|
@ -71,118 +70,74 @@ def is_graphene_type(type_):
|
|||
return True
|
||||
|
||||
|
||||
def resolve_type(resolve_type_func, map_, type_name, root, info, _type):
|
||||
type_ = resolve_type_func(root, info)
|
||||
|
||||
if not type_:
|
||||
return_type = map_[type_name]
|
||||
return default_type_resolver(root, info, return_type)
|
||||
|
||||
if inspect.isclass(type_) and issubclass(type_, ObjectType):
|
||||
graphql_type = map_.get(type_._meta.name)
|
||||
assert graphql_type, "Can't find type {} in schema".format(type_._meta.name)
|
||||
assert graphql_type.graphene_type == type_, (
|
||||
"The type {} does not match with the associated graphene type {}."
|
||||
).format(type_, graphql_type.graphene_type)
|
||||
return graphql_type
|
||||
|
||||
return type_
|
||||
|
||||
|
||||
def is_type_of_from_possible_types(possible_types, root, _info):
|
||||
return isinstance(root, possible_types)
|
||||
|
||||
|
||||
class GrapheneGraphQLSchema(GraphQLSchema):
|
||||
"""A GraphQLSchema that can deal with Graphene types as well."""
|
||||
|
||||
class TypeMap(dict):
|
||||
def __init__(
|
||||
self,
|
||||
query=None,
|
||||
mutation=None,
|
||||
subscription=None,
|
||||
types=None,
|
||||
directives=None,
|
||||
auto_camelcase=True,
|
||||
):
|
||||
assert_valid_root_type(query)
|
||||
assert_valid_root_type(mutation)
|
||||
assert_valid_root_type(subscription)
|
||||
if types is None:
|
||||
types = []
|
||||
for type_ in types:
|
||||
assert is_graphene_type(type_)
|
||||
|
||||
self.auto_camelcase = auto_camelcase
|
||||
super().__init__(query, mutation, subscription, types, directives)
|
||||
|
||||
if query:
|
||||
self.query_type = self.get_type(
|
||||
query.name if isinstance(query, GraphQLObjectType) else query._meta.name
|
||||
)
|
||||
if mutation:
|
||||
self.mutation_type = self.get_type(
|
||||
mutation.name
|
||||
if isinstance(mutation, GraphQLObjectType)
|
||||
else mutation._meta.name
|
||||
)
|
||||
if subscription:
|
||||
self.subscription_type = self.get_type(
|
||||
subscription.name
|
||||
if isinstance(subscription, GraphQLObjectType)
|
||||
else subscription._meta.name
|
||||
)
|
||||
create_graphql_type = self.add_type
|
||||
|
||||
def get_graphql_type(self, _type):
|
||||
if not _type:
|
||||
return _type
|
||||
if is_type(_type):
|
||||
return _type
|
||||
if is_graphene_type(_type):
|
||||
graphql_type = self.get_type(_type._meta.name)
|
||||
assert graphql_type, "Type {} not found in this schema.".format(
|
||||
_type._meta.name
|
||||
self.query = create_graphql_type(query) if query else None
|
||||
self.mutation = create_graphql_type(mutation) if mutation else None
|
||||
self.subscription = create_graphql_type(subscription) if subscription else None
|
||||
|
||||
self.types = [create_graphql_type(graphene_type) for graphene_type in types]
|
||||
|
||||
def add_type(self, graphene_type):
|
||||
if inspect.isfunction(graphene_type):
|
||||
graphene_type = graphene_type()
|
||||
if isinstance(graphene_type, List):
|
||||
return GraphQLList(self.add_type(graphene_type.of_type))
|
||||
if isinstance(graphene_type, NonNull):
|
||||
return GraphQLNonNull(self.add_type(graphene_type.of_type))
|
||||
try:
|
||||
name = graphene_type._meta.name
|
||||
except AttributeError:
|
||||
raise TypeError(
|
||||
"Expected Graphene type, but received: {}.".format(graphene_type)
|
||||
)
|
||||
assert graphql_type.graphene_type == _type
|
||||
graphql_type = self.get(name)
|
||||
if graphql_type:
|
||||
return graphql_type
|
||||
raise Exception("{} is not a valid GraphQL type.".format(_type))
|
||||
|
||||
# noinspection PyMethodOverriding
|
||||
def type_map_reducer(self, map_, type_):
|
||||
if not type_:
|
||||
return map_
|
||||
if inspect.isfunction(type_):
|
||||
type_ = type_()
|
||||
if is_graphene_type(type_):
|
||||
return self.graphene_reducer(map_, type_)
|
||||
return super().type_map_reducer(map_, type_)
|
||||
|
||||
def graphene_reducer(self, map_, type_):
|
||||
if isinstance(type_, (List, NonNull)):
|
||||
return self.type_map_reducer(map_, type_.of_type)
|
||||
if type_._meta.name in map_:
|
||||
_type = map_[type_._meta.name]
|
||||
if isinstance(_type, GrapheneGraphQLType):
|
||||
assert _type.graphene_type == type_, (
|
||||
"Found different types with the same name in the schema: {}, {}."
|
||||
).format(_type.graphene_type, type_)
|
||||
return map_
|
||||
|
||||
if issubclass(type_, ObjectType):
|
||||
internal_type = self.construct_objecttype(map_, type_)
|
||||
elif issubclass(type_, InputObjectType):
|
||||
internal_type = self.construct_inputobjecttype(map_, type_)
|
||||
elif issubclass(type_, Interface):
|
||||
internal_type = self.construct_interface(map_, type_)
|
||||
elif issubclass(type_, Scalar):
|
||||
internal_type = self.construct_scalar(type_)
|
||||
elif issubclass(type_, Enum):
|
||||
internal_type = self.construct_enum(type_)
|
||||
elif issubclass(type_, Union):
|
||||
internal_type = self.construct_union(map_, type_)
|
||||
if issubclass(graphene_type, ObjectType):
|
||||
graphql_type = self.create_objecttype(graphene_type)
|
||||
elif issubclass(graphene_type, InputObjectType):
|
||||
graphql_type = self.create_inputobjecttype(graphene_type)
|
||||
elif issubclass(graphene_type, Interface):
|
||||
graphql_type = self.create_interface(graphene_type)
|
||||
elif issubclass(graphene_type, Scalar):
|
||||
graphql_type = self.create_scalar(graphene_type)
|
||||
elif issubclass(graphene_type, Enum):
|
||||
graphql_type = self.create_enum(graphene_type)
|
||||
elif issubclass(graphene_type, Union):
|
||||
graphql_type = self.construct_union(graphene_type)
|
||||
else:
|
||||
raise Exception("Expected Graphene type, but received: {}.".format(type_))
|
||||
|
||||
return super().type_map_reducer(map_, internal_type)
|
||||
raise TypeError(
|
||||
"Expected Graphene type, but received: {}.".format(graphene_type)
|
||||
)
|
||||
self[name] = graphql_type
|
||||
return graphql_type
|
||||
|
||||
@staticmethod
|
||||
def construct_scalar(type_):
|
||||
def create_scalar(graphene_type):
|
||||
# We have a mapping to the original GraphQL types
|
||||
# so there are no collisions.
|
||||
_scalars = {
|
||||
|
@ -192,29 +147,31 @@ class GrapheneGraphQLSchema(GraphQLSchema):
|
|||
Boolean: GraphQLBoolean,
|
||||
ID: GraphQLID,
|
||||
}
|
||||
if type_ in _scalars:
|
||||
return _scalars[type_]
|
||||
if graphene_type in _scalars:
|
||||
return _scalars[graphene_type]
|
||||
|
||||
return GrapheneScalarType(
|
||||
graphene_type=type_,
|
||||
name=type_._meta.name,
|
||||
description=type_._meta.description,
|
||||
serialize=getattr(type_, "serialize", None),
|
||||
parse_value=getattr(type_, "parse_value", None),
|
||||
parse_literal=getattr(type_, "parse_literal", None),
|
||||
graphene_type=graphene_type,
|
||||
name=graphene_type._meta.name,
|
||||
description=graphene_type._meta.description,
|
||||
serialize=getattr(graphene_type, "serialize", None),
|
||||
parse_value=getattr(graphene_type, "parse_value", None),
|
||||
parse_literal=getattr(graphene_type, "parse_literal", None),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def construct_enum(type_):
|
||||
def create_enum(graphene_type):
|
||||
values = {}
|
||||
for name, value in type_._meta.enum.__members__.items():
|
||||
for name, value in graphene_type._meta.enum.__members__.items():
|
||||
description = getattr(value, "description", None)
|
||||
deprecation_reason = getattr(value, "deprecation_reason", None)
|
||||
if not description and callable(type_._meta.description):
|
||||
description = type_._meta.description(value)
|
||||
if not description and callable(graphene_type._meta.description):
|
||||
description = graphene_type._meta.description(value)
|
||||
|
||||
if not deprecation_reason and callable(type_._meta.deprecation_reason):
|
||||
deprecation_reason = type_._meta.deprecation_reason(value)
|
||||
if not deprecation_reason and callable(
|
||||
graphene_type._meta.deprecation_reason
|
||||
):
|
||||
deprecation_reason = graphene_type._meta.deprecation_reason(value)
|
||||
|
||||
values[name] = GraphQLEnumValue(
|
||||
value=value.value,
|
||||
|
@ -223,107 +180,98 @@ class GrapheneGraphQLSchema(GraphQLSchema):
|
|||
)
|
||||
|
||||
type_description = (
|
||||
type_._meta.description(None)
|
||||
if callable(type_._meta.description)
|
||||
else type_._meta.description
|
||||
graphene_type._meta.description(None)
|
||||
if callable(graphene_type._meta.description)
|
||||
else graphene_type._meta.description
|
||||
)
|
||||
|
||||
return GrapheneEnumType(
|
||||
graphene_type=type_,
|
||||
graphene_type=graphene_type,
|
||||
values=values,
|
||||
name=type_._meta.name,
|
||||
name=graphene_type._meta.name,
|
||||
description=type_description,
|
||||
)
|
||||
|
||||
def construct_objecttype(self, map_, type_):
|
||||
if type_._meta.name in map_:
|
||||
_type = map_[type_._meta.name]
|
||||
if isinstance(_type, GrapheneGraphQLType):
|
||||
assert _type.graphene_type == type_, (
|
||||
"Found different types with the same name in the schema: {}, {}."
|
||||
).format(_type.graphene_type, type_)
|
||||
return _type
|
||||
def create_objecttype(self, graphene_type):
|
||||
create_graphql_type = self.add_type
|
||||
|
||||
def interfaces():
|
||||
interfaces = []
|
||||
for interface in type_._meta.interfaces:
|
||||
self.graphene_reducer(map_, interface)
|
||||
internal_type = map_[interface._meta.name]
|
||||
assert internal_type.graphene_type == interface
|
||||
interfaces.append(internal_type)
|
||||
for graphene_interface in graphene_type._meta.interfaces:
|
||||
interface = create_graphql_type(graphene_interface)
|
||||
assert interface.graphene_type == graphene_interface
|
||||
interfaces.append(interface)
|
||||
return interfaces
|
||||
|
||||
if type_._meta.possible_types:
|
||||
if graphene_type._meta.possible_types:
|
||||
is_type_of = partial(
|
||||
is_type_of_from_possible_types, type_._meta.possible_types
|
||||
is_type_of_from_possible_types, graphene_type._meta.possible_types
|
||||
)
|
||||
else:
|
||||
is_type_of = type_.is_type_of
|
||||
is_type_of = graphene_type.is_type_of
|
||||
|
||||
return GrapheneObjectType(
|
||||
graphene_type=type_,
|
||||
name=type_._meta.name,
|
||||
description=type_._meta.description,
|
||||
fields=partial(self.construct_fields_for_type, map_, type_),
|
||||
graphene_type=graphene_type,
|
||||
name=graphene_type._meta.name,
|
||||
description=graphene_type._meta.description,
|
||||
fields=partial(self.create_fields_for_type, graphene_type),
|
||||
is_type_of=is_type_of,
|
||||
interfaces=interfaces,
|
||||
)
|
||||
|
||||
def construct_interface(self, map_, type_):
|
||||
if type_._meta.name in map_:
|
||||
_type = map_[type_._meta.name]
|
||||
if isinstance(_type, GrapheneInterfaceType):
|
||||
assert _type.graphene_type == type_, (
|
||||
"Found different types with the same name in the schema: {}, {}."
|
||||
).format(_type.graphene_type, type_)
|
||||
return _type
|
||||
|
||||
_resolve_type = None
|
||||
if type_.resolve_type:
|
||||
_resolve_type = partial(
|
||||
resolve_type, type_.resolve_type, map_, type_._meta.name
|
||||
def create_interface(self, graphene_type):
|
||||
resolve_type = (
|
||||
partial(
|
||||
self.resolve_type, graphene_type.resolve_type, graphene_type._meta.name
|
||||
)
|
||||
if graphene_type.resolve_type
|
||||
else None
|
||||
)
|
||||
|
||||
return GrapheneInterfaceType(
|
||||
graphene_type=type_,
|
||||
name=type_._meta.name,
|
||||
description=type_._meta.description,
|
||||
fields=partial(self.construct_fields_for_type, map_, type_),
|
||||
resolve_type=_resolve_type,
|
||||
graphene_type=graphene_type,
|
||||
name=graphene_type._meta.name,
|
||||
description=graphene_type._meta.description,
|
||||
fields=partial(self.create_fields_for_type, graphene_type),
|
||||
resolve_type=resolve_type,
|
||||
)
|
||||
|
||||
def construct_inputobjecttype(self, map_, type_):
|
||||
def create_inputobjecttype(self, graphene_type):
|
||||
return GrapheneInputObjectType(
|
||||
graphene_type=type_,
|
||||
name=type_._meta.name,
|
||||
description=type_._meta.description,
|
||||
out_type=type_._meta.container,
|
||||
graphene_type=graphene_type,
|
||||
name=graphene_type._meta.name,
|
||||
description=graphene_type._meta.description,
|
||||
out_type=graphene_type._meta.container,
|
||||
fields=partial(
|
||||
self.construct_fields_for_type, map_, type_, is_input_type=True
|
||||
self.create_fields_for_type, graphene_type, is_input_type=True
|
||||
),
|
||||
)
|
||||
|
||||
def construct_union(self, map_, type_):
|
||||
_resolve_type = None
|
||||
if type_.resolve_type:
|
||||
_resolve_type = partial(
|
||||
resolve_type, type_.resolve_type, map_, type_._meta.name
|
||||
)
|
||||
def construct_union(self, graphene_type):
|
||||
create_graphql_type = self.add_type
|
||||
|
||||
def types():
|
||||
union_types = []
|
||||
for objecttype in type_._meta.types:
|
||||
self.graphene_reducer(map_, objecttype)
|
||||
internal_type = map_[objecttype._meta.name]
|
||||
assert internal_type.graphene_type == objecttype
|
||||
union_types.append(internal_type)
|
||||
for graphene_objecttype in graphene_type._meta.types:
|
||||
object_type = create_graphql_type(graphene_objecttype)
|
||||
assert object_type.graphene_type == graphene_objecttype
|
||||
union_types.append(object_type)
|
||||
return union_types
|
||||
|
||||
resolve_type = (
|
||||
partial(
|
||||
self.resolve_type, graphene_type.resolve_type, graphene_type._meta.name
|
||||
)
|
||||
if graphene_type.resolve_type
|
||||
else None
|
||||
)
|
||||
|
||||
return GrapheneUnionType(
|
||||
graphene_type=type_,
|
||||
name=type_._meta.name,
|
||||
description=type_._meta.description,
|
||||
graphene_type=graphene_type,
|
||||
name=graphene_type._meta.name,
|
||||
description=graphene_type._meta.description,
|
||||
types=types,
|
||||
resolve_type=_resolve_type,
|
||||
resolve_type=resolve_type,
|
||||
)
|
||||
|
||||
def get_name(self, name):
|
||||
|
@ -331,15 +279,16 @@ class GrapheneGraphQLSchema(GraphQLSchema):
|
|||
return to_camel_case(name)
|
||||
return name
|
||||
|
||||
def construct_fields_for_type(self, map_, type_, is_input_type=False):
|
||||
def create_fields_for_type(self, graphene_type, is_input_type=False):
|
||||
create_graphql_type = self.add_type
|
||||
|
||||
fields = {}
|
||||
for name, field in type_._meta.fields.items():
|
||||
for name, field in graphene_type._meta.fields.items():
|
||||
if isinstance(field, Dynamic):
|
||||
field = get_field_as(field.get_type(self), _as=Field)
|
||||
if not field:
|
||||
continue
|
||||
map_ = self.type_map_reducer(map_, field.type)
|
||||
field_type = self.get_field_type(map_, field.type)
|
||||
field_type = create_graphql_type(field.type)
|
||||
if is_input_type:
|
||||
_field = GraphQLInputField(
|
||||
field_type,
|
||||
|
@ -350,8 +299,7 @@ class GrapheneGraphQLSchema(GraphQLSchema):
|
|||
else:
|
||||
args = {}
|
||||
for arg_name, arg in field.args.items():
|
||||
map_ = self.type_map_reducer(map_, arg.type)
|
||||
arg_type = self.get_field_type(map_, arg.type)
|
||||
arg_type = create_graphql_type(arg.type)
|
||||
processed_arg_name = arg.name or self.get_name(arg_name)
|
||||
args[processed_arg_name] = GraphQLArgument(
|
||||
arg_type,
|
||||
|
@ -361,12 +309,13 @@ class GrapheneGraphQLSchema(GraphQLSchema):
|
|||
if isinstance(arg.type, NonNull)
|
||||
else arg.default_value,
|
||||
)
|
||||
resolve = field.get_resolver(
|
||||
self.get_resolver(graphene_type, name, field.default_value)
|
||||
)
|
||||
_field = GraphQLField(
|
||||
field_type,
|
||||
args=args,
|
||||
resolve=field.get_resolver(
|
||||
self.get_resolver_for_type(type_, name, field.default_value)
|
||||
),
|
||||
resolve=resolve,
|
||||
deprecation_reason=field.deprecation_reason,
|
||||
description=field.description,
|
||||
)
|
||||
|
@ -374,15 +323,32 @@ class GrapheneGraphQLSchema(GraphQLSchema):
|
|||
fields[field_name] = _field
|
||||
return fields
|
||||
|
||||
def get_resolver_for_type(self, type_, name, default_value):
|
||||
if not issubclass(type_, ObjectType):
|
||||
def resolve_type(self, resolve_type_func, type_name, root, info, _type):
|
||||
type_ = resolve_type_func(root, info)
|
||||
|
||||
if not type_:
|
||||
return_type = self[type_name]
|
||||
return default_type_resolver(root, info, return_type)
|
||||
|
||||
if inspect.isclass(type_) and issubclass(type_, ObjectType):
|
||||
graphql_type = self.get(type_._meta.name)
|
||||
assert graphql_type, "Can't find type {} in schema".format(type_._meta.name)
|
||||
assert graphql_type.graphene_type == type_, (
|
||||
"The type {} does not match with the associated graphene type {}."
|
||||
).format(type_, graphql_type.graphene_type)
|
||||
return graphql_type
|
||||
|
||||
return type_
|
||||
|
||||
def get_resolver(self, graphene_type, name, default_value):
|
||||
if not issubclass(graphene_type, ObjectType):
|
||||
return
|
||||
resolver = getattr(type_, "resolve_{}".format(name), None)
|
||||
resolver = getattr(graphene_type, "resolve_{}".format(name), None)
|
||||
if not resolver:
|
||||
# If we don't find the resolver in the ObjectType class, then try to
|
||||
# find it in each of the interfaces
|
||||
interface_resolver = None
|
||||
for interface in type_._meta.interfaces:
|
||||
for interface in graphene_type._meta.interfaces:
|
||||
if name not in interface._meta.fields:
|
||||
continue
|
||||
interface_resolver = getattr(interface, "resolve_{}".format(name), None)
|
||||
|
@ -394,16 +360,11 @@ class GrapheneGraphQLSchema(GraphQLSchema):
|
|||
if resolver:
|
||||
return get_unbound_function(resolver)
|
||||
|
||||
default_resolver = type_._meta.default_resolver or get_default_resolver()
|
||||
default_resolver = (
|
||||
graphene_type._meta.default_resolver or get_default_resolver()
|
||||
)
|
||||
return partial(default_resolver, name, default_value)
|
||||
|
||||
def get_field_type(self, map_, type_):
|
||||
if isinstance(type_, List):
|
||||
return GraphQLList(self.get_field_type(map_, type_.of_type))
|
||||
if isinstance(type_, NonNull):
|
||||
return GraphQLNonNull(self.get_field_type(map_, type_.of_type))
|
||||
return map_.get(type_._meta.name)
|
||||
|
||||
|
||||
class Schema:
|
||||
"""Schema Definition.
|
||||
|
@ -419,11 +380,11 @@ class Schema:
|
|||
fields to *create, update or delete* data in your API.
|
||||
subscription (ObjectType, optional): Root subscription *ObjectType*. Describes entry point
|
||||
for fields to receive continuous updates.
|
||||
types (List[ObjectType], optional): List of any types to include in schema that
|
||||
may not be introspected through root types.
|
||||
directives (List[GraphQLDirective], optional): List of custom directives to include in the
|
||||
GraphQL schema. Defaults to only include directives defined by GraphQL spec (@include
|
||||
and @skip) [GraphQLIncludeDirective, GraphQLSkipDirective].
|
||||
types (List[GraphQLType], optional): List of any types to include in schema that
|
||||
may not be introspected through root types.
|
||||
auto_camelcase (bool): Fieldnames will be transformed in Schema's TypeMap from snake_case
|
||||
to camelCase (preferred by GraphQL standard). Default True.
|
||||
"""
|
||||
|
@ -440,13 +401,15 @@ class Schema:
|
|||
self.query = query
|
||||
self.mutation = mutation
|
||||
self.subscription = subscription
|
||||
self.graphql_schema = GrapheneGraphQLSchema(
|
||||
query,
|
||||
mutation,
|
||||
subscription,
|
||||
types,
|
||||
type_map = TypeMap(
|
||||
query, mutation, subscription, types, auto_camelcase=auto_camelcase
|
||||
)
|
||||
self.graphql_schema = GraphQLSchema(
|
||||
type_map.query,
|
||||
type_map.mutation,
|
||||
type_map.subscription,
|
||||
type_map.types,
|
||||
directives,
|
||||
auto_camelcase=auto_camelcase,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
|
|
|
@ -3,7 +3,7 @@ import datetime
|
|||
import pytz
|
||||
from graphql import GraphQLError
|
||||
|
||||
from pytest import fixture, mark
|
||||
from pytest import fixture
|
||||
|
||||
from ..datetime import Date, DateTime, Time
|
||||
from ..objecttype import ObjectType
|
||||
|
@ -84,8 +84,9 @@ def test_bad_datetime_query():
|
|||
assert result.errors and len(result.errors) == 1
|
||||
error = result.errors[0]
|
||||
assert isinstance(error, GraphQLError)
|
||||
assert error.message == (
|
||||
'Expected type DateTime, found "Some string that\'s not a datetime".'
|
||||
assert (
|
||||
error.message == "DateTime cannot represent value:"
|
||||
' "Some string that\'s not a datetime"'
|
||||
)
|
||||
assert result.data is None
|
||||
|
||||
|
@ -97,8 +98,9 @@ def test_bad_date_query():
|
|||
|
||||
error = result.errors[0]
|
||||
assert isinstance(error, GraphQLError)
|
||||
assert error.message == (
|
||||
'Expected type Date, found "Some string that\'s not a date".'
|
||||
assert (
|
||||
error.message == "Date cannot represent value:"
|
||||
' "Some string that\'s not a date"'
|
||||
)
|
||||
assert result.data is None
|
||||
|
||||
|
@ -110,8 +112,9 @@ def test_bad_time_query():
|
|||
|
||||
error = result.errors[0]
|
||||
assert isinstance(error, GraphQLError)
|
||||
assert error.message == (
|
||||
'Expected type Time, found "Some string that\'s not a time".'
|
||||
assert (
|
||||
error.message == "Time cannot represent value:"
|
||||
' "Some string that\'s not a time"'
|
||||
)
|
||||
assert result.data is None
|
||||
|
||||
|
@ -174,9 +177,6 @@ def test_time_query_variable(sample_time):
|
|||
assert result.data == {"time": isoformat}
|
||||
|
||||
|
||||
@mark.xfail(
|
||||
reason="creating the error message fails when un-parsable object is not JSON serializable."
|
||||
)
|
||||
def test_bad_variables(sample_date, sample_datetime, sample_time):
|
||||
def _test_bad_variables(type_, input_):
|
||||
result = schema.execute(
|
||||
|
@ -185,8 +185,6 @@ def test_bad_variables(sample_date, sample_datetime, sample_time):
|
|||
),
|
||||
variables={"input": input_},
|
||||
)
|
||||
# when `input` is not JSON serializable formatting the error message in
|
||||
# `graphql.utils.is_valid_value` line 79 fails with a TypeError
|
||||
assert isinstance(result.errors, list)
|
||||
assert len(result.errors) == 1
|
||||
assert isinstance(result.errors[0], GraphQLError)
|
||||
|
@ -205,7 +203,6 @@ def test_bad_variables(sample_date, sample_datetime, sample_time):
|
|||
("DateTime", time),
|
||||
("Date", not_a_date),
|
||||
("Date", not_a_date_str),
|
||||
("Date", now),
|
||||
("Date", time),
|
||||
("Time", not_a_date),
|
||||
("Time", not_a_date_str),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from pytest import raises
|
||||
|
||||
from graphql.type import GraphQLObjectType, GraphQLSchema
|
||||
from graphql.pyutils import dedent
|
||||
|
||||
from ..field import Field
|
||||
|
@ -17,8 +18,13 @@ class Query(ObjectType):
|
|||
|
||||
|
||||
def test_schema():
|
||||
schema = Schema(Query).graphql_schema
|
||||
assert schema.query_type == schema.get_graphql_type(Query)
|
||||
schema = Schema(Query)
|
||||
graphql_schema = schema.graphql_schema
|
||||
assert isinstance(graphql_schema, GraphQLSchema)
|
||||
query_type = graphql_schema.query_type
|
||||
assert isinstance(query_type, GraphQLObjectType)
|
||||
assert query_type.name == "Query"
|
||||
assert query_type.graphene_type is Query
|
||||
|
||||
|
||||
def test_schema_get_type():
|
||||
|
@ -39,13 +45,13 @@ def test_schema_str():
|
|||
schema = Schema(Query)
|
||||
assert str(schema) == dedent(
|
||||
"""
|
||||
type MyOtherType {
|
||||
field: String
|
||||
}
|
||||
|
||||
type Query {
|
||||
inner: MyOtherType
|
||||
}
|
||||
|
||||
type MyOtherType {
|
||||
field: String
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
from pytest import raises
|
||||
|
||||
from graphql.type import (
|
||||
GraphQLArgument,
|
||||
GraphQLEnumType,
|
||||
|
@ -21,13 +19,13 @@ from ..interface import Interface
|
|||
from ..objecttype import ObjectType
|
||||
from ..scalars import Int, String
|
||||
from ..structures import List, NonNull
|
||||
from ..schema import GrapheneGraphQLSchema, resolve_type
|
||||
from ..schema import Schema
|
||||
|
||||
|
||||
def create_type_map(types, auto_camelcase=True):
|
||||
query = GraphQLObjectType("Query", {})
|
||||
schema = GrapheneGraphQLSchema(query, types=types, auto_camelcase=auto_camelcase)
|
||||
return schema.type_map
|
||||
query = type("Query", (ObjectType,), {})
|
||||
schema = Schema(query, types=types, auto_camelcase=auto_camelcase)
|
||||
return schema.graphql_schema.type_map
|
||||
|
||||
|
||||
def test_enum():
|
||||
|
@ -272,20 +270,3 @@ def test_objecttype_with_possible_types():
|
|||
assert graphql_type.is_type_of
|
||||
assert graphql_type.is_type_of({}, None) is True
|
||||
assert graphql_type.is_type_of(MyObjectType(), None) is False
|
||||
|
||||
|
||||
def test_resolve_type_with_missing_type():
|
||||
class MyObjectType(ObjectType):
|
||||
foo_bar = String()
|
||||
|
||||
class MyOtherObjectType(ObjectType):
|
||||
fizz_buzz = String()
|
||||
|
||||
def resolve_type_func(root, info):
|
||||
return MyOtherObjectType
|
||||
|
||||
type_map = create_type_map([MyObjectType])
|
||||
with raises(AssertionError) as excinfo:
|
||||
resolve_type(resolve_type_func, type_map, "MyOtherObjectType", {}, {}, None)
|
||||
|
||||
assert "MyOtherObjectTyp" in str(excinfo.value)
|
||||
|
|
2
setup.py
2
setup.py
|
@ -82,7 +82,7 @@ setup(
|
|||
keywords="api graphql protocol rest relay graphene",
|
||||
packages=find_packages(exclude=["tests", "tests.*", "examples"]),
|
||||
install_requires=[
|
||||
"graphql-core>=3.0.3,<3.1",
|
||||
"graphql-core>=3.1.0b1,<4",
|
||||
"graphql-relay>=3.0,<4",
|
||||
"aniso8601>=8,<9",
|
||||
"unidecode>=1.1.1,<2",
|
||||
|
|
Loading…
Reference in New Issue
Block a user