softwaremill / softwaremill/tapir

Schema generation issue

Open
#568 9 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

Guys, I've got the following problem with schema generation for case classes that contain another case classes as fields, and types of these fields are declared in the companion object of case class which contains them

For example if you write this:

final case class AccountType(id: AccountType.Id, name: Option[String], code: Option[AccountType.Code])

object AccountType {

  final case class Id(value: UUID)
  object Id {
    implicit val schema: Schema[Id] = Schema(SchemaType.SString).format("UUID")
  }

  final case class Code(value: String)
  object Code {
    implicit val schema: Schema[Code] = Schema(SchemaType.SString)
  }
}

for field code: Option[AccountType.Code] which type is declared in AccountType object the
following swagger will be generated:

"AccountType" : {
        "required" : [
          "id",
          "code"
        ],
        "type" : "object",
        "properties" : {
          "id" : {
            "type" : "string",
            "format" : "UUID"
          },
          "name" : {
            "type" : "string"
          },
          "code" : {
            "$ref" : "#/components/schemas/Option_Code"
          }
        }
      },
      "Option_Code" : {
        "oneOf" : [
          {
            "$ref" : "#/components/schemas/None"
          },
          {
            "$ref" : "#/components/schemas/Some_Code"
          }
        ]
      },
      "None" : {
        "type" : "object"
      },
      "Some_Code" : {
        "required" : [
          "value"
        ],
        "type" : "object",
        "properties" : {
          "value" : {
            "type" : "string"
          }
        }
      }
}

Note that a strange component Option_Code was generated for the optional field code

But if the Code case class will not be contained in the companion object AccountType, i.e. if you write this:

final case class AccountType(id: AccountType.Id, name: Option[String], code: Option[Code])

object AccountType {

  final case class Id(value: UUID)
  object Id {
    implicit val schema: Schema[Id] = Schema(SchemaType.SString).format("UUID")
  }

}

final case class Code(value: String)
object Code {
  implicit val schema: Schema[Code] = Schema(SchemaType.SString)
}

the swagger component would be as it should be:

"AccountType" : {
        "required" : [
          "id"
        ],
        "type" : "object",
        "properties" : {
          "id" : {
            "type" : "string",
            "format" : "UUID"
          },
          "name" : {
            "type" : "string"
          },
          "code" : {
            "type" : "string"
          }
        }
      }

It seems to be a bug

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 by reproducing the two schema-generation examples from the issue and compare the generated OpenAPI components for nested companion-object types. Trace the schema generation entry point that handles Option and nested case classes; done means the companion-defined Code field produces the same string schema as the top-level Code case class without the incorrect Option_Code component.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.