From eeb11bb72a3d55e1f9ed268b51bcfd1719e80e23 Mon Sep 17 00:00:00 2001 From: Marcus Klimstra Date: Fri, 30 Jul 2021 14:58:14 +0200 Subject: [PATCH 1/3] Test case for #558 --- .../kickstart/tools/EndToEndSpecHelper.kt | 29 +++++++++++++++++++ .../graphql/kickstart/tools/EndToEndTest.kt | 16 ++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/test/kotlin/graphql/kickstart/tools/EndToEndSpecHelper.kt b/src/test/kotlin/graphql/kickstart/tools/EndToEndSpecHelper.kt index 8047a1ae..8d6fe0c4 100644 --- a/src/test/kotlin/graphql/kickstart/tools/EndToEndSpecHelper.kt +++ b/src/test/kotlin/graphql/kickstart/tools/EndToEndSpecHelper.kt @@ -22,6 +22,7 @@ fun createSchema() = SchemaParser.newParser() .dictionary("ThirdItem", ThirdItem::class) .dictionary("ComplexMapItem", ComplexMapItem::class) .dictionary("NestedComplexMapItem", NestedComplexMapItem::class) + .dictionary("OutOfBeerError", OutOfBeerError::class) .build() .makeExecutableSchema() @@ -83,6 +84,9 @@ type Query { arrayItems: [Item!]! throwsIllegalArgumentException: String + + allBars: [Bar!]! + findAvailableBar(persons: Int!): BarResult! } type ExtendedType { @@ -216,6 +220,16 @@ type Tag { type ItemWithGenericProperties { keys: [String!]! } + +type Bar { + name: String +} + +type OutOfBeerError { + msg: String +} + +union BarResult = Bar | OutOfBeerError """ val items = listOf( @@ -314,6 +328,16 @@ class Query : GraphQLQueryResolver, ListListResolver() { fun throwsIllegalArgumentException(): String { throw IllegalArgumentException("Expected") } + + fun allBars(): List { + return listOf(BarEntityImpl("123", "Bar Name")) + } + fun findAvailableBar(persons: Int): Any { + if (persons < 56) + return BarEntityImpl("123", "Bar Name"); + else + return OutOfBeerError("No room for $persons persons") + } } class UnusedRootResolver : GraphQLQueryResolver @@ -410,6 +434,11 @@ class MockPart(private val name: String, private val content: String) : Part { override fun delete() = throw IllegalArgumentException("Not supported") } +interface Bar { val name: String } +interface BarEntity : Bar { val id: String} +class BarEntityImpl(override val id: String, override val name: String) : BarEntity +class OutOfBeerError(val msg: String) + val customScalarId = GraphQLScalarType.newScalar() .name("ID") .description("Overrides built-in ID") diff --git a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt index 6ca764c1..7c12f415 100644 --- a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt @@ -178,6 +178,22 @@ class EndToEndTest { assertNotNull(data["itemByUUID"]) } + @Test + fun `generated schema should handle union types with deep hierarchy`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + findAvailableBar(persons: 42) { + ... on Bar { name } + ... on OutOfBeerError { msg } + } + } + """ + } + + assertNotNull(data["findAvailableBar"]) + } + @Test fun `generated schema should handle non nullable scalar types`() { val fileParts = listOf(MockPart("test.doc", "Hello"), MockPart("test.doc", "World")) From cc0ed8c4dba9823a592f47bd11e0a9259f644e57 Mon Sep 17 00:00:00 2001 From: Marcus Klimstra Date: Fri, 30 Jul 2021 15:13:25 +0200 Subject: [PATCH 2/3] Enhance type resolving for (super)interfaces, fixing #558 --- .../kickstart/tools/DictionaryTypeResolver.kt | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/graphql/kickstart/tools/DictionaryTypeResolver.kt b/src/main/kotlin/graphql/kickstart/tools/DictionaryTypeResolver.kt index 7851c134..e5f0a901 100644 --- a/src/main/kotlin/graphql/kickstart/tools/DictionaryTypeResolver.kt +++ b/src/main/kotlin/graphql/kickstart/tools/DictionaryTypeResolver.kt @@ -16,22 +16,15 @@ internal abstract class DictionaryTypeResolver( private val dictionary: BiMap>, private val types: Map ) : TypeResolver { - private fun getTypeName(clazz: Class): String? { - val name = dictionary[clazz]?.name - - if (name == null && clazz.superclass != null) { - return getTypeName(clazz.superclass) - } - - return name + private fun getTypeDefinition(clazz: Class): TypeDefinition<*>? { + return dictionary[clazz] + ?: (if (clazz.superclass == null) null else getTypeDefinition(clazz.superclass)) + ?: clazz.interfaces.mapNotNull { getTypeDefinition(it) }.firstOrNull() } override fun getType(env: TypeResolutionEnvironment): GraphQLObjectType? { val clazz = env.getObject().javaClass - val name = clazz.interfaces.fold(getTypeName(clazz), { name, interfaceClazz -> - name ?: getTypeName(interfaceClazz) - }) ?: clazz.simpleName - + val name = getTypeDefinition(clazz)?.name ?: clazz.simpleName return types[name] ?: throw TypeResolverError(getError(name)) } From 59b67911107248d1212baecd3f4e7a44db10512e Mon Sep 17 00:00:00 2001 From: Marcus Klimstra Date: Thu, 5 Aug 2021 10:47:05 +0200 Subject: [PATCH 3/3] Move creative type hierarchy and use expression body methods. --- .../kickstart/tools/EndToEndSpecHelper.kt | 43 ++++++++++--------- .../graphql/kickstart/tools/EndToEndTest.kt | 8 ++-- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/test/kotlin/graphql/kickstart/tools/EndToEndSpecHelper.kt b/src/test/kotlin/graphql/kickstart/tools/EndToEndSpecHelper.kt index 8d6fe0c4..e77d5ed6 100644 --- a/src/test/kotlin/graphql/kickstart/tools/EndToEndSpecHelper.kt +++ b/src/test/kotlin/graphql/kickstart/tools/EndToEndSpecHelper.kt @@ -22,7 +22,7 @@ fun createSchema() = SchemaParser.newParser() .dictionary("ThirdItem", ThirdItem::class) .dictionary("ComplexMapItem", ComplexMapItem::class) .dictionary("NestedComplexMapItem", NestedComplexMapItem::class) - .dictionary("OutOfBeerError", OutOfBeerError::class) + .dictionary("NoDogError", NoDogError::class) .build() .makeExecutableSchema() @@ -85,8 +85,8 @@ type Query { throwsIllegalArgumentException: String - allBars: [Bar!]! - findAvailableBar(persons: Int!): BarResult! + allDogs: [Dog!]! + findSuitableDog(preferredColor: String!, minimumFluffiness: Int!): FindDogResult! } type ExtendedType { @@ -221,15 +221,17 @@ type ItemWithGenericProperties { keys: [String!]! } -type Bar { - name: String +type Dog { + name: String! + color: String! + fluffiness: Int! } -type OutOfBeerError { +type NoDogError { msg: String } -union BarResult = Bar | OutOfBeerError +union FindDogResult = Dog | NoDogError """ val items = listOf( @@ -329,15 +331,12 @@ class Query : GraphQLQueryResolver, ListListResolver() { throw IllegalArgumentException("Expected") } - fun allBars(): List { - return listOf(BarEntityImpl("123", "Bar Name")) - } - fun findAvailableBar(persons: Int): Any { - if (persons < 56) - return BarEntityImpl("123", "Bar Name"); - else - return OutOfBeerError("No room for $persons persons") - } + fun allDogs(): List = listOf(LabradorRetriever("Hershey", "chocolate", 42, 3.14159f)) + + fun findSuitableDog(preferredColor: String, minimumFluffiness: Int): Any = + allDogs() + .firstOrNull { it.color == preferredColor && it.fluffiness >= minimumFluffiness } + ?: NoDogError("No $preferredColor-colored dog found that is sufficiently fluffy") } class UnusedRootResolver : GraphQLQueryResolver @@ -434,10 +433,14 @@ class MockPart(private val name: String, private val content: String) : Part { override fun delete() = throw IllegalArgumentException("Not supported") } -interface Bar { val name: String } -interface BarEntity : Bar { val id: String} -class BarEntityImpl(override val id: String, override val name: String) : BarEntity -class OutOfBeerError(val msg: String) +interface Dog { + val name: String + val color: String + val fluffiness: Int +} +interface Retriever : Dog { val speed: Float } +class LabradorRetriever(override val name: String, override val color: String, override val fluffiness: Int, override val speed: Float) : Retriever +class NoDogError(val msg: String) val customScalarId = GraphQLScalarType.newScalar() .name("ID") diff --git a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt index 7c12f415..5d4dc51d 100644 --- a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt @@ -183,15 +183,15 @@ class EndToEndTest { val data = assertNoGraphQlErrors(gql) { """ { - findAvailableBar(persons: 42) { - ... on Bar { name } - ... on OutOfBeerError { msg } + findSuitableDog(preferredColor: "chocolate", minimumFluffiness: 31) { + ... on Dog { name } + ... on NoDogError { msg } } } """ } - assertNotNull(data["findAvailableBar"]) + assertNotNull(data["findSuitableDog"]) } @Test