@@ -89,6 +89,9 @@ public class OpenAPINormalizer {
8989 // when set to true, boolean enum will be converted to just boolean
9090 final String SIMPLIFY_BOOLEAN_ENUM = "SIMPLIFY_BOOLEAN_ENUM" ;
9191
92+ // when set to true, oneOf/anyOf with enum sub-schemas containing single values will be converted to a single enum
93+ final String SIMPLIFY_ONEOF_ANYOF_ENUM = "SIMPLIFY_ONEOF_ANYOF_ENUM" ;
94+
9295 // when set to a string value, tags in all operations will be reset to the string value provided
9396 final String SET_TAGS_FOR_ALL_OPERATIONS = "SET_TAGS_FOR_ALL_OPERATIONS" ;
9497 String setTagsForAllOperations ;
@@ -206,11 +209,12 @@ public OpenAPINormalizer(OpenAPI openAPI, Map<String, String> inputRules) {
206209 ruleNames .add (FILTER );
207210 ruleNames .add (SET_CONTAINER_TO_NULLABLE );
208211 ruleNames .add (SET_PRIMITIVE_TYPES_TO_NULLABLE );
209-
212+ ruleNames . add ( SIMPLIFY_ONEOF_ANYOF_ENUM );
210213
211214 // rules that are default to true
212215 rules .put (SIMPLIFY_ONEOF_ANYOF , true );
213216 rules .put (SIMPLIFY_BOOLEAN_ENUM , true );
217+ rules .put (SIMPLIFY_ONEOF_ANYOF_ENUM , true );
214218
215219 processRules (inputRules );
216220
@@ -973,6 +977,8 @@ protected Schema normalizeOneOf(Schema schema, Set<Schema> visitedSchemas) {
973977 // Remove duplicate oneOf entries
974978 ModelUtils .deduplicateOneOfSchema (schema );
975979
980+ schema = processSimplifyOneOfEnum (schema );
981+
976982 // simplify first as the schema may no longer be a oneOf after processing the rule below
977983 schema = processSimplifyOneOf (schema );
978984
@@ -1001,6 +1007,11 @@ protected Schema normalizeOneOf(Schema schema, Set<Schema> visitedSchemas) {
10011007 }
10021008
10031009 protected Schema normalizeAnyOf (Schema schema , Set <Schema > visitedSchemas ) {
1010+ //transform anyOf into enums if needed
1011+ schema = processSimplifyAnyOfEnum (schema );
1012+ if (schema .getAnyOf () == null ) {
1013+ return schema ;
1014+ }
10041015 for (int i = 0 ; i < schema .getAnyOf ().size (); i ++) {
10051016 // normalize anyOf sub schemas one by one
10061017 Object item = schema .getAnyOf ().get (i );
@@ -1276,6 +1287,145 @@ protected Schema processSimplifyAnyOfStringAndEnumString(Schema schema) {
12761287 }
12771288
12781289
1290+ /**
1291+ * If the schema is anyOf and all sub-schemas are enums (with one or more values),
1292+ * then simplify it to a single enum schema containing all the values.
1293+ *
1294+ * @param schema Schema
1295+ * @return Schema
1296+ */
1297+ protected Schema processSimplifyAnyOfEnum (Schema schema ) {
1298+ if (!getRule (SIMPLIFY_ONEOF_ANYOF_ENUM )) {
1299+ return schema ;
1300+ }
1301+
1302+ if (schema .getAnyOf () == null || schema .getAnyOf ().isEmpty ()) {
1303+ return schema ;
1304+ }
1305+ if (schema .getOneOf () != null && !schema .getOneOf ().isEmpty () ||
1306+ schema .getAllOf () != null && !schema .getAllOf ().isEmpty () ||
1307+ schema .getNot () != null ) {
1308+ //only convert to enum if anyOf is the only composition
1309+ return schema ;
1310+ }
1311+
1312+ return simplifyComposedSchemaWithEnums (schema , schema .getAnyOf (), "anyOf" );
1313+ }
1314+
1315+ /**
1316+ * If the schema is oneOf and all sub-schemas are enums (with one or more values),
1317+ * then simplify it to a single enum schema containing all the values.
1318+ *
1319+ * @param schema Schema
1320+ * @return Schema
1321+ */
1322+ protected Schema processSimplifyOneOfEnum (Schema schema ) {
1323+ if (!getRule (SIMPLIFY_ONEOF_ANYOF_ENUM )) {
1324+ return schema ;
1325+ }
1326+
1327+ if (schema .getOneOf () == null || schema .getOneOf ().isEmpty ()) {
1328+ return schema ;
1329+ }
1330+ if (schema .getAnyOf () != null && !schema .getAnyOf ().isEmpty () ||
1331+ schema .getAllOf () != null && !schema .getAllOf ().isEmpty () ||
1332+ schema .getNot () != null ) {
1333+ //only convert to enum if oneOf is the only composition
1334+ return schema ;
1335+ }
1336+
1337+ return simplifyComposedSchemaWithEnums (schema , schema .getOneOf (), "oneOf" );
1338+ }
1339+
1340+ /**
1341+ * Simplifies a composed schema (oneOf/anyOf) where all sub-schemas are enums
1342+ * to a single enum schema containing all the values.
1343+ *
1344+ * @param schema Schema to modify
1345+ * @param subSchemas List of sub-schemas to check
1346+ * @param schemaType Type of composed schema ("oneOf" or "anyOf")
1347+ * @return Simplified schema
1348+ */
1349+ protected Schema simplifyComposedSchemaWithEnums (Schema schema , List <Object > subSchemas , String composedType ) {
1350+ List <Object > enumValues = new ArrayList <>();
1351+
1352+ if (schema .getTypes () != null && schema .getTypes ().size () > 1 ) {
1353+ // we cannot handle enums with multiple types
1354+ return schema ;
1355+ }
1356+ String schemaType = ModelUtils .getType (schema );
1357+
1358+ for (Object item : subSchemas ) {
1359+ if (!(item instanceof Schema )) {
1360+ return schema ;
1361+ }
1362+
1363+ Schema subSchema = (Schema ) item ;
1364+ //processing references is very possible with this code (subSchema = ModelUtils.getReferencedSchema(openAPI, (Schema) item);),
1365+ // but might lead to reduced reuse in generated code
1366+ if (subSchema .get$ref () != null ) {
1367+ return schema ;
1368+ }
1369+
1370+ // Check if this sub-schema has an enum (with one or more values)
1371+ if (subSchema .getEnum () == null || subSchema .getEnum ().isEmpty ()) {
1372+ return schema ;
1373+ }
1374+
1375+ // Ensure all sub-schemas have the same type (if type is specified)
1376+ if (subSchema .getTypes () != null && subSchema .getTypes ().size () > 1 ) {
1377+ // we cannot handle enums with multiple types
1378+ return schema ;
1379+ }
1380+ String subSchemaType = ModelUtils .getType (subSchema );
1381+ if (subSchemaType != null ) {
1382+ if (schemaType == null ) {
1383+ schemaType = subSchemaType ;
1384+ } else if (!schemaType .equals (subSchema .getType ())) {
1385+ return schema ;
1386+ }
1387+ }
1388+
1389+ // Add all enum values from this sub-schema to our collection
1390+ enumValues .addAll (subSchema .getEnum ());
1391+ }
1392+
1393+ return createSimplifiedEnumSchema (schema , enumValues , schemaType , composedType );
1394+ }
1395+
1396+
1397+ /**
1398+ * Creates a simplified enum schema from collected enum values.
1399+ *
1400+ * @param originalSchema Original schema to modify
1401+ * @param enumValues Collected enum values
1402+ * @param schemaType Consistent type across sub-schemas
1403+ * @param composedType Type of composed schema being simplified
1404+ * @return Simplified enum schema
1405+ */
1406+ protected Schema createSimplifiedEnumSchema (Schema originalSchema , List <Object > enumValues , String schemaType , String composedType ) {
1407+ // Clear the composed schema type
1408+ if ("oneOf" .equals (composedType )) {
1409+ originalSchema .setOneOf (null );
1410+ } else if ("anyOf" .equals (composedType )) {
1411+ originalSchema .setAnyOf (null );
1412+ }
1413+
1414+ if (ModelUtils .getType (originalSchema ) == null && schemaType != null ) {
1415+ //if type was specified in subschemas, keep it in the main schema
1416+ ModelUtils .setType (originalSchema , schemaType );
1417+ }
1418+
1419+ // Set the combined enum values (deduplicated to avoid duplicates)
1420+ List <Object > uniqueEnumValues = enumValues .stream ().distinct ().collect (Collectors .toList ());
1421+ originalSchema .setEnum (uniqueEnumValues );
1422+
1423+ LOGGER .debug ("Simplified {} with enum sub-schemas to single enum: {}" , composedType , originalSchema );
1424+
1425+ return originalSchema ;
1426+ }
1427+
1428+
12791429 /**
12801430 * If the schema is oneOf and the sub-schemas is null, set `nullable: true`
12811431 * instead.
0 commit comments