Skip to content

Commit

Permalink
Fix extractFromRequest in zio http endpoints (#3913)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamw authored Jul 8, 2024
1 parent 3f1f124 commit 424e2d2
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,30 +87,25 @@ trait ZioHttpInterpreter[R] {
case _ => false
}

val pattern = if (hasPath) {
val pathPrefixPattern = if (hasPath) {
val initialPattern = RoutePattern(Method.ANY, PathCodec.empty).asInstanceOf[RoutePattern[Any]]
// The second tuple parameter specifies if PathCodec.trailing has already been added to the route's pattern -
// it can only be added once. It's possible that an endpoint contains both ExtractFromRequest & PathsCapture,
// which would cause it to be added twice.
inputs
.foldLeft((initialPattern, false)) { case ((p, trailingAdded), component) =>
val base = inputs
.foldLeft(initialPattern) { case (p, component) =>
component match {
case i: EndpointInput.PathCapture[_] =>
((p / PathCodec.string(i.name.getOrElse("?"))).asInstanceOf[RoutePattern[Any]], trailingAdded)
case _: EndpointInput.ExtractFromRequest[_] if !trailingAdded =>
((p / PathCodec.trailing).asInstanceOf[RoutePattern[Any]], true)
case _: EndpointInput.PathsCapture[_] if !trailingAdded => ((p / PathCodec.trailing).asInstanceOf[RoutePattern[Any]], true)
case i: EndpointInput.FixedPath[_] => (p / PathCodec.literal(i.s), trailingAdded)
case _ => (p, trailingAdded)
case i: EndpointInput.PathCapture[_] => (p / PathCodec.string(i.name.getOrElse("?"))).asInstanceOf[RoutePattern[Any]]
case i: EndpointInput.FixedPath[_] => p / PathCodec.literal(i.s)
case _ => p
}
}
._1
// because we capture the path prefix, we add a matcher for arbitrary other path components (which might be
// handled by tapir's `paths` or `extractFromRequest`)
base / PathCodec.trailing
} else {
// if there are no path inputs, we return a catch-all
RoutePattern(Method.ANY, PathCodec.trailing).asInstanceOf[RoutePattern[Any]]
}

Route.handled(pattern)(Handler.fromFunctionHandler { (request: Request) => handleRequest(request, sesForPathTemplate) })
Route.handled(pathPrefixPattern)(Handler.fromFunctionHandler { (request: Request) => handleRequest(request, sesForPathTemplate) })
}

Routes(Chunk.fromIterable(handlers))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,24 @@ class ZioHttpServerTest extends TestSuite {

Unsafe.unsafe(implicit u => r.unsafe.runToFuture(test))
},
// https://github.com/softwaremill/tapir/issues/3907
Test("extractFromRequest in the middle") {
val ep = endpoint.get
.in(path[String])
.in(extractFromRequest(_.method))
.in("test" / path[String])
.out(stringBody)
.zServerLogic[Any](_ => ZIO.succeed("works"))
val route = ZioHttpInterpreter().toHttp(ep)

val test: UIO[Assertion] = route
.runZIO(Request.get(url = URL(Path.empty / "p1" / "test" / "p2")))
.flatMap(response => response.body.asString)
.map(_ shouldBe "works")
.catchAll(_ => ZIO.succeed[Assertion](fail("Unable to extract body from Http response")))

Unsafe.unsafe(implicit u => r.unsafe.runToFuture(test))
},
// https://github.com/softwaremill/tapir/issues/2849
Test("Streaming works through the stub backend") {
// given
Expand Down

0 comments on commit 424e2d2

Please sign in to comment.