Skip to content

Commit a927018

Browse files
committed
Generates data fetcher interface for query types
1 parent 2603477 commit a927018

File tree

4 files changed

+160
-0
lines changed

4 files changed

+160
-0
lines changed

graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/CodeGen.kt

+12
Original file line numberDiff line numberDiff line change
@@ -387,12 +387,23 @@ class CodeGen(private val config: CodeGenConfig) {
387387

388388
val constantsClass = KotlinConstantsGenerator(config, document).generate()
389389

390+
val dataFetchers = if (config.generateDataFetcherInterfaces) {
391+
definitions.asSequence()
392+
.filterIsInstance<ObjectTypeDefinition>()
393+
.filter { it.name == "Query" }
394+
.map { KotlinDataFetcherGenerator(config, document).generate(it) }
395+
.fold(CodeGenResult()) { result, next -> result.merge(next) }
396+
} else {
397+
CodeGenResult()
398+
}
399+
390400
datatypesResult
391401
.merge(inputTypes)
392402
.merge(interfacesResult)
393403
.merge(unionResult)
394404
.merge(enumsResult)
395405
.merge(constantsClass)
406+
.merge(dataFetchers)
396407
}
397408

398409
val clientTypes = if (config.generateKotlinClosureProjections) {
@@ -505,6 +516,7 @@ class CodeGenConfig(
505516
var generateInterfaces: Boolean = false,
506517
var generateKotlinNullableClasses: Boolean = false,
507518
var generateKotlinClosureProjections: Boolean = false,
519+
var generateDataFetcherInterfaces: Boolean = false,
508520
var typeMapping: Map<String, String> = emptyMap(),
509521
var includeQueries: Set<String> = emptySet(),
510522
var includeMutations: Set<String> = emptySet(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.netflix.graphql.dgs.codegen.generators.kotlin
2+
3+
import com.netflix.graphql.dgs.DgsData
4+
import com.netflix.graphql.dgs.codegen.CodeGenConfig
5+
import com.netflix.graphql.dgs.codegen.CodeGenResult
6+
import com.netflix.graphql.dgs.codegen.generators.shared.CodeGeneratorUtils.capitalized
7+
import com.squareup.kotlinpoet.AnnotationSpec
8+
import com.squareup.kotlinpoet.FileSpec
9+
import com.squareup.kotlinpoet.FunSpec
10+
import com.squareup.kotlinpoet.KModifier
11+
import com.squareup.kotlinpoet.TypeName
12+
import com.squareup.kotlinpoet.TypeSpec
13+
import graphql.language.Document
14+
import graphql.language.FieldDefinition
15+
import graphql.language.ObjectTypeDefinition
16+
import graphql.schema.DataFetchingEnvironment
17+
18+
class KotlinDataFetcherGenerator(private val config: CodeGenConfig, private val document: Document) {
19+
20+
private val packageName = config.packageNameDatafetchers
21+
private val typeUtils = KotlinTypeUtils(config.packageNameTypes, config, document)
22+
23+
fun generate(query: ObjectTypeDefinition): CodeGenResult =
24+
query.fieldDefinitions
25+
.map { generateField(it) }
26+
.fold(CodeGenResult()) { left, right -> left.merge(right) }
27+
28+
private fun generateField(field: FieldDefinition): CodeGenResult {
29+
val fieldName = field.name.substring(0, 1).capitalized() + field.name.substring(1)
30+
val className = fieldName + "Datafetcher"
31+
32+
val returnType = typeUtils.findReturnType(field.type)
33+
34+
val methodSpec = FunSpec.builder("get$fieldName")
35+
.addAnnotation(AnnotationSpec.builder(DgsData::class).addMember("parentType", "\$S", "Query").addMember("field", "\$S", field.name).build())
36+
.addModifiers(KModifier.ABSTRACT)
37+
.also { builder ->
38+
field.inputValueDefinitions.forEach {
39+
val inputType: TypeName = typeUtils.findReturnType(it.type)
40+
builder.addParameter(it.name, inputType)
41+
}
42+
}
43+
.addParameter("dataFetchingEnvironment", DataFetchingEnvironment::class)
44+
.returns(returnType)
45+
.build()
46+
47+
val interfaceBuilder = TypeSpec.interfaceBuilder(className)
48+
.addFunction(methodSpec)
49+
.build()
50+
51+
val fileSpec = FileSpec.get(packageName, interfaceBuilder)
52+
53+
return CodeGenResult(kotlinDataFetchers = listOf(fileSpec))
54+
}
55+
}

graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinCodeGenTest.kt

+89
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package com.netflix.graphql.dgs.codegen
2020

2121
import com.squareup.kotlinpoet.*
2222
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
23+
import graphql.schema.DataFetchingEnvironment
2324
import org.assertj.core.api.Assertions.assertThat
2425
import org.assertj.core.api.Assertions.assertThatThrownBy
2526
import org.assertj.core.data.Index
@@ -35,6 +36,10 @@ import java.util.stream.Stream.of
3536

3637
class KotlinCodeGenTest {
3738

39+
val basePackageName = "com.netflix.graphql.dgs.codegen.tests.generated"
40+
val typesPackageName = "$basePackageName.types"
41+
val datafetchersPackageName = "$basePackageName.datafetchers"
42+
3843
@Test
3944
fun generateDataClassWithStringProperties() {
4045
val schema = """
@@ -68,6 +73,90 @@ class KotlinCodeGenTest {
6873
assertCompilesKotlin(dataTypes)
6974
}
7075

76+
@Test
77+
fun generateDataFetcherInterfaceWithFunction() {
78+
val schema = """
79+
type Query {
80+
people: [Person]
81+
}
82+
83+
type Person {
84+
firstname: String
85+
lastname: String
86+
}
87+
""".trimIndent()
88+
89+
val dataFetchers = CodeGen(
90+
CodeGenConfig(
91+
schemas = setOf(schema),
92+
packageName = basePackageName,
93+
language = Language.KOTLIN,
94+
generateDataFetcherInterfaces = true
95+
)
96+
).generate().kotlinDataFetchers
97+
98+
assertThat(dataFetchers.size).isEqualTo(1)
99+
assertThat(dataFetchers[0].name).isEqualTo("PeopleDatafetcher")
100+
assertThat(dataFetchers[0].packageName).isEqualTo(datafetchersPackageName)
101+
val type = dataFetchers[0].members[0] as TypeSpec
102+
103+
assertThat(type.kind).isEqualTo(TypeSpec.Kind.INTERFACE)
104+
assertThat(type.funSpecs).hasSize(1)
105+
val fn = type.funSpecs.single()
106+
assertThat(fn.name).isEqualTo("getPeople")
107+
val returnType = fn.returnType as ParameterizedTypeName
108+
assertThat(fn.returnType)
109+
assertThat(returnType.rawType.canonicalName).isEqualTo(List::class.qualifiedName)
110+
assertThat(returnType.typeArguments).hasSize(1)
111+
val arg0 = returnType.typeArguments.single() as ClassName
112+
assertThat(arg0.canonicalName).isEqualTo("$typesPackageName.Person")
113+
assertThat(fn.parameters).hasSize(1)
114+
val param0 = fn.parameters.single()
115+
assertThat(param0.name).isEqualTo("dataFetchingEnvironment")
116+
assertThat((param0.type as ClassName).canonicalName).isEqualTo(DataFetchingEnvironment::class.qualifiedName)
117+
}
118+
119+
@Test
120+
fun generateDataFetcherInterfaceWithArgument() {
121+
val schema = """
122+
type Query {
123+
person(name: String): Person
124+
}
125+
126+
type Person {
127+
firstname: String
128+
lastname: String
129+
}
130+
""".trimIndent()
131+
132+
val dataFetchers = CodeGen(
133+
CodeGenConfig(
134+
schemas = setOf(schema),
135+
packageName = basePackageName,
136+
language = Language.KOTLIN,
137+
generateDataFetcherInterfaces = true
138+
)
139+
).generate().kotlinDataFetchers
140+
141+
assertThat(dataFetchers.size).isEqualTo(1)
142+
assertThat(dataFetchers[0].name).isEqualTo("PersonDatafetcher")
143+
assertThat(dataFetchers[0].packageName).isEqualTo(datafetchersPackageName)
144+
val type = dataFetchers[0].members[0] as TypeSpec
145+
146+
assertThat(type.kind).isEqualTo(TypeSpec.Kind.INTERFACE)
147+
assertThat(type.funSpecs).hasSize(1)
148+
val fn = type.funSpecs.single()
149+
assertThat(fn.name).isEqualTo("getPerson")
150+
assertThat((fn.returnType as ClassName).canonicalName).isEqualTo("$typesPackageName.Person")
151+
assertThat(fn.parameters).hasSize(2)
152+
val arg0 = fn.parameters[0]
153+
assertThat(arg0.name).isEqualTo("name")
154+
assertThat((arg0.type as ClassName).canonicalName).isEqualTo(String::class.qualifiedName)
155+
val arg1 = fn.parameters[1]
156+
assertThat(arg1.name).isEqualTo("dataFetchingEnvironment")
157+
assertThat((arg1.type as ClassName).canonicalName).isEqualTo(DataFetchingEnvironment::class.qualifiedName)
158+
}
159+
71160
@Test
72161
fun generateDataClassWithNullablePrimitive() {
73162
val schema = """

graphql-dgs-codegen-gradle/src/main/kotlin/com/netflix/graphql/dgs/codegen/gradle/GenerateJavaTask.kt

+4
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ open class GenerateJavaTask @Inject constructor(
8383
@Input
8484
var generateKotlinClosureProjections = false
8585

86+
@Input
87+
var generateDataFetcherInterfaces = false
88+
8689
@Input
8790
var generateDataTypes = true
8891

@@ -194,6 +197,7 @@ open class GenerateJavaTask @Inject constructor(
194197
generateClientApiv2 = generateClientv2,
195198
generateKotlinNullableClasses = generateKotlinNullableClasses,
196199
generateKotlinClosureProjections = generateKotlinClosureProjections,
200+
generateDataFetcherInterfaces = generateDataFetcherInterfaces,
197201
generateInterfaces = generateInterfaces,
198202
generateInterfaceSetters = generateInterfaceSetters,
199203
generateInterfaceMethodsForInterfaceFields = generateInterfaceMethodsForInterfaceFields,

0 commit comments

Comments
 (0)