Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 10 additions & 0 deletions bin/configs/csharp-generichost-latest-annotatedEnum.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# for csharp generichost - OAS 3.1 annotated enum with const + deprecated
generatorName: csharp
outputDir: samples/client/petstore/csharp/generichost/latest/AnnotatedEnum
inputSpec: modules/openapi-generator/src/test/resources/3_1/simplifyOneOfAnyOf_test.yaml
templateDir: modules/openapi-generator/src/main/resources/csharp
additionalProperties:
packageGuid: '{A2B4C6D8-1234-5678-9ABC-DEF012345678}'
modelPropertySorting: alphabetical
operationParameterSorting: alphabetical
validateSpec: false
Original file line number Diff line number Diff line change
Expand Up @@ -1614,6 +1614,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 @@ -1634,10 +1635,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 @@ -1652,25 +1658,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 @@ -1679,11 +1688,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 @@ -1701,6 +1711,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 @@ -1568,9 +1568,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
Loading