Skip to content

Commit 9f65d67

Browse files
committed
This adds support for QueryAppliedDirective on operations and documents
1 parent 044d048 commit 9f65d67

File tree

8 files changed

+167
-29
lines changed

8 files changed

+167
-29
lines changed

src/main/java/graphql/execution/Execution.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import graphql.GraphQLException;
1313
import graphql.Internal;
1414
import graphql.Profiler;
15+
import graphql.execution.directives.OperationDirectivesResolver;
16+
import graphql.execution.directives.QueryAppliedDirective;
1517
import graphql.execution.incremental.IncrementalCallState;
1618
import graphql.execution.instrumentation.Instrumentation;
1719
import graphql.execution.instrumentation.InstrumentationContext;
@@ -40,6 +42,7 @@
4042

4143
import java.util.Collections;
4244
import java.util.List;
45+
import java.util.Locale;
4346
import java.util.Map;
4447
import java.util.concurrent.CompletableFuture;
4548
import java.util.function.Supplier;
@@ -55,6 +58,7 @@
5558
@Internal
5659
public class Execution {
5760
private final FieldCollector fieldCollector = new FieldCollector();
61+
private final OperationDirectivesResolver operationDirectivesResolver = new OperationDirectivesResolver();
5862
private final ExecutionStrategy queryStrategy;
5963
private final ExecutionStrategy mutationStrategy;
6064
private final ExecutionStrategy subscriptionStrategy;
@@ -100,9 +104,14 @@ public CompletableFuture<ExecutionResult> execute(Document document, GraphQLSche
100104

101105
boolean propagateErrorsOnNonNullContractFailure = propagateErrorsOnNonNullContractFailure(getOperationResult.operationDefinition.getDirectives());
102106

103-
ResponseMapFactory responseMapFactory = GraphQL.unusualConfiguration(executionInput.getGraphQLContext())
107+
GraphQLContext graphQLContext = executionInput.getGraphQLContext();
108+
Locale locale = executionInput.getLocale();
109+
110+
ResponseMapFactory responseMapFactory = GraphQL.unusualConfiguration(graphQLContext)
104111
.responseMapFactory().getOr(ResponseMapFactory.DEFAULT);
105112

113+
Map<OperationDefinition, List<QueryAppliedDirective>> opDirectivesMap = operationDirectivesResolver.resolveDirectives(document, graphQLSchema, coercedVariables, graphQLContext, locale);
114+
106115
ExecutionContext executionContext = newExecutionContextBuilder()
107116
.instrumentation(instrumentation)
108117
.instrumentationState(instrumentationState)
@@ -112,16 +121,17 @@ public CompletableFuture<ExecutionResult> execute(Document document, GraphQLSche
112121
.mutationStrategy(mutationStrategy)
113122
.subscriptionStrategy(subscriptionStrategy)
114123
.context(executionInput.getContext())
115-
.graphQLContext(executionInput.getGraphQLContext())
124+
.graphQLContext(graphQLContext)
116125
.localContext(executionInput.getLocalContext())
117126
.root(executionInput.getRoot())
118127
.fragmentsByName(getOperationResult.fragmentsByName)
119128
.coercedVariables(coercedVariables)
120129
.normalizedVariableValues(normalizedVariableValues)
121130
.document(document)
122131
.operationDefinition(getOperationResult.operationDefinition)
132+
.operationDirectives(opDirectivesMap)
123133
.dataLoaderRegistry(executionInput.getDataLoaderRegistry())
124-
.locale(executionInput.getLocale())
134+
.locale(locale)
125135
.valueUnboxer(valueUnboxer)
126136
.responseMapFactory(responseMapFactory)
127137
.executionInput(executionInput)

src/main/java/graphql/execution/ExecutionContext.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import graphql.Profiler;
1212
import graphql.PublicApi;
1313
import graphql.collect.ImmutableKit;
14+
import graphql.execution.directives.OperationDirectivesResolver;
15+
import graphql.execution.directives.QueryAppliedDirective;
1416
import graphql.execution.incremental.IncrementalCallState;
1517
import graphql.execution.instrumentation.Instrumentation;
1618
import graphql.execution.instrumentation.InstrumentationState;
@@ -73,6 +75,7 @@ public class ExecutionContext {
7375
private final ResultNodesInfo resultNodesInfo = new ResultNodesInfo();
7476
private final EngineRunningState engineRunningState;
7577

78+
private final Map<OperationDefinition, List<QueryAppliedDirective>> opDirectivesMap;
7679
private final Profiler profiler;
7780

7881
ExecutionContext(ExecutionContextBuilder builder) {
@@ -102,6 +105,7 @@ public class ExecutionContext {
102105
this.queryTree = FpKit.interThreadMemoize(() -> ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, operationDefinition, fragmentsByName, coercedVariables));
103106
this.propagateErrorsOnNonNullContractFailure = builder.propagateErrorsOnNonNullContractFailure;
104107
this.engineRunningState = builder.engineRunningState;
108+
this.opDirectivesMap = builder.opDirectivesMap;
105109
this.profiler = builder.profiler;
106110
}
107111

@@ -137,6 +141,22 @@ public OperationDefinition getOperationDefinition() {
137141
return operationDefinition;
138142
}
139143

144+
/**
145+
* @return the map of {@link QueryAppliedDirective}s by name that were on this executing operation
146+
*/
147+
public Map<String, List<QueryAppliedDirective>> getOperationDirectives() {
148+
List<QueryAppliedDirective> list = opDirectivesMap.get(getOperationDefinition());
149+
return OperationDirectivesResolver.toAppliedDirectivesByName(list);
150+
}
151+
152+
/**
153+
* @return the map of all the {@link QueryAppliedDirective}s that were on the {@link Document} including
154+
* {@link OperationDefinition}s that are not currently executing.
155+
*/
156+
public Map<OperationDefinition, List<QueryAppliedDirective>> getAllOperationDirectives() {
157+
return opDirectivesMap;
158+
}
159+
140160
public CoercedVariables getCoercedVariables() {
141161
return coercedVariables;
142162
}
@@ -156,7 +176,7 @@ public Supplier<NormalizedVariables> getNormalizedVariables() {
156176
* @deprecated use {@link #getGraphQLContext()} instead
157177
*/
158178
@Deprecated(since = "2021-07-05")
159-
@SuppressWarnings({ "unchecked", "TypeParameterUnusedInFormals" })
179+
@SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
160180
public @Nullable <T> T getContext() {
161181
return (T) context;
162182
}

src/main/java/graphql/execution/ExecutionContextBuilder.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import graphql.Internal;
1111
import graphql.Profiler;
1212
import graphql.collect.ImmutableKit;
13+
import graphql.execution.directives.QueryAppliedDirective;
1314
import graphql.execution.instrumentation.Instrumentation;
1415
import graphql.execution.instrumentation.InstrumentationState;
1516
import graphql.language.Document;
@@ -19,6 +20,8 @@
1920
import org.dataloader.DataLoaderRegistry;
2021
import org.jspecify.annotations.Nullable;
2122

23+
import java.util.Collections;
24+
import java.util.List;
2225
import java.util.Locale;
2326
import java.util.Map;
2427
import java.util.function.Supplier;
@@ -55,6 +58,7 @@ public class ExecutionContextBuilder {
5558
EngineRunningState engineRunningState;
5659
ResponseMapFactory responseMapFactory = ResponseMapFactory.DEFAULT;
5760
Profiler profiler;
61+
Map<OperationDefinition, List<QueryAppliedDirective>> opDirectivesMap = Collections.emptyMap();
5862

5963
/**
6064
* @return a new builder of {@link graphql.execution.ExecutionContext}s
@@ -168,6 +172,7 @@ public ExecutionContextBuilder root(Object root) {
168172

169173
/**
170174
* @param variables map of already coerced variables
175+
*
171176
* @return this builder
172177
*
173178
* @deprecated use {@link #coercedVariables(CoercedVariables)} instead
@@ -246,13 +251,6 @@ public ExecutionContextBuilder propagapropagateErrorsOnNonNullContractFailureeEr
246251
return this;
247252
}
248253

249-
250-
public ExecutionContext build() {
251-
// preconditions
252-
assertNotNull(executionId, "You must provide a query identifier");
253-
return new ExecutionContext(this);
254-
}
255-
256254
public ExecutionContextBuilder engineRunningState(EngineRunningState engineRunningState) {
257255
this.engineRunningState = engineRunningState;
258256
return this;
@@ -262,4 +260,16 @@ public ExecutionContextBuilder profiler(Profiler profiler) {
262260
this.profiler = profiler;
263261
return this;
264262
}
263+
264+
public ExecutionContextBuilder operationDirectives(Map<OperationDefinition, List<QueryAppliedDirective>> opDirectivesMap) {
265+
this.opDirectivesMap = opDirectivesMap;
266+
return this;
267+
}
268+
269+
270+
public ExecutionContext build() {
271+
// preconditions
272+
assertNotNull(executionId, "You must provide a query identifier");
273+
return new ExecutionContext(this);
274+
}
265275
}

src/main/java/graphql/execution/directives/DirectivesResolver.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.google.common.collect.BiMap;
44
import com.google.common.collect.HashBiMap;
55
import com.google.common.collect.ImmutableBiMap;
6+
import com.google.common.collect.ImmutableList;
67
import graphql.GraphQLContext;
78
import graphql.Internal;
89
import graphql.execution.CoercedVariables;
@@ -13,6 +14,7 @@
1314
import graphql.schema.GraphQLDirective;
1415
import graphql.schema.GraphQLSchema;
1516

17+
import java.util.Collection;
1618
import java.util.List;
1719
import java.util.Locale;
1820
import java.util.Map;
@@ -61,4 +63,31 @@ private void buildArguments(GraphQLDirective.Builder directiveBuilder,
6163
}
6264
});
6365
}
66+
67+
public List<QueryAppliedDirective> toAppliedDirectives(List<Directive> directives, GraphQLSchema schema, CoercedVariables variables, GraphQLContext graphQLContext, Locale locale) {
68+
BiMap<GraphQLDirective, Directive> directivesMap = resolveDirectives(directives, schema, variables, graphQLContext, locale);
69+
return toAppliedDirectives(directivesMap.keySet());
70+
}
71+
72+
public List<QueryAppliedDirective> toAppliedDirectives(Collection<GraphQLDirective> directives) {
73+
return directives.stream().map(this::toAppliedDirective).collect(ImmutableList.toImmutableList());
74+
}
75+
76+
public QueryAppliedDirective toAppliedDirective(GraphQLDirective directive) {
77+
QueryAppliedDirective.Builder builder = QueryAppliedDirective.newDirective();
78+
builder.name(directive.getName());
79+
for (GraphQLArgument argument : directive.getArguments()) {
80+
builder.argument(toAppliedArgument(argument));
81+
}
82+
return builder.build();
83+
}
84+
85+
public QueryAppliedDirectiveArgument toAppliedArgument(GraphQLArgument argument) {
86+
return QueryAppliedDirectiveArgument.newArgument()
87+
.name(argument.getName())
88+
.type(argument.getType())
89+
.inputValueWithState(argument.getArgumentValue())
90+
.build();
91+
}
92+
6493
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package graphql.execution.directives;
2+
3+
import com.google.common.collect.ImmutableMap;
4+
import graphql.GraphQLContext;
5+
import graphql.Internal;
6+
import graphql.execution.CoercedVariables;
7+
import graphql.language.Document;
8+
import graphql.language.OperationDefinition;
9+
import graphql.schema.GraphQLSchema;
10+
import org.jspecify.annotations.NullMarked;
11+
12+
import java.util.ArrayList;
13+
import java.util.LinkedHashMap;
14+
import java.util.List;
15+
import java.util.Locale;
16+
import java.util.Map;
17+
18+
@Internal
19+
@NullMarked
20+
public class OperationDirectivesResolver {
21+
22+
private final DirectivesResolver directivesResolver = new DirectivesResolver();
23+
24+
public Map<OperationDefinition, List<QueryAppliedDirective>> resolveDirectives(Document document, GraphQLSchema schema, CoercedVariables variables, GraphQLContext graphQLContext, Locale locale) {
25+
Map<OperationDefinition, List<QueryAppliedDirective>> map = new LinkedHashMap<>();
26+
List<OperationDefinition> operations = document.getDefinitionsOfType(OperationDefinition.class);
27+
for (OperationDefinition operationDefinition : operations) {
28+
map.put(operationDefinition, resolveDirectives(operationDefinition, schema, variables, graphQLContext, locale));
29+
}
30+
return ImmutableMap.copyOf(map);
31+
}
32+
33+
public List<QueryAppliedDirective> resolveDirectives(OperationDefinition operationDefinition, GraphQLSchema schema, CoercedVariables variables, GraphQLContext graphQLContext, Locale locale) {
34+
return directivesResolver.toAppliedDirectives(
35+
operationDefinition.getDirectives(),
36+
schema,
37+
variables,
38+
graphQLContext,
39+
locale
40+
);
41+
}
42+
43+
public Map<String, List<QueryAppliedDirective>> resolveDirectiveByName(OperationDefinition operationDefinition, GraphQLSchema schema, CoercedVariables variables, GraphQLContext graphQLContext, Locale locale) {
44+
List<QueryAppliedDirective> list = resolveDirectives(operationDefinition, schema, variables, graphQLContext, locale);
45+
return toAppliedDirectivesByName(list);
46+
}
47+
48+
public static Map<String, List<QueryAppliedDirective>> toAppliedDirectivesByName(List<QueryAppliedDirective> queryAppliedDirectives) {
49+
Map<String, List<QueryAppliedDirective>> map = new LinkedHashMap<>();
50+
for (QueryAppliedDirective queryAppliedDirective : queryAppliedDirectives) {
51+
List<QueryAppliedDirective> list = map.computeIfAbsent(queryAppliedDirective.getName(), k -> new ArrayList<>());
52+
list.add(queryAppliedDirective);
53+
}
54+
return ImmutableMap.copyOf(map);
55+
}
56+
57+
}

src/main/java/graphql/execution/directives/QueryDirectivesImpl.java

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ private void computeValuesLazily() {
8080

8181
ImmutableList.Builder<QueryAppliedDirective> appliedDirectiveBuilder = ImmutableList.builder();
8282
for (GraphQLDirective resolvedDirective : resolvedDirectives) {
83-
QueryAppliedDirective appliedDirective = toAppliedDirective(resolvedDirective);
83+
QueryAppliedDirective appliedDirective = directivesResolver.toAppliedDirective(resolvedDirective);
8484
appliedDirectiveBuilder.add(appliedDirective);
8585
gqlDirectiveCounterParts.put(resolvedDirective, appliedDirective);
8686
}
@@ -125,23 +125,6 @@ private void computeValuesLazily() {
125125
});
126126
}
127127

128-
private QueryAppliedDirective toAppliedDirective(GraphQLDirective directive) {
129-
QueryAppliedDirective.Builder builder = QueryAppliedDirective.newDirective();
130-
builder.name(directive.getName());
131-
for (GraphQLArgument argument : directive.getArguments()) {
132-
builder.argument(toAppliedArgument(argument));
133-
}
134-
return builder.build();
135-
}
136-
137-
private QueryAppliedDirectiveArgument toAppliedArgument(GraphQLArgument argument) {
138-
return QueryAppliedDirectiveArgument.newArgument()
139-
.name(argument.getName())
140-
.type(argument.getType())
141-
.inputValueWithState(argument.getArgumentValue())
142-
.build();
143-
}
144-
145128

146129
@Override
147130
public Map<Field, List<GraphQLDirective>> getImmediateDirectivesByField() {

src/main/java/graphql/normalized/ExecutableNormalizedOperation.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import graphql.PublicApi;
66
import graphql.execution.MergedField;
77
import graphql.execution.ResultPath;
8+
import graphql.execution.directives.QueryAppliedDirective;
89
import graphql.execution.directives.QueryDirectives;
910
import graphql.language.Field;
1011
import graphql.language.OperationDefinition;
@@ -25,6 +26,7 @@
2526
@PublicApi
2627
public class ExecutableNormalizedOperation {
2728
private final OperationDefinition.Operation operation;
29+
private final Map<String, List<QueryAppliedDirective>> operationDirectives;
2830
private final String operationName;
2931
private final List<ExecutableNormalizedField> topLevelFields;
3032
private final ImmutableListMultimap<Field, ExecutableNormalizedField> fieldToNormalizedField;
@@ -37,6 +39,7 @@ public class ExecutableNormalizedOperation {
3739
public ExecutableNormalizedOperation(
3840
OperationDefinition.Operation operation,
3941
String operationName,
42+
Map<String, List<QueryAppliedDirective>> operationDirectives,
4043
List<ExecutableNormalizedField> topLevelFields,
4144
ImmutableListMultimap<Field, ExecutableNormalizedField> fieldToNormalizedField,
4245
Map<ExecutableNormalizedField, MergedField> normalizedFieldToMergedField,
@@ -46,6 +49,7 @@ public ExecutableNormalizedOperation(
4649
int operationDepth) {
4750
this.operation = operation;
4851
this.operationName = operationName;
52+
this.operationDirectives = operationDirectives;
4953
this.topLevelFields = topLevelFields;
5054
this.fieldToNormalizedField = fieldToNormalizedField;
5155
this.normalizedFieldToMergedField = normalizedFieldToMergedField;
@@ -69,6 +73,20 @@ public String getOperationName() {
6973
return operationName;
7074
}
7175

76+
/**
77+
* This is the directives that are on the operation itself and not the fields under it for example
78+
* <pre>
79+
* {@code
80+
* query opName @foo { field }
81+
* }
82+
* </pre>
83+
*
84+
* @return the directives that are on this operation itself.
85+
*/
86+
public Map<String, List<QueryAppliedDirective>> getOperationDirectives() {
87+
return operationDirectives;
88+
}
89+
7290
/**
7391
* @return This returns how many {@link ExecutableNormalizedField}s are in the operation.
7492
*/

0 commit comments

Comments
 (0)