Skip to content

Commit 2e78e17

Browse files
nagabalaji-bsagadiravvb
authored
fix(normalizer): support OAS 3.1 annotated enums (oneOf + const) for consistent enum generation (C#) (OpenAPITools#23869)
* fix(csharp): use numeric .NET version check to prevent double URL-encoding on .NET 9+ On .NET 9+, HttpUtility.ParseQueryString() already URL-encodes values internally. The previous fix only checked for '.NET 9' literally, missing .NET 10+ (PowerShell 7.6). Replace the string prefix check with a numeric major version comparison (>= 9) and move the RuntimeInformation check outside the foreach loop to avoid redundant parsing. Fixes: CSCwu08056 * Introducing Annotated Enum with const * fix: support OpenAPI 3.1 const in annotated enum normalization * fix(docs): use forward slashes in path separators for cross-platform portability * revert: remove unrelated doc file changes from PR scope * refactor(normalizer): extract definesEnum variable per reviewer suggestion * fix(normalizer): preserve x-enum-deprecated for oneOf/anyOf const enums * chore: regenerate samples after normalization improvements * chore(samples): normalize generated samples for cross-platform output * adding samples * chore(csharp): sync HttpSigningConfiguration.mustache from upstream/master * Revert "adding samples" This reverts commit 6ef74e7. * add annotated enum csharp config and regenerate samples * Revert "add annotated enum csharp config and regenerate samples" This reverts commit 5e4b895. * add csharp generichost annotated enum config * regenerate samples for csharp generichost annotated enum * ci(csharp): add AnnotatedEnum sample to .NET 10 workflow * chore(samples): regenerate AnnotatedEnum after upstream merge * chore(samples): align AnnotatedEnum FILES manifest with double-regen convention CI's samples job runs bin/generate-samples.sh twice. On the second pass the generator skips idempotent files (.openapi-generator-ignore and Test/{Api,Model}/*Tests.cs already on disk), so they are omitted from the FILES manifest. Other generichost samples (e.g. ComposedEnum) follow this convention; AnnotatedEnum's FILES was previously committed after a single regen and included 17 stale entries. Regenerated to match CI output. --------- Co-authored-by: sagadira <sagadira@cisco.com> Co-authored-by: Vikrant Balyan <vvb@users.noreply.github.com>
1 parent dfdb6e6 commit 2e78e17

88 files changed

Lines changed: 7063 additions & 10 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/samples-dotnet10.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ jobs:
2323
fail-fast: false
2424
matrix:
2525
sample:
26+
- samples/client/petstore/csharp/generichost/latest/AnnotatedEnum
2627
- samples/client/petstore/csharp/generichost/latest/ComposedEnum
2728
- samples/client/petstore/csharp/generichost/latest/InlineEnumAnyOf
2829
- samples/client/petstore/csharp/generichost/latest/Tags
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# for csharp generichost - OAS 3.1 annotated enum with const + deprecated
2+
generatorName: csharp
3+
outputDir: samples/client/petstore/csharp/generichost/latest/AnnotatedEnum
4+
inputSpec: modules/openapi-generator/src/test/resources/3_1/simplifyOneOfAnyOf_test.yaml
5+
templateDir: modules/openapi-generator/src/main/resources/csharp
6+
additionalProperties:
7+
packageGuid: '{A2B4C6D8-1234-5678-9ABC-DEF012345678}'
8+
modelPropertySorting: alphabetical
9+
operationParameterSorting: alphabetical
10+
validateSpec: false

modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,7 @@ protected Schema processSimplifyOneOfEnum(Schema schema) {
16431643
*/
16441644
protected Schema simplifyComposedSchemaWithEnums(Schema schema, List<Object> subSchemas, String composedType) {
16451645
Map<Object, String> enumValues = new LinkedHashMap<>();
1646+
Map<Object, Boolean> deprecatedValues = new LinkedHashMap<>();
16461647

16471648
if(schema.getTypes() != null && schema.getTypes().size() > 1) {
16481649
// we cannot handle enums with multiple types
@@ -1663,10 +1664,15 @@ protected Schema simplifyComposedSchemaWithEnums(Schema schema, List<Object> sub
16631664

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

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

16711677
// Ensure all sub-schemas have the same type (if type is specified)
16721678
if(subSchema.getTypes() != null && subSchema.getTypes().size() > 1) {
@@ -1681,25 +1687,28 @@ protected Schema simplifyComposedSchemaWithEnums(Schema schema, List<Object> sub
16811687
return schema;
16821688
}
16831689
}
1690+
boolean subSchemaDeprecated = Boolean.TRUE.equals(subSchema.getDeprecated());
16841691
// Add all enum values from this sub-schema to our collection
1685-
if(subSchema.getEnum().size() == 1) {
1692+
if(subSchemaEnumValues.size() == 1) {
16861693
String description = subSchema.getTitle() == null ? "" : subSchema.getTitle();
16871694
if(subSchema.getDescription() != null) {
16881695
if(!description.isEmpty()) {
16891696
description += " - ";
16901697
}
16911698
description += subSchema.getDescription();
16921699
}
1693-
enumValues.put(subSchema.getEnum().get(0), description);
1700+
enumValues.put(subSchemaEnumValues.get(0), description);
1701+
deprecatedValues.put(subSchemaEnumValues.get(0), subSchemaDeprecated);
16941702
} else {
1695-
for(Object e: subSchema.getEnum()) {
1703+
for(Object e: subSchemaEnumValues) {
16961704
enumValues.put(e, "");
1705+
deprecatedValues.put(e, subSchemaDeprecated);
16971706
}
16981707
}
16991708

17001709
}
17011710

1702-
return createSimplifiedEnumSchema(schema, enumValues, schemaType, composedType);
1711+
return createSimplifiedEnumSchema(schema, enumValues, deprecatedValues, schemaType, composedType);
17031712
}
17041713

17051714

@@ -1708,11 +1717,12 @@ protected Schema simplifyComposedSchemaWithEnums(Schema schema, List<Object> sub
17081717
*
17091718
* @param originalSchema Original schema to modify
17101719
* @param enumValues Collected enum values
1720+
* @param deprecatedValues Per-value deprecated flags (aligned with enumValues key order)
17111721
* @param schemaType Consistent type across sub-schemas
17121722
* @param composedType Type of composed schema being simplified
17131723
* @return Simplified enum schema
17141724
*/
1715-
protected Schema createSimplifiedEnumSchema(Schema originalSchema, Map<Object, String> enumValues, String schemaType, String composedType) {
1725+
protected Schema createSimplifiedEnumSchema(Schema originalSchema, Map<Object, String> enumValues, Map<Object, Boolean> deprecatedValues, String schemaType, String composedType) {
17161726
// Clear the composed schema type
17171727
if ("oneOf".equals(composedType)) {
17181728
originalSchema.setOneOf(null);
@@ -1730,6 +1740,10 @@ protected Schema createSimplifiedEnumSchema(Schema originalSchema, Map<Object, S
17301740
//set x-enum-descriptions only if there's at least one non-empty description
17311741
originalSchema.addExtension(X_ENUM_DESCRIPTIONS, new ArrayList<>(enumValues.values()));
17321742
}
1743+
if (deprecatedValues != null && deprecatedValues.values().stream().anyMatch(Boolean.TRUE::equals)) {
1744+
// preserve per-value deprecated flags from OAS 3.1 oneOf/anyOf + const sub-schemas
1745+
originalSchema.addExtension("x-enum-deprecated", new ArrayList<>(deprecatedValues.values()));
1746+
}
17331747

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

modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,9 +1568,16 @@ public void testOpenAPINormalizerSimplifyOneOfAnyOf31Spec() {
15681568
assertEquals(schema14.getType(), null);
15691569

15701570
Schema schema16 = openAPI.getComponents().getSchemas().get("TypeIntegerWithOneOf");
1571-
assertEquals(schema16.getOneOf().size(),3);
1572-
assertEquals(((Schema) schema16.getOneOf().get(0)).getConst(), 1);
1573-
assertEquals(((Schema) schema16.getOneOf().get(0)).getDeprecated(), true);
1571+
// After normalization, oneOf with const values should be simplified to enum
1572+
assertEquals(schema16.getOneOf(), null);
1573+
assertEquals(schema16.getEnum().size(), 3);
1574+
assertEquals(schema16.getEnum().get(0), 1);
1575+
// per-value deprecated flags from oneOf sub-schemas should be preserved as x-enum-deprecated
1576+
List<Boolean> enumDeprecated = (List<Boolean>) schema16.getExtensions().get("x-enum-deprecated");
1577+
assertEquals(enumDeprecated.size(), 3);
1578+
assertEquals(enumDeprecated.get(0), Boolean.TRUE);
1579+
assertEquals(enumDeprecated.get(1), Boolean.FALSE);
1580+
assertEquals(enumDeprecated.get(2), Boolean.FALSE);
15741581

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

0 commit comments

Comments
 (0)