Play framework - make form validation async - compilation error - scala

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*/}

Related

Cannot resolve overloaded method map Scala 2.10.12/Intellij 2019.2.2

def saveOffsetRanges(offsetRanges: ListBuffer[OffsetRange], consumer: String): Unit = {
println(s"offsets to redis${offsetRanges}")
val client = JedisUtil.getClient()
val map = offsetRanges
.groupBy(_.topic)
.map{ // errors found : cannot resolve overloaded method
case (topic , offsets) =>
val partitionToOffset = offsets.map(info => {
info.partition.toString() -> info.untilOffset.toString
})
Serialization.write(partitionToOffset)
}
client.close()
}
Idea errors found: cannot resolve overloaded method 'map', but 'map' is very common in scala. And i am sure the code is correct.
enter image description here
Then I tried to invalidate cache and restart( IDEA -> File -> Invalide Caches/Restart). The problem still exists and other 'map' also mentioned cannot resolve, before i operate, other 'map' is ok.
def getOffsets(consumer: String) = {
val client = JedisUtil.getClient()
client.hgetAll(s"offset_$consumer")
.asScala
.map{// errors found : cannot resolve overloaded method
case (partition,offset) =>
val tp = new TopicPartition(partition.toInt)
val o = offset.toLong
tp->o
}
}
It is really wired, code could work but the errors effect the coding(idea don't automatic prompt method and type )

unable to refactor Scala code to make it more readable

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.

Secured trait causing spec2 unit test compilation errors

I've implemented authorization for my Play Framework (version 2.3.5) application as per the official security documentation:
trait Secured {
def username(request: RequestHeader) = request.session.get(Security.username)
def onUnauthorized(request: RequestHeader) = Results.Redirect(routes.Login.index)
def withAuth(f: => String => Request[AnyContent] => Result) = {
Security.Authenticated(username, onUnauthorized) { user =>
Action(request => f(user)(request))
}
}
}
However, when I decorate my controller actions with the withAuth function like so:
def export = withAuth { username => implicit request =>
val csv = csv()
Ok(csv)
.as("text/csv")
.withHeaders(
CONTENT_DISPOSITION -> ("attachment; filename=export.csv"),
CONTENT_LENGTH -> csv.length.toString
)
}
I get compilation errors in my controller specs2 unit tests:
"export data as CSV" in {
val controller = new controllers.DataController
val result = controller.export().apply(FakeRequest())
headers(result) must havePair("Content-Disposition" -> ("attachment; filename=export.csv"))
}
The call to the headers test helper function fails with the following compiler error message:
type mismatch; found : play.api.libs.iteratee.Iteratee[Array[Byte],play.api.mvc.Result] required: scala.concurrent.Future[play.api.mvc.Result]
Am I doing something wrong? I've tried some of the remedies suggested by other Stackoverflow users but they all seem to rely on setting the body type, but my actions do not have a body type.
It looks as though the call to withAuth obscures some of the types of the Action being wrapped so maybe there is a more type-safe way of expressing the Secured trait?
Try changing this:
val result = controller.export().apply(FakeRequest())
into this:
val result = controller.export().apply(FakeRequest()).run
And then it should work.

type mismatch; found : scala.concurrent.Future[play.api.libs.ws.Response] required: play.api.libs.ws.Response

I am trying to make a post request to Pusher api, but I am having trouble returning the right type, I a type mismatch; found : scala.concurrent.Future[play.api.libs.ws.Response] required: play.api.libs.ws.Response
def trigger(channel:String, event:String, message:String): ws.Response = {
val domain = "api.pusherapp.com"
val url = "/apps/"+appId+"/channels/"+channel+"/events";
val body = message
val params = List(
("auth_key", key),
("auth_timestamp", (new Date().getTime()/1000) toInt ),
("auth_version", "1.0"),
("name", event),
("body_md5", md5(body))
).sortWith((a,b) => a._1 < b._1 ).map( o => o._1+"="+URLEncoder.encode(o._2.toString)).mkString("&");
val signature = sha256(List("POST", url, params).mkString("\n"), secret.get);
val signatureEncoded = URLEncoder.encode(signature, "UTF-8");
implicit val timeout = Timeout(5 seconds)
WS.url("http://"+domain+url+"?"+params+"&auth_signature="+signatureEncoded).post(body
}
The request you are making with post is asynchronous. That call returns immediately, but does not return a Response object. Instead, it returns a Future[Response] object, which will contain the Response object once the http request is completed asynchronously.
If you want to block execution until the request is completed, do:
val f = Ws.url(...).post(...)
Await.result(f)
See more about futures here.
Just append a map:
WS.url("http://"+domain+url+"?"+params+"&auth_signature="+signatureEncoded).post(body).map(_)
Assuming you don't want to create a blocking app, your method should also return a Future[ws.Response]. Let your futures bubble up to the Controller where you return an AsyncResult using Async { ... } and let Play handle the rest.
Controller
def webServiceResult = Action { implicit request =>
Async {
// ... your logic
trigger(channel, event, message).map { response =>
// Do something with the response, e.g. convert to Json
}
}
}

scala, play, futures: combining results from multiple futures

I am using:
Scala 2.10
Play 2.1
Currently, I am using the Future class from scala.concurrent._, but I'm open to trying another API.
I am having trouble combining the results of multiple futures into a single List[(String, String)].
The following Controller method successfully returns the results of a single Future to an HTML template:
def test = Action { implicit request =>
queryForm.bindFromRequest.fold(
formWithErrors => Ok("Error!"),
query => {
Async {
getSearchResponse(query, 0).map { response =>
Ok(views.html.form(queryForm,
getAuthors(response.body, List[(String, String)]())))
}
}
})
}
The method getSearchResult(String, Int) performs a web service API call and returns a Future[play.api.libs.ws.Response]. The method getAuthors(String, List[(String, String)]) returns a List[(String, String)] to the HTML template.
Now, I am trying to call getSearchResult(String, Int) in a for loop to get several Response bodies. The following should give an idea of what I'm trying to do, but I get a compile-time error:
def test = Action { implicit request =>
queryForm.bindFromRequest.fold(
formWithErrors => Ok("Error!"),
query => {
Async {
val authors = for (i <- 0 to 100; if i % 10 == 0) yield {
getSearchResponse(query, i)
}.map { response =>
getAuthors(response.body, List[(String, String)]())
}
Ok(views.html.form(queryForm, authors))
}
})
}
type mismatch; found : scala.collection.immutable.IndexedSeq[scala.concurrent.Future[List[(String, String)]]] required: List[(String, String)]
How can I map the responses of several Future objects to a single Result?
Create a Future parametrized by a List or other Collection of the Result type.
From here:
In Play 1 you can do this:
F.Promise<List<WS.HttpResponse>> promises = F.Promise.waitAll(remoteCall1, remoteCall2, remoteCall3);
// where remoteCall1..3 are promises
List<WS.HttpResponse> httpResponses = await(promises); // request gets suspended here
In Play 2 less direct:
val httpResponses = for {
result1 <- remoteCall1
result2 <- remoteCall2
} yield List(result1, result2)