Skip to content

Commit

Permalink
Simplify how to work with AuthedRoutes
Browse files Browse the repository at this point in the history
  • Loading branch information
alejandrohdezma committed Aug 31, 2023
1 parent 0bc64fb commit e26eab7
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 30 deletions.
18 changes: 9 additions & 9 deletions .github/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,14 @@ import org.typelevel.vault.Key

class MyAuthedRoutesSuite extends munit.Http4sSuite {

implicit val key = Key.newKey[IO, String].unsafeRunSync()
implicit val key: Key[String] = Key.newKey[IO, String].unsafeRunSync()

override def http4sMUnitClientFixture = AuthedRoutes.of[String, IO] {
case GET -> Root / "hello" as user => Ok(s"$user: Hi")
case GET -> Root / "hello" / name as user => Ok(s"$user: Hi $name")
}.orFail
.local((r: Request[IO]) => AuthedRequest(r.getContext[String], r))
.asFixture
override def http4sMUnitClientFixture = AuthedRequest.fromContext[String].andThen {
AuthedRoutes.of[String, IO] {
case GET -> Root / "hello" as user => Ok(s"$user: Hi")
case GET -> Root / "hello" / name as user => Ok(s"$user: Hi $name")
}
}.orFail.asFixture

test(GET(uri"hello" / "Jose").context("alex")).alias("Say hello to Jose") { response =>
assertIO(response.as[String], "alex: Hi Jose")
Expand All @@ -95,9 +95,9 @@ class MyAuthedRoutesSuite extends munit.Http4sSuite {
// You can also override routes per-test
test(GET(uri"hello" / "Jose").context("alex"))
.withHttpApp {
AuthedRoutes.of[String, IO] { case GET -> Root / "hello" / _ as _ => Ok("Hey") }
AuthedRequest.fromContext[String]
.andThen(AuthedRoutes.of[String, IO] { case GET -> Root / "hello" / _ as _ => Ok("Hey") })
.orFail
.local((r: Request[IO]) => AuthedRequest(r.getContext[String], r))
}
.alias("Overriden routes") { response =>
assertIO(response.as[String], "Hey")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package munit

import cats.Show
import cats.effect.IO
import cats.effect.SyncIO

Expand Down Expand Up @@ -53,7 +52,7 @@ import org.typelevel.vault.Key
* }}}
*/
@deprecated("Use `Http4sSuite` overriding `http4sMUnitClientFixture` instead", since = "0.16.0")
abstract class Http4sAuthedRoutesSuite[A: Show] extends Http4sSuite {
abstract class Http4sAuthedRoutesSuite[A] extends Http4sSuite {

@SuppressWarnings(Array("scalafix:DisableSyntax.valInAbstract"))
implicit val key: Key[A] = Key.newKey[IO, A].unsafeRunSync()
Expand All @@ -79,7 +78,7 @@ abstract class Http4sAuthedRoutesSuite[A: Show] extends Http4sSuite {

/** @inheritdoc */
override def http4sMUnitClientFixture: SyncIO[FunFixture[Client[IO]]] =
routes.orFail.local((r: Request[IO]) => AuthedRequest(r.getContext[A], r)).asFixture
AuthedRequest.fromContext[A].andThen(routes).orFail.asFixture

/** Declares a test for the provided request. That request will be executed using the routes provided in `routes`.
*
Expand Down
31 changes: 26 additions & 5 deletions modules/http4s-munit/src/main/scala/munit/Http4sMUnitSyntax.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@

package munit

import scala.util.control.NoStackTrace

import cats.data.Kleisli
import cats.data.OptionT
import cats.effect.IO
import cats.effect.Resource
import cats.effect.SyncIO
import cats.syntax.all._

import org.http4s.AuthedRequest
import org.http4s.Header
import org.http4s.HttpApp
import org.http4s.Request
Expand Down Expand Up @@ -85,19 +88,37 @@ trait Http4sMUnitSyntax extends Http4sDsl[IO] with Http4sClientDsl[IO] with AllS
/** Alias for `http://localhost` */
def localhost = uri"http://localhost"

implicit class Http4sMUnitAuthedRequestTypeOps(t: AuthedRequest.type) {

/** Provides a Kleisli for obtaining an `AuthedRequest` that can be combined with an `AuthedRoutes` instance easily.
*
* @example
* {{{
* val myAuthedRoutes: AuthedRoutes[IO, A] = ???
*
* AuthedRequest.fromContext[A].andThen(myAuthedRoutes).orFail.asFixture
* }}}
*/
def fromContext[A: Key]: Kleisli[OptionT[IO, *], Request[IO], AuthedRequest[IO, A]] =
AuthedRequest((_: Request[IO]).getContext[A])
.mapK(OptionT.liftK)

}

case class ContextNotFound(request: Request[IO])
extends RuntimeException(s"Auth context not found on request $request, remember to add one with `.context()`")
with NoStackTrace

implicit class Http4sMUnitRequestOps(request: Request[IO]) {

/** Adds a request context as an attribute using an implicit key. */
def context[A](context: A)(implicit key: Key[A]): Request[IO] =
request.withAttribute(key, context)
def context[A](context: A)(implicit key: Key[A]): Request[IO] = request.withAttribute(key, context)

/** Retrieves the context stored as an attribute using an implicit key..
*
* You can use `Request#context` to set the context attribute.
*/
def getContext[A](implicit key: Key[A]): A = request.attributes
.lookup(key)
.getOrElse(fail("Auth context not found on request, remember to add one with `.context`", clues(request)))
def getContext[A](implicit key: Key[A]): IO[A] = request.attributes.lookup(key).liftTo[IO](ContextNotFound(request))

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,18 @@ import cats.effect.IO

import org.http4s.AuthedRequest
import org.http4s.AuthedRoutes
import org.http4s.Request
import org.typelevel.vault.Key

class Http4sAuthedRoutesSuiteSuite extends Http4sSuite {

implicit val key = Key.newKey[IO, String].unsafeRunSync()
implicit val key: Key[String] = Key.newKey[IO, String].unsafeRunSync()

override def http4sMUnitClientFixture = AuthedRoutes
.of[String, IO] {
override def http4sMUnitClientFixture = AuthedRequest.fromContext.andThen {
AuthedRoutes.of[String, IO] {
case GET -> Root / "hello" as user => Ok(s"$user: Hi")
case GET -> Root / "hello" / name as user => Ok(s"$user: Hi $name")
}
.orFail
.local((r: Request[IO]) => AuthedRequest(r.getContext[String], r))
.asFixture
}.orFail.asFixture

test(GET(uri"/hello").context("jose")).alias("Test 1") { response =>
assertIO(response.as[String], "jose: Hi")
Expand All @@ -45,19 +42,19 @@ class Http4sAuthedRoutesSuiteSuite extends Http4sSuite {
}

test(GET(uri"/hello").context("jose")).withHttpApp {
AuthedRoutes
.of[String, IO] { case GET -> Root / "hello" as user => Ok(s"$user: Hey") }
AuthedRequest
.fromContext[String]
.andThen(AuthedRoutes.of[String, IO] { case GET -> Root / "hello" as user => Ok(s"$user: Hey") })
.orFail
.local((r: Request[IO]) => AuthedRequest(r.getContext[String], r))
}.alias("Test 1 (overriding routes)") { response =>
assertIO(response.as[String], "jose: Hey")
}

test(GET(uri"/hello" / "Jose").context("alex")).withHttpApp {
AuthedRoutes
.of[String, IO] { case GET -> Root / "hello" / name as user => Ok(s"$user: Hey $name") }
AuthedRequest
.fromContext[String]
.andThen(AuthedRoutes.of[String, IO] { case GET -> Root / "hello" / name as user => Ok(s"$user: Hey $name") })
.orFail
.local((r: Request[IO]) => AuthedRequest(r.getContext[String], r))
}.alias("Test 2 (overriding routes)") { response =>
assertIO(response.as[String], "alex: Hey Jose")
}
Expand Down

0 comments on commit e26eab7

Please sign in to comment.