From ff9a421786901ddd37732bf8b01111d61df240eb Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 21 Dec 2021 08:33:05 +0100 Subject: [PATCH] Auto-configure schema printer endpoint This commit configuresa new endpoint for printing in text format the resolved GraphQL schema. This endpoint is exposed by default under "/graphql/schema" and must be enabled with "spring.graphql.schema.printer=true". See gh-29140 --- .../graphql/GraphQlProperties.java | 24 +++++++++++++++++++ .../GraphQlWebFluxAutoConfiguration.java | 6 +++++ .../GraphQlWebMvcAutoConfiguration.java | 6 +++++ .../GraphQlWebFluxAutoConfigurationTests.java | 13 ++++++++-- .../GraphQlWebMvcAutoConfigurationTests.java | 12 ++++++++-- 5 files changed, 57 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlProperties.java index 2fc1a764296..16cb18749d2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlProperties.java @@ -60,6 +60,8 @@ public class GraphQlProperties { */ private String[] fileExtensions = new String[] { ".graphqls", ".gqls" }; + private final Printer printer = new Printer(); + public String[] getLocations() { return this.locations; } @@ -81,6 +83,28 @@ public class GraphQlProperties { .toArray(String[]::new); } + public Printer getPrinter() { + return this.printer; + } + + public static class Printer { + + /** + * Whether the endpoint that prints the schema is enabled. Schema is available + * under spring.graphql.path + "/schema". + */ + private boolean enabled = false; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + } + } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java index 53a4ccd96ea..0c068bbe08a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java @@ -40,6 +40,7 @@ import org.springframework.graphql.execution.GraphQlSource; import org.springframework.graphql.web.WebGraphQlHandler; import org.springframework.graphql.web.WebInterceptor; import org.springframework.graphql.web.webflux.GraphQlHttpHandler; +import org.springframework.graphql.web.webflux.SchemaHandler; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -96,6 +97,11 @@ public class GraphQlWebFluxAutoConfiguration { .POST(graphQLPath, accept(MediaType.APPLICATION_JSON).and(contentType(MediaType.APPLICATION_JSON)), handler::handleRequest); + if (properties.getSchema().getPrinter().isEnabled()) { + SchemaHandler schemaHandler = new SchemaHandler(graphQlSource); + builder = builder.GET(graphQLPath + "/schema", schemaHandler::handleRequest); + } + return builder.build(); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java index 281eacac446..6820c83b164 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java @@ -41,6 +41,7 @@ import org.springframework.graphql.execution.ThreadLocalAccessor; import org.springframework.graphql.web.WebGraphQlHandler; import org.springframework.graphql.web.WebInterceptor; import org.springframework.graphql.web.webmvc.GraphQlHttpHandler; +import org.springframework.graphql.web.webmvc.SchemaHandler; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -97,6 +98,11 @@ public class GraphQlWebMvcAutoConfiguration { .POST(graphQLPath, RequestPredicates.contentType(MediaType.APPLICATION_JSON) .and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), handler::handleRequest); + if (properties.getSchema().getPrinter().isEnabled()) { + SchemaHandler schemaHandler = new SchemaHandler(graphQlSource); + builder = builder.GET(graphQLPath + "/schema", schemaHandler::handleRequest); + } + return builder.build(); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfigurationTests.java index 2f4ed179ab7..c39d997fc5d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfigurationTests.java @@ -38,6 +38,8 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.test.web.reactive.server.WebTestClient; +import static org.hamcrest.Matchers.containsString; + /** * Tests for {@link GraphQlWebFluxAutoConfiguration} * @@ -51,8 +53,8 @@ class GraphQlWebFluxAutoConfigurationTests { .withConfiguration(AutoConfigurations.of(HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class, CodecsAutoConfiguration.class, JacksonAutoConfiguration.class, GraphQlAutoConfiguration.class, GraphQlWebFluxAutoConfiguration.class)) - .withUserConfiguration(DataFetchersConfiguration.class, CustomWebInterceptor.class) - .withPropertyValues("spring.main.web-application-type=reactive"); + .withUserConfiguration(DataFetchersConfiguration.class, CustomWebInterceptor.class).withPropertyValues( + "spring.main.web-application-type=reactive", "spring.graphql.schema.printer.enabled=true"); @Test void simpleQueryShouldWork() { @@ -92,6 +94,13 @@ class GraphQlWebFluxAutoConfigurationTests { }); } + @Test + void shouldExposeSchemaEndpoint() { + testWithWebClient((client) -> client.get().uri("/schema").accept(MediaType.ALL).exchange() + .expectStatus().isOk().expectHeader().contentType(MediaType.TEXT_PLAIN).expectBody(String.class) + .value(containsString("type Book"))); + } + private void testWithWebClient(Consumer consumer) { this.contextRunner.run((context) -> { WebTestClient client = WebTestClient.bindToApplicationContext(context).configureClient() diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java index f9e19a9df84..37c1f16f29b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.graphql.servlet; import graphql.schema.idl.TypeRuntimeWiring; +import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -56,8 +57,8 @@ class GraphQlWebMvcAutoConfigurationTests { AutoConfigurations.of(DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, JacksonAutoConfiguration.class, GraphQlAutoConfiguration.class, GraphQlWebMvcAutoConfiguration.class)) - .withUserConfiguration(DataFetchersConfiguration.class, CustomWebInterceptor.class) - .withPropertyValues("spring.main.web-application-type=servlet"); + .withUserConfiguration(DataFetchersConfiguration.class, CustomWebInterceptor.class).withPropertyValues( + "spring.main.web-application-type=servlet", "spring.graphql.schema.printer.enabled=true"); @Test void simpleQueryShouldWork() { @@ -99,6 +100,13 @@ class GraphQlWebMvcAutoConfigurationTests { }); } + @Test + void shouldExposeSchemaEndpoint() { + 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) { this.contextRunner.run((context) -> { MediaType mediaType = MediaType.APPLICATION_JSON;