Extracting future in Akka HTTP - scala

Consider the below code:
class TestActor extends Actor {
def receive = {
case "hello" =>
sender ! Future {
"Sample future result"
}
}
}
I have the below route defined:
def r5: Route = {
pathPrefix("check") {
onSuccess(system.actorOf(Props[TestActor]).ask("hello")) {
successResult =>
complete {
"The result is " + successResult
}
}
}
}
When I access the route, I get the output as The result is Success(Sample future result) while I expected it to be The result is Sample future result. From the docs here about onSuccess:
Evaluates its parameter of type Future[T], and once the Future has
been completed successfully, extracts its result as a value of type T
and passes it to the inner route.
and the example given is:
val route =
path("success") {
onSuccess(Future { "Ok" }) { extraction =>
complete(extraction)
}
}
So where am I going wrong?

ask returns a Future[T]. Your pattern match handler inside receive wraps it in an additional Future[T], so you're getting a Future[Future[String]].
If you want to only retrieve T, remove the Future from your TestActor:
case class TestResult(s: String)
case "hello" =>
sender ! TestResult("Sample future result")

If the use of ask is inevitable then in the onComplete in r5 route you need to open the future twice as you are receiving a Future[Future[String]].
The on Complete will unwrap one and you can have
def r5: Route = {
pathPrefix("check") {
onSuccess(system.actorOf(Props[TestActor]).ask("hello")) {
successResult => {
successResult.Oncomplete {
case Success(res) => {
complete {
"The result is " + res //res will have Sample Future Result }
case Failure(ex) => complete("Some Error Occured")
}
}
}

Related

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)
}

Use Future in Spray Routing

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 =>
// ...
}
}

Scala futures and method call backs using spray routing

I have this problem where I want to call a future in spray routing. I have tried below and I have it working and return the future value in onComplete
def sendFuture(input):Future[String]= Future {
//some string manipulation
result
}
post {
entity(as[String]) {
input =>
{
onComplete(sendFuture(input)) {
case Success(result) => {
complete("received: " + result)
}
case Failure(ex) => complete("An error occurred: " + ex.getMessage())
}
}
}
}
Now, I now want to do something more complex. I want to do a call a callback that accepts an argument inside sendFuture and return that string to sendFuture before sendFuture returns it to onComplete. Something like below:
def sendFuture(input):Future[String]= Future {
//some string manipulation
callBack.call(
(arg:String)=>
{
//some string manipulation
return callBackResult
}
)
//do something with result based on callBackResult
result
}
post {
entity(as[String]) {
input =>
{
onComplete(sendFuture(input)) {
case Success(result) => {
complete("received: " + result)
}
case Failure(ex) => complete("An error occurred: " + ex.getMessage())
}
}
}
}
If only callBack.call() would return a string but I it is a void/unit function.
You need to split sendFuture into two function and use map on the result (which is a future) of the first function, but i don't know where you get this callback.call stuff, that's not Scala Future. Anyway the logic is simple:
The first part of the function should contain a promise, which would expect the result of your callback (everything belongs to Scala implementation of Futures), e.g:
def computeFirstpart(input: String): Future[String] = {
val promise: Promise[String] = Promise()
// do something with your string
callback.onComplete { case v => promise.complete(v) }
promise.future
}
This part returns the result of your callback. Now you can attach more computations:
def sendFuture(input: String): Future[String] = {
computeFirstPart(input) flatMap { resultOfFirstPart =>
// do what you need
result // NOTE 1
}
}
NOTE 1 The result should be in Future context, you are returning simple type (like String) use map, not flatMap. Then use it in your route structure:
post {
entity(as[String]) { input =>
onComplete(sendFuture(input)) {
case Success(result) =>
complete("received: " + result)
case Failure(ex) =>
complete("An error occurred: " + ex.getMessage())
}
}
}
}

Scala Play framework-2.1.1 AsyncResult: Handling multiple futures

I need to generate various code snippets based on user posted data, so those snippet generation can be parallel, I am trying to use futures for each and snippet generation and combine them and send AsyncResult, here is the code
val p = for {
pr <- Future { ProviderGenerator.generate(content) }
c <- Future { ModelGenerator.generate(content) }
} yield(pr, c)
Async {
if (p.isCompleted)
println("Futures completed")
p.onSuccess({
case (pr: ActionResult, c: ActionResult) => {
response.add(pr)
response.add(c)
Ok(Json.toJson(response))
}
})
p onFailure {
case _ => println("It failed")
}
p.map {
case (pr: ActionResult, c: ActionResult) => {
println("I am in case")
response.add(pr)
response.add(c)
Ok(Json.toJson(response))
}
case _ => {
println("I am in else case")
Ok(Json.toJson(response))
}
}
}
This is never sending result to user, where as if I use single future in it, I am getting response. How do I handle such multiple futures?
I don't see an obvious problem - try adding a p onFailure { case e: Throwable => Logger.error("failed", e) } perhaps and see if it shows one future is not completing.