Skip to content

Add original enumPropertyNamingType config for java enum generation #20824

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

Merged
merged 1 commit into from
Apr 27, 2025

Conversation

sd-f
Copy link
Contributor

@sd-f sd-f commented Mar 7, 2025

New config property "original" for java Enum generation. If setting enumPropertyNaming to "original" and enum values in openapi match regex [A-Za-z0-9_]+ then those exact values will be used as java enum type name.

Attention: This only covers a few use cases mentioned in other issues and might not comply with all java conventions. Please review.

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 || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in Git BASH)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • File the PR against the correct branch: master (upcoming 7.x.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 members, so they are more likely to review the pull request.

@bbdouglas @sreeshas @jfiala @lukoyanov @cbornet @jeff9finger @karismann @Zomzog @lwlee2608 @martin-mfg

@jpfinne
Copy link
Contributor

jpfinne commented Mar 8, 2025

Underscore should be in the regex, no?

@sd-f
Copy link
Contributor Author

sd-f commented Mar 8, 2025

Underscore should be in the regex, no?

It is not relevant in my usecase, but i've seen it in some other issues in other languages.

I could not find anything specific in the guides except https://google.github.io/styleguide/javaguide.html#s5.1-identifier-names where underscores are mentioned as allowed but not common (special cases only).

https://google.github.io/styleguide/javaguide.html#s5.1-identifier-names does not mention anything about underscores.

Shall I remove it? thx

@sd-f
Copy link
Contributor Author

sd-f commented Mar 8, 2025

Underscore should be in the regex, no?

Sorry maybe I misunderstood your question. Underscore is indeed in the regex in den code, just forgot it in the description. Fixed that.

@wing328
Copy link
Member

wing328 commented Mar 8, 2025

in the generators supporting this option, the original means using whatever values defined in the spec if i remember correctly

would that meet your requirement?

what exact issue(s) your PR aim to fix?

@jpfinne
Copy link
Contributor

jpfinne commented Mar 8, 2025

Instead of a regex, use this:

import javax.lang.model.SourceVersion;

boolean isValidVariableNameInVersion(CharSequence name, SourceVersion version) {
    return SourceVersion.isIdentifier(name) && !SourceVersion.isKeyword(name, version);
}

@sd-f
Copy link
Contributor Author

sd-f commented Mar 8, 2025

Thanks, if I use @jpfinne suggestion it should exactly do that @wing328

@sd-f sd-f force-pushed the java-original-enum-type-naming branch from 99b6991 to 61d8ac2 Compare March 8, 2025 10:54
@sd-f
Copy link
Contributor Author

sd-f commented Mar 8, 2025

Might be related to #16478 #8184 #3098 and others in other languages, but this fix is for java only ;)

@sd-f
Copy link
Contributor Author

sd-f commented Mar 10, 2025

Instead of a regex, use this:

import javax.lang.model.SourceVersion;

boolean isValidVariableNameInVersion(CharSequence name, SourceVersion version) {
    return SourceVersion.isIdentifier(name) && !SourceVersion.isKeyword(name, version);
}

Improvement done.

Copy link
Contributor

@martin-mfg martin-mfg left a comment

Choose a reason for hiding this comment

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

The implementation looks good to me.

I am just wondering what your use case is. Why do you need/prefer using the new 'original' setting?

@jpfinne
Copy link
Contributor

jpfinne commented Apr 6, 2025

The implementation looks good to me.

I am just wondering what your use case is. Why do you need/prefer using the new 'original' setting?

So we can use the standard method name() instead of getValue() to retrieve the value of the enum.

@sd-f
Copy link
Contributor Author

sd-f commented Apr 7, 2025

The implementation looks good to me.

I am just wondering what your use case is. Why do you need/prefer using the new 'original' setting?

@martin-mfg

I wish I would not need this feature, but I am generating clients for apis that already exist and they have some weird (but valid in openapi spec) enum values. With the "original" generation I can use the enum as is, else I would need to define a custom type mapping.

@martin-mfg
Copy link
Contributor

martin-mfg commented Apr 7, 2025

I wish I would not need this feature, but I am generating clients for apis that already exist and they have some weird (but valid in openapi spec) enum values.

This part I totally understand.

With the "original" generation I can use the enum as is, else I would need to define a custom type mapping.

I think the custom mapping exists already, see fromValue and getValue. This is what you need, right?

So we can use the standard method name() instead of getValue() to retrieve the value of the enum.

For reference, the name() method is defined here.

What's the benefit of using name() over getValue()? That it's more widely known because it's a Java standard method? The trade-off is that using name() instead of getValue() will give unexpected results (i.e. not the exact string defined in openAPI spec) if the openAPI enum name can't be converted to a Java enum name. getValue() doesn't have this problem.

(I am not against these changes, I just don't really understand the reason yet.)

@sd-f
Copy link
Contributor Author

sd-f commented Apr 7, 2025

@martin-mfg I think my problem comes from when original names get manipulated and a value is added to the enum:

This is my api definition:

    MyType:
      enum:
      - "OK"
      - "Test"

Generated Enum class is:

public enum MyType {
  OK("OK"),
  TEST("Test")
}

I am completely aware that this is way more a real java enum as I would also define it myself.

But what i expect when with original naming is:

public enum MyType {
  OK,
  Test
}

Since when using this type in any interface again they will not match "Test" != "TEST". Retrieving from API and returning the same type will generate a different API specification. Btw. I am using jaxrs jackson on quarkus.

@martin-mfg
Copy link
Contributor

Since when using this type in any interface again they will not match "Test" != "TEST"

If you need the String "Test" instead of "TEST" in your custom code, you can call MyType.TEST.value().

But given that you're using jaxrs, I guess you want to use jackson and it currently can't handle your enums? Then the preferred way to fix this would be to introduce the annotations @JsonValue and @JsonCreator. The links in my last comment show these annotations being generated for e.g. generator=java,library=reasteasy. I suggest you modify the jaxrs generator to generate these annotations too.

@sd-f
Copy link
Contributor Author

sd-f commented Apr 8, 2025

Since when using this type in any interface again they will not match "Test" != "TEST"

If you need the String "Test" instead of "TEST" in your custom code, you can call MyType.TEST.value().

But given that you're using jaxrs, I guess you want to use jackson and it currently can't handle your enums? Then the preferred way to fix this would be to introduce the annotations @JsonValue and @JsonCreator. The links in my last comment show these annotations being generated for e.g. generator=java,library=reasteasy. I suggest you modify the jaxrs generator to generate these annotations too.

Hi @martin-mfg , thanks for all that Tips, i am aware of these possible workarounds. Guess i am just lazy and wanted to return the Class as it is generated in my own interfaces. At the moment i am simply working around with a import mapping that works great.

Edit: I am using jaxrs-spec generator to keep it a little 3rd party lib agnostic ;)

https://gitlab.campusonline.community/community/tuinx/-/blob/main/pom.xml?ref_type=heads#L624

@martin-mfg
Copy link
Contributor

At the moment i am simply working around with a import mapping

clever :)

https://gitlab.campusonline.community/community/tuinx/-/blob/main/pom.xml?ref_type=heads#L624

I see that you basically have the PersonalStatusType twice. I recommend not implementing this enum again on your own when openapi-generator already generated it for you. Just use the generated version everywhere. Here's a quick demo how to make this change in your project: https://gist.github.com/martin-mfg/26f90d4b4e4392846b8416cc0d8efe4b

@sd-f
Copy link
Contributor Author

sd-f commented Apr 9, 2025

At the moment i am simply working around with a import mapping

clever :)

https://gitlab.campusonline.community/community/tuinx/-/blob/main/pom.xml?ref_type=heads#L624

I see that you basically have the PersonalStatusType twice. I recommend not implementing this enum again on your own when openapi-generator already generated it for you. Just use the generated version everywhere. Here's a quick demo how to make this change in your project: https://gist.github.com/martin-mfg/26f90d4b4e4392846b8416cc0d8efe4b

@martin-mfg Thank you very much for looking in that in detail. But when I use the generated type the exact thing is happening i mentioned above. The API ist not the same anymore Test !== TEST. But that would be exactly what i would want to achieve with the "original" generation option i suggested in this pull request. With the import Mapping, the real generated Type gets replaced, that is why you see the correct Enum. When the mapping is removed the Enum would be:

// generated type
public enum PersonalStatusType {
  OK("OK"),
  TEST("Test")
 // ...
}

Sorry if this is getting out of hand ;) I could totally work with any of the workarounds.

@martin-mfg
Copy link
Contributor

I could totally work with any of the workarounds.

It seems we're on the same page then. In summary - especially for @wing328 who should be the one making a decision here:

@jpfinne
Copy link
Contributor

jpfinne commented Apr 10, 2025

I could totally work with any of the workarounds.

It seems we're on the same page then. In summary - especially for @wing328 who should be the one making a decision here:

@JsonValue and @JsonCreator are nice for json.
Sometimes I want to use the generated enum for other purposes. For example JPA persistence of an entity containing that enum.
@Enumerated(EnumType.STRING) should be enough if name() gives the origin value. The current workaround is to write a jpa AttributeConverter

Same if I use MapStruct, custom configuration needs to be added.

@martin-mfg
Copy link
Contributor

Good point!

Now I am wondering why there are not more issues and PRs addressing JPA. :D

@wing328 wing328 added this to the 7.13.0 milestone Apr 27, 2025
@wing328 wing328 merged commit f656afc into OpenAPITools:master Apr 27, 2025
15 checks passed
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.

4 participants