graphql.schema.idl.SchemaGeneratorDirectiveHelper Maven / Gradle / Ivy
package graphql.schema.idl;
import graphql.Internal;
import graphql.language.NamedNode;
import graphql.language.NodeParentTree;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLCodeRegistry;
import graphql.schema.GraphQLDirective;
import graphql.schema.GraphQLDirectiveContainer;
import graphql.schema.GraphQLEnumType;
import graphql.schema.GraphQLEnumValueDefinition;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLFieldsContainer;
import graphql.schema.GraphQLInputObjectField;
import graphql.schema.GraphQLInputObjectType;
import graphql.schema.GraphQLInterfaceType;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLScalarType;
import graphql.schema.GraphQLSchemaElement;
import graphql.schema.GraphQLUnionType;
import graphql.schema.GraphqlElementParentTree;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import static graphql.Assert.assertNotNull;
import static java.util.stream.Collectors.toList;
/**
* This contains the helper code that allows {@link graphql.schema.idl.SchemaDirectiveWiring} implementations
* to be invoked during schema generation.
*/
@Internal
public class SchemaGeneratorDirectiveHelper {
static class Parameters {
private final TypeDefinitionRegistry typeRegistry;
private final RuntimeWiring runtimeWiring;
private final NodeParentTree nodeParentTree;
private final Map context;
private final GraphQLCodeRegistry.Builder codeRegistry;
private final GraphqlElementParentTree elementParentTree;
private final GraphQLFieldsContainer fieldsContainer;
private final GraphQLFieldDefinition fieldDefinition;
Parameters(TypeDefinitionRegistry typeRegistry, RuntimeWiring runtimeWiring, Map context, GraphQLCodeRegistry.Builder codeRegistry) {
this(typeRegistry, runtimeWiring, context, codeRegistry, null, null, null, null);
}
Parameters(TypeDefinitionRegistry typeRegistry, RuntimeWiring runtimeWiring, Map context, GraphQLCodeRegistry.Builder codeRegistry, NodeParentTree nodeParentTree, GraphqlElementParentTree elementParentTree, GraphQLFieldsContainer fieldsContainer, GraphQLFieldDefinition fieldDefinition) {
this.typeRegistry = typeRegistry;
this.runtimeWiring = runtimeWiring;
this.nodeParentTree = nodeParentTree;
this.context = context;
this.codeRegistry = codeRegistry;
this.elementParentTree = elementParentTree;
this.fieldsContainer = fieldsContainer;
this.fieldDefinition = fieldDefinition;
}
public TypeDefinitionRegistry getTypeRegistry() {
return typeRegistry;
}
public RuntimeWiring getRuntimeWiring() {
return runtimeWiring;
}
public NodeParentTree getNodeParentTree() {
return nodeParentTree;
}
public GraphqlElementParentTree getElementParentTree() {
return elementParentTree;
}
public GraphQLFieldsContainer getFieldsContainer() {
return fieldsContainer;
}
public Map getContext() {
return context;
}
public GraphQLCodeRegistry.Builder getCodeRegistry() {
return codeRegistry;
}
public GraphQLFieldDefinition getFieldsDefinition() {
return fieldDefinition;
}
public Parameters newParams(GraphQLFieldsContainer fieldsContainer, NodeParentTree nodeParentTree, GraphqlElementParentTree elementParentTree) {
return new Parameters(this.typeRegistry, this.runtimeWiring, this.context, this.codeRegistry, nodeParentTree, elementParentTree, fieldsContainer, fieldDefinition);
}
public Parameters newParams(GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer, NodeParentTree nodeParentTree, GraphqlElementParentTree elementParentTree) {
return new Parameters(this.typeRegistry, this.runtimeWiring, this.context, this.codeRegistry, nodeParentTree, elementParentTree, fieldsContainer, fieldDefinition);
}
public Parameters newParams(NodeParentTree nodeParentTree, GraphqlElementParentTree elementParentTree) {
return new Parameters(this.typeRegistry, this.runtimeWiring, this.context, this.codeRegistry, nodeParentTree, elementParentTree, this.fieldsContainer, fieldDefinition);
}
}
private NodeParentTree buildAstTree(NamedNode... nodes) {
Deque nodeStack = new ArrayDeque<>();
for (NamedNode node : nodes) {
nodeStack.push(node);
}
return new NodeParentTree<>(nodeStack);
}
private GraphqlElementParentTree buildRuntimeTree(GraphQLSchemaElement... elements) {
Deque nodeStack = new ArrayDeque<>();
for (GraphQLSchemaElement element : elements) {
nodeStack.push(element);
}
return new GraphqlElementParentTree(nodeStack);
}
private List wireArguments(GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer, NamedNode fieldsContainerNode, Parameters params, GraphQLFieldDefinition field) {
return field.getArguments().stream().map(argument -> {
NodeParentTree nodeParentTree = buildAstTree(fieldsContainerNode, field.getDefinition(), argument.getDefinition());
GraphqlElementParentTree elementParentTree = buildRuntimeTree(fieldsContainer, field, argument);
Parameters argParams = params.newParams(fieldDefinition, fieldsContainer, nodeParentTree, elementParentTree);
return onArgument(argument, argParams);
}).collect(toList());
}
private List wireFields(GraphQLFieldsContainer fieldsContainer, NamedNode fieldsContainerNode, Parameters params) {
return fieldsContainer.getFieldDefinitions().stream().map(fieldDefinition -> {
// and for each argument in the fieldDefinition run the wiring for them - and note that they can change
List newArgs = wireArguments(fieldDefinition, fieldsContainer, fieldsContainerNode, params, fieldDefinition);
// they may have changed the arguments to the fieldDefinition so reflect that
fieldDefinition = fieldDefinition.transform(builder -> builder.clearArguments().arguments(newArgs));
NodeParentTree nodeParentTree = buildAstTree(fieldsContainerNode, fieldDefinition.getDefinition());
GraphqlElementParentTree elementParentTree = buildRuntimeTree(fieldsContainer, fieldDefinition);
Parameters fieldParams = params.newParams(fieldDefinition, fieldsContainer, nodeParentTree, elementParentTree);
// now for each fieldDefinition run the new wiring and capture the results
return onField(fieldDefinition, fieldParams);
}).collect(toList());
}
public GraphQLObjectType onObject(final GraphQLObjectType objectType, Parameters params) {
List newFields = wireFields(objectType, objectType.getDefinition(), params);
GraphQLObjectType newObjectType = objectType.transform(builder -> builder.clearFields().fields(newFields));
NodeParentTree nodeParentTree = buildAstTree(newObjectType.getDefinition());
GraphqlElementParentTree elementParentTree = buildRuntimeTree(newObjectType);
Parameters newParams = params.newParams(newObjectType, nodeParentTree, elementParentTree);
return wireDirectives(params, newObjectType, newObjectType.getDirectives(),
(outputElement, directives, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement, directives, registeredDirective, newParams), SchemaDirectiveWiring::onObject);
}
public GraphQLInterfaceType onInterface(GraphQLInterfaceType interfaceType, Parameters params) {
List newFields = wireFields(interfaceType, interfaceType.getDefinition(), params);
GraphQLInterfaceType newInterfaceType = interfaceType.transform(builder -> builder.clearFields().fields(newFields));
NodeParentTree nodeParentTree = buildAstTree(newInterfaceType.getDefinition());
GraphqlElementParentTree elementParentTree = buildRuntimeTree(newInterfaceType);
Parameters newParams = params.newParams(newInterfaceType, nodeParentTree, elementParentTree);
return wireDirectives(params, newInterfaceType, newInterfaceType.getDirectives(),
(outputElement, directives, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement, directives, registeredDirective, newParams), SchemaDirectiveWiring::onInterface);
}
public GraphQLEnumType onEnum(GraphQLEnumType enumType, Parameters params) {
List newEnums = enumType.getValues().stream().map(enumValueDefinition -> {
NodeParentTree nodeParentTree = buildAstTree(enumType.getDefinition(), enumValueDefinition.getDefinition());
GraphqlElementParentTree elementParentTree = buildRuntimeTree(enumType, enumValueDefinition);
Parameters fieldParams = params.newParams(nodeParentTree, elementParentTree);
// now for each field run the new wiring and capture the results
return onEnumValue(enumValueDefinition, fieldParams);
}).collect(toList());
GraphQLEnumType newEnumType = enumType.transform(builder -> builder.clearValues().values(newEnums));
NodeParentTree nodeParentTree = buildAstTree(newEnumType.getDefinition());
GraphqlElementParentTree elementParentTree = buildRuntimeTree(newEnumType);
Parameters newParams = params.newParams(nodeParentTree, elementParentTree);
return wireDirectives(params, newEnumType, newEnumType.getDirectives(),
(outputElement, directives, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement, directives, registeredDirective, newParams), SchemaDirectiveWiring::onEnum);
}
public GraphQLInputObjectType onInputObjectType(GraphQLInputObjectType inputObjectType, Parameters params) {
List newFields = inputObjectType.getFieldDefinitions().stream().map(inputField -> {
NodeParentTree nodeParentTree = buildAstTree(inputObjectType.getDefinition(), inputField.getDefinition());
GraphqlElementParentTree elementParentTree = buildRuntimeTree(inputObjectType, inputField);
Parameters fieldParams = params.newParams(nodeParentTree, elementParentTree);
// now for each field run the new wiring and capture the results
return onInputObjectField(inputField, fieldParams);
}).collect(toList());
GraphQLInputObjectType newInputObjectType = inputObjectType.transform(builder -> builder.clearFields().fields(newFields));
NodeParentTree nodeParentTree = buildAstTree(newInputObjectType.getDefinition());
GraphqlElementParentTree elementParentTree = buildRuntimeTree(newInputObjectType);
Parameters newParams = params.newParams(nodeParentTree, elementParentTree);
return wireDirectives(params, newInputObjectType, newInputObjectType.getDirectives(),
(outputElement, directives, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement, directives, registeredDirective, newParams), SchemaDirectiveWiring::onInputObjectType);
}
public GraphQLUnionType onUnion(GraphQLUnionType element, Parameters params) {
NodeParentTree nodeParentTree = buildAstTree(element.getDefinition());
GraphqlElementParentTree elementParentTree = buildRuntimeTree(element);
Parameters newParams = params.newParams(nodeParentTree, elementParentTree);
return wireDirectives(params, element, element.getDirectives(),
(outputElement, directives, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement, directives, registeredDirective, newParams), SchemaDirectiveWiring::onUnion);
}
public GraphQLScalarType onScalar(GraphQLScalarType element, Parameters params) {
NodeParentTree nodeParentTree = buildAstTree(element.getDefinition());
GraphqlElementParentTree elementParentTree = buildRuntimeTree(element);
Parameters newParams = params.newParams(nodeParentTree, elementParentTree);
return wireDirectives(params, element, element.getDirectives(),
(outputElement, directives, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement, directives, registeredDirective, newParams), SchemaDirectiveWiring::onScalar);
}
private GraphQLFieldDefinition onField(GraphQLFieldDefinition fieldDefinition, Parameters params) {
return wireDirectives(params, fieldDefinition, fieldDefinition.getDirectives(),
(outputElement, directives, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement, directives, registeredDirective, params), SchemaDirectiveWiring::onField);
}
private GraphQLInputObjectField onInputObjectField(GraphQLInputObjectField element, Parameters params) {
return wireDirectives(params, element, element.getDirectives(),
(outputElement, directives, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement, directives, registeredDirective, params), SchemaDirectiveWiring::onInputObjectField);
}
private GraphQLEnumValueDefinition onEnumValue(GraphQLEnumValueDefinition enumValueDefinition, Parameters params) {
return wireDirectives(params, enumValueDefinition, enumValueDefinition.getDirectives(),
(outputElement, directives, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement, directives, registeredDirective, params), SchemaDirectiveWiring::onEnumValue);
}
private GraphQLArgument onArgument(GraphQLArgument argument, Parameters params) {
return wireDirectives(params, argument, argument.getDirectives(),
(outputElement, directives, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement, directives, registeredDirective, params), SchemaDirectiveWiring::onArgument);
}
//
// builds a type safe SchemaDirectiveWiringEnvironment
//
interface EnvBuilder {
SchemaDirectiveWiringEnvironment apply(T outputElement, List allDirectives, GraphQLDirective registeredDirective);
}
//
// invokes the SchemaDirectiveWiring with the provided environment
//
interface EnvInvoker {
T apply(SchemaDirectiveWiring schemaDirectiveWiring, SchemaDirectiveWiringEnvironment env);
}
private T wireDirectives(
Parameters parameters, T element,
List allDirectives,
EnvBuilder envBuilder,
EnvInvoker invoker) {
RuntimeWiring runtimeWiring = parameters.getRuntimeWiring();
WiringFactory wiringFactory = runtimeWiring.getWiringFactory();
SchemaDirectiveWiring schemaDirectiveWiring;
SchemaDirectiveWiringEnvironment env;
T outputObject = element;
//
// first the specific named directives
Map mapOfWiring = runtimeWiring.getRegisteredDirectiveWiring();
for (GraphQLDirective directive : allDirectives) {
schemaDirectiveWiring = mapOfWiring.get(directive.getName());
if (schemaDirectiveWiring != null) {
env = envBuilder.apply(outputObject, allDirectives, directive);
outputObject = invokeWiring(outputObject, invoker, schemaDirectiveWiring, env);
}
}
//
// now call any statically added to the the runtime
for (SchemaDirectiveWiring directiveWiring : runtimeWiring.getDirectiveWiring()) {
env = envBuilder.apply(outputObject, allDirectives, null);
outputObject = invokeWiring(outputObject, invoker, directiveWiring, env);
}
//
// wiring factory is last (if present)
env = envBuilder.apply(outputObject, allDirectives, null);
if (wiringFactory.providesSchemaDirectiveWiring(env)) {
schemaDirectiveWiring = assertNotNull(wiringFactory.getSchemaDirectiveWiring(env), "Your WiringFactory MUST provide a non null SchemaDirectiveWiring");
outputObject = invokeWiring(outputObject, invoker, schemaDirectiveWiring, env);
}
return outputObject;
}
private T invokeWiring(T element, EnvInvoker invoker, SchemaDirectiveWiring schemaDirectiveWiring, SchemaDirectiveWiringEnvironment env) {
T newElement = invoker.apply(schemaDirectiveWiring, env);
assertNotNull(newElement, "The SchemaDirectiveWiring MUST return a non null return value for element '" + element.getName() + "'");
return newElement;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy