Support use of dataloader from suspend function

Rudimentary implementation to support returning a `CompletableFuture`
from a suspend function.

`CoroutinesUtils.invokeSuspendingFunction` wraps the return value of
the function in a `Mono` (or `Flux`). But it also does this when a
`CompletableFuture` is returned, and thus results in a
`Mono<CompletableFuture<?>>`, which isn't captured by graphql-java,
and thus the dataloader is never dispatched.

By unwrapping the future, and _converting_ it to a mono (as opposed
to wrapping), the dataloader is dispatched correctly.

See gh-653
This commit is contained in:
Koen Punt
2023-03-21 14:58:36 +01:00
committed by rstoyanchev
parent 71b1f5868b
commit e7d72534e7

View File

@@ -26,6 +26,7 @@ import java.util.concurrent.Executor;
import graphql.GraphQLContext;
import io.micrometer.context.ContextSnapshot;
import org.springframework.data.util.KotlinReflectionUtils;
import reactor.core.publisher.Mono;
import org.springframework.core.CoroutinesUtils;
@@ -81,7 +82,18 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod {
Method method = getBridgedMethod();
try {
if (KotlinDetector.isSuspendingFunction(method)) {
return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), argValues);
Object result = CoroutinesUtils.invokeSuspendingFunction(method, getBean(), argValues);
Class<?> returnType = KotlinReflectionUtils.getReturnType(method);
if (CompletableFuture.class.isAssignableFrom(returnType)) {
@SuppressWarnings("unchecked")
Mono<CompletableFuture<?>> mono = (Mono<CompletableFuture<?>>)result;
// Unwrap nested CompletableFuture
return mono.flatMap(Mono::fromFuture);
}
return result;
}
Object result = method.invoke(getBean(), argValues);
return handleReturnValue(graphQLContext, result);