Replace GraphQLError with GraphQlResponseError

This allows exposing additional conveniences for inspecting errors.

See gh-10
This commit is contained in:
rstoyanchev
2022-03-18 08:35:11 +00:00
parent 96135183f5
commit db24c8f62b
15 changed files with 297 additions and 213 deletions

View File

@@ -19,13 +19,13 @@ package org.springframework.graphql.test.tester;
import java.util.List;
import graphql.ExecutionResult;
import graphql.GraphQLError;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.graphql.GraphQlRequest;
import org.springframework.graphql.GraphQlResponse;
import org.springframework.graphql.GraphQlResponseError;
import org.springframework.graphql.RequestOutput;
import org.springframework.graphql.client.GraphQlTransport;
import org.springframework.test.util.AssertionErrors;
@@ -58,7 +58,7 @@ abstract class AbstractDirectTransport implements GraphQlTransport {
Object data = output.getData();
AssertionErrors.assertTrue("Not a Publisher: " + data, data instanceof Publisher);
List<GraphQLError> errors = output.getErrors();
List<GraphQlResponseError> errors = output.getErrors();
AssertionErrors.assertTrue("Subscription errors: " + errors, CollectionUtils.isEmpty(errors));
return Flux.from((Publisher<ExecutionResult>) data)

View File

@@ -24,8 +24,8 @@ import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
import com.jayway.jsonpath.spi.mapper.MappingProvider;
import graphql.GraphQLError;
import org.springframework.graphql.GraphQlResponseError;
import org.springframework.graphql.client.AbstractGraphQlClientBuilder;
import org.springframework.graphql.client.GraphQlTransport;
import org.springframework.graphql.support.CachingDocumentSource;
@@ -57,7 +57,7 @@ public abstract class AbstractGraphQlTesterBuilder<B extends AbstractGraphQlTest
@Nullable
private Predicate<GraphQLError> errorFilter;
private Predicate<GraphQlResponseError> errorFilter;
private DocumentSource documentSource = new CachingDocumentSource(new ResourceDocumentSource());
@@ -67,7 +67,7 @@ public abstract class AbstractGraphQlTesterBuilder<B extends AbstractGraphQlTest
@Override
public B errorFilter(Predicate<GraphQLError> predicate) {
public B errorFilter(Predicate<GraphQlResponseError> predicate) {
this.errorFilter = (this.errorFilter != null ? errorFilter.and(predicate) : predicate);
return self();
}

View File

@@ -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.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.GraphQlResponseError;
import org.springframework.graphql.client.GraphQlTransport;
import org.springframework.graphql.support.DocumentSource;
import org.springframework.lang.Nullable;
@@ -61,7 +61,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
private final GraphQlTransport transport;
@Nullable
private final Predicate<GraphQLError> errorFilter;
private final Predicate<GraphQlResponseError> errorFilter;
private final Configuration jsonPathConfig;
@@ -76,7 +76,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
* Package private constructor for use from {@link AbstractGraphQlTesterBuilder}.
*/
DefaultGraphQlTester(
GraphQlTransport transport, @Nullable Predicate<GraphQLError> errorFilter,
GraphQlTransport transport, @Nullable Predicate<GraphQlResponseError> errorFilter,
Configuration jsonPathConfig, DocumentSource documentSource, Duration timeout,
Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer) {
@@ -209,15 +209,15 @@ final class DefaultGraphQlTester implements GraphQlTester {
private final Supplier<String> jsonContent;
private final List<GraphQLError> errors;
private final List<GraphQlResponseError> errors;
private final List<GraphQLError> unexpectedErrors;
private final List<GraphQlResponseError> unexpectedErrors;
private final Consumer<Runnable> assertDecorator;
private ResponseDelegate(
GraphQlResponse response, @Nullable Predicate<GraphQLError> errorFilter,
GraphQlResponse response, @Nullable Predicate<GraphQlResponseError> errorFilter,
Consumer<Runnable> assertDecorator, Configuration jsonPathConfig) {
this.jsonDoc = JsonPath.parse(response.toMap(), jsonPathConfig);
@@ -253,9 +253,9 @@ final class DefaultGraphQlTester implements GraphQlTester {
this.assertDecorator.accept(task);
}
boolean filterErrors(Predicate<GraphQLError> predicate) {
boolean filterErrors(Predicate<GraphQlResponseError> predicate) {
boolean filtered = false;
for (GraphQLError error : this.errors) {
for (GraphQlResponseError error : this.errors) {
if (predicate.test(error)) {
this.unexpectedErrors.remove(error);
filtered = true;
@@ -264,12 +264,12 @@ final class DefaultGraphQlTester implements GraphQlTester {
return filtered;
}
void expectErrors(Predicate<GraphQLError> predicate) {
void expectErrors(Predicate<GraphQlResponseError> predicate) {
boolean filtered = filterErrors(predicate);
this.assertDecorator.accept(() -> AssertionErrors.assertTrue("No matching errors.", filtered));
}
void consumeErrors(Consumer<List<GraphQLError>> consumer) {
void consumeErrors(Consumer<List<GraphQlResponseError>> consumer) {
filterErrors(error -> true);
consumer.accept(this.errors);
}
@@ -293,7 +293,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
private final ResponseDelegate delegate;
private DefaultResponse(
GraphQlResponse response, @Nullable Predicate<GraphQLError> errorFilter,
GraphQlResponse response, @Nullable Predicate<GraphQlResponseError> errorFilter,
Consumer<Runnable> assertDecorator, Configuration jsonPathConfig) {
this.delegate = new ResponseDelegate(response, errorFilter, assertDecorator, jsonPathConfig);
@@ -311,13 +311,13 @@ final class DefaultGraphQlTester implements GraphQlTester {
}
@Override
public Errors filter(Predicate<GraphQLError> predicate) {
public Errors filter(Predicate<GraphQlResponseError> predicate) {
this.delegate.filterErrors(predicate);
return this;
}
@Override
public Errors expect(Predicate<GraphQLError> predicate) {
public Errors expect(Predicate<GraphQlResponseError> predicate) {
this.delegate.expectErrors(predicate);
return this;
}
@@ -329,7 +329,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
}
@Override
public Traversable satisfy(Consumer<List<GraphQLError>> consumer) {
public Traversable satisfy(Consumer<List<GraphQlResponseError>> consumer) {
this.delegate.consumeErrors(consumer);
return this;
}

View File

@@ -21,10 +21,10 @@ import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import graphql.GraphQLError;
import reactor.core.publisher.Flux;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.graphql.GraphQlResponseError;
import org.springframework.graphql.client.GraphQlTransport;
import org.springframework.graphql.support.DocumentSource;
import org.springframework.graphql.support.ResourceDocumentSource;
@@ -103,7 +103,7 @@ public interface GraphQlTester {
* @param predicate the error filter to add
* @return the same builder instance
*/
B errorFilter(Predicate<GraphQLError> predicate);
B errorFilter(Predicate<GraphQlResponseError> predicate);
/**
* Configure a {@link DocumentSource} for use with
@@ -448,7 +448,7 @@ public interface GraphQlTester {
* @param errorPredicate the error filter to add
* @return the same spec to add more filters before {@link #verify()}
*/
Errors filter(Predicate<GraphQLError> errorPredicate);
Errors filter(Predicate<GraphQlResponseError> errorPredicate);
/**
* Use this to declare errors that are expected.
@@ -461,7 +461,7 @@ public interface GraphQlTester {
* @param errorPredicate the predicate for the expected error
* @return the same spec to add more filters or expected errors
*/
Errors expect(Predicate<GraphQLError> errorPredicate);
Errors expect(Predicate<GraphQlResponseError> errorPredicate);
/**
* Verify there are either no errors or that there no unexpected errors that have
@@ -477,7 +477,7 @@ public interface GraphQlTester {
* @param errorsConsumer to inspect errors with
* @return a spec to switch to a data path
*/
Traversable satisfy(Consumer<List<GraphQLError>> errorsConsumer);
Traversable satisfy(Consumer<List<GraphQlResponseError>> errorsConsumer);
}

View File

@@ -20,8 +20,6 @@ package org.springframework.graphql;
import java.util.List;
import java.util.Map;
import graphql.GraphQLError;
import org.springframework.lang.Nullable;
/**
@@ -61,7 +59,7 @@ public interface GraphQlResponse {
* response is not {@link #isValid() valid} and/or "field errors" for a
* partial response.
*/
List<GraphQLError> getErrors();
List<GraphQlResponseError> getErrors();
/**
* Return implementor specific, protocol extensions, if any.

View File

@@ -0,0 +1,66 @@
package org.springframework.graphql;
import java.util.List;
import java.util.Map;
import graphql.ErrorClassification;
import graphql.language.SourceLocation;
import org.springframework.lang.Nullable;
/**
* Represents a GraphQL response error.
*
* @author Rossen Stoyanchev
* @since 1.0
*/
public interface GraphQlResponseError {
/**
* Return the message with a description of the error intended for the
* developer as a guide to understand and correct the error.
*/
@Nullable
String getMessage();
/**
* Return a list of locations in the GraphQL document, if the error can be
* associated to a particular point in the document. Each location has a
* line and a column, both positive, starting from 1 and describing the
* beginning of an associated syntax element.
*/
List<SourceLocation> getLocations();
/**
* Return a classification for the error that is specific to GraphQL Java.
* This is serialized under {@link #getExtensions() "extensions"} in the
* response map.
* @see graphql.ErrorType
* @see org.springframework.graphql.execution.ErrorType
*/
@Nullable
ErrorClassification getErrorType();
/**
* Return the path to a response field which experienced the error,
* if the error can be associated to a particular field in the result. This
* allows a client to identify whether a {@code null} result is intentional
* or caused by an error.
* <p>This list contains path segments starting at the root of the response
* and ending with the field associated with the error. Path segments that
* represent fields are strings, and path segments that represent list
* indices are 0-indexed integers. If the error happens in an aliased field,
* the path uses the aliased name, since it represents a path in the
* response, not in the request.
*/
@Nullable
List<Object> getPath();
/**
* Return a map with GraphQL Java specific error details such as the
* {@link #getErrorType()}.
*/
@Nullable
Map<String, Object> getExtensions();
}

View File

@@ -18,10 +18,13 @@ package org.springframework.graphql;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import graphql.ErrorClassification;
import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.GraphQLError;
import graphql.language.SourceLocation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -55,9 +58,7 @@ public class RequestOutput implements GraphQlResponse {
* 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;
this(requestOutput.getExecutionInput(), requestOutput.result);
}
@@ -84,8 +85,8 @@ public class RequestOutput implements GraphQlResponse {
return this.result.getData();
}
public List<GraphQLError> getErrors() {
return this.result.getErrors();
public List<GraphQlResponseError> getErrors() {
return this.result.getErrors().stream().map(OutputError::new).collect(Collectors.toList());
}
public Map<Object, Object> getExtensions() {
@@ -102,4 +103,41 @@ public class RequestOutput implements GraphQlResponse {
return this.result.toString();
}
private static class OutputError implements GraphQlResponseError {
private final GraphQLError delegate;
OutputError(GraphQLError delegate) {
this.delegate = delegate;
}
@Override
public String getMessage() {
return this.delegate.getMessage();
}
@Override
public List<SourceLocation> getLocations() {
return this.delegate.getLocations();
}
@Override
public ErrorClassification getErrorType() {
return this.delegate.getErrorType();
}
@Override
public List<Object> getPath() {
return this.delegate.getPath();
}
@Override
public Map<String, Object> getExtensions() {
return this.delegate.getExtensions();
}
}
}

View File

@@ -20,8 +20,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import graphql.GraphQLError;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.core.codec.Decoder;
@@ -31,7 +29,9 @@ import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.graphql.GraphQlRequest;
import org.springframework.graphql.GraphQlResponse;
import org.springframework.graphql.GraphQlResponseError;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
@@ -54,7 +54,7 @@ final class DefaultClientGraphQlResponse extends MapGraphQlResponse implements C
DefaultClientGraphQlResponse(
GraphQlRequest request, GraphQlResponse response, Encoder<?> encoder, Decoder<?> decoder) {
super(response.toMap());
super(response);
this.request = request;
this.encoder = encoder;
@@ -72,7 +72,7 @@ final class DefaultClientGraphQlResponse extends MapGraphQlResponse implements C
List<Object> dataPath = parseFieldPath(path);
Object value = getFieldValue(dataPath);
List<GraphQLError> errors = getFieldErrors(dataPath);
List<GraphQlResponseError> errors = getFieldErrors(dataPath);
return new DefaultField(path, dataPath, (value != NO_VALUE ? value : null), errors);
}
@@ -97,13 +97,13 @@ final class DefaultClientGraphQlResponse extends MapGraphQlResponse implements C
private final List<Object> parsedPath;
private final List<GraphQLError> errors;
private final List<GraphQlResponseError> errors;
@Nullable
private final Object value;
public DefaultField(
String path, List<Object> parsedPath, @Nullable Object value, List<GraphQLError> errors) {
String path, List<Object> parsedPath, @Nullable Object value, List<GraphQlResponseError> errors) {
this.path = path;
this.parsedPath = parsedPath;
@@ -128,8 +128,9 @@ final class DefaultClientGraphQlResponse extends MapGraphQlResponse implements C
}
@Override
public GraphQLError getError() {
for (GraphQLError error : this.errors) {
public GraphQlResponseError getError() {
for (GraphQlResponseError error : this.errors) {
Assert.notNull(error.getPath(), "Expected field error");
if (error.getPath().size() <= this.parsedPath.size()) {
return error;
}
@@ -138,7 +139,7 @@ final class DefaultClientGraphQlResponse extends MapGraphQlResponse implements C
}
@Override
public List<GraphQLError> getErrors() {
public List<GraphQlResponseError> getErrors() {
return this.errors;
}

View File

@@ -1,137 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.client;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import graphql.ErrorClassification;
import graphql.GraphQLError;
import graphql.GraphqlErrorHelper;
import graphql.language.SourceLocation;
import org.springframework.graphql.execution.ErrorType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* {@link GraphQLError} that wraps a deserialized the GraphQL response map.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
@SuppressWarnings("serial")
final class MapGraphQlError implements GraphQLError {
private final Map<String, Object> errorMap;
private final List<SourceLocation> locations;
MapGraphQlError(Map<String, Object> errorMap) {
Assert.notNull(errorMap, "'errorMap' is required");
this.errorMap = errorMap;
this.locations = initLocations(errorMap);
}
@SuppressWarnings("unchecked")
private static List<SourceLocation> initLocations(Map<String, Object> errorMap) {
List<Map<String, Object>> locations = (List<Map<String, Object>>) errorMap.get("locations");
if (locations == null) {
return Collections.emptyList();
}
return locations.stream()
.map(map -> new SourceLocation(
(int) map.getOrDefault("line", 0),
(int) map.getOrDefault("column", 0),
(String) map.get("sourceName")))
.collect(Collectors.toList());
}
@Override
@Nullable
public String getMessage() {
return (String) errorMap.get("message");
}
@Override
public List<SourceLocation> getLocations() {
return this.locations;
}
@Override
@Nullable
public ErrorClassification getErrorType() {
// Attempt the reverse of how errorType is serialized in GraphqlErrorHelper.toSpecification.
// However, we can only do that for ErrorClassification enums that we know of.
String value = (getExtensions() != null ? (String) getExtensions().get("classification") : null);
if (value != null) {
try {
return graphql.ErrorType.valueOf(value);
}
catch (IllegalArgumentException ex) {
// ignore
}
try {
return ErrorType.valueOf(value);
}
catch (IllegalArgumentException ex) {
// ignore
}
}
return null;
}
@SuppressWarnings("unchecked")
@Override
@Nullable
public List<Object> getPath() {
return (List<Object>) this.errorMap.get("path");
}
@SuppressWarnings("unchecked")
@Override
@Nullable
public Map<String, Object> getExtensions() {
return (Map<String, Object>) this.errorMap.get("extensions");
}
@Override
public Map<String, Object> toSpecification() {
return GraphqlErrorHelper.toSpecification(this);
}
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
@Override
public boolean equals(Object other) {
return GraphqlErrorHelper.equals(this, other);
}
@Override
public int hashCode() {
return GraphqlErrorHelper.hashCode(this);
}
@Override
public String toString() {
return toSpecification().toString();
}
}

View File

@@ -20,13 +20,19 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import graphql.ErrorClassification;
import graphql.GraphQLError;
import graphql.language.SourceLocation;
import org.springframework.graphql.GraphQlResponse;
import org.springframework.graphql.GraphQlResponseError;
import org.springframework.graphql.execution.ErrorType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
@@ -45,7 +51,7 @@ class MapGraphQlResponse implements GraphQlResponse {
private final Map<String, Object> responseMap;
private final List<GraphQLError> errors;
private final List<GraphQlResponseError> errors;
MapGraphQlResponse(Map<String, Object> responseMap) {
@@ -54,17 +60,17 @@ class MapGraphQlResponse implements GraphQlResponse {
this.errors = wrapErrors(responseMap);
}
MapGraphQlResponse(GraphQlResponse response) {
Assert.notNull(response, "'GraphQlResponse' is required");
this.responseMap = response.toMap();
this.errors = response.getErrors();
}
@SuppressWarnings("unchecked")
private static List<GraphQLError> wrapErrors(Map<String, Object> responseMap) {
List<Map<String, Object>> rawErrors = (List<Map<String, Object>>) responseMap.get("errors");
if (CollectionUtils.isEmpty(rawErrors)) {
return Collections.emptyList();
}
List<GraphQLError> errors = new ArrayList<>(rawErrors.size());
for (Map<String, Object> map : rawErrors) {
errors.add(new MapGraphQlError(map));
}
return errors;
private static List<GraphQlResponseError> wrapErrors(Map<String, Object> map) {
List<Map<String, Object>> errors = (List<Map<String, Object>>) map.get("errors");
errors = (errors != null ? errors : Collections.emptyList());
return errors.stream().map(MapError::new).collect(Collectors.toList());
}
@@ -74,7 +80,7 @@ class MapGraphQlResponse implements GraphQlResponse {
}
@Override
public List<GraphQLError> getErrors() {
public List<GraphQlResponseError> getErrors() {
return this.errors;
}
@@ -177,12 +183,12 @@ class MapGraphQlResponse implements GraphQlResponse {
* @param fieldPath the field path to match
* @return errors whose path starts with the dataPath
*/
protected List<GraphQLError> getFieldErrors(List<Object> fieldPath) {
protected List<GraphQlResponseError> getFieldErrors(List<Object> fieldPath) {
if (fieldPath.isEmpty()) {
return Collections.emptyList();
}
List<GraphQLError> fieldErrors = Collections.emptyList();
for (GraphQLError error : this.errors) {
List<GraphQlResponseError> fieldErrors = Collections.emptyList();
for (GraphQlResponseError error : this.errors) {
List<Object> errorPath = error.getPath();
if (CollectionUtils.isEmpty(errorPath)) {
continue;
@@ -217,4 +223,115 @@ class MapGraphQlResponse implements GraphQlResponse {
return this.responseMap.toString();
}
/**
* {@link GraphQLError} that wraps a deserialized the GraphQL response map.
*/
@SuppressWarnings("serial")
private static final class MapError implements GraphQlResponseError {
private final Map<String, Object> errorMap;
private final List<SourceLocation> locations;
MapError(Map<String, Object> errorMap) {
Assert.notNull(errorMap, "'errorMap' is required");
this.errorMap = errorMap;
this.locations = initLocations(errorMap);
}
@SuppressWarnings("unchecked")
private static List<SourceLocation> initLocations(Map<String, Object> errorMap) {
List<Map<String, Object>> locations = (List<Map<String, Object>>) errorMap.get("locations");
if (locations == null) {
return Collections.emptyList();
}
return locations.stream()
.map(map -> new SourceLocation(
(int) map.getOrDefault("line", 0),
(int) map.getOrDefault("column", 0),
(String) map.get("sourceName")))
.collect(Collectors.toList());
}
@Override
@Nullable
public String getMessage() {
return (String) errorMap.get("message");
}
@Override
public List<SourceLocation> getLocations() {
return this.locations;
}
@Override
@Nullable
public ErrorClassification getErrorType() {
// Attempt the reverse of how errorType is serialized in GraphqlErrorHelper.toSpecification.
// However, we can only do that for ErrorClassification enums that we know of.
String value = (getExtensions() != null ? (String) getExtensions().get("classification") : null);
if (value != null) {
try {
return graphql.ErrorType.valueOf(value);
}
catch (IllegalArgumentException ex) {
// ignore
}
try {
return ErrorType.valueOf(value);
}
catch (IllegalArgumentException ex) {
// ignore
}
}
return null;
}
@SuppressWarnings("unchecked")
@Override
@Nullable
public List<Object> getPath() {
return (List<Object>) this.errorMap.get("path");
}
@SuppressWarnings("unchecked")
@Override
@Nullable
public Map<String, Object> getExtensions() {
return (Map<String, Object>) this.errorMap.get("extensions");
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || this.getClass() != o.getClass()) {
return false;
}
GraphQlResponseError other = (GraphQlResponseError) o;
return (ObjectUtils.nullSafeEquals(getMessage(), other.getMessage()) &&
ObjectUtils.nullSafeEquals(getLocations(), other.getLocations()) &&
ObjectUtils.nullSafeEquals(getPath(), other.getPath()) &&
getErrorType() == other.getErrorType());
}
@Override
public int hashCode() {
int result = 1;
result = 31 * result + ObjectUtils.nullSafeHashCode(getMessage());
result = 31 * result + ObjectUtils.nullSafeHashCode(getLocations());
result = 31 * result + ObjectUtils.nullSafeHashCode(getPath());
result = 31 * result + ObjectUtils.nullSafeHashCode(getErrorType());
return result;
}
@Override
public String toString() {
return this.errorMap.toString();
}
}
}

View File

@@ -19,9 +19,8 @@ package org.springframework.graphql.client;
import java.util.List;
import graphql.GraphQLError;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.graphql.GraphQlResponseError;
import org.springframework.lang.Nullable;
/**
@@ -72,7 +71,7 @@ public interface ResponseField {
* error with the same path as the field path
*/
@Nullable
GraphQLError getError();
GraphQlResponseError getError();
/**
* Return all field errors including errors above, at, and below this field.
@@ -80,7 +79,7 @@ public interface ResponseField {
* below. When the field does not have a value, there is only one error, and
* it is the same as {@link #getError()}.
*/
List<GraphQLError> getErrors();
List<GraphQlResponseError> getErrors();
/**
* Decode the field to an entity of the given type.

View File

@@ -18,9 +18,8 @@ package org.springframework.graphql.client;
import java.util.List;
import graphql.GraphQLError;
import org.springframework.graphql.GraphQlRequest;
import org.springframework.graphql.GraphQlResponseError;
/**
* WebSocket {@link GraphQlTransportException} raised when a subscription
@@ -33,14 +32,14 @@ import org.springframework.graphql.GraphQlRequest;
@SuppressWarnings("serial")
public class SubscriptionErrorException extends GraphQlTransportException {
private final List<GraphQLError> errors;
private final List<GraphQlResponseError> errors;
/**
* Constructor with the request details and the errors listed in the payload
* of the {@code "errors"} message.
*/
public SubscriptionErrorException(GraphQlRequest request, List<GraphQLError> errors) {
public SubscriptionErrorException(GraphQlRequest request, List<GraphQlResponseError> errors) {
super("GraphQL subscription completed with an \"error\" message, " +
"with the following errors: " + errors, null, request);
this.errors = errors;
@@ -50,7 +49,7 @@ public class SubscriptionErrorException extends GraphQlTransportException {
/**
* Return the errors contained in the GraphQL over WebSocket "errors" message.
*/
public List<GraphQLError> getErrors() {
public List<GraphQlResponseError> getErrors() {
return this.errors;
}

View File

@@ -24,7 +24,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import graphql.GraphQLError;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.Scannable;
@@ -34,6 +33,7 @@ import reactor.core.publisher.Sinks;
import org.springframework.graphql.GraphQlRequest;
import org.springframework.graphql.GraphQlResponse;
import org.springframework.graphql.GraphQlResponseError;
import org.springframework.graphql.web.support.GraphQlMessage;
import org.springframework.graphql.web.support.GraphQlMessageType;
import org.springframework.http.HttpHeaders;
@@ -514,8 +514,8 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
emitResult = responseState.sink().tryEmitValue(response);
}
else {
List<GraphQLError> graphQLErrors = response.getErrors();
Exception ex = new SubscriptionErrorException(subscriptionState.request(), graphQLErrors);
List<GraphQlResponseError> errors = response.getErrors();
Exception ex = new SubscriptionErrorException(subscriptionState.request(), errors);
emitResult = subscriptionState.sink().tryEmitError(ex);
}

View File

@@ -29,6 +29,7 @@ import graphql.execution.ResultPath;
import org.junit.jupiter.api.Test;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.graphql.GraphQlResponseError;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
@@ -140,9 +141,12 @@ public class MapGraphQlResponseTests {
.map(GraphQLError::toSpecification).collect(Collectors.toList());
MapGraphQlResponse response = new MapGraphQlResponse(Collections.singletonMap("errors", errorList));
List<GraphQLError> errors = response.getFieldErrors(path);
List<GraphQlResponseError> errors = response.getFieldErrors(path);
assertThat(errors).containsExactly(error1, error2, error3);
assertThat(errors).hasSize(3);
assertThat(errors.get(0).getPath()).containsExactly("me");
assertThat(errors.get(1).getPath()).containsExactly("me", "friends");
assertThat(errors.get(2).getPath()).containsExactly("me", "friends", 0, "name");
}
private GraphQLError createError(@Nullable String errorPath, String message) {
@@ -150,8 +154,7 @@ public class MapGraphQlResponseTests {
if (errorPath != null) {
builder = builder.path(ResultPath.parse(errorPath));
}
Map<String, Object> errorMap = builder.build().toSpecification();
return new MapGraphQlError(errorMap);
return builder.build();
}
}

View File

@@ -25,7 +25,6 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import graphql.GraphQLError;
import graphql.GraphqlErrorBuilder;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
@@ -34,6 +33,7 @@ import reactor.test.StepVerifier;
import org.springframework.graphql.GraphQlRequest;
import org.springframework.graphql.GraphQlResponse;
import org.springframework.graphql.GraphQlResponseError;
import org.springframework.graphql.web.TestWebSocketClient;
import org.springframework.graphql.web.TestWebSocketConnection;
import org.springframework.graphql.web.support.GraphQlMessage;
@@ -110,7 +110,7 @@ public class MockWebSocketGraphQlTransportTests {
StepVerifier.create(this.transport.execute(request))
.consumeNextWith(result -> {
assertThat(result.isValid()).isFalse();
assertThat(result.getErrors()).extracting(GraphQLError::getMessage).containsExactly("boo");
assertThat(result.getErrors()).extracting(GraphQlResponseError::getMessage).containsExactly("boo");
})
.expectComplete()
.verify(TIMEOUT);
@@ -128,8 +128,8 @@ public class MockWebSocketGraphQlTransportTests {
StepVerifier.create(this.transport.executeSubscription(request))
.expectNext(this.response1)
.expectErrorSatisfies(actualEx -> {
List<GraphQLError> errorList = ((SubscriptionErrorException) actualEx).getErrors();
assertThat(errorList).extracting(GraphQLError::getMessage).containsExactly("boo");
List<GraphQlResponseError> errors = ((SubscriptionErrorException) actualEx).getErrors();
assertThat(errors).extracting(GraphQlResponseError::getMessage).containsExactly("boo");
})
.verify(TIMEOUT);