diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java index 1b1dd2c6538..aa3874d7610 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java @@ -516,7 +516,7 @@ public class OpenAPINormalizer { return schema; } - if ((visitedSchemas.contains(schema))) { + if (visitedSchemas.contains(schema)) { return schema; // skip due to circular reference } else { visitedSchemas.add(schema); @@ -535,7 +535,6 @@ public class OpenAPINormalizer { return normalizeAnyOf(schema, visitedSchemas); } else if (ModelUtils.isAllOfWithProperties(schema)) { // allOf with properties schema = normalizeAllOfWithProperties(schema, visitedSchemas); - normalizeSchema(schema, visitedSchemas); } else if (ModelUtils.isAllOf(schema)) { // allOf return normalizeAllOf(schema, visitedSchemas); } else if (ModelUtils.isComposedSchema(schema)) { // composed schema @@ -643,15 +642,19 @@ public class OpenAPINormalizer { } private Schema normalizeOneOf(Schema schema, Set visitedSchemas) { - for (Object item : schema.getOneOf()) { + for (int i = 0; i < schema.getOneOf().size(); i++) { + // normalize oneOf sub schemas one by one + Object item = schema.getOneOf().get(i); + if (item == null) { continue; } if (!(item instanceof Schema)) { throw new RuntimeException("Error! oneOf schema is not of the type Schema: " + item); } - // normalize oenOf sub schemas one by one - normalizeSchema((Schema) item, visitedSchemas); + + // update sub-schema with the updated schema + schema.getOneOf().set(i, normalizeSchema((Schema) item, visitedSchemas)); } // process rules here schema = processSimplifyOneOf(schema); @@ -660,7 +663,10 @@ public class OpenAPINormalizer { } private Schema normalizeAnyOf(Schema schema, Set visitedSchemas) { - for (Object item : schema.getAnyOf()) { + for (int i = 0; i < schema.getAnyOf().size(); i++) { + // normalize anyOf sub schemas one by one + Object item = schema.getAnyOf().get(i); + if (item == null) { continue; } @@ -668,8 +674,9 @@ public class OpenAPINormalizer { if (!(item instanceof Schema)) { throw new RuntimeException("Error! anyOf schema is not of the type Schema: " + item); } - // normalize anyOf sub schemas one by one - normalizeSchema((Schema) item, visitedSchemas); + + // update sub-schema with the updated schema + schema.getAnyOf().set(i, normalizeSchema((Schema) item, visitedSchemas)); } // process rules here diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java index a4966e301bc..7734adfdec3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java @@ -600,7 +600,9 @@ public class ModelUtils { Schema items = schema.getItems(); if (items == null) { if (schema instanceof JsonSchema) { // 3.1 spec - // do nothing as the schema may contain prefixItems only + // set the items to a new schema (any type) + items = new Schema<>(); + schema.setItems(items); } else { // 3.0 spec, default to string LOGGER.error("Undefined array inner type for `{}`. Default to String.", schema.getName()); items = new StringSchema().description("TODO default missing array inner type to string"); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java index 3537cc5b897..641e935f2b6 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java @@ -605,6 +605,16 @@ public class OpenAPINormalizerTest { assertNotEquals(((Schema) schema7.getProperties().get("with_prefixitems")).getPrefixItems(), null); assertEquals(((Schema) schema7.getProperties().get("without_items")).getItems(), null); + Schema schema9 = openAPI.getComponents().getSchemas().get("AnyOfArrayWithPrefixItems"); + assertEquals(((Schema) schema9.getAnyOf().get(0)).getItems(), null); + assertNotEquals(((Schema) schema9.getAnyOf().get(0)).getPrefixItems(), null); + assertEquals(((Schema) schema9.getAnyOf().get(1)).getItems(), null); + + Schema schema11 = openAPI.getComponents().getSchemas().get("OneOfArrayWithPrefixItems"); + assertEquals(((Schema) schema11.getOneOf().get(0)).getItems(), null); + assertNotEquals(((Schema) schema11.getOneOf().get(0)).getPrefixItems(), null); + assertEquals(((Schema) schema11.getOneOf().get(1)).getItems(), null); + Map inputRules = Map.of("NORMALIZE_31SPEC", "true"); OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, inputRules); openAPINormalizer.normalize(); @@ -632,6 +642,16 @@ public class OpenAPINormalizerTest { assertNotEquals(((Schema) schema8.getProperties().get("with_prefixitems")).getItems(), null); assertEquals(((Schema) schema8.getProperties().get("with_prefixitems")).getPrefixItems(), null); assertNotEquals(((Schema) schema8.getProperties().get("without_items")).getItems(), null); + + Schema schema10 = openAPI.getComponents().getSchemas().get("AnyOfArrayWithPrefixItems"); + assertNotEquals(((Schema) schema10.getAnyOf().get(0)).getItems(), null); + assertEquals(((Schema) schema10.getAnyOf().get(0)).getPrefixItems(), null); + assertNotEquals(((Schema) schema10.getAnyOf().get(1)).getItems(), null); + + Schema schema12 = openAPI.getComponents().getSchemas().get("OneOfArrayWithPrefixItems"); + assertNotEquals(((Schema) schema12.getOneOf().get(0)).getItems(), null); + assertEquals(((Schema) schema12.getOneOf().get(0)).getPrefixItems(), null); + assertNotEquals(((Schema) schema12.getOneOf().get(1)).getItems(), null); } @Test diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java index 255d4eef78f..5aa3c405a26 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java @@ -461,4 +461,12 @@ public class ModelUtilsTest { Assert.assertEquals(deepCopy, schema); Assert.assertNotSame(deepCopy, schema); } + + @Test + public void testGetSchemaItemsWith31Spec() { + OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/issue_18291.yaml"); + Schema arrayWithPrefixItems = ModelUtils.getSchema(openAPI, "ArrayWithPrefixItems"); + Assert.assertNotNull(ModelUtils.getSchemaItems((Schema) arrayWithPrefixItems.getProperties().get("with_prefixitems"))); + Assert.assertNotNull(ModelUtils.getSchemaItems((Schema) arrayWithPrefixItems.getProperties().get("without_items"))); + } } diff --git a/modules/openapi-generator/src/test/resources/3_1/issue_18291.yaml b/modules/openapi-generator/src/test/resources/3_1/issue_18291.yaml index 8ea8a90357f..ceb5f10d1a5 100644 --- a/modules/openapi-generator/src/test/resources/3_1/issue_18291.yaml +++ b/modules/openapi-generator/src/test/resources/3_1/issue_18291.yaml @@ -47,3 +47,23 @@ components: minItems: 2 without_items: type: array + AnyOfArrayWithPrefixItems: + anyOf: + - type: array + prefixItems: + - type: number + title: Longitude + - type: number + title: Latitude + maxItems: 2 + minItems: 2 + - type: array + OneOfArrayWithPrefixItems: + oneOf: + - type: array + prefixItems: + - type: integer + - type: integer + maxItems: 3 + minItems: 3 + - type: array \ No newline at end of file diff --git a/samples/client/petstore/java/okhttp-gson-3.1/api/openapi.yaml b/samples/client/petstore/java/okhttp-gson-3.1/api/openapi.yaml index 28503edd8b0..aabe501dcc3 100644 --- a/samples/client/petstore/java/okhttp-gson-3.1/api/openapi.yaml +++ b/samples/client/petstore/java/okhttp-gson-3.1/api/openapi.yaml @@ -815,6 +815,7 @@ paths: items: $ref: '#/components/schemas/myObject' nullable: true + type: array description: "" tags: - fake @@ -1185,6 +1186,7 @@ components: - type: string - items: type: string + type: array securitySchemes: petstore_auth: flows: