Skip to content

Commit 8d382e6

Browse files
committed
Merge branch 'master' into defer-validation
* master: (105 commits) Avoid repeated Map lookups in SimpleFieldValidation Fix flaky defer test Remove invalid schema example Add more test cases cleanup formatting use ReentrantLock instead of synchronize PR feedback and cleanup PR feedback and cleanup PR feedback and cleanup PR feedback test cleanup naming formatting fix test tests tests wip wip more handling of applied argument cases ... # Conflicts: # src/main/java/graphql/validation/Validator.java
2 parents 10ff520 + 4c97ad2 commit 8d382e6

105 files changed

Lines changed: 7063 additions & 1176 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/invoke_test_runner.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050

5151
- id: 'auth'
5252
name: 'Authenticate to Google Cloud'
53-
uses: google-github-actions/auth@v2.1.0
53+
uses: google-github-actions/auth@v2.1.1
5454
with:
5555
credentials_json: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
5656

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ dependencies {
109109
testImplementation 'org.codehaus.groovy:groovy:3.0.20'
110110
testImplementation 'org.codehaus.groovy:groovy-json:3.0.20'
111111
testImplementation 'com.google.code.gson:gson:2.10.1'
112-
testImplementation 'org.eclipse.jetty:jetty-server:11.0.15'
112+
testImplementation 'org.eclipse.jetty:jetty-server:11.0.20'
113113
testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.16.1'
114114
testImplementation 'org.awaitility:awaitility-groovy:4.2.0'
115115
testImplementation 'com.github.javafaker:javafaker:1.0.2'
@@ -347,8 +347,8 @@ nexusPublishing {
347347
}
348348
}
349349

350-
// to publish to local maven repo skip signing: ./gradlew publishToMavenLocal -x signGraphqlJavaPublication
351350
signing {
351+
required { !project.hasProperty('publishToMavenLocal') }
352352
def signingKey = System.env.MAVEN_CENTRAL_PGP_KEY
353353
useInMemoryPgpKeys(signingKey, "")
354354
sign publishing.publications

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

src/main/java/graphql/ExecutionInput.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import graphql.collect.ImmutableKit;
44
import graphql.execution.ExecutionId;
55
import graphql.execution.RawVariables;
6-
import graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentationState;
76
import org.dataloader.DataLoaderRegistry;
87

98
import java.util.Locale;
@@ -12,6 +11,7 @@
1211
import java.util.function.UnaryOperator;
1312

1413
import static graphql.Assert.assertNotNull;
14+
import static graphql.execution.instrumentation.dataloader.EmptyDataLoaderRegistryInstance.EMPTY_DATALOADER_REGISTRY;
1515

1616
/**
1717
* This represents the series of values that can be input on a graphql query execution
@@ -213,7 +213,7 @@ public static class Builder {
213213
// this is important - it allows code to later known if we never really set a dataloader and hence it can optimize
214214
// dataloader field tracking away.
215215
//
216-
private DataLoaderRegistry dataLoaderRegistry = DataLoaderDispatcherInstrumentationState.EMPTY_DATALOADER_REGISTRY;
216+
private DataLoaderRegistry dataLoaderRegistry = EMPTY_DATALOADER_REGISTRY;
217217
private Locale locale = Locale.getDefault();
218218
private ExecutionId executionId;
219219

@@ -383,4 +383,4 @@ public ExecutionInput build() {
383383
return new ExecutionInput(this);
384384
}
385385
}
386-
}
386+
}

src/main/java/graphql/ExecutionResultImpl.java

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ public ExecutionResultImpl(ExecutionResultImpl other) {
4040
this(other.dataPresent, other.data, other.errors, other.extensions);
4141
}
4242

43+
public <T extends Builder<T>> ExecutionResultImpl(Builder<T> builder) {
44+
this(builder.dataPresent, builder.data, builder.errors, builder.extensions);
45+
}
46+
4347
private ExecutionResultImpl(boolean dataPresent, Object data, List<? extends GraphQLError> errors, Map<Object, Object> extensions) {
4448
this.dataPresent = dataPresent;
4549
this.data = data;
@@ -103,61 +107,61 @@ public String toString() {
103107
'}';
104108
}
105109

106-
public static Builder newExecutionResult() {
107-
return new Builder();
110+
public static <T extends Builder<T>> Builder<T> newExecutionResult() {
111+
return new Builder<>();
108112
}
109113

110-
public static class Builder implements ExecutionResult.Builder<Builder> {
114+
public static class Builder<T extends Builder<T>> implements ExecutionResult.Builder<T> {
111115
private boolean dataPresent;
112116
private Object data;
113117
private List<GraphQLError> errors = new ArrayList<>();
114118
private Map<Object, Object> extensions;
115119

116120
@Override
117-
public Builder from(ExecutionResult executionResult) {
121+
public T from(ExecutionResult executionResult) {
118122
dataPresent = executionResult.isDataPresent();
119123
data = executionResult.getData();
120124
errors = new ArrayList<>(executionResult.getErrors());
121125
extensions = executionResult.getExtensions();
122-
return this;
126+
return (T) this;
123127
}
124128

125129
@Override
126-
public Builder data(Object data) {
130+
public T data(Object data) {
127131
dataPresent = true;
128132
this.data = data;
129-
return this;
133+
return (T) this;
130134
}
131135

132136
@Override
133-
public Builder errors(List<GraphQLError> errors) {
137+
public T errors(List<GraphQLError> errors) {
134138
this.errors = errors;
135-
return this;
139+
return (T) this;
136140
}
137141

138142
@Override
139-
public Builder addErrors(List<GraphQLError> errors) {
143+
public T addErrors(List<GraphQLError> errors) {
140144
this.errors.addAll(errors);
141-
return this;
145+
return (T) this;
142146
}
143147

144148
@Override
145-
public Builder addError(GraphQLError error) {
149+
public T addError(GraphQLError error) {
146150
this.errors.add(error);
147-
return this;
151+
return (T) this;
148152
}
149153

150154
@Override
151-
public Builder extensions(Map<Object, Object> extensions) {
155+
public T extensions(Map<Object, Object> extensions) {
152156
this.extensions = extensions;
153-
return this;
157+
return (T) this;
154158
}
155159

156160
@Override
157-
public Builder addExtension(String key, Object value) {
161+
public T addExtension(String key, Object value) {
158162
this.extensions = (this.extensions == null ? new LinkedHashMap<>() : this.extensions);
159163
this.extensions.put(key, value);
160-
return this;
164+
return (T) this;
161165
}
162166

163167
@Override

src/main/java/graphql/ExperimentalApi.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,8 @@
2020
@Target(value = {CONSTRUCTOR, METHOD, TYPE, FIELD})
2121
@Documented
2222
public @interface ExperimentalApi {
23+
/**
24+
* The key that should be associated with a boolean value which indicates whether @defer and @stream behaviour is enabled for this execution.
25+
*/
26+
String ENABLE_INCREMENTAL_SUPPORT = "ENABLE_INCREMENTAL_SUPPORT";
2327
}

src/main/java/graphql/GraphQL.java

Lines changed: 22 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,11 @@
1212
import graphql.execution.SimpleDataFetcherExceptionHandler;
1313
import graphql.execution.SubscriptionExecutionStrategy;
1414
import graphql.execution.ValueUnboxer;
15-
import graphql.execution.instrumentation.ChainedInstrumentation;
1615
import graphql.execution.instrumentation.DocumentAndVariables;
1716
import graphql.execution.instrumentation.Instrumentation;
1817
import graphql.execution.instrumentation.InstrumentationContext;
1918
import graphql.execution.instrumentation.InstrumentationState;
20-
import graphql.execution.instrumentation.NoContextChainedInstrumentation;
2119
import graphql.execution.instrumentation.SimplePerformantInstrumentation;
22-
import graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentation;
2320
import graphql.execution.instrumentation.parameters.InstrumentationCreateStateParameters;
2421
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
2522
import graphql.execution.instrumentation.parameters.InstrumentationValidationParameters;
@@ -30,7 +27,6 @@
3027
import graphql.schema.GraphQLSchema;
3128
import graphql.validation.ValidationError;
3229

33-
import java.util.ArrayList;
3430
import java.util.List;
3531
import java.util.Locale;
3632
import java.util.Optional;
@@ -96,6 +92,7 @@ public class GraphQL {
9692
private final Instrumentation instrumentation;
9793
private final PreparsedDocumentProvider preparsedDocumentProvider;
9894
private final ValueUnboxer valueUnboxer;
95+
private final boolean doNotAutomaticallyDispatchDataLoader;
9996

10097

10198
private GraphQL(Builder builder) {
@@ -107,6 +104,7 @@ private GraphQL(Builder builder) {
107104
this.instrumentation = assertNotNull(builder.instrumentation, () -> "instrumentation must not be null");
108105
this.preparsedDocumentProvider = assertNotNull(builder.preparsedDocumentProvider, () -> "preparsedDocumentProvider must be non null");
109106
this.valueUnboxer = assertNotNull(builder.valueUnboxer, () -> "valueUnboxer must not be null");
107+
this.doNotAutomaticallyDispatchDataLoader = builder.doNotAutomaticallyDispatchDataLoader;
110108
}
111109

112110
/**
@@ -151,6 +149,10 @@ public Instrumentation getInstrumentation() {
151149
return instrumentation;
152150
}
153151

152+
public boolean isDoNotAutomaticallyDispatchDataLoader() {
153+
return doNotAutomaticallyDispatchDataLoader;
154+
}
155+
154156
/**
155157
* @return the PreparsedDocumentProvider for this {@link GraphQL} instance
156158
*/
@@ -209,7 +211,7 @@ public static class Builder {
209211
private ExecutionIdProvider idProvider = DEFAULT_EXECUTION_ID_PROVIDER;
210212
private Instrumentation instrumentation = null; // deliberate default here
211213
private PreparsedDocumentProvider preparsedDocumentProvider = NoOpPreparsedDocumentProvider.INSTANCE;
212-
private boolean doNotAddDefaultInstrumentations = false;
214+
private boolean doNotAutomaticallyDispatchDataLoader = false;
213215
private ValueUnboxer valueUnboxer = ValueUnboxer.DEFAULT;
214216

215217

@@ -265,20 +267,15 @@ public Builder executionIdProvider(ExecutionIdProvider executionIdProvider) {
265267
return this;
266268
}
267269

270+
268271
/**
269-
* For performance reasons you can opt into situation where the default instrumentations (such
270-
* as {@link graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentation} will not be
271-
* automatically added into the graphql instance.
272-
* <p>
273-
* For most situations this is not needed unless you are really pushing the boundaries of performance
274-
* <p>
275-
* By default a certain graphql instrumentations will be added to the mix to more easily enable certain functionality. This
276-
* allows you to stop this behavior
272+
* Deactivates the automatic dispatching of DataLoaders.
273+
* If deactivated the user is responsible for dispatching the DataLoaders manually.
277274
*
278275
* @return this builder
279276
*/
280-
public Builder doNotAddDefaultInstrumentations() {
281-
this.doNotAddDefaultInstrumentations = true;
277+
public Builder doNotAutomaticallyDispatchDataLoader() {
278+
this.doNotAutomaticallyDispatchDataLoader = true;
282279
return this;
283280
}
284281

@@ -299,7 +296,9 @@ public GraphQL build() {
299296
this.subscriptionExecutionStrategy = new SubscriptionExecutionStrategy(this.defaultExceptionHandler);
300297
}
301298

302-
this.instrumentation = checkInstrumentationDefaultState(this.instrumentation, this.doNotAddDefaultInstrumentations);
299+
if (instrumentation == null) {
300+
this.instrumentation = SimplePerformantInstrumentation.INSTANCE;
301+
}
303302
return new GraphQL(this);
304303
}
305304
}
@@ -415,7 +414,7 @@ public CompletableFuture<ExecutionResult> executeAsync(UnaryOperator<ExecutionIn
415414
public CompletableFuture<ExecutionResult> executeAsync(ExecutionInput executionInput) {
416415
ExecutionInput executionInputWithId = ensureInputHasId(executionInput);
417416

418-
CompletableFuture<InstrumentationState> instrumentationStateCF = instrumentation.createStateAsync(new InstrumentationCreateStateParameters(this.graphQLSchema, executionInput));
417+
CompletableFuture<InstrumentationState> instrumentationStateCF = instrumentation.createStateAsync(new InstrumentationCreateStateParameters(this.graphQLSchema, executionInputWithId));
419418
return Async.orNullCompletedFuture(instrumentationStateCF).thenCompose(instrumentationState -> {
420419
try {
421420
InstrumentationExecutionParameters inputInstrumentationParameters = new InstrumentationExecutionParameters(executionInputWithId, this.graphQLSchema, instrumentationState);
@@ -540,42 +539,16 @@ private List<ValidationError> validate(ExecutionInput executionInput, Document d
540539
return validationErrors;
541540
}
542541

543-
private CompletableFuture<ExecutionResult> execute(ExecutionInput executionInput, Document document, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) {
542+
private CompletableFuture<ExecutionResult> execute(ExecutionInput executionInput,
543+
Document document,
544+
GraphQLSchema graphQLSchema,
545+
InstrumentationState instrumentationState
546+
) {
544547

545-
Execution execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, instrumentation, valueUnboxer);
548+
Execution execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, instrumentation, valueUnboxer, doNotAutomaticallyDispatchDataLoader);
546549
ExecutionId executionId = executionInput.getExecutionId();
547550

548551
return execution.execute(document, graphQLSchema, executionId, executionInput, instrumentationState);
549552
}
550553

551-
private static Instrumentation checkInstrumentationDefaultState(Instrumentation instrumentation, boolean doNotAddDefaultInstrumentations) {
552-
if (doNotAddDefaultInstrumentations) {
553-
return instrumentation == null ? SimplePerformantInstrumentation.INSTANCE : instrumentation;
554-
}
555-
if (instrumentation instanceof DataLoaderDispatcherInstrumentation) {
556-
return instrumentation;
557-
}
558-
if (instrumentation instanceof NoContextChainedInstrumentation) {
559-
return instrumentation;
560-
}
561-
if (instrumentation == null) {
562-
return new DataLoaderDispatcherInstrumentation();
563-
}
564-
565-
//
566-
// if we don't have a DataLoaderDispatcherInstrumentation in play, we add one. We want DataLoader to be 1st class in graphql without requiring
567-
// people to remember to wire it in. Later we may decide to have more default instrumentations but for now it's just the one
568-
//
569-
List<Instrumentation> instrumentationList = new ArrayList<>();
570-
if (instrumentation instanceof ChainedInstrumentation) {
571-
instrumentationList.addAll(((ChainedInstrumentation) instrumentation).getInstrumentations());
572-
} else {
573-
instrumentationList.add(instrumentation);
574-
}
575-
boolean containsDLInstrumentation = instrumentationList.stream().anyMatch(instr -> instr instanceof DataLoaderDispatcherInstrumentation);
576-
if (!containsDLInstrumentation) {
577-
instrumentationList.add(new DataLoaderDispatcherInstrumentation());
578-
}
579-
return new ChainedInstrumentation(instrumentationList);
580-
}
581554
}

src/main/java/graphql/GraphQLError.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ public interface GraphQLError extends Serializable {
3939
ErrorClassification getErrorType();
4040

4141
/**
42-
* The graphql spec says that the (optional) path field of any error should be a list
43-
* of path entries https://spec.graphql.org/October2021/#sec-Handling-Field-Errors
42+
* The graphql spec says that the (optional) path field of any error must be
43+
* a list of path entries starting at the root of the response
44+
* and ending with the field associated with the error
45+
* https://spec.graphql.org/draft/#sec-Errors.Error-Result-Format
4446
*
4547
* @return the path in list format
4648
*/

src/main/java/graphql/execution/AbstractAsyncExecutionStrategy.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,17 @@ public AbstractAsyncExecutionStrategy(DataFetcherExceptionHandler dataFetcherExc
2222
super(dataFetcherExceptionHandler);
2323
}
2424

25-
// This method is kept for backward compatibility. Prefer calling/overriding another handleResults method
26-
protected BiConsumer<List<ExecutionResult>, Throwable> handleResults(ExecutionContext executionContext, List<String> fieldNames, CompletableFuture<ExecutionResult> overallResult) {
27-
return (List<ExecutionResult> results, Throwable exception) -> {
25+
protected BiConsumer<List<Object>, Throwable> handleResults(ExecutionContext executionContext, List<String> fieldNames, CompletableFuture<ExecutionResult> overallResult) {
26+
return (List<Object> results, Throwable exception) -> {
2827
if (exception != null) {
2928
handleNonNullException(executionContext, overallResult, exception);
3029
return;
3130
}
3231
Map<String, Object> resolvedValuesByField = Maps.newLinkedHashMapWithExpectedSize(fieldNames.size());
3332
int ix = 0;
34-
for (ExecutionResult executionResult : results) {
33+
for (Object result : results) {
3534
String fieldName = fieldNames.get(ix++);
36-
resolvedValuesByField.put(fieldName, executionResult.getData());
35+
resolvedValuesByField.put(fieldName, result);
3736
}
3837
overallResult.complete(new ExecutionResultImpl(resolvedValuesByField, executionContext.getErrors()));
3938
};

0 commit comments

Comments
 (0)