Polish GraphQL auto-configuration

pull/29636/head
Phillip Webb 3 years ago
parent ce08b519b1
commit a05903b9d1

@ -19,11 +19,13 @@ package org.springframework.boot.autoconfigure.graphql;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import graphql.GraphQL;
import graphql.execution.instrumentation.Instrumentation;
import graphql.schema.idl.RuntimeWiring.Builder;
import graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -67,29 +69,49 @@ public class GraphQlAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public GraphQlSource graphQlSource(ResourcePatternResolver resourcePatternResolver, GraphQlProperties properties,
ObjectProvider<DataFetcherExceptionResolver> exceptionResolversProvider,
ObjectProvider<Instrumentation> instrumentationsProvider,
ObjectProvider<RuntimeWiringConfigurer> wiringConfigurers,
ObjectProvider<DataFetcherExceptionResolver> exceptionResolvers,
ObjectProvider<Instrumentation> instrumentations, ObjectProvider<RuntimeWiringConfigurer> wiringConfigurers,
ObjectProvider<GraphQlSourceBuilderCustomizer> sourceCustomizers) {
List<Resource> schemaResources = resolveSchemaResources(resourcePatternResolver,
properties.getSchema().getLocations(), properties.getSchema().getFileExtensions());
GraphQlSource.Builder builder = GraphQlSource.builder()
.schemaResources(schemaResources.toArray(new Resource[0]))
.exceptionResolvers(exceptionResolversProvider.orderedStream().collect(Collectors.toList()))
.instrumentation(instrumentationsProvider.orderedStream().collect(Collectors.toList()));
String[] schemaLocations = properties.getSchema().getLocations();
Resource[] schemaResources = resolveSchemaResources(resourcePatternResolver, schemaLocations,
properties.getSchema().getFileExtensions());
GraphQlSource.Builder builder = GraphQlSource.builder().schemaResources(schemaResources)
.exceptionResolvers(toList(exceptionResolvers)).instrumentation(toList(instrumentations));
if (!properties.getSchema().getIntrospection().isEnabled()) {
builder.configureRuntimeWiring((wiring) -> wiring
.fieldVisibility(NoIntrospectionGraphqlFieldVisibility.NO_INTROSPECTION_FIELD_VISIBILITY));
builder.configureRuntimeWiring(this::enableIntrospection);
}
wiringConfigurers.orderedStream().forEach(builder::configureRuntimeWiring);
sourceCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
try {
return builder.build();
}
catch (MissingSchemaException exc) {
throw new InvalidSchemaLocationsException(properties.getSchema().getLocations(), resourcePatternResolver,
exc);
catch (MissingSchemaException ex) {
throw new InvalidSchemaLocationsException(schemaLocations, resourcePatternResolver, ex);
}
}
private Builder enableIntrospection(Builder wiring) {
return wiring.fieldVisibility(NoIntrospectionGraphqlFieldVisibility.NO_INTROSPECTION_FIELD_VISIBILITY);
}
private Resource[] resolveSchemaResources(ResourcePatternResolver resolver, String[] locations,
String[] extensions) {
List<Resource> resources = new ArrayList<>();
for (String location : locations) {
for (String extension : extensions) {
resources.addAll(resolveSchemaResources(resolver, location + "*" + extension));
}
}
return resources.toArray(new Resource[0]);
}
private List<Resource> resolveSchemaResources(ResourcePatternResolver resolver, String pattern) {
try {
return Arrays.asList(resolver.getResources(pattern));
}
catch (IOException ex) {
logger.debug("Could not resolve schema location: '" + pattern + "'", ex);
return Collections.emptyList();
}
}
@ -115,21 +137,8 @@ public class GraphQlAutoConfiguration {
return annotatedControllerConfigurer;
}
private List<Resource> resolveSchemaResources(ResourcePatternResolver resolver, String[] schemaLocations,
String[] fileExtensions) {
List<Resource> schemaResources = new ArrayList<>();
for (String location : schemaLocations) {
for (String extension : fileExtensions) {
String resourcePattern = location + "*" + extension;
try {
schemaResources.addAll(Arrays.asList(resolver.getResources(resourcePattern)));
}
catch (IOException ex) {
logger.debug("Could not resolve schema location: '" + resourcePattern + "'", ex);
}
}
}
return schemaResources;
private <T> List<T> toList(ObjectProvider<T> provider) {
return provider.orderedStream().collect(Collectors.toList());
}
}

@ -24,7 +24,6 @@ import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.web.cors.CorsConfiguration;
@ -73,7 +72,6 @@ public class GraphQlCorsProperties {
/**
* Whether credentials are supported. When not set, credentials are not supported.
*/
@Nullable
private Boolean allowCredentials;
/**
@ -123,7 +121,6 @@ public class GraphQlCorsProperties {
this.exposedHeaders = exposedHeaders;
}
@Nullable
public Boolean getAllowCredentials() {
return this.allowCredentials;
}
@ -140,7 +137,6 @@ public class GraphQlCorsProperties {
this.maxAge = maxAge;
}
@Nullable
public CorsConfiguration toCorsConfiguration() {
if (CollectionUtils.isEmpty(this.allowedOrigins) && CollectionUtils.isEmpty(this.allowedOriginPatterns)) {
return null;

@ -20,8 +20,8 @@ import org.springframework.graphql.execution.GraphQlSource;
/**
* Callback interface that can be implemented by beans wishing to customize properties of
* {@link org.springframework.graphql.execution.GraphQlSource.Builder} whilst retaining
* default auto-configuration.
* {@link org.springframework.graphql.execution.GraphQlSource.Builder Builder} whilst
* retaining default auto-configuration.
*
* @author Rossen Stoyanchev
* @since 2.7.0
@ -30,7 +30,8 @@ import org.springframework.graphql.execution.GraphQlSource;
public interface GraphQlSourceBuilderCustomizer {
/**
* Customize the {@link GraphQlSource.Builder} instance.
* Customize the {@link org.springframework.graphql.execution.GraphQlSource.Builder
* Builder} instance.
* @param builder builder the builder to customize
*/
void customize(GraphQlSource.Builder builder);

@ -17,7 +17,6 @@
package org.springframework.boot.autoconfigure.graphql.data;
import java.util.List;
import java.util.stream.Collectors;
import graphql.GraphQL;
@ -52,19 +51,10 @@ import org.springframework.graphql.execution.GraphQlSource;
public class GraphQlQueryByExampleAutoConfiguration {
@Bean
public GraphQlSourceBuilderCustomizer queryByExampleRegistrar(
ObjectProvider<QueryByExampleExecutor<?>> executorsProvider,
ObjectProvider<ReactiveQueryByExampleExecutor<?>> reactiveExecutorsProvider) {
return (builder) -> {
List<QueryByExampleExecutor<?>> executors = executorsProvider.stream().collect(Collectors.toList());
List<ReactiveQueryByExampleExecutor<?>> reactiveExecutors = reactiveExecutorsProvider.stream()
.collect(Collectors.toList());
if (!executors.isEmpty()) {
builder.configureRuntimeWiring(
QueryByExampleDataFetcher.autoRegistrationConfigurer(executors, reactiveExecutors));
}
};
public GraphQlSourceBuilderCustomizer queryByExampleRegistrar(ObjectProvider<QueryByExampleExecutor<?>> executors,
ObjectProvider<ReactiveQueryByExampleExecutor<?>> reactiveExecutors) {
return new GraphQlQuerydslSourceBuilderCustomizer<>(QueryByExampleDataFetcher::autoRegistrationConfigurer,
executors, reactiveExecutors);
}
}

@ -17,7 +17,6 @@
package org.springframework.boot.autoconfigure.graphql.data;
import java.util.List;
import java.util.stream.Collectors;
import graphql.GraphQL;
@ -53,19 +52,10 @@ import org.springframework.graphql.execution.GraphQlSource;
public class GraphQlQuerydslAutoConfiguration {
@Bean
public GraphQlSourceBuilderCustomizer querydslRegistrar(
ObjectProvider<QuerydslPredicateExecutor<?>> executorsProvider,
ObjectProvider<ReactiveQuerydslPredicateExecutor<?>> reactiveExecutorsProvider) {
return (builder) -> {
List<QuerydslPredicateExecutor<?>> executors = executorsProvider.stream().collect(Collectors.toList());
if (!executors.isEmpty()) {
List<ReactiveQuerydslPredicateExecutor<?>> reactiveExecutors = reactiveExecutorsProvider.stream()
.collect(Collectors.toList());
builder.configureRuntimeWiring(
QuerydslDataFetcher.autoRegistrationConfigurer(executors, reactiveExecutors));
}
};
public GraphQlSourceBuilderCustomizer querydslRegistrar(ObjectProvider<QuerydslPredicateExecutor<?>> executors,
ObjectProvider<ReactiveQuerydslPredicateExecutor<?>> reactiveExecutors) {
return new GraphQlQuerydslSourceBuilderCustomizer<>(QuerydslDataFetcher::autoRegistrationConfigurer, executors,
reactiveExecutors);
}
}

@ -0,0 +1,72 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.graphql.data;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer;
import org.springframework.graphql.execution.GraphQlSource.Builder;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
/**
* {@link GraphQlSourceBuilderCustomizer} to apply auto-configured QueryDSL
* {@link RuntimeWiringConfigurer RuntimeWiringConfigurers}.
*
* @param <E> the executor type
* @param <R> the reactive executor type
* @author Phillip Webb
* @author Rossen Stoyanchev
* @author Brian Clozel
*/
class GraphQlQuerydslSourceBuilderCustomizer<E, R> implements GraphQlSourceBuilderCustomizer {
private final BiFunction<List<E>, List<R>, RuntimeWiringConfigurer> wiringConfigurerFactory;
private final List<E> executors;
private final List<R> reactiveExecutors;
GraphQlQuerydslSourceBuilderCustomizer(
BiFunction<List<E>, List<R>, RuntimeWiringConfigurer> wiringConfigurerFactory, ObjectProvider<E> executors,
ObjectProvider<R> reactiveExecutors) {
this(wiringConfigurerFactory, toList(executors), toList(reactiveExecutors));
}
GraphQlQuerydslSourceBuilderCustomizer(
BiFunction<List<E>, List<R>, RuntimeWiringConfigurer> wiringConfigurerFactory, List<E> executors,
List<R> reactiveExecutors) {
this.wiringConfigurerFactory = wiringConfigurerFactory;
this.executors = executors;
this.reactiveExecutors = reactiveExecutors;
}
@Override
public void customize(Builder builder) {
if (!this.executors.isEmpty() || !this.reactiveExecutors.isEmpty()) {
builder.configureRuntimeWiring(this.wiringConfigurerFactory.apply(this.executors, this.reactiveExecutors));
}
}
private static <T> List<T> toList(ObjectProvider<T> provider) {
return (provider != null) ? provider.orderedStream().collect(Collectors.toList()) : Collections.emptyList();
}
}

@ -16,9 +16,7 @@
package org.springframework.boot.autoconfigure.graphql.data;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import graphql.GraphQL;
@ -31,6 +29,7 @@ import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration;
import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.repository.query.QueryByExampleExecutor;
import org.springframework.data.repository.query.ReactiveQueryByExampleExecutor;
import org.springframework.graphql.data.query.QueryByExampleDataFetcher;
import org.springframework.graphql.execution.GraphQlSource;
@ -53,15 +52,9 @@ public class GraphQlReactiveQueryByExampleAutoConfiguration {
@Bean
public GraphQlSourceBuilderCustomizer reactiveQueryByExampleRegistrar(
ObjectProvider<ReactiveQueryByExampleExecutor<?>> executorsProvider) {
return (builder) -> {
List<ReactiveQueryByExampleExecutor<?>> executors = executorsProvider.stream().collect(Collectors.toList());
if (!executors.isEmpty()) {
builder.configureRuntimeWiring(
QueryByExampleDataFetcher.autoRegistrationConfigurer(Collections.emptyList(), executors));
}
};
ObjectProvider<ReactiveQueryByExampleExecutor<?>> reactiveExecutors) {
return new GraphQlQuerydslSourceBuilderCustomizer<>(QueryByExampleDataFetcher::autoRegistrationConfigurer,
(ObjectProvider<QueryByExampleExecutor<?>>) null, reactiveExecutors);
}
}

@ -16,9 +16,7 @@
package org.springframework.boot.autoconfigure.graphql.data;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import graphql.GraphQL;
@ -31,6 +29,7 @@ import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration;
import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor;
import org.springframework.graphql.data.query.QuerydslDataFetcher;
import org.springframework.graphql.execution.GraphQlSource;
@ -54,16 +53,9 @@ public class GraphQlReactiveQuerydslAutoConfiguration {
@Bean
public GraphQlSourceBuilderCustomizer reactiveQuerydslRegistrar(
ObjectProvider<ReactiveQuerydslPredicateExecutor<?>> executorsProvider) {
return (builder) -> {
List<ReactiveQuerydslPredicateExecutor<?>> executors = executorsProvider.stream()
.collect(Collectors.toList());
if (!executors.isEmpty()) {
builder.configureRuntimeWiring(
QuerydslDataFetcher.autoRegistrationConfigurer(Collections.emptyList(), executors));
}
};
ObjectProvider<ReactiveQuerydslPredicateExecutor<?>> reactiveExecutors) {
return new GraphQlQuerydslSourceBuilderCustomizer<>(QuerydslDataFetcher::autoRegistrationConfigurer,
(ObjectProvider<QuerydslPredicateExecutor<?>>) null, reactiveExecutors);
}
}

@ -22,6 +22,7 @@ import java.util.stream.Collectors;
import graphql.GraphQL;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@ -38,6 +39,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.log.LogMessage;
import org.springframework.graphql.GraphQlService;
import org.springframework.graphql.execution.GraphQlSource;
import org.springframework.graphql.web.WebGraphQlHandler;
@ -46,6 +48,7 @@ import org.springframework.graphql.web.webflux.GraphQlHttpHandler;
import org.springframework.graphql.web.webflux.GraphQlWebSocketHandler;
import org.springframework.graphql.web.webflux.GraphiQlHandler;
import org.springframework.graphql.web.webflux.SchemaHandler;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@ -54,8 +57,10 @@ import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.reactive.socket.server.support.WebSocketUpgradeHandlerPredicate;
@ -78,6 +83,9 @@ import static org.springframework.web.reactive.function.server.RequestPredicates
@EnableConfigurationProperties(GraphQlCorsProperties.class)
public class GraphQlWebFluxAutoConfiguration {
private static final RequestPredicate ACCEPT_JSON_CONTENT = accept(MediaType.APPLICATION_JSON)
.and(contentType(MediaType.APPLICATION_JSON));
private static final Log logger = LogFactory.getLog(GraphQlWebFluxAutoConfiguration.class);
@Bean
@ -95,34 +103,32 @@ public class GraphQlWebFluxAutoConfiguration {
}
@Bean
public RouterFunction<ServerResponse> graphQlEndpoint(GraphQlHttpHandler handler, GraphQlSource graphQlSource,
public RouterFunction<ServerResponse> graphQlEndpoint(GraphQlHttpHandler httpHandler, GraphQlSource graphQlSource,
GraphQlProperties properties, ResourceLoader resourceLoader) {
String graphQLPath = properties.getPath();
if (logger.isInfoEnabled()) {
logger.info("GraphQL endpoint HTTP POST " + graphQLPath);
}
RouterFunctions.Builder builder = RouterFunctions.route()
.GET(graphQLPath,
(request) -> ServerResponse.status(HttpStatus.METHOD_NOT_ALLOWED)
.headers((headers) -> headers.setAllow(Collections.singleton(HttpMethod.POST))).build())
.POST(graphQLPath, accept(MediaType.APPLICATION_JSON).and(contentType(MediaType.APPLICATION_JSON)),
handler::handleRequest);
String path = properties.getPath();
logger.info(LogMessage.format("GraphQL endpoint HTTP POST %s", path));
RouterFunctions.Builder builder = RouterFunctions.route();
builder = builder.GET(path, this::onlyAllowPost);
builder = builder.POST(path, ACCEPT_JSON_CONTENT, httpHandler::handleRequest);
if (properties.getGraphiql().isEnabled()) {
GraphiQlHandler graphiQlHandler = new GraphiQlHandler(graphQLPath, properties.getWebsocket().getPath());
builder = builder.GET(properties.getGraphiql().getPath(), graphiQlHandler::handleRequest);
GraphiQlHandler graphQlHandler = new GraphiQlHandler(path, properties.getWebsocket().getPath());
builder = builder.GET(properties.getGraphiql().getPath(), graphQlHandler::handleRequest);
}
if (properties.getSchema().getPrinter().isEnabled()) {
SchemaHandler schemaHandler = new SchemaHandler(graphQlSource);
builder = builder.GET(graphQLPath + "/schema", schemaHandler::handleRequest);
builder = builder.GET(path + "/schema", schemaHandler::handleRequest);
}
return builder.build();
}
private Mono<ServerResponse> onlyAllowPost(ServerRequest request) {
return ServerResponse.status(HttpStatus.METHOD_NOT_ALLOWED).headers(this::onlyAllowPost).build();
}
private void onlyAllowPost(HttpHeaders headers) {
headers.setAllow(Collections.singleton(HttpMethod.POST));
}
@Configuration(proxyBeanMethods = false)
public static class GraphQlEndpointCorsConfiguration implements WebFluxConfigurer {
@ -161,9 +167,7 @@ public class GraphQlWebFluxAutoConfiguration {
public HandlerMapping graphQlWebSocketEndpoint(GraphQlWebSocketHandler graphQlWebSocketHandler,
GraphQlProperties properties) {
String path = properties.getWebsocket().getPath();
if (logger.isInfoEnabled()) {
logger.info("GraphQL endpoint WebSocket " + path);
}
logger.info(LogMessage.format("GraphQL endpoint WebSocket %s", path));
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setHandlerPredicate(new WebSocketUpgradeHandlerPredicate());
mapping.setUrlMap(Collections.singletonMap(path, graphQlWebSocketHandler));

@ -42,6 +42,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.log.LogMessage;
import org.springframework.graphql.GraphQlService;
import org.springframework.graphql.execution.GraphQlSource;
import org.springframework.graphql.execution.ThreadLocalAccessor;
@ -51,10 +52,12 @@ import org.springframework.graphql.web.webmvc.GraphQlHttpHandler;
import org.springframework.graphql.web.webmvc.GraphQlWebSocketHandler;
import org.springframework.graphql.web.webmvc.GraphiQlHandler;
import org.springframework.graphql.web.webmvc.SchemaHandler;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
@ -62,6 +65,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.function.RequestPredicates;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.RouterFunctions;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
@ -102,34 +106,33 @@ public class GraphQlWebMvcAutoConfiguration {
}
@Bean
public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler handler, GraphQlSource graphQlSource,
GraphQlProperties properties, ResourceLoader resourceLoader) {
String graphQLPath = properties.getPath();
if (logger.isInfoEnabled()) {
logger.info("GraphQL endpoint HTTP POST " + graphQLPath);
}
RouterFunctions.Builder builder = RouterFunctions.route()
.GET(graphQLPath,
(request) -> ServerResponse.status(HttpStatus.METHOD_NOT_ALLOWED)
.headers((headers) -> headers.setAllow(Collections.singleton(HttpMethod.POST))).build())
.POST(graphQLPath, RequestPredicates.contentType(MediaType.APPLICATION_JSON)
.and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), handler::handleRequest);
public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler httpHandler,
GraphQlSource graphQlSource, GraphQlProperties properties, ResourceLoader resourceLoader) {
String path = properties.getPath();
logger.info(LogMessage.format("GraphQL endpoint HTTP POST %s", path));
RouterFunctions.Builder builder = RouterFunctions.route();
builder = builder.GET(path, this::onlyAllowPost);
builder = builder.POST(path, RequestPredicates.contentType(MediaType.APPLICATION_JSON)
.and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), httpHandler::handleRequest);
if (properties.getGraphiql().isEnabled()) {
GraphiQlHandler graphiQLHandler = new GraphiQlHandler(graphQLPath, properties.getWebsocket().getPath());
GraphiQlHandler graphiQLHandler = new GraphiQlHandler(path, properties.getWebsocket().getPath());
builder = builder.GET(properties.getGraphiql().getPath(), graphiQLHandler::handleRequest);
}
if (properties.getSchema().getPrinter().isEnabled()) {
SchemaHandler schemaHandler = new SchemaHandler(graphQlSource);
builder = builder.GET(graphQLPath + "/schema", schemaHandler::handleRequest);
builder = builder.GET(path + "/schema", schemaHandler::handleRequest);
}
return builder.build();
}
private ServerResponse onlyAllowPost(ServerRequest request) {
return ServerResponse.status(HttpStatus.METHOD_NOT_ALLOWED).headers(this::onlyAllowPost).build();
}
private void onlyAllowPost(HttpHeaders headers) {
headers.setAllow(Collections.singleton(HttpMethod.POST));
}
@Configuration(proxyBeanMethods = false)
public static class GraphQlEndpointCorsConfiguration implements WebMvcConfigurer {
@ -161,25 +164,29 @@ public class GraphQlWebMvcAutoConfiguration {
@ConditionalOnMissingBean
public GraphQlWebSocketHandler graphQlWebSocketHandler(WebGraphQlHandler webGraphQlHandler,
GraphQlProperties properties, HttpMessageConverters converters) {
return new GraphQlWebSocketHandler(webGraphQlHandler, getJsonConverter(converters),
properties.getWebsocket().getConnectionInitTimeout());
}
@SuppressWarnings("unchecked")
private static GenericHttpMessageConverter<Object> getJsonConverter(HttpMessageConverters converters) {
return converters.getConverters().stream()
.filter((candidate) -> candidate.canRead(Map.class, MediaType.APPLICATION_JSON)).findFirst()
.map((converter) -> (GenericHttpMessageConverter<Object>) converter)
private GenericHttpMessageConverter<Object> getJsonConverter(HttpMessageConverters converters) {
return converters.getConverters().stream().filter(this::canReadJsonMap).findFirst()
.map(this::asGenericHttpMessageConverter)
.orElseThrow(() -> new IllegalStateException("No JSON converter"));
}
private boolean canReadJsonMap(HttpMessageConverter<?> candidate) {
return candidate.canRead(Map.class, MediaType.APPLICATION_JSON);
}
@SuppressWarnings("unchecked")
private GenericHttpMessageConverter<Object> asGenericHttpMessageConverter(HttpMessageConverter<?> converter) {
return (GenericHttpMessageConverter<Object>) converter;
}
@Bean
public HandlerMapping graphQlWebSocketMapping(GraphQlWebSocketHandler handler, GraphQlProperties properties) {
String path = properties.getWebsocket().getPath();
if (logger.isInfoEnabled()) {
logger.info("GraphQL endpoint WebSocket " + path);
}
logger.info(LogMessage.format("GraphQL endpoint WebSocket %s", path));
WebSocketHandlerMapping mapping = new WebSocketHandlerMapping();
mapping.setWebSocketUpgradeMatch(true);
mapping.setUrlMap(Collections.singletonMap(path,

Loading…
Cancel
Save