Add Order annotation to GraphQL RouterFunction beans

Update GraphQL auto-configuration so that `RouterFunction` beans have
and `@Order` of 0.

Fixes gh-31314
This commit is contained in:
Phillip Webb 2022-06-13 16:44:20 -07:00
parent 7e2b325b36
commit 378e56f1d3
5 changed files with 74 additions and 3 deletions

View File

@ -38,6 +38,7 @@ import org.springframework.boot.autoconfigure.graphql.GraphQlProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.log.LogMessage;
import org.springframework.graphql.ExecutionGraphQlService;
import org.springframework.graphql.execution.GraphQlSource;
@ -101,8 +102,9 @@ public class GraphQlWebFluxAutoConfiguration {
}
@Bean
public RouterFunction<ServerResponse> graphQlEndpoint(GraphQlHttpHandler httpHandler, GraphQlSource graphQlSource,
GraphQlProperties properties) {
@Order(0)
public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler httpHandler,
GraphQlSource graphQlSource, GraphQlProperties properties) {
String path = properties.getPath();
logger.info(LogMessage.format("GraphQL endpoint HTTP POST %s", path));
RouterFunctions.Builder builder = RouterFunctions.route();

View File

@ -41,6 +41,7 @@ import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.log.LogMessage;
import org.springframework.graphql.ExecutionGraphQlService;
import org.springframework.graphql.execution.GraphQlSource;
@ -106,6 +107,7 @@ public class GraphQlWebMvcAutoConfiguration {
}
@Bean
@Order(0)
public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler httpHandler,
GraphQlSource graphQlSource, GraphQlProperties properties) {
String path = properties.getPath();

View File

@ -17,6 +17,7 @@
package org.springframework.boot.autoconfigure.graphql.reactive;
import java.util.Collections;
import java.util.Map;
import java.util.function.Consumer;
import graphql.schema.idl.TypeRuntimeWiring;
@ -32,6 +33,7 @@ import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfigurat
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
import org.springframework.graphql.server.WebGraphQlHandler;
import org.springframework.graphql.server.WebGraphQlInterceptor;
@ -41,6 +43,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.server.RouterFunction;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
@ -147,6 +150,17 @@ class GraphQlWebFluxAutoConfigurationTests {
.run((context) -> assertThat(context).hasSingleBean(GraphQlWebSocketHandler.class));
}
@Test
void routerFunctionShouldHaveOrderZero() throws Exception {
this.contextRunner.withUserConfiguration(CustomRouterFunctions.class).run((context) -> {
Map<String, ?> beans = context.getBeansOfType(RouterFunction.class);
Object[] ordered = context.getBeanProvider(RouterFunction.class).orderedStream().toArray();
assertThat(beans.get("before")).isSameAs(ordered[0]);
assertThat(beans.get("graphQlRouterFunction")).isSameAs(ordered[1]);
assertThat(beans.get("after")).isSameAs(ordered[2]);
});
}
private void testWithWebClient(Consumer<WebTestClient> consumer) {
this.contextRunner.run((context) -> {
WebTestClient client = WebTestClient.bindToApplicationContext(context).configureClient()
@ -180,4 +194,21 @@ class GraphQlWebFluxAutoConfigurationTests {
}
@Configuration
static class CustomRouterFunctions {
@Bean
@Order(-1)
RouterFunction<?> before() {
return (r) -> null;
}
@Bean
@Order(1)
RouterFunction<?> after() {
return (r) -> null;
}
}
}

View File

@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.graphql.servlet;
import java.util.Map;
import graphql.schema.idl.TypeRuntimeWiring;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
@ -30,6 +32,7 @@ import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguratio
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
import org.springframework.graphql.server.WebGraphQlHandler;
import org.springframework.graphql.server.WebGraphQlInterceptor;
@ -40,6 +43,7 @@ import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.servlet.function.RouterFunction;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
@ -153,6 +157,17 @@ class GraphQlWebMvcAutoConfigurationTests {
.run((context) -> assertThat(context).hasSingleBean(GraphQlWebSocketHandler.class));
}
@Test
void routerFunctionShouldHaveOrderZero() throws Exception {
this.contextRunner.withUserConfiguration(CustomRouterFunctions.class).run((context) -> {
Map<String, ?> beans = context.getBeansOfType(RouterFunction.class);
Object[] ordered = context.getBeanProvider(RouterFunction.class).orderedStream().toArray();
assertThat(beans.get("before")).isSameAs(ordered[0]);
assertThat(beans.get("graphQlRouterFunction")).isSameAs(ordered[1]);
assertThat(beans.get("after")).isSameAs(ordered[2]);
});
}
private void testWith(MockMvcConsumer mockMvcConsumer) {
this.contextRunner.run((context) -> {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).defaultRequest(
@ -190,4 +205,21 @@ class GraphQlWebMvcAutoConfigurationTests {
}
@Configuration
static class CustomRouterFunctions {
@Bean
@Order(-1)
RouterFunction<?> before() {
return (r) -> null;
}
@Bean
@Order(1)
RouterFunction<?> after() {
return (r) -> null;
}
}
}

View File

@ -83,7 +83,11 @@ are detected by Spring Boot and considered as candidates for `DataFetcher` for m
[[web.graphql.transports.http-websocket]]
==== HTTP and WebSocket
The GraphQL HTTP endpoint is at HTTP POST "/graphql" by default. The path can be customized with configprop:spring.graphql.path[].
The GraphQL HTTP endpoint is at HTTP POST "/graphql" by default.
The path can be customized with configprop:spring.graphql.path[].
TIP: The HTTP endpoint for both Spring MVC and Spring WebFlux is provided by a `RouterFunction` bean with an `@Order` or `0`.
If you define your own `RouterFunction` beans, you may want to add appropriate `@Order` annotations to ensure that they are sorted correctly.
The GraphQL WebSocket endpoint is off by default. To enable it: