Skip to content

Commit b4329a4

Browse files
committed
allow returning CompletableFuture's from datafetchers
When returning `CompletableFuture`s from datafetchers we sometimes encounter an error in the lines of `Unknown value 'java.util.concurrent.CompletableFuture@e77cd7a[Completed normally]'`. By converting the future to a mono it should be handled properly.
1 parent 1ae33a8 commit b4329a4

File tree

2 files changed

+41
-6
lines changed

2 files changed

+41
-6
lines changed

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.graphql.data.method.annotation.support;
1717

1818
import java.util.Arrays;
19+
import java.util.concurrent.CompletableFuture;
1920
import java.util.concurrent.Executor;
2021
import java.util.function.Consumer;
2122

@@ -51,7 +52,7 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport {
5152
private final HandlerMethodArgumentResolverComposite resolvers;
5253

5354
private final Consumer<Object[]> validationHelper;
54-
55+
5556
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
5657

5758
private final boolean subscription;
@@ -113,7 +114,13 @@ public Object invoke(DataFetchingEnvironment environment) {
113114
}
114115

115116
if (Arrays.stream(args).noneMatch(arg -> arg instanceof Mono)) {
116-
return validateAndInvoke(args, environment);
117+
Object result = validateAndInvoke(args, environment);
118+
119+
if (result instanceof CompletableFuture<?>) {
120+
return Mono.fromFuture((CompletableFuture<?>) result);
121+
}
122+
123+
return result;
117124
}
118125

119126
return this.subscription ?
@@ -127,12 +134,16 @@ public Object invoke(DataFetchingEnvironment environment) {
127134
if (result instanceof Mono) {
128135
return (Mono<?>) result;
129136
}
130-
else if (result instanceof Flux) {
137+
138+
if (result instanceof Flux) {
131139
return Flux.from((Flux<?>) result).collectList();
132140
}
133-
else {
134-
return Mono.justOrEmpty(result);
141+
142+
if (result instanceof CompletableFuture<?>) {
143+
return Mono.fromFuture((CompletableFuture<?>) result);
135144
}
145+
146+
return Mono.justOrEmpty(result);
136147
});
137148
}
138149

spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethodTests.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import graphql.GraphQLContext;
2525
import graphql.schema.DataFetchingEnvironment;
2626
import graphql.schema.DataFetchingEnvironmentImpl;
27-
import io.micrometer.context.ContextSnapshot;
2827
import org.junit.jupiter.api.Test;
2928
import org.mockito.Mockito;
3029

@@ -37,6 +36,7 @@
3736
import org.springframework.graphql.data.method.annotation.QueryMapping;
3837
import org.springframework.lang.Nullable;
3938
import org.springframework.util.ClassUtils;
39+
import reactor.core.publisher.Mono;
4040

4141
import static org.assertj.core.api.Assertions.assertThat;
4242

@@ -86,6 +86,27 @@ void callableReturnValue() throws Exception {
8686
assertThat(future.get()).isEqualTo("A");
8787
}
8888

89+
@Test
90+
void completableFutureReturnValue() throws Exception {
91+
92+
HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
93+
resolvers.addResolver(new ArgumentMethodArgumentResolver(new GraphQlArgumentBinder()));
94+
95+
DataFetcherHandlerMethod handlerMethod = new DataFetcherHandlerMethod(
96+
handlerMethodFor(new TestController(), "handleAndReturnsCompletableFuture"), resolvers, null,
97+
new SimpleAsyncTaskExecutor(), false);
98+
99+
DataFetchingEnvironment environment = DataFetchingEnvironmentImpl
100+
.newDataFetchingEnvironment()
101+
.build();
102+
103+
Object result = handlerMethod.invoke(environment);
104+
105+
assertThat(result).isInstanceOf(Mono.class);
106+
Mono<String> mono = (Mono<String>) result;
107+
assertThat(mono.block()).isEqualTo("B");
108+
}
109+
89110
private static HandlerMethod handlerMethodFor(Object controller, String methodName) {
90111
Method method = ClassUtils.getMethod(controller.getClass(), methodName, (Class<?>[]) null);
91112
return new HandlerMethod(controller, method);
@@ -112,6 +133,9 @@ public Callable<String> handleAndReturnCallable() {
112133
return () -> "A";
113134
}
114135

136+
public CompletableFuture<String> handleAndReturnsCompletableFuture() {
137+
return CompletableFuture.completedFuture("B");
138+
}
115139
}
116140

117141
}

0 commit comments

Comments
 (0)