예제 #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])
예제 #2
0
def _get_output_directive(out_name):
    """Return a Directive representing an @output with the input out_name."""
    return Directive(
        name=Name(value=OutputDirective.name),
        arguments=[
            Argument(
                name=Name(value=u'out_name'),
                value=StringValue(value=out_name),
            ),
        ],
    )
예제 #3
0
def get_schema_with_macros(macro_registry):
    """Get a new GraphQLSchema with fields where macro edges can be used.

    Preconditions:
    1. No macro in the registry has the same name as a field on the vertex where it applies.
    2. Members of a union type do not have outgoing macros with the same name.

    An easy way to satisfy the preconditions is to create the macro_registry using
    create_macro_registry, and only update it with register_macro_edge, which does all
    the necessary validation.

    Postconditions:
    1. Every GraphQLQuery that uses macros from this registry appropriately should
       successfully type-check against the schema generated from this function.
    2. A GraphQLQuery that uses macros not present in the registry, or uses valid
       macros but on types they are not defined at should fail schema validation with
       the schema generated from this function.
    3. This function is total -- A valid macro registry should not fail to create a
       GraphQL schema with macros.

    Args:
        macro_registry: MacroRegistry object containing a schema and macro descriptors
                        we want to add to the schema.

    Returns:
        GraphQLSchema with additional fields where macroe edges can be used.
    """
    # The easiest way to manipulate the schema is through its AST. The easiest
    # way to get an AST is to print it and parse it.
    schema_ast = parse(print_schema(macro_registry.schema_without_macros))

    definitions_by_name = {}
    for definition in schema_ast.definitions:
        if isinstance(definition,
                      (ObjectTypeDefinition, InterfaceTypeDefinition)):
            definitions_by_name[definition.name.value] = definition

    for class_name, macros_for_class in six.iteritems(
            macro_registry.macro_edges_at_class):
        for macro_edge_name, macro_edge_descriptor in six.iteritems(
                macros_for_class):
            list_type_at_target = ListType(
                NamedType(Name(macro_edge_descriptor.target_class_name)))
            arguments = []
            directives = [Directive(Name(MacroEdgeDirective.name))]
            definitions_by_name[class_name].fields.append(
                FieldDefinition(Name(macro_edge_name),
                                arguments,
                                list_type_at_target,
                                directives=directives))

    return build_ast_schema(schema_ast)
예제 #4
0
def _get_in_collection_filter_directive(input_filter_name):
    """Create a @filter directive with in_collecion operation and the desired variable name."""
    return Directive(
        name=Name(value=FilterDirective.name),
        arguments=[
            Argument(
                name=Name(value='op_name'),
                value=StringValue(value='in_collection'),
            ),
            Argument(
                name=Name(value='value'),
                value=ListValue(values=[
                    StringValue(value=u'$' + input_filter_name),
                ], ),
            ),
        ],
    )