From c37b72cc653c41dc1ab90a4e10d2c957c84d52d4 Mon Sep 17 00:00:00 2001 From: MicRyc Date: Thu, 22 Aug 2024 15:53:25 +0200 Subject: [PATCH 01/11] refs #4679 - fix @Schema#implementation resolving for OAS 3.1 --- .../v3/core/jackson/ModelResolver.java | 3 + .../v3/core/resolving/Ticket4679Test.java | 55 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket4679Test.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 73437bb660..66634f693f 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 @@ -683,6 +683,9 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context !(a instanceof io.swagger.v3.oas.annotations.media.ArraySchema)) { ctxAnnotations31List.add(a); } + if ((ctxSchema != null) && (!ctxSchema.implementation().equals(Void.class) || StringUtils.isNotEmpty(ctxSchema.type()))) { + ctxAnnotations31List.add(a); + } } ctxAnnotation31 = ctxAnnotations31List.toArray(new Annotation[ctxAnnotations31List.size()]); } diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket4679Test.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket4679Test.java new file mode 100644 index 0000000000..9998f8b6a2 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket4679Test.java @@ -0,0 +1,55 @@ +package io.swagger.v3.core.resolving; + +import io.swagger.v3.core.converter.ModelConverters; +import io.swagger.v3.core.matchers.SerializationMatchers; +import io.swagger.v3.core.util.Yaml; +import io.swagger.v3.core.util.Yaml31; +import io.swagger.v3.oas.annotations.media.Schema; +import org.testng.annotations.Test; + +import java.util.Map; + +public class Ticket4679Test extends SwaggerTestBase{ + + @Test(description = "Custom schema implementation in property overrides type value") + public void testCustomSchemaImplementation() { + + String expectedYaml = "ModelWithCustomSchemaImplementationInProperty:\n" + + " properties:\n" + + " exampleField:\n" + + " type: integer\n" + + " format: int32\n" + + " secondExampleField:\n" + + " type: string\n"; + + Map stringSchemaMap = ModelConverters.getInstance(true).readAll(ModelWithCustomSchemaImplementationInProperty.class); + SerializationMatchers.assertEqualsToYaml31(stringSchemaMap, expectedYaml); + } + + static class ModelWithCustomSchemaImplementationInProperty { + + @Schema(implementation = Integer.class) + private String exampleField; + + @Schema(type = "string") + private Integer secondExampleField; + + public String getExampleField() { + return exampleField; + } + + public void setExampleField(String exampleField) { + this.exampleField = exampleField; + } + + public Integer getSecondExampleField() { + return secondExampleField; + } + + public void setSecondExampleField(Integer secondExampleField) { + this.secondExampleField = secondExampleField; + } + + + } +} From 70abf2b52f5df0e131ec366f7a5d2b3d7646689f Mon Sep 17 00:00:00 2001 From: micryc Date: Wed, 28 Aug 2024 11:26:24 +0000 Subject: [PATCH 02/11] prepare release 2.2.23 --- README.md | 5 +++-- modules/swagger-annotations/pom.xml | 2 +- modules/swagger-core/pom.xml | 2 +- .../swagger-eclipse-transformer-maven-plugin/pom.xml | 2 +- modules/swagger-gradle-plugin/README.md | 4 ++-- modules/swagger-gradle-plugin/gradle.properties | 2 +- .../io/swagger/v3/plugins/gradle/SwaggerPlugin.java | 2 +- .../v3/plugins/gradle/SwaggerResolveTest.java | 4 ++-- modules/swagger-integration/pom.xml | 2 +- .../swagger-jaxrs2-servlet-initializer-v2/pom.xml | 2 +- modules/swagger-jaxrs2-servlet-initializer/pom.xml | 2 +- modules/swagger-jaxrs2/pom.xml | 2 +- modules/swagger-maven-plugin/README.md | 12 ++++++------ modules/swagger-maven-plugin/pom.xml | 2 +- modules/swagger-models/pom.xml | 2 +- .../modules/swagger-annotations-jakarta/pom.xml | 2 +- .../modules/swagger-core-jakarta/pom.xml | 2 +- .../modules/swagger-integration-jakarta/pom.xml | 2 +- .../modules/swagger-jaxrs2-jakarta/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../modules/swagger-maven-plugin-jakarta/pom.xml | 2 +- .../modules/swagger-models-jakarta/pom.xml | 2 +- modules/swagger-project-jakarta/pom.xml | 2 +- pom.xml | 2 +- 25 files changed, 34 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 8b58ee8099..a87ea4465f 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,8 @@ The OpenAPI Specification has undergone several revisions since initial creation Swagger core Version | Release Date | OpenAPI Spec compatibility | Notes | Status ------------------------- | ------------ | -------------------------- | ----- | ---- -2.2.22 (**current stable**)| 2024-05-15 | 3.x | [tag v2.2.22](https://github.com/swagger-api/swagger-core/tree/v2.2.22) | Supported +2.2.23 (**current stable**)| 2024-08-28 | 3.x | [tag v2.2.23](https://github.com/swagger-api/swagger-core/tree/v2.2.23) | Supported +2.2.22 | 2024-05-15 | 3.x | [tag v2.2.22](https://github.com/swagger-api/swagger-core/tree/v2.2.22) | Supported 2.2.21 | 2024-03-20 | 3.x | [tag v2.2.21](https://github.com/swagger-api/swagger-core/tree/v2.2.21) | Supported 2.2.20 | 2023-12-19 | 3.x | [tag v2.2.20](https://github.com/swagger-api/swagger-core/tree/v2.2.20) | Supported 2.2.19 | 2023-11-10 | 3.x | [tag v2.2.19](https://github.com/swagger-api/swagger-core/tree/v2.2.19) | Supported @@ -115,7 +116,7 @@ You need the following installed and available in your $PATH: * Jackson 2.4.5 or greater -### To build from source (currently 2.2.23-SNAPSHOT) +### To build from source (currently 2.2.24-SNAPSHOT) ``` # first time building locally mvn -N diff --git a/modules/swagger-annotations/pom.xml b/modules/swagger-annotations/pom.xml index 0393c9d748..c8b580d21c 100644 --- a/modules/swagger-annotations/pom.xml +++ b/modules/swagger-annotations/pom.xml @@ -3,7 +3,7 @@ io.swagger.core.v3 swagger-project - 2.2.23-SNAPSHOT + 2.2.23 ../.. 4.0.0 diff --git a/modules/swagger-core/pom.xml b/modules/swagger-core/pom.xml index d4adb10e83..fe6f3c4ce5 100644 --- a/modules/swagger-core/pom.xml +++ b/modules/swagger-core/pom.xml @@ -3,7 +3,7 @@ io.swagger.core.v3 swagger-project - 2.2.23-SNAPSHOT + 2.2.23 ../.. 4.0.0 diff --git a/modules/swagger-eclipse-transformer-maven-plugin/pom.xml b/modules/swagger-eclipse-transformer-maven-plugin/pom.xml index 6c8cf8a910..10ccf63c8a 100644 --- a/modules/swagger-eclipse-transformer-maven-plugin/pom.xml +++ b/modules/swagger-eclipse-transformer-maven-plugin/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project - 2.2.23-SNAPSHOT + 2.2.23 ../.. 4.0.0 diff --git a/modules/swagger-gradle-plugin/README.md b/modules/swagger-gradle-plugin/README.md index 32de4363e6..b353bc3240 100644 --- a/modules/swagger-gradle-plugin/README.md +++ b/modules/swagger-gradle-plugin/README.md @@ -26,7 +26,7 @@ Alternatively provide as value a classpath with the following dependencies (repl ``` plugins { - id "io.swagger.core.v3.swagger-gradle-plugin" version "2.2.22" + id "io.swagger.core.v3.swagger-gradle-plugin" version "2.2.23" } ``` ### Gradle 1.x and 2.0 @@ -43,7 +43,7 @@ buildscript { } } dependencies { - classpath "io.swagger.core.v3:swagger-gradle-plugin:2.2.22" + classpath "io.swagger.core.v3:swagger-gradle-plugin:2.2.23" } } diff --git a/modules/swagger-gradle-plugin/gradle.properties b/modules/swagger-gradle-plugin/gradle.properties index ae4ed9f0c5..5d23d1f97f 100644 --- a/modules/swagger-gradle-plugin/gradle.properties +++ b/modules/swagger-gradle-plugin/gradle.properties @@ -1,2 +1,2 @@ -version=2.2.23-SNAPSHOT +version=2.2.23 jettyVersion=9.4.53.v20231009 diff --git a/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/SwaggerPlugin.java b/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/SwaggerPlugin.java index 4f984fcc53..18adeac02e 100644 --- a/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/SwaggerPlugin.java +++ b/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/SwaggerPlugin.java @@ -16,7 +16,7 @@ public void apply(Project project) { config.defaultDependencies(new Action() { public void execute(DependencySet dependencies) { dependencies.add(project.getDependencies().create("org.apache.commons:commons-lang3:3.12.0")); - dependencies.add(project.getDependencies().create("io.swagger.core.v3:swagger-jaxrs2:2.2.23-SNAPSHOT")); + dependencies.add(project.getDependencies().create("io.swagger.core.v3:swagger-jaxrs2:2.2.23")); dependencies.add(project.getDependencies().create("javax.ws.rs:javax.ws.rs-api:2.1")); dependencies.add(project.getDependencies().create("javax.servlet:javax.servlet-api:3.1.0")); } diff --git a/modules/swagger-gradle-plugin/src/test/java/io/swagger/v3/plugins/gradle/SwaggerResolveTest.java b/modules/swagger-gradle-plugin/src/test/java/io/swagger/v3/plugins/gradle/SwaggerResolveTest.java index b41409eddc..2241972000 100644 --- a/modules/swagger-gradle-plugin/src/test/java/io/swagger/v3/plugins/gradle/SwaggerResolveTest.java +++ b/modules/swagger-gradle-plugin/src/test/java/io/swagger/v3/plugins/gradle/SwaggerResolveTest.java @@ -81,7 +81,7 @@ public void testSwaggerResolveTask() throws IOException { " mavenCentral()\n" + "}\n" + "dependencies { \n" + - " implementation 'io.swagger.core.v3:swagger-jaxrs2:2.2.23-SNAPSHOT'\n" + + " implementation 'io.swagger.core.v3:swagger-jaxrs2:2.2.23'\n" + " implementation 'javax.ws.rs:javax.ws.rs-api:2.1'\n" + " implementation 'javax.servlet:javax.servlet-api:3.1.0'\n" + " testImplementation 'com.github.tomakehurst:wiremock:2.27.2'\n" + @@ -154,7 +154,7 @@ public void testSwaggerResolveWithOAS31OptionTask() throws IOException { " mavenCentral()\n" + "}\n" + "dependencies { \n" + - " implementation 'io.swagger.core.v3:swagger-jaxrs2:2.2.23-SNAPSHOT'\n" + + " implementation 'io.swagger.core.v3:swagger-jaxrs2:2.2.23'\n" + " implementation 'javax.ws.rs:javax.ws.rs-api:2.1'\n" + " implementation 'javax.servlet:javax.servlet-api:3.1.0'\n" + " testImplementation 'com.github.tomakehurst:wiremock:2.27.2'\n" + diff --git a/modules/swagger-integration/pom.xml b/modules/swagger-integration/pom.xml index db4e35a81b..f5d341cb1a 100644 --- a/modules/swagger-integration/pom.xml +++ b/modules/swagger-integration/pom.xml @@ -6,7 +6,7 @@ io.swagger.core.v3 swagger-project - 2.2.23-SNAPSHOT + 2.2.23 ../.. swagger-integration diff --git a/modules/swagger-jaxrs2-servlet-initializer-v2/pom.xml b/modules/swagger-jaxrs2-servlet-initializer-v2/pom.xml index 3c65c21f94..8c96d1b68e 100644 --- a/modules/swagger-jaxrs2-servlet-initializer-v2/pom.xml +++ b/modules/swagger-jaxrs2-servlet-initializer-v2/pom.xml @@ -5,7 +5,7 @@ swagger-project io.swagger.core.v3 - 2.2.23-SNAPSHOT + 2.2.23 ../../ 4.0.0 diff --git a/modules/swagger-jaxrs2-servlet-initializer/pom.xml b/modules/swagger-jaxrs2-servlet-initializer/pom.xml index 1e979ad23b..8855442358 100644 --- a/modules/swagger-jaxrs2-servlet-initializer/pom.xml +++ b/modules/swagger-jaxrs2-servlet-initializer/pom.xml @@ -5,7 +5,7 @@ swagger-project io.swagger.core.v3 - 2.2.23-SNAPSHOT + 2.2.23 ../../ 4.0.0 diff --git a/modules/swagger-jaxrs2/pom.xml b/modules/swagger-jaxrs2/pom.xml index 330ef46283..d3959cee15 100644 --- a/modules/swagger-jaxrs2/pom.xml +++ b/modules/swagger-jaxrs2/pom.xml @@ -5,7 +5,7 @@ swagger-project io.swagger.core.v3 - 2.2.23-SNAPSHOT + 2.2.23 ../../ 4.0.0 diff --git a/modules/swagger-maven-plugin/README.md b/modules/swagger-maven-plugin/README.md index 41147e783f..1e76742b2b 100644 --- a/modules/swagger-maven-plugin/README.md +++ b/modules/swagger-maven-plugin/README.md @@ -22,7 +22,7 @@ Both `javax` and `jakarta` examples are provided below io.swagger.core.v3 swagger-maven-plugin - 2.2.22 + 2.2.23 openapi ${project.build.directory}/generatedtest @@ -47,7 +47,7 @@ Both `javax` and `jakarta` examples are provided below io.swagger.core.v3 swagger-jaxrs2 - 2.2.22 + 2.2.23 @@ -73,7 +73,7 @@ Both `javax` and `jakarta` examples are provided below io.swagger.core.v3 swagger-maven-plugin-jakarta - 2.2.22 + 2.2.23 openapi ${project.build.directory}/generatedtest @@ -98,7 +98,7 @@ Both `javax` and `jakarta` examples are provided below io.swagger.core.v3 swagger-jaxrs2-jakarta - 2.2.22 + 2.2.23 @@ -126,7 +126,7 @@ Both `javax` and `jakarta` examples are provided below io.swagger.core.v3 swagger-maven-plugin - 2.2.22 + 2.2.23 openapi ${project.build.directory}/generatedtest @@ -155,7 +155,7 @@ Both `javax` and `jakarta` examples are provided below io.swagger.core.v3 swagger-maven-plugin-jakarta - 2.2.22 + 2.2.23 openapi ${project.build.directory}/generatedtest diff --git a/modules/swagger-maven-plugin/pom.xml b/modules/swagger-maven-plugin/pom.xml index c6040eab0d..d767853597 100644 --- a/modules/swagger-maven-plugin/pom.xml +++ b/modules/swagger-maven-plugin/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project - 2.2.23-SNAPSHOT + 2.2.23 ../.. 4.0.0 diff --git a/modules/swagger-models/pom.xml b/modules/swagger-models/pom.xml index abfa4a764d..02949da0a7 100644 --- a/modules/swagger-models/pom.xml +++ b/modules/swagger-models/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project - 2.2.23-SNAPSHOT + 2.2.23 ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-annotations-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-annotations-jakarta/pom.xml index d7d777846b..f50409b1ae 100644 --- a/modules/swagger-project-jakarta/modules/swagger-annotations-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-annotations-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23-SNAPSHOT + 2.2.23 ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-core-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-core-jakarta/pom.xml index 866a81ce24..5de6812ccd 100644 --- a/modules/swagger-project-jakarta/modules/swagger-core-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-core-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23-SNAPSHOT + 2.2.23 ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-integration-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-integration-jakarta/pom.xml index 4ffb6e1481..c31c69fdae 100644 --- a/modules/swagger-project-jakarta/modules/swagger-integration-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-integration-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23-SNAPSHOT + 2.2.23 ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-jakarta/pom.xml index c216add713..218fa56687 100644 --- a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23-SNAPSHOT + 2.2.23 ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-jakarta/pom.xml index 7867392518..c97567b808 100644 --- a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23-SNAPSHOT + 2.2.23 ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-v2-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-v2-jakarta/pom.xml index 3e0a7b0136..d3e2a4d82c 100644 --- a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-v2-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-v2-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23-SNAPSHOT + 2.2.23 ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-maven-plugin-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-maven-plugin-jakarta/pom.xml index a4c5915ef8..d921927f4f 100644 --- a/modules/swagger-project-jakarta/modules/swagger-maven-plugin-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-maven-plugin-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23-SNAPSHOT + 2.2.23 ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-models-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-models-jakarta/pom.xml index 67baeb9952..130bc7feb5 100644 --- a/modules/swagger-project-jakarta/modules/swagger-models-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-models-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23-SNAPSHOT + 2.2.23 ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/pom.xml b/modules/swagger-project-jakarta/pom.xml index 0fd42d104a..dc5fdb4f7e 100644 --- a/modules/swagger-project-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/pom.xml @@ -6,7 +6,7 @@ pom swagger-project-jakarta swagger-project-jakarta - 2.2.23-SNAPSHOT + 2.2.23 https://github.com/swagger-api/swagger-core scm:git:git@github.com:swagger-api/swagger-core.git diff --git a/pom.xml b/pom.xml index 340190d349..e590eeff4a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ pom swagger-project swagger-project - 2.2.23-SNAPSHOT + 2.2.23 https://github.com/swagger-api/swagger-core scm:git:git@github.com:swagger-api/swagger-core.git From 5e48e07393a7edff70f7e0ee7e23166f7c8996bb Mon Sep 17 00:00:00 2001 From: micryc Date: Wed, 28 Aug 2024 11:45:10 +0000 Subject: [PATCH 03/11] bump snapshot 2.2.24-SNAPSHOT --- modules/swagger-annotations/pom.xml | 2 +- modules/swagger-core/pom.xml | 2 +- modules/swagger-eclipse-transformer-maven-plugin/pom.xml | 2 +- modules/swagger-gradle-plugin/gradle.properties | 2 +- .../main/java/io/swagger/v3/plugins/gradle/SwaggerPlugin.java | 2 +- .../java/io/swagger/v3/plugins/gradle/SwaggerResolveTest.java | 4 ++-- modules/swagger-integration/pom.xml | 2 +- modules/swagger-jaxrs2-servlet-initializer-v2/pom.xml | 2 +- modules/swagger-jaxrs2-servlet-initializer/pom.xml | 2 +- modules/swagger-jaxrs2/pom.xml | 2 +- modules/swagger-maven-plugin/pom.xml | 2 +- modules/swagger-models/pom.xml | 2 +- .../modules/swagger-annotations-jakarta/pom.xml | 2 +- .../modules/swagger-core-jakarta/pom.xml | 2 +- .../modules/swagger-integration-jakarta/pom.xml | 2 +- .../modules/swagger-jaxrs2-jakarta/pom.xml | 2 +- .../swagger-jaxrs2-servlet-initializer-jakarta/pom.xml | 2 +- .../swagger-jaxrs2-servlet-initializer-v2-jakarta/pom.xml | 2 +- .../modules/swagger-maven-plugin-jakarta/pom.xml | 2 +- .../modules/swagger-models-jakarta/pom.xml | 2 +- modules/swagger-project-jakarta/pom.xml | 2 +- pom.xml | 2 +- 22 files changed, 23 insertions(+), 23 deletions(-) diff --git a/modules/swagger-annotations/pom.xml b/modules/swagger-annotations/pom.xml index c8b580d21c..f106d90afa 100644 --- a/modules/swagger-annotations/pom.xml +++ b/modules/swagger-annotations/pom.xml @@ -3,7 +3,7 @@ io.swagger.core.v3 swagger-project - 2.2.23 + 2.2.24-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-core/pom.xml b/modules/swagger-core/pom.xml index fe6f3c4ce5..4c181abdc8 100644 --- a/modules/swagger-core/pom.xml +++ b/modules/swagger-core/pom.xml @@ -3,7 +3,7 @@ io.swagger.core.v3 swagger-project - 2.2.23 + 2.2.24-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-eclipse-transformer-maven-plugin/pom.xml b/modules/swagger-eclipse-transformer-maven-plugin/pom.xml index 10ccf63c8a..d9f23d22fd 100644 --- a/modules/swagger-eclipse-transformer-maven-plugin/pom.xml +++ b/modules/swagger-eclipse-transformer-maven-plugin/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project - 2.2.23 + 2.2.24-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-gradle-plugin/gradle.properties b/modules/swagger-gradle-plugin/gradle.properties index 5d23d1f97f..34cd5d274e 100644 --- a/modules/swagger-gradle-plugin/gradle.properties +++ b/modules/swagger-gradle-plugin/gradle.properties @@ -1,2 +1,2 @@ -version=2.2.23 +version=2.2.24-SNAPSHOT jettyVersion=9.4.53.v20231009 diff --git a/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/SwaggerPlugin.java b/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/SwaggerPlugin.java index 18adeac02e..c68341250b 100644 --- a/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/SwaggerPlugin.java +++ b/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/SwaggerPlugin.java @@ -16,7 +16,7 @@ public void apply(Project project) { config.defaultDependencies(new Action() { public void execute(DependencySet dependencies) { dependencies.add(project.getDependencies().create("org.apache.commons:commons-lang3:3.12.0")); - dependencies.add(project.getDependencies().create("io.swagger.core.v3:swagger-jaxrs2:2.2.23")); + dependencies.add(project.getDependencies().create("io.swagger.core.v3:swagger-jaxrs2:2.2.24-SNAPSHOT")); dependencies.add(project.getDependencies().create("javax.ws.rs:javax.ws.rs-api:2.1")); dependencies.add(project.getDependencies().create("javax.servlet:javax.servlet-api:3.1.0")); } diff --git a/modules/swagger-gradle-plugin/src/test/java/io/swagger/v3/plugins/gradle/SwaggerResolveTest.java b/modules/swagger-gradle-plugin/src/test/java/io/swagger/v3/plugins/gradle/SwaggerResolveTest.java index 2241972000..9768aa6c97 100644 --- a/modules/swagger-gradle-plugin/src/test/java/io/swagger/v3/plugins/gradle/SwaggerResolveTest.java +++ b/modules/swagger-gradle-plugin/src/test/java/io/swagger/v3/plugins/gradle/SwaggerResolveTest.java @@ -81,7 +81,7 @@ public void testSwaggerResolveTask() throws IOException { " mavenCentral()\n" + "}\n" + "dependencies { \n" + - " implementation 'io.swagger.core.v3:swagger-jaxrs2:2.2.23'\n" + + " implementation 'io.swagger.core.v3:swagger-jaxrs2:2.2.24-SNAPSHOT'\n" + " implementation 'javax.ws.rs:javax.ws.rs-api:2.1'\n" + " implementation 'javax.servlet:javax.servlet-api:3.1.0'\n" + " testImplementation 'com.github.tomakehurst:wiremock:2.27.2'\n" + @@ -154,7 +154,7 @@ public void testSwaggerResolveWithOAS31OptionTask() throws IOException { " mavenCentral()\n" + "}\n" + "dependencies { \n" + - " implementation 'io.swagger.core.v3:swagger-jaxrs2:2.2.23'\n" + + " implementation 'io.swagger.core.v3:swagger-jaxrs2:2.2.24-SNAPSHOT'\n" + " implementation 'javax.ws.rs:javax.ws.rs-api:2.1'\n" + " implementation 'javax.servlet:javax.servlet-api:3.1.0'\n" + " testImplementation 'com.github.tomakehurst:wiremock:2.27.2'\n" + diff --git a/modules/swagger-integration/pom.xml b/modules/swagger-integration/pom.xml index f5d341cb1a..55383f6672 100644 --- a/modules/swagger-integration/pom.xml +++ b/modules/swagger-integration/pom.xml @@ -6,7 +6,7 @@ io.swagger.core.v3 swagger-project - 2.2.23 + 2.2.24-SNAPSHOT ../.. swagger-integration diff --git a/modules/swagger-jaxrs2-servlet-initializer-v2/pom.xml b/modules/swagger-jaxrs2-servlet-initializer-v2/pom.xml index 8c96d1b68e..cebc663de5 100644 --- a/modules/swagger-jaxrs2-servlet-initializer-v2/pom.xml +++ b/modules/swagger-jaxrs2-servlet-initializer-v2/pom.xml @@ -5,7 +5,7 @@ swagger-project io.swagger.core.v3 - 2.2.23 + 2.2.24-SNAPSHOT ../../ 4.0.0 diff --git a/modules/swagger-jaxrs2-servlet-initializer/pom.xml b/modules/swagger-jaxrs2-servlet-initializer/pom.xml index 8855442358..e90c67d2d3 100644 --- a/modules/swagger-jaxrs2-servlet-initializer/pom.xml +++ b/modules/swagger-jaxrs2-servlet-initializer/pom.xml @@ -5,7 +5,7 @@ swagger-project io.swagger.core.v3 - 2.2.23 + 2.2.24-SNAPSHOT ../../ 4.0.0 diff --git a/modules/swagger-jaxrs2/pom.xml b/modules/swagger-jaxrs2/pom.xml index d3959cee15..43a4a18c20 100644 --- a/modules/swagger-jaxrs2/pom.xml +++ b/modules/swagger-jaxrs2/pom.xml @@ -5,7 +5,7 @@ swagger-project io.swagger.core.v3 - 2.2.23 + 2.2.24-SNAPSHOT ../../ 4.0.0 diff --git a/modules/swagger-maven-plugin/pom.xml b/modules/swagger-maven-plugin/pom.xml index d767853597..e314cab31f 100644 --- a/modules/swagger-maven-plugin/pom.xml +++ b/modules/swagger-maven-plugin/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project - 2.2.23 + 2.2.24-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-models/pom.xml b/modules/swagger-models/pom.xml index 02949da0a7..3b0cabbe4e 100644 --- a/modules/swagger-models/pom.xml +++ b/modules/swagger-models/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project - 2.2.23 + 2.2.24-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-annotations-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-annotations-jakarta/pom.xml index f50409b1ae..caf6327b60 100644 --- a/modules/swagger-project-jakarta/modules/swagger-annotations-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-annotations-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23 + 2.2.24-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-core-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-core-jakarta/pom.xml index 5de6812ccd..896ca77849 100644 --- a/modules/swagger-project-jakarta/modules/swagger-core-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-core-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23 + 2.2.24-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-integration-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-integration-jakarta/pom.xml index c31c69fdae..d9091ea7d8 100644 --- a/modules/swagger-project-jakarta/modules/swagger-integration-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-integration-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23 + 2.2.24-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-jakarta/pom.xml index 218fa56687..044997af99 100644 --- a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23 + 2.2.24-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-jakarta/pom.xml index c97567b808..ecd6390232 100644 --- a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23 + 2.2.24-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-v2-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-v2-jakarta/pom.xml index d3e2a4d82c..0d2a616115 100644 --- a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-v2-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-v2-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23 + 2.2.24-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-maven-plugin-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-maven-plugin-jakarta/pom.xml index d921927f4f..67364ce61e 100644 --- a/modules/swagger-project-jakarta/modules/swagger-maven-plugin-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-maven-plugin-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23 + 2.2.24-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-models-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-models-jakarta/pom.xml index 130bc7feb5..c9bce80e88 100644 --- a/modules/swagger-project-jakarta/modules/swagger-models-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-models-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.2.23 + 2.2.24-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/pom.xml b/modules/swagger-project-jakarta/pom.xml index dc5fdb4f7e..11639bc6c5 100644 --- a/modules/swagger-project-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/pom.xml @@ -6,7 +6,7 @@ pom swagger-project-jakarta swagger-project-jakarta - 2.2.23 + 2.2.24-SNAPSHOT https://github.com/swagger-api/swagger-core scm:git:git@github.com:swagger-api/swagger-core.git diff --git a/pom.xml b/pom.xml index e590eeff4a..355de17851 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ pom swagger-project swagger-project - 2.2.23 + 2.2.24-SNAPSHOT https://github.com/swagger-api/swagger-core scm:git:git@github.com:swagger-api/swagger-core.git From fcf98fc18c23d35825176a7781dbdbba2cb0ce9a Mon Sep 17 00:00:00 2001 From: MicRyc Date: Tue, 3 Sep 2024 10:19:37 +0200 Subject: [PATCH 04/11] refs #4715 - change JsonSchemaDialect to one accepted by Swagger-UI --- .../src/main/java/io/swagger/v3/core/util/OpenAPI30To31.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/OpenAPI30To31.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/OpenAPI30To31.java index 967ccf02fa..e2cc8fafc9 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/OpenAPI30To31.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/OpenAPI30To31.java @@ -10,7 +10,7 @@ public class OpenAPI30To31 { public void process(OpenAPI openAPI) { openAPI.openapi("3.1.0") - .jsonSchemaDialect("https://json-schema.org/draft/2020-12/schema") + .jsonSchemaDialect("https://spec.openapis.org/oas/3.1/dialect/base") .specVersion(SpecVersion.V31); removeReservedExtensionsName(openAPI.getExtensions()); From 6c95bfbd0929e308bf859c5be139e43ec6f70017 Mon Sep 17 00:00:00 2001 From: MicRyc Date: Tue, 10 Sep 2024 13:44:27 +0200 Subject: [PATCH 05/11] Add support for OAS 3.1 schema.types processing to make top level type: object visible in generated spec --- .../v3/core/jackson/ModelResolver.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) 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 66634f693f..2624ed42d4 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 @@ -536,18 +536,24 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context } } } else if (isComposedSchema) { - model = new ComposedSchema() - .type("object") - .name(name); + model = new ComposedSchema().name(name); + if (openapi31 && resolvedArrayAnnotation == null){ + model.addType("object"); + }else{ + model.type("object"); + } } else { AnnotatedType aType = ReferenceTypeUtils.unwrapReference(annotatedType); if (aType != null) { model = context.resolve(aType); return model; } else { - model = new Schema() - .type("object") - .name(name); + model = new Schema().name(name); + if (openapi31 && resolvedArrayAnnotation == null){ + model.addType("object"); + }else{ + model.type("object"); + } } } @@ -2980,7 +2986,7 @@ public void setOpenapi31(boolean openapi31) { } protected boolean isObjectSchema(Schema schema) { - return "object".equals(schema.getType()) || (schema.getType() == null && schema.getProperties() != null && !schema.getProperties().isEmpty()); + return (schema.getTypes() != null && schema.getTypes().contains("object")) || "object".equals(schema.getType()) || (schema.getType() == null && schema.getProperties() != null && !schema.getProperties().isEmpty()); } protected Schema buildRefSchemaIfObject(Schema schema, ModelConverterContext context) { From 16d403ec84af034b99e8e53701c88a317c2fe800 Mon Sep 17 00:00:00 2001 From: MicRyc Date: Tue, 10 Sep 2024 14:16:50 +0200 Subject: [PATCH 06/11] Add tests for ModelResolver / fix existing tests --- .../v3/core/resolving/Ticket4679Test.java | 1 + .../resolving/resources/TestArrayType.java | 30 +++++++++++++ .../resolving/resources/TestObject4715.java | 34 ++++++++++++++ .../resolving/v31/ModelResolverOAS31Test.java | 44 +++++++++++++++++++ .../java/io/swagger/v3/jaxrs2/ReaderTest.java | 26 ++++++++++- .../resources/petstore/WebHookResource.yaml | 1 + .../callbacks/ComplexCallback31Resource.yaml | 1 + .../parameters/Parameters31Resource.yaml | 7 ++- .../requestbody/RequestBody31Resource.yaml | 1 + 9 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/resources/TestArrayType.java create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/resources/TestObject4715.java diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket4679Test.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket4679Test.java index 9998f8b6a2..8c51838b3a 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket4679Test.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket4679Test.java @@ -15,6 +15,7 @@ public class Ticket4679Test extends SwaggerTestBase{ public void testCustomSchemaImplementation() { String expectedYaml = "ModelWithCustomSchemaImplementationInProperty:\n" + + " type: object\n" + " properties:\n" + " exampleField:\n" + " type: integer\n" + diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/resources/TestArrayType.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/resources/TestArrayType.java new file mode 100644 index 0000000000..3c180c22b7 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/resources/TestArrayType.java @@ -0,0 +1,30 @@ +package io.swagger.v3.core.resolving.resources; + +import io.swagger.v3.oas.annotations.media.ArraySchema; + +import java.util.ArrayList; +import java.util.List; + +public class TestArrayType { + + private Integer id; + + @ArraySchema(maxItems = 10) + private List names; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public List getNames() { + return names; + } + + public void setNames(List names) { + this.names = names; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/resources/TestObject4715.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/resources/TestObject4715.java new file mode 100644 index 0000000000..f3e9914135 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/resources/TestObject4715.java @@ -0,0 +1,34 @@ +package io.swagger.v3.core.resolving.resources; + +public class TestObject4715 { + + private String foo; + + private String bar; + + private Integer id; + + public String getFoo() { + return foo; + } + + public void setFoo(String foo) { + this.foo = foo; + } + + public String getBar() { + return bar; + } + + public void setBar(String bar) { + this.bar = bar; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/ModelResolverOAS31Test.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/ModelResolverOAS31Test.java index f9aa3ce83c..8b5ce8b67a 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/ModelResolverOAS31Test.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/ModelResolverOAS31Test.java @@ -5,6 +5,8 @@ import io.swagger.v3.core.jackson.ModelResolver; import io.swagger.v3.core.matchers.SerializationMatchers; import io.swagger.v3.core.resolving.SwaggerTestBase; +import io.swagger.v3.core.resolving.resources.TestArrayType; +import io.swagger.v3.core.resolving.resources.TestObject4715; import io.swagger.v3.core.resolving.v31.model.AnnotatedArray; import io.swagger.v3.core.resolving.v31.model.ModelWithDependentSchema; import io.swagger.v3.core.resolving.v31.model.ModelWithOAS31Stuff; @@ -39,6 +41,7 @@ public void testOAS31Fields() { final ModelConverterContextImpl context = new ModelConverterContextImpl(modelResolver); Schema model = context.resolve(new AnnotatedType(ModelWithOAS31Stuff.class)); SerializationMatchers.assertEqualsToYaml31(context.getDefinedModels(), "Address:\n" + + " type: object\n" + " if:\n" + " $ref: '#/components/schemas/AnnotatedCountry'\n" + " then:\n" + @@ -59,10 +62,12 @@ public void testOAS31Fields() { " propertyNames:\n" + " $ref: '#/components/schemas/PropertyNamesPattern'\n" + "AnnotatedCountry:\n" + + " type: object\n" + " properties:\n" + " country:\n" + " const: United States\n" + "Client:\n" + + " type: object\n" + " properties:\n" + " name:\n" + " type: string\n" + @@ -70,6 +75,7 @@ public void testOAS31Fields() { " type: integer\n" + " format: int32\n" + "CreditCard:\n" + + " type: object\n" + " properties:\n" + " billingAddress:\n" + " type: string\n" + @@ -124,6 +130,7 @@ public void testOAS31Fields() { " properties:\n" + " extraObject: {}\n" + "MultipleBaseBean:\n" + + " type: object\n" + " description: MultipleBaseBean\n" + " properties:\n" + " beanType:\n" + @@ -152,14 +159,17 @@ public void testOAS31Fields() { " format: int32\n" + " description: MultipleSub2Bean\n" + "PostalCodeNumberPattern:\n" + + " type: object\n" + " properties:\n" + " postalCode:\n" + " pattern: \"[0-9]{5}(-[0-9]{4})?\"\n" + "PostalCodePattern:\n" + + " type: object\n" + " properties:\n" + " postalCode:\n" + " pattern: \"[A-Z][0-9][A-Z] [0-9][A-Z][0-9]\"\n" + "PropertyNamesPattern:\n" + + " type: object\n" + " pattern: \"^[A-Za-z_][A-Za-z0-9_]*$\"\n"); } @@ -170,10 +180,12 @@ public void testDependentSchemasAnnotation() { io.swagger.v3.oas.models.media.Schema model = context.resolve(new AnnotatedType(ModelWithDependentSchema.class)); SerializationMatchers.assertEqualsToYaml31(context.getDefinedModels(), "BooleanFakeClass:\n" + + " type: object\n" + " properties:\n" + " type:\n" + " type: boolean\n" + "ModelWithDependentSchema:\n" + + " type: object\n" + " dependentSchemas:\n" + " value:\n" + " properties:\n" + @@ -187,4 +199,36 @@ public void testDependentSchemasAnnotation() { " format: int32\n"); } + @Test(description = "Top level type:object should appear in OAS31") + public void testObjectTypeSchemaOAS31(){ + final ModelResolver modelResolver = new ModelResolver(mapper()).openapi31(true); + final ModelConverterContextImpl context = new ModelConverterContextImpl(modelResolver); + io.swagger.v3.oas.models.media.Schema model = context.resolve(new AnnotatedType(TestObject4715.class)); + SerializationMatchers.assertEqualsToYaml31(model, "type: object\n" + + "properties:\n" + + " foo:\n" + + " type: string\n" + + " bar:\n" + + " type: string\n" + + " id:\n" + + " type: integer\n" + + " format: int32"); + } + + @Test + public void testFieldArraySchemaAnnotation() { + final ModelResolver modelResolver = new ModelResolver(mapper()).openapi31(true); + final ModelConverterContextImpl context = new ModelConverterContextImpl(modelResolver); + io.swagger.v3.oas.models.media.Schema model = context.resolve(new AnnotatedType(TestArrayType.class)); + SerializationMatchers.assertEqualsToYaml31(model, " type: object\n" + + " properties:\n" + + " id:\n" + + " type: integer\n" + + " format: int32\n" + + " names:\n" + + " type: array\n" + + " items:\n" + + " type: string\n" + + " maxItems: 10"); + } } diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java index 0f32a4dd30..53a27ce435 100644 --- a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java @@ -3378,6 +3378,7 @@ public void testOas31Petstore() { "components:\n" + " schemas:\n" + " Bar:\n" + + " type: object\n" + " deprecated: true\n" + " description: Bar\n" + " properties:\n" + @@ -3394,6 +3395,7 @@ public void testOas31Petstore() { " - string\n" + " format: int32\n" + " Category:\n" + + " type: object\n" + " properties:\n" + " id:\n" + " type: integer\n" + @@ -3403,6 +3405,7 @@ public void testOas31Petstore() { " xml:\n" + " name: Category\n" + " Foo:\n" + + " type: object\n" + " deprecated: true\n" + " description: Foo\n" + " properties:\n" + @@ -3420,6 +3423,7 @@ public void testOas31Petstore() { " - object\n" + " format: int32\n" + " IfSchema:\n" + + " type: object\n" + " deprecated: true\n" + " description: if schema\n" + " properties:\n" + @@ -3437,6 +3441,7 @@ public void testOas31Petstore() { " - object\n" + " format: int32\n" + " Pet:\n" + + " type: object\n" + " properties:\n" + " id:\n" + " type: integer\n" + @@ -3470,6 +3475,7 @@ public void testOas31Petstore() { " xml:\n" + " name: Pet\n" + " Tag:\n" + + " type: object\n" + " properties:\n" + " id:\n" + " type: integer\n" + @@ -3510,6 +3516,7 @@ public void test31RefSiblings() { "components:\n" + " schemas:\n" + " Foo:\n" + + " type: object\n" + " deprecated: true\n" + " description: Foo\n" + " properties:\n" + @@ -3527,6 +3534,7 @@ public void test31RefSiblings() { " - object\n" + " format: int32\n" + " SimpleTag:\n" + + " type: object\n" + " properties:\n" + " annotated:\n" + " $ref: '#/components/schemas/SimpleCategory'\n" + @@ -3534,7 +3542,8 @@ public void test31RefSiblings() { " properties:\n" + " foo:\n" + " $ref: '#/components/schemas/Foo'\n" + - " SimpleCategory: {}\n"; + " SimpleCategory:\n" + + " type: object\n" ; SerializationMatchers.assertEqualsToYaml31(openAPI, yaml); } @@ -3558,12 +3567,14 @@ public void testSiblings() { "components:\n" + " schemas:\n" + " Category:\n" + + " type: object\n" + " description: parent\n" + " properties:\n" + " id:\n" + " type: integer\n" + " format: int64\n" + " Pet:\n" + + " type: object\n" + " description: Pet\n" + " properties:\n" + " category:\n" + @@ -3606,6 +3617,7 @@ public void testSiblingsOnResource() { "components:\n" + " schemas:\n" + " PetSimple:\n" + + " type: object\n" + " description: Pet\n"; SerializationMatchers.assertEqualsToYaml31(openAPI, yaml); } @@ -3654,6 +3666,7 @@ public void testSiblingsOnResourceResponse() { "components:\n" + " schemas:\n" + " PetSimple:\n" + + " type: object\n" + " description: Pet\n"; SerializationMatchers.assertEqualsToYaml31(openAPI, yaml); } @@ -3699,6 +3712,7 @@ public void testSiblingsOnResourceRequestBody() { "components:\n" + " schemas:\n" + " PetSimple:\n" + + " type: object\n" + " description: Pet\n"; SerializationMatchers.assertEqualsToYaml31(openAPI, yaml); } @@ -3775,6 +3789,7 @@ public void testSiblingsOnResourceRequestBodyMultiple() { "components:\n" + " schemas:\n" + " PetSimple:\n" + + " type: object\n" + " description: Pet\n"; SerializationMatchers.assertEqualsToYaml31(openAPI, yaml); } @@ -3838,12 +3853,14 @@ public void testSiblingsOnProperty() { "components:\n" + " schemas:\n" + " Category:\n" + + " type: object\n" + " description: parent\n" + " properties:\n" + " id:\n" + " type: integer\n" + " format: int64\n" + " Pet:\n" + + " type: object\n" + " description: Pet\n" + " properties:\n" + " category:\n" + @@ -3934,6 +3951,7 @@ public void testMisc31() { " creditCard:\n" + " $ref: '#/components/schemas/CreditCard'\n" + " MultipleBaseBean:\n" + + " type: object\n" + " description: MultipleBaseBean\n" + " properties:\n" + " beanType:\n" + @@ -3962,6 +3980,7 @@ public void testMisc31() { " format: int32\n" + " description: MultipleSub2Bean\n" + " Address:\n" + + " type: object\n" + " if:\n" + " $ref: '#/components/schemas/AnnotatedCountry'\n" + " then:\n" + @@ -3982,22 +4001,27 @@ public void testMisc31() { " propertyNames:\n" + " $ref: '#/components/schemas/PropertyNamesPattern'\n" + " AnnotatedCountry:\n" + + " type: object\n" + " properties:\n" + " country:\n" + " const: United States\n" + " CreditCard:\n" + + " type: object\n" + " properties:\n" + " billingAddress:\n" + " type: string\n" + " PostalCodeNumberPattern:\n" + + " type: object\n" + " properties:\n" + " postalCode:\n" + " pattern: \"[0-9]{5}(-[0-9]{4})?\"\n" + " PostalCodePattern:\n" + + " type: object\n" + " properties:\n" + " postalCode:\n" + " pattern: \"[A-Z][0-9][A-Z] [0-9][A-Z][0-9]\"\n" + " PropertyNamesPattern:\n" + + " type: object\n" + " pattern: \"^[A-Za-z_][A-Za-z0-9_]*$\"\n"; SerializationMatchers.assertEqualsToYaml31(openAPI, yaml); } diff --git a/modules/swagger-jaxrs2/src/test/resources/petstore/WebHookResource.yaml b/modules/swagger-jaxrs2/src/test/resources/petstore/WebHookResource.yaml index 34ed3ea8f9..8569aafe81 100644 --- a/modules/swagger-jaxrs2/src/test/resources/petstore/WebHookResource.yaml +++ b/modules/swagger-jaxrs2/src/test/resources/petstore/WebHookResource.yaml @@ -2,6 +2,7 @@ openapi: 3.1.0 components: schemas: SubscriptionResponse: + type: object properties: subscriptionId: type: string diff --git a/modules/swagger-jaxrs2/src/test/resources/petstore/callbacks/ComplexCallback31Resource.yaml b/modules/swagger-jaxrs2/src/test/resources/petstore/callbacks/ComplexCallback31Resource.yaml index 3fe539678e..65b5cec4f2 100644 --- a/modules/swagger-jaxrs2/src/test/resources/petstore/callbacks/ComplexCallback31Resource.yaml +++ b/modules/swagger-jaxrs2/src/test/resources/petstore/callbacks/ComplexCallback31Resource.yaml @@ -50,6 +50,7 @@ paths: components: schemas: User: + type: object properties: id: type: integer diff --git a/modules/swagger-jaxrs2/src/test/resources/petstore/parameters/Parameters31Resource.yaml b/modules/swagger-jaxrs2/src/test/resources/petstore/parameters/Parameters31Resource.yaml index 3cb83e1350..e6b22d1bcc 100644 --- a/modules/swagger-jaxrs2/src/test/resources/petstore/parameters/Parameters31Resource.yaml +++ b/modules/swagger-jaxrs2/src/test/resources/petstore/parameters/Parameters31Resource.yaml @@ -100,6 +100,7 @@ paths: components: schemas: Category: + type: object properties: id: type: integer @@ -109,6 +110,7 @@ components: xml: name: Category Pet: + type: object properties: id: type: integer @@ -139,6 +141,7 @@ components: xml: name: Pet Tag: + type: object properties: id: type: integer @@ -146,8 +149,9 @@ components: name: type: string xml: - name: Tag + name: tag User: + type: object properties: id: type: integer @@ -171,6 +175,7 @@ components: xml: name: User SubscriptionResponse: + type: object properties: subscriptionId: type: string diff --git a/modules/swagger-jaxrs2/src/test/resources/petstore/requestbody/RequestBody31Resource.yaml b/modules/swagger-jaxrs2/src/test/resources/petstore/requestbody/RequestBody31Resource.yaml index 3229ea10b2..f01cdb9024 100644 --- a/modules/swagger-jaxrs2/src/test/resources/petstore/requestbody/RequestBody31Resource.yaml +++ b/modules/swagger-jaxrs2/src/test/resources/petstore/requestbody/RequestBody31Resource.yaml @@ -59,6 +59,7 @@ paths: components: schemas: User: + type: object properties: id: type: integer From e0838d0be4016b714bafa78d022c76c4d8c34e2a Mon Sep 17 00:00:00 2001 From: MicRyc Date: Wed, 11 Sep 2024 13:43:30 +0200 Subject: [PATCH 07/11] fix-SWG-11909-NPE during webhooks processing with --- .../src/main/java/io/swagger/v3/core/filter/SpecFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/filter/SpecFilter.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/filter/SpecFilter.java index 7d2f00a211..aef8b69384 100755 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/filter/SpecFilter.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/filter/SpecFilter.java @@ -85,7 +85,7 @@ public OpenAPI filter(OpenAPI openAPI, OpenAPISpecFilter filter, Map Date: Mon, 16 Sep 2024 12:14:54 +0200 Subject: [PATCH 08/11] schema resolution options - Phase 1: global inline --- .../v3/core/converter/ModelConverters.java | 33 ++++ .../v3/core/jackson/ModelResolver.java | 32 ++- .../v3/core/util/AnnotationsUtils.java | 49 ++++- .../core/resolving/InlineResolvingTest.java | 172 ++++++++++++++++ .../v3/plugins/gradle/tasks/ResolveTask.java | 18 ++ .../integration/GenericOpenApiContext.java | 35 ++++ .../oas/integration/SwaggerConfiguration.java | 17 ++ .../integration/api/OpenAPIConfiguration.java | 6 + .../java/io/swagger/v3/jaxrs2/Reader.java | 4 +- .../ServletConfigContextUtils.java | 5 + .../ServletOpenApiConfigurationLoader.java | 6 +- .../v3/jaxrs2/integration/SwaggerLoader.java | 20 ++ .../java/io/swagger/v3/jaxrs2/ReaderTest.java | 187 ++++++++++++++++++ .../SchemaResolutionResource.java | 82 ++++++++ .../SchemaResolutionResourceSimple.java | 30 +++ .../swagger/v3/plugin/maven/SwaggerMojo.java | 12 ++ .../swagger/v3/oas/models/media/Schema.java | 21 ++ 17 files changed, 710 insertions(+), 19 deletions(-) create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/InlineResolvingTest.java create mode 100644 modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResource.java create mode 100644 modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResourceSimple.java diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverters.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverters.java index 0bb9c21f02..b9cfc4c661 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverters.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverters.java @@ -42,6 +42,15 @@ public ModelConverters(boolean openapi31) { } } + public ModelConverters(boolean openapi31, Schema.SchemaResolution schemaResolution) { + converters = new CopyOnWriteArrayList<>(); + if (openapi31) { + converters.add(new ModelResolver(Json31.mapper()).openapi31(true)); + } else { + converters.add(new ModelResolver(Json.mapper()).schemaResolution(schemaResolution)); + } + } + public Set getSkippedPackages() { return skippedPackages; } @@ -61,6 +70,30 @@ public static ModelConverters getInstance(boolean openapi31) { return SINGLETON; } + public static void reset() { + synchronized (ModelConverters.class) { + SINGLETON = null; + SINGLETON31 = null; + } + } + + public static ModelConverters getInstance(boolean openapi31, Schema.SchemaResolution schemaResolution) { + synchronized (ModelConverters.class) { + if (openapi31) { + if (SINGLETON31 == null) { + SINGLETON31 = new ModelConverters(openapi31); + init(SINGLETON31); + } + return SINGLETON31; + } + if (SINGLETON == null) { + SINGLETON = new ModelConverters(openapi31, schemaResolution); + init(SINGLETON); + } + return SINGLETON; + } + } + private static void init(ModelConverters converter) { converter.addPackageToSkip("java.lang"); converter.addPackageToSkip("groovy.lang"); 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 2624ed42d4..8662312474 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 @@ -125,6 +125,8 @@ public class ModelResolver extends AbstractModelConverter implements ModelConver private boolean openapi31; + private Schema.SchemaResolution schemaResolution = Schema.SchemaResolution.DEFAULT; + public ModelResolver(ObjectMapper mapper) { super(mapper); } @@ -725,7 +727,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context property = context.resolve(aType); property = clone(property); if (openapi31) { - Optional reResolvedProperty = AnnotationsUtils.getSchemaFromAnnotation(ctxSchema, annotatedType.getComponents(), null, openapi31, property, context); + Optional reResolvedProperty = AnnotationsUtils.getSchemaFromAnnotation(ctxSchema, annotatedType.getComponents(), null, openapi31, property, schemaResolution, context); if (reResolvedProperty.isPresent()) { property = reResolvedProperty.get(); } @@ -773,10 +775,14 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context } if (context.getDefinedModels().containsKey(pName)) { - property = new Schema().$ref(constructRef(pName)); + if (Schema.SchemaResolution.INLINE.equals(this.schemaResolution)) { + property = context.getDefinedModels().get(pName); + } else { + property = new Schema().$ref(constructRef(pName)); + } property = clone(property); - if (openapi31) { - Optional reResolvedProperty = AnnotationsUtils.getSchemaFromAnnotation(ctxSchema, annotatedType.getComponents(), null, openapi31, property, context); + if (openapi31 || Schema.SchemaResolution.INLINE.equals(this.schemaResolution)) { + Optional reResolvedProperty = AnnotationsUtils.getSchemaFromAnnotation(ctxSchema, annotatedType.getComponents(), null, openapi31, property, this.schemaResolution, context); if (reResolvedProperty.isPresent()) { property = reResolvedProperty.get(); } @@ -1000,7 +1006,9 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context StringUtils.isNotBlank(model.getName())) { if (context.getDefinedModels().containsKey(model.getName())) { - model = new Schema().$ref(constructRef(model.getName())); + if (!Schema.SchemaResolution.INLINE.equals(this.schemaResolution)) { + model = new Schema().$ref(constructRef(model.getName())); + } } } else if (model != null && model.get$ref() != null) { model = new Schema().$ref(StringUtils.isNotEmpty(model.get$ref()) ? model.get$ref() : model.getName()); @@ -1255,6 +1263,18 @@ private void handleUnwrapped(List props, Schema innerModel, String prefi } } + public Schema.SchemaResolution getSchemaResolution() { + return schemaResolution; + } + + public void setSchemaResolution(Schema.SchemaResolution schemaResolution) { + this.schemaResolution = schemaResolution; + } + + public ModelResolver schemaResolution(Schema.SchemaResolution schemaResolution) { + setSchemaResolution(schemaResolution); + return this; + } private class GeneratorWrapper { @@ -1870,7 +1890,7 @@ protected Map resolveDependentSchemas(JavaType a, Annotation[] a } Annotation[] propAnnotations = new Annotation[]{dependentSchemaAnnotation.schema(), dependentSchemaAnnotation.array()}; Schema existingSchema = null; - Optional resolvedPropSchema = AnnotationsUtils.getSchemaFromAnnotation(dependentSchemaAnnotation.schema(), components, jsonViewAnnotation, openapi31, null, context); + Optional resolvedPropSchema = AnnotationsUtils.getSchemaFromAnnotation(dependentSchemaAnnotation.schema(), components, jsonViewAnnotation, openapi31, null, Schema.SchemaResolution.DEFAULT, context); if (resolvedPropSchema.isPresent()) { existingSchema = resolvedPropSchema.get(); dependentSchemas.put(name, existingSchema); 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 e1ed50c9f2..ef17f1c7d3 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 @@ -587,24 +587,40 @@ public static Optional getSchemaFromAnnotation( boolean openapi31, Schema existingSchema, ModelConverterContext context) { + return getSchemaFromAnnotation(schema, components, jsonViewAnnotation, openapi31, existingSchema, Schema.SchemaResolution.DEFAULT, null); + } + public static Optional getSchemaFromAnnotation( + io.swagger.v3.oas.annotations.media.Schema schema, + Components components, + JsonView jsonViewAnnotation, + boolean openapi31, + Schema existingSchema, + Schema.SchemaResolution schemaResolution, + ModelConverterContext context) { if (schema == null || !hasSchemaAnnotation(schema)) { - if (existingSchema == null || !openapi31) { + if (existingSchema == null || (!openapi31 && Schema.SchemaResolution.DEFAULT.equals(schemaResolution))) { return Optional.empty(); - } else if (existingSchema != null && openapi31) { + } else if (existingSchema != null && (openapi31 || Schema.SchemaResolution.INLINE.equals(schemaResolution))) { return Optional.of(existingSchema); } } Schema schemaObject = null; if (!openapi31) { if (existingSchema != null) { - return Optional.of(existingSchema); + if (!Schema.SchemaResolution.DEFAULT.equals(schemaResolution)) { + schemaObject = existingSchema; + } else { + return Optional.of(existingSchema); + } } - if (schema.oneOf().length > 0 || - schema.allOf().length > 0 || - schema.anyOf().length > 0) { - schemaObject = new ComposedSchema(); - } else { - schemaObject = new Schema(); + if (Schema.SchemaResolution.DEFAULT.equals(schemaResolution)) { + if (schema != null && (schema.oneOf().length > 0 || + schema.allOf().length > 0 || + schema.anyOf().length > 0)) { + schemaObject = new ComposedSchema(); + } else { + schemaObject = new Schema(); + } } } else { if (existingSchema == null) { @@ -613,6 +629,9 @@ public static Optional getSchemaFromAnnotation( schemaObject = existingSchema; } } + if (schema == null) { + return Optional.of(schemaObject); + } if (StringUtils.isNotBlank(schema.description())) { schemaObject.setDescription(schema.description()); } @@ -1956,11 +1975,15 @@ public static void updateAnnotation(Class clazz, io.swagger.v3.oas.annotation } + public static Annotation mergeSchemaAnnotations( + Annotation[] ctxAnnotations, JavaType type) { + return mergeSchemaAnnotations(ctxAnnotations, type, false); + } /* * returns null if no annotations, otherwise either ArraySchema or Schema */ public static Annotation mergeSchemaAnnotations( - Annotation[] ctxAnnotations, JavaType type) { + Annotation[] ctxAnnotations, JavaType type, boolean contextWins) { // get type array and schema io.swagger.v3.oas.annotations.media.Schema tS = type.getRawClass().getDeclaredAnnotation(io.swagger.v3.oas.annotations.media.Schema.class); if (!hasSchemaAnnotation(tS)) { @@ -2020,6 +2043,9 @@ else if (tS != null && tA == null && cS == null && cA != null) { } else if (tA != null && cA != null) { + if (contextWins) { + return mergeArraySchemaAnnotations(tA, cA); + } return mergeArraySchemaAnnotations(cA, tA); } @@ -2028,6 +2054,9 @@ else if (tS != null && cS == null && cA == null) { } else if (tS != null && cS != null) { + if (contextWins) { + return mergeSchemaAnnotations(cS, tS); + } return mergeSchemaAnnotations(tS, cS); } diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/InlineResolvingTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/InlineResolvingTest.java new file mode 100644 index 0000000000..42afe5c0f9 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/InlineResolvingTest.java @@ -0,0 +1,172 @@ +package io.swagger.v3.core.resolving; + +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverterContextImpl; +import io.swagger.v3.core.jackson.ModelResolver; +import io.swagger.v3.core.matchers.SerializationMatchers; +import io.swagger.v3.oas.annotations.media.Schema; +import org.testng.annotations.Test; + +public class InlineResolvingTest extends SwaggerTestBase{ + + @Test + public void testInlineResolving() { + + final ModelResolver modelResolver = new ModelResolver(mapper()).openapi31(false).schemaResolution(io.swagger.v3.oas.models.media.Schema.SchemaResolution.INLINE); + final ModelConverterContextImpl c = new ModelConverterContextImpl(modelResolver); + // ModelConverters c = ModelConverters.getInstance(false, io.swagger.v3.oas.models.media.Schema.SchemaResolution.INLINE); + c.resolve(new AnnotatedType(InlineSchemaFirst.class)); + + String expectedYaml = "InlineSchemaFirst:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " description: InlineSchemaFirst property 1\n" + + " nullable: true\n" + + " example: example\n" + + " property2:\n" + + " type: object\n" + + " description: ' InlineSchemaFirst property 2'\n" + + " example: example 2\n" + + "InlineSchemaPropertyFirst:\n" + + " type: object\n" + + " description: property\n" + + " example: example\n"; + + SerializationMatchers.assertEqualsToYaml(c.getDefinedModels(), expectedYaml); + // stringSchemaMap = c.readAll(InlineSchemaSecond.class); + c.resolve(new AnnotatedType(InlineSchemaSecond.class)); + expectedYaml = "InlineSchemaFirst:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " description: InlineSchemaFirst property 1\n" + + " nullable: true\n" + + " example: example\n" + + " property2:\n" + + " type: object\n" + + " description: ' InlineSchemaFirst property 2'\n" + + " example: example 2\n" + + "InlineSchemaPropertyFirst:\n" + + " type: object\n" + + " description: property\n" + + " example: example\n" + + "InlineSchemaPropertySecond:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " description: property 1\n" + + " property2:\n" + + " type: object\n" + + " description: property 2\n" + + " example: example\n" + + " description: propertysecond\n" + + " nullable: true\n" + + " example: examplesecond\n" + + "InlineSchemaPropertySimple:\n" + + " type: object\n" + + " description: property\n" + + " example: example\n" + + "InlineSchemaSecond:\n" + + " type: object\n" + + " properties:\n" + + " propertySecond1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " description: property 1\n" + + " property2:\n" + + " type: object\n" + + " description: property 2\n" + + " example: example\n" + + " description: InlineSchemaSecond property 1\n" + + " nullable: true\n" + + " example: examplesecond\n" + + " property2:\n" + + " type: object\n" + + " description: InlineSchemaSecond property 2\n" + + " example: InlineSchemaSecond example 2\n" + + "InlineSchemaSimple:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " description: property 1\n" + + " property2:\n" + + " type: object\n" + + " description: property 2\n" + + " example: example\n"; + SerializationMatchers.assertEqualsToYaml(c.getDefinedModels(), expectedYaml); + } + + static class InlineSchemaFirst { + + // public String foo; + + @Schema(description = "InlineSchemaFirst property 1", nullable = true) + public InlineSchemaPropertyFirst property1; + + + private InlineSchemaPropertyFirst property2; + + @Schema(description = " InlineSchemaFirst property 2", example = "example 2") + public InlineSchemaPropertyFirst getProperty2() { + return null; + } + } + + static class InlineSchemaSecond { + + // public String foo; + + @Schema(description = "InlineSchemaSecond property 1", nullable = true) + public InlineSchemaPropertySecond propertySecond1; + + + private InlineSchemaPropertyFirst property2; + + @Schema(description = "InlineSchemaSecond property 2", example = "InlineSchemaSecond example 2") + public InlineSchemaPropertyFirst getProperty2() { + return null; + } + } + + @Schema(description = "property", example = "example") + static class InlineSchemaPropertyFirst { + // public String bar; + } + + @Schema(description = "propertysecond", example = "examplesecond") + static class InlineSchemaPropertySecond { + public InlineSchemaSimple bar; + } + + static class InlineSchemaSimple { + + @Schema(description = "property 1") + public InlineSchemaPropertySimple property1; + + + private InlineSchemaPropertySimple property2; + + @Schema(description = "property 2", example = "example") + public InlineSchemaPropertySimple getProperty2() { + return null; + } + } + + @Schema(description = "property") + static class InlineSchemaPropertySimple { + // public String bar; + } +} diff --git a/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/tasks/ResolveTask.java b/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/tasks/ResolveTask.java index 87fcdc415f..c885bd593a 100644 --- a/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/tasks/ResolveTask.java +++ b/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/tasks/ResolveTask.java @@ -77,6 +77,8 @@ public enum Format {JSON, YAML, JSONANDYAML}; private Boolean convertToOpenAPI31 = false; + private String schemaResolution; + private String defaultResponseCode; @Input @@ -386,6 +388,19 @@ public void setConvertToOpenAPI31(Boolean convertToOpenAPI31) { } } + /** + * @since 2.2.24 + */ + @Input + @Optional + public String getSchemaResolution() { + return schemaResolution; + } + + public void setSchemaResolution(String schemaResolution) { + this.schemaResolution = schemaResolution; + } + @TaskAction public void resolve() throws GradleException { if (skip) { @@ -505,6 +520,9 @@ public void resolve() throws GradleException { method=swaggerLoaderClass.getDeclaredMethod("setConvertToOpenAPI31", Boolean.class); method.invoke(swaggerLoader, convertToOpenAPI31); + method=swaggerLoaderClass.getDeclaredMethod("setSchemaResolution", String.class); + method.invoke(swaggerLoader, schemaResolution); + method=swaggerLoaderClass.getDeclaredMethod("resolve"); Map specs = (Map)method.invoke(swaggerLoader); diff --git a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/GenericOpenApiContext.java b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/GenericOpenApiContext.java index ff14aa1c5e..76099eb352 100644 --- a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/GenericOpenApiContext.java +++ b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/GenericOpenApiContext.java @@ -76,6 +76,8 @@ public class GenericOpenApiContext implements O private Boolean convertToOpenAPI31; + private Schema.SchemaResolution schemaResolution; + public long getCacheTTL() { return cacheTTL; } @@ -330,6 +332,28 @@ public T convertToOpenAPI31(Boolean convertToOpenAPI31) { return (T) this; } + /** + * @since 2.2.24 + */ + public Schema.SchemaResolution getSchemaResolution() { + return schemaResolution; + } + + /** + * @since 2.2.24 + */ + public void setSchemaResolution(Schema.SchemaResolution schemaResolution) { + this.schemaResolution = schemaResolution; + } + + /** + * @since 2.2.24 + */ + public T schemaResolution(Schema.SchemaResolution schemaResolution) { + this.schemaResolution = schemaResolution; + return (T) this; + } + protected void register() { OpenApiContextLocator.getInstance().putOpenApiContext(id, this); } @@ -467,6 +491,9 @@ public T init() throws OpenApiConfigurationException { ((SwaggerConfiguration) openApiConfiguration).setId(id); ((SwaggerConfiguration) openApiConfiguration).setOpenAPI31(openAPI31); ((SwaggerConfiguration) openApiConfiguration).setConvertToOpenAPI31(convertToOpenAPI31); + if (schemaResolution != null) { + ((SwaggerConfiguration) openApiConfiguration).setSchemaResolution(schemaResolution); + } } openApiConfiguration = mergeParentConfiguration(openApiConfiguration, parent); @@ -559,6 +586,10 @@ public T init() throws OpenApiConfigurationException { if (openApiConfiguration.isConvertToOpenAPI31() != null && this.convertToOpenAPI31 == null) { this.convertToOpenAPI31 = openApiConfiguration.isConvertToOpenAPI31(); } + + if (openApiConfiguration.getSchemaResolution() != null && this.getSchemaResolution() == null) { + this.schemaResolution = openApiConfiguration.getSchemaResolution(); + } register(); return (T) this; } @@ -635,6 +666,10 @@ private OpenAPIConfiguration mergeParentConfiguration(OpenAPIConfiguration confi merged.setDefaultResponseCode(parentConfig.getDefaultResponseCode()); } + if (merged.getSchemaResolution() == null) { + merged.setSchemaResolution(parentConfig.getSchemaResolution()); + } + return merged; } diff --git a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/SwaggerConfiguration.java b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/SwaggerConfiguration.java index 1bd9041905..c9896626e3 100644 --- a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/SwaggerConfiguration.java +++ b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/SwaggerConfiguration.java @@ -2,6 +2,7 @@ import io.swagger.v3.oas.integration.api.OpenAPIConfiguration; import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; import java.util.Collection; import java.util.Map; @@ -40,6 +41,8 @@ public class SwaggerConfiguration implements OpenAPIConfiguration { private Boolean convertToOpenAPI31; + private Schema.SchemaResolution schemaResolution = Schema.SchemaResolution.DEFAULT; + @Override public String getDefaultResponseCode() { return defaultResponseCode; @@ -373,4 +376,18 @@ public SwaggerConfiguration convertToOpenAPI31(Boolean convertToOpenAPI31) { this.setConvertToOpenAPI31(convertToOpenAPI31); return this; } + + @Override + public Schema.SchemaResolution getSchemaResolution() { + return schemaResolution; + } + + public void setSchemaResolution(Schema.SchemaResolution schemaResolution) { + this.schemaResolution = schemaResolution; + } + + public SwaggerConfiguration schemaResolution(Schema.SchemaResolution schemaResolution) { + this.setSchemaResolution(schemaResolution); + return this; + } } diff --git a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/api/OpenAPIConfiguration.java b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/api/OpenAPIConfiguration.java index ae52702a9c..4854c2f5a1 100644 --- a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/api/OpenAPIConfiguration.java +++ b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/api/OpenAPIConfiguration.java @@ -1,6 +1,7 @@ package io.swagger.v3.oas.integration.api; import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; import java.util.Collection; import java.util.Map; @@ -68,4 +69,9 @@ public interface OpenAPIConfiguration { * @since 2.2.17 */ public String getDefaultResponseCode(); + + /** + * @since 2.2.24 + */ + public Schema.SchemaResolution getSchemaResolution(); } diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/Reader.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/Reader.java index e7396b9f46..082a1372fd 100644 --- a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/Reader.java +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/Reader.java @@ -1165,7 +1165,7 @@ protected Operation parseMethod( final Class subResource = getSubResourceWithJaxRsSubresourceLocatorSpecs(method); Schema returnTypeSchema = null; if (!shouldIgnoreClass(returnType.getTypeName()) && !method.getGenericReturnType().equals(subResource)) { - ResolvedSchema resolvedSchema = ModelConverters.getInstance(config.isOpenAPI31()).resolveAsResolvedSchema(new AnnotatedType(returnType).resolveAsRef(true).jsonViewAnnotation(jsonViewAnnotation).components(components)); + ResolvedSchema resolvedSchema = ModelConverters.getInstance(config.isOpenAPI31(), config.getSchemaResolution()).resolveAsResolvedSchema(new AnnotatedType(returnType).resolveAsRef(true).jsonViewAnnotation(jsonViewAnnotation).components(components)); if (resolvedSchema.schema != null) { returnTypeSchema = resolvedSchema.schema; Content content = new Content(); @@ -1318,7 +1318,7 @@ private boolean shouldIgnoreClass(String className) { } ignore = rawClassName.startsWith("javax.ws.rs."); ignore = ignore || rawClassName.equalsIgnoreCase("void"); - ignore = ignore || ModelConverters.getInstance(config.isOpenAPI31()).isRegisteredAsSkippedClass(rawClassName); + ignore = ignore || ModelConverters.getInstance(config.isOpenAPI31(), config.getSchemaResolution()).isRegisteredAsSkippedClass(rawClassName); return ignore; } diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/ServletConfigContextUtils.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/ServletConfigContextUtils.java index aa26058aaa..a2555f4f0b 100644 --- a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/ServletConfigContextUtils.java +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/ServletConfigContextUtils.java @@ -63,6 +63,11 @@ public class ServletConfigContextUtils { public static final String OPENAPI_CONFIGURATION_CONVERT_TO_OPENAPI_31_KEY = "openApi.configuration.convertToOpenAPI31"; + /** + * @since 2.2.24 + */ + public static final String OPENAPI_CONFIGURATION_SCHEMA_RESOLUTION_KEY = "openApi.configuration.schemaResolution"; + public static Set resolveResourcePackages(ServletConfig servletConfig) { if (!isServletConfigAvailable(servletConfig)) { return null; diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/ServletOpenApiConfigurationLoader.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/ServletOpenApiConfigurationLoader.java index baeb5dce76..f54f53769f 100644 --- a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/ServletOpenApiConfigurationLoader.java +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/ServletOpenApiConfigurationLoader.java @@ -6,6 +6,7 @@ import io.swagger.v3.oas.integration.api.OpenAPIConfigBuilder; import io.swagger.v3.oas.integration.api.OpenAPIConfiguration; import io.swagger.v3.oas.integration.api.OpenApiConfigurationLoader; +import io.swagger.v3.oas.models.media.Schema; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +25,7 @@ import static io.swagger.v3.jaxrs2.integration.ServletConfigContextUtils.OPENAPI_CONFIGURATION_READALLRESOURCES_KEY; import static io.swagger.v3.jaxrs2.integration.ServletConfigContextUtils.OPENAPI_CONFIGURATION_READER_KEY; import static io.swagger.v3.jaxrs2.integration.ServletConfigContextUtils.OPENAPI_CONFIGURATION_SCANNER_KEY; +import static io.swagger.v3.jaxrs2.integration.ServletConfigContextUtils.OPENAPI_CONFIGURATION_SCHEMA_RESOLUTION_KEY; import static io.swagger.v3.jaxrs2.integration.ServletConfigContextUtils.OPENAPI_CONFIGURATION_SKIPRESOLVEAPPPATH_KEY; import static io.swagger.v3.jaxrs2.integration.ServletConfigContextUtils.OPENAPI_CONFIGURATION_SORTOUTPUT_KEY; import static io.swagger.v3.jaxrs2.integration.ServletConfigContextUtils.OPENAPI_CONFIGURATION_ALWAYSRESOLVEAPPPATH_KEY; @@ -70,7 +72,9 @@ public OpenAPIConfiguration load(String path) throws IOException { .openAPI31(getBooleanInitParam(servletConfig, OPENAPI_CONFIGURATION_OPENAPI_31_KEY)) .convertToOpenAPI31(getBooleanInitParam(servletConfig, OPENAPI_CONFIGURATION_CONVERT_TO_OPENAPI_31_KEY)) .modelConverterClasses(resolveModelConverterClasses(servletConfig)); - + if (getInitParam(servletConfig, OPENAPI_CONFIGURATION_SCHEMA_RESOLUTION_KEY) != null) { + configuration.schemaResolution(Schema.SchemaResolution.valueOf(getInitParam(servletConfig, OPENAPI_CONFIGURATION_SCHEMA_RESOLUTION_KEY))); + } return configuration; } diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/SwaggerLoader.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/SwaggerLoader.java index c9375dbe5d..36e5987c5c 100644 --- a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/SwaggerLoader.java +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/SwaggerLoader.java @@ -10,6 +10,7 @@ import io.swagger.v3.oas.integration.SwaggerConfiguration; import io.swagger.v3.oas.integration.api.OpenApiContext; import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; import org.apache.commons.lang3.StringUtils; import java.util.Arrays; @@ -49,6 +50,8 @@ public class SwaggerLoader { private Boolean convertToOpenAPI31 = false; + private String schemaResolution; + /** * @since 2.0.6 */ @@ -250,6 +253,20 @@ public void setConvertToOpenAPI31(Boolean convertToOpenAPI31) { this.convertToOpenAPI31 = convertToOpenAPI31; } + /** + * @since 2.2.24 + */ + public String getSchemaResolution() { + return schemaResolution; + } + + /** + * @since 2.2.24 + */ + public void setSchemaResolution(String schemaResolution) { + this.schemaResolution = schemaResolution; + } + public Map resolve() throws Exception{ Set ignoredRoutesSet = null; @@ -301,6 +318,9 @@ public Map resolve() throws Exception{ .skipResolveAppPath(skipResolveAppPath) .openAPI31(openAPI31) .convertToOpenAPI31(convertToOpenAPI31); + if (schemaResolution != null) { + config.schemaResolution(Schema.SchemaResolution.valueOf(schemaResolution)); + } try { GenericOpenApiContextBuilder builder = new JaxrsOpenApiContextBuilder() .openApiConfiguration(config); diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java index 53a27ce435..62575c2f0e 100644 --- a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java @@ -98,6 +98,8 @@ import io.swagger.v3.jaxrs2.resources.generics.ticket3694.Ticket3694ResourceSimpleSameReturn; import io.swagger.v3.jaxrs2.resources.rs.ProcessTokenRestService; import io.swagger.v3.jaxrs2.resources.ticket3624.Service; +import io.swagger.v3.jaxrs2.schemaResolution.SchemaResolutionResource; +import io.swagger.v3.jaxrs2.schemaResolution.SchemaResolutionResourceSimple; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.integration.SwaggerConfiguration; import io.swagger.v3.oas.models.Components; @@ -4167,4 +4169,189 @@ public void test4483Response() { " type: string\n"; SerializationMatchers.assertEqualsToYaml(openAPI, yaml); } + + @Test + public void testSchemaResolution() { + ModelConverters.reset(); + Reader reader = new Reader(new SwaggerConfiguration().openAPI(new OpenAPI()).schemaResolution(Schema.SchemaResolution.INLINE)); + 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" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: InlineSchemaFirst property 1\n" + + " nullable: true\n" + + " example: example\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: ' InlineSchemaFirst property 2'\n" + + " example: example 2\n" + + " /test/inlineSchemaSecond:\n" + + " get:\n" + + " operationId: inlineSchemaSecond\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " '*/*':\n" + + " schema:\n" + + " type: object\n" + + " properties:\n" + + " foo:\n" + + " type: string\n" + + " propertySecond1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 1\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 2\n" + + " example: example\n" + + " description: InlineSchemaSecond property 1\n" + + " nullable: true\n" + + " example: examplesecond\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: InlineSchemaSecond property 2\n" + + " example: InlineSchemaSecond example 2\n" + + "components:\n" + + " schemas:\n" + + " InlineSchemaFirst:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: InlineSchemaFirst property 1\n" + + " nullable: true\n" + + " example: example\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: ' InlineSchemaFirst property 2'\n" + + " example: example 2\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" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 1\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 2\n" + + " example: example\n" + + " description: propertysecond\n" + + " nullable: true\n" + + " example: examplesecond\n" + + " InlineSchemaPropertySimple:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property\n" + + " example: example\n" + + " InlineSchemaSecond:\n" + + " type: object\n" + + " properties:\n" + + " foo:\n" + + " type: string\n" + + " propertySecond1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 1\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 2\n" + + " example: example\n" + + " description: InlineSchemaSecond property 1\n" + + " nullable: true\n" + + " example: examplesecond\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: InlineSchemaSecond property 2\n" + + " example: InlineSchemaSecond example 2\n" + + " InlineSchemaSimple:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 1\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 2\n" + + " example: example\n\n"; + SerializationMatchers.assertEqualsToYaml(openAPI, yaml); + ModelConverters.reset(); + } } diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResource.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResource.java new file mode 100644 index 0000000000..01e691ba1e --- /dev/null +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResource.java @@ -0,0 +1,82 @@ +package io.swagger.v3.jaxrs2.schemaResolution; + +import io.swagger.v3.oas.annotations.media.Schema; +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +@Path("test") +public class SchemaResolutionResource { + + @GET + @Path("/inlineSchemaSecond") + public InlineSchemaSecond inlineSchemaSecond() { + return null; + } + @GET + @Path("/inlineSchemaFirst") + public InlineSchemaFirst inlineSchemaFirst() { + return null; + } + + + static class InlineSchemaFirst { + + // public String foo; + + @Schema(description = "InlineSchemaFirst property 1", nullable = true) + public InlineSchemaPropertyFirst property1; + + + private InlineSchemaPropertyFirst property2; + + @Schema(description = " InlineSchemaFirst property 2", example = "example 2") + public InlineSchemaPropertyFirst getProperty2() { + return null; + } + } + + static class InlineSchemaSecond { + + public String foo; + + @Schema(description = "InlineSchemaSecond property 1", nullable = true) + public InlineSchemaPropertySecond propertySecond1; + + + private InlineSchemaPropertyFirst property2; + + @Schema(description = "InlineSchemaSecond property 2", example = "InlineSchemaSecond example 2") + public InlineSchemaPropertyFirst getProperty2() { + return null; + } + } + + @Schema(description = "property", example = "example") + static class InlineSchemaPropertyFirst { + public String bar; + } + + @Schema(description = "propertysecond", example = "examplesecond") + static class InlineSchemaPropertySecond { + public InlineSchemaSimple bar; + } + + static class InlineSchemaSimple { + + @Schema(description = "property 1") + public InlineSchemaPropertySimple property1; + + + private InlineSchemaPropertySimple property2; + + @Schema(description = "property 2", example = "example") + public InlineSchemaPropertySimple getProperty2() { + return null; + } + } + + @Schema(description = "property") + static class InlineSchemaPropertySimple { + public String bar; + } +} diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResourceSimple.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResourceSimple.java new file mode 100644 index 0000000000..61ac55e6b6 --- /dev/null +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResourceSimple.java @@ -0,0 +1,30 @@ +package io.swagger.v3.jaxrs2.schemaResolution; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +@Path("test") +public class SchemaResolutionResourceSimple { + + @GET + @Path("/inlineSchemaFirst") + public InlineSchemaFirst inlineSchemaFirst() { + return null; + } + + + static class InlineSchemaFirst { + + // public String foo; + + @Schema(description = "InlineSchemaFirst property 1", nullable = true) + public InlineSchemaPropertyFirst property1; + } + + @Schema(description = "property", example = "example") + static class InlineSchemaPropertyFirst { + public String bar; + } +} diff --git a/modules/swagger-maven-plugin/src/main/java/io/swagger/v3/plugin/maven/SwaggerMojo.java b/modules/swagger-maven-plugin/src/main/java/io/swagger/v3/plugin/maven/SwaggerMojo.java index 05911233d7..60bafc3d44 100644 --- a/modules/swagger-maven-plugin/src/main/java/io/swagger/v3/plugin/maven/SwaggerMojo.java +++ b/modules/swagger-maven-plugin/src/main/java/io/swagger/v3/plugin/maven/SwaggerMojo.java @@ -11,6 +11,7 @@ import io.swagger.v3.oas.integration.SwaggerConfiguration; import io.swagger.v3.oas.integration.api.OpenApiContext; import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; @@ -184,6 +185,7 @@ private void setDefaultsIfMissing(SwaggerConfiguration config) { if (config.isConvertToOpenAPI31() == null) { config.setConvertToOpenAPI31(convertToOpenAPI31); } + } /** @@ -355,6 +357,10 @@ private SwaggerConfiguration mergeConfig(OpenAPI openAPIInput, SwaggerConfigurat config.openAPI31(openapi31); } + if (StringUtils.isNotBlank(schemaResolution)) { + config.schemaResolution(Schema.SchemaResolution.valueOf(schemaResolution)); + } + return config; } @@ -453,6 +459,12 @@ private boolean isCollectionNotBlank(Collection collection) { @Parameter(property = "resolve.convertToOpenAPI31") private Boolean convertToOpenAPI31; + /** + * @since 2.2.24 + */ + @Parameter(property = "resolve.schemaResolution") + private String schemaResolution; + private String projectEncoding = "UTF-8"; private SwaggerConfiguration config; 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 8872a74c2c..8f76ad9ce8 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 @@ -1,6 +1,7 @@ package io.swagger.v3.oas.models.media; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.models.annotations.OpenAPI30; import io.swagger.v3.oas.models.annotations.OpenAPI31; import io.swagger.v3.oas.models.Components; @@ -44,6 +45,26 @@ public String toString() { } } + public static final String SCHEMA_RESOLUTION_PROPERTY = "schema-resolution"; + public enum SchemaResolution { + @JsonProperty("default") + DEFAULT("default"), + @JsonProperty("inline") + INLINE("inline"), + @JsonProperty("all-of") + ALL_OF("all-of"); + private String value; + + SchemaResolution(String value) { + this.value = value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + } + protected T _default; private String name; From dd98ce045cc0d9c1e67bf49742012512922415ea Mon Sep 17 00:00:00 2001 From: MicRyc Date: Mon, 16 Sep 2024 11:30:37 +0200 Subject: [PATCH 09/11] refs-#4703/#4702-Fix applyBeanValidatorAnnotations method to support both OAS 3.1/3.0 --- .../v3/core/jackson/ModelResolver.java | 49 ++++--- .../resolving/v31/ModelResolverOAS31Test.java | 120 +++++++++++++++++- 2 files changed, 146 insertions(+), 23 deletions(-) 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 8662312474..ecb141a430 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 @@ -1511,43 +1511,38 @@ protected void applyBeanValidatorAnnotations(Schema property, Annotation[] annot if ("integer".equals(property.getType()) || "number".equals(property.getType())) { property.setMinimum(new BigDecimal(size.min())); property.setMaximum(new BigDecimal(size.max())); - } else if (property instanceof StringSchema) { - StringSchema sp = (StringSchema) property; - sp.minLength(Integer.valueOf(size.min())); - sp.maxLength(Integer.valueOf(size.max())); - } else if (property instanceof ArraySchema) { - ArraySchema sp = (ArraySchema) property; - sp.setMinItems(size.min()); - sp.setMaxItems(size.max()); + } + if (isStringSchema(property)) { + property.setMinLength(Integer.valueOf(size.min())); + property.setMaxLength(Integer.valueOf(size.max())); + } + if (isArraySchema(property)) { + property.setMinItems(size.min()); + property.setMaxItems(size.max()); } } if (annos.containsKey("javax.validation.constraints.DecimalMin")) { DecimalMin min = (DecimalMin) annos.get("javax.validation.constraints.DecimalMin"); - if (property instanceof NumberSchema) { - NumberSchema ap = (NumberSchema) property; - ap.setMinimum(new BigDecimal(min.value())); - ap.setExclusiveMinimum(!min.inclusive()); + if (isNumberSchema(property)) { + property.setMinimum(new BigDecimal(min.value())); + property.setExclusiveMinimum(!min.inclusive()); } } if (annos.containsKey("javax.validation.constraints.DecimalMax")) { DecimalMax max = (DecimalMax) annos.get("javax.validation.constraints.DecimalMax"); - if (property instanceof NumberSchema) { - NumberSchema ap = (NumberSchema) property; - ap.setMaximum(new BigDecimal(max.value())); - ap.setExclusiveMaximum(!max.inclusive()); + if (isNumberSchema(property)) { + property.setMaximum(new BigDecimal(max.value())); + property.setExclusiveMaximum(!max.inclusive()); } } if (annos.containsKey("javax.validation.constraints.Pattern")) { Pattern pattern = (Pattern) annos.get("javax.validation.constraints.Pattern"); - - if (property instanceof StringSchema) { + if (isStringSchema(property)) { property.setPattern(pattern.regexp()); } - - if(property.getItems() != null && property.getItems() instanceof StringSchema) { + if(property.getItems() != null && isStringSchema(property.getItems())) { property.getItems().setPattern(pattern.regexp()); } - } } @@ -3009,6 +3004,18 @@ protected boolean isObjectSchema(Schema schema) { return (schema.getTypes() != null && schema.getTypes().contains("object")) || "object".equals(schema.getType()) || (schema.getType() == null && schema.getProperties() != null && !schema.getProperties().isEmpty()); } + protected boolean isArraySchema(Schema schema){ + return "array".equals(schema.getType()) || (schema.getTypes() != null && schema.getTypes().contains("array")); + } + + protected boolean isStringSchema(Schema schema){ + return "string".equals(schema.getType()) || (schema.getTypes() != null && schema.getTypes().contains("string")); + } + + protected boolean isNumberSchema(Schema schema){ + return "number".equals(schema.getType()) || (schema.getTypes() != null && schema.getTypes().contains("number")); + } + protected Schema buildRefSchemaIfObject(Schema schema, ModelConverterContext context) { if (schema == null) { return null; diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/ModelResolverOAS31Test.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/ModelResolverOAS31Test.java index 8b5ce8b67a..12e2ca78fc 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/ModelResolverOAS31Test.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/ModelResolverOAS31Test.java @@ -2,6 +2,7 @@ import io.swagger.v3.core.converter.AnnotatedType; import io.swagger.v3.core.converter.ModelConverterContextImpl; +import io.swagger.v3.core.converter.ModelConverters; import io.swagger.v3.core.jackson.ModelResolver; import io.swagger.v3.core.matchers.SerializationMatchers; import io.swagger.v3.core.resolving.SwaggerTestBase; @@ -10,10 +11,14 @@ import io.swagger.v3.core.resolving.v31.model.AnnotatedArray; import io.swagger.v3.core.resolving.v31.model.ModelWithDependentSchema; import io.swagger.v3.core.resolving.v31.model.ModelWithOAS31Stuff; -import io.swagger.v3.core.resolving.v31.model.ModelWithOAS31StuffMinimal; -import io.swagger.v3.core.util.Yaml31; import io.swagger.v3.oas.models.media.Schema; import org.testng.annotations.Test; +import javax.validation.constraints.DecimalMax; +import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import java.util.List; +import java.util.Map; public class ModelResolverOAS31Test extends SwaggerTestBase { @@ -231,4 +236,115 @@ public void testFieldArraySchemaAnnotation() { " type: string\n" + " maxItems: 10"); } + + @Test(description = "@Pattern correctly handled in type parameters of properties using collections when using oas 3.1.0") + public void testModelUsingCollectionTypePropertyHandlesPatternAnnotationForOas31() { + String expectedYaml = "ClassWithUsingPatternOnCollection:\n" + + " type: object\n" + + " properties:\n" + + " myField:\n" + + " type: array\n" + + " items:\n" + + " pattern: myPattern\n" + + " type: string"; + + Map stringSchemaMap = ModelConverters.getInstance(true).readAll(ClassWithUsingPatternOnCollection.class); + SerializationMatchers.assertEqualsToYaml31(stringSchemaMap, expectedYaml); + } + + private static class ClassWithUsingPatternOnCollection { + private List<@Pattern(regexp = "myPattern") String> myField; + + public List getMyField() { + return myField; + } + + public void setMyField(List myField) { + this.myField = myField; + } + } + + @Test(description = "@Size correctly handled in properties using collections when using oas 3.1.0") + public void testModelUsingCollectionTypePropertyHandleSizeAnnotationForOas31() { + String expectedYaml = "ClassWithUsingSizeOnCollection:\n" + + " type: object\n" + + " properties:\n" + + " myField:\n" + + " maxItems: 100\n" + + " minItems: 1\n" + + " type: array\n" + + " items:\n" + + " type: string"; + + Map stringSchemaMap = ModelConverters.getInstance(true).readAll(ClassWithUsingSizeOnCollection.class); + SerializationMatchers.assertEqualsToYaml31(stringSchemaMap, expectedYaml); + } + + private static class ClassWithUsingSizeOnCollection { + @Size(min = 1, max = 100) + private List myField; + + public List getMyField() { + return myField; + } + + public void setMyField(List myField) { + this.myField = myField; + } + } + + @Test(description = "@Size correctly handled for field type String using OAS 3.1.0") + public void testSizeAnnotationOnFieldForOAS31() { + String expectedYaml = "ClassWithUsingSizeOnField:\n" + + " type: object\n" + + " properties:\n" + + " myField:\n" + + " type: string\n" + + " maxLength: 100\n" + + " minLength: 1"; + + Map stringSchemaMap = ModelConverters.getInstance(true).readAll(ClassWithUsingSizeOnField.class); + SerializationMatchers.assertEqualsToYaml31(stringSchemaMap, expectedYaml); + } + + private static class ClassWithUsingSizeOnField { + @Size(min = 1, max = 100) + private String myField; + + public String getMyField() { + return myField; + } + + public void setMyField(String myField) { + this.myField = myField; + } + } + + @Test(description = "@DecimalMax/Min annotations correctly handled for field type Number using OAS 3.1.0") + public void testDecimalAnnotationsOnField() { + String expectedYaml = "ClassWithUsingDecimalAnnotationsOnField:\n" + + " type: object\n" + + " properties:\n" + + " myField:\n" + + " type: number\n" + + " maximum: 100\n" + + " minimum: 1"; + + Map stringSchemaMap = ModelConverters.getInstance(true).readAll(ClassWithUsingDecimalAnnotationsOnField.class); + SerializationMatchers.assertEqualsToYaml31(stringSchemaMap, expectedYaml); + } + + private static class ClassWithUsingDecimalAnnotationsOnField { + @DecimalMin("1") + @DecimalMax("100") + private Number myField; + + public Number getMyField() { + return myField; + } + + public void setMyField(Number myField) { + this.myField = myField; + } + } } From a9510c3025d97ee2f81849ff13ba8618ce084e9a Mon Sep 17 00:00:00 2001 From: MicRyc Date: Thu, 19 Sep 2024 15:08:45 +0200 Subject: [PATCH 10/11] Add integer check for isNumberSchema --- .../java/io/swagger/v3/core/jackson/ModelResolver.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 ecb141a430..1cb9e12bbf 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 @@ -1495,20 +1495,20 @@ protected void applyBeanValidatorAnnotations(Schema property, Annotation[] annot } } if (annos.containsKey("javax.validation.constraints.Min")) { - if ("integer".equals(property.getType()) || "number".equals(property.getType())) { + if (isNumberSchema(property)) { Min min = (Min) annos.get("javax.validation.constraints.Min"); property.setMinimum(new BigDecimal(min.value())); } } if (annos.containsKey("javax.validation.constraints.Max")) { - if ("integer".equals(property.getType()) || "number".equals(property.getType())) { + if (isNumberSchema(property)) { Max max = (Max) annos.get("javax.validation.constraints.Max"); property.setMaximum(new BigDecimal(max.value())); } } if (annos.containsKey("javax.validation.constraints.Size")) { Size size = (Size) annos.get("javax.validation.constraints.Size"); - if ("integer".equals(property.getType()) || "number".equals(property.getType())) { + if (isNumberSchema(property)) { property.setMinimum(new BigDecimal(size.min())); property.setMaximum(new BigDecimal(size.max())); } @@ -3013,7 +3013,7 @@ protected boolean isStringSchema(Schema schema){ } protected boolean isNumberSchema(Schema schema){ - return "number".equals(schema.getType()) || (schema.getTypes() != null && schema.getTypes().contains("number")); + return "number".equals(schema.getType()) || (schema.getTypes() != null && schema.getTypes().contains("number")) || "integer".equals(schema.getType()) || (schema.getTypes() != null && schema.getTypes().contains("integer")); } protected Schema buildRefSchemaIfObject(Schema schema, ModelConverterContext context) { From 5fc5d0e61d04a00417a1afe3375e97569a75a8f0 Mon Sep 17 00:00:00 2001 From: frantuma Date: Mon, 23 Sep 2024 12:43:17 +0200 Subject: [PATCH 11/11] schema resolution options - Phase 2: global allOf --- .../v3/core/converter/ModelConverters.java | 4 +- .../v3/core/jackson/ModelResolver.java | 38 ++- .../v3/core/util/AnnotationsUtils.java | 6 + .../v3/core/util/ParameterProcessor.java | 61 ++++- .../v3/core/resolving/AllofResolvingTest.java | 165 ++++++++++++ .../integration/GenericOpenApiContext.java | 2 +- .../v3/jaxrs2/DefaultParameterExtension.java | 13 +- .../java/io/swagger/v3/jaxrs2/Reader.java | 9 +- .../jaxrs2/ext/AbstractOpenAPIExtension.java | 7 + .../v3/jaxrs2/ext/OpenAPIExtension.java | 5 + .../swagger/v3/jaxrs2/util/ReaderUtils.java | 8 +- .../java/io/swagger/v3/jaxrs2/ReaderTest.java | 189 -------------- .../v3/jaxrs2/SchemaResolutionAllOfTest.java | 60 +++++ .../v3/jaxrs2/SchemaResolutionInlineTest.java | 235 ++++++++++++++++++ .../SchemaResolutionResource.java | 2 +- .../SchemaResolutionResourceSimple.java | 14 +- pom.xml | 2 +- 17 files changed, 607 insertions(+), 213 deletions(-) create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/AllofResolvingTest.java create mode 100644 modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/SchemaResolutionAllOfTest.java create mode 100644 modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/SchemaResolutionInlineTest.java diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverters.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverters.java index b9cfc4c661..7149e587c5 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverters.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverters.java @@ -45,7 +45,7 @@ public ModelConverters(boolean openapi31) { public ModelConverters(boolean openapi31, Schema.SchemaResolution schemaResolution) { converters = new CopyOnWriteArrayList<>(); if (openapi31) { - converters.add(new ModelResolver(Json31.mapper()).openapi31(true)); + converters.add(new ModelResolver(Json31.mapper()).openapi31(true).schemaResolution(schemaResolution)); } else { converters.add(new ModelResolver(Json.mapper()).schemaResolution(schemaResolution)); } @@ -81,7 +81,7 @@ public static ModelConverters getInstance(boolean openapi31, Schema.SchemaResolu synchronized (ModelConverters.class) { if (openapi31) { if (SINGLETON31 == null) { - SINGLETON31 = new ModelConverters(openapi31); + SINGLETON31 = new ModelConverters(openapi31, Schema.SchemaResolution.DEFAULT); init(SINGLETON31); } return SINGLETON31; 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 1cb9e12bbf..dc9e74add1 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,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context Annotation[] ctxAnnotation31 = null; - if (openapi31) { + if (Schema.SchemaResolution.ALL_OF.equals(this.schemaResolution) || openapi31) { List ctxAnnotations31List = new ArrayList<>(); if (annotations != null) { for (Annotation a : annotations) { @@ -701,7 +701,6 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context AnnotatedType aType = new AnnotatedType() .type(propType) - .ctxAnnotations(openapi31 ? ctxAnnotation31 : annotations) .parent(model) .resolveAsRef(annotatedType.isResolveAsRef()) .jsonViewAnnotation(annotatedType.getJsonViewAnnotation()) @@ -709,7 +708,11 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context .schemaProperty(true) .components(annotatedType.getComponents()) .propertyName(propName); - + if (Schema.SchemaResolution.ALL_OF.equals(this.schemaResolution) || openapi31) { + aType.ctxAnnotations(ctxAnnotation31); + } else { + aType.ctxAnnotations(annotations); + } final AnnotatedMember propMember = member; aType.jsonUnwrappedHandler(t -> { JsonUnwrapped uw = propMember.getAnnotation(JsonUnwrapped.class); @@ -726,6 +729,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context }); property = context.resolve(aType); property = clone(property); + Schema ctxProperty = null; if (openapi31) { Optional reResolvedProperty = AnnotationsUtils.getSchemaFromAnnotation(ctxSchema, annotatedType.getComponents(), null, openapi31, property, schemaResolution, context); if (reResolvedProperty.isPresent()) { @@ -736,6 +740,16 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context property = reResolvedProperty.get(); } + } else if (Schema.SchemaResolution.ALL_OF.equals(this.schemaResolution)) { + Optional reResolvedProperty = AnnotationsUtils.getSchemaFromAnnotation(ctxSchema, annotatedType.getComponents(), null, openapi31, null, schemaResolution, context); + if (reResolvedProperty.isPresent()) { + ctxProperty = reResolvedProperty.get(); + } + reResolvedProperty = AnnotationsUtils.getArraySchema(ctxArraySchema, annotatedType.getComponents(), null, openapi31, ctxProperty); + if (reResolvedProperty.isPresent()) { + ctxProperty = reResolvedProperty.get(); + } + } if (property != null) { Boolean required = md.getRequired(); @@ -777,10 +791,15 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context if (context.getDefinedModels().containsKey(pName)) { if (Schema.SchemaResolution.INLINE.equals(this.schemaResolution)) { property = context.getDefinedModels().get(pName); + } else if (Schema.SchemaResolution.ALL_OF.equals(this.schemaResolution) && ctxProperty != null) { + property = new Schema() + .addAllOfItem(ctxProperty) + .addAllOfItem(new Schema().$ref(constructRef(pName))); } else { property = new Schema().$ref(constructRef(pName)); } property = clone(property); + // TODO: why is this needed? is it not handled before? if (openapi31 || Schema.SchemaResolution.INLINE.equals(this.schemaResolution)) { Optional reResolvedProperty = AnnotationsUtils.getSchemaFromAnnotation(ctxSchema, annotatedType.getComponents(), null, openapi31, property, this.schemaResolution, context); if (reResolvedProperty.isPresent()) { @@ -794,7 +813,13 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context } } else if (property.get$ref() != null) { if (!openapi31) { - property = new Schema().$ref(StringUtils.isNotEmpty(property.get$ref()) ? property.get$ref() : property.getName()); + if (Schema.SchemaResolution.ALL_OF.equals(this.schemaResolution) && ctxProperty != null) { + property = new Schema() + .addAllOfItem(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()); + } } else { if (StringUtils.isEmpty(property.get$ref())) { property.$ref(property.getName()); @@ -807,9 +832,12 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context if (property != null && io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED.equals(requiredMode)) { addRequiredItem(model, property.getName()); } + if (ctxProperty == null) { + ctxProperty = property; + } final boolean applyNotNullAnnotations = io.swagger.v3.oas.annotations.media.Schema.RequiredMode.AUTO.equals(requiredMode); annotations = addGenericTypeArgumentAnnotationsForOptionalField(propDef, annotations); - applyBeanValidatorAnnotations(propDef, property, annotations, model, applyNotNullAnnotations); + applyBeanValidatorAnnotations(propDef, ctxProperty, annotations, model, applyNotNullAnnotations); props.add(property); } 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 ef17f1c7d3..bdfbc2e691 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,6 +621,12 @@ public static Optional getSchemaFromAnnotation( } else { schemaObject = new Schema(); } + } else if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution)) { + if (existingSchema == null) { + schemaObject = new Schema(); + } else { + schemaObject = existingSchema; + } } } else { if (existingSchema == null) { 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 5c977bdfed..d219d52a55 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 @@ -42,6 +42,19 @@ public static Parameter applyAnnotations( String[] methodTypes, JsonView jsonViewAnnotation, boolean openapi31) { + return applyAnnotations(parameter, type, annotations, components, classTypes, methodTypes, jsonViewAnnotation, openapi31, null); + } + + public static Parameter applyAnnotations( + Parameter parameter, + Type type, + List annotations, + Components components, + String[] classTypes, + String[] methodTypes, + JsonView jsonViewAnnotation, + boolean openapi31, + Schema.SchemaResolution schemaResolution) { final AnnotationsHelper helper = new AnnotationsHelper(annotations, type); if (helper.isContext()) { @@ -59,17 +72,57 @@ public static Parameter applyAnnotations( if (paramSchemaOrArrayAnnotation != null) { reworkedAnnotations.add(paramSchemaOrArrayAnnotation); } + io.swagger.v3.oas.annotations.media.Schema ctxSchema = AnnotationsUtils.getSchemaAnnotation(annotations.toArray(new Annotation[0])); + 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)) { + List ctxAnnotations31List = new ArrayList<>(); + if (annotations != null) { + for (Annotation a : annotations) { + if ( + !(a instanceof io.swagger.v3.oas.annotations.media.Schema) && + !(a instanceof io.swagger.v3.oas.annotations.media.ArraySchema)) { + ctxAnnotations31List.add(a); + } + } + ctxAnnotation31 = ctxAnnotations31List.toArray(new Annotation[ctxAnnotations31List.size()]); + } + } AnnotatedType annotatedType = new AnnotatedType() .type(type) .resolveAsRef(true) .skipOverride(true) - .jsonViewAnnotation(jsonViewAnnotation) - .ctxAnnotations(reworkedAnnotations.toArray(new Annotation[reworkedAnnotations.size()])); + .jsonViewAnnotation(jsonViewAnnotation); + + if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution)) { + annotatedType.ctxAnnotations(ctxAnnotation31); + } else { + annotatedType.ctxAnnotations(reworkedAnnotations.toArray(new Annotation[reworkedAnnotations.size()])); + } - final ResolvedSchema resolvedSchema = ModelConverters.getInstance(openapi31).resolveAsResolvedSchema(annotatedType); + final ResolvedSchema resolvedSchema = ModelConverters.getInstance(openapi31, schemaResolution).resolveAsResolvedSchema(annotatedType); if (resolvedSchema.schema != null) { - parameter.setSchema(resolvedSchema.schema); + Schema resSchema = AnnotationsUtils.clone(resolvedSchema.schema, openapi31); + Schema ctxSchemaObject = null; + if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution)) { + Optional reResolvedSchema = AnnotationsUtils.getSchemaFromAnnotation(ctxSchema, annotatedType.getComponents(), null, openapi31, null, schemaResolution, null); + if (reResolvedSchema.isPresent()) { + ctxSchemaObject = reResolvedSchema.get(); + } + reResolvedSchema = AnnotationsUtils.getArraySchema(ctxArraySchema, annotatedType.getComponents(), null, openapi31, ctxSchemaObject); + if (reResolvedSchema.isPresent()) { + ctxSchemaObject = reResolvedSchema.get(); + } + + } + if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution) && ctxSchemaObject != null) { + resSchema = new Schema() + .addAllOfItem(ctxSchemaObject) + .addAllOfItem(resolvedSchema.schema); + } + parameter.setSchema(resSchema); } resolvedSchema.referencedSchemas.forEach(components::addSchemas); diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/AllofResolvingTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/AllofResolvingTest.java new file mode 100644 index 0000000000..bbec9310c1 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/AllofResolvingTest.java @@ -0,0 +1,165 @@ +package io.swagger.v3.core.resolving; + +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverterContextImpl; +import io.swagger.v3.core.jackson.ModelResolver; +import io.swagger.v3.core.matchers.SerializationMatchers; +import io.swagger.v3.oas.annotations.media.Schema; +import org.testng.annotations.Test; + +public class AllofResolvingTest extends SwaggerTestBase { + + @Test + public void testAllofResolving() { + + final ModelResolver modelResolver = new ModelResolver(mapper()).openapi31(false).schemaResolution(io.swagger.v3.oas.models.media.Schema.SchemaResolution.ALL_OF); + final ModelConverterContextImpl c = new ModelConverterContextImpl(modelResolver); + // ModelConverters c = ModelConverters.getInstance(false, io.swagger.v3.oas.models.media.Schema.SchemaResolution.INLINE); + c.resolve(new AnnotatedType(UserSchema.class)); + + String expectedYaml = "UserProperty:\n" + + " type: object\n" + + " description: Represents a user-specific property\n" + + " example: User-specific example value\n" + + "UserSchema:\n" + + " type: object\n" + + " properties:\n" + + " propertyOne:\n" + + " allOf:\n" + + " - type: object\n" + + " description: First user schema property\n" + + " nullable: true\n" + + " - $ref: '#/components/schemas/UserProperty'\n" + + " propertyTwo:\n" + + " allOf:\n" + + " - type: object\n" + + " description: Second user schema property\n" + + " example: example value for propertyTwo\n" + + " - $ref: '#/components/schemas/UserProperty'\n" + + " propertyThree:\n" + + " allOf:\n" + + " - type: object\n" + + " description: \"Third user schema property, with example for testing\"\n" + + " example: example value for propertyThree\n" + + " - $ref: '#/components/schemas/UserProperty'\n"; + + SerializationMatchers.assertEqualsToYaml(c.getDefinedModels(), expectedYaml); + // stringSchemaMap = c.readAll(InlineSchemaSecond.class); + c.resolve(new AnnotatedType(OrderSchema.class)); + expectedYaml = "BasicProperty:\n" + + " type: object\n" + + " description: Represents a basic schema property\n" + + "OrderProperty:\n" + + " type: object\n" + + " properties:\n" + + " basicProperty:\n" + + " $ref: '#/components/schemas/BasicProperty'\n" + + " description: Represents an order-specific property\n" + + " example: Order-specific example value\n" + + "OrderSchema:\n" + + " type: object\n" + + " properties:\n" + + " propertyOne:\n" + + " allOf:\n" + + " - type: object\n" + + " description: First order schema property\n" + + " nullable: true\n" + + " - $ref: '#/components/schemas/OrderProperty'\n" + + " userProperty:\n" + + " allOf:\n" + + " - type: object\n" + + " description: \"Order schema property, references UserProperty\"\n" + + " example: example value for userProperty\n" + + " - $ref: '#/components/schemas/UserProperty'\n" + + "UserProperty:\n" + + " type: object\n" + + " description: Represents a user-specific property\n" + + " example: User-specific example value\n" + + "UserSchema:\n" + + " type: object\n" + + " properties:\n" + + " propertyOne:\n" + + " allOf:\n" + + " - type: object\n" + + " description: First user schema property\n" + + " nullable: true\n" + + " - $ref: '#/components/schemas/UserProperty'\n" + + " propertyTwo:\n" + + " allOf:\n" + + " - type: object\n" + + " description: Second user schema property\n" + + " example: example value for propertyTwo\n" + + " - $ref: '#/components/schemas/UserProperty'\n" + + " propertyThree:\n" + + " allOf:\n" + + " - type: object\n" + + " description: \"Third user schema property, with example for testing\"\n" + + " example: example value for propertyThree\n" + + " - $ref: '#/components/schemas/UserProperty'\n"; + SerializationMatchers.assertEqualsToYaml(c.getDefinedModels(), expectedYaml); + } + + // Renamed class to better describe what it represents + static class UserSchema { + + @Schema(description = "First user schema property", nullable = true) + public UserProperty propertyOne; + + private UserProperty propertyTwo; + + @Schema(description = "Second user schema property", example = "example value for propertyTwo") + public UserProperty getPropertyTwo() { + return propertyTwo; + } + + // Third property with no specific annotation. It's good to add some description or example for clarity + @Schema(description = "Third user schema property, with example for testing", example = "example value for propertyThree") + public UserProperty getPropertyThree() { + return null; // returning null as per the test scenario + } + } + + // Renamed class to represent a different entity for the schema test + static class OrderSchema { + + @Schema(description = "First order schema property", nullable = true) + public OrderProperty propertyOne; + + private UserProperty userProperty; + + @Schema(description = "Order schema property, references UserProperty", example = "example value for userProperty") + public UserProperty getUserProperty() { + return userProperty; + } + } + + // Renamed properties to make them clearer about their role in the schema + @Schema(description = "Represents a user-specific property", example = "User-specific example value") + static class UserProperty { + // public String value; + } + + @Schema(description = "Represents an order-specific property", example = "Order-specific example value") + static class OrderProperty { + public BasicProperty basicProperty; + } + + static class BasicSchema { + + @Schema(description = "First basic schema property") + public BasicProperty propertyOne; + + private BasicProperty propertyTwo; + + @Schema(description = "Second basic schema property", example = "example value for propertyTwo") + public BasicProperty getPropertyTwo() { + return propertyTwo; + } + } + + // Renamed to represent a basic property common in various schemas + @Schema(description = "Represents a basic schema property") + static class BasicProperty { + // public String value; + } +} diff --git a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/GenericOpenApiContext.java b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/GenericOpenApiContext.java index 76099eb352..27a4216144 100644 --- a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/GenericOpenApiContext.java +++ b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/GenericOpenApiContext.java @@ -552,7 +552,7 @@ public T init() throws OpenApiConfigurationException { if (objectMapperProcessor != null) { ObjectMapper mapper = IntegrationObjectMapperFactory.createJson(); objectMapperProcessor.processJsonObjectMapper(mapper); - ModelConverters.getInstance(Boolean.TRUE.equals(openApiConfiguration.isOpenAPI31())).addConverter(new ModelResolver(mapper)); + ModelConverters.getInstance(Boolean.TRUE.equals(openApiConfiguration.isOpenAPI31()), openApiConfiguration.getSchemaResolution()).addConverter(new ModelResolver(mapper)); objectMapperProcessor.processOutputJsonObjectMapper(outputJsonMapper); objectMapperProcessor.processOutputYamlObjectMapper(outputYamlMapper); diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/DefaultParameterExtension.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/DefaultParameterExtension.java index ed958c0a33..541dda6b52 100644 --- a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/DefaultParameterExtension.java +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/DefaultParameterExtension.java @@ -120,7 +120,7 @@ public ResolvedParameter extractParameters(List annotations, annotations, components, classConsumes == null ? new String[0] : classConsumes.value(), - methodConsumes == null ? new String[0] : methodConsumes.value(), jsonViewAnnotation, openapi31); + methodConsumes == null ? new String[0] : methodConsumes.value(), jsonViewAnnotation, openapi31, this.schemaResolution); if (unknownParameter != null) { if (StringUtils.isNotBlank(unknownParameter.getIn()) && !"form".equals(unknownParameter.getIn())) { extractParametersResult.parameters.add(unknownParameter); @@ -141,7 +141,8 @@ public ResolvedParameter extractParameters(List annotations, classConsumes == null ? new String[0] : classConsumes.value(), methodConsumes == null ? new String[0] : methodConsumes.value(), jsonViewAnnotation, - openapi31); + openapi31, + this.schemaResolution); if (processedParameter != null) { extractParametersResult.parameters.add(processedParameter); } @@ -264,7 +265,9 @@ private boolean handleAdditionalAnnotation(List parameters, List parameters, List cls, final List globalParameters = new ArrayList<>(); // look for constructor-level annotated properties - globalParameters.addAll(ReaderUtils.collectConstructorParameters(cls, components, classConsumes, null)); + globalParameters.addAll(ReaderUtils.collectConstructorParameters(cls, components, classConsumes, null, config.getSchemaResolution())); // look for field-level annotated properties globalParameters.addAll(ReaderUtils.collectFieldParameters(cls, components, classConsumes, null)); @@ -1525,8 +1525,11 @@ protected ResolvedParameter getParameters(Type type, List annotation LOGGER.debug("trying extension {}", extension); extension.setOpenAPI31(Boolean.TRUE.equals(config.isOpenAPI31())); - - return extension.extractParameters(annotations, type, typesToSkip, components, classConsumes, methodConsumes, true, jsonViewAnnotation, chain); + Schema.SchemaResolution curSchemaResolution = config.getSchemaResolution(); + extension.setSchemaResolution(config.getSchemaResolution()); + ResolvedParameter resolvedParameter = extension.extractParameters(annotations, type, typesToSkip, components, classConsumes, methodConsumes, true, jsonViewAnnotation, chain); + ((SwaggerConfiguration)config).setSchemaResolution(curSchemaResolution); + return resolvedParameter; } private Set extractOperationIdFromPathItem(PathItem path) { diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/ext/AbstractOpenAPIExtension.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/ext/AbstractOpenAPIExtension.java index a8792d4e2a..197e248b24 100644 --- a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/ext/AbstractOpenAPIExtension.java +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/ext/AbstractOpenAPIExtension.java @@ -6,6 +6,7 @@ import io.swagger.v3.jaxrs2.ResolvedParameter; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.media.Schema; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -17,6 +18,7 @@ public abstract class AbstractOpenAPIExtension implements OpenAPIExtension { protected boolean openapi31; + protected Schema.SchemaResolution schemaResolution; @Override public String extractOperationMethod(Method method, Iterator chain) { @@ -68,4 +70,9 @@ protected JavaType constructType(Type type) { public void setOpenAPI31(boolean openapi31) { this.openapi31 = openapi31; } + + @Override + public void setSchemaResolution(Schema.SchemaResolution schemaResolution) { + this.schemaResolution = schemaResolution; + } } diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/ext/OpenAPIExtension.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/ext/OpenAPIExtension.java index d966120a0d..b82e8b581d 100644 --- a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/ext/OpenAPIExtension.java +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/ext/OpenAPIExtension.java @@ -4,6 +4,7 @@ import io.swagger.v3.jaxrs2.ResolvedParameter; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.media.Schema; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -31,4 +32,8 @@ ResolvedParameter extractParameters(List annotations, Type type, Set default void setOpenAPI31(boolean openapi31) { //todo: override me! } + + default void setSchemaResolution(Schema.SchemaResolution schemaResolution) { + //todo: override me! + } } diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/util/ReaderUtils.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/util/ReaderUtils.java index 8933a0497e..581bc0bbb4 100644 --- a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/util/ReaderUtils.java +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/util/ReaderUtils.java @@ -7,6 +7,7 @@ import io.swagger.v3.jaxrs2.ext.OpenAPIExtensions; import io.swagger.v3.oas.integration.api.OpenAPIConfiguration; import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import org.apache.commons.lang3.StringUtils; @@ -35,6 +36,9 @@ public class ReaderUtils { private static final String OPTIONS_METHOD = "options"; private static final String PATH_DELIMITER = "/"; + public static List collectConstructorParameters(Class cls, Components components, javax.ws.rs.Consumes classConsumes, JsonView jsonViewAnnotation) { + return collectConstructorParameters(cls, components, classConsumes, jsonViewAnnotation, null); + } /** * Collects constructor-level parameters from class. * @@ -42,7 +46,7 @@ public class ReaderUtils { * @param components * @return the collection of supported parameters */ - public static List collectConstructorParameters(Class cls, Components components, javax.ws.rs.Consumes classConsumes, JsonView jsonViewAnnotation) { + public static List collectConstructorParameters(Class cls, Components components, javax.ws.rs.Consumes classConsumes, JsonView jsonViewAnnotation, Schema.SchemaResolution schemaResolution) { if (cls.isLocalClass() || (cls.isMemberClass() && !Modifier.isStatic(cls.getModifiers()))) { return Collections.emptyList(); } @@ -77,7 +81,7 @@ public static List collectConstructorParameters(Class cls, Compone components, classConsumes == null ? new String[0] : classConsumes.value(), null, - jsonViewAnnotation); + jsonViewAnnotation, false, schemaResolution); if (processedParameter != null) { parameters.add(processedParameter); } diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java index 62575c2f0e..e3ae222c2c 100644 --- a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java @@ -98,8 +98,6 @@ import io.swagger.v3.jaxrs2.resources.generics.ticket3694.Ticket3694ResourceSimpleSameReturn; import io.swagger.v3.jaxrs2.resources.rs.ProcessTokenRestService; import io.swagger.v3.jaxrs2.resources.ticket3624.Service; -import io.swagger.v3.jaxrs2.schemaResolution.SchemaResolutionResource; -import io.swagger.v3.jaxrs2.schemaResolution.SchemaResolutionResourceSimple; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.integration.SwaggerConfiguration; import io.swagger.v3.oas.models.Components; @@ -145,9 +143,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.stream.Collectors; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; @@ -4169,189 +4165,4 @@ public void test4483Response() { " type: string\n"; SerializationMatchers.assertEqualsToYaml(openAPI, yaml); } - - @Test - public void testSchemaResolution() { - ModelConverters.reset(); - Reader reader = new Reader(new SwaggerConfiguration().openAPI(new OpenAPI()).schemaResolution(Schema.SchemaResolution.INLINE)); - 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" + - " type: object\n" + - " properties:\n" + - " property1:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: InlineSchemaFirst property 1\n" + - " nullable: true\n" + - " example: example\n" + - " property2:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: ' InlineSchemaFirst property 2'\n" + - " example: example 2\n" + - " /test/inlineSchemaSecond:\n" + - " get:\n" + - " operationId: inlineSchemaSecond\n" + - " responses:\n" + - " default:\n" + - " description: default response\n" + - " content:\n" + - " '*/*':\n" + - " schema:\n" + - " type: object\n" + - " properties:\n" + - " foo:\n" + - " type: string\n" + - " propertySecond1:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: object\n" + - " properties:\n" + - " property1:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: property 1\n" + - " property2:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: property 2\n" + - " example: example\n" + - " description: InlineSchemaSecond property 1\n" + - " nullable: true\n" + - " example: examplesecond\n" + - " property2:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: InlineSchemaSecond property 2\n" + - " example: InlineSchemaSecond example 2\n" + - "components:\n" + - " schemas:\n" + - " InlineSchemaFirst:\n" + - " type: object\n" + - " properties:\n" + - " property1:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: InlineSchemaFirst property 1\n" + - " nullable: true\n" + - " example: example\n" + - " property2:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: ' InlineSchemaFirst property 2'\n" + - " example: example 2\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" + - " type: object\n" + - " properties:\n" + - " property1:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: property 1\n" + - " property2:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: property 2\n" + - " example: example\n" + - " description: propertysecond\n" + - " nullable: true\n" + - " example: examplesecond\n" + - " InlineSchemaPropertySimple:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: property\n" + - " example: example\n" + - " InlineSchemaSecond:\n" + - " type: object\n" + - " properties:\n" + - " foo:\n" + - " type: string\n" + - " propertySecond1:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: object\n" + - " properties:\n" + - " property1:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: property 1\n" + - " property2:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: property 2\n" + - " example: example\n" + - " description: InlineSchemaSecond property 1\n" + - " nullable: true\n" + - " example: examplesecond\n" + - " property2:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: InlineSchemaSecond property 2\n" + - " example: InlineSchemaSecond example 2\n" + - " InlineSchemaSimple:\n" + - " type: object\n" + - " properties:\n" + - " property1:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: property 1\n" + - " property2:\n" + - " type: object\n" + - " properties:\n" + - " bar:\n" + - " type: string\n" + - " description: property 2\n" + - " example: example\n\n"; - SerializationMatchers.assertEqualsToYaml(openAPI, yaml); - ModelConverters.reset(); - } } diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/SchemaResolutionAllOfTest.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/SchemaResolutionAllOfTest.java new file mode 100644 index 0000000000..4235b7aecc --- /dev/null +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/SchemaResolutionAllOfTest.java @@ -0,0 +1,60 @@ +package io.swagger.v3.jaxrs2; + +import io.swagger.v3.jaxrs2.matchers.SerializationMatchers; +import io.swagger.v3.jaxrs2.schemaResolution.SchemaResolutionResourceSimple; +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 SchemaResolutionAllOfTest { + + @Test + public void testSchemaResolutionAllOf() { + Reader reader = new Reader(new SwaggerConfiguration().openAPI(new OpenAPI()).schemaResolution(Schema.SchemaResolution.ALL_OF)); + OpenAPI openAPI = reader.read(SchemaResolutionResourceSimple.class); + String yaml = "openapi: 3.0.1\n" + + "paths:\n" + + " /test/inlineSchemaFirst:\n" + + " get:\n" + + " operationId: inlineSchemaFirst\n" + + " responses:\n" + + " default:\n" + + " description: InlineSchemaFirst Response API\n" + + " content:\n" + + " '*/*':\n" + + " schema:\n" + + " $ref: '#/components/schemas/InlineSchemaFirst'\n" + + " /test/inlineSchemaSecond:\n" + + " get:\n" + + " operationId: inlineSchemaFirst_1\n" + + " requestBody:\n" + + " content:\n" + + " '*/*':\n" + + " schema:\n" + + " allOf:\n" + + " - description: InlineSchemaSecond API\n" + + " - $ref: '#/components/schemas/InlineSchemaFirst'\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " '*/*': {}\n" + + "components:\n" + + " schemas:\n" + + " InlineSchemaFirst:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " $ref: '#/components/schemas/InlineSchemaPropertyFirst'\n" + + " InlineSchemaPropertyFirst:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property\n" + + " nullable: true\n" + + " example: example\n"; + SerializationMatchers.assertEqualsToYaml(openAPI, yaml); + } +} diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/SchemaResolutionInlineTest.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/SchemaResolutionInlineTest.java new file mode 100644 index 0000000000..320e99b751 --- /dev/null +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/SchemaResolutionInlineTest.java @@ -0,0 +1,235 @@ +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 SchemaResolutionInlineTest { + @Test + public void testSchemaResolutionInline() { + ModelConverters.reset(); + Reader reader = new Reader(new SwaggerConfiguration().openAPI(new OpenAPI()).schemaResolution(Schema.SchemaResolution.INLINE)); + 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" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: InlineSchemaFirst property 1\n" + + " nullable: true\n" + + " example: example\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: ' InlineSchemaFirst property 2'\n" + + " example: example 2\n" + + " /test/inlineSchemaSecond:\n" + + " get:\n" + + " operationId: inlineSchemaSecond\n" + + " requestBody:\n" + + " content:\n" + + " '*/*':\n" + + " schema:\n" + + " type: object\n" + + " properties:\n" + + " foo:\n" + + " type: string\n" + + " propertySecond1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 1\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 2\n" + + " example: example\n" + + " description: InlineSchemaSecond property 1\n" + + " nullable: true\n" + + " example: examplesecond\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: InlineSchemaSecond property 2\n" + + " example: InlineSchemaSecond example 2\n" + + " description: InlineSchemaSecond API\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " '*/*':\n" + + " schema:\n" + + " type: object\n" + + " properties:\n" + + " foo:\n" + + " type: string\n" + + " propertySecond1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 1\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 2\n" + + " example: example\n" + + " description: InlineSchemaSecond property 1\n" + + " nullable: true\n" + + " example: examplesecond\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: InlineSchemaSecond property 2\n" + + " example: InlineSchemaSecond example 2\n" + + "components:\n" + + " schemas:\n" + + " InlineSchemaFirst:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: InlineSchemaFirst property 1\n" + + " nullable: true\n" + + " example: example\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: ' InlineSchemaFirst property 2'\n" + + " example: example 2\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" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 1\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 2\n" + + " example: example\n" + + " description: propertysecond\n" + + " nullable: true\n" + + " example: examplesecond\n" + + " InlineSchemaPropertySimple:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property\n" + + " example: example\n" + + " InlineSchemaSecond:\n" + + " type: object\n" + + " properties:\n" + + " foo:\n" + + " type: string\n" + + " propertySecond1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 1\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 2\n" + + " example: example\n" + + " description: InlineSchemaSecond property 1\n" + + " nullable: true\n" + + " example: examplesecond\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: InlineSchemaSecond property 2\n" + + " example: InlineSchemaSecond example 2\n" + + " description: InlineSchemaSecond API\n" + + " InlineSchemaSimple:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 1\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property 2\n" + + " example: example\n\n"; + SerializationMatchers.assertEqualsToYaml(openAPI, yaml); + ModelConverters.reset(); + } +} diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResource.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResource.java index 01e691ba1e..6f909e1d44 100644 --- a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResource.java +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResource.java @@ -9,7 +9,7 @@ public class SchemaResolutionResource { @GET @Path("/inlineSchemaSecond") - public InlineSchemaSecond inlineSchemaSecond() { + public InlineSchemaSecond inlineSchemaSecond(@Schema(description = "InlineSchemaSecond API") InlineSchemaSecond inlineSchemaSecond) { return null; } @GET diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResourceSimple.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResourceSimple.java index 61ac55e6b6..fc043bc168 100644 --- a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResourceSimple.java +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/schemaResolution/SchemaResolutionResourceSimple.java @@ -1,20 +1,32 @@ package io.swagger.v3.jaxrs2.schemaResolution; +import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import javax.ws.rs.GET; import javax.ws.rs.Path; +import javax.ws.rs.core.Response; @Path("test") public class SchemaResolutionResourceSimple { + @GET @Path("/inlineSchemaFirst") - public InlineSchemaFirst inlineSchemaFirst() { + @ApiResponse(description = "InlineSchemaFirst Response API", content = @Content(schema = @Schema(implementation = InlineSchemaFirst.class))) + public Response inlineSchemaFirst() { return null; } + @GET + @Path("/inlineSchemaSecond") + public void inlineSchemaFirst(@Schema(description = "InlineSchemaSecond API") InlineSchemaFirst inlineSchemaFirst) { + } + + + static class InlineSchemaFirst { // public String foo; diff --git a/pom.xml b/pom.xml index 355de17851..7a1473c3f8 100644 --- a/pom.xml +++ b/pom.xml @@ -654,7 +654,7 @@ 4.5.14 1.16.0 - 2.22.2 + 3.5.0 3.2.1 2.22.2