I am trying to invoke the google geocoding api and retrieve the response.
lazy val geoCodingConnectionFlow: Flow[HttpRequest, HttpResponse, Any] =
Http().outgoingConnectionHttps(config.getString("services.geoCodingApiHost"), config.getInt("services.geoCodingApiPort"))
def geoCodingRequest(request: HttpRequest): Future[HttpResponse] = Source.single(request).via(geoCodingConnectionFlow).runWith(Sink.head)
/**
* This call to google service is limited
* @see https://developers.google.com/maps/documentation/geocoding/#Limits
*/
def ?(l: GeoLocation)(implicit ec: ExecutionContext): Future[Either[String, List[Result]]] = {
val latlang = s"17.3644264,78.3896741"
import org.json4s.native.Serialization
import org.json4s.NoTypeHints
import akka.http.scaladsl.model._
import akka.http.scaladsl.unmarshalling._
implicit val materializer = ActorMaterializer()
implicit val executor = system.dispatcher
implicit val formats = Serialization.formats(NoTypeHints)
geoCodingRequest(RequestBuilding.Get(s"${config.getString("services.geoCodingApiUrlPart")}?latlng=$latlang&key=${config.getString("services.geoCodingApiKey")}")).flatMap { response =>
val nonBinaryType = ContentTypes.`application/json`
def responseEntity: HttpEntity = response.entity
response.status match {
case OK if (response.entity.contentType == ContentTypes.`application/json`) => Unmarshal(response.entity).to[List[Result]].map(Right(_))
case BadRequest => Future.successful(Left(s"$latlang: incorrect Latitude and Longitude format"))
case _ => Unmarshal(response.entity).to[String].flatMap { entity =>
val error = s"Google GeoCoding request failed with status code ${response.status} and entity $entity"
Future.failed(new IOException(error))
}
}
}
}
}
I am getting the following compilation error when trying to execute this!
Service.scala:78: could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.Unmarshaller[akka.http.scaladsl.model.ResponseEntity,List[com.thenewmotion.geocode.Result]]
case OK if(response.entity.contentType == ContentTypes.`application/json`)=> Unmarshal(response.entity).to[List[Result]].map(Right(_))
Please help me to get the result parsed into the following Result case classes:
package com.thenewmotion.geocode
case class Address(
long_name: String,
short_name: String,
types: List[String]
)
case class Result(
address_components: List[Address],
formatted_address: String,
types: List[String]
)
case class Response(
results: List[Result],
status: Status
) {
def allResults = status match {
case Ok => Right(results)
case e: Error => Left(e)
}
}
/** @see https://developers.google.com/maps/documentation/geocoding/#StatusCodes */
sealed trait Status
case object Ok extends Status
sealed trait Error extends Status
case object ZeroResults extends Error
case object OverQuotaLimit extends Error
case object Denied extends Error
case object InvalidRequest extends Error
case class OtherError(description: String) extends Error
^
As said in your error message, you need to provide an implicit Unmarshaller[akka.http.scaladsl.model.ResponseEntity,List[com.thenewmotion.geocode.Result]]
otherwise the framework won't know how to convert the response entity into your model List[com.thenewmotion.geocode.Result]
.
Alternatively, you can use the built in unmarshaller to convert the entity to String first, then use spray-json to parse the json string into the target model:
import akka.http.scaladsl.unmarshalling.Unmarshal
import spray.json._
implicit val modelJsonReader = new JsonReader[List[com.thenewmotion.geocode.Result]] {
// See https://github.com/spray/spray-json on how to implement JsonReader
}
def parseJson(str: String): List[com.thenewmotion.geocode.Result] = {
// parse using spray-json
str.parseJson.convertTo[List[com.thenewmotion.geocode.Result]]
}
response.status match {
case OK if (response.entity.contentType == ContentTypes.`application/json`) =>
Unmarshal(response.entity).to[String].map { jsonString =>
Right(parseJson(jsonString))
}
case BadRequest => Future.successful(Left(s"$latlang: incorrect Latitude and Longitude format"))
case _ => Unmarshal(response.entity).to[String].flatMap { entity =>
val error = s"Google GeoCoding request failed with status code ${response.status} and entity $entity"
Future.failed(new IOException(error))
}
}