Skip to content

Commit d32b697

Browse files
author
Shu Zhang
committed
Improve the support for Map[,]
1 parent 3760e9e commit d32b697

File tree

5 files changed

+95
-167
lines changed

5 files changed

+95
-167
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.wordnik.swagger.converter
2+
3+
import com.wordnik.swagger.model._
4+
import com.wordnik.swagger.core.util.ClassWrapper
5+
import scala.collection.mutable
6+
7+
class MapConverter extends ModelConverter with BaseConverter {
8+
9+
def read(cls: ClassWrapper, typeMap: Map[String, String]): Option[Model] = {
10+
if (cls.getRawClass.getSimpleName == "Map") {
11+
Some(Model(
12+
toName(cls),
13+
toName(cls),
14+
cls.getName,
15+
new mutable.LinkedHashMap[String, ModelProperty]()
16+
))
17+
} else {
18+
None
19+
}
20+
}
21+
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ object ModelConverters {
1212
private val LOGGER = LoggerFactory.getLogger(ModelConverters.getClass)
1313

1414
val converters = new ListBuffer[ModelConverter]() ++ List(
15+
new MapConverter,
1516
new JodaDateTimeConverter,
1617
new SwaggerSchemaConverter
1718
)

modules/swagger-core/src/main/scala/com/wordnik/swagger/core/SwaggerContext.scala

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ object SwaggerContext {
2424
case "List" => classOf[List[_]]
2525
case "Array" => classOf[Array[_]]
2626
case "Set" => classOf[Set[_]]
27+
case "Map" => classOf[Map[_,_]]
2728
case name => Class.forName(name, true, classLoader)
2829
})
2930
} catch {

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

+71-166
Original file line numberDiff line numberDiff line change
@@ -58,45 +58,57 @@ object SwaggerSerializers extends Serializers {
5858
)
5959
}, {
6060
case x: Model =>
61-
implicit val fmts = formats
62-
val required: List[String] = (for((name, prop) <- x.properties) yield {
63-
if(prop.required) Some(name)
64-
else None
65-
}).flatten.toList
66-
("id" -> x.id) ~
67-
("description" -> x.description) ~
68-
("required" -> (required.size match {
69-
case 0 => JNothing
70-
case _ => Extraction.decompose(required)
71-
})) ~
72-
("extends" -> {
73-
x.baseModel match {
74-
case Some(e) if(e != "void" && e != "java.lang.Void") => Extraction.decompose(e)
75-
case _ =>JNothing
61+
val ContainerMatcher = "(List|Array|Set)\\[(.*)\\].*?".r
62+
val MapMatcher = "Map\\[([^\\],]*),\\s*([^\\],]*)\\].*?".r
63+
x.name match {
64+
case ContainerMatcher(container, value) => {
65+
("id" -> x.id) ~ toJsonSchemaContainer(value)
7666
}
77-
}) ~
78-
("discriminator" -> {
79-
x.discriminator match {
80-
case Some(e) if (e.trim != "" && e.trim != "void") => Extraction.decompose(e)
81-
case _ => JNothing
67+
case MapMatcher(key, value) => {
68+
("id" -> x.id) ~ toJsonSchemaMap(value)
8269
}
83-
}) ~
84-
("properties" -> {
85-
x.properties match {
86-
case e: LinkedHashMap[String, ModelProperty] => {
87-
(for((key, value) <- e) yield (key -> Extraction.decompose(value))).toList
88-
}
89-
case _ => List.empty
90-
}
91-
}) ~
92-
("subTypes" -> {
93-
x.subTypes match {
94-
case e: List[String] if (e.size > 0) => {
95-
Extraction.decompose(for(m <- e) yield ModelUtil.cleanDataType(m))
96-
}
97-
case _ => JNothing
70+
case _ => {
71+
implicit val fmts = formats
72+
val required: List[String] = (for((name, prop) <- x.properties) yield {
73+
if(prop.required) Some(name)
74+
else None
75+
}).flatten.toList
76+
("id" -> x.id) ~
77+
("description" -> x.description) ~
78+
("required" -> (required.size match {
79+
case 0 => JNothing
80+
case _ => Extraction.decompose(required)
81+
})) ~
82+
("extends" -> {
83+
x.baseModel match {
84+
case Some(e) if(e != "void" && e != "java.lang.Void") => Extraction.decompose(e)
85+
case _ =>JNothing
86+
}
87+
}) ~
88+
("discriminator" -> {
89+
x.discriminator match {
90+
case Some(e) if (e.trim != "" && e.trim != "void") => Extraction.decompose(e)
91+
case _ => JNothing
92+
}
93+
}) ~
94+
("properties" -> {
95+
x.properties match {
96+
case e: LinkedHashMap[String, ModelProperty] => {
97+
(for((key, value) <- e) yield (key -> Extraction.decompose(value))).toList
98+
}
99+
case _ => List.empty
100+
}
101+
}) ~
102+
("subTypes" -> {
103+
x.subTypes match {
104+
case e: List[String] if (e.size > 0) => {
105+
Extraction.decompose(for(m <- e) yield ModelUtil.cleanDataType(m))
106+
}
107+
case _ => JNothing
108+
}
109+
})
98110
}
99-
})
111+
}
100112
}
101113
))
102114

@@ -115,6 +127,7 @@ object SwaggerSerializers extends Serializers {
115127
case "date-time" => (name -> "string") ~ ("format" -> "date-time")
116128
case _ => {
117129
val ContainerMatcher = "(List|Array|Set)\\[(.*)\\].*?".r
130+
val MapMatcher = "Map\\[([^\\],]*),\\s*([^\\],]*)\\].*?".r
118131
`type` match {
119132
case ContainerMatcher(container, value) =>
120133
toJsonSchemaContainer(container) ~ {
@@ -123,6 +136,8 @@ object SwaggerSerializers extends Serializers {
123136
else
124137
toJsonSchema("$ref", value)})
125138
}
139+
case MapMatcher(key, value) =>
140+
toJsonSchemaMap(value)
126141
case _ => (name -> `type`) ~ ("format" -> JNothing)
127142
}
128143
}
@@ -138,6 +153,16 @@ object SwaggerSerializers extends Serializers {
138153
}
139154
}
140155

156+
def toJsonSchemaMap(value: String): JObject = {
157+
("type" -> "object") ~ ("patternProperties" ->
158+
(".*" -> {
159+
if(isSimpleType(value))
160+
toJsonSchema("type", value)
161+
else
162+
toJsonSchema("$ref", value)
163+
}))
164+
}
165+
141166
def isSimpleType(name: String) = {
142167
Set("int", "long", "float", "double", "string", "byte", "boolean", "Date", "date", "date-time", "array").contains(name)
143168
}
@@ -154,6 +179,16 @@ object SwaggerSerializers extends Serializers {
154179
case "Set" => ("type" -> "array") ~ ("uniqueItems" -> true)
155180
}
156181
}
182+
else if (prop.`type`.startsWith("Map[")) {
183+
val MapMatcher = "Map\\[([^\\],]*),\\s*([^\\],]*)\\].*?".r
184+
prop.`type` match {
185+
case MapMatcher(key, value) => {
186+
toJsonSchemaMap(value)
187+
}
188+
case _ =>
189+
("$ref" -> prop.`type`) ~ ("format" -> JNothing)
190+
}
191+
}
157192
else ("$ref" -> prop.`type`) ~ ("format" -> JNothing)
158193
}
159194

@@ -403,26 +438,6 @@ object SwaggerSerializers extends Serializers {
403438
))
404439
}
405440

406-
object SwaggerJsonSchemaSerializers extends Serializers {
407-
// object SwaggerSerializers extends Serializers {
408-
implicit val formats = DefaultFormats +
409-
new ModelSerializer +
410-
new ModelPropertySerializer +
411-
new ModelRefSerializer +
412-
new AllowableValuesSerializer +
413-
new ParameterSerializer +
414-
new OperationSerializer +
415-
new ResponseMessageSerializer +
416-
new SampleSerializer +
417-
new ApiDescriptionSerializer +
418-
new ApiListingReferenceSerializer +
419-
new ResourceListingSerializer +
420-
new ApiInfoSerializer +
421-
new ApiListingSerializer +
422-
new AuthorizationTypeSerializer +
423-
new AuthorizationSerializer
424-
}
425-
426441
trait Serializers {
427442
import ValidationMessage._
428443
val validationMessages = ListBuffer.empty[ValidationMessage]
@@ -861,116 +876,6 @@ trait Serializers {
861876
}
862877
))
863878

864-
class ModelSerializer extends CustomSerializer[Model](formats => ({
865-
case json =>
866-
implicit val fmts: Formats = formats
867-
val output = new LinkedHashMap[String, ModelProperty]
868-
val properties = (json \ "properties") match {
869-
case JObject(entries) => {
870-
entries.map({
871-
case (key, value) => output += key -> value.extract[ModelProperty]
872-
})
873-
}
874-
case _ =>
875-
}
876-
877-
Model(
878-
(json \ "id").extractOrElse({
879-
!!(json, MODEL, "id", "missing required field", ERROR)
880-
""
881-
}),
882-
(json \ "name").extractOrElse((json \ "id").extract[String]),
883-
(json \ "qualifiedType").extractOrElse(""),
884-
output,
885-
(json \ "description").extractOpt[String],
886-
(json \ "extends").extractOpt[String],
887-
(json \ "discriminator").extractOpt[String]
888-
)
889-
}, {
890-
case x: Model =>
891-
implicit val fmts = formats
892-
("id" -> x.id) ~
893-
("name" -> x.name) ~
894-
("properties" -> {
895-
x.properties match {
896-
case e: LinkedHashMap[String, ModelProperty] => Extraction.decompose(e.toMap)
897-
case _ => JNothing
898-
}
899-
}) ~
900-
("description" -> x.description) ~
901-
("extends" -> {
902-
x.baseModel match {
903-
case Some(e) if(e != "void") => Extraction.decompose(e)
904-
case _ => JNothing
905-
}
906-
}) ~
907-
("discriminator" -> {
908-
x.discriminator match {
909-
case Some(e) if (e.trim != "" && e.trim != "void") => Extraction.decompose(e)
910-
case _ => JNothing
911-
}
912-
})
913-
}
914-
))
915-
916-
class ModelPropertySerializer extends CustomSerializer[ModelProperty] (formats => ({
917-
case json =>
918-
implicit val fmts: Formats = formats
919-
ModelProperty(
920-
`type` = (json \ "type").extractOrElse(""),
921-
qualifiedType = (json \ "type").extractOrElse(""),
922-
position = (json \ "position").extractOrElse(0),
923-
(json \ "required") match {
924-
case e:JString => e.s.toBoolean
925-
case e:JBool => e.value
926-
case _ => false
927-
},
928-
description = (json \ "description").extractOpt[String],
929-
allowableValues = (json \ "allowableValues").extract[AllowableValues],
930-
items = {
931-
(json \ "items").extractOpt[ModelRef] match {
932-
case Some(e: ModelRef) if(e.`type` != null || e.ref != None) => Some(e)
933-
case _ => None
934-
}
935-
}
936-
)
937-
}, {
938-
case x: ModelProperty =>
939-
implicit val fmts = formats
940-
("type" -> x.`type`) ~
941-
("required" -> x.required) ~
942-
("description" -> x.description) ~
943-
("allowableValues" -> {
944-
x.allowableValues match {
945-
case AnyAllowableValues => JNothing // don't serialize when not a concrete type
946-
case e:AllowableValues => Extraction.decompose(x.allowableValues)
947-
case _ => JNothing
948-
}
949-
}) ~
950-
("items" -> Extraction.decompose(x.items))
951-
}
952-
))
953-
954-
class ModelRefSerializer extends CustomSerializer[ModelRef](formats => ({
955-
case json =>
956-
implicit val fmts: Formats = formats
957-
ModelRef(
958-
(json \ "type").extractOrElse(null: String),
959-
(json \ "$ref").extractOpt[String]
960-
)
961-
}, {
962-
case x: ModelRef =>
963-
implicit val fmts = formats
964-
("type" -> {
965-
x.`type` match {
966-
case e:String => Some(e)
967-
case _ => None
968-
}
969-
}) ~
970-
("$ref" -> x.ref)
971-
}
972-
))
973-
974879
class AuthorizationTypeSerializer extends CustomSerializer[AuthorizationType](formats => ({
975880
case json =>
976881
implicit val fmts: Formats = formats

modules/swagger-jaxrs/src/test/scala/PathParamTargetTest.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class JavaPathParamTargetTest extends FlatSpec with ShouldMatchers {
8989

9090
// verify the 2nd api
9191
val detailsOps = apis.filter(_.path == "/javaPathParamTest/{id}/details").head.operations
92-
val detailOp = detailsOps.head
92+
val detailOp = detailsOps.filter(_.method == "POST").head
9393

9494
detailOp.parameters.size should be (3)
9595

0 commit comments

Comments
 (0)