Skip to content

Commit 89ed33a

Browse files
krutikavkkkatelia
andauthored
Add Boolean fields to Data classes for supporting sparse update (#664)
* added support for bitset for data types * added support for bitset for all data types * initialize bitset * generate setField conditionally * bug fixes * adding unit tests * cleanup * Revert "cleanup" This reverts commit f21e6af. * cleanup * updated flag name, CodeGenCliKt.run.xml * added additional unit test without field isset * Added descriptive help for CLI flag * refactor builder function * add parameters to enum constructor * fix linting * Revert "fix linting" This reverts commit cd71b3a. * fix linting, unit tests * updating linting * add field is set to generateJava * Revert "add field is set to generateJava" This reverts commit b5c7af4. * Revert "updating linting" This reverts commit b3513ef. * Revert "fix linting, unit tests" This reverts commit 7a1d567. * update generateJava, formatting * revert xml changes * linting fix * updating isSet and setField to public * update boolean fields * cleanup * update boolean field generation with tests * updating unit tests * updating unit tests * formatting Kotlin * Revert "formatting Kotlin" This reverts commit 5d76a5e. * formatting Kotlin * updating boolean field and setter names * update EntitiesClientApiGenTest and ClientApiGenQueryTest * fix more tests * bug fix * format kotlin * fix unit tests * update boolean fields * cleanup * fix: adding is nullable for input types * fix: adding javapoet formatting * fix: Adding fields for non-nullable without defaults * cleanup * addressing comments * cleanup --------- Co-authored-by: kkatelia <[email protected]>
1 parent 30ac67d commit 89ed33a

File tree

6 files changed

+360
-116
lines changed

6 files changed

+360
-116
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class CodeGenCli : CliktCommand("Generate Java sources for SCHEMA file(s)") {
4646
)
4747
private val language by option("--language", "-l", help = "Output language").choice("java", "kotlin", ignoreCase = true)
4848
.default("java")
49-
private val generateClient by option("--generate-client", "-c", help = "Genereate client api").flag(default = false)
49+
private val generateClient by option("--generate-client", "-c", help = "Generate client api").flag(default = false)
5050
private val generateDataTypes by option(
5151
"--generate-data-types",
5252
help = "Generate data types. Not needed when only generating an API"

graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/DataTypeGenerator.kt

Lines changed: 85 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919
package com.netflix.graphql.dgs.codegen.generators.java
2020

2121
import com.netflix.graphql.dgs.codegen.*
22+
import com.netflix.graphql.dgs.codegen.generators.shared.CodeGeneratorUtils.capitalized
2223
import com.netflix.graphql.dgs.codegen.generators.shared.SiteTarget
2324
import com.netflix.graphql.dgs.codegen.generators.shared.applyDirectivesJava
2425
import com.squareup.javapoet.*
2526
import graphql.language.*
2627
import graphql.language.TypeName
28+
import graphql.schema.idl.TypeUtil
2729
import org.slf4j.Logger
2830
import org.slf4j.LoggerFactory
2931
import java.io.Serializable
@@ -81,13 +83,15 @@ class DataTypeGenerator(config: CodeGenConfig, document: Document) : BaseDataTyp
8183
if (config.generateDataTypes) {
8284
val fieldDefinitions = definition.fieldDefinitions
8385
.filterSkipped()
84-
.map {
86+
.map { fieldDefinition ->
87+
val isNullable = !TypeUtil.isNonNull(fieldDefinition.type)
8588
Field(
86-
it.name,
87-
typeUtils.findReturnType(it.type, useInterfaceType, true),
89+
fieldDefinition.name,
90+
typeUtils.findReturnType(fieldDefinition.type, useInterfaceType, true),
8891
overrideGetter = overrideGetter,
89-
description = it.description,
90-
directives = it.directives
92+
description = fieldDefinition.description,
93+
directives = fieldDefinition.directives,
94+
isNullable = isNullable
9195
)
9296
}
9397
.plus(
@@ -122,6 +126,7 @@ class InputTypeGenerator(config: CodeGenConfig, document: Document) : BaseDataTy
122126

123127
val name = definition.name
124128
val fieldDefinitions = definition.inputValueDefinitions.map {
129+
val isNullable = !TypeUtil.isNonNull(it.type)
125130
val defaultValue = it.defaultValue?.let { defVal ->
126131
when (defVal) {
127132
is BooleanValue -> CodeBlock.of("\$L", defVal.isValue)
@@ -155,7 +160,8 @@ class InputTypeGenerator(config: CodeGenConfig, document: Document) : BaseDataTy
155160
type = typeUtils.findReturnType(it.type),
156161
initialValue = defaultValue,
157162
description = it.description,
158-
directives = it.directives
163+
directives = it.directives,
164+
isNullable = isNullable
159165
)
160166
}.plus(extensions.flatMap { it.inputValueDefinitions }.map { Field(it.name, typeUtils.findReturnType(it.type)) })
161167
return generate(name, emptyList(), fieldDefinitions, definition.description, definition.directives)
@@ -167,7 +173,7 @@ class InputTypeGenerator(config: CodeGenConfig, document: Document) : BaseDataTy
167173
}
168174
}
169175

170-
internal data class Field(val name: String, val type: com.squareup.javapoet.TypeName, val initialValue: CodeBlock? = null, val overrideGetter: Boolean = false, val interfaceType: com.squareup.javapoet.TypeName? = null, val description: Description? = null, val directives: List<Directive> = listOf())
176+
internal data class Field(val name: String, val type: com.squareup.javapoet.TypeName, val initialValue: CodeBlock? = null, val overrideGetter: Boolean = false, val interfaceType: com.squareup.javapoet.TypeName? = null, val description: Description? = null, val directives: List<Directive> = listOf(), val isNullable: Boolean = true)
171177

172178
abstract class BaseDataTypeGenerator(
173179
internal val packageName: String,
@@ -227,7 +233,7 @@ abstract class BaseDataTypeGenerator(
227233

228234
addEquals(javaType)
229235
addHashcode(javaType)
230-
addBuilder(javaType)
236+
addBuilder(fields, javaType)
231237

232238
val javaFile = JavaFile.builder(packageName, javaType.build()).build()
233239

@@ -341,7 +347,22 @@ abstract class BaseDataTypeGenerator(
341347
constructorBuilder
342348
.addParameter(parameterBuilder.build())
343349
.addModifiers(Modifier.PUBLIC)
344-
.addStatement("this.\$N = \$N", ReservedKeywordSanitizer.sanitize(it.name), ReservedKeywordSanitizer.sanitize(it.name))
350+
}
351+
352+
fieldDefinitions.forEach {
353+
val constructor = constructorBuilder
354+
.addStatement(
355+
"this.\$N = \$N",
356+
ReservedKeywordSanitizer.sanitize(it.name),
357+
ReservedKeywordSanitizer.sanitize(it.name)
358+
)
359+
if (it.isNullable && it.initialValue == null) {
360+
constructorBuilder
361+
.addStatement(
362+
"this.\$N = true",
363+
generateBooleanFieldName(ReservedKeywordSanitizer.sanitize(it.name))
364+
)
365+
}
345366
}
346367

347368
javaType.addMethod(constructorBuilder.build())
@@ -360,6 +381,35 @@ abstract class BaseDataTypeGenerator(
360381

361382
private fun addField(fieldDefinition: Field, javaType: TypeSpec.Builder) {
362383
addFieldWithGetterAndSetter(fieldDefinition.type, fieldDefinition, javaType)
384+
// Generate for all nullable fields without any defaults
385+
if (fieldDefinition.isNullable && fieldDefinition.initialValue == null) {
386+
addIsDefinedFieldWithGetters(fieldDefinition, javaType)
387+
}
388+
}
389+
390+
private fun addIsDefinedFieldWithGetters(fieldDefinition: Field, javaType: TypeSpec.Builder) {
391+
val fieldName = generateBooleanFieldName(ReservedKeywordSanitizer.sanitize(fieldDefinition.name))
392+
val field = FieldSpec
393+
.builder(com.squareup.javapoet.TypeName.BOOLEAN, fieldName)
394+
.addModifiers(Modifier.PRIVATE)
395+
.initializer("false")
396+
.build()
397+
val getterName = "${fieldName}Defined"
398+
399+
val getter = MethodSpec
400+
.methodBuilder(getterName)
401+
.addModifiers(Modifier.PUBLIC)
402+
.returns(com.squareup.javapoet.TypeName.BOOLEAN)
403+
.addStatement(
404+
"return \$N",
405+
fieldName
406+
).build()
407+
javaType.addField(field)
408+
javaType.addMethod(getter)
409+
}
410+
411+
private fun generateBooleanFieldName(name: String): String {
412+
return "is${name.capitalized()}"
363413
}
364414

365415
private fun addFieldWithGetterAndSetter(returnType: com.squareup.javapoet.TypeName?, fieldDefinition: Field, javaType: TypeSpec.Builder) {
@@ -390,13 +440,21 @@ abstract class BaseDataTypeGenerator(
390440

391441
val setterName = typeUtils.transformIfDefaultClassMethodExists("set${fieldDefinition.name[0].uppercase()}${fieldDefinition.name.substring(1)}", TypeUtils.Companion.setClass)
392442
val parameterBuilder = ParameterSpec.builder(returnType, ReservedKeywordSanitizer.sanitize(fieldDefinition.name))
443+
393444
val setterMethodBuilder = MethodSpec.methodBuilder(setterName)
394445
.addModifiers(Modifier.PUBLIC)
395446
.addStatement(
396447
"this.\$N = \$N",
397448
ReservedKeywordSanitizer.sanitize(fieldDefinition.name),
398449
ReservedKeywordSanitizer.sanitize(fieldDefinition.name)
399450
)
451+
if (fieldDefinition.isNullable && fieldDefinition.initialValue == null) {
452+
setterMethodBuilder
453+
.addStatement(
454+
"this.\$N = true",
455+
generateBooleanFieldName(ReservedKeywordSanitizer.sanitize(fieldDefinition.name))
456+
)
457+
}
400458

401459
if (fieldDefinition.directives.isNotEmpty()) {
402460
val (annotations, comments) = applyDirectivesJava(fieldDefinition.directives, config)
@@ -431,13 +489,13 @@ abstract class BaseDataTypeGenerator(
431489
)
432490
}
433491

434-
private fun addBuilder(javaType: TypeSpec.Builder) {
492+
private fun addBuilder(fields: List<Field>, javaType: TypeSpec.Builder) {
435493
val className = ClassName.get(packageName, javaType.build().name)
436494
val buildMethod = MethodSpec.methodBuilder("build").returns(className).addStatement(
437495
"""
438-
$className result = new $className();
439-
${javaType.build().fieldSpecs.joinToString("\n") { "result.${it.name} = this.${it.name};" }}
440-
return result
496+
$className result = new $className();
497+
${javaType.build().fieldSpecs.joinToString("\n") { "result.${it.name} = this.${it.name};".trimIndent() }}
498+
return result
441499
""".trimIndent()
442500
).addModifiers(Modifier.PUBLIC).build()
443501

@@ -460,10 +518,23 @@ abstract class BaseDataTypeGenerator(
460518
.addMethod(buildMethod)
461519

462520
javaType.build().fieldSpecs.map {
463-
MethodSpec.methodBuilder(it.name)
521+
val method = MethodSpec.methodBuilder(it.name)
464522
.addJavadoc(it.javadoc)
465523
.returns(builderClassName)
466524
.addStatement("this.${it.name} = ${it.name}")
525+
526+
val fieldName = it.name
527+
val field = fields.find { it.name.contains(fieldName) }
528+
529+
if (field?.isNullable == true && field.initialValue == null) {
530+
method
531+
.addStatement(
532+
"this.\$N = true",
533+
generateBooleanFieldName(it.name)
534+
)
535+
}
536+
537+
method
467538
.addStatement("return this")
468539
.addParameter(ParameterSpec.builder(it.type, it.name).build())
469540
.addModifiers(Modifier.PUBLIC).build()

0 commit comments

Comments
 (0)