scalaparsingserializationakkacirce

How to write codec for HttpResponse(Akka) using circe or jackson or any other library in scala


Hi I am trying to store Request Response of a client to a file.I was easily able to do it for HttpRequest

But when I am trying to write Encoder decoder for HttpResponse I am not able to understand how to write it for entity of HttpResponse in scala.

Heres the code for HTTP Request encoder decoder


val demo=HttpRequest(
  method= HttpMethods.GET,
uri="myUri",
headers=generateHeaders(Map.empty),
  entity="{\"customerReferenceIds\":[{\"customerId\":\"9600007934256702\",\"customerIdType\":\"CUSTOMER_ID\"}]}",
)

demo.asJson.spaces2

I was able to write encoder decoder for HttpRequest easily.

implicit val HttpRequestEncoder: Encoder[HttpRequest] = new Encoder[HttpRequest] {
  final def apply(x: HttpRequest): Json = Json.obj(
    ("method", Json.fromString(x.method.value)),
    ("Uri", Json.fromString(x.uri.toString())) ,
    ("headers", x.headers.map(y=>y.name->y.value).toMap.asJson),
    ("entity", Json.fromString(JsonUtil.toJson(x.entity)))
  )
}

implicit val HttpRequestDecoder: Decoder[HttpRequest] = new Decoder[HttpRequest] {
  final def apply(c: HCursor): Decoder.Result[HttpRequest] =
    for {
      method <- c.downField("method").as[String]
      url <- c.downField("Uri").as[String]
      header <- c.downField("headers").as[Map[String,String]]
      entity <- c.downField("entity").as[String]
    } yield {
      HttpRequest(
        method=HttpMethods.getForKeyCaseInsensitive(method).getOrElse(HttpMethods.GET),
          uri = url,
        headers=generateHeaders(header),
        entity= HttpEntity(ContentTypes.`application/json`,JsonUtil.toJson(entity))
      )
    }
}


I am trying to write for encoder decoder for HttpResponse .Doing something like


//HttpResponse
implicit val HttpResponseEncoder: Encoder[HttpResponse] = new Encoder[HttpResponse] {
  final def apply(x: HttpResponse): Json = {
    Json.obj(
      ("response", Json.fromString(JsonUtil.toJson(x.entity))),
      ("status", Json.fromInt(x.status.intValue()))
    )
  }
}

implicit val HttpResponseDecoder: Decoder[HttpResponse] = new Decoder[HttpResponse] {
  final def apply(c: HCursor): Decoder.Result[HttpResponse] =
    for {
      entity <- c.downField("response").as[String]
      status <- c.downField("status").as[Int]
    } yield {
      HttpResponse(
        status = StatusCode.int2StatusCode(status),
        entity = HttpEntity(ContentTypes.`application/json`, JsonUtil.toJson(entity))
      )
    }
}

Where entity is something like this in debugger enter image description here


Solution

  • For people looking for the answer: Here how I did it:

          val testconfig: Config                 = ConfigFactory.load()
          implicit val actorSystem2: ActorSystem = ActorSystem.create("loyalty-execution-api", testconfig)
        
          implicit val executionContext: ExecutionContext = actorSystem2.dispatcher
        
          implicit val HttpResponseEncoder: Encoder[HttpResponse] = new Encoder[HttpResponse] {
            import akka.http.scaladsl.unmarshalling
            final def apply(x: HttpResponse): Json = {
              val result = Unmarshal(x.entity).to[String].map { z =>
                Json.obj(
                  ("response", Json.fromString(z)),
                  ("status", Json.fromInt(x.status.intValue()))
                )
              }
              val r3     = Await.result(result, 20.seconds)
              r3
            }
          }
        
          implicit val HttpResponseDecoder: Decoder[HttpResponse] = new Decoder[HttpResponse] {
            final def apply(c: HCursor): Decoder.Result[HttpResponse] =
              for {
                entity <- c.downField("response").as[String]
                status <- c.downField("status").as[Int]
              } yield {
                HttpResponse(
                  status = StatusCode.int2StatusCode(status),
                  entity = HttpEntity(ContentTypes.`application/json`, JsonUtil.toJson(entity))
                )
              }
          }
    

    Also you would need to unmarshall twice one for unmarshalling entity where you use the client responose and once in encoder so use this while making a client call:

           val httpResponseFuture = client
              .singleRequest(
                clientRequest,
                settings = httpSettings,
                connectionContext = httpsCtx
              )
              .flatMap { response =>
                response.entity.toStrict(10.seconds).map { allBodyInMemory =>
                  response.withEntity(allBodyInMemory)
                }