From e7d72534e7a9d0324b3f2ad5e68fe5b99e98a7a4 Mon Sep 17 00:00:00 2001 From: Koen Punt Date: Tue, 21 Mar 2023 14:58:36 +0100 Subject: [PATCH] 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>`, 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 --- .../data/method/InvocableHandlerMethodSupport.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/InvocableHandlerMethodSupport.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/InvocableHandlerMethodSupport.java index 5e52e91b..f76a5846 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/InvocableHandlerMethodSupport.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/InvocableHandlerMethodSupport.java @@ -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> mono = (Mono>)result; + // Unwrap nested CompletableFuture + return mono.flatMap(Mono::fromFuture); + } + + return result; } Object result = method.invoke(getBean(), argValues); return handleReturnValue(graphQLContext, result);