Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/configs/kotlin-model-prefix-type-mapping-echo-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ additionalProperties:
enumPropertyNaming: UPPERCASE
serializationLibrary: gson
openapiNormalizer:
SIMPLIFY_ONEOF_ANYOF: false
SIMPLIFY_ONEOF_ANYOF_ALLOF: false
2 changes: 1 addition & 1 deletion bin/configs/kotlin-model-prefix-type-mapping.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ additionalProperties:
serializationLibrary: gson
generateOneOfAnyOfWrappers: true
openapiNormalizer:
SIMPLIFY_ONEOF_ANYOF: false
SIMPLIFY_ONEOF_ANYOF_ALLOF: false
6 changes: 3 additions & 3 deletions docs/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ Another useful option is `inlineSchemaOptions`, which allows you to customize ho

OpenAPI Normalizer transforms the input OpenAPI doc/spec (which may not perfectly conform to the specification) to make it workable with OpenAPI Generator. A few rules are switched on by default since 7.0.0 release:

- SIMPLIFY_ONEOF_ANYOF
- SIMPLIFY_ONEOF_ANYOF_ALLOF
- SIMPLIFY_BOOLEAN_ENUM
- SIMPLIFY_ONEOF_ANYOF_ENUM
- REFACTOR_ALLOF_WITH_PROPERTIES_ONLY
Expand Down Expand Up @@ -581,11 +581,11 @@ 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) 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.
- `SIMPLIFY_ONEOF_ANYOF_ALLOF`: when set to `true`, simplify oneOf/anyOf/allOf by 1) removing null (sub-schema) or enum of null (sub-schema) and setting nullable to true instead, and 2) simplifying oneOf/anyOf/allOf with a single sub-schema to just the sub-schema itself.

Example:
```
java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/simplifyOneOfAnyOf_test.yaml -o /tmp/java-okhttp/ --openapi-normalizer SIMPLIFY_ONEOF_ANYOF=true
java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/simplifyOneOfAnyOf_test.yaml -o /tmp/java-okhttp/ --openapi-normalizer SIMPLIFY_ONEOF_ANYOF_ALLOF=true
```

- `KEEP_ONLY_FIRST_TAG_IN_OPERATION`: when set to `true`, only keep the first tag in operation if there are more than one tag defined.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
import java.util.stream.Collectors;

import static org.openapitools.codegen.CodegenConstants.*;
import static org.openapitools.codegen.utils.ModelUtils.simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema;
import static org.openapitools.codegen.utils.ModelUtils.simplifyOneOfAnyOfAllOfWithOnlyOneNonNullSubSchema;
import static org.openapitools.codegen.utils.StringUtils.getUniqueString;

public class OpenAPINormalizer {
Expand Down Expand Up @@ -75,7 +75,7 @@ public class OpenAPINormalizer {
// when set to true, only keep the first tag in operation if there are more than one tag defined.
final String KEEP_ONLY_FIRST_TAG_IN_OPERATION = "KEEP_ONLY_FIRST_TAG_IN_OPERATION";

// when set to true, complex composed schemas (a mix of oneOf/anyOf/anyOf and properties) with
// when set to true, complex composed schemas (a mix of oneOf/anyOf and properties) with
// oneOf/anyOf containing only `required` and no properties (these are properties inter-dependency rules)
// are removed as most generators cannot handle such case at the moment
final String REMOVE_ANYOF_ONEOF_AND_KEEP_PROPERTIES_ONLY = "REMOVE_ANYOF_ONEOF_AND_KEEP_PROPERTIES_ONLY";
Expand All @@ -88,10 +88,10 @@ public class OpenAPINormalizer {
// to just string
final String SIMPLIFY_ANYOF_STRING_AND_ENUM_STRING = "SIMPLIFY_ANYOF_STRING_AND_ENUM_STRING";

// when set to true, oneOf/anyOf schema with only one sub-schema is simplified to just the sub-schema
// when set to true, oneOf/anyOf/allOf 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";
final String SIMPLIFY_ONEOF_ANYOF_ALLOF = "SIMPLIFY_ONEOF_ANYOF_ALLOF";

// when set to true, boolean enum will be converted to just boolean
final String SIMPLIFY_BOOLEAN_ENUM = "SIMPLIFY_BOOLEAN_ENUM";
Expand Down Expand Up @@ -205,7 +205,7 @@ public OpenAPINormalizer(OpenAPI openAPI, Map<String, String> inputRules) {
ruleNames.add(REF_AS_PARENT_IN_ALLOF);
ruleNames.add(REMOVE_ANYOF_ONEOF_AND_KEEP_PROPERTIES_ONLY);
ruleNames.add(SIMPLIFY_ANYOF_STRING_AND_ENUM_STRING);
ruleNames.add(SIMPLIFY_ONEOF_ANYOF);
ruleNames.add(SIMPLIFY_ONEOF_ANYOF_ALLOF);
ruleNames.add(SIMPLIFY_BOOLEAN_ENUM);
ruleNames.add(KEEP_ONLY_FIRST_TAG_IN_OPERATION);
ruleNames.add(SET_TAGS_FOR_ALL_OPERATIONS);
Expand All @@ -227,7 +227,7 @@ public OpenAPINormalizer(OpenAPI openAPI, Map<String, String> inputRules) {
ruleNames.add(REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING);

// rules that are default to true
rules.put(SIMPLIFY_ONEOF_ANYOF, true);
rules.put(SIMPLIFY_ONEOF_ANYOF_ALLOF, true);
rules.put(SIMPLIFY_BOOLEAN_ENUM, true);
rules.put(SIMPLIFY_ONEOF_ANYOF_ENUM, true);
rules.put(REFACTOR_ALLOF_WITH_PROPERTIES_ONLY, true);
Expand Down Expand Up @@ -929,7 +929,7 @@ public Schema normalizeSchema(Schema schema, Set<Schema> visitedSchemas) {

if (ModelUtils.isArraySchema(schema)) { // array
Schema result = normalizeArraySchema(schema);
normalizeSchema(result.getItems(), visitedSchemas);
result.setItems(normalizeSchema(result.getItems(), visitedSchemas));
return result;
} else if (ModelUtils.isOneOf(schema)) { // oneOf
return normalizeOneOf(schema, visitedSchemas);
Expand Down Expand Up @@ -1203,6 +1203,7 @@ protected Schema normalizeAllOf(Schema schema, Set<Schema> visitedSchemas) {

// process rules here
processUseAllOfRefAsParent(schema);
schema = processSimplifyAllOf(schema);

return schema;
}
Expand Down Expand Up @@ -1306,6 +1307,39 @@ protected Schema normalizeComplexComposedSchema(Schema schema, Set<Schema> visit
// ===================== a list of rules =====================
// all rules (functions ) start with the word "process"


/**
* If the schema is allOf and the sub-schemas is null, set `nullable: true`
* instead.
* If there's only one sub-schema, simply return the sub-schema directly.
*
* @param schema Schema
* @return Schema
*/
protected Schema processSimplifyAllOf(Schema schema) {
if (!getRule(SIMPLIFY_ONEOF_ANYOF_ALLOF)) {
return schema;
}

List<Schema> allOfSchemas = schema.getAllOf();
if (allOfSchemas != null) {
// allOf containing $refs is intentional composition (inheritance, or $ref+sibling wrapping
// from normalizeReferenceSchema) — never simplify these, as the referenced schema may have
// no type/description yet still be a real model (e.g. after oneOf removal by discriminator mapping)
if (allOfSchemas.stream().anyMatch(s -> StringUtils.isNotEmpty(s.get$ref()))) {
return schema;
}
schema = simplifyOneOfAnyOfAllOfWithOnlyOneNonNullSubSchema(openAPI, schema, allOfSchemas);
if (ModelUtils.isIntegerSchema(schema) || ModelUtils.isNumberSchema(schema) || ModelUtils.isStringSchema(schema) || ModelUtils.isBooleanSchema(schema)) {
if (schema.getSpecVersion().equals(SpecVersion.V30)) {
schema.setAllOf(null);
}
}
}

return schema;
}

/**
* Child schemas in `allOf` is considered a parent if it's a `$ref` (instead of inline schema).
*
Expand Down Expand Up @@ -1703,7 +1737,7 @@ protected Schema createSimplifiedEnumSchema(Schema originalSchema, Map<Object, S
* @return Schema
*/
protected Schema processSimplifyOneOf(Schema schema) {
if (!getRule(SIMPLIFY_ONEOF_ANYOF)) {
if (!getRule(SIMPLIFY_ONEOF_ANYOF_ALLOF)) {
return schema;
}

Expand Down Expand Up @@ -1736,8 +1770,8 @@ protected Schema processSimplifyOneOf(Schema schema) {
}
}

schema = simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, schema, oneOfSchemas);
if (ModelUtils.isIntegerSchema(schema) || ModelUtils.isNumberSchema(schema) || ModelUtils.isStringSchema(schema)) {
schema = simplifyOneOfAnyOfAllOfWithOnlyOneNonNullSubSchema(openAPI, schema, oneOfSchemas);
if (ModelUtils.isIntegerSchema(schema) || ModelUtils.isNumberSchema(schema) || ModelUtils.isStringSchema(schema) || ModelUtils.isBooleanSchema(schema)) {
if (schema.getSpecVersion().equals(SpecVersion.V30)) {
schema.setOneOf(null);
} //else {
Expand Down Expand Up @@ -2071,7 +2105,7 @@ protected Schema processSetMapToNullable(Schema schema) {
* @return Schema
*/
protected Schema processSimplifyAnyOf(Schema schema) {
if (!getRule(SIMPLIFY_ONEOF_ANYOF)) {
if (!getRule(SIMPLIFY_ONEOF_ANYOF_ALLOF)) {
return schema;
}

Expand Down Expand Up @@ -2104,7 +2138,7 @@ protected Schema processSimplifyAnyOf(Schema schema) {
}
}

schema = simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, schema, anyOfSchemas);
schema = simplifyOneOfAnyOfAllOfWithOnlyOneNonNullSubSchema(openAPI, schema, anyOfSchemas);
}

return schema;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2323,7 +2323,7 @@ public static Schema cloneSchema(Schema schema, boolean openapi31) {
* @param subSchemas The oneOf or AnyOf schemas
* @return The simplified schema
*/
public static Schema simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(OpenAPI openAPI, Schema schema, List<Schema> subSchemas) {
public static Schema simplifyOneOfAnyOfAllOfWithOnlyOneNonNullSubSchema(OpenAPI openAPI, Schema schema, List<Schema> subSchemas) {
if (subSchemas.removeIf(subSchema -> isNullTypeSchema(openAPI, subSchema))) {
schema.setNullable(true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ public void testSimplifyOneOfAnyOfEnum() throws Exception {

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

Schema schema = openAPI.getComponents().getSchemas().get("AnyOfTest");
Expand Down Expand Up @@ -303,7 +303,7 @@ public void testOpenAPINormalizerSimplifyOneOfAnyOf() {
assertEquals(schema19.getAnyOf().size(), 1);

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

Expand Down Expand Up @@ -353,6 +353,33 @@ public void testOpenAPINormalizerSimplifyOneOfAnyOf() {
assertEquals(schema20.getEnum().size(), 2);
}

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

Schema schema = openAPI.getComponents().getSchemas().get("AllOfTest");
assertEquals(schema.getAllOf().size(), 4);
assertNull(schema.getNullable());

Schema schema3 = openAPI.getComponents().getSchemas().get("SingleAllOfTest");
assertEquals(schema3.getAllOf().size(), 1);

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

Schema schema2 = openAPI.getComponents().getSchemas().get("AllOfTest");
assertNull(schema2.getAllOf());
assertTrue(schema2 instanceof StringSchema);
assertTrue(schema2.getNullable());

Schema schema4 = openAPI.getComponents().getSchemas().get("SingleAllOfTest");
assertEquals(schema4.getAllOf(), null);
assertEquals(schema4.getType(), "string");
assertEquals(schema4.getEnum().size(), 2);
}

@Test
public void testOpenAPINormalizerSimplifyOneOfWithSingleRef() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyOneOfAnyOf_test.yaml");
Expand All @@ -361,7 +388,7 @@ public void testOpenAPINormalizerSimplifyOneOfWithSingleRef() {
assertEquals(((Schema) oneOfWithSingleRef.getProperties().get("number")).getOneOf().size(), 1);

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

Expand Down Expand Up @@ -494,15 +521,15 @@ public void testAddUnsignedToIntegerWithInvalidMaxValue() {

@Test
public void testOpenAPINormalizerConvertEnumNullToNullable() {
// to test the rule SIMPLIFY_ONEOF_ANYOF, which now also covers CONVERT_ENUM_NULL_TO_NULLABLE (removed)
// to test the rule SIMPLIFY_ONEOF_ANYOF_ALLOF, which now also covers CONVERT_ENUM_NULL_TO_NULLABLE (removed)
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/convertEnumNullToNullable_test.yaml");

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

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

Expand All @@ -513,16 +540,16 @@ public void testOpenAPINormalizerConvertEnumNullToNullable() {

@Test
public void testOpenAPINormalizerDefaultRules() {
// to test the rule SIMPLIFY_ONEOF_ANYOF, which now also covers CONVERT_ENUM_NULL_TO_NULLABLE (removed)
// to test the rule SIMPLIFY_ONEOF_ANYOF_ALLOF, which now also covers CONVERT_ENUM_NULL_TO_NULLABLE (removed)
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/convertEnumNullToNullable_test.yaml");

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

Map<String, String> options = new HashMap<>();
// SIMPLIFY_ONEOF_ANYOF is switched on by default as part of v7.0.0 release
//options.put("SIMPLIFY_ONEOF_ANYOF", "true");
// SIMPLIFY_ONEOF_ANYOF_ALLOF is switched on by default as part of v7.0.0 release
//options.put("SIMPLIFY_ONEOF_ANYOF_ALLOF", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();

Expand All @@ -533,7 +560,7 @@ public void testOpenAPINormalizerDefaultRules() {

@Test
public void testOpenAPINormalizerDisableAll() {
// to test the rule SIMPLIFY_ONEOF_ANYOF, which now also covers CONVERT_ENUM_NULL_TO_NULLABLE (removed)
// to test the rule SIMPLIFY_ONEOF_ANYOF_ALLOF, which now also covers CONVERT_ENUM_NULL_TO_NULLABLE (removed)
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/convertEnumNullToNullable_test.yaml");

// before test
Expand Down Expand Up @@ -1321,7 +1348,7 @@ public void testSetPrimitiveTypesToNullable() {

@Test
public void testOpenAPINormalizerSimplifyOneOfAnyOf31SpecForIssue18184() {
// to test the rule SIMPLIFY_ONEOF_ANYOF in 3.1 spec
// to test the rule SIMPLIFY_ONEOF_ANYOF_ALLOF in 3.1 spec
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/issue_18184.yaml");
// test spec contains anyOf with a ref to enum and another scheme type is null

Expand Down Expand Up @@ -1449,7 +1476,7 @@ public void testOpenAPINormalizerProcessingArraySchema31NullabilitySpec() {

@Test
public void testOpenAPINormalizerSimplifyOneOfAnyOf31Spec() {
// to test the rule SIMPLIFY_ONEOF_ANYOF with 3.1 spec
// to test the rule SIMPLIFY_ONEOF_ANYOF_ALLOF with 3.1 spec
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/simplifyOneOfAnyOf_test.yaml");

Schema schema = openAPI.getComponents().getSchemas().get("AnyOfTest");
Expand Down Expand Up @@ -1498,7 +1525,7 @@ public void testOpenAPINormalizerSimplifyOneOfAnyOf31Spec() {

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

Expand Down Expand Up @@ -1573,7 +1600,7 @@ public void testOpenAPINormalizerSimplifyOneOfWithSingleRef31Spec() {
assertEquals(((Schema) oneOfWithSingleRef.getProperties().get("number")).getOneOf().size(), 1);

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

Expand Down
Loading