Skip to content

Commit 43eac34

Browse files
authored
Now handles CompletableFuture coming back from an un batched DF (graphql-java#696)
* Now handles CompletableFuture coming back from an un batched DF * Made it return the signature that was there before * Now uses Async.each
1 parent 5b5a30f commit 43eac34

File tree

7 files changed

+183
-105
lines changed

7 files changed

+183
-105
lines changed

src/main/java/graphql/execution/Async.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
import java.util.List;
99
import java.util.concurrent.CompletableFuture;
1010
import java.util.concurrent.CompletionException;
11+
import java.util.concurrent.CompletionStage;
1112
import java.util.function.BiFunction;
13+
import java.util.stream.Collectors;
1214

1315
@Internal
1416
public class Async {
@@ -20,6 +22,7 @@ public interface CFFactory<T, U> {
2022

2123
public static <U> CompletableFuture<List<U>> each(List<CompletableFuture<U>> futures) {
2224
CompletableFuture<List<U>> overallResult = new CompletableFuture<>();
25+
2326
CompletableFuture
2427
.allOf(futures.toArray(new CompletableFuture[futures.size()]))
2528
.whenComplete((noUsed, exception) -> {
@@ -82,4 +85,23 @@ private static <T, U> void eachSequentiallyImpl(Iterator<T> iterator, CFFactory<
8285
eachSequentiallyImpl(iterator, cfFactory, index + 1, tmpResult, overallResult);
8386
});
8487
}
88+
89+
90+
/**
91+
* Turns an object T into a CompletableFuture if its not already
92+
*
93+
* @param t - the object to check
94+
* @param <T> for two
95+
*
96+
* @return a CompletableFuture
97+
*/
98+
public static <T> CompletableFuture<T> toCompletableFuture(T t) {
99+
if (t instanceof CompletionStage) {
100+
//noinspection unchecked
101+
return ((CompletionStage<T>) t).toCompletableFuture();
102+
} else {
103+
return CompletableFuture.completedFuture(t);
104+
}
105+
}
106+
85107
}

src/main/java/graphql/execution/ExecutionStrategy.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import java.util.Optional;
3737
import java.util.concurrent.CompletableFuture;
3838
import java.util.concurrent.CompletionException;
39-
import java.util.concurrent.CompletionStage;
4039
import java.util.stream.IntStream;
4140

4241
import static graphql.execution.ExecutionTypeInfo.newTypeInfo;
@@ -218,11 +217,7 @@ protected CompletableFuture<Object> fetchField(ExecutionContext executionContext
218217
dataFetcher = instrumentation.instrumentDataFetcher(dataFetcher, instrumentationFieldFetchParams);
219218
try {
220219
Object fetchedValueRaw = dataFetcher.get(environment);
221-
if (fetchedValueRaw instanceof CompletionStage) {
222-
fetchedValue = ((CompletionStage) fetchedValueRaw).toCompletableFuture();
223-
} else {
224-
fetchedValue = CompletableFuture.completedFuture(fetchedValueRaw);
225-
}
220+
fetchedValue = Async.toCompletableFuture(fetchedValueRaw);
226221
} catch (Exception e) {
227222
fetchedValue = new CompletableFuture<>();
228223
fetchedValue.completeExceptionally(e);
@@ -404,6 +399,8 @@ protected Object unboxPossibleOptional(Object result) {
404399
/**
405400
* Converts an object that is known to should be an Iterable into one
406401
*
402+
* @param result the result object
403+
*
407404
* @return an Iterable from that object
408405
*
409406
* @throws java.lang.ClassCastException if its not an Iterable

src/main/java/graphql/execution/batched/BatchedExecutionStrategy.java

Lines changed: 48 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import graphql.ExecutionResultImpl;
66
import graphql.GraphQLException;
77
import graphql.PublicApi;
8+
import graphql.execution.Async;
89
import graphql.execution.DataFetcherExceptionHandler;
910
import graphql.execution.DataFetcherExceptionHandlerParameters;
1011
import graphql.execution.ExecutionContext;
@@ -47,7 +48,6 @@
4748
import java.util.Queue;
4849
import java.util.concurrent.CompletableFuture;
4950
import java.util.concurrent.CompletionException;
50-
import java.util.concurrent.CompletionStage;
5151
import java.util.function.BiFunction;
5252
import java.util.stream.IntStream;
5353

@@ -160,6 +160,7 @@ private void executeImpl(ExecutionContext executionContext,
160160

161161
ExecutionNode finalCurNode = curNode;
162162
Iterator<String> finalCurFieldNames = curFieldNames;
163+
163164
resolveField(executionContext, newParameters, fieldName, curNode)
164165
.whenComplete((childNodes, exception) -> {
165166
if (exception != null) {
@@ -201,6 +202,52 @@ private CompletableFuture<List<ExecutionNode>> resolveField(ExecutionContext exe
201202

202203
}
203204

205+
private CompletableFuture<List<FetchedValue>> fetchData(ExecutionContext executionContext,
206+
ExecutionStrategyParameters parameters,
207+
String fieldName,
208+
ExecutionNode node,
209+
GraphQLFieldDefinition fieldDef) {
210+
GraphQLObjectType parentType = node.getType();
211+
List<Field> fields = node.getFields().get(fieldName);
212+
List<MapOrList> parentResults = node.getParentResults();
213+
214+
Map<String, Object> argumentValues = valuesResolver.getArgumentValues(
215+
fieldDef.getArguments(), fields.get(0).getArguments(), executionContext.getVariables());
216+
217+
GraphQLOutputType fieldType = fieldDef.getType();
218+
DataFetchingFieldSelectionSet fieldCollector = DataFetchingFieldSelectionSetImpl.newCollector(executionContext, fieldType, fields);
219+
220+
DataFetchingEnvironment environment = newDataFetchingEnvironment(executionContext)
221+
.source(node.getSources())
222+
.arguments(argumentValues)
223+
.fieldDefinition(fieldDef)
224+
.fields(fields)
225+
.fieldType(fieldDef.getType())
226+
.fieldTypeInfo(parameters.typeInfo())
227+
.parentType(parentType)
228+
.selectionSet(fieldCollector)
229+
.build();
230+
231+
Instrumentation instrumentation = executionContext.getInstrumentation();
232+
InstrumentationContext<Object> fetchCtx = instrumentation.beginFieldFetch(
233+
new InstrumentationFieldFetchParameters(executionContext, fieldDef, environment)
234+
);
235+
236+
CompletableFuture<Object> fetchedValue;
237+
try {
238+
BatchedDataFetcher dataFetcher = getDataFetcher(fieldDef);
239+
Object fetchedValueRaw = dataFetcher.get(environment);
240+
fetchedValue = Async.toCompletableFuture(fetchedValueRaw);
241+
} catch (Exception e) {
242+
fetchedValue = new CompletableFuture<>();
243+
fetchedValue.completeExceptionally(e);
244+
}
245+
return fetchedValue
246+
.thenApply((result) -> assertResult(parentResults, result))
247+
.whenComplete(fetchCtx::onEnd)
248+
.handle(handleResult(executionContext, parameters, parentResults, fields, fieldDef, argumentValues, environment));
249+
}
250+
204251
private List<ExecutionNode> completeValues(ExecutionContext executionContext, GraphQLObjectType parentType,
205252
List<FetchedValue> fetchedValues, String fieldName, List<Field> fields,
206253
GraphQLOutputType fieldType, ExecutionTypeInfo typeInfo, Map<String, Object> argumentValues) {
@@ -373,56 +420,6 @@ private boolean isObject(GraphQLType type) {
373420
type instanceof GraphQLUnionType;
374421
}
375422

376-
@SuppressWarnings("unchecked")
377-
private CompletableFuture<List<FetchedValue>> fetchData(ExecutionContext executionContext,
378-
ExecutionStrategyParameters parameters,
379-
String fieldName,
380-
ExecutionNode node,
381-
GraphQLFieldDefinition fieldDef) {
382-
GraphQLObjectType parentType = node.getType();
383-
List<Field> fields = node.getFields().get(fieldName);
384-
List<MapOrList> parentResults = node.getParentResults();
385-
386-
Map<String, Object> argumentValues = valuesResolver.getArgumentValues(
387-
fieldDef.getArguments(), fields.get(0).getArguments(), executionContext.getVariables());
388-
389-
GraphQLOutputType fieldType = fieldDef.getType();
390-
DataFetchingFieldSelectionSet fieldCollector = DataFetchingFieldSelectionSetImpl.newCollector(executionContext, fieldType, fields);
391-
392-
DataFetchingEnvironment environment = newDataFetchingEnvironment(executionContext)
393-
.source(node.getSources())
394-
.arguments(argumentValues)
395-
.fieldDefinition(fieldDef)
396-
.fields(fields)
397-
.fieldType(fieldDef.getType())
398-
.fieldTypeInfo(parameters.typeInfo())
399-
.parentType(parentType)
400-
.selectionSet(fieldCollector)
401-
.build();
402-
403-
Instrumentation instrumentation = executionContext.getInstrumentation();
404-
InstrumentationContext<Object> fetchCtx = instrumentation.beginFieldFetch(
405-
new InstrumentationFieldFetchParameters(executionContext, fieldDef, environment)
406-
);
407-
408-
CompletableFuture<Object> valuesFuture;
409-
try {
410-
Object rawValue = getDataFetcher(fieldDef).get(environment);
411-
if (rawValue instanceof CompletionStage) {
412-
valuesFuture = ((CompletionStage) rawValue).toCompletableFuture();
413-
} else {
414-
valuesFuture = CompletableFuture.completedFuture(rawValue);
415-
}
416-
} catch (Exception e) {
417-
valuesFuture = new CompletableFuture<>();
418-
valuesFuture.completeExceptionally(e);
419-
}
420-
return valuesFuture
421-
.thenApply((result) -> assertResult(parentResults, result))
422-
.whenComplete(fetchCtx::onEnd)
423-
.handle(handleResult(executionContext, parameters, parentResults, fields, fieldDef, argumentValues, environment));
424-
}
425-
426423
private BiFunction<List<Object>, Throwable, List<FetchedValue>> handleResult(ExecutionContext executionContext, ExecutionStrategyParameters parameters, List<MapOrList> parentResults, List<Field> fields, GraphQLFieldDefinition fieldDef, Map<String, Object> argumentValues, DataFetchingEnvironment environment) {
427424
return (result, exception) -> {
428425
if (exception != null) {

src/main/java/graphql/execution/batched/UnbatchedDataFetcher.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import java.util.ArrayList;
99
import java.util.List;
1010
import java.util.concurrent.CompletableFuture;
11-
import java.util.concurrent.CompletionStage;
1211

1312
import static graphql.schema.DataFetchingEnvironmentBuilder.newDataFetchingEnvironment;
1413

@@ -34,16 +33,9 @@ public CompletableFuture<List<Object>> get(DataFetchingEnvironment environment)
3433

3534
DataFetchingEnvironment singleEnv = newDataFetchingEnvironment(environment)
3635
.source(source).build();
37-
results.add(getResult(delegate.get(singleEnv)));
36+
CompletableFuture<Object> cf = Async.toCompletableFuture(delegate.get(singleEnv));
37+
results.add(cf);
3838
}
39-
4039
return Async.each(results);
4140
}
42-
43-
private CompletableFuture<Object> getResult(Object rawResult) {
44-
if (!(rawResult instanceof CompletionStage)) {
45-
return CompletableFuture.completedFuture(rawResult);
46-
}
47-
return ((CompletionStage) rawResult).toCompletableFuture();
48-
}
4941
}

src/test/groovy/graphql/execution/AsyncTest.groovy

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,5 +141,4 @@ class AsyncTest extends Specification {
141141
result.isDone()
142142
result.get() == ['x', 'y', 'z']
143143
}
144-
145144
}

src/test/groovy/graphql/execution/batched/BatchedExecutionStrategyTest.groovy

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,4 +400,22 @@ class BatchedExecutionStrategyTest extends Specification {
400400
runTest(query, expected)
401401
}
402402

403+
def "#672-683 handles completable futures ok"() {
404+
405+
given:
406+
String query = "{ string(value: \"test\"){ completableFuture } }"
407+
Map<String, Object> expected = ["string": ["completableFuture": "completableFuture"]]
408+
expect:
409+
runTest(query, expected)
410+
}
411+
412+
def "#672-683 handles completable futures ok in interfaces"() {
413+
414+
given:
415+
String query = "{ interface { value } }"
416+
Map<String, Object> expected = ["interface": ["value": "interfacesHandled"]]
417+
expect:
418+
runTest(query, expected)
419+
}
420+
403421
}

0 commit comments

Comments
 (0)