def _replace_tag_names_in_tag_directive(name_change_map, tag_directive):
    """Apply tag parameter renaming to the given tag directive.

    Args:
        name_change_map: Dict[str, str] mapping tag names to new names
        tag_directive: GraphQL library tag directive whose name is in the name_change_map.
                       This ast is not mutated.

    Returns:
        GraphQL library directive object, equivalent to the input one, with its name changed
        according to the name_change_map. If no changes were made, this is the same object
        as the input tag directive.
    """
    # Schema validation has ensured this exists
    current_name = tag_directive.arguments[0].value.value
    new_name = name_change_map[current_name]

    if new_name == current_name:
        # No changes are necessary, return the original input object.
        return tag_directive

    renamed_tag_directive = copy(tag_directive)
    renamed_tag_directive.arguments = [
        ArgumentNode(name=NameNode(value="tag_name"),
                     value=StringValueNode(value=new_name))
    ]
    return renamed_tag_directive
Exemple #2
0
def _build_stitch_directive(source_field_name: str,
                            sink_field_name: str) -> DirectiveNode:
    """Build a Directive node for the stitch directive."""
    return DirectiveNode(
        name=NameNode(value="stitch"),
        arguments=[
            ArgumentNode(
                name=NameNode(value="source_field"),
                value=StringValueNode(value=source_field_name),
            ),
            ArgumentNode(
                name=NameNode(value="sink_field"),
                value=StringValueNode(value=sink_field_name),
            ),
        ],
    )
def _make_binary_filter_directive_node(op_name: str,
                                       param_name: str) -> DirectiveNode:
    """Make a binary filter directive node with the given binary op_name and parameter name."""
    return DirectiveNode(
        name=NameNode(value="filter"),
        arguments=[
            ArgumentNode(
                name=NameNode(value="op_name"),
                value=StringValueNode(value=op_name),
            ),
            ArgumentNode(
                name=NameNode(value="value"),
                value=ListValueNode(
                    values=[StringValueNode(value="$" + param_name)]),
            ),
        ],
    )
def _get_output_directive(out_name):
    """Return a Directive representing an @output with the input out_name."""
    return DirectiveNode(
        name=NameNode(value=OutputDirective.name),
        arguments=[
            ArgumentNode(name=NameNode(value=u"out_name"), value=StringValueNode(value=out_name),),
        ],
    )
def _get_in_collection_filter_directive(input_filter_name):
    """Create a @filter directive with in_collecion operation and the desired variable name."""
    return DirectiveNode(
        name=NameNode(value=FilterDirective.name),
        arguments=[
            ArgumentNode(
                name=NameNode(value="op_name"),
                value=StringValueNode(value="in_collection"),
            ),
            ArgumentNode(
                name=NameNode(value="value"),
                value=ListValueNode(values=[
                    StringValueNode(value=u"$" + input_filter_name),
                ], ),
            ),
        ],
    )
def _replace_tag_names_in_filter_directive(name_change_map, filter_directive):
    """Apply tag parameter renaming to the given filter directive.

    Args:
        name_change_map: Dict[str, str] mapping tag names to new names
        filter_directive: GraphQL library filter directive object that potentially uses
                          tagged parameters. All such tagged parameters should be in
                          the name_change_map. This directive object is not mutated,
                          and if no changes are necessary then it will be returned

    Returns:
        GraphQL library directive object, equivalent to the input one, with any tagged parameters it
        uses replaced according to the name_change_map. If no changes were made, this is the
        same object as the input filter directive.
    """
    made_changes = False

    new_arguments = []
    for argument in filter_directive.arguments:
        if argument.name.value == "op_name":
            new_arguments.append(argument)
        elif argument.name.value == "value":
            new_value_list = []
            for value in argument.value.values:
                parameter = value.value
                new_value = value

                # Rewrite tagged parameter names if necessary.
                if is_tagged_parameter(parameter):
                    current_name = get_parameter_name(parameter)
                    new_name = name_change_map[current_name]
                    if new_name != current_name:
                        made_changes = True
                        new_value = StringValueNode(value="%" + new_name)

                new_value_list.append(new_value)

            if made_changes:
                new_argument = ArgumentNode(
                    name=NameNode(value="value"),
                    value=ListValueNode(values=new_value_list))
            else:
                new_argument = argument
            new_arguments.append(new_argument)
        else:
            raise AssertionError(
                "Unknown argument name {} in filter directive {}, this should "
                "have been caught in an earlier validation step.".format(
                    argument.name.value, filter_directive))

    if not made_changes:
        # No changes were made, return the original input object.
        return filter_directive

    filter_with_renamed_args = copy(filter_directive)
    filter_with_renamed_args.arguments = new_arguments
    return filter_with_renamed_args
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, DIRECTIVES_REQUIRED_IN_MACRO_EDGE_DEFINITION)

    # We will add this output directive to make the ast a valid query
    output_directive = DirectiveNode(
        name=NameNode(value="output"),
        arguments=[
            ArgumentNode(name=NameNode(value="out_name"),
                         value=StringValueNode(value="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 = list(
        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, FieldNode):
            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,
            FieldNode(name=NameNode(value="__typename"),
                      directives=[output_directive]))

    # Propagate the changes back to the result_ast
    root_level_selection.selection_set = SelectionSetNode(
        selections=first_level_selections)
    query_ast.selection_set = SelectionSetNode(
        selections=[root_level_selection])
    return DocumentNode(definitions=[query_ast])