I have a request like the following
val request =
Request[IO](
method = POST,
uri = Uri.uri("..."),
headers = Headers(
Authorization(BasicCredentials("...", "..."))
)
)
.withEntity(PaymentIntentRequest2(2000, "usd"))
I am looking at the source code and it looks like the withEntity
inherits the headers from the nested EntityDecoder
so the code above defaults to Content-Type: application/json
. Where as if I explicitly pass in UrlForm
everything is fine.
Unfortunately the API I am hitting expected the data as x-www-form-urlencoded
and given the complexity of the target API with all the different endpoints/requests I would like to find a way to encode the given case class as a form. What is the best way of doing that?
I have tried:
Explicitly specifying the
Content-Type
but this doesn't work because the inherited type takes priorityBuilding an implicit generic conversion from
Product
toUrlForm
(extension method for now)
implicit class UrlFormEncode[+B <: Product](val u: B) {
def asUrlForm: UrlForm =
u.productElementNames
.zip(u.productIterator)
.foldLeft(UrlForm()) { (a, b) =>
a.combine(UrlForm(b._1 -> b._2.toString))
}
}
The problem here is UrlForm
expects a string in both sides of the mapping. And if I just convert things with .toString
it doesn't work because of nested typed for example:
ChargeRequest(Amount(refInt), EUR, source = Some(SourceId("...."))
Results in the following json
which is not valid
{
"currency": "EUR",
"amount": "2000",
"source": "Some(SourceId(....))",
"customer": "None"
}
I tried asJson
instead of toString
but circe can not decide on the proper KeyEncoder
What is the right way of approaching this so the given Product
is encoded down the stream ?
I just faced the same issue and this is the way it worked for me.
From https://http4s.org/v0.20/client/
And that's it. For some strange reason using
.withHeaders
didn't work for me, seems like they are overridden or so.