Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Comments to skip multiple endpoints #469

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,23 @@ e.g.
GET /docs/swagger-ui/*file controllers.Assets.at(path:String="/public/lib/swagger-ui", file:String)
```

Multiple lines can be skipped.

※NoDocsEnd must always be listed directly before one line as a comment on <b>the endpoint not to be skipped</b>, or on the last line of the routes fill.

```
### NoDocsStart ###
GET /api/hidden/a controllers.hiddenEndPointA()

GET /api/hidden/b controllers.hiddenEndPointB()

### NoDocsEnd ###
###
# summary: I'm not hiding!
###
GET /api/hidden/c controllers.hiddenEndPointC()
```

#### How to specify body content in a POST endpoint
Body content is specified as a special parameter in swagger. So you need to create a parameter in your swagger spec comment as "body", for example
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import play.routes.compiler._

object SwaggerSpecGenerator {
private val marker = "##"
private val noDocsEnd = s"$marker\\s*NoDocsEnd\\s*$marker#"
val customMappingsFileName = "swagger-custom-mappings"
val baseSpecFileName = "swagger"

Expand Down Expand Up @@ -404,7 +405,13 @@ final case class SwaggerSpecGenerator(

private def paths(routes: Seq[Route], prefix: String, tag: Option[Tag]): JsObject = {
JsObject {
val endPointEntries = routes.flatMap(route ⇒ endPointEntry(route, prefix, tag))
val endPointEntries = routes.foldLeft[(Seq[(String, JsObject)], Boolean)]((Nil, false)) {
case ((o, skipping), route) ⇒
endPointEntry(route, prefix, tag, skipping) match {
case (Some(route), skipping) => (o :+ route, skipping)
case (None, skipping) => (o, skipping)
}
}._1

// maintain the routes order as per the original routing file
val zgbp = endPointEntries.zipWithIndex.groupBy(_._1._1)
Expand All @@ -415,19 +422,36 @@ final case class SwaggerSpecGenerator(
}
}

private def endPointEntry(route: Route, prefix: String, tag: Option[String]): Option[(String, JsObject)] = {
private def endPointEntry(
route: Route,
prefix: String,
tag: Option[String],
skipping: Boolean
): (Option[(String, JsObject)], Boolean) = {
import SwaggerSpecGenerator.marker
import SwaggerSpecGenerator.noDocsEnd

val comments = route.comments.map(_.comment).mkString("\n")

if (s"$marker\\s*NoDocs\\s*$marker".r.findFirstIn(comments).isDefined) {
None
(None, skipping)
} else if (s"$marker\\s*NoDocsStart\\s*$marker".r.findFirstIn(comments).isDefined) {
// NoDocsStart ならスキップを開始する
(None, true)
} else {
val inRoutePath = route.path.parts.map {
case DynamicPart(name, _, _) ⇒ s"{$name}"
case StaticPart(value) ⇒ value
}.mkString
val method = route.verb.value.toLowerCase
Some(fullPath(prefix, inRoutePath) → Json.obj(method → endPointSpec(route, tag)))
val docsEnd = noDocsEnd.r.findFirstIn(comments).isDefined
// スキップ中かつ、 NoDocsEnd が指定されていないならスキップする
if (!docsEnd && skipping) {
(None, skipping)
} else {
// それ以外はスキップしない
val inRoutePath = route.path.parts.map {
case DynamicPart(name, _, _) ⇒ s"{$name}"
case StaticPart(value) ⇒ value
}.mkString
val method = route.verb.value.toLowerCase
(Some(fullPath(prefix, inRoutePath) → Json.obj(method → endPointSpec(route, tag))), false)
}
}
}

Expand Down Expand Up @@ -489,11 +513,17 @@ final case class SwaggerSpecGenerator(

val jsonFromComment = {
import SwaggerSpecGenerator.marker
import SwaggerSpecGenerator.noDocsEnd

val comments = route.comments.map(_.comment)
val commentDocLines = comments match {
case `marker` +: docs :+ `marker` ⇒ docs
case _ ⇒ Nil
case first +: `marker` +: docs :+ `marker` if first.matches(noDocsEnd) ⇒
println(docs)
docs
case docs ⇒
println(docs)
Nil
}

for {
Expand Down
13 changes: 12 additions & 1 deletion core/src/test/resources/liveMeta.routes
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,15 @@ GET /api/station/:sid/playedTracks/last @controllers.LiveMeta.pl
POST /api/station/playedTracks controllers.LiveMeta.addPlayedTracks()

### NoDocs ###
GET /api/station/hidden controllers.LiveMeta.hiddenEndPoint()
GET /api/station/hidden controllers.LiveMeta.hiddenEndPoint()

### NoDocsStart ###
GET /api/station/hidden/a controllers.LiveMeta.hiddenEndPointA()

GET /api/station/hidden/b controllers.LiveMeta.hiddenEndPointB()

### NoDocsEnd ###
###
# summary: I'm not hiding!
###
GET /api/station/hidden/c controllers.LiveMeta.hiddenEndPointC()
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,24 @@ class SwaggerSpecGeneratorIntegrationSpec extends Specification {
}

"does not generate for end points marked as hidden" >> {
(pathJson \ "/api/station/hidden" \ "get").toOption must beEmpty
"single" >> {
(pathJson \ "/api/station/hidden" \ "get").toOption must beEmpty
}
"multiple-a" >> {
(pathJson \ "/api/station/hidden/a" \ "get").toOption must beEmpty
}
"multiple-b" >> {
(pathJson \ "/api/station/hidden/b" \ "get").toOption must beEmpty
}
}

"does generate for end points marked as finished hidden" >> {
"not hidden" >> {
(pathJson \ "/api/station/hidden/c" \ "get").toOption must not(beEmpty)
}
"readable summary" >> {
(pathJson \ "/api/station/hidden/c" \ "get" \ "summary").asOpt[String] === Some("I'm not hiding!")
}
}

"generate path correctly with missing type (String by default) in controller description" >> {
Expand Down