[openapi-normalizer] fix: apply SIMPLIFY_BOOLEAN_ENUM to OpenAPI 3.1 specs
Fixes #25287
Related: #25290 fixes the same instanceof problem for SIMPLIFY_ANYOF_STRING_AND_ENUM_STRING.
SIMPLIFY_BOOLEAN_ENUM (on by default) only handled instanceof BooleanSchema. In OpenAPI 3.1 documents, swagger-parser represents boolean schemas as JsonSchema, so type: boolean, enum: [true] was never simplified. Generators then produced string enums, e.g. the rust client emits an enum serialized as "true", which fails to deserialize {"vaccinated": true}.
Changes:
- New
ModelUtils.isSingleTypeSchema(schema, type): true when the schema has exactly the given type (typein 3.0, a single entry intypesin 3.1). The same helper is added in #25288 and #25290, identical and in the same place, so the two PRs merge cleanly in either order. processSimplifyBooleanEnumusesisSingleTypeSchema(schema, "boolean") && isEnumSchema(schema)instead ofinstanceof BooleanSchema. Multi-type 3.1 schemas such astype: [boolean, string]keep their enum, because there it applies to the whole union.normalizeSimpleSchema, the path 3.1JsonSchemainstances take, now callsprocessSimplifyBooleanEnumafterprocessNormalize31Spec. This coverstype: [boolean, "null"]andconst: true(whichNORMALIZE_31SPECturns intoenum: [true]).
Not covered here: ADD_UNSIGNED_TO_INTEGER_WITH_INVALID_MAX_VALUE relies on instanceof IntegerSchema in the same way (both in the normalizeSchema dispatch and in the rule), so it probably also does nothing for 3.1 specs. SIMPLIFY_ANYOF_STRING_AND_ENUM_STRING has the same pattern (instanceof StringSchema) and is addressed in a separate PR. I kept this PR to the one rule.
Testing:
- New
OpenAPINormalizerTest#testOpenAPINormalizerSimplifyBooleanEnum31Specwithsrc/test/resources/3_1/simplifyBooleanEnum_test.yaml. It coversenum: [true],type: [boolean, "null"]with an enum, andconst: true. It also checks that a string enum and a multi-typetype: [boolean, string]enum are left alone. The test fails on master and passes with this change. - New
ModelUtilsTest#testIsSingleTypeSchema. OpenAPINormalizerTest,DefaultCodegenTest,DefaultGeneratorTest,ModelUtilsTestandRustClientCodegenTestall pass.- Regenerating all samples changes no files, since no existing 3.1 sample spec has a boolean enum.
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./bin/generate-samples.sh ./bin/configs/*.yaml(all configs); no sample files changed.export_docs_generators.shwas not needed because no generator options changed. - If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.
This is a core normalizer fix; it was found through the rust generators.
cc @frol @farcaller @richardwhiuk @paladinzh @jacob-pro @dsteeley
Summary by cubic
Fixes SIMPLIFY_BOOLEAN_ENUM so it applies to OpenAPI 3.1 specs, where boolean schemas are parsed as JsonSchema instead of BooleanSchema. Previously, boolean enums like type: boolean, enum: [true] were left untouched, and generators produced string enums (e.g., the rust client serialized "true"), which failed to deserialize booleans.
Bug Fixes
- Uses
ModelUtils.isSingleTypeSchemainstead ofinstanceof BooleanSchema, and only simplifies single-type boolean schemas. - Runs
processSimplifyBooleanEnumfromnormalizeSimpleSchema, the path 3.1JsonSchemainstances take, coveringtype: [boolean, "null"]andconst: true(afterNORMALIZE_31SPECturnsconstintoenum). - Leaves multi-type schemas like
type: [boolean, string]untouched, and addstestOpenAPINormalizerSimplifyBooleanEnum31Speccovering all cases.