diff --git a/generator/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/execution/KotlinDataFetcherFactoryProvider.kt b/generator/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/execution/KotlinDataFetcherFactoryProvider.kt index 6d40b3313e..723717ed8f 100644 --- a/generator/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/execution/KotlinDataFetcherFactoryProvider.kt +++ b/generator/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/execution/KotlinDataFetcherFactoryProvider.kt @@ -66,10 +66,12 @@ open class SimpleKotlinDataFetcherFactoryProvider : KotlinDataFetcherFactoryProv } /** - * [SimpleSingletonKotlinDataFetcherFactoryProvider] is a specialization of [SimpleKotlinDataFetcherFactoryProvider] that will provide a + * [SimpleSingletonKotlinDataFetcherFactoryProvider] is a specialization of [SimpleKotlinDataFetcherFactoryProvider] that will provide * a [SingletonPropertyDataFetcher] that should be used to target property resolutions without allocating a DataFetcher per property */ open class SimpleSingletonKotlinDataFetcherFactoryProvider : SimpleKotlinDataFetcherFactoryProvider() { override fun propertyDataFetcherFactory(kClass: KClass<*>, kProperty: KProperty<*>): DataFetcherFactory = - SingletonPropertyDataFetcher.getFactoryAndRegister(kClass, kProperty) + SingletonPropertyDataFetcher.factory.also { + SingletonPropertyDataFetcher.register(kClass, kProperty) + } } diff --git a/generator/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/execution/SingletonPropertyDataFetcher.kt b/generator/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/execution/SingletonPropertyDataFetcher.kt index 46bb24e2f3..d3f1481489 100644 --- a/generator/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/execution/SingletonPropertyDataFetcher.kt +++ b/generator/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/execution/SingletonPropertyDataFetcher.kt @@ -5,25 +5,26 @@ import graphql.schema.DataFetcherFactory import graphql.schema.DataFetchingEnvironment import graphql.schema.GraphQLFieldDefinition import graphql.schema.LightDataFetcher +import org.slf4j.LoggerFactory import java.util.concurrent.ConcurrentHashMap import java.util.function.Supplier import kotlin.reflect.KClass import kotlin.reflect.KProperty +import kotlin.reflect.full.memberProperties /** * Singleton Property [DataFetcher] that stores references to underlying properties getters. */ internal object SingletonPropertyDataFetcher : LightDataFetcher { - private val factory: DataFetcherFactory = DataFetcherFactory { SingletonPropertyDataFetcher } - + private val logger = LoggerFactory.getLogger(SingletonPropertyDataFetcher::class.java) + val factory: DataFetcherFactory = DataFetcherFactory { SingletonPropertyDataFetcher } private val getters: ConcurrentHashMap> = ConcurrentHashMap() - fun getFactoryAndRegister(kClass: KClass<*>, kProperty: KProperty<*>): DataFetcherFactory { + fun register(kClass: KClass<*>, kProperty: KProperty<*>) { getters.computeIfAbsent("${kClass.java.name}.${kProperty.name}") { kProperty.getter } - return factory } override fun get( @@ -32,7 +33,17 @@ internal object SingletonPropertyDataFetcher : LightDataFetcher { environmentSupplier: Supplier ): Any? = sourceObject?.let { - getters["${sourceObject.javaClass.name}.${fieldDefinition.name}"]?.call(sourceObject) + getters["${sourceObject.javaClass.name}.${fieldDefinition.name}"]?.call(sourceObject) ?: run { + sourceObject::class.memberProperties + .find { it.name == fieldDefinition.name } + ?.let { kProperty -> + kProperty.getter.call(sourceObject).also { + register(sourceObject::class, kProperty) + } + } + } ?: run { + logger.error("getter method not found: ${sourceObject.javaClass.name}.${fieldDefinition.name}") + } } override fun get(environment: DataFetchingEnvironment): Any? = diff --git a/generator/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/ToSchemaTest.kt b/generator/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/ToSchemaTest.kt index c6a758f979..b0e648f68e 100644 --- a/generator/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/ToSchemaTest.kt +++ b/generator/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/ToSchemaTest.kt @@ -86,6 +86,22 @@ class ToSchemaTest { assertEquals(1, geo?.get("query")?.get("id")) } + @ParameterizedTest(name = "{index} ==> {1}") + @MethodSource("toSchemaTestArguments") + fun `SchemaGenerator generates resolvers for parent classes`(provider: KotlinDataFetcherFactoryProvider, name: String) { + val schema = toSchema( + queries = listOf(TopLevelObject(QueryObject())), + mutations = listOf(TopLevelObject(MutationObject())), + config = testSchemaConfig(provider) + ) + val graphQL = GraphQL.newGraphQL(schema).build() + + val result = graphQL.execute(" { range { start { day } end { day } } }") + val data: Map>>? = result.getData() + assertEquals(30, data?.get("range")?.get("start")?.get("day")) + assertEquals(14, data?.get("range")?.get("end")?.get("day")) + } + @ParameterizedTest(name = "{index} ==> {1}") @MethodSource("toSchemaTestArguments") fun `SchemaGenerator generates a simple GraphQL schema with default builder`(provider: KotlinDataFetcherFactoryProvider, name: String) { @@ -405,9 +421,21 @@ class ToSchemaTest { fun foo(): String = "bar" } + open class ParentDate(val day: Int, val month: Int, val year: Int) + + data class DateRange(val start: ParentDate, val end: ParentDate) + + class ChildDate(day: Int, month: Int, year: Int) : ParentDate(day, month, year) + class QueryObject { @GraphQLDescription("A GraphQL query method") fun query(@GraphQLDescription("A GraphQL value") value: Int): Geography = Geography(value, GeoType.CITY, listOf()) + fun range(): DateRange { + return DateRange( + ChildDate(30, 5, 1992), + ChildDate(14, 6, 1992), + ) + } } class QueryWithIgnored {