Skip to content
This repository has been archived by the owner on Sep 14, 2022. It is now read-only.

Commit

Permalink
Allow sub-routes
Browse files Browse the repository at this point in the history
  • Loading branch information
François LAROCHE committed Feb 15, 2017
1 parent 5111816 commit a664229
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 109 deletions.
121 changes: 67 additions & 54 deletions play-2.5/swagger-play2/app/play/modules/swagger/SwaggerPlugin.scala
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
/**
* Copyright 2014 Reverb Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
* Copyright 2014 Reverb Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package play.modules.swagger

import java.io.File
import javax.inject.Inject

import io.swagger.config.{FilterFactory, ScannerFactory}
import play.modules.swagger.util.SwaggerContext
import io.swagger.core.filter.SwaggerSpecFilter
import play.api.inject.ApplicationLifecycle
import play.api.{Logger, Application}
import play.api.routing.Router
import scala.concurrent.Future
import scala.collection.JavaConversions._
import play.routes.compiler.{Route => PlayRoute, Include => PlayInclude, RoutesFileParser, StaticPart}
import play.api.{Application, Logger}
import play.modules.swagger.util.SwaggerContext
import play.routes.compiler.{RoutesFileParser, StaticPart, Include => PlayInclude, Route => PlayRoute}

import scala.collection.JavaConversions._
import scala.concurrent.Future
import scala.io.Source

trait SwaggerPlugin
Expand All @@ -54,33 +55,33 @@ class SwaggerPluginImpl @Inject()(lifecycle: ApplicationLifecycle, router: Route

val title = config.getString("swagger.api.info.title") match {
case None => ""
case Some(value)=> value
case Some(value) => value
}

val description = config.getString("swagger.api.info.description") match {
case None => ""
case Some(value)=> value
case Some(value) => value
}

val termsOfServiceUrl = config.getString("swagger.api.info.termsOfServiceUrl") match {
case None => ""
case Some(value)=> value
case Some(value) => value
}

val contact = config.getString("swagger.api.info.contact") match {
case None => ""
case Some(value)=> value
case Some(value) => value
}

val license = config.getString("swagger.api.info.license") match {
case None => ""
case Some(value)=> value
case Some(value) => value
}

val licenseUrl = config.getString("swagger.api.info.licenseUrl") match {
// licenceUrl needs to be a valid URL to validate against schema
case None => "http://licenseUrl"
case Some(value)=> value
case Some(value) => value
}

SwaggerContext.registerClassLoader(app.classloader)
Expand All @@ -106,44 +107,23 @@ class SwaggerPluginImpl @Inject()(lifecycle: ApplicationLifecycle, router: Route
val routes = parseRoutes

def parseRoutes: List[PlayRoute] = {
def playRoutesClassNameToFileName(className: String) = className.replace(".Routes", ".routes")

val routesFile = config.underlying.hasPath("play.http.router") match {
case false => "routes"
case true => config.getString("play.http.router") match {
case None => "routes"
case Some(value)=> playRoutesClassNameToFileName(value)
case Some(value) => SwaggerPluginHelper.playRoutesClassNameToFileName(value)
}
}
//Parses multiple route files recursively
def parseRoutesHelper(routesFile: String, prefix: String): List[PlayRoute] = {
logger.debug(s"Processing route file '$routesFile' with prefix '$prefix'")

val routesContent = Source.fromInputStream(app.classloader.getResourceAsStream(routesFile)).mkString
val parsedRoutes = RoutesFileParser.parseContent(routesContent,new File(routesFile))
val routes = parsedRoutes.right.get.collect {
case (route: PlayRoute) => {
logger.debug(s"Adding route '$route'")
Seq(route.copy(path = route.path.copy(parts = StaticPart(prefix) +: route.path.parts)))
}
case (include: PlayInclude) => {
logger.debug(s"Processing route include $include")
parseRoutesHelper(playRoutesClassNameToFileName(include.router), include.prefix)
}
}.flatten
logger.debug(s"Finished processing route file '$routesFile'")
routes
}
parseRoutesHelper(routesFile, "")

SwaggerPluginHelper.parseRoutes(routesFile, "", logger.debug(_), app.classloader)
}

val routesRules = Map(routes map
{ route =>
{
val routeName = s"${route.call.packageName}.${route.call.controller}$$.${route.call.method}"
(routeName -> route)
}
} : _*)
val routesRules = Map(routes map { route => {
val routeName = s"${route.call.packageName}.${route.call.controller}$$.${route.call.method}"
(routeName -> route)
}
}: _*)

val route = new RouteWrapper(routesRules)
RouteFactory.setRoute(route)
Expand Down Expand Up @@ -174,3 +154,36 @@ class SwaggerPluginImpl @Inject()(lifecycle: ApplicationLifecycle, router: Route
}

}

object SwaggerPluginHelper {
def playRoutesClassNameToFileName(className: String): String = className.replace(".Routes", ".routes")

//Parses multiple route files recursively
def parseRoutes(routesFile: String, prefix: String, debug: String => Unit, classLoader: ClassLoader): List[PlayRoute] = {
debug(s"Processing route file '$routesFile' with prefix '$prefix'")

val routesContent = Source.fromInputStream(classLoader.getResourceAsStream(routesFile)).mkString
val parsedRoutes = RoutesFileParser.parseContent(routesContent, new File(routesFile))
val routes = parsedRoutes.right.get.collect {
case (route: PlayRoute) =>
debug(s"Adding route '$route'")
(prefix, route.path.parts) match {
case ("", _) => Seq(route)
case (_, Seq()) => Seq(route.copy(path = route.path.copy(parts = StaticPart(prefix) +: route.path.parts)))
case (_, Seq(StaticPart(""))) => Seq(route.copy(path = route.path.copy(parts = StaticPart(prefix) +: route.path.parts)))
case (_, Seq(StaticPart("/"))) => Seq(route.copy(path = route.path.copy(parts = StaticPart(prefix) +: route.path.parts)))
case (_, _) => Seq(route.copy(path = route.path.copy(parts = StaticPart(prefix) +: StaticPart("/") +: route.path.parts)))
}
case (include: PlayInclude) =>
debug(s"Processing route include $include")
val newPrefix = if(prefix == "") {
include.prefix
} else {
s"$prefix/${include.prefix}"
}
parseRoutes(playRoutesClassNameToFileName(include.router), newPrefix, debug, classLoader)
}.flatten
debug(s"Finished processing route file '$routesFile'")
routes
}
}
3 changes: 3 additions & 0 deletions play-2.5/swagger-play2/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,6 @@ pomExtra := {
}

lazy val root = (project in file(".")).enablePlugins(PlayScala)

resourceDirectory in Test := baseDirectory.value / "test-resources"
parallelExecution in Test := false
4 changes: 4 additions & 0 deletions play-2.5/swagger-play2/test-resources/delegated.routes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-> /subdelegated subdelegated.Routes

GET /my/action testdata.DelegatedController.list
GET /all testdata.DelegatedController.list2
1 change: 1 addition & 0 deletions play-2.5/swagger-play2/test-resources/delegation
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-> /api delegated.Routes
3 changes: 3 additions & 0 deletions play-2.5/swagger-play2/test-resources/subdelegated.routes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
GET /my/action testdata.DelegatedController.list3
GET /all testdata.DelegatedController.list4
GET / testdata.DelegatedController.list5
66 changes: 35 additions & 31 deletions play-2.5/swagger-play2/test/PlayApiListingCacheSpec.scala
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import java.io.File

import io.swagger.config.ScannerFactory
import io.swagger.models.{ModelImpl, HttpMethod}
import io.swagger.models.parameters.{QueryParameter, BodyParameter, PathParameter}
import io.swagger.models.properties.{RefProperty, ArrayProperty}
import play.modules.swagger._
import org.specs2.mutable._
import io.swagger.models.parameters.{BodyParameter, PathParameter, QueryParameter}
import io.swagger.models.properties.{ArrayProperty, RefProperty}
import io.swagger.models.{HttpMethod, ModelImpl}
import io.swagger.util.Json
import org.specs2.mock.Mockito
import org.specs2.mutable._
import org.specs2.specification.BeforeAfterAll
import play.api.Logger
import io.swagger.util.Json
import play.modules.swagger._
import play.routes.compiler.{Route => PlayRoute}

import scala.collection.JavaConversions._
import scala.collection.JavaConverters._
import play.routes.compiler.{ Route => PlayRoute }

class PlayApiListingCacheSpec extends Specification with Mockito {
class PlayApiListingCacheSpec extends Specification with Mockito with BeforeAfterAll {

override def afterAll(): Unit = {}

// set up mock for Play Router
val routesList = {
play.routes.compiler.RoutesFileParser.parseContent("""
val routesList: List[PlayRoute] = {
play.routes.compiler.RoutesFileParser.parseContent(
"""
POST /api/document/:settlementId/files/:fileId/accept testdata.DocumentController.accept(settlementId:String,fileId:String)
GET /api/search testdata.SettlementsSearcherController.search(personalNumber:String,propertyId:String)
GET /api/pointsofinterest testdata.PointOfInterestController.list(eastingMin:Double,northingMin:Double,eastingMax:Double,northingMax:Double)
Expand All @@ -36,19 +41,17 @@ PUT /api/dog/:id testdata.DogController.add0(id:String)
}
}

val routesRules = Map(routesList map
{ route =>
{
val routeName = s"${route.call.packageName}.${route.call.controller}$$.${route.call.method}"
routeName -> route
}
} : _*)
val routesRules = Map(routesList map { route => {
val routeName = s"${route.call.packageName}.${route.call.controller}$$.${route.call.method}"
routeName -> route
}
}: _*)


val apiVersion = "test1"
val basePath = "/api"

var swaggerConfig = new PlaySwaggerConfig()

val swaggerConfig = new PlaySwaggerConfig()
swaggerConfig setDescription "description"
swaggerConfig setBasePath basePath
swaggerConfig setContact "contact"
Expand All @@ -59,12 +62,13 @@ PUT /api/dog/:id testdata.DogController.add0(id:String)
swaggerConfig setLicense "license"
swaggerConfig setLicenseUrl "http://licenseUrl"

PlayConfigFactory.setConfig(swaggerConfig)
override def beforeAll(): Unit = {
ApiListingCache.cache = None
PlayConfigFactory.setConfig(swaggerConfig)
ScannerFactory.setScanner(new PlayApiScanner())
RouteFactory.setRoute(new RouteWrapper(routesRules))
}

var scanner = new PlayApiScanner()
ScannerFactory.setScanner(scanner)
val route = new RouteWrapper(routesRules)
RouteFactory.setRoute(route)

"ApiListingCache" should {

Expand All @@ -73,7 +77,7 @@ PUT /api/dog/:id testdata.DogController.add0(id:String)
val docRoot = ""
val swagger = ApiListingCache.listing(docRoot, "127.0.0.1")

Logger.debug ("swagger: " + toJsonString(swagger))
Logger.debug("swagger: " + toJsonString(swagger))
swagger must beSome

swagger must beSome
Expand Down Expand Up @@ -157,18 +161,18 @@ PUT /api/dog/:id testdata.DogController.add0(id:String)
val opDogGet = pathDog.getOperationMap.get(HttpMethod.GET)
opDogGet.getOperationId must beEqualTo("listDogs")
opDogGet.getParameters must beEmpty
opDogGet.getConsumes.asScala.toList must beEqualTo(List("application/json","application/xml"))
opDogGet.getConsumes.asScala.toList must beEqualTo(List("application/json", "application/xml"))
opDogGet.getResponses.get("200").getSchema.asInstanceOf[ArrayProperty].getItems.asInstanceOf[RefProperty].getSimpleRef must beEqualTo("Dog")
opDogGet.getProduces.asScala.toList must beEqualTo(List("application/json","application/xml"))
opDogGet.getProduces.asScala.toList must beEqualTo(List("application/json", "application/xml"))

val opDogPut = pathDog.getOperationMap.get(HttpMethod.PUT)
opDogPut.getOperationId must beEqualTo("add1")
opDogPut.getParameters.head.getName must beEqualTo("dog")
opDogPut.getParameters.head.getIn must beEqualTo("body")
opDogPut.getParameters.head.asInstanceOf[BodyParameter].getSchema.getReference must beEqualTo("#/definitions/Dog")
opDogPut.getConsumes.asScala.toList must beEqualTo(List("application/json","application/xml"))
opDogPut.getConsumes.asScala.toList must beEqualTo(List("application/json", "application/xml"))
opDogPut.getResponses.get("200").getSchema.asInstanceOf[RefProperty].getSimpleRef must beEqualTo("ActionAnyContent")
opDogPut.getProduces.asScala.toList must beEqualTo(List("application/json","application/xml"))
opDogPut.getProduces.asScala.toList must beEqualTo(List("application/json", "application/xml"))

val pathDogParam = swagger.get.getPaths.get("/dog/{id}")
pathDogParam.getOperations.size must beEqualTo(1)
Expand All @@ -178,8 +182,8 @@ PUT /api/dog/:id testdata.DogController.add0(id:String)
opDogParamPut.getParameters.head.getName must beEqualTo("id")
opDogParamPut.getParameters.head.getIn must beEqualTo("path")
opDogParamPut.getParameters.head.asInstanceOf[PathParameter].getType must beEqualTo("string")
opDogParamPut.getConsumes.asScala.toList must beEqualTo(List("application/json","application/xml"))
opDogParamPut.getProduces.asScala.toList must beEqualTo(List("application/json","application/xml"))
opDogParamPut.getConsumes.asScala.toList must beEqualTo(List("application/json", "application/xml"))
opDogParamPut.getProduces.asScala.toList must beEqualTo(List("application/json", "application/xml"))
opDogParamPut.getResponses.get("200").getSchema.asInstanceOf[RefProperty].getSimpleRef must beEqualTo("ActionAnyContent")

val catDef = swagger.get.getDefinitions.get("Cat").asInstanceOf[ModelImpl]
Expand Down
Loading

0 comments on commit a664229

Please sign in to comment.