Search code examples
scalaakkaakka-streamakka-httpargonaut

Marshal akka `HttpResponse` as Argonaut `Json`


I'm trying to marshal an akka HttpResponse as such:

{
  "code": 200,
  "headers": [],
  "body": "{\"data\": \"Yes!\"}"
}

If I write an Argonaut EncodeJson for this Instance, it might look like this:

implicit def httpResponseEncodeJson: EncodeJson[HttpResponse] =
  EncodeJson(
    (res: HttpResponse) ⇒ {
      ("code" := res._1.value) ->:
      ("headers" := res._2.toList) ->:
      ("body" := res._3) ->: jEmptyObject
    }
  )

I have managed to marshal the headers as json. The only problem is with the body, i.e., the ResponseEntity. Since it is an akka stream, it can only return a future, if I use .toStrict.

Can anybody guide me as to how I might marshal it?


Solution

  • If possible, I would keep the marshalled value as a Future, to preserve the asynchrony of the entity extraction.

    I would start by having something along the lines of

      case class StrictHttpResponse(code: String, headers: List[HttpHeader], body: String)
    
      def toStrictResponse(response: HttpResponse): Future[StrictHttpResponse] = response.entity.dataBytes.runFold(ByteString(""))(_ ++ _).map { bs =>
        StrictHttpResponse(response.status.value, response.headers.toList, bs.utf8String)
      }
    
      implicit def httpResponseEncodeJson: EncodeJson[StrictHttpResponse] =
        EncodeJson(
          (res: StrictHttpResponse) ⇒ {
            ("code" := res.code) ->:
              ("headers" := res.headers) ->:
              ("body" := res.body) ->: jEmptyObject
          }
        )
    
      def encodeResponse(response: HttpResponse): Future[Json] = toStrictResponse(response).map(_.jencode)
    

    and then - e.g. - handle the result of encodeResponse by providing a callback.