Skip to content

Commit 2f5aa58

Browse files
koenpuntbclozel
authored andcommitted
Support contructor binding for input arguments
See gh-139
1 parent f69aa39 commit 2f5aa58

File tree

4 files changed

+64
-7
lines changed

4 files changed

+64
-7
lines changed

spring-graphql/build.gradle

+17
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
plugins {
2+
id 'org.jetbrains.kotlin.jvm' version '1.5.31'
3+
}
14
description = "GraphQL Support for Spring Applications"
25

36
dependencies {
@@ -34,6 +37,7 @@ dependencies {
3437

3538
testRuntimeOnly 'org.apache.logging.log4j:log4j-core'
3639
testRuntimeOnly 'org.apache.logging.log4j:log4j-slf4j-impl'
40+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
3741
}
3842

3943
test {
@@ -42,3 +46,16 @@ test {
4246
events "passed", "skipped", "failed"
4347
}
4448
}
49+
repositories {
50+
mavenCentral()
51+
}
52+
compileKotlin {
53+
kotlinOptions {
54+
jvmTarget = "1.8"
55+
}
56+
}
57+
compileTestKotlin {
58+
kotlinOptions {
59+
jvmTarget = "1.8"
60+
}
61+
}

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java

+27-7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.lang.reflect.Constructor;
1919
import java.util.Collection;
2020
import java.util.Iterator;
21+
import java.util.List;
2122
import java.util.Map;
2223
import java.util.Optional;
2324
import java.util.Stack;
@@ -107,13 +108,32 @@ private Object convert(Object rawValue, Class<?> targetType) {
107108
Object target;
108109
if (rawValue instanceof Map) {
109110
Constructor<?> ctor = BeanUtils.getResolvableConstructor(targetType);
110-
target = BeanUtils.instantiateClass(ctor);
111-
DataBinder dataBinder = new DataBinder(target);
112-
Assert.isTrue(ctor.getParameterCount() == 0,
113-
() -> "Argument of type [" + targetType.getName() +
114-
"] cannot be instantiated because of missing default constructor.");
115-
MutablePropertyValues mpvs = extractPropertyValues((Map) rawValue);
116-
dataBinder.bind(mpvs);
111+
MutablePropertyValues propertyValues = extractPropertyValues((Map) rawValue);
112+
113+
if (ctor.getParameterCount() == 0) {
114+
target = BeanUtils.instantiateClass(ctor);
115+
DataBinder dataBinder = new DataBinder(target);
116+
dataBinder.bind(propertyValues);
117+
} else {
118+
// Data class constructor
119+
DataBinder binder = new DataBinder(null);
120+
String[] paramNames = BeanUtils.getParameterNames(ctor);
121+
Class<?>[] paramTypes = ctor.getParameterTypes();
122+
Object[] args = new Object[paramTypes.length];
123+
for (int i = 0; i < paramNames.length; i++) {
124+
String paramName = paramNames[i];
125+
Object value = propertyValues.get(paramName);
126+
value = (value instanceof List ? ((List<?>) value).toArray() : value);
127+
MethodParameter methodParam = new MethodParameter(ctor, i);
128+
if (value == null && methodParam.isOptional()) {
129+
args[i] = (methodParam.getParameterType() == Optional.class ? Optional.empty() : null);
130+
}
131+
else {
132+
args[i] = binder.convertIfNecessary(value, paramTypes[i], methodParam);
133+
}
134+
}
135+
target = BeanUtils.instantiateClass(ctor, args);
136+
}
117137
}
118138
else if (targetType.isAssignableFrom(rawValue.getClass())) {
119139
return returnValue(rawValue, targetType);

spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolverTests.java

+17
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,18 @@ void shouldResolveJavaBeanArgument() throws Exception {
8686
.hasFieldOrPropertyWithValue("authorId", 42L);
8787
}
8888

89+
@Test
90+
void shouldResolveKotlinBeanArgument() throws Exception {
91+
Method addBook = ClassUtils.getMethod(BookController.class, "ktAddBook", KotlinBookInput.class);
92+
String payload = "{\"bookInput\": { \"name\": \"test name\", \"authorId\": 42} }";
93+
DataFetchingEnvironment environment = initEnvironment(payload);
94+
MethodParameter methodParameter = getMethodParameter(addBook, 0);
95+
Object result = resolver.resolveArgument(methodParameter, environment);
96+
assertThat(result).isNotNull().isInstanceOf(KotlinBookInput.class);
97+
assertThat((KotlinBookInput) result).hasFieldOrPropertyWithValue("name", "test name")
98+
.hasFieldOrPropertyWithValue("authorId", 42L);
99+
}
100+
89101
@Test
90102
void shouldResolveListOfJavaBeansArgument() throws Exception {
91103
Method addBooks = ClassUtils.getMethod(BookController.class, "addBooks", List.class);
@@ -147,6 +159,11 @@ public Book addBook(@Argument BookInput bookInput) {
147159
return null;
148160
}
149161

162+
@MutationMapping
163+
public Book ktAddBook(@Argument KotlinBookInput bookInput) {
164+
return null;
165+
}
166+
150167
@MutationMapping
151168
public List<Book> addBooks(@Argument List<Book> books) {
152169
return null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.springframework.graphql.data.method.annotation.support;
2+
3+
data class KotlinBookInput(val name: String, val authorId: Long)

0 commit comments

Comments
 (0)