From b829935debf06d9f6ff74d1a45aeaab110969bd8 Mon Sep 17 00:00:00 2001 From: Koen Punt Date: Mon, 20 Sep 2021 11:17:49 +0200 Subject: [PATCH] support data classes for method arguments --- .../ArgumentMethodArgumentResolver.java | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java index f8950e9b7..7768e97ab 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java @@ -16,11 +16,7 @@ package org.springframework.graphql.data.method.annotation.support; import java.lang.reflect.Constructor; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Optional; -import java.util.Stack; +import java.util.*; import graphql.schema.DataFetchingEnvironment; @@ -107,13 +103,32 @@ private Object convert(Object rawValue, Class targetType) { Object target; if (rawValue instanceof Map) { Constructor ctor = BeanUtils.getResolvableConstructor(targetType); - target = BeanUtils.instantiateClass(ctor); - DataBinder dataBinder = new DataBinder(target); - Assert.isTrue(ctor.getParameterCount() == 0, - () -> "Argument of type [" + targetType.getName() + - "] cannot be instantiated because of missing default constructor."); - MutablePropertyValues mpvs = extractPropertyValues((Map) rawValue); - dataBinder.bind(mpvs); + MutablePropertyValues propertyValues = extractPropertyValues((Map) rawValue); + + if (ctor.getParameterCount() == 0) { + target = BeanUtils.instantiateClass(ctor); + DataBinder dataBinder = new DataBinder(target); + dataBinder.bind(propertyValues); + } else { + // Data class constructor + DataBinder binder = new DataBinder(null); + String[] paramNames = BeanUtils.getParameterNames(ctor); + Class[] paramTypes = ctor.getParameterTypes(); + Object[] args = new Object[paramTypes.length]; + for (int i = 0; i < paramNames.length; i++) { + String paramName = paramNames[i]; + Object value = propertyValues.get(paramName); + value = (value instanceof List ? ((List) value).toArray() : value); + MethodParameter methodParam = new MethodParameter(ctor, i); + if (value == null && methodParam.isOptional()) { + args[i] = (methodParam.getParameterType() == Optional.class ? Optional.empty() : null); + } + else { + args[i] = binder.convertIfNecessary(value, paramTypes[i], methodParam); + } + } + target = BeanUtils.instantiateClass(ctor, args); + } } else if (targetType.isAssignableFrom(rawValue.getClass())) { return returnValue(rawValue, targetType);