softwaremill / softwaremill/tapir

[BUG] Enumeration schema is inconsistent depending on the encoding function

Open
#3,900 3 comments 0 reactions 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

Tapir version: 1.10.10

Scala version: 2.13/3.3.3

I am deriving apispec.Schema from tapir.Schema for an enumeration and found that the default encode function produces inconsistent apispec.Schemas. Such inconsistency is exposed when apispec.Schema is encoded and decoded to/form json.

This problem is highlighted with the following code

Given following sealed trait

sealed trait Gender
case object Female extends Gender
case object Male extends Gender

with helper methods

import io.circe.syntax.*
import sttp.apispec.circe.*
import sttp.tapir.docs.apispec.schema.TapirSchemaToJsonSchema
import sttp.{apispec, tapir}

def decodeJson(jsonSchema: String): apispec.Schema =
  io.circe.parser
    .decode[apispec.Schema](jsonSchema)
    .toTry
    .get

def encodeJson(schema: apispec.Schema) =  schema.asJson.deepDropNullValues.toString()

def apiSpecSchema[T: tapir.Schema] = TapirSchemaToJsonSchema(
  implicitly[tapir.Schema[T]],
  markOptionsAsNullable = true,
)

Then the following works correctly and assertion passes

implicit val GenderSchema = tapir.Schema.derivedEnumeration[Gender](encode = Some(_.toString))
val derivedSchema: apispec.Schema = apiSpecSchema[Gender]
val decodedSchema = decodeJson(encodeJson(derivedSchema))
assert(derivedSchema == decodedSchema)

But when using derivedEnumeration with the default encode function, the following code fails

implicit val GenderSchema = tapir.Schema.derivedEnumeration[Gender]()
val derivedSchema: apispec.Schema = apiSpecSchema[Gender]
val decodedSchema: apispec.Schema = decodeJson(encodeJson(derivedSchema))
assert(derivedSchema == decodedSchema)

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 TapirSchemaToJsonSchema and the derivedEnumeration handling shown in the reproduction. Run the two Scala examples and compare the apispec.Schema before and after the Circe JSON round trip, focusing on the default encode function. Done means the default enumeration encoding produces a schema that remains equal after encoding and decoding.

Written by the indexing model from the issue text.

Assessment

Tech stack
openapi, scala
Domain
api
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.