1

I have created a simple routes:

class MyRoutes[F[_] : Async](service: MyService[F]) extends Http4sDsl[F] {

  def routes: HttpRoutes[F] = HttpRoutes.of[F] {
    case req@PUT -> Root / "bets" =>
      for {
        bet <- req.as[Bet]
        created <- service.put(bet)
        response <- Created(created)
      } yield response
}

and a jsons implicits for input and output:

object jsons {
  implicit def circeDecoder[A[_] : Sync, B: Decoder]: EntityDecoder[A, B] = jsonOf[A, B]
  implicit def circeEncoder[A[_] : Sync, B: Encoder]: EntityEncoder[A, B] = jsonEncoderOf[A, B]
}

But when I ran this program via Postman, I got an error: The request body was invalid. with 422 error code. I think it is something wrong with json encoder and decoder, because my request was very simple and clear:

{       
    "stake": 434,
    "name": "Something"
}

I tried to add an implicit decoder into routes:

 implicit val betDecoder: EntityDecoder[F, Bet] = jsonOf[F, Bet]

but it also did not help. Could anyone help me with that and tell how to create good encoder and decoder for jsons? I use circe library for parsing.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
Developus
  • 1,400
  • 2
  • 14
  • 50
  • Where's the definition of `Bet` and a `Decoder` for `Bet`? – Koterpillar Apr 19 '19 at 23:59
  • @Koterpillar TS defined `Bet` in his previous [question](https://stackoverflow.com/questions/55767864/scala-monads-value-map-is-not-a-member-of-error). I guess decoder for `Bet` is auto-generated by Circe. I guess the code is https://gist.github.com/DmytroMitin/bbf886b02007f09964b512cfd15208b9 – Dmytro Mitin Apr 20 '19 at 07:42
  • @DmytroMitin yes, it looks like similar to this. So if encoder and decoder are auto-generated by Circe so why it does not work correctly? – Developus Apr 20 '19 at 08:48
  • 1
    If you're deriving a decoder for a case class, it will expect all members of the case class to be in the JSON. Please provide your complete definitions—it's impossible to guess what's wrong with the question as it is. – Travis Brown Apr 20 '19 at 10:35

1 Answers1

1

Ok, stupid me, I solved the problem. I had (probably) wrong definition of Bet. It was:

case class Bet(betId: BetId, stake: BigDecimal, name: String)

case class BetId(betId: String) extends AnyVal

So I should to give Id as a parameter. I changed the code to this one:

case class Bet(betId: Option[BetId], stake: BigDecimal, name: String)

case class BetId(betId: String) extends AnyVal

and after this one everything works correctly. Another question is - is it good practise or could it be done in better way?

Developus
  • 1,400
  • 2
  • 14
  • 50
  • 1
    For me it's ok. Even better option would be to create two case classes, like `PartialBet` (without id) and `Bet` with `Id`. Then when you assign id to your entity just map it to wider case class. You can do it manually or maybe use library like [henkan](https://github.com/kailuowang/henkan). Yet another option is to assign id on client side as uuid. – Krzysztof Atłasik Apr 20 '19 at 10:57
  • 1
    Thanks. Do not know this lib, but looks like interesting. – Developus Apr 20 '19 at 10:59