From eef61843d3491aa2a482ad025ea7f0a3303c8ec1 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 29 Jun 2021 13:48:48 +0200 Subject: [PATCH] Add property for GraphiQL UI path This commit adds a new `spring.graphql.graphiql.path` configuration property for setting the path to the GraphiQL UI page. This change requires updates in the static HTML page to link the JavaScript fetcher back to the actual GraphQL API endpoint. Closes gh-70 --- README.md | 3 ++ .../graphql/boot/GraphQlProperties.java | 35 +++++++++++++ .../boot/WebFluxGraphQlAutoConfiguration.java | 15 +++--- .../graphql/boot/WebFluxGraphiQlHandler.java | 49 +++++++++++++++++++ .../boot/WebMvcGraphQlAutoConfiguration.java | 17 ++++--- .../graphql/boot/WebMvcGraphiQlHandler.java | 47 ++++++++++++++++++ .../src/main/resources/graphiql/index.html | 12 +++-- 7 files changed, 160 insertions(+), 18 deletions(-) create mode 100644 graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphiQlHandler.java create mode 100644 graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphiQlHandler.java diff --git a/README.md b/README.md index e74c5b95..1544e308 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,9 @@ spring.graphql.schema.location=classpath:graphql/schema.graphqls # endpoint path is concatenated with the main path, so "/graphql/schema" by default spring.graphql.schema.printer.enabled=false spring.graphql.schema.printer.path=/schema +# GraphiQL UI configuration +spring.graphql.graphiql.enabled=true +spring.graphql.graphiql.path=/graphiql # whether micrometer metrics should be collected for graphql queries management.metrics.graphql.autotime.enabled=true ```` diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/GraphQlProperties.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/GraphQlProperties.java index 07a0bb60..bb3b2afd 100644 --- a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/GraphQlProperties.java +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/GraphQlProperties.java @@ -36,6 +36,8 @@ public class GraphQlProperties { private final Schema schema = new Schema(); + private final GraphiQL graphiql = new GraphiQL(); + private final WebSocket websocket = new WebSocket(); public String getPath() { @@ -50,6 +52,10 @@ public class GraphQlProperties { return this.schema; } + public GraphiQL getGraphiql() { + return this.graphiql; + } + public WebSocket getWebsocket() { return this.websocket; } @@ -107,6 +113,35 @@ public class GraphQlProperties { } + public static class GraphiQL { + + /** + * Path to the GraphiQL UI endpoint. + */ + private String path = "/graphiql"; + + /** + * Whether the default GraphiQL UI is enabled. + */ + private boolean enabled = true; + + public String getPath() { + return this.path; + } + + public void setPath(String path) { + this.path = path; + } + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + } + public static class WebSocket { /** diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphQlAutoConfiguration.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphQlAutoConfiguration.java index 2854c1b0..2315ab69 100644 --- a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphQlAutoConfiguration.java +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphQlAutoConfiguration.java @@ -87,18 +87,21 @@ public class WebFluxGraphQlAutoConfiguration { public RouterFunction graphQlEndpoint(GraphQlHttpHandler handler, GraphQlSource graphQlSource, GraphQlProperties properties, ResourceLoader resourceLoader) { - String path = properties.getPath(); - Resource resource = resourceLoader.getResource("classpath:graphiql/index.html"); + String graphQLPath = properties.getPath(); if (logger.isInfoEnabled()) { - logger.info("GraphQL endpoint HTTP POST " + path); + logger.info("GraphQL endpoint HTTP POST " + graphQLPath); } // @formatter:off 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); + .POST(graphQLPath, accept(MediaType.APPLICATION_JSON).and(contentType(MediaType.APPLICATION_JSON)), handler::handleRequest); + if (properties.getGraphiql().isEnabled()) { + Resource resource = resourceLoader.getResource("classpath:graphiql/index.html"); + WebFluxGraphiQlHandler graphiQlHandler = new WebFluxGraphiQlHandler(graphQLPath, resource); + builder = builder.GET(properties.getGraphiql().getPath(), graphiQlHandler::showGraphiQlPage); + } if (properties.getSchema().getPrinter().isEnabled()) { SchemaPrinter printer = new SchemaPrinter(); - builder = builder.GET(path + properties.getSchema().getPrinter().getPath(), + builder = builder.GET(graphQLPath + properties.getSchema().getPrinter().getPath(), (req) -> ServerResponse.ok() .contentType(MediaType.TEXT_PLAIN) .bodyValue(printer.print(graphQlSource.schema()))); diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphiQlHandler.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphiQlHandler.java new file mode 100644 index 00000000..23ad4769 --- /dev/null +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphiQlHandler.java @@ -0,0 +1,49 @@ +/* + * 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 reactor.core.publisher.Mono; + +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; + +/** + * WebFlux functional handler for the GraphiQl UI. + * @author Brian Clozel + */ +class WebFluxGraphiQlHandler { + + private final String graphQlPath; + + private final Resource graphiQlResource; + + public WebFluxGraphiQlHandler(String graphQlPath, Resource graphiQlResource) { + this.graphQlPath = graphQlPath; + this.graphiQlResource = graphiQlResource; + } + + public Mono showGraphiQlPage(ServerRequest request) { + if (request.queryParam("path").isPresent()) { + return ServerResponse.ok().contentType(MediaType.TEXT_HTML).bodyValue(this.graphiQlResource); + } + else { + return ServerResponse.temporaryRedirect(request.uriBuilder().queryParam("path", this.graphQlPath).build()).build(); + } + } +} diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphQlAutoConfiguration.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphQlAutoConfiguration.java index b1d30dee..195a2b0c 100644 --- a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphQlAutoConfiguration.java +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphQlAutoConfiguration.java @@ -97,18 +97,21 @@ public class WebMvcGraphQlAutoConfiguration { public RouterFunction graphQlRouterFunction(GraphQlHttpHandler handler, GraphQlSource graphQlSource, GraphQlProperties properties, ResourceLoader resourceLoader) { - String path = properties.getPath(); - Resource resource = resourceLoader.getResource("classpath:graphiql/index.html"); + String graphQLPath = properties.getPath(); if (logger.isInfoEnabled()) { - logger.info("GraphQL endpoint HTTP POST " + path); + logger.info("GraphQL endpoint HTTP POST " + graphQLPath); } // @formatter:off 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); + .POST(graphQLPath, contentType(MediaType.APPLICATION_JSON).and(accept(MediaType.APPLICATION_JSON)), handler::handleRequest); + if (properties.getGraphiql().isEnabled()) { + Resource resource = resourceLoader.getResource("classpath:graphiql/index.html"); + WebMvcGraphiQlHandler graphiQLHandler = new WebMvcGraphiQlHandler(graphQLPath, resource); + builder = builder.GET(properties.getGraphiql().getPath(), graphiQLHandler::showGraphiQlPage); + } if (properties.getSchema().getPrinter().isEnabled()) { SchemaPrinter printer = new SchemaPrinter(); - builder = builder.GET(path + properties.getSchema().getPrinter().getPath(), + builder = builder.GET(graphQLPath + properties.getSchema().getPrinter().getPath(), (req) -> ServerResponse.ok() .contentType(MediaType.TEXT_PLAIN) .body(printer.print(graphQlSource.schema()))); @@ -118,7 +121,7 @@ public class WebMvcGraphQlAutoConfiguration { } @Configuration(proxyBeanMethods = false) - @ConditionalOnClass({ ServerContainer.class, WebSocketHandler.class }) + @ConditionalOnClass({ServerContainer.class, WebSocketHandler.class}) @ConditionalOnProperty(prefix = "spring.graphql.websocket", name = "path") public static class WebSocketConfiguration { diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphiQlHandler.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphiQlHandler.java new file mode 100644 index 00000000..25c26f2e --- /dev/null +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphiQlHandler.java @@ -0,0 +1,47 @@ +/* + * 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 org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.web.servlet.function.ServerRequest; +import org.springframework.web.servlet.function.ServerResponse; + +/** + * Servlet.fn handler for the GraphiQl UI. + * @author Brian Clozel + */ +class WebMvcGraphiQlHandler { + + private final String graphQlPath; + + private final Resource graphiQlResource; + + public WebMvcGraphiQlHandler(String graphQlPath, Resource graphiQlResource) { + this.graphQlPath = graphQlPath; + this.graphiQlResource = graphiQlResource; + } + + public ServerResponse showGraphiQlPage(ServerRequest request) { + if (request.param("path").isPresent()) { + return ServerResponse.ok().contentType(MediaType.TEXT_HTML).body(this.graphiQlResource); + } + else { + return ServerResponse.temporaryRedirect(request.uriBuilder().queryParam("path", this.graphQlPath).build()).build(); + } + } +} diff --git a/graphql-spring-boot-starter/src/main/resources/graphiql/index.html b/graphql-spring-boot-starter/src/main/resources/graphiql/index.html index cc0d57d7..7c4e4f8f 100644 --- a/graphql-spring-boot-starter/src/main/resources/graphiql/index.html +++ b/graphql-spring-boot-starter/src/main/resources/graphiql/index.html @@ -59,11 +59,10 @@ src="https://unpkg.com/graphiql/graphiql.min.js" type="application/javascript" > -