-
-
Notifications
You must be signed in to change notification settings - Fork 6.7k
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
[Java] Feature/wrapped types and default values #11666
base: master
Are you sure you want to change the base?
[Java] Feature/wrapped types and default values #11666
Conversation
} | ||
} | ||
if (useOptional) { | ||
if (Boolean.FALSE.equals(property.required) && Boolean.FALSE.equals(property.isNullable) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this misses a case:
useOptional: true
openApiNullable: false
private Optional<String> message;
private Optional<String> nullableMessage
private String requiredMessage;
private String requiredNullableMessage;
I think this would skip nullableMessage
because it's marked nullable (but the overall nullability feature is disabled)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I think this might still be a problem but I'm happy to iterate on this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean it should be:
a) Boolean.FALSE.equals(property.required) && Boolean.FALSE.equals(property.isNullable)
or
b) Boolean.FALSE.equals(property.required) && Boolean.FALSE.equals(openApiNullable)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes - but happy to follow up with that or merge into my PR dealing with Optionals in POJOs
} | ||
} | ||
} | ||
if (useOptional) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if optionals are enabled, it would be nice for non-nullable non-required maps/arrays to default to empty map/array instead of null
since an empty list/map has the same meanining as a null
one in that case.
modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache
Outdated
Show resolved
Hide resolved
private {{>nullableDataType}} {{name}}{{#isNullable}} = null{{/isNullable}}{{^isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/isNullable}}; | ||
{{/openApiNullable}} | ||
{{/isContainer}} | ||
private {{>dataType}} {{name}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth noting: This changes the default way that containers were working before (defaulted to null
if not required)
@welshm Thx for your review. I think all your findings could be addressed using this approach. Let's see how we can proceed with this. I would like to see you finishing the useOptional story. My main interest here is to avoid to further template clutter with more and more complicated logic - which adds more and more to the maintenance burden. |
I agree - this will make it much simpler to handle wrapped types and greatly simplify the mustache files |
Use Beanvalidation annotation in Optional gette retrun values
@welshm The last commit add the beanvalidation and setter adjustments. For the setter:
I personally would vote for
I there a technical requirement to use the Optional as a method Parameter? |
For the setter - it was a discussion between @MelleD and I, with a decision to be consistent with I don't think there is a technical requirement otherwise - but I did not test that setup with Jackson (I think it typically expected getters and setters to match for serialization - and you may need to use |
See latest commit. We could even introduce a flag - which I would like to avoid, but still possible. |
For me it make more sense getter and setter use optional (see chapter Optional everywhere) and I would not use null! setFoo(null) vs setFoo(Optional.empty()). If you like to use Optionals then you would like to avoid nulls. Additional there is a builder Methode where you can set a value directly without a Optional! |
I don't get it - you even introduce a new possibility to cause a NPE ... (the There even is a Sonar Rule against using Optinal as method Parameter: https://rules.sonarsource.com/java/RSPEC-3553 The Topic seems to be highly opinionated, so introducing an option might by the right thing even at the cost of an additional flag to support. it is already prepared in That said, I'll google Optional everywhere ... |
There is also a Sonar rule don’t use Optional for fields. There are a lot of different opinions around Optional. IMHO use Optional everywhere and you can get rid of null. And this is a good aim don‘t use null in your code!!! See here blog of Nicolai Parlog Another one: And here: Next advantage it looks and works like the JsonNullable Builder. Can’t understand that developer would like to use a type like Optional + would use null to create a empty optional !!! EDIT: Your sonar rule is not for setter! This is if you use the parameter like a Boolean and create a if else with a flag. Then it’s better to create two methods instead of a flag parameter |
IMO Optionals are not used to avoid null, but rather to ensure that null scenarios are properly accounted for. When using an Optional, it's easy to see how the null case is evaluated. Without an Optional, it may not be clear if null is a valid (or expected) value - and not handling the null case may be "correct". So, having said that, we opt to not use Optionals as parameters - and as return types only when null is a valid value. I think a flag for this makes sense. If we don't want to support a flag, I think it is easiest to have Optional be the input argument (easiest to be more strict and ease up than the other way around). |
Next try. Changes:
|
Therefore you need no Optionals just use Annotations https://www.baeldung.com/spring-null-safety-annotations So then another:
Java 8 Optional In Depth - Mkyong.comhttps://mkyong.com › java8 › java-8-optional-in-depth So normally you can completely avoid 'null' values in your code and avoid the billion dollar mistake ;). |
|
||
public AdditionalPropertiesAnyType name(String name) { | ||
this.name = name; | ||
this.name = Optional.ofNullable(name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be just Optional.of().
I like the spring boot way all parameter are non null by default and if you would like to use a null parameter you mark it with @nullable.
So i would use Optional.of and if you would like to reset the Optional you have to use the setter!
Let's be consistent with JavaCxfClient then. I don't think it's a big deal. The value is required so it should be set - if it's not set I am guessing Jackson will set it to This will likely fix the outstanding spring issue where nullable required fields did not need to be set. |
TODO: Unit-Test org.openapitools.codegen.languages.AbstractJavaCodegen#postProcessModelProperty |
This will likely fix the outstanding spring issue where nullable required fields did not need to be set.
|
https://openapi-generator.slack.com/archives/CLSB0U0R5/p1643615281300959 I'll see if I can find an actual issue and not just the Slack discussion... |
Looks good, I really like it. @cachescrubber Out of interest, why do you think it's good to reset with 'null'? |
I still see an issue with Jackson Mappings The default Jackson
Will be serialized to
Should we annotate the Optional fields with
@MelleD Some people might abuse ( WDYT? BTW, I created a demo project to test such issues in isolation. |
No, it's just following the principal of least surprise. Consider for example tools like Mapstruct, which are often used to map entities to model. |
IMO it's fine if it writes it out with an explicit If If People trying to abuse the optional are using the system in a way it was not intended (or supported) for |
No you have nothing todo. Everybody can setup the ObjectMapper how its needed. How I said for me it’s a big surprise to use a null as parameter value and it’s look like a bad design. I also use Mapstruct without nullable values |
Example this is our ObjectMapper
|
And if this is merged we can also close this |
cc @OpenAPITools/generator-core-team as well |
@cachescrubber just wanted to follow up here - anything else you think should go into this PR? Happy to contribute CLI option for using the wrapper in the setter ? |
@welshm The unit tests for the Implementation in AbstractJavaCodegen are still TODO. I think most important is having a go! from the @generator-core-team regarding the extension in the CodegenProperty. |
…:cachescrubber/openapi-generator into feature/wrapped_tyoes_and_default_values
Bringing this PR back up to master and updating - @wing328 what's the best way to seek approval to add to the CodegenProperty? I'll aim to have the tests done this week for |
This PR contains commits by the others (likely due to incorrect rebase). I would suggest you filing a new one by cherry-picking the commits authored by you. |
There are commits by me and @welshm - that would be alright I guess. Or are there other problems? |
I didn't squash the commits when I rebased. I can do that, shouldn't take too much time |
Sorry, this PR need some love.... In the meantime, java-micronaut has added support for Optional. Curious how they implemented it. The PR is #12144. |
Looks like they did what I did originally, which was just to wrap the types directly with Optional and not have a Their |
I also need to find time to actually do some work on this in the next week or two... |
Springboot and spring cloud 3 are having issues with HttpStatus (wrong version) - which I think is likely a misconfiguration of the POM but I am not familiar enough with using the milestone repo - I would guess it's a collisions with other dependencies |
POC - generic wrapper data type
Enhance CodegenProperty to support Wrapper Types
Wrapper Types are for Example
java.util.Optional
ororg.openapitools.jackson.nullable.JsonNullable
.By adding generic support for such types we avoid clumpsy and difficult to read templates - Mustache templates are advertised as Logic-less templates.. for a reason ...
PR checklist
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*
.For Windows users, please run the script in Git BASH.
master
(5.3.0),6.0.x