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.GraphQLAppliedDirective;
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 graphql.util.FpKit;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static graphql.Assert.assertNotNull;
import static graphql.collect.ImmutableKit.map;
/**
* This contains the helper code that allows {@link graphql.schema.idl.SchemaDirectiveWiring} implementations
* to be invoked during schema generation.
*/
@SuppressWarnings("DuplicatedCode")
@Internal
public class SchemaGeneratorDirectiveHelper {
/**
* This will return true if something in the RuntimeWiring requires a {@link SchemaDirectiveWiring}. This is to allow
* a shortcut to decide that we don't need ANY SchemaDirectiveWiring post processing
*
* @param directiveContainer the element that has directives
* @param typeRegistry the type registry
* @param runtimeWiring the runtime wiring
* @param for two
*
* @return true if something in the RuntimeWiring requires a {@link SchemaDirectiveWiring}
*/
public static boolean schemaDirectiveWiringIsRequired(T directiveContainer, TypeDefinitionRegistry typeRegistry, RuntimeWiring runtimeWiring) {
WiringFactory wiringFactory = runtimeWiring.getWiringFactory();
Map registeredWiring = runtimeWiring.getRegisteredDirectiveWiring();
List otherWiring = runtimeWiring.getDirectiveWiring();
boolean thereAreSome = !registeredWiring.isEmpty() || !otherWiring.isEmpty();
if (thereAreSome) {
return true;
}
Parameters params = new Parameters(typeRegistry, runtimeWiring, new HashMap<>(), null);
SchemaDirectiveWiringEnvironment env = new SchemaDirectiveWiringEnvironmentImpl<>(directiveContainer,
directiveContainer.getDirectives(),
directiveContainer.getAppliedDirectives(),
null,
null,
params);
// do they dynamically provide a wiring for this element?
return wiringFactory.providesSchemaDirectiveWiring(env);
}
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 map(field.getArguments(), 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);
});
}
private List wireFields(GraphQLFieldsContainer fieldsContainer, NamedNode> fieldsContainerNode, Parameters params) {
return map(fieldsContainer.getFieldDefinitions(), fieldDefinition -> {
// and for each argument in the fieldDefinition run the wiring for them - and note that they can change
List startingArgs = fieldDefinition.getArguments();
List newArgs = wireArguments(fieldDefinition, fieldsContainer, fieldsContainerNode, params, fieldDefinition);
if (isNotTheSameObjects(startingArgs, newArgs)) {
// 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);
});
}
public GraphQLObjectType onObject(GraphQLObjectType objectType, Parameters params) {
List startingFields = objectType.getFieldDefinitions();
List newFields = wireFields(objectType, objectType.getDefinition(), params);
GraphQLObjectType newObjectType = objectType;
if (isNotTheSameObjects(startingFields, newFields)) {
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(),
newObjectType.getAppliedDirectives(),
(outputElement, directives, appliedDirectives, registeredAppliedDirective, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement,
directives,
appliedDirectives,
registeredAppliedDirective,
registeredDirective,
newParams),
SchemaDirectiveWiring::onObject);
}
public GraphQLInterfaceType onInterface(GraphQLInterfaceType interfaceType, Parameters params) {
List startingFields = interfaceType.getFieldDefinitions();
List newFields = wireFields(interfaceType, interfaceType.getDefinition(), params);
GraphQLInterfaceType newInterfaceType = interfaceType;
if (isNotTheSameObjects(startingFields, newFields)) {
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(),
newInterfaceType.getAppliedDirectives(),
(outputElement, directives, appliedDirectives, registeredAppliedDirective, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement,
directives,
appliedDirectives,
registeredAppliedDirective,
registeredDirective,
newParams),
SchemaDirectiveWiring::onInterface);
}
public GraphQLEnumType onEnum(final GraphQLEnumType enumType, Parameters params) {
List startingEnumValues = enumType.getValues();
List newEnumValues = map(startingEnumValues, 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);
});
GraphQLEnumType newEnumType = enumType;
if (isNotTheSameObjects(startingEnumValues, newEnumValues)) {
newEnumType = enumType.transform(builder -> builder.clearValues().values(newEnumValues));
}
NodeParentTree> nodeParentTree = buildAstTree(newEnumType.getDefinition());
GraphqlElementParentTree elementParentTree = buildRuntimeTree(newEnumType);
Parameters newParams = params.newParams(nodeParentTree, elementParentTree);
return wireDirectives(params,
newEnumType,
newEnumType.getDirectives(),
newEnumType.getAppliedDirectives(),
(outputElement, directives, appliedDirectives, registeredAppliedDirective, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement,
directives,
appliedDirectives,
registeredAppliedDirective,
registeredDirective,
newParams),
SchemaDirectiveWiring::onEnum);
}
public GraphQLInputObjectType onInputObjectType(GraphQLInputObjectType inputObjectType, Parameters params) {
List startingFields = inputObjectType.getFieldDefinitions();
List newFields = map(startingFields, 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);
});
GraphQLInputObjectType newInputObjectType = inputObjectType;
if (isNotTheSameObjects(startingFields, newFields)) {
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(),
newInputObjectType.getAppliedDirectives(),
(outputElement, directives, appliedDirectives, registeredAppliedDirective, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement,
directives,
appliedDirectives,
registeredAppliedDirective,
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(),
element.getAppliedDirectives(),
(outputElement, directives, appliedDirectives, registeredAppliedDirective, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement,
directives,
appliedDirectives,
registeredAppliedDirective,
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(),
element.getAppliedDirectives(),
(outputElement, directives, appliedDirectives, registeredAppliedDirective, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement,
directives,
appliedDirectives,
registeredAppliedDirective,
registeredDirective,
newParams),
SchemaDirectiveWiring::onScalar);
}
private GraphQLFieldDefinition onField(GraphQLFieldDefinition fieldDefinition, Parameters params) {
return wireDirectives(params,
fieldDefinition,
fieldDefinition.getDirectives(),
fieldDefinition.getAppliedDirectives(),
(outputElement, directives, appliedDirectives, registeredAppliedDirective, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement,
directives,
appliedDirectives,
registeredAppliedDirective,
registeredDirective,
params),
SchemaDirectiveWiring::onField);
}
private GraphQLInputObjectField onInputObjectField(GraphQLInputObjectField element, Parameters params) {
return wireDirectives(params,
element,
element.getDirectives(),
element.getAppliedDirectives(),
(outputElement, directives, appliedDirectives, registeredAppliedDirective, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement,
directives,
appliedDirectives,
registeredAppliedDirective,
registeredDirective,
params),
SchemaDirectiveWiring::onInputObjectField);
}
private GraphQLEnumValueDefinition onEnumValue(GraphQLEnumValueDefinition enumValueDefinition, Parameters params) {
return wireDirectives(params,
enumValueDefinition,
enumValueDefinition.getDirectives(),
enumValueDefinition.getAppliedDirectives(),
(outputElement, directives, appliedDirectives, registeredAppliedDirective, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement,
directives,
appliedDirectives,
registeredAppliedDirective,
registeredDirective,
params),
SchemaDirectiveWiring::onEnumValue);
}
private GraphQLArgument onArgument(GraphQLArgument argument, Parameters params) {
return wireDirectives(params,
argument,
argument.getDirectives(),
argument.getAppliedDirectives(),
(outputElement, directives, appliedDirectives, registeredAppliedDirective, registeredDirective) -> new SchemaDirectiveWiringEnvironmentImpl<>(outputElement,
directives,
appliedDirectives,
registeredAppliedDirective,
registeredDirective,
params),
SchemaDirectiveWiring::onArgument);
}
//
// builds a type safe SchemaDirectiveWiringEnvironment
//
interface EnvBuilder {
SchemaDirectiveWiringEnvironment apply(T outputElement, List allDirectives, List allAppliedDirectives, GraphQLAppliedDirective registeredAppliedDirective, 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,
List allAppliedDirectives,
EnvBuilder envBuilder,
EnvInvoker invoker) {
RuntimeWiring runtimeWiring = parameters.getRuntimeWiring();
WiringFactory wiringFactory = runtimeWiring.getWiringFactory();
SchemaDirectiveWiring schemaDirectiveWiring;
SchemaDirectiveWiringEnvironment env;
T outputObject = element;
Map allDirectivesByName = FpKit.getByName(allDirectives, GraphQLDirective::getName);
//
// first the specific named directives
Map mapOfWiring = runtimeWiring.getRegisteredDirectiveWiring();
for (GraphQLAppliedDirective appliedDirective : allAppliedDirectives) {
schemaDirectiveWiring = mapOfWiring.get(appliedDirective.getName());
if (schemaDirectiveWiring != null) {
GraphQLDirective directive = allDirectivesByName.get(appliedDirective.getName());
env = envBuilder.apply(outputObject, allDirectives, allAppliedDirectives, appliedDirective, directive);
outputObject = invokeWiring(outputObject, invoker, schemaDirectiveWiring, env);
}
}
//
// now call any statically added to the runtime
for (SchemaDirectiveWiring directiveWiring : runtimeWiring.getDirectiveWiring()) {
env = envBuilder.apply(outputObject, allDirectives, allAppliedDirectives, null, null);
outputObject = invokeWiring(outputObject, invoker, directiveWiring, env);
}
//
// wiring factory is last (if present)
env = envBuilder.apply(outputObject, allDirectives, allAppliedDirectives, null, 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 '%s'",element.getName());
return newElement;
}
private boolean isNotTheSameObjects(List starting, List ending) {
if (starting == ending) {
return false;
}
if (ending.size() != starting.size()) {
return true;
}
for (int i = 0; i < starting.size(); i++) {
T startObj = starting.get(i);
T endObj = ending.get(i);
// object equality
if (!(startObj == endObj)) {
return true;
}
}
return false;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy