Skip to content

Commit

Permalink
Add Order annotation to GraphQL RouterFunction beans
Browse files Browse the repository at this point in the history
Update GraphQL auto-configuration so that `RouterFunction` beans have
and `@Order` of 0.

Fixes gh-31314
  • Loading branch information
philwebb committed Jun 13, 2022
1 parent 7e2b325 commit 378e56f
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 3 deletions.
Expand Up @@ -38,6 +38,7 @@
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;
Expand Down Expand Up @@ -101,8 +102,9 @@ public WebGraphQlHandler webGraphQlHandler(ExecutionGraphQlService service,
}

@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();
Expand Down
Expand Up @@ -41,6 +41,7 @@
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;
Expand Down Expand Up @@ -106,6 +107,7 @@ public WebGraphQlHandler webGraphQlHandler(ExecutionGraphQlService service,
}

@Bean
@Order(0)
public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler httpHandler,
GraphQlSource graphQlSource, GraphQlProperties properties) {
String path = properties.getPath();
Expand Down
Expand Up @@ -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;
Expand All @@ -32,6 +33,7 @@
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;
Expand All @@ -41,6 +43,7 @@
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;
Expand Down Expand Up @@ -147,6 +150,17 @@ void shouldConfigureWebSocketBeans() {
.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()
Expand Down Expand Up @@ -180,4 +194,21 @@ WebGraphQlInterceptor customWebGraphQlInterceptor() {

}

@Configuration
static class CustomRouterFunctions {

@Bean
@Order(-1)
RouterFunction<?> before() {
return (r) -> null;
}

@Bean
@Order(1)
RouterFunction<?> after() {
return (r) -> null;
}

}

}
Expand Up @@ -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;
Expand All @@ -30,6 +32,7 @@
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;
Expand All @@ -40,6 +43,7 @@
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;
Expand Down Expand Up @@ -153,6 +157,17 @@ void shouldConfigureWebSocketBeans() {
.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(
Expand Down Expand Up @@ -190,4 +205,21 @@ WebGraphQlInterceptor customWebGraphQlInterceptor() {

}

@Configuration
static class CustomRouterFunctions {

@Bean
@Order(-1)
RouterFunction<?> before() {
return (r) -> null;
}

@Bean
@Order(1)
RouterFunction<?> after() {
return (r) -> null;
}

}

}
Expand Up @@ -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:

Expand Down

0 comments on commit 378e56f

Please sign in to comment.