Example #1
0
def _get_minimal_query_ast_from_macro_ast(macro_ast):
    """Get a query that should successfully compile to IR if the macro is valid."""
    ast_without_macro_directives = remove_directives_from_ast(macro_ast, {
        directive.name
        for directive in DIRECTIVES_REQUIRED_IN_MACRO_EDGE_DEFINITION
    })

    # We will add this output directive to make the ast a valid query
    output_directive = Directive(Name('output'), arguments=[
        Argument(Name('out_name'), StringValue('dummy_output_name'))
    ])

    # Shallow copy everything on the path to the first level selection list
    query_ast = copy(ast_without_macro_directives)
    root_level_selection = copy(get_only_selection_from_ast(query_ast, GraphQLInvalidMacroError))
    first_level_selections = copy(root_level_selection.selection_set.selections)

    # Add an output to a new or existing __typename field
    existing_typename_field = None
    for idx, selection in enumerate(first_level_selections):
        if isinstance(selection, Field):
            if selection.name.value == '__typename':
                # We have a copy of the list, but the elements are references to objects
                # in macro_ast that we don't want to mutate. So the following copy is necessary.
                existing_typename_field = copy(selection)
                existing_typename_field.directives = copy(existing_typename_field.directives)
                existing_typename_field.directives.append(output_directive)
                first_level_selections[idx] = existing_typename_field
    if existing_typename_field is None:
        first_level_selections.insert(0, Field(Name('__typename'), directives=[output_directive]))

    # Propagate the changes back to the result_ast
    root_level_selection.selection_set = SelectionSet(first_level_selections)
    query_ast.selection_set = SelectionSet([root_level_selection])
    return Document([query_ast])
def separate_operations(document_ast):
    """Separate operations in a given AST document.
    This function accepts a single AST document which may contain many operations and
    fragments and returns a collection of AST documents each of which contains a single
    operation as well the fragment definitions it refers to.
    """

    # Populate metadata and build a dependency graph.
    visitor = SeparateOperations()
    visit(document_ast, visitor)
    operations = visitor.operations
    fragments = visitor.fragments
    positions = visitor.positions
    dep_graph = visitor.dep_graph

    # For each operation, produce a new synthesized AST which includes only what is
    # necessary for completing that operation.
    separated_document_asts = {}
    for operation in operations:
        operation_name = op_name(operation)
        dependencies = set()
        collect_transitive_dependencies(dependencies, dep_graph,
                                        operation_name)

        # The list of definition nodes to be included for this operation, sorted to
        # retain the same order as the original document.
        definitions = [operation]
        for name in dependencies:
            definitions.append(fragments[name])
            definitions.sort(key=lambda n: positions.get(n, 0))

        separated_document_asts[operation_name] = Document(
            definitions=definitions)

    return separated_document_asts
Example #3
0
def _make_query_plan_recursive(sub_query_node, sub_query_plan,
                               output_join_descriptors):
    """Recursively copy the structure of sub_query_node onto sub_query_plan.

    For each child connection contained in sub_query_node, create a new SubQueryPlan for
    the corresponding child SubQueryNode, add appropriate @filter directive to the child AST,
    and attach the new SubQueryPlan to the list of children of the input sub-query plan.

    Args:
        sub_query_node: SubQueryNode, whose descendents are copied over onto sub_query_plan.
                        It is not modified by this function
        sub_query_plan: SubQueryPlan, whose list of child query plans and query AST are
                        modified
        output_join_descriptors: List[OutputJoinDescriptor], describing which outputs should be
                                 joined and how

    """
    # Iterate through child connections of query node
    for child_query_connection in sub_query_node.child_query_connections:
        child_sub_query_node = child_query_connection.sink_query_node
        parent_out_name = child_query_connection.source_field_out_name
        child_out_name = child_query_connection.sink_field_out_name

        child_query_type = get_only_query_definition(
            child_sub_query_node.query_ast, GraphQLValidationError)
        child_query_type_with_filter = _add_filter_at_field_with_output(
            child_query_type, child_out_name, parent_out_name
            # @filter's local variable is named the same as the out_name of the parent's @output
        )
        if child_query_type is child_query_type_with_filter:
            raise AssertionError(
                u'An @output directive with out_name "{}" is unexpectedly not found in the '
                u'AST "{}".'.format(child_out_name, child_query_type))
        else:
            new_child_query_ast = Document(
                definitions=[child_query_type_with_filter])

        # Create new SubQueryPlan for child
        child_sub_query_plan = SubQueryPlan(
            query_ast=new_child_query_ast,
            schema_id=child_sub_query_node.schema_id,
            parent_query_plan=sub_query_plan,
            child_query_plans=[],
        )

        # Add new SubQueryPlan to parent's child list
        sub_query_plan.child_query_plans.append(child_sub_query_plan)

        # Add information about this edge
        new_output_join_descriptor = OutputJoinDescriptor(
            output_names=(parent_out_name, child_out_name), )
        output_join_descriptors.append(new_output_join_descriptor)

        # Recursively repeat on child SubQueryPlans
        _make_query_plan_recursive(child_sub_query_node, child_sub_query_plan,
                                   output_join_descriptors)
Example #4
0
def _get_query_document(root_vertex_field_name, root_selections):
    """Return a Document representing a query with the specified name and selections."""
    return Document(definitions=[
        OperationDefinition(operation='query',
                            selection_set=SelectionSet(selections=[
                                Field(
                                    name=Name(value=root_vertex_field_name),
                                    selection_set=SelectionSet(
                                        selections=root_selections, ),
                                    directives=[],
                                )
                            ]))
    ])
 def leave(self, node, *args):
     if isinstance(node, Document):
         self.did_leave = True
         return Document(loc=node.loc, definitions=definitions)
 def enter(self, node, *args):
     if isinstance(node, Document):
         self.did_enter = True
         return Document(loc=node.loc, definitions=[])