From bf29d0574bb63846cc3aecde511264a53c0ef696 Mon Sep 17 00:00:00 2001 From: Nabil Abdel-Hafeez <7283535+987Nabil@users.noreply.github.com> Date: Fri, 30 Aug 2024 22:37:46 +0200 Subject: [PATCH] Sanitize scala doc string for open api gen (#3047) --- .../src/main/scala/zio/http/Status.scala | 2 +- .../http/endpoint/openapi/JsonSchema.scala | 16 ++++++++++++-- .../http/endpoint/Scala3OpenAPIGenSpec.scala | 22 +++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/Status.scala b/zio-http/shared/src/main/scala/zio/http/Status.scala index 1727675239..f26acae610 100644 --- a/zio-http/shared/src/main/scala/zio/http/Status.scala +++ b/zio-http/shared/src/main/scala/zio/http/Status.scala @@ -40,7 +40,7 @@ sealed trait Status extends Product with Serializable { self => lazy val text: String = code.toString /** - * Returns an Routes[Any, Nothing] that responses with this http status code. + * Returns a Routes[Any, Nothing] that responses with this http status code. */ def toRoutes(implicit trace: Trace): Routes[Any, Nothing] = Handler.status(self).toRoutes diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/JsonSchema.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/JsonSchema.scala index 40203a8419..b62241ab4d 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/JsonSchema.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/JsonSchema.scala @@ -618,7 +618,7 @@ object JsonSchema { .map(_.name), ) .deprecated(deprecated(record)) - .description(record.annotations.collectFirst { case description(value) => value }) + .description(descriptionFromAnnotations(record.annotations)) case collection: Schema.Collection[_, _] => collection match { case Schema.Sequence(elementSchema, _, _, _, _) => @@ -731,6 +731,18 @@ object JsonSchema { } + private def descriptionFromAnnotations(annotations: Chunk[Any]) = { + def sanitize(str: java.lang.String): java.lang.String = + str.linesIterator + .map(_.trim.stripPrefix("/**").stripPrefix("/*").stripSuffix("*/").stripPrefix("*").trim) + .filterNot(l => l == "\n" || l == "") + .mkString("\n") + annotations.collectFirst { + case description(value) if value.trim.startsWith("/*") => sanitize(value) + case description(value) => value + } + } + sealed trait SchemaStyle extends Product with Serializable object SchemaStyle { @@ -759,7 +771,7 @@ object JsonSchema { schema.annotations.exists(_.isInstanceOf[scala.deprecated]) private def fieldDoc(schema: Schema.Field[_, _]): Option[java.lang.String] = { - val description0 = schema.annotations.collectFirst { case description(value) => value } + val description0 = descriptionFromAnnotations(schema.annotations) val defaultValue = schema.annotations.collectFirst { case fieldDefaultValue(value) => value }.map { _ => s"${if (description0.isDefined) "\n" else ""}If not set, this field defaults to the value of the default annotation." } diff --git a/zio-http/shared/src/test/scala-3/zio/http/endpoint/Scala3OpenAPIGenSpec.scala b/zio-http/shared/src/test/scala-3/zio/http/endpoint/Scala3OpenAPIGenSpec.scala index 06e713757c..ea3dea60a7 100644 --- a/zio-http/shared/src/test/scala-3/zio/http/endpoint/Scala3OpenAPIGenSpec.scala +++ b/zio-http/shared/src/test/scala-3/zio/http/endpoint/Scala3OpenAPIGenSpec.scala @@ -82,6 +82,28 @@ object Scala3OpenAPIGenSpec extends ZIOSpecDefault { zio.http.endpoint.openapi.OpenAPIGen.gen(endpoint = testEndpoint) assertTrue(true) }, + test("scala doc for api doc is sanetized") { + /** + * This is the Input documentation + */ + final case class Input(a: String) + + implicit val schema: Schema[Input] = DeriveSchema.gen[Input] + + val testEndpoint = + (Endpoint(RoutePattern.POST / "test") ?? Doc.p("This is my 'POST /test' endpoint doc")) + .in[Input] + .out[String](mediaType = MediaType.application.json, doc = Doc.p("this is the output doc")) + + val spec: String = + OpenAPIGen.fromEndpoints( + title = "This is my OpenAPI doc title", + version = "0.0.0", + endpoints = List(testEndpoint) + ).toJson + + assertTrue(spec.contains(""""description":"This is the Input documentation"""")) + } ) ) }