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

Conversation

krutikavk
Copy link
Contributor

@krutikavk krutikavk commented Mar 18, 2024

Description

Issue: #609

Add Boolean fields to all data classes. Here is a gist of changes:

  1. Generate boolean fields for Java data classes by default, removed additional flag exposed for CodeGen config
  2. Add a boolean for each field in data class called is<Field>Defined
  3. Getter for boolean called getIs<Field>Defined
  4. Setter for each field in the class explicitly sets value of is<Field>Defined
  5. Update boolean field and getter similarly for Builder class
  6. Update Builder.build() function to use setter functions for each field from data class
  7. Please note since this feature is enabled as default, most tests needed an update to account for additional fields added to data classes.

Example of data classes created:

  1. Schema
  2. Generated data types with this change for schema here

Thanks!

Validation

Sample schema and codegen with bitset: https://github.com/krutikavk/dgs-codegen-run

.run/CodeGenCliKt.run.xml Outdated Show resolved Hide resolved
@krutikavk
Copy link
Contributor Author

Hi @srinivasankavitha Can you take a look at the PR

@srinivasankavitha
Copy link
Contributor

Hi @srinivasankavitha Can you take a look at the PR
Thanks for the PR! Will do later this week.

@krutikavk
Copy link
Contributor Author

krutikavk commented Mar 21, 2024

Thanks for the PR! Will do later this week.

Thanks @srinivasankavitha. I am also working on updating Kotlin codegen.
What is the difference between generators/kotlin and generators/kotlin2--should I be updating both? The flag that exposes Kotlin2 data type generation generateKotlinNullableClasses is not exposed as a flag for codegen CLI

@srinivasankavitha
Copy link
Contributor

yeah that flag is specific to kotlin2. Yes, it would be great of you can add to both since we try to maintain feature parity as much as possible.

@krutikavk
Copy link
Contributor Author

yeah that flag is specific to kotlin2. Yes, it would be great of you can add to both since we try to maintain feature parity as much as possible.

Sounds great, thank you 👍

@srinivasankavitha
Copy link
Contributor

srinivasankavitha commented Mar 21, 2024

Also, you can run ./gradlew formatKotlin to fix linting errors that are failing the build.

@krutikavk
Copy link
Contributor Author

Linting and some unit tests fixed, please check @srinivasankavitha

@krutikavk
Copy link
Contributor Author

Hi @kilink @srinivasankavitha Apologies for the follow-up, I wanted to check in on the PR review. If there were any concerns or questions, I am happy to address them. Thanks for your patience and understanding!

I see the CI build just failed at stage "Build Examples". Can you share any pointers if this needs to be fixed on the PR and how I can run the CI test locally for validating CI builds?

@srinivasankavitha
Copy link
Contributor

srinivasankavitha commented Apr 26, 2024

Both examples are here: https://github.com/Netflix/dgs-examples-java-2.7
https://github.com/Netflix/dgs-examples-kotlin-2.7
You can run things locally with those examples and applying the codegen plugin from your branch.

@krutikavk
Copy link
Contributor Author

Thanks @srinivasankavitha I ran through the examples and dropped in a quick fix adding conditional boolean field generation for input type values as well.

The build failure message is as below:

ERROR:Unable to test /home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7, command '/home/runner/work/dgs-codegen/dgs-codegen/gradlew -p /home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7 -c /home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7/settings.gradle.kts clean check' failed! Command '['/home/runner/work/dgs-codegen/dgs-codegen/gradlew', '-p', '/home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7', '-c', '/home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7/settings.gradle.kts', 'clean', 'check']' returned non-zero exit status 1.

I am wondering if this fix will fix the test as well--I a m unsure what test is failing atm ^ as sent above. Can you please retry the build and guide if I need to do anything else to fix like updating expected values etc.?

ReservedKeywordSanitizer.sanitize(it.name),
ReservedKeywordSanitizer.sanitize(it.name)
)
if (!(!it.isNullable && it.initialValue == null)) {
Copy link
Member

Choose a reason for hiding this comment

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

It's probably cleaner to write this as:

if (it.isNullable && it.initialValue == null) {
   // ...
}

Copy link
Contributor Author

@krutikavk krutikavk Apr 29, 2024

Choose a reason for hiding this comment

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

@kilink Fields are generated for all these cases:

  1. Nullable | No default values --> Generate fields
  2. Nullable | With default values --> Generate fields
  3. Non-Nullable | No default values --> Field not needed
  4. Non-Nullable | With default values --> Generate fields

Fields are generated for both nullable field with default value as well as non-nullable with default value might get assigned default values by initializer without direct request from the client. This is our understanding from existing graphql services leveraging sparse update solution. Having Boolean field for other cases will help identify values set by the client.

With this, I can update the double negative to simplify the logic:

if (it.isNullable && it.initialValue != null))

Is this acceptable?

Copy link
Member

Choose a reason for hiding this comment

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

There's no reason to generate fields for any of the cases except the first one: a nullable field with no defaults. For any case with a default value, it's impossible for DGS to tell if the value was explicitly supplied or not, graphql-java will hand us the variables with the default value already there. So the flag here would always be "true". The flag is pretty useless in that case and misleading.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ack, updated to generate fields for nullable fields with no default values.

@@ -360,6 +381,35 @@ abstract class BaseDataTypeGenerator(

private fun addField(fieldDefinition: Field, javaType: TypeSpec.Builder) {
addFieldWithGetterAndSetter(fieldDefinition.type, fieldDefinition, javaType)
// Generate for all fields except non-nullable without default values
Copy link
Member

Choose a reason for hiding this comment

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

nitpick: I think the comment would be clearer if it was stated as what we want to generate the fields for, e.g.:

Generate for all nullable fields without any defaults

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated 👍

@@ -360,6 +381,35 @@ abstract class BaseDataTypeGenerator(

private fun addField(fieldDefinition: Field, javaType: TypeSpec.Builder) {
addFieldWithGetterAndSetter(fieldDefinition.type, fieldDefinition, javaType)
// Generate for all fields except non-nullable without default values
if (!(!fieldDefinition.isNullable && fieldDefinition.initialValue == null)) {
Copy link
Member

Choose a reason for hiding this comment

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

Same comment here as before, I would avoid that "double negative" and write this as:

if (fieldDefinition.isNullable && fieldDefinition.initialValue == null) {
   // ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated same as above

@srinivasankavitha
Copy link
Contributor

Thanks @srinivasankavitha I ran through the examples and dropped in a quick fix adding conditional boolean field generation for input type values as well.

The build failure message is as below:

ERROR:Unable to test /home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7, command '/home/runner/work/dgs-codegen/dgs-codegen/gradlew -p /home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7 -c /home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7/settings.gradle.kts clean check' failed! Command '['/home/runner/work/dgs-codegen/dgs-codegen/gradlew', '-p', '/home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7', '-c', '/home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7/settings.gradle.kts', 'clean', 'check']' returned non-zero exit status 1.

I am wondering if this fix will fix the test as well--I a m unsure what test is failing atm ^ as sent above. Can you please retry the build and guide if I need to do anything else to fix like updating expected values etc.?

This seems to have fixed the build now, right? I see the CI build is green.

@krutikavk
Copy link
Contributor Author

Thanks @srinivasankavitha I ran through the examples and dropped in a quick fix adding conditional boolean field generation for input type values as well.
The build failure message is as below:
ERROR:Unable to test /home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7, command '/home/runner/work/dgs-codegen/dgs-codegen/gradlew -p /home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7 -c /home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7/settings.gradle.kts clean check' failed! Command '['/home/runner/work/dgs-codegen/dgs-codegen/gradlew', '-p', '/home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7', '-c', '/home/runner/work/dgs-codegen/dgs-codegen/build/examples/dgs-examples-java-2.7/settings.gradle.kts', 'clean', 'check']' returned non-zero exit status 1.
I am wondering if this fix will fix the test as well--I a m unsure what test is failing atm ^ as sent above. Can you please retry the build and guide if I need to do anything else to fix like updating expected values etc.?

This seems to have fixed the build now, right? I see the CI build is green.

@srinivasankavitha Yes I was just able to validate this locally as well, this fixed the build. Please help validate and merge the PR.

@krutikavk
Copy link
Contributor Author

@kilink Thanks for reviewing, I have updated as per your feedback. Please check if the changes are good

@krutikavk
Copy link
Contributor Author

Hi @kilink @srinivasankavitha Checking in if you need anything from me before the PR can be merged

@srinivasankavitha
Copy link
Contributor

Hi @krutikavk - we are out this week. We will take a look next week.

@ramapalani
Copy link

@srinivasankavitha following up on this. Will you be able to review this PR? And let us know if you need any further changes to the PR

@ramapalani
Copy link

@srinivasankavitha Thanks for approving.
What is the next step for this PR? Will you be able to merge this or do you require additional approvals?

@srinivasankavitha srinivasankavitha merged commit 89ed33a into Netflix:master May 8, 2024
2 checks passed
@srinivasankavitha
Copy link
Contributor

Just merged the PR. Will do a release later this week or early next week.

@ramapalani
Copy link

Thank you

@krutikavk
Copy link
Contributor Author

Hi @srinivasankavitha Checking in when you are able to create a new release tag for this feature

srinivasankavitha added a commit that referenced this pull request May 16, 2024
@krutikavk
Copy link
Contributor Author

krutikavk commented May 17, 2024

Hi @srinivasankavitha @kilink I see this change has been recently reverted in commit 08eb4ad. Will this be updated/released soon? Do update if there are any outstanding issues with the feature.

@srinivasankavitha
Copy link
Contributor

Hi @krutikavk - yes, I did a release yesterday and it broke a bunch of our projects and had to rollback. I'm yet to investigate and get a good idea of the failure scenarios (there are multiple). Will post an update, so we can fix forward. At teh very least, we might need to feature flag it and disable by default since the addition of the new field is breaking tests for users that are checking strings using toString().

.addJavadoc(it.javadoc)
.returns(builderClassName)
.addStatement("this.${it.name} = ${it.name}")

val fieldName = it.name
val field = fields.find { it.name.contains(fieldName) }
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be

val field = fields.find { iter-> ReservedKeywordSanitizer.sanitize(iter.name) == fieldName }

Otherwise it matches other fields that are similarly named and is incorrect.
e.g. something and isSomethingThere..will generate isSomething that conflicts with isSomethingThere field.

Also this is problematic if the field isSomething is also there as part of the schema.

@srinivasankavitha
Copy link
Contributor

Hi @krutikavk - yes, I did a release yesterday and it broke a bunch of our projects and had to rollback. I'm yet to investigate and get a good idea of the failure scenarios (there are multiple). Will post an update, so we can fix forward. At the very least, we might need to feature flag it and disable by default since the addition of the new field is breaking tests for users that are checking strings using toString().

Ok so couple things that need fixing:

  1. The check needs to be fixed per my comment here along with test coverage: https://github.com/Netflix/dgs-codegen/pull/664/files#diff-9e24a3fdc10ed2f83c56a842ef173a41cf511d9d3cc154d736a9825e6bd3e699R527

  2. The "==" method for the generated classes should exclude checking the additional isSomething fields since it fails the check when something is set to null explicitly vs it just being null. The objects are technically equivalent wrt values at that point. We'll need tests for that as well.

  3. There's another issue which I haven't figured out yet but it's breaking tests at runtime. Will post when I know more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants