Skip to content

Commit dc3f845

Browse files
samuelAndalonsamvazquez
andauthored
feat: support argument types without primaryConstructor (#1518)
* feat: support argument types without primaryConstructor * feat: refactor * feat: throw exception if multiple constructors were found Co-authored-by: samvazquez <[email protected]>
1 parent 051736c commit dc3f845

File tree

3 files changed

+89
-2
lines changed

3 files changed

+89
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2022 Expedia, Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.expediagroup.graphql.generator.exceptions
18+
19+
import kotlin.reflect.KClass
20+
21+
/**
22+
* Thrown when multiple constructors of an input class were located.
23+
*/
24+
class MultipleConstructorsFound(klazz: KClass<*>) : GraphQLKotlinException("Invalid input object ${klazz.simpleName} - multiple public constructors found")

generator/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/execution/convertArgumentValue.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.expediagroup.graphql.generator.execution
1818

19+
import com.expediagroup.graphql.generator.exceptions.MultipleConstructorsFound
1920
import com.expediagroup.graphql.generator.exceptions.PrimaryConstructorNotFound
2021
import com.expediagroup.graphql.generator.internal.extensions.getGraphQLName
2122
import com.expediagroup.graphql.generator.internal.extensions.getKClass
@@ -93,7 +94,17 @@ private fun convertValue(
9394
* the only thing left to parse is object maps into the nested Kotlin classes
9495
*/
9596
private fun <T : Any> mapToKotlinObject(input: Map<String, *>, targetClass: KClass<T>): T {
96-
val targetConstructor = targetClass.primaryConstructor ?: throw PrimaryConstructorNotFound(targetClass)
97+
98+
val targetConstructor = targetClass.primaryConstructor ?: run {
99+
if (targetClass.constructors.size == 1) {
100+
targetClass.constructors.first()
101+
} else if (targetClass.constructors.size > 1) {
102+
throw MultipleConstructorsFound(targetClass)
103+
} else {
104+
throw PrimaryConstructorNotFound(targetClass)
105+
}
106+
}
107+
97108
val constructorParameters = targetConstructor.parameters
98109
// filter parameters that are actually in the input in order to rely on parameters default values
99110
// in target constructor

generator/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/execution/ConvertArgumentValueTest.kt

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.expediagroup.graphql.generator.execution
1818

1919
import com.expediagroup.graphql.generator.annotations.GraphQLName
20+
import com.expediagroup.graphql.generator.exceptions.MultipleConstructorsFound
2021
import com.expediagroup.graphql.generator.scalars.ID
2122
import graphql.schema.DataFetchingEnvironment
2223
import io.mockk.every
@@ -117,6 +118,39 @@ class ConvertArgumentValueTest {
117118
assertEquals("nested default value", castResult.nested?.value)
118119
}
119120

121+
@Test
122+
fun `generic map object is parsed without using primary constructor`() {
123+
val kParam = assertNotNull(TestFunctions::inputObjectNoPrimaryConstructor.findParameterByName("input"))
124+
val result = convertArgumentValue(
125+
"input",
126+
kParam,
127+
mapOf(
128+
"input" to mapOf(
129+
"value" to "hello"
130+
)
131+
)
132+
)
133+
134+
val castResult = assertIs<TestInputNoPrimaryConstructor>(result)
135+
assertEquals("hello", castResult.value)
136+
}
137+
138+
@Test
139+
fun `exception is thrown when multiple constructors were found`() {
140+
val kParam = assertNotNull(TestFunctions::inputObjectMultipleConstructors.findParameterByName("input"))
141+
assertThrows<MultipleConstructorsFound> {
142+
convertArgumentValue(
143+
"input",
144+
kParam,
145+
mapOf(
146+
"input" to mapOf(
147+
"value" to "hello"
148+
)
149+
)
150+
)
151+
}
152+
}
153+
120154
/**
121155
* this will be solved in Kotlin 1.7
122156
* "KotlinReflectionInternalError" when using `callBy` on constructor that has inline class parameters
@@ -241,6 +275,8 @@ class ConvertArgumentValueTest {
241275
fun enumInput(input: Foo): String = TODO()
242276
fun idInput(input: ID): String = TODO()
243277
fun inputObject(input: TestInput): String = TODO()
278+
fun inputObjectNoPrimaryConstructor(input: TestInputNoPrimaryConstructor): String = TODO()
279+
fun inputObjectMultipleConstructors(input: TestInputMultipleConstructors): String = TODO()
244280
fun inputObjectNested(input: TestInputNested): String = TODO()
245281
fun inputObjectNullableScalar(input: TestInputNullableScalar): String = TODO()
246282
fun inputObjectNotNullableScalar(input: TestInputNotNullableScalar): String = TODO()
@@ -257,8 +293,24 @@ class ConvertArgumentValueTest {
257293
class TestInputNestedType(val value: String = "nested default value")
258294
class TestInputNullableScalar(val foo: String? = null, val id: ID? = null)
259295
class TestInputNotNullableScalar(val foo: String, val id: ID = ID("1234"))
260-
261296
class TestInputRenamed(@GraphQLName("bar") val foo: String)
297+
class TestInputNoPrimaryConstructor {
298+
val value: String
299+
constructor(value: String) {
300+
this.value = value
301+
}
302+
}
303+
class TestInputMultipleConstructors {
304+
val value: String
305+
306+
constructor() {
307+
this.value = "Default Value"
308+
}
309+
310+
constructor(value: String) {
311+
this.value = value
312+
}
313+
}
262314

263315
enum class Foo {
264316
BAR,

0 commit comments

Comments
 (0)