11package graphql .execution ;
22
3+ import com .google .common .collect .ImmutableListMultimap ;
4+ import com .google .common .collect .ImmutableSet ;
35import graphql .ExecutionResult ;
46import graphql .PublicApi ;
5- import graphql .execution .defer .DeferSupport ;
7+ import graphql .execution .defer .DeferExecutionSupport ;
68import graphql .execution .defer .DeferredCall ;
79import graphql .execution .defer .DeferredErrorSupport ;
8- import graphql .execution .instrumentation . DeferredFieldInstrumentationContext ;
10+ import graphql .execution .incremental . DeferExecution ;
911import graphql .execution .instrumentation .ExecutionStrategyInstrumentationContext ;
1012import graphql .execution .instrumentation .Instrumentation ;
1113import graphql .execution .instrumentation .InstrumentationContext ;
1214import graphql .execution .instrumentation .parameters .InstrumentationDeferredFieldParameters ;
1315import graphql .execution .instrumentation .parameters .InstrumentationExecutionStrategyParameters ;
1416import graphql .schema .GraphQLFieldDefinition ;
1517import graphql .util .FpKit ;
18+ import graphql .util .Pair ;
1619
1720import java .util .LinkedHashMap ;
1821import java .util .List ;
1922import java .util .Map ;
23+ import java .util .Set ;
2024import java .util .concurrent .CompletableFuture ;
2125import java .util .function .BiConsumer ;
26+ import java .util .function .BiFunction ;
2227import java .util .function .Supplier ;
28+ import java .util .stream .Collectors ;
2329
2430import static graphql .execution .MergedSelectionSet .newMergedSelectionSet ;
2531
@@ -56,7 +62,20 @@ public CompletableFuture<ExecutionResult> execute(ExecutionContext executionCont
5662
5763 MergedSelectionSet fields = parameters .getFields ();
5864 List <String > fieldNames = fields .getKeys ();
59- Async .CombinedBuilder <FieldValueInfo > futures = Async .ofExpectedSize (fields .size ());
65+
66+ // if(true /* check if incremental support is enabled*/) {
67+ SomethingDefer somethingDefer = new SomethingDefer (
68+ fields ,
69+ parameters ,
70+ executionContext ,
71+ this ::resolveFieldWithInfo
72+ );
73+
74+ executionContext .getDeferSupport ().enqueue (somethingDefer .createCalls ());
75+ // }
76+
77+ Async .CombinedBuilder <FieldValueInfo > futures = Async .ofExpectedSize (fields .size () - somethingDefer .deferredFields .size ());
78+
6079 for (String fieldName : fieldNames ) {
6180 MergedField currentField = fields .getSubField (fieldName );
6281
@@ -66,13 +85,14 @@ public CompletableFuture<ExecutionResult> execute(ExecutionContext executionCont
6685
6786 CompletableFuture <FieldValueInfo > future ;
6887
69- if (isDeferred ( executionContext , newParameters , currentField )) {
88+ if (somethingDefer . isDeferredField ( currentField )) {
7089 executionStrategyCtx .onDeferredField (currentField );
71- future = resolveFieldWithInfoToNull (executionContext , newParameters );
90+ // future = resolveFieldWithInfoToNull(executionContext, newParameters);
7291 } else {
7392 future = resolveFieldWithInfo (executionContext , newParameters );
93+ futures .add (future );
7494 }
75- futures . add ( future );
95+
7696 }
7797 CompletableFuture <ExecutionResult > overallResult = new CompletableFuture <>();
7898 executionStrategyCtx .onDispatched (overallResult );
@@ -104,8 +124,9 @@ public CompletableFuture<ExecutionResult> execute(ExecutionContext executionCont
104124 }
105125
106126 private boolean isDeferred (ExecutionContext executionContext , ExecutionStrategyParameters parameters , MergedField currentField ) {
107- DeferSupport deferSupport = executionContext .getDeferSupport ();
108- if (deferSupport .checkForDeferDirective (currentField , executionContext )) {
127+ DeferExecutionSupport deferSupport = executionContext .getDeferSupport ();
128+
129+ if (currentField .getDeferExecutions () != null && !currentField .getDeferExecutions ().isEmpty ()) {
109130 DeferredErrorSupport errorSupport = new DeferredErrorSupport ();
110131
111132 // with a deferred field we are really resetting where we execute from, that is from this current field onwards
@@ -122,8 +143,8 @@ private boolean isDeferred(ExecutionContext executionContext, ExecutionStrategyP
122143 }
123144 );
124145
125- DeferredCall call = new DeferredCall (parameters .getPath (), deferredExecutionResult (executionContext , callParameters ), errorSupport );
126- deferSupport .enqueue (call );
146+ // DeferredCall call = new DeferredCall(null /* TODO extract label somehow*/, parameters.getPath(), deferredExecutionResult(executionContext, callParameters), errorSupport);
147+ // deferSupport.enqueue(call);
127148 return true ;
128149 }
129150 return false ;
@@ -160,4 +181,112 @@ private Supplier<CompletableFuture<ExecutionResult>> deferredExecutionResult(Exe
160181 return result ;
161182 };
162183 }
184+
185+
186+ private static class SomethingDefer {
187+ private final ImmutableListMultimap <DeferExecution , MergedField > deferExecutionToFields ;
188+ private final ImmutableSet <MergedField > deferredFields ;
189+ private final ExecutionStrategyParameters parameters ;
190+ private final ExecutionContext executionContext ;
191+ private final BiFunction <ExecutionContext , ExecutionStrategyParameters , CompletableFuture <FieldValueInfo >> resolveFieldWithInfoFn ;
192+
193+ private SomethingDefer (
194+ MergedSelectionSet mergedSelectionSet ,
195+ ExecutionStrategyParameters parameters ,
196+ ExecutionContext executionContext , BiFunction <ExecutionContext , ExecutionStrategyParameters , CompletableFuture <FieldValueInfo >> resolveFieldWithInfoFn
197+ ) {
198+ this .executionContext = executionContext ;
199+ this .resolveFieldWithInfoFn = resolveFieldWithInfoFn ;
200+ ImmutableListMultimap .Builder <DeferExecution , MergedField > deferExecutionToFieldsBuilder = ImmutableListMultimap .builder ();
201+ ImmutableSet .Builder <MergedField > deferredFieldsBuilder = ImmutableSet .builder ();
202+
203+ mergedSelectionSet .getSubFields ().values ().forEach (mergedField -> {
204+ mergedField .getDeferExecutions ().forEach (de -> {
205+ deferExecutionToFieldsBuilder .put (de , mergedField );
206+ deferredFieldsBuilder .add (mergedField );
207+ });
208+ });
209+
210+ this .deferExecutionToFields = deferExecutionToFieldsBuilder .build ();
211+ this .deferredFields = deferredFieldsBuilder .build ();
212+ this .parameters = parameters ;
213+ }
214+
215+ private boolean isDeferredField (MergedField mergedField ) {
216+ return deferredFields .contains (mergedField );
217+ }
218+
219+ private Set <DeferredCall > createCalls () {
220+ return deferExecutionToFields .keySet ().stream ().map (deferExecution -> {
221+ DeferredErrorSupport errorSupport = new DeferredErrorSupport ();
222+
223+ List <MergedField > mergedFields = deferExecutionToFields .get (deferExecution );
224+
225+ List <Supplier <CompletableFuture <DeferredCall .FieldWithExecutionResult >>> collect = mergedFields .stream ()
226+ .map (currentField -> {
227+ Map <String , MergedField > fields = new LinkedHashMap <>();
228+ fields .put (currentField .getName (), currentField );
229+
230+ ExecutionStrategyParameters callParameters = parameters .transform (builder ->
231+ {
232+ MergedSelectionSet mergedSelectionSet = newMergedSelectionSet ().subFields (fields ).build ();
233+ builder .deferredErrorSupport (errorSupport )
234+ .field (currentField )
235+ .fields (mergedSelectionSet )
236+ .parent (null ); // this is a break in the parent -> child chain - its a new start effectively
237+ }
238+ );
239+
240+
241+ return (Supplier <CompletableFuture <DeferredCall .FieldWithExecutionResult >>) () -> resolveFieldWithInfoFn
242+ .apply (executionContext , callParameters )
243+ .thenCompose (FieldValueInfo ::getFieldValue )
244+ .thenApply (executionResult -> new DeferredCall .FieldWithExecutionResult (currentField .getName (), executionResult ));
245+
246+ })
247+ .collect (Collectors .toList ());
248+
249+ // with a deferred field we are really resetting where we execute from, that is from this current field onwards
250+ return new DeferredCall (
251+ deferExecution .getLabel (),
252+ this .parameters .getPath (),
253+ collect ,
254+ errorSupport
255+ );
256+ })
257+ .collect (Collectors .toSet ());
258+ }
259+
260+ @ SuppressWarnings ("FutureReturnValueIgnored" )
261+ private Supplier <CompletableFuture <ExecutionResult >> deferredExecutionResult (ExecutionContext executionContext , ExecutionStrategyParameters parameters ) {
262+ return () -> {
263+ // GraphQLFieldDefinition fieldDef = getFieldDef(executionContext, parameters, parameters.getField().getSingleField());
264+ // TODO: freis: This is suddenly not needed anymore
265+ // GraphQLObjectType fieldContainer = (GraphQLObjectType) parameters.getExecutionStepInfo().getUnwrappedNonNullType();
266+
267+ // Instrumentation instrumentation = executionContext.getInstrumentation();
268+
269+ // Supplier<ExecutionStepInfo> executionStepInfo = FpKit.intraThreadMemoize(() -> createExecutionStepInfo(executionContext, parameters, fieldDef, null));
270+
271+ // InstrumentationContext<ExecutionResult> fieldCtx = instrumentation.beginDeferredField(
272+ // new InstrumentationDeferredFieldParameters(executionContext, executionStepInfo, parameters),
273+ // executionContext.getInstrumentationState()
274+ // );
275+
276+ CompletableFuture <ExecutionResult > result = new CompletableFuture <>();
277+ // fieldCtx.onDispatched(result);
278+ CompletableFuture <FieldValueInfo > fieldValueInfoFuture = resolveFieldWithInfoFn .apply (executionContext , parameters );
279+
280+ fieldValueInfoFuture .whenComplete ((fieldValueInfo , throwable ) -> {
281+ // TODO:
282+ // fieldCtx.onFieldValueInfo(fieldValueInfo);
283+
284+ CompletableFuture <ExecutionResult > execResultFuture = fieldValueInfo .getFieldValue ();
285+ // execResultFuture = execResultFuture.whenComplete(fieldCtx::onCompleted);
286+ Async .copyResults (execResultFuture , result );
287+ });
288+ return result ;
289+ };
290+ }
291+ }
163292}
0 commit comments