From 7d94b0e87511c0218a6223d167b883391c873744 Mon Sep 17 00:00:00 2001 From: Marco Schaeck Date: Tue, 14 Apr 2026 15:58:36 +0200 Subject: [PATCH] [Core] Support map/object format for x-enum-varnames and x-enum-descriptions The generator crashes with a ClassCastException when these vendor extensions are provided as a JSON object (map) instead of an array. Both formats are valid conventions used in the ecosystem (Redocly, Speakeasy, etc.). For map format, keys are matched to enum values via toEnumValue(key, dataType) to ensure correct matching regardless of language-specific value transformations. --- .../openapitools/codegen/DefaultCodegen.java | 28 +++++++++--- .../codegen/languages/AbstractPhpCodegen.java | 45 +++++++++++++++---- .../codegen/DefaultCodegenTest.java | 41 +++++++++++++++++ 3 files changed, 98 insertions(+), 16 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index f91f22a93ca3..a1f0fe6246de 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -7028,17 +7028,31 @@ private String getUniqueEnumName(String name, List> enumVars protected void updateEnumVarsWithExtensions(List> enumVars, Map vendorExtensions, String dataType) { if (vendorExtensions != null) { - updateEnumVarsWithExtensions(enumVars, vendorExtensions, "x-enum-varnames", "name"); - updateEnumVarsWithExtensions(enumVars, vendorExtensions, "x-enum-descriptions", "enumDescription"); + updateEnumVarsWithExtensions(enumVars, vendorExtensions, "x-enum-varnames", "name", dataType); + updateEnumVarsWithExtensions(enumVars, vendorExtensions, "x-enum-descriptions", "enumDescription", dataType); } } - private void updateEnumVarsWithExtensions(List> enumVars, Map vendorExtensions, String extensionKey, String key) { + private void updateEnumVarsWithExtensions(List> enumVars, Map vendorExtensions, String extensionKey, String key, String dataType) { if (vendorExtensions.containsKey(extensionKey)) { - List values = (List) vendorExtensions.get(extensionKey); - int size = Math.min(enumVars.size(), values.size()); - for (int i = 0; i < size; i++) { - enumVars.get(i).put(key, values.get(i)); + Object extensionValue = vendorExtensions.get(extensionKey); + if (extensionValue instanceof List) { + List values = (List) extensionValue; + int size = Math.min(enumVars.size(), values.size()); + for (int i = 0; i < size; i++) { + enumVars.get(i).put(key, values.get(i)); + } + } else if (extensionValue instanceof Map) { + Map valueMap = (Map) extensionValue; + for (Map enumVar : enumVars) { + String enumValue = (String) enumVar.get("value"); + for (Map.Entry entry : valueMap.entrySet()) { + if (toEnumValue(entry.getKey(), dataType).equals(enumValue)) { + enumVar.put(key, entry.getValue()); + break; + } + } + } } } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java index 68520b123d99..612ccf5c079d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java @@ -711,19 +711,46 @@ public void setParameterExampleValue(CodegenParameter p) { protected void updateEnumVarsWithExtensions(List> enumVars, Map vendorExtensions, String dataType) { if (vendorExtensions != null) { if (vendorExtensions.containsKey("x-enum-varnames")) { - List values = (List) vendorExtensions.get("x-enum-varnames"); - int size = Math.min(enumVars.size(), values.size()); - - for (int i = 0; i < size; i++) { - enumVars.get(i).put("name", toEnumVarName(values.get(i), dataType)); + Object extensionValue = vendorExtensions.get("x-enum-varnames"); + if (extensionValue instanceof List) { + List values = (List) extensionValue; + int size = Math.min(enumVars.size(), values.size()); + for (int i = 0; i < size; i++) { + enumVars.get(i).put("name", toEnumVarName(values.get(i), dataType)); + } + } else if (extensionValue instanceof Map) { + Map valueMap = (Map) extensionValue; + for (Map enumVar : enumVars) { + String enumValue = (String) enumVar.get("value"); + for (Map.Entry entry : valueMap.entrySet()) { + if (toEnumValue(entry.getKey(), dataType).equals(enumValue)) { + enumVar.put("name", toEnumVarName(entry.getValue(), dataType)); + break; + } + } + } } } if (vendorExtensions.containsKey("x-enum-descriptions")) { - List values = (List) vendorExtensions.get("x-enum-descriptions"); - int size = Math.min(enumVars.size(), values.size()); - for (int i = 0; i < size; i++) { - enumVars.get(i).put("enumDescription", values.get(i)); + Object extensionValue = vendorExtensions.get("x-enum-descriptions"); + if (extensionValue instanceof List) { + List values = (List) extensionValue; + int size = Math.min(enumVars.size(), values.size()); + for (int i = 0; i < size; i++) { + enumVars.get(i).put("enumDescription", values.get(i)); + } + } else if (extensionValue instanceof Map) { + Map valueMap = (Map) extensionValue; + for (Map enumVar : enumVars) { + String enumValue = (String) enumVar.get("value"); + for (Map.Entry entry : valueMap.entrySet()) { + if (toEnumValue(entry.getKey(), dataType).equals(enumValue)) { + enumVar.put("enumDescription", entry.getValue()); + break; + } + } + } } } } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java index 1f0e4a8f8fc6..f1406f134b73 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java @@ -995,6 +995,26 @@ public void postProcessModelsEnumWithExtension() { assertEquals("This is a cat", enumVars.get(1).getOrDefault("enumDescription", "")); } + @Test + public void postProcessModelsEnumWithMapExtension() { + final DefaultCodegen codegen = new DefaultCodegen(); + ModelsMap objs = codegenModelWithXEnumVarNameAsMap(); + CodegenModel cm = objs.getModels().get(0).getModel(); + + codegen.postProcessModelsEnum(objs); + + List> enumVars = (List>) cm.getAllowableValues().get("enumVars"); + Assertions.assertNotNull(enumVars); + Assertions.assertNotNull(enumVars.get(0)); + assertEquals("DOGVAR", enumVars.get(0).getOrDefault("name", "")); + assertEquals("\"dog\"", enumVars.get(0).getOrDefault("value", "")); + assertEquals("This is a dog", enumVars.get(0).getOrDefault("enumDescription", "")); + Assertions.assertNotNull(enumVars.get(1)); + assertEquals("CATVAR", enumVars.get(1).getOrDefault("name", "")); + assertEquals("\"cat\"", enumVars.get(1).getOrDefault("value", "")); + assertEquals("This is a cat", enumVars.get(1).getOrDefault("enumDescription", "")); + } + @Test public void testExample1() { final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/examples.yaml"); @@ -2314,6 +2334,27 @@ private ModelsMap codegenModelWithXEnumVarName() { return TestUtils.createCodegenModelWrapper(cm); } + private ModelsMap codegenModelWithXEnumVarNameAsMap() { + final CodegenModel cm = new CodegenModel(); + cm.isEnum = true; + final HashMap allowableValues = new HashMap<>(); + allowableValues.put("values", Arrays.asList("dog", "cat")); + cm.setAllowableValues(allowableValues); + cm.dataType = "String"; + Map aliases = new LinkedHashMap<>(); + aliases.put("dog", "DOGVAR"); + aliases.put("cat", "CATVAR"); + Map descriptions = new LinkedHashMap<>(); + descriptions.put("dog", "This is a dog"); + descriptions.put("cat", "This is a cat"); + Map extensions = new HashMap<>(); + extensions.put("x-enum-varnames", aliases); + extensions.put("x-enum-descriptions", descriptions); + cm.setVendorExtensions(extensions); + cm.setVars(Collections.emptyList()); + return TestUtils.createCodegenModelWrapper(cm); + } + @Test public void objectQueryParamIdentifyAsObject() { final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/objectQueryParam.yaml");