Skip to content

Commit a9d13a6

Browse files
Anthony TODISCOAnthony TODISCO
authored andcommitted
fix: Manage case with Enum in lists
1 parent dbf98ab commit a9d13a6

3 files changed

Lines changed: 136 additions & 6 deletions

File tree

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

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -716,12 +716,34 @@ public ModelsMap postProcessModels(ModelsMap objs) {
716716
}
717717
}
718718

719-
if (property.isEnum) {
720-
addUnspecifiedToAllowableValues(property.allowableValues);
721-
addEnumValuesPrefix(property.allowableValues, property.getEnumName());
719+
// Check if this property is an enum or an array of enums.
720+
//
721+
// This handles two distinct cases:
722+
// 1. Direct enum property: property.isEnum=true
723+
// Example OpenAPI: myStatus: {$ref: '#/components/schemas/Status'}
724+
// Generated Protobuf: Status.Enum my_status = N;
725+
//
726+
// 2. Array of enums: property.isArray=true && property.items.isEnum=true
727+
// Example OpenAPI: tags: {type: array, items: {$ref: '#/components/schemas/Tag'}}
728+
// Generated Protobuf: repeated Tag.Enum tags = N;
729+
//
730+
// Both cases require the same enum extraction and wrapper processing when
731+
// EXTRACT_ENUMS_TO_SEPARATE_FILES is enabled. This fix ensures arrays of enums
732+
// are handled consistently with direct enum references (previously arrays were
733+
// not being wrapped with the .Enum suffix).
734+
boolean isDirectEnum = property.isEnum;
735+
boolean isArrayOfEnums = property.isArray && property.items != null && property.items.isEnum;
736+
737+
if (isDirectEnum || isArrayOfEnums) {
738+
// For arrays of enums, extract enum values from items property;
739+
// for direct enums, extract from the property itself.
740+
CodegenProperty enumProperty = isArrayOfEnums ? property.items : property;
741+
742+
addUnspecifiedToAllowableValues(enumProperty.allowableValues);
743+
addEnumValuesPrefix(enumProperty.allowableValues, enumProperty.getEnumName());
722744

723-
if (property.allowableValues.containsKey("enumVars")) {
724-
List<Map<String, Object>> enumVars = (List<Map<String, Object>>) property.allowableValues.get("enumVars");
745+
if (enumProperty.allowableValues.containsKey("enumVars")) {
746+
List<Map<String, Object>> enumVars = (List<Map<String, Object>>) enumProperty.allowableValues.get("enumVars");
725747
addEnumIndexes(enumVars);
726748
}
727749

@@ -749,7 +771,7 @@ public ModelsMap postProcessModels(ModelsMap objs) {
749771

750772
// Compute the wrapper message name: ParentModelName_FieldName
751773
// This naming scheme ensures uniqueness across models
752-
String enumTypeName = cm.getClassname() + "_" + toModelName(toEnumName(property));
774+
String enumTypeName = cm.getClassname() + "_" + toModelName(toEnumName(enumProperty));
753775
if (StringUtils.isBlank(enumTypeName)) {
754776
LOGGER.warn("Unable to determine enum type name for property: {}", property.name);
755777
continue;

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

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,4 +902,101 @@ public void testEnumsRemainsInlineWithoutExtraction() throws IOException {
902902
System.setProperty("line.separator", originalLineSeparator);
903903
}
904904
}
905+
906+
@Test(description = "Validate that enums in arrays are correctly handled with .Enum suffix when extractEnumsToSeparateFiles is enabled")
907+
public void testEnumsInArraysWithExtraction() throws IOException {
908+
// set line break to \n across all platforms
909+
System.setProperty("line.separator", "\n");
910+
911+
File output = Files.createTempDirectory("test").toFile();
912+
System.out.println("EnumsInArrays Temporary output directory: " + output.getAbsolutePath());
913+
914+
final CodegenConfigurator configurator = new CodegenConfigurator()
915+
.setGeneratorName("protobuf-schema")
916+
.setInputSpec("src/test/resources/3_0/protobuf-schema/extracted_enum.yaml")
917+
.setOutputDir(output.getAbsolutePath().replace("\\", "/"))
918+
.addAdditionalProperty(EXTRACT_ENUMS_TO_SEPARATE_FILES, true);
919+
920+
final ClientOptInput clientOptInput = configurator.toClientOptInput();
921+
DefaultGenerator generator = new DefaultGenerator();
922+
List<File> files = generator.opts(clientOptInput).generate();
923+
924+
// Check that the model file was generated
925+
TestUtils.ensureContainsFile(files, output, "models/all_of_model_with_enums.proto");
926+
Path modelPath = Paths.get(output + "/models/all_of_model_with_enums.proto");
927+
String modelContent = new String(Files.readAllBytes(modelPath), StandardCharsets.UTF_8)
928+
.replace("\r\n", "\n").replace("\r", "\n");
929+
930+
// Test Case 1: Array with REFERENCED enum (listOfReferencedEnums)
931+
// Should have .Enum suffix: repeated SeparatedEnum.Enum
932+
// Use regex to validate structure without depending on specific field numbers
933+
java.util.regex.Pattern referencedEnumPattern = java.util.regex.Pattern.compile(
934+
"repeated\\s+SeparatedEnum\\.Enum\\s+list_of_referenced_enums\\s*=\\s*\\d+");
935+
Assert.assertTrue(
936+
referencedEnumPattern.matcher(modelContent).find(),
937+
"Array with referenced enum should use .Enum suffix with pattern: repeated SeparatedEnum.Enum list_of_referenced_enums = <number>");
938+
939+
// Test Case 2: Array with INLINE enum (listOfEnums) - in AllOfModelWithEnums
940+
// Should have .Enum suffix: repeated AllOfModelWithEnums_ListOfEnums.Enum
941+
// Use regex to validate structure without depending on specific field numbers
942+
java.util.regex.Pattern inlineEnumPattern = java.util.regex.Pattern.compile(
943+
"repeated\\s+AllOfModelWithEnums_ListOfEnums\\.Enum\\s+list_of_enums\\s*=\\s*\\d+");
944+
Assert.assertTrue(
945+
inlineEnumPattern.matcher(modelContent).find(),
946+
"Array with inline enum should use .Enum suffix with pattern: repeated AllOfModelWithEnums_ListOfEnums.Enum list_of_enums = <number>");
947+
948+
// Verify the imported enum file for referenced enum exists
949+
TestUtils.ensureContainsFile(files, output, "models/separated_enum.proto");
950+
951+
// Verify the extracted inline enum file for arrays exists
952+
TestUtils.ensureContainsFile(files, output, "models/all_of_model_with_enums_list_of_enums.proto");
953+
Path listOfEnumsPath = Paths.get(output + "/models/all_of_model_with_enums_list_of_enums.proto");
954+
String listOfEnumsContent = new String(Files.readAllBytes(listOfEnumsPath), StandardCharsets.UTF_8)
955+
.replace("\r\n", "\n").replace("\r", "\n");
956+
957+
// Verify the wrapper message structure
958+
Assert.assertTrue(listOfEnumsContent.contains("message AllOfModelWithEnums_ListOfEnums"),
959+
"Extracted inline enum file should contain wrapper message");
960+
Assert.assertTrue(listOfEnumsContent.contains("enum Enum"),
961+
"Wrapper message should contain inner Enum");
962+
Assert.assertTrue(listOfEnumsContent.contains("VALUE10") && listOfEnumsContent.contains("VALUE11"),
963+
"Wrapper message should contain enum values");
964+
965+
output.deleteOnExit();
966+
}
967+
968+
@Test(description = "Validate that enums in arrays remain inline when extractEnumsToSeparateFiles is NOT enabled")
969+
public void testEnumsInArraysWithoutExtraction() throws IOException {
970+
// set line break to \n across all platforms
971+
System.setProperty("line.separator", "\n");
972+
973+
File output = Files.createTempDirectory("test").toFile();
974+
975+
final CodegenConfigurator configurator = new CodegenConfigurator()
976+
.setGeneratorName("protobuf-schema")
977+
.setInputSpec("src/test/resources/3_0/protobuf-schema/extracted_enum.yaml")
978+
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
979+
// Note: EXTRACT_ENUMS_TO_SEPARATE_FILES is NOT enabled
980+
981+
final ClientOptInput clientOptInput = configurator.toClientOptInput();
982+
DefaultGenerator generator = new DefaultGenerator();
983+
List<File> files = generator.opts(clientOptInput).generate();
984+
985+
// Check that the model file was generated
986+
TestUtils.ensureContainsFile(files, output, "models/model_with_enums.proto");
987+
Path modelPath = Paths.get(output + "/models/model_with_enums.proto");
988+
String modelContent = new String(Files.readAllBytes(modelPath), StandardCharsets.UTF_8)
989+
.replace("\r\n", "\n").replace("\r", "\n");
990+
991+
// Without extraction: Arrays of enums should NOT use .Enum suffix
992+
// Instead, they should use the type directly or inline definition
993+
Assert.assertFalse(modelContent.contains(".Enum"),
994+
"Without extraction, enums should NOT use .Enum suffix");
995+
996+
// Extracted inline enum files should NOT exist
997+
Assert.assertFalse(Files.exists(Paths.get(output + "/models/model_with_enums_list_of_enums.proto")),
998+
"Without extraction, inline enum array files should NOT be generated");
999+
1000+
output.deleteOnExit();
1001+
}
9051002
}

modules/openapi-generator/src/test/resources/3_0/protobuf-schema/extracted_enum.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ components:
2121
- VALUE2
2222
- VALUE3
2323
- VALUE4
24+
listOfEnums:
25+
type: array
26+
items:
27+
type: string
28+
enum:
29+
- VALUE10
30+
- VALUE11
2431
AllOfModelWithEnums:
2532
allOf:
2633
- $ref: "#/components/schemas/ModelWithEnums"
@@ -31,6 +38,10 @@ components:
3138
enum:
3239
- VALUE5
3340
- VALUE6
41+
listOfReferencedEnums:
42+
type: array
43+
items:
44+
$ref: '#/components/schemas/SeparatedEnum'
3445
DiscriminatedModel:
3546
type: object
3647
properties:

0 commit comments

Comments
 (0)