Skip to content

Commit

Permalink
Fix OpenAPI schema generation when the discriminator field is an enum (
Browse files Browse the repository at this point in the history
  • Loading branch information
adamw authored Feb 27, 2025
1 parent ae19885 commit b47e8b8
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package sttp.tapir.docs.apispec.schema

import sttp.tapir.Schema.Title
import sttp.tapir.Schema.{EncodedDiscriminatorValue, Title}
import sttp.tapir.{Codec, Schema => TSchema, SchemaType => TSchemaType}

private[docs] object ToKeyedSchemas {
Expand Down Expand Up @@ -53,6 +53,8 @@ private[docs] object ToKeyedSchemas {
if (s1.deprecated != s2.deprecated) result = result.deprecated(false)
if (s1.attributes.get(Title.Attribute) != s2.attributes.get(Title.Attribute))
result = result.copy(attributes = result.attributes.remove(Title.Attribute))
if (s1.attributes.get(EncodedDiscriminatorValue.Attribute) != s2.attributes.get(EncodedDiscriminatorValue.Attribute))
result = result.copy(attributes = result.attributes.remove(EncodedDiscriminatorValue.Attribute))
result
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
openapi: 3.1.0
info:
title: title
version: '1.0'
paths:
/figure:
post:
operationId: postFigure
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
required: true
responses:
'200':
description: ''
'400':
description: 'Invalid value for: body'
content:
text/plain:
schema:
type: string
components:
schemas:
Cat:
title: Cat
type: object
required:
- name
- petType
properties:
name:
type: string
petType:
$ref: '#/components/schemas/PetType'
Dog:
title: Dog
type: object
required:
- breed
- petType
properties:
breed:
type: string
petType:
$ref: '#/components/schemas/PetType'
Pet:
title: Pet
oneOf:
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Dog'
discriminator:
propertyName: petType
mapping:
Canine: '#/components/schemas/Dog'
Feline: '#/components/schemas/Cat'
PetType:
title: PetType
type: string
enum:
- Canine
- Feline
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,16 @@ class VerifyYamlCoproductTest extends AnyFunSuite with Matchers {

noIndentation(actualYaml) shouldBe expectedYaml
}

test("coproduct using an enum discriminator, added using `.addDiscriminatorField`") {
val petEndpoint = endpoint.post
.in("figure")
.in(jsonBody[Pet])

val expectedYaml = load("coproduct/expected_coproduct_discriminator_enum.yml")

val actualYaml = OpenAPIDocsInterpreter().toOpenAPI(List(petEndpoint), "title", "1.0").toYaml

noIndentation(actualYaml) shouldBe expectedYaml
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package sttp.tapir.docs.openapi.dtos

import sttp.tapir.tests.data.Entity
import sttp.tapir.{Schema, Validator}
import sttp.tapir.SchemaType.SRef
import sttp.tapir.Schema.SName
import sttp.tapir.SchemaType
import sttp.tapir.FieldName

// TODO: move back to VerifyYamlTest companion after https://github.com/lampepfl/dotty/issues/12849 is fixed
object VerifyYamlCoproductTestData {
Expand All @@ -24,4 +28,30 @@ object VerifyYamlCoproductTestData {
}

case class Square(color: Color.Value, shapeType: String = "square") extends Shape

sealed trait PetType
object PetType {
case object Canine extends PetType
case object Feline extends PetType
implicit val schema: Schema[PetType] = Schema.derivedEnumeration[PetType].defaultStringBased
}

sealed trait Pet
object Pet {
implicit val schema: Schema[Pet] = {
val derived = Schema.derived[Pet]
val mapping = Map(
PetType.Canine.toString -> SRef(SName("sttp.tapir.docs.openapi.dtos.VerifyYamlCoproductTestData.Dog")),
PetType.Feline.toString -> SRef(SName("sttp.tapir.docs.openapi.dtos.VerifyYamlCoproductTestData.Cat"))
)

derived.schemaType match {
case s: SchemaType.SCoproduct[Pet] =>
derived.copy(schemaType = s.addDiscriminatorField(FieldName("petType"), PetType.schema, mapping))
case _ => derived
}
}
}
final case class Dog(breed: String) extends Pet
final case class Cat(name: String) extends Pet
}

0 comments on commit b47e8b8

Please sign in to comment.