openapi-generator: [BUG][Java][Spring] openapiNullable - JsonNullable usage is based on nullable property instead of required property
Bug Report Checklist
- Have you provided a full/minimal spec to reproduce the issue?
- Have you validated the input using an OpenAPI validator (example)?
- Have you tested with the latest master to confirm the issue still exists?
- Have you searched for related issues/PRs?
- What’s the actual output vs expected output?
- [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
In Java CodeGeneration openapiNullable is used to determine if the special type JsonNullable should be used or not.
The description of this type from the original documentation is as follows:
The JsonNullable wrapper shall be used to wrap Java bean fields for which it is important to distinguish between an explicit “null” and the field not being present. A typical usage is when implementing Json Merge Patch where an explicit "null"has the meaning “set this field to null / remove this field” whereas a non-present field has the meaning “don’t change the value of this field”.
So, in a request body (especially in PATCH requests) it is utilized to signalize, if a specifc field was specified in the body or not. This can be used to just ignore any change to the property and keep the old value or to apply a new value to it.
BUT. The current implementation is using the nullable property of a field instead of the required property. So nullable should actually tell about if a field can be null or not IF it is specified. nullable tells nothing about if a field must be present or can be omitted. This is what required is for. required declares, if a field as to be present or not, independent of if it may be nullable or not.
The fix would be to swap nullable and required for the usage of JsonNullable.
openapi-generator version
6.4.0, but goes back until 5.6.0 minimum (probably since openapiNullable was implemented first).
OpenAPI declaration file content or url
Existing /3_0/petstore-with-nullable-required.yaml can be used as an example
Taking the following Pet model from the referency specification:
Pet:
title: a Pet
description: A pet for sale in the pet store
type: object
required:
- name
- photoUrls
properties:
id:
type: integer
format: int64
category:
$ref: '#/components/schemas/Category'
name:
type: string
example: doggie
nullable: true
photoUrls:
type: array
xml:
name: photoUrl
wrapped: true
items:
type: string
The Pet class would be generated like:
public class Pet {
@JsonProperty("id")
private Long id;
@JsonProperty("category")
private Category category;
@JsonProperty("name")
private String name;
@JsonProperty("photoUrls")
@Valid
private List<String> photoUrls = new ArrayList<>();
}
Although, because only name and photoUrls are declared as required, all other properties should be wrapped in JsonNullable and name should be initialized with null as it is declared as nullable additionally.
public class Pet {
@JsonProperty("id")
private JsonNullable<Long> id = JsonNullable.undefined();
@JsonProperty("category")
private JsonNullable<Category> category = JsonNullable.undefined();
@JsonProperty("name")
private String name = null;
@JsonProperty("photoUrls")
@Valid
private List<String> photoUrls = new ArrayList<>();
}
Generation Details
./bin/generate-samples.sh bin/configs/spring-openapi-nullable-required.yaml
Steps to reproduce
I already created a pull request: #14766
Related issues/PRs
Didn’t find any existing issue to this (which is quite suprising).
Suggest a fix
I already created a pull request: #14766
I am totally aware that this is a huge breaking change so I would like to discuss this issue openly. Maybe we need some kind of flag for the new behaviour. Any critics to this behaviour are welcome.
About this issue
- Original URL
- State: open
- Created a year ago
- Reactions: 4
- Comments: 36 (22 by maintainers)
@daberni in general there are 4 possible scenarion for
nullable+requirednull. No need to addJsonNullablenullwith the meaning “let’s set value (ex. in DB) explicitly tonull” c) can be absent which mean “do nothing with this field”nulldoes not allowed. No need to useJsonNullableb)does not applicable here since can not be set tonull. No need to useJsonNullableSo based on all these cases only for point 2 we can use
JsonNullabl, because that’s the only case wherevalid value,null&undefined(not present) can happen@gnuemacscoin When the property is null in the source object, it means that it should be set to null. When it is not present, than it should be not modified. That is why JsonNullable is used. To provide isPresent() function and distinguish the third state.
@welshm Interesting approach, I didn’t see it this way so far but I understand the idea.
Probably this would be a somehow working approach, the problem is, that it is not implemented this way right now.
Currently, usage of
JsonNullableis only determined by the definition ofnullable. You can find this in the nullableDataType.mustache template. As far as I can see, there is no further differentiation.If this would be combined with
requiredthis might be one way.But additionally, which is quite confusing, the beanValidation.mustache depends on
requiredinstead ofnullable.So that this would work as you described, one would have to combine this together with
openapiNullabletoo.One problem which currently also arises, probably because of the combination of the above incomplete implementations, that Spring Model Binding Validation doesn’t work properly either.
@NotNullannotations are missing onnullable=falseannotations, etc.Also after generating the code it is no longer clear which combination of flags resulted in the current code. So as a developer I would have to write different validation and mapping logic depending on required/nullable attributes, instead of coupling my mapping logic to the present types directly, which would be way more straight forward.
I see that optimizing away the one case
required=false & nullable=falsewhere theoreticallyJsonNullablecould be determined by if the value isnullor not might be an idea. But this kinda doesn’t work with real life scenarios, where only because the openapi-spec tells it this way, clients of the API wouldn’t sentnullalthough it is definednullable=false. So as a developer one must still be able to determine, if a client did sendnullfor anullable=false & required=falseproperty to throw proper errors. This is currently not possible and the source of why we started to discuss this topic.For better understanding, It would be more verbose to couple
JsonNullableto therequiredattribute, and@NotNullto thenullableattribute.Just stumbled around an additional mustache template
>beanValidationwhererequiredandisNullableare also mixed up.