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())
}
}
Related
I wonder is it possible to pass implicit params through singletons like that
case class Greet(g: String)
object Foo {
def greet(name: String)(implicit greet: Greet = Greet("Hello")) = println(greet.g + " " + name)
}
object Bar {
def greetBar = Foo.greet("Bar")
}
object Main {
def main(args: Array[String]): Unit = {
implicit val greet: Greet = Greet("Goodbye")
Foo.greet("Sunshine") // Goodbye Sunshine
Bar.greetBar // Hello Bar
}
}
Bar.greetBar doesn't affected by implicit value in main, but I want it to be affected without passing implicit param to greetBar, so is there any way to do something like that? Maybe there is a way to set an implicit for object but in outer of it?
You should add implicit parameter to the method
object Bar {
def greetBar(implicit greet: Greet /*= Greet("Hello")*/) = Foo.greet("Bar")
}
implicit val greet: Greet = Greet("Goodbye")
Bar.greetBar // Goodbye Bar
or make the object a class and add implicit parameter to the class
class Bar(implicit greet: Greet /*= Greet("Hello")*/) {
def greetBar = Foo.greet("Bar")
}
implicit val greet: Greet = Greet("Goodbye")
(new Bar).greetBar // Goodbye Bar
I commented out default value /*= Greet("Hello")*/. If you want greetBar not to compile when there is no implicit in scope then you should keep it commented out. If you want behavior similar to greet (i.e. Greet("Hello") when there is no implicit in scope) then you should uncomment it.
Please notice that you can avoid repeating default value if you define lower-priority implicit in companion object
case class Greet(g: String)
object Greet {
implicit val lowPriorityGreet: Greet = Greet("Hello")
}
object Foo {
def greet(name: String)(implicit greet: Greet) = println(greet.g + " " + name)
}
object Bar {
def greetBar(implicit greet: Greet) = Foo.greet("Bar")
}
// class Bar(implicit greet: Greet) {
// def greetBar = Foo.greet("Bar")
// }
implicit val greet: Greet = Greet("Goodbye")
Foo.greet("Sunshine") // Goodbye Sunshine
Bar.greetBar // Goodbye Bar
// (new Bar).greetBar // Goodbye Bar
See also
How to wrap a method having implicits with another method in Scala?
I want to do this to set Greet implict for all methods in Bar
In principle, you can do this with a macro annotation (but you shouldn't)
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
#compileTimeOnly("enable macro annotations")
class greetAware extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro GreetAwareMacro.impl
}
object GreetAwareMacro {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
val greet = TermName(c.freshName("greet"))
val implicitGreet = q"""implicit val $greet: Greet = Greet("Hello")"""
def isImplicit(param: Tree): Boolean = param match {
case q"$mods val $_: $_ = $_" => mods.hasFlag(Flag.IMPLICIT)
}
annottees match {
case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" :: Nil =>
val body1 = body.map {
case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" =>
val paramss1 =
if (paramss.nonEmpty && paramss.last.nonEmpty && isImplicit(paramss.last.head))
paramss.init :+ (paramss.last :+ implicitGreet)
else paramss :+ List(implicitGreet)
q"$mods def $tname[..$tparams](...$paramss1): $tpt = $expr"
case notMethod => notMethod
}
q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body1 }"
}
}
}
Usage:
#greetAware
object Foo {
def greet(name: String) = println(implicitly[Greet].g + " " + name)
}
#greetAware
object Bar {
def greetBar = Foo.greet("Bar")
def xxx(i: Int) = ???
def yyy(i: Int)(implicit s: String) = ???
}
implicit val greet: Greet = Greet("Goodbye")
Foo.greet("Sunshine") // Goodbye Sunshine
Bar.greetBar // Goodbye Bar
//scalac: object Foo extends scala.AnyRef {
// def <init>() = {
// super.<init>();
// ()
// };
// def greet(name: String)(implicit greet$macro$1: Greet = Greet("Hello")) = println(implicitly[Greet].g.$plus(" ").$plus(name))
//}
//scalac: object Bar extends scala.AnyRef {
// def <init>() = {
// super.<init>();
// ()
// };
// def greetBar(implicit greet$macro$2: Greet = Greet("Hello")) = Foo.greet("Bar");
// def xxx(i: Int)(implicit greet$macro$2: Greet = Greet("Hello")) = $qmark$qmark$qmark;
// def yyy(i: Int)(implicit s: String, greet$macro$2: Greet = Greet("Hello")) = $qmark$qmark$qmark
//}
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.
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."))
}
}
I'm trying to implement a type class solution for error handling in a Play application. What I want is to have some type class instances representing some validated (caught) errors and a default type class instance for any unvalidated (uncaught) errors.
I don't know if this is possible, but here's what I have so far:
trait ResponseError[E] {
def report(e: E)(implicit logger: Logger): Unit
def materialize(e: E): Result
}
trait ValidatedError[E <: Throwable] extends ResponseError[E] {
def report(e: E)(implicit logger: Logger): Unit =
ResponseError.logError(e)
}
trait UnvalidatedError[E <: Throwable] extends ResponseError[E] {
def report(e: E)(implicit logger: Logger): Unit = {
ResponseError.logError(e)
UnvalidatedError.notify(e)
}
}
object ResponseError {
def logError(e: Throwable)(implicit logger: Logger): Unit =
logger.error(e.getMessage)
}
object ValidatedError {
import java.util.concurrent.{ExecutionException, TimeoutException}
implicit val executionError = new ValidatedError[ExecutionException] {
def materialize(e: E): Result =
play.api.mvc.Results.BadRequest
}
implicit val timeoutError = new ValidatedError[TimeoutException] {
def materialize(e: E): Result =
play.api.mvc.Results.RequestTimeout
}
}
object UnvalidatedError {
implicit uncaughtError = new UnvalidatedError[Throwable] {
def materialize(e: E): Result =
play.api.mvc.Results.ServiceUnavailable
}
private def notify(e: Throwable) = ??? // send email notification
}
However how can I make sure to try my ValidatedError type class instances first, before falling back to my UnvalidatedError type class instance?
There you go. See my comment for details.
import java.util.concurrent.{TimeoutException, ExecutionException}
type Result = String
val badRequest: Result = "BadRequest"
val requestTimeout: Result = "RequestTimeout"
val serviceUnavailable: Result = "ServiceUnavailable"
class Logger {
def error(s: String) = println(s + "\n")
}
trait ResponseError[E] {
def report(e: E)(implicit logger: Logger): Unit
def materialize(e: E): Result
}
trait ValidatedError[E <: Throwable] extends UnvalidatedError[E] {
override def report(e: E)(implicit logger: Logger): Unit =
ResponseError.logError(e, validated = true)
}
trait UnvalidatedError[E <: Throwable] extends ResponseError[E] {
def report(e: E)(implicit logger: Logger): Unit = {
ResponseError.logError(e, validated = false)
UnvalidatedError.notify(e)
}
}
object ResponseError {
def logError(e: Throwable, validated: Boolean)(implicit logger: Logger): Unit =
logger.error({
validated match {
case true => "VALIDATED : "
case false => "UNVALIDATED : "
}
} + e.getMessage)
}
object ValidatedError {
import java.util.concurrent.{ExecutionException, TimeoutException}
implicit def executionError[E <: ExecutionException] = new ValidatedError[E] {
def materialize(e: E): Result =
badRequest
}
implicit def timeoutError[E <: TimeoutException] = new ValidatedError[E] {
def materialize(e: E): Result =
requestTimeout
}
}
object UnvalidatedError {
implicit def uncaughtError[E <: Throwable] = new UnvalidatedError[E] {
def materialize(e: E): Result =
serviceUnavailable
}
private def notify(e: Throwable) = println("Sending email: " + e) // send email notification
}
def testTypeclass[E](e: E)(implicit logger: Logger, ev: ResponseError[E]): Unit ={
ev.report(e)
}
import ValidatedError._
import UnvalidatedError._
implicit val logger: Logger = new Logger
val executionErr = new ExecutionException(new Throwable("execution exception!"))
testTypeclass(executionErr)
val timeoutErr = new TimeoutException("timeout exception!")
testTypeclass(timeoutErr)
val otherErr = new Exception("other exception!")
testTypeclass(otherErr)
Output:
VALIDATED : java.lang.Throwable: execution exception!
VALIDATED : timeout exception!
UNVALIDATED : other exception!
Sending email: java.lang.Exception: other exception!
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) )
}