Fix so that the oneOfAnyOf normalizer retains the read/write only attribute (#21737)

This commit is contained in:
Mattias Sehlstedt 2025-08-17 18:36:14 +02:00 committed by GitHub
parent 3ebb299981
commit 490de02971
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 102 additions and 14 deletions

View File

@ -33,12 +33,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.openapitools.codegen.utils.ModelUtils.simplyOneOfAnyOfWithOnlyOneNonNullSubSchema; import static org.openapitools.codegen.utils.ModelUtils.simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema;
import static org.openapitools.codegen.utils.StringUtils.getUniqueString; import static org.openapitools.codegen.utils.StringUtils.getUniqueString;
public class OpenAPINormalizer { public class OpenAPINormalizer {
@ -1318,7 +1317,7 @@ public class OpenAPINormalizer {
} }
} }
schema = simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, schema, oneOfSchemas); schema = simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, schema, oneOfSchemas);
if (ModelUtils.isIntegerSchema(schema) || ModelUtils.isNumberSchema(schema) || ModelUtils.isStringSchema(schema)) { if (ModelUtils.isIntegerSchema(schema) || ModelUtils.isNumberSchema(schema) || ModelUtils.isStringSchema(schema)) {
// TODO convert oneOf const to enum // TODO convert oneOf const to enum
@ -1445,7 +1444,7 @@ public class OpenAPINormalizer {
} }
} }
schema = simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, schema, anyOfSchemas); schema = simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, schema, anyOfSchemas);
} }
return schema; return schema;

View File

@ -2227,17 +2227,24 @@ public class ModelUtils {
* @param subSchemas The oneOf or AnyOf schemas * @param subSchemas The oneOf or AnyOf schemas
* @return The simplified schema * @return The simplified schema
*/ */
public static Schema simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(OpenAPI openAPI, Schema schema, List<Schema> subSchemas) { public static Schema simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(OpenAPI openAPI, Schema schema, List<Schema> subSchemas) {
if (subSchemas.removeIf(subSchema -> isNullTypeSchema(openAPI, subSchema))) { if (subSchemas.removeIf(subSchema -> isNullTypeSchema(openAPI, subSchema))) {
schema.setNullable(true); schema.setNullable(true);
} }
// if only one element left, simplify to just the element (schema) // if only one element left, simplify to just the element (schema)
if (subSchemas.size() == 1) { if (subSchemas.size() == 1) {
Schema<?> subSchema = subSchemas.get(0);
if (Boolean.TRUE.equals(schema.getNullable())) { // retain nullable setting if (Boolean.TRUE.equals(schema.getNullable())) { // retain nullable setting
subSchemas.get(0).setNullable(true); subSchema.setNullable(true);
} }
return subSchemas.get(0); if (Boolean.TRUE.equals(schema.getReadOnly())) {
subSchema.setReadOnly(true);
}
if (Boolean.TRUE.equals(schema.getWriteOnly())) {
subSchema.setWriteOnly(true);
}
return subSchema;
} }
return schema; return schema;

View File

@ -476,14 +476,14 @@ public class ModelUtilsTest {
} }
@Test @Test
public void simplyOneOfAnyOfWithOnlyOneNonNullSubSchema() { public void simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyOneOfAnyOf_test.yaml"); OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyOneOfAnyOf_test.yaml");
Schema schema; Schema schema;
List<Schema> subSchemas; List<Schema> subSchemas;
Schema anyOfWithSeveralSubSchemasButSingleNonNull = ModelUtils.getSchema(openAPI, "AnyOfTest"); Schema anyOfWithSeveralSubSchemasButSingleNonNull = ModelUtils.getSchema(openAPI, "AnyOfTest");
subSchemas = anyOfWithSeveralSubSchemasButSingleNonNull.getAnyOf(); subSchemas = anyOfWithSeveralSubSchemasButSingleNonNull.getAnyOf();
schema = ModelUtils.simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, anyOfWithSeveralSubSchemasButSingleNonNull, subSchemas); schema = ModelUtils.simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, anyOfWithSeveralSubSchemasButSingleNonNull, subSchemas);
assertNull(schema.getOneOf()); assertNull(schema.getOneOf());
assertNull(schema.getAnyOf()); assertNull(schema.getAnyOf());
assertTrue(schema.getNullable()); assertTrue(schema.getNullable());
@ -491,7 +491,7 @@ public class ModelUtilsTest {
Schema anyOfWithSingleNonNullSubSchema = ModelUtils.getSchema(openAPI, "Parent"); Schema anyOfWithSingleNonNullSubSchema = ModelUtils.getSchema(openAPI, "Parent");
subSchemas = ((Schema) anyOfWithSingleNonNullSubSchema.getProperties().get("number")).getAnyOf(); subSchemas = ((Schema) anyOfWithSingleNonNullSubSchema.getProperties().get("number")).getAnyOf();
schema = ModelUtils.simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, anyOfWithSingleNonNullSubSchema, subSchemas); schema = ModelUtils.simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, anyOfWithSingleNonNullSubSchema, subSchemas);
assertNull(schema.getOneOf()); assertNull(schema.getOneOf());
assertNull(schema.getAnyOf()); assertNull(schema.getAnyOf());
assertNull(schema.getNullable()); assertNull(schema.getNullable());
@ -499,7 +499,7 @@ public class ModelUtilsTest {
Schema oneOfWithSeveralSubSchemasButSingleNonNull = ModelUtils.getSchema(openAPI, "OneOfTest"); Schema oneOfWithSeveralSubSchemasButSingleNonNull = ModelUtils.getSchema(openAPI, "OneOfTest");
subSchemas = oneOfWithSeveralSubSchemasButSingleNonNull.getOneOf(); subSchemas = oneOfWithSeveralSubSchemasButSingleNonNull.getOneOf();
schema = ModelUtils.simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, oneOfWithSeveralSubSchemasButSingleNonNull, subSchemas); schema = ModelUtils.simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, oneOfWithSeveralSubSchemasButSingleNonNull, subSchemas);
assertNull(schema.getOneOf()); assertNull(schema.getOneOf());
assertNull(schema.getAnyOf()); assertNull(schema.getAnyOf());
assertTrue(schema.getNullable()); assertTrue(schema.getNullable());
@ -507,7 +507,7 @@ public class ModelUtilsTest {
Schema oneOfWithSingleNonNullSubSchema = ModelUtils.getSchema(openAPI, "ParentWithOneOfProperty"); Schema oneOfWithSingleNonNullSubSchema = ModelUtils.getSchema(openAPI, "ParentWithOneOfProperty");
subSchemas = ((Schema) oneOfWithSingleNonNullSubSchema.getProperties().get("number")).getOneOf(); subSchemas = ((Schema) oneOfWithSingleNonNullSubSchema.getProperties().get("number")).getOneOf();
schema = ModelUtils.simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, oneOfWithSingleNonNullSubSchema, subSchemas); schema = ModelUtils.simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, oneOfWithSingleNonNullSubSchema, subSchemas);
assertNull(schema.getOneOf()); assertNull(schema.getOneOf());
assertNull(schema.getAnyOf()); assertNull(schema.getAnyOf());
assertNull(schema.getNullable()); assertNull(schema.getNullable());
@ -515,7 +515,7 @@ public class ModelUtilsTest {
Schema oneOfWithSeveralSubSchemas = ModelUtils.getSchema(openAPI, "ParentWithPluralOneOfProperty"); Schema oneOfWithSeveralSubSchemas = ModelUtils.getSchema(openAPI, "ParentWithPluralOneOfProperty");
subSchemas = ((Schema) oneOfWithSeveralSubSchemas.getProperties().get("number")).getOneOf(); subSchemas = ((Schema) oneOfWithSeveralSubSchemas.getProperties().get("number")).getOneOf();
schema = ModelUtils.simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, oneOfWithSeveralSubSchemas, subSchemas); schema = ModelUtils.simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, oneOfWithSeveralSubSchemas, subSchemas);
assertNull(schema.getOneOf()); assertNull(schema.getOneOf());
assertNotNull(oneOfWithSeveralSubSchemas.getProperties().get("number")); assertNotNull(oneOfWithSeveralSubSchemas.getProperties().get("number"));
assertNull(schema.getAnyOf()); assertNull(schema.getAnyOf());
@ -523,6 +523,62 @@ public class ModelUtilsTest {
assertEquals(((Schema) oneOfWithSeveralSubSchemas.getProperties().get("number")).getOneOf().size(), 2); assertEquals(((Schema) oneOfWithSeveralSubSchemas.getProperties().get("number")).getOneOf().size(), 2);
} }
@Test
public void simplifyOneOfWithOnlyOneNonNullSubSchemaKeepsReadOnlyWriteOnlyAttribute() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyOneOfAnyOf_test.yaml");
Schema schema;
List<Schema> subSchemas;
Schema oneOfWithNullAndRefSubSchema = ModelUtils.getSchema(openAPI, "OneOfParentRefTest");
Schema numberPropertySchema = ((Schema) oneOfWithNullAndRefSubSchema.getProperties().get("number"));
subSchemas = numberPropertySchema.getOneOf();
schema = ModelUtils.simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, numberPropertySchema, subSchemas);
assertNull(schema.getOneOf());
assertNull(schema.getAnyOf());
assertTrue(schema.getNullable());
assertNull(schema.getReadOnly());
assertTrue(schema.getWriteOnly());
assertEquals(schema.get$ref(), "#/components/schemas/IntegerRef");
Schema number2PropertySchema = ((Schema) oneOfWithNullAndRefSubSchema.getProperties().get("number2"));
subSchemas = number2PropertySchema.getOneOf();
schema = ModelUtils.simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, number2PropertySchema, subSchemas);
assertNull(schema.getOneOf());
assertNull(schema.getAnyOf());
assertTrue(schema.getNullable());
assertTrue(schema.getReadOnly());
assertNull(schema.getWriteOnly());
assertEquals(schema.get$ref(), "#/components/schemas/IntegerRef");
}
@Test
public void simplifyAnyOfWithOnlyOneNonNullSubSchemaKeepsReadOnlyWriteOnlyAttribute() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyOneOfAnyOf_test.yaml");
Schema schema;
List<Schema> subSchemas;
Schema anyOfWithNullAndRefSubSchema = ModelUtils.getSchema(openAPI, "AnyOfParentRefTest");
Schema numberPropertySchema = ((Schema) anyOfWithNullAndRefSubSchema.getProperties().get("number"));
subSchemas = numberPropertySchema.getAnyOf();
schema = ModelUtils.simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, numberPropertySchema, subSchemas);
assertNull(schema.getOneOf());
assertNull(schema.getAnyOf());
assertTrue(schema.getNullable());
assertNull(schema.getReadOnly());
assertTrue(schema.getWriteOnly());
assertEquals(schema.get$ref(), "#/components/schemas/IntegerRef");
Schema number2PropertySchema = ((Schema) anyOfWithNullAndRefSubSchema.getProperties().get("number2"));
subSchemas = number2PropertySchema.getAnyOf();
schema = ModelUtils.simplifyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, number2PropertySchema, subSchemas);
assertNull(schema.getOneOf());
assertNull(schema.getAnyOf());
assertTrue(schema.getNullable());
assertTrue(schema.getReadOnly());
assertNull(schema.getWriteOnly());
assertEquals(schema.get$ref(), "#/components/schemas/IntegerRef");
}
@Test @Test
public void isNullTypeSchemaTest() { public void isNullTypeSchemaTest() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/null_schema_test.yaml"); OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/null_schema_test.yaml");

View File

@ -122,4 +122,30 @@ components:
- type: string - type: string
- type: integer - type: integer
- type: array - type: array
items: {} items: {}
AnyOfParentRefTest:
type: object
properties:
number:
anyOf:
- type: null
- $ref: '#/components/schemas/IntegerRef'
writeOnly: true
number2:
anyOf:
- type: null
- $ref: '#/components/schemas/IntegerRef'
readOnly: true
OneOfParentRefTest:
type: object
properties:
number:
oneOf:
- type: null
- $ref: '#/components/schemas/IntegerRef'
writeOnly: true
number2:
oneOf:
- type: null
- $ref: '#/components/schemas/IntegerRef'
readOnly: true