Skip to content

Commit 331a161

Browse files
smyrickShane Myrick
and
Shane Myrick
authored
Valid polymorphic types in lists as input (ExpediaGroup#801)
* Valid polymorphic types in lists as input * Add more unit tests for listtype Co-authored-by: Shane Myrick <[email protected]>
1 parent b84356a commit 331a161

File tree

8 files changed

+98
-21
lines changed

8 files changed

+98
-21
lines changed

examples/spark/src/main/kotlin/com/expediagroup/graphql/examples/spark/Application.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class Application {
5959
graphQLHandler.handle(request, response)
6060
}
6161

62-
internalServerError() { _, response ->
62+
internalServerError { _, response ->
6363
response.status(500)
6464
response.type("application/text")
6565
"Unable to process request"

examples/spark/src/main/kotlin/com/expediagroup/graphql/examples/spark/schema/LoginMutationService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import com.expediagroup.graphql.examples.spark.schema.models.User
1919

2020
data class AuthPayload(val token: String? = null, val user: User? = null)
2121

22-
class LoginMutationService() {
22+
class LoginMutationService {
2323
suspend fun login(email: String, password: String, aliasUUID: String?): AuthPayload {
2424
val token = "fake-token"
2525
val user = User(

examples/spring/src/main/kotlin/com/expediagroup/graphql/examples/model/Fruit.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ package com.expediagroup.graphql.examples.model
1818

1919
sealed class Fruit(val color: String) {
2020
class Apple(private val variety: String) : Fruit(if (variety == "red delicious") "red" else "green")
21-
class Orange() : Fruit("orange")
21+
class Orange : Fruit("orange")
2222
}

graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/extensions/kParameterExtensions.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ internal fun KParameter.isInterface() = this.type.getKClass().isInterface()
2626

2727
internal fun KParameter.isList() = this.type.getKClass().isSubclassOf(List::class)
2828

29+
internal fun KParameter.isListType() = this.isList() || this.type.getJavaClass().isArray
30+
2931
internal fun KParameter.isGraphQLContext() = this.type.getKClass().isSubclassOf(GraphQLContext::class)
3032

3133
internal fun KParameter.isDataFetchingEnvironment() = this.type.classifier == DataFetchingEnvironment::class

graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateArgument.kt

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,25 @@ package com.expediagroup.graphql.generator.types
1919
import com.expediagroup.graphql.exceptions.InvalidInputFieldTypeException
2020
import com.expediagroup.graphql.generator.SchemaGenerator
2121
import com.expediagroup.graphql.generator.extensions.getGraphQLDescription
22+
import com.expediagroup.graphql.generator.extensions.getKClass
2223
import com.expediagroup.graphql.generator.extensions.getName
24+
import com.expediagroup.graphql.generator.extensions.getWrappedType
2325
import com.expediagroup.graphql.generator.extensions.isInterface
24-
import com.expediagroup.graphql.generator.extensions.isList
26+
import com.expediagroup.graphql.generator.extensions.isListType
27+
import com.expediagroup.graphql.generator.extensions.isUnion
2528
import com.expediagroup.graphql.generator.extensions.safeCast
2629
import graphql.schema.GraphQLArgument
30+
import kotlin.reflect.KClass
2731
import kotlin.reflect.KParameter
2832

2933
@Throws(InvalidInputFieldTypeException::class)
3034
internal fun generateArgument(generator: SchemaGenerator, parameter: KParameter): GraphQLArgument {
3135

32-
if (parameter.isInterface() && parameter.isList().not()) {
36+
// Validate that the input is not a polymorphic type
37+
// This is not currently supported by the GraphQL spec
38+
// https://github.com/graphql/graphql-spec/blob/master/rfcs/InputUnion.md
39+
val unwrappedClass = getUnwrappedClass(parameter)
40+
if (unwrappedClass.isInterface() || unwrappedClass.isUnion()) {
3341
throw InvalidInputFieldTypeException(parameter)
3442
}
3543

@@ -47,3 +55,10 @@ internal fun generateArgument(generator: SchemaGenerator, parameter: KParameter)
4755

4856
return generator.config.hooks.onRewireGraphQLType(builder.build()).safeCast()
4957
}
58+
59+
private fun getUnwrappedClass(parameter: KParameter): KClass<*> =
60+
if (parameter.isListType()) {
61+
parameter.type.getWrappedType().getKClass()
62+
} else {
63+
parameter.type.getKClass()
64+
}

graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/extensions/FieldExtenstionsKtTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ internal class FieldExtenstionsKtTest {
4040

4141
@Test
4242
fun `verify @Deprecated on fields`() {
43-
val propertyDeprecation = AnnotatedEnum::class.java.getField("ONE")?.getDeprecationReason()
43+
val propertyDeprecation = AnnotatedEnum::class.java.getField("ONE").getDeprecationReason()
4444
assertEquals(expected = "do not use, replace with TWO", actual = propertyDeprecation)
4545
}
4646
}

graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/extensions/KParameterExtensionsKtTest.kt

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,40 +30,42 @@ import kotlin.test.assertFalse
3030
import kotlin.test.assertNull
3131
import kotlin.test.assertTrue
3232

33-
internal class KParameterExtensionsKtTest {
33+
class KParameterExtensionsKtTest {
3434

3535
@GraphQLDescription("class description")
36-
internal data class MyClass(val foo: String)
36+
data class MyClass(val foo: String)
3737

38-
internal interface MyInterface {
38+
interface MyInterface {
3939
val value: String
4040
}
4141

42-
internal abstract class MyAbstractClass {
42+
abstract class MyAbstractClass {
4343

4444
abstract val implementMe: String
4545

4646
val value: String = "test"
4747
}
4848

49-
internal class Container {
49+
class Container {
5050

51-
internal fun interfaceInput(myInterface: MyInterface) = myInterface
51+
fun interfaceInput(myInterface: MyInterface) = myInterface
5252

53-
internal fun absctractInput(myAbstractClass: MyAbstractClass) = myAbstractClass
53+
fun absctractInput(myAbstractClass: MyAbstractClass) = myAbstractClass
5454

55-
internal fun listInput(myList: List<Int>) = myList
55+
fun listInput(myList: List<Int>) = myList
5656

57-
internal fun arrayInput(myArray: IntArray) = myArray
57+
fun arrayListInput(myList: ArrayList<Int>) = myList
5858

59-
internal fun noDescription(myClass: MyClass) = myClass
59+
fun arrayInput(myArray: IntArray) = myArray
6060

61-
internal fun paramDescription(@GraphQLDescription("param description") myClass: MyClass) = myClass
61+
fun noDescription(myClass: MyClass) = myClass
6262

63-
internal fun dataFetchingEnvironment(environment: DataFetchingEnvironment) = environment.field.name
63+
fun paramDescription(@GraphQLDescription("param description") myClass: MyClass) = myClass
64+
65+
fun dataFetchingEnvironment(environment: DataFetchingEnvironment) = environment.field.name
6466
}
6567

66-
internal class MyKotlinClass {
68+
class MyKotlinClass {
6769
fun stringFun(string: String) = "hello $string"
6870
}
6971

@@ -131,4 +133,12 @@ internal class KParameterExtensionsKtTest {
131133
assertTrue(Container::arrayInput.findParameterByName("myArray")?.isList() == false)
132134
assertTrue(Container::interfaceInput.findParameterByName("myInterface")?.isList() == false)
133135
}
136+
137+
@Test
138+
fun isListType() {
139+
assertTrue(Container::listInput.findParameterByName("myList")?.isListType() == true)
140+
assertTrue(Container::arrayListInput.findParameterByName("myList")?.isListType() == true)
141+
assertTrue(Container::arrayInput.findParameterByName("myArray")?.isListType() == true)
142+
assertTrue(Container::interfaceInput.findParameterByName("myInterface")?.isListType() == false)
143+
}
134144
}

graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateArgumentTest.kt

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ import kotlin.test.assertNotNull
3434

3535
class GenerateArgumentTest : TypeTestHelper() {
3636

37-
internal interface MyInterface {
37+
interface MyInterface {
3838
val id: String
3939
}
4040

41-
internal class ArgumentTestClass {
41+
interface MyUnion
42+
43+
class ArgumentTestClass {
4244
fun description(@GraphQLDescription("Argument description") input: String) = input
4345

4446
fun directive(@SimpleDirective input: String) = input
@@ -53,7 +55,15 @@ class GenerateArgumentTest : TypeTestHelper() {
5355

5456
fun arrayListArg(input: ArrayList<String>) = input
5557

58+
fun arrayListInterfaceArg(input: ArrayList<MyInterface>) = input
59+
60+
fun arrayListUnionArg(input: ArrayList<MyUnion>) = input
61+
5662
fun listArg(input: List<String>) = input
63+
64+
fun listInterfaceArg(input: List<MyInterface>) = input
65+
66+
fun listUnionArg(input: List<MyUnion>) = input
5767
}
5868

5969
@Test
@@ -127,6 +137,26 @@ class GenerateArgumentTest : TypeTestHelper() {
127137
assertNotNull(GraphQLTypeUtil.unwrapNonNull(result.type) as? GraphQLList)
128138
}
129139

140+
@Test
141+
fun `ArrayList of interfaces as input is invalid`() {
142+
val kParameter = ArgumentTestClass::arrayListInterfaceArg.findParameterByName("input")
143+
assertNotNull(kParameter)
144+
145+
assertFailsWith(InvalidInputFieldTypeException::class) {
146+
generateArgument(generator, kParameter)
147+
}
148+
}
149+
150+
@Test
151+
fun `ArrayList of unions as input is invalid`() {
152+
val kParameter = ArgumentTestClass::arrayListUnionArg.findParameterByName("input")
153+
assertNotNull(kParameter)
154+
155+
assertFailsWith(InvalidInputFieldTypeException::class) {
156+
generateArgument(generator, kParameter)
157+
}
158+
}
159+
130160
@Test
131161
fun `List argument type is valid`() {
132162
val kParameter = ArgumentTestClass::listArg.findParameterByName("input")
@@ -136,4 +166,24 @@ class GenerateArgumentTest : TypeTestHelper() {
136166
assertEquals(expected = "input", actual = result.name)
137167
assertNotNull(GraphQLTypeUtil.unwrapNonNull(result.type) as? GraphQLList)
138168
}
169+
170+
@Test
171+
fun `List of interfaces as input is invalid`() {
172+
val kParameter = ArgumentTestClass::listInterfaceArg.findParameterByName("input")
173+
assertNotNull(kParameter)
174+
175+
assertFailsWith(InvalidInputFieldTypeException::class) {
176+
generateArgument(generator, kParameter)
177+
}
178+
}
179+
180+
@Test
181+
fun `List of unions as input is invalid`() {
182+
val kParameter = ArgumentTestClass::listUnionArg.findParameterByName("input")
183+
assertNotNull(kParameter)
184+
185+
assertFailsWith(InvalidInputFieldTypeException::class) {
186+
generateArgument(generator, kParameter)
187+
}
188+
}
139189
}

0 commit comments

Comments
 (0)