diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java index 9aae9968..95aa7053 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java @@ -16,6 +16,7 @@ package org.springframework.graphql.data.method.annotation.support; import java.util.Arrays; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.function.BiConsumer; @@ -133,11 +134,14 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport { }) : toArgsMono(args).flatMap(argValues -> { Object result = validateAndInvoke(argValues, environment); - if (result instanceof Mono) { - return (Mono) result; + if (result instanceof Mono mono) { + return mono; } - else if (result instanceof Flux) { - return Flux.from((Flux) result).collectList(); + else if (result instanceof Flux flux) { + return Flux.from(flux).collectList(); + } + else if (result instanceof CompletableFuture future) { + return Mono.fromFuture(future); } else { return Mono.justOrEmpty(result); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethodTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethodTests.java index 74e4944c..3c43c9e3 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethodTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,9 +24,9 @@ import java.util.concurrent.CompletableFuture; import graphql.GraphQLContext; import graphql.schema.DataFetchingEnvironment; import graphql.schema.DataFetchingEnvironmentImpl; -import io.micrometer.context.ContextSnapshot; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import reactor.core.publisher.Mono; import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.graphql.data.GraphQlArgumentBinder; @@ -36,6 +36,8 @@ import org.springframework.graphql.data.method.HandlerMethodArgumentResolverComp import org.springframework.graphql.data.method.annotation.Argument; import org.springframework.graphql.data.method.annotation.QueryMapping; import org.springframework.lang.Nullable; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.User; import org.springframework.util.ClassUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -86,6 +88,23 @@ public class DataFetcherHandlerMethodTests { assertThat(future.get()).isEqualTo("A"); } + @Test + void completableFutureReturnValue() { + + HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite(); + resolvers.addResolver(new AuthenticationPrincipalArgumentResolver((beanName, context) -> null)); + resolvers.addResolver(new ArgumentMethodArgumentResolver(new GraphQlArgumentBinder())); + + DataFetcherHandlerMethod handlerMethod = new DataFetcherHandlerMethod( + handlerMethodFor(new TestController(), "handleAndReturnFuture"), resolvers, + null, null, false); + + Object result = handlerMethod.invoke(DataFetchingEnvironmentImpl.newDataFetchingEnvironment().build()); + + assertThat(result).isInstanceOf(Mono.class); + assertThat(((Mono) result).block()).isEqualTo("B"); + } + private static HandlerMethod handlerMethodFor(Object controller, String methodName) { Method method = ClassUtils.getMethod(controller.getClass(), methodName, (Class[]) null); return new HandlerMethod(controller, method); @@ -112,6 +131,10 @@ public class DataFetcherHandlerMethodTests { return () -> "A"; } + public CompletableFuture handleAndReturnFuture(@AuthenticationPrincipal User user) { + return CompletableFuture.completedFuture("B"); + } + } }