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.normalized.ExecutableNormalizedOperationFactory Maven / Gradle / Ivy
package graphql.normalized;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import graphql.Internal;
import graphql.execution.ConditionalNodes;
import graphql.execution.MergedField;
import graphql.execution.ValuesResolver;
import graphql.execution.nextgen.Common;
import graphql.introspection.Introspection;
import graphql.language.Document;
import graphql.language.Field;
import graphql.language.FragmentDefinition;
import graphql.language.FragmentSpread;
import graphql.language.InlineFragment;
import graphql.language.NodeUtil;
import graphql.language.OperationDefinition;
import graphql.language.Selection;
import graphql.language.SelectionSet;
import graphql.language.VariableDefinition;
import graphql.schema.FieldCoordinates;
import graphql.schema.GraphQLCompositeType;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLInterfaceType;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLSchema;
import graphql.schema.GraphQLType;
import graphql.schema.GraphQLTypeUtil;
import graphql.schema.GraphQLUnionType;
import graphql.schema.GraphQLUnmodifiedType;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static graphql.Assert.assertNotNull;
import static graphql.Assert.assertShouldNeverHappen;
import static graphql.collect.ImmutableKit.map;
import static graphql.execution.MergedField.newMergedField;
import static graphql.schema.GraphQLTypeUtil.unwrapAll;
import static graphql.util.FpKit.filterSet;
import static graphql.util.FpKit.groupingBy;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
@Internal
public class ExecutableNormalizedOperationFactory {
private final ValuesResolver valuesResolver = new ValuesResolver();
private final ConditionalNodes conditionalNodes = new ConditionalNodes();
public static ExecutableNormalizedOperation createExecutableNormalizedOperation(GraphQLSchema graphQLSchema,
Document document,
String operationName,
Map coercedVariableValues) {
NodeUtil.GetOperationResult getOperationResult = NodeUtil.getOperation(document, operationName);
return new ExecutableNormalizedOperationFactory().createNormalizedQueryImpl(graphQLSchema, getOperationResult.operationDefinition, getOperationResult.fragmentsByName, coercedVariableValues, null);
}
public static ExecutableNormalizedOperation createExecutableNormalizedOperation(GraphQLSchema graphQLSchema,
OperationDefinition operationDefinition,
Map fragments,
Map coercedVariableValues) {
return new ExecutableNormalizedOperationFactory().createNormalizedQueryImpl(graphQLSchema, operationDefinition, fragments, coercedVariableValues, null);
}
public static ExecutableNormalizedOperation createExecutableNormalizedOperationWithRawVariables(GraphQLSchema graphQLSchema,
Document document,
String operationName,
Map rawVariables) {
NodeUtil.GetOperationResult getOperationResult = NodeUtil.getOperation(document, operationName);
return new ExecutableNormalizedOperationFactory().createExecutableNormalizedOperationImplWithRawVariables(graphQLSchema, getOperationResult.operationDefinition, getOperationResult.fragmentsByName, rawVariables);
}
private ExecutableNormalizedOperation createExecutableNormalizedOperationImplWithRawVariables(GraphQLSchema graphQLSchema,
OperationDefinition operationDefinition,
Map fragments,
Map rawVariables
) {
List variableDefinitions = operationDefinition.getVariableDefinitions();
Map coerceVariableValues = valuesResolver.coerceVariableValues(graphQLSchema, variableDefinitions, rawVariables);
Map normalizedVariableValues = valuesResolver.getNormalizedVariableValues(graphQLSchema, variableDefinitions, rawVariables);
return createNormalizedQueryImpl(graphQLSchema, operationDefinition, fragments, coerceVariableValues, normalizedVariableValues);
}
/**
* Creates a new Normalized query tree for the provided query
*/
private ExecutableNormalizedOperation createNormalizedQueryImpl(GraphQLSchema graphQLSchema,
OperationDefinition operationDefinition,
Map fragments,
Map coercedVariableValues,
@Nullable Map normalizedVariableValues) {
FieldCollectorNormalizedQueryParams parameters = FieldCollectorNormalizedQueryParams
.newParameters()
.fragments(fragments)
.schema(graphQLSchema)
.coercedVariables(coercedVariableValues)
.normalizedVariables(normalizedVariableValues)
.build();
GraphQLObjectType rootType = Common.getOperationRootType(graphQLSchema, operationDefinition);
CollectNFResult collectFromOperationResult = collectFromOperation(parameters, operationDefinition, rootType);
ImmutableListMultimap.Builder fieldToNormalizedField = ImmutableListMultimap.builder();
ImmutableMap.Builder normalizedFieldToMergedField = ImmutableMap.builder();
ImmutableListMultimap.Builder coordinatesToNormalizedFields = ImmutableListMultimap.builder();
for (ExecutableNormalizedField topLevel : collectFromOperationResult.children) {
ImmutableList mergedField = collectFromOperationResult.normalizedFieldToAstFields.get(topLevel);
normalizedFieldToMergedField.put(topLevel, newMergedField(map(mergedField, fieldAndAstParent -> fieldAndAstParent.field)).build());
updateFieldToNFMap(topLevel, mergedField, fieldToNormalizedField);
updateCoordinatedToNFMap(coordinatesToNormalizedFields, topLevel);
buildFieldWithChildren(topLevel,
mergedField,
parameters,
fieldToNormalizedField,
normalizedFieldToMergedField,
coordinatesToNormalizedFields,
1);
}
for (FieldCollectorNormalizedQueryParams.PossibleMerger possibleMerger : parameters.possibleMergerList) {
List childrenWithSameResultKey = possibleMerger.parent.getChildrenWithSameResultKey(possibleMerger.resultKey);
ENFMerger.merge(possibleMerger.parent, childrenWithSameResultKey, graphQLSchema);
}
return new ExecutableNormalizedOperation(
operationDefinition.getOperation(),
operationDefinition.getName(),
new ArrayList<>(collectFromOperationResult.children),
fieldToNormalizedField.build(),
normalizedFieldToMergedField.build(),
coordinatesToNormalizedFields.build()
);
}
private void buildFieldWithChildren(ExecutableNormalizedField field,
ImmutableList mergedField,
FieldCollectorNormalizedQueryParams fieldCollectorNormalizedQueryParams,
ImmutableListMultimap.Builder fieldNormalizedField,
ImmutableMap.Builder normalizedFieldToMergedField,
ImmutableListMultimap.Builder coordinatesToNormalizedFields,
int curLevel) {
CollectNFResult nextLevel = collectFromMergedField(fieldCollectorNormalizedQueryParams, field, mergedField, curLevel + 1);
for (ExecutableNormalizedField child : nextLevel.children) {
field.addChild(child);
ImmutableList mergedFieldForChild = nextLevel.normalizedFieldToAstFields.get(child);
normalizedFieldToMergedField.put(child, newMergedField(map(mergedFieldForChild, fieldAndAstParent -> fieldAndAstParent.field)).build());
updateFieldToNFMap(child, mergedFieldForChild, fieldNormalizedField);
updateCoordinatedToNFMap(coordinatesToNormalizedFields, child);
buildFieldWithChildren(child,
mergedFieldForChild,
fieldCollectorNormalizedQueryParams,
fieldNormalizedField,
normalizedFieldToMergedField,
coordinatesToNormalizedFields,
curLevel + 1);
}
}
private void updateFieldToNFMap(ExecutableNormalizedField executableNormalizedField,
ImmutableList mergedField,
ImmutableListMultimap.Builder fieldToNormalizedField) {
for (FieldAndAstParent astField : mergedField) {
fieldToNormalizedField.put(astField.field, executableNormalizedField);
}
}
private void updateCoordinatedToNFMap(ImmutableListMultimap.Builder coordinatesToNormalizedFields, ExecutableNormalizedField topLevel) {
for (String objectType : topLevel.getObjectTypeNames()) {
FieldCoordinates coordinates = FieldCoordinates.coordinates(objectType, topLevel.getFieldName());
coordinatesToNormalizedFields.put(coordinates, topLevel);
}
}
private static class FieldAndAstParent {
final Field field;
final GraphQLCompositeType astParentType;
private FieldAndAstParent(Field field, GraphQLCompositeType astParentType) {
this.field = field;
this.astParentType = astParentType;
}
}
public static class CollectNFResult {
private final Collection children;
private final ImmutableListMultimap normalizedFieldToAstFields;
public CollectNFResult(Collection children, ImmutableListMultimap normalizedFieldToAstFields) {
this.children = children;
this.normalizedFieldToAstFields = normalizedFieldToAstFields;
}
}
public CollectNFResult collectFromMergedField(FieldCollectorNormalizedQueryParams parameters,
ExecutableNormalizedField executableNormalizedField,
ImmutableList mergedField,
int level) {
GraphQLUnmodifiedType fieldType = unwrapAll(executableNormalizedField.getType(parameters.getGraphQLSchema()));
// if not composite we don't have any selectionSet because it is a Scalar or enum
if (!(fieldType instanceof GraphQLCompositeType)) {
return new CollectNFResult(Collections.emptyList(), ImmutableListMultimap.of());
}
Set possibleObjects = resolvePossibleObjects((GraphQLCompositeType) fieldType, parameters.getGraphQLSchema());
List collectedFields = new ArrayList<>();
for (FieldAndAstParent fieldAndAstParent : mergedField) {
if (fieldAndAstParent.field.getSelectionSet() == null) {
continue;
}
GraphQLFieldDefinition fieldDefinition = Introspection.getFieldDef(parameters.getGraphQLSchema(), fieldAndAstParent.astParentType, fieldAndAstParent.field.getName());
GraphQLUnmodifiedType astParentType = unwrapAll(fieldDefinition.getType());
this.collectFromSelectionSet(parameters,
fieldAndAstParent.field.getSelectionSet(),
collectedFields,
(GraphQLCompositeType) astParentType,
possibleObjects
);
}
Map> fieldsByName = new LinkedHashMap<>();
for (CollectedField collectedField : collectedFields) {
fieldsByName.computeIfAbsent(collectedField.field.getResultKey(), ignored -> new ArrayList<>()).add(collectedField);
}
List resultNFs = new ArrayList<>();
ImmutableListMultimap.Builder normalizedFieldToAstFields = ImmutableListMultimap.builder();
createNFs(parameters, fieldsByName, resultNFs, normalizedFieldToAstFields, level, executableNormalizedField);
return new CollectNFResult(resultNFs, normalizedFieldToAstFields.build());
}
public CollectNFResult collectFromOperation(FieldCollectorNormalizedQueryParams parameters,
OperationDefinition operationDefinition,
GraphQLObjectType rootType) {
Set possibleObjects = new LinkedHashSet<>();
possibleObjects.add(rootType);
List collectedFields = new ArrayList<>();
collectFromSelectionSet(parameters, operationDefinition.getSelectionSet(), collectedFields, rootType, possibleObjects);
// group by result key
Map> fieldsByName = new LinkedHashMap<>();
for (CollectedField collectedField : collectedFields) {
fieldsByName.computeIfAbsent(collectedField.field.getResultKey(), ignored -> new ArrayList<>()).add(collectedField);
}
List resultNFs = new ArrayList<>();
ImmutableListMultimap.Builder normalizedFieldToAstFields = ImmutableListMultimap.builder();
createNFs(parameters, fieldsByName, resultNFs, normalizedFieldToAstFields, 1, null);
return new CollectNFResult(resultNFs, normalizedFieldToAstFields.build());
}
private void createNFs(FieldCollectorNormalizedQueryParams parameters,
Map> fieldsByName,
List resultNFs,
ImmutableListMultimap.Builder normalizedFieldToAstFields,
int level,
ExecutableNormalizedField parent) {
for (String resultKey : fieldsByName.keySet()) {
List fieldsWithSameResultKey = fieldsByName.get(resultKey);
List commonParentsGroups = groupByCommonParents(fieldsWithSameResultKey);
for (CollectedFieldGroup fieldGroup : commonParentsGroups) {
ExecutableNormalizedField nf = createNF(parameters, fieldGroup, level, parent);
if (nf == null) {
continue;
}
for (CollectedField collectedField : fieldGroup.fields) {
normalizedFieldToAstFields.put(nf, new FieldAndAstParent(collectedField.field, collectedField.astTypeCondition));
}
resultNFs.add(nf);
}
if (commonParentsGroups.size() > 1) {
parameters.addPossibleMergers(parent, resultKey);
}
}
}
private ExecutableNormalizedField createNF(FieldCollectorNormalizedQueryParams parameters,
CollectedFieldGroup collectedFieldGroup,
int level,
ExecutableNormalizedField parent) {
Field field;
Set objectTypes = collectedFieldGroup.objectTypes;
field = collectedFieldGroup.fields.iterator().next().field;
String fieldName = field.getName();
GraphQLFieldDefinition fieldDefinition = Introspection.getFieldDef(parameters.getGraphQLSchema(), objectTypes.iterator().next(), fieldName);
Map argumentValues = valuesResolver.getArgumentValues(fieldDefinition.getArguments(), field.getArguments(), parameters.getCoercedVariableValues());
Map normalizedArgumentValues = null;
if (parameters.getNormalizedVariableValues() != null) {
normalizedArgumentValues = valuesResolver.getNormalizedArgumentValues(fieldDefinition.getArguments(), field.getArguments(), parameters.getNormalizedVariableValues());
}
ImmutableList objectTypeNames = map(objectTypes, GraphQLObjectType::getName);
ExecutableNormalizedField executableNormalizedField = ExecutableNormalizedField.newNormalizedField()
.alias(field.getAlias())
.resolvedArguments(argumentValues)
.normalizedArguments(normalizedArgumentValues)
.astArguments(field.getArguments())
.objectTypeNames(objectTypeNames)
.fieldName(fieldName)
.level(level)
.parent(parent)
.build();
return executableNormalizedField;
}
private static class CollectedFieldGroup {
Set objectTypes;
Set fields;
public CollectedFieldGroup(Set fields, Set objectTypes) {
this.fields = fields;
this.objectTypes = objectTypes;
}
}
private List groupByCommonParents(Collection fields) {
Set allRelevantObjects = new LinkedHashSet<>();
for (CollectedField collectedField : fields) {
allRelevantObjects.addAll(collectedField.objectTypes);
}
Map> groupByAstParent = groupingBy(fields, fieldAndType -> fieldAndType.astTypeCondition);
if (groupByAstParent.size() == 1) {
return singletonList(new CollectedFieldGroup(new LinkedHashSet<>(fields), allRelevantObjects));
}
List result = new ArrayList<>();
for (GraphQLObjectType objectType : allRelevantObjects) {
Set relevantFields = filterSet(fields, field -> field.objectTypes.contains(objectType));
result.add(new CollectedFieldGroup(relevantFields, singleton(objectType)));
}
return result;
}
private void collectFromSelectionSet(FieldCollectorNormalizedQueryParams parameters,
SelectionSet selectionSet,
List result,
GraphQLCompositeType astTypeCondition,
Set possibleObjects
) {
for (Selection> selection : selectionSet.getSelections()) {
if (selection instanceof Field) {
collectField(parameters, result, (Field) selection, possibleObjects, astTypeCondition);
} else if (selection instanceof InlineFragment) {
collectInlineFragment(parameters, result, (InlineFragment) selection, possibleObjects, astTypeCondition);
} else if (selection instanceof FragmentSpread) {
collectFragmentSpread(parameters, result, (FragmentSpread) selection, possibleObjects, astTypeCondition);
}
}
}
private static class CollectedField {
Field field;
Set objectTypes;
GraphQLCompositeType astTypeCondition;
public CollectedField(Field field, Set objectTypes, GraphQLCompositeType astTypeCondition) {
this.field = field;
this.objectTypes = objectTypes;
this.astTypeCondition = astTypeCondition;
}
public boolean isAbstract() {
return GraphQLTypeUtil.isInterfaceOrUnion(astTypeCondition);
}
public boolean isConcrete() {
return GraphQLTypeUtil.isObjectType(astTypeCondition);
}
}
private void collectFragmentSpread(FieldCollectorNormalizedQueryParams parameters,
List result,
FragmentSpread fragmentSpread,
Set possibleObjects,
GraphQLCompositeType astTypeCondition
) {
if (!conditionalNodes.shouldInclude(parameters.getCoercedVariableValues(), fragmentSpread.getDirectives())) {
return;
}
FragmentDefinition fragmentDefinition = assertNotNull(parameters.getFragmentsByName().get(fragmentSpread.getName()));
if (!conditionalNodes.shouldInclude(parameters.getCoercedVariableValues(), fragmentDefinition.getDirectives())) {
return;
}
GraphQLCompositeType newAstTypeCondition = (GraphQLCompositeType) assertNotNull(parameters.getGraphQLSchema().getType(fragmentDefinition.getTypeCondition().getName()));
Set newPossibleObjects = narrowDownPossibleObjects(possibleObjects, newAstTypeCondition, parameters.getGraphQLSchema());
collectFromSelectionSet(parameters, fragmentDefinition.getSelectionSet(), result, newAstTypeCondition, newPossibleObjects);
}
private void collectInlineFragment(FieldCollectorNormalizedQueryParams parameters,
List result,
InlineFragment inlineFragment,
Set possibleObjects,
GraphQLCompositeType astTypeCondition
) {
if (!conditionalNodes.shouldInclude(parameters.getCoercedVariableValues(), inlineFragment.getDirectives())) {
return;
}
Set newPossibleObjects = possibleObjects;
GraphQLCompositeType newAstTypeCondition = astTypeCondition;
if (inlineFragment.getTypeCondition() != null) {
newAstTypeCondition = (GraphQLCompositeType) parameters.getGraphQLSchema().getType(inlineFragment.getTypeCondition().getName());
newPossibleObjects = narrowDownPossibleObjects(possibleObjects, newAstTypeCondition, parameters.getGraphQLSchema());
}
collectFromSelectionSet(parameters, inlineFragment.getSelectionSet(), result, newAstTypeCondition, newPossibleObjects);
}
private void collectField(FieldCollectorNormalizedQueryParams parameters,
List result,
Field field,
Set possibleObjectTypes,
GraphQLCompositeType astTypeCondition
) {
if (!conditionalNodes.shouldInclude(parameters.getCoercedVariableValues(), field.getDirectives())) {
return;
}
// this means there is actually no possible type for this field and we are done
if (possibleObjectTypes.size() == 0) {
return;
}
result.add(new CollectedField(field, possibleObjectTypes, astTypeCondition));
}
private Set narrowDownPossibleObjects(Set currentOnes,
GraphQLCompositeType typeCondition,
GraphQLSchema graphQLSchema) {
ImmutableSet resolvedTypeCondition = resolvePossibleObjects(typeCondition, graphQLSchema);
if (currentOnes.size() == 0) {
return resolvedTypeCondition;
}
return Sets.intersection(currentOnes, resolvedTypeCondition);
}
private ImmutableSet resolvePossibleObjects(GraphQLCompositeType type, GraphQLSchema graphQLSchema) {
if (type instanceof GraphQLObjectType) {
return ImmutableSet.of((GraphQLObjectType) type);
} else if (type instanceof GraphQLInterfaceType) {
return ImmutableSet.copyOf(graphQLSchema.getImplementations((GraphQLInterfaceType) type));
} else if (type instanceof GraphQLUnionType) {
List types = ((GraphQLUnionType) type).getTypes();
return ImmutableSet.copyOf((types));
} else {
return assertShouldNeverHappen();
}
}
}