How to properly use future/actorSelection/resolveOne in Play app? - scala

Below is my code:
def runAsync(crewType: String) = Action.async {
val temp: Future[Result] = Future(Crew.findCaptainByCrewType(crewType) match {
case None =>
BadRequest(s"Invalid crew name provided: $crewType")
case Some(crew) =>
system.actorSelection(s"/user/${crew.cptName}").resolveOne().map { actorRef =>
Ok(println("hi hi"))
}
})
temp
}
I don't understand why this doesn't work?
My objective is to have user pass in a name, which then I try to find an actor with that name using the actorSelection and resolveOne. I assume I am using it incorrectly?!

ActorSelection.resolveOne() returns a Future[ActorRef], and because you are inside a Future(...) you get a Future[Future[Result]] in case of a valid crewname.
You can solve this by using flatMap in which case you should also return a Future in case of an invalid crewname (None).
Unrelated: You can also leave out the temp value and can replace the pattern matching on Option by Option.fold.
def runAsync(crewType: String) = Action.async {
Future(Crew.findCaptainByCrewType(crewType)).flatMap( _.fold(
Future.successful(BadRequest(s"Invalid crew name provided: $crewType"))
)( crew =>
system.actorSelection(s"/user/${crew.cptName}").resolveOne().map {
actorRef => Ok(println("hi hi")) // not sure you want println here
}
))
}

Related

How can an action convey that it has no body using a type?

An action in the controller have a type Action[A], which means that the body is an A (i.e should parse to A).
I'd like to look at an action's signature and know if it has no body, which is useful for GET requests for example.
e.g here's a code example with two actions, one that accepts a Person in the body and one that does not have a body:
case class Person(name: String)
implicit val personFormats: Format[Person] = Json.format[Person]
val people: mutable.Set[Person] = mutable.Set(Person("alice"), Person("bob"))
/** here it is clear that the body should contain a person info, since the method returns `Action[Person]` */
def createPerson(): Action[Person] =
Action(parse.json[Person]) { request: Request[Person] =>
val person: Person = request.body
if (people.add(person))
Ok(s"added ${person.name}")
else
Ok(s"${person.name} is already there :) nothing special to do")
}
/** Can it be clearer from the signature that there's no body? */
def findPerson(name: String): Action[AnyContent] =
Action {
people.find(_.name == name) match {
case Some(person) => Ok(Json.toJson(person))
case None => NotFound(s"no person named $name")
}
}
When I look at the body of the second action, findPerson, it is clear from Action(parse.empty) that the body is empty.
How can it be clear from the signature as well without messing up the body?
Returning Action[AnyContentAsEmpty] cannot work because play.api.mvc.AnyContentAsEmpty is an object.
There's the following version, where we pass parse.empty as the parser, and it would work, but request is unused, and I wonder if there's another way:
def findPerson2(name: String): Action[Unit] =
Action(parse.empty) { request =>
people.find(_.name == name) match {
case Some(person) => Ok(Json.toJson(person))
case None => NotFound(s"no person named $name")
}
}
Consider EssentialAction which by-design does not have a body parser like so
def findPerson(name: String) =
EssentialAction { _ =>
people.find(_.name == name) match {
case Some(person) => Accumulator.done(Ok(Json.toJson(person)))
case None => Accumulator.done(NotFound(s"no person named $name"))
}
}
or ActionBuilder.IgnoringBody() like so
val ignoringBodyAction = new ActionBuilder.IgnoringBody()(executionContext)
def findPerson(name: String) =
ignoringBodyAction {
people.find(_.name == name) match {
case Some(person) => Ok(Json.toJson(person))
case None => NotFound(s"no person named $name")
}
}

How to break after response in Play (Scala)

I am new to Play Framework using Scala. I want to evaluate a condition and in that condition evaluates to true, I want to send a response and exit at that point. However, the code below that I am trying continues till the end.
I tried breaking with a return statement - However, I get a type mismatch. Can someone help me out with this?
def hello = Action { request =>
if (true) {
Ok("in If")
// Return at this point
}
print("This line should not be printed")
Ok("final")
}
EDIT
Assume a GET call is being made with 4 parameters - name, age, married, spouse. I want to make sure all 3 params (name, age, married) are passed in, and if married is true, check if spouse is passed in. If this validation fails, I want to respond saying Bad Request. Else, continue with logic. How do I write this?
Here is an alternative way to do it:
case class QueryInput(name: String, age: Int, married: Boolean, spouse: Option[String]) {
def validated = if(married && spouse.isEmpty) None else Some(this)
}
def validateInput(request: RequestHeader) = {
val input = for {
name <- request.queryString.get("name").flatMap(_.headOption)
age <- request.queryString.get("age").flatMap(_.headOption.flatMap(a=>Try(a.toInt).toOption))
married <- request.queryString.get("married").flatMap(_.headOption.map(_=="true"))
} yield {
QueryInput(name, age, married, request.queryString.get("spouse").flatMap(_.headOption))
}
input.flatMap(_.validated)
}
def hello() = Action { request =>
validateInput(request) match {
case Some(input) => Ok(input.toString())
case None => BadRequest
}
}
In fact, there are many options. You could also play with the Either class to do validation: Left value to accumulate errors and return bad request, right value to construct your validated input.
My recommendation would be to have a method for validating the parameters. Then do a simple if/else to check if the parameters are valid and return a success or a general error.
If you really want a specific
First thing:
When the block evaluates, all of its expressions and declarations are processed in order, and then the block returns the value of the last expression as its own value.
Second: don't use return.
And the third one is a Play Framework way of resolving your problem: action composition. Though I would not say that it is trivial.
You can do this, by putting a return Ok in but really, thats not the scala way. What you want to do is to change your mindset and imagine everything as a function. If you didnt know, if-then-else always returns a value. For example, you can actually write if this way:
def hello = Action { request =>
val result = if (true) {
Ok("foo")
} else {
Ok("bar")
}
result
}
of course, an even more scala way is to use matchers
def hello = Action { request =>
val result = true match {
case true => Ok("foo")
case _ => Ok("bar")
}
result
}
Take that one step further and you dont even need to specify the result object at all, because scala figures out the returning object based on the last object returned/created.
def hello = Action { request =>
true match {
case true => Ok("foo")
case _ => Ok("bar")
}
}
EDIT: TO answer the OP's edit, you still want to use the matcher. Assuming your vals are options, heres what you do:
def hello(nameOpt:Option[String], ageOpt:Option[String], marriedOpt:Option[String]) = Action { request =>
(nameOpt, ageOpt, marriedOpt) match {
case (Some(name), Some(age), Some(married)) => Ok("all 3 are given")
case (Some(name), Some(age), _) => Ok("all 2 are given")
// functionally same as above
// case (Some(name), Some(age), None) => Ok("all 2 are given")
// some combination of the above
case (None, None, Some(married)) => Ok("married but no name or age")
// default case
case _ => Ok("bar")
}
}

Spray Dead Letter msg

I'm trying to execute the following code
trait CustomHttpService extends HttpService {
import MyJsonProtocol._
import spray.httpx.SprayJsonSupport._
implicit def executionContext = actorRefFactory.dispatcher
implicit val timeout = Timeout(5 seconds)
val offerActor = actorRefFactory.actorOf(Props[OfferActor], "offer-actor")
val defaultRoute = {
path("offer" / JavaUUID) { uuid =>
get {
respondWithMediaType(`application/json`) {
complete {
(offerActor ? Get(uuid)).mapTo[Offer]
}
}
}
}
}
}
class OfferActor extends Actor {
override def receive = {
case Get(id) =>
val future = OfferService.genericService.getById(id)
future.onComplete {
case Success(s) =>
s match {
case Some(offer) => sender ! offer
case None => println("none")
}
case Failure(f) => f
}
case id: String => println("received string id: " + id)
case _ => println("receive nothing")
}
}
Initially I was trying to return directly the future, but it was giving me an error, complaining about the promise that I was trying to cast to my Offer object.
Then I just ugly solve my future inside my actor to finally get the Offer and then return it to the sender.
Doing this I'm getting the following:
[06/09/2015 15:16:43.056]
[spray-system-akka.actor.default-dispatcher-4]
[akka://spray-system/deadLetters] Message
[com.spray.entity.Offer] from
Actor[akka://spray-system/user/spray-actor/offer-actor#-617290326] to
Actor[akka://spray-system/deadLetters] was not delivered. [2] dead
letters encountered. This logging can be turned off or adjusted with
configuration settings 'akka.log-dead-letters' and
'akka.log-dead-letters-during-shutdown'.
Indeed, I'm sending a msg with an Offer that I got from the database.
Instead if I simply create an Offer like this, works perfectly.
case Get(id) => sender ! Offer(Some(id), "offer", new DateTime())
I'm believing the future.onComplete inside the actor is causing something wrong.
Any thoughts?
sender is really a function, so you could write sender() to show that it is not just accessing an immutable value. When you call sender inside the future.onComplete the value of sender isn't valid anymore.
I've run into this problem before and the way I worked around was by saving the value of sender outside of the future:
class OfferActor extends Actor {
override def receive = {
case Get(id) =>
val future = OfferService.genericService.getById(id)
val replyTo = sender
future.onComplete {
case Success(s) =>
s match {
case Some(offer) => replyTo ! offer
case None => println("none")
}
case Failure(f) => f
}
case id: String => println("received string id: " + id)
case _ => println("receive nothing")
}
}
Well, just solved it trying to block my future.
I just created a blocked version of
OfferService.genericService.getByIdBlocking(id)
Where I blocked it with
Await.result
then it worked!
So basically I had to let akka embrace my call with a future using the ask pattern but do blocking operations inside the actor.

return type for findAndModify with futures

Say I'm using an async library to read and write data. If a failure, I get an error message as a string, otherwise the data. Is there a better way to represent the return type than Future[Either[String, Future[Data]]]?
Possible implementation:
def createData(name: String): Future[Either[String, Future[Data]]] = {
dataDAO.findOneByName(name).flatMap {
case Some(data) =>
Future.successful(Left(s"Data with name already exists. $name"))
case None =>
val data = Data.createFromName(name)
dataDAO.save(data).map {
lastError =>
data
}.right.futureSuccessful
}
}
Your return type should be Future[Either[String, Data]]
To archive this you need to change your second case to:
case None =>
val data = Data.createFromName(name)
dataDAO.save(data).map {
_ => Right(data)
}
I even would improve your error type from String to e.g. CreateDataError, so your return type would be Future[Either[CreateDataError, Data]] and your first case would be
// Having somewhere decalred
trait CreateDataError
object DataAlreadyExistsError
//...
//...
case Some(data) =>
Future.successful(Left(DataAlreadyExistsError))

How do I write this without using a Try/Catch block?

I am looking to rewrite this scala function, but I am new to the language, I understand there is a alternative to using try\catch blocks. How would you guys rewrite this function?
def updateStationPost = Action { implicit request =>
StationForm.bindFromRequest.fold(
errors => { //needs to be revised!!
BadRequest(html.updateStation(errors,
Station(
request.body.asFormUrlEncoded.get("id")(0).toLong,
request.body.asFormUrlEncoded.get("operator")(0).toLong,
request.body.asFormUrlEncoded.get("name")(0),
try {
request.body.asFormUrlEncoded.get("number")(0).toInt
} catch {
case e:Exception => { 0 } //this exception happens when trying to convert the number when there is nothing in the flash scope to convert.
},
request.body.asFormUrlEncoded.get("timezone")(0)
),
Operators.retrieveJustOperators() //ugh... needs to be revised..
)
)
},
{ case(stationFormObj) =>
Stations.update(stationFormObj)
Redirect(routes.StationsController.index)
}
)
}
A general way of managing this is to use Try to wrap code that could throw an exception. Some of the ways of using this are illustrated below:
def unpredictable() = {
Try(Console.readLine("Int please: ").toInt) getOrElse 0
}
If the console read does not contain a parseable integer, then it throws an exception. This code just returns a 0 if there was an error, but you could put other statements there. As an alternative, you could use pattern matching to handle the situation.
def unpredictable() = {
Try(Console.readLine("Int please: ").toInt) match {
case Success(i) => i
case Failure(e) => println(e.getMessage())
}
}
You can also just return a Try and let the caller decide how to handle the failure.
How about:
import scala.util.control.Exception.handling
// Create a val like this as you reuse it over and over
val form: Option[Map[String, Seq[String]]] = request.body.asFormUrlEncoded
// Create some helper functions like this
val nfeHandler = handling(classOf[NumberFormatException]) by (_ => 0)
val intNFEHandler = (str: String) => nfeHandler apply str.toInt
val longNFEHandler = (str: String) => nfeHandler apply str.toLong
// You can use this instead of your try catch.. but this is just a sugar.. perhaps cleaner
intNFEHandler apply form.get("id")(0)
Here if the form was something like: Option(Map("id" -> Seq.empty[String]))
form.get("id")(0) would blow up with java.lang.IndexOutOfBoundsException.
I would suggest to have another helper:
// takes fieldNames and returns Option(fieldValue)
val fieldValueOpt = (fieldName: String) => form.flatMap(_.get(fieldName).flatMap(_.headOption))
Then create a validate method which performs pattern matching on all the fieldValue optionals, extract the values and create your Station object.