From 0d7e01607e7d36ca46b19edb37845e1e5bbf9a7c Mon Sep 17 00:00:00 2001 From: frantuma Date: Thu, 26 Sep 2024 13:12:12 +0200 Subject: [PATCH] schema resolution options - Phase 3: global all-of-ref --- .../v3/core/jackson/ModelResolver.java | 21 +++- .../v3/core/util/AnnotationsUtils.java | 2 +- .../v3/core/util/ParameterProcessor.java | 9 +- modules/swagger-jaxrs2/pom.xml | 3 +- .../jaxrs2/SchemaResolutionAllOfRefTest.java | 112 ++++++++++++++++++ .../swagger/v3/oas/models/media/Schema.java | 5 +- 6 files changed, 141 insertions(+), 11 deletions(-) create mode 100644 modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/SchemaResolutionAllOfRefTest.java diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java index dc9e74add1..3f6c15f94a 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java @@ -682,7 +682,10 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context Annotation[] ctxAnnotation31 = null; - if (Schema.SchemaResolution.ALL_OF.equals(this.schemaResolution) || openapi31) { + if ( + Schema.SchemaResolution.ALL_OF.equals(this.schemaResolution) || + Schema.SchemaResolution.ALL_OF_REF.equals(this.schemaResolution) || + openapi31) { List ctxAnnotations31List = new ArrayList<>(); if (annotations != null) { for (Annotation a : annotations) { @@ -692,8 +695,8 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context ctxAnnotations31List.add(a); } if ((ctxSchema != null) && (!ctxSchema.implementation().equals(Void.class) || StringUtils.isNotEmpty(ctxSchema.type()))) { - ctxAnnotations31List.add(a); - } + ctxAnnotations31List.add(a); + } } ctxAnnotation31 = ctxAnnotations31List.toArray(new Annotation[ctxAnnotations31List.size()]); } @@ -708,7 +711,10 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context .schemaProperty(true) .components(annotatedType.getComponents()) .propertyName(propName); - if (Schema.SchemaResolution.ALL_OF.equals(this.schemaResolution) || openapi31) { + if ( + Schema.SchemaResolution.ALL_OF.equals(this.schemaResolution) || + Schema.SchemaResolution.ALL_OF_REF.equals(this.schemaResolution) || + openapi31) { aType.ctxAnnotations(ctxAnnotation31); } else { aType.ctxAnnotations(annotations); @@ -740,7 +746,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context property = reResolvedProperty.get(); } - } else if (Schema.SchemaResolution.ALL_OF.equals(this.schemaResolution)) { + } else if (Schema.SchemaResolution.ALL_OF.equals(this.schemaResolution) || Schema.SchemaResolution.ALL_OF_REF.equals(this.schemaResolution)) { Optional reResolvedProperty = AnnotationsUtils.getSchemaFromAnnotation(ctxSchema, annotatedType.getComponents(), null, openapi31, null, schemaResolution, context); if (reResolvedProperty.isPresent()) { ctxProperty = reResolvedProperty.get(); @@ -795,6 +801,8 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context property = new Schema() .addAllOfItem(ctxProperty) .addAllOfItem(new Schema().$ref(constructRef(pName))); + } else if (Schema.SchemaResolution.ALL_OF_REF.equals(this.schemaResolution) && ctxProperty != null) { + property = ctxProperty.addAllOfItem(new Schema().$ref(constructRef(pName))); } else { property = new Schema().$ref(constructRef(pName)); } @@ -817,6 +825,9 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context property = new Schema() .addAllOfItem(ctxProperty) .addAllOfItem(new Schema().$ref(StringUtils.isNotEmpty(property.get$ref()) ? property.get$ref() : property.getName())); + } else if (Schema.SchemaResolution.ALL_OF_REF.equals(this.schemaResolution) && ctxProperty != null) { + property = ctxProperty + .addAllOfItem(new Schema().$ref(StringUtils.isNotEmpty(property.get$ref()) ? property.get$ref() : property.getName())); } else { property = new Schema().$ref(StringUtils.isNotEmpty(property.get$ref()) ? property.get$ref() : property.getName()); } diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java index e4654e177c..bfd909a494 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java @@ -621,7 +621,7 @@ public static Optional getSchemaFromAnnotation( } else { schemaObject = new Schema(); } - } else if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution)) { + } else if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution) || Schema.SchemaResolution.ALL_OF_REF.equals(schemaResolution)) { if (existingSchema == null) { schemaObject = new Schema(); } else { diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ParameterProcessor.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ParameterProcessor.java index d219d52a55..18e1277328 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ParameterProcessor.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ParameterProcessor.java @@ -76,7 +76,7 @@ public static Parameter applyAnnotations( io.swagger.v3.oas.annotations.media.ArraySchema ctxArraySchema = AnnotationsUtils.getArraySchemaAnnotation(annotations.toArray(new Annotation[0])); Annotation[] ctxAnnotation31 = null; - if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution)) { + if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution) || Schema.SchemaResolution.ALL_OF_REF.equals(schemaResolution)) { List ctxAnnotations31List = new ArrayList<>(); if (annotations != null) { for (Annotation a : annotations) { @@ -95,7 +95,7 @@ public static Parameter applyAnnotations( .skipOverride(true) .jsonViewAnnotation(jsonViewAnnotation); - if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution)) { + if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution) || Schema.SchemaResolution.ALL_OF_REF.equals(schemaResolution)) { annotatedType.ctxAnnotations(ctxAnnotation31); } else { annotatedType.ctxAnnotations(reworkedAnnotations.toArray(new Annotation[reworkedAnnotations.size()])); @@ -106,7 +106,7 @@ public static Parameter applyAnnotations( if (resolvedSchema.schema != null) { Schema resSchema = AnnotationsUtils.clone(resolvedSchema.schema, openapi31); Schema ctxSchemaObject = null; - if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution)) { + if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution) || Schema.SchemaResolution.ALL_OF_REF.equals(schemaResolution)) { Optional reResolvedSchema = AnnotationsUtils.getSchemaFromAnnotation(ctxSchema, annotatedType.getComponents(), null, openapi31, null, schemaResolution, null); if (reResolvedSchema.isPresent()) { ctxSchemaObject = reResolvedSchema.get(); @@ -121,6 +121,9 @@ public static Parameter applyAnnotations( resSchema = new Schema() .addAllOfItem(ctxSchemaObject) .addAllOfItem(resolvedSchema.schema); + } else if (Schema.SchemaResolution.ALL_OF_REF.equals(schemaResolution) && ctxSchemaObject != null) { + resSchema = ctxSchemaObject + .addAllOfItem(resolvedSchema.schema); } parameter.setSchema(resSchema); } diff --git a/modules/swagger-jaxrs2/pom.xml b/modules/swagger-jaxrs2/pom.xml index 45c6887658..985108505b 100644 --- a/modules/swagger-jaxrs2/pom.xml +++ b/modules/swagger-jaxrs2/pom.xml @@ -96,7 +96,8 @@ maven-surefire-plugin ${surefire-version} - --add-opens java.base/java.lang=ALL-UNNAMED + -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 --add-opens java.base/java.lang=ALL-UNNAMED + 0 diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/SchemaResolutionAllOfRefTest.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/SchemaResolutionAllOfRefTest.java new file mode 100644 index 0000000000..f2a5cb29eb --- /dev/null +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/SchemaResolutionAllOfRefTest.java @@ -0,0 +1,112 @@ +package io.swagger.v3.jaxrs2; + +import io.swagger.v3.core.converter.ModelConverters; +import io.swagger.v3.jaxrs2.matchers.SerializationMatchers; +import io.swagger.v3.jaxrs2.schemaResolution.SchemaResolutionResource; +import io.swagger.v3.oas.integration.SwaggerConfiguration; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; +import org.testng.annotations.Test; + +public class SchemaResolutionAllOfRefTest { + + @Test + public void testSchemaResolutionAllOfRef() { + ModelConverters.reset(); + Reader reader = new Reader(new SwaggerConfiguration().openAPI(new OpenAPI()).schemaResolution(Schema.SchemaResolution.ALL_OF_REF)); + OpenAPI openAPI = reader.read(SchemaResolutionResource.class); + String yaml = "openapi: 3.0.1\n" + + "paths:\n" + + " /test/inlineSchemaFirst:\n" + + " get:\n" + + " operationId: inlineSchemaFirst\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " '*/*':\n" + + " schema:\n" + + " $ref: \"#/components/schemas/InlineSchemaFirst\"\n" + + " /test/inlineSchemaSecond:\n" + + " get:\n" + + " operationId: inlineSchemaSecond\n" + + " requestBody:\n" + + " content:\n" + + " '*/*':\n" + + " schema:\n" + + " description: InlineSchemaSecond API\n" + + " allOf:\n" + + " - $ref: \"#/components/schemas/InlineSchemaSecond\"\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " '*/*':\n" + + " schema:\n" + + " $ref: \"#/components/schemas/InlineSchemaSecond\"\n" + + "components:\n" + + " schemas:\n" + + " InlineSchemaFirst:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " description: InlineSchemaFirst property 1\n" + + " nullable: true\n" + + " allOf:\n" + + " - $ref: \"#/components/schemas/InlineSchemaPropertyFirst\"\n" + + " property2:\n" + + " description: ' InlineSchemaFirst property 2'\n" + + " example: example 2\n" + + " allOf:\n" + + " - $ref: \"#/components/schemas/InlineSchemaPropertyFirst\"\n" + + " InlineSchemaPropertyFirst:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property\n" + + " example: example\n" + + " InlineSchemaPropertySecond:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " $ref: \"#/components/schemas/InlineSchemaSimple\"\n" + + " description: propertysecond\n" + + " example: examplesecond\n" + + " InlineSchemaPropertySimple:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property\n" + + " InlineSchemaSecond:\n" + + " type: object\n" + + " properties:\n" + + " foo:\n" + + " type: string\n" + + " propertySecond1:\n" + + " description: InlineSchemaSecond property 1\n" + + " nullable: true\n" + + " allOf:\n" + + " - $ref: \"#/components/schemas/InlineSchemaPropertySecond\"\n" + + " property2:\n" + + " description: InlineSchemaSecond property 2\n" + + " example: InlineSchemaSecond example 2\n" + + " allOf:\n" + + " - $ref: \"#/components/schemas/InlineSchemaPropertyFirst\"\n" + + " InlineSchemaSimple:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " description: property 1\n" + + " allOf:\n" + + " - $ref: \"#/components/schemas/InlineSchemaPropertySimple\"\n" + + " property2:\n" + + " description: property 2\n" + + " example: example\n" + + " allOf:\n" + + " - $ref: \"#/components/schemas/InlineSchemaPropertySimple\"\n"; + SerializationMatchers.assertEqualsToYaml(openAPI, yaml); + ModelConverters.reset(); + } +} diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Schema.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Schema.java index 8f76ad9ce8..d17c2a4f62 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Schema.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Schema.java @@ -52,7 +52,10 @@ public enum SchemaResolution { @JsonProperty("inline") INLINE("inline"), @JsonProperty("all-of") - ALL_OF("all-of"); + ALL_OF("all-of"), + @JsonProperty("all-of-ref") + ALL_OF_REF("all-of-ref"); + private String value; SchemaResolution(String value) {