Support Optional constructor arg for argument binding
Closes gh-470
This commit is contained in:
@@ -188,9 +188,10 @@ public class GraphQlArgumentBinder {
|
||||
return (type.resolve(Object.class).equals(Optional.class) ? Optional.ofNullable(value) : value);
|
||||
}
|
||||
|
||||
private boolean isApproximableCollectionType(Object rawValue) {
|
||||
return (CollectionFactory.isApproximableCollectionType(rawValue.getClass()) ||
|
||||
rawValue instanceof List); // it may be SingletonList
|
||||
private boolean isApproximableCollectionType(@Nullable Object rawValue) {
|
||||
return (rawValue != null &&
|
||||
(CollectionFactory.isApproximableCollectionType(rawValue.getClass()) ||
|
||||
rawValue instanceof List)); // it may be SingletonList
|
||||
}
|
||||
|
||||
@SuppressWarnings({"ConstantConditions", "unchecked"})
|
||||
@@ -283,12 +284,15 @@ public class GraphQlArgumentBinder {
|
||||
if (rawValue == null && methodParam.isOptional()) {
|
||||
args[i] = (paramTypes[i] == Optional.class ? Optional.empty() : null);
|
||||
}
|
||||
else if (rawValue != null && isApproximableCollectionType(rawValue)) {
|
||||
else if (isApproximableCollectionType(rawValue)) {
|
||||
ResolvableType elementType = ResolvableType.forMethodParameter(methodParam);
|
||||
args[i] = createCollection((Collection<Object>) rawValue, elementType, bindingResult, segments);
|
||||
}
|
||||
else if (rawValue instanceof Map) {
|
||||
args[i] = createValueOrNull((Map<String, Object>) rawValue, paramTypes[i], bindingResult, segments);
|
||||
boolean isOptional = (paramTypes[i] == Optional.class);
|
||||
Class<?> type = (isOptional ? methodParam.nestedIfOptional().getNestedParameterType() : paramTypes[i]);
|
||||
Object value = createValueOrNull((Map<String, Object>) rawValue, type, bindingResult, segments);
|
||||
args[i] = (isOptional ? Optional.ofNullable(value) : value);
|
||||
}
|
||||
else {
|
||||
args[i] = convertValue(rawValue, paramTypes[i], new TypeDescriptor(methodParam), bindingResult, segments);
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
@@ -32,6 +33,7 @@ import graphql.schema.DataFetchingEnvironmentImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.graphql.Book;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.FieldError;
|
||||
@@ -182,6 +184,26 @@ class GraphQlArgumentBinderTests {
|
||||
assertThat(((PrimaryConstructorItemBean) result).getAge()).isEqualTo(30);
|
||||
}
|
||||
|
||||
@Test
|
||||
void primaryConstructorWithOptionalBeanArgument() throws Exception {
|
||||
|
||||
GraphQlArgumentBinder argumentBinder =
|
||||
new GraphQlArgumentBinder(new DefaultFormattingConversionService());
|
||||
|
||||
Object result = argumentBinder.bind(
|
||||
environment(
|
||||
"{\"key\":{" +
|
||||
"\"item\":{\"name\":\"Item name\"}," +
|
||||
"\"name\":\"Hello\"," +
|
||||
"\"age\":\"30\"}}"),
|
||||
"key",
|
||||
ResolvableType.forClass(PrimaryConstructorOptionalItemBean.class));
|
||||
|
||||
assertThat(result).isNotNull().isInstanceOf(PrimaryConstructorOptionalItemBean.class);
|
||||
assertThat(((PrimaryConstructorOptionalItemBean) result).getItem().get().getName()).isEqualTo("Item name");
|
||||
assertThat(((PrimaryConstructorOptionalItemBean) result).getName().get()).isEqualTo("Hello");
|
||||
}
|
||||
|
||||
@Test
|
||||
void primaryConstructorWithNestedBeanList() throws Exception {
|
||||
|
||||
@@ -390,6 +412,28 @@ class GraphQlArgumentBinderTests {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
static class PrimaryConstructorOptionalItemBean {
|
||||
|
||||
private final Optional<String> name;
|
||||
|
||||
private final Optional<Item> item;
|
||||
|
||||
public PrimaryConstructorOptionalItemBean(Optional<String> name, Optional<Item> item) {
|
||||
this.name = name;
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public Optional<String> getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public Optional<Item> getItem() {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class NoPrimaryConstructorBean {
|
||||
|
||||
NoPrimaryConstructorBean(String name) {
|
||||
|
||||
Reference in New Issue
Block a user