Refactoring in GraphQL argument initialization
Consolidate GraphQL argument initialization by pushing logic from ArgumentMethodArgumentResolver down into GraphQlArgumentInitializer, which now takes the DataFetchingEnvironment, an optional argument name, and a target type, and does the rest of the work.
This commit is contained in:
@@ -24,14 +24,15 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Stack;
|
||||
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.SimpleTypeConverter;
|
||||
import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.validation.DataBinder;
|
||||
@@ -56,27 +57,108 @@ public class GraphQlArgumentInitializer {
|
||||
|
||||
|
||||
/**
|
||||
* Return the underlying {@link DataBinder}.
|
||||
* Initialize an Object of the given {@code targetType}, either from a named
|
||||
* {@link DataFetchingEnvironment#getArgument(String) argument value}, or from all
|
||||
* {@link DataFetchingEnvironment#getArguments() values} as the source.
|
||||
* @param environment the environment with the argument values
|
||||
* @param name optionally, the name of an argument to initialize from,
|
||||
* or if {@code null}, the full map of arguments is used.
|
||||
* @param targetType the type of Object to initialize
|
||||
* @return the initialized Object, or {@code null}
|
||||
*/
|
||||
public TypeConverter getTypeConverter() {
|
||||
return this.typeConverter;
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object initializeArgument(
|
||||
DataFetchingEnvironment environment, @Nullable String name, ResolvableType targetType) {
|
||||
|
||||
Object sourceValue = (name != null ? environment.getArgument(name) : environment.getArguments());
|
||||
|
||||
if (sourceValue == null) {
|
||||
return wrapAsOptionalIfNecessary(null, targetType);
|
||||
}
|
||||
|
||||
Class<?> targetClass = targetType.resolve();
|
||||
Assert.notNull(targetClass, "Could not determine target type from " + targetType);
|
||||
|
||||
// From Collection
|
||||
|
||||
if (CollectionFactory.isApproximableCollectionType(sourceValue.getClass())) {
|
||||
Assert.isAssignable(Collection.class, targetClass,
|
||||
"Argument '" + name + "' is a Collection while method parameter is " + targetClass.getName());
|
||||
Class<?> elementType = targetType.asCollection().getGeneric(0).resolve();
|
||||
Assert.notNull(elementType, "Could not determine element type for " + targetType);
|
||||
return initializeFromCollection((Collection<Object>) sourceValue, elementType);
|
||||
}
|
||||
|
||||
if (targetClass == Optional.class) {
|
||||
targetClass = targetType.getNested(2).resolve();
|
||||
Assert.notNull(targetClass, "Could not determine Optional<T> type from " + targetType);
|
||||
}
|
||||
|
||||
// From Map
|
||||
|
||||
if (sourceValue instanceof Map) {
|
||||
Object target = initializeFromMap((Map<String, Object>) sourceValue, targetClass);
|
||||
return wrapAsOptionalIfNecessary(target, targetType);
|
||||
}
|
||||
|
||||
// From Scalar
|
||||
|
||||
if (targetClass.isInstance(sourceValue)) {
|
||||
return wrapAsOptionalIfNecessary(sourceValue, targetType);
|
||||
}
|
||||
|
||||
Object target = this.typeConverter.convertIfNecessary(sourceValue, targetClass);
|
||||
if (target == null) {
|
||||
throw new IllegalStateException("Cannot convert argument value " +
|
||||
"type [" + sourceValue.getClass().getName() + "] to method parameter " +
|
||||
"type [" + targetClass.getName() + "].");
|
||||
}
|
||||
|
||||
return wrapAsOptionalIfNecessary(target, targetType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object wrapAsOptionalIfNecessary(@Nullable Object value, ResolvableType type) {
|
||||
return (type.resolve(Object.class).equals(Optional.class) ? Optional.ofNullable(value) : value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a collection of {@code elementType} using the given {@code values}.
|
||||
* <p>This will instantiate a new Collection of the closest type possible
|
||||
* from the one provided as an argument.
|
||||
*
|
||||
* @param <T> the type of Collection elements
|
||||
* @param values the collection of values to bind and instantiate
|
||||
* @param elementClass the type of elements in the given Collection
|
||||
* @return the instantiated and populated Collection.
|
||||
* @throws IllegalStateException if there is no suitable constructor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Collection<T> initializeFromCollection(Collection<Object> values, Class<T> elementClass) {
|
||||
Collection<T> collection = CollectionFactory.createApproximateCollection(values, values.size());
|
||||
for (Object item : values) {
|
||||
if (elementClass.isAssignableFrom(item.getClass())) {
|
||||
collection.add((T) item);
|
||||
}
|
||||
else if (item instanceof Map) {
|
||||
collection.add((T) this.initializeFromMap((Map<String, Object>) item, elementClass));
|
||||
}
|
||||
else {
|
||||
collection.add(this.typeConverter.convertIfNecessary(item, elementClass));
|
||||
}
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate an Object of the given target type and bind
|
||||
* {@link graphql.schema.DataFetchingEnvironment} argument values to it.
|
||||
* This considers using the default constructor or a primary constructor,
|
||||
* if available.
|
||||
*
|
||||
* @param arguments the data fetching environment arguments
|
||||
* @param targetType the type of the argument to instantiate
|
||||
* @param <T> the type of the input argument
|
||||
* @return the instantiated and populated input argument.
|
||||
* This considers the default constructor or a primary constructor, if available.
|
||||
* @throws IllegalStateException if there is no suitable constructor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T initializeFromMap(Map<String, Object> arguments, Class<T> targetType) {
|
||||
private Object initializeFromMap(Map<String, Object> arguments, Class<?> targetType) {
|
||||
Object target;
|
||||
Constructor<?> ctor = BeanUtils.getResolvableConstructor(targetType);
|
||||
|
||||
@@ -85,7 +167,7 @@ public class GraphQlArgumentInitializer {
|
||||
target = BeanUtils.instantiateClass(ctor);
|
||||
DataBinder dataBinder = new DataBinder(target);
|
||||
dataBinder.bind(propertyValues);
|
||||
return (T) target;
|
||||
return target;
|
||||
}
|
||||
|
||||
// Data class constructor
|
||||
@@ -96,56 +178,25 @@ public class GraphQlArgumentInitializer {
|
||||
for (int i = 0; i < paramNames.length; i++) {
|
||||
String paramName = paramNames[i];
|
||||
Object value = arguments.get(paramName);
|
||||
MethodParameter methodParam = new MethodParameter(ctor, i);
|
||||
if (value == null && methodParam.isOptional()) {
|
||||
args[i] = (methodParam.getParameterType() == Optional.class ? Optional.empty() : null);
|
||||
MethodParameter methodParameter = new MethodParameter(ctor, i);
|
||||
if (value == null && methodParameter.isOptional()) {
|
||||
args[i] = (methodParameter.getParameterType() == Optional.class ? Optional.empty() : null);
|
||||
}
|
||||
else if (value != null && CollectionFactory.isApproximableCollectionType(value.getClass())) {
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(methodParam);
|
||||
Class<?> elementType = typeDescriptor.getElementTypeDescriptor().getType();
|
||||
ResolvableType resolvableType = ResolvableType.forMethodParameter(methodParameter);
|
||||
Class<?> elementType = resolvableType.asCollection().getGeneric(0).resolve();
|
||||
Assert.notNull(elementType, "Cannot determine element type for " + resolvableType);
|
||||
args[i] = initializeFromCollection((Collection<Object>) value, elementType);
|
||||
}
|
||||
else if (value instanceof Map) {
|
||||
args[i] = this.initializeFromMap((Map<String, Object>) value, methodParam.getParameterType());
|
||||
args[i] = this.initializeFromMap((Map<String, Object>) value, methodParameter.getParameterType());
|
||||
}
|
||||
else {
|
||||
args[i] = this.typeConverter.convertIfNecessary(value, paramTypes[i], methodParam);
|
||||
args[i] = this.typeConverter.convertIfNecessary(value, paramTypes[i], methodParameter);
|
||||
}
|
||||
}
|
||||
|
||||
return (T) BeanUtils.instantiateClass(ctor, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a collection of {@code elementType} using the given {@code values}.
|
||||
* <p>This will instantiate a new Collection of the closest type possible
|
||||
* from the one provided as an argument.
|
||||
*
|
||||
* @param <T> the type of Collection elements
|
||||
* @param values the collection of values to bind and instantiate
|
||||
* @param elementType the type of elements in the given Collection
|
||||
* @return the instantiated and populated Collection.
|
||||
* @throws IllegalStateException if there is no suitable constructor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Collection<T> initializeFromCollection(Collection<Object> values, Class<T> elementType) {
|
||||
Assert.state(CollectionFactory.isApproximableCollectionType(values.getClass()),
|
||||
() -> "Cannot instantiate Collection for type " + values.getClass());
|
||||
Collection<T> instances = CollectionFactory.createApproximateCollection(values, values.size());
|
||||
values.forEach(item -> {
|
||||
T value;
|
||||
if (elementType.isAssignableFrom(item.getClass())) {
|
||||
value = (T) item;
|
||||
}
|
||||
else if (item instanceof Map) {
|
||||
value = this.initializeFromMap((Map<String, Object>)item, elementType);
|
||||
}
|
||||
else {
|
||||
value = this.typeConverter.convertIfNecessary(item, elementType);
|
||||
}
|
||||
instances.add(value);
|
||||
});
|
||||
return instances;
|
||||
return BeanUtils.instantiateClass(ctor, args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,6 +46,7 @@ import org.springframework.core.MethodIntrospector;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.graphql.data.GraphQlArgumentInitializer;
|
||||
import org.springframework.graphql.data.method.HandlerMethod;
|
||||
import org.springframework.graphql.data.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.graphql.data.method.HandlerMethodArgumentResolverComposite;
|
||||
@@ -128,7 +129,8 @@ public class AnnotatedControllerConfigurer
|
||||
this.argumentResolvers.addResolver(new ProjectedPayloadMethodArgumentResolver());
|
||||
}
|
||||
this.argumentResolvers.addResolver(new ArgumentMapMethodArgumentResolver());
|
||||
this.argumentResolvers.addResolver(new ArgumentMethodArgumentResolver(this.conversionService));
|
||||
GraphQlArgumentInitializer initializer = new GraphQlArgumentInitializer(this.conversionService);
|
||||
this.argumentResolvers.addResolver(new ArgumentMethodArgumentResolver(initializer));
|
||||
this.argumentResolvers.addResolver(new ContextValueMethodArgumentResolver());
|
||||
|
||||
// Type based
|
||||
|
||||
@@ -15,20 +15,13 @@
|
||||
*/
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.graphql.data.GraphQlArgumentInitializer;
|
||||
import org.springframework.graphql.data.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.graphql.data.method.annotation.Argument;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -46,8 +39,9 @@ public class ArgumentMethodArgumentResolver implements HandlerMethodArgumentReso
|
||||
private final GraphQlArgumentInitializer argumentInitializer;
|
||||
|
||||
|
||||
public ArgumentMethodArgumentResolver(@Nullable ConversionService conversionService) {
|
||||
this.argumentInitializer = new GraphQlArgumentInitializer(conversionService);
|
||||
public ArgumentMethodArgumentResolver(GraphQlArgumentInitializer initializer) {
|
||||
Assert.notNull(initializer, "GraphQlArgumentInitializer is required");
|
||||
this.argumentInitializer = initializer;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,47 +51,10 @@ public class ArgumentMethodArgumentResolver implements HandlerMethodArgumentReso
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception {
|
||||
String name = getArgumentName(parameter);
|
||||
Object rawValue = environment.getArgument(name);
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(parameter);
|
||||
|
||||
if (rawValue == null) {
|
||||
return wrapAsOptionalIfNecessary(null, typeDescriptor.getType());
|
||||
}
|
||||
|
||||
// From Collection
|
||||
|
||||
if (CollectionFactory.isApproximableCollectionType(rawValue.getClass())) {
|
||||
Assert.isAssignable(Collection.class, typeDescriptor.getType(),
|
||||
"Argument '" + name + "' is a Collection " +
|
||||
"while the @Argument method parameter is " + typeDescriptor.getType());
|
||||
Class<?> elementType = typeDescriptor.getElementTypeDescriptor().getType();
|
||||
return this.argumentInitializer.initializeFromCollection((Collection<Object>) rawValue, elementType);
|
||||
}
|
||||
|
||||
Class<?> targetType = parameter.nestedIfOptional().getNestedParameterType();
|
||||
Object target;
|
||||
|
||||
// From Map
|
||||
|
||||
if (rawValue instanceof Map) {
|
||||
target = this.argumentInitializer.initializeFromMap((Map<String, Object>) rawValue, targetType);
|
||||
return wrapAsOptionalIfNecessary(target, typeDescriptor.getType());
|
||||
}
|
||||
|
||||
// From Scalar
|
||||
|
||||
if (targetType.isAssignableFrom(rawValue.getClass())) {
|
||||
return wrapAsOptionalIfNecessary(rawValue, targetType);
|
||||
}
|
||||
|
||||
target = this.argumentInitializer.getTypeConverter().convertIfNecessary(rawValue, targetType);
|
||||
Assert.state(target != null, () ->
|
||||
"Cannot convert value type [" + rawValue.getClass() + "] " +
|
||||
"to argument type [" + targetType.getName() + "].");
|
||||
return wrapAsOptionalIfNecessary(target, typeDescriptor.getType());
|
||||
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
|
||||
return this.argumentInitializer.initializeArgument(environment, name, resolvableType);
|
||||
}
|
||||
|
||||
static String getArgumentName(MethodParameter parameter) {
|
||||
@@ -115,9 +72,4 @@ public class ArgumentMethodArgumentResolver implements HandlerMethodArgumentReso
|
||||
"] not specified, and parameter name information not found in class file either.");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object wrapAsOptionalIfNecessary(@Nullable Object value, Class<?> type) {
|
||||
return (type.equals(Optional.class) ? Optional.ofNullable(value) : value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import graphql.schema.GraphQLTypeVisitor;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.query.FluentQuery;
|
||||
@@ -103,8 +104,10 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* @param env contextual info for the GraphQL query
|
||||
* @return the resulting example
|
||||
*/
|
||||
@SuppressWarnings({"ConstantConditions", "unchecked"})
|
||||
protected Example<T> buildExample(DataFetchingEnvironment env) {
|
||||
return Example.of(this.argumentInitializer.initializeFromMap(env.getArguments(), this.domainType.getType()));
|
||||
ResolvableType targetType = ResolvableType.forClass(this.domainType.getType());
|
||||
return (Example<T>) Example.of(this.argumentInitializer.initializeArgument(env, null, targetType));
|
||||
}
|
||||
|
||||
protected boolean requiresProjection(Class<?> resultType) {
|
||||
|
||||
@@ -33,6 +33,8 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.SynthesizingMethodParameter;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.graphql.Book;
|
||||
import org.springframework.graphql.data.GraphQlArgumentInitializer;
|
||||
import org.springframework.graphql.data.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.graphql.data.method.annotation.Argument;
|
||||
import org.springframework.graphql.data.method.annotation.MutationMapping;
|
||||
import org.springframework.graphql.data.method.annotation.QueryMapping;
|
||||
@@ -49,7 +51,8 @@ class ArgumentMethodArgumentResolverTests {
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
ArgumentMethodArgumentResolver resolver = new ArgumentMethodArgumentResolver(new DefaultFormattingConversionService());
|
||||
private final HandlerMethodArgumentResolver resolver =
|
||||
new ArgumentMethodArgumentResolver(new GraphQlArgumentInitializer(new DefaultFormattingConversionService()));
|
||||
|
||||
@Test
|
||||
void shouldSupportAnnotatedParameters() {
|
||||
|
||||
@@ -26,6 +26,7 @@ import graphql.schema.DataFetchingEnvironment;
|
||||
import graphql.schema.DataFetchingEnvironmentImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.graphql.Book;
|
||||
import org.springframework.graphql.data.GraphQlArgumentInitializer;
|
||||
|
||||
@@ -41,13 +42,15 @@ class GraphQlArgumentInitializerTests {
|
||||
|
||||
private ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
private GraphQlArgumentInitializer instantiator = new GraphQlArgumentInitializer(null);
|
||||
private GraphQlArgumentInitializer initializer = new GraphQlArgumentInitializer(null);
|
||||
|
||||
|
||||
@Test
|
||||
void shouldInstantiateDefaultConstructor() throws Exception {
|
||||
String payload = "{\"simpleBean\": { \"name\": \"test\"} }";
|
||||
DataFetchingEnvironment environment = initEnvironment(payload);
|
||||
SimpleBean result = instantiator.initializeFromMap(environment.getArgument("simpleBean"), SimpleBean.class);
|
||||
Object result = initializer.initializeArgument(
|
||||
environment, "simpleBean", ResolvableType.forClass(SimpleBean.class));
|
||||
|
||||
assertThat(result).isNotNull().isInstanceOf(SimpleBean.class);
|
||||
assertThat(result).hasFieldOrPropertyWithValue("name", "test");
|
||||
@@ -57,7 +60,8 @@ class GraphQlArgumentInitializerTests {
|
||||
void shouldInstantiatePrimaryConstructor() throws Exception {
|
||||
String payload = "{\"constructorBean\": { \"name\": \"test\"} }";
|
||||
DataFetchingEnvironment environment = initEnvironment(payload);
|
||||
ContructorBean result = instantiator.initializeFromMap(environment.getArgument("constructorBean"), ContructorBean.class);
|
||||
Object result = initializer.initializeArgument(
|
||||
environment, "constructorBean", ResolvableType.forClass(ContructorBean.class));
|
||||
|
||||
assertThat(result).isNotNull().isInstanceOf(ContructorBean.class);
|
||||
assertThat(result).hasFieldOrPropertyWithValue("name", "test");
|
||||
@@ -67,19 +71,21 @@ class GraphQlArgumentInitializerTests {
|
||||
void shouldFailIfNoPrimaryConstructor() throws Exception {
|
||||
String payload = "{\"noPrimary\": { \"name\": \"test\"} }";
|
||||
DataFetchingEnvironment environment = initEnvironment(payload);
|
||||
assertThatThrownBy(() -> instantiator.initializeFromMap(environment.getArgument("noPrimary"), NoPrimaryConstructor.class))
|
||||
.isInstanceOf(IllegalStateException.class).hasMessageContaining("No primary or single unique constructor found");
|
||||
assertThatThrownBy(() -> {
|
||||
ResolvableType targetType = ResolvableType.forClass(NoPrimaryConstructor.class);
|
||||
initializer.initializeArgument(environment, "noPrimary", targetType);
|
||||
}).isInstanceOf(IllegalStateException.class).hasMessageContaining("No primary or single unique constructor found");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInstantiateNestedBean() throws Exception {
|
||||
String payload = "{\"book\": { \"name\": \"test name\", \"author\": { \"firstName\": \"Jane\", \"lastName\": \"Spring\"} } }";
|
||||
DataFetchingEnvironment environment = initEnvironment(payload);
|
||||
Book result = instantiator.initializeFromMap(environment.getArgument("book"), Book.class);
|
||||
Object result = initializer.initializeArgument(environment, "book", ResolvableType.forClass(Book.class));
|
||||
|
||||
assertThat(result).isNotNull().isInstanceOf(Book.class);
|
||||
assertThat(result).hasFieldOrPropertyWithValue("name", "test name");
|
||||
assertThat(result.getAuthor()).isNotNull()
|
||||
assertThat(((Book) result).getAuthor()).isNotNull()
|
||||
.hasFieldOrPropertyWithValue("firstName", "Jane")
|
||||
.hasFieldOrPropertyWithValue("lastName", "Spring");
|
||||
}
|
||||
@@ -88,31 +94,35 @@ class GraphQlArgumentInitializerTests {
|
||||
void shouldInstantiateNestedBeanLists() throws Exception {
|
||||
String payload = "{\"nestedList\": { \"items\": [ {\"name\": \"first\"}, {\"name\": \"second\"}] } }";
|
||||
DataFetchingEnvironment environment = initEnvironment(payload);
|
||||
NestedList result = instantiator.initializeFromMap(environment.getArgument("nestedList"), NestedList.class);
|
||||
Object result = initializer.initializeArgument(
|
||||
environment, "nestedList", ResolvableType.forClass(NestedList.class));
|
||||
|
||||
assertThat(result).isNotNull().isInstanceOf(NestedList.class);
|
||||
assertThat(result.getItems()).hasSize(2).extracting("name").containsExactly("first", "second");
|
||||
assertThat(((NestedList) result).getItems()).hasSize(2).extracting("name").containsExactly("first", "second");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInstantiatePrimaryConstructorNestedBeanLists() throws Exception {
|
||||
String payload = "{\"nestedList\": { \"items\": [ {\"name\": \"first\"}, {\"name\": \"second\"}] } }";
|
||||
DataFetchingEnvironment environment = initEnvironment(payload);
|
||||
PrimaryConstructorNestedList result = instantiator.initializeFromMap(environment.getArgument("nestedList"), PrimaryConstructorNestedList.class);
|
||||
Object result = initializer.initializeArgument(
|
||||
environment, "nestedList", ResolvableType.forClass(PrimaryConstructorNestedList.class));
|
||||
|
||||
assertThat(result).isNotNull().isInstanceOf(PrimaryConstructorNestedList.class);
|
||||
assertThat(result.getItems()).hasSize(2).extracting("name").containsExactly("first", "second");
|
||||
assertThat(((PrimaryConstructorNestedList) result).getItems())
|
||||
.hasSize(2).extracting("name").containsExactly("first", "second");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInstantiateComplexNestedBean() throws Exception {
|
||||
String payload = "{\"complex\": { \"item\": {\"name\": \"Item name\"}, \"name\": \"Hello\" } }";
|
||||
DataFetchingEnvironment environment = initEnvironment(payload);
|
||||
PrimaryConstructorComplexInput result = instantiator.initializeFromMap(environment.getArgument("complex"), PrimaryConstructorComplexInput.class);
|
||||
Object result = initializer.initializeArgument(
|
||||
environment, "complex", ResolvableType.forClass(PrimaryConstructorComplexInput.class));
|
||||
|
||||
assertThat(result).isNotNull().isInstanceOf(PrimaryConstructorComplexInput.class);
|
||||
assertThat(result.item.name).isEqualTo("Item name");
|
||||
assertThat(result.name).isEqualTo("Hello");
|
||||
assertThat(((PrimaryConstructorComplexInput) result).item.name).isEqualTo("Item name");
|
||||
assertThat(((PrimaryConstructorComplexInput) result).name).isEqualTo("Hello");
|
||||
}
|
||||
|
||||
private DataFetchingEnvironment initEnvironment(String jsonPayload) throws JsonProcessingException {
|
||||
@@ -121,6 +131,7 @@ class GraphQlArgumentInitializerTests {
|
||||
return DataFetchingEnvironmentImpl.newDataFetchingEnvironment().arguments(arguments).build();
|
||||
}
|
||||
|
||||
|
||||
static class SimpleBean {
|
||||
|
||||
String name;
|
||||
@@ -134,6 +145,7 @@ class GraphQlArgumentInitializerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class ContructorBean {
|
||||
|
||||
final String name;
|
||||
@@ -147,6 +159,7 @@ class GraphQlArgumentInitializerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class NoPrimaryConstructor {
|
||||
|
||||
NoPrimaryConstructor(String name) {
|
||||
@@ -156,6 +169,7 @@ class GraphQlArgumentInitializerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class NestedList {
|
||||
|
||||
List<Item> items;
|
||||
@@ -182,6 +196,7 @@ class GraphQlArgumentInitializerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class Item {
|
||||
|
||||
String name;
|
||||
@@ -195,6 +210,7 @@ class GraphQlArgumentInitializerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class PrimaryConstructorComplexInput {
|
||||
final String name;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user