From 182e9e66f045b7990dd9e5c3944ccd80c0bf645a Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Fri, 18 Mar 2022 21:27:09 +0000 Subject: [PATCH] Extract ExecutionGraphQl[Request|Response] interfaces Rename the implementation classes Request[Input|Output] accordingly to match the interfaces, and move them into the support package. Create AbstractGraphQlResponse in the support package that pre-implements response field access. This leaves mainly contracts in the top-level package. See gh-332 --- .../test/tester/AbstractDirectTransport.java | 25 ++- .../test/tester/DefaultGraphQlTester.java | 2 +- .../test/tester/GraphQlServiceTransport.java | 14 +- .../tester/WebGraphQlHandlerTransport.java | 8 +- .../tester/GraphQlTesterBuilderTests.java | 6 +- .../test/tester/GraphQlTesterTestSupport.java | 14 +- .../test/tester/GraphQlTesterTests.java | 40 ++-- .../tester/WebGraphQlTesterBuilderTests.java | 19 +- .../graphql/DefaultGraphQlResponseField.java | 182 ---------------- .../graphql/ExecutionGraphQlRequest.java | 101 +++++++++ .../graphql/ExecutionGraphQlResponse.java | 45 ++++ .../graphql/GraphQlResponse.java | 4 +- .../graphql/GraphQlService.java | 10 +- .../client/DefaultClientGraphQlResponse.java | 2 +- .../DefaultClientGraphQlResponseField.java | 55 ++++- .../graphql/client/DefaultGraphQlClient.java | 2 +- .../graphql/client/MapGraphQlResponse.java | 3 +- .../execution/ExecutionGraphQlService.java | 15 +- .../support/AbstractGraphQlResponse.java | 195 ++++++++++++++++++ .../DefaultExecutionGraphQlRequest.java} | 61 +----- .../DefaultExecutionGraphQlResponse.java} | 28 +-- .../{ => support}/DefaultGraphQlRequest.java | 3 +- .../springframework/graphql/web/WebInput.java | 4 +- .../graphql/web/WebOutput.java | 35 ++-- .../graphql/web/support/GraphQlMessage.java | 6 +- ... DefaultExecutionGraphQlRequestTests.java} | 15 +- .../graphql/ResponseHelper.java | 16 +- .../DefaultGraphQlClientResponseTests.java | 2 +- .../client/GraphQlClientTestSupport.java | 2 +- .../graphql/client/GraphQlClientTests.java | 2 +- .../client/MockGraphQlWebSocketServer.java | 3 +- .../MockWebSocketGraphQlTransportTests.java | 2 +- .../client/WebGraphQlClientBuilderTests.java | 19 +- .../support/BatchMappingInvocationTests.java | 16 +- ...gPrincipalMethodArgumentResolverTests.java | 10 +- .../support/SchemaMappingInvocationTests.java | 40 ++-- ...gPrincipalMethodArgumentResolverTests.java | 16 +- .../graphql/execution/BatchLoadingTests.java | 8 +- .../execution/ClassNameTypeResolverTests.java | 16 +- .../graphql/web/WebInterceptorTests.java | 15 +- ...stInput.java => TestExecutionRequest.java} | 14 +- 41 files changed, 623 insertions(+), 452 deletions(-) delete mode 100644 spring-graphql/src/main/java/org/springframework/graphql/DefaultGraphQlResponseField.java create mode 100644 spring-graphql/src/main/java/org/springframework/graphql/ExecutionGraphQlRequest.java create mode 100644 spring-graphql/src/main/java/org/springframework/graphql/ExecutionGraphQlResponse.java create mode 100644 spring-graphql/src/main/java/org/springframework/graphql/support/AbstractGraphQlResponse.java rename spring-graphql/src/main/java/org/springframework/graphql/{RequestInput.java => support/DefaultExecutionGraphQlRequest.java} (61%) rename spring-graphql/src/main/java/org/springframework/graphql/{RequestOutput.java => support/DefaultExecutionGraphQlResponse.java} (81%) rename spring-graphql/src/main/java/org/springframework/graphql/{ => support}/DefaultGraphQlRequest.java (97%) rename spring-graphql/src/test/java/org/springframework/graphql/{RequestInputTests.java => DefaultExecutionGraphQlRequestTests.java} (64%) rename spring-graphql/src/testFixtures/java/org/springframework/graphql/{TestRequestInput.java => TestExecutionRequest.java} (66%) diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/AbstractDirectTransport.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/AbstractDirectTransport.java index 49feeb6d..e4a05702 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/AbstractDirectTransport.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/AbstractDirectTransport.java @@ -23,10 +23,13 @@ import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import org.springframework.graphql.ExecutionGraphQlRequest; +import org.springframework.graphql.ExecutionGraphQlResponse; import org.springframework.graphql.GraphQlRequest; import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.GraphQlResponseError; -import org.springframework.graphql.RequestOutput; +import org.springframework.graphql.support.DefaultExecutionGraphQlRequest; +import org.springframework.graphql.support.DefaultExecutionGraphQlResponse; import org.springframework.graphql.client.GraphQlTransport; import org.springframework.test.util.AssertionErrors; import org.springframework.util.AlternativeJdkIdGenerator; @@ -47,22 +50,22 @@ abstract class AbstractDirectTransport implements GraphQlTransport { @Override public Mono execute(GraphQlRequest request) { - return executeInternal(request).cast(GraphQlResponse.class); + return executeInternal(toExecutionRequest(request)).cast(GraphQlResponse.class); } @SuppressWarnings({"ConstantConditions", "unchecked"}) @Override public Flux executeSubscription(GraphQlRequest request) { - return executeInternal(request).flatMapMany(output -> { + return executeInternal(toExecutionRequest(request)).flatMapMany(response -> { try { - Object data = output.getData(); + Object data = response.getData(); AssertionErrors.assertTrue("Not a Publisher: " + data, data instanceof Publisher); - List errors = output.getErrors(); + List errors = response.getErrors(); AssertionErrors.assertTrue("Subscription errors: " + errors, CollectionUtils.isEmpty(errors)); - return Flux.from((Publisher) data) - .map(result -> new RequestOutput(output.getExecutionInput(), result)); + return Flux.from((Publisher) data).map(executionResult -> + new DefaultExecutionGraphQlResponse(response.getExecutionInput(), executionResult)); } catch (AssertionError ex) { throw new AssertionError(ex.getMessage() + "\nRequest: " + request, ex); @@ -70,9 +73,15 @@ abstract class AbstractDirectTransport implements GraphQlTransport { }); } + private ExecutionGraphQlRequest toExecutionRequest(GraphQlRequest request) { + return new DefaultExecutionGraphQlRequest( + request.getDocument(), request.getOperationName(), request.getVariables(), + idGenerator.generateId().toString(), null); + } + /** * Subclasses must implement this to execute requests. */ - protected abstract Mono executeInternal(GraphQlRequest request); + protected abstract Mono executeInternal(ExecutionGraphQlRequest request); } diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java index 3b64c07e..5011a9e8 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java @@ -34,7 +34,7 @@ import com.jayway.jsonpath.TypeRef; import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.ResolvableType; -import org.springframework.graphql.DefaultGraphQlRequest; +import org.springframework.graphql.support.DefaultGraphQlRequest; import org.springframework.graphql.GraphQlRequest; import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.GraphQlResponseError; diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlServiceTransport.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlServiceTransport.java index 1c911e50..51a3b6bf 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlServiceTransport.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlServiceTransport.java @@ -19,10 +19,9 @@ package org.springframework.graphql.test.tester; import reactor.core.publisher.Mono; -import org.springframework.graphql.GraphQlRequest; +import org.springframework.graphql.ExecutionGraphQlRequest; +import org.springframework.graphql.ExecutionGraphQlResponse; import org.springframework.graphql.GraphQlService; -import org.springframework.graphql.RequestInput; -import org.springframework.graphql.RequestOutput; import org.springframework.util.Assert; @@ -48,13 +47,8 @@ final class GraphQlServiceTransport extends AbstractDirectTransport { } @Override - protected Mono executeInternal(GraphQlRequest request) { - - RequestInput requestInput = new RequestInput( - request.getDocument(), request.getOperationName(), request.getVariables(), - idGenerator.generateId().toString(), null); - - return this.graphQlService.execute(requestInput); + protected Mono executeInternal(ExecutionGraphQlRequest request) { + return this.graphQlService.execute(request); } } diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/WebGraphQlHandlerTransport.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/WebGraphQlHandlerTransport.java index ed6a37f5..4b0ae189 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/WebGraphQlHandlerTransport.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/WebGraphQlHandlerTransport.java @@ -21,6 +21,8 @@ import java.net.URI; import reactor.core.publisher.Mono; +import org.springframework.graphql.ExecutionGraphQlRequest; +import org.springframework.graphql.ExecutionGraphQlResponse; import org.springframework.graphql.GraphQlRequest; import org.springframework.graphql.web.WebGraphQlHandler; import org.springframework.graphql.web.WebInput; @@ -75,9 +77,9 @@ final class WebGraphQlHandlerTransport extends AbstractDirectTransport { @Override - protected Mono executeInternal(GraphQlRequest request) { - return this.graphQlHandler.handleRequest( - new WebInput(this.url, this.headers, request.toMap(), idGenerator.generateId().toString(), null)); + protected Mono executeInternal(ExecutionGraphQlRequest request) { + WebInput input = new WebInput(this.url, this.headers, request.toMap(), idGenerator.generateId().toString(), null); + return this.graphQlHandler.handleRequest(input).cast(ExecutionGraphQlResponse.class); } } diff --git a/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterBuilderTests.java b/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterBuilderTests.java index 24a789d9..7de80353 100644 --- a/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterBuilderTests.java +++ b/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterBuilderTests.java @@ -48,13 +48,13 @@ public class GraphQlTesterBuilderTests extends GraphQlTesterTestSupport { GraphQlTester tester = builder.build(); tester.documentName("name").execute(); - assertThat(requestInput().getDocument()).isEqualTo(DOCUMENT); + assertThat(request().getDocument()).isEqualTo(DOCUMENT); // Mutate tester = tester.mutate().build(); tester.documentName("name").execute(); - assertThat(requestInput().getDocument()).isEqualTo(DOCUMENT); + assertThat(request().getDocument()).isEqualTo(DOCUMENT); } @Test @@ -74,7 +74,7 @@ public class GraphQlTesterBuilderTests extends GraphQlTesterTestSupport { .errors().verify() .path("me").pathDoesNotExist(); - assertThat(requestInput().getDocument()).contains(document); + assertThat(request().getDocument()).contains(document); } } diff --git a/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTestSupport.java b/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTestSupport.java index c570bcfc..bf48a4c9 100644 --- a/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTestSupport.java +++ b/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTestSupport.java @@ -29,9 +29,9 @@ import graphql.GraphQLError; import org.mockito.ArgumentCaptor; import reactor.core.publisher.Mono; +import org.springframework.graphql.ExecutionGraphQlRequest; import org.springframework.graphql.GraphQlService; -import org.springframework.graphql.RequestInput; -import org.springframework.graphql.RequestOutput; +import org.springframework.graphql.support.DefaultExecutionGraphQlResponse; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -46,7 +46,7 @@ public class GraphQlTesterTestSupport { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private final ArgumentCaptor inputCaptor = ArgumentCaptor.forClass(RequestInput.class); + private final ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ExecutionGraphQlRequest.class); private final GraphQlService graphQlService = mock(GraphQlService.class); @@ -63,8 +63,8 @@ public class GraphQlTesterTestSupport { return this.graphQlTesterBuilder; } - protected RequestInput requestInput() { - return this.inputCaptor.getValue(); + protected ExecutionGraphQlRequest request() { + return this.requestCaptor.getValue(); } @@ -83,8 +83,8 @@ public class GraphQlTesterTestSupport { ExecutionInput executionInput = ExecutionInput.newExecutionInput("{}").build(); ExecutionResult result = builder.build(); - given(this.graphQlService.execute(this.inputCaptor.capture())) - .willReturn(Mono.just(new RequestOutput(executionInput, result))); + given(this.graphQlService.execute(this.requestCaptor.capture())) + .willReturn(Mono.just(new DefaultExecutionGraphQlResponse(executionInput, result))); } private void serialize(String data, ExecutionResultImpl.Builder builder) { diff --git a/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java b/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java index fd2728b8..68e46f7a 100644 --- a/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java +++ b/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java @@ -26,8 +26,8 @@ import graphql.language.SourceLocation; import org.junit.jupiter.api.Test; import org.springframework.core.ParameterizedTypeReference; +import org.springframework.graphql.ExecutionGraphQlRequest; import org.springframework.graphql.GraphQlService; -import org.springframework.graphql.RequestInput; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -51,7 +51,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { response.path("me.friends").pathExists().valueExists(); response.path("hero").pathDoesNotExist().valueDoesNotExist(); - assertThat(requestInput().getDocument()).contains(document); + assertThat(request().getDocument()).contains(document); } @Test @@ -69,7 +69,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { .as("Path does not even exist") .hasMessageContaining("No value at JSON path \"$['data']['hero']"); - assertThat(requestInput().getDocument()).contains(document); + assertThat(request().getDocument()).contains(document); } @Test @@ -88,7 +88,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { .as("Extended fields should fail in strict mode") .hasMessageContaining("Unexpected: name"); - assertThat(requestInput().getDocument()).contains(document); + assertThat(request().getDocument()).contains(document); } @Test @@ -118,7 +118,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { .entity(new ParameterizedTypeReference>() {}) .isEqualTo(Collections.singletonMap("me", luke)); - assertThat(requestInput().getDocument()).contains(document); + assertThat(request().getDocument()).contains(document); } @Test @@ -153,7 +153,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { .entityList(new ParameterizedTypeReference() {}) .containsExactly(han, leia); - assertThat(requestInput().getDocument()).contains(document); + assertThat(request().getDocument()).contains(document); } @Test @@ -176,13 +176,13 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { response.path("hero").entity(MovieCharacter.class).isEqualTo(MovieCharacter.create("R2-D2")); - RequestInput input = requestInput(); - assertThat(input.getDocument()).contains(document); - assertThat(input.getOperationName()).isEqualTo("HeroNameAndFriends"); - assertThat(input.getVariables()).hasSize(3); - assertThat(input.getVariables()).containsEntry("episode", "JEDI"); - assertThat(input.getVariables()).containsEntry("foo", "bar"); - assertThat(input.getVariables()).containsEntry("keyOnly", null); + ExecutionGraphQlRequest request = request(); + assertThat(request.getDocument()).contains(document); + assertThat(request.getOperationName()).isEqualTo("HeroNameAndFriends"); + assertThat(request.getVariables()).hasSize(3); + assertThat(request.getVariables()).containsEntry("episode", "JEDI"); + assertThat(request.getVariables()).containsEntry("foo", "bar"); + assertThat(request.getVariables()).containsEntry("keyOnly", null); } @Test @@ -193,7 +193,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { graphQlTester().document(document).executeAndVerify(); - assertThat(requestInput().getDocument()).contains(document); + assertThat(request().getDocument()).contains(document); } @Test @@ -205,7 +205,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { assertThatThrownBy(() -> graphQlTester().document(document).executeAndVerify()) .hasMessageContaining("Response has 1 unexpected error(s)"); - assertThat(requestInput().getDocument()).contains(document); + assertThat(request().getDocument()).contains(document); } @Test @@ -217,7 +217,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { assertThatThrownBy(() -> graphQlTester().document(document).execute().path("me")) .hasMessageContaining("Response has 1 unexpected error(s)"); - assertThat(requestInput().getDocument()).contains(document); + assertThat(request().getDocument()).contains(document); } @Test @@ -236,7 +236,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { .verify()) .hasMessageContaining("Response has 1 unexpected error(s) of 2 total."); - assertThat(requestInput().getDocument()).contains(document); + assertThat(request().getDocument()).contains(document); } @Test @@ -255,7 +255,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { .path("me") .pathDoesNotExist(); - assertThat(requestInput().getDocument()).contains(document); + assertThat(request().getDocument()).contains(document); } @Test @@ -273,7 +273,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { .verify() .path("me").pathDoesNotExist(); - assertThat(requestInput().getDocument()).contains(document); + assertThat(request().getDocument()).contains(document); } @Test @@ -312,7 +312,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { }) .path("me").pathDoesNotExist(); - assertThat(requestInput().getDocument()).contains(document); + assertThat(request().getDocument()).contains(document); } } diff --git a/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/WebGraphQlTesterBuilderTests.java b/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/WebGraphQlTesterBuilderTests.java index 33fa63fc..3349ea62 100644 --- a/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/WebGraphQlTesterBuilderTests.java +++ b/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/WebGraphQlTesterBuilderTests.java @@ -34,7 +34,8 @@ import reactor.core.publisher.Mono; import org.springframework.core.ResolvableType; import org.springframework.core.codec.DecodingException; import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.graphql.RequestOutput; +import org.springframework.graphql.ExecutionGraphQlResponse; +import org.springframework.graphql.support.DefaultExecutionGraphQlResponse; import org.springframework.graphql.support.DocumentSource; import org.springframework.graphql.web.TestWebSocketClient; import org.springframework.graphql.web.TestWebSocketConnection; @@ -210,11 +211,11 @@ public class WebGraphQlTesterBuilderTests { private WebInput webInput; - private final Map responses = new HashMap<>(); + private final Map responses = new HashMap<>(); public WebBuilderSetup() { - RequestOutput defaultResponse = new RequestOutput( + ExecutionGraphQlResponse defaultResponse = new DefaultExecutionGraphQlResponse( ExecutionInput.newExecutionInput().query(DOCUMENT).build(), ExecutionResultImpl.newExecutionResult().build()); @@ -227,11 +228,11 @@ public class WebGraphQlTesterBuilderTests { } protected WebGraphQlHandler webGraphQlHandler() { - return WebGraphQlHandler.builder(requestInput -> { - String document = requestInput.getDocument(); - RequestOutput output = this.responses.get(document); - Assert.notNull(output, "Unexpected request: " + document); - return Mono.just(output); + return WebGraphQlHandler.builder(request -> { + String document = request.getDocument(); + ExecutionGraphQlResponse response = this.responses.get(document); + Assert.notNull(response, "Unexpected request: " + document); + return Mono.just(response); }) .interceptor((input, chain) -> { this.webInput = input; @@ -243,7 +244,7 @@ public class WebGraphQlTesterBuilderTests { @Override public void setMockResponse(String document, ExecutionResult result) { ExecutionInput executionInput = ExecutionInput.newExecutionInput().query(document).build(); - this.responses.put(document, new RequestOutput(executionInput, result)); + this.responses.put(document, new DefaultExecutionGraphQlResponse(executionInput, result)); } @Override diff --git a/spring-graphql/src/main/java/org/springframework/graphql/DefaultGraphQlResponseField.java b/spring-graphql/src/main/java/org/springframework/graphql/DefaultGraphQlResponseField.java deleted file mode 100644 index 9f922e29..00000000 --- a/spring-graphql/src/main/java/org/springframework/graphql/DefaultGraphQlResponseField.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2002-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; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - - -/** - * Default implementation of {@link GraphQlResponseField}. - * - * @author Rossen Stoyanchev - * @since 1.0.0 - */ -public class DefaultGraphQlResponseField implements GraphQlResponseField { - - private final GraphQlResponse response; - - private final String path; - - private final List parsedPath; - - @Nullable - private final Object value; - - private final List fieldErrors; - - - protected DefaultGraphQlResponseField(GraphQlResponse response, String path) { - - this.response = response; - this.path = path; - this.parsedPath = parsePath(path); - this.value = initFieldValue(this.parsedPath, response); - this.fieldErrors = initFieldErrors(path, response); - } - - private static List parsePath(String path) { - if (!StringUtils.hasText(path)) { - return Collections.emptyList(); - } - - String invalidPathMessage = "Invalid path: '" + path + "'"; - List dataPath = new ArrayList<>(); - - StringBuilder sb = new StringBuilder(); - boolean readingIndex = false; - - for (int i = 0; i < path.length(); i++) { - char c = path.charAt(i); - switch (c) { - case '.': - case '[': - Assert.isTrue(!readingIndex, invalidPathMessage); - break; - case ']': - i++; - Assert.isTrue(readingIndex, invalidPathMessage); - Assert.isTrue(i == path.length() || path.charAt(i) == '.', invalidPathMessage); - break; - default: - sb.append(c); - if (i < path.length() - 1) { - continue; - } - } - String token = sb.toString(); - Assert.hasText(token, invalidPathMessage); - dataPath.add(readingIndex ? Integer.parseInt(token) : token); - sb.delete(0, sb.length()); - - readingIndex = (c == '['); - } - - return dataPath; - } - - @Nullable - private static Object initFieldValue(List path, GraphQlResponse response) { - Object value = (response.isValid() ? response.getData() : null); - for (Object segment : path) { - if (value == null) { - return null; - } - if (segment instanceof String) { - Assert.isTrue(value instanceof Map, () -> "Invalid path " + path + ", data: " + response.getData()); - value = ((Map) value).getOrDefault(segment, null); - } - else { - Assert.isTrue(value instanceof List, () -> "Invalid path " + path + ", data: " + response.getData()); - int index = (int) segment; - value = (index < ((List) value).size() ? ((List) value).get(index) : null); - } - } - return value; - } - - /** - * Return field errors whose path starts with the given field path. - * @param path the field path to match - * @return errors whose path starts with the dataPath - */ - private static List initFieldErrors(String path, GraphQlResponse response) { - if (path.isEmpty() || response.getErrors().isEmpty()) { - return Collections.emptyList(); - } - return response.getErrors().stream() - .filter(error -> { - String errorPath = error.getPath(); - return !errorPath.isEmpty() && (errorPath.startsWith(path) || path.startsWith(errorPath)); - }) - .collect(Collectors.toList()); - } - - - @SuppressWarnings("unchecked") - protected R getResponse() { - return (R) this.response; - } - - @Override - public String getPath() { - return this.path; - } - - @Override - public List getParsedPath() { - return this.parsedPath; - } - - @Override - public boolean hasValue() { - return (this.value != null); - } - - @SuppressWarnings("unchecked") - @Override - public T getValue() { - return (T) this.value; - } - - @Override - public GraphQlResponseError getError() { - if (!hasValue()) { - if (!this.fieldErrors.isEmpty()) { - return this.fieldErrors.get(0); - } - if (!this.response.getErrors().isEmpty()) { - return this.response.getErrors().get(0); - } - // No errors, set to null by DataFetcher - } - return null; - } - - @Override - public List getErrors() { - return this.fieldErrors; - } - -} diff --git a/spring-graphql/src/main/java/org/springframework/graphql/ExecutionGraphQlRequest.java b/spring-graphql/src/main/java/org/springframework/graphql/ExecutionGraphQlRequest.java new file mode 100644 index 00000000..9b672875 --- /dev/null +++ b/spring-graphql/src/main/java/org/springframework/graphql/ExecutionGraphQlRequest.java @@ -0,0 +1,101 @@ +/* + * Copyright 2002-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; + +import java.util.Locale; +import java.util.function.BiFunction; + +import graphql.ExecutionInput; +import graphql.execution.ExecutionId; + +import org.springframework.lang.Nullable; + + +/** + * Implementation of {@link GraphQlRequest} for request handling through GraphQL + * Java with support for customizing the {@link ExecutionInput} passed into + * {@link graphql.GraphQL}. + * + * @author Rossen Stoyanchev + * @author Brian Clozel + * @since 1.0.0 + */ +public interface ExecutionGraphQlRequest extends GraphQlRequest { + + /** + * Return the transport assigned id for the request that in turn sets + * {@link ExecutionInput.Builder#executionId(ExecutionId) executionId}. + *

By default, the id is initialized as follows: + *

    + *
  • On WebFlux, this is the {@code ServerHttpRequest} id which correlates + * to WebFlux log messages. For Reactor Netty, it also correlates to server + * log messages. + *
  • On Spring MVC, the id is generated via + * {@link org.springframework.util.AlternativeJdkIdGenerator}, which does + * not correlate to anything, but is more efficient than the default + * {@link graphql.execution.ExecutionIdProvider} which relies on + * {@code UUID.randomUUID()}. + *
  • On WebSocket, the id is set to the message id of the {@code "subscribe"} + * message from the GraphQL over WebSocket protocol that is used to correlate + * request and response messages on the the WebSocket. + *
+ *

To override this id, use {@link #executionId(ExecutionId)} or configure + * {@link graphql.GraphQL} with an {@link graphql.execution.ExecutionIdProvider}. + * @return the request id + */ + String getId(); + + /** + * Configure the {@link ExecutionId} to set on + * {@link ExecutionInput#getExecutionId()}, overriding the transport assigned + * {@link #getId() id}. + * @param executionId the id to use + */ + void executionId(ExecutionId executionId); + + /** + * Return the configured {@link #executionId(ExecutionId) executionId}. + */ + @Nullable + ExecutionId getExecutionId(); + + /** + * Return the transport assigned locale value, if any. + */ + @Nullable + Locale getLocale(); + + /** + * Provide a {@code BiFunction} to help initialize the {@link ExecutionInput} + * passed to {@link graphql.GraphQL}. The {@code ExecutionInput} is first + * pre-populated with values from "this" {@code RequestInput}, and is then + * customized with the functions provided here. + * @param configurer a {@code BiFunction} that accepts the + * {@code ExecutionInput} initialized so far, and a builder to customize it. + */ + void configureExecutionInput(BiFunction configurer); + + /** + * Create the {@link ExecutionInput} to pass to {@link graphql.GraphQL}. + * passed to {@link graphql.GraphQL}. The {@code ExecutionInput} is populated + * with values from "this" {@code RequestInput}, and then customized with + * functions provided via {@link #configureExecutionInput(BiFunction)}. + * @return the resulting {@code ExecutionInput} + */ + ExecutionInput toExecutionInput(); + +} diff --git a/spring-graphql/src/main/java/org/springframework/graphql/ExecutionGraphQlResponse.java b/spring-graphql/src/main/java/org/springframework/graphql/ExecutionGraphQlResponse.java new file mode 100644 index 00000000..00891876 --- /dev/null +++ b/spring-graphql/src/main/java/org/springframework/graphql/ExecutionGraphQlResponse.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002-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; + +import graphql.ExecutionInput; +import graphql.ExecutionResult; + + +/** + * Implementation of {@link GraphQlResponse} that wraps the {@link ExecutionResult} + * returned from {@link graphql.GraphQL} to expose it as {@link GraphQlResponse}, + * also providing access to the {@link ExecutionInput} used for the request. + * + * @author Rossen Stoyanchev + * @since 1.0.0 + */ +public interface ExecutionGraphQlResponse extends GraphQlResponse { + + /** + * Return the {@link ExecutionInput} that was prepared through the + * {@link ExecutionGraphQlRequest} and passed to {@link graphql.GraphQL}. + */ + ExecutionInput getExecutionInput(); + + /** + * Return the {@link ExecutionResult} that was returned from the invocation + * to {@link graphql.GraphQL}. + */ + ExecutionResult getExecutionResult(); + +} diff --git a/spring-graphql/src/main/java/org/springframework/graphql/GraphQlResponse.java b/spring-graphql/src/main/java/org/springframework/graphql/GraphQlResponse.java index 636e9e45..11291576 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/GraphQlResponse.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/GraphQlResponse.java @@ -81,9 +81,7 @@ public interface GraphQlResponse { * decode its value; use {@link GraphQlResponseField#hasValue()} to check if * the field actually exists and has a value. */ - default GraphQlResponseField field(String path) { - return new DefaultGraphQlResponseField(this, path); - } + GraphQlResponseField field(String path); /** * Return implementor specific, protocol extensions, if any. diff --git a/spring-graphql/src/main/java/org/springframework/graphql/GraphQlService.java b/spring-graphql/src/main/java/org/springframework/graphql/GraphQlService.java index b63215da..738dc53f 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/GraphQlService.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/GraphQlService.java @@ -19,7 +19,7 @@ package org.springframework.graphql; import reactor.core.publisher.Mono; /** - * Strategy to execute a GraphQL request. + * Strategy to execute a GraphQL request by inoking GraphQL Java. * * @author Rossen Stoyanchev * @since 1.0.0 @@ -27,10 +27,10 @@ import reactor.core.publisher.Mono; public interface GraphQlService { /** - * Execute the GraphQL request and return the result. - * @param input container for GraphQL request input - * @return the result from execution + * Execute the request and return the response. + * @param request the request to execute + * @return the resulting response */ - Mono execute(RequestInput input); + Mono execute(ExecutionGraphQlRequest request); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientGraphQlResponse.java b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientGraphQlResponse.java index e6e88df9..7da9f122 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientGraphQlResponse.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientGraphQlResponse.java @@ -65,7 +65,7 @@ final class DefaultClientGraphQlResponse extends MapGraphQlResponse implements C @Override public ClientGraphQlResponseField field(String path) { - return new DefaultClientGraphQlResponseField(this, path); + return new DefaultClientGraphQlResponseField(this, super.field(path)); } @Override diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientGraphQlResponseField.java b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientGraphQlResponseField.java index 61fdc4ac..58178d66 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientGraphQlResponseField.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientGraphQlResponseField.java @@ -27,25 +27,63 @@ import org.springframework.core.codec.Encoder; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.core.io.buffer.DefaultDataBufferFactory; -import org.springframework.graphql.DefaultGraphQlResponseField; +import org.springframework.graphql.GraphQlResponseError; +import org.springframework.graphql.GraphQlResponseField; import org.springframework.util.MimeType; import org.springframework.util.MimeTypeUtils; /** - * Default implementation of {@link ClientGraphQlResponseField}. + * Default implementation of {@link ClientGraphQlResponseField} that wraps the + * field from {@link org.springframework.graphql.GraphQlResponse} and adds + * support for decoding. * * @author Rossen Stoyanchev * @since 1.0.0 */ -final class DefaultClientGraphQlResponseField extends DefaultGraphQlResponseField implements ClientGraphQlResponseField { +final class DefaultClientGraphQlResponseField implements ClientGraphQlResponseField { + + private final DefaultClientGraphQlResponse response; + + private final GraphQlResponseField field; - DefaultClientGraphQlResponseField(DefaultClientGraphQlResponse response, String path) { - super(response, path); + DefaultClientGraphQlResponseField(DefaultClientGraphQlResponse response, GraphQlResponseField field) { + this.response = response; + this.field = field; } + @Override + public boolean hasValue() { + return this.field.hasValue(); + } + + @Override + public String getPath() { + return this.field.getPath(); + } + + @Override + public List getParsedPath() { + return this.field.getParsedPath(); + } + + @Override + public T getValue() { + return this.field.getValue(); + } + + @Override + public GraphQlResponseError getError() { + return this.field.getError(); + } + + @Override + public List getErrors() { + return this.field.getErrors(); + } + @Override public D toEntity(Class entityType) { return toEntity(ResolvableType.forType(entityType)); @@ -68,19 +106,18 @@ final class DefaultClientGraphQlResponseField extends DefaultGraphQlResponseFiel @SuppressWarnings({"unchecked", "ConstantConditions"}) private T toEntity(ResolvableType targetType) { - DefaultClientGraphQlResponse response = getResponse(); if (!hasValue()) { - throw new FieldAccessException(response, this); + throw new FieldAccessException(this.response, this); } DataBufferFactory bufferFactory = DefaultDataBufferFactory.sharedInstance; MimeType mimeType = MimeTypeUtils.APPLICATION_JSON; Map hints = Collections.emptyMap(); - DataBuffer buffer = ((Encoder) response.getEncoder()).encodeValue( + DataBuffer buffer = ((Encoder) this.response.getEncoder()).encodeValue( (T) getValue(), bufferFactory, ResolvableType.forInstance(getValue()), mimeType, hints); - return ((Decoder) response.getDecoder()).decode(buffer, targetType, mimeType, hints); + return ((Decoder) this.response.getDecoder()).decode(buffer, targetType, mimeType, hints); } } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultGraphQlClient.java b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultGraphQlClient.java index 02fbb826..486060b2 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultGraphQlClient.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultGraphQlClient.java @@ -27,7 +27,7 @@ import reactor.core.publisher.Mono; import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.codec.Decoder; import org.springframework.core.codec.Encoder; -import org.springframework.graphql.DefaultGraphQlRequest; +import org.springframework.graphql.support.DefaultGraphQlRequest; import org.springframework.graphql.GraphQlRequest; import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.support.DocumentSource; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/MapGraphQlResponse.java b/spring-graphql/src/main/java/org/springframework/graphql/client/MapGraphQlResponse.java index 70b17503..8ecd5ab1 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/MapGraphQlResponse.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/MapGraphQlResponse.java @@ -27,6 +27,7 @@ import graphql.language.SourceLocation; import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.GraphQlResponseError; +import org.springframework.graphql.support.AbstractGraphQlResponse; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -37,7 +38,7 @@ import org.springframework.util.ObjectUtils; * @author Rossen Stoyanchev * @since 1.0.0 */ -class MapGraphQlResponse implements GraphQlResponse { +class MapGraphQlResponse extends AbstractGraphQlResponse implements GraphQlResponse { private final Map responseMap; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/ExecutionGraphQlService.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/ExecutionGraphQlService.java index fd6afdaf..61e0fbdd 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/ExecutionGraphQlService.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/ExecutionGraphQlService.java @@ -27,9 +27,10 @@ import graphql.execution.ExecutionIdProvider; import org.dataloader.DataLoaderRegistry; import reactor.core.publisher.Mono; +import org.springframework.graphql.ExecutionGraphQlRequest; +import org.springframework.graphql.ExecutionGraphQlResponse; import org.springframework.graphql.GraphQlService; -import org.springframework.graphql.RequestInput; -import org.springframework.graphql.RequestOutput; +import org.springframework.graphql.support.DefaultExecutionGraphQlResponse; /** * {@link GraphQlService} that uses a {@link GraphQlSource} to obtain a @@ -69,16 +70,16 @@ public class ExecutionGraphQlService implements GraphQlService { @Override - public final Mono execute(RequestInput requestInput) { + public final Mono execute(ExecutionGraphQlRequest request) { return Mono.deferContextual((contextView) -> { - if (!this.isDefaultExecutionIdProvider && requestInput.getExecutionId() == null) { - requestInput.configureExecutionInput(RESET_EXECUTION_ID_CONFIGURER); + if (!this.isDefaultExecutionIdProvider && request.getExecutionId() == null) { + request.configureExecutionInput(RESET_EXECUTION_ID_CONFIGURER); } - ExecutionInput executionInput = requestInput.toExecutionInput(); + ExecutionInput executionInput = request.toExecutionInput(); ReactorContextManager.setReactorContext(contextView, executionInput); ExecutionInput updatedExecutionInput = registerDataLoaders(executionInput); return Mono.fromFuture(this.graphQlSource.graphQl().executeAsync(updatedExecutionInput)) - .map(result -> new RequestOutput(updatedExecutionInput, result)); + .map(result -> new DefaultExecutionGraphQlResponse(updatedExecutionInput, result)); }); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/support/AbstractGraphQlResponse.java b/spring-graphql/src/main/java/org/springframework/graphql/support/AbstractGraphQlResponse.java new file mode 100644 index 00000000..f01dbef9 --- /dev/null +++ b/spring-graphql/src/main/java/org/springframework/graphql/support/AbstractGraphQlResponse.java @@ -0,0 +1,195 @@ +/* + * Copyright 2002-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.support; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.springframework.graphql.GraphQlResponse; +import org.springframework.graphql.GraphQlResponseError; +import org.springframework.graphql.GraphQlResponseField; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + + +/** + * Base class for {@link GraphQlResponse} that pre-implements the ability to + * access a {@link GraphQlResponseField}. + * + * @author Rossen Stoyanchev + * @since 1.0.0 + */ +public abstract class AbstractGraphQlResponse implements GraphQlResponse { + + + @Override + public GraphQlResponseField field(String path) { + return new DefaultGraphQlResponseField(this, path); + } + + + /** + * Default implementation of {@link GraphQlResponseField}. + */ + private static class DefaultGraphQlResponseField implements GraphQlResponseField { + + private final GraphQlResponse response; + + private final String path; + + private final List parsedPath; + + @Nullable + private final Object value; + + private final List fieldErrors; + + + DefaultGraphQlResponseField(GraphQlResponse response, String path) { + this.response = response; + this.path = path; + this.parsedPath = parsePath(path); + this.value = initFieldValue(this.parsedPath, response); + this.fieldErrors = initFieldErrors(path, response); + } + + private static List parsePath(String path) { + if (!StringUtils.hasText(path)) { + return Collections.emptyList(); + } + + String invalidPathMessage = "Invalid path: '" + path + "'"; + List dataPath = new ArrayList<>(); + + StringBuilder sb = new StringBuilder(); + boolean readingIndex = false; + + for (int i = 0; i < path.length(); i++) { + char c = path.charAt(i); + switch (c) { + case '.': + case '[': + Assert.isTrue(!readingIndex, invalidPathMessage); + break; + case ']': + i++; + Assert.isTrue(readingIndex, invalidPathMessage); + Assert.isTrue(i == path.length() || path.charAt(i) == '.', invalidPathMessage); + break; + default: + sb.append(c); + if (i < path.length() - 1) { + continue; + } + } + String token = sb.toString(); + Assert.hasText(token, invalidPathMessage); + dataPath.add(readingIndex ? Integer.parseInt(token) : token); + sb.delete(0, sb.length()); + + readingIndex = (c == '['); + } + + return dataPath; + } + + @Nullable + private static Object initFieldValue(List path, GraphQlResponse response) { + Object value = (response.isValid() ? response.getData() : null); + for (Object segment : path) { + if (value == null) { + return null; + } + if (segment instanceof String) { + Assert.isTrue(value instanceof Map, () -> "Invalid path " + path + ", data: " + response.getData()); + value = ((Map) value).getOrDefault(segment, null); + } + else { + Assert.isTrue(value instanceof List, () -> "Invalid path " + path + ", data: " + response.getData()); + int index = (int) segment; + value = (index < ((List) value).size() ? ((List) value).get(index) : null); + } + } + return value; + } + + /** + * Return field errors whose path starts with the given field path. + * @param path the field path to match + * @return errors whose path starts with the dataPath + */ + private static List initFieldErrors(String path, GraphQlResponse response) { + if (path.isEmpty() || response.getErrors().isEmpty()) { + return Collections.emptyList(); + } + return response.getErrors().stream() + .filter(error -> { + String errorPath = error.getPath(); + return !errorPath.isEmpty() && (errorPath.startsWith(path) || path.startsWith(errorPath)); + }) + .collect(Collectors.toList()); + } + + + @Override + public String getPath() { + return this.path; + } + + @Override + public List getParsedPath() { + return this.parsedPath; + } + + @Override + public boolean hasValue() { + return (this.value != null); + } + + @SuppressWarnings("unchecked") + @Override + public T getValue() { + return (T) this.value; + } + + @Override + public GraphQlResponseError getError() { + if (!hasValue()) { + if (!this.fieldErrors.isEmpty()) { + return this.fieldErrors.get(0); + } + if (!this.response.getErrors().isEmpty()) { + return this.response.getErrors().get(0); + } + // No errors, set to null by DataFetcher + } + return null; + } + + @Override + public List getErrors() { + return this.fieldErrors; + } + + } + +} diff --git a/spring-graphql/src/main/java/org/springframework/graphql/RequestInput.java b/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultExecutionGraphQlRequest.java similarity index 61% rename from spring-graphql/src/main/java/org/springframework/graphql/RequestInput.java rename to spring-graphql/src/main/java/org/springframework/graphql/support/DefaultExecutionGraphQlRequest.java index c53aae7b..4def027d 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/RequestInput.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultExecutionGraphQlRequest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.graphql; +package org.springframework.graphql.support; import java.util.ArrayList; import java.util.List; @@ -25,6 +25,8 @@ import java.util.function.BiFunction; import graphql.ExecutionInput; import graphql.execution.ExecutionId; +import org.springframework.graphql.ExecutionGraphQlRequest; +import org.springframework.graphql.GraphQlRequest; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -41,7 +43,7 @@ import org.springframework.util.Assert; * @author Brian Clozel * @since 1.0.0 */ -public class RequestInput extends DefaultGraphQlRequest { +public class DefaultExecutionGraphQlRequest extends DefaultGraphQlRequest implements ExecutionGraphQlRequest { private final String id; @@ -62,7 +64,7 @@ public class RequestInput extends DefaultGraphQlRequest { * @param id the request id, to be used as the {@link ExecutionId} * @param locale the locale associated with the request */ - public RequestInput( + public DefaultExecutionGraphQlRequest( String document, @Nullable String operationName, @Nullable Map variables, String id, @Nullable Locale locale) { @@ -73,76 +75,35 @@ public class RequestInput extends DefaultGraphQlRequest { } - /** - * Return the transport assigned id for the request which is then used to set - * {@link ExecutionInput.Builder#executionId(ExecutionId) executionId}. - * The is initialized as follows: - *
    - *
  • For WebFlux, this is the {@code ServerHttpRequest} id which correlates - * to WebFlux log messages. For Reactor Netty, it also correlates to server - * log messages. - *
  • For Spring MVC, the id is generated via - * {@link org.springframework.util.AlternativeJdkIdGenerator}, which does - * not correlate to anything, but is more efficient than the default - * {@link graphql.execution.ExecutionIdProvider} which relies on - * {@code UUID.randomUUID()}. - *
  • For WebSocket, this is the GraphQL over WebSocket {@code "subscribe"} - * message id, which correlates to WebSocket messages. - *
- *

To override this id, use {@link #executionId(ExecutionId)} or configure - * {@link graphql.GraphQL} with an {@link graphql.execution.ExecutionIdProvider}. - * @return the request id - */ + @Override public String getId() { return this.id; } - /** - * Configure the {@link ExecutionId} to set on - * {@link ExecutionInput#getExecutionId()}, overriding the transport assigned - * {@link #getId() id}. - * @param executionId the id to use - */ + @Override public void executionId(ExecutionId executionId) { Assert.notNull(executionId, "executionId is required"); this.executionId = executionId; } - /** - * Return the configured {@link #executionId(ExecutionId) executionId}. - */ + @Override @Nullable public ExecutionId getExecutionId() { return this.executionId; } - /** - * Return the transport assigned locale value, if any. - */ + @Override @Nullable public Locale getLocale() { return this.locale; } - /** - * Provide a {@code BiFunction} to help initialize the {@link ExecutionInput} - * passed to {@link graphql.GraphQL}. The {@code ExecutionInput} is first - * pre-populated with values from "this" {@code RequestInput}, and is then - * customized with the functions provided here. - * @param configurer a {@code BiFunction} that accepts the - * {@code ExecutionInput} initialized so far, and a builder to customize it. - */ + @Override public void configureExecutionInput(BiFunction configurer) { this.executionInputConfigurers.add(configurer); } - /** - * Create the {@link ExecutionInput} to pass to {@link graphql.GraphQL}. - * passed to {@link graphql.GraphQL}. The {@code ExecutionInput} is populated - * with values from "this" {@code RequestInput}, and then customized with - * functions provided via {@link #configureExecutionInput(BiFunction)}. - * @return the resulting {@code ExecutionInput} - */ + @Override public ExecutionInput toExecutionInput() { ExecutionInput.Builder inputBuilder = ExecutionInput.newExecutionInput() .query(getDocument()) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/RequestOutput.java b/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultExecutionGraphQlResponse.java similarity index 81% rename from spring-graphql/src/main/java/org/springframework/graphql/RequestOutput.java rename to spring-graphql/src/main/java/org/springframework/graphql/support/DefaultExecutionGraphQlResponse.java index 22a4917e..ca3b670c 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/RequestOutput.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultExecutionGraphQlResponse.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.graphql; +package org.springframework.graphql.support; import java.util.Collections; import java.util.List; @@ -26,6 +26,9 @@ import graphql.ExecutionResult; import graphql.GraphQLError; import graphql.language.SourceLocation; +import org.springframework.graphql.ExecutionGraphQlResponse; +import org.springframework.graphql.GraphQlResponse; +import org.springframework.graphql.GraphQlResponseError; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -37,7 +40,7 @@ import org.springframework.util.Assert; * @author Rossen Stoyanchev * @since 1.0.0 */ -public class RequestOutput implements GraphQlResponse { +public class DefaultExecutionGraphQlResponse extends AbstractGraphQlResponse implements ExecutionGraphQlResponse { private final ExecutionInput input; @@ -47,9 +50,9 @@ public class RequestOutput implements GraphQlResponse { /** * Constructor to create initial instance. */ - public RequestOutput(ExecutionInput input, ExecutionResult result) { - Assert.notNull(input, "ExecutionInput is required."); - Assert.notNull(result, "ExecutionResult is required."); + public DefaultExecutionGraphQlResponse(ExecutionInput input, ExecutionResult result) { + Assert.notNull(input, "ExecutionInput is required"); + Assert.notNull(result, "ExecutionResult is required"); this.input = input; this.result = result; } @@ -57,20 +60,18 @@ public class RequestOutput implements GraphQlResponse { /** * Constructor to re-wrap from transport specific subclass. */ - protected RequestOutput(RequestOutput requestOutput) { - this(requestOutput.getExecutionInput(), requestOutput.result); + protected DefaultExecutionGraphQlResponse(ExecutionGraphQlResponse response) { + this(response.getExecutionInput(), response.getExecutionResult()); } - /** - * Return the {@link ExecutionInput} that was prepared from the - * {@link RequestInput} and passed to {@link graphql.GraphQL}. - */ + @Override public ExecutionInput getExecutionInput() { return this.input; } - protected ExecutionResult getExecutionResult() { + @Override + public ExecutionResult getExecutionResult() { return this.result; } @@ -85,10 +86,12 @@ public class RequestOutput implements GraphQlResponse { return this.result.getData(); } + @Override public List getErrors() { return this.result.getErrors().stream().map(Error::new).collect(Collectors.toList()); } + @Override public Map getExtensions() { return (this.result.getExtensions() != null ? this.result.getExtensions() : Collections.emptyMap()); } @@ -150,5 +153,4 @@ public class RequestOutput implements GraphQlResponse { } - } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/DefaultGraphQlRequest.java b/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultGraphQlRequest.java similarity index 97% rename from spring-graphql/src/main/java/org/springframework/graphql/DefaultGraphQlRequest.java rename to spring-graphql/src/main/java/org/springframework/graphql/support/DefaultGraphQlRequest.java index bec021a4..14b3ef7e 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/DefaultGraphQlRequest.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultGraphQlRequest.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package org.springframework.graphql; +package org.springframework.graphql.support; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; +import org.springframework.graphql.GraphQlRequest; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/web/WebInput.java b/spring-graphql/src/main/java/org/springframework/graphql/web/WebInput.java index 2dd0d783..15ceb19b 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/web/WebInput.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/web/WebInput.java @@ -20,7 +20,7 @@ import java.net.URI; import java.util.Locale; import java.util.Map; -import org.springframework.graphql.RequestInput; +import org.springframework.graphql.support.DefaultExecutionGraphQlRequest; import org.springframework.http.HttpHeaders; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -38,7 +38,7 @@ import org.springframework.web.util.UriComponentsBuilder; * @author Rossen Stoyanchev * @since 1.0.0 */ -public class WebInput extends RequestInput { +public class WebInput extends DefaultExecutionGraphQlRequest { private final UriComponents uri; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/web/WebOutput.java b/spring-graphql/src/main/java/org/springframework/graphql/web/WebOutput.java index 6cf7b922..5d50a812 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/web/WebOutput.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/web/WebOutput.java @@ -20,15 +20,14 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; -import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.ExecutionResultImpl; import graphql.GraphQLError; -import org.springframework.graphql.RequestOutput; +import org.springframework.graphql.ExecutionGraphQlResponse; +import org.springframework.graphql.support.DefaultExecutionGraphQlResponse; import org.springframework.http.HttpHeaders; import org.springframework.lang.Nullable; -import org.springframework.util.Assert; /** * Decorate an {@link ExecutionResult}, provide a way to {@link #transform(Consumer) @@ -38,24 +37,23 @@ import org.springframework.util.Assert; * @author Rossen Stoyanchev * @since 1.0.0 */ -public class WebOutput extends RequestOutput { +public class WebOutput extends DefaultExecutionGraphQlResponse { private final HttpHeaders responseHeaders; /** - * Create an instance from the given {@link RequestOutput}. - * @param requestOutput the output from an executed request + * Create an instance that wraps the given {@link ExecutionGraphQlResponse}. + * @param response the response to wrap */ - public WebOutput(RequestOutput requestOutput) { - super(requestOutput); + public WebOutput(ExecutionGraphQlResponse response) { + super(response); this.responseHeaders = new HttpHeaders(); } - private WebOutput(ExecutionInput executionInput, ExecutionResult executionResult, HttpHeaders headers) { - super(executionInput, executionResult); - Assert.notNull(headers, "HttpHeaders is required"); - this.responseHeaders = headers; + private WebOutput(WebOutput original, ExecutionResult executionResult) { + super(original.getExecutionInput(), executionResult); + this.responseHeaders = original.getResponseHeaders(); } @@ -90,11 +88,11 @@ public class WebOutput extends RequestOutput { private final WebOutput original; - private final ExecutionResultImpl.Builder builder; + private final ExecutionResultImpl.Builder executionResultBuilder; private Builder(WebOutput original) { this.original = original; - this.builder = ExecutionResultImpl.newExecutionResult().from(original.getExecutionResult()); + this.executionResultBuilder = ExecutionResultImpl.newExecutionResult().from(original.getExecutionResult()); } /** @@ -103,7 +101,7 @@ public class WebOutput extends RequestOutput { * @return the current builder */ public Builder data(Object data) { - this.builder.data(data); + this.executionResultBuilder.data(data); return this; } @@ -114,7 +112,7 @@ public class WebOutput extends RequestOutput { * @return the current builder */ public Builder errors(@Nullable List errors) { - this.builder.errors(errors); + this.executionResultBuilder.errors(errors); return this; } @@ -125,13 +123,12 @@ public class WebOutput extends RequestOutput { * @return the current builder */ public Builder extensions(@Nullable Map extensions) { - this.builder.extensions(extensions); + this.executionResultBuilder.extensions(extensions); return this; } public WebOutput build() { - return new WebOutput(this.original.getExecutionInput(), this.builder.build(), - this.original.getResponseHeaders()); + return new WebOutput(this.original, this.executionResultBuilder.build()); } } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/web/support/GraphQlMessage.java b/spring-graphql/src/main/java/org/springframework/graphql/web/support/GraphQlMessage.java index 6c56e4e8..f52918cc 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/web/support/GraphQlMessage.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/web/support/GraphQlMessage.java @@ -22,8 +22,8 @@ import java.util.Map; import graphql.GraphQLError; +import org.springframework.graphql.ExecutionGraphQlResponse; import org.springframework.graphql.GraphQlRequest; -import org.springframework.graphql.RequestOutput; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -179,8 +179,8 @@ public class GraphQlMessage { * @param id unique request id * @param output the output to obtain the result map from */ - public static GraphQlMessage next(String id, RequestOutput output) { - Assert.notNull(output, "'RequestOutput' is required"); + public static GraphQlMessage next(String id, ExecutionGraphQlResponse output) { + Assert.notNull(output, "ExecutionGraphQlResponse is required"); return next(id, output.toMap()); } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/RequestInputTests.java b/spring-graphql/src/test/java/org/springframework/graphql/DefaultExecutionGraphQlRequestTests.java similarity index 64% rename from spring-graphql/src/test/java/org/springframework/graphql/RequestInputTests.java rename to spring-graphql/src/test/java/org/springframework/graphql/DefaultExecutionGraphQlRequestTests.java index 333f44c1..77f886f7 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/RequestInputTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/DefaultExecutionGraphQlRequestTests.java @@ -19,28 +19,31 @@ package org.springframework.graphql; import graphql.execution.ExecutionId; import org.junit.jupiter.api.Test; +import org.springframework.graphql.support.DefaultExecutionGraphQlRequest; + import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link RequestInput}. + * Tests for {@link DefaultExecutionGraphQlRequest}. * * @author Brian Clozel */ -class RequestInputTests { +class DefaultExecutionGraphQlRequestTests { - private final RequestInput requestInput = new RequestInput("greeting", "Greeting", null, "id", null); + private final DefaultExecutionGraphQlRequest request = + new DefaultExecutionGraphQlRequest("greeting", "Greeting", null, "id", null); @Test void shouldUseRequestId() { - assertThat(this.requestInput.toExecutionInput().getExecutionId()).isEqualTo(ExecutionId.from("id")); + assertThat(this.request.toExecutionInput().getExecutionId()).isEqualTo(ExecutionId.from("id")); } @Test void shouldUseExecutionId() { ExecutionId customId = ExecutionId.from("customId"); - this.requestInput.executionId(customId); - assertThat(this.requestInput.toExecutionInput().getExecutionId()).isEqualTo(customId); + this.request.executionId(customId); + assertThat(this.request.toExecutionInput().getExecutionId()).isEqualTo(customId); } } \ No newline at end of file diff --git a/spring-graphql/src/test/java/org/springframework/graphql/ResponseHelper.java b/spring-graphql/src/test/java/org/springframework/graphql/ResponseHelper.java index 98e982aa..e2e2ab65 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/ResponseHelper.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/ResponseHelper.java @@ -142,10 +142,10 @@ public class ResponseHelper { return forResult(result); } - public static ResponseHelper forResponse(Mono outputMono) { - RequestOutput output = outputMono.block(Duration.ofSeconds(5)); - assertThat(output).isNotNull(); - return forResult(output.getExecutionResult()); + public static ResponseHelper forResponse(Mono responseMono) { + ExecutionGraphQlResponse response = responseMono.block(Duration.ofSeconds(5)); + assertThat(response).isNotNull(); + return forResult(response.getExecutionResult()); } public static Flux forSubscription(ExecutionResult result) { @@ -155,10 +155,10 @@ public class ResponseHelper { } @SuppressWarnings("BlockingMethodInNonBlockingContext") - public static Flux forSubscription(Mono resultMono) { - RequestOutput output = resultMono.block(Duration.ofSeconds(5)); - assertThat(output).isNotNull(); - return forSubscription(output.getExecutionResult()); + public static Flux forSubscription(Mono resultMono) { + ExecutionGraphQlResponse response = resultMono.block(Duration.ofSeconds(5)); + assertThat(response).isNotNull(); + return forSubscription(response.getExecutionResult()); } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/client/DefaultGraphQlClientResponseTests.java b/spring-graphql/src/test/java/org/springframework/graphql/client/DefaultGraphQlClientResponseTests.java index 982b3319..e00a1687 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/client/DefaultGraphQlClientResponseTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/client/DefaultGraphQlClientResponseTests.java @@ -28,7 +28,7 @@ import graphql.execution.ResultPath; import org.junit.jupiter.api.Test; import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.graphql.DefaultGraphQlRequest; +import org.springframework.graphql.support.DefaultGraphQlRequest; import org.springframework.graphql.GraphQlResponseError; import org.springframework.http.codec.json.Jackson2JsonDecoder; import org.springframework.http.codec.json.Jackson2JsonEncoder; diff --git a/spring-graphql/src/test/java/org/springframework/graphql/client/GraphQlClientTestSupport.java b/spring-graphql/src/test/java/org/springframework/graphql/client/GraphQlClientTestSupport.java index 39b2c885..c716ea35 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/client/GraphQlClientTestSupport.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/client/GraphQlClientTestSupport.java @@ -28,7 +28,7 @@ import graphql.GraphQLError; import org.mockito.ArgumentCaptor; import reactor.core.publisher.Mono; -import org.springframework.graphql.DefaultGraphQlRequest; +import org.springframework.graphql.support.DefaultGraphQlRequest; import org.springframework.graphql.GraphQlRequest; import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; diff --git a/spring-graphql/src/test/java/org/springframework/graphql/client/GraphQlClientTests.java b/spring-graphql/src/test/java/org/springframework/graphql/client/GraphQlClientTests.java index 7f9daabd..dc88f66c 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/client/GraphQlClientTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/client/GraphQlClientTests.java @@ -30,7 +30,7 @@ import graphql.validation.ValidationErrorType; import org.junit.jupiter.api.Test; import org.springframework.core.ParameterizedTypeReference; -import org.springframework.graphql.DefaultGraphQlRequest; +import org.springframework.graphql.support.DefaultGraphQlRequest; import org.springframework.graphql.GraphQlRequest; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-graphql/src/test/java/org/springframework/graphql/client/MockGraphQlWebSocketServer.java b/spring-graphql/src/test/java/org/springframework/graphql/client/MockGraphQlWebSocketServer.java index e66ce1c8..6bfe0be1 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/client/MockGraphQlWebSocketServer.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/client/MockGraphQlWebSocketServer.java @@ -29,6 +29,7 @@ import reactor.core.publisher.Mono; import org.springframework.graphql.GraphQlRequest; import org.springframework.graphql.GraphQlResponse; +import org.springframework.graphql.support.DefaultGraphQlRequest; import org.springframework.graphql.web.support.GraphQlMessage; import org.springframework.lang.Nullable; import org.springframework.web.reactive.socket.WebSocketHandler; @@ -154,7 +155,7 @@ public final class MockGraphQlWebSocketServer implements WebSocketHandler { private Exchange(String operation) { - this.request = new GraphQlRequest(operation); + this.request = new DefaultGraphQlRequest(operation); } @Override diff --git a/spring-graphql/src/test/java/org/springframework/graphql/client/MockWebSocketGraphQlTransportTests.java b/spring-graphql/src/test/java/org/springframework/graphql/client/MockWebSocketGraphQlTransportTests.java index 92262164..665b9eb8 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/client/MockWebSocketGraphQlTransportTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/client/MockWebSocketGraphQlTransportTests.java @@ -31,7 +31,7 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -import org.springframework.graphql.DefaultGraphQlRequest; +import org.springframework.graphql.support.DefaultGraphQlRequest; import org.springframework.graphql.GraphQlRequest; import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.GraphQlResponseError; diff --git a/spring-graphql/src/test/java/org/springframework/graphql/client/WebGraphQlClientBuilderTests.java b/spring-graphql/src/test/java/org/springframework/graphql/client/WebGraphQlClientBuilderTests.java index 52d3e3d2..adadb893 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/client/WebGraphQlClientBuilderTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/client/WebGraphQlClientBuilderTests.java @@ -34,7 +34,8 @@ import reactor.core.publisher.Mono; import org.springframework.core.ResolvableType; import org.springframework.core.codec.DecodingException; import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.graphql.RequestOutput; +import org.springframework.graphql.ExecutionGraphQlResponse; +import org.springframework.graphql.support.DefaultExecutionGraphQlResponse; import org.springframework.graphql.support.DocumentSource; import org.springframework.graphql.web.TestWebSocketClient; import org.springframework.graphql.web.TestWebSocketConnection; @@ -223,11 +224,11 @@ public class WebGraphQlClientBuilderTests { private WebInput webInput; - private final Map responses = new HashMap<>(); + private final Map responses = new HashMap<>(); public AbstractBuilderSetup() { - RequestOutput defaultResponse = new RequestOutput( + ExecutionGraphQlResponse defaultResponse = new DefaultExecutionGraphQlResponse( ExecutionInput.newExecutionInput().query(DOCUMENT).build(), ExecutionResultImpl.newExecutionResult().build()); @@ -235,11 +236,11 @@ public class WebGraphQlClientBuilderTests { } protected WebGraphQlHandler webGraphQlHandler() { - return WebGraphQlHandler.builder(requestInput -> { - String document = requestInput.getDocument(); - RequestOutput output = this.responses.get(document); - Assert.notNull(output, "Unexpected request: " + document); - return Mono.just(output); + return WebGraphQlHandler.builder(request -> { + String document = request.getDocument(); + ExecutionGraphQlResponse response = this.responses.get(document); + Assert.notNull(response, "Unexpected request: " + document); + return Mono.just(response); }) .interceptor((input, chain) -> { this.webInput = input; @@ -251,7 +252,7 @@ public class WebGraphQlClientBuilderTests { @Override public void setMockResponse(String document, ExecutionResult result) { ExecutionInput executionInput = ExecutionInput.newExecutionInput().query(document).build(); - this.responses.put(document, new RequestOutput(executionInput, result)); + this.responses.put(document, new DefaultExecutionGraphQlResponse(executionInput, result)); } @Override diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingInvocationTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingInvocationTests.java index b2d61967..6d23982d 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingInvocationTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingInvocationTests.java @@ -28,9 +28,9 @@ import org.junit.jupiter.params.provider.MethodSource; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import org.springframework.graphql.ExecutionGraphQlResponse; import org.springframework.graphql.ResponseHelper; -import org.springframework.graphql.RequestOutput; -import org.springframework.graphql.TestRequestInput; +import org.springframework.graphql.TestExecutionRequest; import org.springframework.graphql.data.method.annotation.BatchMapping; import org.springframework.stereotype.Controller; @@ -70,10 +70,10 @@ public class BatchMappingInvocationTests extends BatchMappingTestSupport { " }" + "}"; - Mono outputMono = createGraphQlService(controller) - .execute(TestRequestInput.forDocument(query)); + Mono responseMono = createGraphQlService(controller) + .execute(TestExecutionRequest.forDocument(query)); - List actualCourses = ResponseHelper.forResponse(outputMono).toList("courses", Course.class); + List actualCourses = ResponseHelper.forResponse(responseMono).toList("courses", Course.class); List courses = Course.allCourses(); assertThat(actualCourses).hasSize(courses.size()); @@ -103,10 +103,10 @@ public class BatchMappingInvocationTests extends BatchMappingTestSupport { " }" + "}"; - Mono outputMono = createGraphQlService(controller) - .execute(TestRequestInput.forDocument(document)); + Mono responseMono = createGraphQlService(controller) + .execute(TestExecutionRequest.forDocument(document)); - List actualCourses = ResponseHelper.forResponse(outputMono).toList("courses", Course.class); + List actualCourses = ResponseHelper.forResponse(responseMono).toList("courses", Course.class); List courses = Course.allCourses(); assertThat(actualCourses).hasSize(courses.size()); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingPrincipalMethodArgumentResolverTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingPrincipalMethodArgumentResolverTests.java index 4cae1be2..0e12d127 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingPrincipalMethodArgumentResolverTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingPrincipalMethodArgumentResolverTests.java @@ -30,9 +30,9 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.util.context.Context; +import org.springframework.graphql.ExecutionGraphQlResponse; import org.springframework.graphql.ResponseHelper; -import org.springframework.graphql.RequestOutput; -import org.springframework.graphql.TestRequestInput; +import org.springframework.graphql.TestExecutionRequest; import org.springframework.graphql.data.method.annotation.BatchMapping; import org.springframework.graphql.execution.ReactorContextManager; import org.springframework.graphql.execution.SecurityContextThreadLocalAccessor; @@ -92,14 +92,14 @@ public class BatchMappingPrincipalMethodArgumentResolverTests extends BatchMappi } private void testBatchLoading(PrincipalCourseController controller, Function contextWriter) { - Mono outputMono = Mono.delay(Duration.ofMillis(10)) + Mono responseMono = Mono.delay(Duration.ofMillis(10)) .flatMap(aLong -> { String document = "{ courses { id instructor { id } } }"; - return createGraphQlService(controller).execute(TestRequestInput.forDocument(document)); + return createGraphQlService(controller).execute(TestExecutionRequest.forDocument(document)); }) .contextWrite(contextWriter); - List actualCourses = ResponseHelper.forResponse(outputMono).toList("courses", Course.class); + List actualCourses = ResponseHelper.forResponse(responseMono).toList("courses", Course.class); List courses = Course.allCourses(); assertThat(actualCourses).hasSize(courses.size()); for (int i = 0; i < courses.size(); i++) { diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingInvocationTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingInvocationTests.java index 0b70284e..0cd2137e 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingInvocationTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingInvocationTests.java @@ -33,12 +33,12 @@ import org.springframework.graphql.Author; import org.springframework.graphql.Book; import org.springframework.graphql.BookCriteria; import org.springframework.graphql.BookSource; -import org.springframework.graphql.ResponseHelper; +import org.springframework.graphql.ExecutionGraphQlRequest; +import org.springframework.graphql.ExecutionGraphQlResponse; import org.springframework.graphql.GraphQlService; import org.springframework.graphql.GraphQlSetup; -import org.springframework.graphql.RequestInput; -import org.springframework.graphql.RequestOutput; -import org.springframework.graphql.TestRequestInput; +import org.springframework.graphql.ResponseHelper; +import org.springframework.graphql.TestExecutionRequest; import org.springframework.graphql.data.method.annotation.Argument; import org.springframework.graphql.data.method.annotation.MutationMapping; import org.springframework.graphql.data.method.annotation.QueryMapping; @@ -71,9 +71,9 @@ public class SchemaMappingInvocationTests { " }" + "}"; - Mono outputMono = graphQlService().execute(TestRequestInput.forDocument(document)); + Mono responseMono = graphQlService().execute(TestExecutionRequest.forDocument(document)); - Book book = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class); + Book book = ResponseHelper.forResponse(responseMono).toEntity("bookById", Book.class); assertThat(book.getId()).isEqualTo(1); assertThat(book.getName()).isEqualTo("Nineteen Eighty-Four"); @@ -91,9 +91,9 @@ public class SchemaMappingInvocationTests { " }" + "}"; - Mono outputMono = graphQlService().execute(TestRequestInput.forDocument(document)); + Mono responseMono = graphQlService().execute(TestExecutionRequest.forDocument(document)); - List bookList = ResponseHelper.forResponse(outputMono).toList("booksByCriteria", Book.class); + List bookList = ResponseHelper.forResponse(responseMono).toList("booksByCriteria", Book.class); assertThat(bookList).hasSize(2); assertThat(bookList.get(0).getName()).isEqualTo("Nineteen Eighty-Four"); assertThat(bookList.get(1).getName()).isEqualTo("Animal Farm"); @@ -108,9 +108,9 @@ public class SchemaMappingInvocationTests { " }" + "}"; - Mono outputMono = graphQlService().execute(TestRequestInput.forDocument(document)); + Mono responseMono = graphQlService().execute(TestExecutionRequest.forDocument(document)); - List bookList = ResponseHelper.forResponse(outputMono).toList("booksByProjectedArguments", Book.class); + List bookList = ResponseHelper.forResponse(responseMono).toList("booksByProjectedArguments", Book.class); assertThat(bookList).hasSize(2); assertThat(bookList.get(0).getName()).isEqualTo("Nineteen Eighty-Four"); assertThat(bookList.get(1).getName()).isEqualTo("Animal Farm"); @@ -125,9 +125,9 @@ public class SchemaMappingInvocationTests { " }" + "}"; - Mono outputMono = graphQlService().execute(TestRequestInput.forDocument(document)); + Mono responseMono = graphQlService().execute(TestExecutionRequest.forDocument(document)); - List bookList = ResponseHelper.forResponse(outputMono).toList("booksByProjectedCriteria", Book.class); + List bookList = ResponseHelper.forResponse(responseMono).toList("booksByProjectedCriteria", Book.class); assertThat(bookList).hasSize(2); assertThat(bookList.get(0).getName()).isEqualTo("Nineteen Eighty-Four"); assertThat(bookList.get(1).getName()).isEqualTo("Animal Farm"); @@ -144,15 +144,15 @@ public class SchemaMappingInvocationTests { "}"; AtomicReference contextRef = new AtomicReference<>(); - RequestInput requestInput = TestRequestInput.forDocument(document); - requestInput.configureExecutionInput((executionInput, builder) -> { + ExecutionGraphQlRequest request = TestExecutionRequest.forDocument(document); + request.configureExecutionInput((executionInput, builder) -> { contextRef.set(executionInput.getGraphQLContext()); return executionInput; }); - Mono outputMono = graphQlService().execute(requestInput); + Mono responseMono = graphQlService().execute(request); - Author author = ResponseHelper.forResponse(outputMono).toEntity("authorById", Author.class); + Author author = ResponseHelper.forResponse(responseMono).toEntity("authorById", Author.class); assertThat(author.getId()).isEqualTo(101); assertThat(author.getFirstName()).isEqualTo("George"); assertThat(author.getLastName()).isEqualTo("Orwell"); @@ -170,9 +170,9 @@ public class SchemaMappingInvocationTests { " }" + "}"; - Mono outputMono = graphQlService().execute(TestRequestInput.forDocument(document)); + Mono responseMono = graphQlService().execute(TestExecutionRequest.forDocument(document)); - Author author = ResponseHelper.forResponse(outputMono).toEntity("addAuthor", Author.class); + Author author = ResponseHelper.forResponse(responseMono).toEntity("addAuthor", Author.class); assertThat(author.getId()).isEqualTo(99); assertThat(author.getFirstName()).isEqualTo("James"); assertThat(author.getLastName()).isEqualTo("Joyce"); @@ -187,9 +187,9 @@ public class SchemaMappingInvocationTests { " }" + "}"; - Mono outputMono = graphQlService().execute(TestRequestInput.forDocument(document)); + Mono responseMono = graphQlService().execute(TestExecutionRequest.forDocument(document)); - Flux bookFlux = ResponseHelper.forSubscription(outputMono) + Flux bookFlux = ResponseHelper.forSubscription(responseMono) .map(response -> response.toEntity("bookSearch", Book.class)); StepVerifier.create(bookFlux) diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPrincipalMethodArgumentResolverTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPrincipalMethodArgumentResolverTests.java index df1f2e4e..acdaea77 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPrincipalMethodArgumentResolverTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPrincipalMethodArgumentResolverTests.java @@ -31,10 +31,10 @@ import reactor.util.context.Context; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.MethodParameter; +import org.springframework.graphql.ExecutionGraphQlResponse; import org.springframework.graphql.ResponseHelper; import org.springframework.graphql.GraphQlSetup; -import org.springframework.graphql.RequestOutput; -import org.springframework.graphql.TestRequestInput; +import org.springframework.graphql.TestExecutionRequest; import org.springframework.graphql.data.method.annotation.QueryMapping; import org.springframework.graphql.data.method.annotation.SubscriptionMapping; import org.springframework.graphql.execution.ExecutionGraphQlService; @@ -103,10 +103,10 @@ public class SchemaMappingPrincipalMethodArgumentResolverTests { } private void testQuery(String field, Function contextWriter) { - Mono resultMono = executeAsync( + Mono responseMono = executeAsync( "type Query { " + field + ": String }", "{ " + field + " }", contextWriter); - String greeting = ResponseHelper.forResponse(resultMono).toEntity(field, String.class); + String greeting = ResponseHelper.forResponse(responseMono).toEntity(field, String.class); assertThat(greeting).isEqualTo("Hello"); assertThat(greetingController.principal()).isSameAs(authentication); } @@ -136,12 +136,12 @@ public class SchemaMappingPrincipalMethodArgumentResolverTests { private void testSubscription(Function contextModifier) { String field = "greetingSubscription"; - Mono resultMono = executeAsync( + Mono responseMono = executeAsync( "type Query { greeting: String } type Subscription { " + field + ": String }", "subscription Greeting { " + field + " }", contextModifier); - Flux greetingFlux = ResponseHelper.forSubscription(resultMono) + Flux greetingFlux = ResponseHelper.forSubscription(responseMono) .map(response -> response.toEntity(field, String.class)); StepVerifier.create(greetingFlux).expectNext("Hello", "Hi").verifyComplete(); @@ -150,7 +150,7 @@ public class SchemaMappingPrincipalMethodArgumentResolverTests { } - private Mono executeAsync( + private Mono executeAsync( String schema, String document, Function contextWriter) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); @@ -162,7 +162,7 @@ public class SchemaMappingPrincipalMethodArgumentResolverTests { .toGraphQlService(); return Mono.delay(Duration.ofMillis(10)) - .flatMap(aLong -> graphQlService.execute(TestRequestInput.forDocument(document))) + .flatMap(aLong -> graphQlService.execute(TestExecutionRequest.forDocument(document))) .contextWrite(contextWriter); } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/execution/BatchLoadingTests.java b/spring-graphql/src/test/java/org/springframework/graphql/execution/BatchLoadingTests.java index 11789db0..d383f52e 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/execution/BatchLoadingTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/execution/BatchLoadingTests.java @@ -27,11 +27,11 @@ import reactor.core.publisher.Mono; import org.springframework.graphql.Author; import org.springframework.graphql.Book; import org.springframework.graphql.BookSource; +import org.springframework.graphql.ExecutionGraphQlResponse; import org.springframework.graphql.ResponseHelper; import org.springframework.graphql.GraphQlService; import org.springframework.graphql.GraphQlSetup; -import org.springframework.graphql.RequestOutput; -import org.springframework.graphql.TestRequestInput; +import org.springframework.graphql.TestExecutionRequest; import static org.assertj.core.api.Assertions.assertThat; @@ -76,9 +76,9 @@ public class BatchLoadingTests { .dataLoaders(this.registry) .toGraphQlService(); - Mono outputMono = service.execute(TestRequestInput.forDocument(document)); + Mono responseMono = service.execute(TestExecutionRequest.forDocument(document)); - List books = ResponseHelper.forResponse(outputMono).toList("booksByCriteria", Book.class); + List books = ResponseHelper.forResponse(responseMono).toList("booksByCriteria", Book.class); assertThat(books).hasSize(2); Author author = books.get(0).getAuthor(); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/execution/ClassNameTypeResolverTests.java b/spring-graphql/src/test/java/org/springframework/graphql/execution/ClassNameTypeResolverTests.java index 416865f9..7c3c2e38 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/execution/ClassNameTypeResolverTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/execution/ClassNameTypeResolverTests.java @@ -22,10 +22,10 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.junit.jupiter.api.Test; import reactor.core.publisher.Mono; +import org.springframework.graphql.ExecutionGraphQlResponse; import org.springframework.graphql.ResponseHelper; import org.springframework.graphql.GraphQlSetup; -import org.springframework.graphql.RequestOutput; -import org.springframework.graphql.TestRequestInput; +import org.springframework.graphql.TestExecutionRequest; import static org.assertj.core.api.Assertions.assertThat; @@ -83,11 +83,11 @@ public class ClassNameTypeResolverTests { " }" + "}"; - Mono outputMono = graphQlSetup.queryFetcher("animals", env -> animalList) + Mono responseMono = graphQlSetup.queryFetcher("animals", env -> animalList) .toGraphQlService() - .execute(TestRequestInput.forDocument(document)); + .execute(TestExecutionRequest.forDocument(document)); - ResponseHelper response = ResponseHelper.forResponse(outputMono); + ResponseHelper response = ResponseHelper.forResponse(responseMono); for (int i = 0; i < animalList.size(); i++) { Animal animal = animalList.get(i); if (animal instanceof Bird) { @@ -125,12 +125,12 @@ public class ClassNameTypeResolverTests { ClassNameTypeResolver typeResolver = new ClassNameTypeResolver(); typeResolver.addMapping(Tree.class, "Plant"); - Mono output = graphQlSetup.queryFetcher("sightings", env -> animalAndPlantList) + Mono responseMono = graphQlSetup.queryFetcher("sightings", env -> animalAndPlantList) .typeResolver(typeResolver) .toGraphQlService() - .execute(TestRequestInput.forDocument(document)); + .execute(TestExecutionRequest.forDocument(document)); - ResponseHelper response = ResponseHelper.forResponse(output); + ResponseHelper response = ResponseHelper.forResponse(responseMono); for (int i = 0; i < animalAndPlantList.size(); i++) { Object sighting = animalAndPlantList.get(i); if (sighting instanceof Animal) { diff --git a/spring-graphql/src/test/java/org/springframework/graphql/web/WebInterceptorTests.java b/spring-graphql/src/test/java/org/springframework/graphql/web/WebInterceptorTests.java index aeb3a733..6154df4f 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/web/WebInterceptorTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/web/WebInterceptorTests.java @@ -27,8 +27,9 @@ import org.junit.jupiter.api.Test; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; -import org.springframework.graphql.RequestInput; -import org.springframework.graphql.RequestOutput; +import org.springframework.graphql.ExecutionGraphQlRequest; +import org.springframework.graphql.ExecutionGraphQlResponse; +import org.springframework.graphql.support.DefaultExecutionGraphQlResponse; import org.springframework.http.HttpHeaders; import static org.assertj.core.api.Assertions.assertThat; @@ -76,9 +77,9 @@ public class WebInterceptorTests { AtomicReference actualName = new AtomicReference<>(); WebGraphQlHandler handler = WebGraphQlHandler - .builder((input) -> { - actualName.set(input.toExecutionInput().getOperationName()); - return emptyExecutionResult(input); + .builder((request) -> { + actualName.set(request.toExecutionInput().getOperationName()); + return emptyExecutionResult(request); }) .interceptor((webInput, next) -> { webInput.configureExecutionInput((input, builder) -> builder.operationName("testOp").build()); @@ -91,8 +92,8 @@ public class WebInterceptorTests { assertThat(actualName.get()).isEqualTo("testOp"); } - private Mono emptyExecutionResult(RequestInput input) { - return Mono.just(new RequestOutput( + private Mono emptyExecutionResult(ExecutionGraphQlRequest request) { + return Mono.just(new DefaultExecutionGraphQlResponse( ExecutionInput.newExecutionInput("{}").build(), ExecutionResultImpl.newExecutionResult().build())); } diff --git a/spring-graphql/src/testFixtures/java/org/springframework/graphql/TestRequestInput.java b/spring-graphql/src/testFixtures/java/org/springframework/graphql/TestExecutionRequest.java similarity index 66% rename from spring-graphql/src/testFixtures/java/org/springframework/graphql/TestRequestInput.java rename to spring-graphql/src/testFixtures/java/org/springframework/graphql/TestExecutionRequest.java index b56452e7..255e6dac 100644 --- a/spring-graphql/src/testFixtures/java/org/springframework/graphql/TestRequestInput.java +++ b/spring-graphql/src/testFixtures/java/org/springframework/graphql/TestExecutionRequest.java @@ -18,24 +18,26 @@ package org.springframework.graphql; import java.util.concurrent.atomic.AtomicLong; +import org.springframework.graphql.support.DefaultExecutionGraphQlRequest; + /** - * {@link RequestInput} for use in tests with a convenient single-arg constructor - * and simple incrementing id generation. + * {@link ExecutionGraphQlRequest} for use in tests with a convenient single-arg + * constructor and simple incrementing id generation. * * @author Rossen Stoyanchev */ -public class TestRequestInput extends RequestInput { +public class TestExecutionRequest extends DefaultExecutionGraphQlRequest { private static final AtomicLong idIndex = new AtomicLong(); - private TestRequestInput(String document) { + private TestExecutionRequest(String document) { super(document, null, null, String.valueOf(idIndex.incrementAndGet()), null); } - public static RequestInput forDocument(String document) { - return new TestRequestInput(document); + public static ExecutionGraphQlRequest forDocument(String document) { + return new TestExecutionRequest(document); } }