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" > -