diff --git a/modules/swagger-core/src/main/java/io/swagger/util/ObjectMapperFactory.java b/modules/swagger-core/src/main/java/io/swagger/util/ObjectMapperFactory.java index 6e7016a999..45efb8b480 100644 --- a/modules/swagger-core/src/main/java/io/swagger/util/ObjectMapperFactory.java +++ b/modules/swagger-core/src/main/java/io/swagger/util/ObjectMapperFactory.java @@ -23,23 +23,30 @@ public class ObjectMapperFactory { - protected static ObjectMapper createJson() { + public static ObjectMapper createJson(JsonFactory jsonFactory) { + return create(jsonFactory, true, true); + } + public static ObjectMapper createJson() { return createJson(true, true); } - protected static ObjectMapper createJson(boolean includePathDeserializer, boolean includeResponseDeserializer) { + public static ObjectMapper createJson(boolean includePathDeserializer, boolean includeResponseDeserializer) { return create(null, includePathDeserializer, includeResponseDeserializer); } - protected static ObjectMapper createYaml() { + public static ObjectMapper createYaml(YAMLFactory yamlFactory) { + return create(yamlFactory, true, true); + } + + public static ObjectMapper createYaml() { return createYaml(true, true); } - protected static ObjectMapper createYaml(boolean includePathDeserializer, boolean includeResponseDeserializer) { + public static ObjectMapper createYaml(boolean includePathDeserializer, boolean includeResponseDeserializer) { return create(new YAMLFactory(), includePathDeserializer, includeResponseDeserializer); } - private static ObjectMapper create(JsonFactory jsonFactory, boolean includePathDeserializer, boolean includeResponseDeserializer) { + public static ObjectMapper create(JsonFactory jsonFactory, boolean includePathDeserializer, boolean includeResponseDeserializer) { ObjectMapper mapper = jsonFactory == null ? new ObjectMapper() : new ObjectMapper(jsonFactory); mapper.registerModule(new SimpleModule() { diff --git a/modules/swagger-core/src/test/java/io/swagger/util/JsonSerializationTest.java b/modules/swagger-core/src/test/java/io/swagger/util/JsonSerializationTest.java index 73b8614033..a09d671c50 100644 --- a/modules/swagger-core/src/test/java/io/swagger/util/JsonSerializationTest.java +++ b/modules/swagger-core/src/test/java/io/swagger/util/JsonSerializationTest.java @@ -1,6 +1,8 @@ package io.swagger.util; +import com.fasterxml.jackson.core.JsonFactory; +import io.swagger.matchers.SerializationMatchers; import io.swagger.models.*; import org.testng.annotations.Test; @@ -66,4 +68,18 @@ public void testSerializeSecurityRequirement_UsingSpecCompliantMethods() throws json = Json.mapper().writeValueAsString(swagger); assertEquals(json, "{\"swagger\":\"2.0\",\"security\":[{\"api_key\":[],\"basic_auth\":[]},{\"oauth2\":[\"hello\",\"world\"]}]}"); } + + @Test + public void testSerializeWithCustomFactory() throws Exception { + // given + JsonFactory jsonFactory = new JsonFactory(); + final String json = ResourceUtils.loadClassResource(getClass(), "specFiles/petstore.json"); + final String expectedJson = ResourceUtils.loadClassResource(getClass(), "specFiles/jsonSerialization-expected-petstore.json"); + + // when + Swagger deser = ObjectMapperFactory.createJson(jsonFactory).readValue(json, Swagger.class); + + // then + SerializationMatchers.assertEqualsToJson(deser, expectedJson); + } } diff --git a/modules/swagger-core/src/test/java/io/swagger/util/YamlSerializationTest.java b/modules/swagger-core/src/test/java/io/swagger/util/YamlSerializationTest.java new file mode 100644 index 0000000000..2011c48656 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/util/YamlSerializationTest.java @@ -0,0 +1,45 @@ +package io.swagger.util; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.dataformat.yaml.JacksonYAMLParseException; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import io.swagger.matchers.SerializationMatchers; +import io.swagger.models.Swagger; +import org.testng.annotations.Test; +import org.yaml.snakeyaml.LoaderOptions; + +public class YamlSerializationTest { + + @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/petstore.yaml"); + + // when + Swagger swagger = ObjectMapperFactory.createYaml(yamlFactory).readValue(yaml, Swagger.class); + + // then + SerializationMatchers.assertEqualsToYaml(swagger, 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/petstore.yaml"); + + // when + Swagger swagger = ObjectMapperFactory.createYaml(yamlFactory).readValue(yaml, Swagger.class); + + // then - Throw JacksonYAMLParseException + } +} diff --git a/modules/swagger-core/src/test/resources/specFiles/jsonSerialization-expected-petstore.json b/modules/swagger-core/src/test/resources/specFiles/jsonSerialization-expected-petstore.json new file mode 100644 index 0000000000..df8a2afb7e --- /dev/null +++ b/modules/swagger-core/src/test/resources/specFiles/jsonSerialization-expected-petstore.json @@ -0,0 +1,530 @@ +{ + "swagger":"2.0", + "info":{ + "description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "title":"Petstore Sample API", + "contact":{ + "name":"Swagger API Team" + }, + "license":{ + "name":"MIT", + "url":"http://github.com/gruntjs/grunt/blob/master/LICENSE-MIT" + }, + "x-name":"value", + "x-extension":{ + "name":"value" + } + }, + "host":"petstore.swagger.io", + "basePath":"/api", + "tags":[ + { + "name":"pet", + "description":"Everything about your Pets", + "externalDocs":{ + "description":"Find out more", + "url":"http://swagger.io" + } + }, + { + "name":"user", + "description":"Operations about user" + }, + { + "name":"store", + "description":"Access to Petstore orders", + "externalDocs":{ + "description":"Find out more", + "url":"http://swagger.io" + } + } + ], + "paths":{ + "/pet":{ + "post":{ + "tags":[ + "pet" + ], + "parameters":[ + { + "in":"body", + "name":"body", + "description":"Pet object that needs to be added to the store", + "required":false, + "schema":{ + "$ref":"#/definitions/Tag" + } + } + ], + "responses":{ + "405":{ + "description":"Invalid input" + } + } + }, + "put":{ + "tags":[ + "pet" + ], + "parameters":[ + { + "in":"body", + "name":"body", + "description":"Pet object that needs to be added to the store", + "required":false, + "schema":{ + "$ref":"#/definitions/Pet" + } + } + ], + "responses":{ + "405":{ + "description":"Validation exception" + }, + "404":{ + "description":"Pet not found" + }, + "400":{ + "description":"Invalid ID supplied" + } + }, + "x-name":"value", + "x-extension":{ + "name":"value" + } + } + }, + "/user/createWithList":{ + "post":{ + "tags":[ + "user" + ], + "parameters":[ + { + "in":"body", + "name":"body", + "description":"List of user object", + "required":false + } + ] + } + }, + "/store/order/{orderId}":{ + "get":{ + "tags":[ + "store" + ], + "parameters":[ + { + "name":"orderId", + "in":"path", + "description":"ID of pet that needs to be fetched", + "required":true, + "type":"string" + } + ], + "responses":{ + "404":{ + "description":"Order not found" + }, + "400":{ + "description":"Invalid ID supplied" + } + } + }, + "delete":{ + "tags":[ + "store" + ], + "parameters":[ + { + "name":"orderId", + "in":"path", + "description":"ID of the order that needs to be deleted", + "required":true, + "type":"string" + } + ], + "responses":{ + "404":{ + "description":"Order not found" + }, + "400":{ + "description":"Invalid ID supplied" + } + } + } + }, + "/user/createWithArray":{ + "post":{ + "tags":[ + "user" + ], + "parameters":[ + { + "in":"body", + "name":"body", + "description":"List of user object", + "required":false, + "schema":{ + "$ref":"#/definitions/User" + } + } + ] + } + }, + "/store/order":{ + "post":{ + "tags":[ + "store" + ], + "parameters":[ + { + "in":"body", + "name":"body", + "description":"order placed for purchasing the pet", + "required":false, + "schema":{ + "$ref":"#/definitions/Order" + } + } + ], + "responses":{ + "400":{ + "description":"Invalid Order" + } + } + } + }, + "/pet/findByStatus":{ + "get":{ + "tags":[ + "pet" + ], + "parameters":[ + { + "name":"status", + "in":"query", + "description":"Status values that need to be considered for filter", + "required":false, + "type":"string" + } + ], + "responses":{ + "400":{ + "description":"Invalid status value" + } + } + } + }, + "/user/{username}":{ + "get":{ + "tags":[ + "user" + ], + "parameters":[ + { + "name":"username", + "in":"path", + "description":"The name that needs to be fetched. Use user1 for testing. ", + "required":true, + "type":"string" + } + ], + "responses":{ + "404":{ + "description":"User not found" + }, + "400":{ + "description":"Invalid username supplied" + } + } + }, + "put":{ + "tags":[ + "user" + ], + "parameters":[ + { + "name":"username", + "in":"path", + "description":"name that need to be deleted", + "required":true, + "type":"string" + }, + { + "in":"body", + "name":"body", + "description":"Updated user object", + "required":false, + "schema":{ + "$ref":"#/definitions/User" + } + } + ], + "responses":{ + "404":{ + "description":"User not found" + }, + "400":{ + "description":"Invalid user supplied" + } + } + }, + "delete":{ + "tags":[ + "user" + ], + "parameters":[ + { + "name":"username", + "in":"path", + "description":"The name that needs to be deleted", + "required":true, + "type":"string" + } + ], + "responses":{ + "404":{ + "description":"User not found" + }, + "400":{ + "description":"Invalid username supplied" + } + } + } + }, + "/pet/findByTags":{ + "get":{ + "tags":[ + "pet" + ], + "parameters":[ + { + "name":"tags", + "in":"query", + "description":"Tags to filter by", + "required":false, + "type":"string" + } + ], + "responses":{ + "400":{ + "description":"Invalid tag value" + } + } + } + }, + "/user":{ + "post":{ + "tags":[ + "user" + ], + "parameters":[ + { + "in":"body", + "name":"body", + "description":"Created user object", + "required":false, + "schema":{ + "$ref":"#/definitions/User" + } + } + ] + } + }, + "/pet/{petId}":{ + "get":{ + "tags":[ + "pet" + ], + "parameters":[ + { + "name":"petId", + "in":"path", + "description":"ID of pet that needs to be fetched", + "required":true, + "type":"string" + } + ], + "responses":{ + "404":{ + "description":"Pet not found" + }, + "400":{ + "description":"Invalid ID supplied" + } + } + }, + "head":{ + "tags":[ + "pet" + ], + "parameters":[ + { + "name":"petId", + "in":"path", + "description":"ID of pet that needs to be fetched", + "required":true, + "type":"string" + } + ], + "responses":{ + "404":{ + "description":"Pet not found" + }, + "400":{ + "description":"Invalid ID supplied" + } + } + } + }, + "/user/logout":{ + "get":{ + "tags":[ + "user" + ], + "parameters":[ + + ] + } + }, + "/user/login":{ + "get":{ + "tags":[ + "user" + ], + "parameters":[ + { + "name":"username", + "in":"query", + "description":"The user name for login", + "required":false, + "type":"string" + }, + { + "name":"password", + "in":"query", + "description":"The password for login in clear text", + "required":false, + "type":"string" + } + ], + "responses":{ + "400":{ + "description":"Invalid username/password supplied" + } + } + } + } + }, + "definitions":{ + "User":{ + "properties":{ + "id":{ + "type":"integer", + "format":"int32" + }, + "lastName":{ + "type":"string" + }, + "username":{ + "type":"string" + }, + "phone":{ + "type":"string" + }, + "email":{ + "type":"string" + }, + "userStatus":{ + "type":"integer", + "format":"int32" + }, + "firstName":{ + "type":"string" + }, + "password":{ + "type":"string" + } + } + }, + "Category":{ + "properties":{ + "id":{ + "type":"integer", + "format":"int32" + }, + "name":{ + "type":"string" + } + } + }, + "Pet":{ + "properties":{ + "id":{ + "type":"integer", + "format":"int32" + }, + "tags":{ + "type":"array", + "items":{ + "$ref":"#/definitions/Tag" + } + }, + "category":{ + "$ref":"#/definitions/Category" + }, + "status":{ + "type":"string" + }, + "name":{ + "type":"string" + }, + "photoUrls":{ + "type":"array", + "items":{ + "type":"string" + } + } + } + }, + "Tag":{ + "properties":{ + "id":{ + "type":"integer", + "format":"int32" + }, + "name":{ + "type":"string" + } + } + }, + "Order":{ + "properties":{ + "id":{ + "type":"integer", + "format":"int32" + }, + "petId":{ + "type":"integer", + "format":"int32" + }, + "status":{ + "type":"string" + }, + "complete":{ + "type":"boolean" + }, + "quantity":{ + "type":"integer", + "format":"int32" + }, + "shipDate":{ + "type":"string", + "format":"date-time" + } + } + } + } +} \ No newline at end of file diff --git a/modules/swagger-core/src/test/resources/specFiles/petstore.yaml b/modules/swagger-core/src/test/resources/specFiles/petstore.yaml new file mode 100644 index 0000000000..05705d54cf --- /dev/null +++ b/modules/swagger-core/src/test/resources/specFiles/petstore.yaml @@ -0,0 +1,208 @@ +swagger: '2.0' +info: + description: | + This is a sample Petstore server. You can find + out more about Swagger at + [http://swagger.io](http://swagger.io) or on + [irc.freenode.net, #swagger](http://swagger.io/irc/). + version: 1.0.0 + title: Swagger Petstore + termsOfService: http://swagger.io/terms/ + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html +host: petstore.swagger.io +basePath: /v2 +tags: + - name: pet + description: Everything about your Pets + externalDocs: + description: Find out more + url: http://swagger.io + - name: store + description: Access to Petstore orders + - name: user + description: Operations about user + externalDocs: + description: Find out more about our store + url: http://swagger.io +schemes: + - http +paths: + /pet: + post: + tags: + - pet + summary: Add a new pet to the store + operationId: addPet + consumes: + - application/json + - application/xml + produces: + - application/json + - application/xml + parameters: + - in: body + name: body + description: Pet object that needs to be added to the store + required: true + schema: + $ref: '#/definitions/Pet' + responses: + 405: + description: Invalid input + security: + - petstore_auth: + - write:pets + - read:pets + put: + tags: + - pet + summary: Update an existing pet + operationId: updatePet + consumes: + - application/json + - application/xml + produces: + - application/json + - application/xml + parameters: + - in: body + name: body + description: Pet object that needs to be added to the store + required: true + schema: + $ref: '#/definitions/Pet' + responses: + 400: + description: Invalid ID supplied + 404: + description: Pet not found + 405: + description: Validation exception + security: + - petstore_auth: + - write:pets + - read:pets + /pet/findByStatus: + get: + tags: + - pet + summary: Finds Pets by status + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + produces: + - application/json + - application/xml + parameters: + - name: status + in: query + description: Status values that need to be considered for filter + required: true + type: array + items: + type: string + enum: + - available + - pending + - sold + default: available + collectionFormat: multi + responses: + 200: + description: successful operation + schema: + type: array + items: + $ref: '#/definitions/Pet' + 400: + description: Invalid status value + security: + - petstore_auth: + - write:pets + - read:pets +securityDefinitions: + petstore_auth: + type: oauth2 + authorizationUrl: http://petstore.swagger.io/oauth/dialog + flow: implicit + scopes: + write:pets: modify pets in your account + read:pets: read your pets + api_key: + type: apiKey + name: api_key + in: header +definitions: + Category: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Category + Tag: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Tag + Pet: + type: object + required: + - name + - photoUrls + properties: + id: + type: integer + format: int64 + category: + $ref: '#/definitions/Category' + name: + type: string + example: doggie + photoUrls: + type: array + xml: + name: photoUrl + wrapped: true + items: + type: string + tags: + type: array + xml: + name: tag + wrapped: true + items: + $ref: '#/definitions/Tag' + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: Pet + ApiResponse: + type: object + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string +externalDocs: + description: Find out more about Swagger + url: http://swagger.io \ No newline at end of file