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

[typescript] fix: nullable enums should not serialize a null value to a string #19540

Merged
merged 4 commits into from
Sep 24, 2024

Conversation

joscha
Copy link
Contributor

@joscha joscha commented Sep 6, 2024

nullable enums with a value of null (e.g. ['a', 'b', null]) currently receive an additional (incorrect) value Null = 'null'.

This issue was introduced in #19027
Sample is here: #19027 (comment)

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project and update samples:
    ./mvnw clean package 
    ./bin/generate-samples.sh ./bin/configs/*.yaml
    ./bin/utils/export_docs_generators.sh
    
  • File the PR against the correct branch: master (upcoming 7.6.0 minor release - breaking changes with fallbacks), 8.0.x (breaking changes without fallbacks)
  • If your PR is targeting a particular programming language, @mention the [technical committee] @TiFu (2017/07) @taxpon (2017/07) @sebastianhaas (2017/07) @kenisteward (2017/07) @Vrolijkx (2017/09) @macjohnny (2018/01) @topce (2018/10) @akehir (2019/07) @petejohansonxo (2019/11) @amakhrov (2020/02) @davidgamero (2022/03) @mkusaka (2022/04)

@@ -3947,7 +3947,9 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo
List<Object> _enum = p.getEnum();
property._enum = new ArrayList<>();
for (Object i : _enum) {
property._enum.add(String.valueOf(i));
if(i != null) {
Copy link
Contributor Author

@joscha joscha Sep 6, 2024

Choose a reason for hiding this comment

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

I believe this can be rolled back, as isValueNull is sufficient.
The current model before this change is:

      "_enum" : [ "affinity-data", "dealroom", null ],
      "allowableValues" : {
        "enumVars" : [ {
          "name" : "AffinityData",
          "isString" : false,
          "value" : "'affinity-data'"
        }, {
          "name" : "Dealroom",
          "isString" : false,
          "value" : "'dealroom'"
        }, {
          "name" : "Null",
          "isString" : false,
          "value" : "'null'"
        } ],
        "values" : [ "affinity-data", "dealroom", null ]
      },

and after:

      "_enum" : [ "affinity-data", "dealroom" ],
      "allowableValues" : {
        "enumVars" : [ {
          "name" : "AffinityData",
          "isString" : false,
          "isNullValue": false,
          "value" : "'affinity-data'"
        }, {
          "name" : "Dealroom",
          "isString" : false,
          "isNullValue": false,
          "value" : "'dealroom'"
        }, {
          "name" : "Null",
          "isString" : false,
          "isNullValue": true,
          "value" : "'null'"
        } ],
        "values" : [ "affinity-data", "dealroom", null ]
      },

you can see there is an oddity in allowableValues, where the value is already serialized to 'null', however, #19027 didn't change this I believe, as it only touched modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java not the DefaultCodegen.

The 'null' value is set here:

enumVar.put("value", toEnumValue(String.valueOf(value), dataType));

maybe the surrounding loop:

needs the guard as well and we just remove the value from the possible enum values altogether? WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

@wing328 whats the core guidance here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am just trying to guard the second part:

diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java
index f27e4a400e4..9215daee503 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java
@@ -6630,6 +6630,9 @@ public class DefaultCodegen implements CodegenConfig {
                 : 0;
 
         for (Object value : values) {
+            if(value == null) {
+                continue;
+            }
             Map<String, Object> enumVar = new HashMap<>();
             String enumName = truncateIdx == 0
                     ? String.valueOf(value)

and see how it will affect the samples. My guess is that it might be the better solution, as we don't introduce another template variable, keep the model more sane and it will fix all future generators as well.

Copy link
Member

Choose a reason for hiding this comment

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

yes that would be nice, and it would work for all generators without modifying the templates.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I will make the change later.

Just OOI: is there a better way to crate fixtures for a very specific part of the generation? It feels like quite the overkill to generate a whole scaffold with 10+ files just to assert one tiny part of it.
For the model change in this PR I could write a unit test, but generally, is there a way to create a partial scaffold in any way?

Copy link
Contributor Author

@joscha joscha Sep 6, 2024

Choose a reason for hiding this comment

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

just pushed 01209d5 with the changes; seems cleaner (have a look at the diff)?

Copy link
Member

Choose a reason for hiding this comment

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

looks good, thanks!

Copy link
Member

Choose a reason for hiding this comment

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

Just OOI: is there a better way to crate fixtures for a very specific part of the generation? It feels like quite the overkill to generate a whole scaffold with 10+ files just to assert one tiny part of it.
For the model change in this PR I could write a unit test, but generally, is there a way to create a partial scaffold in any way?

yeah unit tests are the more compact/preferred option for these specific aspects

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah unit tests are the more compact/preferred option for these specific aspects

I can change this PR to use unit tests if/when @wing328 approves and we merge it.

@@ -384,10 +384,12 @@ public void enumArrayModelTest() {
fish.put("name", "Fish");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this test seems to be identical to modules/openapi-generator/src/test/java/org/openapitools/codegen/php/PhpModelTest.java - it seems maybe these files / tests were duplicated at some point. Might be worth moving this up onto a higher level and remove the duplication?

enum:
- "affinity-data"
- "dealroom"
- 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.

this is the line of the fixture that shows the issue

/**
* The source of the data in this Field (if it is enriched)
*/
'enrichmentSource'?: ResponseEnrichmentSourceEnum | 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.

nullability (e.g. the possible value null) is declared here already.


export enum ResponseEnrichmentSourceEnum {
AffinityData = 'affinity-data',
Dealroom = 'dealroom'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Before the change in this PR, there is an additional line

Null = 'null'

@joscha joscha changed the title fix: nullable enums should not serialize a null value to a string [typescript] fix: nullable enums should not serialize a null value to a string Sep 6, 2024
@joscha
Copy link
Contributor Author

joscha commented Sep 17, 2024

@macjohnny @wing328 is there any more changes needed for this one to land, please?

@macjohnny
Copy link
Member

we just need approval from the core team

@joscha
Copy link
Contributor Author

joscha commented Sep 24, 2024

we just need approval from the core team

@wing328 could I get your 👀 on this one, please?

@joscha
Copy link
Contributor Author

joscha commented Sep 24, 2024

🎉 all green @wing328

@@ -0,0 +1,4 @@
generatorName: typescript
outputDir: samples/openapi3/client/petstore/typescript/builds/nullable-enum
Copy link
Member

Choose a reason for hiding this comment

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

for new TS sample folder, do we need to add it to the Circle CI script to ensure the output compiles without issues?

https://github.com/OpenAPITools/openapi-generator/blob/master/CI/circle_parallel.sh#L76

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess, yes, would it make sense to determine this:

(cd samples/client/others/typescript-angular && mvn integration-test)
(cd samples/client/petstore/typescript-angular-v12-provided-in-root && mvn integration-test)
(cd samples/client/petstore/typescript-angular-v13-provided-in-root && mvn integration-test)
(cd samples/client/petstore/typescript-angular-v14-provided-in-root && mvn integration-test)
(cd samples/client/petstore/typescript-angular-v15-provided-in-root && mvn integration-test)
(cd samples/client/petstore/typescript-angular-v16-provided-in-root && mvn integration-test)
(cd samples/client/petstore/typescript-angular-v17-provided-in-root && mvn integration-test)
(cd samples/client/petstore/typescript-angular-v18-provided-in-root && mvn integration-test)
(cd samples/openapi3/client/petstore/typescript/builds/default && mvn integration-test)
(cd samples/openapi3/client/petstore/typescript/tests/default && mvn integration-test)
(cd samples/openapi3/client/petstore/typescript/builds/jquery && mvn integration-test)
(cd samples/openapi3/client/petstore/typescript/tests/jquery && mvn integration-test)
(cd samples/openapi3/client/petstore/typescript/builds/object_params && mvn integration-test)
(cd samples/openapi3/client/petstore/typescript/tests/object_params && mvn integration-test)
(cd samples/openapi3/client/petstore/typescript/builds/inversify && mvn integration-test)
(cd samples/openapi3/client/petstore/typescript/tests/inversify && mvn integration-test)
#(cd samples/openapi3/client/petstore/typescript/tests/deno && mvn integration-test)
(cd samples/openapi3/client/petstore/typescript/builds/browser && mvn integration-test)
(cd samples/openapi3/client/petstore/typescript/tests/browser && mvn integration-test)
(cd samples/client/petstore/typescript-fetch/builds/default && mvn integration-test)
(cd samples/client/petstore/typescript-fetch/builds/es6-target && mvn integration-test)
(cd samples/client/petstore/typescript-fetch/builds/with-npm-version && mvn integration-test)
(cd samples/client/petstore/typescript-fetch/tests/default && mvn integration-test)
(cd samples/client/petstore/typescript-node/npm && mvn integration-test)
(cd samples/client/petstore/typescript-rxjs/builds/with-npm-version && mvn integration-test)
(cd samples/client/petstore/typescript-axios/builds/with-npm-version && mvn integration-test)
(cd samples/client/petstore/typescript-axios/tests/default && mvn integration-test)
(cd samples/client/petstore/javascript-flowtyped && mvn integration-test)
(cd samples/client/petstore/javascript-es6 && mvn integration-test)
(cd samples/client/petstore/javascript-promise-es6 && mvn integration-test)

automatically?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added bbe6d34 for now

Copy link
Member

Choose a reason for hiding this comment

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

not against automating anything

maybe we should take this opportunity to move some tests (which don't require a local petstore server) to github workflows instead

cc @macjohnny

@wing328 wing328 merged commit 17e0b7c into OpenAPITools:master Sep 24, 2024
15 checks passed
@joscha joscha deleted the joscha/fix-nullable-enums-value branch September 24, 2024 15:57
@wing328 wing328 added this to the 7.9.0 milestone Oct 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants