Extract GraphQlResponseField in the top-level package
GraphQlResponseField is now extracted as a super type at the top-level package and is exposed from GraphQlResponse. ClientGraphQlResponseField extends this to provide decoding options. The change ensures consistency with both GraphQlResponseField and GraphQlResponseError accessible through GraphQlResponse, also making both available for client and server side handling. See gh-10
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation of {@link GraphQlResponseField}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class DefaultGraphQlResponseField implements GraphQlResponseField {
|
||||
|
||||
private final GraphQlResponse response;
|
||||
|
||||
private final String path;
|
||||
|
||||
private final List<Object> parsedPath;
|
||||
|
||||
@Nullable
|
||||
private final Object value;
|
||||
|
||||
private final List<GraphQlResponseError> fieldErrors;
|
||||
|
||||
|
||||
protected DefaultGraphQlResponseField(GraphQlResponse response, String path) {
|
||||
|
||||
this.response = response;
|
||||
this.path = path;
|
||||
this.parsedPath = parsePath(path);
|
||||
this.value = initFieldValue(this.parsedPath, response);
|
||||
this.fieldErrors = initFieldErrors(path, response);
|
||||
}
|
||||
|
||||
private static List<Object> parsePath(String path) {
|
||||
if (!StringUtils.hasText(path)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String invalidPathMessage = "Invalid path: '" + path + "'";
|
||||
List<Object> dataPath = new ArrayList<>();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean readingIndex = false;
|
||||
|
||||
for (int i = 0; i < path.length(); i++) {
|
||||
char c = path.charAt(i);
|
||||
switch (c) {
|
||||
case '.':
|
||||
case '[':
|
||||
Assert.isTrue(!readingIndex, invalidPathMessage);
|
||||
break;
|
||||
case ']':
|
||||
i++;
|
||||
Assert.isTrue(readingIndex, invalidPathMessage);
|
||||
Assert.isTrue(i == path.length() || path.charAt(i) == '.', invalidPathMessage);
|
||||
break;
|
||||
default:
|
||||
sb.append(c);
|
||||
if (i < path.length() - 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
String token = sb.toString();
|
||||
Assert.hasText(token, invalidPathMessage);
|
||||
dataPath.add(readingIndex ? Integer.parseInt(token) : token);
|
||||
sb.delete(0, sb.length());
|
||||
|
||||
readingIndex = (c == '[');
|
||||
}
|
||||
|
||||
return dataPath;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Object initFieldValue(List<Object> path, GraphQlResponse response) {
|
||||
Object value = (response.isValid() ? response.getData() : null);
|
||||
for (Object segment : path) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (segment instanceof String) {
|
||||
Assert.isTrue(value instanceof Map, () -> "Invalid path " + path + ", data: " + response.getData());
|
||||
value = ((Map<?, ?>) value).getOrDefault(segment, null);
|
||||
}
|
||||
else {
|
||||
Assert.isTrue(value instanceof List, () -> "Invalid path " + path + ", data: " + response.getData());
|
||||
int index = (int) segment;
|
||||
value = (index < ((List<?>) value).size() ? ((List<?>) value).get(index) : null);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return field errors whose path starts with the given field path.
|
||||
* @param path the field path to match
|
||||
* @return errors whose path starts with the dataPath
|
||||
*/
|
||||
private static List<GraphQlResponseError> initFieldErrors(String path, GraphQlResponse response) {
|
||||
if (path.isEmpty() || response.getErrors().isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return response.getErrors().stream()
|
||||
.filter(error -> {
|
||||
String errorPath = error.getPath();
|
||||
return !errorPath.isEmpty() && (errorPath.startsWith(path) || path.startsWith(errorPath));
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <R extends GraphQlResponse> R getResponse() {
|
||||
return (R) this.response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getParsedPath() {
|
||||
return this.parsedPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasValue() {
|
||||
return (this.value != null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getValue() {
|
||||
return (T) this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphQlResponseError getError() {
|
||||
if (!hasValue()) {
|
||||
if (!this.fieldErrors.isEmpty()) {
|
||||
return this.fieldErrors.get(0);
|
||||
}
|
||||
if (!this.response.getErrors().isEmpty()) {
|
||||
return this.response.getErrors().get(0);
|
||||
}
|
||||
// No errors, set to null by DataFetcher
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GraphQlResponseError> getErrors() {
|
||||
return this.fieldErrors;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -65,6 +65,26 @@ public interface GraphQlResponse {
|
||||
*/
|
||||
List<GraphQlResponseError> getErrors();
|
||||
|
||||
/**
|
||||
* Navigate to the given path under the "data" key of the response map where
|
||||
* the path is a dot-separated string with optional array indexes.
|
||||
* <p>Example paths:
|
||||
* <pre>
|
||||
* "hero"
|
||||
* "hero.name"
|
||||
* "hero.friends"
|
||||
* "hero.friends[2]"
|
||||
* "hero.friends[2].name"
|
||||
* </pre>
|
||||
* @param path relative to the "data" key
|
||||
* @return representation for the field with further options to inspect or
|
||||
* decode its value; use {@link GraphQlResponseField#hasValue()} to check if
|
||||
* the field actually exists and has a value.
|
||||
*/
|
||||
default GraphQlResponseField field(String path) {
|
||||
return new DefaultGraphQlResponseField(this, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return implementor specific, protocol extensions, if any.
|
||||
*/
|
||||
|
||||
@@ -14,19 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.client;
|
||||
|
||||
package org.springframework.graphql;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.GraphQlResponseError;
|
||||
import org.springframework.graphql.client.ClientGraphQlResponse;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
* Representation for a field in a GraphQL response, with options to examine its
|
||||
* value and errors, and to decode it.
|
||||
* Representation for a field in a GraphQL response, with options to examine
|
||||
* the field value and errors.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
@@ -58,8 +56,8 @@ public interface GraphQlResponseField {
|
||||
List<Object> getParsedPath();
|
||||
|
||||
/**
|
||||
* Return the field value without any decoding.
|
||||
* @param <T> the expected value type, e.g. Map, List, or a scalar type.
|
||||
* Return the raw field value, e.g. Map, List, or a scalar type.
|
||||
* @param <T> the expected value type to cast to
|
||||
* @return the value
|
||||
*/
|
||||
@Nullable
|
||||
@@ -98,32 +96,4 @@ public interface GraphQlResponseField {
|
||||
*/
|
||||
List<GraphQlResponseError> getErrors();
|
||||
|
||||
/**
|
||||
* Decode the field to an entity of the given type.
|
||||
* @param entityType the type to convert to
|
||||
* @return the decoded entity, never {@code null}
|
||||
* @throws FieldAccessException if the target field is not present or
|
||||
* has no value, checked via {@link #hasValue()}.
|
||||
*/
|
||||
<D> D toEntity(Class<D> entityType);
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}.
|
||||
*/
|
||||
<D> D toEntity(ParameterizedTypeReference<D> entityType);
|
||||
|
||||
/**
|
||||
* Decode the field to a list of entities with the given type.
|
||||
* @param elementType the type of elements in the list
|
||||
* @return the decoded list of entities, possibly empty
|
||||
* @throws FieldAccessException if the target field is not present or
|
||||
* has no value, checked via {@link #hasValue()}.
|
||||
*/
|
||||
<D> List<D> toEntityList(Class<D> elementType);
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntityList(Class)} with {@link ParameterizedTypeReference}.
|
||||
*/
|
||||
<D> List<D> toEntityList(ParameterizedTypeReference<D> elementType);
|
||||
|
||||
}
|
||||
@@ -36,22 +36,9 @@ public interface ClientGraphQlResponse extends GraphQlResponse {
|
||||
GraphQlRequest getRequest();
|
||||
|
||||
/**
|
||||
* Navigate to the given path under the "data" key of the response map where
|
||||
* the path is a dot-separated string with optional array indexes.
|
||||
* <p>Example paths:
|
||||
* <pre>
|
||||
* "hero"
|
||||
* "hero.name"
|
||||
* "hero.friends"
|
||||
* "hero.friends[2]"
|
||||
* "hero.friends[2].name"
|
||||
* </pre>
|
||||
* @param path relative to the "data" key
|
||||
* @return representation for the field with further options to inspect or
|
||||
* decode its value; use {@link GraphQlResponseField#hasValue()} to check if
|
||||
* the field actually exists and has a value.
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
GraphQlResponseField field(String path);
|
||||
ClientGraphQlResponseField field(String path);
|
||||
|
||||
/**
|
||||
* Decode the full response map to the given target type.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.List;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.graphql.GraphQlResponseField;
|
||||
|
||||
/**
|
||||
* Extends {@link GraphQlResponseField} to add options for decoding the field value.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface ClientGraphQlResponseField extends GraphQlResponseField {
|
||||
|
||||
/**
|
||||
* Decode the field to an entity of the given type.
|
||||
* @param entityType the type to convert to
|
||||
* @return the decoded entity, never {@code null}
|
||||
* @throws FieldAccessException if the target field is not present or
|
||||
* has no value, checked via {@link #hasValue()}.
|
||||
*/
|
||||
<D> D toEntity(Class<D> entityType);
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}.
|
||||
*/
|
||||
<D> D toEntity(ParameterizedTypeReference<D> entityType);
|
||||
|
||||
/**
|
||||
* Decode the field to a list of entities with the given type.
|
||||
* @param elementType the type of elements in the list
|
||||
* @return the decoded list of entities, possibly empty
|
||||
* @throws FieldAccessException if the target field is not present or
|
||||
* has no value, checked via {@link #hasValue()}.
|
||||
*/
|
||||
<D> List<D> toEntityList(Class<D> elementType);
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntityList(Class)} with {@link ParameterizedTypeReference}.
|
||||
*/
|
||||
<D> List<D> toEntityList(ParameterizedTypeReference<D> elementType);
|
||||
|
||||
}
|
||||
@@ -16,21 +16,11 @@
|
||||
|
||||
package org.springframework.graphql.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.codec.Decoder;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
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.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
@@ -74,86 +64,8 @@ final class DefaultClientGraphQlResponse extends MapGraphQlResponse implements C
|
||||
|
||||
|
||||
@Override
|
||||
public GraphQlResponseField field(String path) {
|
||||
List<Object> parsedPath = parsePath(path);
|
||||
return new DefaultGraphQlResponseField(this, path, parsedPath, getValue(parsedPath), getFieldErrors(path));
|
||||
}
|
||||
|
||||
private static List<Object> parsePath(String path) {
|
||||
if (!StringUtils.hasText(path)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String invalidPathMessage = "Invalid path: '" + path + "'";
|
||||
List<Object> dataPath = new ArrayList<>();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean readingIndex = false;
|
||||
|
||||
for (int i = 0; i < path.length(); i++) {
|
||||
char c = path.charAt(i);
|
||||
switch (c) {
|
||||
case '.':
|
||||
case '[':
|
||||
Assert.isTrue(!readingIndex, invalidPathMessage);
|
||||
break;
|
||||
case ']':
|
||||
i++;
|
||||
Assert.isTrue(readingIndex, invalidPathMessage);
|
||||
Assert.isTrue(i == path.length() || path.charAt(i) == '.', invalidPathMessage);
|
||||
break;
|
||||
default:
|
||||
sb.append(c);
|
||||
if (i < path.length() - 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
String token = sb.toString();
|
||||
Assert.hasText(token, invalidPathMessage);
|
||||
dataPath.add(readingIndex ? Integer.parseInt(token) : token);
|
||||
sb.delete(0, sb.length());
|
||||
|
||||
readingIndex = (c == '[');
|
||||
}
|
||||
|
||||
return dataPath;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object getValue(List<Object> path) {
|
||||
Object value = (isValid() ? getData() : null);
|
||||
for (Object segment : path) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (segment instanceof String) {
|
||||
Assert.isTrue(value instanceof Map, () -> "Invalid path " + path + ", data: " + getData());
|
||||
value = ((Map<?, ?>) value).getOrDefault(segment, null);
|
||||
}
|
||||
else {
|
||||
Assert.isTrue(value instanceof List, () -> "Invalid path " + path + ", data: " + getData());
|
||||
int index = (int) segment;
|
||||
value = (index < ((List<?>) value).size() ? ((List<?>) value).get(index) : null);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return field errors whose path starts with the given field path.
|
||||
* @param path the field path to match
|
||||
* @return errors whose path starts with the dataPath
|
||||
*/
|
||||
private List<GraphQlResponseError> getFieldErrors(String path) {
|
||||
if (path.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return getErrors().stream()
|
||||
.filter(error -> {
|
||||
String errorPath = error.getPath();
|
||||
return !errorPath.isEmpty() && (errorPath.startsWith(path) || path.startsWith(errorPath));
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
public ClientGraphQlResponseField field(String path) {
|
||||
return new DefaultClientGraphQlResponseField(this, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,84 +27,25 @@ import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.graphql.GraphQlResponseError;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.graphql.DefaultGraphQlResponseField;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation of {@link GraphQlResponseField}.
|
||||
* Default implementation of {@link ClientGraphQlResponseField}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class DefaultGraphQlResponseField implements GraphQlResponseField {
|
||||
|
||||
private final DefaultClientGraphQlResponse response;
|
||||
|
||||
private final String path;
|
||||
|
||||
private final List<Object> parsedPath;
|
||||
|
||||
@Nullable
|
||||
private final Object value;
|
||||
|
||||
private final List<GraphQlResponseError> fieldErrors;
|
||||
final class DefaultClientGraphQlResponseField extends DefaultGraphQlResponseField implements ClientGraphQlResponseField {
|
||||
|
||||
|
||||
DefaultGraphQlResponseField(
|
||||
DefaultClientGraphQlResponse response, String path, List<Object> parsedPath,
|
||||
@Nullable Object value, List<GraphQlResponseError> errors) {
|
||||
|
||||
this.response = response;
|
||||
this.path = path;
|
||||
this.parsedPath = parsedPath;
|
||||
this.value = value;
|
||||
this.fieldErrors = errors;
|
||||
DefaultClientGraphQlResponseField(DefaultClientGraphQlResponse response, String path) {
|
||||
super(response, path);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getParsedPath() {
|
||||
return this.parsedPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasValue() {
|
||||
return (this.value != null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getValue() {
|
||||
return (T) this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphQlResponseError getError() {
|
||||
if (!hasValue()) {
|
||||
if (!this.fieldErrors.isEmpty()) {
|
||||
return this.fieldErrors.get(0);
|
||||
}
|
||||
if (!this.response.getErrors().isEmpty()) {
|
||||
return this.response.getErrors().get(0);
|
||||
}
|
||||
// No errors, set to null by DataFetcher
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GraphQlResponseError> getErrors() {
|
||||
return this.fieldErrors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> D toEntity(Class<D> entityType) {
|
||||
return toEntity(ResolvableType.forType(entityType));
|
||||
@@ -127,18 +68,19 @@ final class DefaultGraphQlResponseField implements GraphQlResponseField {
|
||||
|
||||
@SuppressWarnings({"unchecked", "ConstantConditions"})
|
||||
private <T> T toEntity(ResolvableType targetType) {
|
||||
if (this.value == null) {
|
||||
throw new FieldAccessException(this.response, this);
|
||||
DefaultClientGraphQlResponse response = getResponse();
|
||||
if (!hasValue()) {
|
||||
throw new FieldAccessException(response, this);
|
||||
}
|
||||
|
||||
DataBufferFactory bufferFactory = DefaultDataBufferFactory.sharedInstance;
|
||||
MimeType mimeType = MimeTypeUtils.APPLICATION_JSON;
|
||||
Map<String, Object> hints = Collections.emptyMap();
|
||||
|
||||
DataBuffer buffer = ((Encoder<T>) this.response.getEncoder()).encodeValue(
|
||||
(T) this.value, bufferFactory, ResolvableType.forInstance(this.value), mimeType, hints);
|
||||
DataBuffer buffer = ((Encoder<T>) response.getEncoder()).encodeValue(
|
||||
(T) getValue(), bufferFactory, ResolvableType.forInstance(getValue()), mimeType, hints);
|
||||
|
||||
return ((Decoder<T>) this.response.getDecoder()).decode(buffer, targetType, mimeType, hints);
|
||||
return ((Decoder<T>) response.getDecoder()).decode(buffer, targetType, mimeType, hints);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -203,8 +203,8 @@ final class DefaultGraphQlClient implements GraphQlClient {
|
||||
* @throws FieldAccessException for invalid response or failed field
|
||||
*/
|
||||
@Nullable
|
||||
protected GraphQlResponseField getValidField(ClientGraphQlResponse response) {
|
||||
GraphQlResponseField field = response.field(this.path);
|
||||
protected ClientGraphQlResponseField getValidField(ClientGraphQlResponse response) {
|
||||
ClientGraphQlResponseField field = response.field(this.path);
|
||||
if (!response.isValid() || field.getError() != null) {
|
||||
throw new FieldAccessException(response, field);
|
||||
}
|
||||
@@ -236,7 +236,7 @@ final class DefaultGraphQlClient implements GraphQlClient {
|
||||
@Override
|
||||
public <D> Mono<List<D>> toEntityList(Class<D> elementType) {
|
||||
return this.responseMono.map(response -> {
|
||||
GraphQlResponseField field = getValidField(response);
|
||||
ClientGraphQlResponseField field = getValidField(response);
|
||||
return (field != null ? field.toEntityList(elementType) : Collections.emptyList());
|
||||
});
|
||||
}
|
||||
@@ -244,7 +244,7 @@ final class DefaultGraphQlClient implements GraphQlClient {
|
||||
@Override
|
||||
public <D> Mono<List<D>> toEntityList(ParameterizedTypeReference<D> elementType) {
|
||||
return this.responseMono.map(response -> {
|
||||
GraphQlResponseField field = getValidField(response);
|
||||
ClientGraphQlResponseField field = getValidField(response);
|
||||
return (field != null ? field.toEntityList(elementType) : Collections.emptyList());
|
||||
});
|
||||
}
|
||||
@@ -274,7 +274,7 @@ final class DefaultGraphQlClient implements GraphQlClient {
|
||||
@Override
|
||||
public <D> Flux<List<D>> toEntityList(Class<D> elementType) {
|
||||
return this.responseFlux.map(response -> {
|
||||
GraphQlResponseField field = getValidField(response);
|
||||
ClientGraphQlResponseField field = getValidField(response);
|
||||
return (field != null ? field.toEntityList(elementType) : Collections.emptyList());
|
||||
});
|
||||
}
|
||||
@@ -282,7 +282,7 @@ final class DefaultGraphQlClient implements GraphQlClient {
|
||||
@Override
|
||||
public <D> Flux<List<D>> toEntityList(ParameterizedTypeReference<D> elementType) {
|
||||
return this.responseFlux.map(response -> {
|
||||
GraphQlResponseField field = getValidField(response);
|
||||
ClientGraphQlResponseField field = getValidField(response);
|
||||
return (field != null ? field.toEntityList(elementType) : Collections.emptyList());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ import org.springframework.graphql.GraphQlResponse;
|
||||
/**
|
||||
* An exception raised on an attempt to decode data from a
|
||||
* {@link GraphQlResponse#isValid() failed response} or a field is not present,
|
||||
* or has no value, checked via {@link GraphQlResponseField#hasValue()}.
|
||||
* or has no value, checked via
|
||||
* {@link org.springframework.graphql.GraphQlResponseField#hasValue()}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
@@ -32,19 +33,19 @@ public class FieldAccessException extends GraphQlClientException {
|
||||
|
||||
private final ClientGraphQlResponse response;
|
||||
|
||||
private final GraphQlResponseField field;
|
||||
private final ClientGraphQlResponseField field;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor with the request and response, and the accessed field.
|
||||
*/
|
||||
public FieldAccessException(ClientGraphQlResponse response, GraphQlResponseField field) {
|
||||
public FieldAccessException(ClientGraphQlResponse response, ClientGraphQlResponseField field) {
|
||||
super(initDefaultMessage(field), null, response.getRequest());
|
||||
this.response = response;
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
private static String initDefaultMessage(GraphQlResponseField field) {
|
||||
private static String initDefaultMessage(ClientGraphQlResponseField field) {
|
||||
return "Invalid field '" + field.getPath() + "', errors: " + field.getErrors();
|
||||
}
|
||||
|
||||
@@ -59,7 +60,7 @@ public class FieldAccessException extends GraphQlClientException {
|
||||
/**
|
||||
* Return the field that needed to be accessed.
|
||||
*/
|
||||
public GraphQlResponseField getField() {
|
||||
public ClientGraphQlResponseField getField() {
|
||||
return this.field;
|
||||
}
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ public interface GraphQlClient {
|
||||
* the field is {@code null} without errors, or ends with
|
||||
* {@link FieldAccessException} for an invalid response or a failed field
|
||||
* @see GraphQlResponse#isValid()
|
||||
* @see GraphQlResponseField#getError()
|
||||
* @see org.springframework.graphql.GraphQlResponseField#getError()
|
||||
*/
|
||||
<D> Mono<D> toEntity(Class<D> entityType);
|
||||
|
||||
@@ -201,7 +201,7 @@ public interface GraphQlClient {
|
||||
* empty list, or ends with {@link FieldAccessException} if the target
|
||||
* field is not present or has no value.
|
||||
* @see GraphQlResponse#isValid()
|
||||
* @see GraphQlResponseField#getError()
|
||||
* @see org.springframework.graphql.GraphQlResponseField#getError()
|
||||
*/
|
||||
<D> Mono<List<D>> toEntityList(Class<D> elementType);
|
||||
|
||||
@@ -226,7 +226,7 @@ public interface GraphQlClient {
|
||||
* {@link FieldAccessException} for an invalid response or a failed field.
|
||||
* May also end with a {@link GraphQlTransportException}.
|
||||
* @see GraphQlResponse#isValid()
|
||||
* @see GraphQlResponseField#getError()
|
||||
* @see org.springframework.graphql.GraphQlResponseField#getError()
|
||||
*/
|
||||
<D> Flux<D> toEntity(Class<D> entityType);
|
||||
|
||||
@@ -243,7 +243,7 @@ public interface GraphQlClient {
|
||||
* {@link FieldAccessException} for an invalid response or a failed field.
|
||||
* May also end with a {@link GraphQlTransportException}.
|
||||
* @see GraphQlResponse#isValid()
|
||||
* @see GraphQlResponseField#getError()
|
||||
* @see org.springframework.graphql.GraphQlResponseField#getError()
|
||||
*/
|
||||
<D> Flux<List<D>> toEntityList(Class<D> elementType);
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ public class DefaultGraphQlClientResponseTests {
|
||||
GraphQLError error2 = createError("/me/friends", "fail-me-friends");
|
||||
GraphQLError error3 = createError("/me/friends[0]/name", "fail-me-friends-name");
|
||||
|
||||
GraphQlResponseField field = getField(path, error0, error1, error2, error3);
|
||||
ClientGraphQlResponseField field = getField(path, error0, error1, error2, error3);
|
||||
List<GraphQlResponseError> errors = field.getErrors();
|
||||
|
||||
assertThat(errors).hasSize(3);
|
||||
@@ -144,13 +144,13 @@ public class DefaultGraphQlClientResponseTests {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private GraphQlResponseField getField(String path, String dataJson) throws Exception {
|
||||
private ClientGraphQlResponseField getField(String path, String dataJson) throws Exception {
|
||||
Map<?, ?> dataMap = mapper.readValue(dataJson, Map.class);
|
||||
ClientGraphQlResponse response = creatResponse(Collections.singletonMap("data", dataMap));
|
||||
return response.field(path);
|
||||
}
|
||||
|
||||
private GraphQlResponseField getField(String path, GraphQLError... errors) {
|
||||
private ClientGraphQlResponseField getField(String path, GraphQLError... errors) {
|
||||
List<?> list = Arrays.stream(errors).map(GraphQLError::toSpecification).collect(Collectors.toList());
|
||||
ClientGraphQlResponse response = creatResponse(Collections.singletonMap("errors", list));
|
||||
return response.field(path);
|
||||
|
||||
@@ -188,7 +188,7 @@ public class GraphQlClientTests extends GraphQlClientTestSupport {
|
||||
.as("Partial response with field errors should be considered valid")
|
||||
.isTrue();
|
||||
|
||||
GraphQlResponseField field = response.field("me");
|
||||
ClientGraphQlResponseField field = response.field("me");
|
||||
assertThat(field.hasValue()).isTrue();
|
||||
assertThat(field.getErrors()).hasSize(1);
|
||||
assertThat(field.getErrors().get(0).getParsedPath()).containsExactly("me", "name");
|
||||
@@ -196,7 +196,7 @@ public class GraphQlClientTests extends GraphQlClientTestSupport {
|
||||
.as("Decoding with nested field error should not be precluded")
|
||||
.isNotNull();
|
||||
|
||||
GraphQlResponseField nameField = response.field("me.name");
|
||||
ClientGraphQlResponseField nameField = response.field("me.name");
|
||||
assertThat(nameField.hasValue()).isFalse();
|
||||
assertThat(nameField.getError()).isNotNull();
|
||||
assertThat(nameField.getError().getParsedPath()).containsExactly("me", "name");
|
||||
@@ -204,7 +204,7 @@ public class GraphQlClientTests extends GraphQlClientTestSupport {
|
||||
.as("Decoding field null with direct field error should be rejected")
|
||||
.isInstanceOf(FieldAccessException.class);
|
||||
|
||||
GraphQlResponseField nonExistingField = response.field("me.name.other");
|
||||
ClientGraphQlResponseField nonExistingField = response.field("me.name.other");
|
||||
assertThat(nonExistingField.hasValue()).isFalse();
|
||||
assertThat(nameField.getError()).isNotNull();
|
||||
assertThat(nameField.getError().getParsedPath()).containsExactly("me", "name");
|
||||
|
||||
Reference in New Issue
Block a user