From b533c5d9034a1a50fbbd94c402a62eaa5f5ac435 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Wed, 2 Oct 2024 21:16:08 +0800 Subject: [PATCH 1/2] fix anyOf/oneOf normalization for 3.1 spec --- .../codegen/OpenAPINormalizer.java | 23 ++++++++++++------- .../codegen/utils/ModelUtils.java | 4 +++- .../codegen/OpenAPINormalizerTest.java | 20 ++++++++++++++++ .../codegen/utils/ModelUtilsTest.java | 8 +++++++ .../src/test/resources/3_1/issue_18291.yaml | 20 ++++++++++++++++ 5 files changed, 66 insertions(+), 9 deletions(-) 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 1b1dd2c6538e..aa3874d76107 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 Schema normalizeSchema(Schema schema, Set visitedSchemas) { 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 Schema normalizeSchema(Schema schema, Set visitedSchemas) { 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 @@ private Schema normalizeAllOfWithProperties(Schema schema, Set visitedSc } 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 @@ private Schema normalizeOneOf(Schema schema, Set visitedSchemas) { } 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 @@ private Schema normalizeAnyOf(Schema schema, Set visitedSchemas) { 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 a4966e301bcb..7734adfdec3c 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 static Schema getSchemaItems(Schema schema) { 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 3537cc5b8974..641e935f2b64 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 void testOpenAPINormalizerProcessingArraySchema31Spec() { 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 void testOpenAPINormalizerProcessingArraySchema31Spec() { 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 255d4eef78ff..5aa3c405a261 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 void testCloneDateTimeSchemaWithExample() { 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 8ea8a90357f6..ceb5f10d1a5e 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 From 20fe612f4aaada698abea7a1646f23877d58d119 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Thu, 3 Oct 2024 01:05:21 +0800 Subject: [PATCH 2/2] update samples --- samples/client/petstore/java/okhttp-gson-3.1/api/openapi.yaml | 2 ++ 1 file changed, 2 insertions(+) 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 28503edd8b0d..aabe501dcc3a 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: