Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
graphql.nadel.engine.execution.OverallQueryTransformer Maven / Gradle / Ivy
Go to download
Nadel is a Java library that combines multiple GrahpQL services together into one API.
package graphql.nadel.engine.execution;
import graphql.Internal;
import graphql.execution.Async;
import graphql.execution.ExecutionContext;
import graphql.execution.MergedField;
import graphql.language.AstNodeAdapter;
import graphql.language.Document;
import graphql.language.Field;
import graphql.language.FragmentDefinition;
import graphql.language.Node;
import graphql.language.NodeTraverser;
import graphql.language.NodeVisitorStub;
import graphql.language.OperationDefinition;
import graphql.language.SelectionSet;
import graphql.language.VariableDefinition;
import graphql.language.VariableReference;
import graphql.nadel.OperationKind;
import graphql.nadel.Service;
import graphql.nadel.dsl.TypeMappingDefinition;
import graphql.nadel.engine.NadelContext;
import graphql.nadel.engine.execution.transformation.OverallTypeInfo;
import graphql.nadel.engine.execution.transformation.OverallTypeInformation;
import graphql.nadel.engine.execution.transformation.RecordOverallTypeInformation;
import graphql.nadel.engine.execution.transformation.TransformationMetadata;
import graphql.nadel.hooks.HydrationArguments;
import graphql.nadel.hooks.ServiceExecutionHooks;
import graphql.nadel.util.FpKit;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLCompositeType;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLNamedOutputType;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLOutputType;
import graphql.schema.GraphQLSchema;
import graphql.schema.GraphQLType;
import graphql.schema.GraphQLTypeUtil;
import graphql.schema.idl.TypeInfo;
import graphql.util.TraversalControl;
import graphql.util.TraverserContext;
import graphql.util.TraverserVisitorStub;
import graphql.util.TreeTransformer;
import graphql.util.TreeTransformerUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import static graphql.language.OperationDefinition.newOperationDefinition;
import static graphql.language.SelectionSet.newSelectionSet;
import static graphql.nadel.dsl.NodeId.getId;
import static graphql.nadel.engine.execution.UnderlyingTypeContext.newUnderlyingTypeContext;
import static graphql.nadel.util.FpKit.map;
import static graphql.nadel.util.Util.getTypeMappingDefinitionFor;
import static graphql.util.FpKit.groupingByUniqueKey;
@Internal
public class OverallQueryTransformer {
private static final Logger log = LoggerFactory.getLogger(OverallQueryTransformer.class);
private final RecordOverallTypeInformation recordOverallTypeInformation = new RecordOverallTypeInformation();
CompletableFuture transformHydratedTopLevelField(
ExecutionContext executionContext,
GraphQLSchema underlyingSchema,
String operationName,
OperationKind operationKind,
Field rootField,
GraphQLCompositeType topLevelFieldTypeOverall,
ServiceExecutionHooks serviceExecutionHooks,
Service service,
Object serviceContext,
boolean isSynthetic
) {
long startTime = System.currentTimeMillis();
Set referencedFragmentNames = new LinkedHashSet<>();
Map variableValues = new LinkedHashMap<>(executionContext.getVariables());
TransformationMetadata removedFieldMap = new TransformationMetadata();
TransformationState transformations = new TransformationState();
NadelContext nadelContext = executionContext.getContext();
SelectionSet selectionSet = rootField.getSelectionSet();
Field topLevelField = rootField;
if (isSynthetic) {
topLevelField = (Field) selectionSet.getSelections().get(0);
selectionSet = topLevelField.getSelectionSet();
}
HydrationArguments hydrationArguments = getHydrationArguments(executionContext, operationKind, rootField, topLevelField, isSynthetic);
Map variableDefinitionMap = FpKit.getByName(executionContext.getOperationDefinition().getVariableDefinitions(), VariableDefinition::getName);
Map referencedVariables = collectReferencedVariables(topLevelField, variableDefinitionMap);
CompletableFuture topLevelFieldSelectionSetCF = transformNode(
executionContext,
underlyingSchema,
selectionSet,
topLevelFieldTypeOverall,
referencedFragmentNames,
referencedVariables,
nadelContext,
serviceExecutionHooks,
variableValues,
service,
serviceContext,
removedFieldMap,
transformations,
hydrationArguments
);
Field finalTopLevelField = topLevelField;
return topLevelFieldSelectionSetCF.thenCompose(topLevelFieldSelectionSet -> {
Field transformedRootField = finalTopLevelField.transform(builder -> builder.selectionSet(topLevelFieldSelectionSet));
Field maybeTransformedRootField = ArtificialFieldUtils.maybeAddUnderscoreTypeName(nadelContext, transformedRootField, topLevelFieldTypeOverall);
if (maybeTransformedRootField != transformedRootField) {
transformations.addHintTypename(maybeTransformedRootField.getName());
}
transformedRootField = maybeTransformedRootField;
transformedRootField = ArtificialFieldUtils.maybeAddEmptySelectionSetUnderscoreTypeName(nadelContext, transformedRootField, topLevelFieldTypeOverall);
if (isSynthetic) {
Field tempTransformedRootLevelField = transformedRootField;
transformedRootField = rootField.transform(builder -> builder.selectionSet(newSelectionSet().selection(tempTransformedRootLevelField).build()));
}
Map referencedVariablesForTransformedField = collectReferencedVariables(transformedRootField, variableDefinitionMap);
List variableDefinitions = buildReferencedVariableDefinitions(referencedVariablesForTransformedField, executionContext.getGraphQLSchema(), transformations.getTypeRenameMappings());
List referencedVariableNames = new ArrayList<>(referencedVariablesForTransformedField.keySet());
CompletableFuture> transformedFragmentsCF = transformFragments(executionContext,
underlyingSchema,
executionContext.getFragmentsByName(),
referencedFragmentNames,
referencedVariables,
serviceExecutionHooks,
variableValues,
service,
serviceContext,
removedFieldMap,
transformations);
Field finalTransformedRootField = transformedRootField;
return transformedFragmentsCF.thenApply(transformedFragments -> {
SelectionSet newOperationSelectionSet = newSelectionSet().selection(finalTransformedRootField).build();
OperationDefinition operationDefinition = newOperationDefinition()
.name(operationName)
.operation(operationKind.getAstOperation())
.selectionSet(newOperationSelectionSet)
.variableDefinitions(variableDefinitions)
.build();
Document newDocument = newDocument(operationDefinition, transformedFragments);
MergedField transformedMergedField = MergedField.newMergedField(finalTransformedRootField).build();
long elapsedTime = System.currentTimeMillis() - startTime;
log.debug("OverallQueryTransformer.transformHydratedTopLevelField time: {}, executionId: {}", elapsedTime, executionContext.getExecutionId());
return new QueryTransformationResult(
newDocument,
operationDefinition,
Collections.singletonList(transformedMergedField),
referencedVariableNames,
transformedFragments,
variableValues,
removedFieldMap,
transformations);
});
});
}
private static Map collectReferencedVariables(Field field, Map variableDefinitionMap) {
Map referencedVariables = new LinkedHashMap<>();
NodeVisitorStub collectReferencedVariables = new NodeVisitorStub() {
@Override
public TraversalControl visitVariableReference(VariableReference variableReference, TraverserContext context) {
String name = variableReference.getName();
referencedVariables.put(name, variableDefinitionMap.get(name));
return super.visitVariableReference(variableReference, context);
}
};
new NodeTraverser().depthFirst(collectReferencedVariables, field);
return referencedVariables;
}
private static HydrationArguments getHydrationArguments(ExecutionContext executionContext, OperationKind operation, Field rootField, Field topLevelField, boolean isSynthetic) {
final List hydrationGqlArguments;
if (isSynthetic) {
hydrationGqlArguments = Optional.ofNullable(
operation.getRootType(executionContext.getGraphQLSchema())
.getFieldDefinition(rootField.getName()))
.map(fieldDefinition -> ((GraphQLObjectType) fieldDefinition.getType()).getFieldDefinition(topLevelField.getName()))
.map(GraphQLFieldDefinition::getArguments)
.orElse(Collections.emptyList());
} else {
hydrationGqlArguments = Optional.ofNullable(operation.getRootType(executionContext.getGraphQLSchema())
.getFieldDefinition(topLevelField.getName()))
.map(GraphQLFieldDefinition::getArguments)
.orElse(Collections.emptyList());
}
return new HydrationArguments(hydrationGqlArguments, topLevelField.getArguments());
}
CompletableFuture transformMergedFields(
ExecutionContext executionContext,
GraphQLSchema underlyingSchema,
String operationName, OperationKind operationKind,
List mergedFields,
ServiceExecutionHooks serviceExecutionHooks,
Service service,
Object serviceContext
) {
long startTime = System.currentTimeMillis();
NadelContext nadelContext = executionContext.getContext();
Set fragmentsDirectlyReferenced = new LinkedHashSet<>();
Map referencedVariables = new LinkedHashMap<>();
Map variableValues = new LinkedHashMap<>(executionContext.getVariables());
TransformationMetadata removedFieldMap = new TransformationMetadata();
TransformationState transformations = new TransformationState();
List> transformedFieldsCF = new ArrayList<>();
for (MergedField mergedField : mergedFields) {
List fields = mergedField.getFields();
List> transformedCF = map(fields, field -> {
GraphQLObjectType rootType = operationKind.getRootType(executionContext.getGraphQLSchema());
CompletableFuture newFieldCF = transformNode(
executionContext,
underlyingSchema,
field,
rootType,
fragmentsDirectlyReferenced,
referencedVariables,
nadelContext,
serviceExecutionHooks,
variableValues,
service,
serviceContext,
removedFieldMap,
transformations,
HydrationArguments.empty()
);
return newFieldCF.thenApply(newField -> {
// Case happens when the high level field is removed
if (newField == null) {
newField = field.transform(builder -> builder.selectionSet(SelectionSet.newSelectionSet().build()));
}
// if all child fields of the high level field are removed then the top-level field is nulled
GraphQLOutputType fieldType = rootType.getFieldDefinition(field.getName()).getType();
Field maybeNewField = ArtificialFieldUtils.maybeAddUnderscoreTypeName(nadelContext, newField, fieldType);
if (maybeNewField != newField) {
transformations.addHintTypename(maybeNewField.getName());
}
newField = maybeNewField;
return newField;
});
});
transformedFieldsCF.addAll(transformedCF);
}
return Async.each(transformedFieldsCF).thenCompose(transformedFields -> {
List variableDefinitions = buildReferencedVariableDefinitions(referencedVariables, executionContext.getGraphQLSchema(), transformations.getTypeRenameMappings());
List referencedVariableNames = new ArrayList<>(referencedVariables.keySet());
// create a new Document including referenced Fragments
SelectionSet newSelectionSet = newSelectionSet(transformedFields).build();
OperationDefinition operationDefinition = newOperationDefinition()
.operation(operationKind.getAstOperation())
.name(operationName)
.selectionSet(newSelectionSet)
.variableDefinitions(variableDefinitions)
.build();
CompletableFuture> transformedFragmentsCF = transformFragments(
executionContext,
underlyingSchema,
executionContext.getFragmentsByName(),
fragmentsDirectlyReferenced,
referencedVariables,
serviceExecutionHooks,
variableValues,
service,
serviceContext,
removedFieldMap,
transformations);
return transformedFragmentsCF.thenApply(transformedFragments -> {
Document newDocument = newDocument(operationDefinition, transformedFragments);
List transformedMergedFields = map(transformedFields, transformed -> MergedField.newMergedField(transformed).build());
long elapsedTime = System.currentTimeMillis() - startTime;
log.debug("OverallQueryTransformer.transformMergedFields time: {}, executionId: {}", elapsedTime, executionContext.getExecutionId());
return new QueryTransformationResult(
newDocument,
operationDefinition,
transformedMergedFields,
referencedVariableNames,
transformedFragments,
variableValues,
removedFieldMap,
transformations);
});
});
}
private Document newDocument(OperationDefinition operationDefinition, Map transformedFragments) {
Document.Builder newDocumentBuilder = Document.newDocument();
newDocumentBuilder.definition(operationDefinition);
for (FragmentDefinition transformedFragment : transformedFragments.values()) {
newDocumentBuilder.definition(transformedFragment);
}
return newDocumentBuilder.build();
}
private CompletableFuture> transformFragments(ExecutionContext executionContext,
GraphQLSchema underlyingSchema,
Map fragments,
Set referencedFragmentNames,
Map referencedVariables,
ServiceExecutionHooks serviceExecutionHooks,
Map variableValues,
Service service,
Object serviceContext,
TransformationMetadata removedFieldMap,
TransformationState transformations) {
Set fragmentsToTransform = Collections.synchronizedSet(new LinkedHashSet<>(referencedFragmentNames));
Set transformedFragmentsInput = Collections.synchronizedSet(new LinkedHashSet<>());
return transformFragmentImpl(
executionContext,
underlyingSchema,
fragments,
referencedVariables,
serviceExecutionHooks,
variableValues,
service,
serviceContext,
removedFieldMap,
fragmentsToTransform,
transformedFragmentsInput,
transformations).
thenApply(transformedFragments -> groupingByUniqueKey(transformedFragmentsInput, FragmentDefinition::getName));
}
private CompletableFuture> transformFragmentImpl(ExecutionContext executionContext,
GraphQLSchema underlyingSchema,
Map fragments,
Map referencedVariables,
ServiceExecutionHooks serviceExecutionHooks,
Map variableValues,
Service service,
Object serviceContext,
TransformationMetadata removedFieldMap,
Set fragmentsToTransform,
Set transformedFragments,
TransformationState transformations) {
if (fragmentsToTransform.isEmpty()) {
return CompletableFuture.completedFuture(transformedFragments);
}
String fragmentName = fragmentsToTransform.iterator().next();
Set newReferencedFragments = new LinkedHashSet<>();
CompletableFuture transformedFragmentCF = transformFragmentDefinition(
executionContext,
underlyingSchema,
fragments.get(fragmentName),
newReferencedFragments,
referencedVariables,
serviceExecutionHooks,
variableValues,
service,
serviceContext,
removedFieldMap,
transformations
);
return transformedFragmentCF.thenCompose(transformedFragment -> {
fragmentsToTransform.remove(fragmentName);
transformedFragments.add(transformedFragment);
fragmentsToTransform.addAll(newReferencedFragments);
return transformFragmentImpl(
executionContext,
underlyingSchema,
fragments,
referencedVariables,
serviceExecutionHooks,
variableValues,
service,
serviceContext,
removedFieldMap,
fragmentsToTransform,
transformedFragments,
transformations);
});
}
private CompletableFuture transformFragmentDefinition(ExecutionContext executionContext,
GraphQLSchema underlyingSchema,
FragmentDefinition fragmentDefinitionWithoutTypeInfo,
Set referencedFragmentNames,
Map referencedVariables,
ServiceExecutionHooks serviceExecutionHooks,
Map variableValues,
Service service,
Object serviceContext,
TransformationMetadata removedFieldMap,
TransformationState transformations) {
NadelContext nadelContext = executionContext.getContext();
OverallTypeInformation overallTypeInformation = recordOverallTypeInformation.recordOverallTypes(
fragmentDefinitionWithoutTypeInfo,
executionContext.getGraphQLSchema(),
null);
AsyncIsFieldForbidden asyncIsFieldForbidden = new AsyncIsFieldForbidden(
serviceExecutionHooks,
nadelContext,
executionContext.getGraphQLSchema(),
HydrationArguments.empty(),
variableValues
);
return asyncIsFieldForbidden.getForbiddenFields(fragmentDefinitionWithoutTypeInfo).thenApply(forbiddenFields -> {
Transformer transformer = new Transformer(
executionContext,
underlyingSchema,
referencedFragmentNames,
referencedVariables,
nadelContext,
serviceExecutionHooks,
overallTypeInformation,
variableValues,
service,
serviceContext,
removedFieldMap,
forbiddenFields,
transformations
);
Map, Object> rootVars = new LinkedHashMap<>();
rootVars.put(UnderlyingTypeContext.class, newUnderlyingTypeContext().build());
TreeTransformer treeTransformer = new TreeTransformer<>(AstNodeAdapter.AST_NODE_ADAPTER);
Node newNode = treeTransformer.transform(fragmentDefinitionWithoutTypeInfo, new TraverserVisitorStub() {
@Override
public TraversalControl enter(TraverserContext context) {
return context.thisNode().accept(context, transformer);
}
},
rootVars
);
//noinspection unchecked
return (FragmentDefinition) newNode;
});
}
private List buildReferencedVariableDefinitions(Map referencedVariables,
GraphQLSchema graphQLSchema,
Map typeRenameMappings) {
List variableDefinitions = new ArrayList<>();
for (VariableDefinition vd : referencedVariables.values()) {
TypeInfo typeInfo = TypeInfo.typeInfo(vd.getType());
GraphQLType type = graphQLSchema.getType(typeInfo.getName());
TypeMappingDefinition mappingDefinition = getTypeMappingDefinitionFor(type);
if (mappingDefinition != null) {
typeRenameMappings.put(mappingDefinition.getUnderlyingName(), mappingDefinition.getOverallName());
String newName = mappingDefinition.getUnderlyingName();
TypeInfo newTypeInfo = typeInfo.renameAs(newName);
vd = vd.transform(builder -> builder.type(newTypeInfo.getRawType()));
}
variableDefinitions.add(vd);
}
return variableDefinitions;
}
private CompletableFuture transformNode(ExecutionContext executionContext,
GraphQLSchema underlyingSchema,
T nodeWithoutTypeInfo,
GraphQLCompositeType parentTypeOverall,
Set referencedFragmentNames,
Map referencedVariables,
NadelContext nadelContext,
ServiceExecutionHooks serviceExecutionHooks,
Map variableValues,
Service service,
Object serviceContext,
TransformationMetadata removedFieldMap,
TransformationState transformations,
HydrationArguments hydrationArguments) {
OverallTypeInformation overallTypeInformation = recordOverallTypeInformation.recordOverallTypes(
nodeWithoutTypeInfo,
executionContext.getGraphQLSchema(),
parentTypeOverall);
AsyncIsFieldForbidden asyncIsFieldForbidden = new AsyncIsFieldForbidden(
serviceExecutionHooks,
nadelContext,
executionContext.getGraphQLSchema(),
hydrationArguments,
variableValues
);
return asyncIsFieldForbidden.getForbiddenFields(nodeWithoutTypeInfo).thenApply(forbiddenFields -> {
Transformer transformer = new Transformer(
executionContext,
underlyingSchema,
referencedFragmentNames,
referencedVariables,
nadelContext,
serviceExecutionHooks,
overallTypeInformation,
variableValues,
service,
serviceContext,
removedFieldMap,
forbiddenFields,
transformations
);
Map, Object> rootVars = new LinkedHashMap<>();
String underlyingParentName = getUnderlyingTypeNameAndRecordMapping(parentTypeOverall, transformations.getTypeRenameMappings());
GraphQLOutputType underlyingSchemaParent = (GraphQLOutputType) underlyingSchema.getType(underlyingParentName);
rootVars.put(UnderlyingTypeContext.class, newUnderlyingTypeContext()
.outputTypeUnderlying(underlyingSchemaParent)
.build());
TreeTransformer treeTransformer = new TreeTransformer<>(AstNodeAdapter.AST_NODE_ADAPTER);
Node newNode = treeTransformer.transform(nodeWithoutTypeInfo, new TraverserVisitorStub() {
@Override
public TraversalControl enter(TraverserContext context) {
return context.thisNode().accept(context, transformer);
}
},
rootVars
);
if (removedFieldMap.hasRemovedFields()) {
newNode = addUnderscoreTypeNameToEmptySelectionSets(nadelContext, overallTypeInformation, rootVars, treeTransformer, newNode);
}
//noinspection unchecked
return (T) newNode;
});
}
private Node addUnderscoreTypeNameToEmptySelectionSets(NadelContext nadelContext,
OverallTypeInformation> overallTypeInformation,
Map, Object> rootVars,
TreeTransformer treeTransformer,
Node field
) {
if (field == null) {
return null;
}
NodeVisitorStub addDummyFieldToEmptySubSelectsVisitor = new NodeVisitorStub() {
@Override
public TraversalControl visitField(Field node, TraverserContext context) {
OverallTypeInfo overallTypeInfo = overallTypeInformation.getOverallTypeInfo(getId(node));
if (overallTypeInfo == null) {
return TraversalControl.CONTINUE;
}
GraphQLFieldDefinition fieldDefinitionOverall = overallTypeInfo.getFieldDefinition();
GraphQLNamedOutputType fieldTypeOverall = (GraphQLNamedOutputType) GraphQLTypeUtil.unwrapAll(fieldDefinitionOverall.getType());
Field newField = ArtificialFieldUtils.maybeAddEmptySelectionSetUnderscoreTypeName(nadelContext, node, fieldTypeOverall);
if (newField != node) {
TreeTransformerUtil.changeNode(context, newField);
}
return TraversalControl.CONTINUE;
}
};
return treeTransformer.transform(field, new TraverserVisitorStub() {
@Override
public TraversalControl enter(TraverserContext context) {
return context.thisNode().accept(context, addDummyFieldToEmptySubSelectsVisitor);
}
},
rootVars
);
}
private String getUnderlyingTypeNameAndRecordMapping(GraphQLCompositeType typeOverall, Map typeRenameMappings) {
TypeMappingDefinition mappingDefinition = getTypeMappingDefinitionFor(typeOverall);
if (mappingDefinition == null) {
return typeOverall.getName();
}
typeRenameMappings.put(mappingDefinition.getUnderlyingName(), mappingDefinition.getOverallName());
return mappingDefinition.getUnderlyingName();
}
}