Skip to content

Commit c854a23

Browse files
authored
[protobuf] fix generation of enums with UNSPECIFIED values (#21774)
1 parent 1c2fd67 commit c854a23

File tree

2 files changed

+103
-10
lines changed

2 files changed

+103
-10
lines changed

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

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ public void processOpts() {
272272
if (additionalProperties.containsKey(CUSTOM_OPTIONS_MODEL)) {
273273
this.setCustomOptionsModel((String) additionalProperties.get(CUSTOM_OPTIONS_MODEL));
274274
}
275-
275+
276276
if (additionalProperties.containsKey(this.SUPPORT_MULTIPLE_RESPONSES)) {
277277
this.supportMultipleResponses = convertPropertyToBooleanAndWriteBack(SUPPORT_MULTIPLE_RESPONSES);
278278
} else {
@@ -546,22 +546,32 @@ public void addEnumValuesPrefix(Map<String, Object> allowableValues, String pref
546546
* @param allowableValues allowable values
547547
*/
548548
public void addUnspecifiedToAllowableValues(Map<String, Object> allowableValues) {
549+
550+
final String UNSPECIFIED = "UNSPECIFIED";
551+
549552
if (startEnumsWithUnspecified) {
550553
if (allowableValues.containsKey("enumVars")) {
551554
List<Map<String, Object>> enumVars = (List<Map<String, Object>>) allowableValues.get("enumVars");
552-
553-
HashMap<String, Object> unspecified = new HashMap<String, Object>();
554-
unspecified.put("name", "UNSPECIFIED");
555-
unspecified.put("isString", "false");
556-
unspecified.put("value", "\"UNSPECIFIED\"");
557-
enumVars.add(0, unspecified);
555+
boolean unspecifiedPresent = enumVars.stream()
556+
.anyMatch(e -> {
557+
return UNSPECIFIED.equals(e.get("name"));
558+
});
559+
if (!unspecifiedPresent) {
560+
HashMap<String, Object> unspecifiedEnum = new HashMap<String, Object>();
561+
unspecifiedEnum.put("name", UNSPECIFIED);
562+
unspecifiedEnum.put("isString", "false");
563+
unspecifiedEnum.put("value", "\"" + UNSPECIFIED + "\"");
564+
enumVars.add(0, unspecifiedEnum);
565+
}
558566
}
559567

560568
if (allowableValues.containsKey("values")) {
561569
List<String> values = (List<String>) allowableValues.get("values");
562-
List<String> modifiableValues = new ArrayList<>(values);
563-
modifiableValues.add(0, "UNSPECIFIED");
564-
allowableValues.put("values", modifiableValues);
570+
if (!values.contains(UNSPECIFIED)) {
571+
List<String> modifiableValues = new ArrayList<>(values);
572+
modifiableValues.add(0, UNSPECIFIED);
573+
allowableValues.put("values", modifiableValues);
574+
}
565575
}
566576
}
567577
}

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

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import static org.openapitools.codegen.TestUtils.createCodegenModelWrapper;
4646
import static org.openapitools.codegen.languages.ProtobufSchemaCodegen.USE_SIMPLIFIED_ENUM_NAMES;
4747
import static org.testng.Assert.assertEquals;
48+
import static org.openapitools.codegen.languages.ProtobufSchemaCodegen.START_ENUMS_WITH_UNSPECIFIED;
4849

4950
public class ProtobufSchemaCodegenTest {
5051

@@ -213,4 +214,86 @@ private void testEnumValues(boolean simpleEnumValue) {
213214
Assert.assertEquals(enumVars2.get(1).get("value"), simpleEnumValue ? "_2" : "\"TEST_INT_ENUM__2\"");
214215
Assert.assertEquals(enumVars2.get(1).get("isString"), false);
215216
}
217+
218+
@SuppressWarnings("unchecked")
219+
@Test(description = "Validate that unspecified enum values are added when the option is selected")
220+
public void unspecifiedEnumValuesAreAdded() {
221+
String enumKey = "aValidEnumWithoutUnspecifiedValues";
222+
223+
final Schema<?> model = new Schema<>()
224+
.description("a sample model")
225+
.addProperty(enumKey, new StringSchema()._enum(Arrays.asList("foo", "bar")));
226+
227+
final ProtobufSchemaCodegen codegen = new ProtobufSchemaCodegen();
228+
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", model);
229+
codegen.setOpenAPI(openAPI);
230+
final CodegenModel cm = codegen.fromModel("sample", model);
231+
codegen.additionalProperties().put(USE_SIMPLIFIED_ENUM_NAMES, true);
232+
codegen.additionalProperties().put(START_ENUMS_WITH_UNSPECIFIED, true);
233+
234+
codegen.processOpts();
235+
codegen.postProcessModels(createCodegenModelWrapper(cm));
236+
237+
final CodegenProperty property1 = cm.vars.get(0);
238+
Assert.assertEquals(property1.baseName, enumKey);
239+
Assert.assertEquals(property1.dataType, "string");
240+
Assert.assertEquals(property1.baseType, "string");
241+
Assert.assertEquals(property1.datatypeWithEnum, "A_valid_enum_without_unspecified_values");
242+
Assert.assertEquals(property1.name, "a_valid_enum_without_unspecified_values");
243+
Assert.assertTrue(property1.isEnum);
244+
Assert.assertEquals(property1.allowableValues.size(), 2);
245+
List<Map<String, Object>> enumVars1 = (List<Map<String, Object>>) property1.allowableValues.get("enumVars");
246+
Assert.assertEquals(enumVars1.size(), 3);
247+
248+
Assert.assertEquals(enumVars1.get(0).get("name"), "UNSPECIFIED");
249+
Assert.assertEquals(enumVars1.get(0).get("value"), "UNSPECIFIED");
250+
Assert.assertEquals(Boolean.valueOf((String) enumVars1.get(0).get("isString")), false);
251+
252+
Assert.assertEquals(enumVars1.get(1).get("name"), "FOO");
253+
Assert.assertEquals(enumVars1.get(1).get("value"), "FOO");
254+
Assert.assertEquals(enumVars1.get(1).get("isString"), false);
255+
256+
Assert.assertEquals(enumVars1.get(2).get("name"), "BAR");
257+
Assert.assertEquals(enumVars1.get(2).get("value"), "BAR");
258+
Assert.assertEquals(enumVars1.get(2).get("isString"), false);
259+
}
260+
261+
@SuppressWarnings("unchecked")
262+
@Test(description = "Validate that unspecified enum values are NOT added when the option is selected if they are already present")
263+
public void unspecifiedEnumValuesIgnoredIfAlreadyPresent() {
264+
String enumKey = "aValidEnumWithUnspecifiedValues";
265+
266+
final Schema<?> model = new Schema<>()
267+
.description("a sample model")
268+
.addProperty(enumKey, new StringSchema()._enum(Arrays.asList( "UNSPECIFIED", "foo")));
269+
270+
final ProtobufSchemaCodegen codegen = new ProtobufSchemaCodegen();
271+
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", model);
272+
codegen.setOpenAPI(openAPI);
273+
final CodegenModel cm = codegen.fromModel("sample", model);
274+
codegen.additionalProperties().put(USE_SIMPLIFIED_ENUM_NAMES, true);
275+
codegen.additionalProperties().put(START_ENUMS_WITH_UNSPECIFIED, true);
276+
277+
codegen.processOpts();
278+
codegen.postProcessModels(createCodegenModelWrapper(cm));
279+
280+
final CodegenProperty property1 = cm.vars.get(0);
281+
Assert.assertEquals(property1.baseName, enumKey);
282+
Assert.assertEquals(property1.dataType, "string");
283+
Assert.assertEquals(property1.baseType, "string");
284+
Assert.assertEquals(property1.datatypeWithEnum, "A_valid_enum_with_unspecified_values");
285+
Assert.assertEquals(property1.name, "a_valid_enum_with_unspecified_values");
286+
Assert.assertTrue(property1.isEnum);
287+
Assert.assertEquals(property1.allowableValues.size(), 2);
288+
List<Map<String, Object>> enumVars1 = (List<Map<String, Object>>) property1.allowableValues.get("enumVars");
289+
Assert.assertEquals(enumVars1.size(), 2);
290+
291+
Assert.assertEquals(enumVars1.get(0).get("name"), "UNSPECIFIED");
292+
Assert.assertEquals(enumVars1.get(0).get("value"), "UNSPECIFIED");
293+
Assert.assertEquals(enumVars1.get(0).get("isString"), false);
294+
295+
Assert.assertEquals(enumVars1.get(1).get("name"), "FOO");
296+
Assert.assertEquals(enumVars1.get(1).get("value"), "FOO");
297+
Assert.assertEquals(enumVars1.get(1).get("isString"), false);
298+
}
216299
}

0 commit comments

Comments
 (0)