Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
seveneves opened this issue Jul 3, 2024 · 3 comments

Comments

@seveneves
Copy link

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)
@seveneves seveneves changed the title [BUG] [BUG] Enumeration schema are inconsistent depending on the encoding function Jul 3, 2024
@seveneves seveneves changed the title [BUG] Enumeration schema are inconsistent depending on the encoding function [BUG] Enumeration schema is inconsistent depending on the encoding function Jul 3, 2024
@adamw
Copy link
Member

adamw commented Jul 4, 2024

Hm it seems that this should work, being encoded to json here:

https://github.com/softwaremill/sttp-apispec/blob/a85fff8e6aeb8af7ab162c77a1d1a6db58493841/jsonschema-circe/src/main/scala/sttp/apispec/internal/JsonSchemaCirceEncoders.scala#L134

and converted to a validator here:

case Validator.Enumeration(v, Some(encode), _) => addEnumeration(aschema, v, encode)

But clearly something doesn't work. Would need debugging :)

@seveneves
Copy link
Author

@adamw the problem is that with the default encode function, the produced Enumeration has wraped instances of sealed trait as SString schema. While after encoding and decoding, it represents such values as actual Scala String. Then of course equality breaks when it compares Scala String with an instance of the sealed trait. So the equality boils down to "Female" == Female which of course is false.

When using encode as _.toString then it forces the derived schema to have actual String values instead of instances of the sealed trait. And this fixes the equality check.

@adamw
Copy link
Member

adamw commented Jul 4, 2024

Ah right ... so we would need special comparison logic for example values, which mirrors what encoderExampleSingleValue is doing, that is for all non-primitive values, it just performs .toString

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants