Akka HTTP client - Unmarshal with Play JSON - scala

I am using Akka HTTP as a client to do a POST request and parse the answer. I am using Play JSON and I get the following compiler error:
could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.Unmarshaller[akka.http.javadsl.model.ResponseEntity,B]
[ERROR] Unmarshal(response.entity).to[B].recoverWith {
This is the dependency I added to use Play JSON instead of Spray:
"de.heikoseeberger" %% "akka-http-play-json"
My class definition is:
class HttpClient(implicit val system: ActorSystem, val materializer: Materializer) extends PlayJsonSupport {
and the method definition is:
private def parseResponse[B](response: HttpResponse)(implicit reads: Reads[B]): Future[B] = {
if (response.status().isSuccess) {
Unmarshal(response.entity).to[B].recoverWith {
....
In the imports I have:
import play.api.libs.json._
import scala.concurrent.ExecutionContext.Implicits.global
import de.heikoseeberger.akkahttpplayjson.PlayJsonSupport._
It seems to me that I have the required implicits in scope. The Marshal part has a similar logic (but with Writes instead of Reads) and compiles fine. What am I missing?

Check your other imports. Based on the error message, it appears that you're using akka.http.javadsl.model.HttpResponse instead of akka.http.scaladsl.model.HttpResponse; PlayJsonSupport only supports the Scala DSL:
private def parseResponse[B](response: HttpResponse)(implicit reads: Reads[B]): Future[B] = ???
// ^ this should be akka.http.scaladsl.model.HttpResponse
In other words, use
import akka.http.scaladsl.model._
instead of
import akka.http.javadsl.model._

Related

Could not find implicit value for parameter write eror, yet I defined the handler using the macro

I have the following:
Account.scala
package modules.accounts
import java.time.Instant
import reactivemongo.api.bson._
case class Account(id: String, name: String)
object Account {
type ID = String
implicit val accountHandler: BSONDocumentHandler[Account] = Macros.handler[Account]
// implicit def accountWriter: BSONDocumentWriter[Account] = Macros.writer[Account]
// implicit def accountReader: BSONDocumentReader[Account] = Macros.reader[Account]
}
AccountRepo.scala
package modules.accounts
import java.time.Instant
import reactivemongo.api.collections.bson.BSONCollection
import scala.concurrent.ExecutionContext
final class AccountRepo(
val coll: BSONCollection
)(implicit ec: ExecutionContext) {
import Account.{ accountHandler, ID }
def insertTest() = {
val doc = Account(s"account123", "accountName") //, Instant.now)
coll.insert.one(doc)
}
}
The error I am getting is:
could not find implicit value for parameter writer: AccountRepo.this.coll.pack.Writer[modules.accounts.Account]
[error] coll.insert.one(doc)
From what I understand the implicit handler that is generated by the macro should be enough and create the Writer. What am I doing wrong?
Reference: http://reactivemongo.org/releases/1.0/documentation/bson/typeclasses.html
The code is mismixing different versions.
The macro generated handler is using the new BSON API, as it can be seen with the import reactivemongo.api.bson, whereas the collection is using an old driver, as it can be seen as it uses reactivemongo.api.collections.bson instead of reactivemongo.api.bson.collection.
It's recommended to have a look at the documentation, and not mixing incompatible versions of related libraries.

Scala Akka HTTP casting parameter as java.time.ZonedDateTime

I'm working on a REST service using Akka HTTP (in Scala). I would like a parameter that is passed in to a http get request to be converted to the ZonedDateTime type. The code works fine if I try to use String or Int but fails with a ZonedDateTime type. The code would look like so:
parameters('testparam.as[ZonedDateTime])
Here is the error that I'm seeing:
Error:(23, 35) type mismatch;
found : akka.http.scaladsl.common.NameReceptacle[java.time.ZonedDateTime]
required: akka.http.scaladsl.server.directives.ParameterDirectives.ParamMagnet
parameters('testparam.as[ZonedDateTime]){
If I add more than one parameter to the list I get a different error:
Error:(23, 21) too many arguments for method parameters: (pdm: akka.http.scaladsl.server.directives.ParameterDirectives.ParamMagnet)pdm.Out
parameters('testparam.as[ZonedDateTime], 'testp2){
I found this in the documentation when I was researching the problem http://doc.akka.io/japi/akka-stream-and-http-experimental/2.0/akka/http/scaladsl/server/directives/ParameterDirectives.html and I tried the workaround of adding import akka.http.scaladsl.server.directives.ParameterDirectives.ParamMagnet along with using Scala 2.11 but the problem persisted.
Could someone please explain what I'm doing wrong and why the ZonedDateTime type doesn't work? Thanks in advance!
Here is a code snippet that should reproduce the problem I'm seeing
import java.time.ZonedDateTime
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import scala.io.StdIn
object WebServer {
def main(args: Array[String]) {
implicit val system = ActorSystem("my-system")
implicit val materializer = ActorMaterializer()
// needed for the future flatMap/onComplete in the end
implicit val executionContext = system.dispatcher
val route =
path("hello") {
get {
parameters('testparam.as[ZonedDateTime]){
(testparam) =>
complete(testparam.toString)
}
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
StdIn.readLine() // let it run until user presses return
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done
}
}
As ZonedDateTime is not natively unmarshalled by Akka-HTTP, you will need to provide a custom unmarshaller to the parameters directive.
This functionality is briefly described in the docs here.
Your unmarshaller can be created from a function by using Unmarshaller.strict, e.g.
val stringToZonedDateTime = Unmarshaller.strict[String, ZonedDateTime](ZonedDateTime.parse)
This example assumes your param is provided in an ISO format. If it's not, you'll need to amend the unmarshalling function.
You can then use the unmarshaller passing it to the parameters directive:
parameters('testparam.as(stringToZonedDateTime)){ testparam =>
complete(testparam.toString)
}

Spray/Akka missing implicit

MyService.scala:33: could not find implicit value for parameter eh: spray.routing.ExceptionHandler
I have run into a "missing implicit" compilation error using Akka, in spray.io code that makes an http call to a separate back-end server, as part of responding to an http get. The code needs to import quite a lot of Spray and Akka libraries, so it's a bit hard figuring whether there may be some library conflicts causing this, and I'd rather figure how to logically trace this sort of problem for this and other cases.
The missing implicit is encountered on calling runRoute(myRoute)
Here's the code:
import spray.routing._
import akka.actor.Actor
import akka.actor.ActorSystem
import spray.http._
import MediaTypes._
import akka.io.IO
import spray.httpx.RequestBuilding._
import scala.concurrent.Future
import spray.can.Http
import spray.http._
import akka.util.Timeout
import HttpMethods._
import akka.pattern.ask
import akka.event.Logging
import scala.concurrent.duration._
// we don't implement our route structure directly in the service actor because
// we want to be able to test it independently, without having to spin up an actor
class MyServiceActor extends Actor with MyService with akka.actor.ActorLogging {
log.info("Starting")
// the HttpService trait defines only one abstract member, which
// connects the services environment to the enclosing actor or test
def actorRefFactory = context
// this actor only runs our route, but you could add
// other things here, like request stream processing
// or timeout handling
def receive = runRoute(myRoute)
}
// this trait defines our service behavior independently from the service actor
trait MyService extends HttpService {
implicit val system: ActorSystem = ActorSystem()
implicit val timeout: Timeout = Timeout(15.seconds)
import system.dispatcher // implicit execution context
//val logger = context.actorSelection("/user/logger")
val logger = actorRefFactory.actorSelection("../logger")
val myRoute =
{
def forward(): String = {
logger ! Log("forwarding to backend")
val response: Future[HttpResponse] =
(IO(Http) ? Get("http:3080//localhost/backend")).mapTo[HttpResponse]
"<html><body><h1>api response after backend processing</h1></body></html>"
}
path("") {
get {
respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here
complete(forward)
}
}
}
}
}
I am wondering what's the best way to solve this, hopefully providing insight into how to solve similar problems with implicits being missing, as they are somehow inherently not straightforward to track down.
EDIT: when trying to directly pass implicits as in #christian's answer below, I get:
MyService.scala:35: ambiguous implicit values:
both value context in trait Actor of type => akka.actor.ActorContext
and value system in trait MyService of type => akka.actor.ActorSystem
match expected type akka.actor.ActorRefFactory
RoutingSettings.default, LoggingContext.fromActorRefFactory)
^
Not quite sure why being specific as in #christian's answer leaves room for ambiguity for the compiler...
I ran into the same "could not find implicit value for parameter eh: spray.routing.ExceptionHandler" error earlier today. I tried #Christian's approach but saw a few "implicit values for xxx" creeping up. After scouting the error message a little I found adding implicit val system = context.system to the actor that runRoute solved the problem.
runRoute expects a few implicits. You are missing an import:
import spray.routing.RejectionHandler.Default
Update:
I think we also did have some problems with runRoute because we are supplying the implicit parameters explicitly:
runRoute(route)(ExceptionHandler.default, RejectionHandler.Default, context,
RoutingSettings.default, LoggingContext.fromActorRefFactory)
Update2:
To fix the last error, remove the creation of the ActorSystem (in MyService you get the actor system from MyServiceActor - therefore you have to use a self type annotation). This compiles:
import akka.actor.Actor
import akka.io.IO
import spray.httpx.RequestBuilding._
import spray.http.MediaTypes._
import spray.routing.{RoutingSettings, RejectionHandler, ExceptionHandler, HttpService}
import spray.util.LoggingContext
import scala.concurrent.Future
import spray.can.Http
import spray.http._
import akka.util.Timeout
import HttpMethods._
import akka.pattern.ask
import akka.event.Logging
import scala.concurrent.duration._
// we don't implement our route structure directly in the service actor because
// we want to be able to test it independently, without having to spin up an actor
class MyServiceActor extends Actor with MyService with akka.actor.ActorLogging {
log.info("Starting")
// the HttpService trait defines only one abstract member, which
// connects the services environment to the enclosing actor or test
implicit def actorRefFactory = context
// this actor only runs our route, but you could add
// other things here, like request stream processing
// or timeout handling
def receive = runRoute(myRoute)(ExceptionHandler.default, RejectionHandler.Default, context,
RoutingSettings.default, LoggingContext.fromActorRefFactory)
}
// this trait defines our service behavior independently from the service actor
trait MyService extends HttpService { this: MyServiceActor =>
implicit val timeout: Timeout = Timeout(15.seconds)
implicit val system = context.system
//val logger = context.actorSelection("/user/logger")
val logger = actorRefFactory.actorSelection("../logger")
val myRoute =
{
def forward(): String = {
//logger ! Log("forwarding to backend")
val response: Future[HttpResponse] =
(IO(Http) ? Get("http:3080//localhost/backend")).mapTo[HttpResponse]
"<html><body><h1>api response after backend processing</h1></body></html>"
}
path("") {
get {
respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here
complete(forward)
}
}
}
}
}

Implicits getting out of scope in Spray example code: what's happening here?

I have copied Spray Client's example code into my own project, to have it easily available. I use IntelliJ 13.
Here is the code I have:
package mypackage
import scala.util.Success
import scala.concurrent.duration._
import akka.actor.ActorSystem
import akka.pattern.ask
import akka.event.Logging
import akka.io.IO
import spray.json.{JsonFormat, DefaultJsonProtocol}
import spray.can.Http
import spray.util._
import spray.client.pipelining._
import scala.util.Success
import scala.util.Failure
case class Elevation(location: Location, elevation: Double)
case class Location(lat: Double, lng: Double)
case class GoogleApiResult[T](status: String, results: List[T])
object ElevationJsonProtocol extends DefaultJsonProtocol {
implicit val locationFormat = jsonFormat2(Location)
implicit val elevationFormat = jsonFormat2(Elevation)
implicit def googleApiResultFormat[T :JsonFormat] = jsonFormat2(GoogleApiResult.apply[T])
}
object SprayExample extends App {
// we need an ActorSystem to host our application in
implicit val system = ActorSystem("simple-spray-client")
import system.dispatcher // execution context for futures below
val log = Logging(system, getClass)
log.info("Requesting the elevation of Mt. Everest from Googles Elevation API...")
val pipeline = sendReceive ~> unmarshal[GoogleApiResult[Elevation]]
val responseFuture = pipeline {
Get("http://maps.googleapis.com/maps/api/elevation/json?locations=27.988056,86.925278&sensor=false")
}
responseFuture onComplete {
case Success(GoogleApiResult(_, Elevation(_, elevation) :: _)) =>
log.info("The elevation of Mt. Everest is: {} m", elevation)
shutdown()
case Success(somethingUnexpected) =>
log.warning("The Google API call was successful but returned something unexpected: '{}'.", somethingUnexpected)
shutdown()
case Failure(error) =>
log.error(error, "Couldn't get elevation")
shutdown()
}
def shutdown(): Unit = {
IO(Http).ask(Http.CloseAll)(1.second).await
system.shutdown()
}
}
As it stands, this works perfectly and it prints the height of Mt.Everest as expected.
The strange thing happens if I move the file down one level in the package structure, that is I create a mypackage.myinnerpackage and move the file inside it.
IDEA changes my first line of code into package mypackage.myinnerpackage and that's it.
Then I try to run the app and the compilation will fail with the following message:
could not find implicit value for evidence parameter of type spray.httpx.unmarshalling.FromResponseUnmarshaller[courserahelper.sprayexamples.GoogleApiResult[courserahelper.sprayexamples.Elevation]]
val pipeline = sendReceive ~> unmarshal[GoogleApiResult[Elevation]]
^
I did not change anything in the code, I effectively just changed the package! Additionally, this code is self contained, it does not rely on any other implicit I declared in any other part of my code....
What am I missing?
Thanks!
(Replaced the comment by this answer which supports proper formatting.)
The code you posted is missing these two imports before the usage of unmarshal:
import ElevationJsonProtocol._
import SprayJsonSupport._
val pipeline = sendReceive ~> unmarshal[GoogleApiResult[Elevation]]
which exist in the original code. IntelliJ is sometimes messing with imports so that may be the reason they got lost in the transition.
You need to provide a Json Formatter for your case class.
case class Foo(whatever: Option[String])
object FooProtocol extends DefaultJsonProtocol {
implicit val fooJsonFormat = jsonFormat1(Foo)
}
Then include the following near the implementation...
import SprayJsonSupport._
import co.xxx.FooProtocol._

spray-json cannot marshal Map[String,String]

I have the following route setup, but when my map is returned in the first complete block I get an error:
could not find implicit value for evidence parameter of type spray.httpx.marshalling.Marshaller[scala.collection.immutable.Map[String,String]]
import spray.routing.HttpService
import akka.actor.Actor
import spray.http.HttpRequest
import spray.routing.RequestContext
import spray.json.DefaultJsonProtocol._
class UserServiceActor extends Actor with RestUserService {
def actorRefFactory = context
def receive = runRoute(linkRoute)
}
trait RestUserService extends HttpService {
val userService = new LinkUserService
def linkRoute =
pathPrefix("user" / Segment) {
userId =>
path("link") {
parameters('service ! "YT") {
complete {
Map("status"-> "OK", "auth_url" -> "http://mydomain.com/auth")
}
}
}
}
}
According to this test I should be able to convert a Map to json when DefaultJsonProtocol._ is imported but even that's failing:
val map:Map[String, String] = Map("hi"->"bye")
map.toJson
Cannot find JsonWriter or JsonFormat type class for scala.collection.mutable.Map[String,String]
Not sure what's wrong :(
Someone on the spray mailing list pointed out that the Map being created was a mutable one, spray-json won't marshal that. I changed it to be scala.collection.immutable.Map and also added the following imports:
import spray.httpx.SprayJsonSupport._
import spray.json.DefaultJsonProtocol._
And now everything works great.