Last active
December 14, 2015 12:36
-
-
Save vdebergue/851b3e4b29e320cbe83b to your computer and use it in GitHub Desktop.
ActionPartial
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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