updated serializers, tests

This commit is contained in:
Tony Tam 2012-12-03 22:12:40 -08:00
parent 61a9cc6e6a
commit 25837e3367
3 changed files with 616 additions and 68 deletions

View File

@ -5,12 +5,202 @@ import org.json4s.JsonDSL._
import org.json4s.jackson.JsonMethods._ import org.json4s.jackson.JsonMethods._
import org.json4s.native.Serialization.{read, write} import org.json4s.native.Serialization.{read, write}
import scala.collection.mutable.HashMap
import scala.collection.JavaConverters._
object SwaggerSerializers {
implicit val formats = DefaultFormats +
new ModelSerializer +
new ModelPropertySerializer +
new ModelRefSerializer +
new AllowableValuesSerializer +
new ParameterSerializer +
new OperationSerializer +
new ErrorResponseSerializer +
new ApiDescriptionSerializer +
new ApiListingReferenceSerializer +
new ResourceListingSerializer
class ResourceListingSerializer extends CustomSerializer[ResourceListing](formats => ({
case json =>
implicit val fmts: Formats = formats
ResourceListing(
(json \ "apiVersion").extract[String],
(json \ "swaggerVersion").extract[String],
(json \ "basePath").extract[String],
(json \ "apis").extract[List[ApiListingReference]]
)
}, {
case x: ResourceListing =>
implicit val fmts = formats
("apiVersion" -> x.apiVersion) ~
("swaggerVersion" -> x.swaggerVersion) ~
("basePath" -> x.basePath) ~
("apis" -> {
x.apis match {
case e: List[ApiListingReference] if (e.size > 0) => Extraction.decompose(e)
case _ => JNothing
}
})
}
))
class ApiListingReferenceSerializer extends CustomSerializer[ApiListingReference](formats => ({
case json =>
implicit val fmts: Formats = formats
ApiListingReference(
(json \ "path").extract[String],
(json \ "description").extractOrElse("")
)
}, {
case x: ApiListingReference =>
implicit val fmts = formats
("path" -> x.path) ~
("description" -> x.description)
}
))
class ApiDescriptionSerializer extends CustomSerializer[ApiDescription](formats => ({
case json =>
implicit val fmts: Formats = formats
ApiDescription(
(json \ "path").extract[String],
(json \ "description").extractOrElse(""),
(json \ "operations").extract[List[Operation]]
)
}, {
case x: ApiDescription =>
implicit val fmts = formats
("path" -> x.path) ~
("description" -> x.description) ~
("operations" -> {
x.operations match {
case e:List[Operation] if(e.size > 0) => Extraction.decompose(e)
case _ => JNothing
}
})
}
))
class ErrorResponseSerializer extends CustomSerializer[ErrorResponse](formats => ({
case json =>
implicit val fmts: Formats = formats
ErrorResponse(
(json \ "code").extract[Int],
(json \ "reason").extract[String]
)
}, {
case x: ErrorResponse =>
implicit val fmts = formats
("code" -> x.code) ~
("reason" -> x.reason)
}
))
class OperationSerializer extends CustomSerializer[Operation](formats => ({
case json =>
implicit val fmts: Formats = formats
Operation(
(json \ "httpMethod").extract[String],
(json \ "summary").extract[String],
(json \ "notes").extract[String],
(json \ "responseClass").extract[String],
(json \ "nickname").extract[String],
(json \ "parameters").extract[List[Parameter]],
(json \ "errorResponses").extract[List[ErrorResponse]],
(json \ "deprecated").extractOpt[String]
)
}, {
case x: Operation =>
implicit val fmts = formats
("httpMethod" -> x.httpMethod) ~
("summary" -> x.summary) ~
("notes" -> x.notes) ~
("responseClass" -> x.responseClass) ~
("nickname" -> x.nickname) ~
("parameters" -> Extraction.decompose(x.parameters)) ~
("errorResponses" -> {
x.errorResponses match {
case e: List[ErrorResponse] if(e.size > 0) => Extraction.decompose(e)
case _ => JNothing
}
}) ~
("deprecated" -> x.`deprecated`)
}
))
class ParameterSerializer extends CustomSerializer[Parameter](formats => ({
case json =>
implicit val fmts: Formats = formats
Parameter(
(json \ "name").extract[String],
(json \ "description").extract[String],
(json \ "defaultValue").extract[String],
(json \ "required").extractOrElse(false),
(json \ "allowMultiple").extractOrElse(false),
(json \ "dataType").extract[String],
(json \ "allowableValues").extract[AllowableValuesFoo],
(json \ "paramType").extract[String]
)
}, {
case x: Parameter =>
implicit val fmts = formats
("name" -> x.name) ~
("description" -> x.description) ~
("defaultValue" -> x.defaultValue) ~
("required" -> x.required) ~
("allowMultiple" -> x.allowMultiple) ~
("dataType" -> x.dataType) ~
("allowableValues" -> {
x.allowableValues match {
case Any => JNothing // don't serialize when not a concrete type
case e:AllowableValuesFoo => Extraction.decompose(x.allowableValues)
case _ => JNothing
}
}) ~
("paramType" -> x.paramType)
}
))
class ModelSerializer extends CustomSerializer[Model](formats => ({
case json =>
implicit val fmts: Formats = formats
val output = new HashMap[String, ModelProperty]
val properties = (json \ "properties") match {
case JObject(entries) => {
entries.map({
case (key, value) => output += key -> value.extract[ModelProperty]
})
}
case _ =>
}
Model(
(json \ "id").extract[String],
(json \ "name").extract[String],
output.asJava,
(json \ "description").extractOpt[String]
)
}, {
case x: Model =>
implicit val fmts = formats
("id" -> x.id) ~
("name" -> x.name) ~
("properties" -> {
x.properties match {
case e:java.util.Map[String, ModelProperty] => Extraction.decompose(e.asScala.toMap)
case _ => JNothing
}
})
}
))
class ModelPropertySerializer extends CustomSerializer[ModelProperty] (formats => ({ class ModelPropertySerializer extends CustomSerializer[ModelProperty] (formats => ({
case json => case json =>
implicit val fmts: Formats = formats implicit val fmts: Formats = formats
ModelProperty( ModelProperty(
`type` = (json \ "type").extractOrElse(""), `type` = (json \ "type").extractOrElse(""),
required = ((json \ "required").extractOrElse("false")).toBoolean, required = ((json \ "required").extractOrElse(false)),
description = (json \ "description").extractOpt[String], description = (json \ "description").extractOpt[String],
allowableValues = (json \ "allowableValues").extract[AllowableValuesFoo], allowableValues = (json \ "allowableValues").extract[AllowableValuesFoo],
items = (json \ "items").extractOpt[ModelRef] items = (json \ "items").extractOpt[ModelRef]
@ -29,7 +219,8 @@ class ModelPropertySerializer extends CustomSerializer[ModelProperty] (formats =
} }
}) ~ }) ~
("items" -> Extraction.decompose(x.items)) ("items" -> Extraction.decompose(x.items))
})) }
))
class ModelRefSerializer extends CustomSerializer[ModelRef](formats => ({ class ModelRefSerializer extends CustomSerializer[ModelRef](formats => ({
case json => case json =>
@ -43,7 +234,8 @@ class ModelRefSerializer extends CustomSerializer[ModelRef](formats => ({
implicit val fmts = formats implicit val fmts = formats
("$ref" -> x.ref) ~ ("$ref" -> x.ref) ~
("type" -> x.`type`) ("type" -> x.`type`)
})) }
))
class AllowableValuesSerializer extends CustomSerializer[AllowableValuesFoo](formats => ({ class AllowableValuesSerializer extends CustomSerializer[AllowableValuesFoo](formats => ({
case json => case json =>
@ -61,4 +253,6 @@ class AllowableValuesSerializer extends CustomSerializer[AllowableValuesFoo](for
("valueType" -> "LIST") ~ ("values" -> Extraction.decompose(values)) ("valueType" -> "LIST") ~ ("values" -> Extraction.decompose(values))
case AllowableRangeValues(min, max) => case AllowableRangeValues(min, max) =>
("valueType" -> "RANGE") ~ ("min" -> min) ~ ("max" -> max) ("valueType" -> "RANGE") ~ ("min" -> min) ~ ("max" -> max)
})) }
))
}

View File

@ -26,10 +26,10 @@ case class ResourceListing(
case class ApiListingReference(path:String, description: String) case class ApiListingReference(path:String, description: String)
abstract class AllowableValuesFoo trait AllowableValuesFoo
case object Any extends AllowableValuesFoo case object Any extends AllowableValues with AllowableValuesFoo
case class AllowableListValues (values: List[String] = List(), valueType: String = "LIST") extends AllowableValuesFoo case class AllowableListValues (values: List[String] = List(), valueType: String = "LIST") extends AllowableValues with AllowableValuesFoo
case class AllowableRangeValues(min: String, max: String) extends AllowableValuesFoo case class AllowableRangeValues(min: String, max: String) extends AllowableValues with AllowableValuesFoo
// using java.util.Map because Jackon 2 isn't deserializing ListMap correctly, and ordered // using java.util.Map because Jackon 2 isn't deserializing ListMap correctly, and ordered
// insertion is required // insertion is required

View File

@ -10,9 +10,367 @@ import org.scalatest.junit.JUnitRunner
import org.scalatest.FlatSpec import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers import org.scalatest.matchers.ShouldMatchers
import scala.collection.JavaConverters._
@RunWith(classOf[JUnitRunner])
class ResourceListingSerializersTest extends FlatSpec with ShouldMatchers {
implicit val formats = SwaggerSerializers.formats
it should "deserialize an ResourceListing with no apis" in {
val jsonString = """
{
"apiVersion":"1.2.3",
"swaggerVersion":"1.1",
"basePath":"http://foo/bar"
}
"""
val json = parse(jsonString)
json.extract[ResourceListing] match {
case p: ResourceListing => {
p.apiVersion should be ("1.2.3")
p.swaggerVersion should be ("1.1")
p.basePath should be ("http://foo/bar")
p.apis.size should be (0)
}
case _ => fail("wrong type returned, should be ResourceListing")
}
}
it should "serialize an ApiListingReference with no apis" in {
val l = ApiListingReference("/foo/bar", "the description")
write(l) should be ("""{"path":"/foo/bar","description":"the description"}""")
}
it should "deserialize an ResourceListing" in {
val jsonString = """
{
"apiVersion":"1.2.3",
"swaggerVersion":"1.1",
"basePath":"http://foo/bar",
"apis":[
{
"path":"/a/b",
"description":"path ab apis"
},{
"path":"/c",
"description":"path c apis"
}
]
}
"""
val json = parse(jsonString)
json.extract[ResourceListing] match {
case p: ResourceListing => {
p.apiVersion should be ("1.2.3")
p.swaggerVersion should be ("1.1")
p.basePath should be ("http://foo/bar")
p.apis.size should be (2)
}
case _ => fail("wrong type returned, should be ResourceListing")
}
}
it should "serialize an ApiListingReference" in {
val l = ApiListingReference("/foo/bar", "the description")
write(l) should be ("""{"path":"/foo/bar","description":"the description"}""")
}
}
@RunWith(classOf[JUnitRunner])
class ApiListingReferenceSerializersTest extends FlatSpec with ShouldMatchers {
implicit val formats = SwaggerSerializers.formats
it should "deserialize an ApiListingReference" in {
val jsonString = """
{
"path":"/foo/bar",
"description":"the description"
}
"""
val json = parse(jsonString)
json.extract[ApiListingReference] match {
case p: ApiListingReference => {
p.path should be ("/foo/bar")
p.description should be ("the description")
}
case _ => fail("wrong type returned, should be ApiListingReference")
}
}
it should "serialize an ApiListingReference" in {
val l = ApiListingReference("/foo/bar", "the description")
write(l) should be ("""{"path":"/foo/bar","description":"the description"}""")
}
}
@RunWith(classOf[JUnitRunner])
class ApiDescriptionSerializersTest extends FlatSpec with ShouldMatchers {
implicit val formats = SwaggerSerializers.formats
it should "deserialize an ApiDescription with no ops" in {
val jsonString = """
{
"path":"/foo/bar",
"description":"the description"
}
"""
val json = parse(jsonString)
json.extract[ApiDescription] match {
case p: ApiDescription => {
p.path should be ("/foo/bar")
p.description should be ("the description")
p.operations.size should be (0)
}
case _ => fail("wrong type returned, should be ApiDescription")
}
}
it should "serialize an ApiDescription with no operations" in {
val l = ApiDescription("/foo/bar", "the description")
write(l) should be ("""{"path":"/foo/bar","description":"the description"}""")
}
it should "deserialize an ApiDescription" in {
val jsonString = """
{
"path":"/foo/bar",
"description":"the description",
"operations":[
{
"httpMethod":"GET",
"summary":"the summary",
"notes":"the notes",
"responseClass":"string",
"nickname":"getMeSomeStrings",
"parameters":[
{
"name":"id",
"description":"the id",
"defaultValue":"-1",
"required":false,
"allowMultiple":true,
"dataType":"string",
"allowableValues":{
"valueType":"LIST",
"values":["a","b","c"]
},
"paramType":"query"
}
]
}
]
}
"""
val json = parse(jsonString)
json.extract[ApiDescription] match {
case p: ApiDescription => {
p.path should be ("/foo/bar")
p.description should be ("the description")
p.operations.size should be (1)
}
case _ => fail("wrong type returned, should be ApiDescription")
}
}
it should "serialize an ApiDescription" in {
val l = ApiDescription(
"/foo/bar",
"the description",
List(Operation(
"get",
"the summary",
"the notes",
"string",
"getMeSomeStrings",
List(Parameter("id", "the id", "-1", false, true, "string", AllowableListValues(List("a","b","c")), "query"))
))
)
write(l) should be ("""{"path":"/foo/bar","description":"the description","operations":[{"httpMethod":"get","summary":"the summary","notes":"the notes","responseClass":"string","nickname":"getMeSomeStrings","parameters":[{"name":"id","description":"the id","defaultValue":"-1","required":false,"allowMultiple":true,"dataType":"string","allowableValues":{"valueType":"LIST","values":["a","b","c"]},"paramType":"query"}]}]}""")
}
}
@RunWith(classOf[JUnitRunner])
class OperationSerializersTest extends FlatSpec with ShouldMatchers {
implicit val formats = SwaggerSerializers.formats
it should "deserialize an Operation" in {
val jsonString = """
{
"httpMethod":"GET",
"summary":"the summary",
"notes":"the notes",
"responseClass":"string",
"nickname":"getMeSomeStrings",
"parameters":[
{
"name":"id",
"description":"the id",
"defaultValue":"-1",
"required":false,
"allowMultiple":true,
"dataType":"string",
"allowableValues":{
"valueType":"LIST",
"values":["a","b","c"]
},
"paramType":"query"
}
]
}
"""
val json = parse(jsonString)
json.extract[Operation] match {
case op: Operation => {
op.httpMethod should be ("GET")
op.summary should be ("the summary")
op.notes should be ("the notes")
op.responseClass should be ("string")
op.nickname should be ("getMeSomeStrings")
op.parameters.size should be (1)
}
case _ => fail("wrong type returned, should be Operation")
}
}
it should "serialize an operation" in {
val op = Operation(
"get",
"the summary",
"the notes",
"string",
"getMeSomeStrings",
List(Parameter("id", "the id", "-1", false, true, "string", AllowableListValues(List("a","b","c")), "query"))
)
write(op) should be ("""{"httpMethod":"get","summary":"the summary","notes":"the notes","responseClass":"string","nickname":"getMeSomeStrings","parameters":[{"name":"id","description":"the id","defaultValue":"-1","required":false,"allowMultiple":true,"dataType":"string","allowableValues":{"valueType":"LIST","values":["a","b","c"]},"paramType":"query"}]}""")
}
}
@RunWith(classOf[JUnitRunner])
class ErrorResponseSerializersTest extends FlatSpec with ShouldMatchers {
implicit val formats = SwaggerSerializers.formats
it should "deserialize an ErrorResponse" in {
val jsonString = """
{
"code":101,
"reason":"the reason"
}
"""
val json = parse(jsonString)
json.extract[ErrorResponse] match {
case p: ErrorResponse => {
p.code should be (101)
p.reason should be ("the reason")
}
case _ => fail("wrong type returned, should be ErrorResponse")
}
}
it should "serialize an operation" in {
val l = ErrorResponse(101, "the reason")
write(l) should be ("""{"code":101,"reason":"the reason"}""")
}
}
@RunWith(classOf[JUnitRunner])
class ParameterSerializersTest extends FlatSpec with ShouldMatchers {
implicit val formats = SwaggerSerializers.formats
it should "deserialize a parameter" in {
val jsonString = """
{
"name":"name",
"description":"description",
"defaultValue":"tony",
"required":false,
"allowMultiple":true,
"dataType":"string",
"paramType":"query"
}
"""
val json = parse(jsonString)
json.extract[Parameter] match {
case p: Parameter => {
p.name should be ("name")
p.description should be ("description")
p.defaultValue should be ("tony")
p.required should be (false)
p.allowMultiple should be (true)
p.dataType should be ("string")
p.paramType should be ("query")
}
case _ => fail("wrong type returned, should be Parameter")
}
}
it should "serialize a parameter" in {
val l = Parameter("name", "description", "tony", false, true, "string", Any, "query")
write(l) should be ("""{"name":"name","description":"description","defaultValue":"tony","required":false,"allowMultiple":true,"dataType":"string","paramType":"query"}""")
}
}
@RunWith(classOf[JUnitRunner])
class ModelSerializationTest extends FlatSpec with ShouldMatchers {
implicit val formats = SwaggerSerializers.formats
it should "deserialize a model" in {
val jsonString = """
{
"id":"Foo",
"name":"Bar",
"properties": {
"id": {
"type":"string",
"required":true,
"description":"id"
},
"name": {
"type":"string",
"required":false,
"description":"name"
}
},
"description":"nice model"
}
"""
val json = parse(jsonString)
json.extract[Model] match {
case model: Model => {
model.id should be ("Foo")
model.name should be ("Bar")
model.properties should not be (null)
model.properties.size should be (2)
model.description should be (Some("nice model"))
model.properties.asScala("id") match {
case e: ModelProperty => {
e.`type` should be ("string")
e.required should be (true)
e.description should be (Some("id"))
}
case _ => fail("missing property id")
}
model.properties.asScala("name") match {
case e: ModelProperty => {
e.`type` should be ("string")
e.required should be (false)
e.description should be (Some("name"))
}
case _ => fail("missing property name")
}
}
case _ => fail("expected type Model")
}
}
it should "serialize a model" in {
val ref = Model("Foo", "Bar", (Map("s" -> ModelProperty("string", true, Some("a string")))).asJava)
write(ref) should be ("""{"id":"Foo","name":"Bar","properties":{"s":{"type":"string","required":true,"description":"a string"}}}""")
}
}
@RunWith(classOf[JUnitRunner]) @RunWith(classOf[JUnitRunner])
class ModelRefSerializationTest extends FlatSpec with ShouldMatchers { class ModelRefSerializationTest extends FlatSpec with ShouldMatchers {
implicit val formats = DefaultFormats + new ModelRefSerializer implicit val formats = SwaggerSerializers.formats
it should "deserialize a model ref" in { it should "deserialize a model ref" in {
val jsonString = """ val jsonString = """
@ -39,7 +397,7 @@ class ModelRefSerializationTest extends FlatSpec with ShouldMatchers {
@RunWith(classOf[JUnitRunner]) @RunWith(classOf[JUnitRunner])
class ModelPropertySerializationTest extends FlatSpec with ShouldMatchers { class ModelPropertySerializationTest extends FlatSpec with ShouldMatchers {
implicit val formats = DefaultFormats + new AllowableValuesSerializer + new ModelPropertySerializer + new ModelRefSerializer implicit val formats = SwaggerSerializers.formats
it should "deserialize a model property with allowable values and ref" in { it should "deserialize a model property with allowable values and ref" in {
val jsonString = """ val jsonString = """
@ -120,19 +478,15 @@ class ModelPropertySerializationTest extends FlatSpec with ShouldMatchers {
val jsonString = """ val jsonString = """
{ {
"type":"string", "type":"string",
"required":false, "required":true,
"description":"nice", "description":"nice"
"allowableValues": {
"valueType":"LIST",
"values":["1","2","3"]
}
} }
""" """
val json = parse(jsonString) val json = parse(jsonString)
json.extract[ModelProperty] match { json.extract[ModelProperty] match {
case p: ModelProperty => { case p: ModelProperty => {
p.`type` should be ("string") p.`type` should be ("string")
p.required should be (false) p.required should be (true)
p.description should be (Some("nice")) p.description should be (Some("nice"))
} }
case _ => fail("expected type ModelProperty") case _ => fail("expected type ModelProperty")
@ -147,7 +501,7 @@ class ModelPropertySerializationTest extends FlatSpec with ShouldMatchers {
@RunWith(classOf[JUnitRunner]) @RunWith(classOf[JUnitRunner])
class AllowableValuesSerializersTest extends FlatSpec with ShouldMatchers { class AllowableValuesSerializersTest extends FlatSpec with ShouldMatchers {
implicit val formats = DefaultFormats + new AllowableValuesSerializer implicit val formats = SwaggerSerializers.formats
it should "deserialize allowable value list" in { it should "deserialize allowable value list" in {
val allowableValuesListString = """ val allowableValuesListString = """