Skip to content

Instantly share code, notes, and snippets.

@vdebergue
Last active December 14, 2015 12:36
Show Gist options
  • Save vdebergue/851b3e4b29e320cbe83b to your computer and use it in GitHub Desktop.
Save vdebergue/851b3e4b29e320cbe83b to your computer and use it in GitHub Desktop.
ActionPartial
package play.api.mvc
// needed to access protected method ActionRefiner#refine
import scala.concurrent.Future
import scala.language.higherKinds
trait ActionPartial[R[_], P[_]] extends ActionRefiner[R, P] { self =>
def partial[A]: PartialFunction[R[A], Future[Either[Result, P[A]]]]
def refine[A](request: R[A]): Future[Either[Result, P[A]]] = partial(request)
def orElse(other: ActionPartial[R, P]): ActionPartial[R, P] = new ActionPartial[R, P] {
def partial[A] = self.partial[A].orElse(other.partial[A])
}
}
object ActionPartial {
def toActionBuilder[P[_]](ap: ActionPartial[Request, P]): ActionBuilder[P] = new ActionBuilder[P] {
def invokeBlock[A](request: Request[A], block: (P[A]) => Future[Result]): Future[Result] = ap.invokeBlock(request, block)
}
}
object Test {
import scala.concurrent.ExecutionContext.Implicits.global
// check if header Authorization is present
val hasAuthorization = new ActionPartial[Request, Request] {
def partial[A] = {
case request if request.headers.keys.exists(_ == "Authorization") =>
// check auth ...
Future.successful(Left(Results.Forbidden("auth")))
}
}
// catch all and send Forbidden result
val forbidden = new ActionPartial[Request, Request] {
def partial[A] = {
case _ => Future.successful(Left(Results.Forbidden))
}
}
// combine the two partial actions
val combo: ActionPartial[Request, Request] = hasAuthorization.orElse(forbidden)
// To use the `combo` as an Action we need to start with an ActionBuilder
val action: Action[AnyContent] = (Action andThen combo).async { req =>
Future.successful(Results.Ok)
}
// Or we can transform the action partial to an ActionBuilder
val comboAsAction = ActionPartial.toActionBuilder(combo)
val action2 = comboAsAction(BodyParsers.parse.json) { req =>
Results.Ok(req.body)
}
type User = String
case class MaybeUserRequest[A](user: Option[User], request: Request[A]) extends WrappedRequest(request)
val localAction = new ActionPartial[Request, MaybeUserRequest] {
val localIps = Set("127.0.0.1")
def partial[A] = {
case req if localIps.contains(req.remoteAddress) => Future.successful(Right(MaybeUserRequest(None, req)))
}
}
val userAction = new ActionPartial[Request, MaybeUserRequest] {
def findUser(auth: String): Future[Option[User]] = ???
def partial[A] = {
case req if req.headers.keys.exists(_ == "Authorization") =>
val auth = req.headers("Authorization")
findUser(auth).map {
case Some(user) => Right(MaybeUserRequest(Some(user), req))
case None => Left(Results.Forbidden)
}
}
}
def forbiddenAll[R[_]] = new ActionPartial[Request, R] {
def partial[A] = {
case _ => Future.successful(Left(Results.Forbidden))
}
}
val myComposition = userAction.orElse(localAction).orElse(forbiddenAll)
case class UserWithAppsRequest[A](user: Option[User], apps: Seq[Int], request: Request[A]) extends WrappedRequest(request)
val userWithApps = new ActionTransformer[MaybeUserRequest, UserWithAppsRequest] {
def findApps(user: Option[User]): Future[Seq[Int]] = ???
def transform[A](req: MaybeUserRequest[A]): Future[UserWithAppsRequest[A]] = {
findApps(req.user).map { apps =>
UserWithAppsRequest(req.user, apps, req.request)
}
}
}
val myAction = (Action andThen (userAction orElse localAction orElse forbiddenAll) andThen userWithApps) { req =>
Results.Ok(s"user with apps ${req.apps}")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment