From aa1fbb680bd07c759011ba2c147f14a273fa7d69 Mon Sep 17 00:00:00 2001 From: Tony Tam Date: Sun, 11 Aug 2013 15:36:16 -0700 Subject: [PATCH] updates for response class to 1.2 spec --- .../model/SwaggerModelSerializer.scala | 76 ++++++- src/test/resources/petstore-1.2/pet | 75 +++--- src/test/resources/petstore-1.2/store | 22 +- src/test/resources/petstore-1.2/user | 214 +++++++++--------- .../ModelSerializerValidations.scala | 15 +- .../swaggerSpec1_2/ModelSerializersTest.scala | 8 +- 6 files changed, 239 insertions(+), 171 deletions(-) diff --git a/src/main/scala/com/wordnik/swagger/model/SwaggerModelSerializer.scala b/src/main/scala/com/wordnik/swagger/model/SwaggerModelSerializer.scala index be6630a9f67..1cf93fbaeae 100644 --- a/src/main/scala/com/wordnik/swagger/model/SwaggerModelSerializer.scala +++ b/src/main/scala/com/wordnik/swagger/model/SwaggerModelSerializer.scala @@ -27,6 +27,47 @@ object SwaggerSerializers { ("array", "") -> "Array" ) + def toJsonSchema(name: String, `type`: String): JObject = { + `type` match { + case "int" => (name -> "integer") ~ ("format" -> "int32") + case "long" => (name -> "integer") ~ ("format" -> "int64") + case "float" => (name -> "number") ~ ("format" -> "float") + case "double" => (name -> "number") ~ ("format" -> "double") + case "string" => (name -> "string") ~ ("format" -> JNothing) + case "byte" => (name -> "string") ~ ("format" -> "byte") + case "boolean" => (name -> "boolean") ~ ("format" -> JNothing) + case "Date" => (name -> "string") ~ ("format" -> "date-time") + case "date" => (name -> "string") ~ ("format" -> "date") + case "date-time" => (name -> "string") ~ ("format" -> "date-time") + case _ => { + val ComplexTypeMatcher = "([a-zA-Z]*)\\[([a-zA-Z\\.\\-]*)\\].*".r + `type` match { + case ComplexTypeMatcher(container, value) => + toJsonSchemaContainer(container) ~ { + ("items" -> {if(isSimpleType(value)) + toJsonSchema("type", value) + else + toJsonSchema("$ref", value)}) + } + case _ => (name -> `type`) ~ ("format" -> JNothing) + } + } + } + } + + def toJsonSchemaContainer(name: String): JObject = { + name match { + case "List" => ("type" -> "array") ~ ("format" -> JNothing) + case "Array" => ("type" -> "array") ~ ("format" -> JNothing) + case "Set" => ("type" -> "array") ~ ("uniqueItems" -> true) + case _ => ("type" -> JNothing) + } + } + + def isSimpleType(name: String) = { + Set("int", "long", "float", "double", "string", "byte", "boolean", "Date", "date", "date-time", "array").contains(name) + } + def formats(version: String) = { version match { case "1.1" => LegacySerializers.formats @@ -205,6 +246,21 @@ object SwaggerSerializers { case json => implicit val fmts: Formats = formats + val responseClass = (json \ "items") match { + case e: JObject => { + val inner = { + (e \ "type").extractOrElse({ + (e \ "$ref").extract[String] + }) + } + "%s[%s]".format((json \ "type").extract[String], inner) + } + case _ => (json \ "type").extractOrElse({ + !!(json, OPERATION, "responseClass", "missing required field", ERROR) + "" + }) + } + Operation( (json \ "httpMethod").extractOrElse( (json \ "method").extractOrElse({ @@ -214,10 +270,7 @@ object SwaggerSerializers { ), (json \ "summary").extract[String], (json \ "notes").extractOrElse(""), - (json \ "responseClass").extractOrElse({ - !!(json, OPERATION, "responseClass", "missing required field", ERROR) - "" - }), + responseClass, (json \ "nickname").extractOrElse({ !!(json, OPERATION, "nickname", "missing required field", ERROR) "" @@ -233,11 +286,24 @@ object SwaggerSerializers { ) }, { case x: Operation => + + val ComplexTypeMatcher = "([a-zA-Z]*)\\[([a-zA-Z\\.\\-]*)\\].*".r + val output = x.responseClass match { + case ComplexTypeMatcher(container, value) => + toJsonSchemaContainer(container) ~ { + ("items" -> {if(isSimpleType(value)) + toJsonSchema("type", value) + else + toJsonSchema("$ref", value)}) + } + case _ => toJsonSchema("type", x.responseClass) ~ ("format" -> JNothing) + } + implicit val fmts = formats ("method" -> x.method) ~ ("summary" -> x.summary) ~ ("notes" -> x.notes) ~ - ("responseClass" -> x.responseClass) ~ + output ~ ("nickname" -> x.nickname) ~ ("parameters" -> Extraction.decompose(x.parameters)) ~ ("responseMessages" -> { diff --git a/src/test/resources/petstore-1.2/pet b/src/test/resources/petstore-1.2/pet index a36d679818d..7f1c17ecd72 100644 --- a/src/test/resources/petstore-1.2/pet +++ b/src/test/resources/petstore-1.2/pet @@ -12,13 +12,12 @@ "apis": [ { "path": "/pet/{petId}", - "description":"Operations about pets", "operations": [ { "method": "GET", "summary": "Find pet by ID", "notes": "Returns a pet based on ID", - "responseClass": "Pet", + "type": "Pet", "nickname": "getPetById", "produces": [ "application/json", @@ -52,7 +51,7 @@ "method": "DELETE", "summary": "Deletes a pet", "notes": "", - "responseClass": "void", + "type": "void", "nickname": "deletePet", "parameters": [ { @@ -76,11 +75,34 @@ { "path": "/pet", "operations": [ + { + "method": "POST", + "summary": "Add a new pet to the store", + "notes": "", + "type": "void", + "nickname": "addPet", + "parameters": [ + { + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "allowMultiple": false, + "type": "Pet", + "paramType": "body" + } + ], + "responseMessages": [ + { + "code": 405, + "message": "Invalid input" + } + ] + }, { "method": "PUT", "summary": "Update an existing pet", "notes": "", - "responseClass": "void", + "type": "void", "nickname": "updatePet", "parameters": [ { @@ -106,29 +128,6 @@ "message": "Validation exception" } ] - }, - { - "method": "POST", - "summary": "Add a new pet to the store", - "notes": "", - "responseClass": "void", - "nickname": "addPet", - "parameters": [ - { - "name": "body", - "description": "Pet object that needs to be added to the store", - "required": true, - "allowMultiple": false, - "type": "Pet", - "paramType": "body" - } - ], - "responseMessages": [ - { - "code": 405, - "message": "Invalid input" - } - ] } ] }, @@ -139,7 +138,10 @@ "method": "GET", "summary": "Finds Pets by status", "notes": "Multiple status values can be provided with comma seperated strings", - "responseClass": "List[Pet]", + "type": "array", + "items": { + "$ref": "Pet" + }, "nickname": "findPetsByStatus", "produces": [ "application/json", @@ -177,7 +179,10 @@ "method": "GET", "summary": "Finds Pets by tags", "notes": "Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.", - "responseClass": "List[Pet]", + "type": "array", + "items": { + "$ref": "Pet" + }, "nickname": "findPetsByTags", "produces": [ "application/json", @@ -221,10 +226,14 @@ "id": "Pet", "description": "A pet is a person's best friend", "required": [ - "name", - "id" + "id", + "name" ], "properties": { + "id": { + "type": "integer", + "format": "int64" + }, "tags": { "type": "array", "items": { @@ -234,10 +243,6 @@ "name": { "type": "string" }, - "id": { - "type": "integer", - "format": "int64" - }, "status": { "type": "string", "description": "pet status in the store", diff --git a/src/test/resources/petstore-1.2/store b/src/test/resources/petstore-1.2/store index 3aa07b3e078..040ceeca274 100644 --- a/src/test/resources/petstore-1.2/store +++ b/src/test/resources/petstore-1.2/store @@ -15,7 +15,7 @@ "method": "GET", "summary": "Find purchase order by ID", "notes": "For valid response try integer IDs with value <= 5. Anything above 5 or nonintegers will generate API errors", - "responseClass": "Order", + "type": "Order", "nickname": "getOrderById", "produces": [ "application/json", @@ -46,7 +46,7 @@ "method": "DELETE", "summary": "Delete purchase order by ID", "notes": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", - "responseClass": "void", + "type": "void", "nickname": "deleteOrder", "parameters": [ { @@ -78,7 +78,7 @@ "method": "POST", "summary": "Place an order for a pet", "notes": "", - "responseClass": "void", + "type": "void", "nickname": "placeOrder", "parameters": [ { @@ -104,6 +104,10 @@ "Order": { "id": "Order", "properties": { + "id": { + "type": "integer", + "format": "int64" + }, "status": { "type": "string", "description": "Order Status", @@ -113,14 +117,6 @@ "delivered" ] }, - "id": { - "type": "integer", - "format": "int64" - }, - "shipDate": { - "type": "string", - "format": "date-time" - }, "petId": { "type": "integer", "format": "int64" @@ -128,6 +124,10 @@ "quantity": { "type": "integer", "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" } } } diff --git a/src/test/resources/petstore-1.2/user b/src/test/resources/petstore-1.2/user index 7da7d3012a7..7f0e6764a5f 100644 --- a/src/test/resources/petstore-1.2/user +++ b/src/test/resources/petstore-1.2/user @@ -9,44 +9,85 @@ ], "apis": [ { - "path": "/user/{username}", + "path": "/user", "operations": [ { - "method": "GET", - "summary": "Get user by user name", - "notes": "", - "responseClass": "User", - "nickname": "getUserByName", - "produces": [ - "application/json", - "application/xml" - ], + "method": "POST", + "summary": "Create user", + "notes": "This can only be done by the logged in user.", + "type": "void", + "nickname": "createUser", "parameters": [ { - "name": "username", - "description": "The name that needs to be fetched. Use user1 for testing.", + "name": "body", + "description": "Created user object", "required": true, "allowMultiple": false, - "type": "string", - "paramType": "path" - } - ], - "responseMessages": [ - { - "code": 400, - "message": "Invalid username supplied" - }, - { - "code": 404, - "message": "User not found" + "type": "User", + "paramType": "body" } ] - }, + } + ] + }, + { + "path": "/user/createWithArray", + "operations": [ + { + "method": "POST", + "summary": "Creates list of users with given input array", + "notes": "", + "type": "void", + "nickname": "createUsersWithArrayInput", + "parameters": [ + { + "name": "body", + "description": "List of user object", + "required": true, + "allowMultiple": false, + "type": "array", + "items": { + "$ref": "User" + }, + "paramType": "body" + } + ] + } + ] + }, + { + "path": "/user/createWithList", + "operations": [ + { + "method": "POST", + "summary": "Creates list of users with given list input", + "notes": "", + "type": "void", + "nickname": "createUsersWithListInput", + "parameters": [ + { + "name": "body", + "description": "List of user object", + "required": true, + "allowMultiple": false, + "type": "array", + "items": { + "$ref": "User" + }, + "paramType": "body" + } + ] + } + ] + }, + { + "path": "/user/{username}", + "operations": [ { "method": "PUT", "summary": "Updated user", "notes": "This can only be done by the logged in user.", - "responseClass": "void", + "type": "void", "nickname": "updateUser", "parameters": [ { @@ -81,7 +122,7 @@ "method": "DELETE", "summary": "Delete user", "notes": "This can only be done by the logged in user.", - "responseClass": "void", + "type": "void", "nickname": "deleteUser", "parameters": [ { @@ -103,6 +144,37 @@ "message": "User not found" } ] + }, + { + "method": "GET", + "summary": "Get user by user name", + "notes": "", + "type": "User", + "nickname": "getUserByName", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "username", + "description": "The name that needs to be fetched. Use user1 for testing.", + "required": true, + "allowMultiple": false, + "type": "string", + "paramType": "path" + } + ], + "responseMessages": [ + { + "code": 400, + "message": "Invalid username supplied" + }, + { + "code": 404, + "message": "User not found" + } + ] } ] }, @@ -113,7 +185,7 @@ "method": "GET", "summary": "Logs user into the system", "notes": "", - "responseClass": "string", + "type": "string", "nickname": "loginUser", "produces": [ "text/plain" @@ -152,7 +224,7 @@ "method": "GET", "summary": "Logs out current logged in user session", "notes": "", - "responseClass": "void", + "type": "void", "nickname": "logoutUser", "produces": [ "text/plain" @@ -160,84 +232,16 @@ "parameters": [] } ] - }, - { - "path": "/user", - "operations": [ - { - "method": "POST", - "summary": "Create user", - "notes": "This can only be done by the logged in user.", - "responseClass": "void", - "nickname": "createUser", - "parameters": [ - { - "name": "body", - "description": "Created user object", - "required": true, - "allowMultiple": false, - "type": "User", - "paramType": "body" - } - ] - } - ] - }, - { - "path": "/user/createWithArray", - "operations": [ - { - "method": "POST", - "summary": "Creates list of users with given input array", - "notes": "", - "responseClass": "void", - "nickname": "createUsersWithArrayInput", - "parameters": [ - { - "name": "body", - "description": "List of user object", - "required": true, - "allowMultiple": false, - "type": "array", - "items": { - "$ref": "User" - }, - "paramType": "body" - } - ] - } - ] - }, - { - "path": "/user/createWithList", - "operations": [ - { - "method": "POST", - "summary": "Creates list of users with given list input", - "notes": "", - "responseClass": "void", - "nickname": "createUsersWithListInput", - "parameters": [ - { - "name": "body", - "description": "List of user object", - "required": true, - "allowMultiple": false, - "type": "array", - "items": { - "$ref": "User" - }, - "paramType": "body" - } - ] - } - ] } ], "models": { "User": { "id": "User", "properties": { + "id": { + "type": "integer", + "format": "int64" + }, "username": { "type": "string" }, @@ -247,9 +251,8 @@ "email": { "type": "string" }, - "id": { - "type": "integer", - "format": "int64" + "firstName": { + "type": "string" }, "lastName": { "type": "string" @@ -266,9 +269,6 @@ "2-active", "3-closed" ] - }, - "firstName": { - "type": "string" } } } diff --git a/src/test/scala/swaggerSpec1_2/ModelSerializerValidations.scala b/src/test/scala/swaggerSpec1_2/ModelSerializerValidations.scala index 76989787a9b..55053cdb8ac 100644 --- a/src/test/scala/swaggerSpec1_2/ModelSerializerValidations.scala +++ b/src/test/scala/swaggerSpec1_2/ModelSerializerValidations.scala @@ -145,7 +145,7 @@ class OperationValidationTest extends FlatSpec with ShouldMatchers { "httpMethod":"GET", "summary":"the summary", "notes":"the notes", - "responseClass":"string", + "type":"string", "nickname":"getMeSomeStrings", "parameters":[ { @@ -183,7 +183,7 @@ class OperationValidationTest extends FlatSpec with ShouldMatchers { List.empty, List(Parameter("id", Some("the id"), Some("-1"), false, true, "string", AllowableListValues(List("a","b","c")), "query")) ) - write(op) should be ("""{"method":"get","summary":"the summary","notes":"the notes","responseClass":"string","nickname":"getMeSomeStrings","parameters":[{"name":"id","description":"the id","defaultValue":"-1","required":false,"allowMultiple":true,"type":"string","allowableValues":{"valueType":"LIST","values":["a","b","c"]},"paramType":"query"}]}""") + write(op) should be ("""{"method":"get","summary":"the summary","notes":"the notes","type":"string","nickname":"getMeSomeStrings","parameters":[{"name":"id","description":"the id","defaultValue":"-1","required":false,"allowMultiple":true,"type":"string","allowableValues":{"valueType":"LIST","values":["a","b","c"]},"paramType":"query"}]}""") } } @@ -225,13 +225,10 @@ class ParameterValidationTest extends FlatSpec with ShouldMatchers { "defaultValue":"false", "description":"Show duplicate examples from different sources", "required":"false", - "allowableValues":{ - "values":[ - false, - true - ], - "valueType":"LIST" - }, + "enum":[ + false, + true + ], "type":"string", "allowMultiple":false, "paramType":"query" diff --git a/src/test/scala/swaggerSpec1_2/ModelSerializersTest.scala b/src/test/scala/swaggerSpec1_2/ModelSerializersTest.scala index ad0851ebf55..d7d81d5fa65 100644 --- a/src/test/scala/swaggerSpec1_2/ModelSerializersTest.scala +++ b/src/test/scala/swaggerSpec1_2/ModelSerializersTest.scala @@ -138,7 +138,7 @@ class ApiDescriptionSerializersTest extends FlatSpec with ShouldMatchers { "method":"GET", "summary":"the summary", "notes":"the notes", - "responseClass":"string", + "type":"string", "nickname":"getMeSomeStrings", "parameters":[ { @@ -203,7 +203,7 @@ class ApiDescriptionSerializersTest extends FlatSpec with ShouldMatchers { List(Parameter("id", Some("the id"), Some("-1"), false, true, "string", AllowableListValues(List("a","b","c")), "query")) )) ) - write(l) should be ("""{"path":"/foo/bar","description":"the description","operations":[{"method":"get","summary":"the summary","notes":"the notes","responseClass":"string","nickname":"getMeSomeStrings","parameters":[{"name":"id","description":"the id","defaultValue":"-1","required":false,"allowMultiple":true,"type":"string","allowableValues":{"valueType":"LIST","values":["a","b","c"]},"paramType":"query"}]}]}""") + write(l) should be ("""{"path":"/foo/bar","description":"the description","operations":[{"method":"get","summary":"the summary","notes":"the notes","type":"string","nickname":"getMeSomeStrings","parameters":[{"name":"id","description":"the id","defaultValue":"-1","required":false,"allowMultiple":true,"type":"string","allowableValues":{"valueType":"LIST","values":["a","b","c"]},"paramType":"query"}]}]}""") } } @@ -217,7 +217,7 @@ class OperationSerializersTest extends FlatSpec with ShouldMatchers { "method":"GET", "summary":"the summary", "notes":"the notes", - "responseClass":"string", + "type":"string", "nickname":"getMeSomeStrings", "parameters":[ { @@ -271,7 +271,7 @@ class OperationSerializersTest extends FlatSpec with ShouldMatchers { List.empty, List(Parameter("id", Some("the id"), Some("-1"), false, true, "string", AllowableListValues(List("a","b","c")), "query")) ) - write(op) should be ("""{"method":"get","summary":"the summary","notes":"the notes","responseClass":"string","nickname":"getMeSomeStrings","parameters":[{"name":"id","description":"the id","defaultValue":"-1","required":false,"allowMultiple":true,"type":"string","allowableValues":{"valueType":"LIST","values":["a","b","c"]},"paramType":"query"}]}""") + write(op) should be ("""{"method":"get","summary":"the summary","notes":"the notes","type":"string","nickname":"getMeSomeStrings","parameters":[{"name":"id","description":"the id","defaultValue":"-1","required":false,"allowMultiple":true,"type":"string","allowableValues":{"valueType":"LIST","values":["a","b","c"]},"paramType":"query"}]}""") } }