Apply Spring JavaFormat to Spring GraphQL Boot starter
This commit applies the build conventions to the Spring Boot GraphQL starter module and fixes all formatting issues. See gh-54
This commit is contained in:
@@ -4,6 +4,7 @@ plugins {
|
||||
id 'org.springframework.boot' version '2.4.5' apply false
|
||||
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
|
||||
id 'java-library'
|
||||
id "org.springframework.graphql.conventions"
|
||||
}
|
||||
|
||||
description = "GraphQL Spring Boot Starter"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.boot;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
@@ -22,6 +23,7 @@ import graphql.execution.instrumentation.Instrumentation;
|
||||
import graphql.schema.idl.RuntimeWiring;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -31,7 +33,14 @@ import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.graphql.execution.DataFetcherExceptionResolver;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
|
||||
@Configuration
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for creating a
|
||||
* {@link GraphQlSource}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(GraphQL.class)
|
||||
@ConditionalOnMissingBean(GraphQlSource.class)
|
||||
@EnableConfigurationProperties(GraphQlProperties.class)
|
||||
@@ -42,31 +51,30 @@ public class GraphQlAutoConfiguration {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(GraphQlSource.Builder.class)
|
||||
static class GraphQlSourceConfiguration {
|
||||
public static class GraphQlSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RuntimeWiring runtimeWiring(ObjectProvider<RuntimeWiringCustomizer> customizers) {
|
||||
RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring();
|
||||
customizers.orderedStream().forEach(customizer -> customizer.customize(builder));
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GraphQlSource.Builder graphQlSourceBuilder(
|
||||
GraphQlProperties properties, RuntimeWiring runtimeWiring,
|
||||
ObjectProvider<DataFetcherExceptionResolver> exceptionResolversProvider,
|
||||
ResourceLoader resourceLoader, ObjectProvider<Instrumentation> instrumentationsProvider) {
|
||||
public GraphQlSource.Builder graphQlSourceBuilder(GraphQlProperties properties, RuntimeWiring runtimeWiring,
|
||||
ObjectProvider<DataFetcherExceptionResolver> exceptionResolversProvider, ResourceLoader resourceLoader,
|
||||
ObjectProvider<Instrumentation> instrumentationsProvider) {
|
||||
|
||||
String schemaLocation = properties.getSchema().getLocation();
|
||||
return GraphQlSource.builder()
|
||||
.schemaResource(resourceLoader.getResource(schemaLocation))
|
||||
return GraphQlSource.builder().schemaResource(resourceLoader.getResource(schemaLocation))
|
||||
.runtimeWiring(runtimeWiring)
|
||||
.exceptionResolvers(exceptionResolversProvider.orderedStream().collect(Collectors.toList()))
|
||||
.instrumentation(instrumentationsProvider.orderedStream().collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,12 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.boot;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* {@link ConfigurationProperties properties} for Spring GraphQL.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.graphql")
|
||||
public class GraphQlProperties {
|
||||
|
||||
@@ -68,7 +75,6 @@ public class GraphQlProperties {
|
||||
return this.printer;
|
||||
}
|
||||
|
||||
|
||||
public static class Printer {
|
||||
|
||||
/**
|
||||
@@ -96,11 +102,11 @@ public class GraphQlProperties {
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class WebSocket {
|
||||
|
||||
/**
|
||||
@@ -109,7 +115,8 @@ public class GraphQlProperties {
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* Time within which the initial {@code CONNECTION_INIT} type message must be received.
|
||||
* Time within which the initial {@code CONNECTION_INIT} type message must be
|
||||
* received.
|
||||
*/
|
||||
private Duration connectionInitTimeout = Duration.ofSeconds(60);
|
||||
|
||||
@@ -128,5 +135,7 @@ public class GraphQlProperties {
|
||||
public void setConnectionInitTimeout(Duration connectionInitTimeout) {
|
||||
this.connectionInitTimeout = connectionInitTimeout;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.boot;
|
||||
|
||||
import graphql.GraphQL;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -26,7 +28,14 @@ import org.springframework.graphql.GraphQlService;
|
||||
import org.springframework.graphql.execution.ExecutionGraphQlService;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
|
||||
@Configuration
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for creating a
|
||||
* {@link GraphQlService}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(GraphQL.class)
|
||||
@ConditionalOnMissingBean(GraphQlService.class)
|
||||
@AutoConfigureAfter(GraphQlAutoConfiguration.class)
|
||||
|
||||
@@ -13,13 +13,25 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.boot;
|
||||
|
||||
import graphql.schema.idl.RuntimeWiring;
|
||||
|
||||
/**
|
||||
* Callback interface that can be used to customize the GraphQL
|
||||
* {@link RuntimeWiring.Builder}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RuntimeWiringCustomizer {
|
||||
|
||||
/**
|
||||
* Callback to customize a {@link RuntimeWiring.Builder} instance.
|
||||
* @param builder builder instance to customize
|
||||
*/
|
||||
void customize(RuntimeWiring.Builder builder);
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.boot;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -25,6 +26,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
@@ -43,16 +45,21 @@ import org.springframework.graphql.web.webflux.GraphQlWebSocketHandler;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
import org.springframework.web.reactive.function.server.RequestPredicates;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.reactive.socket.server.support.WebSocketUpgradeHandlerPredicate;
|
||||
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.contentType;
|
||||
|
||||
@Configuration
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for enabling Spring GraphQL over
|
||||
* WebFlux.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
@ConditionalOnClass(GraphQL.class)
|
||||
@ConditionalOnBean(GraphQlSource.class)
|
||||
@@ -61,13 +68,11 @@ public class WebFluxGraphQlAutoConfiguration {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(WebFluxGraphQlAutoConfiguration.class);
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public WebGraphQlHandler webGraphQlHandler(ObjectProvider<WebInterceptor> interceptors, GraphQlService service) {
|
||||
return WebGraphQlHandler.builder(service)
|
||||
.interceptors(interceptors.orderedStream().collect(Collectors.toList()))
|
||||
.build();
|
||||
.interceptors(interceptors.orderedStream().collect(Collectors.toList())).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -86,33 +91,33 @@ public class WebFluxGraphQlAutoConfiguration {
|
||||
logger.info("GraphQL endpoint HTTP POST " + path);
|
||||
}
|
||||
RouterFunctions.Builder builder = RouterFunctions.route()
|
||||
.GET(path, req -> ServerResponse.ok().bodyValue(resource))
|
||||
.POST(path, accept(MediaType.APPLICATION_JSON).and(contentType(MediaType.APPLICATION_JSON)), handler::handleRequest);
|
||||
.GET(path, (req) -> ServerResponse.ok().bodyValue(resource))
|
||||
.POST(path, RequestPredicates.accept(MediaType.APPLICATION_JSON)
|
||||
.and(RequestPredicates.contentType(MediaType.APPLICATION_JSON)), handler::handleRequest);
|
||||
if (properties.getSchema().getPrinter().isEnabled()) {
|
||||
SchemaPrinter printer = new SchemaPrinter();
|
||||
builder = builder.GET(path + properties.getSchema().getPrinter().getPath(),
|
||||
req -> ServerResponse.ok()
|
||||
.contentType(MediaType.TEXT_PLAIN)
|
||||
.bodyValue(printer.print(graphQlSource.schema())));
|
||||
builder = builder.GET(path + properties.getSchema().getPrinter().getPath(), (req) -> ServerResponse.ok()
|
||||
.contentType(MediaType.TEXT_PLAIN).bodyValue(printer.print(graphQlSource.schema())));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(prefix = "spring.graphql.websocket", name = "path")
|
||||
static class WebSocketConfiguration {
|
||||
public static class WebSocketConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public GraphQlWebSocketHandler graphQlWebSocketHandler(
|
||||
WebGraphQlHandler webGraphQlHandler, GraphQlProperties properties, ServerCodecConfigurer configurer) {
|
||||
public GraphQlWebSocketHandler graphQlWebSocketHandler(WebGraphQlHandler webGraphQlHandler,
|
||||
GraphQlProperties properties, ServerCodecConfigurer configurer) {
|
||||
|
||||
return new GraphQlWebSocketHandler(
|
||||
webGraphQlHandler, configurer, properties.getWebsocket().getConnectionInitTimeout());
|
||||
return new GraphQlWebSocketHandler(webGraphQlHandler, configurer,
|
||||
properties.getWebsocket().getConnectionInitTimeout());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HandlerMapping graphQlWebSocketEndpoint(
|
||||
GraphQlWebSocketHandler graphQlWebSocketHandler, GraphQlProperties properties) {
|
||||
public HandlerMapping graphQlWebSocketEndpoint(GraphQlWebSocketHandler graphQlWebSocketHandler,
|
||||
GraphQlProperties properties) {
|
||||
|
||||
String path = properties.getWebsocket().getPath();
|
||||
if (logger.isInfoEnabled()) {
|
||||
@@ -124,6 +129,7 @@ public class WebFluxGraphQlAutoConfiguration {
|
||||
mapping.setOrder(-2); // Ahead of HTTP endpoint ("routerFunctionMapping" bean)
|
||||
return mapping;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.boot;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -28,6 +29,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
@@ -48,6 +50,7 @@ import org.springframework.graphql.web.webmvc.GraphQlWebSocketHandler;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
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.ServerResponse;
|
||||
@@ -56,10 +59,14 @@ import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
|
||||
import org.springframework.web.socket.server.support.WebSocketHandlerMapping;
|
||||
import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler;
|
||||
|
||||
import static org.springframework.web.servlet.function.RequestPredicates.accept;
|
||||
import static org.springframework.web.servlet.function.RequestPredicates.contentType;
|
||||
|
||||
@Configuration
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for enabling Spring GraphQL over
|
||||
* Spring MVC.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||
@ConditionalOnClass(GraphQL.class)
|
||||
@ConditionalOnBean(GraphQlSource.class)
|
||||
@@ -68,17 +75,14 @@ public class WebMvcGraphQlAutoConfiguration {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(WebMvcGraphQlAutoConfiguration.class);
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public WebGraphQlHandler webGraphQlHandler(
|
||||
ObjectProvider<WebInterceptor> interceptorsProvider, GraphQlService service,
|
||||
ObjectProvider<ThreadLocalAccessor> accessorsProvider) {
|
||||
public WebGraphQlHandler webGraphQlHandler(ObjectProvider<WebInterceptor> interceptorsProvider,
|
||||
GraphQlService service, ObjectProvider<ThreadLocalAccessor> accessorsProvider) {
|
||||
|
||||
return WebGraphQlHandler.builder(service)
|
||||
.interceptors(interceptorsProvider.orderedStream().collect(Collectors.toList()))
|
||||
.threadLocalAccessors(accessorsProvider.orderedStream().collect(Collectors.toList()))
|
||||
.build();
|
||||
.threadLocalAccessors(accessorsProvider.orderedStream().collect(Collectors.toList())).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -96,36 +100,33 @@ public class WebMvcGraphQlAutoConfiguration {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("GraphQL endpoint HTTP POST " + path);
|
||||
}
|
||||
RouterFunctions.Builder builder = RouterFunctions.route()
|
||||
.GET(path, req -> ServerResponse.ok().body(resource))
|
||||
.POST(path, contentType(MediaType.APPLICATION_JSON).and(accept(MediaType.APPLICATION_JSON)), handler::handleRequest);
|
||||
RouterFunctions.Builder builder = RouterFunctions.route().GET(path, (req) -> ServerResponse.ok().body(resource))
|
||||
.POST(path, RequestPredicates.contentType(MediaType.APPLICATION_JSON)
|
||||
.and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), handler::handleRequest);
|
||||
if (properties.getSchema().getPrinter().isEnabled()) {
|
||||
SchemaPrinter printer = new SchemaPrinter();
|
||||
builder = builder.GET(path + properties.getSchema().getPrinter().getPath(),
|
||||
req -> ServerResponse.ok()
|
||||
.contentType(MediaType.TEXT_PLAIN)
|
||||
.body(printer.print(graphQlSource.schema())));
|
||||
builder = builder.GET(path + properties.getSchema().getPrinter().getPath(), (req) -> ServerResponse.ok()
|
||||
.contentType(MediaType.TEXT_PLAIN).body(printer.print(graphQlSource.schema())));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
@ConditionalOnClass({ServerContainer.class, WebSocketHandler.class})
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ ServerContainer.class, WebSocketHandler.class })
|
||||
@ConditionalOnProperty(prefix = "spring.graphql.websocket", name = "path")
|
||||
static class WebSocketConfiguration {
|
||||
public static class WebSocketConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public GraphQlWebSocketHandler graphQlWebSocketHandler(
|
||||
WebGraphQlHandler webGraphQlHandler, GraphQlProperties properties, HttpMessageConverters converters) {
|
||||
public GraphQlWebSocketHandler graphQlWebSocketHandler(WebGraphQlHandler webGraphQlHandler,
|
||||
GraphQlProperties properties, HttpMessageConverters converters) {
|
||||
|
||||
HttpMessageConverter<?> converter = converters.getConverters().stream()
|
||||
.filter(candidate -> candidate.canRead(Map.class, MediaType.APPLICATION_JSON))
|
||||
.findFirst()
|
||||
.filter((candidate) -> candidate.canRead(Map.class, MediaType.APPLICATION_JSON)).findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("No JSON converter"));
|
||||
|
||||
return new GraphQlWebSocketHandler(
|
||||
webGraphQlHandler, converter, properties.getWebsocket().getConnectionInitTimeout());
|
||||
return new GraphQlWebSocketHandler(webGraphQlHandler, converter,
|
||||
properties.getWebsocket().getConnectionInitTimeout());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -144,6 +145,4 @@ public class WebMvcGraphQlAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,12 @@ import graphql.schema.DataFetcher;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
import io.micrometer.core.instrument.Tags;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link GraphQlTagsProvider}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class DefaultGraphQlTagsProvider implements GraphQlTagsProvider {
|
||||
|
||||
private final List<GraphQlTagsContributor> contributors;
|
||||
@@ -34,9 +40,9 @@ public class DefaultGraphQlTagsProvider implements GraphQlTagsProvider {
|
||||
this.contributors = contributors;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Iterable<Tag> getExecutionTags(InstrumentationExecutionParameters parameters, ExecutionResult result, Throwable exception) {
|
||||
public Iterable<Tag> getExecutionTags(InstrumentationExecutionParameters parameters, ExecutionResult result,
|
||||
Throwable exception) {
|
||||
Tags tags = Tags.of(GraphQlTags.executionOutcome(result, exception));
|
||||
for (GraphQlTagsContributor contributor : this.contributors) {
|
||||
tags = tags.and(contributor.getExecutionTags(parameters, result, exception));
|
||||
@@ -54,11 +60,13 @@ public class DefaultGraphQlTagsProvider implements GraphQlTagsProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Tag> getDataFetchingTags(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters, Throwable exception) {
|
||||
public Iterable<Tag> getDataFetchingTags(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters,
|
||||
Throwable exception) {
|
||||
Tags tags = Tags.of(GraphQlTags.dataFetchingOutcome(exception), GraphQlTags.dataFetchingPath(parameters));
|
||||
for (GraphQlTagsContributor contributor : this.contributors) {
|
||||
tags = tags.and(contributor.getDataFetchingTags(dataFetcher, parameters, exception));
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,12 +33,15 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for instrumentation of Spring GraphQL
|
||||
* endpoints.
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for instrumentation of Spring
|
||||
* GraphQL endpoints.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigureAfter({MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class})
|
||||
@AutoConfigureAfter({ MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class })
|
||||
@ConditionalOnBean(MeterRegistry.class)
|
||||
@EnableConfigurationProperties(GraphQlMetricsProperties.class)
|
||||
public class GraphQlMetricsAutoConfiguration {
|
||||
@@ -54,4 +57,5 @@ public class GraphQlMetricsAutoConfiguration {
|
||||
GraphQlTagsProvider tagsProvider, GraphQlMetricsProperties properties) {
|
||||
return new GraphQlMetricsInstrumentation(meterRegistry, tagsProvider, properties.getAutotime());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,8 +31,9 @@ import io.micrometer.core.instrument.Tag;
|
||||
import io.micrometer.core.instrument.Timer;
|
||||
|
||||
import org.springframework.boot.actuate.metrics.AutoTimer;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
public class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
|
||||
class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
|
||||
|
||||
private final MeterRegistry registry;
|
||||
|
||||
@@ -40,7 +41,7 @@ public class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
|
||||
|
||||
private final AutoTimer autoTimer;
|
||||
|
||||
public GraphQlMetricsInstrumentation(MeterRegistry registry, GraphQlTagsProvider tagsProvider, AutoTimer autoTimer) {
|
||||
GraphQlMetricsInstrumentation(MeterRegistry registry, GraphQlTagsProvider tagsProvider, AutoTimer autoTimer) {
|
||||
this.registry = registry;
|
||||
this.tagsProvider = tagsProvider;
|
||||
this.autoTimer = autoTimer;
|
||||
@@ -59,12 +60,14 @@ public class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
|
||||
return new SimpleInstrumentationContext<ExecutionResult>() {
|
||||
@Override
|
||||
public void onCompleted(ExecutionResult result, Throwable exc) {
|
||||
Iterable<Tag> tags = tagsProvider.getExecutionTags(parameters, result, exc);
|
||||
Iterable<Tag> tags = GraphQlMetricsInstrumentation.this.tagsProvider.getExecutionTags(parameters,
|
||||
result, exc);
|
||||
state.tags(tags).stopTimer();
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
result.getErrors().forEach(error -> {
|
||||
registry.counter("graphql.error", tagsProvider.getErrorTags(parameters, error)).increment();
|
||||
});
|
||||
result.getErrors()
|
||||
.forEach((error) -> GraphQlMetricsInstrumentation.this.registry.counter("graphql.error",
|
||||
GraphQlMetricsInstrumentation.this.tagsProvider.getErrorTags(parameters, error))
|
||||
.increment());
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -73,7 +76,8 @@ public class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFetcher<?> instrumentDataFetcher(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters) {
|
||||
public DataFetcher<?> instrumentDataFetcher(DataFetcher<?> dataFetcher,
|
||||
InstrumentationFieldFetchParameters parameters) {
|
||||
if (this.autoTimer.isEnabled() && !parameters.isTrivialDataFetcher()) {
|
||||
return (environment) -> {
|
||||
Timer.Sample sample = Timer.start(this.registry);
|
||||
@@ -81,9 +85,8 @@ public class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
|
||||
Object value = dataFetcher.get(environment);
|
||||
if (value instanceof CompletionStage<?>) {
|
||||
CompletionStage<?> completion = (CompletionStage<?>) value;
|
||||
return completion.whenComplete((result, error) -> {
|
||||
recordDataFetcherMetric(sample, dataFetcher, parameters, error);
|
||||
});
|
||||
return completion.whenComplete(
|
||||
(result, error) -> recordDataFetcherMetric(sample, dataFetcher, parameters, error));
|
||||
}
|
||||
else {
|
||||
recordDataFetcherMetric(sample, dataFetcher, parameters, null);
|
||||
@@ -100,13 +103,13 @@ public class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
|
||||
return super.instrumentDataFetcher(dataFetcher, parameters);
|
||||
}
|
||||
|
||||
private void recordDataFetcherMetric(Timer.Sample sample, DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters, Throwable throwable) {
|
||||
private void recordDataFetcherMetric(Timer.Sample sample, DataFetcher<?> dataFetcher,
|
||||
InstrumentationFieldFetchParameters parameters, @Nullable Throwable throwable) {
|
||||
Timer.Builder timer = this.autoTimer.builder("graphql.datafetcher");
|
||||
timer.tags(this.tagsProvider.getDataFetchingTags(dataFetcher, parameters, throwable));
|
||||
sample.stop(timer.register(this.registry));
|
||||
}
|
||||
|
||||
|
||||
static class RequestMetricsInstrumentationState implements InstrumentationState {
|
||||
|
||||
private final MeterRegistry registry;
|
||||
@@ -120,18 +123,19 @@ public class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
public RequestMetricsInstrumentationState tags(Iterable<Tag> tags) {
|
||||
RequestMetricsInstrumentationState tags(Iterable<Tag> tags) {
|
||||
this.timer.tags(tags);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void startTimer() {
|
||||
void startTimer() {
|
||||
this.sample = Timer.start(this.registry);
|
||||
}
|
||||
|
||||
public void stopTimer() {
|
||||
void stopTimer() {
|
||||
this.sample.stop(this.timer.register(this.registry));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,13 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
|
||||
/**
|
||||
* This class could be merged with {@link org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties}
|
||||
* {@link ConfigurationProperties properties} for Spring GraphQL.
|
||||
* <p>
|
||||
* This class could be later merged with
|
||||
* {@link org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ConfigurationProperties("management.metrics.graphql")
|
||||
public class GraphQlMetricsProperties {
|
||||
@@ -35,4 +41,5 @@ public class GraphQlMetricsProperties {
|
||||
public AutoTimeProperties getAutotime() {
|
||||
return this.autotime;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,12 +27,14 @@ import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchPar
|
||||
import graphql.schema.GraphQLObjectType;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Factory methods for Tags associated with a GraphQL requests.
|
||||
*
|
||||
* Factory methods for Tags associated with a GraphQL request.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public final class GraphQlTags {
|
||||
|
||||
@@ -42,7 +44,11 @@ public final class GraphQlTags {
|
||||
|
||||
private static final Tag UNKNOWN_ERRORTYPE = Tag.of("errorType", "UNKNOWN");
|
||||
|
||||
public static Tag executionOutcome(ExecutionResult result, Throwable exception) {
|
||||
private GraphQlTags() {
|
||||
|
||||
}
|
||||
|
||||
public static Tag executionOutcome(ExecutionResult result, @Nullable Throwable exception) {
|
||||
if (exception == null && result.getErrors().isEmpty()) {
|
||||
return OUTCOME_SUCCESS;
|
||||
}
|
||||
@@ -78,19 +84,19 @@ public final class GraphQlTags {
|
||||
return Tag.of("errorPath", builder.toString());
|
||||
}
|
||||
|
||||
public static Tag dataFetchingOutcome(Throwable exception) {
|
||||
return (exception == null) ? OUTCOME_SUCCESS : OUTCOME_ERROR;
|
||||
public static Tag dataFetchingOutcome(@Nullable Throwable exception) {
|
||||
return (exception != null) ? OUTCOME_ERROR : OUTCOME_SUCCESS;
|
||||
}
|
||||
|
||||
public static Tag dataFetchingPath(InstrumentationFieldFetchParameters parameters) {
|
||||
ExecutionStepInfo executionStepInfo = parameters.getExecutionStepInfo();
|
||||
StringBuilder dataFetchingType = new StringBuilder();
|
||||
if (executionStepInfo.hasParent() &&
|
||||
executionStepInfo.getParent().getType() instanceof GraphQLObjectType) {
|
||||
if (executionStepInfo.hasParent() && executionStepInfo.getParent().getType() instanceof GraphQLObjectType) {
|
||||
dataFetchingType.append(((GraphQLObjectType) executionStepInfo.getParent().getType()).getName());
|
||||
dataFetchingType.append('.');
|
||||
}
|
||||
dataFetchingType.append(executionStepInfo.getPath().getSegmentName());
|
||||
return Tag.of("path", dataFetchingType.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,11 +23,23 @@ import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchPar
|
||||
import graphql.schema.DataFetcher;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A contributor of {@link Tag Tags} for Spring GraphQL-based request handling. Typically
|
||||
* used by a {@link GraphQlTagsProvider} to provide tags in addition to its defaults.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface GraphQlTagsContributor {
|
||||
|
||||
Iterable<Tag> getExecutionTags(InstrumentationExecutionParameters parameters, ExecutionResult result, Throwable exception);
|
||||
Iterable<Tag> getExecutionTags(InstrumentationExecutionParameters parameters, ExecutionResult result,
|
||||
@Nullable Throwable exception);
|
||||
|
||||
Iterable<Tag> getErrorTags(InstrumentationExecutionParameters parameters, GraphQLError error);
|
||||
|
||||
Iterable<Tag> getDataFetchingTags(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters, Throwable exception);
|
||||
Iterable<Tag> getDataFetchingTags(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters,
|
||||
@Nullable Throwable exception);
|
||||
|
||||
}
|
||||
|
||||
@@ -23,12 +23,22 @@ import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchPar
|
||||
import graphql.schema.DataFetcher;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Provides {@link Tag Tags} for Spring GraphQL-based request handling.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface GraphQlTagsProvider {
|
||||
|
||||
Iterable<Tag> getExecutionTags(InstrumentationExecutionParameters parameters, ExecutionResult result, Throwable exception);
|
||||
Iterable<Tag> getExecutionTags(InstrumentationExecutionParameters parameters, ExecutionResult result,
|
||||
@Nullable Throwable exception);
|
||||
|
||||
Iterable<Tag> getErrorTags(InstrumentationExecutionParameters parameters, GraphQLError error);
|
||||
|
||||
Iterable<Tag> getDataFetchingTags(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters, Throwable exception);
|
||||
|
||||
Iterable<Tag> getDataFetchingTags(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters,
|
||||
@Nullable Throwable exception);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2020-2021 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides instrumentation support.
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
/*
|
||||
* Copyright 2020-2021 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration classes to configure
|
||||
* {@link org.springframework.graphql.execution.GraphQlSource},
|
||||
* {@link org.springframework.graphql.GraphQlService}, and HTTP and WebSocket
|
||||
* endpoints.
|
||||
* {@link org.springframework.graphql.GraphQlService}, and HTTP and WebSocket endpoints.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
|
||||
@@ -31,8 +31,9 @@ import org.springframework.graphql.test.tester.GraphQlTester;
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @see GraphQlTesterAutoConfiguration
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
|
||||
@@ -30,18 +30,19 @@ import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* Auto-configuration for {@link GraphQlTester} in mock environments.
|
||||
*
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({WebClient.class, WebTestClient.class, GraphQlTester.class})
|
||||
@ConditionalOnClass({ WebClient.class, WebTestClient.class, GraphQlTester.class })
|
||||
@AutoConfigureAfter(value = WebTestClientMockMvcAutoConfiguration.class,
|
||||
name = "org.springframework.boot.test.autoconfigure.web.reactive.WebTestClientAutoConfiguration")
|
||||
public class GraphQlTesterAutoConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(WebTestClient.class)
|
||||
static class WebTestClientGraphQlTesterConfiguration {
|
||||
public static class WebTestClientGraphQlTesterConfiguration {
|
||||
|
||||
@Bean
|
||||
public GraphQlTester clientGraphQlTester(WebTestClient webTestClient, GraphQlProperties properties) {
|
||||
@@ -54,7 +55,7 @@ public class GraphQlTesterAutoConfiguration {
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(WebTestClient.class)
|
||||
@ConditionalOnBean(WebGraphQlHandler.class)
|
||||
static class WebGraphQlHandlerGraphQlTesterConfiguration {
|
||||
public static class WebGraphQlHandlerGraphQlTesterConfiguration {
|
||||
|
||||
@Bean
|
||||
public GraphQlTester handlerGraphQlTester(WebGraphQlHandler handler) {
|
||||
|
||||
@@ -42,7 +42,7 @@ import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizer} for {@link GraphQlTester}
|
||||
* {@link ContextCustomizer} for {@link GraphQlTester}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
@@ -65,12 +65,13 @@ class GraphQlTesterContextCustomizer implements ContextCustomizer {
|
||||
}
|
||||
|
||||
private void registerGraphQlTester(BeanDefinitionRegistry registry) {
|
||||
RootBeanDefinition definition = new RootBeanDefinition(GraphQlTesterContextCustomizer.GraphQlTesterRegistrar.class);
|
||||
RootBeanDefinition definition = new RootBeanDefinition(
|
||||
GraphQlTesterContextCustomizer.GraphQlTesterRegistrar.class);
|
||||
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registry.registerBeanDefinition(GraphQlTesterContextCustomizer.GraphQlTesterRegistrar.class.getName(), definition);
|
||||
registry.registerBeanDefinition(GraphQlTesterContextCustomizer.GraphQlTesterRegistrar.class.getName(),
|
||||
definition);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return (obj != null) && (obj.getClass() == getClass());
|
||||
@@ -81,8 +82,8 @@ class GraphQlTesterContextCustomizer implements ContextCustomizer {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
|
||||
private static class GraphQlTesterRegistrar implements BeanDefinitionRegistryPostProcessor, Ordered, BeanFactoryAware {
|
||||
private static class GraphQlTesterRegistrar
|
||||
implements BeanDefinitionRegistryPostProcessor, Ordered, BeanFactoryAware {
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@@ -107,8 +108,9 @@ class GraphQlTesterContextCustomizer implements ContextCustomizer {
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE -1;
|
||||
return Ordered.LOWEST_PRECEDENCE - 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class GraphQlTesterFactory implements FactoryBean<GraphQlTester>, ApplicationContextAware {
|
||||
@@ -170,4 +172,5 @@ class GraphQlTesterContextCustomizer implements ContextCustomizer {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,7 +40,8 @@ class GraphQlTesterContextCustomizerFactory implements ContextCustomizerFactory
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
SpringBootTest springBootTest = TestContextAnnotationUtils.findMergedAnnotation(testClass, SpringBootTest.class);
|
||||
SpringBootTest springBootTest = TestContextAnnotationUtils.findMergedAnnotation(testClass,
|
||||
SpringBootTest.class);
|
||||
return (springBootTest != null && isGraphQlTesterPresent()) ? new GraphQlTesterContextCustomizer() : null;
|
||||
}
|
||||
|
||||
@@ -48,4 +49,5 @@ class GraphQlTesterContextCustomizerFactory implements ContextCustomizerFactory
|
||||
return ClassUtils.isPresent(WEBTESTCLIENT_CLASS, getClass().getClassLoader())
|
||||
&& ClassUtils.isPresent(GRAPHQLTESTER_CLASS, getClass().getClassLoader());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,13 +32,16 @@ import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* Auto-configuration for {@link WebTestClient} support with {@link MockMvc}.
|
||||
* <p>Temporary workaround for upcoming enhancement request in Spring Boot 2.6.0.
|
||||
* <p>
|
||||
* Temporary workaround for upcoming enhancement request in Spring Boot 2.6.0.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @see <a href="https://github.com/spring-projects/spring-boot/issues/23067">Spring Boot 2.6.x issue</a>
|
||||
* @since 1.0.0
|
||||
* @see <a href="https://github.com/spring-projects/spring-boot/issues/23067">Spring Boot
|
||||
* 2.6.x issue</a>
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ WebClient.class, WebTestClient.class, MockMvcWebTestClient.class})
|
||||
@ConditionalOnClass({ WebClient.class, WebTestClient.class, MockMvcWebTestClient.class })
|
||||
@AutoConfigureAfter(name = "org.springframework.boot.test.autoconfigure.web.reactive.WebTestClientAutoConfiguration")
|
||||
public class WebTestClientMockMvcAutoConfiguration {
|
||||
|
||||
@@ -52,5 +55,5 @@ public class WebTestClientMockMvcAutoConfiguration {
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2020-2021 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.graphql.boot;
|
||||
|
||||
public class Book {
|
||||
@@ -21,7 +37,7 @@ public class Book {
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
@@ -29,7 +45,7 @@ public class Book {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
@@ -37,7 +53,7 @@ public class Book {
|
||||
}
|
||||
|
||||
public int getPageCount() {
|
||||
return pageCount;
|
||||
return this.pageCount;
|
||||
}
|
||||
|
||||
public void setPageCount(int pageCount) {
|
||||
@@ -45,10 +61,11 @@ public class Book {
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
return this.author;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,10 +35,9 @@ class GraphQlAutoConfigurationTests {
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(GraphQlAutoConfiguration.class));
|
||||
|
||||
|
||||
@Test
|
||||
void shouldFailWhenSchemaFileIsMissing() {
|
||||
contextRunner.run((context) -> {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).hasFailed();
|
||||
assertThat(context).getFailure().getRootCause().hasMessage("'schemaResource' does not exist");
|
||||
});
|
||||
@@ -46,29 +45,27 @@ class GraphQlAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void shouldCreateBuilderWithSdl() {
|
||||
contextRunner
|
||||
.withPropertyValues("spring.graphql.schema.location:classpath:books/schema.graphqls")
|
||||
this.contextRunner.withPropertyValues("spring.graphql.schema.location:classpath:books/schema.graphqls")
|
||||
.run((context) -> assertThat(context).hasSingleBean(GraphQlSource.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseProgrammaticallyDefinedBuilder() {
|
||||
contextRunner
|
||||
.withPropertyValues("spring.graphql.schema.location:classpath:books/schema.graphqls")
|
||||
.withUserConfiguration(CustomGraphQlBuilderConfiguration.class)
|
||||
.run((context) -> {
|
||||
this.contextRunner.withPropertyValues("spring.graphql.schema.location:classpath:books/schema.graphqls")
|
||||
.withUserConfiguration(CustomGraphQlBuilderConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasBean("customGraphQlSourceBuilder");
|
||||
assertThat(context).hasSingleBean(GraphQlSource.Builder.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomGraphQlBuilderConfiguration {
|
||||
|
||||
@Bean
|
||||
public GraphQlSource.Builder customGraphQlSourceBuilder() {
|
||||
GraphQlSource.Builder customGraphQlSourceBuilder() {
|
||||
return GraphQlSource.builder().schemaResource(new ClassPathResource("books/schema.graphqls"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2020-2021 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.graphql.boot;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -6,25 +22,24 @@ import java.util.List;
|
||||
import graphql.schema.DataFetcher;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
public class GraphQlDataFetchers {
|
||||
public final class GraphQlDataFetchers {
|
||||
|
||||
private static List<Book> books = Arrays.asList(
|
||||
new Book("book-1", "GraphQL for beginners", 100, "John GraphQL"),
|
||||
private static List<Book> books = Arrays.asList(new Book("book-1", "GraphQL for beginners", 100, "John GraphQL"),
|
||||
new Book("book-2", "Harry Potter and the Philosopher's Stone", 223, "Joanne Rowling"),
|
||||
new Book("book-3", "Moby Dick", 635, "Moby Dick"),
|
||||
new Book("book-3", "Moby Dick", 635, "Moby Dick"));
|
||||
new Book("book-3", "Moby Dick", 635, "Moby Dick"), new Book("book-3", "Moby Dick", 635, "Moby Dick"));
|
||||
|
||||
private GraphQlDataFetchers() {
|
||||
|
||||
}
|
||||
|
||||
public static DataFetcher getBookByIdDataFetcher() {
|
||||
return environment -> books.stream()
|
||||
.filter(book -> book.getId().equals(environment.getArgument("id")))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
return (environment) -> books.stream().filter((book) -> book.getId().equals(environment.getArgument("id")))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public static DataFetcher getBooksOnSale() {
|
||||
return environment -> Flux.fromIterable(books)
|
||||
.filter(book -> book.getPageCount() >= (int) environment.getArgument("minPages"));
|
||||
return (environment) -> Flux.fromIterable(books)
|
||||
.filter((book) -> book.getPageCount() >= (int) environment.getArgument("minPages"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.boot;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import graphql.schema.idl.TypeRuntimeWiring;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -35,124 +37,94 @@ import org.springframework.graphql.web.WebInterceptor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring;
|
||||
|
||||
class WebFluxApplicationContextTests {
|
||||
|
||||
private static final AutoConfigurations AUTO_CONFIGURATIONS = AutoConfigurations.of(
|
||||
HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class,
|
||||
CodecsAutoConfiguration.class, JacksonAutoConfiguration.class,
|
||||
GraphQlAutoConfiguration.class, GraphQlServiceAutoConfiguration.class,
|
||||
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";
|
||||
|
||||
|
||||
@Test
|
||||
void query() {
|
||||
testWithWebClient(client -> {
|
||||
String query = "{" +
|
||||
" bookById(id: \\\"book-1\\\"){ " +
|
||||
" id" +
|
||||
" name" +
|
||||
" pageCount" +
|
||||
" author" +
|
||||
" }" +
|
||||
"}";
|
||||
testWithWebClient((client) -> {
|
||||
String query = "{" + " bookById(id: \\\"book-1\\\"){ " + " id" + " name" + " pageCount"
|
||||
+ " author" + " }" + "}";
|
||||
|
||||
client.post().uri("")
|
||||
.bodyValue("{ \"query\": \"" + query + "\"}")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
client.post().uri("").bodyValue("{ \"query\": \"" + query + "\"}").exchange().expectStatus().isOk()
|
||||
.expectBody().jsonPath("data.bookById.name").isEqualTo("GraphQL for beginners");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryMissing() {
|
||||
testWithWebClient(client -> client.post().uri("").bodyValue("{}").exchange().expectStatus().isBadRequest());
|
||||
testWithWebClient((client) -> client.post().uri("").bodyValue("{}").exchange().expectStatus().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryIsInvalidJson() {
|
||||
testWithWebClient(client -> client.post().uri("").bodyValue(":)").exchange().expectStatus().isBadRequest());
|
||||
testWithWebClient((client) -> client.post().uri("").bodyValue(":)").exchange().expectStatus().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
void interceptedQuery() {
|
||||
testWithWebClient(client -> {
|
||||
String query = "{" +
|
||||
" bookById(id: \\\"book-1\\\"){ " +
|
||||
" id" +
|
||||
" name" +
|
||||
" pageCount" +
|
||||
" author" +
|
||||
" }" +
|
||||
"}";
|
||||
testWithWebClient((client) -> {
|
||||
String query = "{" + " bookById(id: \\\"book-1\\\"){ " + " id" + " name" + " pageCount"
|
||||
+ " author" + " }" + "}";
|
||||
|
||||
client.post().uri("")
|
||||
.bodyValue("{ \"query\": \"" + query + "\"}")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
client.post().uri("").bodyValue("{ \"query\": \"" + query + "\"}").exchange().expectStatus().isOk()
|
||||
.expectHeader().valueEquals("X-Custom-Header", "42");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void schemaEndpoint() {
|
||||
testWithWebClient(client -> {
|
||||
client.get().uri("/schema").accept(MediaType.ALL).exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectHeader().contentType(MediaType.TEXT_PLAIN)
|
||||
.expectBody(String.class).value(Matchers.containsString("type Book"));
|
||||
});
|
||||
testWithWebClient((client) -> client.get().uri("/schema").accept(MediaType.ALL).exchange().expectStatus().isOk()
|
||||
.expectHeader().contentType(MediaType.TEXT_PLAIN).expectBody(String.class)
|
||||
.value(Matchers.containsString("type Book")));
|
||||
}
|
||||
|
||||
private void testWithWebClient(Consumer<WebTestClient> consumer) {
|
||||
testWithApplicationContext(context -> {
|
||||
WebTestClient client = WebTestClient.bindToApplicationContext(context)
|
||||
.configureClient()
|
||||
.defaultHeaders(headers -> {
|
||||
testWithApplicationContext((context) -> {
|
||||
WebTestClient client = WebTestClient.bindToApplicationContext(context).configureClient()
|
||||
.defaultHeaders((headers) -> {
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||
})
|
||||
.baseUrl(BASE_URL)
|
||||
.build();
|
||||
}).baseUrl(BASE_URL).build();
|
||||
consumer.accept(client);
|
||||
});
|
||||
}
|
||||
|
||||
private void testWithApplicationContext(ContextConsumer<ApplicationContext> consumer) {
|
||||
new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AUTO_CONFIGURATIONS)
|
||||
new ReactiveWebApplicationContextRunner().withConfiguration(AUTO_CONFIGURATIONS)
|
||||
.withUserConfiguration(DataFetchersConfiguration.class, CustomWebInterceptor.class)
|
||||
.withPropertyValues(
|
||||
"spring.main.web-application-type=reactive",
|
||||
.withPropertyValues("spring.main.web-application-type=reactive",
|
||||
"spring.graphql.schema.printer.enabled=true",
|
||||
"spring.graphql.schema.location=classpath:books/schema.graphqls")
|
||||
.run(consumer);
|
||||
}
|
||||
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class DataFetchersConfiguration {
|
||||
public static class DataFetchersConfiguration {
|
||||
|
||||
@Bean
|
||||
public RuntimeWiringCustomizer bookDataFetcher() {
|
||||
return (runtimeWiring) ->
|
||||
runtimeWiring.type(newTypeWiring("Query")
|
||||
.dataFetcher("bookById", GraphQlDataFetchers.getBookByIdDataFetcher()));
|
||||
return (runtimeWiring) -> runtimeWiring.type(TypeRuntimeWiring.newTypeWiring("Query")
|
||||
.dataFetcher("bookById", GraphQlDataFetchers.getBookByIdDataFetcher()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomWebInterceptor {
|
||||
public static class CustomWebInterceptor {
|
||||
|
||||
@Bean
|
||||
public WebInterceptor customWebInterceptor() {
|
||||
return (input, next) -> next.handle(input).map(output ->
|
||||
output.transform(builder -> builder.responseHeader("X-Custom-Header", "42")));
|
||||
return (input, next) -> next.handle(input)
|
||||
.map((output) -> output.transform((builder) -> builder.responseHeader("X-Custom-Header", "42")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.springframework.graphql.boot;
|
||||
|
||||
|
||||
import graphql.schema.idl.TypeRuntimeWiring;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
@@ -54,18 +53,12 @@ class WebMvcApplicationContextTests {
|
||||
|
||||
@Test
|
||||
void endpointHandlesGraphQlQuery() {
|
||||
testWith(mockMvc -> {
|
||||
String query = "{" +
|
||||
" bookById(id: \\\"book-1\\\"){ " +
|
||||
" id" +
|
||||
" name" +
|
||||
" pageCount" +
|
||||
" author" +
|
||||
" }" +
|
||||
"}";
|
||||
MvcResult asyncResult = mockMvc.perform(post("/graphql").content("{\"query\": \"" + query + "\"}")).andReturn();
|
||||
mockMvc.perform(asyncDispatch(asyncResult))
|
||||
.andExpect(status().isOk())
|
||||
testWith((mockMvc) -> {
|
||||
String query = "{" + " bookById(id: \\\"book-1\\\"){ " + " id" + " name" + " pageCount"
|
||||
+ " author" + " }" + "}";
|
||||
MvcResult asyncResult = mockMvc.perform(post("/graphql").content("{\"query\": \"" + query + "\"}"))
|
||||
.andReturn();
|
||||
mockMvc.perform(asyncDispatch(asyncResult)).andExpect(status().isOk())
|
||||
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
|
||||
.andExpect(jsonPath("data.bookById.name").value("GraphQL for beginners"));
|
||||
});
|
||||
@@ -73,83 +66,73 @@ class WebMvcApplicationContextTests {
|
||||
|
||||
@Test
|
||||
void missingQuery() {
|
||||
testWith(mockMvc -> mockMvc.perform(post("/graphql").content("{}")).andExpect(status().isBadRequest()));
|
||||
testWith((mockMvc) -> mockMvc.perform(post("/graphql").content("{}")).andExpect(status().isBadRequest()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidJson() {
|
||||
testWith(mockMvc -> mockMvc.perform(post("/graphql").content(":)")).andExpect(status().isBadRequest()));
|
||||
testWith((mockMvc) -> mockMvc.perform(post("/graphql").content(":)")).andExpect(status().isBadRequest()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void interceptedQuery() {
|
||||
testWith(mockMvc -> {
|
||||
String query = "{" +
|
||||
" bookById(id: \\\"book-1\\\"){ " +
|
||||
" id" +
|
||||
" name" +
|
||||
" pageCount" +
|
||||
" author" +
|
||||
" }" +
|
||||
"}";
|
||||
MvcResult asyncResult = mockMvc.perform(post("/graphql").content("{\"query\": \"" + query + "\"}")).andReturn();
|
||||
mockMvc.perform(asyncDispatch(asyncResult))
|
||||
.andExpect(status().isOk())
|
||||
testWith((mockMvc) -> {
|
||||
String query = "{" + " bookById(id: \\\"book-1\\\"){ " + " id" + " name" + " pageCount"
|
||||
+ " author" + " }" + "}";
|
||||
MvcResult asyncResult = mockMvc.perform(post("/graphql").content("{\"query\": \"" + query + "\"}"))
|
||||
.andReturn();
|
||||
mockMvc.perform(asyncDispatch(asyncResult)).andExpect(status().isOk())
|
||||
.andExpect(header().string("X-Custom-Header", "42"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void schemaEndpoint() {
|
||||
testWith(mockMvc -> {
|
||||
mockMvc.perform(get("/graphql/schema")).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.TEXT_PLAIN))
|
||||
.andExpect(content().string(Matchers.containsString("type Book")));
|
||||
});
|
||||
testWith((mockMvc) -> mockMvc.perform(get("/graphql/schema")).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.TEXT_PLAIN))
|
||||
.andExpect(content().string(Matchers.containsString("type Book"))));
|
||||
}
|
||||
|
||||
private void testWith(MockMvcConsumer mockMvcConsumer) {
|
||||
new WebApplicationContextRunner()
|
||||
.withConfiguration(AUTO_CONFIGURATIONS)
|
||||
new WebApplicationContextRunner().withConfiguration(AUTO_CONFIGURATIONS)
|
||||
.withUserConfiguration(DataFetchersConfiguration.class, CustomWebInterceptor.class)
|
||||
.withPropertyValues(
|
||||
"spring.main.web-application-type=servlet",
|
||||
.withPropertyValues("spring.main.web-application-type=servlet",
|
||||
"spring.graphql.schema.printer.enabled=true",
|
||||
"spring.graphql.schema.location=classpath:books/schema.graphqls")
|
||||
.run((context) -> {
|
||||
MockHttpServletRequestBuilder builder = post("/graphql")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
MockHttpServletRequestBuilder builder = post("/graphql").contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON);
|
||||
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).defaultRequest(builder).build();
|
||||
mockMvcConsumer.accept(mockMvc);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static interface MockMvcConsumer {
|
||||
private interface MockMvcConsumer {
|
||||
|
||||
void accept(MockMvc mockMvc) throws Exception;
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class DataFetchersConfiguration {
|
||||
public static class DataFetchersConfiguration {
|
||||
|
||||
@Bean
|
||||
public RuntimeWiringCustomizer bookDataFetcher() {
|
||||
return (builder) -> builder.type(newTypeWiring("Query")
|
||||
.dataFetcher("bookById", GraphQlDataFetchers.getBookByIdDataFetcher()));
|
||||
return (builder) -> builder.type(TypeRuntimeWiring.newTypeWiring("Query").dataFetcher("bookById",
|
||||
GraphQlDataFetchers.getBookByIdDataFetcher()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomWebInterceptor {
|
||||
public static class CustomWebInterceptor {
|
||||
|
||||
@Bean
|
||||
public WebInterceptor customWebInterceptor() {
|
||||
return (input, next) -> next.handle(input).map(output ->
|
||||
output.transform(builder -> builder.responseHeader("X-Custom-Header", "42")));
|
||||
return (input, next) -> next.handle(input)
|
||||
.map((output) -> output.transform((builder) -> builder.responseHeader("X-Custom-Header", "42")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link GraphQlTags}
|
||||
*
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class GraphQlTagsTests {
|
||||
@@ -44,7 +44,8 @@ class GraphQlTagsTests {
|
||||
|
||||
@Test
|
||||
void executionOutcomeShouldErrorWhenExceptionThrown() {
|
||||
Tag outcomeTag = GraphQlTags.executionOutcome(new TestExecutionResult(), new IllegalArgumentException("test error"));
|
||||
Tag outcomeTag = GraphQlTags.executionOutcome(new TestExecutionResult(),
|
||||
new IllegalArgumentException("test error"));
|
||||
assertThat(outcomeTag.getValue()).isEqualTo("ERROR");
|
||||
}
|
||||
|
||||
@@ -58,21 +59,24 @@ class GraphQlTagsTests {
|
||||
|
||||
@Test
|
||||
void errorTypeShouldBeDefinedIfPresent() {
|
||||
GraphQLError error = GraphqlErrorBuilder.newError().errorType(ErrorType.DataFetchingException).message("test error").build();
|
||||
GraphQLError error = GraphqlErrorBuilder.newError().errorType(ErrorType.DataFetchingException)
|
||||
.message("test error").build();
|
||||
Tag errorTypeTag = GraphQlTags.errorType(error);
|
||||
assertThat(errorTypeTag.getValue()).isEqualTo("DataFetchingException");
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorPathShouldUseJsonPathFormat() {
|
||||
GraphQLError error = GraphqlErrorBuilder.newError().path(Arrays.asList("project", "name")).message("test error").build();
|
||||
GraphQLError error = GraphqlErrorBuilder.newError().path(Arrays.asList("project", "name")).message("test error")
|
||||
.build();
|
||||
Tag errorPathTag = GraphQlTags.errorPath(error);
|
||||
assertThat(errorPathTag.getValue()).isEqualTo("$.project.name");
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorPathShouldUseJsonPathFormatForIndices() {
|
||||
GraphQLError error = GraphqlErrorBuilder.newError().path(Arrays.asList("issues", "42", "title")).message("test error").build();
|
||||
GraphQLError error = GraphqlErrorBuilder.newError().path(Arrays.asList("issues", "42", "title"))
|
||||
.message("test error").build();
|
||||
Tag errorPathTag = GraphQlTags.errorPath(error);
|
||||
assertThat(errorPathTag.getValue()).isEqualTo("$.issues[*].title");
|
||||
}
|
||||
@@ -89,4 +93,4 @@ class GraphQlTagsTests {
|
||||
assertThat(fetchingOutcomeTag.getValue()).isEqualTo("ERROR");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,6 @@ class GraphQlTesterAutoConfigurationTests {
|
||||
return mock(WebGraphQlHandler.class);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@@ -84,7 +83,7 @@ class GraphQlTesterAutoConfigurationTests {
|
||||
@Bean
|
||||
WebTestClient webTestClient() {
|
||||
RouterFunction<ServerResponse> routerFunction = RouterFunctions.route()
|
||||
.POST("/graphql", request -> ServerResponse.ok().build()).build();
|
||||
.POST("/graphql", (request) -> ServerResponse.ok().build()).build();
|
||||
return WebTestClient.bindToRouterFunction(routerFunction).build();
|
||||
}
|
||||
|
||||
@@ -94,4 +93,5 @@ class GraphQlTesterAutoConfigurationTests {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,11 +43,13 @@ import org.springframework.test.annotation.DirtiesContext;
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=reactive")
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = "spring.main.web-application-type=reactive")
|
||||
@DirtiesContext
|
||||
class GraphQlTesterContextCustomizerIntegrationTests {
|
||||
|
||||
@Autowired GraphQlTester graphQlTester;
|
||||
@Autowired
|
||||
GraphQlTester graphQlTester;
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
@@ -69,6 +71,7 @@ class GraphQlTesterContextCustomizerIntegrationTests {
|
||||
Map<String, HttpHandler> handlersMap = Collections.singletonMap(properties.getPath(), httpHandler);
|
||||
return new ContextPathCompositeHandler(handlersMap);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestHandler implements HttpHandler {
|
||||
@@ -83,4 +86,5 @@ class GraphQlTesterContextCustomizerIntegrationTests {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user