From 5d67884af6557f358cce0c59c9a04d21061c0afd Mon Sep 17 00:00:00 2001 From: Tony Tam Date: Tue, 2 Dec 2014 21:40:59 -0800 Subject: [PATCH] reapplied #504 which was clobbered in a bad merge --- .../play/modules/swagger/PlayApiReader.scala | 35 ++++-- .../test/PlayApiReaderSpec.scala | 109 ++++++++++-------- .../test/testdata/CatController.scala | 1 + 3 files changed, 87 insertions(+), 58 deletions(-) diff --git a/modules/swagger-play2/app/play/modules/swagger/PlayApiReader.scala b/modules/swagger-play2/app/play/modules/swagger/PlayApiReader.scala index ee40a09510..5b9f5325e5 100644 --- a/modules/swagger-play2/app/play/modules/swagger/PlayApiReader.scala +++ b/modules/swagger-play2/app/play/modules/swagger/PlayApiReader.scala @@ -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)) } @@ -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() @@ -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, @@ -302,6 +310,7 @@ 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, @@ -309,7 +318,7 @@ class PlayApiReader(val routes: Option[Routes]) extends JaxrsApiReader { Option(param.defaultValue).filter(_.trim.nonEmpty), param.required, param.allowMultiple, - param.dataType, + dataType, allowableValues, param.paramType, Option(param.access).filter(_.trim.nonEmpty)) @@ -397,7 +406,7 @@ class PlayApiReader(val routes: Option[Routes]) extends JaxrsApiReader { } } - def routesCache = { + def routesCache = synchronized { if (_routesCache == null) _routesCache = populateRoutesCache _routesCache } @@ -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 { diff --git a/modules/swagger-play2/test/PlayApiReaderSpec.scala b/modules/swagger-play2/test/PlayApiReaderSpec.scala index dc5b79bf21..da0b4a419b 100644 --- a/modules/swagger-play2/test/PlayApiReaderSpec.scala +++ b/modules/swagger-play2/test/PlayApiReaderSpec.scala @@ -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<[^/]+>" @@ -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") } @@ -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") } + } @@ -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)) } @@ -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 } } @@ -402,4 +417,4 @@ class PlayApiReaderSpec extends Specification with Mockito { } -} +} \ No newline at end of file diff --git a/modules/swagger-play2/test/testdata/CatController.scala b/modules/swagger-play2/test/testdata/CatController.scala index 9268ccd402..23fcf89a0f 100644 --- a/modules/swagger-play2/test/testdata/CatController.scala +++ b/modules/swagger-play2/test/testdata/CatController.scala @@ -17,6 +17,7 @@ class CatController extends Controller { @ApiOperation(value = "addCat1", httpMethod = "PUT", + authorizations = Array(), consumes = "", protocols = "") @ApiImplicitParams(Array(