Skip to content

Commit 7a75357

Browse files
committed
Merge pull request #755 from gorgonblot/contrib/develop_scala-2.10/issue751
Better support for Options and Containers
2 parents 649ba55 + 1e6f954 commit 7a75357

File tree

6 files changed

+120
-14
lines changed

6 files changed

+120
-14
lines changed

modules/swagger-core/src/main/scala/com/wordnik/swagger/converter/ModelPropertyParser.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,10 @@ class ModelPropertyParser(cls: Class[_], t: Map[String, String] = Map.empty) (im
206206
else simpleTypeRef
207207
}
208208
simpleName = containerType
209-
if(isComplex(simpleTypeRef)) {
210-
Some(ModelRef(null, Some(simpleTypeRef), Some(basePart)))
209+
if(isComplex(typeRef)) {
210+
Some(ModelRef(null, Some(typeRef), Some(basePart)))
211211
}
212-
else Some(ModelRef(simpleTypeRef, None, Some(basePart)))
212+
else Some(ModelRef(typeRef, None, Some(basePart)))
213213
}
214214
case _ => None
215215
}

modules/swagger-core/src/main/scala/com/wordnik/swagger/model/SwaggerSerializers.scala

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,15 @@ object SwaggerSerializers extends Serializers {
175175
}
176176
}
177177

178+
def toJsonSchemaTypeFromRef(ref: ModelRef): JObject = {
179+
// see if primitive
180+
if(SwaggerSpec.baseTypes.contains(ref.`type`)) {
181+
toJsonSchema("type", ref.`type`)
182+
} else {
183+
("type" -> ref.`type`)
184+
}
185+
}
186+
178187
class JsonSchemaOperationSerializer extends CustomSerializer[Operation](formats => ({
179188
case json =>
180189
implicit val fmts: Formats = formats
@@ -337,23 +346,22 @@ object SwaggerSerializers extends Serializers {
337346
))
338347

339348
class JsonSchemaModelRefSerializer extends CustomSerializer[ModelRef](formats => ({
340-
case json =>
349+
case json => {
341350
implicit val fmts: Formats = formats
342351
ModelRef(
343352
(json \ "type").extractOrElse(null: String),
344353
(json \ "$ref").extractOpt[String]
345354
)
346-
}, {
347-
case x: ModelRef =>
355+
}}, {
356+
case x: ModelRef => {
348357
implicit val fmts = formats
349-
("type" -> {
350-
x.`type` match {
351-
case e:String => Some(e)
352-
case _ => None
353-
}
354-
}) ~
355-
("$ref" -> x.ref)
358+
val output: JObject = x.`type` match {
359+
case e: String => toJsonSchemaTypeFromRef(x)
360+
case _ => ("type" -> None)
361+
}
362+
output ~ ("$ref" -> x.ref)
356363
}
364+
}
357365
))
358366

359367
class JsonSchemaParameterSerializer extends CustomSerializer[Parameter](formats => ({
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package converter
2+
3+
import com.wordnik.swagger.core.SwaggerSpec
4+
import com.wordnik.swagger.core.util.ModelUtil
5+
import converter.models._
6+
import com.wordnik.swagger.model._
7+
import com.wordnik.swagger.converter._
8+
import org.json4s._
9+
import org.json4s.jackson.Serialization.write
10+
import org.json4s.jackson._
11+
12+
import org.junit.runner.RunWith
13+
import org.scalatest.junit.JUnitRunner
14+
import org.scalatest.FlatSpec
15+
import org.scalatest.Matchers
16+
17+
@RunWith(classOf[JUnitRunner])
18+
class BoxedTypesTest extends FlatSpec with Matchers {
19+
implicit val formats = SwaggerSerializers.formats
20+
21+
"ModelConverters" should "format a BoxedType" in {
22+
val model = ModelConverters.read(classOf[BoxedTypesIssue31]).getOrElse(fail("no model found"))
23+
model.properties.size should be(5)
24+
write(model) should be( """{"id":"BoxedTypesIssue31","description":"Options of boxed types produces an Object ref instead of correct type","properties":{"stringSeq":{"type":"array","items":{"type":"string"}},"stringOpt":{"type":"string"},"intSeq":{"type":"array","description":"Integers in a Sequence Box","items":{"$ref":"Object"}},"intOpt":{"$ref":"Object","description":"Integer in an Option Box"},"justInt":{"type":"integer","format":"int32"}}}""")
25+
}
26+
27+
it should "format a BoxedTypeWithDataType provided in the annotation for the boxed object types" in {
28+
val model = ModelConverters.read(classOf[BoxedTypesIssue31WithDataType]).getOrElse(fail("no model found"))
29+
model.properties.size should be(5)
30+
write(model) should be( """{"id":"BoxedTypesIssue31WithDataType","description":"Options of boxed types produces an Object ref instead of correct type, but can be overcome with dataType","properties":{"stringSeq":{"type":"array","items":{"type":"string"}},"stringOpt":{"type":"string"},"intSeq":{"type":"array","description":"Integers in a Sequence Box","items":{"type":"integer","format":"int32"}},"intOpt":{"type":"integer","format":"int32","description":"Integer in an Option Box"},"justInt":{"type":"integer","format":"int32"}}}""")
31+
}
32+
}

modules/swagger-core/src/test/scala/converter/FormatTest.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ class FormatTest extends FlatSpec with Matchers {
3030
val model = ModelConverters.read(classOf[SetModel]).getOrElse(fail("no model found"))
3131
model.properties.size should be (1)
3232

33-
write(model) should be ("""{"id":"SetModel","properties":{"longs":{"type":"array","uniqueItems":true,"items":{"type":"long"}}}}""")
33+
// gbolt; 11/9/2014 - Unclear why the type is expected to be "long"? As far as I understand, long is not one of the
34+
// 7 primitive types defined in JSON Schema. It should be integer with format of int64.
35+
// write(model) should be ("""{"id":"SetModel","properties":{"longs":{"type":"array","uniqueItems":true,"items":{"type":"long"}}}}""")
36+
write(model) should be ("""{"id":"SetModel","properties":{"longs":{"type":"array","uniqueItems":true,"items":{"type":"integer","format":"int64"}}}}""")
37+
3438
}
3539
}
3640

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package converter
2+
3+
import org.junit.runner.RunWith
4+
import org.scalatest.junit.JUnitRunner
5+
import org.scalatest.FlatSpec
6+
import org.scalatest.Matchers
7+
8+
@RunWith(classOf[JUnitRunner])
9+
class ModelUtilTest extends FlatSpec with Matchers {
10+
import com.wordnik.swagger.core.util.ModelUtil._
11+
12+
"ModelUtil cleanDataType" should "convert a fully-qualified primitive type to a SwaggerTypes primitive" in {
13+
val primitiveName = "java.lang.Integer"
14+
val cleanName = cleanDataType(primitiveName)
15+
cleanName should equal ("int")
16+
}
17+
18+
it should "convert a primitive type simple name to a SwaggerTypes primitive" in {
19+
val primitiveName = "Integer"
20+
val cleanName = cleanDataType(primitiveName)
21+
cleanName should equal ("int")
22+
}
23+
24+
it should "convert the inner class of a container to a SwaggerTypes primitive" in {
25+
val origName = "List[java.lang.Integer]"
26+
val cleanName = cleanDataType(origName)
27+
cleanName should equal ("List[int]")
28+
}
29+
30+
it should "return a fully-qualified class name unchanged" in {
31+
val fqcn = "com.wordnik.swagger.core.ModelUtil"
32+
val cleanName = cleanDataType(fqcn)
33+
cleanName should equal (fqcn)
34+
}
35+
36+
it should "return a container with a fully-qualified inner class name unchanged" in {
37+
val fqcn = "List[com.wordnik.swagger.core.ModelUtil]"
38+
val cleanName = cleanDataType(fqcn)
39+
cleanName should equal (fqcn)
40+
}
41+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package converter.models
2+
3+
import com.wordnik.swagger.annotations.{ ApiModel, ApiModelProperty }
4+
5+
import scala.annotation.meta.field
6+
7+
// Issue #31: https://github.com/gettyimages/spray-swagger/issues/31
8+
// It would be nice if the Seq[Int] and Option[Int] could create the proper spec, but due
9+
// to erasure the parameterized types are only identified as Object
10+
@ApiModel(description = "Options of boxed types produces an Object ref instead of correct type")
11+
case class BoxedTypesIssue31(stringSeq: Seq[String], stringOpt: Option[String],
12+
@(ApiModelProperty @field)(value = "Integers in a Sequence Box") intSeq: Seq[Int],
13+
@(ApiModelProperty @field)(value = "Integer in an Option Box") intOpt: Option[Int],
14+
justInt: Int)
15+
16+
// Get around the erasure by providing the dataType explicitly using the dataType common names.
17+
@ApiModel(description = "Options of boxed types produces an Object ref instead of correct type, but can be overcome with dataType")
18+
case class BoxedTypesIssue31WithDataType(stringSeq: Seq[String], stringOpt: Option[String],
19+
@(ApiModelProperty @field)(value = "Integers in a Sequence Box", dataType = "List[int]") intSeq: Seq[Int],
20+
@(ApiModelProperty @field)(value = "Integer in an Option Box", dataType = "int") intOpt: Option[Int],
21+
justInt: Int)

0 commit comments

Comments
 (0)