Support contructor binding for input arguments
See gh-139
This commit is contained in:
@@ -1,3 +1,6 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.jetbrains.kotlin.jvm' version '1.5.31'
|
||||||
|
}
|
||||||
description = "GraphQL Support for Spring Applications"
|
description = "GraphQL Support for Spring Applications"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -34,6 +37,7 @@ dependencies {
|
|||||||
|
|
||||||
testRuntimeOnly 'org.apache.logging.log4j:log4j-core'
|
testRuntimeOnly 'org.apache.logging.log4j:log4j-core'
|
||||||
testRuntimeOnly 'org.apache.logging.log4j:log4j-slf4j-impl'
|
testRuntimeOnly 'org.apache.logging.log4j:log4j-slf4j-impl'
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
@@ -42,3 +46,16 @@ test {
|
|||||||
events "passed", "skipped", "failed"
|
events "passed", "skipped", "failed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package org.springframework.graphql.data.method.annotation.support;
|
|||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
@@ -107,13 +108,32 @@ public class ArgumentMethodArgumentResolver implements HandlerMethodArgumentReso
|
|||||||
Object target;
|
Object target;
|
||||||
if (rawValue instanceof Map) {
|
if (rawValue instanceof Map) {
|
||||||
Constructor<?> ctor = BeanUtils.getResolvableConstructor(targetType);
|
Constructor<?> ctor = BeanUtils.getResolvableConstructor(targetType);
|
||||||
target = BeanUtils.instantiateClass(ctor);
|
MutablePropertyValues propertyValues = extractPropertyValues((Map) rawValue);
|
||||||
DataBinder dataBinder = new DataBinder(target);
|
|
||||||
Assert.isTrue(ctor.getParameterCount() == 0,
|
if (ctor.getParameterCount() == 0) {
|
||||||
() -> "Argument of type [" + targetType.getName() +
|
target = BeanUtils.instantiateClass(ctor);
|
||||||
"] cannot be instantiated because of missing default constructor.");
|
DataBinder dataBinder = new DataBinder(target);
|
||||||
MutablePropertyValues mpvs = extractPropertyValues((Map) rawValue);
|
dataBinder.bind(propertyValues);
|
||||||
dataBinder.bind(mpvs);
|
} 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())) {
|
else if (targetType.isAssignableFrom(rawValue.getClass())) {
|
||||||
return returnValue(rawValue, targetType);
|
return returnValue(rawValue, targetType);
|
||||||
|
|||||||
@@ -86,6 +86,18 @@ class ArgumentMethodArgumentResolverTests {
|
|||||||
.hasFieldOrPropertyWithValue("authorId", 42L);
|
.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
|
@Test
|
||||||
void shouldResolveListOfJavaBeansArgument() throws Exception {
|
void shouldResolveListOfJavaBeansArgument() throws Exception {
|
||||||
Method addBooks = ClassUtils.getMethod(BookController.class, "addBooks", List.class);
|
Method addBooks = ClassUtils.getMethod(BookController.class, "addBooks", List.class);
|
||||||
@@ -147,6 +159,11 @@ class ArgumentMethodArgumentResolverTests {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MutationMapping
|
||||||
|
public Book ktAddBook(@Argument KotlinBookInput bookInput) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@MutationMapping
|
@MutationMapping
|
||||||
public List<Book> addBooks(@Argument List<Book> books) {
|
public List<Book> addBooks(@Argument List<Book> books) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package org.springframework.graphql.data.method.annotation.support;
|
||||||
|
|
||||||
|
data class KotlinBookInput(val name: String, val authorId: Long)
|
||||||
Reference in New Issue
Block a user