unable to refactor Scala code to make it more readable - scala

I struggle to understand Scala and thus thought to refactor a piece of code to make it more de-sugared. But I am unable to do so.
The original code is
def index(signupMessage:String = "") = addToken { //addToken is of CSRFAddToken
silhouette.UserAwareAction { implicit request =>
println(s"index action called with request ${utilities.printPlayHttpRequest(request)}")
//TODOM - fix as signup value is coming up as ;signup=error or ;signup=success instead of just success or error
println(s"index page's argument "+signupMessage)
val Token(name, value) = CSRF.getToken.get
println(s"Token name ${name}, value ${value}")
Ok(views.html.index(signupMessage,messagesApi("app.title")(langs.availables(0))))
}
}
I want to de-sugar the code and make calls more explicit. I am thinking of something in the following line
def index(signupMessage:String = "") = {
val block:Action[AnyContent] = (implicit request) =>{ //THE ERROR IS HERE - '=>' expected but ')' found
println(s"index action called with request ${utilities.printPlayHttpRequest(request)}")
//TODOM - fix as signup value is coming up as ;signup=error or ;signup=success instead of just success or error
println(s"index page's argument "+signupMessage)
val Token(name, value) = CSRF.getToken.get
println(s"Token name ${name}, value ${value}")
Ok(views.html.index(signupMessage,messagesApi("app.title")(langs.availables(0))))
}
val silhouttedCode = silhouette.UserAwareAction.apply(block)
addToken.apply(silhouettedCode)
}
the above piece of code doesn't compile. What am I doing wrong?

I think you need to write your Action like this:
val block = Action { implicit request =>
// Rest of code here
}
See the docs for Actions.

Related

Play framework - make form validation async - compilation error

Trying to use map (line 6) instead of using Await (lines 3-4) results in compilation saying Cannot resolve overloaded method 'Action' marking the first line.
I think the problem is in the last line of the function using fold which now has another value type returned from errorFunction.
can someone help understanding whats happening and how to make create user fully async?
def createUser = Action { implicit request: MessagesRequest[AnyContent] =>
val errorFunction = { formWithErrors: Form[Data] =>
val readAllResponse = Await.result(usersDao.readAll(), Duration.Inf)
BadRequest(views.html.listUsers(readAllResponse, formWithErrors, postUrl))
// TODO - try to make it async.
// usersDao.readAll().map(allUsersList => BadRequest(views.html.listUsers(allUsersList, formWithErrors, postUrl)))
}
val successFunction = { data: Data =>
val user = User(id = data.id, firstName = data.firstName, lastName = data.lastName)
// call to DAO
val createUsers: Future[User] = usersDao.create(user)
Redirect(routes.UserController.listUsers()).flashing("Info" -> "user added!")
}
val formValidationResult = form.bindFromRequest
formValidationResult.fold(errorFunction, successFunction)
}
code for readAll():
def readAll()(implicit transaction: Transaction[User]): Future[List[User]] = Future {
println(s"Reading all users...")
Thread.sleep(500)
transaction.modelObjectsList.toList
}
Error (when uncommenting line 6):
Compilation error[type mismatch;
found : play.api.data.Form[controllers.UserForm.Data] => scala.concurrent.Future[play.api.mvc.Result]
required: play.api.data.Form[controllers.UserForm.Data] => play.api.mvc.Result]
That fails in compilation because you are using the sync Action, and pass a Future. For that line to work you need to change your Action to an async Action, and it should be as simple as this:
Action.async {/*your code here*/}

Exception not getting handled in recover method

This method throws exception
#throws(classOf[QuestionNotFoundException])
def getQuestionFromQuestionID(questionKey: PracticeQuestionKeys) = {
logger.trace(s"getting question with keys ${questionKey}")
val practiceQuestionFuture: Future[Option[PracticeQuestion]] = findOne(questionKey)
for (questionOption <- practiceQuestionFuture) yield {
questionOption.fold(throw QuestionNotFoundException())(question => {
logger.trace("got question " + question)
question
})
}
}
I use it like follows
def function1 = {
...
val res = for{existingQuestion <- questionsRepository.getQuestionFromQuestionID(questionKey){..}
res.recover {
case exception => {
logger.trace(s"exception ${exception}")
Ok(Json.toJson(JsonResultError(messagesApi("error.answerAdditionFail")(langs.availables(0))+". "+exception.getMessage())))
}
...
}
I want to test that function1 handles the thrown exception. I have written the following test case
"function" should {
"should return error if the question id is not correct" in {
...
when(answerTestEnv.mockPracticeQuestionsRepository.getQuestionFromQuestionID(ArgumentMatchers.any[PracticeQuestionKeys])).thenThrow(
QuestionNotFoundException()
)
val response:Accumulator[ByteString,Result] = controller.function1(request)
//note that run is how accumulator gets the data and starts processing it. no parameter means no data
val resultOfAccumulatorRun:Future[Result] = response.run(ByteString(body))
val responseBody = contentAsJson(resultOfAccumulatorRun)(Timeout(Duration(5000,"millis")),TestEnvImplicits.mat)
println(s"received response ${responseBody}")
val result = (responseBody \ "result").get.as[String]
val additionalInfo = (responseBody \ "additional-info").get.as[String]
result mustBe "error"
additionalInfo mustBe components.messagesApi("error.answerAdditionFail")(components.langs.availables(0))+". "+QuestionNotFoundException().msg
}
}
But when I run my test case, the Exception gets thrown but it seems that res.recover doesn't handle it. I see this in the console.
Question not found
utilities.QuestionNotFoundException: Question not found
at controllers.AnswerController.$anonfun$newAnswer$1(AnswerController.scala:428)
Why doestres.recover` doesn't handle it.
the partial function passed to recover() gets invoked on a failed Future, but your Future isn't a failed Future, it is a Future that contains an exception as its 'successful' result type (actually it will be forced to be something more like Future[AnyRef], as it could be either an Exception or a PracticeQuestion).
You probably don't want to be using Option.fold() like that. Instead try:
practiceQuestionFuture.flatMap { qOpt =>
qOpt.map(q => Future(q))
.getOrElse(Future.failed(QuestionNotFoundException()))
}
(NB: I'm not near a compiler to try this out right now, so I may have made a syntactical mistake or two, but there should be enough there to get you going).

Why does it need implicit arg if code works without it

There is Security Example where you can find => implicit request for def withUser(... method.
Based on that example I've created one-file-sample here:
https://github.com/Sergey80/scala-samples/blob/master/src/main/scala/implicits/usecases/RequestResponseTest.scala (where is no implicit .. and I do not see why I need use it)
The question is:
why do I need type implicit request (as was shown in Security Example) if it works without implicit. Since we are already referring on requestas val (defined above in the code).
Copy-pasting my simplified-sample here (where not implicit is used), this code has the same functions as in initial Security Example (withUser, withAtuh) with same set of parameters, the point was just to make it launchable from one file:
package implicits.usecases
object RequestResponseTest extends App {
type User = String // for simplicity sake - all are strings
type Request = String
type Result = String
trait Action // there are two auth Action - Ok and Not Ok
case class OkAction(content:Result) extends Action
case class UnauthorizedAction(content:Result) extends Action
var userDB = List("user1", "user2", "user3") // our simple database
val user = "user1" // usually user available from the session
var request : Request = "request" // current request
// check authorization and wraps request into Action
def withAuth(f: User => (Request => Result) ): Action = {
println("withAuth in play...")
val result:Result = f("user1")(request) // (is user was found or not)
def isAuthorized(user:User): Boolean = {
println("check authorisation...")
true // authorize everyone, welcome :)
}
def onUnAuthorized(request: Request): Action = {
println("wrapped to Action as not authorized")
UnauthorizedAction(request)
}
if (result == "ok") {
if (isAuthorized(request)) {
println("wrapped to Action as authorized")
OkAction(request)
} else onUnAuthorized(request)
} else onUnAuthorized(request)
}
//
def withUser(f: User => (Request => Result)) : Action = {
println("withUser in play...")
def findInDb(user: User): Option[User] = {
userDB.find(u => u == user)
}
val authResult:Action = withAuth ( // call 'withAuth'
/* Create anonymous function to please the 'withAuth'
it is already known who is current user, and what request is, but doesn't what Result is.
And he Result is the fact whether the user exists in users database or not.
So it tries to get the Result by searching for the current user.
If user has been found then it makes sense to continue with Authorisation (withAuth)
*/
user => request => { // passing anonymous function with user, request, and result (where result is based on user existence in the user db )
val userOption = findInDb("user1") // find the user in users db
val result:Result = userOption match { // check if user exists
case Some(_) => // user has been found
println("user has been found")
"ok"
case None => // user has not been found
println("user has not been found")
"not ok"
}
result // "ok" / "not ok" (user has been found or not)
} // end of passing anonymous function to 'withAuth'
)
authResult match {
case OkAction(_) => f(user)(request) // if authorized do The work
}
authResult
} // edn of 'withUser'
// Let's run this and to some work (no `implicit request` here)
def doWork() = withUser { // doWork -> withUser -> withAuth (like Decorator/Wrapper pattern)
user => request => {
// if user exists (withUser) and authorization is ok (withAuth), then this work will done
println("do some important work here!!")
"work is done!" // Result is just a String
}
}
val result = doWork() // doWork doesn't care about user or request
}
/* Output:
withUser in play...
withAuth in play...
user has been found
check authorisation...
wrapped to Action as authorized
do some important work here!!
*/
Update:
I understand if implicit was put on type Request to let compiler look up for value of this type which is defined implicitly somewhere. Like Inversion of Control injection. But there in Security Example it is put on value itself. On "request" value. If I'm putting 'request' it automatically means that I already have access to it and compiler knows about it (without implicit).
One word answer: convenience.
Let's take this simple example from your first link:
def login = Action { implicit request =>
Ok(views.html.login(loginForm))
}
You probably don't need the implicit here, but it's often convenient to have. Here's why:
This is really calling Action.apply. In particular, an overload that accepts a parameter:
block: Request[A] => Result
The block being passed here is the anonymous function:
request => Ok(views.html.login(loginForm))
The Request is probably not being used at all here, so whether it's implicit or not doesn't really matter. However, Play has many functions which require an implicit Request (for i18n, checking the uri accessed, whether or not a secure path was used, etc).
If views.html.login (a Play view) requires an implicit Request parameter, then it would be very useful to mark request as implicit, which we have the option of doing so.
implicit request => Ok(views.html.login(loginForm))
All the above code is doing is creating an anonymous function Request[A] => Result, in which the Request[A] parameter is made implicit. It isn't looking for an implicit, it's creating one. Similar to:
def anon(req: Request[A]): Result = {
implicit val request = req
Ok(views.html.login(loginForm)
}
Why? It makes for a much less boiler-plate, especially when many methods within the block require Request parameters. There's also no ambiguity, because the implicit is contained within the block of the anonymous function, and in this example, there is only one Request that each method should receive (there's only method call in this example, but there could be several more).
Otherwise you'd have to write:
def login = Action { request =>
Ok(views.html.login(loginForm)(request))
}
The withUser example isn't really any different. It just involves another nested anonymous function where request is made implicit.
This simple snippet explained me the thing (let me remember). Kind of essential:
implicit val integer = 3
def function4(implicit i:Int) = i // this works as default variable (ALMOST)
// same as this
def function4_1(i:Int = implicitly[Int]) = i // implicit scope has Int = 3
val result4 = function4 // should be without ()
val result4_1 = function4_1() // requires ()
println("result4: " + result4) // 3
println("result4_1: " + result4_1) // 3
Also I've prepared couple examples here: 1 2 (2 - improving my initial code with Request-Response case)

Play Framework/Scala: "return" statement in Action?

I know that this is not the Scala way of writing things. I think, in Scala you would use map. But I would like to write it this way, because it is more Java/c++ like.
However writing the following code the Scala compiler complains "method addGroup has return statement; needs result type".
Omitting the return and using an else branch works. But for formating reasons I'd like to use a return, because I don't want to indent the rest of the code which would happen if you use "else {}".
Where to add the result type. And is "Future[Result]" the correct type?
def addGroup = Action { implicit request =>
val optionUser = GetUserFromSession(request)
if (optionUser == None) {
return Redirect(routes.ApplicationUser.show(0))
}
Redirect(routes.ApplicationUser.show(optionUser.get.id))
}
You can't. The body of Action.apply is an anonymous function that you're trying to prematurely return from. The problem is, the return keyword in Scala returns from the inner-most named method, which this most certainly is not. So you'll be trying to return a Result where the method requires a Action[A].
The only way this can work is if you split the functions:
def addGroup = Action { implicit request =>
result(request)
}
// Could have a better name, but whatever, you shouldn't do this.
def result(request: Request): Result = {
val optionUser = GetUserFromSession(request)
if (optionUser == None) {
return Redirect(routes.ApplicationUser.show(0))
}
Redirect(routes.ApplicationUser.show(optionUser.get.id))
}
Using return makes code weird and difficult to read, so please don't.
If saving a single indentation is really a concern, what about this?
def addGroup = Action { implicit request =>
val optionUser = GetUserFromSession(request)
if (optionUser == None) Redirect(routes.ApplicationUser.show(0))
else Redirect(routes.ApplicationUser.show(optionUser.get.id))
}
Personally, I would re-write this using map and getOrElse:
def addGroup = Action { implicit request =>
GetUserFromSession(request) map { user =>
Redirect(routes.ApplicationUser.show(user.id))
} getOrElse {
Redirect(routes.ApplicationUser.show(0))
}
}
It removes the need to use .get and also prioritizes the positive branch.

WebSocket Action call Ignored in runtime

Hye Geeks. I am coding for a live notification module in my project. I am tyring to call WebSocket Action method from a function to pass the notification data over the connection to the client.
Here's my code..
def liveNotification(data: String) = WebSocket.using[JsValue] { implicit request =>
val iteratee = Iteratee.ignore[JsValue]
val enumerator = Enumerator[JsValue](Json.toJson(data))
(iteratee,enumerator)
}
def createNotification(notificationTo: BigInteger, notiParams:Tuple5[String,String,BigInteger,BigInteger,BigInteger]) = {
val retData = NotificationModel.createNotification(notificationTo,notiParams)
val strData = write(retData)
liveNotification(strData)
}
Problem is that the 'liveNotification()' call is simply ignored. Please help me with any suggestions that what i am doing wrong ?
Be sure to invoke it with a Json value, at least an empty object. The parser will only match against something that it recognizes as Json.