mapping future not working in play controller - scala

I have a play controller:
def createBlokPost() = Action { implicit request =>
val postvals: Option[Map[String, Seq[String]]] = request.body.asFormUrlEncoded
postvals.map { args =>
val email = args("email").head
val name = args("name").head
val population = args("population").head
val location = args("location").head
val studentRequirements = args("Student Requirements").head
val schoolFundingMethod = args("School funding method").head
val createSchool = modelPersistent.createSchool(email, name, population.toShort, location, studentRequirements.toShort, schoolFundingMethod.toShort)
var workedVal = false
createSchool.map { thing =>
thing match {
case Some(i) => workedVal = true
case None => workedVal = false
}
}
if (workedVal) {
Ok("It frickin worked!")
} else {
Ok("Something went wrong on our end :( \nYour school may already have a Blok.")
}
}.getOrElse(Ok("Oops. You're a loser. Something went wrong and we cannot help you because in reality... we dont care enough. \nImagine being such a loser that something went wrong on the sign up page 😂.\n\nBTW. Every time this happens I get a notification telling me that someone fucked up and do you know what I do? I laugh knowing that there is some degenerate out there with the ability to fail so quickly."))
}
createSchool is a future[option[Int]]. In my test project this is working so I believe it to be an issue with putting it in the controller. The error I am receiving is:
[error] a.a.ActorSystemImpl - Uncaught error from thread [application-akka.actor.default-dispatcher-10]
scala.runtime.NonLocalReturnControl: null
I do not know what this means but the controller is still completing meaning that the row is added to the database and it returns "Something went wrong on our end :( \nYour school may already have a Blok.". I have also tried a flatMap and onComplete but neither of those are working. Additionally, I tried making the action async but it would not compile.
The issue is that it is not changing the boolean workedVal and it is always returning false even if it is working.
Any help would be greatly appreciated.

What is happening is basically:
the Future result of createSchool(...) is bound to createSchool
workedVal is initialized to false
a callback is attached to createSchool
workedVal is checked and false
Ok with the error message is returned
The createSchool Future completes
The callback is executed, possibly setting workedVal
You'll have to make it an async Action, which means every path has to result in a Future
So something like this should work
postvals.map { args =>
// the args lookups... they're a bit hinky, but setting that aside
modelPersistent.createSchool(email, name, population.toShort, location, studentRequirements.toShort, schoolFundingMethod.toShort)
.map { createResult =>
if (createResult.isDefined) Ok("It frickin' worked!")
else Ok("Something went wrong on our end :( \nYour school may already have a Blok.")
}
}.getOrElse {
Future.successful(Ok("Oops. You're a loser. Something went wrong and we cannot help you because in reality... we dont care enough. \nImagine being such a loser that something went wrong on the sign up page 😂.\n\nBTW. Every time this happens I get a notification telling me that someone fucked up and do you know what I do? I laugh knowing that there is some degenerate out there with the ability to fail so quickly."))
}

Related

Scala IO wait during map external call

I will start mentioning I am very new to Scala but I have now to maintain a legacy code where some new feature are being tried to be include.
I have the following code:
Where a list is coming as a parameter where a new output needs to be processed. However it seems like code is not waiting for the response to the external service when processing.
def historyBet(jackpotListUser : List[JackpotBetHistory])(implicit MC: AppMarkerContext) : List[LegacyJackpotHistoryResponse] =
for {
bet <- jackpotListUser
prize = jackpotIntegratorService.findJackpotByJackpotHumanId(bet.jackpotHumanId) match {
case Some(jackpot : JackpotResponse) =>
...
extra code extracting price from jackpot : JackpotResponse
...
extra code generating result with prize
} yield result
How can I do a call to jackpotIntegratorService.findJackpotByJackpotHumanId to execute at that time. instead of returning something that F[Option....?
def findJackpotByJackpotHumanId(
jackpotHumanId: JackpotHumanId
)(implicit MC: AppMarkerContext): F[Option[JackpotResponse]] =
jackpotIntegratorRepo.findJackpotByJackpotHumanId(jackpotHumanId)
where it is finally implemented as:
override def findJackpotByJackpotHumanId(
jackpotHumanId: JackpotHumanId
)(implicit mc: AppMarkerContext): IO[Option[JackpotResponse]] =
... code calling an API which return the IO.
Thanks!
I thought I could do IO.await somewhere... but not sure where or how...
because in the "historyBet" function I got a F[] when it was an IO... so what is the syntax to be able to wait for the response and the continue?
Extra Comment:
The real issue we notice is that the method call is starting (the logs shows part of it) but the caller with in the maps continues too.
prize = jackpotIntegratorService.findJackpotByJackpotHumanId
this part of the code continues even when prize, which we want the final object JackpotResponse, not the IO or F.
So, if your method needs to call an IO then it must return an IO unless you unsafeRunSync them... but, as the name suggest, you should not do that.
So the return type is now: IO[List[LegacyJackpotHistoryResponse]
And can be implemented like this:
def historyBet(jackpotListUser: List[JackpotBetHistory])(implicit MC: AppMarkerContext): IO[List[LegacyJackpotHistoryResponse]] =
jackpotListUser.traverse { bet =>
jackpotIntegratorService.findJackpotByJackpotHumanId(bet.jackpotHumanId).map {
case Some(jackpot) =>
// ...
case None =>
// ...
}
}

Strange timeout with ScalaTest's Selenium DSL

I'm writing Selenium tests with ScalaTest's Selenium DSL and I'm running into timeouts I can't explain. To make matters more complicated, they only seem to happen some of the time.
The problem occurs whenever I access an Element after a page load or some Javascript rendering. It looks like this:
click on "editEmployee"
eventually {
textField(name("firstName")).value = "Steve"
}
My PatienceConfig is configured like this:
override implicit val patienceConfig: PatienceConfig =
PatienceConfig(timeout = Span(5, Seconds), interval = Span(50, Millis))
The test fails with the following error:
- should not display the old data after an employee was edited *** FAILED ***
The code passed to eventually never returned normally. Attempted 1 times over 10.023253653000001 seconds.
Last failure message: WebElement 'firstName' not found.. (EditOwnerTest.scala:24)
It makes sense that it doesn't succeed immediately, because the click causes some rendering, and the textfield may not be available right away. However, it shouldn't take 10 seconds to make an attempt to find it, right?
Also, I find it very interesting that the eventually block tried it only once, and that it took almost precisely 10 seconds. This smells like a timeout occurred somewhere, and it's not my PatienceConfig, because that was set to time out after 5 seconds.
With this workaround, it does work:
click on "editEmployee"
eventually {
find(name("firstName")).value // from ScalaTest's `OptionValues`
}
textField(name("firstName")).value = "Steve"
I did some digging in the ScalaTest source, and I've noticed that all calls that have this problem (it's not just textField), eventually call webElement at some point. The reason why the workaround works, is because it doesn't call webElement. webElement is defined like this:
def webElement(implicit driver: WebDriver, pos: source.Position = implicitly[source.Position]): WebElement = {
try {
driver.findElement(by)
}
catch {
case e: org.openqa.selenium.NoSuchElementException =>
// the following is avoid the suite instance to be bound/dragged into the messageFun, which can cause serialization problem.
val queryStringValue = queryString
throw new TestFailedException(
(_: StackDepthException) => Some("WebElement '" + queryStringValue + "' not found."),
Some(e),
pos
)
}
}
I've copied that code into my project and played around with it, and it looks like constructing and/or throwing the exception is where most of the 10 seconds are spent.
(EDIT Clarification: I've actually seen the code actually spend its 10 seconds inside the catch block. The implicit wait is set to 0, and besides, if I remove the catch block everything simply works as expected.)
So my question is, what can I do to avoid this strange behaviour? I don't want to have to insert superfluous calls to find all the time, because it's easily forgotten, especially since, as I said, the error occurs only some of the time. (I haven't been able to determine when the behaviour occurs and when it doesn't.)
It is clear that the textField(name("firstName")).value = "Steve" ends up calling the WebElement as you have found out.
Since the issue in the op is happening where ever web elements are involved (which in turn implies that webdriver is involved), I think it is safe to assume that the issue is related to the implicit wait on the Web driver.
implicitlyWait(Span(0, Seconds))
The above should ideally fix the issue. Also, making implicit wait to be 0 is a bad practice. Any web page might have some loading issues. The page load is handled by Selenium outside its wait conditions. But slow element load (may be due to ajax calls) could result in failure. I usually keep 10 seconds as my standard implicit wait. For scenarios which require more wait, explicit waits can be used.
def implicitlyWait(timeout: Span)(implicit driver: WebDriver): Unit = {
driver.manage.timeouts.implicitlyWait(timeout.totalNanos, TimeUnit.NANOSECONDS)
}
Execution Flow:
name("firstName") ends up having value as Query {Val by = By.className("firstName") }.
def name(elementName: String): NameQuery = new NameQuery(elementName)
case class NameQuery(queryString: String) extends Query { val by = By.name(queryString) }
Query is fed to the textField method which calls the Query.webElement as below.
def textField(query: Query)(implicit driver: WebDriver, pos: source.Position): TextField = new TextField(query.webElement)(pos)
sealed trait Query extends Product with Serializable {
val by: By
val queryString: String
def webElement(implicit driver: WebDriver, pos: source.Position = implicitly[source.Position]): WebElement = {
try {
driver.findElement(by)
}
catch {
case e: org.openqa.selenium.NoSuchElementException =>
// the following is avoid the suite instance to be bound/dragged into the messageFun, which can cause serialization problem.
val queryStringValue = queryString
throw new TestFailedException(
(_: StackDepthException) => Some("WebElement '" + queryStringValue + "' not found."),
Some(e),
pos
)
}
}
}
I don't know ScalaTest's specifics, but such strange timeouts usually occur when you're mixing up implicit and explicit waits together.
driver.findElement uses implicit waits internally. And depending on specified explicit waits timeout, you may face with summing both together.
Ideally, implicit waits should be set to 0 to avoid such issues.

Returning value from Scala future completion

Coming from a Java background, I have been trying to teach myself Scala for some time now. As part of that, I am doing a small pet project that exposes a HTTP endpoint that saves the registration numberof a vehicle against the owner and returns the status.
To give more context, I am using Slick as FRM which performs DB operations asynchronously and returns a Future.
Based on the output of this Future, I want to set the status variable to return back to the client.
Here, is the code
def addVehicleOwner(vehicle: Vehicle): String = {
var status = ""
val addFuture = db.run((vehicles returning vehicles.map(_.id)) += vehicle)
addFuture onComplete {
case Success(id) => {
BotLogger.info(LOGTAG, s"Vehicle registered at $id ")
status = String.format("Registration number - '%s' mapped to owner '%s' successfully", vehicle.registration,
vehicle.owner)
println(s"status inside success $status") //--------- (1)
}
case Failure(e: SQLException) if e.getMessage.contains("SQLITE_CONSTRAINT") => {
status = updateVehicleOwner(vehicle)
BotLogger.info(LOGTAG, s"Updated owner='${vehicle.owner}' for '${vehicle.registration}'")
}
case Failure(e) => {
BotLogger.error(LOGTAG, e)
status = "Sorry, unable to add now!"
}
}
exec(addFuture)
println(s"Status=$status") //--------- (2)
status
}
// Helper method for running a query in this example file:
def exec[T](sqlFuture: Future[T]):T = Await.result(sqlFuture, 1 seconds)
This was fairly simple in Java. With Scala, I am facing the following problems:
The expected value gets printed at (1), but (2) always prints empty string and same is what method returns. Can someone explain why?
I even tried marking the var status as #volatile var status, it still evaluates to empty string.
I know, that the above is not the functional way of doing things as I am muting state. What is the clean way of writing code for such cases.
Almost all the examples I could find described how to map the result of Success or handle Failure by doing a println. I want to do more than that.
What are some good references of small projects that I can refer to? Specially, that follow TDD.
Instead of relying on status to complete inside the closure, you can recover over the Future[T] which handle the exception if they occur, and always returns the result you want. This is taking advantage of the nature of expressions in Scala:
val addFuture =
db.run((vehicles returning vehicles.map(_.id)) += vehicle)
.recover {
case e: SQLException if e.getMessage.contains("SQLITE_CONSTRAINT") => {
val status = updateVehicleOwner(vehicle)
BotLogger.info(
LOGTAG,
s"Updated owner='${vehicle.owner}' for '${vehicle.registration}'"
)
status
}
case e => {
BotLogger.error(LOGTAG, e)
val status = "Sorry, unable to add now!"
status
}
}
val result: String = exec(addFuture)
println(s"Status = $result")
result
Note that Await.result should not be used in any production environment as it synchronously blocks on the Future, which is exactly the opposite of what you actually want. If you're already using a Future to delegate work, you want it to complete asynchronously. I'm assuming your exec method was simply for testing purposes.

How to do: Try {block of code} for X duration and catch {Exceptions, such as Timeout}?

Alright so the title is a little obscure since I was unsure how to word it, but essentially I am trying to do a try-catch statement that will timeout... Here's some pseudo-code that may help describe what I'm trying to do:
try (10 seconds) {
*make some connection and do some things*
} catch {
case ex1: TimeoutException => *do something*
case ex2: Exception => *do something else*
}
Currently there is a bug in the hardware I'm working with where the request for a connection never gets a response back, so it just sits there and doesn't catch any exceptions. Since it's only a bug (that should be temporary), I don't want to manipulate the architecture of the application (specifically I do not want to create a new actor just to account for something small) and it would be very ideal if I could implement this pseudo-code within the scope of the class.
So essentially my question is how do I implement the pseudo-code above within the scope of the class it's in?
Let me know if anything is unclear! Thank you!
Try:
import scala.concurrent._
import ExecutionContext.Implicits.global
val f = future {
// make some connection and do some things
}
try {
Await.result(f, 10 seconds);
} catch {
case e: TimeoutException => // do something
case _ => // do something else
}
More info: Futures and Promises

Play slick and Async - is it a race condition?

Reading Play-Slick DBAction code, I thought that this code might contain a race condition:
object DBAction{
// snip
def apply(r: (RequestWithDbSession) => Result)(implicit app:Application) = {
Action { implicit request =>
AsyncResult {
DB.withSession{ s:scala.slick.session.Session =>
Future(r( RequestWithDbSession(request,s) ))(executionContext)
}
}
}
}
The function r runs at a future time, after withSession has returned a Future[Result], and called session.close(). Is there a race condition in this code?
I am not sure if that is called a race condition. However to me it seems that you are correct that something is wrong here. The session might no longer be valid when the future executes the code.
It would be better to invert the execution and request a database session from within the future:
Async {
Future {
DB.withSession{ s:scala.slick.session.Session =>
r( RequestWithDbSession(request, s) )
}
}
}
I think your are right and fix suggested by EECOLOR looks correct. We are tracking this in a ticket: https://github.com/freekh/play-slick/issues/81
Thx