Skip to content

Commit

Permalink
Add prefix method to Route/Routes (#2547)
Browse files Browse the repository at this point in the history
Add nest method to `Route`/`Routes`
  • Loading branch information
987Nabil authored Dec 20, 2023
1 parent fa99f66 commit d1e87cb
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 1 deletion.
22 changes: 22 additions & 0 deletions zio-http/src/main/scala/zio/http/Route.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ package zio.http

import zio._

import zio.http.codec.PathCodec

/*
* Represents a single route, which has either handled its errors by converting
* them into responses, or which has polymorphic errors, which must later be
Expand Down Expand Up @@ -207,6 +209,16 @@ sealed trait Route[-Env, +Err] { self =>
*/
def location: Trace

def nest(prefix: PathCodec[Unit])(implicit ev: Err <:< Response): 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(routePattern.nest(prefix), handler, location)

case Unhandled(rpm, handler, zippable, location) =>
Unhandled(rpm.prefix(prefix), handler, zippable, location)
}

final def provideEnvironment(env: ZEnvironment[Env]): Route[Any, Err] =
Route.Provided(self, env)

Expand Down Expand Up @@ -316,6 +328,16 @@ object Route {
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

Expand Down
3 changes: 3 additions & 0 deletions zio-http/src/main/scala/zio/http/RoutePattern.scala
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ final case class RoutePattern[A](method: Method, pathCodec: PathCodec[A]) { self
*/
def matches(method: Method, path: Path): Boolean = decode(method, path).isRight

def nest(prefix: PathCodec[Unit]): RoutePattern[A] =
copy(pathCodec = prefix ++ pathCodec)

/**
* Renders the route pattern as a string.
*/
Expand Down
5 changes: 5 additions & 0 deletions zio-http/src/main/scala/zio/http/Routes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ package zio.http

import zio._

import zio.http.codec.PathCodec

/**
* Represents a collection of routes, each of which is defined by a pattern and
* a handler. This data type can be thought of as modeling a routing table,
Expand Down Expand Up @@ -90,6 +92,9 @@ final class Routes[-Env, +Err] private (val routes: Chunk[zio.http.Route[Env, Er
def handleErrorCauseZIO(f: Cause[Err] => ZIO[Any, Nothing, Response])(implicit trace: Trace): Routes[Env, Nothing] =
new Routes(routes.map(_.handleErrorCauseZIO(f)))

def nest(prefix: PathCodec[Unit])(implicit trace: Trace, ev: Err <:< Response): Routes[Env, Err] =
new Routes(self.routes.map(_.nest(prefix)))

/**
* Handles all typed errors in the routes by converting them into responses,
* taking into account the request that caused the error. This method can be
Expand Down
7 changes: 6 additions & 1 deletion zio-http/src/main/scala/zio/http/codec/PathCodec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import scala.language.implicitConversions

import zio._

import zio.http.Path
import zio.http._

/**
* A codec for paths, which consists of segments, where each segment may be a
Expand All @@ -48,6 +48,11 @@ sealed trait PathCodec[A] { self =>
final def /[B](that: PathCodec[B])(implicit combiner: Combiner[A, B]): PathCodec[combiner.Out] =
self ++ that

final def /[Env](routes: Routes[Env, Response])(implicit
ev: PathCodec[A] <:< PathCodec[Unit],
): Routes[Env, Response] =
routes.nest(ev(self))

final def asType[B](implicit ev: A =:= B): PathCodec[B] = self.asInstanceOf[PathCodec[B]]

/**
Expand Down
10 changes: 10 additions & 0 deletions zio-http/src/test/scala/zio/http/RouteSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ object RouteSpec extends ZIOHttpSpec {
def extractStatus(response: Response): Status = response.status

def spec = suite("RouteSpec")(
suite("Route#prefix")(
test("prefix should add a prefix to the route") {
val route =
Method.GET / "foo" -> handler(Response.ok)

val prefixed = route.nest("bar")

assertTrue(prefixed.isDefinedAt(Request.get(url"/bar/foo")))
},
),
suite("Route#sandbox")(
test("infallible route does not change under sandbox") {
val route =
Expand Down

0 comments on commit d1e87cb

Please sign in to comment.