Use Future in Spray Routing - scala

I'm new to asynchronous programming. I read this tutorial http://danielwestheide.com/blog/2013/01/09/the-neophytes-guide-to-scala-part-8-welcome-to-the-future.html and was surprised by how effortless I can incorporate Future into the program. However, when I was using Future with Routing, the return type is kind of wrong.
get {
optionalCookie("commToken") {
case Some(commCookie) =>
val response = (MTurkerProgressActor ? Register).mapTo[..].map({...})
val result = Await.result(response, 5 seconds)
setCookie(HttpCookie("commToken", content = result._2.mturker.get.commToken)) {
complete(result._1, result._2.mturker.get)
}
case None => // ...
}
}
I really don't want to use Await (what's the point of asynchronous if I just block the thread and wait for 5 seconds?). I tried to use for-comprehension or flatMap and place the setCookie and complete actions inside, but the return type is unacceptable to Spray. For-comprehension returns "Unit", and flatMap returns a Future.
Since I need to set up this cookie, I need the data inside. Is Await the solution? Or is there a smatter way?

You can use the onSuccess directive:
get {
optionalCookie("commToken") { cookie =>
//....
val response = (MTurkerProgressActor ? Register).mapTo[..].map({...})
onSuccess(response) {
case (result, mTurkerResponse) =>
setCookie(HttpCookie("commToken", content = mTurkerResponse.mturker.get.commToken)) {
complete(result, mturkerResponse.mturker.get)
}
}
}
There's also onFailure and onComplete (for which you have to match on Success and Failure) See http://spray.io/documentation/1.2.1/spray-routing/future-directives/onComplete/
Also, instead of using get directly it's much more idiomatic to use map (I assume the mturker is an Option or something similar):
case (result, mTurkerResponse) =>
mTurkerResponse.mturker.map { mt =>
setCookie(HttpCookie("commToken", content = mt.commToken)) {
complete(result, mt)
}
}

You can also make a custom directive using this code -
case class ExceptionRejection(ex: Throwable) extends Rejection
protected def futureDirective[T](x: Future[T],
exceptionHandler: (Throwable) => Rejection = ExceptionRejection(_)) =
new Directive1[T] {
override def happly(f: (::[T, HNil]) => Route): Route = { ctx =>
x
.map(t => f(t :: HNil)(ctx))
.onFailure { case ex: Exception =>
ctx.reject(exceptionHandler(ex))
}
}
}
Example usage -
protected def getLogin(account: Account) = futureDirective(
logins.findById(account.id)
)
getAccount(access_token) { account =>
getLogin(account) { login =>
// ...
}
}

Related

Return value from method definition that has multiple case statements

val value = authenticateUser
private def authenticateUser = {
val holder = WS.url(platformUrl + "/userApi/auth/login?username=testuser&password=testPass")
val res = holder.post(Results.EmptyContent()).onComplete {
case Success(response) => response.cookies.map{cookie => println(cookie.value.get)}
case Failure(errors) => println("")
// The `Future` failed.
}
}
How to return cookie.value.get from authenticateUser method?
First of all, you seem to have many cookies, so it's not clear, the value of which one you want to return.
More importantly, you should not actually return the value itself. The idea is to return the Future, that can be further transformed downstream:
def authenticateUser = WS.url(..)
.post(Results.EmptyContent)
.map { _.cookies }
.map { _.find(_.name == "Auth").flatMap(_.value).getOrElse("") }
Now, somewhere downstream, you can have something like:
def serve(request: Request): Future[Response] = authenticateUser
.map { user =>
serveRequest(user, request)
}.map { result => makeResponse(result) }
I don't know the specifics of Play, so, consider the Request/Response stuff "pseudocode", but the basic idea is that your entire processing should be a chain of Future transformations. The general rule is to never block the service thread.

How to solve Scala asynchronous behaviour

In my backend controller function I have the following code:
def getContentComponents = Action.async {
contentComponentDTO.list().map { contentComponentsFuture =>
contentComponentsFuture.foreach(contentComponentFuture =>
contentComponentFuture.typeOf match {
case 5 => contentComponentDTO.getContentComponentText(contentComponentFuture.id.get).map(text => {
contentComponentFuture.text = text.text
println(contentComponentFuture.text)
})
}
)
Ok(Json.toJson(contentComponentsFuture))
}
}
The problem is, that OK() is called before the stuff above is finished. Is there a smart way to wait until the foreach is finished?
thanks
That have been two differen questions with different problems! So thats the reason for two questions looking similar
There are two approaches that you can take, but before going to that let's recap what you are trying to do.
You have a list of items contentComponentsFuture, I am assuming retrieving from database, which is why you are getting a future.
Now your contentComponentsFuture is a mutable variable (which I strongly suggest not to use, stick to immutable data) has a text field which you need to update.
Now all that code block before the Ok() will return a future as it is working on a future list. So the easiest solution is to do a map over the future and return the result. The map over a future is just a onComplete function which triggers once the future is resolved. So the code will look like:
def getContentComponents = Action.async {
val futureResult = contentComponentDTO.list().map { contentComponentsFuture =>
contentComponentsFuture.map(contentComponentFuture =>
contentComponentFuture.typeOf match {
case 5 => contentComponentDTO.getContentComponentText(contentComponentFuture.id.get).map(text => {
contentComponentFuture.text = text.text
contentComponentFuture
})
}
)
}
futureResult.map(result => {
Future.sequence(result).map(t => Ok(Json.toJson(t))
}))
}
The other option will be to use scala asycn library: https://github.com/scala/scala-async
which gives a handy wrapper so that you do not need to map over future explicitly, the same code above with scala async library will look like below:
def getContentComponents = Action.async {
Async.async {
val result = Async.await(contentComponentDTO.list().map { contentComponentsFuture =>
contentComponentsFuture.map(contentComponentFuture =>
contentComponentFuture.typeOf match {
case 5 => contentComponentDTO.getContentComponentText(contentComponentFuture.id.get).map(text => {
contentComponentFuture.text = text.text
contentComponentFuture
})
}
)
})
Ok(Json.toJson(result))
}
}

Scala future with akka-http

I need to write simple web service with akka-http and reactivemongo.
Function to save data looks like this
def saveRoute(route: Route):Future[WriteResult] = {
collection.insert(route)
}
a code that calls this function looks like this
val userRoutes = {
logRequestResult("akka-http-microservice") {
path("routes") {
(post & entity(as[Route])) { route =>
Database.saveRoute(route)
}
}
}
}
I need to return result with inserted ID of Route and do this without making the thread to wait.
if try
Database.saveRoute(route).onComplete{
case Success(r) => complete(r.toString)
case Failure(e) => complete(e.getMessage)
}
It cannot compile, because it doesn't return value.
I know how to make it in dirty way, but really want to make in appropriate manner.
What should be done in this case?
Seems like I've found most efficient way to do this. It's built in onComplete directive
(path("routes" / "add") & post & entity(as[Route])) {
route =>
onComplete(routesController.addRoute(route)) {
case Success(result) => complete(StatusCodes.Created, "OK")
case Failure(ex) => complete(new ErrorResponse(StatusCodes.InternalServerError.intValue, ErrorResponse.ERROR, ex.getMessage))
}
}
Use onSuccess to handle the valid response when the future finishes and handleExceptions to handle when the future does not succeed.
val userRoutes = {
handleExceptions(mongoDbExceptionHandler) {
logRequestResult("akka-http-microservice") {
path("routes") {
(post & entity(as[Route])) { route =>
onSuccess(Database.saveRoute(route)) { result =>
complete(result)
}
}
}
}
}
}
// Something like this for whatever the exceptions you expect are
val mongoDbExceptionHandler = ExceptionHandler {
case ex: MongoDbReadException => complete(HttpResponse(InternalServerError, "No database")))
}
onSuccess:
http://doc.akka.io/docs/akka/2.4.9/scala/http/routing-dsl/directives/future-directives/onSuccess.html
handleExceptions:
http://doc.akka.io/docs/akka/2.4.9/scala/http/routing-dsl/exception-handling.html
You can map over the future and then complete the request like below.
val future = Database.saveRoute(route)
val response = future.map(_.getId).recover(_.getMessage)
complete(response)
On a side note, for handling exceptions, it is a good practice to have a ExceptionHandler and wrap it with your route. You can find example here.
You have few option i will try to put the most commonly used ones for REST API based solutions:
OnSuccess use it when you want your expectations to be bubbled and handled by expectionHandler
concat(
path("success") {
onSuccess(Future { "Ok" }) { extraction =>
complete(extraction)
}
},
path("failure") {
onSuccess(Future.failed[String](TestException)) { extraction =>
complete(extraction)
}
}
)
https://doc.akka.io/docs/akka-http/current/routing-dsl/directives/future-directives/onSuccess.html
onComplete: When you want to manually handle the exception. Try Monad wrapped.
val route =
path("divide" / IntNumber / IntNumber) { (a, b) =>
onComplete(divide(a, b)) {
case Success(value) => complete(s"The result was $value")
case Failure(ex) => complete((InternalServerError, s"An error occurred: ${ex.getMessage}"))
}
}
https://doc.akka.io/docs/akka-http/current/routing-dsl/directives/future-directives/onComplete.html
How about this, replace:
Database.saveRoute(route)
with:
complete(Database.saveRoute(route).map(_.toString).recover(_.getMessage))
When you use RequestContext you should use something like this:
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.RouteResult.{Complete, Rejected}
...
val myRoute: Route = (path("my-path") & get) { req: RequestContext =>
val futureResp: Future[HttpResponse] = ???
futureResp.map(resp => RouteResult.Complete(resp))
}

Handle services returning Try values in Spray

I am working on a codebase where calling my Spray API need to synchronously call a service that returns a Try which Spray need to format and return over HTTP.
My initial attempt looked like this :
// Assume myService has a run method that returns a Try[Unit]
lazy val myService = new Service()
val routes =
path("api") {
get {
tryToComplete {
myService.run()
}
}
} ~
path("api" / LongNumber) { (num: Long) =>
get {
tryToComplete {
myService.run(num)
}
}
}
def tryToComplete(result: => Try[Unit]): routing.Route = result match {
case Failure(t) => complete(StatusCodes.BadRequest, t.getMessage)
case Success(_) => complete("success")
}
However this caused myService.run() to be called when the application started. I am not sure why this method was called as there was no HTTP call made.
So I have two questions :
Why is my service being called as part of initialising the routes?
What is the cleanest way to handle this case? Imagine that there are a few other end points following a similar pattern. So I need to be able to handle this consistently.
Even though you have result parameter as call-by-name, it'll immediately get evaluated as you're doing
result match {
For it not to get evaluated, it has to be within the complete, ie your code should look something like (haven't tried to compile this):
def tryToComplete(result: => Try[Unit]): routing.Route = complete {
result match {
case Failure(t) => StatusCodes.BadRequest, t.getMessage
case Success(_) => "success"
}
}
The way I solved this was do do the following :
lazy val myService = new Service()
val routes =
path("api") {
get {
complete {
handleTry {
myService.run()
}
}
}
} ~
path("api" / LongNumber) { (num: Long) =>
get {
complete {
handleTry {
myService.run(num)
}
}
}
}
private def handleTry(operation: Try[_]):HttpResponse = operation match {
case Failure(t) =>
HttpResponse(status = StatusCodes.BadRequest, entity = t.getMessage)
case Success(_) =>
HttpResponse(status = StatusCodes.OK, entity = successMessage)
}

Unable to find Writable inside Action.async in play framework

Appears I am missing something but here is what I have got (posting only relevant piece). where MyService.save returns Future[Option[MyCompany] ].
def myPost = Action.async(parse.json) { request =>
val mn = Json.fromJson[MyEntity](request.body)
mn.map{
case m : MyEntity => MyService.save(m).map{f=>
f.map(mm=> Ok(mm ) )
}
}.recoverTotal {
e => Future { BadRequest("Detected error:" + JsError.toFlatJson(e)) }
}
}
Although I have defined
implicit val companyWriter: Writes[MyCompany] = (...)
And this implicit is in the scope, it shows compile error
Cannot write an instance of MyCompany to HTTP response. Try to define
a Writeable[MyCompany]
FYI: This writer is used elsewhere where I do Json.toJson(myCompany) and over there it finds and works fine.
Anything in particular to async Ok that it's missing?
EDIT
It appears that Ok() method cannot figure out the MyCompany needs to be transformed to json. following seems to have worked.
Ok(Json.toJson(mm) )
Is this because arguments to Ok can vary? Separately there are too many "map" in the above code. Any recommendation for improvement and making it more concise ?
Your compiler error is about a Writeable, not a Writes. Writeables are used to convert whatever you have to something that can be written to an HTTP response, Writes are used to marshall objects to JSON. The names can be a little confusing.
As for style...
def myPost = Action.async(parse.json) { request =>
request.body.validate[MyEntity] map { myEntity =>
MyService.save(myEntity).map { maybeCompany =>
maybeCompany match {
case Some(company) => Ok(Json.toJson(company))
case None => NoContent // or whatever's appropriate
}
}
} recoverTotal { t =>
Future { BadRequest("Detected error: " + JsError.toFlatJson(e)) }
}
}