softwaremill / softwaremill/tapir
[BUG] incorrect names for newtyped map schemas
Open
@kciesielski is already working on this.
Since Jun 12, 2024.
- Dominant language
- Scala
- Stars
- 1.5k
- Forks
- 468
- Avg merge
- 5h 37m
- Merged PRs (30d)
- 34
Description
Tapir version: 1.10.8
Scala version: 2.13.14
Describe the bug
both Keys and Values are incorrectly named in map schemas - most probably also in other cases
resulting docs for code example:
openapi: 3.1.0
info:
title: test
version: v1
paths:
/test/1:
put:
operationId: test1
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Map_Type_Type'
required: true
responses:
'200':
description: ''
'400':
description: 'Invalid value for: body'
content:
text/plain:
schema:
type: string
/test/2:
put:
operationId: test2
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Map_Type_Type1'
required: true
responses:
'200':
description: ''
'400':
description: 'Invalid value for: body'
content:
text/plain:
schema:
type: string
/test/3:
put:
operationId: test3
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Map_Type_Type2'
required: true
responses:
'200':
description: ''
'400':
description: 'Invalid value for: body'
content:
text/plain:
schema:
type: string
components:
schemas:
Map_Type_Type:
title: Map_Type_Type
type: object
additionalProperties:
type: integer
format: int32
Map_Type_Type1:
title: Map_Type_Type
type: object
additionalProperties:
type: integer
format: int32
Map_Type_Type2:
title: Map_Type_Type
type: object
additionalProperties:
type: integer
format: int32
How to reproduce?
package com.test
import com.test.Defs._
import eu.timepit.refined.types.numeric.PosInt
import eu.timepit.refined.types.string.NonEmptyString
import io.circe.refined._
import io.circe.{Decoder, Encoder, KeyDecoder, KeyEncoder}
import io.estatico.newtype.Coercible
import io.estatico.newtype.macros.newtype
import sttp.apispec.openapi.circe.yaml.RichOpenAPI
import sttp.tapir._
import sttp.tapir.codec.newtype.TapirCodecNewType
import sttp.tapir.codec.refined.TapirCodecRefined
import sttp.tapir.docs.openapi.OpenAPIDocsInterpreter
import sttp.tapir.json.circe.jsonBody
object Test extends App with TapirCodecRefined with TapirCodecNewType with CirceCodecRefined with CirceNewtype {
lazy implicit val map1Schema: Schema[Map[UserId, VeryImportantCount]] = Schema.schemaForMap[UserId, VeryImportantCount](_.value.value)
lazy implicit val map2Schema: Schema[Map[AdminId, VeryImportantCount]] = Schema.schemaForMap[AdminId, VeryImportantCount](_.value.value)
lazy implicit val map3Schema: Schema[Map[IntId, VeryImportantCount]] = Schema.schemaForMap[IntId, VeryImportantCount](_.value.value.toString)
val endpoint1 = endpoint
.put
.name("test1")
.in("test" / "1")
.in(jsonBody[Map[UserId, VeryImportantCount]])
val endpoint2 = endpoint
.put
.name("test2")
.in("test" / "2")
.in(jsonBody[Map[AdminId, VeryImportantCount]])
val endpoint3 = endpoint
.put
.name("test3")
.in("test" / "3")
.in(jsonBody[Map[IntId, VeryImportantCount]])
val endpoints = List(endpoint1, endpoint2, endpoint3)
val yamlString: String = OpenAPIDocsInterpreter()
.toOpenAPI(endpoints, "test", "v1")
.toYaml
println(yamlString)
}
object Defs {
@newtype final case class UserId(value: NonEmptyString)
@newtype final case class AdminId(value: NonEmptyString)
@newtype final case class IntId(value: PosInt)
@newtype final case class VeryImportantCount(value: Int)
}
trait CirceNewtype {
implicit def coerceDecoder[R, N](
implicit
ev: Coercible[Decoder[R], Decoder[N]],
R: Decoder[R],
): Decoder[N] = ev(R)
implicit def coerceEncoder[R, N](
implicit
ev: Coercible[Encoder[R], Encoder[N]],
R: Encoder[R],
): Encoder[N] = ev(R)
implicit def coerceKeyDecoder[R, N](
implicit
ev: Coercible[KeyDecoder[R], KeyDecoder[N]],
R: KeyDecoder[R],
): KeyDecoder[N] = ev(R)
implicit def coerceKeyEncoder[R, N](
implicit
ev: Coercible[KeyEncoder[R], KeyEncoder[N]],
R: KeyEncoder[R],
): KeyEncoder[N] = ev(R)
implicit def coerceAsObjectEncoder[A, B](
implicit
ev: Coercible[Encoder.AsObject[A], Encoder.AsObject[B]],
encA: Encoder.AsObject[A],
): Encoder.AsObject[B] = ev(Encoder.AsObject[A])
}
Additional information
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.