Skip to content

Commit f644b46

Browse files
dariuszkucsmyrick
authored andcommitted
Fix type resolvers for renamed polymorphic types and fix hooks signature (ExpediaGroup#282)
* devs can use @GraphQLName annotation to rename the underlying types - this change updates interface and union type resolvers to retrieve correct name when resolving the types * update SchemaGeneratorHooks.didGenerateGraphQLType method to return the generated type instead of returning Unit, this makes it consistent with return type of other hook methods
1 parent 5af896d commit f644b46

File tree

6 files changed

+84
-12
lines changed

6 files changed

+84
-12
lines changed

graphql-kotlin-schema-generator/src/main/kotlin/com/expedia/graphql/generator/TypeBuilder.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
package com.expedia.graphql.generator
22

3+
import com.expedia.graphql.SchemaGeneratorConfig
34
import com.expedia.graphql.generator.extensions.getKClass
45
import com.expedia.graphql.generator.extensions.isEnum
56
import com.expedia.graphql.generator.extensions.isInterface
67
import com.expedia.graphql.generator.extensions.isListType
78
import com.expedia.graphql.generator.extensions.isUnion
89
import com.expedia.graphql.generator.extensions.wrapInNonNull
910
import com.expedia.graphql.generator.state.KGraphQLType
11+
import com.expedia.graphql.generator.state.SchemaGeneratorState
1012
import com.expedia.graphql.generator.state.TypesCacheKey
13+
import graphql.schema.GraphQLCodeRegistry
1114
import graphql.schema.GraphQLType
1215
import graphql.schema.GraphQLTypeReference
1316
import kotlin.reflect.KClass
1417
import kotlin.reflect.KType
1518

1619
internal open class TypeBuilder constructor(protected val generator: SchemaGenerator) {
17-
protected val state = generator.state
18-
protected val config = generator.config
19-
protected val subTypeMapper = generator.subTypeMapper
20-
protected val codeRegistry = generator.codeRegistry
20+
protected val state: SchemaGeneratorState = generator.state
21+
protected val config: SchemaGeneratorConfig = generator.config
22+
protected val subTypeMapper: SubTypeMapper = generator.subTypeMapper
23+
protected val codeRegistry: GraphQLCodeRegistry.Builder = generator.codeRegistry
2124

2225
internal fun graphQLTypeOf(type: KType, inputType: Boolean = false, annotatedAsID: Boolean = false): GraphQLType {
2326
val hookGraphQLType = config.hooks.willGenerateGraphQLType(type)
@@ -26,10 +29,7 @@ internal open class TypeBuilder constructor(protected val generator: SchemaGener
2629
?: objectFromReflection(type, inputType)
2730

2831
val typeWithNullability = graphQLType.wrapInNonNull(type)
29-
30-
config.hooks.didGenerateGraphQLType(type, typeWithNullability)
31-
32-
return typeWithNullability
32+
return config.hooks.didGenerateGraphQLType(type, typeWithNullability)
3333
}
3434

3535
internal fun objectFromReflection(type: KType, inputType: Boolean): GraphQLType {

graphql-kotlin-schema-generator/src/main/kotlin/com/expedia/graphql/generator/types/InterfaceBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ internal class InterfaceBuilder(generator: SchemaGenerator) : TypeBuilder(genera
4343
}
4444
}
4545

46-
codeRegistry.typeResolver(interfaceType) { env: TypeResolutionEnvironment -> env.schema.getObjectType(env.getObject<Any>().javaClass.simpleName) }
46+
codeRegistry.typeResolver(interfaceType) { env: TypeResolutionEnvironment -> env.schema.getObjectType(env.getObject<Any>().javaClass.kotlin.getSimpleName()) }
4747
config.hooks.onRewireGraphQLType(interfaceType).safeCast()
4848
}
4949
}

graphql-kotlin-schema-generator/src/main/kotlin/com/expedia/graphql/generator/types/UnionBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ internal class UnionBuilder(generator: SchemaGenerator) : TypeBuilder(generator)
4242
}
4343
}
4444
val unionType = builder.build()
45-
codeRegistry.typeResolver(unionType) { env: TypeResolutionEnvironment -> env.schema.getObjectType(env.getObject<Any>().javaClass.simpleName) }
45+
codeRegistry.typeResolver(unionType) { env: TypeResolutionEnvironment -> env.schema.getObjectType(env.getObject<Any>().javaClass.kotlin.getSimpleName()) }
4646
config.hooks.onRewireGraphQLType(unionType)
4747
}
4848
}

graphql-kotlin-schema-generator/src/main/kotlin/com/expedia/graphql/hooks/SchemaGeneratorHooks.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ interface SchemaGeneratorHooks {
7575
/**
7676
* Called after wrapping the type based on nullity but before adding the generated type to the schema
7777
*/
78-
fun didGenerateGraphQLType(type: KType, generatedType: GraphQLType) = Unit
78+
fun didGenerateGraphQLType(type: KType, generatedType: GraphQLType) = generatedType
7979

8080
/**
8181
* Called after converting the function to a field definition but before adding to the schema to allow customization

graphql-kotlin-schema-generator/src/test/kotlin/com/expedia/graphql/generator/PolymorphicTests.kt

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package com.expedia.graphql.generator
22

33
import com.expedia.graphql.TopLevelObject
4+
import com.expedia.graphql.annotations.GraphQLName
45
import com.expedia.graphql.exceptions.InvalidInputFieldTypeException
56
import com.expedia.graphql.testSchemaConfig
67
import com.expedia.graphql.toSchema
8+
import graphql.TypeResolutionEnvironment
79
import graphql.schema.GraphQLInterfaceType
810
import graphql.schema.GraphQLObjectType
11+
import graphql.schema.GraphQLSchema
912
import graphql.schema.GraphQLUnionType
1013
import org.junit.jupiter.api.Assertions.assertThrows
1114
import org.junit.jupiter.api.Test
15+
import kotlin.random.Random
1216
import kotlin.test.assertEquals
1317
import kotlin.test.assertNotNull
1418
import kotlin.test.assertTrue
@@ -93,6 +97,41 @@ internal class PolymorphicTests {
9397
assertEquals(1, classWithBaseAbstractType.interfaces.size)
9498
assertEquals(classWithBaseAbstractType.interfaces.first(), abstractInterface)
9599
}
100+
101+
@Test
102+
fun `Interface types can be correctly resolved`() {
103+
val schema = toSchema(queries = listOf(TopLevelObject(QueryWithRenamedAbstracts())), config = testSchemaConfig)
104+
105+
val cakeInterface = schema.getType("Cake") as? GraphQLInterfaceType
106+
assertNotNull(cakeInterface)
107+
val cakeResolver = schema.codeRegistry.getTypeResolver(cakeInterface)
108+
val cheesecakeResolver = cakeResolver.getType(mockTypeResolutionEnvironment(Cheesecake(), schema))
109+
assertNotNull(cheesecakeResolver)
110+
assertEquals("Cheesecake", cheesecakeResolver.name)
111+
112+
val strawberryCakeResolver = cakeResolver.getType(mockTypeResolutionEnvironment(BerryCake(), schema))
113+
assertNotNull(strawberryCakeResolver)
114+
assertEquals("StrawberryCake", strawberryCakeResolver.name)
115+
}
116+
117+
@Test
118+
fun `Union types can be correctly resolved`() {
119+
val schema = toSchema(queries = listOf(TopLevelObject(QueryWithRenamedAbstracts())), config = testSchemaConfig)
120+
121+
val dessertUnion = schema.getType("Dessert") as? GraphQLUnionType
122+
assertNotNull(dessertUnion)
123+
val dessertResolver = schema.codeRegistry.getTypeResolver(dessertUnion)
124+
val iceCreamResolver = dessertResolver.getType(mockTypeResolutionEnvironment(IceCream(), schema))
125+
assertNotNull(iceCreamResolver)
126+
assertEquals("IceCream", iceCreamResolver.name)
127+
128+
val strawberryCakeResolver = dessertResolver.getType(mockTypeResolutionEnvironment(BerryCake(), schema))
129+
assertNotNull(strawberryCakeResolver)
130+
assertEquals("StrawberryCake", strawberryCakeResolver.name)
131+
}
132+
133+
private fun mockTypeResolutionEnvironment(target: Any, schema: GraphQLSchema): TypeResolutionEnvironment =
134+
TypeResolutionEnvironment(target, emptyMap(), null, null, schema, null)
96135
}
97136

98137
class QueryWithInterface {
@@ -183,3 +222,35 @@ abstract class MyAbstract {
183222
}
184223

185224
data class MyClass(override val id: Int, val name: String) : MyAbstract()
225+
226+
class QueryWithRenamedAbstracts {
227+
228+
fun randomCake(): Cake = if (Random.nextBoolean()) {
229+
BerryCake()
230+
} else {
231+
Cheesecake()
232+
}
233+
234+
fun randomDessert(): Dessert = if (Random.nextBoolean()) {
235+
IceCream()
236+
} else {
237+
BerryCake()
238+
}
239+
}
240+
241+
interface Cake {
242+
fun recipe(): String
243+
}
244+
245+
@GraphQLName("StrawberryCake")
246+
class BerryCake : Cake, Dessert {
247+
override fun recipe(): String = "google it"
248+
}
249+
250+
class Cheesecake : Cake {
251+
override fun recipe(): String = "use bing"
252+
}
253+
254+
interface Dessert
255+
256+
class IceCream : Dessert

graphql-kotlin-schema-generator/src/test/kotlin/com/expedia/graphql/hooks/SchemaGeneratorHooksTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,10 @@ class SchemaGeneratorHooksTest {
108108
class MockSchemaGeneratorHooks : SchemaGeneratorHooks {
109109
var lastSeenType: KType? = null
110110
var lastSeenGeneratedType: GraphQLType? = null
111-
override fun didGenerateGraphQLType(type: KType, generatedType: GraphQLType) {
111+
override fun didGenerateGraphQLType(type: KType, generatedType: GraphQLType): GraphQLType {
112112
lastSeenType = type
113113
lastSeenGeneratedType = generatedType
114+
return generatedType
114115
}
115116
}
116117

0 commit comments

Comments
 (0)