Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fix // in swagger resource at root path

  • Loading branch information...
commit 54aff9878c09aae211582ad8e76a7cf4d1e6990e 1 parent b784b04
@casualjim casualjim authored
View
4 .gitignore
@@ -29,3 +29,7 @@ target/
*.sublime-*
atlassian-ide-plugin.xml
.lib
+
+.history
+
+.jvmopts
View
6 atmosphere/src/main/scala/org/scalatra/atmosphere/ScalatraAtmosphereHandler.scala
@@ -24,7 +24,7 @@ object ScalatraAtmosphereHandler {
val resource = event.getResource
resource.transport match {
case JSONP | AJAX | LONG_POLLING =>
- case _ => resource.getResponse().flushBuffer()
+ case _ => resource.getResponse.flushBuffer()
}
}
@@ -34,7 +34,7 @@ object ScalatraAtmosphereHandler {
val disconnector = if (event.isCancelled) ClientDisconnected else ServerDisconnected
client(event.getResource) foreach (_.receive.lift(Disconnected(disconnector, Option(event.throwable))))
if (!event.getResource.isResumed) {
- event.getResource.session.invalidate
+ event.getResource.session.invalidate()
}
}
@@ -54,7 +54,7 @@ class ScalatraAtmosphereException(message: String) extends ScalatraException(mes
class ScalatraAtmosphereHandler(implicit wireFormat: WireFormat) extends AbstractReflectorAtmosphereHandler {
import ScalatraAtmosphereHandler._
- private[this] val internalLogger = Logger[this.type]
+ private[this] val internalLogger = Logger(getClass)
def onRequest(resource: AtmosphereResource) {
val req = resource.getRequest
View
17 swagger-ext/src/main/scala/org/scalatra/swagger/SwaggerAuth.scala
@@ -147,17 +147,18 @@ trait SwaggerAuthSupport[TypeForUser <: AnyRef] extends SwaggerSupportBase with
val ops = (for {
(method, routes) routes.methodRoutes
route routes
+ endpoint = route.metadata.get(Symbols.Endpoint) map (_.asInstanceOf[String]) getOrElse ""
+ operation = operations(route, method)
+ if (operation.nonEmpty && operation.head.nickname.isDefined)
} yield {
- val endpoint = route.metadata.get(Symbols.Endpoint) map (_.asInstanceOf[String]) getOrElse ""
- Entry(endpoint, operations(route, method))
+ Entry(endpoint, operation)
})
- val filtered = ops filter (l l.value.nonEmpty && l.value.head.nickname.isDefined) groupBy (_.key)
- filtered.toList map { op =>
- val name = op._1
- val sec = op._2.exists(_.value.exists(!_.allows.apply(None))) //_secured.lift apply name getOrElse true
+ (ops groupBy (_.key)).toList map { case (name, entries) =>
+ val sec = entries.exists(_.value.exists(!_.allows.apply(None)))
val desc = _description.lift apply name getOrElse ""
- new AuthEndpoint[TypeForUser]("%s/%s" format (basePath, name), desc, sec, op._2.toList flatMap (_.value))
- } sortWith { (a, b) a.path < b.path }
+ val pth = if (basePath endsWith "/") basePath else basePath + "/"
+ new AuthEndpoint[TypeForUser](pth + name, desc, sec, entries.toList flatMap (_.value))
+ } sortBy (_.path)
}
/**
* Returns a list of operations based on the given route. The default implementation returns a list with only 1
View
31 swagger/src/main/scala/org/scalatra/swagger/SwaggerSupport.scala
@@ -138,7 +138,6 @@ trait SwaggerSupportSyntax extends Initializable with CorsSupport { this: Scalat
* Provides the necessary support for adding documentation to your routes.
*/
trait SwaggerSupport extends ScalatraSyntax with SwaggerSupportBase with SwaggerSupportSyntax {
-/*
/**
* Builds the documentation for all the endpoints discovered in an API.
*/
@@ -152,34 +151,12 @@ trait SwaggerSupport extends ScalatraSyntax with SwaggerSupportBase with Swagger
if (operation.nonEmpty && operation.head.nickname.isDefined)
} yield Entry(endpoint, operation))
- (List.empty[Endpoint] /: (ops groupBy (_.key))) { (r, op)
- val name = op._1
+ (ops groupBy (_.key)).toList map { case (name, entries)
val sec = false //_secured.lift apply name getOrElse true
- val desc = _description.lift apply name getOrElse ""
- new Endpoint("%s/%s" format (basePath, name), desc, sec, (op._2.toList flatMap (_.value)) ) :: r
+ val desc = _description lift name getOrElse ""
+ val pth = if (basePath endsWith "/") basePath else basePath + "/"
+ new Endpoint(pth + name, desc, sec, (entries.toList flatMap (_.value)) )
} sortBy (_.path)
- }*/
-
-
- /**
- * Builds the documentation for all the endpoints discovered in an API.
- */
- def endpoints(basePath: String): List[Endpoint] = {
- case class Entry(key: String, value: List[Operation])
- val ops = (for {
- (method, routes) routes.methodRoutes
- route routes
- } yield {
- val endpoint = route.metadata.get(Symbols.Endpoint) map (_.asInstanceOf[String]) getOrElse ""
- Entry(endpoint, operations(route, method))
- }) filter (l l.value.nonEmpty && l.value.head.nickname.isDefined) groupBy (_.key)
-
- (List.empty[Endpoint] /: ops) { (r, op)
- val name = op._1
- val sec = false //_secured.lift apply name getOrElse true
- val desc = _description.lift apply name getOrElse ""
- new Endpoint("%s/%s" format (basePath, name), desc, sec, (op._2.toList flatMap (_.value)) ) :: r
- } sortWith { (a, b) a.path < b.path }
}
/**
View
218 swagger/src/test/resources/rootpet.json
@@ -0,0 +1,218 @@
+{
+ "apiVersion": "1",
+ "apis": [
+ {
+ "description": "",
+ "operations": [
+ {
+ "deprecated": false,
+ "errorResponses": [],
+ "httpMethod": "GET",
+ "nickname": "allPets",
+ "notes": "shows all the pets in the data store",
+ "parameters": [],
+ "responseClass": "List[Pet]",
+ "summary": "Show all pets"
+ },
+ {
+ "deprecated": false,
+ "errorResponses": [
+ {
+ "code": 404,
+ "reason": "Pet not found"
+ }
+ ],
+ "httpMethod": "PUT",
+ "nickname": "updatePet",
+ "parameters": [
+ {
+ "allowMultiple": false,
+ "dataType": "Pet",
+ "description": "Pet object that needs to be updated in the store",
+ "name": "body",
+ "paramType": "body",
+ "required": true
+ }
+ ],
+ "responseClass": "void",
+ "summary": "Update an existing pet"
+ },
+ {
+ "deprecated": false,
+ "errorResponses": [
+ {
+ "code": 400,
+ "reason": "Invalid pet data supplied"
+ }
+ ],
+ "httpMethod": "POST",
+ "nickname": "addPet",
+ "parameters": [
+ {
+ "allowMultiple": false,
+ "dataType": "Pet",
+ "description": "Pet object that needs to be added to the store",
+ "name": "body",
+ "paramType": "body",
+ "required": true
+ }
+ ],
+ "responseClass": "void",
+ "summary": "Add a new pet to the store"
+ }
+ ],
+ "path": "/pet",
+ "secured": true
+ },
+ {
+ "description": "",
+ "operations": [
+ {
+ "deprecated": false,
+ "errorResponses": [],
+ "httpMethod": "GET",
+ "nickname": "findPetsByStatus",
+ "notes": "Multiple status values can be provided with comma separated strings",
+ "parameters": [
+ {
+ "allowMultiple": false,
+ "allowableValues": {
+ "valueType": "LIST",
+ "values": [
+ "available",
+ "pending",
+ "sold"
+ ]
+ },
+ "dataType": "string",
+ "defaultValue": "available",
+ "description": "Status values that need to be considered for filter",
+ "name": "status",
+ "paramType": "query",
+ "required": true
+ }
+ ],
+ "responseClass": "List[Pet]",
+ "summary": "Finds Pets by status"
+ }
+ ],
+ "path": "/pet/findByStatus",
+ "secured": true
+ },
+ {
+ "description": "",
+ "operations": [
+ {
+ "deprecated": false,
+ "errorResponses": [],
+ "httpMethod": "GET",
+ "nickname": "findByTags",
+ "notes": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.",
+ "parameters": [
+ {
+ "allowMultiple": false,
+ "dataType": "string",
+ "description": "Tags to filter by",
+ "name": "tags",
+ "paramType": "query",
+ "required": true
+ }
+ ],
+ "responseClass": "List[Pet]",
+ "summary": "Finds Pets by tags"
+ }
+ ],
+ "path": "/pet/findByTags",
+ "secured": true
+ },
+ {
+ "description": "",
+ "operations": [
+ {
+ "deprecated": false,
+ "errorResponses": [
+ {
+ "code": 400,
+ "reason": "Invalid ID supplied"
+ },
+ {
+ "code": 404,
+ "reason": "Pet not found"
+ }
+ ],
+ "httpMethod": "GET",
+ "nickname": "findById",
+ "notes": "Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions",
+ "parameters": [
+ {
+ "allowMultiple": false,
+ "dataType": "string",
+ "description": "ID of pet that needs to be fetched",
+ "name": "id",
+ "paramType": "path",
+ "required": true
+ }
+ ],
+ "responseClass": "Pet",
+ "summary": "Find by ID"
+ }
+ ],
+ "path": "/pet/{id}",
+ "secured": true
+ }
+ ],
+ "basePath": "http://localhost:8080/api",
+ "description": "Operations about pets",
+ "models": {
+ "Pet": {
+ "description": "Pet",
+ "id": "Pet",
+ "properties": {
+ "category": {
+ "description": null,
+ "enum": [],
+ "name": "category",
+ "required": true,
+ "type": "Category"
+ },
+ "id": {
+ "description": null,
+ "enum": [],
+ "name": "id",
+ "required": true,
+ "type": "long"
+ },
+ "name": {
+ "description": null,
+ "enum": [],
+ "name": "name",
+ "required": true,
+ "type": "string"
+ },
+ "status": {
+ "description": null,
+ "enum": [],
+ "name": "status",
+ "required": true,
+ "type": "string"
+ },
+ "tags": {
+ "description": null,
+ "enum": [],
+ "name": "tags",
+ "required": true,
+ "type": "List[Tag]"
+ },
+ "urls": {
+ "description": null,
+ "enum": [],
+ "name": "urls",
+ "required": true,
+ "type": "List[string]"
+ }
+ }
+ }
+ },
+ "resourcePath": "/",
+ "swaggerVersion": "1.1"
+}
View
11 swagger/src/test/resources/subresources.json
@@ -0,0 +1,11 @@
+{
+ "apiVersion": "1",
+ "apis": [
+ {
+ "description": "Operations about pets",
+ "path": "/api-docs/pet.{format}"
+ }
+ ],
+ "basePath": "http://localhost:58468",
+ "swaggerVersion": "1.1"
+}
View
129 swagger/src/test/scala/org/scalatra/swagger/SwaggerRootServletSpec.scala
@@ -0,0 +1,129 @@
+package org.scalatra
+package swagger
+
+import test.specs2.MutableScalatraSpec
+import org.json4s._
+import JsonDSL._
+import native.JsonMethods
+import java.net.ServerSocket
+import io.Source
+import org.json4s.native.JsonParser
+import org.specs2.matcher.MatchResult
+
+class SwaggerRootServletSpec extends MutableScalatraSpec {
+ "Swagger integration for resources in a sub path should" ^
+ "list resources" ! listResources ^
+ "list operations" ! listOperations ^
+ end
+
+ val swagger = new Swagger("1.1", "1")
+ val testServlet = new SwaggerTestServlet(swagger)
+
+ addServlet(testServlet, "/*")
+ addServlet(new SwaggerResourcesServlet(swagger), "/api-docs/*")
+ implicit val formats = DefaultFormats
+
+ /**
+ * Sets the port to listen on. 0 means listen on any available port.
+ */
+ override lazy val port: Int = { val s = new ServerSocket(0); try { s.getLocalPort } finally { s.close() } }//58468
+
+ val listResourceJValue = readJson("subresources.json") merge (("basePath" -> ("http://localhost:" + port)):JValue)
+// println(JsonMethods.pretty(JsonMethods.render(listResourceJValue)))
+ val listOperationsJValue = readJson("rootpet.json") merge (("basePath" -> ("http://localhost:" + port)):JValue)
+// println(JsonMethods.pretty(JsonMethods.render(listOperationsJValue)))
+
+ private def readJson(file: String) = {
+ val f = if ( file startsWith "/" ) file else "/"+file
+ val rdr = Source.fromInputStream(getClass.getResourceAsStream(f)).bufferedReader()
+ JsonParser.parse(rdr)
+ }
+
+
+ def listResources = get("/api-docs/resources.json") {
+ println(JsonMethods.pretty(JsonMethods.render(JsonParser.parse(body))))
+ JsonParser.parseOpt(body) must beSome(listResourceJValue)
+ }
+
+ val operations = "allPets" :: "updatePet" :: "addPet" :: "findByTags" :: "findPetsByStatus" :: "findById" :: Nil
+// val operations = "allPets" :: Nil
+ def listOperations = {
+ get("/api-docs/pet.json") {
+ val bo = JsonParser.parseOpt(body)
+ println(JsonMethods.pretty(JsonMethods.render(JsonParser.parse(body))))
+ bo must beSome[JValue] and
+ verifyCommon(bo.get) and
+ operations.map(verifyOperation(bo.get, _)).reduce(_ and _)
+ }
+ }
+
+ def verifyCommon(jv: JValue): MatchResult[Any] = {
+ (jv \ "apiVersion" must_== listOperationsJValue \ "apiVersion") and
+ (jv \ "swaggerVersion" must_== listOperationsJValue \ "swaggerVersion") and
+ (jv \ "basePath" must_== listOperationsJValue \ "basePath") and
+ (jv \ "description" must_== listOperationsJValue \ "description") and
+ (jv \ "resourcePath" must_== listOperationsJValue \ "resourcePath") and {
+ val ja = jv \ "apis" \ "path" \\ classOf[JString]
+ (ja.size must_== 4) and
+ (ja must haveTheSameElementsAs(List("/{id}", "/findByTags", "/findByStatus", "/")))
+ }
+ }
+
+ def verifyOperation(jv: JValue, name: String) = {
+ val op = findOperation(jv, name)
+ val exp = findOperation(listOperationsJValue, name)
+ (op must beSome[JValue]).setMessage("Couldn't find operation: " + name) and {
+ val m = verifyFields(op.get, exp.get, "httpMethod", "nickname", "responseClass", "summary", "parameters", "notes", "errorResponses")
+ m setMessage (m.message + " of the operation " + name)
+ }
+ }
+
+ def verifyFields(actual: JValue, expected: JValue, fields: String*): MatchResult[Any] = {
+ def verifyField(act: JValue, exp: JValue, fn: String): MatchResult[Any] = {
+ fn match {
+ case "errorResponses" =>
+ val JArray(af) = act \ fn
+ val JArray(ef) = exp \ fn
+ val r = af map { v =>
+ val mm = verifyFields(
+ v,
+ ef find (_ \ "code" == v \ "code") get,
+ "code", "reason")
+ mm setMessage (mm.message + " in error responses collection")
+ }
+ if (r.nonEmpty) r reduce (_ and _) else 1.must_==(1)
+ case "parameters" =>
+ val JArray(af) = act \ fn
+ val JArray(ef) = exp \ fn
+ val r = af map { v =>
+ val mm = verifyFields(
+ v,
+ ef find (_ \ "name" == v \ "name") get,
+ "allowableValues", "dataType", "paramType", "allowMultiple", "defaultValue", "description", "name", "required")
+ mm setMessage (mm.message + " for parameter " + ( v \ "name" ).extractOrElse("N/A"))
+ }
+
+ if (r.nonEmpty) r reduce (_ and _) else 1.must_==(1)
+ case _ =>
+ val m = act \ fn must_== exp \ fn
+ val rdr = (act \ fn) match {
+ case JNothing => "<nothing>"
+ case jv => JsonMethods.compact(JsonMethods.render(jv))
+ }
+ m setMessage (rdr + " does not match\n" + rdr + " for field " + fn)
+ }
+ }
+
+ (fields map (verifyField(actual, expected, _)) reduce (_ and _))
+ }
+
+ def findOperation(jv: JValue, name: String) = {
+ val JArray(ja) = jv \ "apis"
+ ja find { jn =>
+ val JArray(ops) = jn \ "operations"
+ ops.exists(_ \ "nickname" == JString(name))
+ } flatMap { api =>
+ (api \ "operations").find(_ \ "nickname" == JString(name))
+ }
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.