Skip to content

Commit

Permalink
reapplied #504 which was clobbered in a bad merge
Browse files Browse the repository at this point in the history
  • Loading branch information
fehguy committed Dec 3, 2014
1 parent 28f2ac6 commit 5d67884
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 58 deletions.
35 changes: 24 additions & 11 deletions modules/swagger-play2/app/play/modules/swagger/PlayApiReader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -185,18 +185,18 @@ class PlayApiReader(val routes: Option[Routes]) extends JaxrsApiReader {
// only process methods with @ApiOperation annotations
if (method.getAnnotation(classOf[ApiOperation]) != null) {
Logger("swagger").debug("ApiOperation: found on method: %s".format(method.getName))
val operation = readMethod(method).get
val fullOperationResourcePath = getPath(cls, method)

fullOperationResourcePath match {
case Some(path) =>
// got to remove any path element specified in basepath
val fullMethodName = getFullMethodName(cls, method)
routesCache.get(fullMethodName) match {
case Some(RouteEntry(httpMethod, path)) => {
// got to remove any path element specified in basepath
val operation = readMethod(method, fullMethodName).get
val basepathUrl = new java.net.URL(config.getBasePath)
val basepath = basepathUrl.getPath
val resourcePath = path.stripPrefix(basepath)
Logger("swagger").debug("method: %s, fullOperationResourcePath: %s, basepath: %s, resourcePath: %s".format(method.getName, path, basepath, resourcePath))
// store operations in our Map keyed by resourcepath
operationsMap = appendOperation(resourcePath, operation, operationsMap)
}
case _ =>
Logger("swagger").debug("Method: %s has no web route defined".format(method.getName))
}
Expand Down Expand Up @@ -233,12 +233,20 @@ class PlayApiReader(val routes: Option[Routes]) extends JaxrsApiReader {
}
}

def readMethod(method: Method): Option[Operation] = {
def readMethod(method: Method, fullMethodName: String): Option[Operation] = {
val apiOperation = method.getAnnotation(classOf[ApiOperation])
val routeEntry = routesCache.get(fullMethodName)

if (method.getAnnotation(classOf[ApiOperation]) != null) {
Logger("swagger").debug("annotation: ApiOperation: %s,".format(apiOperation.toString))

val httpMethod = routeEntry.map(_.httpMethod).getOrElse(apiOperation.httpMethod)

val nickname = apiOperation.nickname match {
case e: String if e.trim != "" => e
case _ => genNickname(fullMethodName, routeEntry.map(_.httpMethod))
}

val produces = apiOperation.produces match {
case e: String if e.trim != "" => e.split(",").map(_.trim).toList
case _ => List()
Expand Down Expand Up @@ -278,11 +286,11 @@ class PlayApiReader(val routes: Option[Routes]) extends JaxrsApiReader {
val params = processParams(method)

Some(Operation(
apiOperation.httpMethod,
httpMethod,
apiOperation.value,
apiOperation.notes,
responseClass,
apiOperation.nickname,
nickname,
apiOperation.position,
produces,
consumes,
Expand All @@ -302,14 +310,15 @@ class PlayApiReader(val routes: Option[Routes]) extends JaxrsApiReader {
case Some(e) =>
(for (param <- e.value) yield {
Logger("swagger").debug("processing " + param)
val dataType = if (param.dataType.isEmpty) "String" else param.dataType
val allowableValues = toAllowableValues(param.allowableValues)
Parameter(
param.name,
Option(readString(param.value)),
Option(param.defaultValue).filter(_.trim.nonEmpty),
param.required,
param.allowMultiple,
param.dataType,
dataType,
allowableValues,
param.paramType,
Option(param.access).filter(_.trim.nonEmpty))
Expand Down Expand Up @@ -397,7 +406,7 @@ class PlayApiReader(val routes: Option[Routes]) extends JaxrsApiReader {
}
}

def routesCache = {
def routesCache = synchronized {
if (_routesCache == null) _routesCache = populateRoutesCache
_routesCache
}
Expand All @@ -424,6 +433,10 @@ class PlayApiReader(val routes: Option[Routes]) extends JaxrsApiReader {
}
}

def genNickname(fullMethodName: String, httpMethod: Option[String] = None): String = {
httpMethod.getOrElse("") + "_" + fullMethodName.replace(".", "_")
}

private def populateRoutesCache: Map[String, RouteEntry] = {
val r = routes.get.documentation
(for (route <- r) yield {
Expand Down
109 changes: 62 additions & 47 deletions modules/swagger-play2/test/PlayApiReaderSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import org.mockito.Mockito._


class PlayApiReaderSpec extends Specification with Mockito {

"PlayApiReader.SwaggerUtils" should {
"convert a simple play route comment" in {
val path = "/pet.json/$id<[^/]+>/test/$nothing<[^/]+>"
Expand Down Expand Up @@ -81,8 +82,15 @@ class PlayApiReaderSpec extends Specification with Mockito {
}
}

def readDogMethod(name: String) = {
val method = dogMethod(name).get
val fullMethodName = reader.getFullMethodName(dogControllerClass, method)
reader.readMethod(method, fullMethodName)
}


"with Object as Controller" should {

"get full name for method" in {
reader.getFullMethodName(dogControllerClass, dogMethod("add1").get) must beEqualTo("test.testdata.DogController$.add1")
}
Expand Down Expand Up @@ -117,6 +125,7 @@ class PlayApiReaderSpec extends Specification with Mockito {
"get request path for a method that has path params" in {
reader.getPath(catControllerClass, catControllerClass.getMethod("get1", classOf[Long])).getOrElse("") must beEqualTo("/api/cat/:id")
}

}


Expand Down Expand Up @@ -154,196 +163,200 @@ class PlayApiReaderSpec extends Specification with Mockito {
}

"readMethod with Object as controller" should {

"create Operation for annotated method" in {
val maybeOperation: Option[Operation] = reader.readMethod(dogMethod("list").get)
val maybeOperation: Option[Operation] = readDogMethod("list")
maybeOperation.nonEmpty must beTrue
}

"does not creates Operation when no annotation" in {
val maybeOperation: Option[Operation] = reader.readMethod(dogMethod("no_annotations").get)
val maybeOperation: Option[Operation] = readDogMethod("no_annotations")
"an operation should not have been returned" ! maybeOperation.isEmpty
}

"adds empty 'consumes' when not defined" in {
val operation: Operation = reader.readMethod(dogMethod("add1").get).get
val operation: Operation = readDogMethod("add1").get
operation.consumes must beEqualTo(List.empty)
}

"adds 'consumes' when defined and trims" in {
val operation: Operation = reader.readMethod(dogMethod("add2").get).get
val operation: Operation = readDogMethod("add2").get
operation.consumes.length must beEqualTo(1)
operation.consumes.head must beEqualTo("application/json")
}

"adds mulitple 'consumes' when defined and trims" in {
val operation: Operation = reader.readMethod(dogMethod("add3").get).get
"adds multiple 'consumes' when defined and trims" in {
val operation: Operation = readDogMethod("add3").get
operation.consumes.length must beEqualTo(2)
operation.consumes.contains("application/json") must beTrue
operation.consumes.contains("text/yaml") must beTrue
}

"adds empty 'authorizations' when not defined" in {
val operation: Operation = reader.readMethod(dogMethod("add1").get).get
println(operation.authorizations)
operation.authorizations must beEqualTo(List.empty)
val operation: Operation = readDogMethod("add1").get
operation.authorizations must beEqualTo(Nil)
}

"adds 'authorizations' when defined" in {
val operation: Operation = reader.readMethod(dogMethod("add2").get).get
val operation: Operation = readDogMethod("add2").get
operation.authorizations.length must beEqualTo(1)
operation.authorizations.head.`type` must beEqualTo("oauth2")
}

"adds multiple 'authorizations' when defined" in {
val operation: Operation = reader.readMethod(dogMethod("add3").get).get
val operation: Operation = readDogMethod("add3").get
operation.authorizations.length must beEqualTo(2)
val authNames = (for(auth <- operation.authorizations) yield auth.`type`).toSet
authNames.contains("oauth2") must beTrue
authNames.contains("api_key") must beTrue
operation.authorizations.map(_.`type`).contains("oauth2") must beTrue
}

"adds empty 'produces' when not defined" in {
val operation: Operation = reader.readMethod(get1Method).get
val fullMethodName = reader.getFullMethodName(dogControllerClass, get1Method)
val operation: Operation = reader.readMethod(get1Method, fullMethodName).get
operation.produces must beEqualTo(List.empty)
}

"adds 'produces' when defined and trims" in {
val operation: Operation = reader.readMethod(get2Method).get
val fullMethodName = reader.getFullMethodName(dogControllerClass, get2Method)
val operation: Operation = reader.readMethod(get2Method, fullMethodName).get
operation.produces.length must beEqualTo(1)
operation.produces.head must beEqualTo("application/json")
}

"adds mulitple 'produces' when defined and trims" in {
val operation: Operation = reader.readMethod(get3Method).get
"adds multiple 'produces' when defined and trims" in {
val fullMethodName = reader.getFullMethodName(dogControllerClass, get3Method)
val operation: Operation = reader.readMethod(get3Method, fullMethodName).get
operation.produces.length must beEqualTo(2)
operation.produces.contains("application/json") must beTrue
operation.produces.contains("application/xml") must beTrue
}

"adds 'protocols' when defined and trims" in {
val operation: Operation = reader.readMethod(dogMethod("add2").get).get
val operation: Operation = readDogMethod("add2").get
operation.protocols.length must beEqualTo(1)
operation.protocols.head must beEqualTo("http")
}

"adds mulitple 'protocols' when defined and trims" in {
val operation: Operation = reader.readMethod(dogMethod("add3").get).get
"adds multiple 'protocols' when defined and trims" in {
val operation: Operation = readDogMethod("add3").get
operation.protocols.length must beEqualTo(2)
operation.protocols.contains("http") must beTrue
operation.protocols.contains("https") must beTrue
}

"adds empty 'protocols' when not defined" in {
val operation: Operation = reader.readMethod(dogMethod("add1").get).get
val operation: Operation = readDogMethod("add1").get
operation.protocols must beEqualTo(List.empty)
}

"adds 'deprecated' when defined" in {
val operation: Operation = reader.readMethod(dogMethod("deprecated").get).get
val operation: Operation = readDogMethod("deprecated").get
operation.deprecated must beEqualTo(Some("true"))
}

"does not add 'deprecated' when not defined" in {
val operation: Operation = reader.readMethod(dogMethod("add1").get).get
val operation: Operation = readDogMethod("add1").get
operation.deprecated must beEqualTo(None)
}

"adds 'method' when defined" in {
val operation: Operation = reader.readMethod(dogMethod("add1").get).get
val operation: Operation = readDogMethod("add1").get
operation.method must beEqualTo("PUT")
}

"adds any-string for 'method' when defined" in {
val operation: Operation = reader.readMethod(dogMethod("unknown_method").get).get
val operation: Operation = readDogMethod("unknown_method").get
operation.method must beEqualTo("UNKNOWN")
}

"results in empty String for 'method' when not defined" in {
val operation: Operation = reader.readMethod(dogMethod("undefined_method").get).get
val operation: Operation = readDogMethod("undefined_method").get
operation.method must beEqualTo("")
}

"results in empty String for 'nickname' when not defined" in {
val operation: Operation = reader.readMethod(dogMethod("add1").get).get
operation.nickname must beEqualTo("")
"results in httpMethod + fullMethodName for 'nickname' when not defined" in {
val operation: Operation = readDogMethod("add1").get
val fullMethodName = reader.getFullMethodName(dogControllerClass, dogMethod("add1").get)
operation.nickname.toLowerCase must beEqualTo(reader.genNickname(fullMethodName, Some("PUT")).toLowerCase)
}

"adds 'nickname' when defined" in {
val operation: Operation = reader.readMethod(dogMethod("add2").get).get
val operation: Operation = readDogMethod("add2").get
operation.nickname must beEqualTo("addDog2_nickname")
}

"results in empty String for 'notes' when not defined" in {
val operation: Operation = reader.readMethod(dogMethod("add1").get).get
val operation: Operation = readDogMethod("add1").get
operation.notes must beEqualTo("")
}

"adds 'notes' when defined" in {
val operation: Operation = reader.readMethod(dogMethod("add2").get).get
val operation: Operation = readDogMethod("add2").get
operation.notes must beEqualTo("Adds a dogs better")
}


"adds 'summary' when defined in 'value'" in {
val operation: Operation = reader.readMethod(dogMethod("add2").get).get
val operation: Operation = readDogMethod("add2").get
operation.summary must beEqualTo("addDog2")
}

"defaults to 0 for 'position' when not defined" in {
val operation: Operation = reader.readMethod(dogMethod("add1").get).get
val operation: Operation = readDogMethod("add1").get
operation.position must beEqualTo(0)
}

"adds 'position' when defined" in {
val operation: Operation = reader.readMethod(dogMethod("add2").get).get
val operation: Operation = readDogMethod("add2").get
operation.position must beEqualTo(2)
}

"results in 0 'responseMessages' when not defined" in {
val operation: Operation = reader.readMethod(dogMethod("add1").get).get
val operation: Operation = readDogMethod("add1").get
operation.responseMessages.length must beEqualTo(0)
}

"results in 0 'responseMessages' when defined as empty array" in {
val operation: Operation = reader.readMethod(dogMethod("add2").get).get
val operation: Operation = readDogMethod("add2").get
operation.responseMessages.length must beEqualTo(0)
}

"adds multiple 'responseMessages' when defined" in {
val operation: Operation = reader.readMethod(dogMethod("add3").get).get
val operation: Operation = readDogMethod("add3").get
operation.responseMessages.length must beEqualTo(2)
operation.responseMessages.filter(responseMessage => responseMessage.code == 405).head.message must beEqualTo("Invalid input")
operation.responseMessages.filter(responseMessage => responseMessage.code == 666).head.message must beEqualTo("Big Problem")
}

"returns 'void' for 'responseClass' when not defined" in {
val operation: Operation = reader.readMethod(dogMethod("add1").get).get
val operation: Operation = readDogMethod("add1").get
operation.responseClass must beEqualTo("java.lang.Void")
}

"adds 'responseClass' when defined" in {
val operation: Operation = reader.readMethod(get1Method).get
val fullMethodName = reader.getFullMethodName(dogControllerClass, get1Method)
val operation: Operation = reader.readMethod(get1Method, fullMethodName).get
operation.responseClass must beEqualTo("test.testdata.Dog")
}

"adds 'responseClass' correctly when defined as list" in {
val operation = reader.readMethod(dogMethod("list").get).get
val operation = readDogMethod("list").get
operation.responseClass must beEqualTo("List[test.testdata.Dog]")
}

"results in 0 'parameters' when not defined" in {
val operation: Operation = reader.readMethod(dogMethod("list").get).get
val operation: Operation = readDogMethod("list").get
operation.parameters must beEqualTo(List.empty)
}

"adds 'parameters' when defined by @ApiParam" in {
val operation: Operation = reader.readMethod(get1Method).get
val fullMethodName = reader.getFullMethodName(dogControllerClass, get1Method)
val operation: Operation = reader.readMethod(get1Method, fullMethodName).get
operation.parameters.length must beEqualTo(1)
operation.parameters.head must beEqualTo(Parameter("dogId", Some("ID of dog to fetch"), None, true, false, "long", null, "path", None))
}

"adds 'parameters' when defined by @ApiImplicitParams" in {
val operation: Operation = reader.readMethod(dogMethod("add1").get).get
val operation: Operation = readDogMethod("add1").get
operation.parameters.length must beEqualTo(1)
operation.parameters.head must beEqualTo(Parameter("dog", Some("Dog object to add"), None, true, false, "Dog", null, "body", None))
}
Expand All @@ -353,7 +366,9 @@ class PlayApiReaderSpec extends Specification with Mockito {
"readMethod with Class as controller" should {

"create Operation for annotated method" in {
val maybeOperation: Option[Operation] = reader.readMethod(catMethod("list").get)
val listMethod = catMethod("list").get
val fullMethodName = reader.getFullMethodName(catControllerClass, listMethod)
val maybeOperation: Option[Operation] = reader.readMethod(listMethod, fullMethodName)
maybeOperation.nonEmpty must beTrue
}
}
Expand Down Expand Up @@ -402,4 +417,4 @@ class PlayApiReaderSpec extends Specification with Mockito {
}


}
}
Loading

0 comments on commit 5d67884

Please sign in to comment.