Composing `Future` result in Play Framework with Scala - scala

I am trying to write a Play Framework asynchronous Action for the following URL:
POST /users/:userId/items
My database calls all return Future[...], where ... is Option[A] for find methods and Option[Id] for create methods.
I would like to check for the existence of the userId before trying to create the new item. I have a method Users.findById(userId) that returns a Future[Option[User]]. The result is Some(User) if the user exists and None if not. Items.create() also returns a Future[Option[itemId]].
I am trying to compose something using for:
for {
user <- Users.findById(userId)
if user.isDefined
} yield {
Items.create(...) map { itemId => Ok(itemId) } getOrElse NotFound
}
I would like to return Ok(itemId) if the item is successfully created. I'm not sure how to handle the error case. I would like to return NotFound if either the userId is invalid or the item cannot be created (maybe a field conflicts with a unique value already in the database).
I'm not sure what to put after the for structure. I tried getOrElse, but that does not compile, since Future does not have a getOrElse method.
Ideally, I can handle URLs containing several ids to check, e.g.:
PUT /users/:userId/foo/:fooId/bar/:barId
and confirm that userId, fooId, and barId are all valid before doing the update. All of those calls (Users.findById, Foo.findById, and Bar.findById) will return Future[Option[A]].

It's that double-nesting (Future of Option) that seems to get people every time. Things become a lot easier if you can flatten stuff out first.
In this case, Future already has a way of representing an error condition, it can wrap an Exception as well as a success value, that's something you can use...
// making this a Singleton avoids the cost of building a stack trace,
// which only happens when an Exception is constructed (not when it's thrown)
object NotFoundException extends RuntimeException("Empty Option")
// The map operation will trap any thrown exception and fail the Future
def squish[T](x: Future[Option[T]]) =
x map { _.getOrElse(throw NotFoundException) }
It's now a lot easier to use those squished results in a comprehension:
val result = for {
user <- squish(Users findById userId)
itemId <- squish(Items.create(user, ...))
} yield {
Ok(itemId)
} recover {
case NotFoundException => NotFound
}
Which will, of course, evaluate to a Future. This is async programming, after all :)
Any exceptions other than NotFoundException will still be exposed.

Related

How to do a `getOrElseComplete` on `Promise`?

Does it make sense to have an operation like getOrElseComplete that tries to complete a Promise with a value but, if the Promise is already completed, returns the existing completed value instead. Here's a sample implementation:
implicit class PromiseOps[T](promise: Promise[T]) {
def getOrElseComplete(value: Try[T]): Try[T] = {
val didComplete = promise.tryComplete(value)
if (didComplete) {
value
} else {
// The `tryComplete` returned `false`, which means the promise
// was already completed, so the following is safe (?)
promise.future.value.get
}
}
}
Is this safe to do? If not, why not? If so, is there a way to do this directly (eg. without relying on questionable things like _.value.get) or why isn't there such a way in the standard library?
From your comments it seems to me that this is a valid solution for your problem but I also feel that a method like this doesn't belong in Promise API because Promise is supposed to be only a settable "backend" of its Future.
I'd prefer to have a completely separate function which could look like this:
def getOrComplete[T](promise: Promise[T], value: Try[T]): Try[T] =
promise.future.value.getOrElse {
if (promise.tryComplete(value)) value
else getOrComplete(promise, value)
}
The recursive call may seem weird - it serves two purposes:
it protects against a race condition where some other thread completes the future just before we call tryComplete
it avoids usage of .value.get on the Future
You might also want to pass value as a by-name parameter to avoid evaluating it when the Promise is already completed.
This operation does what it promises. It may make more sense to take value by name, and don't try to complete if already completed, maybe something like
def getOrElseComplete(value: => Try[T]): Try[T] = {
if (!promise.completed) {
promise.tryComplete(value)
}
promise.future.value.get
}
It's kinda dodgy though. Sharing a promise and having multiple places where it might be completed sounds like a difficult to maintain design, and one has to ask what's happening with the other path that might still complete the Promise? Shouldn't something be cancelled there?

Is calling a Future method parameter "by-name" necessary?

I have something like the following for caching JSON responses in Redis for a Scala application using http4s, Argonaut, and Slick, and want to confirm that it is working how I expect is. The idea is if a Redis key was not found, then call the given fallback method to get the data from the original source (MySQL) and cache it for future requests, otherwise skip MySQL:
/** Get from MySQL */
def getThingsFromDatabase(matchingId: Int): Future[Seq[Thing]] = {
println("getThingsFromDatabase")
val query = things.filter(_.fieldId === matchingId)
db.run(query.result)
}
/** Get from Redis, else MySQL via `fallback` */
def getThingsFromRedisOrDatabase(key: String,
fallback: Future[Seq[Thing]]):
Future[argonaut.Json] = {
val stored = redis.get(key)
stored match {
// Data exists, return from redis
case Some(s) => {
Parse.parse(s) match { // convert string to Json
case Right(r) => Future { r } // Json => Future[argonaut.Json]
case Left(l) => println(l) // error
}
}
// Data does not exist, get from database and store
case None() => {
val data = fallback.map(_.toList.asJson)
data map { redis.set(key, _) }
data // Future[argonaut.Json]
}
}
}
// GET /things/12
Ok(getThingsFromRedisOrDatabase("things:12", getThingsFromDatabase(12)))
This works, however the above code will always print "getThingsFromDatabase" regardless of whether or not there is data in Redis because getThingsFromDatabase(12) executes when called as a parameter. The original database does not seem to be hit (no errors if it is shutdown) with data in Redis, as intended. I think this is because the fallback Future is not being used in this scenario so it does not complete even though the method was executed.
If fallback: Future[Seq[Thing]] is changed to be call-by-name (i.e. fallback: => Future[Seq[Thing]]), "getThingsFromDatabase" is only printed the first time when the cache is empty, as expected, since fallback is called only in the None() condition and not executed as a parameter.
While the latter is the intended functionality, would there be a difference between the original and the call-by-name version if there wasn't a println in the getThingsFromDatabase method? Both appear to meet the need of not going to MySQL if Redis has the desired data, with neither actually completing the Future even though the former executes the method.
There would be significant difference. As written, the db.run() will be invoked, and the database will execute the query; the results may be discarded but typically the server will do all the work.
If things is a big, unindexed table or if this code is frequently invoked, then yes, you could see significant performance degradation from the unnecessary calls. This example is a poster-child for the usefulness of call-by-name.

Working with futures in slick and scalatra

I am trying to handle a future I got from slick in order to generate a response to a request, but I'm stuck at the "async" part.
Here is a snippet:
get("/tmp") {
new AsyncResult() {
override val is: Future[_] = db.run(Users.findUserWithLogin("user"))
}
}
Now, the db.run call returns a Future[Option[User]]. How do I returns a response depending on the content of the option?
In this case, you need to map the future returned by Slick to the result that you want rather than setting is directly to the Slick result. So, the following would be an example of how you might handle it:
get("/tmp") {
new AsyncResult() {
val is = db.run(Users.findUserWithLogin("user")) map {
case Some(u) => //return some stuff about the user
case None => //return some stuff about user not being found
}
}
}
As noted in the comments below, the AsyncResult is not strictly necessary. You can find more details in the Scalatra documentation.

Play 2.3 - Changing to WebSocket.tryAccept from async

I'm new rather new to Scala so I think this might be a very small problem.
I'm currently trying to change the method chat from using the deprecated WebSocket.async to WebSocket.tryAccept. The application uses the sample chat found at PlayFramework websocket-chat
I'm having trouble creating the complex Future type that the method requires.
This is the old method:
def chat() = WebSocket.async[JsValue] {
request =>
ChatRoom.join("User: 1")
}
New method:
def chat2() = WebSocket.tryAccept[JsValue] {
request =>
try {
// ChatRoom.join returns (iteratee,enumerator)
ChatRoom.join("User: 1").map(e => Right(e))
} catch {
case e: Exception =>
Left(Ok("Failed")) // Error here
}
}
My error message:
found : Left[Result,Nothing]
required: Future[Either[Result,(Iteratee[JsValue, _], Enumerator[JsValue])]]
I have no idea how I am supposed to create such a complex result for such a simple message.
Although ChatRoom.join("User: 1").map(e => Right(e)) doesn't show any errors now, I'm unsure if this is the correct implementation.
I'm not in front of an IDE at the moment, so I can't answer fully, but the return type it's asking for isn't as complex as it seems. An "Either" is a "Left" or a "Right" in the same way that an "Option" is a "Some" or a "None". So what it's asking for is a Future (which Websocket.async should also have required) that contains either a Left[Result] -- the fail-to-connect case, or a Right[(Iteratee, Enumerator)] -- the success case. Assuming that Chatroom.join returns a Future[(Iteratee, Enumerator)], the map operation is simply wrapping that in a "Right". The first thing I'd try is wrapping Left(Ok("Failed")) in a Future and see what happens.

Multiple Futures in Play and using case classes to hold the future data

The scenario:
I have two different Api calls to make (across the web). ApiCall1, ApiCall2. Eventually ApiCall1 will return an Option[Catalogue] and ApiCall2 will return a Seq[Catalogue]
I then need to take these two and build a FrontPage object. During the instantiation of the FrontPage object it creates a Seq[NewProducts]. Everytime it makes a NewProduct the NewProduct must also call a MongoDB across the web inside a Future. Every Future must have completed prior to handing the FrontPage object off to the view.
Here is the code for the FrontPage class:
case class FrontPage(maybeCat1: Option[Catalogue], maybeCat2: Seq[Catalogue]) {
val newProducts:Seq[NewProduct] = {
maybeCat2.map( { cat =>
NewProduct(cat)
})
}
}
Here is the code so far for the NewProduct class:
case class NewProduct(cat:Catalogue) {
val indivProduct:Option[IndivProduct] = {
// ???
// This next line goes out to Mongo and returns a Future[List[JsObject]]
val indiv:Future[List[JsObject]] = MongoFetch.getIndivProduct(cat)
//need to strip out the 'Future', wait for it to return?
val listJS = indiv .. ???? // <-- need just the List[JsObject]]
return IndivProduct(listJs) // <-- constructs a new Option[IndivProduct]
}
}
Here is the code so far for the controller:
def landing() = Action.async {
for {
catalogue1 <- models.Granite.getCatalogue("front-page") // <- ApiCall1
catalogue2 <- models.Granite.getCatalogue("tags") // <- ApiCall2
} yield {
//??? How to now build the FrontPage object
// surely it also depends on the future?
val fp = FrontPage(catalogue1, catalogue2)
Ok(views.html.frontpage.landing(fp)) // <- at this point all futures must have returned.
}
}
I really want to be able to pass a nice neat FrontPage object to the View (and hence the designers) with a really simple set of function defined on it for them to template with. All the futures must return. Catalogue1 and Catalogue2 are not dependent on anything or even each other. Creating a Seq[NewProducts] inside the FrontPage object is depending on both of them having returned. And I can not then pass the FrontPage object to the view until it to has returned the NewProducts from Mongo.
This level of complexity is above what I am used to. I am getting confused with where and when to use for/yield comprehensions. I am afraid that this will somehow just block because the Futures are too far embedded in a case class, within a case class. The very top level of the controller is wrapped in an Async, so will this mean that any and all Futures within that Async call will be non-blocking?
See the futures as steps to get to a fully complete front page, not parts of it and think what each little part of those steps would be.
For example, to construct an instance of NewProduct, create a method that talks to the db and returns the future completed NewProduct instances.
case class NewProduct(cat:Catalogue, indiv: Option[IndivProduct])
def newProductFor(cat: Catalogue): Future[NewProduct] =
for {
listJs <- MongoFetch.getIndivProduct(cat)
} yield NewProduct(cat, IndivProduct(listJs))
You can then create your front page, again, in a function/method that handles the loading/future:
case class FrontPage(
maybeCat1: Option[Catalogue],
maybeCat2: Seq[Catalogue],
newProducts: Seq[NewProduct])
def loadFrontPage: Future[FrontPage] =
for {
catalogue1 <- models.Granite.getCatalogue("front-page")
tags <- models.Granite.getCatalogue("tags")
newProducts <- loadNewProducts(tags)
} yield FrontPage(catalogue1, tags, newProducts)
def loadNewProducts(catalogues: Seq[Catalogue]): Future[Seq[NewProduct]] = {
Future.traverse(catalogues) { catalogue =>
newProductFor(catalogue)
}
}
Note Future.traverse which takes a collection with A:s in and a function that goes from A => Future[B] and returns a Future[collection[B]].
You can then call that in an async controller to feed to a template:
def page() = Action.async {
for {
frontPage <- loadFrontPage
} yield Ok(views.some.template(frontPage))
}