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
CodePudding user response:
The entity filed of the HttpResponse
is a stream so you have to work with some effects - Future
or Stream
, that Encoder
can't handle.
If your entity is a json then I think the easiest way is using entity.toStrict(timeout)
. It returns Future[HttpEntity.Strict] which has a data
method with a plain String.
So your logic might look like this:
val r: Future[EntityResponse] =
response
.entity
.toStrict(timeout)
.map { e =>
Json.obj(
("response", Json.fromString(JsonUtil.toJson(e.data))),
("status", Json.fromInt(response.status.intValue()))
)
}
There https://doc.akka.io/docs/akka-http/current/implications-of-streaming-http-entity.html you can find details about the http entity and how to work with it.
CodePudding user response:
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)
}