Play framework 2.3 ActionBuilder composition issue - scala

I really like play framework 2.3's ActionBuilder and the andThen method that allows you to dynamically compose actions.
Here's a snippet of how I want to use action composition:
def showHomepage = RedirectingAction andThen
AuthenticatedAction andThen NotificationAction async { request =>
Future {
Ok(views.html.homepage.render(request.user, request.notifications ))
}
}
As you can guess, NotificationAction depends on AuthenticatedAction, and hence needs AuthenticatedRequest which contains the User object.
The code complains on:
object NotificationAction extends ActionBuilder[NotificationAuthRequest] {
def invokeBlock[A](request: AuthenticatedRequest[A], block: (NotificationAuthRequest[A]) => Future[Result]) = { ...
The error is:
object creation impossible, since method invokeBlock in trait ActionFunction of type [A](request: play.api.mvc.Request[A], block: controllers.v3.ScalaHomepageController.NotificationAuthRequest[A] => scala.concurrent.Future[play.api.mvc.Result])scala.concurrent.Future[play.api.mvc.Result] is not defined
Apparently it allows only:
def invokeBlock[A](request: Request[A], block: ...
but not:
def invokeBlock[A](request: AuthenticatedRequest[A], block: ...
I'd really appreciate if someone could throw light on this. Maybe my approach is wrong, but I don't like the idea of precomposed actions (like using ActionFunction) because I could have more actions that I could be mixing in at a later point.
Here's the code:
case class AuthenticatedRequest[A](val user: Option[User], request: Request[A]) extends WrappedRequest(request)
case class NotificationAuthRequest[A](val user: Option[User], val notifications: Option[List[UserNotificationData]], request: Request[A]) extends WrappedRequest(request)
object RedirectingAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
Future {
Redirect(REDIRECT_URL + request.uri + paramString)
}
}
}
object AuthenticatedAction extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[Result]) = {
request.cookies.get("uid") map {
cookie =>
val user = userClient.getUserById(userId(cookie)).get
block(AuthenticatedRequest[A](user, request))
} getOrElse {
block(AuthenticatedRequest[A](userClient.getUserById(uid).get, request))
}
}
def userId(cookie: Cookie) = {
if(AppUtil.isProd) cookie.value else IMPERSONATE_ID.getOrElse(cookie.value)
}
}
object NotificationAction extends ActionBuilder[NotificationAuthRequest] {
def invokeBlock[A](request: AuthenticatedRequest[A], block: (NotificationAuthRequest[A]) => Future[Result]) = {
request.user.map {
user => block(NotificationAuthRequest[A](Some(user), userClient.getNotifications(user.getId).get.map(_.toList), request))
}.getOrElse {
block(NotificationAuthRequest[A](None, None, request))
}
}
}

Reading the docs I think you need to have ActionRefiners and ActionTransformers.
This is what I came up with:
package controllers
import play.api.mvc._
import scala.concurrent.Future
case class User(id: Long)
case class UserNotificationData(text: String)
case class AuthRequest[A](user: Option[User], request: Request[A]) extends WrappedRequest(request)
case class AuthNotificationRequest[A](user: Option[User], notifications: Option[List[UserNotificationData]], request: Request[A]) extends WrappedRequest(request)
object RedirectingAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
block(request)
}
}
object AuthenticatedAction extends ActionBuilder[AuthRequest] with ActionTransformer[Request, AuthRequest] {
def transform[A](request: Request[A]) = Future.successful {
request.cookies.get("uid") map {
cookie =>
val user = Some(User(1))
AuthRequest[A](user, request)
} getOrElse {
AuthRequest[A](Some(User(1)), request)
}
}
}
object WithNotifications extends ActionTransformer[AuthRequest, AuthNotificationRequest] {
def transform[A](request: AuthRequest[A]) = Future.successful {
request.user.map { user => AuthNotificationRequest[A](Some(user), Some(List(UserNotificationData("Notification"))), request)} getOrElse {
AuthNotificationRequest[A](None, None, request)
}
}
}
object Application extends Controller {
def index = (RedirectingAction andThen AuthenticatedAction andThen WithNotifications) { request: AuthNotificationRequest[AnyContent] =>
Ok(views.html.index("Your new application is ready."))
}
}

Related

How to implement multiple thread pools in a Play application using cats-effect IO

In my Play application, I service my requests usings cats-effect's IO, instead of Future in the controller, like this (super-simplified):
def handleServiceResult(serviceResult: ServiceResult): Result = ...
def serviceMyRequest(request: Request): IO[ServiceResult] = ...
def myAction = Action { request =>
handleServiceResult(
serviceMyRequest(request).unsafeRunSync()
)
}
Requests are then processed (asynchronously) on Play's default thread pool. Now, I want to implement multiple thread pools to handle different sorts of requests. Were I using Futures, I could do this:
val myCustomExecutionContext: ExecutionContext = ...
def serviceMyRequest(request: Request): Future[ServiceResult] = ...
def myAction = Action.async { request =>
Future(serviceMyRequest(request))(myCustomExecutionContext)
.map(handleServiceResult)(defaultExecutionContext)
}
But I'm not using Futures, I'm using IO, and I'm not sure about the right way to go about implementing it. This looks promising, but seems a bit clunky:
def serviceMyRequest(request: Request): IO[ServiceResult] = ...
def myAction = Action { request =>
val ioServiceResult = for {
_ <- IO.shift(myCustomExecutionContext)
serviceResult <- serviceMyRequest(request)
_ <- IO.shift(defaultExecutionContext)
} yield {
serviceResult
}
handleServiceResult(ioServiceResult.unsafeRunSync())
}
Is this the right way to implement it? Is there a best practice here? Am I screwing up badly? Thanks.
Ok, so since this doesn't seem to be well-trodden ground, this is what I ended up implementing:
trait PlayIO { self: BaseControllerHelpers =>
implicit class IOActionBuilder[A](actionBuilder: ActionBuilder[Request, A]) {
def io(block: Request[A] => IO[Result]): Action[A] = {
actionBuilder.apply(block.andThen(_.unsafeRunSync()))
}
def io(executionContext: ExecutionContext)(block: Request[A] => IO[Result]): Action[A] = {
val shiftedBlock = block.andThen(IO.shift(executionContext) *> _ <* IO.shift(defaultExecutionContext))
actionBuilder.apply(shiftedBlock.andThen(_.unsafeRunSync()))
}
}
}
Then (using the framework from the question) if I mix PlayIO into the controller, I can do this,
val myCustomExecutionContext: ExecutionContext = ...
def handleServiceResult(serviceResult: ServiceResult): Result = ...
def serviceMyRequest(request: Request): IO[ServiceResult] = ...
def myAction = Action.io(myCustomExecutionContext) { request =>
serviceMyRequest(request).map(handleServiceResult)
}
such that I execute the action's code block on myCustomExecutionContext and then, once complete, thread-shift back to Play's default execution context.
Update:
This is a bit more flexible:
trait PlayIO { self: BaseControllerHelpers =>
implicit class IOActionBuilder[R[_], A](actionBuilder: ActionBuilder[R, A]) {
def io(block: R[A] => IO[Result]): Action[A] = {
actionBuilder.apply(block.andThen(_.unsafeRunSync()))
}
def io(executionContext: ExecutionContext)(block: R[A] => IO[Result]): Action[A] = {
if (executionContext == defaultExecutionContext) io(block) else {
val shiftedBlock = block.andThen(IO.shift(executionContext) *> _ <* IO.shift(defaultExecutionContext))
io(shiftedBlock)
}
}
}
}
Update2:
Per the comment above, this will ensure we always shift back to the default thread pool:
trait PlayIO { self: BaseControllerHelpers =>
implicit class IOActionBuilder[R[_], A](actionBuilder: ActionBuilder[R, A]) {
def io(block: R[A] => IO[Result]): Action[A] = {
actionBuilder.apply(block.andThen(_.unsafeRunSync()))
}
def io(executionContext: ExecutionContext)(block: R[A] => IO[Result]): Action[A] = {
if (executionContext == defaultExecutionContext) io(block) else {
val shiftedBlock = block.andThen { ioResult =>
IO.shift(executionContext).bracket(_ => ioResult)(_ => IO.shift(defaultExecutionContext))
}
io(shiftedBlock)
}
}
}
}

In which controller will action builder executes

I have Action builder
class LoggingAction #Inject()(parser: BodyParsers.Default)(implicit ec: ExecutionContext) extends ActionBuilderImpl(parser) {
override def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]): Future[Result] = {
Logger.info(s"Controller = ${}, " +
s"method = ${request.method}, " +
s"URI = ${request.uri}, " +
s"remote-address = ${request.remoteAddress}, " +
s"domain = ${request.domain}")
block(request)
}
}
}
And I have some simple routes, one of them
...
def index() = loggingAction { implicit request: Request[AnyContent] =>
Ok(views.html.index())
}
...
How to log that LoggingAction is executed in this controller within LogginAction
You could change the signature of LoggingAction to pass the current class.
class LoggingAction #Inject() (controller: AbstractController)(parser: BodyParsers.Default)(implicit ec: ExecutionContext) extends ActionBuilderImpl(parser) {
override def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]): Future[Result] = {
Logger.info(s"Controller = ${controller.getClass.getName}, " +
s"method = ${request.method}, " +
s"URI = ${request.uri}, " +
s"remote-address = ${request.remoteAddress}, " +
s"domain = ${request.domain}")
block(request)
}
}
and then create an instance of the logging action specifically for your controller (this can be made easier by creating a Guice factory for it)
class TestController #Inject() (cc: ControllerComponents, parser: BodyParsers.Default)(implicit ec: ExecutionContext)
extends AbstractController(cc) {
lazy val loggingAction = new LoggingAction(this, parser)
def index ()= loggingAction { implicit request =>
Ok(views.html.index())
}
}

PlayFramework migration 2.5.x to 2.6.x deprecated Action.async

Trying to migrate from 2.5.x to 2.6.x. I have trait Secured with old method IsAuthenticatedAsync, i'm trying to write new method IsAuthenticatedAsyncNew. Compiler says that Action.async is deprecated, so I tried to replace Action.asyinc block with ActionBuilder but I'm struggling as I can't find good 2.6.x examples how to do this.
trait Secured {
private def username(request: RequestHeader) = request.session.get("username")
private def onUnauthorized(request: RequestHeader) = Results.Redirect(routes.Application.login())
def userService: UserService
def cc: ControllerComponents
/**
* Async action for authenticated users.
*/
def IsAuthenticatedAsync(f: => User => Request[AnyContent] => Future[Result]) = Security.Authenticated(username, onUnauthorized) { user =>
Action.async{ implicit request =>
val _user = userService.findByUsername(user)
if(_user.nonEmpty)
f(_user.get)(request)
else
Future.successful(Results.Redirect(routes.Application.login()))
}
}
def IsAuthenticatedAsyncNew(f: => User => Request[AnyContent] => Future[Result]) = new ActionBuilder[Request, AnyContent] {
override def invokeBlock[A](request: Request[A], block: Request[A] => Future[Result]) = {
val username = request.session.get("username")
val user = userService.findByUsername(username.get)
user match {
case Some(user) => f(user)(request)
case None => Future.successful(Results.Redirect(routes.Application.login()))
}
}
override protected def executionContext: ExecutionContext = cc.executionContext
override def parser: BodyParser[AnyContent] = cc.parsers.defaultBodyParser
}
}
Compiler returns error
Error:(40, 36) type mismatch;
found : play.api.mvc.Request[A]
required: play.api.mvc.Request[play.api.mvc.AnyContent]
case Some(user) => f(user)(request)
If i change footprint of method to
override def invokeBlock(request: Request[AnyContent], block: Request[AnyContent] => Future[Result])
then it doesn't ovveride nothing ... Can it be done this way at all?
Action in play 2.6 is defined in BaseController trait as
def Action: ActionBuilder[Request, AnyContent] = controllerComponents.actionBuilder
This means that you can simply replace Action.async with cc.actionBuilder.async(...)
or even copy that method
def Action = cc.actionBuilder
and use Action.async as previously.

[Play][Scala] Can I get the contents of multiple chained Actions?

I'm trying to learn to use Action composition in the Play framework in Scala.
I understand the ability to do basic action composition and WrappedRequests, my question however is : Is it possible to access the contents of multiple WrappedRequests in a composed Action?
Let me explain, I have the following code:
class RequestWithUser[A](val user: models.User, request: Request[A]) extends WrappedRequest[A](request)
def UserAction(userId: Long) = new ActionBuilder[RequestWithUser] {
def invokeBlock[A](request: Request[A], block: (RequestWithUser[A]) => Future[SimpleResult]) = {
models.UsersDAO.findById(userId).map { user =>
block(new RequestWithUser(user, request))
} getOrElse {
Future.successful(NotFound)
}
}
}
case class AuthenticatedRequest[A](user: models.User, request: Request[A]) extends WrappedRequest[A](request)
object Authorized extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
userTokenForm.bindFromRequest()(request).fold(
formWithErrors => {
resolve(Results.Unauthorized(formWithErrors.errorsAsJson))
},
userData => {
models.UsersDAO.findByToken(userData.token) map { user=>
block(AuthenticatedRequest(user, request))
} getOrElse {
resolve(Results.Unauthorized("Token matched no one."))
}
}
)
}
}
I'd like to be able to compose them into a third action potentially called "UserPermissionAction" which composes Authorized and UserAction.
It should check if the Authorized user is the same as user in the RequestWithUser (i.e. only allow users to edit themselves).
Is this possible?
Here is what I ended up doing.
it works but I'd like to see feedback if there is more idiomatic Scala way:
/** Contains the security token, extracted from the RequestHeader */
case class AuthenticatedRequest[A](user: models.User, request: Request[A]) extends WrappedRequest[A](request)
case class AuthorizedAction[A](action: Action[A]) extends Action[A] {
lazy val parser = action.parser
def apply(request: Request[A]): Future[SimpleResult] = {
userTokenForm.bindFromRequest()(request).fold(
formWithErrors => {
resolve(Results.Unauthorized(formWithErrors.errorsAsJson))
},
userData => {
models.UsersDAO.findByToken(userData.token) map { user=>
action(AuthenticatedRequest(user, request))
} getOrElse {
resolve(Results.Unauthorized("Token matched no one."))
}
}
)
}
}
class RequestWithUser[A](val user: models.User, request: Request[A]) extends WrappedRequest[A](request)
case class UserAction[A](userId: Long, action: Action[A]) extends Action[A] {
lazy val parser = action.parser
def apply(request: Request[A]): Future[SimpleResult] = {
models.UsersDAO.findById(userId).map { user =>
action(new RequestWithUser(user, request))
} getOrElse {
Future.successful(NotFound)
}
}
}
def UserHasPermission[A](userId: Long) = new ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
request match {
case req: AuthenticatedRequest[A] => {
{
for (
authUserId <- req.user.id
if authUserId == userId
) yield block(req)
} getOrElse Future.successful(Unauthorized)
}
case _ => Future.successful(BadRequest)
}
}
override def composeAction[A](action: Action[A]) = UserAction(userId, AuthorizedAction(action) )
}

Play2.2.x, BodyParser, Authentication, and Future[Result]

I'm trying to implement authentication in my Play 2.2.1 app, and I can't quite figure out how to make it work with an action that returns a Future[Result].
This post describes pretty close to what I'm trying to do, except without returning Future[Result]:
Play 2.0 Framework, using a BodyParser with an authenticated request
How can I get it to work with Futures? I.e. how would I implement this function:
def IsAuthenticated(f: => String => Request[Any] => Future[Result])
or, better yet, this function:
def IsAuthenticated[A}(b:BodyParser[A])(f: => String => Request[Any] => Future[Result])
which would feed into this function:
def AuthenticatedUser(g: Account => Request[AnyContent] => SimpleResult) = IsAuthenticated {...}
to wrap asynchronous actions in my controllers?
This part I can do:
def IsAuthenticated(f: => String => Request[AnyContent] => Future[SimpleResult]) = {
Security.Authenticated(email, onUnauthorized) {
user => Action.async(request => f(user)(request))
}
}
But if I try to use IsAuthenticated in my wrapper function:
def AuthenticatedUser(g: Account => Request[AnyContent] => Future[SimpleResult]) = IsAuthenticated {
email => implicit request => Account.find(email).map {
opt => opt match {
case Some(account) => g(account)(request)
case None => Future(onUnauthorized(request))
}
}
}
(Account.find returns a Future[Option[Account]] 'cause it's a mongodb call that may take some time. The desire to do the future thing right is what's causing me so much grief now)
I can't get AuthenticatedUser to satisfy the compiler. It says it's getting a Future[Future[SimpleResult]] instead of a Future[SimpleResult].
So, how best to build this whole thing? I need to be able to make authentication wrappers that rely on db calls that are asynchronous.
I'm sure I'm just dense and missing something obvious...
EDIT: Here's what I ended up with. Thank you Jean for pointing me in the right direction.
I found AuthenticatedController while rooting around and it's pretty close to what I'm looking for. I wanted two types of authentication: User (authenticated user) and Administrator (to wrap code for admin tasks).
package controllers
import models.Account
import play.api.mvc._
import scala.concurrent.Future
trait Secured {
class AuthenticatedRequest[A](val account: Account, request: Request[A]) extends WrappedRequest[A](request)
object User extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
request.session.get("email") match {
case Some(email) => {
Account.find(email).flatMap {
case Some(account) => {
block(new AuthenticatedRequest(account, request))
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
}
object Administrator extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
request.session.get("email") match {
case Some(email) => {
Account.find(email).flatMap {
case Some(account) => if (account.admin) {
block(new AuthenticatedRequest(account, request))
} else {
Future(Results.Redirect(routes.Index.index()))
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
}
}
There have been changes in play 2.2 to make it easier to compose actions. The resource you are referring to is outdated.
Instead you should create a custom action builder by extending ActionBuilder to create your action, this will get you all the fancy apply methods you may need (including async support and all)
For example you may do :
trait MyAction extends Results{
class MyActionBuilder[A] extends ActionBuilder[({ type R[A] = Request[A] })#R] {
def invokeBlock[A](request: Request[A],
block: Request[A] => Future[SimpleResult]) ={
// your authentication code goes here :
request.cookies.get("loggedIn").map { _=>
block(request)
} getOrElse Future.successful(Unauthorized)
}
}
object MyAction extends MyActionBuilder
}
which you can then use as such :
object MyController extends Controller with MyAction{
def authenticatedAction=MyAction {
Ok
}
def asyncAuthenticatedAction=MyAction.async {
Future.successful(Ok)
}
def authenticatedActionWithBodyParser = MyAction(parse.json){ request =>
Ok(request.body)
}
}
For brevity's sake I used a very trivial authentication mechanism you will want to change that :)
Additionally, you can create a custom "request" type to provide additional information. For instance you could define a AuthenticatedRequest as such :
case class AuthenticatedRequest[A](user: User, request: Request[A]) extends WrappedRequest(request)
Provided you have a way to get your user such as
object User{
def find(s:String): Option[User] = ???
}
Then change your builder definition a bit as such
class MyActionBuilder[A] extends
ActionBuilder[({ type R[A] = AuthenticatedRequest[A] })#R] {
def invokeBlock[A](request: Request[A],
block: AuthenticatedRequest[A] => Future[SimpleResult]) ={
// your authentication code goes here :
(for{
userId <- request.cookies.get("userId")
user <- User.find(userId.value)
}yield {
block(AuthenticatedRequest(user,request))
}) getOrElse Future.successful(Unauthorized)
}
}
Your controller now has access to your user in authenticatedActions:
object MyController extends Controller with MyAction{
val logger = Logger("application.controllers.MyController")
def authenticatedAction=MyAction { authenticatedRequest =>
val user = authenticatedRequest.user
logger.info(s"User(${user.id} is accessing the authenticatedAction")
Ok(user.id)
}
def asyncAuthenticatedAction = MyAction.async { authenticatedRequest=>
Future.successful(Ok(authenticatedRequest.user.id))
}
def authenticatedActionWithBodyParser = MyAction(parse.json){ authenticatedRequest =>
Ok(authenticatedRequest.body)
}
}