示例#1
0
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"?'
    )
示例#2
0
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))
示例#3
0
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]
    ]
示例#4
0
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]
    ]
示例#5
0
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],
    ]
示例#6
0
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],
    ]