-
Notifications
You must be signed in to change notification settings - Fork 422
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG] zio-http
aspect gets run for each Tapir app, instead of only once
#3845
Comments
zio-http
aspect gets run for each Tapir app, instead of only once
This code reproduces it for import sttp.tapir.*
import sttp.tapir.server.ziohttp.ZioHttpInterpreter
import zio.http.*
import zio.*
object ReproTapirIssue extends ZIOAppDefault:
def run =
for {
_ <- (tapirApp @@ Foo("A")).runZIO(Request.get(URL.empty.addPath("1")))
// Foo: A
_ <- (tapirApp @@ Foo("B")).runZIO(Request.get(URL.empty.addPath("2")))
// Foo: B
// Foo: B
_ <- (tapirApp @@ Foo("C")).runZIO(Request.get(URL.empty.addPath("3")))
// Foo: C
// Foo: C
// Foo: C
_ <- (zhttpApp @@ Foo("D")).runZIO(Request.get(URL.empty.addPath("1")))
// Foo: D
_ <- (zhttpApp @@ Foo("E")).runZIO(Request.get(URL.empty.addPath("2")))
// Foo: E
_ <- (zhttpApp @@ Foo("F")).runZIO(Request.get(URL.empty.addPath("3")))
// Foo: F
} yield ()
def tapirApp = (tapirRoute("1") ++ tapirRoute("2") ++ tapirRoute("3"))
def zhttpApp = (zhttpRoute("1") ++ zhttpRoute("2") ++ zhttpRoute("3"))
// returns GET route, from Tapir
def tapirRoute = (path: String) =>
ZioHttpInterpreter().toHttp(
sttp.tapir.endpoint.get
.in(path)
.serverLogic(_ => ZIO.succeed(Right(())))
)
// returns GET route
def zhttpRoute = (path: String) =>
zio.http.Routes(
zio.http.Method.GET / path -> zio.http.Handler.ok
)
// aspect that prints when executed
case class Foo(i: String) extends Middleware[Any] {
def apply[Env1 <: Any, Err](routes: Routes[Env1, Err]): Routes[Env1, Err] =
routes.transform[Env1] {
handler =>
Handler.fromFunctionZIO {
request =>
println(s"Foo: $i")
handler.runZIO(request)
}
}
}
end ReproTapirIssue |
@godspeedelbow This used to be a problem before, and due to missing test coverage it got re-introduced. I added a failing test case (see PR), however I don't know how to make a proper fix. ZIO HTTP changes too fast, there's always some problems with it ;) Anyway ... what I need is to create a |
@adamw From what I see, the issue is this line tapir/server/zio-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpInterpreter.scala Line 30 in 5b85646
|
I'd prefer @987Nabil 's approach, because currently it's impossible to concatenate two or more Tapir -> ZIO HTTP routes together due to the wildcarded path matching. |
Hm I'm that proficient in the new ZIO HTTP APIs, maybe you could create a PR for this, or suggest a change to this one? |
@adamw My issue is, that I don't know tapir 😅 I would hope that one could just extract the method and path out of the Tapir endpoint definition. The parsed results of path variables could maybe just get thrown away, if it is to complicated to hand them over. I would expect that the creation of Maybe @guersam has some time to offer help. He knows for sure zio-http well and knows tapir better than me, since he used it 🙂 |
Can you create a pattern with wildcards? How to do that? If we have a tapir endpoint, let's say, with |
@adamw That route pattern can be encoded in ZIO HTTP like this: import zio.http.Method
import zio.http.codec.PathCodec
Method.GET / "a" / "b" / PathCodec.string("c")
// results in
import zio.http.RoutePattern
import zio.http.codec.SegmentCodec
RoutePattern(
Method.GET,
PathCodec.Concat(
PathCodec.Concat(
PathCodec.Segment(
SegmentCodec.Literal("a"),
),
PathCodec.Segment(
SegmentCodec.Literal("b")
),
???: Combiner.WithOut[Unit, Unit, Unit]
),
PathCodec.Text("c"),
???: Combiner.WithOut[Unit, String, String]
)
) There are some major obstacles when converting Tapir path input to ZIO HTTP route pattern:
After some research, I realized that building Besides the mismatch between the path encodings, the sealed @987Nabil What do you think? |
I think we don't need to change things in zio http, since we have transformOrFail on the codecs. Not on the segment, but on the path codec |
@guersam @987Nabil hm I'm still not sure how/if this can be done, is it possible to generate zio-http code which will run a route given e.g. a path representation such as |
@adamw like this? Routes(
Method.GET / "a" / "b" / string("param1") / "c" / string("param2") / "d" -> handler((param1: String, param2: String, req: Request) => ZIO.succeed(Response.ok))
) Note, the |
@987Nabil thanks, but I'd need this in a generic way - sth like given a |
@adamw I did not try to run it, but I think this should work import zio.http._
case class Wildcard(name: String)
val segments: List[Any] = List("api", "v1", "users", Wildcard("id"))
val pattern: RoutePattern[Any] = segments.map {
case s: String => PathCodec.literal(s)
case w: Wildcard => PathCodec.string(w.name)
}.foldLeft(RoutePattern(Method.GET, PathCodec.empty).asInstanceOf[RoutePattern[Any]]){
case (codec, next) => (codec / next).asInstanceOf[RoutePattern[Any]]
}
val routes = Routes(
pattern -> handler((req: Request) => ZIO.succeed(Response.text("Hello World")))
) |
@987Nabil thanks, something similar indeed did work :) |
Glad I could help :) |
Tapir version:
1.8.4 - 1.8.10
Scala version:
3.3.0
This occurs first in Tapir version
1.8.4
and has been like this each version since.[email protected]
/[email protected]
is the last versions that produces the expected correct behavior.Describe the bug
When I combine multiple Tapir apps, the aspect is executed for each route until the request is handled. The zio-http equivalent behaves as expected, logging once, regardless of how many apps.
How to reproduce?
Additional information
The text was updated successfully, but these errors were encountered: