Support contructor binding for input arguments

See gh-139
This commit is contained in:
Koen Punt
2021-09-19 20:25:43 +02:00
committed by Brian Clozel
parent f69aa39211
commit 2f5aa588ef
4 changed files with 64 additions and 7 deletions

View File

@@ -1,3 +1,6 @@
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.5.31'
}
description = "GraphQL Support for Spring Applications"
dependencies {
@@ -34,6 +37,7 @@ dependencies {
testRuntimeOnly 'org.apache.logging.log4j:log4j-core'
testRuntimeOnly 'org.apache.logging.log4j:log4j-slf4j-impl'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
}
test {
@@ -42,3 +46,16 @@ test {
events "passed", "skipped", "failed"
}
}
repositories {
mavenCentral()
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}

View File

@@ -18,6 +18,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.List;
import java.util.Map;
import java.util.Optional;
import java.util.Stack;
@@ -107,13 +108,32 @@ public class ArgumentMethodArgumentResolver implements HandlerMethodArgumentReso
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);

View File

@@ -86,6 +86,18 @@ class ArgumentMethodArgumentResolverTests {
.hasFieldOrPropertyWithValue("authorId", 42L);
}
@Test
void shouldResolveKotlinBeanArgument() throws Exception {
Method addBook = ClassUtils.getMethod(BookController.class, "ktAddBook", KotlinBookInput.class);
String payload = "{\"bookInput\": { \"name\": \"test name\", \"authorId\": 42} }";
DataFetchingEnvironment environment = initEnvironment(payload);
MethodParameter methodParameter = getMethodParameter(addBook, 0);
Object result = resolver.resolveArgument(methodParameter, environment);
assertThat(result).isNotNull().isInstanceOf(KotlinBookInput.class);
assertThat((KotlinBookInput) result).hasFieldOrPropertyWithValue("name", "test name")
.hasFieldOrPropertyWithValue("authorId", 42L);
}
@Test
void shouldResolveListOfJavaBeansArgument() throws Exception {
Method addBooks = ClassUtils.getMethod(BookController.class, "addBooks", List.class);
@@ -147,6 +159,11 @@ class ArgumentMethodArgumentResolverTests {
return null;
}
@MutationMapping
public Book ktAddBook(@Argument KotlinBookInput bookInput) {
return null;
}
@MutationMapping
public List<Book> addBooks(@Argument List<Book> books) {
return null;

View File

@@ -0,0 +1,3 @@
package org.springframework.graphql.data.method.annotation.support;
data class KotlinBookInput(val name: String, val authorId: Long)