Refine getError(s) methods on ResponseField

getError now returns either the error at the field or on a parent
field, which provides a reliable answer for why a field failed even
for a field whose path is below where the error occurred.

getErrors likewise now also includes errors above. There is still no
need for special logic to get only errors below since where there is
a field value, errors are below anyway.

See gh-10
This commit is contained in:
rstoyanchev
2022-03-17 12:27:52 +00:00
parent f28d3dc2a0
commit 5e95fcf60a
5 changed files with 27 additions and 9 deletions

View File

@@ -136,7 +136,7 @@ final class DefaultClientGraphQlResponse extends MapGraphQlResponse implements C
@Override
public GraphQLError getError() {
for (GraphQLError error : this.errors) {
if (this.parsedPath.size() == error.getPath().size()) {
if (error.getPath().size() <= this.parsedPath.size()) {
return error;
}
}

View File

@@ -60,15 +60,24 @@ public interface ResponseField {
<T> T getValue();
/**
* Return the first error whose path is equal to the field path.
* <p>According to section 6.4.4 "Handling Field Errors" of the GraphQL
* spec, only one error should be added to the errors list per field.
* Return the error for this field, if any. The error may be for this field
* when the field is {@code null}, or it may be for a parent field, when the
* current field does not exist.
* <p><strong>Note:</strong> The field error is identified by searching for
* the first error with a matching path that is shorter or the same as the
* field path. According to the GraphQL spec, section 6.4.4,
* "Handling Field Errors", there should be only one field error per field.
* @return return the error for this field, or {@code null} if there is no
* error with the same path as the field path
*/
@Nullable
GraphQLError getError();
/**
* Return all field errors including those whose path is below the field path.
* Return all field errors including errors above, at, and below this field.
* <p>In practice, when the field has a value, all errors are for fields
* 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();

View File

@@ -185,11 +185,11 @@ public class MapGraphQlResponse implements GraphQlResponse {
List<GraphQLError> fieldErrors = Collections.emptyList();
for (GraphQLError error : this.errors) {
List<Object> errorPath = error.getPath();
if (CollectionUtils.isEmpty(errorPath) || errorPath.size() < fieldPath.size()) {
if (CollectionUtils.isEmpty(errorPath)) {
continue;
}
boolean match = true;
for (int i = 0; match && i < fieldPath.size(); i++) {
for (int i = 0; match && i < fieldPath.size() && i < errorPath.size(); i++) {
match = fieldPath.get(i).equals(errorPath.get(i));
}
if (!match) {

View File

@@ -188,15 +188,24 @@ public class GraphQlClientTests extends GraphQlClientTestSupport {
ResponseField field = response.field("me");
assertThat(field.isValid()).isTrue();
assertThat(field.getErrors()).hasSize(1);
assertThat(field.getErrors().get(0).getPath()).containsExactly("me", "name");
assertThat(field.toEntity(MovieCharacter.class))
.as("Decoding with nested field error should not be precluded")
.isNotNull();
ResponseField nameField = response.field("me.name");
assertThat(nameField.isValid()).isFalse();
assertThat(nameField.getError()).isNotNull();
assertThat(nameField.getError().getPath()).containsExactly("me", "name");
assertThatThrownBy(() -> nameField.toEntity(String.class))
.as("Decoding field null with direct field error should be rejected")
.isInstanceOf(FieldAccessException.class);
ResponseField nonExistingField = response.field("me.name.other");
assertThat(nonExistingField.isValid()).isFalse();
assertThat(nameField.getError()).isNotNull();
assertThat(nameField.getError().getPath()).containsExactly("me", "name");
}
private GraphQLError errorForPath(String errorPath) {

View File

@@ -142,11 +142,11 @@ public class MapGraphQlResponseTests {
MapGraphQlResponse response = MapGraphQlResponse.forErrorsOnly(errorList);
List<GraphQLError> errors = response.getFieldErrors(path);
assertThat(errors).containsExactly(error2, error3);
assertThat(errors).containsExactly(error1, error2, error3);
}
private GraphQLError createError(@Nullable String errorPath, String message) {
GraphqlErrorBuilder builder = GraphqlErrorBuilder.newError().message(message);
GraphqlErrorBuilder<?> builder = GraphqlErrorBuilder.newError().message(message);
if (errorPath != null) {
builder = builder.path(ResultPath.parse(errorPath));
}