Skip to content

Commit

Permalink
zio#2541 Fix bug where the OpenAPI generator fails to generate multip…
Browse files Browse the repository at this point in the history
…le methods for the same path. (zio#2542)

* zio#2541 Fix bug where the OpenAPI generator fails to generate multiple methods for the same path.

* Update version in README.
  • Loading branch information
daharon authored Dec 8, 2023
1 parent 94f5ec1 commit 6e230b9
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 3 deletions.
22 changes: 21 additions & 1 deletion zio-http/src/main/scala/zio/http/endpoint/openapi/OpenAPI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,33 @@ final case class OpenAPI(
openapi = openapi,
info = info,
servers = servers ++ other.servers,
paths = paths ++ other.paths,
paths = mergePaths(paths, other.paths),
components = (components.toSeq ++ other.components).reduceOption(_ ++ _),
security = security ++ other.security,
tags = tags ++ other.tags,
externalDocs = externalDocs,
)

private def mergePaths(paths: Map[OpenAPI.Path, OpenAPI.PathItem]*): Map[OpenAPI.Path, OpenAPI.PathItem] =
paths
.foldRight[Seq[(OpenAPI.Path, OpenAPI.PathItem)]](Seq.empty)((z, p) => z.toSeq ++ p)
.groupBy(_._1)
.map { case (path, pathItems) =>
val pathItem = pathItems.map(_._2).reduce { (i, j) =>
i.copy(
get = i.get.orElse(j.get),
put = i.put.orElse(j.put),
post = i.post.orElse(j.post),
delete = i.delete.orElse(j.delete),
options = i.options.orElse(j.options),
head = i.head.orElse(j.head),
patch = i.patch.orElse(j.patch),
trace = i.trace.orElse(j.trace),
)
}
(path, pathItem)
}

def toJson: String =
JsonCodec
.jsonEncoder(JsonCodec.Config(ignoreEmptyCollections = true))(OpenAPI.schema)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"openapi": "3.1.0",
"info": {
"title": "Multiple Methods on Same Path",
"version": "1.0"
},
"paths": {
"/test": {
"get": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "null"
}
}
},
"required": false
},
"responses": {
"200": {
"description": "",
"content": {
"text/plain": {
"schema": {
"type": "string"
}
}
}
}
},
"deprecated": false
},
"post": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "string"
}
}
},
"required": true
},
"responses": {
"201": {
"description": "",
"content": {
"text/plain": {
"schema": {
"type": "string"
}
}
}
}
},
"deprecated": false
}
}
},
"components": {}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package zio.http.endpoint.openapi

import zio.Scope
import zio.json.ast.Json
import zio.json.{EncoderOps, JsonEncoder}
import zio.test._
import zio.{Scope, ZIO}

import zio.schema.annotation.{caseName, discriminatorName, noDiscriminator, optionalField, transientField}
import zio.schema.codec.JsonCodec
import zio.schema.{DeriveSchema, Schema}

import zio.http.Method.GET
import zio.http.Method.{GET, POST}
import zio.http._
import zio.http.codec.{Doc, HttpCodec, QueryCodec}
import zio.http.endpoint._
Expand Down Expand Up @@ -2228,6 +2228,25 @@ object OpenAPIGenSpec extends ZIOSpecDefault {
|}""".stripMargin
assertTrue(json == toJsonAst(expectedJson))
},
test("multiple methods on same path") {
val getEndpoint = Endpoint(GET / "test")
.out[String](MediaType.text.`plain`)
val postEndpoint = Endpoint(POST / "test")
.in[String]
.out[String](Status.Created, MediaType.text.`plain`)
val generated = OpenAPIGen.fromEndpoints(
"Multiple Methods on Same Path",
"1.0",
getEndpoint,
postEndpoint,
)
val json = toJsonAst(generated)
for {
expectedJson <- ZIO.acquireReleaseWith(
ZIO.attemptBlockingIO(scala.io.Source.fromResource("endpoint/openapi/multiple-methods-on-same-path.json")),
)(buf => ZIO.attemptBlockingIO(buf.close()).orDie)(buf => ZIO.attemptBlockingIO(buf.mkString))
} yield assertTrue(json == toJsonAst(expectedJson))
},
)

}

0 comments on commit 6e230b9

Please sign in to comment.