diff --git a/docs/reference/aop/handler_aspect.md b/docs/reference/aop/handler_aspect.md index 4c3688557c..2181e6f025 100644 --- a/docs/reference/aop/handler_aspect.md +++ b/docs/reference/aop/handler_aspect.md @@ -303,11 +303,11 @@ object UserRepository { ```scala mdoc:silent Routes( - Method.GET / "user" / int("userId") -> sessionMiddleware -> handler { - (userId: Int, session: Session, request: Request) => - UserRepository.getUser(session.organizationId, userId) + Method.GET / "user" / int("userId") -> handler { + (userId: Int, request: Request) => + withContext((session: Session) => UserRepository.getUser(session.organizationId, userId)) } -) +) @@ sessionMiddleware ``` The `HandlerAspect` companion object provides a number of helpful constructors for these middlewares. diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/AuthSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/AuthSpec.scala index 3546e06804..32274f0f50 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/AuthSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/AuthSpec.scala @@ -82,8 +82,8 @@ object AuthSpec extends ZIOHttpSpec with TestExtensions { test("Extract username via context with Routes") { val app = { Routes( - Method.GET / "context" -> basicAuthContextM -> - handler { (c: AuthContext, _: Request) => Response.text(c.value) }, + Method.GET / "context" -> + handler { (_: Request) => withContext((c: AuthContext) => Response.text(c.value)) } @@ basicAuthContextM, ) } assertZIO( diff --git a/zio-http/shared/src/main/scala/zio/http/Route.scala b/zio-http/shared/src/main/scala/zio/http/Route.scala index 69254f602d..9db2d28d13 100644 --- a/zio-http/shared/src/main/scala/zio/http/Route.scala +++ b/zio-http/shared/src/main/scala/zio/http/Route.scala @@ -53,7 +53,9 @@ sealed trait Route[-Env, +Err] { self => final def handleError(f: Err => Response)(implicit trace: Trace): Route[Env, Nothing] = self.handleErrorCauseZIO(c => ErrorResponseConfig.configRef.get.map(Response.fromCauseWith(c, _)(f))) - final def handleErrorZIO(f: Err => ZIO[Any, Nothing, Response])(implicit trace: Trace): Route[Env, Nothing] = + final def handleErrorZIO[Env1 <: Env]( + f: Err => ZIO[Env1, Nothing, Response], + )(implicit trace: Trace): Route[Env1, Nothing] = self.handleErrorCauseZIO { cause => cause.failureOrCause match { case Left(err) => f(err) @@ -73,27 +75,25 @@ sealed trait Route[-Env, +Err] { self => case Handled(routePattern, handler, location) => Handled(routePattern, handler.map(_.mapErrorCause(c => f(c.asInstanceOf[Cause[Nothing]]))), location) - case Unhandled(rpm, handler, zippable, location) => - val handler2: Handler[Any, Nothing, RoutePattern[_], Handler[Env, Response, Request, Response]] = { - Handler.fromFunction[RoutePattern[_]] { pattern => - val paramHandler = { - Handler.fromFunctionZIO[(rpm.Context, Request)] { case (ctx, request) => - pattern.asInstanceOf[RoutePattern[rpm.PathInput]].decode(request.method, request.path) match { - case Left(error) => ZIO.dieMessage(error) - case Right(value) => - val params = rpm.zippable.zip(value, ctx) - - handler(zippable.zip(params, request)) + case Unhandled(pattern, handler0, zippable, location) => + val handler2: Handler[Any, Nothing, RoutePattern[_], Handler[Env, Response, Request, Response]] = + handler { (pattern: RoutePattern[_]) => + val paramHandler = + handler { (request: Request) => + pattern.decode(request.method, request.path) match { + case Left(error) => ZIO.dieMessage(error) + case Right(params) => + handler0.asInstanceOf[Handler[Any, Err, Any, Response]]( + zippable.asInstanceOf[Zippable[Any, Any]].zip(params, request), + ) } } - } // Sandbox before applying aspect: - rpm.aspect.applyHandlerContext(paramHandler.mapErrorCause(f)) + paramHandler.mapErrorCause(f) } - } - Handled(rpm.routePattern, handler2, location) + Handled(pattern, handler2, location) } /** @@ -102,33 +102,44 @@ sealed trait Route[-Env, +Err] { self => * can be used to convert a route that does not handle its errors into one * that does handle its errors. */ - final def handleErrorCauseZIO( - f: Cause[Err] => ZIO[Any, Nothing, Response], - )(implicit trace: Trace): Route[Env, Nothing] = + final def handleErrorCauseZIO[Env1 <: Env]( + f: Cause[Err] => ZIO[Env1, Nothing, Response], + )(implicit trace: Trace): Route[Env1, Nothing] = self match { - case Provided(route, env) => Provided(route.handleErrorCauseZIO(f), env) - case Augmented(route, aspect) => Augmented(route.handleErrorCauseZIO(f), aspect) + case Provided(route, env) => + Route.handledIgnoreParams(route.routePattern)( + Handler.fromZIO(ZIO.environment[Env1]).flatMap { (env1: ZEnvironment[Env1]) => + val env0 = env.asInstanceOf[ZEnvironment[Any]] ++ env1.asInstanceOf[ZEnvironment[Any]] + route + .handleErrorCauseZIO(f) + .toHandler + .asInstanceOf[Handler[Any, Response, Request, Response]] + .provideEnvironment(env0) + }, + ) + case Augmented(route, aspect) => + Augmented(route.handleErrorCauseZIO(f).asInstanceOf[Route[Any, Nothing]], aspect) case Handled(routePattern, handler, location) => - Handled[Env](routePattern, handler.map(_.mapErrorCauseZIO(c => f(c.asInstanceOf[Cause[Nothing]]))), location) + Handled(routePattern, handler.map(_.mapErrorCauseZIO(c => f(c.asInstanceOf[Cause[Nothing]]))), location) - case Unhandled(rpm, handler, zippable, location) => - val handler2: Handler[Any, Nothing, RoutePattern[_], Handler[Env, Response, Request, Response]] = { - Handler.fromFunction[RoutePattern[_]] { pattern => + case Unhandled(pattern, handler0, zippable, location) => + val handler2: Handler[Any, Nothing, RoutePattern[_], Handler[Env1, Response, Request, Response]] = { + handler { (pattern: RoutePattern[_]) => val paramHandler = - Handler.fromFunctionZIO[(rpm.Context, Request)] { case (ctx, request) => - pattern.asInstanceOf[RoutePattern[rpm.PathInput]].decode(request.method, request.path) match { - case Left(error) => ZIO.dieMessage(error) - case Right(value) => - val params = rpm.zippable.zip(value, ctx) - - handler(zippable.zip(params, request)) + handler { (request: Request) => + pattern.decode(request.method, request.path) match { + case Left(error) => ZIO.dieMessage(error) + case Right(params) => + handler0.asInstanceOf[Handler[Any, Err, Any, Response]]( + zippable.asInstanceOf[Zippable[Any, Any]].zip(params, request), + ) } } - rpm.aspect.applyHandlerContext(paramHandler.mapErrorCauseZIO(f)) + paramHandler.mapErrorCauseZIO(f) } } - Handled(rpm.routePattern, handler2, location) + Handled(pattern, handler2, location) } /** @@ -139,7 +150,7 @@ sealed trait Route[-Env, +Err] { self => self match { case Provided(route, env) => Provided(route.mapError(fxn), env) case Augmented(route, aspect) => Augmented(route.mapError(fxn), aspect) - case Handled(routePattern, handler, location) => Handled[Env](routePattern, handler, location) + case Handled(routePattern, handler, location) => Handled(routePattern, handler, location) case Unhandled(rpm, handler, zippable, location) => Unhandled(rpm, handler.mapError(fxn), zippable, location) } @@ -154,7 +165,7 @@ sealed trait Route[-Env, +Err] { self => self match { case Provided(route, env) => Provided(route.mapErrorZIO(fxn), env) case Augmented(route, aspect) => Augmented(route.mapErrorZIO(fxn), aspect) - case Handled(routePattern, handler, location) => Handled[Env](routePattern, handler, location) + case Handled(routePattern, handler, location) => Handled(routePattern, handler, location) case Unhandled(rpm, handler, zippable, location) => Unhandled(rpm, handler.mapErrorZIO(fxn), zippable, location) } @@ -177,43 +188,39 @@ sealed trait Route[-Env, +Err] { self => */ final def handleErrorRequestCause(f: (Request, Cause[Err]) => Response)(implicit trace: Trace): Route[Env, Nothing] = self match { - case Provided(route, env) => Provided(route.handleErrorRequestCause(f), env) - case Augmented(route, aspect) => Augmented(route.handleErrorRequestCause(f), aspect) - case Handled(routePattern, handler, location) => - Handled[Env]( + case Provided(route, env) => Provided(route.handleErrorRequestCause(f), env) + case Augmented(route, aspect) => Augmented(route.handleErrorRequestCause(f), aspect) + case Handled(routePattern, handler0, location) => + Handled( routePattern, - handler.map { handler => - Handler.fromFunctionHandler[Request] { (req: Request) => - handler.mapErrorCause(c => f(req, c.asInstanceOf[Cause[Nothing]])) + handler0.map { h => + Handler.fromFunctionHandler { (req: Request) => + h.mapErrorCause(c => f(req, c.asInstanceOf[Cause[Nothing]])) } }, location, ) - case Unhandled(rpm, handler, zippable, location) => + case Unhandled(pattern, handler0, zippable, location) => val handler2: Handler[Any, Nothing, RoutePattern[_], Handler[Env, Response, Request, Response]] = { - Handler.fromFunction[RoutePattern[_]] { pattern => + handler { (pattern: RoutePattern[_]) => val paramHandler = - Handler.fromFunctionZIO[(rpm.Context, Request)] { case (ctx, request) => - pattern.asInstanceOf[RoutePattern[rpm.PathInput]].decode(request.method, request.path) match { - case Left(error) => ZIO.dieMessage(error) - case Right(value) => - val params = rpm.zippable.zip(value, ctx) - - handler(zippable.zip(params, request)) + handler { (request: Request) => + pattern.decode(request.method, request.path) match { + case Left(error) => ZIO.dieMessage(error) + case Right(params) => + handler0.asInstanceOf[Handler[Any, Err, Any, Response]]( + zippable.asInstanceOf[Zippable[Any, Any]].zip(params, request), + ) } } // Sandbox before applying aspect: - rpm.aspect.applyHandlerContext( - Handler.fromFunctionHandler[(rpm.Context, Request)] { case (_, req) => - paramHandler.mapErrorCause(f(req, _)) - }, - ) + Handler.fromFunctionHandler((req: Request) => paramHandler.mapErrorCause(f(req, _))) } } - Handled(rpm.routePattern, handler2, location) + Handled(pattern, handler2, location) } /** @@ -223,45 +230,51 @@ sealed trait Route[-Env, +Err] { self => * convert a route that does not handle its errors into one that does handle * its errors. */ - final def handleErrorRequestCauseZIO( - f: (Request, Cause[Err]) => ZIO[Any, Nothing, Response], - )(implicit trace: Trace): Route[Env, Nothing] = + final def handleErrorRequestCauseZIO[Env1 <: Env]( + f: (Request, Cause[Err]) => ZIO[Env1, Nothing, Response], + )(implicit trace: Trace): Route[Env1, Nothing] = self match { - case Provided(route, env) => Provided(route.handleErrorRequestCauseZIO(f), env) - case Augmented(route, aspect) => Augmented(route.handleErrorRequestCauseZIO(f), aspect) + case Provided(route, env) => + Route.handledIgnoreParams(route.routePattern)( + Handler.fromZIO(ZIO.environment[Env1]).flatMap { (env1: ZEnvironment[Env1]) => + val env0 = env.asInstanceOf[ZEnvironment[Any]] ++ env1.asInstanceOf[ZEnvironment[Any]] + route + .handleErrorRequestCauseZIO(f) + .toHandler + .asInstanceOf[Handler[Any, Response, Request, Response]] + .provideEnvironment(env0) + }, + ) + case Augmented(route, aspect) => + Augmented(route.handleErrorRequestCauseZIO(f).asInstanceOf[Route[Any, Nothing]], aspect) case Handled(routePattern, handler, location) => - Handled[Env]( + Handled( routePattern, handler.map { handler => - Handler.fromFunctionHandler[Request] { (req: Request) => + Handler.fromFunctionHandler { (req: Request) => handler.mapErrorCauseZIO(c => f(req, c.asInstanceOf[Cause[Nothing]])) } }, location, ) - case Unhandled(rpm, handler, zippable, location) => - val handler2: Handler[Any, Nothing, RoutePattern[_], Handler[Env, Response, Request, Response]] = { - Handler.fromFunction[RoutePattern[_]] { pattern => + case Unhandled(routePattern, handler0, zippable, location) => + val handler2: Handler[Any, Nothing, RoutePattern[_], Handler[Env1, Response, Request, Response]] = + handler { (pattern: RoutePattern[_]) => val paramHandler = - Handler.fromFunctionZIO[(rpm.Context, Request)] { case (ctx, request) => - pattern.asInstanceOf[RoutePattern[rpm.PathInput]].decode(request.method, request.path) match { - case Left(error) => ZIO.dieMessage(error) - case Right(value) => - val params = rpm.zippable.zip(value, ctx) - - handler(zippable.zip(params, request)) + handler { (request: Request) => + pattern.decode(request.method, request.path) match { + case Left(error) => ZIO.dieMessage(error) + case Right(params) => + handler0.asInstanceOf[Handler[Any, Err, Any, Response]]( + zippable.asInstanceOf[Zippable[Any, Any]].zip(params, request), + ) } } - rpm.aspect.applyHandlerContext( - Handler.fromFunctionHandler[(rpm.Context, Request)] { case (_, req) => - paramHandler.mapErrorCauseZIO(f(req, _)) - }, - ) + Handler.fromFunctionHandler((req: Request) => paramHandler.mapErrorCauseZIO(f(req, _))) } - } - Handled(rpm.routePattern, handler2, location) + Handled(routePattern, handler2, location) } /** @@ -277,12 +290,12 @@ sealed trait Route[-Env, +Err] { self => def nest(prefix: PathCodec[Unit]): Route[Env, Err] = self match { - case Provided(route, env) => Provided(route.nest(prefix), env) - case Augmented(route, aspect) => Augmented(route.nest(prefix), aspect) - case Handled(routePattern, handler, location) => Handled[Env](routePattern.nest(prefix), handler, location) + case Provided(route, env) => Provided(route.nest(prefix), env) + case Augmented(route, aspect) => Augmented(route.nest(prefix), aspect) + case Handled(pattern, handler, location) => Handled(pattern.nest(prefix), handler, location) - case Unhandled(rpm, handler, zippable, location) => - Unhandled(rpm.prefix(prefix), handler, zippable, location) + case Unhandled(pattern, handler, zippable, location) => + Unhandled(pattern.nest(prefix), handler, zippable, location) } final def provideEnvironment(env: ZEnvironment[Env]): Route[Any, Err] = @@ -321,112 +334,52 @@ sealed trait Route[-Env, +Err] { self => } object Route { - def handled[Env]( + def handledIgnoreParams[Env]( routePattern: RoutePattern[_], - )(handler: Handler[Env, Response, Request, Response])(implicit trace: Trace): Route[Env, Nothing] = { + )(handler0: Handler[Env, Response, Request, Response])(implicit trace: Trace): Route[Env, Nothing] = { // Sandbox before constructing: - Route.Handled(routePattern, Handler.fromFunction[RoutePattern[_]](_ => handler.sandbox), Trace.empty) + Route.Handled(routePattern, handler((_: RoutePattern[_]) => handler0.sandbox), Trace.empty) } - def handled[Params, Env](rpm: Route.Builder[Env, Params]): HandledConstructor[Env, Params] = + def handled[Params, Env](rpm: RoutePattern[Params]): HandledConstructor[Env, Params] = new HandledConstructor[Env, Params](rpm) val notFound: Route[Any, Nothing] = - Handled(RoutePattern.any, Handler.fromFunction[RoutePattern[_]](_ => Handler.notFound), Trace.empty) + Handled(RoutePattern.any, handler((_: RoutePattern[_]) => Handler.notFound), Trace.empty) - def route[Params](routePattern: RoutePattern[Params]): UnhandledConstructor[Any, Params] = - route(Route.Builder(routePattern, HandlerAspect.identity)) - - def route[Params, Env](rpm: Route.Builder[Env, Params]): UnhandledConstructor[Env, Params] = + def route[Params, Env](rpm: RoutePattern[Params]): UnhandledConstructor[Env, Params] = new UnhandledConstructor[Env, Params](rpm) - final class HandledConstructor[-Env, Params](val rpm: Route.Builder[Env, Params]) extends AnyVal { + final class HandledConstructor[-Env, Params](val pattern: RoutePattern[Params]) extends AnyVal { def apply[Env1 <: Env, In]( - handler: Handler[Env1, Response, In, Response], + handler0: Handler[Env1, Response, In, Response], )(implicit zippable: Zippable.Out[Params, Request, In], trace: Trace): Route[Env1, Nothing] = { - val handler2: Handler[Any, Nothing, RoutePattern[_], Handler[Env1, Response, Request, Response]] = { - Handler.fromFunction[RoutePattern[_]] { pattern => + val handler2: Handler[Any, Nothing, RoutePattern[Params], Handler[Env1, Response, Request, Response]] = { + handler { (pattern: RoutePattern[Params]) => val paramHandler = - Handler.fromFunctionZIO[(rpm.Context, Request)] { case (ctx, request) => - pattern.asInstanceOf[RoutePattern[rpm.PathInput]].decode(request.method, request.path) match { - case Left(error) => ZIO.dieMessage(error) - case Right(value) => - val params = rpm.zippable.zip(value, ctx) - - handler(zippable.zip(params, request)) + handler { (request: Request) => + pattern.decode(request.method, request.path) match { + case Left(error) => ZIO.dieMessage(error) + case Right(params) => handler0(zippable.zip(params, request)) } } // Sandbox before applying aspect: - rpm.aspect.applyHandlerContext(paramHandler.sandbox) + paramHandler.sandbox } } - Handled(rpm.routePattern, handler2, trace) + Handled(pattern, handler2, trace) } } - final class UnhandledConstructor[-Env, Params](val rpm: Route.Builder[Env, Params]) extends AnyVal { + final class UnhandledConstructor[-Env, Params](val rpm: RoutePattern[Params]) extends AnyVal { def apply[Env1 <: Env, Err, Input]( handler: Handler[Env1, Err, Input, Response], )(implicit zippable: Zippable.Out[Params, Request, Input], trace: Trace): Route[Env1, Err] = Unhandled(rpm, handler, zippable, trace) } - /** - * A combination of a route pattern and aspect, used for building routes that - * depend on aspect context (such as authentication). - */ - sealed abstract class Builder[-Env, A] { self => - type PathInput - type Context - - def routePattern: RoutePattern[PathInput] - def aspect: HandlerAspect[Env, Context] - def zippable: Zippable.Out[PathInput, Context, A] - - /** - * Constructs a route from this route pattern and aspect. - */ - def ->[Env1 <: Env, Err, I](handler: Handler[Env1, Err, I, Response])(implicit - input: RequestHandlerInput[A, I], - trace: Trace, - ): Route[Env1, Err] = { - implicit val z = input.zippable - - Route.route[A, Env1](self)(handler) - } - - def prefix(path: PathCodec[Unit]): Builder[Env, A] = - new Builder[Env, A] { - type PathInput = self.PathInput - type Context = self.Context - - def routePattern: RoutePattern[PathInput] = self.routePattern.nest(path) - def aspect: HandlerAspect[Env, Context] = self.aspect - def zippable: Zippable.Out[PathInput, Context, A] = self.zippable - } - - def provideEnvironment(env: ZEnvironment[Env]): Route.Builder[Any, A] = { - implicit val z = zippable - - Route.Builder(routePattern, aspect.provideEnvironment(env)) - } - } - object Builder { - def apply[Env, PI, MC, Out](rp: RoutePattern[PI], mc: HandlerAspect[Env, MC])(implicit - z: Zippable.Out[PI, MC, Out], - ): Route.Builder[Env, Out] = - new Route.Builder[Env, Out] { - type PathInput = PI - type Context = MC - - def routePattern: RoutePattern[PathInput] = rp - def aspect: HandlerAspect[Env, Context] = mc - def zippable: Zippable.Out[PathInput, Context, Out] = z - } - } - private final case class Provided[Env, +Err]( route: Route[Env, Err], env: ZEnvironment[Env], @@ -455,9 +408,9 @@ object Route { override def toString = s"Route.Augmented(${route}, ${aspect})" } - private final case class Handled[-Env]( - routePattern: RoutePattern[_], - handler: Handler[Any, Nothing, RoutePattern[_], Handler[Env, Response, Request, Response]], + private final case class Handled[-Env, Params]( + routePattern: RoutePattern[Params], + handler: Handler[Any, Nothing, RoutePattern[Params], Handler[Env, Response, Request, Response]], location: Trace, ) extends Route[Env, Nothing] { override def toHandler(implicit ev: Nothing <:< Response, trace: Trace): Handler[Env, Response, Request, Response] = @@ -465,15 +418,14 @@ object Route { override def toString = s"Route.Handled(${routePattern}, ${location})" } + private final case class Unhandled[Params, Input, -Env, +Err]( - rpm: Route.Builder[Env, Params], + routePattern: RoutePattern[Params], handler: Handler[Env, Err, Input, Response], zippable: Zippable.Out[Params, Request, Input], location: Trace, ) extends Route[Env, Err] { self => - def routePattern = rpm.routePattern - override def toHandler(implicit ev: Err <:< Response, trace: Trace): Handler[Env, Response, Request, Response] = { convert(handler.asErrorType[Response]) } @@ -485,7 +437,7 @@ object Route { )(implicit trace: Trace): Handler[Env1, Response, Request, Response] = { implicit val z = zippable - Route.handled(rpm)(handler).toHandler + Route.handled(routePattern)(handler).toHandler } } diff --git a/zio-http/shared/src/main/scala/zio/http/RoutePattern.scala b/zio-http/shared/src/main/scala/zio/http/RoutePattern.scala index 2c47d99d48..f0adad7730 100644 --- a/zio-http/shared/src/main/scala/zio/http/RoutePattern.scala +++ b/zio-http/shared/src/main/scala/zio/http/RoutePattern.scala @@ -41,6 +41,7 @@ import zio.http.codec._ * `RoutePattern.GET`. */ final case class RoutePattern[A](method: Method, pathCodec: PathCodec[A]) { self => + type Params = A /** * Attaches documentation to the route pattern, which may be used when @@ -61,7 +62,7 @@ final case class RoutePattern[A](method: Method, pathCodec: PathCodec[A]) { self zippable: RequestHandlerInput[A, I], trace: zio.Trace, ): Route[Env, Err] = - Route.route(Route.Builder(self, HandlerAspect.identity))(handler)(zippable.zippable, trace) + Route.route(self)(handler)(zippable.zippable, trace) /** * Creates a route from this pattern and the specified handler, which ignores @@ -72,16 +73,7 @@ final case class RoutePattern[A](method: Method, pathCodec: PathCodec[A]) { self def ->[Env, Err](handler: Handler[Env, Response, Request, Response])(implicit trace: zio.Trace, ): Route[Env, Err] = - Route.handled(self)(handler) - - /** - * Combines this route pattern with the specified middleware, which can be - * used to build a route by providing a handler. - */ - def ->[Env, Context](middleware: HandlerAspect[Env, Context])(implicit - zippable: Zippable[A, Context], - ): Route.Builder[Env, zippable.Out] = - Route.Builder(self, middleware)(zippable) + Route.handledIgnoreParams(self)(handler) def alternatives: List[RoutePattern[A]] = pathCodec.alternatives.map(RoutePattern(method, _)) @@ -147,7 +139,7 @@ final case class RoutePattern[A](method: Method, pathCodec: PathCodec[A]) { self def unapply(tuple: (Method, Path)): Option[A] = decode(tuple._1, tuple._2).toOption } -object RoutePattern { +object RoutePattern { import PathCodec.SegmentSubtree val CONNECT: RoutePattern[Unit] = fromMethod(Method.CONNECT) diff --git a/zio-http/shared/src/main/scala/zio/http/Routes.scala b/zio-http/shared/src/main/scala/zio/http/Routes.scala index 84136a6595..720a959683 100644 --- a/zio-http/shared/src/main/scala/zio/http/Routes.scala +++ b/zio-http/shared/src/main/scala/zio/http/Routes.scala @@ -82,7 +82,9 @@ final case class Routes[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) { s def handleError(f: Err => Response)(implicit trace: Trace): Routes[Env, Nothing] = new Routes(routes.map(_.handleError(f))) - def handleErrorZIO(f: Err => ZIO[Any, Nothing, Response])(implicit trace: Trace): Routes[Env, Nothing] = + def handleErrorZIO[Env1 <: Env](f: Err => ZIO[Env1, Nothing, Response])(implicit + trace: Trace, + ): Routes[Env1, Nothing] = new Routes(routes.map(_.handleErrorZIO(f))) /** diff --git a/zio-http/shared/src/main/scala/zio/http/codec/QueryCodecs.scala b/zio-http/shared/src/main/scala/zio/http/codec/QueryCodecs.scala index aac9ce744c..4bc203f5e1 100644 --- a/zio-http/shared/src/main/scala/zio/http/codec/QueryCodecs.scala +++ b/zio-http/shared/src/main/scala/zio/http/codec/QueryCodecs.scala @@ -15,6 +15,8 @@ */ package zio.http.codec +import scala.annotation.tailrec + import zio.stacktracer.TracingImplicits.disableAutoTrace import zio.schema.Schema @@ -118,10 +120,14 @@ private[codec] trait QueryCodecs { ) } - private def supportedElementSchema(elementSchema: Schema[Any]) = - elementSchema.isInstanceOf[Schema.Primitive[_]] || + @tailrec + private def supportedElementSchema(elementSchema: Schema[Any]): Boolean = elementSchema match { + case Schema.Lazy(schema0) => supportedElementSchema(schema0()) + case _ => + elementSchema.isInstanceOf[Schema.Primitive[_]] || elementSchema.isInstanceOf[Schema.Enum[_]] && elementSchema.annotations.exists(_.isInstanceOf[simpleEnum]) || elementSchema.isInstanceOf[Schema.Record[_]] && elementSchema.asInstanceOf[Schema.Record[_]].fields.size == 1 + } def queryAll[A](implicit schema: Schema[A]): QueryCodec[A] = schema match { diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala index 786a149b36..95a0222b9a 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala @@ -360,7 +360,7 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( } } - Route.handled(self.route)(handler) + Route.handledIgnoreParams(self.route)(handler) } /**