diff --git a/spring-graphql/src/main/java/org/springframework/graphql/web/webmvc/GraphiQlHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/web/webmvc/GraphiQlHandler.java index 441932cf..6f57c6e0 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/web/webmvc/GraphiQlHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/web/webmvc/GraphiQlHandler.java @@ -69,7 +69,9 @@ public class GraphiQlHandler { } private URI getRedirectUrl(ServerRequest request) { - return request.uriBuilder().queryParam("path", this.graphQlPath).build(); + String contextPath = request.requestPath().contextPath().toString(); + String path = request.requestPath().pathWithinApplication().toString(); + return request.uriBuilder().replacePath(contextPath).path(path).queryParam("path", this.graphQlPath).build(); } } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/web/webflux/GraphiQlHandlerTests.java b/spring-graphql/src/test/java/org/springframework/graphql/web/webflux/GraphiQlHandlerTests.java new file mode 100644 index 00000000..26a2bf1c --- /dev/null +++ b/spring-graphql/src/test/java/org/springframework/graphql/web/webflux/GraphiQlHandlerTests.java @@ -0,0 +1,107 @@ +/* + * Copyright 2020-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.graphql.web.webflux; + +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import org.springframework.core.codec.ResourceEncoder; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.codec.EncoderHttpMessageWriter; +import org.springframework.http.codec.HttpMessageReader; +import org.springframework.http.codec.HttpMessageWriter; +import org.springframework.mock.http.server.reactive.MockServerHttpRequest; +import org.springframework.mock.web.server.MockServerWebExchange; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.reactive.result.view.ViewResolver; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link GraphiQlHandler}. + * @author Brian Clozel + */ +class GraphiQlHandlerTests { + + private static final List> MESSAGE_READERS = Collections.emptyList(); + + private final GraphiQlHandler handler = new GraphiQlHandler("/graphql", + new ByteArrayResource("GRAPHIQL".getBytes(StandardCharsets.UTF_8))); + + + @Test + void shouldRedirectWithPathQueryParameter() { + MockServerHttpRequest httpRequest = MockServerHttpRequest.get("/graphiql").build(); + MockServerWebExchange exchange = MockServerWebExchange.from(httpRequest); + ServerRequest request = ServerRequest.create(exchange, MESSAGE_READERS); + ServerResponse response = this.handler.handleRequest(request).block(); + assertThat(response.statusCode()).isEqualTo(HttpStatus.TEMPORARY_REDIRECT); + assertThat(response.headers().getLocation()).isNotNull(); + assertThat(response.headers().getLocation().toASCIIString()).isEqualTo("/graphiql?path=/graphql"); + } + + @Test + void shouldServeGraphiQlHtmlResource() { + MockServerHttpRequest httpRequest = MockServerHttpRequest.get("/graphiql").queryParam("path", "/graphql").build(); + MockServerWebExchange exchange = MockServerWebExchange.from(httpRequest); + ServerRequest request = ServerRequest.create(exchange, MESSAGE_READERS); + ServerResponse response = this.handler.handleRequest(request).block(); + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.headers().getContentType()).isEqualTo(MediaType.TEXT_HTML); + assertThat(getResponseContent(exchange, response)).isEqualTo("GRAPHIQL"); + } + + @Test + void shouldConsiderContextPathWhenRedirecting() { + MockServerHttpRequest httpRequest = MockServerHttpRequest.get("/context/graphiql").contextPath("/context").build(); + MockServerWebExchange exchange = MockServerWebExchange.from(httpRequest); + ServerRequest request = ServerRequest.create(exchange, MESSAGE_READERS); + assertThat(request.requestPath().contextPath().toString()).isEqualTo("/context"); + assertThat(request.requestPath().pathWithinApplication().toString()).isEqualTo("/graphiql"); + + ServerResponse response = this.handler.handleRequest(request).block(); + assertThat(response.statusCode()).isEqualTo(HttpStatus.TEMPORARY_REDIRECT); + assertThat(response.headers().getLocation()).isNotNull(); + assertThat(response.headers().getLocation().toASCIIString()).isEqualTo("/context/graphiql?path=/graphql"); + } + + private String getResponseContent(MockServerWebExchange exchange, ServerResponse response) { + response.writeTo(exchange, new DefaultContext()).block(); + return exchange.getResponse().getBodyAsString().block(); + } + + private static class DefaultContext implements ServerResponse.Context { + + @Override + public List> messageWriters() { + return Collections.singletonList(new EncoderHttpMessageWriter<>(new ResourceEncoder())); + } + + @Override + public List viewResolvers() { + return Collections.emptyList(); + } + + } + +} \ No newline at end of file diff --git a/spring-graphql/src/test/java/org/springframework/graphql/web/webmvc/GraphiQlHandlerTests.java b/spring-graphql/src/test/java/org/springframework/graphql/web/webmvc/GraphiQlHandlerTests.java new file mode 100644 index 00000000..ef1d9842 --- /dev/null +++ b/spring-graphql/src/test/java/org/springframework/graphql/web/webmvc/GraphiQlHandlerTests.java @@ -0,0 +1,100 @@ +/* + * Copyright 2020-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.graphql.web.webmvc; + + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; + +import javax.servlet.ServletException; + +import org.junit.jupiter.api.Test; + +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.ResourceHttpMessageConverter; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.web.servlet.function.ServerRequest; +import org.springframework.web.servlet.function.ServerResponse; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * Tests for {@link GraphiQlHandler}. + * @author Brian Clozel + */ +class GraphiQlHandlerTests { + + private static final List> MESSAGE_READERS = Collections.emptyList(); + + private GraphiQlHandler handler = new GraphiQlHandler("/graphql", + new ByteArrayResource("GRAPHIQL".getBytes(StandardCharsets.UTF_8))); + + @Test + void shouldRedirectWithPathQueryParameter() { + MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/graphiql"); + ServerRequest request = ServerRequest.create(servletRequest, MESSAGE_READERS); + ServerResponse response = this.handler.handleRequest(request); + assertThat(response.statusCode()).isEqualTo(HttpStatus.TEMPORARY_REDIRECT); + assertThat(response.headers().getLocation()).isNotNull(); + assertThat(response.headers().getLocation().toASCIIString()).isEqualTo("http://localhost/graphiql?path=/graphql"); + } + + @Test + void shouldServeGraphiQlHtmlResource() throws Exception { + MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/graphiql"); + servletRequest.addParameter("path", "/graphql"); + ServerRequest request = ServerRequest.create(servletRequest, MESSAGE_READERS); + ServerResponse response = this.handler.handleRequest(request); + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.headers().getContentType()).isEqualTo(MediaType.TEXT_HTML); + assertThat(getResponseContent(servletRequest, response)).isEqualTo("GRAPHIQL"); + } + + @Test + void shouldConsiderContextPathWhenRedirecting() { + MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/context/graphiql"); + servletRequest.setContextPath("/context"); + ServerRequest request = ServerRequest.create(servletRequest, MESSAGE_READERS); + ServerResponse response = this.handler.handleRequest(request); + assertThat(response.statusCode()).isEqualTo(HttpStatus.TEMPORARY_REDIRECT); + assertThat(response.headers().getLocation()).isNotNull(); + assertThat(response.headers().getLocation().toASCIIString()).isEqualTo("http://localhost/context/graphiql?path=/graphql"); + } + + private String getResponseContent(MockHttpServletRequest servletRequest, ServerResponse response) throws ServletException, IOException { + MockHttpServletResponse servletResponse = new MockHttpServletResponse(); + response.writeTo(servletRequest, servletResponse, new DefaultContext()); + return servletResponse.getContentAsString(); + } + + private static class DefaultContext implements ServerResponse.Context { + + @Override + public List> messageConverters() { + return Collections.singletonList(new ResourceHttpMessageConverter()); + } + + } + +} \ No newline at end of file