def test_parse_provides_useful_errors(): with raises(GraphQLSyntaxError) as excinfo: parse("""{""") assert ( u'Syntax Error GraphQL (1:2) Expected Name, found EOF\n' u'\n' u'1: {\n' u' ^\n' u'' ) == excinfo.value.message assert excinfo.value.positions == [1] assert excinfo.value.locations == [SourceLocation(line=1, column=2)] with raises(GraphQLSyntaxError) as excinfo: parse("""{ ...MissingOn } fragment MissingOn Type """) assert 'Syntax Error GraphQL (2:20) Expected "on", found Name "Type"' in str(excinfo.value) with raises(GraphQLSyntaxError) as excinfo: parse('{ field: {} }') assert 'Syntax Error GraphQL (1:10) Expected Name, found {' in str(excinfo.value) with raises(GraphQLSyntaxError) as excinfo: parse('notanoperation Foo { field }') assert 'Syntax Error GraphQL (1:1) Unexpected Name "notanoperation"' in str(excinfo.value) with raises(GraphQLSyntaxError) as excinfo: parse('...') assert 'Syntax Error GraphQL (1:1) Unexpected ...' in str(excinfo.value)
def tesst_allows_non_keywords_anywhere_a_name_is_allowed(): non_keywords = [ 'on', 'fragment', 'query', 'mutation', 'subscription', 'true', 'false' ] query_template = ''' query {keyword} { ... {fragment_name} ... on {keyword} { field } } fragment {fragment_name} on Type { {keyword}({keyword}: ${keyword}) @{keyword}({keyword}: {keyword}) } ''' for keyword in non_keywords: fragment_name = keyword if keyword == 'on': fragment_name = 'a' parse(query_template.format(fragment_name=fragment_name, keyword=keyword))
def test_empty_parse(): with raises(GraphQLSyntaxError) as excinfo: parse("") assert ( u'Syntax Error GraphQL (1:1) Unexpected EOF\n' u'\n' ) == excinfo.value.message
def test_visits_in_pararell_allows_for_editing_on_leave(): visited = [] ast = parse('{ a, b, c { a, b, c } }', no_location=True) class TestVisitor1(Visitor): def leave(self, node, key, parent, *args): if type(node).__name__ == 'Field' and node.name.value == 'b': return REMOVE class TestVisitor2(Visitor): def enter(self, node, key, parent, *args): visited.append( ['enter', type(node).__name__, getattr(node, 'value', None)]) def leave(self, node, key, parent, *args): visited.append( ['leave', type(node).__name__, getattr(node, 'value', None)]) edited_ast = visit(ast, ParallelVisitor([TestVisitor1(), TestVisitor2()])) assert ast == parse('{ a, b, c { a, b, c } }', no_location=True) assert edited_ast == parse('{ a, c { a, c } }', no_location=True) assert visited == [ ['enter', 'Document', None], ['enter', 'OperationDefinition', None], ['enter', 'SelectionSet', None], ['enter', 'Field', None], ['enter', 'Name', 'a'], ['leave', 'Name', 'a'], ['leave', 'Field', None], ['enter', 'Field', None], ['enter', 'Name', 'b'], ['leave', 'Name', 'b'], ['enter', 'Field', None], ['enter', 'Name', 'c'], ['leave', 'Name', 'c'], ['enter', 'SelectionSet', None], ['enter', 'Field', None], ['enter', 'Name', 'a'], ['leave', 'Name', 'a'], ['leave', 'Field', None], ['enter', 'Field', None], ['enter', 'Name', 'b'], ['leave', 'Name', 'b'], ['enter', 'Field', None], ['enter', 'Name', 'c'], ['leave', 'Name', 'c'], ['leave', 'Field', None], ['leave', 'SelectionSet', None], ['leave', 'Field', None], ['leave', 'SelectionSet', None], ['leave', 'OperationDefinition', None], ['leave', 'Document', None] ]
def test_allows_for_editing_on_enter(): ast = parse('{ a, b, c { a, b, c } }', no_location=True) class TestVisitor(Visitor): def enter(self, node, *args): if isinstance(node, Field) and node.name.value == 'b': return REMOVE edited_ast = visit(ast, TestVisitor()) assert ast == parse('{ a, b, c { a, b, c } }', no_location=True) assert edited_ast == parse('{ a, c { a, c } }', no_location=True)
def test_reraise_from_promise(): # type: () -> None ast = parse("query Example { a }") def fail(): raise Exception("Failed") def resolver(context, *_): # type: (Optional[Any], *ResolveInfo) -> None return Promise(lambda resolve, reject: resolve(fail())) Type = GraphQLObjectType( "Type", {"a": GraphQLField(GraphQLString, resolver=resolver)}) result = execute(GraphQLSchema(Type), ast) with pytest.raises(Exception) as exc_info: result.errors[0].reraise() extracted = traceback.extract_tb(exc_info.tb) formatted_tb = [row[2:] for row in extracted] formatted_tb = [tb for tb in formatted_tb if tb[0] != "reraise"] print(formatted_tb) assert formatted_tb == [ ("test_reraise_from_promise", "result.errors[0].reraise()"), ("_resolve_from_executor", "executor(resolve, reject)"), ("<lambda>", "return Promise(lambda resolve, reject: resolve(fail()))"), ("fail", 'raise Exception("Failed")'), ] assert str(exc_info.value) == "Failed"
def test_reraise(): ast = parse('query Example { a }') def resolver(context, *_): raise Exception('Failed') Type = GraphQLObjectType('Type', { 'a': GraphQLField(GraphQLString, resolver=resolver), }) result = execute(GraphQLSchema(Type), ast) with pytest.raises(Exception) as exc_info: result.errors[0].reraise() extracted = traceback.extract_tb(exc_info.tb) formatted_tb = [row[2:] for row in extracted] if formatted_tb[2][0] == 'reraise': formatted_tb[2:] = formatted_tb[3:] assert formatted_tb == [ ('test_reraise', 'result.errors[0].reraise()'), ('reraise', 'six.reraise(type(self), self, self.stack)'), # ('reraise', 'raise value.with_traceback(tb)'), ('resolve_or_error', 'return executor.execute(resolve_fn, source, args, context, info)'), ('execute', 'return fn(*args, **kwargs)'), ('resolver', "raise Exception('Failed')") ] assert str(exc_info.value) == 'Failed'
def test_allows_skipping_a_subtree(): # type: () -> None visited = [] ast = parse("{ a, b { x }, c }") class TestVisitor(Visitor): def enter(self, node, *args): # type: (Any, *Any) -> Optional[Any] visited.append(["enter", type(node).__name__, getattr(node, "value", None)]) if isinstance(node, Field) and node.name.value == "b": return False def leave(self, node, *args): # type: (Union[Field, Name, SelectionSet], *Any) -> None visited.append(["leave", type(node).__name__, getattr(node, "value", None)]) visit(ast, TestVisitor()) assert visited == [ ["enter", "Document", None], ["enter", "OperationDefinition", None], ["enter", "SelectionSet", None], ["enter", "Field", None], ["enter", "Name", "a"], ["leave", "Name", "a"], ["leave", "Field", None], ["enter", "Field", None], ["enter", "Field", None], ["enter", "Name", "c"], ["leave", "Name", "c"], ["leave", "Field", None], ["leave", "SelectionSet", None], ["leave", "OperationDefinition", None], ["leave", "Document", None], ]
def test_visits_in_pararell_allows_early_exit_while_visiting(): visited = [] ast = parse('{ a, b { x }, c }') class TestVisitor(Visitor): def enter(self, node, key, parent, *args): visited.append( ['enter', type(node).__name__, getattr(node, 'value', None)]) def leave(self, node, key, parent, *args): visited.append( ['leave', type(node).__name__, getattr(node, 'value', None)]) if type(node).__name__ == 'Name' and node.value == 'x': return BREAK visit(ast, ParallelVisitor([TestVisitor()])) assert visited == [ ['enter', 'Document', None], ['enter', 'OperationDefinition', None], ['enter', 'SelectionSet', None], ['enter', 'Field', None], ['enter', 'Name', 'a'], ['leave', 'Name', 'a'], ['leave', 'Field', None], ['enter', 'Field', None], ['enter', 'Name', 'b'], ['leave', 'Name', 'b'], ['enter', 'SelectionSet', None], ['enter', 'Field', None], ['enter', 'Name', 'x'], ['leave', 'Name', 'x'] ]
def test_visits_in_pararell_allows_skipping_a_subtree(): visited = [] ast = parse('{ a, b { x }, c }') class TestVisitor(Visitor): def enter(self, node, key, parent, *args): visited.append( ['enter', type(node).__name__, getattr(node, 'value', None)]) if type(node).__name__ == 'Field' and node.name.value == 'b': return False def leave(self, node, key, parent, *args): visited.append( ['leave', type(node).__name__, getattr(node, 'value', None)]) visit(ast, ParallelVisitor([TestVisitor()])) assert visited == [ ['enter', 'Document', None], ['enter', 'OperationDefinition', None], ['enter', 'SelectionSet', None], ['enter', 'Field', None], ['enter', 'Name', 'a'], ['leave', 'Name', 'a'], ['leave', 'Field', None], ['enter', 'Field', None], ['enter', 'Field', None], ['enter', 'Name', 'c'], ['leave', 'Name', 'c'], ['leave', 'Field', None], ['leave', 'SelectionSet', None], ['leave', 'OperationDefinition', None], ['leave', 'Document', None], ]
def test_allows_early_exit_while_visiting(): visited = [] ast = parse('{ a, b { x }, c }') class TestVisitor(Visitor): def enter(self, node, *args): visited.append( ['enter', type(node).__name__, getattr(node, 'value', None)]) if isinstance(node, Name) and node.value == 'x': return BREAK def leave(self, node, *args): visited.append( ['leave', type(node).__name__, getattr(node, 'value', None)]) visit(ast, TestVisitor()) assert visited == [ ['enter', 'Document', None], ['enter', 'OperationDefinition', None], ['enter', 'SelectionSet', None], ['enter', 'Field', None], ['enter', 'Name', 'a'], ['leave', 'Name', 'a'], ['leave', 'Field', None], ['enter', 'Field', None], ['enter', 'Name', 'b'], ['leave', 'Name', 'b'], ['enter', 'SelectionSet', None], ['enter', 'Field', None], ['enter', 'Name', 'x'], ]
def test_middleware_class(): # type: () -> None doc = """{ ok not_ok }""" class Data(object): def ok(self): # type: () -> str return "ok" def not_ok(self): # type: () -> str return "not_ok" doc_ast = parse(doc) Type = GraphQLObjectType( "Type", {"ok": GraphQLField(GraphQLString), "not_ok": GraphQLField(GraphQLString)}, ) class MyMiddleware(object): def resolve(self, next, *args, **kwargs): # type: (Callable, *Any, **Any) -> Promise p = next(*args, **kwargs) return p.then(lambda x: x[::-1]) middlewares = MiddlewareManager(MyMiddleware()) result = execute(GraphQLSchema(Type), doc_ast, Data(), middleware=middlewares) assert result.data == {"ok": "ko", "not_ok": "ko_ton"}
def test_asyncio_py35_executor_return_promise(): ast = parse('query Example { a, b, c }') async def resolver(context, *_): await asyncio.sleep(0.001) return 'hey' async def resolver_2(context, *_): await asyncio.sleep(0.003) return 'hey2' def resolver_3(context, *_): return 'hey3' Type = GraphQLObjectType( 'Type', { 'a': GraphQLField(GraphQLString, resolver=resolver), 'b': GraphQLField(GraphQLString, resolver=resolver_2), 'c': GraphQLField(GraphQLString, resolver=resolver_3) }) loop = asyncio.get_event_loop() async def do_exec(): result = await execute(GraphQLSchema(Type), ast, executor=AsyncioExecutor(loop), return_promise=True) assert not result.errors assert result.data == {'a': 'hey', 'b': 'hey2', 'c': 'hey3'} loop.run_until_complete(do_exec())
def test_visits_in_pararell_allows_early_exit_while_visiting(): visited = [] ast = parse('{ a, b { x }, c }') class TestVisitor(Visitor): def enter(self, node, key, parent, *args): visited.append( ['enter', type(node).__name__, getattr(node, 'value', None)]) def leave(self, node, key, parent, *args): visited.append( ['leave', type(node).__name__, getattr(node, 'value', None)]) if type(node).__name__ == 'Name' and node.value == 'x': return BREAK visit(ast, ParallelVisitor([TestVisitor()])) assert visited == [['enter', 'Document', None], ['enter', 'OperationDefinition', None], ['enter', 'SelectionSet', None], ['enter', 'Field', None], ['enter', 'Name', 'a'], ['leave', 'Name', 'a'], ['leave', 'Field', None], ['enter', 'Field', None], ['enter', 'Name', 'b'], ['leave', 'Name', 'b'], ['enter', 'SelectionSet', None], ['enter', 'Field', None], ['enter', 'Name', 'x'], ['leave', 'Name', 'x']]
def test_allows_editing_the_root_node_on_enter_and_on_leave(): ast = parse('{ a, b, c { a, b, c } }', no_location=True) definitions = ast.definitions class TestVisitor(Visitor): def __init__(self): self.did_enter = False self.did_leave = False def enter(self, node, *args): if isinstance(node, Document): self.did_enter = True return Document(loc=node.loc, definitions=[]) def leave(self, node, *args): if isinstance(node, Document): self.did_leave = True return Document(loc=node.loc, definitions=definitions) visitor = TestVisitor() edited_ast = visit(ast, visitor) assert edited_ast == ast assert visitor.did_enter assert visitor.did_leave
def test_allows_a_named_functions_visitor_api(): visited = [] ast = parse('{ a, b { x }, c }') class TestVisitor(Visitor): def enter_Name(self, node, *args): visited.append( ['enter', type(node).__name__, getattr(node, 'value', None)]) def enter_SelectionSet(self, node, *args): visited.append( ['enter', type(node).__name__, getattr(node, 'value', None)]) def leave_SelectionSet(self, node, *args): visited.append( ['leave', type(node).__name__, getattr(node, 'value', None)]) visit(ast, TestVisitor()) assert visited == [ ['enter', 'SelectionSet', None], ['enter', 'Name', 'a'], ['enter', 'Name', 'b'], ['enter', 'SelectionSet', None], ['enter', 'Name', 'x'], ['leave', 'SelectionSet', None], ['enter', 'Name', 'c'], ['leave', 'SelectionSet', None], ]
def test_parses_multi_byte_characters(): result = parse(u''' # This comment has a \u0A0A multi-byte character. { field(arg: "Has a \u0A0A multi-byte character.") } ''', no_location=True, no_source=True) assert result == ast.Document(definitions=[ ast.OperationDefinition( operation='query', name=None, variable_definitions=None, directives=[], selection_set=ast.SelectionSet(selections=[ ast.Field( alias=None, name=ast.Name(value=u'field'), arguments=[ ast.Argument( name=ast. Name(value=u'arg'), value=ast .StringValue( value=u'Has a \u0a0a multi-byte character.')) ], directives=[], selection_set=None) ])) ])
def read_operation(src, variables=None, operation_name=None): """Reads an operation from the GraphQL document Example: .. code-block:: python op = read_operation('{ foo bar }') if op.type is OperationType.QUERY: result = engine.execute(query_graph, op.query) :param str src: GraphQL document :param dict variables: query variables :param str operation_name: Name of the operation to execute :return: :py:class:`Operation` """ doc = parse(src) op = OperationGetter.get(doc, operation_name=operation_name) query = GraphQLTransformer.transform(doc, op, variables) type_ = OperationType._value2member_map_.get(op.operation) name = op.name.value if op.name else None if type_ is None: raise TypeError('Unsupported operation type: {}'.format(op.operation)) else: return Operation(type_, query, name)
def test_asyncio_executor_custom_loop(): # type: () -> None loop = asyncio.get_event_loop() def resolver(context, *_): # type: (Optional[Any], *ResolveInfo) -> str asyncio.sleep(0.001, loop=loop) return "hey" @asyncio.coroutine def resolver_2(context, *_): # type: (Optional[Any], *ResolveInfo) -> str asyncio.sleep(0.003, loop=loop) return "hey2" def resolver_3(contest, *_): # type: (Optional[Any], *ResolveInfo) -> str return "hey3" Type = GraphQLObjectType( "Type", { "a": GraphQLField(GraphQLString, resolver=resolver), "b": GraphQLField(GraphQLString, resolver=resolver_2), "c": GraphQLField(GraphQLString, resolver=resolver_3), }, ) ast = parse("{ a b c }") result = execute(GraphQLSchema(Type), ast, executor=AsyncioExecutor(loop=loop)) assert not result.errors assert result.data == {"a": "hey", "b": "hey2", "c": "hey3"}
def test_asyncio_py35_executor(): ast = parse("query Example { a, b, c }") async def resolver(context, *_): await asyncio.sleep(0.001) return "hey" async def resolver_2(context, *_): await asyncio.sleep(0.003) return "hey2" def resolver_3(context, *_): return "hey3" Type = GraphQLObjectType( "Type", { "a": GraphQLField(GraphQLString, resolver=resolver), "b": GraphQLField(GraphQLString, resolver=resolver_2), "c": GraphQLField(GraphQLString, resolver=resolver_3), }, ) result = execute(GraphQLSchema(Type), ast, executor=AsyncioExecutor()) assert not result.errors assert result.data == {"a": "hey", "b": "hey2", "c": "hey3"}
def test_asyncio_py35_executor_with_error(): ast = parse('query Example { a, b }') async def resolver(context, *_): await asyncio.sleep(0.001) return 'hey' async def resolver_2(context, *_): await asyncio.sleep(0.003) raise Exception('resolver_2 failed!') Type = GraphQLObjectType( 'Type', { 'a': GraphQLField(GraphQLString, resolver=resolver), 'b': GraphQLField(GraphQLString, resolver=resolver_2) }) result = execute(GraphQLSchema(Type), ast, executor=AsyncioExecutor()) formatted_errors = list(map(format_error, result.errors)) assert formatted_errors == [{ 'locations': [{ 'line': 1, 'column': 20 }], 'message': 'resolver_2 failed!' }] assert result.data == {'a': 'hey', 'b': None}
def test_allows_a_named_functions_visitor_api(): # type: () -> None visited = [] ast = parse("{ a, b { x }, c }") class TestVisitor(Visitor): def enter_Name(self, node, *args): # type: (Name, *Any) -> None visited.append(["enter", type(node).__name__, getattr(node, "value", None)]) def enter_SelectionSet(self, node, *args): # type: (SelectionSet, *Any) -> None visited.append(["enter", type(node).__name__, getattr(node, "value", None)]) def leave_SelectionSet(self, node, *args): # type: (SelectionSet, *Any) -> None visited.append(["leave", type(node).__name__, getattr(node, "value", None)]) visit(ast, TestVisitor()) assert visited == [ ["enter", "SelectionSet", None], ["enter", "Name", "a"], ["enter", "Name", "b"], ["enter", "SelectionSet", None], ["enter", "Name", "x"], ["leave", "SelectionSet", None], ["enter", "Name", "c"], ["leave", "SelectionSet", None], ]
def test_uses_the_subscription_schema_for_subscriptions(): from rx import Observable doc = 'query Q { a } subscription S { a }' class Data(object): a = 'b' c = 'd' ast = parse(doc) Q = GraphQLObjectType('Q', {'a': GraphQLField(GraphQLString)}) S = GraphQLObjectType( 'S', { 'a': GraphQLField(GraphQLString, resolver=lambda root, info: Observable.from_(['b'])) }) result = execute(GraphQLSchema(Q, subscription=S), ast, Data(), operation_name='S', allow_subscriptions=True) assert isinstance(result, Observable) l = [] result.subscribe(l.append) result = l[0] assert not result.errors assert result.data == {'a': 'b'}
def test_correctly_threads_arguments(): doc = ''' query Example { b(numArg: 123, stringArg: "foo") } ''' def resolver(_, args, *_args): assert args['numArg'] == 123 assert args['stringArg'] == 'foo' resolver.got_here = True resolver.got_here = False doc_ast = parse(doc) Type = GraphQLObjectType( 'Type', { 'b': GraphQLField(GraphQLString, args={ 'numArg': GraphQLArgument(GraphQLInt), 'stringArg': GraphQLArgument(GraphQLString), }, resolver=resolver), }) result = execute(GraphQLSchema(Type), doc_ast, None, operation_name='Example') assert not result.errors assert resolver.got_here
def test_nulls_out_error_subtrees(): doc = '''{ ok, error }''' class Data(object): def ok(self): return 'ok' def error(self): raise Exception('Error getting error') doc_ast = parse(doc) Type = GraphQLObjectType('Type', { 'ok': GraphQLField(GraphQLString), 'error': GraphQLField(GraphQLString), }) result = execute(GraphQLSchema(Type), doc_ast, Data()) assert result.data == {'ok': 'ok', 'error': None} assert len(result.errors) == 1 assert result.errors[0].message == 'Error getting error'
def test_fails_when_an_is_type_of_check_is_not_met(): class Special(object): def __init__(self, value): self.value = value class NotSpecial(object): def __init__(self, value): self.value = value SpecialType = GraphQLObjectType( 'SpecialType', fields={ 'value': GraphQLField(GraphQLString), }, is_type_of=lambda obj, context, info: isinstance(obj, Special)) schema = GraphQLSchema( GraphQLObjectType(name='Query', fields={ 'specials': GraphQLField( GraphQLList(SpecialType), resolver=lambda root, *_: root['specials']) })) query = parse('{ specials { value } }') value = {'specials': [Special('foo'), NotSpecial('bar')]} result = execute(schema, query, value) assert result.data == {'specials': [{'value': 'foo'}, None]} assert 'Expected value of type "SpecialType" but got: NotSpecial.' in [ str(e) for e in result.errors ]
def test_allows_early_exit_while_visiting(): # type: () -> None visited = [] ast = parse("{ a, b { x }, c }") class TestVisitor(Visitor): def enter(self, node, *args): # type: (Any, *Any) -> Optional[Any] visited.append(["enter", type(node).__name__, getattr(node, "value", None)]) if isinstance(node, Name) and node.value == "x": return BREAK def leave(self, node, *args): # type: (Union[Field, Name], *Any) -> None visited.append(["leave", type(node).__name__, getattr(node, "value", None)]) visit(ast, TestVisitor()) assert visited == [ ["enter", "Document", None], ["enter", "OperationDefinition", None], ["enter", "SelectionSet", None], ["enter", "Field", None], ["enter", "Name", "a"], ["leave", "Name", "a"], ["leave", "Field", None], ["enter", "Field", None], ["enter", "Name", "b"], ["leave", "Name", "b"], ["enter", "SelectionSet", None], ["enter", "Field", None], ["enter", "Name", "x"], ]
def test_middleware_class(): doc = '''{ ok not_ok }''' class Data(object): def ok(self): return 'ok' def not_ok(self): return 'not_ok' doc_ast = parse(doc) Type = GraphQLObjectType('Type', { 'ok': GraphQLField(GraphQLString), 'not_ok': GraphQLField(GraphQLString), }) class MyMiddleware(object): def resolve(self, next, *args, **kwargs): p = next(*args, **kwargs) return p.then(lambda x: x[::-1]) middlewares = MiddlewareManager(MyMiddleware()) result = execute(GraphQLSchema(Type), doc_ast, Data(), middleware=middlewares) assert result.data == {'ok': 'ko', 'not_ok': 'ko_ton'}
def test_asyncio_py35_executor_return_promise(): ast = parse('query Example { a, b, c }') async def resolver(context, *_): await asyncio.sleep(0.001) return 'hey' async def resolver_2(context, *_): await asyncio.sleep(0.003) return 'hey2' def resolver_3(context, *_): return 'hey3' Type = GraphQLObjectType('Type', { 'a': GraphQLField(GraphQLString, resolver=resolver), 'b': GraphQLField(GraphQLString, resolver=resolver_2), 'c': GraphQLField(GraphQLString, resolver=resolver_3) }) loop = asyncio.get_event_loop() async def do_exec(): result = await execute(GraphQLSchema(Type), ast, executor=AsyncioExecutor(loop), return_promise=True) assert not result.errors assert result.data == {'a': 'hey', 'b': 'hey2', 'c': 'hey3'} loop.run_until_complete(do_exec())
def test_threads_root_value_context_correctly(): # type: () -> None doc = "query Example { a }" class Data(object): context_thing = "thing" ast = parse(doc) def resolver(root_value, *_): # type: (Data, *ResolveInfo) -> None assert root_value.context_thing == "thing" resolver.got_here = True resolver.got_here = False Type = GraphQLObjectType( "Type", {"a": GraphQLField(GraphQLString, resolver=resolver)}) result = execute(GraphQLSchema(Type), ast, Data(), operation_name="Example") assert not result.errors assert resolver.got_here
def test_reraise(): # type: () -> None ast = parse("query Example { a }") def resolver(context, *_): # type: (Optional[Any], *ResolveInfo) -> None raise Exception("Failed") Type = GraphQLObjectType( "Type", {"a": GraphQLField(GraphQLString, resolver=resolver)}) result = execute(GraphQLSchema(Type), ast) with pytest.raises(Exception) as exc_info: result.errors[0].reraise() extracted = traceback.extract_tb(exc_info.tb) formatted_tb = [row[2:] for row in extracted] formatted_tb = [tb for tb in formatted_tb if tb[0] != "reraise"] assert formatted_tb == [ ("test_reraise", "result.errors[0].reraise()"), ( "resolve_or_error", "return executor.execute(resolve_fn, source, info, **args)", ), ("execute", "return fn(*args, **kwargs)"), ("resolver", 'raise Exception("Failed")'), ] assert str(exc_info.value) == "Failed"
def test_correctly_threads_arguments(): # type: () -> None doc = """ query Example { b(numArg: 123, stringArg: "foo") } """ def resolver(source, info, numArg, stringArg): # type: (Optional[Any], ResolveInfo, int, str) -> None assert numArg == 123 assert stringArg == "foo" resolver.got_here = True resolver.got_here = False doc_ast = parse(doc) Type = GraphQLObjectType( "Type", { "b": GraphQLField( GraphQLString, args={ "numArg": GraphQLArgument(GraphQLInt), "stringArg": GraphQLArgument(GraphQLString), }, resolver=resolver, ) }, ) result = execute(GraphQLSchema(Type), doc_ast, None, operation_name="Example") assert not result.errors assert resolver.got_here
def assert_evaluate_mutations_serially(executor=None): doc = '''mutation M { first: immediatelyChangeTheNumber(newNumber: 1) { theNumber }, second: promiseToChangeTheNumber(newNumber: 2) { theNumber }, third: immediatelyChangeTheNumber(newNumber: 3) { theNumber } fourth: promiseToChangeTheNumber(newNumber: 4) { theNumber }, fifth: immediatelyChangeTheNumber(newNumber: 5) { theNumber } }''' ast = parse(doc) result = execute(schema, ast, Root(6), operation_name='M', executor=executor) assert not result.errors assert result.data == \ { 'first': {'theNumber': 1}, 'second': {'theNumber': 2}, 'third': {'theNumber': 3}, 'fourth': {'theNumber': 4}, 'fifth': {'theNumber': 5}, }
def test_uses_the_subscription_schema_for_subscriptions(): # type: () -> None from rx import Observable doc = "query Q { a } subscription S { a }" class Data(object): a = "b" c = "d" ast = parse(doc) Q = GraphQLObjectType("Q", {"a": GraphQLField(GraphQLString)}) S = GraphQLObjectType( "S", { "a": GraphQLField( GraphQLString, resolver=lambda root, info: Observable.from_(["b"]) ) }, ) result = execute( GraphQLSchema(Q, subscription=S), ast, Data(), operation_name="S", allow_subscriptions=True, ) assert isinstance(result, Observable) l = [] result.subscribe(l.append) result = l[0] assert not result.errors assert result.data == {"a": "b"}
def test_asyncio_py35_executor_with_error(): ast = parse("query Example { a, b }") async def resolver(context, *_): await asyncio.sleep(0.001) return "hey" async def resolver_2(context, *_): await asyncio.sleep(0.003) raise Exception("resolver_2 failed!") Type = GraphQLObjectType( "Type", { "a": GraphQLField(GraphQLString, resolver=resolver), "b": GraphQLField(GraphQLString, resolver=resolver_2), }, ) result = execute(GraphQLSchema(Type), ast, executor=AsyncioExecutor()) formatted_errors = list(map(format_error, result.errors)) assert formatted_errors == [ { "locations": [{"line": 1, "column": 20}], "path": ["b"], "message": "resolver_2 failed!", } ] assert result.data == {"a": "hey", "b": None}
def test_gevent_executor(): def resolver(context, *_): gevent.sleep(0.001) return "hey" def resolver_2(context, *_): gevent.sleep(0.003) return "hey2" def resolver_3(contest, *_): return "hey3" Type = GraphQLObjectType( "Type", { "a": GraphQLField(GraphQLString, resolver=resolver), "b": GraphQLField(GraphQLString, resolver=resolver_2), "c": GraphQLField(GraphQLString, resolver=resolver_3), }, ) ast = parse("{ a b c }") result = execute(GraphQLSchema(Type), ast, executor=GeventExecutor()) assert not result.errors assert result.data == {"a": "hey", "b": "hey2", "c": "hey3"}
def test_does_not_include_arguments_that_were_not_set(): # type: () -> None schema = GraphQLSchema( GraphQLObjectType( "Type", { "field": GraphQLField( GraphQLString, resolver=lambda source, info, **args: args and json.dumps( args, sort_keys=True, separators=(",", ":")), args={ "a": GraphQLArgument(GraphQLBoolean), "b": GraphQLArgument(GraphQLBoolean), "c": GraphQLArgument(GraphQLBoolean), "d": GraphQLArgument(GraphQLInt), "e": GraphQLArgument(GraphQLInt), }, ) }, )) ast = parse("{ field(a: true, c: false, e: 0) }") result = execute(schema, ast) assert result.data == {"field": '{"a":true,"c":false,"e":0}'}
def test_asyncio_executor_custom_loop(): loop = asyncio.get_event_loop() def resolver(context, *_): asyncio.sleep(0.001, loop=loop) return 'hey' @asyncio.coroutine def resolver_2(context, *_): asyncio.sleep(0.003, loop=loop) return 'hey2' def resolver_3(contest, *_): return 'hey3' Type = GraphQLObjectType( 'Type', { 'a': GraphQLField(GraphQLString, resolver=resolver), 'b': GraphQLField(GraphQLString, resolver=resolver_2), 'c': GraphQLField(GraphQLString, resolver=resolver_3) }) ast = parse('{ a b c }') result = execute(GraphQLSchema(Type), ast, executor=AsyncioExecutor(loop=loop)) assert not result.errors assert result.data == {'a': 'hey', 'b': 'hey2', 'c': 'hey3'}
def test_nulls_out_error_subtrees(): # type: () -> None doc = """{ ok, error }""" class Data(object): def ok(self): # type: () -> str return "ok" def error(self): # type: () -> NoReturn raise Exception("Error getting error") doc_ast = parse(doc) Type = GraphQLObjectType( "Type", {"ok": GraphQLField(GraphQLString), "error": GraphQLField(GraphQLString)}, ) result = execute(GraphQLSchema(Type), doc_ast, Data()) assert result.data == {"ok": "ok", "error": None} assert len(result.errors) == 1 assert result.errors[0].message == "Error getting error"
def graphql_to_ir(schema, graphql_string, type_equivalence_hints=None): """Convert the given GraphQL string into compiler IR, using the given schema object. Args: schema: GraphQL schema object, created using the GraphQL library graphql_string: string containing the GraphQL to compile to compiler IR type_equivalence_hints: optional dict of GraphQL interface or type -> GraphQL union. Used as a workaround for GraphQL's lack of support for inheritance across "types" (i.e. non-interfaces), as well as a workaround for Gremlin's total lack of inheritance-awareness. The key-value pairs in the dict specify that the "key" type is equivalent to the "value" type, i.e. that the GraphQL type or interface in the key is the most-derived common supertype of every GraphQL type in the "value" GraphQL union. Recursive expansion of type equivalence hints is not performed, and only type-level correctness of this argument is enforced. See README.md for more details on everything this parameter does. ***** Be very careful with this option, as bad input here will lead to incorrect output queries being generated. ***** Returns: IrAndMetadata named tuple, containing fields: - ir_blocks: a list of IR basic block objects - input_metadata: a dict of expected input parameters (string) -> inferred GraphQL type - output_metadata: a dict of output name (string) -> OutputMetadata object - location_types: a dict of location objects -> GraphQL type objects at that location Raises flavors of GraphQLError in the following cases: - if the query is invalid GraphQL (GraphQLParsingError); - if the query doesn't match the schema (GraphQLValidationError); - if the query has more than one definition block (GraphQLValidationError); - if the query has more than one selection in the root object (GraphQLCompilationError); - if the query does not obey directive usage rules (GraphQLCompilationError); - if the query provides invalid / disallowed / wrong number of arguments for a directive (GraphQLCompilationError). In the case of implementation bugs, could also raise ValueError, TypeError, or AssertionError. """ graphql_string = _preprocess_graphql_string(graphql_string) try: ast = parse(graphql_string) except GraphQLSyntaxError as e: raise GraphQLParsingError(e) validation_errors = _validate_schema_and_ast(schema, ast) if validation_errors: raise GraphQLValidationError( u'String does not validate: {}'.format(validation_errors)) if len(ast.definitions) != 1: raise AssertionError( u'Unsupported graphql string with multiple definitions, should have ' u'been caught in validation: \n{}\n{}'.format(graphql_string, ast)) base_ast = ast.definitions[0] return _compile_root_ast_to_ir( schema, base_ast, type_equivalence_hints=type_equivalence_hints)
def test_correctly_threads_arguments(): doc = ''' query Example { b(numArg: 123, stringArg: "foo") } ''' def resolver(_, args, *_args): assert args['numArg'] == 123 assert args['stringArg'] == 'foo' resolver.got_here = True resolver.got_here = False doc_ast = parse(doc) Type = GraphQLObjectType('Type', { 'b': GraphQLField( GraphQLString, args={ 'numArg': GraphQLArgument(GraphQLInt), 'stringArg': GraphQLArgument(GraphQLString), }, resolver=resolver), }) result = execute(GraphQLSchema(Type), doc_ast, None, operation_name='Example') assert not result.errors assert resolver.got_here
def test_allows_editing_the_root_node_on_enter_and_on_leave(): ast = parse('{ a, b, c { a, b, c } }', no_location=True) definitions = ast.definitions class TestVisitor(Visitor): def __init__(self): self.did_enter = False self.did_leave = False def enter(self, node, *args): if isinstance(node, Document): self.did_enter = True return Document( loc=node.loc, definitions=[]) def leave(self, node, *args): if isinstance(node, Document): self.did_leave = True return Document( loc=node.loc, definitions=definitions) visitor = TestVisitor() edited_ast = visit(ast, visitor) assert edited_ast == ast assert visitor.did_enter assert visitor.did_leave
def test_asyncio_executor_custom_loop(): loop = asyncio.get_event_loop() def resolver(context, *_): asyncio.sleep(0.001, loop=loop) return 'hey' @asyncio.coroutine def resolver_2(context, *_): asyncio.sleep(0.003, loop=loop) return 'hey2' def resolver_3(contest, *_): return 'hey3' Type = GraphQLObjectType('Type', { 'a': GraphQLField(GraphQLString, resolver=resolver), 'b': GraphQLField(GraphQLString, resolver=resolver_2), 'c': GraphQLField(GraphQLString, resolver=resolver_3) }) ast = parse('{ a b c }') result = execute(GraphQLSchema(Type), ast, executor=AsyncioExecutor(loop=loop)) assert not result.errors assert result.data == {'a': 'hey', 'b': 'hey2', 'c': 'hey3'}
def test_allows_editing_a_node_both_on_enter_and_on_leave(): ast = parse('{ a, b, c { a, b, c } }', no_location=True) class TestVisitor(Visitor): def __init__(self): self.did_enter = False self.did_leave = False def enter(self, node, *args): if isinstance(node, OperationDefinition): self.did_enter = True selection_set = node.selection_set self.selections = None if selection_set: self.selections = selection_set.selections new_selection_set = SelectionSet( selections=[]) return OperationDefinition( name=node.name, variable_definitions=node.variable_definitions, directives=node.directives, loc=node.loc, operation=node.operation, selection_set=new_selection_set) def leave(self, node, *args): if isinstance(node, OperationDefinition): self.did_leave = True new_selection_set = None if self.selections: new_selection_set = SelectionSet( selections=self.selections) return OperationDefinition( name=node.name, variable_definitions=node.variable_definitions, directives=node.directives, loc=node.loc, operation=node.operation, selection_set=new_selection_set) visitor = TestVisitor() edited_ast = visit(ast, visitor) assert ast == parse('{ a, b, c { a, b, c } }', no_location=True) assert edited_ast == ast assert visitor.did_enter assert visitor.did_leave
def test_visits_in_pararell_allows_early_exit_from_different_points(): visited = [] ast = parse('{ a { y }, b { x } }') class TestVisitor(Visitor): def __init__(self, name): self.name = name def enter(self, node, key, parent, *args): visited.append(["break-{}".format(self.name), 'enter', type(node).__name__, getattr(node, 'value', None)]) def leave(self, node, key, parent, *args): visited.append(["break-{}".format(self.name), 'leave', type(node).__name__, getattr(node, 'value', None)]) if type(node).__name__ == 'Field' and node.name.value == self.name: return BREAK visit(ast, ParallelVisitor([TestVisitor('a'), TestVisitor('b')])) assert visited == [ ['break-a', 'enter', 'Document', None], ['break-b', 'enter', 'Document', None], ['break-a', 'enter', 'OperationDefinition', None], ['break-b', 'enter', 'OperationDefinition', None], ['break-a', 'enter', 'SelectionSet', None], ['break-b', 'enter', 'SelectionSet', None], ['break-a', 'enter', 'Field', None], ['break-b', 'enter', 'Field', None], ['break-a', 'enter', 'Name', 'a'], ['break-b', 'enter', 'Name', 'a'], ['break-a', 'leave', 'Name', 'a'], ['break-b', 'leave', 'Name', 'a'], ['break-a', 'enter', 'SelectionSet', None], ['break-b', 'enter', 'SelectionSet', None], ['break-a', 'enter', 'Field', None], ['break-b', 'enter', 'Field', None], ['break-a', 'enter', 'Name', 'y'], ['break-b', 'enter', 'Name', 'y'], ['break-a', 'leave', 'Name', 'y'], ['break-b', 'leave', 'Name', 'y'], ['break-a', 'leave', 'Field', None], ['break-b', 'leave', 'Field', None], ['break-a', 'leave', 'SelectionSet', None], ['break-b', 'leave', 'SelectionSet', None], ['break-a', 'leave', 'Field', None], ['break-b', 'leave', 'Field', None], ['break-b', 'enter', 'Field', None], ['break-b', 'enter', 'Name', 'b'], ['break-b', 'leave', 'Name', 'b'], ['break-b', 'enter', 'SelectionSet', None], ['break-b', 'enter', 'Field', None], ['break-b', 'enter', 'Name', 'x'], ['break-b', 'leave', 'Name', 'x'], ['break-b', 'leave', 'Field', None], ['break-b', 'leave', 'SelectionSet', None], ['break-b', 'leave', 'Field', None] ]
def graphql_to_ir(schema, graphql_string, type_equivalence_hints=None): """Convert the given GraphQL string into compiler IR, using the given schema object. Args: schema: GraphQL schema object, created using the GraphQL library graphql_string: string containing the GraphQL to compile to compiler IR type_equivalence_hints: optional dict of GraphQL interface or type -> GraphQL union. Used as a workaround for GraphQL's lack of support for inheritance across "types" (i.e. non-interfaces), as well as a workaround for Gremlin's total lack of inheritance-awareness. The key-value pairs in the dict specify that the "key" type is equivalent to the "value" type, i.e. that the GraphQL type or interface in the key is the most-derived common supertype of every GraphQL type in the "value" GraphQL union. Recursive expansion of type equivalence hints is not performed, and only type-level correctness of this argument is enforced. See README.md for more details on everything this parameter does. ***** Be very careful with this option, as bad input here will lead to incorrect output queries being generated. ***** Returns: IrAndMetadata named tuple, containing fields: - ir_blocks: a list of IR basic block objects - input_metadata: a dict of expected input parameters (string) -> inferred GraphQL type - output_metadata: a dict of output name (string) -> OutputMetadata object - location_types: a dict of location objects -> GraphQL type objects at that location Raises flavors of GraphQLError in the following cases: - if the query is invalid GraphQL (GraphQLParsingError); - if the query doesn't match the schema (GraphQLValidationError); - if the query has more than one definition block (GraphQLValidationError); - if the query has more than one selection in the root object (GraphQLCompilationError); - if the query does not obey directive usage rules (GraphQLCompilationError); - if the query provides invalid / disallowed / wrong number of arguments for a directive (GraphQLCompilationError). In the case of implementation bugs, could also raise ValueError, TypeError, or AssertionError. """ graphql_string = _preprocess_graphql_string(graphql_string) try: ast = parse(graphql_string) except GraphQLSyntaxError as e: raise GraphQLParsingError(e) validation_errors = _validate_schema_and_ast(schema, ast) if validation_errors: raise GraphQLValidationError(u'String does not validate: {}'.format(validation_errors)) if len(ast.definitions) != 1: raise AssertionError(u'Unsupported graphql string with multiple definitions, should have ' u'been caught in validation: \n{}\n{}'.format(graphql_string, ast)) base_ast = ast.definitions[0] return _compile_root_ast_to_ir(schema, base_ast, type_equivalence_hints=type_equivalence_hints)
def test_supports_the_type_root_field(): TestType = GraphQLObjectType('TestType', { 'testField': GraphQLField(GraphQLString) }) schema = GraphQLSchema(TestType) request = '{ __type(name: "TestType") { name } }' result = execute(schema, parse(request), object()) assert not result.errors assert result.data == {'__type': {'name': 'TestType'}}
def test_parse_creates_ast(): source = Source("""{ node(id: 4) { id, name } } """) result = parse(source) assert result == \ ast.Document( loc=Loc(start=0, end=41, source=source), definitions=[ast.OperationDefinition( loc=Loc(start=0, end=40, source=source), operation='query', name=None, variable_definitions=None, directives=[], selection_set=ast.SelectionSet( loc=Loc(start=0, end=40, source=source), selections=[ast.Field( loc=Loc(start=4, end=38, source=source), alias=None, name=ast.Name( loc=Loc(start=4, end=8, source=source), value='node'), arguments=[ast.Argument( name=ast.Name(loc=Loc(start=9, end=11, source=source), value='id'), value=ast.IntValue( loc=Loc(start=13, end=14, source=source), value='4'), loc=Loc(start=9, end=14, source=source))], directives=[], selection_set=ast.SelectionSet( loc=Loc(start=16, end=38, source=source), selections=[ast.Field( loc=Loc(start=22, end=24, source=source), alias=None, name=ast.Name( loc=Loc(start=22, end=24, source=source), value='id'), arguments=[], directives=[], selection_set=None), ast.Field( loc=Loc(start=30, end=34, source=source), alias=None, name=ast.Name( loc=Loc(start=30, end=34, source=source), value='name'), arguments=[], directives=[], selection_set=None)]))]))])
def test_raise(): ast = parse('query Example { a }') def resolver(context, *_): raise Exception('Failed') Type = GraphQLObjectType('Type', { 'a': GraphQLField(GraphQLString, resolver=resolver), }) result = execute(GraphQLSchema(Type), ast) assert str(result.errors[0]) == 'Failed'
def test_does_not_include_illegal_fields_in_output(): doc = 'mutation M { thisIsIllegalDontIncludeMe }' ast = parse(doc) Q = GraphQLObjectType('Q', { 'a': GraphQLField(GraphQLString) }) M = GraphQLObjectType('M', { 'c': GraphQLField(GraphQLString) }) result = execute(GraphQLSchema(Q, M), ast) assert not result.errors assert result.data == {}
def expect_invalid(schema, rules, query, expected_errors, sort_list=True): errors = validate(schema, parse(query), rules) assert errors, 'Should not validate' for error in expected_errors: error['locations'] = [ {'line': loc.line, 'column': loc.column} for loc in error['locations'] ] if sort_list: assert sort_lists(list(map(format_error, errors))) == sort_lists(expected_errors) else: assert list(map(format_error, errors)) == expected_errors
def test_raises_if_unknown_operation_name_is_provided(): doc = 'query Example { a } query OtherExample { a }' class Data(object): a = 'b' ast = parse(doc) Type = GraphQLObjectType('Type', { 'a': GraphQLField(GraphQLString) }) with raises(GraphQLError) as excinfo: execute(GraphQLSchema(Type), ast, Data()) assert 'Must provide operation name if query contains multiple operations.' == str(excinfo.value)
def test_raises_if_no_operation_name_is_provided_with_multiple_operations(): doc = 'query Example { a } query OtherExample { a }' class Data(object): a = 'b' ast = parse(doc) Type = GraphQLObjectType('Type', { 'a': GraphQLField(GraphQLString) }) with raises(GraphQLError) as excinfo: execute(GraphQLSchema(Type), ast, Data(), operation_name="UnknownExample") assert 'Unknown operation named "UnknownExample".' == str(excinfo.value)
def test_raises_if_no_operation_is_provided(): doc = 'fragment Example on Type { a }' class Data(object): a = 'b' ast = parse(doc) Type = GraphQLObjectType('Type', { 'a': GraphQLField(GraphQLString) }) with raises(GraphQLError) as excinfo: execute(GraphQLSchema(Type), ast, Data()) assert 'Must provide an operation.' == str(excinfo.value)
def test_uses_the_named_operation_if_operation_name_is_provided(): doc = 'query Example { first: a } query OtherExample { second: a }' class Data(object): a = 'b' ast = parse(doc) Type = GraphQLObjectType('Type', { 'a': GraphQLField(GraphQLString) }) result = execute(GraphQLSchema(Type), ast, Data(), operation_name='OtherExample') assert not result.errors assert result.data == {'second': 'b'}
def test_uses_the_only_operation_if_no_operation_name_is_provided(): doc = 'query Example { a }' class Data(object): a = 'b' ast = parse(doc) Type = GraphQLObjectType('Type', { 'a': GraphQLField(GraphQLString) }) result = execute(GraphQLSchema(Type), ast, Data()) assert not result.errors assert result.data == {'a': 'b'}