Sujit Baniya
Sujit Baniya

Reputation: 915

How to resolve Future[Any] in akka-http

I've created a route:

post {
        path("validate" / Segment / Segment / Segment) { (userId, listId, prefix) =>
            parameters('filter.?) { filter =>
                def result = (
                    phoneValidationActor ? ValidateUserList(userId.toInt, listId.toInt, prefix.toUpperCase, filter)
                )
                complete(result)
            }
        }
    }

And an actor

class PhoneNumberActor extends Actor with ActorLogging {

    import PhoneNumberActor._

    def receive: Receive = {
        case ValidateUserList(userId, listId, prefix, filter) =>
            sender() ! validateUserList(userId, listId, prefix, filter)
    }
}

And a receive function for actor

def validateUserList(user_id: Int, list_id: Int, prefix: String, filter: Option[String]): Future[Seq[PhoneNumber]] = {
    val prefixTrim = prefix.trim
    val listContact = new ListContactRepository
    listContact.getAllContacts(user_id, list_id).map { lines =>
        lines.map { line =>
            validateNumber(line.phone, prefixTrim)
        }
    }
}

In route, result is parsed as Future[Any] instead of Future[Seq[PhoneNumber]] Need help to resolve this issue

Upvotes: 0

Views: 243

Answers (2)

Ian Shiundu
Ian Shiundu

Reputation: 121

Just like @Ivan Stanislavciuc said, you need to add the mapTo method to safely cast a Future to an expected type which in this case is [Seq[PhoneNumber]]. for your route I'd update it to something like such:

post {
    path("validate" / Segment / Segment / Segment) { (userId, listId, prefix) =>
        parameters('filter.?) { filter =>
            val result = 
                (phoneValidationActor ? ValidateUserList(userId.toInt, listId.toInt, prefix.toUpperCase, filter)).mapTo[Seq[PhoneNumber]]
            onSuccess(result) { maybeResult =>
              complete(maybeResult)
           }
        }
    }
}

And if you'd like to handle a Success and Failure you can use onComplete in place of onSuccess and have something like:

                onComplete(result) {
                  case scala.util.Success(res) => complete(res)
                  case scala.util.Failure(ex) => complete(StatusCodes.BadRequest, ex)}

Upvotes: 0

Ivan Stanislavciuc
Ivan Stanislavciuc

Reputation: 7275

You need to call mapTo on result of the ask call.

(phoneValidationActor ? ValidateUserList(userId.toInt, listId.toInt, prefix.toUpperCase, filter))).mapTo[Seq[PhoneNumber]]

and handle future inside the actor to avoid ClassCastException

class PhoneNumberActor extends Actor with ActorLogging {

    import PhoneNumberActor._

    def receive: Receive = {
        case ValidateUserList(userId, listId, prefix, filter) =>
          val theSender = sender() //do not call this method in callback function onSuccess as it breaks actor principles and can send message to a wrong sender 
          validateUserList(userId, listId, prefix, filter).onSuccess { phoneNumbers => 
            theSender ! phoneNumbers
          }
    }
}

Upvotes: 1

Related Questions