def test_validates_using_a_custom_type_info(): type_info = TypeInfo(test_schema, lambda *_: None) ast = parse(""" query { catOrDog { ... on Cat { furColor } ... on Dog { isHousetrained } } } """) errors = visit_using_rules(test_schema, type_info, ast, specified_rules) assert len(errors) == 3 assert ( errors[0].message == 'Cannot query field "catOrDog" on type "QueryRoot". Did you mean "catOrDog"?' ) assert ( errors[1].message == 'Cannot query field "furColor" on type "Cat". Did you mean "furColor"?' ) assert ( errors[2].message == 'Cannot query field "isHousetrained" on type "Dog". Did you mean "isHousetrained"?' )
def _split_query_one_level(query_node, merged_schema_descriptor, edge_to_stitch_fields, name_assigner): """Split the query node, creating children out of all branches across cross schema edges. The input query_node will be modified. Its query_ast will be replaced by a new AST with branches leading out of cross schema edges removed, and new property fields and @output directives added as necessary. Its child_query_connections will be modified by tacking on SubQueryNodes created from these cut-off branches. Args: query_node: SubQueryNode that we're splitting into its child components. Its query_ast will be replaced (but the original AST will not be modified) and its child_query_connections will be modified merged_schema_descriptor: MergedSchemaDescriptor, the schema that the query AST contained in the input query_node targets edge_to_stitch_fields: Dict[Tuple(str, str), Tuple(str, str)], mapping (type name, vertex field name) to (source field name, sink field name) used in the @stitch directive for each cross schema edge name_assigner: IntermediateOutNameAssigner, object used to generate and keep track of names of newly created @output directive Raises: - GraphQLValidationError if the query AST contained in the input query_node is invalid, for example, having an @output directive on a cross schema edge - SchemaStructureError if the merged_schema_descriptor provided appears to be invalid or inconsistent """ type_info = TypeInfo(merged_schema_descriptor.schema) operation_definition = get_only_query_definition(query_node.query_ast, GraphQLValidationError) type_info.enter(operation_definition) new_operation_definition = _split_query_ast_one_level_recursive( query_node, operation_definition, type_info, edge_to_stitch_fields, name_assigner) type_info.leave(operation_definition) if new_operation_definition is not operation_definition: new_query_ast = copy(query_node.query_ast) new_query_ast.definitions = [new_operation_definition] query_node.query_ast = new_query_ast # Check resulting AST is valid validation_errors = validate(merged_schema_descriptor.schema, query_node.query_ast) if len(validation_errors) > 0: raise AssertionError( u'The resulting split query "{}" is invalid, with the following error messages: {}' u''.format(query_node.query_ast, validation_errors)) # Set schema id, check for consistency visitor = TypeInfoVisitor( type_info, SchemaIdSetterVisitor(type_info, query_node, merged_schema_descriptor.type_name_to_schema_id)) visit(query_node.query_ast, visitor) if query_node.schema_id is None: raise AssertionError( u'Unreachable code reached. The schema id of query piece "{}" has not been ' u'determined.'.format(query_node.query_ast))
def test_visits_with_typeinfo_maintains_type_info_during_edit(): visited = [] ast = parse('{ human(id: 4) { name, pets }, alien }') type_info = TypeInfo(test_schema) class TestVisitor(Visitor): def enter(self, node, key, parent, *args): parent_type = type_info.get_parent_type() _type = type_info.get_type() input_type = type_info.get_input_type() visited.append([ 'enter', type(node).__name__, node.value if type(node).__name__ == "Name" else None, str(parent_type) if parent_type else None, str(_type) if _type else None, str(input_type) if input_type else None ]) # Make a query valid by adding missing selection sets. if type( node ).__name__ == "Field" and not node.selection_set and is_composite_type( get_named_type(_type)): return Field(alias=node.alias, name=node.name, arguments=node.arguments, directives=node.directives, selection_set=SelectionSet( [Field(name=Name(value='__typename'))])) def leave(self, node, key, parent, *args): parent_type = type_info.get_parent_type() _type = type_info.get_type() input_type = type_info.get_input_type() visited.append([ 'leave', type(node).__name__, node.value if type(node).__name__ == "Name" else None, str(parent_type) if parent_type else None, str(_type) if _type else None, str(input_type) if input_type else None ]) edited_ast = visit(ast, TypeInfoVisitor(type_info, TestVisitor())) # assert print_ast(ast) == print_ast(parse( # '{ human(id: 4) { name, pets }, alien }' # )) assert print_ast(edited_ast) == print_ast( parse( '{ human(id: 4) { name, pets { __typename } }, alien { __typename } }' )) assert visited == [ ['enter', 'Document', None, None, None, None], ['enter', 'OperationDefinition', None, None, 'QueryRoot', None], ['enter', 'SelectionSet', None, 'QueryRoot', 'QueryRoot', None], ['enter', 'Field', None, 'QueryRoot', 'Human', None], ['enter', 'Name', 'human', 'QueryRoot', 'Human', None], ['leave', 'Name', 'human', 'QueryRoot', 'Human', None], ['enter', 'Argument', None, 'QueryRoot', 'Human', 'ID'], ['enter', 'Name', 'id', 'QueryRoot', 'Human', 'ID'], ['leave', 'Name', 'id', 'QueryRoot', 'Human', 'ID'], ['enter', 'IntValue', None, 'QueryRoot', 'Human', 'ID'], ['leave', 'IntValue', None, 'QueryRoot', 'Human', 'ID'], ['leave', 'Argument', None, 'QueryRoot', 'Human', 'ID'], ['enter', 'SelectionSet', None, 'Human', 'Human', None], ['enter', 'Field', None, 'Human', 'String', None], ['enter', 'Name', 'name', 'Human', 'String', None], ['leave', 'Name', 'name', 'Human', 'String', None], ['leave', 'Field', None, 'Human', 'String', None], ['enter', 'Field', None, 'Human', '[Pet]', None], ['enter', 'Name', 'pets', 'Human', '[Pet]', None], ['leave', 'Name', 'pets', 'Human', '[Pet]', None], ['enter', 'SelectionSet', None, 'Pet', '[Pet]', None], ['enter', 'Field', None, 'Pet', 'String!', None], ['enter', 'Name', '__typename', 'Pet', 'String!', None], ['leave', 'Name', '__typename', 'Pet', 'String!', None], ['leave', 'Field', None, 'Pet', 'String!', None], ['leave', 'SelectionSet', None, 'Pet', '[Pet]', None], ['leave', 'Field', None, 'Human', '[Pet]', None], ['leave', 'SelectionSet', None, 'Human', 'Human', None], ['leave', 'Field', None, 'QueryRoot', 'Human', None], ['enter', 'Field', None, 'QueryRoot', 'Alien', None], ['enter', 'Name', 'alien', 'QueryRoot', 'Alien', None], ['leave', 'Name', 'alien', 'QueryRoot', 'Alien', None], ['enter', 'SelectionSet', None, 'Alien', 'Alien', None], ['enter', 'Field', None, 'Alien', 'String!', None], ['enter', 'Name', '__typename', 'Alien', 'String!', None], ['leave', 'Name', '__typename', 'Alien', 'String!', None], ['leave', 'Field', None, 'Alien', 'String!', None], ['leave', 'SelectionSet', None, 'Alien', 'Alien', None], ['leave', 'Field', None, 'QueryRoot', 'Alien', None], ['leave', 'SelectionSet', None, 'QueryRoot', 'QueryRoot', None], ['leave', 'OperationDefinition', None, None, 'QueryRoot', None], ['leave', 'Document', None, None, None, None] ]
def test_visits_with_typeinfo_maintains_type_info_during_visit(): visited = [] ast = parse('{ human(id: 4) { name, pets { name }, unknown } }') type_info = TypeInfo(test_schema) class TestVisitor(Visitor): def enter(self, node, key, parent, *args): parent_type = type_info.get_parent_type() _type = type_info.get_type() input_type = type_info.get_input_type() visited.append([ 'enter', type(node).__name__, node.value if type(node).__name__ == "Name" else None, str(parent_type) if parent_type else None, str(_type) if _type else None, str(input_type) if input_type else None ]) def leave(self, node, key, parent, *args): parent_type = type_info.get_parent_type() _type = type_info.get_type() input_type = type_info.get_input_type() visited.append([ 'leave', type(node).__name__, node.value if type(node).__name__ == "Name" else None, str(parent_type) if parent_type else None, str(_type) if _type else None, str(input_type) if input_type else None ]) visit(ast, TypeInfoVisitor(type_info, TestVisitor())) assert visited == [ ['enter', 'Document', None, None, None, None], ['enter', 'OperationDefinition', None, None, 'QueryRoot', None], ['enter', 'SelectionSet', None, 'QueryRoot', 'QueryRoot', None], ['enter', 'Field', None, 'QueryRoot', 'Human', None], ['enter', 'Name', 'human', 'QueryRoot', 'Human', None], ['leave', 'Name', 'human', 'QueryRoot', 'Human', None], ['enter', 'Argument', None, 'QueryRoot', 'Human', 'ID'], ['enter', 'Name', 'id', 'QueryRoot', 'Human', 'ID'], ['leave', 'Name', 'id', 'QueryRoot', 'Human', 'ID'], ['enter', 'IntValue', None, 'QueryRoot', 'Human', 'ID'], ['leave', 'IntValue', None, 'QueryRoot', 'Human', 'ID'], ['leave', 'Argument', None, 'QueryRoot', 'Human', 'ID'], ['enter', 'SelectionSet', None, 'Human', 'Human', None], ['enter', 'Field', None, 'Human', 'String', None], ['enter', 'Name', 'name', 'Human', 'String', None], ['leave', 'Name', 'name', 'Human', 'String', None], ['leave', 'Field', None, 'Human', 'String', None], ['enter', 'Field', None, 'Human', '[Pet]', None], ['enter', 'Name', 'pets', 'Human', '[Pet]', None], ['leave', 'Name', 'pets', 'Human', '[Pet]', None], ['enter', 'SelectionSet', None, 'Pet', '[Pet]', None], ['enter', 'Field', None, 'Pet', 'String', None], ['enter', 'Name', 'name', 'Pet', 'String', None], ['leave', 'Name', 'name', 'Pet', 'String', None], ['leave', 'Field', None, 'Pet', 'String', None], ['leave', 'SelectionSet', None, 'Pet', '[Pet]', None], ['leave', 'Field', None, 'Human', '[Pet]', None], ['enter', 'Field', None, 'Human', None, None], ['enter', 'Name', 'unknown', 'Human', None, None], ['leave', 'Name', 'unknown', 'Human', None, None], ['leave', 'Field', None, 'Human', None, None], ['leave', 'SelectionSet', None, 'Human', 'Human', None], ['leave', 'Field', None, 'QueryRoot', 'Human', None], ['leave', 'SelectionSet', None, 'QueryRoot', 'QueryRoot', None], ['leave', 'OperationDefinition', None, None, 'QueryRoot', None], ['leave', 'Document', None, None, None, None] ]
def test_visits_with_typeinfo_maintains_type_info_during_edit(): # type: () -> None visited = [] ast = parse("{ human(id: 4) { name, pets }, alien }") type_info = TypeInfo(test_schema) class TestVisitor(Visitor): def enter(self, node, key, parent, *args): # type: (Any, Union[None, int, str], Any, *List[Any]) -> Optional[Any] parent_type = type_info.get_parent_type() _type = type_info.get_type() input_type = type_info.get_input_type() visited.append( [ "enter", type(node).__name__, node.value if type(node).__name__ == "Name" else None, str(parent_type) if parent_type else None, str(_type) if _type else None, str(input_type) if input_type else None, ] ) # Make a query valid by adding missing selection sets. if ( type(node).__name__ == "Field" and not node.selection_set and is_composite_type(get_named_type(_type)) ): return Field( alias=node.alias, name=node.name, arguments=node.arguments, directives=node.directives, selection_set=SelectionSet([Field(name=Name(value="__typename"))]), ) def leave( self, node, # type: Union[Argument, IntValue, Name] key, # type: Union[int, str] parent, # type: Union[List[Argument], Argument, Field] *args # type: List[Any] ): # type: (...) -> None parent_type = type_info.get_parent_type() _type = type_info.get_type() input_type = type_info.get_input_type() visited.append( [ "leave", type(node).__name__, node.value if type(node).__name__ == "Name" else None, str(parent_type) if parent_type else None, str(_type) if _type else None, str(input_type) if input_type else None, ] ) edited_ast = visit(ast, TypeInfoVisitor(type_info, TestVisitor())) # assert print_ast(ast) == print_ast(parse( # '{ human(id: 4) { name, pets }, alien }' # )) assert print_ast(edited_ast) == print_ast( parse("{ human(id: 4) { name, pets { __typename } }, alien { __typename } }") ) assert visited == [ ["enter", "Document", None, None, None, None], ["enter", "OperationDefinition", None, None, "QueryRoot", None], ["enter", "SelectionSet", None, "QueryRoot", "QueryRoot", None], ["enter", "Field", None, "QueryRoot", "Human", None], ["enter", "Name", "human", "QueryRoot", "Human", None], ["leave", "Name", "human", "QueryRoot", "Human", None], ["enter", "Argument", None, "QueryRoot", "Human", "ID"], ["enter", "Name", "id", "QueryRoot", "Human", "ID"], ["leave", "Name", "id", "QueryRoot", "Human", "ID"], ["enter", "IntValue", None, "QueryRoot", "Human", "ID"], ["leave", "IntValue", None, "QueryRoot", "Human", "ID"], ["leave", "Argument", None, "QueryRoot", "Human", "ID"], ["enter", "SelectionSet", None, "Human", "Human", None], ["enter", "Field", None, "Human", "String", None], ["enter", "Name", "name", "Human", "String", None], ["leave", "Name", "name", "Human", "String", None], ["leave", "Field", None, "Human", "String", None], ["enter", "Field", None, "Human", "[Pet]", None], ["enter", "Name", "pets", "Human", "[Pet]", None], ["leave", "Name", "pets", "Human", "[Pet]", None], ["enter", "SelectionSet", None, "Pet", "[Pet]", None], ["enter", "Field", None, "Pet", "String!", None], ["enter", "Name", "__typename", "Pet", "String!", None], ["leave", "Name", "__typename", "Pet", "String!", None], ["leave", "Field", None, "Pet", "String!", None], ["leave", "SelectionSet", None, "Pet", "[Pet]", None], ["leave", "Field", None, "Human", "[Pet]", None], ["leave", "SelectionSet", None, "Human", "Human", None], ["leave", "Field", None, "QueryRoot", "Human", None], ["enter", "Field", None, "QueryRoot", "Alien", None], ["enter", "Name", "alien", "QueryRoot", "Alien", None], ["leave", "Name", "alien", "QueryRoot", "Alien", None], ["enter", "SelectionSet", None, "Alien", "Alien", None], ["enter", "Field", None, "Alien", "String!", None], ["enter", "Name", "__typename", "Alien", "String!", None], ["leave", "Name", "__typename", "Alien", "String!", None], ["leave", "Field", None, "Alien", "String!", None], ["leave", "SelectionSet", None, "Alien", "Alien", None], ["leave", "Field", None, "QueryRoot", "Alien", None], ["leave", "SelectionSet", None, "QueryRoot", "QueryRoot", None], ["leave", "OperationDefinition", None, None, "QueryRoot", None], ["leave", "Document", None, None, None, None], ]
def test_visits_with_typeinfo_maintains_type_info_during_visit(): # type: () -> None visited = [] ast = parse("{ human(id: 4) { name, pets { name }, unknown } }") type_info = TypeInfo(test_schema) class TestVisitor(Visitor): def enter(self, node, key, parent, *args): # type: (Any, Union[None, int, str], Any, *List[Any]) -> None parent_type = type_info.get_parent_type() _type = type_info.get_type() input_type = type_info.get_input_type() visited.append( [ "enter", type(node).__name__, node.value if type(node).__name__ == "Name" else None, str(parent_type) if parent_type else None, str(_type) if _type else None, str(input_type) if input_type else None, ] ) def leave( self, node, # type: Union[Argument, IntValue, Name] key, # type: Union[int, str] parent, # type: Union[List[Argument], Argument, Field] *args # type: List[Any] ): # type: (...) -> None parent_type = type_info.get_parent_type() _type = type_info.get_type() input_type = type_info.get_input_type() visited.append( [ "leave", type(node).__name__, node.value if type(node).__name__ == "Name" else None, str(parent_type) if parent_type else None, str(_type) if _type else None, str(input_type) if input_type else None, ] ) visit(ast, TypeInfoVisitor(type_info, TestVisitor())) assert visited == [ ["enter", "Document", None, None, None, None], ["enter", "OperationDefinition", None, None, "QueryRoot", None], ["enter", "SelectionSet", None, "QueryRoot", "QueryRoot", None], ["enter", "Field", None, "QueryRoot", "Human", None], ["enter", "Name", "human", "QueryRoot", "Human", None], ["leave", "Name", "human", "QueryRoot", "Human", None], ["enter", "Argument", None, "QueryRoot", "Human", "ID"], ["enter", "Name", "id", "QueryRoot", "Human", "ID"], ["leave", "Name", "id", "QueryRoot", "Human", "ID"], ["enter", "IntValue", None, "QueryRoot", "Human", "ID"], ["leave", "IntValue", None, "QueryRoot", "Human", "ID"], ["leave", "Argument", None, "QueryRoot", "Human", "ID"], ["enter", "SelectionSet", None, "Human", "Human", None], ["enter", "Field", None, "Human", "String", None], ["enter", "Name", "name", "Human", "String", None], ["leave", "Name", "name", "Human", "String", None], ["leave", "Field", None, "Human", "String", None], ["enter", "Field", None, "Human", "[Pet]", None], ["enter", "Name", "pets", "Human", "[Pet]", None], ["leave", "Name", "pets", "Human", "[Pet]", None], ["enter", "SelectionSet", None, "Pet", "[Pet]", None], ["enter", "Field", None, "Pet", "String", None], ["enter", "Name", "name", "Pet", "String", None], ["leave", "Name", "name", "Pet", "String", None], ["leave", "Field", None, "Pet", "String", None], ["leave", "SelectionSet", None, "Pet", "[Pet]", None], ["leave", "Field", None, "Human", "[Pet]", None], ["enter", "Field", None, "Human", None, None], ["enter", "Name", "unknown", "Human", None, None], ["leave", "Name", "unknown", "Human", None, None], ["leave", "Field", None, "Human", None, None], ["leave", "SelectionSet", None, "Human", "Human", None], ["leave", "Field", None, "QueryRoot", "Human", None], ["leave", "SelectionSet", None, "QueryRoot", "QueryRoot", None], ["leave", "OperationDefinition", None, None, "QueryRoot", None], ["leave", "Document", None, None, None, None], ]