Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/main/java/graphql/execution/ExecutionContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
import graphql.language.Document;
import graphql.language.FragmentDefinition;
import graphql.language.OperationDefinition;
import graphql.normalized.NormalizedQueryFactory;
import graphql.normalized.NormalizedQueryFromAst;
import graphql.schema.GraphQLSchema;
import graphql.util.FpKit;
import org.dataloader.DataLoaderRegistry;

import java.util.Collections;
Expand All @@ -21,6 +24,7 @@
import java.util.Set;
import java.util.ArrayList;
import java.util.function.Consumer;
import java.util.function.Supplier;

@SuppressWarnings("TypeParameterUnusedInFormals")
@PublicApi
Expand Down Expand Up @@ -112,6 +116,7 @@ public Map<String, Object> getVariables() {
public <T> T getContext() {
return (T) context;
}

@SuppressWarnings("unchecked")
public <T> T getLocalContext() {
return (T) localContext;
Expand Down Expand Up @@ -208,4 +213,8 @@ public ExecutionContext transform(Consumer<ExecutionContextBuilder> builderConsu
builderConsumer.accept(builder);
return builder.build();
}

public Supplier<NormalizedQueryFromAst> getNormalizedQuery() {
return FpKit.interThreadMemoize(() -> NormalizedQueryFactory.createNormalizedQuery(graphQLSchema, operationDefinition, fragmentsByName, variables));
}
}
4 changes: 2 additions & 2 deletions src/main/java/graphql/execution/ExecutionStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ protected CompletableFuture<ExecutionResult> resolveField(ExecutionContext execu
*/
protected CompletableFuture<FieldValueInfo> resolveFieldWithInfo(ExecutionContext executionContext, ExecutionStrategyParameters parameters) {
GraphQLFieldDefinition fieldDef = getFieldDef(executionContext, parameters, parameters.getField().getSingleField());
Supplier<ExecutionStepInfo> executionStepInfo = FpKit.memoize(() -> createExecutionStepInfo(executionContext, parameters, fieldDef, null));
Supplier<ExecutionStepInfo> executionStepInfo = FpKit.intraThreadMemoize(() -> createExecutionStepInfo(executionContext, parameters, fieldDef, null));

Instrumentation instrumentation = executionContext.getInstrumentation();
InstrumentationContext<ExecutionResult> fieldCtx = instrumentation.beginField(
Expand Down Expand Up @@ -238,7 +238,7 @@ protected CompletableFuture<FetchedValue> fetchField(ExecutionContext executionC
QueryDirectives queryDirectives = new QueryDirectivesImpl(field, executionContext.getGraphQLSchema(), executionContext.getVariables());

// if the DF (like PropertyDataFetcher) does not use the arguments of execution step info then dont build any
Supplier<ExecutionStepInfo> executionStepInfo = FpKit.memoize(
Supplier<ExecutionStepInfo> executionStepInfo = FpKit.intraThreadMemoize(
() -> createExecutionStepInfo(executionContext, parameters, fieldDef, parentType));
Supplier<Map<String, Object>> argumentValues = () -> executionStepInfo.get().getArguments();

Expand Down
18 changes: 18 additions & 0 deletions src/main/java/graphql/execution/ResultPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,24 @@ public List<Object> toList() {
return list;
}

/**
* @return this path as a list of result keys, without any indices
*/
public List<String> getKeysOnly() {
if (parent == null) {
return new LinkedList<>();
}
LinkedList<String> list = new LinkedList<>();
ResultPath p = this;
while (p.segment != null) {
if (p.segment instanceof String) {
list.addFirst((String) p.segment);
}
p = p.parent;
}
return list;
}


/**
* @return the path as a string which represents the call hierarchy
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/graphql/execution/nextgen/ValueFetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public CompletableFuture<FetchedValue> fetchValue(ExecutionContext executionCont
GraphQLCodeRegistry codeRegistry = executionContext.getGraphQLSchema().getCodeRegistry();
GraphQLFieldsContainer parentType = getFieldsContainer(executionInfo);

Supplier<Map<String, Object>> argumentValues = FpKit.memoize(() -> valuesResolver.getArgumentValues(codeRegistry, fieldDef.getArguments(), field.getArguments(), executionContext.getVariables()));
Supplier<Map<String, Object>> argumentValues = FpKit.intraThreadMemoize(() -> valuesResolver.getArgumentValues(codeRegistry, fieldDef.getArguments(), field.getArguments(), executionContext.getVariables()));

QueryDirectivesImpl queryDirectives = new QueryDirectivesImpl(sameFields, executionContext.getGraphQLSchema(), executionContext.getVariables());

Expand Down
273 changes: 273 additions & 0 deletions src/main/java/graphql/normalized/FieldCollectorNormalizedQuery.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
package graphql.normalized;


import graphql.Assert;
import graphql.Internal;
import graphql.execution.ConditionalNodes;
import graphql.execution.MergedField;
import graphql.execution.ValuesResolver;
import graphql.introspection.Introspection;
import graphql.language.Field;
import graphql.language.FragmentDefinition;
import graphql.language.FragmentSpread;
import graphql.language.InlineFragment;
import graphql.language.OperationDefinition;
import graphql.language.Selection;
import graphql.language.SelectionSet;
import graphql.schema.GraphQLCompositeType;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLInterfaceType;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLSchema;
import graphql.schema.GraphQLTypeUtil;
import graphql.schema.GraphQLUnionType;
import graphql.schema.GraphQLUnmodifiedType;

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.Set;

import static graphql.Assert.assertNotNull;
import static graphql.introspection.Introspection.SchemaMetaFieldDef;
import static graphql.introspection.Introspection.TypeMetaFieldDef;
import static graphql.introspection.Introspection.TypeNameMetaFieldDef;


/**
* Creates a the direct NormalizedFields children, this means it goes only one level deep!
* This also means the NormalizedFields returned dont have any children.
*/
@Internal
public class FieldCollectorNormalizedQuery {

private final ConditionalNodes conditionalNodes = new ConditionalNodes();
private final ValuesResolver valuesResolver = new ValuesResolver();

public static class CollectFieldResult {
private final List<NormalizedField> children;
private final Map<NormalizedField, MergedField> mergedFieldByNormalized;

public CollectFieldResult(List<NormalizedField> children, Map<NormalizedField, MergedField> mergedFieldByNormalized) {
this.children = children;
this.mergedFieldByNormalized = mergedFieldByNormalized;
}

public List<NormalizedField> getChildren() {
return children;
}

public Map<NormalizedField, MergedField> getMergedFieldByNormalized() {
return mergedFieldByNormalized;
}
}


public CollectFieldResult collectFields(FieldCollectorNormalizedQueryParams parameters, NormalizedField normalizedField, MergedField mergedField, int level) {
GraphQLUnmodifiedType fieldType = GraphQLTypeUtil.unwrapAll(normalizedField.getFieldDefinition().getType());
// if not composite we don't have any selectionSet because it is a Scalar or enum
if (!(fieldType instanceof GraphQLCompositeType)) {
return new CollectFieldResult(Collections.emptyList(), Collections.emptyMap());
}

// result key -> ObjectType -> NormalizedField
Map<String, Map<GraphQLObjectType, NormalizedField>> subFields = new LinkedHashMap<>();
Map<NormalizedField, MergedField> mergedFieldByNormalizedField = new LinkedHashMap<>();
List<String> visitedFragments = new ArrayList<>();
Set<GraphQLObjectType> possibleObjects
= new LinkedHashSet<>(resolvePossibleObjects((GraphQLCompositeType) fieldType, parameters.getGraphQLSchema()));
for (Field field : mergedField.getFields()) {
if (field.getSelectionSet() == null) {
continue;
}
this.collectFields(parameters,
field.getSelectionSet(),
visitedFragments,
subFields,
mergedFieldByNormalizedField,
possibleObjects,
level,
normalizedField);
}
List<NormalizedField> children = subFieldsToList(subFields);
return new CollectFieldResult(children, mergedFieldByNormalizedField);
}

public CollectFieldResult collectFromOperation(FieldCollectorNormalizedQueryParams parameters,
OperationDefinition operationDefinition,
GraphQLObjectType rootType) {
Map<String, Map<GraphQLObjectType, NormalizedField>> subFields = new LinkedHashMap<>();
Map<NormalizedField, MergedField> mergedFieldByNormalizedField = new LinkedHashMap<>();
List<String> visitedFragments = new ArrayList<>();
Set<GraphQLObjectType> possibleObjects = new LinkedHashSet<>();
possibleObjects.add(rootType);
this.collectFields(parameters, operationDefinition.getSelectionSet(), visitedFragments, subFields, mergedFieldByNormalizedField, possibleObjects, 1, null);
List<NormalizedField> children = subFieldsToList(subFields);
return new CollectFieldResult(children, mergedFieldByNormalizedField);
}

private List<NormalizedField> subFieldsToList(Map<String, Map<GraphQLObjectType, NormalizedField>> subFields) {
List<NormalizedField> children = new ArrayList<>();
subFields.values().forEach(setMergedFieldWTCMap -> {
children.addAll(setMergedFieldWTCMap.values());
});
return children;
}


private void collectFields(FieldCollectorNormalizedQueryParams parameters,
SelectionSet selectionSet,
List<String> visitedFragments,
Map<String, Map<GraphQLObjectType, NormalizedField>> result,
Map<NormalizedField, MergedField> mergedFieldByNormalizedField,
Set<GraphQLObjectType> possibleObjects,
int level,
NormalizedField parent) {

for (Selection selection : selectionSet.getSelections()) {
if (selection instanceof Field) {
collectField(parameters, result, mergedFieldByNormalizedField, (Field) selection, possibleObjects, level, parent);
} else if (selection instanceof InlineFragment) {
collectInlineFragment(parameters, visitedFragments, result, mergedFieldByNormalizedField, (InlineFragment) selection, possibleObjects, level, parent);
} else if (selection instanceof FragmentSpread) {
collectFragmentSpread(parameters, visitedFragments, result, mergedFieldByNormalizedField, (FragmentSpread) selection, possibleObjects, level, parent);
}
}
}

private void collectFragmentSpread(FieldCollectorNormalizedQueryParams parameters,
List<String> visitedFragments,
Map<String, Map<GraphQLObjectType, NormalizedField>> result,
Map<NormalizedField, MergedField> mergedFieldByNormalizedField,
FragmentSpread fragmentSpread,
Set<GraphQLObjectType> possibleObjects,
int level,
NormalizedField parent) {
if (visitedFragments.contains(fragmentSpread.getName())) {
return;
}
if (!conditionalNodes.shouldInclude(parameters.getVariables(), fragmentSpread.getDirectives())) {
return;
}
visitedFragments.add(fragmentSpread.getName());
FragmentDefinition fragmentDefinition = assertNotNull(parameters.getFragmentsByName().get(fragmentSpread.getName()));

if (!conditionalNodes.shouldInclude(parameters.getVariables(), fragmentDefinition.getDirectives())) {
return;
}
GraphQLCompositeType newCondition = (GraphQLCompositeType) parameters.getGraphQLSchema().getType(fragmentDefinition.getTypeCondition().getName());
Set<GraphQLObjectType> newConditions = narrowDownPossibleObjects(possibleObjects, newCondition, parameters.getGraphQLSchema());
collectFields(parameters, fragmentDefinition.getSelectionSet(), visitedFragments, result, mergedFieldByNormalizedField, newConditions, level, parent);
}

private void collectInlineFragment(FieldCollectorNormalizedQueryParams parameters,
List<String> visitedFragments,
Map<String, Map<GraphQLObjectType, NormalizedField>> result,
Map<NormalizedField, MergedField> mergedFieldByNormalizedField,
InlineFragment inlineFragment,
Set<GraphQLObjectType> possibleObjects,
int level, NormalizedField parent) {
if (!conditionalNodes.shouldInclude(parameters.getVariables(), inlineFragment.getDirectives())) {
return;
}
Set<GraphQLObjectType> newPossibleObjects = possibleObjects;

if (inlineFragment.getTypeCondition() != null) {
GraphQLCompositeType newCondition = (GraphQLCompositeType) parameters.getGraphQLSchema().getType(inlineFragment.getTypeCondition().getName());
newPossibleObjects = narrowDownPossibleObjects(possibleObjects, newCondition, parameters.getGraphQLSchema());

}
collectFields(parameters, inlineFragment.getSelectionSet(), visitedFragments, result, mergedFieldByNormalizedField, newPossibleObjects, level, parent);
}

private void collectField(FieldCollectorNormalizedQueryParams parameters,
Map<String, Map<GraphQLObjectType, NormalizedField>> result,
Map<NormalizedField, MergedField> mergedFieldByNormalizedField,
Field field,
Set<GraphQLObjectType> objectTypes,
int level,
NormalizedField parent) {
if (!conditionalNodes.shouldInclude(parameters.getVariables(), field.getDirectives())) {
return;
}
String name = getFieldEntryKey(field);
result.computeIfAbsent(name, ignored -> new LinkedHashMap<>());
Map<GraphQLObjectType, NormalizedField> existingFieldWTC = result.get(name);

for (GraphQLObjectType objectType : objectTypes) {

if (existingFieldWTC.containsKey(objectType)) {
NormalizedField normalizedField = existingFieldWTC.get(objectType);

MergedField mergedField1 = mergedFieldByNormalizedField.get(normalizedField);
MergedField updatedMergedField = mergedField1.transform(builder -> builder.addField(field));
mergedFieldByNormalizedField.put(normalizedField, updatedMergedField);

} else {
GraphQLFieldDefinition fieldDefinition;
if (field.getName().equals(TypeNameMetaFieldDef.getName())) {
fieldDefinition = TypeNameMetaFieldDef;
} else if (field.getName().equals(Introspection.SchemaMetaFieldDef.getName())) {
fieldDefinition = SchemaMetaFieldDef;
} else if (field.getName().equals(Introspection.TypeMetaFieldDef.getName())) {
fieldDefinition = TypeMetaFieldDef;
} else {
fieldDefinition = assertNotNull(objectType.getFieldDefinition(field.getName()), () -> String.format("no field with name %s found in object %s", field.getName(), objectType.getName()));
}

Map<String, Object> argumentValues = valuesResolver.getArgumentValues(fieldDefinition.getArguments(), field.getArguments(), parameters.getVariables());
NormalizedField newFieldWTC = NormalizedField.newQueryExecutionField()
.alias(field.getAlias())
.arguments(argumentValues)
.objectType(objectType)
.fieldDefinition(fieldDefinition)
.level(level)
.parent(parent)
.build();
existingFieldWTC.put(objectType, newFieldWTC);
mergedFieldByNormalizedField.put(newFieldWTC, MergedField.newMergedField(field).build());
}
}
}


private String getFieldEntryKey(Field field) {
if (field.getAlias() != null) {
return field.getAlias();
} else {
return field.getName();
}
}

private Set<GraphQLObjectType> narrowDownPossibleObjects(Set<GraphQLObjectType> currentOnes,
GraphQLCompositeType typeCondition,
GraphQLSchema graphQLSchema) {

List<GraphQLObjectType> resolvedTypeCondition = resolvePossibleObjects(typeCondition, graphQLSchema);
if (currentOnes.size() == 0) {
return new LinkedHashSet<>(resolvedTypeCondition);
}

Set<GraphQLObjectType> result = new LinkedHashSet<>(currentOnes);
result.retainAll(resolvedTypeCondition);
return result;
}

private List<GraphQLObjectType> resolvePossibleObjects(GraphQLCompositeType type, GraphQLSchema graphQLSchema) {
if (type instanceof GraphQLObjectType) {
return Collections.singletonList((GraphQLObjectType) type);
} else if (type instanceof GraphQLInterfaceType) {
return graphQLSchema.getImplementations((GraphQLInterfaceType) type);
} else if (type instanceof GraphQLUnionType) {
List types = ((GraphQLUnionType) type).getTypes();
return new ArrayList<GraphQLObjectType>(types);
} else {
return Assert.assertShouldNeverHappen();
}

}

}
Loading