forked from loafle/openapi-generator-original
Better handling of allOf with unsupported schemas (#19964)
* better handling of allOf with unsupported schemas * add test spec * better messages
This commit is contained in:
parent
30ff0d7ca9
commit
62c0258e04
@ -665,7 +665,41 @@ public class OpenAPINormalizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove unsupported schemas (e.g. if, then) from allOf.
|
||||||
|
*
|
||||||
|
* @param schema Schema
|
||||||
|
*/
|
||||||
|
private void removeUnsupportedSchemasFromAllOf(Schema schema) {
|
||||||
|
if (schema.getAllOf() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<Schema> iterator = schema.getAllOf().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Schema item = iterator.next();
|
||||||
|
|
||||||
|
// remove unsupported schemas (e.g. if, then)
|
||||||
|
if (ModelUtils.isUnsupportedSchema(openAPI, item)) {
|
||||||
|
LOGGER.debug("Removed allOf sub-schema that's not yet supported: {}", item);
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schema.getAllOf().size() == 0) {
|
||||||
|
// no more schema in allOf so reset to null instead
|
||||||
|
LOGGER.info("Unset/Removed allOf after cleaning up allOf sub-schemas that are not yet supported.");
|
||||||
|
schema.setAllOf(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Schema normalizeAllOf(Schema schema, Set<Schema> visitedSchemas) {
|
private Schema normalizeAllOf(Schema schema, Set<Schema> visitedSchemas) {
|
||||||
|
removeUnsupportedSchemasFromAllOf(schema);
|
||||||
|
|
||||||
|
if (schema.getAllOf() == null) {
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
for (Object item : schema.getAllOf()) {
|
for (Object item : schema.getAllOf()) {
|
||||||
if (!(item instanceof Schema)) {
|
if (!(item instanceof Schema)) {
|
||||||
throw new RuntimeException("Error! allOf schema is not of the type Schema: " + item);
|
throw new RuntimeException("Error! allOf schema is not of the type Schema: " + item);
|
||||||
@ -673,6 +707,7 @@ public class OpenAPINormalizer {
|
|||||||
// normalize allOf sub schemas one by one
|
// normalize allOf sub schemas one by one
|
||||||
normalizeSchema((Schema) item, visitedSchemas);
|
normalizeSchema((Schema) item, visitedSchemas);
|
||||||
}
|
}
|
||||||
|
|
||||||
// process rules here
|
// process rules here
|
||||||
processUseAllOfRefAsParent(schema);
|
processUseAllOfRefAsParent(schema);
|
||||||
|
|
||||||
@ -680,6 +715,12 @@ public class OpenAPINormalizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Schema normalizeAllOfWithProperties(Schema schema, Set<Schema> visitedSchemas) {
|
private Schema normalizeAllOfWithProperties(Schema schema, Set<Schema> visitedSchemas) {
|
||||||
|
removeUnsupportedSchemasFromAllOf(schema);
|
||||||
|
|
||||||
|
if (schema.getAllOf() == null) {
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
for (Object item : schema.getAllOf()) {
|
for (Object item : schema.getAllOf()) {
|
||||||
if (!(item instanceof Schema)) {
|
if (!(item instanceof Schema)) {
|
||||||
throw new RuntimeException("Error! allOf schema is not of the type Schema: " + item);
|
throw new RuntimeException("Error! allOf schema is not of the type Schema: " + item);
|
||||||
|
@ -616,6 +616,16 @@ public class ModelUtils {
|
|||||||
return ModelUtils.isArraySchema(schema) && Boolean.TRUE.equals(schema.getUniqueItems());
|
return ModelUtils.isArraySchema(schema) && Boolean.TRUE.equals(schema.getUniqueItems());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the schema is a string/integer/number/boolean type in OpenAPI.
|
||||||
|
*
|
||||||
|
* @param schema the OAS schema
|
||||||
|
* @return true if the schema is a string/integer/number/boolean type in OpenAPI.
|
||||||
|
*/
|
||||||
|
public static boolean isPrimitiveType(Schema schema) {
|
||||||
|
return (isStringSchema(schema) || isIntegerSchema(schema) || isNumberSchema(schema) || isBooleanSchema(schema));
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isStringSchema(Schema schema) {
|
public static boolean isStringSchema(Schema schema) {
|
||||||
return schema instanceof StringSchema || SchemaTypeUtil.STRING_TYPE.equals(getType(schema));
|
return schema instanceof StringSchema || SchemaTypeUtil.STRING_TYPE.equals(getType(schema));
|
||||||
}
|
}
|
||||||
@ -2262,6 +2272,35 @@ public class ModelUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the schema is supported by OpenAPI Generator.
|
||||||
|
* <p>
|
||||||
|
* Return true if the schema can be handled by OpenAPI Generator
|
||||||
|
*
|
||||||
|
* @param schema Schema
|
||||||
|
* @param openAPI OpenAPIs
|
||||||
|
*
|
||||||
|
* @return true if schema is null type
|
||||||
|
*/
|
||||||
|
public static boolean isUnsupportedSchema(OpenAPI openAPI, Schema schema) {
|
||||||
|
if (schema == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dereference the schema
|
||||||
|
schema = ModelUtils.getReferencedSchema(openAPI, schema);
|
||||||
|
|
||||||
|
if (schema.getTypes() == null && hasValidation(schema)) {
|
||||||
|
// just validation without type
|
||||||
|
return true;
|
||||||
|
} else if (schema.getIf() != null && schema.getThen() != null) {
|
||||||
|
// if, then in 3.1 spec
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
private interface OpenAPISchemaVisitor {
|
private interface OpenAPISchemaVisitor {
|
||||||
|
|
||||||
|
@ -774,4 +774,24 @@ public class OpenAPINormalizerTest {
|
|||||||
assertEquals(normalizedTypeSchema.getEnum().size(), 1);
|
assertEquals(normalizedTypeSchema.getEnum().size(), 1);
|
||||||
assertEquals(Arrays.asList(originalConst), normalizedTypeSchema.getEnum());
|
assertEquals(Arrays.asList(originalConst), normalizedTypeSchema.getEnum());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOpenAPINormalizerProcessingAllOfSchema31Spec() {
|
||||||
|
// to test array schema processing in 3.1 spec
|
||||||
|
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/unsupported_schema_test.yaml");
|
||||||
|
|
||||||
|
Schema schema = openAPI.getComponents().getSchemas().get("Dummy");
|
||||||
|
assertEquals(((Schema) schema.getProperties().get("property1")).getAllOf().size(), 2);
|
||||||
|
assertNotEquals(((Schema) ((Schema) schema.getProperties().get("property2")).getAllOf().get(0)).getIf(), null); // if is set before normalization
|
||||||
|
assertNotEquals(((Schema) ((Schema) schema.getProperties().get("property2")).getAllOf().get(1)).getThen(), null); // then is set before normalization
|
||||||
|
|
||||||
|
Map<String, String> inputRules = Map.of("NORMALIZE_31SPEC", "true");
|
||||||
|
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, inputRules);
|
||||||
|
openAPINormalizer.normalize();
|
||||||
|
|
||||||
|
Schema schema2 = openAPI.getComponents().getSchemas().get("Dummy");
|
||||||
|
assertEquals(((Schema) schema2.getProperties().get("property1")).getAllOf(), null);
|
||||||
|
assertEquals(((Schema) schema2.getProperties().get("property2")).getAllOf(), null);
|
||||||
|
assertEquals(((Schema) schema2.getProperties().get("property2")).getAllOf(), null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -501,4 +501,26 @@ public class ModelUtilsTest {
|
|||||||
assertTrue(ModelUtils.isNullTypeSchema(openAPI, (Schema) schema.getAnyOf().get(2)));
|
assertTrue(ModelUtils.isNullTypeSchema(openAPI, (Schema) schema.getAnyOf().get(2)));
|
||||||
assertTrue(ModelUtils.isNullTypeSchema(openAPI, (Schema) schema.getAnyOf().get(3)));
|
assertTrue(ModelUtils.isNullTypeSchema(openAPI, (Schema) schema.getAnyOf().get(3)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isUnsupportedSchemaTest() {
|
||||||
|
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/unsupported_schema_test.yaml");
|
||||||
|
Map<String, String> options = new HashMap<>();
|
||||||
|
Schema schema = openAPI.getComponents().getSchemas().get("Dummy");
|
||||||
|
Schema property1 = (Schema) schema.getProperties().get("property1");
|
||||||
|
Schema property2 = (Schema) schema.getProperties().get("property2");
|
||||||
|
|
||||||
|
// a test of string type with allOf (2 patterns)
|
||||||
|
assertTrue(ModelUtils.isUnsupportedSchema(openAPI, (Schema) property1.getAllOf().get(0)));
|
||||||
|
assertTrue(ModelUtils.isUnsupportedSchema(openAPI, (Schema) property1.getAllOf().get(1)));
|
||||||
|
|
||||||
|
// if, then test
|
||||||
|
assertTrue(ModelUtils.isUnsupportedSchema(openAPI, (Schema) property2.getAllOf().get(0)));
|
||||||
|
assertTrue(ModelUtils.isUnsupportedSchema(openAPI, (Schema) property2.getAllOf().get(1)));
|
||||||
|
|
||||||
|
// typical schemas, e.g. boolean, string, array of string (enum)
|
||||||
|
assertFalse(ModelUtils.isUnsupportedSchema(openAPI, (Schema) property2.getProperties().get("aBooleanCheck")));
|
||||||
|
assertFalse(ModelUtils.isUnsupportedSchema(openAPI, (Schema) property2.getProperties().get("condition")));
|
||||||
|
assertFalse(ModelUtils.isUnsupportedSchema(openAPI, (Schema) property2.getProperties().get("purpose")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
version: 1.0.0
|
||||||
|
title: Example
|
||||||
|
servers:
|
||||||
|
- url: http://api.example.xyz/v1
|
||||||
|
paths:
|
||||||
|
/person/display/{personId}:
|
||||||
|
get:
|
||||||
|
parameters:
|
||||||
|
- name: personId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: The id of the person to retrieve
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
operationId: list
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Dummy"
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
Dummy:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
property1:
|
||||||
|
type: string
|
||||||
|
allOf:
|
||||||
|
- pattern: "[abc]"
|
||||||
|
- pattern: "[a-z]"
|
||||||
|
property2:
|
||||||
|
type: object
|
||||||
|
allOf:
|
||||||
|
- if:
|
||||||
|
properties:
|
||||||
|
aBooleanCheck:
|
||||||
|
const: false
|
||||||
|
then:
|
||||||
|
required:
|
||||||
|
- condition
|
||||||
|
- if:
|
||||||
|
properties:
|
||||||
|
aBooleanCheck:
|
||||||
|
const: true
|
||||||
|
then:
|
||||||
|
required:
|
||||||
|
- purpose
|
||||||
|
properties:
|
||||||
|
aBooleanCheck:
|
||||||
|
type: boolean
|
||||||
|
condition:
|
||||||
|
type: string
|
||||||
|
purpose:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- FIRST
|
||||||
|
- SECOND
|
||||||
|
- THIRD
|
Loading…
x
Reference in New Issue
Block a user