tapir icon indicating copy to clipboard operation
tapir copied to clipboard

Snake case and discriminator config ignored

Open axaluss opened this issue 1 year ago • 5 comments

Tapir version: 1.10.12

Scala version: 2.13

I created a simple http post endpoint that receives a json request and produces a json response. It uses schema derivation to map the request and response to case classes. i added implicit config to use discrinator and snake case members

‘’’

implicit val customConfiguration: Configuration = Configuration.default.withDiscriminator(“type_discriminator”).withSnakeCaseMemberNames ‘’’

What is the problem?

The discriminator and snake case work in openapi spec generation but they are completely ignored for zio http server interpretation https://tapir.softwaremill.com/en/latest/server/ziohttp.html

How to reproduce? Apply the implicit config to a tapir endpoint with schema/json derivation and run it in zio server interpreter.

Maybe you can provide code to reproduce the problem?

Additional information

axaluss avatar Jul 05 '24 02:07 axaluss

Which JSON library are you using to perform the deserialisation?

You need to configure both tapir & the JSON codec separately, see: https://tapir.softwaremill.com/en/latest/endpoint/json.html

adamw avatar Jul 05 '24 12:07 adamw

i am using circe. I think I figured it out.

I had to make sure the Configurations are of the right package.

object MyTapirJsonCirce  extends TapirJsonCirce {
  implicit lazy val customTapirSchemaConfig: sttp.tapir.generic.Configuration =
    sttp.tapir.generic.Configuration.default
      .withDiscriminator("type_discriminator")
      .withSnakeCaseMemberNames

  implicit lazy val customCirceJsonConfig: io.circe.generic.extras.Configuration =
    io.circe.generic.extras.Configuration.default
      .withDiscriminator("type_discriminator")
      .withSnakeCaseMemberNames
}

Then I also had to figure out the right import incantation such that the implicits align in just the right way to work... not sure which of these are actually needed.

...
import io.circe._
import io.circe.syntax._
import io.circe.generic.extras._
import io.circe.generic.extras.auto._
import sttp.tapir.Codec.JsonCodec
import sttp.tapir.Schema
import sttp.tapir.generic.auto._
import MyTapirJsonCirce._

val theEndpoint: Endpoint[Unit, Request, ErrorResponse, Response, Any] =
      endpoint
        .in("product")
        .in("events")
        .post
        .in(EndpointInput.derived[Request])
        .out(EndpointOutput.derived[Response])

case class Request(
        @jsonbody
        event:                            Event,
        @query accountId:                 String)
    sealed trait Event
    case class CreateUpdate(
        adminGraphqlApiId: String)
        extends Event

axaluss avatar Jul 05 '24 23:07 axaluss

It may be beneficial to have a documented nontrivial example of this use case of snake case + discriminator

axaluss avatar Jul 06 '24 14:07 axaluss

There's an example here on how to use discriminators: https://github.com/softwaremill/tapir/blob/7f6d4218ff72fcd259eced12113fb38666c4270b/examples/src/main/scala/sttp/tapir/examples/custom_types/sealedTraitWithDiscriminator.scala#L23

I'm currently working on having these examples better visible and discoverable :)

adamw avatar Jul 07 '24 10:07 adamw

As for the improts, you need the import MyTapirJsonCirce._ as it brings into scope the configuraton (which is used during the EndpointInput.derived[Request] call), and the import io.circe.generic.extras.auto._ + import sttp.tapir.generic.auto._ to automatically derive the codec/schema. I don't think the others are needed.

adamw avatar Jul 07 '24 10:07 adamw