From 769941a19763c329db074dded43fcc305074ccdb Mon Sep 17 00:00:00 2001 From: MateuszPol Date: Fri, 28 Apr 2023 09:49:58 +0200 Subject: [PATCH] Added possibility to provide JSON/YAML Factory to initialise ObjectMapper (#4396) * Added possibility to provide custom JsonFactory/YamlFactory to yaml mapper initializer. * Cleared unused import in ObjectMapperFactory * - Removed mapper methods from Json/Json31/Yaml/Yaml31 classes - Added public methods to ObjectMapperFactory to give possibility to create mapper with custom factory * Redundant space remove in ObjectMapperFactory * Changed access modifiers to public in all methods of ObjectMapperFactory --- .../v3/core/util/ObjectMapperFactory.java | 31 ++- .../serialization/JsonSerializationTest.java | 52 +++++ .../OpenAPI3_1SerializationTest.java | 42 +++- .../specFiles/3.1.0/petstore-3.1.json | 200 ++++++++++++++++++ ...onSerialization-expected-petstore-3.0.json | 177 ++++++++++++++++ 5 files changed, 489 insertions(+), 13 deletions(-) create mode 100644 modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1.json create mode 100644 modules/swagger-core/src/test/resources/specFiles/jsonSerialization-expected-petstore-3.0.json diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ObjectMapperFactory.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ObjectMapperFactory.java index 1376616e80..a02539f54c 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ObjectMapperFactory.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ObjectMapperFactory.java @@ -73,11 +73,23 @@ public class ObjectMapperFactory { - protected static ObjectMapper createJson() { + public static ObjectMapper createJson(JsonFactory jsonFactory) { + return create(jsonFactory, false); + } + + public static ObjectMapper createJson() { return create(null, false); } - protected static ObjectMapper createYaml(boolean openapi31) { + public static ObjectMapper createYaml(YAMLFactory yamlFactory) { + return create(yamlFactory, false); + } + + public static ObjectMapper createYaml() { + return createYaml(false); + } + + public static ObjectMapper createYaml(boolean openapi31) { YAMLFactory factory = new YAMLFactory(); factory.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER); factory.enable(YAMLGenerator.Feature.MINIMIZE_QUOTES); @@ -87,20 +99,23 @@ protected static ObjectMapper createYaml(boolean openapi31) { return create(factory, openapi31); } - protected static ObjectMapper createYaml() { - return createYaml(false); + public static ObjectMapper createJson31(JsonFactory jsonFactory) { + return create(jsonFactory, true); } - protected static ObjectMapper createJson31() { + public static ObjectMapper createJson31() { return create(null, true); } + public static ObjectMapper createYaml31(YAMLFactory yamlFactory) { + return create(yamlFactory, true); + } - protected static ObjectMapper createYaml31() { + public static ObjectMapper createYaml31() { return createYaml(true); } - private static ObjectMapper create(JsonFactory jsonFactory, boolean openapi31) { + public static ObjectMapper create(JsonFactory jsonFactory, boolean openapi31) { ObjectMapper mapper = jsonFactory == null ? new ObjectMapper() : new ObjectMapper(jsonFactory); if (!openapi31) { @@ -215,7 +230,7 @@ public JsonSerializer modifySerializer( return mapper; } - protected static ObjectMapper createJsonConverter() { + public static ObjectMapper createJsonConverter() { ObjectMapper mapper = new ObjectMapper(); diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/JsonSerializationTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/JsonSerializationTest.java index 00d2ea0b4f..2cc3c5deab 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/JsonSerializationTest.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/JsonSerializationTest.java @@ -1,7 +1,11 @@ package io.swagger.v3.core.serialization; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.dataformat.yaml.JacksonYAMLParseException; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import io.swagger.v3.core.matchers.SerializationMatchers; import io.swagger.v3.core.util.Json; +import io.swagger.v3.core.util.ObjectMapperFactory; import io.swagger.v3.core.util.ResourceUtils; import io.swagger.v3.core.util.Yaml; import io.swagger.v3.oas.models.OpenAPI; @@ -12,6 +16,7 @@ import io.swagger.v3.oas.models.responses.ApiResponses; import io.swagger.v3.oas.models.servers.Server; import org.testng.annotations.Test; +import org.yaml.snakeyaml.LoaderOptions; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; @@ -86,4 +91,51 @@ public void testSerializeNullInSchemaExample() throws Exception { SerializationMatchers.assertEqualsToYaml(deser, yaml); } + + @Test + public void testSerializeJSONWithCustomFactory() throws Exception { + // given + JsonFactory jsonFactory = new JsonFactory(); + final String json = ResourceUtils.loadClassResource(getClass(), "specFiles/petstore-3.0.json"); + final String expectedJson = ResourceUtils.loadClassResource(getClass(), "specFiles/jsonSerialization-expected-petstore-3.0.json"); + + // when + OpenAPI deser = ObjectMapperFactory.createJson(jsonFactory).readValue(json, OpenAPI.class); + + // then + SerializationMatchers.assertEqualsToJson(deser, expectedJson); + } + + @Test + public void testSerializeYAMLWithCustomFactory() throws Exception { + // given + LoaderOptions loaderOptions = new LoaderOptions(); + loaderOptions.setCodePointLimit(5 * 1024 * 1024); + YAMLFactory yamlFactory = YAMLFactory.builder() + .loaderOptions(loaderOptions) + .build(); + final String yaml = ResourceUtils.loadClassResource(getClass(), "specFiles/null-example.yaml"); + + // when + OpenAPI deser = ObjectMapperFactory.createYaml(yamlFactory).readValue(yaml, OpenAPI.class); + + // then + SerializationMatchers.assertEqualsToYaml(deser, yaml); + } + + @Test(expectedExceptions = JacksonYAMLParseException.class) + public void testSerializeYAMLWithCustomFactoryAndCodePointLimitReached() throws Exception { + // given + LoaderOptions loaderOptions = new LoaderOptions(); + loaderOptions.setCodePointLimit(1); + YAMLFactory yamlFactory = YAMLFactory.builder() + .loaderOptions(loaderOptions) + .build(); + final String yaml = ResourceUtils.loadClassResource(getClass(), "specFiles/null-example.yaml"); + + // when + OpenAPI deser = ObjectMapperFactory.createYaml(yamlFactory).readValue(yaml, OpenAPI.class); + + // then - Throw JacksonYAMLParseException + } } diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/OpenAPI3_1SerializationTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/OpenAPI3_1SerializationTest.java index 234c5f5a62..97c5d02d98 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/OpenAPI3_1SerializationTest.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/OpenAPI3_1SerializationTest.java @@ -1,12 +1,12 @@ package io.swagger.v3.core.serialization; +import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.util.DefaultIndenter; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.dataformat.yaml.JacksonYAMLParseException; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import io.swagger.v3.core.matchers.SerializationMatchers; -import io.swagger.v3.core.util.Json; -import io.swagger.v3.core.util.Json31; -import io.swagger.v3.core.util.ResourceUtils; -import io.swagger.v3.core.util.Yaml; -import io.swagger.v3.core.util.Yaml31; +import io.swagger.v3.core.util.*; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; @@ -30,6 +30,7 @@ import io.swagger.v3.oas.models.responses.ApiResponses; import io.swagger.v3.oas.models.security.SecurityScheme; import org.testng.annotations.Test; +import org.yaml.snakeyaml.LoaderOptions; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; @@ -351,6 +352,21 @@ public void testSerializePetstore() throws Exception { } + @Test + public void testJSONSerializePetstoreWithCustomFactory() throws Exception { + + //given + final String jsonString = ResourceUtils.loadClassResource(getClass(), "specFiles/3.1.0/petstore-3.1.json"); + JsonFactory jsonFactory = new JsonFactory(); + + //when + final OpenAPI swagger = ObjectMapperFactory.createJson31(jsonFactory).readValue(jsonString, OpenAPI.class); + + // then + assertNotNull(swagger); + SerializationMatchers.assertEqualsToJson31(swagger, jsonString); + } + @Test public void testInfoSerialization() { OpenAPI openAPI = new OpenAPI() @@ -1497,6 +1513,22 @@ public void testBooleanAdditionalPropertiesSerialization() throws Exception{ assertTrue(Boolean.TRUE.equals(openAPI.getComponents().getSchemas().get("test").getAdditionalProperties())); } + @Test(expectedExceptions = JacksonYAMLParseException.class) + public void testSerializeYAML31WithCustomFactoryAndCodePointLimitReached() throws Exception { + // given + LoaderOptions loaderOptions = new LoaderOptions(); + loaderOptions.setCodePointLimit(1); + YAMLFactory yamlFactory = YAMLFactory.builder() + .loaderOptions(loaderOptions) + .build(); + final String yaml = ResourceUtils.loadClassResource(getClass(), "specFiles/petstore-3.0.yaml"); + + // when + OpenAPI deser = ObjectMapperFactory.createYaml31(yamlFactory).readValue(yaml, OpenAPI.class); + + // then - Throw JacksonYAMLParseException + } + private static String withJacksonSystemLineSeparator(String s) { return s.replace("\n", DefaultIndenter.SYS_LF); } diff --git a/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1.json b/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1.json new file mode 100644 index 0000000000..7f09963f99 --- /dev/null +++ b/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1.json @@ -0,0 +1,200 @@ +{ + "openapi": "3.1.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "license": { + "name": "MIT", + "identifier": "test" + } + }, + "servers": [ + { + "url": "http://petstore.swagger.io/v1" + } + ], + "webhooks": { + "newPet": { + "post": { + "requestBody": { + "description": "Information about a new pet in the system", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "responses": { + "200": { + "description": "Return a 200 status to indicate that the data was received successfully" + } + } + } + } + }, + "paths": { + "/pets": { + "get": { + "summary": "List all pets", + "operationId": "listPets", + "tags": [ + "pets" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "How many items to return at one time (max 100)", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "An paged array of pets", + "headers": { + "x-next": { + "description": "A link to the next page of responses", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pets" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "post": { + "summary": "Create a pet", + "operationId": "createPets", + "tags": [ + "pets" + ], + "responses": { + "201": { + "description": "Null response" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/pets/{petId}": { + "get": { + "summary": "Info for a specific pet", + "operationId": "showPetById", + "tags": [ + "pets" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "required": true, + "description": "The id of the pet to retrieve", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Expected response to a valid request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pets" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": [ + "string", + "integer" + ] + }, + "tag": { + "type": "string" + } + } + }, + "Pets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/modules/swagger-core/src/test/resources/specFiles/jsonSerialization-expected-petstore-3.0.json b/modules/swagger-core/src/test/resources/specFiles/jsonSerialization-expected-petstore-3.0.json new file mode 100644 index 0000000000..3973f339af --- /dev/null +++ b/modules/swagger-core/src/test/resources/specFiles/jsonSerialization-expected-petstore-3.0.json @@ -0,0 +1,177 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Swagger Petstore", + "license": { + "name": "MIT" + }, + "version": "1.0.0" + }, + "servers": [ + { + "url": "http://petstore.swagger.io/v1" + } + ], + "paths": { + "/pets": { + "get": { + "tags": [ + "pets" + ], + "summary": "List all pets", + "operationId": "listPets", + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "How many items to return at one time (max 100)", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "An paged array of pets", + "headers": { + "x-next": { + "description": "A link to the next page of responses", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pets" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "post": { + "tags": [ + "pets" + ], + "summary": "Create a pet", + "operationId": "createPets", + "responses": { + "201": { + "description": "Null response" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/pets/{petId}": { + "get": { + "tags": [ + "pets" + ], + "summary": "Info for a specific pet", + "operationId": "showPetById", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "The id of the pet to retrieve", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Expected response to a valid request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pets" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Pet": { + "required": [ + "id", + "name" + ], + "type": "object", + "properties": { + "tag": { + "type": "string" + }, + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + } + }, + "Pets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "code": { + "type": "integer", + "format": "int32" + } + } + } + } + } +} \ No newline at end of file