softwaremill / softwaremill/tapir

AsyncApi schema - use of `.withDiscriminator` for `Schema` renders illegal async api spec

Open
#3,754 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Scala
Stars
1.5k
Forks
468
Avg merge
5h 37m
Merged PRs (30d)
34

Description

/** Use of discriminator, resulting yaml does not validate. Problematic part:
  * ```
  * discriminator:
  *   propertyName: fruit
  *   mapping:
  *     Apple: '#/components/schemas/Apple'
  *     Potato: '#/components/schemas/Potato'
  * ```
  *
  * It should explicitly provide discriminator values using `const` override.
  * ```
  * Fruit:
  *   title: Fruit
  *   oneOf:
  *   - $ref: '#/components/schemas/Apple'
  *   - $ref: '#/components/schemas/Potato'
  *   discriminator: fruit
  * Apple:
  *   title: Apple
  *   type: object
  *   required:
  *   - color
  *   - fruit
  *   properties:
  *     color:
  *       type: string
  *     fruit:
  *       type: string
  *       const: Apple
  * ```
  *
  * Also note `sttp.tapir.Schema` lacks `const: Option[T]` property which would be needed to represent such schemas.
  */
object AsyncApiExample2 {

  sttp.tapir.Schema

  sealed trait Fruit

  object Fruit {
    case class Apple(color: String) extends Fruit

    case class Potato(weight: Double) extends Fruit

    private implicit val circeConfig: Configuration = Configuration.default.withDiscriminator("fruit")
    implicit val fruitCodec: io.circe.Codec[Fruit] = deriveConfiguredCodec

    private implicit val tapirConfig = sttp.tapir.generic.Configuration.default.withDiscriminator("fruit")
    implicit val fruitSchema: sttp.tapir.Schema[Fruit] = sttp.tapir.Schema.derived
  }

  val ws = endpoint.get
    .in("ws")
    .out(
      webSocketBody[Fruit, CodecFormat.Json, Fruit, CodecFormat.Json](Fs2Streams[IO])
        .responsesExample(Fruit.Apple("red"))
        .responsesExample(Fruit.Potato(1.0))
    )

  // print raw jsonschema for Fruit
  val jsonSchema = TapirSchemaToJsonSchema(Fruit.fruitSchema, markOptionsAsNullable = true, metaSchema = MetaSchemaDraft04)
  import sttp.apispec.circe._
  println(jsonSchema.asJson.deepDropNullValues)

  // print async api
  val asyncApiYaml = AsyncAPIInterpreter()
    .toAsyncAPI(ws, "web socket", "1.0")
    .toYaml

  def main(args: Array[String]): Unit = println(asyncApiYaml)
}

Repo with code: https://github.com/kamilkloch/tapir-async-api/blob/master/src/main/scala/AsyncApiExample2.scala

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with AsyncApiExample2.scala in the linked reproduction repository, then trace how sttp.tapir.Schema.derived and AsyncAPIInterpreter generate the Fruit schema. Confirm the generated AsyncAPI is invalid without explicit discriminator values, and use the raw JSON Schema and YAML output to verify the resulting schema validates with the required const information.

Written by the indexing model from the issue text.

Assessment

Tech stack
scala
Domain
api
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.