Refine null handling in ClientResponseField#toEntity
closes gh-525
This commit is contained in:
@@ -20,7 +20,9 @@ package org.springframework.graphql.client;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.ResponseField;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Extends {@link ResponseField} to add options for decoding the field value.
|
||||
@@ -33,27 +35,41 @@ public interface ClientResponseField extends ResponseField {
|
||||
/**
|
||||
* 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 {@code null} and has
|
||||
* @return the decoded entity, or {@code null} if the field is {@code null}
|
||||
* but otherwise there are no errors
|
||||
* @throws FieldAccessException if the target field is {@code null} and the
|
||||
* response is not {@link GraphQlResponse#isValid() valid} or the field has
|
||||
* {@link ResponseField#getErrors() errors}.
|
||||
*/
|
||||
@Nullable
|
||||
<D> D toEntity(Class<D> entityType);
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}.
|
||||
*/
|
||||
@Nullable
|
||||
<D> D toEntity(ParameterizedTypeReference<D> entityType);
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} to decode to a list of entities.
|
||||
* @param elementType the type of elements in the list
|
||||
* @return the list of decoded entities, or an empty list if the field is
|
||||
* {@code null} but otherwise there are no errors
|
||||
* @throws FieldAccessException if the target field is {@code null} and the
|
||||
* response is not {@link GraphQlResponse#isValid() valid} or the field has
|
||||
* {@link ResponseField#getErrors() errors}.
|
||||
*/
|
||||
<D> List<D> toEntityList(Class<D> elementType);
|
||||
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} to decode to a list of entities.
|
||||
* @param elementType the type of elements in the list
|
||||
*/
|
||||
/**
|
||||
* Variant of {@link #toEntity(Class)} to decode to a list of entities.
|
||||
* @param elementType the type of elements in the list
|
||||
* @return the list of decoded entities, or an empty list if the field is
|
||||
* {@code null} but otherwise there are no errors
|
||||
* @throws FieldAccessException if the target field is {@code null} and the
|
||||
* response is not {@link GraphQlResponse#isValid() valid} or the field has
|
||||
* {@link ResponseField#getErrors() errors}.
|
||||
*/
|
||||
<D> List<D> toEntityList(ParameterizedTypeReference<D> elementType);
|
||||
|
||||
}
|
||||
|
||||
@@ -68,12 +68,28 @@ final class DefaultClientGraphQlResponse extends ResponseMapGraphQlResponse impl
|
||||
|
||||
@Override
|
||||
public <D> D toEntity(Class<D> type) {
|
||||
return field("").toEntity(type);
|
||||
ClientResponseField field = field("");
|
||||
D entity = field.toEntity(type);
|
||||
|
||||
// should never happen because toEntity checks response.isValid
|
||||
if (entity == null) {
|
||||
throw new FieldAccessException(getRequest(), this, field);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> D toEntity(ParameterizedTypeReference<D> type) {
|
||||
return field("").toEntity(type);
|
||||
ClientResponseField field = field("");
|
||||
D entity = field.toEntity(type);
|
||||
|
||||
// should never happen because toEntity checks response.isValid
|
||||
if (entity == null) {
|
||||
throw new FieldAccessException(getRequest(), this, field);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.graphql.ResponseError;
|
||||
import org.springframework.graphql.ResponseField;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
@@ -98,17 +99,23 @@ final class DefaultClientResponseField implements ClientResponseField {
|
||||
|
||||
@Override
|
||||
public <D> List<D> toEntityList(Class<D> elementType) {
|
||||
return toEntity(ResolvableType.forClassWithGenerics(List.class, elementType));
|
||||
List<D> list = toEntity(ResolvableType.forClassWithGenerics(List.class, elementType));
|
||||
return (list != null ? list : Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> List<D> toEntityList(ParameterizedTypeReference<D> elementType) {
|
||||
return toEntity(ResolvableType.forClassWithGenerics(List.class, ResolvableType.forType(elementType)));
|
||||
List<D> list = toEntity(ResolvableType.forClassWithGenerics(List.class, ResolvableType.forType(elementType)));
|
||||
return (list != null ? list : Collections.emptyList());
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "ConstantConditions"})
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
private <T> T toEntity(ResolvableType targetType) {
|
||||
if (getValue() == null) {
|
||||
if (this.response.isValid() && getErrors().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
throw new FieldAccessException(this.response.getRequest(), this.response, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -186,9 +186,8 @@ final class DefaultGraphQlClient implements GraphQlClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the field or {@code null}, but only if the response is valid
|
||||
* and there are no field errors, or raise {@link FieldAccessException}
|
||||
* otherwise.
|
||||
* Return the field or {@code null}, but only if the response is valid and
|
||||
* there are no field errors. Raise {@link FieldAccessException} otherwise.
|
||||
* @throws FieldAccessException in case of an invalid response or any
|
||||
* field error at, above or below the field path
|
||||
*/
|
||||
@@ -216,12 +215,12 @@ final class DefaultGraphQlClient implements GraphQlClient {
|
||||
|
||||
@Override
|
||||
public <D> Mono<D> toEntity(Class<D> entityType) {
|
||||
return this.responseMono.mapNotNull(this::getValidField).map(field -> field.toEntity(entityType));
|
||||
return this.responseMono.mapNotNull(this::getValidField).mapNotNull(field -> field.toEntity(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> Mono<D> toEntity(ParameterizedTypeReference<D> entityType) {
|
||||
return this.responseMono.mapNotNull(this::getValidField).map(field -> field.toEntity(entityType));
|
||||
return this.responseMono.mapNotNull(this::getValidField).mapNotNull(field -> field.toEntity(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -254,12 +253,12 @@ final class DefaultGraphQlClient implements GraphQlClient {
|
||||
|
||||
@Override
|
||||
public <D> Flux<D> toEntity(Class<D> entityType) {
|
||||
return this.responseFlux.mapNotNull(this::getValidField).map(field -> field.toEntity(entityType));
|
||||
return this.responseFlux.mapNotNull(this::getValidField).mapNotNull(field -> field.toEntity(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> Flux<D> toEntity(ParameterizedTypeReference<D> entityType) {
|
||||
return this.responseFlux.mapNotNull(this::getValidField).map(field -> field.toEntity(entityType));
|
||||
return this.responseFlux.mapNotNull(this::getValidField).mapNotNull(field -> field.toEntity(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user