Description
Hello,
In GraphQL, it is possible to rename the query, mutation and subscription objects in the schema. When doing so, data fetcher that returns Flux
won't work properly in spring-graphql.
Sample schema:
schema
query: MyQuery
subscription: MySubscription
}
type MyQuery {
greetings: [String]
}
type MySubscription {
greetings: String
}
For subscription, this is due to this line in org.springframework.graphql.execution.ContextDataFetcherDecorator
:
boolean handlesSubscription = parent.getName().equals("Subscription");
To check that, I executed the test provided below, after changing Subscription to MySubscription in ContextDataFetcherDecorator
: doing this makes the test pass ok.
My first attempt to create a unit test generates wrong failures for Query and Mutation, as the queryFetcher
and mutationFetcher
hard coded the Query
and Mutation
name. So I updated the test below which is ok for query and mutation.
I added the test below in the org.springframework.graphql.execution.ContextDataFetcherDecoratorTests
. This test is basically a copy/paste of the fluxDataFetcher()
and fluxDataFetcherSubscription()
tests:
@Test
void fluxDataFetcherRenamedSubscription() throws Exception {
GraphQL graphQl = GraphQlSetup.schemaContent("" +
"schema {" +
" query: MyQuery" +
" mutation: MyMutation" +
" subscription: MySubscription" +
"}" +
"type MyQuery { " +
" querySomeData: [String] " +
"} " +
"type MyMutation { " +
" createSomeData: [String] " +
"} " +
"type MySubscription { " +
" greetings: String " +
"}")
// A query fetcher that returns a Flux (to check Flux behavior on Subscription and non Subscription)
// (need to use the dataFetcher method as queryFetcher hard coded the query name to "Query")
.dataFetcher("MyQuery", "querySomeData", (env) ->
Mono.delay(Duration.ofMillis(50))
.flatMapMany((aLong) -> Flux.deferContextual((context) -> {
String name = context.get("name");
return Flux.just("Hi", "Bonjour", "Hola").map((s) -> s + " " + name);
})))
// (need to use the dataFetcher method as queryFetcher hard coded the mutation name to "Mutation")
.dataFetcher("MyMutation", "createSomeData", (env) ->
Mono.delay(Duration.ofMillis(50))
.flatMapMany((aLong) -> Flux.deferContextual((context) -> {
String name = context.get("name");
return Flux.just("Hi", "Bonjour", "Hola").map((s) -> s + " " + name);
})))
.dataFetcher("MySubscription", "greetings", (env) ->
Mono.delay(Duration.ofMillis(50))
.flatMapMany((aLong) -> Flux.deferContextual((context) -> {
String name = context.get("name");
return Flux.just("Hi", "Bonjour", "Hola").map((s) -> s + " " + name);
})))
.toGraphQl();
//////////////////////////////////////////////////////////////////////////////////////
// MyQuery check
ExecutionInput input = ExecutionInput.newExecutionInput().query("{ querySomeData }").build();
ReactorContextManager.setReactorContext(Context.of("name", "007"), input.getGraphQLContext());
ExecutionResult result = graphQl.executeAsync(input).get();
List<String> data = ResponseHelper.forResult(result).toList("querySomeData", String.class);
assertThat(data).containsExactly("Hi 007", "Bonjour 007", "Hola 007");
//////////////////////////////////////////////////////////////////////////////////////
// MyMutation check
input = ExecutionInput.newExecutionInput().query("mutation { createSomeData }").build();
ReactorContextManager.setReactorContext(Context.of("name", "007"), input.getGraphQLContext());
result = graphQl.executeAsync(input).get();
data = ResponseHelper.forResult(result).toList("createSomeData", String.class);
assertThat(data).containsExactly("Hi 007", "Bonjour 007", "Hola 007");
//////////////////////////////////////////////////////////////////////////////////////
// MySubscription check
input = ExecutionInput.newExecutionInput().query("subscription { greetings }").build();
ReactorContextManager.setReactorContext(Context.of("name", "007"), input.getGraphQLContext());
result = graphQl.executeAsync(input).get();
Flux<String> greetingsFlux = ResponseHelper.forSubscription(result)
.map(response -> response.toEntity("greetings", String.class));
StepVerifier.create(greetingsFlux)
.expectNext("Hi 007", "Bonjour 007", "Hola 007")
.verifyComplete();
}
I'll try to find how to solve this issue, and propose a PR.
If you have hint, it would be nice: I don't know how to retrieve the GraphQL schema's data from the org.springframework.graphql.execution.ContextDataFetcherDecorator.createVisitor(..)
method.
Etienne