Separate transports in GraphQL auto-configurations
This commit revisits the existing GraphQL configuration properties to better reflect which ones belong to specific transports. This also relaxes the Web auto-configurations to only require the `ExecutionGraphQlService` as a bean. The `GraphQlSource` is now an optional bean dependency. Closes gh-44495
This commit is contained in:
parent
e886785f76
commit
83f678a8b1
@ -20,6 +20,7 @@ import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
@ -31,31 +32,35 @@ import org.springframework.core.io.Resource;
|
||||
@ConfigurationProperties("spring.graphql")
|
||||
public class GraphQlProperties {
|
||||
|
||||
/**
|
||||
* Path at which to expose a GraphQL request HTTP endpoint.
|
||||
*/
|
||||
private String path = "/graphql";
|
||||
private final Http http = new Http();
|
||||
|
||||
private final Graphiql graphiql = new Graphiql();
|
||||
|
||||
private final Rsocket rsocket = new Rsocket();
|
||||
|
||||
private final Schema schema = new Schema();
|
||||
|
||||
private final DeprecatedSse sse = new DeprecatedSse(this.http.getSse());
|
||||
|
||||
private final Websocket websocket = new Websocket();
|
||||
|
||||
private final Rsocket rsocket = new Rsocket();
|
||||
|
||||
private final Sse sse = new Sse();
|
||||
public Http getHttp() {
|
||||
return this.http;
|
||||
}
|
||||
|
||||
public Graphiql getGraphiql() {
|
||||
return this.graphiql;
|
||||
}
|
||||
|
||||
@DeprecatedConfigurationProperty(replacement = "spring.graphql.http.path", since = "3.5.0")
|
||||
@Deprecated(since = "3.5.0", forRemoval = true)
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
return getHttp().getPath();
|
||||
}
|
||||
|
||||
@Deprecated(since = "3.5.0", forRemoval = true)
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
getHttp().setPath(path);
|
||||
}
|
||||
|
||||
public Schema getSchema() {
|
||||
@ -70,10 +75,33 @@ public class GraphQlProperties {
|
||||
return this.rsocket;
|
||||
}
|
||||
|
||||
public Sse getSse() {
|
||||
public DeprecatedSse getSse() {
|
||||
return this.sse;
|
||||
}
|
||||
|
||||
public static class Http {
|
||||
|
||||
/**
|
||||
* Path at which to expose a GraphQL request HTTP endpoint.
|
||||
*/
|
||||
private String path = "/graphql";
|
||||
|
||||
private final Sse sse = new Sse();
|
||||
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public Sse getSse() {
|
||||
return this.sse;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Schema {
|
||||
|
||||
/**
|
||||
@ -178,7 +206,7 @@ public class GraphQlProperties {
|
||||
|
||||
/**
|
||||
* Whether the endpoint that prints the schema is enabled. Schema is available
|
||||
* under spring.graphql.path + "/schema".
|
||||
* under spring.graphql.http.path + "/schema".
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
@ -302,4 +330,25 @@ public class GraphQlProperties {
|
||||
|
||||
}
|
||||
|
||||
public static class DeprecatedSse {
|
||||
|
||||
private final Sse sse;
|
||||
|
||||
public DeprecatedSse(Sse sse) {
|
||||
this.sse = sse;
|
||||
}
|
||||
|
||||
@DeprecatedConfigurationProperty(replacement = "spring.graphql.http.sse.timeout", since = "3.5.0")
|
||||
@Deprecated(since = "3.5.0", forRemoval = true)
|
||||
public Duration getTimeout() {
|
||||
return this.sse.getTimeout();
|
||||
}
|
||||
|
||||
@Deprecated(since = "3.5.0", forRemoval = true)
|
||||
public void setTimeout(Duration timeout) {
|
||||
this.sse.setTimeout(timeout);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -85,13 +85,6 @@ public class GraphQlWebFluxAutoConfiguration {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GraphQlWebFluxAutoConfiguration.class);
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public WebGraphQlHandler webGraphQlHandler(ExecutionGraphQlService service,
|
||||
ObjectProvider<WebGraphQlInterceptor> interceptors) {
|
||||
return WebGraphQlHandler.builder(service).interceptors(interceptors.orderedStream().toList()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public GraphQlHttpHandler graphQlHttpHandler(WebGraphQlHandler webGraphQlHandler) {
|
||||
@ -100,15 +93,23 @@ public class GraphQlWebFluxAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public GraphQlSseHandler graphQlSseHandler(WebGraphQlHandler webGraphQlHandler) {
|
||||
return new GraphQlSseHandler(webGraphQlHandler);
|
||||
public GraphQlSseHandler graphQlSseHandler(WebGraphQlHandler webGraphQlHandler, GraphQlProperties properties) {
|
||||
return new GraphQlSseHandler(webGraphQlHandler, properties.getHttp().getSse().getTimeout());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public WebGraphQlHandler webGraphQlHandler(ExecutionGraphQlService service,
|
||||
ObjectProvider<WebGraphQlInterceptor> interceptors) {
|
||||
return WebGraphQlHandler.builder(service).interceptors(interceptors.orderedStream().toList()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(0)
|
||||
public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler httpHandler,
|
||||
GraphQlSseHandler sseHandler, GraphQlSource graphQlSource, GraphQlProperties properties) {
|
||||
String path = properties.getPath();
|
||||
GraphQlSseHandler sseHandler, ObjectProvider<GraphQlSource> graphQlSourceProvider,
|
||||
GraphQlProperties properties) {
|
||||
String path = properties.getHttp().getPath();
|
||||
logger.info(LogMessage.format("GraphQL endpoint HTTP POST %s", path));
|
||||
RouterFunctions.Builder builder = RouterFunctions.route();
|
||||
builder.route(GraphQlRequestPredicates.graphQlHttp(path), httpHandler::handleRequest);
|
||||
@ -119,7 +120,8 @@ public class GraphQlWebFluxAutoConfiguration {
|
||||
GraphiQlHandler graphQlHandler = new GraphiQlHandler(path, properties.getWebsocket().getPath());
|
||||
builder.GET(properties.getGraphiql().getPath(), graphQlHandler::handleRequest);
|
||||
}
|
||||
if (properties.getSchema().getPrinter().isEnabled()) {
|
||||
GraphQlSource graphQlSource = graphQlSourceProvider.getIfAvailable();
|
||||
if (properties.getSchema().getPrinter().isEnabled() && graphQlSource != null) {
|
||||
SchemaHandler schemaHandler = new SchemaHandler(graphQlSource);
|
||||
builder.GET(path + "/schema", schemaHandler::handleRequest);
|
||||
}
|
||||
@ -158,7 +160,7 @@ public class GraphQlWebFluxAutoConfiguration {
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
CorsConfiguration configuration = this.corsProperties.toCorsConfiguration();
|
||||
if (configuration != null) {
|
||||
registry.addMapping(this.graphQlProperties.getPath()).combine(configuration);
|
||||
registry.addMapping(this.graphQlProperties.getHttp().getPath()).combine(configuration);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
|
||||
import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.graphql.GraphQlCorsProperties;
|
||||
import org.springframework.boot.autoconfigure.graphql.GraphQlProperties;
|
||||
import org.springframework.boot.autoconfigure.graphql.GraphQlProperties.Sse;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -99,8 +98,7 @@ public class GraphQlWebMvcAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public GraphQlSseHandler graphQlSseHandler(WebGraphQlHandler webGraphQlHandler, GraphQlProperties properties) {
|
||||
Sse sse = properties.getSse();
|
||||
return new GraphQlSseHandler(webGraphQlHandler, sse.getTimeout());
|
||||
return new GraphQlSseHandler(webGraphQlHandler, properties.getHttp().getSse().getTimeout());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ -113,8 +111,9 @@ public class GraphQlWebMvcAutoConfiguration {
|
||||
@Bean
|
||||
@Order(0)
|
||||
public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler httpHandler,
|
||||
GraphQlSseHandler sseHandler, GraphQlSource graphQlSource, GraphQlProperties properties) {
|
||||
String path = properties.getPath();
|
||||
GraphQlSseHandler sseHandler, ObjectProvider<GraphQlSource> graphQlSourceProvider,
|
||||
GraphQlProperties properties) {
|
||||
String path = properties.getHttp().getPath();
|
||||
logger.info(LogMessage.format("GraphQL endpoint HTTP POST %s", path));
|
||||
RouterFunctions.Builder builder = RouterFunctions.route();
|
||||
builder.route(GraphQlRequestPredicates.graphQlHttp(path), httpHandler::handleRequest);
|
||||
@ -125,7 +124,8 @@ public class GraphQlWebMvcAutoConfiguration {
|
||||
GraphiQlHandler graphiQLHandler = new GraphiQlHandler(path, properties.getWebsocket().getPath());
|
||||
builder.GET(properties.getGraphiql().getPath(), graphiQLHandler::handleRequest);
|
||||
}
|
||||
if (properties.getSchema().getPrinter().isEnabled()) {
|
||||
GraphQlSource graphQlSource = graphQlSourceProvider.getIfAvailable();
|
||||
if (properties.getSchema().getPrinter().isEnabled() && graphQlSource != null) {
|
||||
SchemaHandler schemaHandler = new SchemaHandler(graphQlSource);
|
||||
builder.GET(path + "/schema", schemaHandler::handleRequest);
|
||||
}
|
||||
@ -164,7 +164,7 @@ public class GraphQlWebMvcAutoConfiguration {
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
CorsConfiguration configuration = this.corsProperties.toCorsConfiguration();
|
||||
if (configuration != null) {
|
||||
registry.addMapping(this.graphQlProperties.getPath()).combine(configuration);
|
||||
registry.addMapping(this.graphQlProperties.getHttp().getPath()).combine(configuration);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ class GraphQlWebMvcAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void shouldConfigureSseTimeout() {
|
||||
this.contextRunner.withPropertyValues("spring.graphql.sse.timeout=10s").run((context) -> {
|
||||
this.contextRunner.withPropertyValues("spring.graphql.http.sse.timeout=10s").run((context) -> {
|
||||
assertThat(context).hasSingleBean(GraphQlSseHandler.class);
|
||||
GraphQlSseHandler handler = context.getBean(GraphQlSseHandler.class);
|
||||
assertThat(handler).hasFieldOrPropertyWithValue("timeout", Duration.ofSeconds(10));
|
||||
|
@ -94,7 +94,7 @@ are detected by Spring Boot and considered as candidates for javadoc:graphql.sch
|
||||
|
||||
The GraphQL HTTP endpoint is at HTTP POST `/graphql` by default.
|
||||
It also supports the `"text/event-stream"` media type over Server Sent Events for subscriptions only.
|
||||
The path can be customized with configprop:spring.graphql.path[].
|
||||
The path can be customized with configprop:spring.graphql.http.path[].
|
||||
|
||||
TIP: The HTTP endpoint for both Spring MVC and Spring WebFlux is provided by a `RouterFunction` bean with an javadoc:org.springframework.core.annotation.Order[format=annotation] of `0`.
|
||||
If you define your own `RouterFunction` beans, you may want to add appropriate javadoc:org.springframework.core.annotation.Order[format=annotation] annotations to ensure that they are sorted correctly.
|
||||
|
@ -43,7 +43,7 @@ public class HttpGraphQlTesterAutoConfiguration {
|
||||
@ConditionalOnBean(WebTestClient.class)
|
||||
@ConditionalOnMissingBean
|
||||
public HttpGraphQlTester webTestClientGraphQlTester(WebTestClient webTestClient, GraphQlProperties properties) {
|
||||
WebTestClient mutatedWebTestClient = webTestClient.mutate().baseUrl(properties.getPath()).build();
|
||||
WebTestClient mutatedWebTestClient = webTestClient.mutate().baseUrl(properties.getHttp().getPath()).build();
|
||||
return HttpGraphQlTester.create(mutatedWebTestClient);
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ class HttpGraphQlTesterContextCustomizer implements ContextCustomizer {
|
||||
}
|
||||
|
||||
private String findConfiguredGraphQlPath() {
|
||||
String configuredPath = this.applicationContext.getEnvironment().getProperty("spring.graphql.path");
|
||||
String configuredPath = this.applicationContext.getEnvironment().getProperty("spring.graphql.http.path");
|
||||
return StringUtils.hasText(configuredPath) ? configuredPath : "/graphql";
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user