Reputation: 4232
I'm trying to write an actor in Akka/Scala that calls an HTTP REST API and sends the result back to the calling actor. The API may returns a collection/vector of results that must first be converted to an internal vendor neutral format so that the vendor can be changed in future without much changes required on our code. Most of the code is working but I'm not sure how to unpack and send the inner vector.
This is the code I have and it returns a Promise
to the invoking actor. What I would like to return is the actual vector that gets created in the final map
operation:
class RESTActor extends Actor with ActorLogging with JsonSupport {
final implicit val materializer: ActorMaterializer = ActorMaterializer(ActorMaterializerSettings(context.system))
val http = Http(context.system)
import akka.pattern.pipe
import context.dispatcher
override def receive: Receive = {
case query: String => {
val requester = sender
var uri = Uri(Settings.autoCompleteURL).withQuery(Query(Map("query" -> query)))
sender! http
.singleRequest(HttpRequest(HttpMethods.GET, uri = uri))
.flatMap(response =>
response.status match {
case status if status.isSuccess() => Unmarshal(response.entity).to[VendorResponse].map(_.result.map(x => VendorNeutralResponse(x.id, x.field)))
case _ => response.entity.toStrict(5.seconds).map { entity =>
val body = entity.data.decodeString("UTF-8")
log.warning(errorMessage(response, body))
Left(errorMessage(response, body))
}
})
}
}
The calling actor:
pathPrefix("search") {
get {
parameter("query") { query =>
{
complete(restActor.ask(query)) //Doesn't work as the reply is a promise
}
}
}
}
How do I change the above code in the RESTActor to send actual result back instead of a future or promise?
Edit: After changing the code based on my own research and suggestions from @michał and @cyrille-corpet, the following code works:
pathPrefix("search") {
get {
parameter("query") { query =>
{
onComplete(gisRouter.ask(query)) {
case Success(resp: Future[Vector[VendorNeutralResponse]]) => {
resp.map(println)
complete("ok")
}
case Failure(e) => complete(e.toString)
}
}
}
}
}
It seems like I'm still getting a future
as a response from my Actor. How do I get the actor to respond with actual data and not a Future
?
Upvotes: 2
Views: 361
Reputation: 2484
You can do it monadic-like using map:
pathPrefix("search") {
get {
parameter("query") { query =>
restActor.ask(query) map {
case Success(resp) => ...
case Failure(e) => ...
}
}
}
}
EDIT: Currently your Actor responds with future. Try refactoring it so it does return unwrapped value instead:
class RESTActor extends Actor with ActorLogging with JsonSupport {
final implicit val materializer: ActorMaterializer = ActorMaterializer(ActorMaterializerSettings(context.system))
val http = Http(context.system)
import akka.pattern.pipe
import context.dispatcher
private def handleHttpResponse = {
case status if status.isSuccess() => Unmarshal(response.entity).to[VendorResponse].map(_.result.map(x => VendorNeutralResponse(x.id, x.field)))
case _ => response.entity.toStrict(5.seconds).map { entity =>
val body = entity.data.decodeString("UTF-8")
log.warning(errorMessage(response, body))
Left(errorMessage(response, body))
}
}
override def receive: Receive = {
case query: String => {
val requester = sender
var uri = Uri(Settings.autoCompleteURL).withQuery(Query(Map("query" -> query)))
http.singleRequest(HttpRequest(HttpMethods.GET, uri = uri)).flatMap(response =>
response.status map handleHttpResponse ) pipeTo requester
}
}
Upvotes: 1
Reputation: 5315
You can use the onComplete
function, that takes a Future[T]
as input, and returns a Directive1[Try[T]]
, so you can use as following:
pathPrefix("search") {
get {
parameter("query") { query =>
onComplete(restActor.ask(query)) {
case Success(resp) => ...
case Failure(e) => ...
}
}
}
}
EDIT about the return from your actor, you should pipe the result of http.singleRequest
to the sender, instead of telling it:
http.singleRequest(...).flatMap(...) pipeTo requester
That way, the actual tell (!
) will be done only once the Future
is resolved.
Upvotes: 1