Introduce GraphQlResponse
Replace the use of ExecutionResult on the client side where we are dealing with a response map rather, and also incorporate it into the server-side hierarchy where it wraps an ExecutionResult instead. See gh-10
This commit is contained in:
@@ -25,6 +25,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.graphql.GraphQlRequest;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.RequestOutput;
|
||||
import org.springframework.graphql.client.GraphQlTransport;
|
||||
import org.springframework.test.util.AssertionErrors;
|
||||
@@ -45,22 +46,23 @@ abstract class AbstractDirectTransport implements GraphQlTransport {
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<ExecutionResult> execute(GraphQlRequest request) {
|
||||
return executeInternal(request).cast(ExecutionResult.class);
|
||||
public Mono<GraphQlResponse> execute(GraphQlRequest request) {
|
||||
return executeInternal(request).cast(GraphQlResponse.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"ConstantConditions", "unchecked"})
|
||||
@Override
|
||||
public Flux<ExecutionResult> executeSubscription(GraphQlRequest request) {
|
||||
return executeInternal(request).flatMapMany(result -> {
|
||||
public Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
|
||||
return executeInternal(request).flatMapMany(output -> {
|
||||
try {
|
||||
Object data = result.getData();
|
||||
Object data = output.getData();
|
||||
AssertionErrors.assertTrue("Not a Publisher: " + data, data instanceof Publisher);
|
||||
|
||||
List<GraphQLError> errors = result.getErrors();
|
||||
List<GraphQLError> errors = output.getErrors();
|
||||
AssertionErrors.assertTrue("Subscription errors: " + errors, CollectionUtils.isEmpty(errors));
|
||||
|
||||
return Flux.from((Publisher<ExecutionResult>) data);
|
||||
return Flux.from((Publisher<ExecutionResult>) data)
|
||||
.map(result -> new RequestOutput(output.getExecutionInput(), result));
|
||||
}
|
||||
catch (AssertionError ex) {
|
||||
throw new AssertionError(ex.getMessage() + "\nRequest: " + request, ex);
|
||||
|
||||
@@ -31,12 +31,12 @@ import com.jayway.jsonpath.Configuration;
|
||||
import com.jayway.jsonpath.DocumentContext;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import com.jayway.jsonpath.TypeRef;
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQLError;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.graphql.GraphQlRequest;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.client.GraphQlTransport;
|
||||
import org.springframework.graphql.support.DocumentSource;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -165,7 +165,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Response execute() {
|
||||
return transport.execute(request()).map(result -> response(result, request())).block(responseTimeout);
|
||||
return transport.execute(request()).map(response -> mapResponse(response, request())).block(responseTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -175,15 +175,15 @@ final class DefaultGraphQlTester implements GraphQlTester {
|
||||
|
||||
@Override
|
||||
public Subscription executeSubscription() {
|
||||
return () -> transport.executeSubscription(request()).map(result -> response(result, request()));
|
||||
return () -> transport.executeSubscription(request()).map(result -> mapResponse(result, request()));
|
||||
}
|
||||
|
||||
private GraphQlRequest request() {
|
||||
return new GraphQlRequest(this.document, this.operationName, this.variables);
|
||||
}
|
||||
|
||||
private DefaultResponse response(ExecutionResult result, GraphQlRequest request) {
|
||||
return new DefaultResponse(result, errorFilter, assertDecorator(request), jsonPathConfig);
|
||||
private DefaultResponse mapResponse(GraphQlResponse response, GraphQlRequest request) {
|
||||
return new DefaultResponse(response, errorFilter, assertDecorator(request), jsonPathConfig);
|
||||
}
|
||||
|
||||
private Consumer<Runnable> assertDecorator(GraphQlRequest request) {
|
||||
@@ -217,12 +217,12 @@ final class DefaultGraphQlTester implements GraphQlTester {
|
||||
|
||||
|
||||
private ResponseDelegate(
|
||||
ExecutionResult result, @Nullable Predicate<GraphQLError> errorFilter,
|
||||
GraphQlResponse response, @Nullable Predicate<GraphQLError> errorFilter,
|
||||
Consumer<Runnable> assertDecorator, Configuration jsonPathConfig) {
|
||||
|
||||
this.jsonDoc = JsonPath.parse(result.toSpecification(), jsonPathConfig);
|
||||
this.jsonDoc = JsonPath.parse(response.toMap(), jsonPathConfig);
|
||||
this.jsonContent = this.jsonDoc::jsonString;
|
||||
this.errors = result.getErrors();
|
||||
this.errors = response.getErrors();
|
||||
this.unexpectedErrors = new ArrayList<>(this.errors);
|
||||
this.assertDecorator = assertDecorator;
|
||||
|
||||
@@ -293,10 +293,10 @@ final class DefaultGraphQlTester implements GraphQlTester {
|
||||
private final ResponseDelegate delegate;
|
||||
|
||||
private DefaultResponse(
|
||||
ExecutionResult result, @Nullable Predicate<GraphQLError> errorFilter,
|
||||
GraphQlResponse response, @Nullable Predicate<GraphQLError> errorFilter,
|
||||
Consumer<Runnable> assertDecorator, Configuration jsonPathConfig) {
|
||||
|
||||
this.delegate = new ResponseDelegate(result, errorFilter, assertDecorator, jsonPathConfig);
|
||||
this.delegate = new ResponseDelegate(response, errorFilter, assertDecorator, jsonPathConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,11 +20,11 @@ package org.springframework.graphql.test.tester;
|
||||
import java.net.URI;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.graphql.GraphQlRequest;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.client.CodecMappingProvider;
|
||||
import org.springframework.graphql.client.GraphQlClient;
|
||||
import org.springframework.graphql.client.GraphQlTransport;
|
||||
@@ -164,7 +164,7 @@ final class DefaultWebSocketGraphQlTester extends AbstractDelegatingGraphQlTeste
|
||||
return new GraphQlTransport() {
|
||||
|
||||
@Override
|
||||
public Mono<ExecutionResult> execute(GraphQlRequest request) {
|
||||
public Mono<GraphQlResponse> execute(GraphQlRequest request) {
|
||||
return client
|
||||
.document(request.getDocument())
|
||||
.operationName(request.getOperationName())
|
||||
@@ -174,7 +174,7 @@ final class DefaultWebSocketGraphQlTester extends AbstractDelegatingGraphQlTeste
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ExecutionResult> executeSubscription(GraphQlRequest request) {
|
||||
public Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
|
||||
return client
|
||||
.document(request.getDocument())
|
||||
.operationName(request.getOperationName())
|
||||
|
||||
@@ -19,14 +19,14 @@ package org.springframework.graphql.test.tester;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.graphql.GraphQlRequest;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.client.GraphQlTransport;
|
||||
import org.springframework.graphql.support.MapExecutionResult;
|
||||
import org.springframework.graphql.support.MapGraphQlResponse;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -53,9 +53,9 @@ final class WebTestClientTransport implements GraphQlTransport {
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<ExecutionResult> execute(GraphQlRequest request) {
|
||||
public Mono<GraphQlResponse> execute(GraphQlRequest request) {
|
||||
|
||||
Map<String, Object> resultMap = this.webTestClient.post()
|
||||
Map<String, Object> responseMap = this.webTestClient.post()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request.toMap())
|
||||
@@ -66,13 +66,13 @@ final class WebTestClientTransport implements GraphQlTransport {
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
resultMap = (resultMap != null ? resultMap : Collections.emptyMap());
|
||||
ExecutionResult result = MapExecutionResult.from(resultMap);
|
||||
return Mono.just(result);
|
||||
responseMap = (responseMap != null ? responseMap : Collections.emptyMap());
|
||||
GraphQlResponse response = MapGraphQlResponse.forResponse(responseMap);
|
||||
return Mono.just(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ExecutionResult> executeSubscription(GraphQlRequest request) {
|
||||
public Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
|
||||
throw new UnsupportedOperationException("Subscriptions not supported over HTTP");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.List;
|
||||
import java.util.Map;
|
||||
|
||||
import graphql.GraphQLError;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a GraphQL response with the result of executing a request operation.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface GraphQlResponse {
|
||||
|
||||
/**
|
||||
* Whether the response is valid. A response is invalid in one of the
|
||||
* following two cases:
|
||||
* <ul>
|
||||
* <li>the {@link #toMap() response map} has no "data" entry indicating
|
||||
* errors before execution, e.g. grammar parse and validation
|
||||
* <li>the "data" entry has a {@code null} value indicating errors during
|
||||
* execution that prevented a valid response
|
||||
* </ul>
|
||||
* <p>A valid response has a "data" key with a {@code non-null} value, but
|
||||
* it may still be partial and have some fields set to {@code null} due to
|
||||
* field errors.
|
||||
* <p>For more details, see section 7 "Response" in the GraphQL spec.
|
||||
*/
|
||||
boolean isValid();
|
||||
|
||||
/**
|
||||
* Return the data part of the response, or {@code null} when the response
|
||||
* is not {@link #isValid() valid}.
|
||||
* @param <T> a map or a list
|
||||
*/
|
||||
@Nullable
|
||||
<T> T getData();
|
||||
|
||||
/**
|
||||
* Return errors for the response. This contains "request errors" when the
|
||||
* response is not {@link #isValid() valid} and/or "field errors" for a
|
||||
* partial response.
|
||||
*/
|
||||
List<GraphQLError> getErrors();
|
||||
|
||||
/**
|
||||
* Return implementor specific, protocol extensions, if any.
|
||||
*/
|
||||
Map<Object, Object> getExtensions();
|
||||
|
||||
/**
|
||||
* Return a map representation of the response, formatted as required in the
|
||||
* "Response" section of the GraphQL spec.
|
||||
*/
|
||||
Map<String, Object> toMap();
|
||||
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.graphql;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -26,29 +27,37 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Wraps an {@link ExecutionResult} and also exposes the {@link ExecutionInput}
|
||||
* prepared for the request.
|
||||
* {@link GraphQlResponse} for server use that wraps the {@link ExecutionResult}
|
||||
* returned from {@link graphql.GraphQL} and also exposes the actual
|
||||
* {@link ExecutionInput} instance passed into it.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class RequestOutput implements ExecutionResult {
|
||||
public class RequestOutput implements GraphQlResponse {
|
||||
|
||||
private final ExecutionInput executionInput;
|
||||
private final ExecutionInput input;
|
||||
|
||||
private final ExecutionResult executionResult;
|
||||
private final ExecutionResult result;
|
||||
|
||||
|
||||
/**
|
||||
* Create an instance.
|
||||
* @param executionInput the input prepared for the request
|
||||
* @param executionResult the result from performing the request
|
||||
* Constructor to create initial instance.
|
||||
*/
|
||||
public RequestOutput(ExecutionInput executionInput, ExecutionResult executionResult) {
|
||||
Assert.notNull(executionInput, "ExecutionInput is required.");
|
||||
Assert.notNull(executionResult, "ExecutionResult is required.");
|
||||
this.executionInput = executionInput;
|
||||
this.executionResult = executionResult;
|
||||
public RequestOutput(ExecutionInput input, ExecutionResult result) {
|
||||
Assert.notNull(input, "ExecutionInput is required.");
|
||||
Assert.notNull(result, "ExecutionResult is required.");
|
||||
this.input = input;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor to re-wrap from transport specific subclass.
|
||||
*/
|
||||
protected RequestOutput(RequestOutput requestOutput) {
|
||||
Assert.notNull(requestOutput, "RequestOutput is required.");
|
||||
this.input = requestOutput.getExecutionInput();
|
||||
this.result = requestOutput.result;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,37 +66,40 @@ public class RequestOutput implements ExecutionResult {
|
||||
* {@link RequestInput} and passed to {@link graphql.GraphQL}.
|
||||
*/
|
||||
public ExecutionInput getExecutionInput() {
|
||||
return this.executionInput;
|
||||
return this.input;
|
||||
}
|
||||
|
||||
protected ExecutionResult getExecutionResult() {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return (this.result.isDataPresent() && this.result.getData() != null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T getData() {
|
||||
return this.executionResult.getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataPresent() {
|
||||
return this.executionResult.isDataPresent();
|
||||
return this.result.getData();
|
||||
}
|
||||
|
||||
public List<GraphQLError> getErrors() {
|
||||
return this.executionResult.getErrors();
|
||||
return this.result.getErrors();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Map<Object, Object> getExtensions() {
|
||||
return this.executionResult.getExtensions();
|
||||
return (this.result.getExtensions() != null ? this.result.getExtensions() : Collections.emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> toSpecification() {
|
||||
return this.executionResult.toSpecification();
|
||||
public Map<String, Object> toMap() {
|
||||
return this.result.toSpecification();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.executionResult.toString();
|
||||
return this.result.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public abstract class AbstractGraphQlClientBuilder<B extends AbstractGraphQlClie
|
||||
Configuration.defaultConfiguration().mappingProvider().getClass();
|
||||
|
||||
// We only need a MappingProvider:
|
||||
// GraphQlTransport returns ExecutionResult with JSON parsed to Map/List
|
||||
// GraphQlTransport returns GraphQlResponse with already parsed JSON
|
||||
|
||||
static Configuration configure(Configuration config) {
|
||||
MappingProvider provider = config.mappingProvider();
|
||||
|
||||
@@ -25,7 +25,6 @@ import com.jayway.jsonpath.Configuration;
|
||||
import com.jayway.jsonpath.DocumentContext;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import com.jayway.jsonpath.TypeRef;
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQLError;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -33,6 +32,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.graphql.GraphQlRequest;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.support.DocumentSource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -178,16 +178,16 @@ final class DefaultGraphQlClient implements GraphQlClient {
|
||||
*/
|
||||
private static class DefaultResponse implements Response {
|
||||
|
||||
private final ExecutionResult result;
|
||||
private final GraphQlResponse response;
|
||||
|
||||
private final DocumentContext jsonPathDoc;
|
||||
|
||||
private final List<GraphQLError> errors;
|
||||
|
||||
private DefaultResponse(ExecutionResult result, Configuration jsonPathConfig) {
|
||||
this.result = result;
|
||||
this.jsonPathDoc = JsonPath.parse(result.toSpecification(), jsonPathConfig);
|
||||
this.errors = result.getErrors();
|
||||
private DefaultResponse(GraphQlResponse response, Configuration jsonPathConfig) {
|
||||
this.response = response;
|
||||
this.jsonPathDoc = JsonPath.parse(response.toMap(), jsonPathConfig);
|
||||
this.errors = response.getErrors();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -226,8 +226,8 @@ final class DefaultGraphQlClient implements GraphQlClient {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutionResult andReturn() {
|
||||
return this.result;
|
||||
public GraphQlResponse andReturn() {
|
||||
return this.response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ package org.springframework.graphql.client;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQLError;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.support.DocumentSource;
|
||||
import org.springframework.graphql.support.ResourceDocumentSource;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -218,9 +218,9 @@ public interface GraphQlClient {
|
||||
List<GraphQLError> errors();
|
||||
|
||||
/**
|
||||
* Return the underlying {@link ExecutionResult} for the response.
|
||||
* Return the underlying {@link GraphQlResponse}.
|
||||
*/
|
||||
ExecutionResult andReturn();
|
||||
GraphQlResponse andReturn();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
package org.springframework.graphql.client;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.graphql.GraphQlRequest;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
|
||||
|
||||
/**
|
||||
@@ -34,16 +34,16 @@ public interface GraphQlTransport {
|
||||
/**
|
||||
* Execute a request with a single response such as a "query" or "mutation".
|
||||
* @param request the request to execute
|
||||
* @return a {@code Mono} with the {@code ExecutionResult} for the response.
|
||||
* @return a {@code Mono} with the {@code GraphQlResponse} for the response.
|
||||
* The {@code Mono} may end wth an error due to transport or other issues
|
||||
* such as failures to encode the request or decode the response.
|
||||
*/
|
||||
Mono<ExecutionResult> execute(GraphQlRequest request);
|
||||
Mono<GraphQlResponse> execute(GraphQlRequest request);
|
||||
|
||||
/**
|
||||
* Execute a "subscription" request with a stream of responses.
|
||||
* @param request the request to execute
|
||||
* @return a {@code Flux} of {@code ExecutionResult} responses.
|
||||
* @return a {@code Flux} of {@code GraphQlResponse} responses.
|
||||
* The {@code Flux} may terminate as follows:
|
||||
* <ul>
|
||||
* <li>Completes if the subscription completes before the connection is closed.
|
||||
@@ -55,6 +55,6 @@ public interface GraphQlTransport {
|
||||
* <p>The {@code Flux} may be cancelled to notify the server to end the
|
||||
* subscription stream.
|
||||
*/
|
||||
Flux<ExecutionResult> executeSubscription(GraphQlRequest request);
|
||||
Flux<GraphQlResponse> executeSubscription(GraphQlRequest request);
|
||||
|
||||
}
|
||||
|
||||
@@ -18,13 +18,13 @@ package org.springframework.graphql.client;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.graphql.GraphQlRequest;
|
||||
import org.springframework.graphql.support.MapExecutionResult;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.support.MapGraphQlResponse;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
@@ -54,18 +54,18 @@ final class HttpGraphQlTransport implements GraphQlTransport {
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<ExecutionResult> execute(GraphQlRequest request) {
|
||||
public Mono<GraphQlResponse> execute(GraphQlRequest request) {
|
||||
return this.webClient.post()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request.toMap())
|
||||
.retrieve()
|
||||
.bodyToMono(MAP_TYPE)
|
||||
.map(MapExecutionResult::from);
|
||||
.map(MapGraphQlResponse::forResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ExecutionResult> executeSubscription(GraphQlRequest request) {
|
||||
public Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
|
||||
throw new UnsupportedOperationException("Subscriptions not supported over HTTP");
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQLError;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -34,8 +33,9 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.Sinks;
|
||||
|
||||
import org.springframework.graphql.GraphQlRequest;
|
||||
import org.springframework.graphql.support.MapExecutionResult;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.support.MapGraphQlError;
|
||||
import org.springframework.graphql.support.MapGraphQlResponse;
|
||||
import org.springframework.graphql.web.support.GraphQlMessage;
|
||||
import org.springframework.graphql.web.support.GraphQlMessageType;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -145,12 +145,12 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ExecutionResult> execute(GraphQlRequest request) {
|
||||
public Mono<GraphQlResponse> execute(GraphQlRequest request) {
|
||||
return this.graphQlSessionMono.flatMap(session -> session.execute(request));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ExecutionResult> executeSubscription(GraphQlRequest request) {
|
||||
public Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
|
||||
return this.graphQlSessionMono.flatMapMany(session -> session.executeSubscription(request));
|
||||
}
|
||||
|
||||
@@ -372,9 +372,9 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
|
||||
private final Sinks.Many<GraphQlMessage> requestSink = Sinks.many().unicast().onBackpressureBuffer();
|
||||
|
||||
private final Map<String, Sinks.One<ExecutionResult>> resultSinks = new ConcurrentHashMap<>();
|
||||
private final Map<String, Sinks.One<GraphQlResponse>> responseSinks = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, Sinks.Many<ExecutionResult>> streamingSinks = new ConcurrentHashMap<>();
|
||||
private final Map<String, Sinks.Many<GraphQlResponse>> streamSinks = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
GraphQlSession(WebSocketSession webSocketSession) {
|
||||
@@ -389,32 +389,32 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
return this.requestSink.asFlux();
|
||||
}
|
||||
|
||||
public Mono<ExecutionResult> execute(GraphQlRequest request) {
|
||||
public Mono<GraphQlResponse> execute(GraphQlRequest request) {
|
||||
String id = String.valueOf(this.requestIndex.incrementAndGet());
|
||||
try {
|
||||
GraphQlMessage message = GraphQlMessage.subscribe(id, request);
|
||||
Sinks.One<ExecutionResult> sink = Sinks.one();
|
||||
this.resultSinks.put(id, sink);
|
||||
Sinks.One<GraphQlResponse> sink = Sinks.one();
|
||||
this.responseSinks.put(id, sink);
|
||||
trySend(message);
|
||||
return sink.asMono().doOnCancel(() -> this.resultSinks.remove(id));
|
||||
return sink.asMono().doOnCancel(() -> this.responseSinks.remove(id));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
this.resultSinks.remove(id);
|
||||
this.responseSinks.remove(id);
|
||||
return Mono.error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Flux<ExecutionResult> executeSubscription(GraphQlRequest request) {
|
||||
public Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
|
||||
String id = String.valueOf(this.requestIndex.incrementAndGet());
|
||||
try {
|
||||
GraphQlMessage message = GraphQlMessage.subscribe(id, request);
|
||||
Sinks.Many<ExecutionResult> sink = Sinks.many().unicast().onBackpressureBuffer();
|
||||
this.streamingSinks.put(id, sink);
|
||||
Sinks.Many<GraphQlResponse> sink = Sinks.many().unicast().onBackpressureBuffer();
|
||||
this.streamSinks.put(id, sink);
|
||||
trySend(message);
|
||||
return sink.asFlux().doOnCancel(() -> cancelStream(id));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
this.streamingSinks.remove(id);
|
||||
this.streamSinks.remove(id);
|
||||
return Flux.error(ex);
|
||||
}
|
||||
}
|
||||
@@ -438,7 +438,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
}
|
||||
|
||||
private void cancelStream(String id) {
|
||||
Sinks.Many<ExecutionResult> streamSink = this.streamingSinks.remove(id);
|
||||
Sinks.Many<GraphQlResponse> streamSink = this.streamSinks.remove(id);
|
||||
if (streamSink != null) {
|
||||
try {
|
||||
trySend(GraphQlMessage.complete(id));
|
||||
@@ -458,8 +458,8 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
*/
|
||||
public void handleNext(GraphQlMessage message) {
|
||||
String id = message.getId();
|
||||
Sinks.One<ExecutionResult> sink = this.resultSinks.remove(id);
|
||||
Sinks.Many<ExecutionResult> streamingSink = this.streamingSinks.get(id);
|
||||
Sinks.One<GraphQlResponse> sink = this.responseSinks.remove(id);
|
||||
Sinks.Many<GraphQlResponse> streamingSink = this.streamSinks.get(id);
|
||||
|
||||
if (sink == null && streamingSink == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -468,10 +468,10 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> resultMap = message.getPayload();
|
||||
ExecutionResult result = MapExecutionResult.from(resultMap);
|
||||
Map<String, Object> responseMap = message.getPayload();
|
||||
GraphQlResponse response = MapGraphQlResponse.forResponse(responseMap);
|
||||
|
||||
Sinks.EmitResult emitResult = (sink != null ? sink.tryEmitValue(result) : streamingSink.tryEmitNext(result));
|
||||
Sinks.EmitResult emitResult = (sink != null ? sink.tryEmitValue(response) : streamingSink.tryEmitNext(response));
|
||||
if (emitResult.isFailure()) {
|
||||
// Just log: cannot overflow, is serialized, and cancel is handled in doOnCancel
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -481,13 +481,13 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an "error" message, turning it into an {@link ExecutionResult}
|
||||
* for a single result response, or signaling an error to streams.
|
||||
* Handle an "error" message, turning it into an {@link GraphQlResponse}
|
||||
* for single responses, or signaling an error for streams.
|
||||
*/
|
||||
public void handleError(GraphQlMessage message) {
|
||||
String id = message.getId();
|
||||
Sinks.One<ExecutionResult> sink = this.resultSinks.remove(id);
|
||||
Sinks.Many<ExecutionResult> streamingSink = this.streamingSinks.remove(id);
|
||||
Sinks.One<GraphQlResponse> sink = this.responseSinks.remove(id);
|
||||
Sinks.Many<GraphQlResponse> streamingSink = this.streamSinks.remove(id);
|
||||
|
||||
if (sink == null && streamingSink == null ) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -500,8 +500,8 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
|
||||
Sinks.EmitResult emitResult;
|
||||
if (sink != null) {
|
||||
ExecutionResult result = MapExecutionResult.forErrorsOnly(payload);
|
||||
emitResult = sink.tryEmitValue(result);
|
||||
GraphQlResponse response = MapGraphQlResponse.forErrorsOnly(payload);
|
||||
emitResult = sink.tryEmitValue(response);
|
||||
}
|
||||
else {
|
||||
List<GraphQLError> graphQLErrors = MapGraphQlError.from(payload);
|
||||
@@ -518,14 +518,14 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
* Handle a "complete" message.
|
||||
*/
|
||||
public void handleComplete(GraphQlMessage message) {
|
||||
Sinks.One<ExecutionResult> resultSink = this.resultSinks.remove(message.getId());
|
||||
Sinks.Many<ExecutionResult> streamingResultSink = this.streamingSinks.remove(message.getId());
|
||||
Sinks.One<GraphQlResponse> sink = this.responseSinks.remove(message.getId());
|
||||
Sinks.Many<GraphQlResponse> streamSink = this.streamSinks.remove(message.getId());
|
||||
|
||||
if (resultSink != null) {
|
||||
resultSink.tryEmitEmpty();
|
||||
if (sink != null) {
|
||||
sink.tryEmitEmpty();
|
||||
}
|
||||
else if (streamingResultSink != null) {
|
||||
streamingResultSink.tryEmitComplete();
|
||||
else if (streamSink != null) {
|
||||
streamSink.tryEmitComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,10 +548,10 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
|
||||
* Terminate and clean all in-progress requests with the given error.
|
||||
*/
|
||||
public void terminateRequests(Exception ex) {
|
||||
this.resultSinks.values().forEach(sink -> sink.tryEmitError(ex));
|
||||
this.streamingSinks.values().forEach(sink -> sink.tryEmitError(ex));
|
||||
this.resultSinks.clear();
|
||||
this.streamingSinks.clear();
|
||||
this.responseSinks.values().forEach(sink -> sink.tryEmitError(ex));
|
||||
this.streamSinks.values().forEach(sink -> sink.tryEmitError(ex));
|
||||
this.responseSinks.clear();
|
||||
this.streamSinks.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,18 +21,18 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.ExecutionResultImpl;
|
||||
import graphql.GraphQLError;
|
||||
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ExecutionResult} backed by a {@link Map}.
|
||||
* {@link GraphQlResponse} for client use that wraps the GraphQL response map.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public final class MapExecutionResult implements ExecutionResult {
|
||||
public final class MapGraphQlResponse implements GraphQlResponse {
|
||||
|
||||
private final Map<String, Object> resultMap;
|
||||
|
||||
@@ -40,13 +40,18 @@ public final class MapExecutionResult implements ExecutionResult {
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private MapExecutionResult(Map<String, Object> resultMap) {
|
||||
private MapGraphQlResponse(Map<String, Object> resultMap) {
|
||||
Assert.notNull(resultMap, "'resultMap' is required");
|
||||
this.resultMap = resultMap;
|
||||
this.errors = MapGraphQlError.from((List<Map<String, Object>>) resultMap.get("errors"));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return (this.resultMap.containsKey("data") && this.resultMap.get("data") != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GraphQLError> getErrors() {
|
||||
return this.errors;
|
||||
@@ -58,26 +63,21 @@ public final class MapExecutionResult implements ExecutionResult {
|
||||
return (T) this.resultMap.get("data");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataPresent() {
|
||||
return (this.resultMap.get("data") != null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Map<Object, Object> getExtensions() {
|
||||
return (Map<Object, Object>) this.resultMap.get("extensions");
|
||||
return (Map<Object, Object>) this.resultMap.getOrDefault("extensions", Collections.emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> toSpecification() {
|
||||
return ExecutionResultImpl.newExecutionResult().from(this).build().toSpecification();
|
||||
public Map<String, Object> toMap() {
|
||||
return this.resultMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return (other instanceof MapExecutionResult &&
|
||||
this.resultMap.equals(((MapExecutionResult) other).resultMap));
|
||||
return (other instanceof MapGraphQlResponse &&
|
||||
this.resultMap.equals(((MapGraphQlResponse) other).resultMap));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,24 +95,24 @@ public final class MapExecutionResult implements ExecutionResult {
|
||||
* Create an instance from an {@code ExecutionResult} serialized to map via
|
||||
* {@link ExecutionResult#toSpecification()}.
|
||||
*/
|
||||
public static ExecutionResult from(Map<String, Object> map) {
|
||||
return new MapExecutionResult(map);
|
||||
public static GraphQlResponse forResponse(Map<String, Object> map) {
|
||||
return new MapGraphQlResponse(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@code ExecutionResult} with a "data" key that returns the
|
||||
* given map.
|
||||
*/
|
||||
public static ExecutionResult forDataOnly(Map<String, Object> map) {
|
||||
return new MapExecutionResult(Collections.singletonMap("data", map));
|
||||
public static GraphQlResponse forDataOnly(Map<String, Object> map) {
|
||||
return new MapGraphQlResponse(Collections.singletonMap("data", map));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@code ExecutionResult} with an "errors" key that returns the
|
||||
* given serialized errors.
|
||||
*/
|
||||
public static ExecutionResult forErrorsOnly(List<Map<String, Object>> errors) {
|
||||
return new MapExecutionResult(Collections.singletonMap("errors", errors));
|
||||
public static GraphQlResponse forErrorsOnly(List<Map<String, Object>> errors) {
|
||||
return new MapGraphQlResponse(Collections.singletonMap("errors", errors));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.graphql.web;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
@@ -49,15 +48,14 @@ public class WebOutput extends RequestOutput {
|
||||
* @param requestOutput the output from an executed request
|
||||
*/
|
||||
public WebOutput(RequestOutput requestOutput) {
|
||||
this(requestOutput.getExecutionInput(), requestOutput, new HttpHeaders());
|
||||
super(requestOutput);
|
||||
this.responseHeaders = new HttpHeaders();
|
||||
}
|
||||
|
||||
private WebOutput(ExecutionInput executionInput, ExecutionResult executionResult,
|
||||
HttpHeaders responseHeaders) {
|
||||
|
||||
private WebOutput(ExecutionInput executionInput, ExecutionResult executionResult, HttpHeaders headers) {
|
||||
super(executionInput, executionResult);
|
||||
Assert.notNull(responseHeaders, "HttpHeaders is required");
|
||||
this.responseHeaders = responseHeaders;
|
||||
Assert.notNull(headers, "HttpHeaders is required");
|
||||
this.responseHeaders = headers;
|
||||
}
|
||||
|
||||
|
||||
@@ -90,21 +88,13 @@ public class WebOutput extends RequestOutput {
|
||||
*/
|
||||
public static final class Builder {
|
||||
|
||||
private final WebOutput webOutput;
|
||||
private final WebOutput original;
|
||||
|
||||
@Nullable
|
||||
private Object data;
|
||||
private final ExecutionResultImpl.Builder builder;
|
||||
|
||||
private List<GraphQLError> errors;
|
||||
|
||||
@Nullable
|
||||
private Map<Object, Object> extensions;
|
||||
|
||||
private Builder(WebOutput output) {
|
||||
this.webOutput = output;
|
||||
this.data = output.getData();
|
||||
this.errors = output.getErrors();
|
||||
this.extensions = output.getExtensions();
|
||||
private Builder(WebOutput original) {
|
||||
this.original = original;
|
||||
this.builder = ExecutionResultImpl.newExecutionResult().from(original.getExecutionResult());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,8 +102,8 @@ public class WebOutput extends RequestOutput {
|
||||
* @param data the execution result data
|
||||
* @return the current builder
|
||||
*/
|
||||
public Builder data(@Nullable Object data) {
|
||||
this.data = data;
|
||||
public Builder data(Object data) {
|
||||
this.builder.data(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -124,7 +114,7 @@ public class WebOutput extends RequestOutput {
|
||||
* @return the current builder
|
||||
*/
|
||||
public Builder errors(@Nullable List<GraphQLError> errors) {
|
||||
this.errors = (errors != null) ? errors : Collections.emptyList();
|
||||
this.builder.errors(errors);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -135,13 +125,13 @@ public class WebOutput extends RequestOutput {
|
||||
* @return the current builder
|
||||
*/
|
||||
public Builder extensions(@Nullable Map<Object, Object> extensions) {
|
||||
this.extensions = extensions;
|
||||
this.builder.extensions(extensions);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WebOutput build() {
|
||||
ExecutionResult result = new ExecutionResultImpl(this.data, this.errors, this.extensions);
|
||||
return new WebOutput(this.webOutput.getExecutionInput(), result, this.webOutput.getResponseHeaders());
|
||||
return new WebOutput(this.original.getExecutionInput(), this.builder.build(),
|
||||
this.original.getResponseHeaders());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,10 +20,10 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQLError;
|
||||
|
||||
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;
|
||||
@@ -63,6 +63,7 @@ public class GraphQlMessage {
|
||||
/**
|
||||
* Constructor for deserialization.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
GraphQlMessage() {
|
||||
this.type = GraphQlMessageType.NOT_SPECIFIED;
|
||||
}
|
||||
@@ -176,11 +177,21 @@ public class GraphQlMessage {
|
||||
/**
|
||||
* Create a {@code "next"} server message.
|
||||
* @param id unique request id
|
||||
* @param result the result from request execution to add as the message payload
|
||||
* @param output the output to obtain the result map from
|
||||
*/
|
||||
public static GraphQlMessage next(String id, ExecutionResult result) {
|
||||
Assert.notNull(result, "ExecutionResult is required");
|
||||
return new GraphQlMessage(id, GraphQlMessageType.NEXT, result.toSpecification());
|
||||
public static GraphQlMessage next(String id, RequestOutput output) {
|
||||
Assert.notNull(output, "'RequestOutput' is required");
|
||||
return next(id, output.toMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code "next"} server message.
|
||||
* @param id unique request id
|
||||
* @param responseMap the response map
|
||||
*/
|
||||
public static GraphQlMessage next(String id, Map<String, Object> responseMap) {
|
||||
Assert.notNull(responseMap, "'responseMap' is required");
|
||||
return new GraphQlMessage(id, GraphQlMessageType.NEXT, responseMap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
*/
|
||||
package org.springframework.graphql.web.webflux;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import java.util.Map;
|
||||
|
||||
import graphql.GraphQLError;
|
||||
import graphql.GraphqlErrorBuilder;
|
||||
|
||||
@@ -93,8 +94,8 @@ final class CodecDelegate {
|
||||
return encode(session, GraphQlMessage.connectionAck(ackPayload));
|
||||
}
|
||||
|
||||
public WebSocketMessage encodeNext(WebSocketSession session, String id, ExecutionResult result) {
|
||||
return encode(session, GraphQlMessage.next(id, result));
|
||||
public WebSocketMessage encodeNext(WebSocketSession session, String id, Map<String, Object> responseMap) {
|
||||
return encode(session, GraphQlMessage.next(id, responseMap));
|
||||
}
|
||||
|
||||
public WebSocketMessage encodeError(WebSocketSession session, String id, Throwable ex) {
|
||||
|
||||
@@ -72,14 +72,13 @@ public class GraphQlHttpHandler {
|
||||
}
|
||||
return this.graphQlHandler.handleRequest(input);
|
||||
})
|
||||
.flatMap((output) -> {
|
||||
Map<String, Object> spec = output.toSpecification();
|
||||
.flatMap(output -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Execution complete");
|
||||
}
|
||||
ServerResponse.BodyBuilder builder = ServerResponse.ok();
|
||||
builder.headers(headers -> headers.putAll(output.getResponseHeaders()));
|
||||
return builder.bodyValue(spec);
|
||||
return builder.bodyValue(output.toMap());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -185,10 +185,11 @@ public class GraphQlWebSocketHandler implements WebSocketHandler {
|
||||
+ ".");
|
||||
}
|
||||
|
||||
Flux<ExecutionResult> outputFlux;
|
||||
Flux<Map<String, Object>> responseFlux;
|
||||
if (output.getData() instanceof Publisher) {
|
||||
// Subscription
|
||||
outputFlux = Flux.from((Publisher<ExecutionResult>) output.getData())
|
||||
responseFlux = Flux.from((Publisher<ExecutionResult>) output.getData())
|
||||
.map(ExecutionResult::toSpecification)
|
||||
.doOnSubscribe((subscription) -> {
|
||||
Subscription previous = subscriptions.putIfAbsent(id, subscription);
|
||||
if (previous != null) {
|
||||
@@ -198,11 +199,11 @@ public class GraphQlWebSocketHandler implements WebSocketHandler {
|
||||
}
|
||||
else {
|
||||
// Single response (query or mutation) that may contain errors
|
||||
outputFlux = Flux.just(output);
|
||||
responseFlux = Flux.just(output.toMap());
|
||||
}
|
||||
|
||||
return outputFlux
|
||||
.map(result -> this.codecDelegate.encodeNext(session, id, result))
|
||||
return responseFlux
|
||||
.map(responseMap -> this.codecDelegate.encodeNext(session, id, responseMap))
|
||||
.concatWith(Mono.fromCallable(() -> this.codecDelegate.encodeComplete(session, id)))
|
||||
.onErrorResume(ex -> {
|
||||
if (ex instanceof SubscriptionExistsException) {
|
||||
|
||||
@@ -82,14 +82,15 @@ public class GraphQlHttpHandler {
|
||||
logger.debug("Executing: " + input);
|
||||
}
|
||||
|
||||
Mono<ServerResponse> responseMono = this.graphQlHandler.handleRequest(input).map((output) -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Execution complete");
|
||||
}
|
||||
ServerResponse.BodyBuilder builder = ServerResponse.ok();
|
||||
builder.headers(headers -> headers.putAll(output.getResponseHeaders()));
|
||||
return builder.body(output.toSpecification());
|
||||
});
|
||||
Mono<ServerResponse> responseMono = this.graphQlHandler.handleRequest(input)
|
||||
.map(output -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Execution complete");
|
||||
}
|
||||
ServerResponse.BodyBuilder builder = ServerResponse.ok();
|
||||
builder.headers(headers -> headers.putAll(output.getResponseHeaders()));
|
||||
return builder.body(output.toMap());
|
||||
});
|
||||
|
||||
return ServerResponse.async(responseMono);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ import org.springframework.graphql.web.WebInput;
|
||||
import org.springframework.graphql.web.WebOutput;
|
||||
import org.springframework.graphql.web.WebSocketInterceptor;
|
||||
import org.springframework.graphql.web.support.GraphQlMessage;
|
||||
import org.springframework.graphql.web.support.GraphQlMessageType;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
@@ -226,10 +225,11 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub
|
||||
+ (!CollectionUtils.isEmpty(output.getErrors()) ? " with errors: " + output.getErrors() : "")
|
||||
+ ".");
|
||||
}
|
||||
Flux<ExecutionResult> outputFlux;
|
||||
Flux<Map<String, Object>> responseFlux;
|
||||
if (output.getData() instanceof Publisher) {
|
||||
// Subscription
|
||||
outputFlux = Flux.from((Publisher<ExecutionResult>) output.getData())
|
||||
responseFlux = Flux.from((Publisher<ExecutionResult>) output.getData())
|
||||
.map(ExecutionResult::toSpecification)
|
||||
.doOnSubscribe((subscription) -> {
|
||||
Subscription prev = getSessionInfo(session).getSubscriptions().putIfAbsent(id, subscription);
|
||||
if (prev != null) {
|
||||
@@ -239,11 +239,11 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub
|
||||
}
|
||||
else {
|
||||
// Single response (query or mutation) that may contain errors
|
||||
outputFlux = Flux.just(output);
|
||||
responseFlux = Flux.just(output.toMap());
|
||||
}
|
||||
|
||||
return outputFlux
|
||||
.map(result -> encode(GraphQlMessage.next(id, result)))
|
||||
return responseFlux
|
||||
.map(responseMap -> encode(GraphQlMessage.next(id, responseMap)))
|
||||
.concatWith(Mono.fromCallable(() -> encode(GraphQlMessage.complete(id))))
|
||||
.onErrorResume((ex) -> {
|
||||
if (ex instanceof SubscriptionExistsException) {
|
||||
|
||||
@@ -48,9 +48,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class GraphQlResponse {
|
||||
public class ResponseHelper {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GraphQlResponse.class);
|
||||
private static final Log logger = LogFactory.getLog(ResponseHelper.class);
|
||||
|
||||
|
||||
private final DocumentContext documentContext;
|
||||
@@ -60,9 +60,9 @@ public class GraphQlResponse {
|
||||
private boolean errorsChecked;
|
||||
|
||||
|
||||
private GraphQlResponse(ExecutionResult result) {
|
||||
this.documentContext = JsonPath.parse(result.toSpecification(), initJsonPathConfig());
|
||||
this.errors = result.getErrors();
|
||||
private ResponseHelper(Map<String, Object> responseMap, List<GraphQLError> errors) {
|
||||
this.documentContext = JsonPath.parse(responseMap, initJsonPathConfig());
|
||||
this.errors = errors;
|
||||
}
|
||||
|
||||
private static Configuration initJsonPathConfig() {
|
||||
@@ -73,7 +73,7 @@ public class GraphQlResponse {
|
||||
}
|
||||
|
||||
|
||||
public GraphQlResponse log() {
|
||||
public ResponseHelper log() {
|
||||
logger.debug("GraphQlResponse: " + this.documentContext.jsonString());
|
||||
return this;
|
||||
}
|
||||
@@ -132,27 +132,33 @@ public class GraphQlResponse {
|
||||
}
|
||||
|
||||
|
||||
public static GraphQlResponse from(ExecutionResult result) {
|
||||
return new GraphQlResponse(result);
|
||||
public static ResponseHelper forResult(ExecutionResult result) {
|
||||
return new ResponseHelper(result.toSpecification(), result.getErrors());
|
||||
}
|
||||
|
||||
public static GraphQlResponse from(Mono<? extends ExecutionResult> resultMono) {
|
||||
public static ResponseHelper forResult(Mono<? extends ExecutionResult> resultMono) {
|
||||
ExecutionResult result = resultMono.block(Duration.ofSeconds(5));
|
||||
assertThat(result).isNotNull();
|
||||
return from(result);
|
||||
return forResult(result);
|
||||
}
|
||||
|
||||
public static Flux<GraphQlResponse> forSubscription(ExecutionResult result) {
|
||||
public static ResponseHelper forResponse(Mono<? extends RequestOutput> outputMono) {
|
||||
RequestOutput output = outputMono.block(Duration.ofSeconds(5));
|
||||
assertThat(output).isNotNull();
|
||||
return forResult(output.getExecutionResult());
|
||||
}
|
||||
|
||||
public static Flux<ResponseHelper> forSubscription(ExecutionResult result) {
|
||||
assertThat(result.getErrors()).as("Errors present in GraphQL response").isEmpty();
|
||||
Publisher<ExecutionResult> publisher = result.getData();
|
||||
return Flux.from(publisher).map(GraphQlResponse::from);
|
||||
return Flux.from(publisher).map(ResponseHelper::forResult);
|
||||
}
|
||||
|
||||
@SuppressWarnings("BlockingMethodInNonBlockingContext")
|
||||
public static Flux<GraphQlResponse> forSubscription(Mono<? extends ExecutionResult> resultMono) {
|
||||
ExecutionResult result = resultMono.block(Duration.ofSeconds(5));
|
||||
assertThat(result).isNotNull();
|
||||
return forSubscription(result);
|
||||
public static Flux<ResponseHelper> forSubscription(Mono<? extends RequestOutput> resultMono) {
|
||||
RequestOutput output = resultMono.block(Duration.ofSeconds(5));
|
||||
assertThat(output).isNotNull();
|
||||
return forSubscription(output.getExecutionResult());
|
||||
}
|
||||
|
||||
|
||||
@@ -165,15 +171,15 @@ public class GraphQlResponse {
|
||||
}
|
||||
|
||||
public String message() {
|
||||
return GraphQlResponse.this.errors.get(index).getMessage();
|
||||
return ResponseHelper.this.errors.get(index).getMessage();
|
||||
}
|
||||
|
||||
public String errorType() {
|
||||
return GraphQlResponse.this.errors.get(index).getErrorType().toString();
|
||||
return ResponseHelper.this.errors.get(index).getErrorType().toString();
|
||||
}
|
||||
|
||||
public Map<String, Object> extensions() {
|
||||
return GraphQlResponse.this.errors.get(index).getExtensions();
|
||||
return ResponseHelper.this.errors.get(index).getExtensions();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import java.util.function.Consumer;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import graphql.ExecutionInput;
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.ExecutionResultImpl;
|
||||
import graphql.GraphQLError;
|
||||
@@ -31,7 +30,8 @@ import org.mockito.ArgumentCaptor;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.graphql.GraphQlRequest;
|
||||
import org.springframework.graphql.RequestOutput;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.support.MapGraphQlResponse;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -80,14 +80,11 @@ public class GraphQlClientTestSupport {
|
||||
}
|
||||
|
||||
private void setMockResponse(Consumer<ExecutionResultImpl.Builder> consumer) {
|
||||
|
||||
ExecutionResultImpl.Builder builder = new ExecutionResultImpl.Builder();
|
||||
consumer.accept(builder);
|
||||
ExecutionInput executionInput = ExecutionInput.newExecutionInput("{}").build();
|
||||
ExecutionResult result = builder.build();
|
||||
|
||||
when(this.transport.execute(this.requestCaptor.capture()))
|
||||
.thenReturn(Mono.just(new RequestOutput(executionInput, result)));
|
||||
GraphQlResponse response = MapGraphQlResponse.forResponse(result.toSpecification());
|
||||
when(this.transport.execute(this.requestCaptor.capture())).thenReturn(Mono.just(response));
|
||||
}
|
||||
|
||||
private void serialize(String data, ExecutionResultImpl.Builder builder) {
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQLError;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -29,6 +28,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.graphql.GraphQlRequest;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.web.support.GraphQlMessage;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.reactive.socket.WebSocketHandler;
|
||||
@@ -100,7 +100,7 @@ public final class MockGraphQlWebSocketServer implements WebSocketHandler {
|
||||
return Flux.error(new IllegalStateException("Unexpected request: " + message));
|
||||
}
|
||||
return request.getResponseFlux()
|
||||
.map(result -> GraphQlMessage.next(id, result))
|
||||
.map(response -> GraphQlMessage.next(id, response.toMap()))
|
||||
.concatWithValues(
|
||||
request.getError() != null ?
|
||||
GraphQlMessage.error(id, request.getError()) :
|
||||
@@ -118,12 +118,12 @@ public final class MockGraphQlWebSocketServer implements WebSocketHandler {
|
||||
/**
|
||||
* Respond with the given a single result.
|
||||
*/
|
||||
GraphQlRequest andRespond(ExecutionResult result);
|
||||
GraphQlRequest andRespond(GraphQlResponse response);
|
||||
|
||||
/**
|
||||
* Respond with the given a single result {@code Mono}.
|
||||
*/
|
||||
GraphQlRequest andRespond(Mono<ExecutionResult> resultMono);
|
||||
GraphQlRequest andRespond(Mono<GraphQlResponse> responseMono);
|
||||
|
||||
/**
|
||||
* Respond with a GraphQL over WebSocket "error" message.
|
||||
@@ -133,12 +133,12 @@ public final class MockGraphQlWebSocketServer implements WebSocketHandler {
|
||||
/**
|
||||
* Respond with the given stream of responses.
|
||||
*/
|
||||
GraphQlRequest andStream(Flux<ExecutionResult> resultFlux);
|
||||
GraphQlRequest andStream(Flux<GraphQlResponse> responseFlux);
|
||||
|
||||
/**
|
||||
* Respond with the given stream of responses and terminate with an error.
|
||||
*/
|
||||
GraphQlRequest andStreamWithError(Flux<ExecutionResult> resultFlux, GraphQLError error);
|
||||
GraphQlRequest andStreamWithError(Flux<GraphQlResponse> responseFlux, GraphQLError error);
|
||||
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ public final class MockGraphQlWebSocketServer implements WebSocketHandler {
|
||||
|
||||
private final GraphQlRequest request;
|
||||
|
||||
private Flux<ExecutionResult> responseFlux = Flux.empty();
|
||||
private Flux<GraphQlResponse> responseFlux = Flux.empty();
|
||||
|
||||
@Nullable
|
||||
private GraphQLError error;
|
||||
@@ -158,13 +158,13 @@ public final class MockGraphQlWebSocketServer implements WebSocketHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphQlRequest andRespond(ExecutionResult result) {
|
||||
return addResponse(Flux.just(result), null);
|
||||
public GraphQlRequest andRespond(GraphQlResponse response) {
|
||||
return addResponse(Flux.just(response), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphQlRequest andRespond(Mono<ExecutionResult> resultMono) {
|
||||
return addResponse(Flux.from(resultMono), null);
|
||||
public GraphQlRequest andRespond(Mono<GraphQlResponse> responseMono) {
|
||||
return addResponse(Flux.from(responseMono), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,17 +173,17 @@ public final class MockGraphQlWebSocketServer implements WebSocketHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphQlRequest andStream(Flux<ExecutionResult> resultFlux) {
|
||||
return addResponse(resultFlux, null);
|
||||
public GraphQlRequest andStream(Flux<GraphQlResponse> responseFlux) {
|
||||
return addResponse(responseFlux, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphQlRequest andStreamWithError(Flux<ExecutionResult> resultFlux, GraphQLError error) {
|
||||
return addResponse(resultFlux, error);
|
||||
public GraphQlRequest andStreamWithError(Flux<GraphQlResponse> responseFlux, GraphQLError error) {
|
||||
return addResponse(responseFlux, error);
|
||||
}
|
||||
|
||||
private GraphQlRequest addResponse(Flux<ExecutionResult> resultFlux, @Nullable GraphQLError error) {
|
||||
this.responseFlux = resultFlux;
|
||||
private GraphQlRequest addResponse(Flux<GraphQlResponse> responseFlux, @Nullable GraphQLError error) {
|
||||
this.responseFlux = responseFlux;
|
||||
this.error = error;
|
||||
return this.request;
|
||||
}
|
||||
@@ -192,7 +192,7 @@ public final class MockGraphQlWebSocketServer implements WebSocketHandler {
|
||||
return this.request;
|
||||
}
|
||||
|
||||
public Flux<ExecutionResult> getResponseFlux() {
|
||||
public Flux<GraphQlResponse> getResponseFlux() {
|
||||
return this.responseFlux;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQLError;
|
||||
import graphql.GraphqlErrorBuilder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -34,7 +33,8 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.graphql.GraphQlRequest;
|
||||
import org.springframework.graphql.support.MapExecutionResult;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.support.MapGraphQlResponse;
|
||||
import org.springframework.graphql.web.TestWebSocketClient;
|
||||
import org.springframework.graphql.web.TestWebSocketConnection;
|
||||
import org.springframework.graphql.web.support.GraphQlMessage;
|
||||
@@ -70,17 +70,17 @@ public class MockWebSocketGraphQlTransportTests {
|
||||
|
||||
private final WebSocketGraphQlTransport transport = createTransport(this.webSocketClient);
|
||||
|
||||
private final ExecutionResult result1 = MapExecutionResult.forDataOnly(Collections.singletonMap("key1", "value1"));
|
||||
private final GraphQlResponse response1 = MapGraphQlResponse.forDataOnly(Collections.singletonMap("key1", "value1"));
|
||||
|
||||
private final ExecutionResult result2 = MapExecutionResult.forDataOnly(Collections.singletonMap("key2", "value2"));
|
||||
private final GraphQlResponse response2 = MapGraphQlResponse.forDataOnly(Collections.singletonMap("key2", "value2"));
|
||||
|
||||
|
||||
@Test
|
||||
void request() {
|
||||
GraphQlRequest request = this.mockServer.expectOperation("{Query1}").andRespond(this.result1);
|
||||
GraphQlRequest request = this.mockServer.expectOperation("{Query1}").andRespond(this.response1);
|
||||
|
||||
StepVerifier.create(this.transport.execute(request))
|
||||
.expectNext(this.result1).expectComplete()
|
||||
.expectNext(this.response1).expectComplete()
|
||||
.verify(TIMEOUT);
|
||||
|
||||
assertActualClientMessages(
|
||||
@@ -90,10 +90,10 @@ public class MockWebSocketGraphQlTransportTests {
|
||||
|
||||
@Test
|
||||
void requestStream() {
|
||||
GraphQlRequest request = this.mockServer.expectOperation("{Sub1}").andStream(Flux.just(this.result1, result2));
|
||||
GraphQlRequest request = this.mockServer.expectOperation("{Sub1}").andStream(Flux.just(this.response1, response2));
|
||||
|
||||
StepVerifier.create(this.transport.executeSubscription(request))
|
||||
.expectNext(this.result1, result2).expectComplete()
|
||||
.expectNext(this.response1, response2).expectComplete()
|
||||
.verify(TIMEOUT);
|
||||
|
||||
assertActualClientMessages(
|
||||
@@ -108,7 +108,7 @@ public class MockWebSocketGraphQlTransportTests {
|
||||
|
||||
StepVerifier.create(this.transport.execute(request))
|
||||
.consumeNextWith(result -> {
|
||||
assertThat(result.isDataPresent()).isFalse();
|
||||
assertThat(result.isValid()).isFalse();
|
||||
assertThat(result.getErrors()).extracting(GraphQLError::getMessage).containsExactly("boo");
|
||||
})
|
||||
.expectComplete()
|
||||
@@ -122,10 +122,10 @@ public class MockWebSocketGraphQlTransportTests {
|
||||
@Test
|
||||
void requestStreamError() {
|
||||
GraphQlRequest request = this.mockServer.expectOperation("{Sub1}")
|
||||
.andStreamWithError(Flux.just(this.result1), GraphqlErrorBuilder.newError().message("boo").build());
|
||||
.andStreamWithError(Flux.just(this.response1), GraphqlErrorBuilder.newError().message("boo").build());
|
||||
|
||||
StepVerifier.create(this.transport.executeSubscription(request))
|
||||
.expectNext(this.result1)
|
||||
.expectNext(this.response1)
|
||||
.expectErrorSatisfies(actualEx -> {
|
||||
List<GraphQLError> errorList = ((SubscriptionErrorException) actualEx).getErrors();
|
||||
assertThat(errorList).extracting(GraphQLError::getMessage).containsExactly("boo");
|
||||
@@ -154,10 +154,10 @@ public class MockWebSocketGraphQlTransportTests {
|
||||
@Test
|
||||
void requestStreamCancelled() {
|
||||
GraphQlRequest request = this.mockServer.expectOperation("{Sub1}")
|
||||
.andStream(Flux.just(this.result1).concatWith(Flux.never()));
|
||||
.andStream(Flux.just(this.response1).concatWith(Flux.never()));
|
||||
|
||||
StepVerifier.create(this.transport.executeSubscription(request))
|
||||
.expectNext(this.result1)
|
||||
.expectNext(this.response1)
|
||||
.thenAwait(Duration.ofMillis(200))
|
||||
.thenCancel()
|
||||
.verify(TIMEOUT);
|
||||
@@ -171,11 +171,11 @@ public class MockWebSocketGraphQlTransportTests {
|
||||
@Test
|
||||
void pingHandling() {
|
||||
|
||||
TestWebSocketClient client = new TestWebSocketClient(new PingResponseHandler(this.result1));
|
||||
TestWebSocketClient client = new TestWebSocketClient(new PingResponseHandler(this.response1));
|
||||
WebSocketGraphQlTransport transport = createTransport(client);
|
||||
|
||||
StepVerifier.create(transport.execute(new GraphQlRequest("{Query1}")))
|
||||
.expectNext(this.result1)
|
||||
.expectNext(this.response1)
|
||||
.expectComplete()
|
||||
.verify(TIMEOUT);
|
||||
|
||||
@@ -219,7 +219,7 @@ public class MockWebSocketGraphQlTransportTests {
|
||||
assertThat(this.webSocketClient.getConnection(0).closeStatus().block(TIMEOUT)).isEqualTo(CloseStatus.NORMAL);
|
||||
|
||||
// New requests are rejected
|
||||
GraphQlRequest request = this.mockServer.expectOperation("{Query1}").andRespond(this.result1);
|
||||
GraphQlRequest request = this.mockServer.expectOperation("{Query1}").andRespond(this.response1);
|
||||
StepVerifier.create(this.transport.execute(request))
|
||||
.expectErrorMessage("WebSocketGraphQlTransport has been stopped")
|
||||
.verify(TIMEOUT);
|
||||
@@ -230,23 +230,23 @@ public class MockWebSocketGraphQlTransportTests {
|
||||
assertThat(this.webSocketClient.getConnection(1).isOpen()).isTrue();
|
||||
|
||||
// Requests allowed again
|
||||
request = this.mockServer.expectOperation("{Query1}").andRespond(this.result1);
|
||||
request = this.mockServer.expectOperation("{Query1}").andRespond(this.response1);
|
||||
StepVerifier.create(this.transport.execute(request))
|
||||
.expectNext(this.result1).expectComplete()
|
||||
.expectNext(this.response1).expectComplete()
|
||||
.verify(TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sessionIsCachedUntilClosed() {
|
||||
|
||||
GraphQlRequest request1 = this.mockServer.expectOperation("{Query1}").andRespond(this.result1);
|
||||
StepVerifier.create(this.transport.execute(request1)).expectNext(this.result1).expectComplete().verify(TIMEOUT);
|
||||
GraphQlRequest request1 = this.mockServer.expectOperation("{Query1}").andRespond(this.response1);
|
||||
StepVerifier.create(this.transport.execute(request1)).expectNext(this.response1).expectComplete().verify(TIMEOUT);
|
||||
|
||||
assertThat(this.webSocketClient.getConnectionCount()).isEqualTo(1);
|
||||
TestWebSocketConnection originalConnection = this.webSocketClient.getConnection(0);
|
||||
|
||||
GraphQlRequest request2 = this.mockServer.expectOperation("{Query2}").andRespond(this.result2);
|
||||
StepVerifier.create(this.transport.execute(request2)).expectNext(this.result2).expectComplete().verify(TIMEOUT);
|
||||
GraphQlRequest request2 = this.mockServer.expectOperation("{Query2}").andRespond(this.response2);
|
||||
StepVerifier.create(this.transport.execute(request2)).expectNext(this.response2).expectComplete().verify(TIMEOUT);
|
||||
|
||||
assertThat(this.webSocketClient.getConnectionCount()).isEqualTo(1);
|
||||
assertThat(this.webSocketClient.getConnection(0)).isSameAs(originalConnection);
|
||||
@@ -254,8 +254,8 @@ public class MockWebSocketGraphQlTransportTests {
|
||||
// Close the connection
|
||||
originalConnection.closeServerSession(CloseStatus.NORMAL).block(TIMEOUT);
|
||||
|
||||
request1 = this.mockServer.expectOperation("{Query1}").andRespond(this.result1);
|
||||
StepVerifier.create(this.transport.execute(request1)).expectNext(this.result1).expectComplete().verify(TIMEOUT);
|
||||
request1 = this.mockServer.expectOperation("{Query1}").andRespond(this.response1);
|
||||
StepVerifier.create(this.transport.execute(request1)).expectNext(this.response1).expectComplete().verify(TIMEOUT);
|
||||
|
||||
assertThat(this.webSocketClient.getConnectionCount()).isEqualTo(2);
|
||||
assertThat(this.webSocketClient.getConnection(1)).isNotSameAs(originalConnection);
|
||||
@@ -332,12 +332,12 @@ public class MockWebSocketGraphQlTransportTests {
|
||||
*/
|
||||
private static class PingResponseHandler implements WebSocketHandler {
|
||||
|
||||
private final ExecutionResult result;
|
||||
private final GraphQlResponse response;
|
||||
|
||||
private final CodecDelegate codecDelegate = new CodecDelegate();
|
||||
|
||||
private PingResponseHandler(ExecutionResult result) {
|
||||
this.result = result;
|
||||
private PingResponseHandler(GraphQlResponse response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -349,7 +349,7 @@ public class MockWebSocketGraphQlTransportTests {
|
||||
case CONNECTION_INIT:
|
||||
return Flux.just(GraphQlMessage.connectionAck(null), GraphQlMessage.ping(null));
|
||||
case SUBSCRIBE:
|
||||
return Flux.just(GraphQlMessage.next("1", this.result));
|
||||
return Flux.just(GraphQlMessage.next("1", this.response.toMap()));
|
||||
case PONG:
|
||||
return Flux.empty();
|
||||
default:
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.junit.jupiter.params.provider.MethodSource;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.ResponseHelper;
|
||||
import org.springframework.graphql.RequestOutput;
|
||||
import org.springframework.graphql.TestRequestInput;
|
||||
import org.springframework.graphql.data.method.annotation.BatchMapping;
|
||||
@@ -70,10 +70,10 @@ public class BatchMappingInvocationTests extends BatchMappingTestSupport {
|
||||
" }" +
|
||||
"}";
|
||||
|
||||
Mono<RequestOutput> resultMono = createGraphQlService(controller)
|
||||
Mono<RequestOutput> outputMono = createGraphQlService(controller)
|
||||
.execute(TestRequestInput.forDocument(query));
|
||||
|
||||
List<Course> actualCourses = GraphQlResponse.from(resultMono).toList("courses", Course.class);
|
||||
List<Course> actualCourses = ResponseHelper.forResponse(outputMono).toList("courses", Course.class);
|
||||
List<Course> courses = Course.allCourses();
|
||||
assertThat(actualCourses).hasSize(courses.size());
|
||||
|
||||
@@ -103,10 +103,10 @@ public class BatchMappingInvocationTests extends BatchMappingTestSupport {
|
||||
" }" +
|
||||
"}";
|
||||
|
||||
Mono<RequestOutput> resultMono = createGraphQlService(controller)
|
||||
Mono<RequestOutput> outputMono = createGraphQlService(controller)
|
||||
.execute(TestRequestInput.forDocument(document));
|
||||
|
||||
List<Course> actualCourses = GraphQlResponse.from(resultMono).toList("courses", Course.class);
|
||||
List<Course> actualCourses = ResponseHelper.forResponse(outputMono).toList("courses", Course.class);
|
||||
List<Course> courses = Course.allCourses();
|
||||
assertThat(actualCourses).hasSize(courses.size());
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.context.Context;
|
||||
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.ResponseHelper;
|
||||
import org.springframework.graphql.RequestOutput;
|
||||
import org.springframework.graphql.TestRequestInput;
|
||||
import org.springframework.graphql.data.method.annotation.BatchMapping;
|
||||
@@ -92,14 +92,14 @@ public class BatchMappingPrincipalMethodArgumentResolverTests extends BatchMappi
|
||||
}
|
||||
|
||||
private void testBatchLoading(PrincipalCourseController controller, Function<Context, Context> contextWriter) {
|
||||
Mono<RequestOutput> resultMono = Mono.delay(Duration.ofMillis(10))
|
||||
Mono<RequestOutput> outputMono = Mono.delay(Duration.ofMillis(10))
|
||||
.flatMap(aLong -> {
|
||||
String document = "{ courses { id instructor { id } } }";
|
||||
return createGraphQlService(controller).execute(TestRequestInput.forDocument(document));
|
||||
})
|
||||
.contextWrite(contextWriter);
|
||||
|
||||
List<Course> actualCourses = GraphQlResponse.from(resultMono).toList("courses", Course.class);
|
||||
List<Course> actualCourses = ResponseHelper.forResponse(outputMono).toList("courses", Course.class);
|
||||
List<Course> courses = Course.allCourses();
|
||||
assertThat(actualCourses).hasSize(courses.size());
|
||||
for (int i = 0; i < courses.size(); i++) {
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.springframework.graphql.Author;
|
||||
import org.springframework.graphql.Book;
|
||||
import org.springframework.graphql.BookCriteria;
|
||||
import org.springframework.graphql.BookSource;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.ResponseHelper;
|
||||
import org.springframework.graphql.GraphQlService;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.RequestInput;
|
||||
@@ -71,9 +71,9 @@ public class SchemaMappingInvocationTests {
|
||||
" }" +
|
||||
"}";
|
||||
|
||||
Mono<RequestOutput> resultMono = graphQlService().execute(TestRequestInput.forDocument(document));
|
||||
Mono<RequestOutput> outputMono = graphQlService().execute(TestRequestInput.forDocument(document));
|
||||
|
||||
Book book = GraphQlResponse.from(resultMono).toEntity("bookById", Book.class);
|
||||
Book book = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(book.getId()).isEqualTo(1);
|
||||
assertThat(book.getName()).isEqualTo("Nineteen Eighty-Four");
|
||||
|
||||
@@ -91,9 +91,9 @@ public class SchemaMappingInvocationTests {
|
||||
" }" +
|
||||
"}";
|
||||
|
||||
Mono<RequestOutput> resultMono = graphQlService().execute(TestRequestInput.forDocument(document));
|
||||
Mono<RequestOutput> outputMono = graphQlService().execute(TestRequestInput.forDocument(document));
|
||||
|
||||
List<Book> bookList = GraphQlResponse.from(resultMono).toList("booksByCriteria", Book.class);
|
||||
List<Book> bookList = ResponseHelper.forResponse(outputMono).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<RequestOutput> resultMono = graphQlService().execute(TestRequestInput.forDocument(document));
|
||||
Mono<RequestOutput> outputMono = graphQlService().execute(TestRequestInput.forDocument(document));
|
||||
|
||||
List<Book> bookList = GraphQlResponse.from(resultMono).toList("booksByProjectedArguments", Book.class);
|
||||
List<Book> bookList = ResponseHelper.forResponse(outputMono).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<RequestOutput> resultMono = graphQlService().execute(TestRequestInput.forDocument(document));
|
||||
Mono<RequestOutput> outputMono = graphQlService().execute(TestRequestInput.forDocument(document));
|
||||
|
||||
List<Book> bookList = GraphQlResponse.from(resultMono).toList("booksByProjectedCriteria", Book.class);
|
||||
List<Book> bookList = ResponseHelper.forResponse(outputMono).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");
|
||||
@@ -150,9 +150,9 @@ public class SchemaMappingInvocationTests {
|
||||
return executionInput;
|
||||
});
|
||||
|
||||
Mono<RequestOutput> resultMono = graphQlService().execute(requestInput);
|
||||
Mono<RequestOutput> outputMono = graphQlService().execute(requestInput);
|
||||
|
||||
Author author = GraphQlResponse.from(resultMono).toEntity("authorById", Author.class);
|
||||
Author author = ResponseHelper.forResponse(outputMono).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<RequestOutput> resultMono = graphQlService().execute(TestRequestInput.forDocument(document));
|
||||
Mono<RequestOutput> outputMono = graphQlService().execute(TestRequestInput.forDocument(document));
|
||||
|
||||
Author author = GraphQlResponse.from(resultMono).toEntity("addAuthor", Author.class);
|
||||
Author author = ResponseHelper.forResponse(outputMono).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<RequestOutput> resultMono = graphQlService().execute(TestRequestInput.forDocument(document));
|
||||
Mono<RequestOutput> outputMono = graphQlService().execute(TestRequestInput.forDocument(document));
|
||||
|
||||
Flux<Book> bookFlux = GraphQlResponse.forSubscription(resultMono)
|
||||
Flux<Book> bookFlux = ResponseHelper.forSubscription(outputMono)
|
||||
.map(response -> response.toEntity("bookSearch", Book.class));
|
||||
|
||||
StepVerifier.create(bookFlux)
|
||||
|
||||
@@ -31,7 +31,7 @@ import reactor.util.context.Context;
|
||||
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.ResponseHelper;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.RequestOutput;
|
||||
import org.springframework.graphql.TestRequestInput;
|
||||
@@ -106,7 +106,7 @@ public class SchemaMappingPrincipalMethodArgumentResolverTests {
|
||||
Mono<RequestOutput> resultMono = executeAsync(
|
||||
"type Query { " + field + ": String }", "{ " + field + " }", contextWriter);
|
||||
|
||||
String greeting = GraphQlResponse.from(resultMono).toEntity(field, String.class);
|
||||
String greeting = ResponseHelper.forResponse(resultMono).toEntity(field, String.class);
|
||||
assertThat(greeting).isEqualTo("Hello");
|
||||
assertThat(greetingController.principal()).isSameAs(authentication);
|
||||
}
|
||||
@@ -141,7 +141,7 @@ public class SchemaMappingPrincipalMethodArgumentResolverTests {
|
||||
"subscription Greeting { " + field + " }",
|
||||
contextModifier);
|
||||
|
||||
Flux<String> greetingFlux = GraphQlResponse.forSubscription(resultMono)
|
||||
Flux<String> greetingFlux = ResponseHelper.forSubscription(resultMono)
|
||||
.map(response -> response.toEntity(field, String.class));
|
||||
|
||||
StepVerifier.create(greetingFlux).expectNext("Hello", "Hi").verifyComplete();
|
||||
|
||||
@@ -43,7 +43,7 @@ import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.graphql.Author;
|
||||
import org.springframework.graphql.BookSource;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.ResponseHelper;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.data.GraphQlRepository;
|
||||
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
|
||||
@@ -77,7 +77,7 @@ class QuerydslDataFetcherTests {
|
||||
|
||||
Consumer<GraphQlSetup> tester = setup -> {
|
||||
Mono<WebOutput> output = setup.toWebGraphQlHandler().handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
Book actualBook = GraphQlResponse.from(output).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(output).toEntity("bookById", Book.class);
|
||||
|
||||
assertThat(actualBook.getName()).isEqualTo(book.getName());
|
||||
};
|
||||
@@ -98,7 +98,7 @@ class QuerydslDataFetcherTests {
|
||||
Consumer<GraphQlSetup> tester = graphQlSetup -> {
|
||||
Mono<WebOutput> output = graphQlSetup.toWebGraphQlHandler().handleRequest(input("{ books {name}}"));
|
||||
|
||||
List<String> names = GraphQlResponse.from(output).toList("books", Book.class)
|
||||
List<String> names = ResponseHelper.forResponse(output).toList("books", Book.class)
|
||||
.stream().map(Book::getName).collect(Collectors.toList());
|
||||
|
||||
assertThat(names).containsExactlyInAnyOrder(book1.getName(), book2.getName());
|
||||
@@ -120,7 +120,7 @@ class QuerydslDataFetcherTests {
|
||||
Mono<WebOutput> output = graphQlSetup(mockRepository).toWebGraphQlHandler()
|
||||
.handleRequest(input("{ booksById(id: [42,53]) {name}}"));
|
||||
|
||||
List<String> names = GraphQlResponse.from(output).toList("booksById", Book.class)
|
||||
List<String> names = ResponseHelper.forResponse(output).toList("booksById", Book.class)
|
||||
.stream().map(Book::getName).collect(Collectors.toList());
|
||||
|
||||
assertThat(names).containsExactlyInAnyOrder(book1.getName(), book2.getName());
|
||||
@@ -136,7 +136,7 @@ class QuerydslDataFetcherTests {
|
||||
Consumer<GraphQlSetup> tester = graphQlSetup -> {
|
||||
Mono<WebOutput> output = graphQlSetup.toWebGraphQlHandler().handleRequest(input("{ books {name}}"));
|
||||
|
||||
List<String> names = GraphQlResponse.from(output).toList("books", Book.class)
|
||||
List<String> names = ResponseHelper.forResponse(output).toList("books", Book.class)
|
||||
.stream().map(Book::getName).collect(Collectors.toList());
|
||||
|
||||
assertThat(names).containsExactlyInAnyOrder(book1.getName(), book2.getName());
|
||||
@@ -179,7 +179,7 @@ class QuerydslDataFetcherTests {
|
||||
WebGraphQlHandler handler = graphQlSetup(mockRepository).toWebGraphQlHandler();
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 1) {name}}"));
|
||||
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("Hitchhiker's Guide to the Galaxy");
|
||||
|
||||
// 2) Automatic registration and explicit wiring
|
||||
@@ -189,7 +189,7 @@ class QuerydslDataFetcherTests {
|
||||
|
||||
outputMono = handler.handleRequest(input("{ bookById(id: 1) {name}}"));
|
||||
|
||||
actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("Breaking Bad");
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ class QuerydslDataFetcherTests {
|
||||
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("Hitchhiker's Guide to the Galaxy by Douglas Adams");
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ class QuerydslDataFetcherTests {
|
||||
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("The book is: Hitchhiker's Guide to the Galaxy");
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ class QuerydslDataFetcherTests {
|
||||
|
||||
Consumer<GraphQlSetup> tester = setup -> {
|
||||
Mono<WebOutput> outputMono = setup.toWebGraphQlHandler().handleRequest(input("{ bookById(id: 1) {name}}"));
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
|
||||
assertThat(actualBook.getName()).isEqualTo(book.getName());
|
||||
};
|
||||
@@ -251,7 +251,7 @@ class QuerydslDataFetcherTests {
|
||||
Consumer<GraphQlSetup> tester = setup -> {
|
||||
Mono<WebOutput> outputMono = setup.toWebGraphQlHandler().handleRequest(input("{ books {name}}"));
|
||||
|
||||
List<String> names = GraphQlResponse.from(outputMono).toList("books", Book.class)
|
||||
List<String> names = ResponseHelper.forResponse(outputMono).toList("books", Book.class)
|
||||
.stream().map(Book::getName).collect(Collectors.toList());
|
||||
|
||||
assertThat(names).containsExactlyInAnyOrder("Breaking Bad", "Hitchhiker's Guide to the Galaxy");
|
||||
|
||||
@@ -40,7 +40,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.data.repository.query.QueryByExampleExecutor;
|
||||
import org.springframework.graphql.BookSource;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.ResponseHelper;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.data.query.QueryByExampleDataFetcher;
|
||||
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
|
||||
@@ -80,7 +80,7 @@ class QueryByExampleDataFetcherJpaTests {
|
||||
|
||||
Consumer<GraphQlSetup> tester = setup -> {
|
||||
Mono<WebOutput> output = setup.toWebGraphQlHandler().handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
Book actualBook = GraphQlResponse.from(output).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(output).toEntity("bookById", Book.class);
|
||||
|
||||
assertThat(actualBook.getName()).isEqualTo(book.getName());
|
||||
};
|
||||
@@ -101,7 +101,7 @@ class QueryByExampleDataFetcherJpaTests {
|
||||
Consumer<GraphQlSetup> tester = graphQlSetup -> {
|
||||
Mono<WebOutput> output = graphQlSetup.toWebGraphQlHandler().handleRequest(input("{ books {name}}"));
|
||||
|
||||
List<String> names = GraphQlResponse.from(output).toList("books", Book.class)
|
||||
List<String> names = ResponseHelper.forResponse(output).toList("books", Book.class)
|
||||
.stream()
|
||||
.map(Book::getName)
|
||||
.collect(Collectors.toList());
|
||||
@@ -126,7 +126,7 @@ class QueryByExampleDataFetcherJpaTests {
|
||||
WebGraphQlHandler handler = graphQlSetup(mockRepository).toWebGraphQlHandler();
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 1) {name}}"));
|
||||
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("Hitchhiker's Guide to the Galaxy");
|
||||
|
||||
// 2) Automatic registration and explicit wiring
|
||||
@@ -136,7 +136,7 @@ class QueryByExampleDataFetcherJpaTests {
|
||||
|
||||
outputMono = handler.handleRequest(input("{ bookById(id: 1) {name}}"));
|
||||
|
||||
actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("Breaking Bad");
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ class QueryByExampleDataFetcherJpaTests {
|
||||
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("Hitchhiker's Guide to the Galaxy by Douglas Adams");
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ class QueryByExampleDataFetcherJpaTests {
|
||||
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("The book is: Hitchhiker's Guide to the Galaxy");
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
|
||||
import org.springframework.data.repository.query.QueryByExampleExecutor;
|
||||
import org.springframework.graphql.BookSource;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.ResponseHelper;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.data.query.QueryByExampleDataFetcher;
|
||||
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
|
||||
@@ -81,7 +81,7 @@ class QueryByExampleDataFetcherMongoDbTests {
|
||||
|
||||
Consumer<GraphQlSetup> tester = setup -> {
|
||||
Mono<WebOutput> output = setup.toWebGraphQlHandler().handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
Book actualBook = GraphQlResponse.from(output).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(output).toEntity("bookById", Book.class);
|
||||
|
||||
assertThat(actualBook.getName()).isEqualTo(book.getName());
|
||||
};
|
||||
@@ -102,7 +102,7 @@ class QueryByExampleDataFetcherMongoDbTests {
|
||||
Consumer<GraphQlSetup> tester = graphQlSetup -> {
|
||||
Mono<WebOutput> output = graphQlSetup.toWebGraphQlHandler().handleRequest(input("{ books {name}}"));
|
||||
|
||||
List<String> names = GraphQlResponse.from(output).toList("books", Book.class)
|
||||
List<String> names = ResponseHelper.forResponse(output).toList("books", Book.class)
|
||||
.stream().map(Book::getName).collect(Collectors.toList());
|
||||
|
||||
assertThat(names).containsExactlyInAnyOrder(book1.getName(), book2.getName());
|
||||
@@ -125,7 +125,7 @@ class QueryByExampleDataFetcherMongoDbTests {
|
||||
WebGraphQlHandler handler = graphQlSetup(mockRepository).toWebGraphQlHandler();
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 1) {name}}"));
|
||||
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("Hitchhiker's Guide to the Galaxy");
|
||||
|
||||
// 2) Automatic registration and explicit wiring
|
||||
@@ -135,7 +135,7 @@ class QueryByExampleDataFetcherMongoDbTests {
|
||||
|
||||
outputMono = handler.handleRequest(input("{ bookById(id: 1) {name}}"));
|
||||
|
||||
actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("Breaking Bad");
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ class QueryByExampleDataFetcherMongoDbTests {
|
||||
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("Hitchhiker's Guide to the Galaxy by Douglas Adams");
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ class QueryByExampleDataFetcherMongoDbTests {
|
||||
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("The book is: Hitchhiker's Guide to the Galaxy");
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
||||
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;
|
||||
import org.springframework.data.repository.query.ReactiveQueryByExampleExecutor;
|
||||
import org.springframework.graphql.BookSource;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.ResponseHelper;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.data.query.QueryByExampleDataFetcher;
|
||||
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
|
||||
@@ -77,7 +77,7 @@ class QueryByExampleDataFetcherReactiveMongoDbTests {
|
||||
|
||||
Consumer<GraphQlSetup> tester = setup -> {
|
||||
Mono<WebOutput> outputMono = setup.toWebGraphQlHandler().handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
|
||||
assertThat(actualBook.getName()).isEqualTo(book.getName());
|
||||
};
|
||||
@@ -99,7 +99,7 @@ class QueryByExampleDataFetcherReactiveMongoDbTests {
|
||||
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("Hitchhiker's Guide to the Galaxy by Douglas Adams");
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ class QueryByExampleDataFetcherReactiveMongoDbTests {
|
||||
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
Book actualBook = ResponseHelper.forResponse(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("The book is: Hitchhiker's Guide to the Galaxy");
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ class QueryByExampleDataFetcherReactiveMongoDbTests {
|
||||
Consumer<GraphQlSetup> tester = setup -> {
|
||||
Mono<WebOutput> outputMono = setup.toWebGraphQlHandler().handleRequest(input("{ books {name}}"));
|
||||
|
||||
List<String> names = GraphQlResponse.from(outputMono).toList("books", Book.class)
|
||||
List<String> names = ResponseHelper.forResponse(outputMono).toList("books", Book.class)
|
||||
.stream().map(Book::getName).collect(Collectors.toList());
|
||||
|
||||
assertThat(names).containsExactlyInAnyOrder("Breaking Bad", "Hitchhiker's Guide to the Galaxy");
|
||||
|
||||
@@ -27,7 +27,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.graphql.Author;
|
||||
import org.springframework.graphql.Book;
|
||||
import org.springframework.graphql.BookSource;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.ResponseHelper;
|
||||
import org.springframework.graphql.GraphQlService;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.RequestOutput;
|
||||
@@ -76,9 +76,9 @@ public class BatchLoadingTests {
|
||||
.dataLoaders(this.registry)
|
||||
.toGraphQlService();
|
||||
|
||||
Mono<RequestOutput> resultMono = service.execute(TestRequestInput.forDocument(document));
|
||||
Mono<RequestOutput> outputMono = service.execute(TestRequestInput.forDocument(document));
|
||||
|
||||
List<Book> books = GraphQlResponse.from(resultMono).toList("booksByCriteria", Book.class);
|
||||
List<Book> books = ResponseHelper.forResponse(outputMono).toList("booksByCriteria", Book.class);
|
||||
assertThat(books).hasSize(2);
|
||||
|
||||
Author author = books.get(0).getAuthor();
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.ResponseHelper;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.RequestOutput;
|
||||
import org.springframework.graphql.TestRequestInput;
|
||||
@@ -83,11 +83,11 @@ public class ClassNameTypeResolverTests {
|
||||
" }" +
|
||||
"}";
|
||||
|
||||
Mono<RequestOutput> resultMono = graphQlSetup.queryFetcher("animals", env -> animalList)
|
||||
Mono<RequestOutput> outputMono = graphQlSetup.queryFetcher("animals", env -> animalList)
|
||||
.toGraphQlService()
|
||||
.execute(TestRequestInput.forDocument(document));
|
||||
|
||||
GraphQlResponse response = GraphQlResponse.from(resultMono);
|
||||
ResponseHelper response = ResponseHelper.forResponse(outputMono);
|
||||
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<RequestOutput> result = graphQlSetup.queryFetcher("sightings", env -> animalAndPlantList)
|
||||
Mono<RequestOutput> output = graphQlSetup.queryFetcher("sightings", env -> animalAndPlantList)
|
||||
.typeResolver(typeResolver)
|
||||
.toGraphQlService()
|
||||
.execute(TestRequestInput.forDocument(document));
|
||||
|
||||
GraphQlResponse response = GraphQlResponse.from(result);
|
||||
ResponseHelper response = ResponseHelper.forResponse(output);
|
||||
for (int i = 0; i < animalAndPlantList.size(); i++) {
|
||||
Object sighting = animalAndPlantList.get(i);
|
||||
if (sighting instanceof Animal) {
|
||||
|
||||
@@ -29,7 +29,7 @@ import reactor.test.StepVerifier;
|
||||
import reactor.util.context.Context;
|
||||
import reactor.util.context.ContextView;
|
||||
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.ResponseHelper;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.TestThreadLocalAccessor;
|
||||
|
||||
@@ -56,7 +56,7 @@ public class ContextDataFetcherDecoratorTests {
|
||||
|
||||
ExecutionResult executionResult = graphQl.executeAsync(input).get();
|
||||
|
||||
String greeting = GraphQlResponse.from(executionResult).toEntity("greeting", String.class);
|
||||
String greeting = ResponseHelper.forResult(executionResult).toEntity("greeting", String.class);
|
||||
assertThat(greeting).isEqualTo("Hello 007");
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ public class ContextDataFetcherDecoratorTests {
|
||||
|
||||
ExecutionResult result = graphQl.executeAsync(input).get();
|
||||
|
||||
List<String> data = GraphQlResponse.from(result).toList("greetings", String.class);
|
||||
List<String> data = ResponseHelper.forResult(result).toList("greetings", String.class);
|
||||
assertThat(data).containsExactly("Hi 007", "Bonjour 007", "Hola 007");
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ public class ContextDataFetcherDecoratorTests {
|
||||
|
||||
ExecutionResult executionResult = graphQl.executeAsync(input).get();
|
||||
|
||||
Flux<String> greetingsFlux = GraphQlResponse.forSubscription(executionResult)
|
||||
Flux<String> greetingsFlux = ResponseHelper.forSubscription(executionResult)
|
||||
.map(response -> response.toEntity("greetings", String.class));
|
||||
|
||||
StepVerifier.create(greetingsFlux)
|
||||
@@ -121,7 +121,7 @@ public class ContextDataFetcherDecoratorTests {
|
||||
Mono<ExecutionResult> resultMono = Mono.delay(Duration.ofMillis(10))
|
||||
.flatMap((aLong) -> Mono.fromFuture(graphQl.executeAsync(input)));
|
||||
|
||||
String greeting = GraphQlResponse.from(resultMono).toEntity("greeting", String.class);
|
||||
String greeting = ResponseHelper.forResult(resultMono).toEntity("greeting", String.class);
|
||||
assertThat(greeting).isEqualTo("Hello 007");
|
||||
}
|
||||
finally {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,19 +18,16 @@ package org.springframework.graphql.execution;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import graphql.ExecutionInput;
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQLError;
|
||||
import graphql.GraphqlErrorBuilder;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.context.Context;
|
||||
import reactor.util.context.ContextView;
|
||||
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.ResponseHelper;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.TestThreadLocalAccessor;
|
||||
|
||||
@@ -62,7 +59,7 @@ public class ExceptionResolversExceptionHandlerTests {
|
||||
ExecutionResult result = this.graphQlSetup.exceptionResolver(resolver).toGraphQl()
|
||||
.executeAsync(this.input).get();
|
||||
|
||||
GraphQlResponse response = GraphQlResponse.from(result);
|
||||
ResponseHelper response = ResponseHelper.forResult(result);
|
||||
assertThat(response.errorCount()).isEqualTo(1);
|
||||
assertThat(response.error(0).message()).isEqualTo("Resolved error: Invalid greeting");
|
||||
assertThat(response.error(0).errorType()).isEqualTo("BAD_REQUEST");
|
||||
@@ -84,7 +81,7 @@ public class ExceptionResolversExceptionHandlerTests {
|
||||
ExecutionResult result = this.graphQlSetup.exceptionResolver(resolver).toGraphQl()
|
||||
.executeAsync(this.input).get();
|
||||
|
||||
GraphQlResponse response = GraphQlResponse.from(result);
|
||||
ResponseHelper response = ResponseHelper.forResult(result);
|
||||
assertThat(response.errorCount()).isEqualTo(1);
|
||||
assertThat(response.error(0).message()).isEqualTo("Resolved error: Invalid greeting, name=007");
|
||||
}
|
||||
@@ -110,7 +107,7 @@ public class ExceptionResolversExceptionHandlerTests {
|
||||
Mono<ExecutionResult> result = Mono.delay(Duration.ofMillis(10)).flatMap((aLong) ->
|
||||
Mono.fromFuture(this.graphQlSetup.exceptionResolver(resolver).toGraphQl().executeAsync(this.input)));
|
||||
|
||||
GraphQlResponse response = GraphQlResponse.from(result);
|
||||
ResponseHelper response = ResponseHelper.forResult(result);
|
||||
assertThat(response.errorCount()).isEqualTo(1);
|
||||
assertThat(response.error(0).message()).isEqualTo("Resolved error: Invalid greeting, name=007");
|
||||
}
|
||||
@@ -127,7 +124,7 @@ public class ExceptionResolversExceptionHandlerTests {
|
||||
ExecutionResult result = this.graphQlSetup.exceptionResolver(resolver).toGraphQl()
|
||||
.executeAsync(this.input).get();
|
||||
|
||||
GraphQlResponse response = GraphQlResponse.from(result);
|
||||
ResponseHelper response = ResponseHelper.forResult(result);
|
||||
assertThat(response.errorCount()).isEqualTo(1);
|
||||
assertThat(response.error(0).message()).isEqualTo("Invalid greeting");
|
||||
assertThat(response.error(0).errorType()).isEqualTo("INTERNAL_ERROR");
|
||||
@@ -143,7 +140,7 @@ public class ExceptionResolversExceptionHandlerTests {
|
||||
.exceptionResolver((ex, env) -> Mono.just(Collections.emptyList())).toGraphQl()
|
||||
.executeAsync(input).get();
|
||||
|
||||
String greeting = GraphQlResponse.from(result).rawValue("greeting");
|
||||
String greeting = ResponseHelper.forResult(result).rawValue("greeting");
|
||||
assertThat(greeting).isNull();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import graphql.schema.DataFetcher;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.ResponseHelper;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.TestThreadLocalAccessor;
|
||||
import org.springframework.graphql.execution.DataFetcherExceptionResolver;
|
||||
@@ -63,7 +63,7 @@ public class WebGraphQlHandlerTests {
|
||||
.handleRequest(webInput)
|
||||
.contextWrite((context) -> context.put("name", "007"));
|
||||
|
||||
String greeting = GraphQlResponse.from(outputMono).toEntity("greeting", String.class);
|
||||
String greeting = ResponseHelper.forResponse(outputMono).toEntity("greeting", String.class);
|
||||
assertThat(greeting).isEqualTo("Hello 007");
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public class WebGraphQlHandlerTests {
|
||||
.handleRequest(webInput)
|
||||
.contextWrite((cxt) -> cxt.put("name", "007"));
|
||||
|
||||
GraphQlResponse response = GraphQlResponse.from(outputMono);
|
||||
ResponseHelper response = ResponseHelper.forResponse(outputMono);
|
||||
assertThat(response.errorCount()).isEqualTo(1);
|
||||
assertThat(response.error(0).message()).isEqualTo("Resolved error: Invalid greeting, name=007");
|
||||
|
||||
@@ -104,7 +104,7 @@ public class WebGraphQlHandlerTests {
|
||||
.toWebGraphQlHandler()
|
||||
.handleRequest(webInput);
|
||||
|
||||
String greeting = GraphQlResponse.from(outputMono).toEntity("greeting", String.class);
|
||||
String greeting = ResponseHelper.forResponse(outputMono).toEntity("greeting", String.class);
|
||||
assertThat(greeting).isEqualTo("Hello 007");
|
||||
}
|
||||
finally {
|
||||
@@ -131,7 +131,7 @@ public class WebGraphQlHandlerTests {
|
||||
.toWebGraphQlHandler()
|
||||
.handleRequest(webInput);
|
||||
|
||||
GraphQlResponse response = GraphQlResponse.from(outputMono);
|
||||
ResponseHelper response = ResponseHelper.forResponse(outputMono);
|
||||
assertThat(response.errorCount()).isEqualTo(1);
|
||||
assertThat(response.error(0).message()).isEqualTo("Resolved error: Invalid greeting, name=007");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user