4

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:

  1. Explicitly specifying the Content-Type but this doesn't work because the inherited type takes priority

  2. Building an implicit generic conversion from Product to UrlForm (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 ?

sinanspd
  • 1,989
  • 2
  • 14
  • 28

0 Answers0