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 _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), ), ], )
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)
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), ], ), ), ], )