Skip to content

Commit

Permalink
[OpenAPI Normalizer] update SIMPLIFY_ONEOF_ANYOF to convert enum of n…
Browse files Browse the repository at this point in the history
…ull to nullable (#14898)

* reorganize openapi normalizer tests

* add the logic to simply oneof anyof rule instead

* minor fix
  • Loading branch information
wing328 committed Mar 9, 2023
1 parent 77dd499 commit f9efb7b
Show file tree
Hide file tree
Showing 5 changed files with 345 additions and 189 deletions.
2 changes: 1 addition & 1 deletion docs/customization.md
Expand Up @@ -485,7 +485,7 @@ Example:
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
```

- `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.
- `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.

Example:
```
Expand Down
Expand Up @@ -65,6 +65,7 @@ public class OpenAPINormalizer {

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

Expand Down Expand Up @@ -578,7 +579,7 @@ private Schema processSimplifyAnyOfStringAndEnumString(Schema schema) {
return schema;
}

Schema s0 = null, s1 = null;
Schema result = null, s0 = null, s1 = null;
if (schema.getAnyOf().size() == 2) {
s0 = ModelUtils.unaliasSchema(openAPI, (Schema) schema.getAnyOf().get(0));
s1 = ModelUtils.unaliasSchema(openAPI, (Schema) schema.getAnyOf().get(1));
Expand All @@ -592,15 +593,27 @@ private Schema processSimplifyAnyOfStringAndEnumString(Schema schema) {
// find the string schema (not enum)
if (s0 instanceof StringSchema && s1 instanceof StringSchema) {
if (((StringSchema) s0).getEnum() != null) { // s0 is enum, s1 is string
return (StringSchema) s1;
result = (StringSchema) s1;
} else if (((StringSchema) s1).getEnum() != null) { // s1 is enum, s0 is string
return (StringSchema) s0;
result = (StringSchema) s0;
} else { // both are string
return schema;
result = schema;
}
} else {
return schema;
result = schema;
}

// set nullable
if (schema.getNullable() != null) {
result.setNullable(schema.getNullable());
}

// set default
if (schema.getDefault() != null) {
result.setDefault(schema.getDefault());
}

return result;
}

/**
Expand All @@ -616,11 +629,22 @@ private Schema processSimplifyOneOf(Schema schema) {
}

if (schema.getOneOf() != null && !schema.getOneOf().isEmpty()) {
// convert null sub-schema to `nullable: true`
for (int i = 0; i < schema.getOneOf().size(); i++) {
// convert null sub-schema to `nullable: true`
if (schema.getOneOf().get(i) == null || ((Schema) schema.getOneOf().get(i)).getType() == null) {
schema.getOneOf().remove(i);
schema.setNullable(true);
continue;
}

// convert enum of null only to `nullable:true`
Schema oneOfElement = ModelUtils.getReferencedSchema(openAPI, (Schema) schema.getOneOf().get(i));
if (oneOfElement.getEnum() != null && oneOfElement.getEnum().size() == 1) {
if ("null".equals(String.valueOf(oneOfElement.getEnum().get(0)))) {
schema.setNullable(true);
schema.getOneOf().remove(i);
continue;
}
}
}

Expand Down Expand Up @@ -649,11 +673,22 @@ private Schema processSimplifyAnyOf(Schema schema) {
}

if (schema.getAnyOf() != null && !schema.getAnyOf().isEmpty()) {
// convert null sub-schema to `nullable: true`
for (int i = 0; i < schema.getAnyOf().size(); i++) {
// convert null sub-schema to `nullable: true`
if (schema.getAnyOf().get(i) == null || ((Schema) schema.getAnyOf().get(i)).getType() == null) {
schema.getAnyOf().remove(i);
schema.setNullable(true);
continue;
}

// convert enum of null only to `nullable:true`
Schema anyOfElement = ModelUtils.getReferencedSchema(openAPI, (Schema) schema.getAnyOf().get(i));
if (anyOfElement.getEnum() != null && anyOfElement.getEnum().size() == 1) {
if ("null".equals(String.valueOf(anyOfElement.getEnum().get(0)))) {
schema.setNullable(true);
schema.getAnyOf().remove(i);
continue;
}
}
}

Expand Down Expand Up @@ -721,4 +756,4 @@ private void processAddUnsignedToIntegerWithInvalidMaxValue(Schema schema) {
}

// ===================== end of rules =====================
}
}
Expand Up @@ -4342,184 +4342,4 @@ public void testInlineEnumType() {
Assert.assertFalse(inlineEnumSchemaProperty.isPrimitiveType);
}

@Test
public void testOpenAPINormalizerRefAsParentInAllOf() {
// to test the rule REF_AS_PARENT_IN_ALLOF
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/allOf_extension_parent.yaml");

Schema schema = openAPI.getComponents().getSchemas().get("AnotherPerson");
assertNull(schema.getExtensions());

Schema schema2 = openAPI.getComponents().getSchemas().get("Person");
assertEquals(schema2.getExtensions().get("x-parent"), "abstract");

Map<String, String> options = new HashMap<>();
options.put("REF_AS_PARENT_IN_ALLOF", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();

Schema schema3 = openAPI.getComponents().getSchemas().get("AnotherPerson");
assertEquals(schema3.getExtensions().get("x-parent"), true);

Schema schema4 = openAPI.getComponents().getSchemas().get("AnotherParent");
assertEquals(schema4.getExtensions().get("x-parent"), true);

Schema schema5 = openAPI.getComponents().getSchemas().get("Person");
assertEquals(schema5.getExtensions().get("x-parent"), "abstract");
}

@Test
public void testOpenAPINormalizerEnableKeepOnlyFirstTagInOperation() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml");

assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getTags().size(), 2);
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getTags().size(), 1);

Map<String, String> options = new HashMap<>();
options.put("KEEP_ONLY_FIRST_TAG_IN_OPERATION", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();

assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getTags().size(), 1);
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getTags().size(), 1);
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getTags().get(0), "person");
}

@Test
public void testOpenAPINormalizerRemoveAnyOfOneOfAndKeepPropertiesOnly() {
// to test the rule REMOVE_ANYOF_ONEOF_AND_KEEP_PROPERTIIES_ONLY
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/removeAnyOfOneOfAndKeepPropertiesOnly_test.yaml");

Schema schema = openAPI.getComponents().getSchemas().get("Person");
assertEquals(schema.getAnyOf().size(), 2);

Map<String, String> options = new HashMap<>();
options.put("REMOVE_ANYOF_ONEOF_AND_KEEP_PROPERTIES_ONLY", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();

Schema schema3 = openAPI.getComponents().getSchemas().get("Person");
assertNull(schema.getAnyOf());
}

@Test
public void testOpenAPINormalizerSimplifyOneOfAnyOfStringAndEnumString() {
// to test the rule SIMPLIFY_ONEOF_ANYOF_STRING_AND_ENUM_STRING
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyAnyOfStringAndEnumString_test.yaml");

Schema schema = openAPI.getComponents().getSchemas().get("AnyOfTest");
assertEquals(schema.getAnyOf().size(), 2);

Map<String, String> options = new HashMap<>();
options.put("SIMPLIFY_ANYOF_STRING_AND_ENUM_STRING", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();

Schema schema3 = openAPI.getComponents().getSchemas().get("AnyOfTest");
assertNull(schema3.getAnyOf());
assertTrue(schema3 instanceof StringSchema);
}

@Test
public void testOpenAPINormalizerSimplifyOneOfAnyOf() {
// to test the rule SIMPLIFY_ONEOF_ANYOF
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyOneOfAnyOf_test.yaml");

Schema schema = openAPI.getComponents().getSchemas().get("AnyOfTest");
assertEquals(schema.getAnyOf().size(), 2);
assertNull(schema.getNullable());

Schema schema2 = openAPI.getComponents().getSchemas().get("OneOfTest");
assertEquals(schema2.getOneOf().size(), 2);
assertNull(schema2.getNullable());

Schema schema5 = openAPI.getComponents().getSchemas().get("OneOfNullableTest");
assertEquals(schema5.getOneOf().size(), 3);
assertNull(schema5.getNullable());

Map<String, String> options = new HashMap<>();
options.put("SIMPLIFY_ONEOF_ANYOF", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();

Schema schema3 = openAPI.getComponents().getSchemas().get("AnyOfTest");
assertNull(schema3.getAnyOf());
assertTrue(schema3 instanceof StringSchema);
assertTrue(schema3.getNullable());

Schema schema4 = openAPI.getComponents().getSchemas().get("OneOfTest");
assertNull(schema4.getOneOf());
assertTrue(schema4 instanceof IntegerSchema);

Schema schema6 = openAPI.getComponents().getSchemas().get("OneOfNullableTest");
assertEquals(schema6.getOneOf().size(), 2);
assertTrue(schema6.getNullable());
}

@Test
public void testOpenAPINormalizerSimplifyBooleanEnum() {
// to test the rule SIMPLIFY_BOOLEAN_ENUM
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyBooleanEnum_test.yaml");

Schema schema = openAPI.getComponents().getSchemas().get("BooleanEnumTest");
assertEquals(schema.getProperties().size(), 3);
assertTrue(schema.getProperties().get("boolean_enum") instanceof BooleanSchema);
BooleanSchema bs = (BooleanSchema) schema.getProperties().get("boolean_enum");
assertEquals(bs.getEnum().size(), 2);

Map<String, String> options = new HashMap<>();
options.put("SIMPLIFY_BOOLEAN_ENUM", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();

Schema schema3 = openAPI.getComponents().getSchemas().get("BooleanEnumTest");
assertEquals(schema.getProperties().size(), 3);
assertTrue(schema.getProperties().get("boolean_enum") instanceof BooleanSchema);
BooleanSchema bs2 = (BooleanSchema) schema.getProperties().get("boolean_enum");
assertNull(bs2.getEnum()); //ensure the enum has been erased
}

@Test
public void testOpenAPINormalizerSetTagsInAllOperations() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml");

assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getTags().size(), 2);
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getTags().size(), 1);

Map<String, String> options = new HashMap<>();
options.put("SET_TAGS_FOR_ALL_OPERATIONS", "core");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();

assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getTags().size(), 1);
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getTags().size(), 1);
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getTags().get(0), "core");
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getTags().get(0), "core");
}

@Test
public void testAddUnsignedToIntegerWithInvalidMaxValue() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/addUnsignedToIntegerWithInvalidMaxValue_test.yaml");

Schema person = openAPI.getComponents().getSchemas().get("Person");
assertNull(((Schema)person.getProperties().get("integer")).getExtensions());
assertNull(((Schema)person.getProperties().get("int32")).getExtensions());
assertNull(((Schema)person.getProperties().get("int64")).getExtensions());
assertNull(((Schema)person.getProperties().get("integer_max")).getExtensions());
assertNull(((Schema)person.getProperties().get("int32_max")).getExtensions());
assertNull(((Schema)person.getProperties().get("int64_max")).getExtensions());

Map<String, String> options = new HashMap<>();
options.put("ADD_UNSIGNED_TO_INTEGER_WITH_INVALID_MAX_VALUE", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();

Schema person2 = openAPI.getComponents().getSchemas().get("Person");
assertNull(((Schema)person2.getProperties().get("integer")).getExtensions());
assertNull(((Schema)person2.getProperties().get("int32")).getExtensions());
assertNull(((Schema)person2.getProperties().get("int64")).getExtensions());
assertTrue((Boolean)((Schema)person2.getProperties().get("integer_max")).getExtensions().get("x-unsigned"));
assertTrue((Boolean)((Schema)person2.getProperties().get("int32_max")).getExtensions().get("x-unsigned"));
assertTrue((Boolean)((Schema)person2.getProperties().get("int64_max")).getExtensions().get("x-unsigned"));
}
}

0 comments on commit f9efb7b

Please sign in to comment.