[protobuf] fix generation of enums with UNSPECIFIED values (#21774)

This commit is contained in:
Leo Gomes @Amadeus 2025-09-02 06:42:27 +02:00 committed by GitHub
parent 1c2fd67cc9
commit c854a23682
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 103 additions and 10 deletions

View File

@ -272,7 +272,7 @@ public class ProtobufSchemaCodegen extends DefaultCodegen implements CodegenConf
if (additionalProperties.containsKey(CUSTOM_OPTIONS_MODEL)) { if (additionalProperties.containsKey(CUSTOM_OPTIONS_MODEL)) {
this.setCustomOptionsModel((String) additionalProperties.get(CUSTOM_OPTIONS_MODEL)); this.setCustomOptionsModel((String) additionalProperties.get(CUSTOM_OPTIONS_MODEL));
} }
if (additionalProperties.containsKey(this.SUPPORT_MULTIPLE_RESPONSES)) { if (additionalProperties.containsKey(this.SUPPORT_MULTIPLE_RESPONSES)) {
this.supportMultipleResponses = convertPropertyToBooleanAndWriteBack(SUPPORT_MULTIPLE_RESPONSES); this.supportMultipleResponses = convertPropertyToBooleanAndWriteBack(SUPPORT_MULTIPLE_RESPONSES);
} else { } else {
@ -546,22 +546,32 @@ public class ProtobufSchemaCodegen extends DefaultCodegen implements CodegenConf
* @param allowableValues allowable values * @param allowableValues allowable values
*/ */
public void addUnspecifiedToAllowableValues(Map<String, Object> allowableValues) { public void addUnspecifiedToAllowableValues(Map<String, Object> allowableValues) {
final String UNSPECIFIED = "UNSPECIFIED";
if (startEnumsWithUnspecified) { if (startEnumsWithUnspecified) {
if (allowableValues.containsKey("enumVars")) { if (allowableValues.containsKey("enumVars")) {
List<Map<String, Object>> enumVars = (List<Map<String, Object>>) allowableValues.get("enumVars"); List<Map<String, Object>> enumVars = (List<Map<String, Object>>) allowableValues.get("enumVars");
boolean unspecifiedPresent = enumVars.stream()
HashMap<String, Object> unspecified = new HashMap<String, Object>(); .anyMatch(e -> {
unspecified.put("name", "UNSPECIFIED"); return UNSPECIFIED.equals(e.get("name"));
unspecified.put("isString", "false"); });
unspecified.put("value", "\"UNSPECIFIED\""); if (!unspecifiedPresent) {
enumVars.add(0, unspecified); HashMap<String, Object> unspecifiedEnum = new HashMap<String, Object>();
unspecifiedEnum.put("name", UNSPECIFIED);
unspecifiedEnum.put("isString", "false");
unspecifiedEnum.put("value", "\"" + UNSPECIFIED + "\"");
enumVars.add(0, unspecifiedEnum);
}
} }
if (allowableValues.containsKey("values")) { if (allowableValues.containsKey("values")) {
List<String> values = (List<String>) allowableValues.get("values"); List<String> values = (List<String>) allowableValues.get("values");
List<String> modifiableValues = new ArrayList<>(values); if (!values.contains(UNSPECIFIED)) {
modifiableValues.add(0, "UNSPECIFIED"); List<String> modifiableValues = new ArrayList<>(values);
allowableValues.put("values", modifiableValues); modifiableValues.add(0, UNSPECIFIED);
allowableValues.put("values", modifiableValues);
}
} }
} }
} }

View File

@ -45,6 +45,7 @@ import java.util.Map;
import static org.openapitools.codegen.TestUtils.createCodegenModelWrapper; import static org.openapitools.codegen.TestUtils.createCodegenModelWrapper;
import static org.openapitools.codegen.languages.ProtobufSchemaCodegen.USE_SIMPLIFIED_ENUM_NAMES; import static org.openapitools.codegen.languages.ProtobufSchemaCodegen.USE_SIMPLIFIED_ENUM_NAMES;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.openapitools.codegen.languages.ProtobufSchemaCodegen.START_ENUMS_WITH_UNSPECIFIED;
public class ProtobufSchemaCodegenTest { public class ProtobufSchemaCodegenTest {
@ -213,4 +214,86 @@ public class ProtobufSchemaCodegenTest {
Assert.assertEquals(enumVars2.get(1).get("value"), simpleEnumValue ? "_2" : "\"TEST_INT_ENUM__2\""); Assert.assertEquals(enumVars2.get(1).get("value"), simpleEnumValue ? "_2" : "\"TEST_INT_ENUM__2\"");
Assert.assertEquals(enumVars2.get(1).get("isString"), false); Assert.assertEquals(enumVars2.get(1).get("isString"), false);
} }
@SuppressWarnings("unchecked")
@Test(description = "Validate that unspecified enum values are added when the option is selected")
public void unspecifiedEnumValuesAreAdded() {
String enumKey = "aValidEnumWithoutUnspecifiedValues";
final Schema<?> model = new Schema<>()
.description("a sample model")
.addProperty(enumKey, new StringSchema()._enum(Arrays.asList("foo", "bar")));
final ProtobufSchemaCodegen codegen = new ProtobufSchemaCodegen();
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", model);
codegen.setOpenAPI(openAPI);
final CodegenModel cm = codegen.fromModel("sample", model);
codegen.additionalProperties().put(USE_SIMPLIFIED_ENUM_NAMES, true);
codegen.additionalProperties().put(START_ENUMS_WITH_UNSPECIFIED, true);
codegen.processOpts();
codegen.postProcessModels(createCodegenModelWrapper(cm));
final CodegenProperty property1 = cm.vars.get(0);
Assert.assertEquals(property1.baseName, enumKey);
Assert.assertEquals(property1.dataType, "string");
Assert.assertEquals(property1.baseType, "string");
Assert.assertEquals(property1.datatypeWithEnum, "A_valid_enum_without_unspecified_values");
Assert.assertEquals(property1.name, "a_valid_enum_without_unspecified_values");
Assert.assertTrue(property1.isEnum);
Assert.assertEquals(property1.allowableValues.size(), 2);
List<Map<String, Object>> enumVars1 = (List<Map<String, Object>>) property1.allowableValues.get("enumVars");
Assert.assertEquals(enumVars1.size(), 3);
Assert.assertEquals(enumVars1.get(0).get("name"), "UNSPECIFIED");
Assert.assertEquals(enumVars1.get(0).get("value"), "UNSPECIFIED");
Assert.assertEquals(Boolean.valueOf((String) enumVars1.get(0).get("isString")), false);
Assert.assertEquals(enumVars1.get(1).get("name"), "FOO");
Assert.assertEquals(enumVars1.get(1).get("value"), "FOO");
Assert.assertEquals(enumVars1.get(1).get("isString"), false);
Assert.assertEquals(enumVars1.get(2).get("name"), "BAR");
Assert.assertEquals(enumVars1.get(2).get("value"), "BAR");
Assert.assertEquals(enumVars1.get(2).get("isString"), false);
}
@SuppressWarnings("unchecked")
@Test(description = "Validate that unspecified enum values are NOT added when the option is selected if they are already present")
public void unspecifiedEnumValuesIgnoredIfAlreadyPresent() {
String enumKey = "aValidEnumWithUnspecifiedValues";
final Schema<?> model = new Schema<>()
.description("a sample model")
.addProperty(enumKey, new StringSchema()._enum(Arrays.asList( "UNSPECIFIED", "foo")));
final ProtobufSchemaCodegen codegen = new ProtobufSchemaCodegen();
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", model);
codegen.setOpenAPI(openAPI);
final CodegenModel cm = codegen.fromModel("sample", model);
codegen.additionalProperties().put(USE_SIMPLIFIED_ENUM_NAMES, true);
codegen.additionalProperties().put(START_ENUMS_WITH_UNSPECIFIED, true);
codegen.processOpts();
codegen.postProcessModels(createCodegenModelWrapper(cm));
final CodegenProperty property1 = cm.vars.get(0);
Assert.assertEquals(property1.baseName, enumKey);
Assert.assertEquals(property1.dataType, "string");
Assert.assertEquals(property1.baseType, "string");
Assert.assertEquals(property1.datatypeWithEnum, "A_valid_enum_with_unspecified_values");
Assert.assertEquals(property1.name, "a_valid_enum_with_unspecified_values");
Assert.assertTrue(property1.isEnum);
Assert.assertEquals(property1.allowableValues.size(), 2);
List<Map<String, Object>> enumVars1 = (List<Map<String, Object>>) property1.allowableValues.get("enumVars");
Assert.assertEquals(enumVars1.size(), 2);
Assert.assertEquals(enumVars1.get(0).get("name"), "UNSPECIFIED");
Assert.assertEquals(enumVars1.get(0).get("value"), "UNSPECIFIED");
Assert.assertEquals(enumVars1.get(0).get("isString"), false);
Assert.assertEquals(enumVars1.get(1).get("name"), "FOO");
Assert.assertEquals(enumVars1.get(1).get("value"), "FOO");
Assert.assertEquals(enumVars1.get(1).get("isString"), false);
}
} }