Skip to content

Commit f9efb7b

Browse files
authored
[OpenAPI Normalizer] update SIMPLIFY_ONEOF_ANYOF to convert enum of null to nullable (#14898)
* reorganize openapi normalizer tests * add the logic to simply oneof anyof rule instead * minor fix
1 parent 77dd499 commit f9efb7b

File tree

5 files changed

+345
-189
lines changed

5 files changed

+345
-189
lines changed

docs/customization.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ Example:
485485
java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/simplifyBooleanEnum_test.yaml -o /tmp/java-okhttp/ --openapi-normalizer SIMPLIFY_BOOLEAN_ENUM=true
486486
```
487487
488-
- `SIMPLIFY_ONEOF_ANYOF`: when set to `true`, simplify oneOf/anyOf by 1) removing null (sub-schema) and setting nullable to true instead, and 2) simplifying oneOf/anyOf with a single sub-schema to just the sub-schema itself.
488+
- `SIMPLIFY_ONEOF_ANYOF`: when set to `true`, simplify oneOf/anyOf by 1) removing null (sub-schema) or enum of null (sub-schema) and setting nullable to true instead, and 2) simplifying oneOf/anyOf with a single sub-schema to just the sub-schema itself.
489489
490490
Example:
491491
```

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

+43-8
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public class OpenAPINormalizer {
6565

6666
// when set to true, oneOf/anyOf schema with only one sub-schema is simplified to just the sub-schema
6767
// and if sub-schema contains "null", remove it and set nullable to true instead
68+
// and if sub-schema contains enum of "null", remove it and set nullable to true instead
6869
final String SIMPLIFY_ONEOF_ANYOF = "SIMPLIFY_ONEOF_ANYOF";
6970
boolean simplifyOneOfAnyOf;
7071

@@ -578,7 +579,7 @@ private Schema processSimplifyAnyOfStringAndEnumString(Schema schema) {
578579
return schema;
579580
}
580581

581-
Schema s0 = null, s1 = null;
582+
Schema result = null, s0 = null, s1 = null;
582583
if (schema.getAnyOf().size() == 2) {
583584
s0 = ModelUtils.unaliasSchema(openAPI, (Schema) schema.getAnyOf().get(0));
584585
s1 = ModelUtils.unaliasSchema(openAPI, (Schema) schema.getAnyOf().get(1));
@@ -592,15 +593,27 @@ private Schema processSimplifyAnyOfStringAndEnumString(Schema schema) {
592593
// find the string schema (not enum)
593594
if (s0 instanceof StringSchema && s1 instanceof StringSchema) {
594595
if (((StringSchema) s0).getEnum() != null) { // s0 is enum, s1 is string
595-
return (StringSchema) s1;
596+
result = (StringSchema) s1;
596597
} else if (((StringSchema) s1).getEnum() != null) { // s1 is enum, s0 is string
597-
return (StringSchema) s0;
598+
result = (StringSchema) s0;
598599
} else { // both are string
599-
return schema;
600+
result = schema;
600601
}
601602
} else {
602-
return schema;
603+
result = schema;
604+
}
605+
606+
// set nullable
607+
if (schema.getNullable() != null) {
608+
result.setNullable(schema.getNullable());
609+
}
610+
611+
// set default
612+
if (schema.getDefault() != null) {
613+
result.setDefault(schema.getDefault());
603614
}
615+
616+
return result;
604617
}
605618

606619
/**
@@ -616,11 +629,22 @@ private Schema processSimplifyOneOf(Schema schema) {
616629
}
617630

618631
if (schema.getOneOf() != null && !schema.getOneOf().isEmpty()) {
619-
// convert null sub-schema to `nullable: true`
620632
for (int i = 0; i < schema.getOneOf().size(); i++) {
633+
// convert null sub-schema to `nullable: true`
621634
if (schema.getOneOf().get(i) == null || ((Schema) schema.getOneOf().get(i)).getType() == null) {
622635
schema.getOneOf().remove(i);
623636
schema.setNullable(true);
637+
continue;
638+
}
639+
640+
// convert enum of null only to `nullable:true`
641+
Schema oneOfElement = ModelUtils.getReferencedSchema(openAPI, (Schema) schema.getOneOf().get(i));
642+
if (oneOfElement.getEnum() != null && oneOfElement.getEnum().size() == 1) {
643+
if ("null".equals(String.valueOf(oneOfElement.getEnum().get(0)))) {
644+
schema.setNullable(true);
645+
schema.getOneOf().remove(i);
646+
continue;
647+
}
624648
}
625649
}
626650

@@ -649,11 +673,22 @@ private Schema processSimplifyAnyOf(Schema schema) {
649673
}
650674

651675
if (schema.getAnyOf() != null && !schema.getAnyOf().isEmpty()) {
652-
// convert null sub-schema to `nullable: true`
653676
for (int i = 0; i < schema.getAnyOf().size(); i++) {
677+
// convert null sub-schema to `nullable: true`
654678
if (schema.getAnyOf().get(i) == null || ((Schema) schema.getAnyOf().get(i)).getType() == null) {
655679
schema.getAnyOf().remove(i);
656680
schema.setNullable(true);
681+
continue;
682+
}
683+
684+
// convert enum of null only to `nullable:true`
685+
Schema anyOfElement = ModelUtils.getReferencedSchema(openAPI, (Schema) schema.getAnyOf().get(i));
686+
if (anyOfElement.getEnum() != null && anyOfElement.getEnum().size() == 1) {
687+
if ("null".equals(String.valueOf(anyOfElement.getEnum().get(0)))) {
688+
schema.setNullable(true);
689+
schema.getAnyOf().remove(i);
690+
continue;
691+
}
657692
}
658693
}
659694

@@ -721,4 +756,4 @@ private void processAddUnsignedToIntegerWithInvalidMaxValue(Schema schema) {
721756
}
722757

723758
// ===================== end of rules =====================
724-
}
759+
}

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

-180
Original file line numberDiff line numberDiff line change
@@ -4342,184 +4342,4 @@ public void testInlineEnumType() {
43424342
Assert.assertFalse(inlineEnumSchemaProperty.isPrimitiveType);
43434343
}
43444344

4345-
@Test
4346-
public void testOpenAPINormalizerRefAsParentInAllOf() {
4347-
// to test the rule REF_AS_PARENT_IN_ALLOF
4348-
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/allOf_extension_parent.yaml");
4349-
4350-
Schema schema = openAPI.getComponents().getSchemas().get("AnotherPerson");
4351-
assertNull(schema.getExtensions());
4352-
4353-
Schema schema2 = openAPI.getComponents().getSchemas().get("Person");
4354-
assertEquals(schema2.getExtensions().get("x-parent"), "abstract");
4355-
4356-
Map<String, String> options = new HashMap<>();
4357-
options.put("REF_AS_PARENT_IN_ALLOF", "true");
4358-
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
4359-
openAPINormalizer.normalize();
4360-
4361-
Schema schema3 = openAPI.getComponents().getSchemas().get("AnotherPerson");
4362-
assertEquals(schema3.getExtensions().get("x-parent"), true);
4363-
4364-
Schema schema4 = openAPI.getComponents().getSchemas().get("AnotherParent");
4365-
assertEquals(schema4.getExtensions().get("x-parent"), true);
4366-
4367-
Schema schema5 = openAPI.getComponents().getSchemas().get("Person");
4368-
assertEquals(schema5.getExtensions().get("x-parent"), "abstract");
4369-
}
4370-
4371-
@Test
4372-
public void testOpenAPINormalizerEnableKeepOnlyFirstTagInOperation() {
4373-
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml");
4374-
4375-
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getTags().size(), 2);
4376-
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getTags().size(), 1);
4377-
4378-
Map<String, String> options = new HashMap<>();
4379-
options.put("KEEP_ONLY_FIRST_TAG_IN_OPERATION", "true");
4380-
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
4381-
openAPINormalizer.normalize();
4382-
4383-
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getTags().size(), 1);
4384-
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getTags().size(), 1);
4385-
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getTags().get(0), "person");
4386-
}
4387-
4388-
@Test
4389-
public void testOpenAPINormalizerRemoveAnyOfOneOfAndKeepPropertiesOnly() {
4390-
// to test the rule REMOVE_ANYOF_ONEOF_AND_KEEP_PROPERTIIES_ONLY
4391-
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/removeAnyOfOneOfAndKeepPropertiesOnly_test.yaml");
4392-
4393-
Schema schema = openAPI.getComponents().getSchemas().get("Person");
4394-
assertEquals(schema.getAnyOf().size(), 2);
4395-
4396-
Map<String, String> options = new HashMap<>();
4397-
options.put("REMOVE_ANYOF_ONEOF_AND_KEEP_PROPERTIES_ONLY", "true");
4398-
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
4399-
openAPINormalizer.normalize();
4400-
4401-
Schema schema3 = openAPI.getComponents().getSchemas().get("Person");
4402-
assertNull(schema.getAnyOf());
4403-
}
4404-
4405-
@Test
4406-
public void testOpenAPINormalizerSimplifyOneOfAnyOfStringAndEnumString() {
4407-
// to test the rule SIMPLIFY_ONEOF_ANYOF_STRING_AND_ENUM_STRING
4408-
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyAnyOfStringAndEnumString_test.yaml");
4409-
4410-
Schema schema = openAPI.getComponents().getSchemas().get("AnyOfTest");
4411-
assertEquals(schema.getAnyOf().size(), 2);
4412-
4413-
Map<String, String> options = new HashMap<>();
4414-
options.put("SIMPLIFY_ANYOF_STRING_AND_ENUM_STRING", "true");
4415-
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
4416-
openAPINormalizer.normalize();
4417-
4418-
Schema schema3 = openAPI.getComponents().getSchemas().get("AnyOfTest");
4419-
assertNull(schema3.getAnyOf());
4420-
assertTrue(schema3 instanceof StringSchema);
4421-
}
4422-
4423-
@Test
4424-
public void testOpenAPINormalizerSimplifyOneOfAnyOf() {
4425-
// to test the rule SIMPLIFY_ONEOF_ANYOF
4426-
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyOneOfAnyOf_test.yaml");
4427-
4428-
Schema schema = openAPI.getComponents().getSchemas().get("AnyOfTest");
4429-
assertEquals(schema.getAnyOf().size(), 2);
4430-
assertNull(schema.getNullable());
4431-
4432-
Schema schema2 = openAPI.getComponents().getSchemas().get("OneOfTest");
4433-
assertEquals(schema2.getOneOf().size(), 2);
4434-
assertNull(schema2.getNullable());
4435-
4436-
Schema schema5 = openAPI.getComponents().getSchemas().get("OneOfNullableTest");
4437-
assertEquals(schema5.getOneOf().size(), 3);
4438-
assertNull(schema5.getNullable());
4439-
4440-
Map<String, String> options = new HashMap<>();
4441-
options.put("SIMPLIFY_ONEOF_ANYOF", "true");
4442-
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
4443-
openAPINormalizer.normalize();
4444-
4445-
Schema schema3 = openAPI.getComponents().getSchemas().get("AnyOfTest");
4446-
assertNull(schema3.getAnyOf());
4447-
assertTrue(schema3 instanceof StringSchema);
4448-
assertTrue(schema3.getNullable());
4449-
4450-
Schema schema4 = openAPI.getComponents().getSchemas().get("OneOfTest");
4451-
assertNull(schema4.getOneOf());
4452-
assertTrue(schema4 instanceof IntegerSchema);
4453-
4454-
Schema schema6 = openAPI.getComponents().getSchemas().get("OneOfNullableTest");
4455-
assertEquals(schema6.getOneOf().size(), 2);
4456-
assertTrue(schema6.getNullable());
4457-
}
4458-
4459-
@Test
4460-
public void testOpenAPINormalizerSimplifyBooleanEnum() {
4461-
// to test the rule SIMPLIFY_BOOLEAN_ENUM
4462-
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyBooleanEnum_test.yaml");
4463-
4464-
Schema schema = openAPI.getComponents().getSchemas().get("BooleanEnumTest");
4465-
assertEquals(schema.getProperties().size(), 3);
4466-
assertTrue(schema.getProperties().get("boolean_enum") instanceof BooleanSchema);
4467-
BooleanSchema bs = (BooleanSchema) schema.getProperties().get("boolean_enum");
4468-
assertEquals(bs.getEnum().size(), 2);
4469-
4470-
Map<String, String> options = new HashMap<>();
4471-
options.put("SIMPLIFY_BOOLEAN_ENUM", "true");
4472-
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
4473-
openAPINormalizer.normalize();
4474-
4475-
Schema schema3 = openAPI.getComponents().getSchemas().get("BooleanEnumTest");
4476-
assertEquals(schema.getProperties().size(), 3);
4477-
assertTrue(schema.getProperties().get("boolean_enum") instanceof BooleanSchema);
4478-
BooleanSchema bs2 = (BooleanSchema) schema.getProperties().get("boolean_enum");
4479-
assertNull(bs2.getEnum()); //ensure the enum has been erased
4480-
}
4481-
4482-
@Test
4483-
public void testOpenAPINormalizerSetTagsInAllOperations() {
4484-
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml");
4485-
4486-
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getTags().size(), 2);
4487-
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getTags().size(), 1);
4488-
4489-
Map<String, String> options = new HashMap<>();
4490-
options.put("SET_TAGS_FOR_ALL_OPERATIONS", "core");
4491-
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
4492-
openAPINormalizer.normalize();
4493-
4494-
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getTags().size(), 1);
4495-
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getTags().size(), 1);
4496-
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getTags().get(0), "core");
4497-
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getTags().get(0), "core");
4498-
}
4499-
4500-
@Test
4501-
public void testAddUnsignedToIntegerWithInvalidMaxValue() {
4502-
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/addUnsignedToIntegerWithInvalidMaxValue_test.yaml");
4503-
4504-
Schema person = openAPI.getComponents().getSchemas().get("Person");
4505-
assertNull(((Schema)person.getProperties().get("integer")).getExtensions());
4506-
assertNull(((Schema)person.getProperties().get("int32")).getExtensions());
4507-
assertNull(((Schema)person.getProperties().get("int64")).getExtensions());
4508-
assertNull(((Schema)person.getProperties().get("integer_max")).getExtensions());
4509-
assertNull(((Schema)person.getProperties().get("int32_max")).getExtensions());
4510-
assertNull(((Schema)person.getProperties().get("int64_max")).getExtensions());
4511-
4512-
Map<String, String> options = new HashMap<>();
4513-
options.put("ADD_UNSIGNED_TO_INTEGER_WITH_INVALID_MAX_VALUE", "true");
4514-
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
4515-
openAPINormalizer.normalize();
4516-
4517-
Schema person2 = openAPI.getComponents().getSchemas().get("Person");
4518-
assertNull(((Schema)person2.getProperties().get("integer")).getExtensions());
4519-
assertNull(((Schema)person2.getProperties().get("int32")).getExtensions());
4520-
assertNull(((Schema)person2.getProperties().get("int64")).getExtensions());
4521-
assertTrue((Boolean)((Schema)person2.getProperties().get("integer_max")).getExtensions().get("x-unsigned"));
4522-
assertTrue((Boolean)((Schema)person2.getProperties().get("int32_max")).getExtensions().get("x-unsigned"));
4523-
assertTrue((Boolean)((Schema)person2.getProperties().get("int64_max")).getExtensions().get("x-unsigned"));
4524-
}
45254345
}

0 commit comments

Comments
 (0)