Skip to content

Commit 89a3373

Browse files
heoYHheo-d
andauthored
change preparsedDocumentProvider return value to CompletableFuture<PreparsedDocumentEntry> (#2612)
* change preparsedDocumentProvider return value PreparsedDocumentEntry to CompletableFuture<PreparsedDocumentEntry> * create async method because break the existing publicSpi interface * remove not use import * add javaDoc and rollback async method test * add test that sync method and async method result is same * explicit imports * change javaDoc do not use generic parameter * Add handling of AbortExecutionException thrown inside an asynchronous call * Add handling of AbortExecutionException thrown inside an asynchronous call Co-authored-by: heod <heo.d@kakaocorp.com>
1 parent dede00e commit 89a3373

File tree

5 files changed

+95
-11
lines changed

5 files changed

+95
-11
lines changed

src/main/java/graphql/GraphQL.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -530,12 +530,17 @@ private CompletableFuture<ExecutionResult> parseValidateAndExecute(ExecutionInpu
530530
executionInputRef.set(transformedInput);
531531
return parseAndValidate(executionInputRef, graphQLSchema, instrumentationState);
532532
};
533-
PreparsedDocumentEntry preparsedDoc = preparsedDocumentProvider.getDocument(executionInput, computeFunction);
534-
if (preparsedDoc.hasErrors()) {
535-
return CompletableFuture.completedFuture(new ExecutionResultImpl(preparsedDoc.getErrors()));
536-
}
537-
538-
return execute(executionInputRef.get(), preparsedDoc.getDocument(), graphQLSchema, instrumentationState);
533+
CompletableFuture<PreparsedDocumentEntry> preparsedDoc = preparsedDocumentProvider.getDocumentAsync(executionInput, computeFunction);
534+
return preparsedDoc.thenCompose(preparsedDocumentEntry -> {
535+
if (preparsedDocumentEntry.hasErrors()) {
536+
return CompletableFuture.completedFuture(new ExecutionResultImpl(preparsedDocumentEntry.getErrors()));
537+
}
538+
try {
539+
return execute(executionInputRef.get(), preparsedDocumentEntry.getDocument(), graphQLSchema, instrumentationState);
540+
} catch (AbortExecutionException e){
541+
return CompletableFuture.completedFuture(e.toExecutionResult());
542+
}
543+
});
539544
}
540545

541546
private PreparsedDocumentEntry parseAndValidate(AtomicReference<ExecutionInput> executionInputRef, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) {

src/main/java/graphql/execution/preparsed/PreparsedDocumentProvider.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import graphql.ExecutionInput;
55
import graphql.PublicSpi;
66

7+
import java.util.concurrent.CompletableFuture;
78
import java.util.function.Function;
89

910
/**
@@ -22,8 +23,27 @@ public interface PreparsedDocumentProvider {
2223
* @param executionInput The {@link graphql.ExecutionInput} containing the query
2324
* @param parseAndValidateFunction If the query has not be pre-parsed, this function MUST be called to parse and validate it
2425
* @return an instance of {@link PreparsedDocumentEntry}
26+
* <p>
27+
* @deprecated - use {@link #getDocumentAsync(ExecutionInput executionInput, Function parseAndValidateFunction)}
2528
*/
29+
@Deprecated
2630
PreparsedDocumentEntry getDocument(ExecutionInput executionInput, Function<ExecutionInput, PreparsedDocumentEntry> parseAndValidateFunction);
31+
32+
/**
33+
* This is called to get a "cached" pre-parsed query and if its not present, then the "parseAndValidateFunction"
34+
* can be called to parse and validate the query.
35+
* <p>
36+
* Note - the "parseAndValidateFunction" MUST be called if you dont have a per parsed version of the query because it not only parses
37+
* and validates the query, it invokes {@link graphql.execution.instrumentation.Instrumentation} calls as well for parsing and validation.
38+
* if you dont make a call back on this then these wont happen.
39+
*
40+
* @param executionInput The {@link graphql.ExecutionInput} containing the query
41+
* @param parseAndValidateFunction If the query has not be pre-parsed, this function MUST be called to parse and validate it
42+
* @return a promise to an {@link PreparsedDocumentEntry}
43+
*/
44+
default CompletableFuture<PreparsedDocumentEntry> getDocumentAsync(ExecutionInput executionInput, Function<ExecutionInput, PreparsedDocumentEntry> parseAndValidateFunction) {
45+
return CompletableFuture.completedFuture(getDocument(executionInput, parseAndValidateFunction));
46+
}
2747
}
2848

2949

src/main/java/graphql/execution/preparsed/persisted/PersistedQueryCache.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import graphql.PublicSpi;
55
import graphql.execution.preparsed.PreparsedDocumentEntry;
66

7+
import java.util.concurrent.CompletableFuture;
8+
79
/**
810
* This interface is used to abstract an actual cache that can cache parsed persistent queries.
911
*/
@@ -26,6 +28,30 @@ public interface PersistedQueryCache {
2628
* @param onCacheMiss the call back should it be a valid query id but its not currently not in the cache
2729
* @return a parsed and validated PreparsedDocumentEntry where {@link graphql.execution.preparsed.PreparsedDocumentEntry#getDocument()} is set
2830
* @throws graphql.execution.preparsed.persisted.PersistedQueryNotFound if the query id is not know at all and you have no query text
31+
*
32+
* @deprecated - use {@link #getPersistedQueryDocumentAsync(Object persistedQueryId, ExecutionInput executionInput, PersistedQueryCacheMiss onCacheMiss)}
2933
*/
34+
@Deprecated
3035
PreparsedDocumentEntry getPersistedQueryDocument(Object persistedQueryId, ExecutionInput executionInput, PersistedQueryCacheMiss onCacheMiss) throws PersistedQueryNotFound;
36+
37+
/**
38+
* This is called to get a persisted query from cache.
39+
* <p>
40+
* If its present in cache then it must return a PreparsedDocumentEntry where {@link graphql.execution.preparsed.PreparsedDocumentEntry#getDocument()}
41+
* is already parsed and validated. This will be passed onto the graphql engine as is.
42+
* <p>
43+
* If its a valid query id but its no present in cache, (cache miss) then you need to call back the "onCacheMiss" function with associated query text.
44+
* This will be compiled and validated by the graphql engine and the a PreparsedDocumentEntry will be passed back ready for you to cache it.
45+
* <p>
46+
* If its not a valid query id then throw a {@link graphql.execution.preparsed.persisted.PersistedQueryNotFound} to indicate this.
47+
*
48+
* @param persistedQueryId the persisted query id
49+
* @param executionInput the original execution input
50+
* @param onCacheMiss the call back should it be a valid query id but its not currently not in the cache
51+
* @return a promise to parsed and validated {@link PreparsedDocumentEntry} where {@link graphql.execution.preparsed.PreparsedDocumentEntry#getDocument()} is set
52+
* @throws graphql.execution.preparsed.persisted.PersistedQueryNotFound if the query id is not know at all and you have no query text
53+
*/
54+
default CompletableFuture<PreparsedDocumentEntry> getPersistedQueryDocumentAsync(Object persistedQueryId, ExecutionInput executionInput, PersistedQueryCacheMiss onCacheMiss) throws PersistedQueryNotFound{
55+
return CompletableFuture.completedFuture(getPersistedQueryDocument(persistedQueryId, executionInput, onCacheMiss));
56+
}
3157
}

src/main/java/graphql/nextgen/GraphQL.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,17 @@ private CompletableFuture<ExecutionResult> parseValidateAndExecute(ExecutionInpu
182182
executionInputRef.set(transformedInput);
183183
return parseAndValidate(executionInputRef, graphQLSchema, instrumentationState);
184184
};
185-
PreparsedDocumentEntry preparsedDoc = preparsedDocumentProvider.getDocument(executionInput, computeFunction);
186-
if (preparsedDoc.hasErrors()) {
187-
return CompletableFuture.completedFuture(new ExecutionResultImpl(preparsedDoc.getErrors()));
188-
}
189-
return execute(executionInputRef.get(), preparsedDoc.getDocument(), graphQLSchema, instrumentationState);
185+
CompletableFuture<PreparsedDocumentEntry> preparsedDoc = preparsedDocumentProvider.getDocumentAsync(executionInput, computeFunction);
186+
return preparsedDoc.thenCompose(preparsedDocumentEntry -> {
187+
if (preparsedDocumentEntry.hasErrors()) {
188+
return CompletableFuture.completedFuture(new ExecutionResultImpl(preparsedDocumentEntry.getErrors()));
189+
}
190+
try {
191+
return execute(executionInputRef.get(), preparsedDocumentEntry.getDocument(), graphQLSchema, instrumentationState);
192+
} catch (AbortExecutionException e) {
193+
return CompletableFuture.completedFuture(e.toExecutionResult());
194+
}
195+
});
190196
}
191197

192198
private PreparsedDocumentEntry parseAndValidate(AtomicReference<ExecutionInput> executionInputRef, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) {

src/test/groovy/graphql/execution/preparsed/PreparsedDocumentProviderTest.groovy

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ import graphql.execution.instrumentation.SimpleInstrumentation
1010
import graphql.execution.instrumentation.TestingInstrumentation
1111
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters
1212
import graphql.language.Document
13+
import graphql.parser.Parser
1314
import spock.lang.Specification
1415

1516
import java.util.function.Function
1617

18+
import static graphql.ExecutionInput.newExecutionInput
19+
1720
class PreparsedDocumentProviderTest extends Specification {
1821

1922
def 'Preparse of simple serial execution'() {
@@ -220,4 +223,28 @@ class PreparsedDocumentProviderTest extends Specification {
220223
resultB.data == [hero: [name: "R2-D2"]]
221224
instrumentationB.capturedInput.getQuery() == queryB
222225
}
226+
227+
def "sync method and async method result is same"() {
228+
given:
229+
def provider = new TestingPreparsedDocumentProvider()
230+
def queryA = """
231+
query A {
232+
hero {
233+
id
234+
}
235+
}
236+
"""
237+
def engineParser = {
238+
ExecutionInput ei ->
239+
def doc = new Parser().parseDocument(ei.getQuery())
240+
return new PreparsedDocumentEntry(doc)
241+
}
242+
when:
243+
def syncMethod = provider.getDocument(newExecutionInput(queryA).build(), engineParser)
244+
def asyncMethod = provider.getDocumentAsync(newExecutionInput(queryA).build(), engineParser)
245+
246+
then:
247+
asyncMethod != null
248+
asyncMethod.get().equals(syncMethod)
249+
}
223250
}

0 commit comments

Comments
 (0)