Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,7 @@ protected Schema processSimplifyOneOfEnum(Schema schema) {
*/
protected Schema simplifyComposedSchemaWithEnums(Schema schema, List<Object> subSchemas, String composedType) {
Map<Object, String> enumValues = new LinkedHashMap<>();
Map<Object, Boolean> deprecatedValues = new LinkedHashMap<>();

if(schema.getTypes() != null && schema.getTypes().size() > 1) {
// we cannot handle enums with multiple types
Expand All @@ -1620,10 +1621,15 @@ protected Schema simplifyComposedSchemaWithEnums(Schema schema, List<Object> sub

Schema subSchema = ModelUtils.getReferencedSchema(openAPI, (Schema) item);

// Check if this sub-schema has an enum (with one or more values)
if (subSchema.getEnum() == null || subSchema.getEnum().isEmpty()) {
// Check if this sub-schema has an enum or const value (OAS 3.1 uses const for single-value enums)
boolean definesEnum = ModelUtils.hasEnum(subSchema);
if (!definesEnum && subSchema.getConst() == null) {
return schema;
}
// If const is present but enum is not, treat const as a single enum value
List<Object> subSchemaEnumValues = definesEnum
? subSchema.getEnum()
: Arrays.asList(subSchema.getConst());

// Ensure all sub-schemas have the same type (if type is specified)
if(subSchema.getTypes() != null && subSchema.getTypes().size() > 1) {
Expand All @@ -1638,25 +1644,28 @@ protected Schema simplifyComposedSchemaWithEnums(Schema schema, List<Object> sub
return schema;
}
}
boolean subSchemaDeprecated = Boolean.TRUE.equals(subSchema.getDeprecated());
// Add all enum values from this sub-schema to our collection
if(subSchema.getEnum().size() == 1) {
if(subSchemaEnumValues.size() == 1) {
String description = subSchema.getTitle() == null ? "" : subSchema.getTitle();
if(subSchema.getDescription() != null) {
if(!description.isEmpty()) {
description += " - ";
}
description += subSchema.getDescription();
}
enumValues.put(subSchema.getEnum().get(0), description);
enumValues.put(subSchemaEnumValues.get(0), description);
deprecatedValues.put(subSchemaEnumValues.get(0), subSchemaDeprecated);
} else {
for(Object e: subSchema.getEnum()) {
for(Object e: subSchemaEnumValues) {
enumValues.put(e, "");
deprecatedValues.put(e, subSchemaDeprecated);
}
}

}

return createSimplifiedEnumSchema(schema, enumValues, schemaType, composedType);
return createSimplifiedEnumSchema(schema, enumValues, deprecatedValues, schemaType, composedType);
}


Expand All @@ -1665,11 +1674,12 @@ protected Schema simplifyComposedSchemaWithEnums(Schema schema, List<Object> sub
*
* @param originalSchema Original schema to modify
* @param enumValues Collected enum values
* @param deprecatedValues Per-value deprecated flags (aligned with enumValues key order)
* @param schemaType Consistent type across sub-schemas
* @param composedType Type of composed schema being simplified
* @return Simplified enum schema
*/
protected Schema createSimplifiedEnumSchema(Schema originalSchema, Map<Object, String> enumValues, String schemaType, String composedType) {
protected Schema createSimplifiedEnumSchema(Schema originalSchema, Map<Object, String> enumValues, Map<Object, Boolean> deprecatedValues, String schemaType, String composedType) {
// Clear the composed schema type
if ("oneOf".equals(composedType)) {
originalSchema.setOneOf(null);
Expand All @@ -1687,6 +1697,10 @@ protected Schema createSimplifiedEnumSchema(Schema originalSchema, Map<Object, S
//set x-enum-descriptions only if there's at least one non-empty description
originalSchema.addExtension(X_ENUM_DESCRIPTIONS, new ArrayList<>(enumValues.values()));
}
if (deprecatedValues != null && deprecatedValues.values().stream().anyMatch(Boolean.TRUE::equals)) {
// preserve per-value deprecated flags from OAS 3.1 oneOf/anyOf + const sub-schemas
originalSchema.addExtension("x-enum-deprecated", new ArrayList<>(deprecatedValues.values()));
}

LOGGER.debug("Simplified {} with enum sub-schemas to single enum: {}", composedType, originalSchema);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1536,9 +1536,16 @@ public void testOpenAPINormalizerSimplifyOneOfAnyOf31Spec() {
assertEquals(schema14.getType(), null);

Schema schema16 = openAPI.getComponents().getSchemas().get("TypeIntegerWithOneOf");
assertEquals(schema16.getOneOf().size(),3);
assertEquals(((Schema) schema16.getOneOf().get(0)).getConst(), 1);
assertEquals(((Schema) schema16.getOneOf().get(0)).getDeprecated(), true);
// After normalization, oneOf with const values should be simplified to enum
assertEquals(schema16.getOneOf(), null);
assertEquals(schema16.getEnum().size(), 3);
assertEquals(schema16.getEnum().get(0), 1);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

(I'm not repo maintainer but the PR is something I was thinking about myself: so thank you)

The test against deprecated (https://github.com/nagabalaji-b/openapi-generator/blob/3496c3b7cffbc0cdc728cc77bd64708b8ba6ae9a/modules/openapi-generator/src/test/resources/3_1/simplifyOneOfAnyOf_test.yaml#L133) is lost here (and the information from schema too)

Here, seems it is not possible with current enum support implementation ("simple" list of values and optional list of descriptions with x-enum-descriptions) to support enum as "schema" enabling to keep all "schemas" related information with no loss.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@nagabalaji-b can you please take a look and add back the deprecated test?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@wing328 Done. I added back the deprecated test coverage:

Code change: Modified simplifyComposedSchemaWithEnums() to collect and preserve per-value deprecated flags from OAS 3.1 oneOf/anyOf + const sub-schemas into x-enum-deprecated extension.

Test assertion: Added explicit checks in testOpenAPINormalizerSimplifyOneOfAnyOf31Spec() for TypeIntegerWithOneOf schema:

Verifies x-enum-deprecated list size = 3
Verifies flags match expected values: [true, false, false] (first enum value was deprecated in the original YAML)
Validation: Ran the test locally and confirmed BUILD SUCCESS.

The deprecated metadata from the annotated enum pattern (oneOf + const) is now preserved through normalization, so generators can access it via the x-enum-deprecated extension.

// per-value deprecated flags from oneOf sub-schemas should be preserved as x-enum-deprecated
List<Boolean> enumDeprecated = (List<Boolean>) schema16.getExtensions().get("x-enum-deprecated");
assertEquals(enumDeprecated.size(), 3);
assertEquals(enumDeprecated.get(0), Boolean.TRUE);
assertEquals(enumDeprecated.get(1), Boolean.FALSE);
assertEquals(enumDeprecated.get(2), Boolean.FALSE);

Schema schema18 = openAPI.getComponents().getSchemas().get("OneOfNullAndRef3");
// original oneOf removed and simplified to just $ref (oneOf sub-schema) instead
Expand Down