diff --git a/example/pom.xml b/example/pom.xml
index 7a669a738c..eadd130b30 100644
--- a/example/pom.xml
+++ b/example/pom.xml
@@ -60,15 +60,15 @@
1.8
- 1.2.70
+ 1.2.71
4.12
5.0.2
- DEVELOPMENT
+ 0.0.18-SNAPSHOT
6.1.2
- ${project.basedir}/src/main/kotlin
+ src/main/kotlin
kotlin-maven-plugin
@@ -140,5 +140,16 @@
+
+ org.jetbrains.kotlin
+ kotlin-stdlib-jdk8
+ ${kotlin.version}
+
+
+ org.jetbrains.kotlin
+ kotlin-test
+ ${kotlin.version}
+ test
+
diff --git a/example/src/main/kotlin/com.expedia.graphql.sample/Application.kt b/example/src/main/kotlin/com.expedia.graphql.sample/Application.kt
index 3fa5f94f4d..8429207f9d 100644
--- a/example/src/main/kotlin/com.expedia.graphql.sample/Application.kt
+++ b/example/src/main/kotlin/com.expedia.graphql.sample/Application.kt
@@ -2,6 +2,7 @@ package com.expedia.graphql.sample
import com.expedia.graphql.TopLevelObjectDef
import com.expedia.graphql.sample.context.MyGraphQLContextBuilder
+import com.expedia.graphql.sample.dataFetchers.SpringDataFetcherFactory
import com.expedia.graphql.sample.extension.CustomSchemaGeneratorHooks
import com.expedia.graphql.sample.mutation.Mutation
import com.expedia.graphql.sample.query.Query
@@ -30,7 +31,11 @@ class Application {
private val logger = LoggerFactory.getLogger(Application::class.java)
@Bean
- fun schemaConfig(): SchemaGeneratorConfig = SchemaGeneratorConfig(supportedPackages = "com.expedia", hooks = CustomSchemaGeneratorHooks())
+ fun schemaConfig(dataFetcherFactory: SpringDataFetcherFactory): SchemaGeneratorConfig = SchemaGeneratorConfig(
+ supportedPackages = "com.expedia",
+ hooks = CustomSchemaGeneratorHooks(),
+ dataFetcherFactory = dataFetcherFactory
+ )
@Bean
fun schema(
diff --git a/example/src/main/kotlin/com.expedia.graphql.sample/dataFetchers/SpringDataFetcherFactory.kt b/example/src/main/kotlin/com.expedia.graphql.sample/dataFetchers/SpringDataFetcherFactory.kt
new file mode 100644
index 0000000000..86a1a69f50
--- /dev/null
+++ b/example/src/main/kotlin/com.expedia.graphql.sample/dataFetchers/SpringDataFetcherFactory.kt
@@ -0,0 +1,26 @@
+package com.expedia.graphql.sample.dataFetchers
+
+import com.expedia.graphql.schema.extensions.deepName
+import graphql.schema.DataFetcher
+import graphql.schema.DataFetcherFactory
+import graphql.schema.DataFetcherFactoryEnvironment
+import org.springframework.beans.factory.BeanFactory
+import org.springframework.beans.factory.BeanFactoryAware
+import org.springframework.stereotype.Component
+
+@Component
+class SpringDataFetcherFactory: DataFetcherFactory, BeanFactoryAware {
+ private lateinit var beanFactory: BeanFactory
+
+ override fun setBeanFactory(beanFactory: BeanFactory?) {
+ this.beanFactory = beanFactory!!
+ }
+
+ override fun get(environment: DataFetcherFactoryEnvironment?): DataFetcher {
+
+ //Strip out possible `Input` and `!` suffixes added to by the SchemaGenerator
+ val targetedTypeName = environment?.fieldDefinition?.type?.deepName?.removeSuffix("!")?.removeSuffix("Input")
+ return beanFactory.getBean("${targetedTypeName}DataFetcher") as DataFetcher
+ }
+
+}
\ No newline at end of file
diff --git a/example/src/main/kotlin/com.expedia.graphql.sample/query/NestedQueries.kt b/example/src/main/kotlin/com.expedia.graphql.sample/query/NestedQueries.kt
new file mode 100644
index 0000000000..26b8881a39
--- /dev/null
+++ b/example/src/main/kotlin/com.expedia.graphql.sample/query/NestedQueries.kt
@@ -0,0 +1,50 @@
+package com.expedia.graphql.sample.query
+
+import com.fasterxml.jackson.annotation.JsonIgnore
+import graphql.schema.DataFetcher
+import graphql.schema.DataFetchingEnvironment
+import org.springframework.beans.factory.BeanFactory
+import org.springframework.beans.factory.BeanFactoryAware
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.beans.factory.getBean
+import org.springframework.context.annotation.Scope
+import org.springframework.stereotype.Component
+
+@Component
+class NestedQueries : Query {
+ fun findAnimal(context: String): NestedAnimal = NestedAnimal(1, "cat")
+}
+
+data class NestedAnimal(
+ val id: Int,
+ val type: String
+) {
+ @JsonIgnore
+ lateinit var details: NestedAnimalDetails
+}
+
+@Component
+@Scope("prototype")
+data class NestedAnimalDetails @Autowired(required = false) constructor(private val animalId: Int) {
+ fun veryDetailledFunction(): String = "Details($animalId)"
+}
+
+@Component("NestedAnimalDetailsDataFetcher")
+@Scope("prototype")
+class AnimalDetailsDataFetcher : DataFetcher, BeanFactoryAware {
+
+ private lateinit var beanFactory: BeanFactory
+
+ override fun setBeanFactory(beanFactory: BeanFactory) {
+ this.beanFactory = beanFactory
+ }
+
+ override fun get(environment: DataFetchingEnvironment?): NestedAnimalDetails {
+ val id = environment?.getSource()?.id
+ if (id == null) {
+ throw Exception("Cannot retrieve animal details, the id is null")
+ } else {
+ return beanFactory.getBean(id)
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index 6cd42b52b6..19c2239768 100644
--- a/pom.xml
+++ b/pom.xml
@@ -75,6 +75,7 @@
0.29.0
1.0.0.RC8
4.12
+ 1.8.9.kotlin13
@@ -209,14 +210,14 @@
org.jetbrains.kotlin
- kotlin-test-junit
+ kotlin-test-junit5
${kotlin.version}
test
- junit
- junit
- ${junit.version}
+ io.mockk
+ mockk
+ ${mockk.version}
test
diff --git a/src/main/kotlin/com/expedia/graphql/schema/SchemaGeneratorConfig.kt b/src/main/kotlin/com/expedia/graphql/schema/SchemaGeneratorConfig.kt
index 1c3ecbb72f..ec2f9bf8ab 100644
--- a/src/main/kotlin/com/expedia/graphql/schema/SchemaGeneratorConfig.kt
+++ b/src/main/kotlin/com/expedia/graphql/schema/SchemaGeneratorConfig.kt
@@ -3,6 +3,7 @@ package com.expedia.graphql.schema
import com.expedia.graphql.schema.generator.completableFutureResolver
import com.expedia.graphql.schema.hooks.NoopSchemaGeneratorHooks
import com.expedia.graphql.schema.hooks.SchemaGeneratorHooks
+import graphql.schema.DataFetcherFactory
import kotlin.reflect.KType
/**
@@ -13,5 +14,6 @@ data class SchemaGeneratorConfig(
val topLevelQueryName: String = "TopLevelQuery",
val topLevelMutationName: String = "TopLevelMutation",
val hooks: SchemaGeneratorHooks = NoopSchemaGeneratorHooks(),
- val monadResolver: (KType) -> KType = completableFutureResolver
+ val monadResolver: (KType) -> KType = completableFutureResolver,
+ val dataFetcherFactory: DataFetcherFactory<*>? = null
)
diff --git a/src/main/kotlin/com/expedia/graphql/schema/generator/SchemaGenerator.kt b/src/main/kotlin/com/expedia/graphql/schema/generator/SchemaGenerator.kt
index 97d78aa3cc..ecb03d598d 100644
--- a/src/main/kotlin/com/expedia/graphql/schema/generator/SchemaGenerator.kt
+++ b/src/main/kotlin/com/expedia/graphql/schema/generator/SchemaGenerator.kt
@@ -22,6 +22,7 @@ import graphql.schema.GraphQLInputObjectType
import graphql.schema.GraphQLInputType
import graphql.schema.GraphQLInterfaceType
import graphql.schema.GraphQLList
+import graphql.schema.GraphQLNonNull
import graphql.schema.GraphQLObjectType
import graphql.schema.GraphQLOutputType
import graphql.schema.GraphQLSchema
@@ -146,12 +147,30 @@ internal class SchemaGenerator(
return builder.build()
}
- private fun property(prop: KProperty<*>): GraphQLFieldDefinition = GraphQLFieldDefinition.newFieldDefinition()
- .description(prop.graphQLDescription())
- .name(prop.name)
- .type(graphQLTypeOf(prop.returnType) as GraphQLOutputType)
- .deprecate(prop.getDeprecationReason())
- .build()
+ private fun property(prop: KProperty<*>): GraphQLFieldDefinition {
+ val propertyType = graphQLTypeOf(prop.returnType) as GraphQLOutputType
+
+ val fieldBuilder = GraphQLFieldDefinition.newFieldDefinition()
+ .description(prop.graphQLDescription())
+ .name(prop.name)
+ .type(propertyType)
+ .deprecate(prop.getDeprecationReason())
+
+ return if (config.dataFetcherFactory != null && prop.isLateinit) {
+ updatePropertyFieldBuilder(propertyType, fieldBuilder)
+ } else {
+ fieldBuilder
+ }.build()
+ }
+
+ private fun updatePropertyFieldBuilder(propertyType: GraphQLOutputType, fieldBuilder: GraphQLFieldDefinition.Builder): GraphQLFieldDefinition.Builder {
+ val updatedFieldBuilder = if (propertyType is GraphQLNonNull) {
+ fieldBuilder.type(propertyType.wrappedType as GraphQLOutputType)
+ } else {
+ fieldBuilder
+ }
+ return updatedFieldBuilder.dataFetcherFactory(config.dataFetcherFactory)
+ }
private fun argument(parameter: KParameter): GraphQLArgument {
throwIfInterfaceIsNotAuthorized(parameter)
diff --git a/src/test/kotlin/com/expedia/graphql/schema/dataFetchers/CustomDataFetcherTests.kt b/src/test/kotlin/com/expedia/graphql/schema/dataFetchers/CustomDataFetcherTests.kt
new file mode 100644
index 0000000000..8c34b3f0af
--- /dev/null
+++ b/src/test/kotlin/com/expedia/graphql/schema/dataFetchers/CustomDataFetcherTests.kt
@@ -0,0 +1,60 @@
+package com.expedia.graphql.schema.dataFetchers
+
+import com.expedia.graphql.TopLevelObjectDef
+import com.expedia.graphql.schema.SchemaGeneratorConfig
+import com.expedia.graphql.schema.extensions.deepName
+import com.expedia.graphql.toSchema
+import graphql.GraphQL
+import graphql.schema.DataFetcher
+import graphql.schema.DataFetcherFactory
+import graphql.schema.DataFetcherFactoryEnvironment
+import graphql.schema.DataFetchingEnvironment
+import org.junit.jupiter.api.Test
+import kotlin.test.assertEquals
+
+class CustomDataFetcherTests {
+ @Test
+ fun `Custom DataFetcher can be used on functions`() {
+ val config = SchemaGeneratorConfig(supportedPackages = "com.expedia", dataFetcherFactory = PetDataFetcherFactory())
+ val schema = toSchema(listOf(TopLevelObjectDef(AnimalQuery())), config = config)
+
+ val animalType = schema.getObjectType("Animal")
+ assertEquals("AnimalDetails", animalType.getFieldDefinition("details").type.deepName)
+
+ val graphQL = GraphQL.newGraphQL(schema).build()
+ val execute = graphQL.execute("{ findAnimal { id type details { specialId } } }")
+
+ val data = execute.getData