Skip to content

Commit

Permalink
feat: make it a plugin option
Browse files Browse the repository at this point in the history
  • Loading branch information
Javakky-pxv committed Sep 6, 2022
1 parent 0fc44d3 commit 8644ecb
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ final case class DefinitionGenerator(
mappings: CustomMappings = Nil,
swaggerPlayJava: Boolean = false,
_mapper: ObjectMapper = new ObjectMapper(),
namingStrategy: NamingStrategy = NamingStrategy.None
namingStrategy: NamingStrategy = NamingStrategy.None,
embedScaladoc: Boolean = false
)(implicit cl: ClassLoader) {

private val refinedTypePattern = raw"(eu\.timepit\.refined\.api\.Refined(?:\[.+])?)".r
Expand Down Expand Up @@ -70,27 +71,31 @@ final case class DefinitionGenerator(
case m: MethodSymbol if m.isPrimaryConstructor m
}.toList.flatMap(_.paramLists).headOption.getOrElse(Nil)

val scaladoc = for {
annotation <- tpe.typeSymbol.annotations
if typeOf[Scaladoc] == annotation.tree.tpe
value <- annotation.tree.children.tail.headOption
docTree <- value.children.tail.headOption
docString = docTree.toString().tail.init.replace("\\n", "\n")
doc <- ScaladocParser.parse(docString)
} yield doc

val paramDescriptions = (for {
doc <- scaladoc
paragraph <- doc.para
term <- paragraph.terms
tag <- term match {
case iScaladoc.Tag(iScaladoc.TagType.Param, Some(iScaladoc.Word(key)), Seq(text)) =>
Some(key -> text)
case _ => None
}
} yield tag).map {
case (name, term) => name -> scalaDocToMarkdown(term).toString
}.toMap
val paramDescriptions = if (embedScaladoc) {
val scaladoc = for {
annotation <- tpe.typeSymbol.annotations
if typeOf[Scaladoc] == annotation.tree.tpe
value <- annotation.tree.children.tail.headOption
docTree <- value.children.tail.headOption
docString = docTree.toString().tail.init.replace("\\n", "\n")
doc <- ScaladocParser.parse(docString)
} yield doc

(for {
doc <- scaladoc
paragraph <- doc.para
term <- paragraph.terms
tag <- term match {
case iScaladoc.Tag(iScaladoc.TagType.Param, Some(iScaladoc.Word(key)), Seq(text)) =>
Some(key -> text)
case _ => None
}
} yield tag).map {
case (name, term) => name -> scalaDocToMarkdown(term).toString
}.toMap
} else {
Map.empty[String, String]
}

fields.map { field: Symbol
// TODO: find a better way to get the string representation of typeSignature
Expand Down Expand Up @@ -194,11 +199,13 @@ object DefinitionGenerator {
def apply(
domainNameSpace: String,
customParameterTypeMappings: CustomMappings,
namingStrategy: NamingStrategy
namingStrategy: NamingStrategy,
embedScaladoc: Boolean
)(implicit cl: ClassLoader): DefinitionGenerator =
DefinitionGenerator(
PrefixDomainModelQualifier(domainNameSpace),
customParameterTypeMappings,
namingStrategy = namingStrategy
namingStrategy = namingStrategy,
embedScaladoc = embedScaladoc
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ object SwaggerSpecGenerator {
)
}

def apply(swaggerV3: Boolean, operationIdFully: Boolean, domainNameSpaces: String*)(implicit
def apply(swaggerV3: Boolean, operationIdFully: Boolean, embedScaladoc: Boolean, domainNameSpaces: String*)(implicit
cl: ClassLoader): SwaggerSpecGenerator = {
SwaggerSpecGenerator(
NamingStrategy.None,
PrefixDomainModelQualifier(domainNameSpaces: _*),
swaggerV3 = swaggerV3,
operationIdFully = operationIdFully
operationIdFully = operationIdFully,
embedScaladoc = embedScaladoc
)
}
def apply(outputTransformers: Seq[OutputTransformer], domainNameSpaces: String*)(implicit
Expand All @@ -69,7 +70,8 @@ final case class SwaggerSpecGenerator(
swaggerV3: Boolean = false,
swaggerPlayJava: Boolean = false,
apiVersion: Option[String] = None,
operationIdFully: Boolean = false
operationIdFully: Boolean = false,
embedScaladoc: Boolean = false
)(implicit cl: ClassLoader) {

import SwaggerSpecGenerator.{MissingBaseSpecException, baseSpecFileName, customMappingsFileName}
Expand Down Expand Up @@ -202,7 +204,8 @@ final case class SwaggerSpecGenerator(
modelQualifier = modelQualifier,
mappings = customMappings,
swaggerPlayJava = swaggerPlayJava,
namingStrategy = namingStrategy
namingStrategy = namingStrategy,
embedScaladoc = embedScaladoc
).allDefinitions(referredClasses)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import play.api.libs.json.{JsValue, Json}
object SwaggerSpecRunner extends App {
implicit def cl: ClassLoader = getClass.getClassLoader

val targetFile :: routesFile :: domainNameSpaceArgs :: outputTransformersArgs :: swaggerV3String :: apiVersion :: swaggerPrettyJson :: swaggerPlayJavaString :: namingStrategy :: operationIdNamingFullyString :: Nil =
val targetFile :: routesFile :: domainNameSpaceArgs :: outputTransformersArgs :: swaggerV3String :: apiVersion :: swaggerPrettyJson :: swaggerPlayJavaString :: namingStrategy :: operationIdNamingFullyString :: embedScaladocString :: Nil =
args.toList
private def fileArg = Paths.get(targetFile)
private def swaggerJson = {
val swaggerV3 = java.lang.Boolean.parseBoolean(swaggerV3String)
val swaggerOperationIdNamingFully = java.lang.Boolean.parseBoolean(operationIdNamingFullyString)
val embedScaladoc = java.lang.Boolean.parseBoolean(embedScaladocString)
val swaggerPlayJava = java.lang.Boolean.parseBoolean(swaggerPlayJavaString)
val domainModelQualifier = PrefixDomainModelQualifier(domainNameSpaceArgs.split(","): _*)
val transformersStrs: Seq[String] = if (outputTransformersArgs.isEmpty) Seq() else outputTransformersArgs.split(",")
Expand All @@ -36,7 +37,8 @@ object SwaggerSpecRunner extends App {
swaggerV3 = swaggerV3,
swaggerPlayJava = swaggerPlayJava,
apiVersion = Some(apiVersion),
operationIdFully = swaggerOperationIdNamingFully
operationIdFully = swaggerOperationIdNamingFully,
embedScaladoc = embedScaladoc
).generate(routesFile).get

if (swaggerPrettyJson.toBoolean) Json.prettyPrint(swaggerSpec)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ class DefinitionGeneratorSpec extends Specification {

"generate properties" >> {

val result = DefinitionGenerator("com.iheart.playSwagger", Nil, NamingStrategy.None).definition[Foo].properties
val result = DefinitionGenerator(
"com.iheart.playSwagger",
Nil,
NamingStrategy.None,
embedScaladoc = false
).definition[Foo].properties

result.length === 7

Expand Down Expand Up @@ -118,7 +123,9 @@ class DefinitionGeneratorSpec extends Specification {
"generate properties using snake case naming strategy" >> {

val result =
DefinitionGenerator("com.iheart.playSwagger", Nil, NamingStrategy.SnakeCase).definition[Foo].properties
DefinitionGenerator("com.iheart.playSwagger", Nil, NamingStrategy.SnakeCase, embedScaladoc = false).definition[
Foo
].properties

result.length === 7

Expand Down Expand Up @@ -167,7 +174,9 @@ class DefinitionGeneratorSpec extends Specification {
"generate properties using kebab case naming strategy" >> {

val result =
DefinitionGenerator("com.iheart.playSwagger", Nil, NamingStrategy.KebabCase).definition[Foo].properties
DefinitionGenerator("com.iheart.playSwagger", Nil, NamingStrategy.KebabCase, embedScaladoc = false).definition[
Foo
].properties

result.length === 7

Expand Down Expand Up @@ -214,14 +223,14 @@ class DefinitionGeneratorSpec extends Specification {
}

"read class in Object" >> {
val result = DefinitionGenerator("com.iheart", Nil, NamingStrategy.None).definition(
val result = DefinitionGenerator("com.iheart", Nil, NamingStrategy.None, embedScaladoc = false).definition(
"com.iheart.playSwagger.MyObject.MyInnerClass"
)
result.properties.head.name === "bar"
}

"read alias type in Object" >> {
val result = DefinitionGenerator("com.iheart", Nil, NamingStrategy.None).definition(
val result = DefinitionGenerator("com.iheart", Nil, NamingStrategy.None, embedScaladoc = false).definition(
"com.iheart.playSwagger.MyObject.MyInnerClass"
)

Expand All @@ -233,14 +242,16 @@ class DefinitionGeneratorSpec extends Specification {

"read sequence items" >> {
val result =
DefinitionGenerator("com.iheart", Nil, NamingStrategy.None).definition("com.iheart.playSwagger.FooWithSeq")
DefinitionGenerator("com.iheart", Nil, NamingStrategy.None, embedScaladoc = false).definition(
"com.iheart.playSwagger.FooWithSeq"
)
result.properties.head.asInstanceOf[GenSwaggerParameter].items.get.asInstanceOf[
GenSwaggerParameter
].referenceType === Some("com.iheart.playSwagger.SeqItem")
}

"read primitive sequence items" >> {
val result = DefinitionGenerator("com.iheart", Nil, NamingStrategy.None).definition(
val result = DefinitionGenerator("com.iheart", Nil, NamingStrategy.None, embedScaladoc = false).definition(
"com.iheart.playSwagger.WithListOfPrimitive"
)
result.properties.head.asInstanceOf[GenSwaggerParameter].items.get.asInstanceOf[
Expand All @@ -251,7 +262,9 @@ class DefinitionGeneratorSpec extends Specification {

"read Optional items " >> {
val result =
DefinitionGenerator("com.iheart", Nil, NamingStrategy.None).definition("com.iheart.playSwagger.FooWithOption")
DefinitionGenerator("com.iheart", Nil, NamingStrategy.None, embedScaladoc = false).definition(
"com.iheart.playSwagger.FooWithOption"
)
result.properties.head.asInstanceOf[GenSwaggerParameter].referenceType must beSome(
"com.iheart.playSwagger.OptionItem"
)
Expand All @@ -260,7 +273,9 @@ class DefinitionGeneratorSpec extends Specification {
"with dates" >> {
"no override" >> {
val result =
DefinitionGenerator("com.iheart", Nil, NamingStrategy.None).definition("com.iheart.playSwagger.WithDate")
DefinitionGenerator("com.iheart", Nil, NamingStrategy.None, embedScaladoc = false).definition(
"com.iheart.playSwagger.WithDate"
)
val prop = result.properties.head.asInstanceOf[GenSwaggerParameter]
prop.`type` must beSome("integer")
prop.format must beSome("epoch")
Expand All @@ -275,7 +290,9 @@ class DefinitionGeneratorSpec extends Specification {
)
)
val result =
DefinitionGenerator("com.iheart", mappings, NamingStrategy.None).definition("com.iheart.playSwagger.WithDate")
DefinitionGenerator("com.iheart", mappings, NamingStrategy.None, embedScaladoc = false).definition(
"com.iheart.playSwagger.WithDate"
)
val prop = result.properties.head.asInstanceOf[CustomSwaggerParameter]
prop.specAsParameter === customJson
}
Expand All @@ -289,7 +306,7 @@ class DefinitionGeneratorSpec extends Specification {
specAsParameter = customJson
)
)
val result = DefinitionGenerator("com.iheart", mappings, NamingStrategy.None).definition(
val result = DefinitionGenerator("com.iheart", mappings, NamingStrategy.None, embedScaladoc = false).definition(
"com.iheart.playSwagger.WithOptionalDate"
)
val prop = result.properties.head.asInstanceOf[CustomSwaggerParameter]
Expand All @@ -304,7 +321,7 @@ class DefinitionGeneratorSpec extends Specification {
`type` = "com.iheart.playSwagger.WrappedString",
specAsParameter = customJson
)
val generator = DefinitionGenerator("com.iheart", List(customMapping), NamingStrategy.None)
val generator = DefinitionGenerator("com.iheart", List(customMapping), NamingStrategy.None, embedScaladoc = false)
val definition = generator.definition[FooWithWrappedStringProperties]

"support simple property types" >> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class SwaggerSpecGeneratorIntegrationSpec extends Specification {
(json \ "paths" \ "/player/{pid}/context/{bid}").asOpt[JsObject] must beSome
}

lazy val json = SwaggerSpecGenerator(false, false, "com.iheart").generate("test.routes").get
lazy val json = SwaggerSpecGenerator(false, false, false, "com.iheart").generate("test.routes").get
lazy val pathJson = json \ "paths"
lazy val definitionsJson = json \ "definitions"
lazy val postBodyJson = (pathJson \ "/post-body" \ "post").as[JsObject]
Expand Down Expand Up @@ -513,10 +513,18 @@ class SwaggerSpecGeneratorIntegrationSpec extends Specification {
}

"embedded scaladoc strings" >> {
lazy val json = SwaggerSpecGenerator(false, false, embedScaladoc = true, "com.iheart").generate("test.routes").get
lazy val definitionsJson = json \ "definitions"
lazy val dayOfWeekJson = (definitionsJson \ "com.iheart.playSwagger.DayOfWeek").asOpt[JsObject]
dayOfWeekJson must beSome[JsObject]
(dayOfWeekJson.get \ "properties" \ "name" \ "description").as[String] === "e.g. Sunday, Monday, TuesDay..."
}

"don't embedded scaladoc strings" >> {
dayOfWeekJson must beSome[JsObject]
(dayOfWeekJson.get \ "properties" \ "name" \ "description").asOpt[String] === None
}

"parse mixin referenced external file" >> {
lazy val subjectJson = (pathJson \ "/api/subjects/dow/{subject}" \ "get").as[JsObject]

Expand Down Expand Up @@ -684,7 +692,7 @@ class SwaggerSpecGeneratorIntegrationSpec extends Specification {
}

"fully operation id" >> {
lazy val json = SwaggerSpecGenerator(false, true, "com.iheart").generate("test.routes").get
lazy val json = SwaggerSpecGenerator(false, true, false, "com.iheart").generate("test.routes").get
lazy val addTrackJson = (json \ "paths" \ "/api/station/playedTracks" \ "post").as[JsObject]
(addTrackJson \ "operationId").as[String] ==== "LiveMeta.addPlayedTracks"
}
Expand Down Expand Up @@ -719,7 +727,7 @@ class SwaggerSpecGeneratorIntegrationSpec extends Specification {
}

"integration v3" >> {
lazy val json = SwaggerSpecGenerator(true, false, "com.iheart").generate("testV3.routes").get
lazy val json = SwaggerSpecGenerator(true, false, false, "com.iheart").generate("testV3.routes").get
lazy val componentSchemasJson = json \ "components" \ "schemas"
lazy val trackJson = (componentSchemasJson \ "com.iheart.playSwagger.Track").as[JsObject]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,10 @@ trait SwaggerKeys {
"swaggerOperationIdNaming",
"Either use the operationId of the generated json as the method name"
)

val embedScaladoc: SettingKey[Boolean] =
SettingKey[Boolean](
"embedScaladoc",
"Output schema description using scaladoc of case class"
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ object SwaggerPlugin extends AutoPlugin {
swaggerPlayJava := false,
swaggerNamingStrategy := "none",
swaggerOperationIdNamingFully := false,
embedScaladoc := false,
swagger := Def.task[File] {
(swaggerTarget.value).mkdirs()
val file = swaggerTarget.value / swaggerFileName.value
Expand All @@ -48,8 +49,9 @@ object SwaggerPlugin extends AutoPlugin {
swaggerAPIVersion.value ::
swaggerPrettyJson.value.toString ::
swaggerPlayJava.value.toString ::
swaggerNamingStrategy.value.toString ::
swaggerNamingStrategy.value ::
swaggerOperationIdNamingFully.value.toString ::
embedScaladoc.value.toString ::
Nil
val swaggerClasspath =
data((fullClasspath in Runtime).value) ++ update.value.select(configurationFilter(SwaggerConfig.name))
Expand Down

0 comments on commit 8644ecb

Please sign in to comment.