This commit is contained in:
Stephane Nicoll
2021-06-30 15:10:28 +02:00
committed by Brian Clozel
parent 4a1ee14f88
commit 9d4ebfe743
16 changed files with 90 additions and 99 deletions

View File

@@ -31,7 +31,6 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
@@ -65,18 +64,18 @@ public class GraphQlAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public RuntimeWiring runtimeWiring(ObjectProvider<RuntimeWiringCustomizer> customizers) { public RuntimeWiring runtimeWiring(ObjectProvider<RuntimeWiringBuilderCustomizer> customizers) {
RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring(); RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring();
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder.build(); return builder.build();
} }
@Bean @Bean
public GraphQlSource.Builder graphQlSourceBuilder(ApplicationContext applicationContext, GraphQlProperties properties, public GraphQlSource.Builder graphQlSourceBuilder(ResourcePatternResolver resourcePatternResolver, GraphQlProperties properties,
RuntimeWiring runtimeWiring, ObjectProvider<DataFetcherExceptionResolver> exceptionResolversProvider, RuntimeWiring runtimeWiring, ObjectProvider<DataFetcherExceptionResolver> exceptionResolversProvider,
ObjectProvider<Instrumentation> instrumentationsProvider) throws IOException { ObjectProvider<Instrumentation> instrumentationsProvider) throws IOException {
List<Resource> schemaResources = resolveSchemaResources(applicationContext, properties.getSchema().getLocations()); List<Resource> schemaResources = resolveSchemaResources(resourcePatternResolver, properties.getSchema().getLocations());
return GraphQlSource.builder().schemaResources(schemaResources.toArray(new Resource[0])) return GraphQlSource.builder().schemaResources(schemaResources.toArray(new Resource[0]))
.runtimeWiring(runtimeWiring) .runtimeWiring(runtimeWiring)
.exceptionResolvers(exceptionResolversProvider.orderedStream().collect(Collectors.toList())) .exceptionResolvers(exceptionResolversProvider.orderedStream().collect(Collectors.toList()))

View File

@@ -42,7 +42,7 @@ public class GraphQlProperties {
private final GraphiQL graphiql = new GraphiQL(); private final GraphiQL graphiql = new GraphiQL();
private final WebSocket websocket = new WebSocket(); private final Websocket websocket = new Websocket();
public String getPath() { public String getPath() {
return this.path; return this.path;
@@ -60,7 +60,7 @@ public class GraphQlProperties {
return this.graphiql; return this.graphiql;
} }
public WebSocket getWebsocket() { public Websocket getWebsocket() {
return this.websocket; return this.websocket;
} }
@@ -152,7 +152,7 @@ public class GraphQlProperties {
} }
} }
public static class WebSocket { public static class Websocket {
/** /**
* Path of the GraphQL WebSocket subscription endpoint. * Path of the GraphQL WebSocket subscription endpoint.

View File

@@ -65,14 +65,15 @@ import static org.springframework.web.reactive.function.server.RequestPredicates
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnClass(GraphQL.class) @ConditionalOnClass(GraphQL.class)
@ConditionalOnBean(GraphQlSource.class) @ConditionalOnBean(GraphQlSource.class)
@AutoConfigureAfter(GraphQlAutoConfiguration.class) @AutoConfigureAfter(GraphQlServiceAutoConfiguration.class)
public class WebFluxGraphQlAutoConfiguration { public class GraphQlWebFluxAutoConfiguration {
private static final Log logger = LogFactory.getLog(WebFluxGraphQlAutoConfiguration.class); private static final Log logger = LogFactory.getLog(GraphQlWebFluxAutoConfiguration.class);
@Bean @Bean
@ConditionalOnBean(GraphQlService.class)
@ConditionalOnMissingBean @ConditionalOnMissingBean
public WebGraphQlHandler webGraphQlHandler(ObjectProvider<WebInterceptor> interceptors, GraphQlService service) { public WebGraphQlHandler webGraphQlHandler(GraphQlService service, ObjectProvider<WebInterceptor> interceptors) {
return WebGraphQlHandler.builder(service) return WebGraphQlHandler.builder(service)
.interceptors(interceptors.orderedStream().collect(Collectors.toList())).build(); .interceptors(interceptors.orderedStream().collect(Collectors.toList())).build();
} }
@@ -86,7 +87,6 @@ public class WebFluxGraphQlAutoConfiguration {
@Bean @Bean
public RouterFunction<ServerResponse> graphQlEndpoint(GraphQlHttpHandler handler, GraphQlSource graphQlSource, public RouterFunction<ServerResponse> graphQlEndpoint(GraphQlHttpHandler handler, GraphQlSource graphQlSource,
GraphQlProperties properties, ResourceLoader resourceLoader) { GraphQlProperties properties, ResourceLoader resourceLoader) {
String graphQLPath = properties.getPath(); String graphQLPath = properties.getPath();
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info("GraphQL endpoint HTTP POST " + graphQLPath); logger.info("GraphQL endpoint HTTP POST " + graphQLPath);
@@ -96,7 +96,7 @@ public class WebFluxGraphQlAutoConfiguration {
.POST(graphQLPath, accept(MediaType.APPLICATION_JSON).and(contentType(MediaType.APPLICATION_JSON)), handler::handleRequest); .POST(graphQLPath, accept(MediaType.APPLICATION_JSON).and(contentType(MediaType.APPLICATION_JSON)), handler::handleRequest);
if (properties.getGraphiql().isEnabled()) { if (properties.getGraphiql().isEnabled()) {
Resource resource = resourceLoader.getResource("classpath:graphiql/index.html"); Resource resource = resourceLoader.getResource("classpath:graphiql/index.html");
WebFluxGraphiQlHandler graphiQlHandler = new WebFluxGraphiQlHandler(graphQLPath, resource); GraphiQlWebFluxHandler graphiQlHandler = new GraphiQlWebFluxHandler(graphQLPath, resource);
builder = builder.GET(properties.getGraphiql().getPath(), graphiQlHandler::showGraphiQlPage); builder = builder.GET(properties.getGraphiql().getPath(), graphiQlHandler::showGraphiQlPage);
} }
if (properties.getSchema().getPrinter().isEnabled()) { if (properties.getSchema().getPrinter().isEnabled()) {
@@ -118,7 +118,6 @@ public class WebFluxGraphQlAutoConfiguration {
@ConditionalOnMissingBean @ConditionalOnMissingBean
public GraphQlWebSocketHandler graphQlWebSocketHandler(WebGraphQlHandler webGraphQlHandler, public GraphQlWebSocketHandler graphQlWebSocketHandler(WebGraphQlHandler webGraphQlHandler,
GraphQlProperties properties, ServerCodecConfigurer configurer) { GraphQlProperties properties, ServerCodecConfigurer configurer) {
return new GraphQlWebSocketHandler(webGraphQlHandler, configurer, return new GraphQlWebSocketHandler(webGraphQlHandler, configurer,
properties.getWebsocket().getConnectionInitTimeout()); properties.getWebsocket().getConnectionInitTimeout());
} }
@@ -126,7 +125,6 @@ public class WebFluxGraphQlAutoConfiguration {
@Bean @Bean
public HandlerMapping graphQlWebSocketEndpoint(GraphQlWebSocketHandler graphQlWebSocketHandler, public HandlerMapping graphQlWebSocketEndpoint(GraphQlWebSocketHandler graphQlWebSocketHandler,
GraphQlProperties properties) { GraphQlProperties properties) {
String path = properties.getWebsocket().getPath(); String path = properties.getWebsocket().getPath();
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info("GraphQL endpoint WebSocket " + path); logger.info("GraphQL endpoint WebSocket " + path);

View File

@@ -72,16 +72,16 @@ import static org.springframework.web.servlet.function.RequestPredicates.content
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(GraphQL.class) @ConditionalOnClass(GraphQL.class)
@ConditionalOnBean(GraphQlSource.class) @ConditionalOnBean(GraphQlSource.class)
@AutoConfigureAfter(GraphQlAutoConfiguration.class) @AutoConfigureAfter(GraphQlServiceAutoConfiguration.class)
public class WebMvcGraphQlAutoConfiguration { public class GraphQlWebMvcAutoConfiguration {
private static final Log logger = LogFactory.getLog(WebMvcGraphQlAutoConfiguration.class); private static final Log logger = LogFactory.getLog(GraphQlWebMvcAutoConfiguration.class);
@Bean @Bean
@ConditionalOnBean(GraphQlService.class)
@ConditionalOnMissingBean @ConditionalOnMissingBean
public WebGraphQlHandler webGraphQlHandler(ObjectProvider<WebInterceptor> interceptorsProvider, public WebGraphQlHandler webGraphQlHandler(GraphQlService service, ObjectProvider<WebInterceptor> interceptorsProvider,
GraphQlService service, ObjectProvider<ThreadLocalAccessor> accessorsProvider) { ObjectProvider<ThreadLocalAccessor> accessorsProvider) {
return WebGraphQlHandler.builder(service) return WebGraphQlHandler.builder(service)
.interceptors(interceptorsProvider.orderedStream().collect(Collectors.toList())) .interceptors(interceptorsProvider.orderedStream().collect(Collectors.toList()))
.threadLocalAccessors(accessorsProvider.orderedStream().collect(Collectors.toList())).build(); .threadLocalAccessors(accessorsProvider.orderedStream().collect(Collectors.toList())).build();
@@ -96,7 +96,6 @@ public class WebMvcGraphQlAutoConfiguration {
@Bean @Bean
public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler handler, GraphQlSource graphQlSource, public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler handler, GraphQlSource graphQlSource,
GraphQlProperties properties, ResourceLoader resourceLoader) { GraphQlProperties properties, ResourceLoader resourceLoader) {
String graphQLPath = properties.getPath(); String graphQLPath = properties.getPath();
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info("GraphQL endpoint HTTP POST " + graphQLPath); logger.info("GraphQL endpoint HTTP POST " + graphQLPath);
@@ -106,7 +105,7 @@ public class WebMvcGraphQlAutoConfiguration {
.POST(graphQLPath, contentType(MediaType.APPLICATION_JSON).and(accept(MediaType.APPLICATION_JSON)), handler::handleRequest); .POST(graphQLPath, contentType(MediaType.APPLICATION_JSON).and(accept(MediaType.APPLICATION_JSON)), handler::handleRequest);
if (properties.getGraphiql().isEnabled()) { if (properties.getGraphiql().isEnabled()) {
Resource resource = resourceLoader.getResource("classpath:graphiql/index.html"); Resource resource = resourceLoader.getResource("classpath:graphiql/index.html");
WebMvcGraphiQlHandler graphiQLHandler = new WebMvcGraphiQlHandler(graphQLPath, resource); GraphiQlWebMvcHandler graphiQLHandler = new GraphiQlWebMvcHandler(graphQLPath, resource);
builder = builder.GET(properties.getGraphiql().getPath(), graphiQLHandler::showGraphiQlPage); builder = builder.GET(properties.getGraphiql().getPath(), graphiQLHandler::showGraphiQlPage);
} }
if (properties.getSchema().getPrinter().isEnabled()) { if (properties.getSchema().getPrinter().isEnabled()) {
@@ -121,7 +120,7 @@ public class WebMvcGraphQlAutoConfiguration {
} }
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ServerContainer.class, WebSocketHandler.class}) @ConditionalOnClass({ ServerContainer.class, WebSocketHandler.class })
@ConditionalOnProperty(prefix = "spring.graphql.websocket", name = "path") @ConditionalOnProperty(prefix = "spring.graphql.websocket", name = "path")
public static class WebSocketConfiguration { public static class WebSocketConfiguration {
@@ -129,7 +128,6 @@ public class WebMvcGraphQlAutoConfiguration {
@ConditionalOnMissingBean @ConditionalOnMissingBean
public GraphQlWebSocketHandler graphQlWebSocketHandler(WebGraphQlHandler webGraphQlHandler, public GraphQlWebSocketHandler graphQlWebSocketHandler(WebGraphQlHandler webGraphQlHandler,
GraphQlProperties properties, HttpMessageConverters converters) { GraphQlProperties properties, HttpMessageConverters converters) {
// @formatter:off // @formatter:off
HttpMessageConverter<?> converter = converters.getConverters().stream() HttpMessageConverter<?> converter = converters.getConverters().stream()
.filter((candidate) -> candidate.canRead(Map.class, MediaType.APPLICATION_JSON)) .filter((candidate) -> candidate.canRead(Map.class, MediaType.APPLICATION_JSON))

View File

@@ -25,20 +25,21 @@ import org.springframework.web.reactive.function.server.ServerResponse;
/** /**
* WebFlux functional handler for the GraphiQl UI. * WebFlux functional handler for the GraphiQl UI.
*
* @author Brian Clozel * @author Brian Clozel
*/ */
class WebFluxGraphiQlHandler { class GraphiQlWebFluxHandler {
private final String graphQlPath; private final String graphQlPath;
private final Resource graphiQlResource; private final Resource graphiQlResource;
public WebFluxGraphiQlHandler(String graphQlPath, Resource graphiQlResource) { GraphiQlWebFluxHandler(String graphQlPath, Resource graphiQlResource) {
this.graphQlPath = graphQlPath; this.graphQlPath = graphQlPath;
this.graphiQlResource = graphiQlResource; this.graphiQlResource = graphiQlResource;
} }
public Mono<ServerResponse> showGraphiQlPage(ServerRequest request) { Mono<ServerResponse> showGraphiQlPage(ServerRequest request) {
if (request.queryParam("path").isPresent()) { if (request.queryParam("path").isPresent()) {
return ServerResponse.ok().contentType(MediaType.TEXT_HTML).bodyValue(this.graphiQlResource); return ServerResponse.ok().contentType(MediaType.TEXT_HTML).bodyValue(this.graphiQlResource);
} }

View File

@@ -23,20 +23,21 @@ import org.springframework.web.servlet.function.ServerResponse;
/** /**
* Servlet.fn handler for the GraphiQl UI. * Servlet.fn handler for the GraphiQl UI.
*
* @author Brian Clozel * @author Brian Clozel
*/ */
class WebMvcGraphiQlHandler { class GraphiQlWebMvcHandler {
private final String graphQlPath; private final String graphQlPath;
private final Resource graphiQlResource; private final Resource graphiQlResource;
public WebMvcGraphiQlHandler(String graphQlPath, Resource graphiQlResource) { GraphiQlWebMvcHandler(String graphQlPath, Resource graphiQlResource) {
this.graphQlPath = graphQlPath; this.graphQlPath = graphQlPath;
this.graphiQlResource = graphiQlResource; this.graphiQlResource = graphiQlResource;
} }
public ServerResponse showGraphiQlPage(ServerRequest request) { ServerResponse showGraphiQlPage(ServerRequest request) {
if (request.param("path").isPresent()) { if (request.param("path").isPresent()) {
return ServerResponse.ok().contentType(MediaType.TEXT_HTML).body(this.graphiQlResource); return ServerResponse.ok().contentType(MediaType.TEXT_HTML).body(this.graphiQlResource);
} }
@@ -44,4 +45,5 @@ class WebMvcGraphiQlHandler {
return ServerResponse.temporaryRedirect(request.uriBuilder().queryParam("path", this.graphQlPath).build()).build(); return ServerResponse.temporaryRedirect(request.uriBuilder().queryParam("path", this.graphQlPath).build()).build();
} }
} }
} }

View File

@@ -19,18 +19,19 @@ package org.springframework.graphql.boot;
import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.RuntimeWiring;
/** /**
* Callback interface that can be used to customize the GraphQL * Callback interface that can be implemented by beans wishing to customize the
* {@link RuntimeWiring.Builder}. * {@link RuntimeWiring} via a {@link RuntimeWiring.Builder} whilst retaining default
* auto-configuration.
* *
* @author Brian Clozel * @author Brian Clozel
* @since 1.0.0 * @since 1.0.0
*/ */
@FunctionalInterface @FunctionalInterface
public interface RuntimeWiringCustomizer { public interface RuntimeWiringBuilderCustomizer {
/** /**
* Callback to customize a {@link RuntimeWiring.Builder} instance. * Customize the {@link RuntimeWiring.Builder} instance.
* @param builder builder instance to customize * @param builder builder the builder to customize
*/ */
void customize(RuntimeWiring.Builder builder); void customize(RuntimeWiring.Builder builder);

View File

@@ -2,12 +2,14 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.graphql.boot.actuate.metrics.GraphQlMetricsAutoConfiguration,\ org.springframework.graphql.boot.actuate.metrics.GraphQlMetricsAutoConfiguration,\
org.springframework.graphql.boot.GraphQlAutoConfiguration,\ org.springframework.graphql.boot.GraphQlAutoConfiguration,\
org.springframework.graphql.boot.GraphQlServiceAutoConfiguration,\ org.springframework.graphql.boot.GraphQlServiceAutoConfiguration,\
org.springframework.graphql.boot.WebFluxGraphQlAutoConfiguration,\ org.springframework.graphql.boot.GraphQlWebFluxAutoConfiguration,\
org.springframework.graphql.boot.WebMvcGraphQlAutoConfiguration org.springframework.graphql.boot.GraphQlWebMvcAutoConfiguration
# Spring Test @AutoConfigureGraphQlTester # Spring Test @AutoConfigureGraphQlTester
org.springframework.graphql.boot.test.tester.AutoConfigureGraphQlTester=\ org.springframework.graphql.boot.test.tester.AutoConfigureGraphQlTester=\
org.springframework.graphql.boot.test.tester.WebTestClientMockMvcAutoConfiguration,\ org.springframework.graphql.boot.test.tester.WebTestClientMockMvcAutoConfiguration,\
org.springframework.graphql.boot.test.tester.GraphQlTesterAutoConfiguration org.springframework.graphql.boot.test.tester.GraphQlTesterAutoConfiguration
# Spring Test ContextCustomizerFactories # Spring Test ContextCustomizerFactories
org.springframework.test.context.ContextCustomizerFactory=\ org.springframework.test.context.ContextCustomizerFactory=\
org.springframework.graphql.boot.test.tester.GraphQlTesterContextCustomizerFactory org.springframework.graphql.boot.test.tester.GraphQlTesterContextCustomizerFactory

View File

@@ -27,9 +27,7 @@ import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration; import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration; import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.graphql.web.WebInterceptor; import org.springframework.graphql.web.WebInterceptor;
@@ -40,15 +38,20 @@ import static org.hamcrest.Matchers.containsString;
// @formatter:off // @formatter:off
class WebFluxApplicationContextTests { class GraphQlWebFluxAutoConfigurationTests {
private static final AutoConfigurations AUTO_CONFIGURATIONS = AutoConfigurations.of(
HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class, CodecsAutoConfiguration.class,
JacksonAutoConfiguration.class, GraphQlAutoConfiguration.class, GraphQlServiceAutoConfiguration.class,
WebFluxGraphQlAutoConfiguration.class);
private static final String BASE_URL = "https://spring.example.org/graphql"; private static final String BASE_URL = "https://spring.example.org/graphql";
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class,
CodecsAutoConfiguration.class, JacksonAutoConfiguration.class, GraphQlAutoConfiguration.class,
GraphQlServiceAutoConfiguration.class, GraphQlWebFluxAutoConfiguration.class))
.withUserConfiguration(DataFetchersConfiguration.class, CustomWebInterceptor.class)
.withPropertyValues(
"spring.main.web-application-type=reactive",
"spring.graphql.schema.printer.enabled=true",
"spring.graphql.schema.locations=classpath:books/");
@Test @Test
void query() { void query() {
testWithWebClient((client) -> { testWithWebClient((client) -> {
@@ -56,7 +59,7 @@ class WebFluxApplicationContextTests {
" bookById(id: \\\"book-1\\\"){ " + " bookById(id: \\\"book-1\\\"){ " +
" id" + " id" +
" name" + " name" +
" pageCount" + " pageCount" +
" author" + " author" +
" }" + " }" +
"}"; "}";
@@ -124,7 +127,7 @@ class WebFluxApplicationContextTests {
} }
private void testWithWebClient(Consumer<WebTestClient> consumer) { private void testWithWebClient(Consumer<WebTestClient> consumer) {
testWithApplicationContext((context) -> { this.contextRunner.run((context) -> {
WebTestClient client = WebTestClient.bindToApplicationContext(context) WebTestClient client = WebTestClient.bindToApplicationContext(context)
.configureClient() .configureClient()
.defaultHeaders((headers) -> { .defaultHeaders((headers) -> {
@@ -137,22 +140,11 @@ class WebFluxApplicationContextTests {
}); });
} }
private void testWithApplicationContext(ContextConsumer<ApplicationContext> consumer) {
new ReactiveWebApplicationContextRunner()
.withConfiguration(AUTO_CONFIGURATIONS)
.withUserConfiguration(DataFetchersConfiguration.class, CustomWebInterceptor.class)
.withPropertyValues(
"spring.main.web-application-type=reactive",
"spring.graphql.schema.printer.enabled=true",
"spring.graphql.schema.locations=classpath:books/")
.run(consumer);
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
public static class DataFetchersConfiguration { static class DataFetchersConfiguration {
@Bean @Bean
public RuntimeWiringCustomizer bookDataFetcher() { RuntimeWiringBuilderCustomizer bookDataFetcher() {
return (runtimeWiring) -> return (runtimeWiring) ->
runtimeWiring.type(TypeRuntimeWiring.newTypeWiring("Query") runtimeWiring.type(TypeRuntimeWiring.newTypeWiring("Query")
.dataFetcher("bookById", GraphQlDataFetchers.getBookByIdDataFetcher())); .dataFetcher("bookById", GraphQlDataFetchers.getBookByIdDataFetcher()));
@@ -161,10 +153,10 @@ class WebFluxApplicationContextTests {
} }
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
public static class CustomWebInterceptor { static class CustomWebInterceptor {
@Bean @Bean
public WebInterceptor customWebInterceptor() { WebInterceptor customWebInterceptor() {
return (input, next) -> next.handle(input).map((output) -> return (input, next) -> next.handle(input).map((output) ->
output.transform((builder) -> builder.responseHeader("X-Custom-Header", "42"))); output.transform((builder) -> builder.responseHeader("X-Custom-Header", "42")));
} }

View File

@@ -44,13 +44,18 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
// @formatter:off // @formatter:off
class WebMvcApplicationContextTests { class GraphQlWebMvcAutoConfigurationTests {
public static final AutoConfigurations AUTO_CONFIGURATIONS = AutoConfigurations.of( private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, .withConfiguration(AutoConfigurations.of(DispatcherServletAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, JacksonAutoConfiguration.class, WebMvcAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
GraphQlAutoConfiguration.class, GraphQlServiceAutoConfiguration.class, JacksonAutoConfiguration.class, GraphQlAutoConfiguration.class,
WebMvcGraphQlAutoConfiguration.class); GraphQlServiceAutoConfiguration.class, GraphQlWebMvcAutoConfiguration.class))
.withUserConfiguration(DataFetchersConfiguration.class, CustomWebInterceptor.class)
.withPropertyValues(
"spring.main.web-application-type=servlet",
"spring.graphql.schema.printer.enabled=true",
"spring.graphql.schema.locations=classpath:books/");
@Test @Test
void endpointHandlesGraphQlQuery() { void endpointHandlesGraphQlQuery() {
@@ -108,20 +113,13 @@ class WebMvcApplicationContextTests {
} }
private void testWith(MockMvcConsumer mockMvcConsumer) { private void testWith(MockMvcConsumer mockMvcConsumer) {
new WebApplicationContextRunner() this.contextRunner.run((context) -> {
.withConfiguration(AUTO_CONFIGURATIONS) MediaType mediaType = MediaType.APPLICATION_JSON;
.withUserConfiguration(DataFetchersConfiguration.class, CustomWebInterceptor.class) MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context)
.withPropertyValues( .defaultRequest(post("/graphql").contentType(mediaType).accept(mediaType))
"spring.main.web-application-type=servlet", .build();
"spring.graphql.schema.printer.enabled=true", mockMvcConsumer.accept(mockMvc);
"spring.graphql.schema.locations=classpath:books/") });
.run((context) -> {
MediaType mediaType = MediaType.APPLICATION_JSON;
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context)
.defaultRequest(post("/graphql").contentType(mediaType).accept(mediaType))
.build();
mockMvcConsumer.accept(mockMvc);
});
} }
private interface MockMvcConsumer { private interface MockMvcConsumer {
@@ -131,10 +129,10 @@ class WebMvcApplicationContextTests {
} }
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
public static class DataFetchersConfiguration { static class DataFetchersConfiguration {
@Bean @Bean
public RuntimeWiringCustomizer bookDataFetcher() { RuntimeWiringBuilderCustomizer bookDataFetcher() {
return (builder) -> builder.type(TypeRuntimeWiring.newTypeWiring("Query") return (builder) -> builder.type(TypeRuntimeWiring.newTypeWiring("Query")
.dataFetcher("bookById", GraphQlDataFetchers.getBookByIdDataFetcher())); .dataFetcher("bookById", GraphQlDataFetchers.getBookByIdDataFetcher()));
} }
@@ -142,10 +140,10 @@ class WebMvcApplicationContextTests {
} }
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
public static class CustomWebInterceptor { static class CustomWebInterceptor {
@Bean @Bean
public WebInterceptor customWebInterceptor() { WebInterceptor customWebInterceptor() {
return (input, next) -> next.handle(input).map((output) -> return (input, next) -> next.handle(input).map((output) ->
output.transform((builder) -> builder.responseHeader("X-Custom-Header", "42"))); output.transform((builder) -> builder.responseHeader("X-Custom-Header", "42")));
} }

View File

@@ -20,11 +20,11 @@ import java.util.Map;
import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.RuntimeWiring;
import org.springframework.graphql.boot.RuntimeWiringCustomizer; import org.springframework.graphql.boot.RuntimeWiringBuilderCustomizer;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
public class SampleWiring implements RuntimeWiringCustomizer { public class SampleWiring implements RuntimeWiringBuilderCustomizer {
final EmployeeService employeeService; final EmployeeService employeeService;

View File

@@ -18,11 +18,11 @@ package io.spring.sample.graphql;
import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.RuntimeWiring;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.graphql.boot.RuntimeWiringCustomizer; import org.springframework.graphql.boot.RuntimeWiringBuilderCustomizer;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
public class SampleWiring implements RuntimeWiringCustomizer { public class SampleWiring implements RuntimeWiringBuilderCustomizer {
private final DataRepository repository; private final DataRepository repository;

View File

@@ -5,11 +5,11 @@ import java.util.Map;
import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.RuntimeWiring;
import org.springframework.graphql.boot.RuntimeWiringCustomizer; import org.springframework.graphql.boot.RuntimeWiringBuilderCustomizer;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
public class SampleWiring implements RuntimeWiringCustomizer { public class SampleWiring implements RuntimeWiringBuilderCustomizer {
final EmployeeService employeeService; final EmployeeService employeeService;

View File

@@ -2,7 +2,7 @@ package io.spring.sample.graphql.greeting;
import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.RuntimeWiring;
import org.springframework.graphql.boot.RuntimeWiringCustomizer; import org.springframework.graphql.boot.RuntimeWiringBuilderCustomizer;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
@@ -10,7 +10,7 @@ import org.springframework.web.context.request.RequestContextHolder;
import static org.springframework.web.context.request.RequestAttributes.SCOPE_REQUEST; import static org.springframework.web.context.request.RequestAttributes.SCOPE_REQUEST;
@Component @Component
public class GreetingDataWiring implements RuntimeWiringCustomizer { public class GreetingDataWiring implements RuntimeWiringBuilderCustomizer {
@Override @Override
public void customize(RuntimeWiring.Builder builder) { public void customize(RuntimeWiring.Builder builder) {

View File

@@ -1,11 +1,11 @@
package io.spring.sample.graphql.project; package io.spring.sample.graphql.project;
import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.RuntimeWiring;
import org.springframework.graphql.boot.RuntimeWiringCustomizer; import org.springframework.graphql.boot.RuntimeWiringBuilderCustomizer;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
public class ProjectDataWiring implements RuntimeWiringCustomizer { public class ProjectDataWiring implements RuntimeWiringBuilderCustomizer {
private final SpringProjectsClient client; private final SpringProjectsClient client;

View File

@@ -2,12 +2,12 @@ package io.spring.sample.graphql.repository;
import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.RuntimeWiring;
import org.springframework.graphql.boot.RuntimeWiringCustomizer; import org.springframework.graphql.boot.RuntimeWiringBuilderCustomizer;
import org.springframework.graphql.data.QuerydslDataFetcher; import org.springframework.graphql.data.QuerydslDataFetcher;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
public class ArtifactRepositoryDataWiring implements RuntimeWiringCustomizer { public class ArtifactRepositoryDataWiring implements RuntimeWiringBuilderCustomizer {
private final ArtifactRepositories repositories; private final ArtifactRepositories repositories;