Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Boolean fields to Data classes for supporting sparse update #664

Merged
merged 48 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
3ca2c92
added support for bitset for data types
Mar 15, 2024
00750c2
added support for bitset for all data types
Mar 18, 2024
5da690e
initialize bitset
Mar 18, 2024
874e237
generate setField conditionally
Mar 18, 2024
a189da3
bug fixes
Mar 18, 2024
39e4878
adding unit tests
Mar 18, 2024
f21e6af
cleanup
Mar 18, 2024
c10ceb9
Revert "cleanup"
Mar 18, 2024
caff38c
cleanup
Mar 18, 2024
e178a13
updated flag name, CodeGenCliKt.run.xml
Mar 18, 2024
ecdee83
added additional unit test without field isset
Mar 18, 2024
36d06c0
Added descriptive help for CLI flag
Mar 18, 2024
3bae6d6
refactor builder function
Mar 18, 2024
0ccf925
add parameters to enum constructor
Mar 20, 2024
cd71b3a
fix linting
Mar 21, 2024
909d1fc
Revert "fix linting"
Mar 21, 2024
7a1d567
fix linting, unit tests
Mar 22, 2024
b3513ef
updating linting
Mar 22, 2024
b5c7af4
add field is set to generateJava
Mar 24, 2024
4052436
Revert "add field is set to generateJava"
Mar 25, 2024
7fa7f2f
Revert "updating linting"
Mar 25, 2024
3279171
Revert "fix linting, unit tests"
Mar 25, 2024
544cc66
update generateJava, formatting
Mar 25, 2024
ecdae30
revert xml changes
Mar 25, 2024
f2025ed
linting fix
Mar 25, 2024
e22b4cc
updating isSet and setField to public
Mar 25, 2024
f4833f2
update boolean fields
Mar 29, 2024
7274e31
cleanup
Mar 29, 2024
2b01466
update boolean field generation with tests
Apr 2, 2024
6a515b3
updating unit tests
Apr 3, 2024
e8e60fa
updating unit tests
Apr 3, 2024
5d76a5e
formatting Kotlin
Apr 3, 2024
8513595
Revert "formatting Kotlin"
Apr 3, 2024
0cf86d3
formatting Kotlin
Apr 3, 2024
0e8cc84
updating boolean field and setter names
Apr 3, 2024
bab8c0e
update EntitiesClientApiGenTest and ClientApiGenQueryTest
Apr 17, 2024
659de3d
fix more tests
Apr 17, 2024
aa449b8
bug fix
Apr 19, 2024
f3f16ac
format kotlin
Apr 19, 2024
258cb3b
fix unit tests
Apr 20, 2024
2c4515e
update boolean fields
Apr 25, 2024
dd0935d
cleanup
Apr 25, 2024
9dec043
fix: adding is nullable for input types
Apr 26, 2024
1e7bb05
fix: adding javapoet formatting
Apr 29, 2024
8a83b91
fix: Adding fields for non-nullable without defaults
Apr 29, 2024
3c076cc
cleanup
Apr 29, 2024
228ce44
addressing comments
Apr 29, 2024
9cb3ec6
cleanup
Apr 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .run/CodeGenCliKt.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,17 @@
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="false" name="CodeGenCliKt" type="JetRunConfigurationType" nameIsGenerated="true">
<module name="graphql-dgs-codegen.graphql-dgs-codegen-core.main" />
<option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="--package-name=tester.generated --include-query=acct_allCompanies --include-mutation=prdcl_importCrewContacts --output-dir=$PROJECT_DIR$/../generated-api-tester/src --generate-client --language=java --skip-entities --type-mapping=BigDecimal=java.math.BigDecimal --type-mapping=Currency=java.util.Currency --type-mapping=BGTAccountNumber=java.lang.String --type-mapping=Url=java.lang.String --type-mapping=CountryCode=java.lang.String --type-mapping=BGTRevisionNumber=java.lang.String composed-schema.graphqls" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="MAIN_CLASS_NAME" value="com.netflix.graphql.dgs.codegen.CodeGenCliKt" />
<option name="WORKING_DIRECTORY" value="" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
krutikavk marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ class CodeGenConfig(
private val subPackageNameTypes: String = "types",
private val subPackageNameDocs: String = "docs",
var language: Language = Language.JAVA,
var generateFieldIsSet: Boolean = false,
var generateBoxedTypes: Boolean = false,
var generateIsGetterForPrimitiveBooleanFields: Boolean = false,
var generateClientApi: Boolean = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class CodeGenCli : CliktCommand("Generate Java sources for SCHEMA file(s)") {
)
private val language by option("--language", "-l", help = "Output language").choice("java", "kotlin", ignoreCase = true)
.default("java")
krutikavk marked this conversation as resolved.
Show resolved Hide resolved
private val generateClient by option("--generate-client", "-c", help = "Genereate client api").flag(default = false)
private val generateClient by option("--generate-client", "-c", help = "Generate client api").flag(default = false)
private val generateDataTypes by option(
"--generate-data-types",
help = "Generate data types. Not needed when only generating an API"
Expand All @@ -59,6 +59,8 @@ class CodeGenCli : CliktCommand("Generate Java sources for SCHEMA file(s)") {
private val shortProjectionNames by option("--short-projection-names").flag()
private val generateInterfaceSetters by option("--generate-interface-setters").flag()
private val generateDocs by option("--generate-docs").flag()
// Generate an additional bitset field and supporting getters, setters, builder functions for data classes
private val generateFieldIsSet by option("--generate-field-is-set", help = "Generate an additional bitset field and supporting getters, setters, and builder functions for data classes").flag(default = false)

override fun run() {
val inputSchemas = if (schemas.isEmpty()) {
Expand Down Expand Up @@ -94,7 +96,8 @@ class CodeGenCli : CliktCommand("Generate Java sources for SCHEMA file(s)") {
generateDataTypes = generateDataTypes,
generateInterfaces = generateInterfaces,
generateInterfaceSetters = generateInterfaceSetters,
generateDocs = generateDocs
generateDocs = generateDocs,
generateFieldIsSet = generateFieldIsSet
)
} else {
CodeGenConfig(
Expand All @@ -115,7 +118,8 @@ class CodeGenCli : CliktCommand("Generate Java sources for SCHEMA file(s)") {
generateDataTypes = generateDataTypes,
generateInterfaces = generateInterfaces,
generateInterfaceSetters = generateInterfaceSetters,
generateDocs = generateDocs
generateDocs = generateDocs,
generateFieldIsSet = generateFieldIsSet
)
}
).generate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package com.netflix.graphql.dgs.codegen.generators.java

import com.netflix.graphql.dgs.codegen.*
import com.netflix.graphql.dgs.codegen.generators.shared.CodeGeneratorUtils.capitalized
import com.netflix.graphql.dgs.codegen.generators.shared.SiteTarget
import com.netflix.graphql.dgs.codegen.generators.shared.applyDirectivesJava
import com.squareup.javapoet.*
Expand Down Expand Up @@ -223,6 +224,11 @@ abstract class BaseDataTypeGenerator(
addParameterizedConstructor(fields, javaType)
}

if(config.generateFieldIsSet) {
addBitsetField(javaType)
addBitSetEnum(fields, javaType)
}

addToString(fields, javaType)

addEquals(javaType)
Expand Down Expand Up @@ -362,6 +368,66 @@ abstract class BaseDataTypeGenerator(
addFieldWithGetterAndSetter(fieldDefinition.type, fieldDefinition, javaType)
}

private fun addBitsetField(javaType: TypeSpec.Builder) {
val fieldsPresent = Field("fieldsPresent", com.squareup.javapoet.TypeName.get(java.util.BitSet::class.java))
val fieldBuilder = FieldSpec
.builder(fieldsPresent.type, ReservedKeywordSanitizer.sanitize(fieldsPresent.name))
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.TRANSIENT)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this transient?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The BitSet variable is set to be transient so that serialization of data classes is unchanged.

.initializer("new BitSet()")
javaType.addField(fieldBuilder.build())
addBitsetFieldGetterAndSetter(javaType)
}

private fun addBitSetEnum(fields: List<Field>, javaType: TypeSpec.Builder) {
val enumBuilder = TypeSpec
.enumBuilder("Field")
.addModifiers(Modifier.PUBLIC)
.addField(FieldSpec.builder(com.squareup.javapoet.TypeName.INT, "ordinal").initializer("-1").build())
.addMethod(MethodSpec
.methodBuilder("getOrdinal")
.addModifiers(Modifier.PUBLIC)
.returns(ClassName.INT)
.addCode(
"""
|return ordinal;
""".trimMargin()
)
.build()
)
.addMethod(MethodSpec
.constructorBuilder()
.addCode(
"""
|this.ordinal = ordinal;
""".trimMargin()
)
.build()
)

fields.forEach {
enumBuilder.addEnumConstant(it.name.uppercase())
}

javaType.addType(enumBuilder.build())
}

private fun addBitsetFieldGetterAndSetter(javaType: TypeSpec.Builder) {
val setFieldSetter = MethodSpec.methodBuilder("setField")
.addModifiers(Modifier.PRIVATE)
.addParameter(ClassName.get("", "Field"), "field")
.addStatement("fieldsPresent.set(field.getOrdinal())")

val isSetGetter = MethodSpec.methodBuilder("isSet")
.addModifiers(Modifier.PRIVATE)
.returns(ClassName.BOOLEAN)
.addParameter(ClassName.get("", "Field"), "field")
.addStatement("return fieldsPresent.get(field.getOrdinal())")

javaType.addMethod(setFieldSetter.build())
javaType.addMethod(isSetGetter.build())
}


private fun addFieldWithGetterAndSetter(returnType: com.squareup.javapoet.TypeName?, fieldDefinition: Field, javaType: TypeSpec.Builder) {
val fieldBuilder = if (fieldDefinition.initialValue != null) {
FieldSpec
Expand Down Expand Up @@ -398,6 +464,13 @@ abstract class BaseDataTypeGenerator(
ReservedKeywordSanitizer.sanitize(fieldDefinition.name)
)

if(config.generateFieldIsSet) {
setterMethodBuilder.addStatement(
"setField(Field.\$N)",
ReservedKeywordSanitizer.sanitize(fieldDefinition.name.uppercase())
)
}

if (fieldDefinition.directives.isNotEmpty()) {
val (annotations, comments) = applyDirectivesJava(fieldDefinition.directives, config)
if (!comments.isNullOrBlank()) {
Expand Down Expand Up @@ -433,13 +506,29 @@ abstract class BaseDataTypeGenerator(

private fun addBuilder(javaType: TypeSpec.Builder) {
val className = ClassName.get(packageName, javaType.build().name)
val buildMethod = MethodSpec.methodBuilder("build").returns(className).addStatement(
"""
$className result = new $className();
${javaType.build().fieldSpecs.joinToString("\n") { "result.${it.name} = this.${it.name};" }}
return result

val buildMethod = if(config.generateFieldIsSet) {
MethodSpec.methodBuilder("build").returns(className).addCode(
"""
$className result = new $className();
${javaType.build().fieldSpecs.filter{it.name!="fieldsPresent"}.joinToString("\n") { "result.${it.name} = this.${it.name};" }}
for (Field field: Field.values()) {
if (this.isSet(field)) {
result.setField(field);
}
}
return result;
""".trimIndent()
)
} else {
MethodSpec.methodBuilder("build").returns(className).addCode(
"""
$className result = new $className();
${javaType.build().fieldSpecs.joinToString("\n") { "result.${it.name} = this.${it.name};" }}
return result;
""".trimIndent()
).addModifiers(Modifier.PUBLIC).build()
)
}.addModifiers(Modifier.PUBLIC).build()

val builderClassName = ClassName.get(packageName, "$className.Builder")
val newBuilderMethod =
Expand All @@ -452,19 +541,32 @@ abstract class BaseDataTypeGenerator(

javaType.addMethod(newBuilderMethod)



val builderType =
TypeSpec
.classBuilder("Builder")
.addOptionalGeneratedAnnotation(config)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addMethod(buildMethod)

javaType.build().fieldSpecs.map {
MethodSpec.methodBuilder(it.name)
if(config.generateFieldIsSet) {
addBitsetFieldGetterAndSetter(builderType)
}

javaType.build().fieldSpecs.filter{it.name!="fieldsPresent"}.map {
var methodBuilder = MethodSpec.methodBuilder(it.name)
.addJavadoc(it.javadoc)
.returns(builderClassName)
.addStatement("this.${it.name} = ${it.name}")
.addStatement("return this")
if(config.generateFieldIsSet) {
methodBuilder.addStatement(
"setField(Field.\$N)",
ReservedKeywordSanitizer.sanitize(it.name.uppercase())
)
}

methodBuilder.addStatement("return this")
.addParameter(ParameterSpec.builder(it.type, it.name).build())
.addModifiers(Modifier.PUBLIC).build()
}.forEach { builderType.addMethod(it) }
Expand Down
Loading