HTTP handlers set Locale in ExecutionInput
See gh-3
This commit is contained in:
@@ -20,6 +20,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
@@ -113,6 +114,12 @@ class DefaultGraphQlTester implements GraphQlTester {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultRequestSpec locale(Locale locale) {
|
||||
setLocale(locale);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseSpec execute() {
|
||||
return this.requestStrategy.execute(createRequestInput());
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
package org.springframework.graphql.test.tester;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.graphql.RequestInput;
|
||||
import org.springframework.graphql.web.WebInput;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -122,6 +124,12 @@ class DefaultWebGraphQlTester implements WebGraphQlTester {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebRequestSpec locale(Locale locale) {
|
||||
setLocale(locale);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebRequestSpec httpHeader(String headerName, String... headerValues) {
|
||||
for (String headerValue : headerValues) {
|
||||
@@ -152,7 +160,8 @@ class DefaultWebGraphQlTester implements WebGraphQlTester {
|
||||
}
|
||||
|
||||
private WebInput createWebInput() {
|
||||
return new WebInput(DEFAULT_URL, this.headers, createRequestInput().toMap(), null);
|
||||
RequestInput input = createRequestInput();
|
||||
return new WebInput(DEFAULT_URL, this.headers, input.toMap(), input.getLocale(), null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.graphql.test.tester;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@@ -174,6 +175,13 @@ public interface GraphQlTester {
|
||||
*/
|
||||
T variable(String name, Object value);
|
||||
|
||||
/**
|
||||
* Set the locale to associate with the request.
|
||||
* @param locale the locale to use
|
||||
* @return this request spec
|
||||
*/
|
||||
T locale(Locale locale);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.graphql.test.tester;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.graphql.RequestInput;
|
||||
@@ -38,6 +39,9 @@ class GraphQlTesterRequestSpecSupport {
|
||||
|
||||
private final Map<String, Object> variables = new LinkedHashMap<>();
|
||||
|
||||
@Nullable
|
||||
private Locale locale;
|
||||
|
||||
|
||||
protected GraphQlTesterRequestSpecSupport(String query) {
|
||||
Assert.notNull(query, "`query` is required");
|
||||
@@ -53,12 +57,16 @@ class GraphQlTesterRequestSpecSupport {
|
||||
this.variables.put(name, value);
|
||||
}
|
||||
|
||||
protected void setLocale(Locale locale) {
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
protected void verify(GraphQlTester.ResponseSpec responseSpec) {
|
||||
responseSpec.path("$.errors").valueIsEmpty();
|
||||
}
|
||||
|
||||
protected RequestInput createRequestInput() {
|
||||
return new RequestInput(this.query, this.operationName, this.variables);
|
||||
return new RequestInput(this.query, this.operationName, this.variables, this.locale);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ package org.springframework.graphql.test.tester;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.jayway.jsonpath.Configuration;
|
||||
@@ -82,7 +84,13 @@ final class WebTestClientRequestStrategy extends RequestStrategySupport implemen
|
||||
FluxExchangeResult<TestExecutionResult> exchangeResult = this.client.post()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.TEXT_EVENT_STREAM)
|
||||
.headers(headers -> headers.putAll(webInput.getHeaders()))
|
||||
.headers(headers -> {
|
||||
Locale locale = webInput.getLocale();
|
||||
if (locale != null) {
|
||||
headers.setAcceptLanguageAsLocales(Collections.singletonList(locale));
|
||||
}
|
||||
headers.putAll(webInput.getHeaders());
|
||||
})
|
||||
.bodyValue(webInput.toMap())
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
|
||||
@@ -227,7 +227,7 @@ public class WebGraphQlTesterTests {
|
||||
|
||||
String content = request.getBody().readUtf8();
|
||||
Map<String, Object> map = new ObjectMapper().readValue(content, new TypeReference<Map<String, Object>>() {});
|
||||
WebInput webInput = new WebInput(request.getRequestUrl().uri(), headers, map, null);
|
||||
WebInput webInput = new WebInput(request.getRequestUrl().uri(), headers, map, null, null);
|
||||
|
||||
consumer.accept(webInput);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
@@ -47,18 +48,23 @@ public class RequestInput {
|
||||
|
||||
private final Map<String, Object> variables;
|
||||
|
||||
@Nullable
|
||||
private final Locale locale;
|
||||
|
||||
private final List<BiFunction<ExecutionInput, ExecutionInput.Builder, ExecutionInput>> executionInputConfigurers = new ArrayList<>();
|
||||
|
||||
public RequestInput(String query, @Nullable String operationName, @Nullable Map<String, Object> vars) {
|
||||
|
||||
public RequestInput(
|
||||
String query, @Nullable String operationName, @Nullable Map<String, Object> vars,
|
||||
@Nullable Locale locale) {
|
||||
|
||||
Assert.notNull(query, "'query' is required");
|
||||
this.query = query;
|
||||
this.operationName = operationName;
|
||||
this.variables = ((vars != null) ? vars : Collections.emptyMap());
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
public RequestInput(Map<String, Object> body) {
|
||||
this(getKey("query", body), getKey("operationName", body), getKey("variables", body));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T getKey(String key, Map<String, Object> body) {
|
||||
@@ -66,8 +72,8 @@ public class RequestInput {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query name extracted from the request body. This is guaranteed to be a
|
||||
* non-empty string.
|
||||
* Return the query name extracted from the request. This is guaranteed to
|
||||
* be a non-empty string.
|
||||
* @return the query name
|
||||
*/
|
||||
public String getQuery() {
|
||||
@@ -75,8 +81,8 @@ public class RequestInput {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the operation name extracted from the request body or {@code null} if not
|
||||
* provided.
|
||||
* Return the operation name extracted from the request or {@code null} if
|
||||
* not provided.
|
||||
* @return the operation name or {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
@@ -85,14 +91,23 @@ public class RequestInput {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the variables that can be referenced via $syntax extracted from the request
|
||||
* body or a {@code null} if not provided.
|
||||
* Return the variables that can be referenced via $syntax extracted from
|
||||
* the request body or a {@code null} if not provided.
|
||||
* @return the request variables or {@code null}
|
||||
*/
|
||||
public Map<String, Object> getVariables() {
|
||||
return this.variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the locale associated with the request, if available.
|
||||
* @return the locale of {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public Locale getLocale() {
|
||||
return this.locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a consumer to configure the {@link ExecutionInput} used for input to
|
||||
* {@link graphql.GraphQL#executeAsync(ExecutionInput)}. The builder is initially
|
||||
@@ -113,8 +128,12 @@ public class RequestInput {
|
||||
* @return the execution input
|
||||
*/
|
||||
public ExecutionInput toExecutionInput() {
|
||||
ExecutionInput executionInput = ExecutionInput.newExecutionInput().query(this.query)
|
||||
.operationName(this.operationName).variables(this.variables).build();
|
||||
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
|
||||
.query(this.query)
|
||||
.operationName(this.operationName)
|
||||
.variables(this.variables)
|
||||
.locale(this.locale)
|
||||
.build();
|
||||
|
||||
for (BiFunction<ExecutionInput, ExecutionInput.Builder, ExecutionInput> configurer : this.executionInputConfigurers) {
|
||||
ExecutionInput current = executionInput;
|
||||
@@ -142,9 +161,10 @@ public class RequestInput {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Query='" + getQuery() + "'"
|
||||
+ ((getOperationName() != null) ? ", Operation='" + getOperationName() + "'" : "")
|
||||
+ (!CollectionUtils.isEmpty(getVariables()) ? ", Variables=" + getVariables() : "");
|
||||
return "Query='" + getQuery() + "'" +
|
||||
((getOperationName() != null) ? ", Operation='" + getOperationName() + "'" : "") +
|
||||
(!CollectionUtils.isEmpty(getVariables()) ? ", Variables=" + getVariables() : "") +
|
||||
(getLocale() != null ? ", Locale=" + getLocale() : "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.graphql.web;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.graphql.RequestInput;
|
||||
@@ -50,12 +51,16 @@ public class WebInput extends RequestInput {
|
||||
* @param uri the url for the HTTP request, or WebSocket handshake
|
||||
* @param headers the HTTP request headers
|
||||
* @param body the content of the request deserialized from JSON
|
||||
* @param locale the locale associated with the request, if any
|
||||
* @param id an identifier for the GraphQL request, e.g. a subscription id for
|
||||
* correlating request and response messages, or it could be an id associated with the
|
||||
* underlying request/connection id, if available
|
||||
*/
|
||||
public WebInput(URI uri, HttpHeaders headers, Map<String, Object> body, @Nullable String id) {
|
||||
super(validateQuery(body));
|
||||
public WebInput(
|
||||
URI uri, HttpHeaders headers, Map<String, Object> body,
|
||||
@Nullable Locale locale, @Nullable String id) {
|
||||
|
||||
super(getKey("query", body), getKey("operationName", body), getKey("variables", body), locale);
|
||||
Assert.notNull(uri, "URI is required'");
|
||||
Assert.notNull(headers, "HttpHeaders is required'");
|
||||
this.uri = UriComponentsBuilder.fromUri(uri).build(true);
|
||||
@@ -63,14 +68,15 @@ public class WebInput extends RequestInput {
|
||||
this.id = (id != null) ? id : ObjectUtils.identityToString(this);
|
||||
}
|
||||
|
||||
private static Map<String, Object> validateQuery(Map<String, Object> body) {
|
||||
String query = (String) body.get("query");
|
||||
if (!StringUtils.hasText(query)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T getKey(String key, Map<String, Object> body) {
|
||||
if (key.equals("query") && !StringUtils.hasText((String) body.get(key))) {
|
||||
throw new ServerWebInputException("Query is required");
|
||||
}
|
||||
return body;
|
||||
return (T) body.get(key);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the URI of the HTTP request including {@link UriComponents#getQueryParams()
|
||||
* URL query parameters}.
|
||||
|
||||
@@ -61,8 +61,10 @@ public class GraphQlHttpHandler {
|
||||
public Mono<ServerResponse> handleRequest(ServerRequest request) {
|
||||
return request.bodyToMono(MAP_PARAMETERIZED_TYPE_REF)
|
||||
.flatMap((body) -> {
|
||||
String id = request.exchange().getRequest().getId();
|
||||
WebInput input = new WebInput(request.uri(), request.headers().asHttpHeaders(), body, id);
|
||||
WebInput input = new WebInput(
|
||||
request.uri(), request.headers().asHttpHeaders(), body,
|
||||
request.exchange().getLocaleContext().getLocale(),
|
||||
request.exchange().getRequest().getId());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Executing: " + input);
|
||||
}
|
||||
|
||||
@@ -161,7 +161,8 @@ public class GraphQlWebSocketHandler implements WebSocketHandler {
|
||||
if (id == null) {
|
||||
return GraphQlStatus.close(session, GraphQlStatus.INVALID_MESSAGE_STATUS);
|
||||
}
|
||||
WebInput input = new WebInput(handshakeInfo.getUri(), handshakeInfo.getHeaders(), getPayload(map), id);
|
||||
WebInput input = new WebInput(
|
||||
handshakeInfo.getUri(), handshakeInfo.getHeaders(), getPayload(map), null, id);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Executing: " + input);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.graphql.web.WebGraphQlHandler;
|
||||
import org.springframework.graphql.web.WebInput;
|
||||
@@ -68,10 +69,15 @@ public class GraphQlHttpHandler {
|
||||
* {@link HttpMediaTypeNotSupportedException}.
|
||||
*/
|
||||
public ServerResponse handleRequest(ServerRequest request) throws ServletException {
|
||||
WebInput input = new WebInput(request.uri(), request.headers().asHttpHeaders(), readBody(request), null);
|
||||
|
||||
WebInput input = new WebInput(
|
||||
request.uri(), request.headers().asHttpHeaders(), readBody(request),
|
||||
LocaleContextHolder.getLocale(), null);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Executing: " + input);
|
||||
}
|
||||
|
||||
Mono<ServerResponse> responseMono = this.graphQlHandler.handleRequest(input).map((output) -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Execution complete");
|
||||
@@ -82,6 +88,7 @@ public class GraphQlHttpHandler {
|
||||
}
|
||||
return builder.body(output.toSpecification());
|
||||
});
|
||||
|
||||
return ServerResponse.async(responseMono);
|
||||
}
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub
|
||||
URI uri = session.getUri();
|
||||
Assert.notNull(uri, "Expected handshake url");
|
||||
HttpHeaders headers = session.getHandshakeHeaders();
|
||||
WebInput input = new WebInput(uri, headers, getPayload(map), id);
|
||||
WebInput input = new WebInput(uri, headers, getPayload(map), null, id);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Executing: " + input);
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ public class BatchMappingInvocationTests {
|
||||
"}";
|
||||
|
||||
ExecutionResult result = initGraphQlService(controllerClass, CourseConfig.class)
|
||||
.execute(new RequestInput(query, null, null))
|
||||
.execute(new RequestInput(query, null, null, null))
|
||||
.block();
|
||||
|
||||
List<Map<String, Object>> actualCourses = GraphQlTestUtils.checkErrorsAndGetData(result, "courses");
|
||||
@@ -150,7 +150,7 @@ public class BatchMappingInvocationTests {
|
||||
"}";
|
||||
|
||||
ExecutionResult result = initGraphQlService(controllerClass, CourseConfig.class)
|
||||
.execute(new RequestInput(query, null, null))
|
||||
.execute(new RequestInput(query, null, null, null))
|
||||
.block();
|
||||
|
||||
List<Map<String, Object>> actualCourses = GraphQlTestUtils.checkErrorsAndGetData(result, "courses");
|
||||
|
||||
@@ -74,7 +74,7 @@ public class SchemaMappingInvocationTests {
|
||||
"}";
|
||||
|
||||
ExecutionResult result = initGraphQlService()
|
||||
.execute(new RequestInput(query, null, null))
|
||||
.execute(new RequestInput(query, null, null, null))
|
||||
.block();
|
||||
|
||||
Map<String, Object> book = GraphQlTestUtils.checkErrorsAndGetData(result, "bookById");
|
||||
@@ -96,7 +96,7 @@ public class SchemaMappingInvocationTests {
|
||||
"}";
|
||||
|
||||
ExecutionResult result = initGraphQlService()
|
||||
.execute(new RequestInput(query, null, null))
|
||||
.execute(new RequestInput(query, null, null, null))
|
||||
.block();
|
||||
|
||||
List<Map<String, Object>> bookList = GraphQlTestUtils.checkErrorsAndGetData(result, "booksByCriteria");
|
||||
@@ -117,7 +117,7 @@ public class SchemaMappingInvocationTests {
|
||||
"}";
|
||||
|
||||
AtomicReference<GraphQLContext> contextRef = new AtomicReference<>();
|
||||
RequestInput requestInput = new RequestInput(query, null, null);
|
||||
RequestInput requestInput = new RequestInput(query, null, null, null);
|
||||
requestInput.configureExecutionInput((executionInput, builder) -> {
|
||||
contextRef.set(executionInput.getGraphQLContext());
|
||||
return executionInput;
|
||||
@@ -147,7 +147,7 @@ public class SchemaMappingInvocationTests {
|
||||
"}";
|
||||
|
||||
ExecutionResult result = initGraphQlService()
|
||||
.execute(new RequestInput(operation, null, null))
|
||||
.execute(new RequestInput(operation, null, null, null))
|
||||
.block();
|
||||
|
||||
Map<String, Object> author = GraphQlTestUtils.checkErrorsAndGetData(result, "addAuthor");
|
||||
@@ -166,7 +166,7 @@ public class SchemaMappingInvocationTests {
|
||||
"}";
|
||||
|
||||
ExecutionResult result = initGraphQlService()
|
||||
.execute(new RequestInput(operation, null, null))
|
||||
.execute(new RequestInput(operation, null, null, null))
|
||||
.block();
|
||||
|
||||
Publisher<ExecutionResult> publisher = GraphQlTestUtils.checkErrorsAndGetData(result);
|
||||
|
||||
@@ -298,8 +298,9 @@ class QuerydslDataFetcherTests {
|
||||
}
|
||||
|
||||
private WebInput input(String query) {
|
||||
return new WebInput(URI.create("http://abc.org"), new HttpHeaders(),
|
||||
Collections.singletonMap("query", query), "1");
|
||||
return new WebInput(
|
||||
URI.create("http://abc.org"), new HttpHeaders(), Collections.singletonMap("query", query),
|
||||
null, "1");
|
||||
}
|
||||
|
||||
interface BookProjection {
|
||||
|
||||
@@ -72,7 +72,7 @@ public class BatchLoadingTests {
|
||||
" }" +
|
||||
"}";
|
||||
|
||||
RequestInput input = new RequestInput(query, null, null);
|
||||
RequestInput input = new RequestInput(query, null, null, null);
|
||||
ExecutionResult result = service.execute(input).block();
|
||||
|
||||
assertThat(result.getErrors()).isEmpty();
|
||||
|
||||
@@ -84,7 +84,7 @@ public class ClassNameTypeResolverTests {
|
||||
"}";
|
||||
|
||||
ExecutionResult result = new ExecutionGraphQlService(graphQlSource)
|
||||
.execute(new RequestInput(query, null, null))
|
||||
.execute(new RequestInput(query, null, null, null))
|
||||
.block();
|
||||
|
||||
List<Map<String, Object>> actualAnimals = GraphQlTestUtils.checkErrorsAndGetData(result, "animals");
|
||||
@@ -134,7 +134,7 @@ public class ClassNameTypeResolverTests {
|
||||
"}";
|
||||
|
||||
ExecutionResult result = new ExecutionGraphQlService(graphQlSource)
|
||||
.execute(new RequestInput(query, null, null))
|
||||
.execute(new RequestInput(query, null, null, null))
|
||||
.block();
|
||||
|
||||
List<Map<String, Object>> actualSightings = GraphQlTestUtils.checkErrorsAndGetData(result, "sightings");
|
||||
|
||||
@@ -48,7 +48,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class WebGraphQlHandlerTests {
|
||||
|
||||
private static final WebInput webInput = new WebInput(
|
||||
URI.create("http://abc.org"), new HttpHeaders(), Collections.singletonMap("query", "{ greeting }"), "1");
|
||||
URI.create("http://abc.org"), new HttpHeaders(), Collections.singletonMap("query", "{ greeting }"),
|
||||
null, "1");
|
||||
|
||||
@Test
|
||||
void reactorContextPropagation() {
|
||||
|
||||
@@ -37,8 +37,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
public class WebInterceptorTests {
|
||||
|
||||
private static final WebInput webInput = new WebInput(URI.create("http://abc.org"), new HttpHeaders(),
|
||||
Collections.singletonMap("query", "{ notUsed }"), "1");
|
||||
private static final WebInput webInput = new WebInput(
|
||||
URI.create("http://abc.org"), new HttpHeaders(), Collections.singletonMap("query", "{ notUsed }"),
|
||||
null, "1");
|
||||
|
||||
@Test
|
||||
void interceptorOrder() {
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2002-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.web.webflux;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import graphql.schema.DataFetcher;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.graphql.GraphQlService;
|
||||
import org.springframework.graphql.GraphQlTestUtils;
|
||||
import org.springframework.graphql.TestGraphQlSource;
|
||||
import org.springframework.graphql.execution.ExecutionGraphQlService;
|
||||
import org.springframework.graphql.web.WebGraphQlHandler;
|
||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.mock.web.reactive.function.server.MockServerRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link GraphQlHttpHandler}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class GraphQlHttpHandlerTests {
|
||||
|
||||
@Test
|
||||
void locale() {
|
||||
GraphQlHttpHandler handler = createHttpHandler(
|
||||
"type Query { greeting: String }", "Query", "greeting",
|
||||
(env) -> "Hello in " + env.getLocale());
|
||||
|
||||
MockServerHttpRequest httpRequest =
|
||||
MockServerHttpRequest.post("/").acceptLanguageAsLocales(Locale.FRENCH).build();
|
||||
|
||||
MockServerHttpResponse httpResponse = handleRequest(
|
||||
httpRequest, handler, Collections.singletonMap("query", "{greeting}"));
|
||||
|
||||
assertThat(httpResponse.getBodyAsString().block())
|
||||
.isEqualTo("{\"data\":{\"greeting\":\"Hello in fr\"}}");
|
||||
}
|
||||
|
||||
private GraphQlHttpHandler createHttpHandler(
|
||||
String schemaContent, String type, String field, DataFetcher<Object> dataFetcher) {
|
||||
|
||||
GraphQL graphQl = GraphQlTestUtils.initGraphQl(schemaContent, type, field, dataFetcher);
|
||||
GraphQlService service = new ExecutionGraphQlService(new TestGraphQlSource(graphQl));
|
||||
return new GraphQlHttpHandler(WebGraphQlHandler.builder(service).build());
|
||||
}
|
||||
|
||||
private MockServerHttpResponse handleRequest(
|
||||
MockServerHttpRequest httpRequest, GraphQlHttpHandler handler, Map<String, String> body) {
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(httpRequest);
|
||||
|
||||
MockServerRequest serverRequest = MockServerRequest.builder()
|
||||
.exchange(exchange)
|
||||
.uri(((ServerWebExchange) exchange).getRequest().getURI())
|
||||
.method(((ServerWebExchange) exchange).getRequest().getMethod())
|
||||
.headers(((ServerWebExchange) exchange).getRequest().getHeaders())
|
||||
.body(Mono.just((Object) body));
|
||||
|
||||
handler.handleRequest(serverRequest)
|
||||
.flatMap(response -> response.writeTo(exchange, new DefaultContext()))
|
||||
.block();
|
||||
|
||||
return exchange.getResponse();
|
||||
}
|
||||
|
||||
|
||||
private static class DefaultContext implements ServerResponse.Context {
|
||||
|
||||
@Override
|
||||
public List<HttpMessageWriter<?>> messageWriters() {
|
||||
return Collections.singletonList(new EncoderHttpMessageWriter<>(new Jackson2JsonEncoder()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ViewResolver> viewResolvers() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2002-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.web.webmvc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import graphql.schema.DataFetcher;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.graphql.GraphQlService;
|
||||
import org.springframework.graphql.GraphQlTestUtils;
|
||||
import org.springframework.graphql.TestGraphQlSource;
|
||||
import org.springframework.graphql.execution.ExecutionGraphQlService;
|
||||
import org.springframework.graphql.web.WebGraphQlHandler;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.servlet.function.AsyncServerResponse;
|
||||
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 GraphQlHttpHandler}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class GraphQlHttpHandlerTests {
|
||||
|
||||
private static final List<HttpMessageConverter<?>> MESSAGE_READERS =
|
||||
Collections.singletonList(new MappingJackson2HttpMessageConverter());
|
||||
|
||||
|
||||
@Test
|
||||
void locale() throws Exception {
|
||||
GraphQlHttpHandler handler = createHttpHandler(
|
||||
"type Query { greeting: String }", "Query", "greeting", (env) -> "Hello in " + env.getLocale());
|
||||
|
||||
MockHttpServletRequest servletRequest = new MockHttpServletRequest("POST", "/");
|
||||
servletRequest.setContentType("application/json");
|
||||
servletRequest.setContent("{\"query\":\"{ greeting }\"}".getBytes(StandardCharsets.UTF_8));
|
||||
servletRequest.setAsyncSupported(true);
|
||||
|
||||
LocaleContextHolder.setLocale(Locale.FRENCH);
|
||||
try {
|
||||
MockHttpServletResponse servletResponse = handleRequest(servletRequest, handler);
|
||||
|
||||
assertThat(servletResponse.getContentAsString())
|
||||
.isEqualTo("{\"data\":{\"greeting\":\"Hello in fr\"}}");
|
||||
}
|
||||
finally {
|
||||
LocaleContextHolder.resetLocaleContext();
|
||||
}
|
||||
}
|
||||
|
||||
private GraphQlHttpHandler createHttpHandler(
|
||||
String schemaContent, String type, String field, DataFetcher<Object> dataFetcher) {
|
||||
|
||||
GraphQL graphQl = GraphQlTestUtils.initGraphQl(schemaContent, type, field, dataFetcher);
|
||||
GraphQlService service = new ExecutionGraphQlService(new TestGraphQlSource(graphQl));
|
||||
return new GraphQlHttpHandler(WebGraphQlHandler.builder(service).build());
|
||||
}
|
||||
|
||||
private MockHttpServletResponse handleRequest(
|
||||
MockHttpServletRequest servletRequest, GraphQlHttpHandler handler) throws ServletException, IOException {
|
||||
|
||||
ServerRequest request = ServerRequest.create(servletRequest, MESSAGE_READERS);
|
||||
ServerResponse response = ((AsyncServerResponse) handler.handleRequest(request)).block();
|
||||
|
||||
MockHttpServletResponse servletResponse = new MockHttpServletResponse();
|
||||
response.writeTo(servletRequest, servletResponse, new DefaultContext());
|
||||
return servletResponse;
|
||||
}
|
||||
|
||||
|
||||
private static class DefaultContext implements ServerResponse.Context {
|
||||
|
||||
@Override
|
||||
public List<HttpMessageConverter<?>> messageConverters() {
|
||||
return MESSAGE_READERS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user