Skip to content

Commit 4ed5f96

Browse files
committed
Generates data fetcher interface for query types
1 parent c651a77 commit 4ed5f96

File tree

3 files changed

+150
-1
lines changed

3 files changed

+150
-1
lines changed

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -262,14 +262,20 @@ class CodeGen(private val config: CodeGenConfig) {
262262
}
263263
.fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) }
264264

265+
val dataFetchersResult = definitions.asSequence()
266+
.filterIsInstance<ObjectTypeDefinition>()
267+
.filter { it.name == "Query" }
268+
.map { KotlinDataFetcherGenerator(config).generate(it) }
269+
.fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) }
270+
265271
val constantsClass = KotlinConstantsGenerator(config, document).generate()
266272

267273
val client = generateJavaClientApi(definitions)
268274
val entitiesClient = generateJavaClientEntitiesApi(definitions)
269275
val entitiesRepresentationsTypes = generateKotlinClientEntitiesRepresentations(definitions)
270276

271277
return datatypesResult.merge(inputTypes).merge(interfacesResult).merge(unionResult).merge(enumsResult)
272-
.merge(client).merge(entitiesClient).merge(entitiesRepresentationsTypes).merge(constantsClass)
278+
.merge(client).merge(entitiesClient).merge(entitiesRepresentationsTypes).merge(dataFetchersResult).merge(constantsClass)
273279
}
274280

275281
private fun generateKotlinClientEntitiesRepresentations(definitions: Collection<Definition<*>>): CodeGenResult {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.squareup.kotlinpoet.AnnotationSpec
7+
import com.squareup.kotlinpoet.FileSpec
8+
import com.squareup.kotlinpoet.FunSpec
9+
import com.squareup.kotlinpoet.KModifier
10+
import com.squareup.kotlinpoet.TypeName
11+
import com.squareup.kotlinpoet.TypeSpec
12+
import graphql.language.FieldDefinition
13+
import graphql.language.ObjectTypeDefinition
14+
import graphql.schema.DataFetchingEnvironment
15+
16+
class KotlinDataFetcherGenerator(private val config: CodeGenConfig) {
17+
18+
private val packageName = config.packageNameDatafetchers
19+
private val typeUtils = KotlinTypeUtils(config.packageNameTypes, config)
20+
21+
fun generate(query: ObjectTypeDefinition): CodeGenResult =
22+
query.fieldDefinitions
23+
.map { generateField(it) }
24+
.fold(CodeGenResult()) { left, right -> left.merge(right) }
25+
26+
private fun generateField(field: FieldDefinition): CodeGenResult {
27+
val fieldName = field.name.substring(0, 1).toUpperCase() + field.name.substring(1)
28+
val className = fieldName + "Datafetcher"
29+
30+
val returnType = typeUtils.findReturnType(field.type)
31+
32+
val methodSpec = FunSpec.builder("get$fieldName")
33+
.addAnnotation(AnnotationSpec.builder(DgsData::class).addMember("parentType", "\$S", "Query").addMember("field", "\$S", field.name).build())
34+
.addModifiers(KModifier.ABSTRACT)
35+
.also { builder ->
36+
field.inputValueDefinitions.forEach {
37+
val inputType: TypeName = typeUtils.findReturnType(it.type)
38+
builder.addParameter(it.name, inputType)
39+
}
40+
}
41+
.addParameter("dataFetchingEnvironment", DataFetchingEnvironment::class)
42+
.returns(returnType)
43+
.build()
44+
45+
val interfaceBuilder = TypeSpec.interfaceBuilder(className)
46+
.addFunction(methodSpec)
47+
.build()
48+
49+
val fileSpec = FileSpec.get(packageName, interfaceBuilder)
50+
51+
return CodeGenResult(kotlinDataFetchers = listOf(fileSpec))
52+
}
53+
}

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

+90
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.junit.jupiter.api.Nested
2526
import org.junit.jupiter.api.Test
@@ -32,6 +33,10 @@ import java.util.stream.Stream
3233

3334
class KotlinCodeGenTest {
3435

36+
val basePackageName = "com.netflix.graphql.dgs.codegen.tests.generated"
37+
val typesPackageName = "$basePackageName.types"
38+
val datafetchersPackageName = "$basePackageName.datafetchers"
39+
3540
@Test
3641
fun generateDataClassWithStringProperties() {
3742

@@ -66,6 +71,91 @@ class KotlinCodeGenTest {
6671
assertCompilesKotlin(dataTypes)
6772
}
6873

74+
@Test
75+
fun generateDataFetcherInterfaceWithFunction() {
76+
77+
val schema = """
78+
type Query {
79+
people: [Person]
80+
}
81+
82+
type Person {
83+
firstname: String
84+
lastname: String
85+
}
86+
""".trimIndent()
87+
88+
val dataFetchers = CodeGen(
89+
CodeGenConfig(
90+
schemas = setOf(schema),
91+
packageName = basePackageName,
92+
language = Language.KOTLIN
93+
)
94+
).generate().kotlinDataFetchers
95+
96+
assertThat(dataFetchers.size).isEqualTo(1)
97+
assertThat(dataFetchers[0].name).isEqualTo("PeopleDatafetcher")
98+
assertThat(dataFetchers[0].packageName).isEqualTo(datafetchersPackageName)
99+
val type = dataFetchers[0].members[0] as TypeSpec
100+
101+
assertThat(type.kind).isEqualTo(TypeSpec.Kind.INTERFACE)
102+
assertThat(type.funSpecs).hasSize(1)
103+
val fn = type.funSpecs.single()
104+
assertThat(fn.name).isEqualTo("getPeople")
105+
val returnType = fn.returnType as ParameterizedTypeName
106+
assertThat(fn.returnType)
107+
assertThat(returnType.rawType.canonicalName).isEqualTo(List::class.qualifiedName)
108+
assertThat(returnType.typeArguments).hasSize(1)
109+
val arg0 = returnType.typeArguments.single() as ClassName
110+
assertThat(arg0.canonicalName).isEqualTo("$typesPackageName.Person")
111+
assertThat(fn.parameters).hasSize(1)
112+
val param0 = fn.parameters.single()
113+
assertThat(param0.name).isEqualTo("dataFetchingEnvironment")
114+
assertThat((param0.type as ClassName).canonicalName).isEqualTo(DataFetchingEnvironment::class.qualifiedName)
115+
}
116+
117+
118+
@Test
119+
fun generateDataFetcherInterfaceWithArgument() {
120+
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+
)
138+
).generate() as CodeGenResult).kotlinDataFetchers
139+
140+
assertThat(dataFetchers.size).isEqualTo(1)
141+
assertThat(dataFetchers[0].name).isEqualTo("PersonDatafetcher")
142+
assertThat(dataFetchers[0].packageName).isEqualTo(datafetchersPackageName)
143+
val type = dataFetchers[0].members[0] as TypeSpec
144+
145+
assertThat(type.kind).isEqualTo(TypeSpec.Kind.INTERFACE)
146+
assertThat(type.funSpecs).hasSize(1)
147+
val fn = type.funSpecs.single()
148+
assertThat(fn.name).isEqualTo("getPerson")
149+
assertThat((fn.returnType as ClassName).canonicalName).isEqualTo("$typesPackageName.Person")
150+
assertThat(fn.parameters).hasSize(2);
151+
val arg0 = fn.parameters[0]
152+
assertThat(arg0.name).isEqualTo("name")
153+
assertThat((arg0.type as ClassName).canonicalName).isEqualTo(String::class.qualifiedName)
154+
val arg1 = fn.parameters[1]
155+
assertThat(arg1.name).isEqualTo("dataFetchingEnvironment")
156+
assertThat((arg1.type as ClassName).canonicalName).isEqualTo(DataFetchingEnvironment::class.qualifiedName)
157+
}
158+
69159
@Test
70160
fun generateDataClassWithNullablePrimitive() {
71161

0 commit comments

Comments
 (0)