mirror of
https://github.com/graphql-python/graphene.git
synced 2025-09-21 19:32:33 +03:00
Make all 345 tests pass with graphql-core-next
This commit is contained in:
parent
f1ae6e4cd7
commit
c4a850f2ea
|
@ -30,21 +30,22 @@ snapshots["test_correctly_refetches_xwing 1"] = {
|
||||||
|
|
||||||
snapshots[
|
snapshots[
|
||||||
"test_str_schema 1"
|
"test_str_schema 1"
|
||||||
] = """schema {
|
] = '''"""A faction in the Star Wars saga"""
|
||||||
query: Query
|
|
||||||
mutation: Mutation
|
|
||||||
}
|
|
||||||
|
|
||||||
type Faction implements Node {
|
type Faction implements Node {
|
||||||
|
"""The ID of the object"""
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""The name of the faction."""
|
||||||
name: String
|
name: String
|
||||||
ships(before: String, after: String, first: Int, last: Int): ShipConnection
|
|
||||||
|
"""The ships used by the faction."""
|
||||||
|
ships(before: String = null, after: String = null, first: Int = null, last: Int = null): ShipConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
input IntroduceShipInput {
|
input IntroduceShipInput {
|
||||||
shipName: String!
|
shipName: String!
|
||||||
factionId: String!
|
factionId: String!
|
||||||
clientMutationId: String
|
clientMutationId: String = null
|
||||||
}
|
}
|
||||||
|
|
||||||
type IntroduceShipPayload {
|
type IntroduceShipPayload {
|
||||||
|
@ -57,35 +58,60 @@ type Mutation {
|
||||||
introduceShip(input: IntroduceShipInput!): IntroduceShipPayload
|
introduceShip(input: IntroduceShipInput!): IntroduceShipPayload
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""An object with an ID"""
|
||||||
interface Node {
|
interface Node {
|
||||||
|
"""The ID of the object"""
|
||||||
id: ID!
|
id: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The Relay compliant `PageInfo` type, containing data necessary to paginate this connection.
|
||||||
|
"""
|
||||||
type PageInfo {
|
type PageInfo {
|
||||||
|
"""When paginating forwards, are there more items?"""
|
||||||
hasNextPage: Boolean!
|
hasNextPage: Boolean!
|
||||||
|
|
||||||
|
"""When paginating backwards, are there more items?"""
|
||||||
hasPreviousPage: Boolean!
|
hasPreviousPage: Boolean!
|
||||||
|
|
||||||
|
"""When paginating backwards, the cursor to continue."""
|
||||||
startCursor: String
|
startCursor: String
|
||||||
|
|
||||||
|
"""When paginating forwards, the cursor to continue."""
|
||||||
endCursor: String
|
endCursor: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
rebels: Faction
|
rebels: Faction
|
||||||
empire: Faction
|
empire: Faction
|
||||||
|
|
||||||
|
"""The ID of the object"""
|
||||||
node(id: ID!): Node
|
node(id: ID!): Node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""A ship in the Star Wars saga"""
|
||||||
type Ship implements Node {
|
type Ship implements Node {
|
||||||
|
"""The ID of the object"""
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""The name of the ship."""
|
||||||
name: String
|
name: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShipConnection {
|
type ShipConnection {
|
||||||
|
"""Pagination data for this connection."""
|
||||||
pageInfo: PageInfo!
|
pageInfo: PageInfo!
|
||||||
|
|
||||||
|
"""Contains the nodes in this connection."""
|
||||||
edges: [ShipEdge]!
|
edges: [ShipEdge]!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""A Relay edge containing a `Ship` and its cursor."""
|
||||||
type ShipEdge {
|
type ShipEdge {
|
||||||
|
"""The item at the end of the edge"""
|
||||||
node: Ship
|
node: Ship
|
||||||
|
|
||||||
|
"""A cursor for use in pagination"""
|
||||||
cursor: String!
|
cursor: String!
|
||||||
}
|
}
|
||||||
"""
|
'''
|
||||||
|
|
|
@ -133,7 +133,7 @@ class IterableConnectionField(Field):
|
||||||
)
|
)
|
||||||
|
|
||||||
assert issubclass(connection_type, Connection), (
|
assert issubclass(connection_type, Connection), (
|
||||||
'{} type have to be a subclass of Connection. Received "{}".'
|
'{} type has to be a subclass of Connection. Received "{}".'
|
||||||
).format(self.__class__.__name__, connection_type)
|
).format(self.__class__.__name__, connection_type)
|
||||||
return type
|
return type
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ class IterableConnectionField(Field):
|
||||||
return resolved
|
return resolved
|
||||||
|
|
||||||
assert isinstance(resolved, Iterable), (
|
assert isinstance(resolved, Iterable), (
|
||||||
"Resolved value from the connection field have to be iterable or instance of {}. "
|
"Resolved value from the connection field has to be iterable or instance of {}. "
|
||||||
'Received "{}"'
|
'Received "{}"'
|
||||||
).format(connection_type, resolved)
|
).format(connection_type, resolved)
|
||||||
connection = connection_from_list(
|
connection = connection_from_list(
|
||||||
|
|
|
@ -73,7 +73,7 @@ class AbstractNode(Interface):
|
||||||
def __init_subclass_with_meta__(cls, **options):
|
def __init_subclass_with_meta__(cls, **options):
|
||||||
_meta = InterfaceOptions(cls)
|
_meta = InterfaceOptions(cls)
|
||||||
_meta.fields = OrderedDict(
|
_meta.fields = OrderedDict(
|
||||||
id=GlobalID(cls, description="The ID of the object.")
|
id=GlobalID(cls, description="The ID of the object")
|
||||||
)
|
)
|
||||||
super(AbstractNode, cls).__init_subclass_with_meta__(_meta=_meta, **options)
|
super(AbstractNode, cls).__init_subclass_with_meta__(_meta=_meta, **options)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from collections import OrderedDict
|
from pytest import mark
|
||||||
|
|
||||||
from graphql_relay.utils import base64
|
from graphql_relay.utils import base64
|
||||||
from promise import Promise
|
|
||||||
|
|
||||||
from ...types import ObjectType, Schema, String
|
from ...types import ObjectType, Schema, String
|
||||||
from ..connection import Connection, ConnectionField, PageInfo
|
from ..connection import Connection, ConnectionField, PageInfo
|
||||||
|
@ -25,15 +24,15 @@ class LetterConnection(Connection):
|
||||||
class Query(ObjectType):
|
class Query(ObjectType):
|
||||||
letters = ConnectionField(LetterConnection)
|
letters = ConnectionField(LetterConnection)
|
||||||
connection_letters = ConnectionField(LetterConnection)
|
connection_letters = ConnectionField(LetterConnection)
|
||||||
promise_letters = ConnectionField(LetterConnection)
|
async_letters = ConnectionField(LetterConnection)
|
||||||
|
|
||||||
node = Node.Field()
|
node = Node.Field()
|
||||||
|
|
||||||
def resolve_letters(self, info, **args):
|
def resolve_letters(self, info, **args):
|
||||||
return list(letters.values())
|
return list(letters.values())
|
||||||
|
|
||||||
def resolve_promise_letters(self, info, **args):
|
async def resolve_async_letters(self, info, **args):
|
||||||
return Promise.resolve(list(letters.values()))
|
return list(letters.values())
|
||||||
|
|
||||||
def resolve_connection_letters(self, info, **args):
|
def resolve_connection_letters(self, info, **args):
|
||||||
return LetterConnection(
|
return LetterConnection(
|
||||||
|
@ -46,9 +45,7 @@ class Query(ObjectType):
|
||||||
|
|
||||||
schema = Schema(Query)
|
schema = Schema(Query)
|
||||||
|
|
||||||
letters = OrderedDict()
|
letters = {letter: Letter(id=i, letter=letter) for i, letter in enumerate(letter_chars)}
|
||||||
for i, letter in enumerate(letter_chars):
|
|
||||||
letters[letter] = Letter(id=i, letter=letter)
|
|
||||||
|
|
||||||
|
|
||||||
def edges(selected_letters):
|
def edges(selected_letters):
|
||||||
|
@ -66,11 +63,11 @@ def cursor_for(ltr):
|
||||||
return base64("arrayconnection:%s" % letter.id)
|
return base64("arrayconnection:%s" % letter.id)
|
||||||
|
|
||||||
|
|
||||||
def execute(args=""):
|
async def execute(args=""):
|
||||||
if args:
|
if args:
|
||||||
args = "(" + args + ")"
|
args = "(" + args + ")"
|
||||||
|
|
||||||
return schema.execute(
|
return await schema.execute_async(
|
||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
letters%s {
|
letters%s {
|
||||||
|
@ -94,8 +91,8 @@ def execute(args=""):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def check(args, letters, has_previous_page=False, has_next_page=False):
|
async def check(args, letters, has_previous_page=False, has_next_page=False):
|
||||||
result = execute(args)
|
result = await execute(args)
|
||||||
expected_edges = edges(letters)
|
expected_edges = edges(letters)
|
||||||
expected_page_info = {
|
expected_page_info = {
|
||||||
"hasPreviousPage": has_previous_page,
|
"hasPreviousPage": has_previous_page,
|
||||||
|
@ -110,96 +107,114 @@ def check(args, letters, has_previous_page=False, has_next_page=False):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_returns_all_elements_without_filters():
|
@mark.asyncio
|
||||||
check("", "ABCDE")
|
async def test_returns_all_elements_without_filters():
|
||||||
|
await check("", "ABCDE")
|
||||||
|
|
||||||
|
|
||||||
def test_respects_a_smaller_first():
|
@mark.asyncio
|
||||||
check("first: 2", "AB", has_next_page=True)
|
async def test_respects_a_smaller_first():
|
||||||
|
await check("first: 2", "AB", has_next_page=True)
|
||||||
|
|
||||||
|
|
||||||
def test_respects_an_overly_large_first():
|
@mark.asyncio
|
||||||
check("first: 10", "ABCDE")
|
async def test_respects_an_overly_large_first():
|
||||||
|
await check("first: 10", "ABCDE")
|
||||||
|
|
||||||
|
|
||||||
def test_respects_a_smaller_last():
|
@mark.asyncio
|
||||||
check("last: 2", "DE", has_previous_page=True)
|
async def test_respects_a_smaller_last():
|
||||||
|
await check("last: 2", "DE", has_previous_page=True)
|
||||||
|
|
||||||
|
|
||||||
def test_respects_an_overly_large_last():
|
@mark.asyncio
|
||||||
check("last: 10", "ABCDE")
|
async def test_respects_an_overly_large_last():
|
||||||
|
await check("last: 10", "ABCDE")
|
||||||
|
|
||||||
|
|
||||||
def test_respects_first_and_after():
|
@mark.asyncio
|
||||||
check('first: 2, after: "{}"'.format(cursor_for("B")), "CD", has_next_page=True)
|
async def test_respects_first_and_after():
|
||||||
|
await check('first: 2, after: "{}"'.format(cursor_for("B")), "CD", has_next_page=True)
|
||||||
|
|
||||||
|
|
||||||
def test_respects_first_and_after_with_long_first():
|
@mark.asyncio
|
||||||
check('first: 10, after: "{}"'.format(cursor_for("B")), "CDE")
|
async def test_respects_first_and_after_with_long_first():
|
||||||
|
await check('first: 10, after: "{}"'.format(cursor_for("B")), "CDE")
|
||||||
|
|
||||||
|
|
||||||
def test_respects_last_and_before():
|
@mark.asyncio
|
||||||
check('last: 2, before: "{}"'.format(cursor_for("D")), "BC", has_previous_page=True)
|
async def test_respects_last_and_before():
|
||||||
|
await check('last: 2, before: "{}"'.format(cursor_for("D")), "BC", has_previous_page=True)
|
||||||
|
|
||||||
|
|
||||||
def test_respects_last_and_before_with_long_last():
|
@mark.asyncio
|
||||||
check('last: 10, before: "{}"'.format(cursor_for("D")), "ABC")
|
async def test_respects_last_and_before_with_long_last():
|
||||||
|
await check('last: 10, before: "{}"'.format(cursor_for("D")), "ABC")
|
||||||
|
|
||||||
|
|
||||||
def test_respects_first_and_after_and_before_too_few():
|
@mark.asyncio
|
||||||
check(
|
async def test_respects_first_and_after_and_before_too_few():
|
||||||
|
await check(
|
||||||
'first: 2, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")),
|
'first: 2, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")),
|
||||||
"BC",
|
"BC",
|
||||||
has_next_page=True,
|
has_next_page=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_respects_first_and_after_and_before_too_many():
|
@mark.asyncio
|
||||||
check(
|
async def test_respects_first_and_after_and_before_too_many():
|
||||||
|
await check(
|
||||||
'first: 4, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")),
|
'first: 4, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")),
|
||||||
"BCD",
|
"BCD",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_respects_first_and_after_and_before_exactly_right():
|
@mark.asyncio
|
||||||
check(
|
async def test_respects_first_and_after_and_before_exactly_right():
|
||||||
|
await check(
|
||||||
'first: 3, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")),
|
'first: 3, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")),
|
||||||
"BCD",
|
"BCD",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_respects_last_and_after_and_before_too_few():
|
@mark.asyncio
|
||||||
check(
|
async def test_respects_last_and_after_and_before_too_few():
|
||||||
|
await check(
|
||||||
'last: 2, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")),
|
'last: 2, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")),
|
||||||
"CD",
|
"CD",
|
||||||
has_previous_page=True,
|
has_previous_page=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_respects_last_and_after_and_before_too_many():
|
@mark.asyncio
|
||||||
check(
|
async def test_respects_last_and_after_and_before_too_many():
|
||||||
|
await check(
|
||||||
'last: 4, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")),
|
'last: 4, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")),
|
||||||
"BCD",
|
"BCD",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_respects_last_and_after_and_before_exactly_right():
|
@mark.asyncio
|
||||||
check(
|
async def test_respects_last_and_after_and_before_exactly_right():
|
||||||
|
await check(
|
||||||
'last: 3, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")),
|
'last: 3, after: "{}", before: "{}"'.format(cursor_for("A"), cursor_for("E")),
|
||||||
"BCD",
|
"BCD",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_returns_no_elements_if_first_is_0():
|
@mark.asyncio
|
||||||
check("first: 0", "", has_next_page=True)
|
async def test_returns_no_elements_if_first_is_0():
|
||||||
|
await check("first: 0", "", has_next_page=True)
|
||||||
|
|
||||||
|
|
||||||
def test_returns_all_elements_if_cursors_are_invalid():
|
@mark.asyncio
|
||||||
check('before: "invalid" after: "invalid"', "ABCDE")
|
async def test_returns_all_elements_if_cursors_are_invalid():
|
||||||
|
await check('before: "invalid" after: "invalid"', "ABCDE")
|
||||||
|
|
||||||
|
|
||||||
def test_returns_all_elements_if_cursors_are_on_the_outside():
|
@mark.asyncio
|
||||||
check(
|
async def test_returns_all_elements_if_cursors_are_on_the_outside():
|
||||||
|
await check(
|
||||||
'before: "{}" after: "{}"'.format(
|
'before: "{}" after: "{}"'.format(
|
||||||
base64("arrayconnection:%s" % 6), base64("arrayconnection:%s" % -1)
|
base64("arrayconnection:%s" % 6), base64("arrayconnection:%s" % -1)
|
||||||
),
|
),
|
||||||
|
@ -207,8 +222,9 @@ def test_returns_all_elements_if_cursors_are_on_the_outside():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_returns_no_elements_if_cursors_cross():
|
@mark.asyncio
|
||||||
check(
|
async def test_returns_no_elements_if_cursors_cross():
|
||||||
|
await check(
|
||||||
'before: "{}" after: "{}"'.format(
|
'before: "{}" after: "{}"'.format(
|
||||||
base64("arrayconnection:%s" % 2), base64("arrayconnection:%s" % 4)
|
base64("arrayconnection:%s" % 2), base64("arrayconnection:%s" % 4)
|
||||||
),
|
),
|
||||||
|
@ -216,8 +232,9 @@ def test_returns_no_elements_if_cursors_cross():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_connection_type_nodes():
|
@mark.asyncio
|
||||||
result = schema.execute(
|
async def test_connection_type_nodes():
|
||||||
|
result = await schema.execute_async(
|
||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
connectionLetters {
|
connectionLetters {
|
||||||
|
@ -248,11 +265,12 @@ def test_connection_type_nodes():
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_connection_promise():
|
@mark.asyncio
|
||||||
result = schema.execute(
|
async def test_connection_async():
|
||||||
|
result = await schema.execute_async(
|
||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
promiseLetters(first:1) {
|
asyncLetters(first:1) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
|
@ -270,7 +288,7 @@ def test_connection_promise():
|
||||||
|
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == {
|
assert result.data == {
|
||||||
"promiseLetters": {
|
"asyncLetters": {
|
||||||
"edges": [{"node": {"id": "TGV0dGVyOjA=", "letter": "A"}}],
|
"edges": [{"node": {"id": "TGV0dGVyOjA=", "letter": "A"}}],
|
||||||
"pageInfo": {"hasPreviousPage": False, "hasNextPage": True},
|
"pageInfo": {"hasPreviousPage": False, "hasNextPage": True},
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,11 +80,11 @@ class OtherMutation(ClientIDMutation):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def mutate_and_get_payload(
|
def mutate_and_get_payload(
|
||||||
self, info, shared="", additional_field="", client_mutation_id=None
|
self, info, shared, additional_field, client_mutation_id=None
|
||||||
):
|
):
|
||||||
edge_type = MyEdge
|
edge_type = MyEdge
|
||||||
return OtherMutation(
|
return OtherMutation(
|
||||||
name=shared + additional_field,
|
name=(shared or "") + (additional_field or ""),
|
||||||
my_node_edge=edge_type(cursor="1", node=MyNode(name="name")),
|
my_node_edge=edge_type(cursor="1", node=MyNode(name="name")),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
from graphql_relay import to_global_id
|
from graphql_relay import to_global_id
|
||||||
|
|
||||||
|
from graphql.pyutils import dedent
|
||||||
|
|
||||||
from ...types import ObjectType, Schema, String
|
from ...types import ObjectType, Schema, String
|
||||||
from ..node import Node, is_node
|
from ..node import Node, is_node
|
||||||
|
|
||||||
|
@ -70,17 +70,13 @@ def test_subclassed_node_query():
|
||||||
% to_global_id("MyOtherNode", 1)
|
% to_global_id("MyOtherNode", 1)
|
||||||
)
|
)
|
||||||
assert not executed.errors
|
assert not executed.errors
|
||||||
assert executed.data == OrderedDict(
|
assert executed.data == {
|
||||||
{
|
"node": {
|
||||||
"node": OrderedDict(
|
"shared": "1",
|
||||||
[
|
"extraField": "extra field info.",
|
||||||
("shared", "1"),
|
"somethingElse": "----"
|
||||||
("extraField", "extra field info."),
|
}
|
||||||
("somethingElse", "----"),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_node_requesting_non_node():
|
def test_node_requesting_non_node():
|
||||||
|
@ -124,7 +120,7 @@ def test_node_field_only_type_wrong():
|
||||||
% Node.to_global_id("MyOtherNode", 1)
|
% Node.to_global_id("MyOtherNode", 1)
|
||||||
)
|
)
|
||||||
assert len(executed.errors) == 1
|
assert len(executed.errors) == 1
|
||||||
assert str(executed.errors[0]) == "Must receive a MyNode id."
|
assert str(executed.errors[0]).startswith("Must receive a MyNode id.")
|
||||||
assert executed.data == {"onlyNode": None}
|
assert executed.data == {"onlyNode": None}
|
||||||
|
|
||||||
|
|
||||||
|
@ -143,39 +139,48 @@ def test_node_field_only_lazy_type_wrong():
|
||||||
% Node.to_global_id("MyOtherNode", 1)
|
% Node.to_global_id("MyOtherNode", 1)
|
||||||
)
|
)
|
||||||
assert len(executed.errors) == 1
|
assert len(executed.errors) == 1
|
||||||
assert str(executed.errors[0]) == "Must receive a MyNode id."
|
assert str(executed.errors[0]).startswith("Must receive a MyNode id.")
|
||||||
assert executed.data == {"onlyNodeLazy": None}
|
assert executed.data == {"onlyNodeLazy": None}
|
||||||
|
|
||||||
|
|
||||||
def test_str_schema():
|
def test_str_schema():
|
||||||
assert (
|
assert (str(schema) == dedent(
|
||||||
str(schema)
|
'''
|
||||||
== """
|
schema {
|
||||||
schema {
|
|
||||||
query: RootQuery
|
query: RootQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
type MyNode implements Node {
|
type MyNode implements Node {
|
||||||
|
"""The ID of the object"""
|
||||||
id: ID!
|
id: ID!
|
||||||
name: String
|
name: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type MyOtherNode implements Node {
|
type MyOtherNode implements Node {
|
||||||
|
"""The ID of the object"""
|
||||||
id: ID!
|
id: ID!
|
||||||
shared: String
|
shared: String
|
||||||
somethingElse: String
|
somethingElse: String
|
||||||
extraField: String
|
extraField: String
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Node {
|
"""An object with an ID"""
|
||||||
|
interface Node {
|
||||||
|
"""The ID of the object"""
|
||||||
id: ID!
|
id: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
type RootQuery {
|
type RootQuery {
|
||||||
first: String
|
first: String
|
||||||
|
|
||||||
|
"""The ID of the object"""
|
||||||
node(id: ID!): Node
|
node(id: ID!): Node
|
||||||
|
|
||||||
|
"""The ID of the object"""
|
||||||
onlyNode(id: ID!): MyNode
|
onlyNode(id: ID!): MyNode
|
||||||
|
|
||||||
|
"""The ID of the object"""
|
||||||
onlyNodeLazy(id: ID!): MyNode
|
onlyNodeLazy(id: ID!): MyNode
|
||||||
}
|
}
|
||||||
""".lstrip()
|
''')
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from graphql import graphql
|
from graphql import graphql_sync
|
||||||
|
from graphql.pyutils import dedent
|
||||||
|
|
||||||
from ...types import Interface, ObjectType, Schema
|
from ...types import Interface, ObjectType, Schema
|
||||||
from ...types.scalars import Int, String
|
from ...types.scalars import Int, String
|
||||||
|
@ -15,7 +16,7 @@ class CustomNode(Node):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_node_from_global_id(info, id, only_type=None):
|
def get_node_from_global_id(info, id, only_type=None):
|
||||||
assert info.schema == schema
|
assert info.schema is graphql_schema
|
||||||
if id in user_data:
|
if id in user_data:
|
||||||
return user_data.get(id)
|
return user_data.get(id)
|
||||||
else:
|
else:
|
||||||
|
@ -23,14 +24,14 @@ class CustomNode(Node):
|
||||||
|
|
||||||
|
|
||||||
class BasePhoto(Interface):
|
class BasePhoto(Interface):
|
||||||
width = Int()
|
width = Int(description="The width of the photo in pixels")
|
||||||
|
|
||||||
|
|
||||||
class User(ObjectType):
|
class User(ObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
interfaces = [CustomNode]
|
interfaces = [CustomNode]
|
||||||
|
|
||||||
name = String()
|
name = String(description="The full name of the user")
|
||||||
|
|
||||||
|
|
||||||
class Photo(ObjectType):
|
class Photo(ObjectType):
|
||||||
|
@ -48,37 +49,47 @@ class RootQuery(ObjectType):
|
||||||
|
|
||||||
|
|
||||||
schema = Schema(query=RootQuery, types=[User, Photo])
|
schema = Schema(query=RootQuery, types=[User, Photo])
|
||||||
|
graphql_schema = schema.graphql_schema
|
||||||
|
|
||||||
|
|
||||||
def test_str_schema_correct():
|
def test_str_schema_correct():
|
||||||
assert (
|
assert (str(schema) == dedent(
|
||||||
str(schema)
|
'''
|
||||||
== """schema {
|
schema {
|
||||||
query: RootQuery
|
query: RootQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BasePhoto {
|
interface BasePhoto {
|
||||||
|
"""The width of the photo in pixels"""
|
||||||
width: Int
|
width: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Node {
|
interface Node {
|
||||||
|
"""The ID of the object"""
|
||||||
id: ID!
|
id: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Photo implements Node, BasePhoto {
|
type Photo implements Node & BasePhoto {
|
||||||
|
"""The ID of the object"""
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""The width of the photo in pixels"""
|
||||||
width: Int
|
width: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
type RootQuery {
|
type RootQuery {
|
||||||
|
"""The ID of the object"""
|
||||||
node(id: ID!): Node
|
node(id: ID!): Node
|
||||||
}
|
}
|
||||||
|
|
||||||
type User implements Node {
|
type User implements Node {
|
||||||
|
"""The ID of the object"""
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""The full name of the user"""
|
||||||
name: String
|
name: String
|
||||||
}
|
}
|
||||||
"""
|
''')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,7 +102,7 @@ def test_gets_the_correct_id_for_users():
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
expected = {"node": {"id": "1"}}
|
expected = {"node": {"id": "1"}}
|
||||||
result = graphql(schema, query)
|
result = graphql_sync(graphql_schema, query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
|
||||||
|
@ -105,7 +116,7 @@ def test_gets_the_correct_id_for_photos():
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
expected = {"node": {"id": "4"}}
|
expected = {"node": {"id": "4"}}
|
||||||
result = graphql(schema, query)
|
result = graphql_sync(graphql_schema, query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
|
||||||
|
@ -122,7 +133,7 @@ def test_gets_the_correct_name_for_users():
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
expected = {"node": {"id": "1", "name": "John Doe"}}
|
expected = {"node": {"id": "1", "name": "John Doe"}}
|
||||||
result = graphql(schema, query)
|
result = graphql_sync(graphql_schema, query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
|
||||||
|
@ -139,7 +150,7 @@ def test_gets_the_correct_width_for_photos():
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
expected = {"node": {"id": "4", "width": 400}}
|
expected = {"node": {"id": "4", "width": 400}}
|
||||||
result = graphql(schema, query)
|
result = graphql_sync(graphql_schema, query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
|
||||||
|
@ -154,7 +165,7 @@ def test_gets_the_correct_typename_for_users():
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
expected = {"node": {"id": "1", "__typename": "User"}}
|
expected = {"node": {"id": "1", "__typename": "User"}}
|
||||||
result = graphql(schema, query)
|
result = graphql_sync(graphql_schema, query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
|
||||||
|
@ -169,7 +180,7 @@ def test_gets_the_correct_typename_for_photos():
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
expected = {"node": {"id": "4", "__typename": "Photo"}}
|
expected = {"node": {"id": "4", "__typename": "Photo"}}
|
||||||
result = graphql(schema, query)
|
result = graphql_sync(graphql_schema, query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
|
||||||
|
@ -186,7 +197,7 @@ def test_ignores_photo_fragments_on_user():
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
expected = {"node": {"id": "1"}}
|
expected = {"node": {"id": "1"}}
|
||||||
result = graphql(schema, query)
|
result = graphql_sync(graphql_schema, query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
|
||||||
|
@ -200,7 +211,7 @@ def test_returns_null_for_bad_ids():
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
expected = {"node": None}
|
expected = {"node": None}
|
||||||
result = graphql(schema, query)
|
result = graphql_sync(graphql_schema, query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
|
||||||
|
@ -239,7 +250,7 @@ def test_have_correct_node_interface():
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = graphql(schema, query)
|
result = graphql_sync(graphql_schema, query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
|
||||||
|
@ -291,6 +302,6 @@ def test_has_correct_node_root_field():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = graphql(schema, query)
|
result = graphql_sync(graphql_schema, query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
|
|
@ -27,6 +27,7 @@ def test_issue():
|
||||||
graphene.Schema(query=Query)
|
graphene.Schema(query=Query)
|
||||||
|
|
||||||
assert str(exc_info.value) == (
|
assert str(exc_info.value) == (
|
||||||
"IterableConnectionField type have to be a subclass of Connection. "
|
"Query fields cannot be resolved:"
|
||||||
'Received "MyUnion".'
|
" IterableConnectionField type has to be a subclass of Connection."
|
||||||
|
' Received "MyUnion".'
|
||||||
)
|
)
|
||||||
|
|
|
@ -185,7 +185,6 @@ def test_bad_variables(sample_date, sample_datetime, sample_time):
|
||||||
# when `input` is not JSON serializable formatting the error message in
|
# when `input` is not JSON serializable formatting the error message in
|
||||||
# `graphql.utils.is_valid_value` line 79 fails with a TypeError
|
# `graphql.utils.is_valid_value` line 79 fails with a TypeError
|
||||||
assert isinstance(result.errors[0], GraphQLError)
|
assert isinstance(result.errors[0], GraphQLError)
|
||||||
print(result.errors[0])
|
|
||||||
assert result.data is None
|
assert result.data is None
|
||||||
|
|
||||||
not_a_date = dict()
|
not_a_date = dict()
|
||||||
|
|
|
@ -229,11 +229,11 @@ def test_query_arguments():
|
||||||
|
|
||||||
result = test_schema.execute("{ test }", None)
|
result = test_schema.execute("{ test }", None)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == {"test": "[null,{}]"}
|
assert result.data == {"test": '[null,{"a_str":null,"a_int":null}]'}
|
||||||
|
|
||||||
result = test_schema.execute('{ test(aStr: "String!") }', "Source!")
|
result = test_schema.execute('{ test(aStr: "String!") }', "Source!")
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == {"test": '["Source!",{"a_str":"String!"}]'}
|
assert result.data == {"test": '["Source!",{"a_str":"String!","a_int":null}]'}
|
||||||
|
|
||||||
result = test_schema.execute('{ test(aInt: -123, aStr: "String!") }', "Source!")
|
result = test_schema.execute('{ test(aInt: -123, aStr: "String!") }', "Source!")
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
|
@ -258,18 +258,20 @@ def test_query_input_field():
|
||||||
|
|
||||||
result = test_schema.execute("{ test }", None)
|
result = test_schema.execute("{ test }", None)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == {"test": "[null,{}]"}
|
assert result.data == {"test": '[null,{"a_input":null}]'}
|
||||||
|
|
||||||
result = test_schema.execute('{ test(aInput: {aField: "String!"} ) }', "Source!")
|
result = test_schema.execute('{ test(aInput: {aField: "String!"} ) }', "Source!")
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == {"test": '["Source!",{"a_input":{"a_field":"String!"}}]'}
|
assert result.data == {
|
||||||
|
"test": '["Source!",{"a_input":{"a_field":"String!","recursive_field":null}}]'}
|
||||||
|
|
||||||
result = test_schema.execute(
|
result = test_schema.execute(
|
||||||
'{ test(aInput: {recursiveField: {aField: "String!"}}) }', "Source!"
|
'{ test(aInput: {recursiveField: {aField: "String!"}}) }', "Source!"
|
||||||
)
|
)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == {
|
assert result.data == {
|
||||||
"test": '["Source!",{"a_input":{"recursive_field":{"a_field":"String!"}}}]'
|
"test": '["Source!",{"a_input":{"a_field":null,"recursive_field":'
|
||||||
|
'{"a_field":"String!","recursive_field":null}}}]'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -285,8 +287,7 @@ def test_query_middlewares():
|
||||||
return "other"
|
return "other"
|
||||||
|
|
||||||
def reversed_middleware(next, *args, **kwargs):
|
def reversed_middleware(next, *args, **kwargs):
|
||||||
p = next(*args, **kwargs)
|
return next(*args, **kwargs)[::-1]
|
||||||
return p.then(lambda x: x[::-1])
|
|
||||||
|
|
||||||
hello_schema = Schema(Query)
|
hello_schema = Schema(Query)
|
||||||
|
|
||||||
|
@ -348,10 +349,11 @@ def test_big_list_query_compiled_query_benchmark(benchmark):
|
||||||
return big_list
|
return big_list
|
||||||
|
|
||||||
hello_schema = Schema(Query)
|
hello_schema = Schema(Query)
|
||||||
|
graphql_schema = hello_schema.graphql_schema
|
||||||
source = Source("{ allInts }")
|
source = Source("{ allInts }")
|
||||||
query_ast = parse(source)
|
query_ast = parse(source)
|
||||||
|
|
||||||
big_list_query = partial(execute, hello_schema, query_ast)
|
big_list_query = partial(execute, graphql_schema, query_ast)
|
||||||
result = benchmark(big_list_query)
|
result = benchmark(big_list_query)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == {"allInts": list(big_list)}
|
assert result.data == {"allInts": list(big_list)}
|
||||||
|
|
|
@ -1,28 +1,15 @@
|
||||||
"""
|
"""
|
||||||
This file is used mainly as a bridge for thenable abstractions.
|
This file is used mainly as a bridge for thenable abstractions.
|
||||||
This includes:
|
|
||||||
- Promises
|
|
||||||
- Asyncio Coroutines
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
from inspect import isawaitable
|
||||||
from promise import Promise, is_thenable # type: ignore
|
|
||||||
except ImportError:
|
|
||||||
|
|
||||||
class Promise(object): # type: ignore
|
|
||||||
pass
|
|
||||||
|
|
||||||
def is_thenable(obj): # type: ignore
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
def await_and_execute(obj, on_resolve):
|
||||||
from inspect import isawaitable
|
async def build_resolve_async():
|
||||||
from .thenables_asyncio import await_and_execute
|
return on_resolve(await obj)
|
||||||
except ImportError:
|
|
||||||
|
|
||||||
def isawaitable(obj): # type: ignore
|
return build_resolve_async()
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def maybe_thenable(obj, on_resolve):
|
def maybe_thenable(obj, on_resolve):
|
||||||
|
@ -31,12 +18,8 @@ def maybe_thenable(obj, on_resolve):
|
||||||
returning the same type of object inputed.
|
returning the same type of object inputed.
|
||||||
If the object is not thenable, it should return on_resolve(obj)
|
If the object is not thenable, it should return on_resolve(obj)
|
||||||
"""
|
"""
|
||||||
if isawaitable(obj) and not isinstance(obj, Promise):
|
if isawaitable(obj):
|
||||||
return await_and_execute(obj, on_resolve)
|
return await_and_execute(obj, on_resolve)
|
||||||
|
|
||||||
if is_thenable(obj):
|
# If it's not awaitable, return the function executed over the object
|
||||||
return Promise.resolve(obj).then(on_resolve)
|
|
||||||
|
|
||||||
# If it's not awaitable not a Promise, return
|
|
||||||
# the function executed over the object
|
|
||||||
return on_resolve(obj)
|
return on_resolve(obj)
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
def await_and_execute(obj, on_resolve):
|
|
||||||
async def build_resolve_async():
|
|
||||||
return on_resolve(await obj)
|
|
||||||
|
|
||||||
return build_resolve_async()
|
|
|
@ -1,7 +1,4 @@
|
||||||
import pytest
|
from pytest import mark
|
||||||
|
|
||||||
from collections import OrderedDict
|
|
||||||
from graphql.execution.executors.asyncio import AsyncioExecutor
|
|
||||||
|
|
||||||
from graphql_relay.utils import base64
|
from graphql_relay.utils import base64
|
||||||
|
|
||||||
|
@ -27,14 +24,14 @@ class LetterConnection(Connection):
|
||||||
class Query(ObjectType):
|
class Query(ObjectType):
|
||||||
letters = ConnectionField(LetterConnection)
|
letters = ConnectionField(LetterConnection)
|
||||||
connection_letters = ConnectionField(LetterConnection)
|
connection_letters = ConnectionField(LetterConnection)
|
||||||
promise_letters = ConnectionField(LetterConnection)
|
async_letters = ConnectionField(LetterConnection)
|
||||||
|
|
||||||
node = Node.Field()
|
node = Node.Field()
|
||||||
|
|
||||||
def resolve_letters(self, info, **args):
|
def resolve_letters(self, info, **args):
|
||||||
return list(letters.values())
|
return list(letters.values())
|
||||||
|
|
||||||
async def resolve_promise_letters(self, info, **args):
|
async def resolve_async_letters(self, info, **args):
|
||||||
return list(letters.values())
|
return list(letters.values())
|
||||||
|
|
||||||
def resolve_connection_letters(self, info, **args):
|
def resolve_connection_letters(self, info, **args):
|
||||||
|
@ -48,9 +45,7 @@ class Query(ObjectType):
|
||||||
|
|
||||||
schema = Schema(Query)
|
schema = Schema(Query)
|
||||||
|
|
||||||
letters = OrderedDict()
|
letters = {letter: Letter(id=i, letter=letter) for i, letter in enumerate(letter_chars)}
|
||||||
for i, letter in enumerate(letter_chars):
|
|
||||||
letters[letter] = Letter(id=i, letter=letter)
|
|
||||||
|
|
||||||
|
|
||||||
def edges(selected_letters):
|
def edges(selected_letters):
|
||||||
|
@ -96,12 +91,12 @@ def execute(args=""):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@mark.asyncio
|
||||||
async def test_connection_promise():
|
async def test_connection_async():
|
||||||
result = await schema.execute(
|
result = await schema.execute_async(
|
||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
promiseLetters(first:1) {
|
asyncLetters(first:1) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
|
@ -115,13 +110,11 @@ async def test_connection_promise():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""",
|
""",
|
||||||
executor=AsyncioExecutor(),
|
|
||||||
return_promise=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == {
|
assert result.data == {
|
||||||
"promiseLetters": {
|
"asyncLetters": {
|
||||||
"edges": [{"node": {"id": "TGV0dGVyOjA=", "letter": "A"}}],
|
"edges": [{"node": {"id": "TGV0dGVyOjA=", "letter": "A"}}],
|
||||||
"pageInfo": {"hasPreviousPage": False, "hasNextPage": True},
|
"pageInfo": {"hasPreviousPage": False, "hasNextPage": True},
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,11 +42,11 @@ class OtherMutation(ClientIDMutation):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def mutate_and_get_payload(
|
def mutate_and_get_payload(
|
||||||
self, info, shared="", additional_field="", client_mutation_id=None
|
self, info, shared, additional_field, client_mutation_id=None
|
||||||
):
|
):
|
||||||
edge_type = MyEdge
|
edge_type = MyEdge
|
||||||
return OtherMutation(
|
return OtherMutation(
|
||||||
name=shared + additional_field,
|
name=(shared or "") + (additional_field or ""),
|
||||||
my_node_edge=edge_type(cursor="1", node=MyNode(name="name")),
|
my_node_edge=edge_type(cursor="1", node=MyNode(name="name")),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user