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 f822095018..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 @@ -11,7 +11,6 @@ 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.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.models.media.Schema; import org.testng.annotations.Test; import javax.validation.constraints.DecimalMax; @@ -348,70 +347,4 @@ public void setMyField(Number myField) { this.myField = myField; } } - - @Test - public void testOAS31JavaRecord() { - String expectedYaml = "JavaRecordWithOAS31Fields:\n" + - " type: object\n" + - " $comment: Random comment at schema level\n" + - " $id: http://yourdomain.com/schemas/myschema.json\n" + - " description: this is model for testing OAS 3.1 Java Record resolving\n" + - " properties:\n" + - " test:\n" + - " type: string\n" + - " isLatest:\n" + - " type: boolean\n" + - " randomList:\n" + - " type: array\n" + - " contains:\n" + - " type: string\n" + - " items:\n" + - " type: string\n" + - " maxContains: 10\n" + - " minContains: 1\n" + - " prefixItems:\n" + - " - type: string\n" + - " unevaluatedItems:\n" + - " type: number\n" + - " Status:\n" + - " type:\n" + - " - string\n" + - " - number\n"; - - Map stringSchemaMap = ModelConverters.getInstance(true).readAll(JavaRecordWithOAS31Fields.class); - SerializationMatchers.assertEqualsToYaml31(stringSchemaMap, expectedYaml); - } - - @io.swagger.v3.oas.annotations.media.Schema( - $id = "http://yourdomain.com/schemas/myschema.json", - description = "this is model for testing OAS 3.1 Java Record resolving", - $comment = "Random comment at schema level", - types = "object" - ) - private record JavaRecordWithOAS31Fields( - String test, - boolean isLatest, - @ArraySchema( - maxContains = 10, - minContains = 1, - contains = @io.swagger.v3.oas.annotations.media.Schema( - types = "string" - ), - unevaluatedItems = @io.swagger.v3.oas.annotations.media.Schema( - types = "number" - ), - prefixItems = { - @io.swagger.v3.oas.annotations.media.Schema( - types = "string" - ) - } - ) - List randomList, - @io.swagger.v3.oas.annotations.media.Schema(types = { - "string", - "number" - }) - Object Status - ){ - } } diff --git a/modules/swagger-java17-support/pom.xml b/modules/swagger-java17-support/pom.xml new file mode 100644 index 0000000000..6bbd74ab9c --- /dev/null +++ b/modules/swagger-java17-support/pom.xml @@ -0,0 +1,91 @@ + + 4.0.0 + + io.swagger.core.v3 + swagger-project + 2.2.26-SNAPSHOT + ../../pom.xml + + swagger-java17-support + jar + swagger-java17-support + Module for Java 17 specific tests + + + org.testng + testng + test + + + io.swagger.core.v3 + swagger-jaxrs2 + ${project.version} + + + io.swagger.core.v3 + swagger-core + ${project.version} + + + io.swagger.core.v3 + swagger-annotations + ${project.version} + + + io.swagger.core.v3 + swagger-models + ${project.version} + + + org.glassfish.jersey.media + jersey-media-multipart + ${jersey2-version} + test + + + javax.annotation + javax.annotation-api + + + org.javassist + javassist + + + + + org.glassfish.jersey.inject + jersey-hk2 + ${jersey2-version} + test + + + javax.annotation + javax.annotation-api + + + org.javassist + javassist + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 17 + + + + + + 17 + + + diff --git a/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/Reader/ReaderTest.java b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/Reader/ReaderTest.java new file mode 100644 index 0000000000..bd4ff8d8c1 --- /dev/null +++ b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/Reader/ReaderTest.java @@ -0,0 +1,108 @@ +package io.swagger.v3.java17.Reader; + +import io.swagger.v3.java17.matchers.SerializationMatchers; +import io.swagger.v3.java17.resources.JavaRecordWithPathResource; +import io.swagger.v3.java17.resources.OtherJavaRecordWithPathsResource; +import io.swagger.v3.java17.resources.TestControllerWithRecordResource; +import io.swagger.v3.jaxrs2.Reader; +import io.swagger.v3.oas.integration.SwaggerConfiguration; +import io.swagger.v3.oas.models.OpenAPI; +import org.testng.annotations.Test; + +import java.util.HashSet; +import java.util.Set; + +public class ReaderTest { + + @Test + public void TestJavaRecordRef(){ + Reader reader = new Reader(new SwaggerConfiguration().openAPI(new OpenAPI()).openAPI31(true)); + + OpenAPI openAPI = reader.read(TestControllerWithRecordResource.class); + String yaml = "openapi: 3.1.0\n" + + "paths:\n" + + " /v17:\n" + + " post:\n" + + " operationId: opsRecordID\n" + + " responses:\n" + + " default:\n" + + " description: Successful operation\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " $ref: '#/components/schemas/JavaRecordResource'\n" + + "components:\n" + + " schemas:\n" + + " JavaRecordResource:\n" + + " type: object\n" + + " properties:\n" + + " test:\n" + + " type: string\n" + + " description: Testing of Java Record Processing\n" + + " isLatest:\n" + + " type: boolean\n" + + " id:\n" + + " type: string\n" + + " age:\n" + + " type: integer\n" + + " format: int32"; + SerializationMatchers.assertEqualsToYaml31(openAPI, yaml); + } + + @Test + public void TestSetOfRecords(){ + Set> classes = new HashSet<>(); + classes.add(JavaRecordWithPathResource.class); + classes.add(OtherJavaRecordWithPathsResource.class); + + Reader reader = new Reader(new OpenAPI()); + OpenAPI openAPI = reader.read(classes); + String yaml = "openapi: 3.0.1\n" + + "paths:\n" + + " /sample/1:\n" + + " post:\n" + + " description: description 1\n" + + " operationId: id 1\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " '*/*': {}\n" + + " /sample/2:\n" + + " post:\n" + + " description: description 2\n" + + " operationId: id 2\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " '*/*': {}\n" + + " /sample2:\n" + + " get:\n" + + " description: description\n" + + " operationId: Operation Id\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " '*/*': {}\n" + + " security:\n" + + " - security_key:\n" + + " - write:pets\n" + + " - read:pets\n" + + " /sample2/2:\n" + + " get:\n" + + " description: description 2\n" + + " operationId: Operation Id 2\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " '*/*': {}\n" + + " security:\n" + + " - security_key2:\n" + + " - write:pets\n" + + " - read:pets"; + SerializationMatchers.assertEqualsToYaml(openAPI, yaml); + } +} diff --git a/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/Reader/SchemaResolutionRecordsTest.java b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/Reader/SchemaResolutionRecordsTest.java new file mode 100644 index 0000000000..e7fc559933 --- /dev/null +++ b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/Reader/SchemaResolutionRecordsTest.java @@ -0,0 +1,383 @@ +package io.swagger.v3.java17.Reader; + +import io.swagger.v3.core.converter.ModelConverters; +import io.swagger.v3.java17.matchers.SerializationMatchers; +import io.swagger.v3.java17.resources.SchemaResolutionWithRecordSimpleResource; +import io.swagger.v3.java17.resources.SchemaResolutionWithRecordsResource; +import io.swagger.v3.jaxrs2.Reader; +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 SchemaResolutionRecordsTest { + + @Test + public void testSchemaResolutionInlineWithRecords(){ + ModelConverters.reset(); + Reader reader = new Reader(new SwaggerConfiguration().openAPI(new OpenAPI()).schemaResolution(Schema.SchemaResolution.INLINE)); + OpenAPI openAPI = reader.read(SchemaResolutionWithRecordsResource.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: property\n" + + " example: example\n" + + " property2:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property\n" + + " example: example\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" + + " nullable: true\n" + + " example: example\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" + + " nullable: true\n" + + " example: example\n" + + "components:\n" + + " schemas:\n" + + " InlineSchemaPropertyFirst:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property\n" + + " nullable: true\n" + + " example: example\n" + + " InlineSchemaRecordFirst:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property\n" + + " example: example\n" + + " property2:\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" + + " InlineSchemaRecordSecond:\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" + + " nullable: true\n" + + " example: example\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"; + SerializationMatchers.assertEqualsToYaml(openAPI, yaml); + ModelConverters.reset(); + } + + @Test + public void testSchemaResolutionAllOfWithRecordTest(){ + ModelConverters.reset(); + Reader reader = new Reader(new SwaggerConfiguration().openAPI(new OpenAPI()).schemaResolution(Schema.SchemaResolution.ALL_OF)); + OpenAPI openAPI = reader.read(SchemaResolutionWithRecordSimpleResource.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/SchemaRecordFirst'\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/SchemaRecordFirst'\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " '*/*': {}\n" + + "components:\n" + + " schemas:\n" + + " InlineSchemaPropertyFirst:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property\n" + + " nullable: true\n" + + " example: example\n" + + " SchemaRecordFirst:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " $ref: '#/components/schemas/InlineSchemaPropertyFirst'\n"; + SerializationMatchers.assertEqualsToYaml(openAPI, yaml); + ModelConverters.reset(); + } + + @Test + public void testSchemaResolutionAllOfRefWithRecordsTest(){ + Reader reader = new Reader(new SwaggerConfiguration().openAPI(new OpenAPI()).schemaResolution(Schema.SchemaResolution.ALL_OF_REF)); + OpenAPI openAPI = reader.read(SchemaResolutionWithRecordsResource.class); + String yaml = "openapi: 3.0.1\n" + + "paths:\n" + + " /test/inlineSchemaFirst:\n" + + " get:\n" + + " operationId: inlineSchemaFirst\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " '*/*':\n" + + " schema:\n" + + " $ref: '#/components/schemas/InlineSchemaRecordFirst'\n" + + " /test/inlineSchemaSecond:\n" + + " get:\n" + + " operationId: inlineSchemaSecond\n" + + " requestBody:\n" + + " content:\n" + + " '*/*':\n" + + " schema:\n" + + " description: InlineSchemaSecond API\n" + + " allOf:\n" + + " - $ref: '#/components/schemas/InlineSchemaRecordSecond'\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " '*/*':\n" + + " schema:\n" + + " $ref: '#/components/schemas/InlineSchemaRecordSecond'\n" + + "components:\n" + + " schemas:\n" + + " InlineSchemaPropertyFirst:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property\n" + + " example: example\n" + + " InlineSchemaRecordFirst:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " $ref: '#/components/schemas/InlineSchemaPropertyFirst'\n" + + " property2:\n" + + " $ref: '#/components/schemas/InlineSchemaPropertyFirst'\n" + + " InlineSchemaPropertySecond:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " $ref: '#/components/schemas/InlineSchemaSimple'\n" + + " description: propertySecond\n" + + " example: exampleSecond\n" + + " InlineSchemaPropertySimple:\n" + + " type: object\n" + + " properties:\n" + + " bar:\n" + + " type: string\n" + + " description: property\n" + + " InlineSchemaRecordSecond:\n" + + " type: object\n" + + " properties:\n" + + " foo:\n" + + " type: string\n" + + " propertySecond1:\n" + + " description: InlineSchemaSecond property 1\n" + + " nullable: true\n" + + " allOf:\n" + + " - $ref: '#/components/schemas/InlineSchemaPropertySecond'\n" + + " property2:\n" + + " description: InlineSchemaSecond property 2\n" + + " nullable: true\n" + + " allOf:\n" + + " - $ref: '#/components/schemas/InlineSchemaPropertyFirst'\n" + + " InlineSchemaSimple:\n" + + " type: object\n" + + " properties:\n" + + " property1:\n" + + " description: property 1\n" + + " allOf:\n" + + " - $ref: '#/components/schemas/InlineSchemaPropertySimple'\n" + + " property2:\n" + + " description: property 2\n" + + " example: example\n" + + " allOf:\n" + + " - $ref: '#/components/schemas/InlineSchemaPropertySimple'\n"; + SerializationMatchers.assertEqualsToYaml(openAPI, yaml); + } +} diff --git a/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/matchers/SerializationMatchers.java b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/matchers/SerializationMatchers.java new file mode 100644 index 0000000000..01e5c00de2 --- /dev/null +++ b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/matchers/SerializationMatchers.java @@ -0,0 +1,88 @@ +package io.swagger.v3.java17.matchers; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.NumericNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.swagger.v3.core.util.Json; +import io.swagger.v3.core.util.Json31; +import io.swagger.v3.core.util.Yaml; +import io.swagger.v3.core.util.Yaml31; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Comparator; + +import static org.testng.Assert.assertEquals; + +public class SerializationMatchers { + private static final Logger LOGGER = LoggerFactory.getLogger(SerializationMatchers.class); + + public static void assertEqualsToYaml(Object objectToSerialize, String yamlStr) { + apply(objectToSerialize, yamlStr, Yaml.mapper(), false); + } + + public static void assertEqualsToYamlExact(Object objectToSerialize, String yamlStr) { + apply(objectToSerialize, yamlStr, Yaml.mapper(), true); + } + + public static void assertEqualsToJson(Object objectToSerialize, String jsonStr) { + apply(objectToSerialize, jsonStr, Json.mapper(), false); + } + + public static void assertEqualsToYaml31(Object objectToSerialize, String yamlStr) { + apply31(objectToSerialize, yamlStr, Yaml31.mapper()); + } + + public static void assertEqualsToJson31(Object objectToSerialize, String jsonStr) { + apply31(objectToSerialize, jsonStr, Json31.mapper()); + } + + private static void apply(Object objectToSerialize, String str, ObjectMapper mapper, boolean exactMatch) { + final ObjectNode lhs = mapper.convertValue(objectToSerialize, ObjectNode.class); + ObjectNode rhs = null; + try { + rhs = mapper.readValue(str, ObjectNode.class); + } catch (IOException e) { + LOGGER.error("Failed to read value", e); + } + if (exactMatch || !lhs.equals(new ObjectNodeComparator(), rhs)) { + assertEquals(Yaml.pretty(lhs), Yaml.pretty(rhs)); + //fail(String.format("Serialized object:\n%s\ndoes not equal to expected serialized string:\n%s", lhs, rhs)); + } + } + + private static void apply31(Object objectToSerialize, String str, ObjectMapper mapper) { + final ObjectNode lhs = mapper.convertValue(objectToSerialize, ObjectNode.class); + ObjectNode rhs = null; + try { + rhs = mapper.readValue(str, ObjectNode.class); + } catch (IOException e) { + LOGGER.error("Failed to read value", e); + } + if (!lhs.equals(new ObjectNodeComparator(), rhs)) { + assertEquals(Yaml31.pretty(lhs), Yaml31.pretty(rhs)); + } + } + + static final class ObjectNodeComparator implements Comparator { + @Override + public int compare(JsonNode o1, JsonNode o2) { + if (o1.equals(o2)) { + return 0; + } + if ((o1 instanceof NumericNode) && (o2 instanceof NumericNode)) { + double d1 = ((NumericNode) o1).asDouble(); + double d2 = ((NumericNode) o2).asDouble(); + return Double.compare(d1, d2); + } + int comp = o1.asText().compareTo(o2.asText()); + if (comp == 0) { + return Integer.compare(o1.hashCode(), o2.hashCode()); + } + return comp; + } + } + +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/JavaRecordTest.java b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resolving/JavaRecordTest.java similarity index 96% rename from modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/JavaRecordTest.java rename to modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resolving/JavaRecordTest.java index 5c1c7f65cc..5e483a1f94 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/JavaRecordTest.java +++ b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resolving/JavaRecordTest.java @@ -1,8 +1,8 @@ -package io.swagger.v3.core.resolving; +package io.swagger.v3.java17.resolving; import io.swagger.v3.core.converter.ModelConverters; -import io.swagger.v3.core.matchers.SerializationMatchers; import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.java17.matchers.SerializationMatchers; import org.testng.annotations.Test; import javax.validation.constraints.*; diff --git a/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resolving/SwaggerTestBase.java b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resolving/SwaggerTestBase.java new file mode 100644 index 0000000000..2735cb788a --- /dev/null +++ b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resolving/SwaggerTestBase.java @@ -0,0 +1,34 @@ +package io.swagger.v3.java17.resolving; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import io.swagger.v3.core.jackson.ModelResolver; + +public abstract class SwaggerTestBase { + static ObjectMapper mapper; + + public static ObjectMapper mapper() { + if (mapper == null) { + mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + return mapper; + } + + protected ModelResolver modelResolver() { + return new ModelResolver(new ObjectMapper()); + } + + protected void prettyPrint(Object o) { + try { + System.out.println(mapper().writer(new DefaultPrettyPrinter()).writeValueAsString(o)); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resolving/v31/ModelResolverOAS31Test.java b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resolving/v31/ModelResolverOAS31Test.java new file mode 100644 index 0000000000..902f4336da --- /dev/null +++ b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resolving/v31/ModelResolverOAS31Test.java @@ -0,0 +1,79 @@ +package io.swagger.v3.java17.resolving.v31; + +import io.swagger.v3.core.converter.ModelConverters; +import io.swagger.v3.java17.resolving.SwaggerTestBase; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.models.media.Schema; +import org.testng.annotations.Test; +import io.swagger.v3.java17.matchers.SerializationMatchers; +import java.util.List; +import java.util.Map; + +public class ModelResolverOAS31Test extends SwaggerTestBase { + + @Test + public void testOAS31JavaRecord() { + String expectedYaml = "JavaRecordWithOAS31Fields:\n" + + " type: object\n" + + " $comment: Random comment at schema level\n" + + " $id: http://yourdomain.com/schemas/myschema.json\n" + + " description: this is model for testing OAS 3.1 Java Record resolving\n" + + " properties:\n" + + " test:\n" + + " type: string\n" + + " isLatest:\n" + + " type: boolean\n" + + " randomList:\n" + + " type: array\n" + + " contains:\n" + + " type: string\n" + + " items:\n" + + " type: string\n" + + " maxContains: 10\n" + + " minContains: 1\n" + + " prefixItems:\n" + + " - type: string\n" + + " unevaluatedItems:\n" + + " type: number\n" + + " Status:\n" + + " type:\n" + + " - string\n" + + " - number\n"; + + Map stringSchemaMap = ModelConverters.getInstance(true).readAll(JavaRecordWithOAS31Fields.class); + SerializationMatchers.assertEqualsToYaml31(stringSchemaMap, expectedYaml); + } + + @io.swagger.v3.oas.annotations.media.Schema( + $id = "http://yourdomain.com/schemas/myschema.json", + description = "this is model for testing OAS 3.1 Java Record resolving", + $comment = "Random comment at schema level", + types = "object" + ) + private record JavaRecordWithOAS31Fields( + String test, + boolean isLatest, + @ArraySchema( + maxContains = 10, + minContains = 1, + contains = @io.swagger.v3.oas.annotations.media.Schema( + types = "string" + ), + unevaluatedItems = @io.swagger.v3.oas.annotations.media.Schema( + types = "number" + ), + prefixItems = { + @io.swagger.v3.oas.annotations.media.Schema( + types = "string" + ) + } + ) + List randomList, + @io.swagger.v3.oas.annotations.media.Schema(types = { + "string", + "number" + }) + Object Status + ){ + } +} diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/resources/JavaRecordResource.java b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/JavaRecordResource.java similarity index 84% rename from modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/resources/JavaRecordResource.java rename to modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/JavaRecordResource.java index 44a5fdb4cb..eb1d392e40 100644 --- a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/resources/JavaRecordResource.java +++ b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/JavaRecordResource.java @@ -1,4 +1,4 @@ -package io.swagger.v3.jaxrs2.resources; +package io.swagger.v3.java17.resources; public record JavaRecordResource( @io.swagger.v3.oas.annotations.media.Schema(description = "Testing of Java Record Processing") String test, diff --git a/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/JavaRecordWithPathResource.java b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/JavaRecordWithPathResource.java new file mode 100644 index 0000000000..45b0af2967 --- /dev/null +++ b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/JavaRecordWithPathResource.java @@ -0,0 +1,24 @@ +package io.swagger.v3.java17.resources; + +import io.swagger.v3.oas.annotations.Operation; + +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +@Path("sample") +public record JavaRecordWithPathResource() { + + @POST + @Path("/1") + @Operation(description = "description 1", operationId = "id 1") + public void postExample(){ + + } + + @POST + @Path("/2") + @Operation(description = "description 2", operationId = "id 2") + public void postExample2(){ + + } +} diff --git a/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/OtherJavaRecordWithPathsResource.java b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/OtherJavaRecordWithPathsResource.java new file mode 100644 index 0000000000..e4a5437f54 --- /dev/null +++ b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/OtherJavaRecordWithPathsResource.java @@ -0,0 +1,30 @@ +package io.swagger.v3.java17.resources; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.*; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +@Path("sample2") +public record OtherJavaRecordWithPathsResource() { + @GET + @Path("/") + @Operation(operationId = "Operation Id", + description = "description") + @SecurityRequirement(name = "security_key", + scopes = {"write:pets", "read:pets"} + ) + public void getSecurity() { + } + + @GET + @Path("/2") + @Operation(operationId = "Operation Id 2", + description = "description 2") + @SecurityRequirement(name = "security_key2", + scopes = {"write:pets", "read:pets"} + ) + public void getSecurity2() { + } +} diff --git a/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/SchemaResolutionWithRecordSimpleResource.java b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/SchemaResolutionWithRecordSimpleResource.java new file mode 100644 index 0000000000..a2952bbe4d --- /dev/null +++ b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/SchemaResolutionWithRecordSimpleResource.java @@ -0,0 +1,39 @@ +package io.swagger.v3.java17.resources; + +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 SchemaResolutionWithRecordSimpleResource{ + + + @GET + @Path("/inlineSchemaFirst") + @ApiResponse(description = "InlineSchemaFirst Response API", content = @Content(schema = @Schema(implementation = SchemaRecordFirst.class))) + public Response inlineSchemaFirst() { + return null; + } + + + @GET + @Path("/inlineSchemaSecond") + public void inlineSchemaFirst(@Schema(description = "InlineSchemaSecond API") SchemaRecordFirst inlineSchemaFirst) { + } + + + + public record SchemaRecordFirst ( + @Schema(description = "InlineSchemaFirst property 1", nullable = true) InlineSchemaPropertyFirst property1 + ){ + } + + @Schema(description = "property", example = "example") + static class InlineSchemaPropertyFirst { + public String bar; + } +} diff --git a/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/SchemaResolutionWithRecordsResource.java b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/SchemaResolutionWithRecordsResource.java new file mode 100644 index 0000000000..0271762a58 --- /dev/null +++ b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/SchemaResolutionWithRecordsResource.java @@ -0,0 +1,68 @@ +package io.swagger.v3.java17.resources; + + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +@Path("test") +public class SchemaResolutionWithRecordsResource { + + @GET + @Path("/inlineSchemaSecond") + public InlineSchemaRecordSecond inlineSchemaSecond(@Schema(description = "InlineSchemaSecond API") InlineSchemaRecordSecond inlineSchemaSecond) { + return null; + } + + @GET + @Path("/inlineSchemaFirst") + public InlineSchemaRecordFirst inlineSchemaFirst() { + return null; + } + + + public record InlineSchemaRecordFirst( + InlineSchemaPropertyFirst property1, + InlineSchemaPropertyFirst property2 + ){ + } + + public record InlineSchemaRecordSecond ( + String foo, + @Schema(description = "InlineSchemaSecond property 1", nullable = true) + InlineSchemaPropertySecond propertySecond1, + @Schema(description = "InlineSchemaSecond property 2", nullable = true) + InlineSchemaPropertyFirst property2 + ){ + } + + @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/resources/TestControllerWithRecordResource.java b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/TestControllerWithRecordResource.java similarity index 94% rename from modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/resources/TestControllerWithRecordResource.java rename to modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/TestControllerWithRecordResource.java index 6dc79649a2..3ba8205b51 100644 --- a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/resources/TestControllerWithRecordResource.java +++ b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resources/TestControllerWithRecordResource.java @@ -1,4 +1,4 @@ -package io.swagger.v3.jaxrs2.resources; +package io.swagger.v3.java17.resources; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; 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 aeb048d1a8..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 @@ -16,7 +16,6 @@ import io.swagger.v3.jaxrs2.petstore31.PetResource; import io.swagger.v3.jaxrs2.petstore31.TagResource; import io.swagger.v3.jaxrs2.resources.DefaultResponseResource; -import io.swagger.v3.jaxrs2.resources.TestControllerWithRecordResource; import io.swagger.v3.jaxrs2.resources.Misc31Resource; import io.swagger.v3.jaxrs2.resources.ParameterMaximumValueResource; import io.swagger.v3.jaxrs2.resources.ResponseReturnTypeResource; @@ -4166,40 +4165,4 @@ public void test4483Response() { " type: string\n"; SerializationMatchers.assertEqualsToYaml(openAPI, yaml); } - - @Test - public void TestJavaRecordRef(){ - Reader reader = new Reader(new SwaggerConfiguration().openAPI(new OpenAPI()).openAPI31(true)); - - OpenAPI openAPI = reader.read(TestControllerWithRecordResource.class); - String yaml = "openapi: 3.1.0\n" + - "paths:\n" + - " /v17:\n" + - " post:\n" + - " operationId: opsRecordID\n" + - " responses:\n" + - " default:\n" + - " description: Successful operation\n" + - " content:\n" + - " application/json:\n" + - " schema:\n" + - " $ref: '#/components/schemas/JavaRecordResource'\n" + - "components:\n" + - " schemas:\n" + - " JavaRecordResource:\n" + - " type: object\n" + - " properties:\n" + - " test:\n" + - " type: string\n" + - " description: Testing of Java Record Processing\n" + - " isLatest:\n" + - " type: boolean\n" + - " id:\n" + - " type: string\n" + - " age:\n" + - " type: integer\n" + - " format: int32"; - SerializationMatchers.assertEqualsToYaml31(openAPI, yaml); - - } } diff --git a/pom.xml b/pom.xml index 4b3f3c70c6..6cc5ff775a 100644 --- a/pom.xml +++ b/pom.xml @@ -395,6 +395,16 @@ + + java-17-modules + + [17,) + + + modules/swagger-java17-support + + + modules/swagger-annotations @@ -627,7 +637,7 @@ - 17 + 8 2.2.3 2.13.0 2.3