Skip to content

Commit

Permalink
codegen: limited support for oneOfVariant in responses (#3993)
Browse files Browse the repository at this point in the history
  • Loading branch information
hughsimpson authored Sep 2, 2024
1 parent 2a574d0 commit 8995b1c
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -309,35 +309,55 @@ class EndpointGenerator {
// .errorOut(stringBody)
// .out(jsonBody[List[Book]])

responses
.map { resp =>
val d = s""".description("${JavaEscape.escapeString(resp.description)}")"""
val (outs, errorOuts) = responses.partition { resp =>
resp.content match {
case Nil | _ +: Nil =>
resp.code match {
case okStatus(_) => true
case "default" | errorStatus(_) => false
case x => bail(s"Statuscode mapping is incomplete! Cannot handle $x")
}
case _ => bail("We can handle only one return content!")
}
}
def bodyFmt(resp: OpenapiResponse): String = {
val d = s""".description("${JavaEscape.escapeString(resp.description)}")"""
resp.content match {
case Nil => ""
case content +: Nil =>
s"${contentTypeMapper(content.contentType, content.schema, streamingImplementation)}$d"
}
}
def mappedGroup(group: Seq[OpenapiResponse]) = group match {
case Nil => None
case resp +: Nil =>
resp.content match {
case Nil =>
val d = s""".description("${JavaEscape.escapeString(resp.description)}")"""
resp.code match {
case "200" | "default" => ""
case okStatus(s) => s".out(statusCode(sttp.model.StatusCode($s))$d)"
case errorStatus(s) => s".errorOut(statusCode(sttp.model.StatusCode($s))$d)"
}
case content +: Nil =>
resp.code match {
case "200" =>
s".out(${contentTypeMapper(content.contentType, content.schema, streamingImplementation)}$d)"
case okStatus(s) =>
s".out(${contentTypeMapper(content.contentType, content.schema, streamingImplementation)}$d.and(statusCode(sttp.model.StatusCode($s))))"
case "default" =>
s".errorOut(${contentTypeMapper(content.contentType, content.schema, streamingImplementation)}$d)"
case errorStatus(s) =>
s".errorOut(${contentTypeMapper(content.contentType, content.schema, streamingImplementation)}$d.and(statusCode(sttp.model.StatusCode($s))))"
case x =>
bail(s"Statuscode mapping is incomplete! Cannot handle $x")
case "200" | "default" => None
case okStatus(s) => Some(s"statusCode(sttp.model.StatusCode($s))$d")
case errorStatus(s) => Some(s"statusCode(sttp.model.StatusCode($s))$d")
}
case _ => bail("We can handle only one return content!")
case _ =>
Some(resp.code match {
case "200" | "default" => s"${bodyFmt(resp)}"
case okStatus(s) => s"${bodyFmt(resp)}.and(statusCode(sttp.model.StatusCode($s)))"
case errorStatus(s) => s"${bodyFmt(resp)}.and(statusCode(sttp.model.StatusCode($s)))"
})
}
}
.sorted
.filter(_.nonEmpty)
.mkString("\n")
case many =>
if (many.map(_.code).distinct.size != many.size) bail("Cannot construct schema for multiple responses with same status code")
val oneOfs = many.map { m =>
val code = if (m.code == "default") "400" else m.code
s"oneOfVariant(sttp.model.StatusCode(${code}), ${bodyFmt(m)})"
}
Some(s"oneOf(${oneOfs.mkString(", ")})")
}
val mappedOuts = mappedGroup(outs).map(s => s".out($s)")
val mappedErrorOuts = mappedGroup(errorOuts).map(s => s".errorOut($s)")

Seq(mappedErrorOuts, mappedOuts).flatten.mkString("\n")
}

private def contentTypeMapper(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ object SchemaGenerator {
false
) -> "implicit lazy val anyTapirSchema: sttp.tapir.Schema[io.circe.Json] = sttp.tapir.Schema.any[io.circe.Json]"
)
else throw new NotImplementedError("any not implemented for json libs other than circe")
else
throw new NotImplementedError(
s"any not implemented for json libs other than circe (problematic models: ${schemasWithAny.map(_._1)})"
)
val openApiSchemasWithTapirSchemas = doc.components
.map(_.schemas.map {
case (name, _: OpenapiSchemaEnum) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ object TestHelpers {
| type: array
| items:
| $ref: '#/components/schemas/Book'
| '401':
| description: 'unauthorized'
| content:
| text/plain:
| schema:
| type: string
| default:
| description: ''
| content:
Expand Down Expand Up @@ -165,6 +171,7 @@ object TestHelpers {
"",
Seq(OpenapiResponseContent("application/json", OpenapiSchemaArray(OpenapiSchemaRef("#/components/schemas/Book"), false)))
),
OpenapiResponse("401", "unauthorized", Seq(OpenapiResponseContent("text/plain", OpenapiSchemaString(false)))),
OpenapiResponse("default", "", Seq(OpenapiResponseContent("text/plain", OpenapiSchemaString(false))))
),
requestBody = None,
Expand Down

0 comments on commit 8995b1c

Please sign in to comment.