[java] Map unknown values of nullable enums to the unknown default case
With enumUnknownDefaultCase=true, fromValue returns UNKNOWN_DEFAULT_OPEN_API for an unknown value only when the enum is not nullable. For a nullable enum it returns null, so a value added to the API after the client was generated is read as "no value", and the caller can't tell it apart from an explicit null.
This PR makes fromValue of nullable enums return null for a null value and UNKNOWN_DEFAULT_OPEN_API for any other unknown value, as non-nullable enums already do. Without enumUnknownDefaultCase the generated code is unchanged.
The change is the same in each Java client template that has this fromValue: modelEnum, modelInnerEnum, libraries/native/modelEnum, libraries/okhttp-gson/modelEnum and modelInnerEnum, and libraries/microprofile/enumClass.
Spec
openapi: 3.0.3
info: { title: repro, version: 1.0.0 }
paths: {}
components:
schemas:
Mode:
type: string
nullable: true
enum: [auto, manual]
Pet:
type: object
properties:
kind: { type: string, nullable: true, enum: [cat, dog] }
mode: { $ref: '#/components/schemas/Mode' }
-g java --library jersey2 --additional-properties=enumUnknownDefaultCase=true
Before
public static Mode fromValue(String value) {
for (Mode b : Mode.values()) {
if (b.value.equals(value)) {
return b;
}
}
return null;
}
After
public static Mode fromValue(String value) {
for (Mode b : Mode.values()) {
if (b.value.equals(value)) {
return b;
}
}
if (value == null) {
return null;
}
return UNKNOWN_DEFAULT_OPEN_API;
}
Deserializing with Jackson (new ObjectMapper().readValue(json, Mode.class)):
| JSON | Before | After |
|---|---|---|
"auto" | AUTO | AUTO |
"scheduled" | null | UNKNOWN_DEFAULT_OPEN_API |
null | null | null |
Testing
./mvnw -DskipTests -pl modules/openapi-generator-cli -am package./bin/generate-samples.sh bin/configs/java-*.yaml(140 configs): no changes, because no Java sample config setsenumUnknownDefaultCase. For the spec above without the option, master and this branch generate identical code apart from the@Generatedtimestamps.- Added
JavaClientCodegenTest#testEnumUnknownDefaultCaseWithNullableEnums, covering a nullable inline enum and a nullable top-level enum. It fails without the template change and passes with it.JavaClientCodegenTest: 312 tests, 0 failures. - Compiled the generated
Mode.javawith Jackson 2.18 and ran the table above, before and after. Also checked the generatedfromValuefor theokhttp-gsonandnativelibraries.
PR checklist
- Read the contribution guidelines.
- Run the following to build the project and update samples:
(For Windows users, please run the script in WSL)./mvnw clean package || exit ./bin/generate-samples.sh ./bin/configs/*.yaml || exit ./bin/utils/export_docs_generators.sh || exit
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.
(Ran the build and regenerated alljava-*configs; nothing changed. Not all configs.) - 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 @KannaKim
Summary by cubic
Makes fromValue on nullable enums map unknown values to UNKNOWN_DEFAULT_OPEN_API when enumUnknownDefaultCase=true, instead of returning null.
- Previously, an unknown value in a nullable enum was read as "no value", indistinguishable from an explicit null.
- Now only a null input returns
null; any other unknown value returnsUNKNOWN_DEFAULT_OPEN_API. - Generated code is unchanged when
enumUnknownDefaultCaseis not set. - Applies to the Java templates containing this
fromValue: base, native, okhttp-gson, and microprofile. - MicroProfile's top-level enums also return
nullfor a null input rather than the unknown default case. - Adds
JavaClientCodegenTest#testEnumUnknownDefaultCaseWithNullableEnumscovering nullable inline and top-level enums across okhttp-gson, resttemplate, native, and microprofile.