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

Add const attribute to Schema. #3763

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/src/main/scala-2/sttp/tapir/internal/OneOfMacro.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private[tapir] object OneOfMacro {
val discriminatorName = _root_.sttp.tapir.FieldName($name, $conf.toEncodedName($name))
// cannot use .collect because of a bug in ScalaJS (Trying to access the this of another class ... during phase: jscode)
val discriminatorMapping = mappingAsMap.toList.flatMap {
case (k, _root_.sttp.tapir.Schema(_, _root_.scala.Some(fname), _, _, _, _, _, _, _, _, _)) => _root_.scala.collection.immutable.List($asString.apply(k) -> _root_.sttp.tapir.SchemaType.SRef(fname))
case (k, _root_.sttp.tapir.Schema(_, _root_.scala.Some(fname), _, _, _, _, _, _, _, _, _, _)) => _root_.scala.collection.immutable.List($asString.apply(k) -> _root_.sttp.tapir.SchemaType.SRef(fname))
case _ => _root_.scala.Nil
}
.toMap
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala-3/sttp/tapir/macros/SchemaMacros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ private[tapir] object SchemaCompanionMacros {
val mappingAsMap = mappingAsList.toMap

val discriminatorName = _root_.sttp.tapir.FieldName(${ Expr(functionName) }, $conf.toEncodedName(${ Expr(functionName) }))
val discriminatorMapping = mappingAsMap.collect { case (k, sf @ Schema(_, Some(fname), _, _, _, _, _, _, _, _, _)) =>
val discriminatorMapping = mappingAsMap.collect { case (k, sf @ Schema(_, Some(fname), _, _, _, _, _, _, _, _, _, _)) =>
$asString.apply(k) -> SRef(fname)
}

Expand Down
108 changes: 102 additions & 6 deletions core/src/main/scala/sttp/tapir/Schema.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,106 @@ case class Schema[T](
deprecated: Boolean = false,
hidden: Boolean = false,
validator: Validator[T] = Validator.pass[T],
attributes: AttributeMap = AttributeMap.Empty
attributes: AttributeMap = AttributeMap.Empty,
// The const value together with the value encoded to a raw format, which will then be directly rendered in the
// documentation. This is needed as codecs for nested types aren't available. Similar to Validator.EncodeToRaw
const: Option[(T, Option[Any])] = None
) extends SchemaMacros[T] {

def map[TT](f: T => Option[TT])(g: TT => T): Schema[TT] = copy(
// required for binary compatibility
def this(
schemaType: SchemaType[T],
name: Option[SName],
isOptional: Boolean,
description: Option[String],
default: Option[(T, Option[Any])],
format: Option[String],
encodedExample: Option[Any],
deprecated: Boolean,
hidden: Boolean,
validator: Validator[T],
attributes: AttributeMap
) =
this(schemaType, name, isOptional, description, default, format, encodedExample, deprecated, hidden, validator, attributes, None)

// def copy(
// schemaType: SchemaType[T] = this.schemaType,
// name: Option[SName] = this.name,
// isOptional: Boolean = this.isOptional,
// description: Option[String] = this.description,
// default: Option[(T, Option[Any])] = this.default,
// format: Option[String] = this.format,
// encodedExample: Option[Any] = this.encodedExample,
// deprecated: Boolean = this.deprecated,
// hidden: Boolean = this.hidden,
// validator: Validator[T] = this.validator,
// attributes: AttributeMap = this.attributes,
// const: Option[(T, Option[Any])] = this.const
// ): Schema[T] = new Schema(
// schemaType,
// name,
// isOptional,
// description,
// default,
// format,
// encodedExample,
// deprecated,
// hidden,
// validator,
// attributes,
// const
// )

// // required for binary compatibility
// def copy(
// schemaType: SchemaType[T],
// name: Option[SName],
// isOptional: Boolean,
// description: Option[String],
// default: Option[(T, Option[Any])],
// format: Option[String],
// encodedExample: Option[Any],
// deprecated: Boolean,
// hidden: Boolean,
// validator: Validator[T],
// attributes: AttributeMap
// ): Schema[T] =
// new Schema(
// schemaType,
// name,
// isOptional,
// description,
// default,
// format,
// encodedExample,
// deprecated,
// hidden,
// validator,
// attributes,
// this.const
// )

def map[TT](f: T => Option[TT])(g: TT => T): Schema[TT] = Schema(
schemaType = schemaType.contramap(g),
default = default.flatMap { case (t, raw) =>
f(t).map(tt => (tt, raw))
},
validator = validator.contramap(g)
validator = validator.contramap(g),
const = const.flatMap { case (t, raw) =>
f(t).map(tt => (tt, raw))
}
)

/** Adapt this schema to type `TT`. Only the meta-data is retained, except for default values and the validator (however, product
* field/subtypes validators are retained). Run-time functionality:
* - traversing collection elements, product fields, or coproduct subtypes
* - validating an instance of type `TT` the top-level type is lost.
*/
def as[TT]: Schema[TT] = copy(
def as[TT]: Schema[TT] = Schema(
schemaType = schemaType.as[TT],
default = None,
validator = Validator.pass
validator = Validator.pass,
const = None
)

/** Returns an optional version of this schema, with `isOptional` set to true. */
Expand Down Expand Up @@ -257,6 +337,22 @@ case class Schema[T](
}

object Schema extends LowPrioritySchema with SchemaCompanionMacros {
// // required for binary compatibility
// def apply[T](
// schemaType: SchemaType[T],
// name: Option[SName],
// isOptional: Boolean,
// description: Option[String],
// default: Option[(T, Option[Any])],
// format: Option[String],
// encodedExample: Option[Any],
// deprecated: Boolean,
// hidden: Boolean,
// validator: Validator[T],
// attributes: AttributeMap
// ): Schema[T] =
// new Schema(schemaType, name, isOptional, description, default, format, encodedExample, deprecated, hidden, validator, attributes, None)

val ModifyCollectionElements = "each"

/** Creates a schema for type `T`, where the low-level representation is a `String`. */
Expand Down Expand Up @@ -309,7 +405,7 @@ object Schema extends LowPrioritySchema with SchemaCompanionMacros {

implicit def schemaForEither[A, B](implicit sa: Schema[A], sb: Schema[B]): Schema[Either[A, B]] = {
Schema[Either[A, B]](
SchemaType.SCoproduct(List(sa, sb), None) {
SchemaType.SCoproduct[Either[A, B]](List(sa, sb), None) {
case Left(v) => Some(SchemaWithValue(sa, v))
case Right(v) => Some(SchemaWithValue(sb, v))
},
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/sttp/tapir/SchemaType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ object SchemaType {
): SCoproduct[T] = {
SCoproduct(
subtypes.map {
case s @ Schema(st: SchemaType.SProduct[Any @unchecked], _, _, _, _, _, _, _, _, _, _)
case s @ Schema(st: SchemaType.SProduct[Any @unchecked], _, _, _, _, _, _, _, _, _, _, _)
if st.fields.forall(_.name != discriminatorName) =>
s.copy(schemaType = st.copy(fields = st.fields :+ SProductField[Any, D](discriminatorName, discriminatorSchema, _ => None)))
case s => s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import sttp.apispec.{Schema => ASchema, _}
import sttp.tapir.Schema.{SName, Title, UniqueItems}
import sttp.tapir.Validator.EncodeToRaw
import sttp.tapir.docs.apispec.DocsExtensionAttribute.RichSchema
import sttp.tapir.docs.apispec.schema.TSchemaToASchema.{tDefaultToADefault, tExampleToAExample}
import sttp.tapir.docs.apispec.schema.TSchemaToASchema.{tConstToAConst, tDefaultToADefault, tExampleToAExample}
import sttp.tapir.docs.apispec.{DocsExtensions, exampleValue}
import sttp.tapir.internal._
import sttp.tapir.{Codec, Validator, Schema => TSchema, SchemaType => TSchemaType}
Expand Down Expand Up @@ -43,7 +43,7 @@ private[docs] class TSchemaToASchema(
properties = extractProperties(fields)
)
case TSchemaType.SArray(el) => ASchema(SchemaType.Array).copy(items = Some(apply(el, allowReference = true)))
case opt @ TSchemaType.SOption(nested @ TSchema(_, Some(name), _, _, _, _, _, _, _, _, _)) =>
case opt @ TSchemaType.SOption(nested @ TSchema(_, Some(name), _, _, _, _, _, _, _, _, _, _)) =>
// #3288: in case there are multiple different customisations of the nested schema, we need to propagate the
// metadata to properly customise the reference. These are also propagated in ToKeyedSchemas when computing
// the initial list of schemas.
Expand Down Expand Up @@ -121,6 +121,7 @@ private[docs] class TSchemaToASchema(
oschema.copy(
description = tschema.description.orElse(oschema.description),
default = tDefaultToADefault(tschema).orElse(oschema.default),
const = tConstToAConst(tschema).orElse(oschema.const),
examples = tExampleToAExample(tschema).map(List(_)).orElse(oschema.examples),
format = tschema.format.orElse(oschema.format),
deprecated = (if (tschema.deprecated) Some(true) else None).orElse(oschema.deprecated),
Expand Down Expand Up @@ -193,5 +194,10 @@ object TSchemaToASchema {
def tDefaultToADefault(schema: TSchema[_]): Option[ExampleValue] = schema.default.flatMap { case (_, raw) =>
raw.flatMap(r => exampleValue(schema, r))
}

def tConstToAConst(schema: TSchema[_]): Option[ExampleValue] = schema.const.flatMap { case (_, raw) =>
raw.flatMap(r => exampleValue(schema, r))
}

def tExampleToAExample(schema: TSchema[_]): Option[ExampleValue] = schema.encodedExample.flatMap(exampleValue(schema, _))
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ private[docs] object ToKeyedSchemas {
def apply(schema: TSchema[_]): List[KeyedSchema] = {
val thisSchema = SchemaKey(schema).map(_ -> schema).toList
val nestedSchemas = schema match {
case TSchema(TSchemaType.SArray(o), _, _, _, _, _, _, _, _, _, _) => apply(o)
case t @ TSchema(o: TSchemaType.SOption[_, _], _, _, _, _, _, _, _, _, _, _) =>
case TSchema(TSchemaType.SArray(o), _, _, _, _, _, _, _, _, _, _, _) => apply(o)
case t @ TSchema(o: TSchemaType.SOption[_, _], _, _, _, _, _, _, _, _, _, _, _) =>
// #1168: if there's an optional field which is an object, with metadata defined (such as description), this
// needs to be propagated to the target object, so that it isn't omitted.
apply(propagateMetadataForOption(t, o).element)
case TSchema(st: TSchemaType.SProduct[_], _, _, _, _, _, _, _, _, _, _) => productSchemas(st)
case TSchema(st: TSchemaType.SCoproduct[_], _, _, _, _, _, _, _, _, _, _) => coproductSchemas(st)
case TSchema(st: TSchemaType.SOpenProduct[_, _], _, _, _, _, _, _, _, _, _, _) => apply(st.valueSchema)
case TSchema(st: TSchemaType.SProduct[_], _, _, _, _, _, _, _, _, _, _, _) => productSchemas(st)
case TSchema(st: TSchemaType.SCoproduct[_], _, _, _, _, _, _, _, _, _, _, _) => coproductSchemas(st)
case TSchema(st: TSchemaType.SOpenProduct[_, _], _, _, _, _, _, _, _, _, _, _, _) => apply(st.valueSchema)
case _ => List.empty
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
openapi: 3.1.0
info:
title: Entities
version: '1.0'
paths:
/:
get:
operationId: getRoot
responses:
'200':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/FruitConst'
components:
schemas:
FruitConst:
title: FruitConst
type: object
required:
- fruitType
properties:
fruitType:
type: string
const: Golden Delicious
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,36 @@
import io.circe.generic.auto._
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
import sttp.capabilities.Streams
import sttp.apispec.openapi.Info
import sttp.apispec.openapi.circe.yaml._
import sttp.capabilities.Streams
import sttp.tapir.EndpointIO.Example
import sttp.tapir.docs.openapi.dtos.{Author, Book, Country, Genre}
import sttp.tapir.generic.Derived
import sttp.tapir.generic.auto._
import sttp.tapir.json.circe._
import sttp.tapir.tests.data.{Entity, Organization, Person}
import sttp.tapir.tests.data.{Entity, FruitConst, Organization, Person}
import sttp.tapir.{endpoint, _}

import java.nio.ByteBuffer
import java.time.ZoneOffset.UTC
import java.time.ZonedDateTime

class VerifyYamlExampleTest extends AnyFunSuite with Matchers {

Check failure on line 21 in docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/VerifyYamlExampleTest.scala

View workflow job for this annotation

GitHub Actions / Test report for 3-JVM-11

sttp.tapir.docs.openapi.VerifyYamlExampleTest ► should support encoded examples for streaming bodies

Failed test found in: docs/openapi-docs/target/jvm-3/test-reports/TEST-sttp.tapir.docs.openapi.VerifyYamlExampleTest.xml Error: org.scalatest.exceptions.TestFailedException: "...ation/json:
Raw output
org.scalatest.exceptions.TestFailedException: "...ation/json:
schema:
[type:object
required:
-name
-age
properties:
name:
type:string
age:
type:integer
format:int32
example:
name:michal
age:40
required:true
responses:
'200':
description:''
'400':
description:'Invalidvaluefor:body'
content:
text/plain:
schema:
type:string]" was not equal to "...ation/json:
schema:
[$ref:'#/components/schemas/Person'
example:
name:michal
age:40
required:true
responses:
'200':
description:''
'400':
description:'Invalidvaluefor:body'
content:
text/plain:
schema:
type:string
components:
schemas:
Person:
title:Person
type:object
required:
-name
-age
properties:
name:
type:string
age:
type:integer
format:int32]"
	at org.scalatest.matchers.MatchersHelper$.indicateFailure(MatchersHelper.scala:392)
	at org.scalatest.matchers.should.Matchers.shouldBe(Matchers.scala:7017)
	at org.scalatest.matchers.should.Matchers.shouldBe$(Matchers.scala:1808)
	at sttp.tapir.docs.openapi.VerifyYamlExampleTest.shouldBe(VerifyYamlExampleTest.scala:21)
	at sttp.tapir.docs.openapi.VerifyYamlExampleTest.testFun$proxy11$1(VerifyYamlExampleTest.scala:199)
	at sttp.tapir.docs.openapi.VerifyYamlExampleTest.$init$$$anonfun$11(VerifyYamlExampleTest.scala:182)
	at org.scalatest.Transformer.apply$$anonfun$1(Transformer.scala:22)
	at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
	at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:31)
	at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
	at org.scalatest.Transformer.apply(Transformer.scala:22)
	at org.scalatest.Transformer.apply(Transformer.scala:21)
	at org.scalatest.funsuite.AnyFunSuiteLike$$anon$1.apply(AnyFunSuiteLike.scala:206)
	at org.scalatest.TestSuite.withFixture(TestSuite.scala:196)
	at org.scalatest.TestSuite.withFixture$(TestSuite.scala:138)
	at org.scalatest.funsuite.AnyFunSuite.withFixture(AnyFunSuite.scala:1563)
	at org.scalatest.funsuite.AnyFunSuiteLike.invokeWithFixture$1(AnyFunSuiteLike.scala:212)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTest$$anonfun$1(AnyFunSuiteLike.scala:216)
	at org.scalatest.SuperEngine.runTestImpl(Engine.scala:306)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTest(AnyFunSuiteLike.scala:216)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTest$(AnyFunSuiteLike.scala:47)
	at org.scalatest.funsuite.AnyFunSuite.runTest(AnyFunSuite.scala:1563)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTests$$anonfun$1(AnyFunSuiteLike.scala:249)
	at org.scalatest.SuperEngine.traverseSubNodes$1$$anonfun$1(Engine.scala:413)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
	at scala.collection.immutable.List.foreach(List.scala:333)
	at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:429)
	at org.scalatest.SuperEngine.runTestsInBranch(Engine.scala:396)
	at org.scalatest.SuperEngine.runTestsImpl(Engine.scala:475)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTests(AnyFunSuiteLike.scala:249)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTests$(AnyFunSuiteLike.scala:47)
	at org.scalatest.funsuite.AnyFunSuite.runTests(AnyFunSuite.scala:1563)
	at org.scalatest.Suite.run(Suite.scala:1114)
	at org.scalatest.Suite.run$(Suite.scala:564)
	at org.scalatest.funsuite.AnyFunSuite.org$scalatest$funsuite$AnyFunSuiteLike$$super$run(AnyFunSuite.scala:1563)
	at org.scalatest.funsuite.AnyFunSuiteLike.run$$anonfun$1(AnyFunSuiteLike.scala:253)
	at org.scalatest.SuperEngine.runImpl(Engine.scala:535)
	at org.scalatest.funsuite.AnyFunSuiteLike.run(AnyFunSuiteLike.scala:253)
	at org.scalatest.funsuite.AnyFunSuiteLike.run$(AnyFunSuiteLike.scala:47)
	at org.scalatest.funsuite.AnyFunSuite.run(AnyFunSuite.scala:1563)
	at org.scalatest.tools.Framework.org$scalatest$tools$Framework$$runSuite(Framework.scala:321)
	at org.scalatest.tools.Framework$ScalaTestTask.execute(Framework.scala:517)
	at sbt.TestRunner.runTest$1(TestFramework.scala:153)
	at sbt.TestRunner.run(TestFramework.scala:168)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.$anonfun$apply$1(TestFramework.scala:336)
	at sbt.TestFramework$.sbt$TestFramework$$withContextLoader(TestFramework.scala:296)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.apply(TestFramework.scala:336)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.apply(TestFramework.scala:336)
	at sbt.TestFunction.apply(TestFramework.scala:348)
	at sbt.Tests$.processRunnable$1(Tests.scala:475)
	at sbt.Tests$.$anonfun$makeSerial$1(Tests.scala:481)
	at sbt.std.Transform$$anon$3.$anonfun$apply$2(Transform.scala:47)
	at sbt.std.Transform$$anon$4.work(Transform.scala:69)
	at sbt.Execute.$anonfun$submit$2(Execute.scala:283)
	at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:24)
	at sbt.Execute.work(Execute.scala:292)
	at sbt.Execute.$anonfun$submit$1(Execute.scala:283)
	at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
	at sbt.CompletionService$$anon$2.call(CompletionService.scala:65)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:829)

test("support const attribute") {
val expectedYaml = load("example/expected_single_example_with_const.yml")
val actualYaml = OpenAPIDocsInterpreter()
.toOpenAPI(
endpoint.get.out(jsonBody[FruitConst]),
Info("Entities", "1.0")
)
.toYaml

val actualYamlNoIndent = noIndentation(actualYaml)
actualYamlNoIndent shouldBe expectedYaml
}

test("support example of list and not-list types") {
val expectedYaml = load("example/expected_examples_of_list_and_not_list_types.yml")
val actualYaml = OpenAPIDocsInterpreter()
Expand Down Expand Up @@ -183,7 +196,7 @@
val actualYaml = OpenAPIDocsInterpreter().toOpenAPI(e, Info("Examples", "1.0")).toYaml
val actualYamlNoIndent = noIndentation(actualYaml)

actualYamlNoIndent shouldBe expectedYaml

Check failure on line 199 in docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/VerifyYamlExampleTest.scala

View workflow job for this annotation

GitHub Actions / Test report for 2.12-JVM-11

sttp.tapir.docs.openapi.VerifyYamlExampleTest ► should support encoded examples for streaming bodies

Failed test found in: docs/openapi-docs/target/jvm-2.12/test-reports/TEST-sttp.tapir.docs.openapi.VerifyYamlExampleTest.xml Error: org.scalatest.exceptions.TestFailedException: "...ation/json:
Raw output
org.scalatest.exceptions.TestFailedException: "...ation/json:
schema:
[type:object
required:
-name
-age
properties:
name:
type:string
age:
type:integer
format:int32
example:
name:michal
age:40
required:true
responses:
'200':
description:''
'400':
description:'Invalidvaluefor:body'
content:
text/plain:
schema:
type:string]" was not equal to "...ation/json:
schema:
[$ref:'#/components/schemas/Person'
example:
name:michal
age:40
required:true
responses:
'200':
description:''
'400':
description:'Invalidvaluefor:body'
content:
text/plain:
schema:
type:string
components:
schemas:
Person:
title:Person
type:object
required:
-name
-age
properties:
name:
type:string
age:
type:integer
format:int32]"
	at org.scalatest.matchers.MatchersHelper$.indicateFailure(MatchersHelper.scala:392)
	at org.scalatest.matchers.should.Matchers$AnyShouldWrapper.shouldBe(Matchers.scala:7539)
	at sttp.tapir.docs.openapi.VerifyYamlExampleTest.$anonfun$new$78(VerifyYamlExampleTest.scala:199)
	at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
	at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
	at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
	at org.scalatest.Transformer.apply(Transformer.scala:22)
	at org.scalatest.Transformer.apply(Transformer.scala:20)
	at org.scalatest.funsuite.AnyFunSuiteLike$$anon$1.apply(AnyFunSuiteLike.scala:226)
	at org.scalatest.TestSuite.withFixture(TestSuite.scala:196)
	at org.scalatest.TestSuite.withFixture$(TestSuite.scala:195)
	at org.scalatest.funsuite.AnyFunSuite.withFixture(AnyFunSuite.scala:1564)
	at org.scalatest.funsuite.AnyFunSuiteLike.invokeWithFixture$1(AnyFunSuiteLike.scala:224)
	at org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$runTest$1(AnyFunSuiteLike.scala:236)
	at org.scalatest.SuperEngine.runTestImpl(Engine.scala:306)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTest(AnyFunSuiteLike.scala:236)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTest$(AnyFunSuiteLike.scala:218)
	at org.scalatest.funsuite.AnyFunSuite.runTest(AnyFunSuite.scala:1564)
	at org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$runTests$1(AnyFunSuiteLike.scala:269)
	at org.scalatest.SuperEngine.$anonfun$runTestsInBranch$1(Engine.scala:413)
	at scala.collection.immutable.List.foreach(List.scala:431)
	at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:401)
	at org.scalatest.SuperEngine.runTestsInBranch(Engine.scala:396)
	at org.scalatest.SuperEngine.runTestsImpl(Engine.scala:475)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTests(AnyFunSuiteLike.scala:269)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTests$(AnyFunSuiteLike.scala:268)
	at org.scalatest.funsuite.AnyFunSuite.runTests(AnyFunSuite.scala:1564)
	at org.scalatest.Suite.run(Suite.scala:1114)
	at org.scalatest.Suite.run$(Suite.scala:1096)
	at org.scalatest.funsuite.AnyFunSuite.org$scalatest$funsuite$AnyFunSuiteLike$$super$run(AnyFunSuite.scala:1564)
	at org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$run$1(AnyFunSuiteLike.scala:273)
	at org.scalatest.SuperEngine.runImpl(Engine.scala:535)
	at org.scalatest.funsuite.AnyFunSuiteLike.run(AnyFunSuiteLike.scala:273)
	at org.scalatest.funsuite.AnyFunSuiteLike.run$(AnyFunSuiteLike.scala:272)
	at org.scalatest.funsuite.AnyFunSuite.run(AnyFunSuite.scala:1564)
	at org.scalatest.tools.Framework.org$scalatest$tools$Framework$$runSuite(Framework.scala:321)
	at org.scalatest.tools.Framework$ScalaTestTask.execute(Framework.scala:517)
	at sbt.TestRunner.runTest$1(TestFramework.scala:153)
	at sbt.TestRunner.run(TestFramework.scala:168)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.$anonfun$apply$1(TestFramework.scala:336)
	at sbt.TestFramework$.sbt$TestFramework$$withContextLoader(TestFramework.scala:296)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.apply(TestFramework.scala:336)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.apply(TestFramework.scala:336)
	at sbt.TestFunction.apply(TestFramework.scala:348)
	at sbt.Tests$.processRunnable$1(Tests.scala:475)
	at sbt.Tests$.$anonfun$makeSerial$1(Tests.scala:481)
	at sbt.std.Transform$$anon$3.$anonfun$apply$2(Transform.scala:47)
	at sbt.std.Transform$$anon$4.work(Transform.scala:69)
	at sbt.Execute.$anonfun$submit$2(Execute.scala:283)
	at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:24)
	at sbt.Execute.work(Execute.scala:292)
	at sbt.Execute.$anonfun$submit$1(Execute.scala:283)
	at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
	at sbt.CompletionService$$anon$2.call(CompletionService.scala:65)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:829)

Check failure on line 199 in docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/VerifyYamlExampleTest.scala

View workflow job for this annotation

GitHub Actions / Test report for 2.13-JVM-11

sttp.tapir.docs.openapi.VerifyYamlExampleTest ► should support encoded examples for streaming bodies

Failed test found in: docs/openapi-docs/target/jvm-2.13/test-reports/TEST-sttp.tapir.docs.openapi.VerifyYamlExampleTest.xml Error: org.scalatest.exceptions.TestFailedException: "...ation/json:
Raw output
org.scalatest.exceptions.TestFailedException: "...ation/json:
schema:
[type:object
required:
-name
-age
properties:
name:
type:string
age:
type:integer
format:int32
example:
name:michal
age:40
required:true
responses:
'200':
description:''
'400':
description:'Invalidvaluefor:body'
content:
text/plain:
schema:
type:string]" was not equal to "...ation/json:
schema:
[$ref:'#/components/schemas/Person'
example:
name:michal
age:40
required:true
responses:
'200':
description:''
'400':
description:'Invalidvaluefor:body'
content:
text/plain:
schema:
type:string
components:
schemas:
Person:
title:Person
type:object
required:
-name
-age
properties:
name:
type:string
age:
type:integer
format:int32]"
	at org.scalatest.matchers.MatchersHelper$.indicateFailure(MatchersHelper.scala:392)
	at org.scalatest.matchers.should.Matchers$AnyShouldWrapper.shouldBe(Matchers.scala:7539)
	at sttp.tapir.docs.openapi.VerifyYamlExampleTest.$anonfun$new$78(VerifyYamlExampleTest.scala:199)
	at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
	at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
	at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
	at org.scalatest.Transformer.apply(Transformer.scala:22)
	at org.scalatest.Transformer.apply(Transformer.scala:20)
	at org.scalatest.funsuite.AnyFunSuiteLike$$anon$1.apply(AnyFunSuiteLike.scala:226)
	at org.scalatest.TestSuite.withFixture(TestSuite.scala:196)
	at org.scalatest.TestSuite.withFixture$(TestSuite.scala:195)
	at org.scalatest.funsuite.AnyFunSuite.withFixture(AnyFunSuite.scala:1564)
	at org.scalatest.funsuite.AnyFunSuiteLike.invokeWithFixture$1(AnyFunSuiteLike.scala:224)
	at org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$runTest$1(AnyFunSuiteLike.scala:236)
	at org.scalatest.SuperEngine.runTestImpl(Engine.scala:306)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTest(AnyFunSuiteLike.scala:236)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTest$(AnyFunSuiteLike.scala:218)
	at org.scalatest.funsuite.AnyFunSuite.runTest(AnyFunSuite.scala:1564)
	at org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$runTests$1(AnyFunSuiteLike.scala:269)
	at org.scalatest.SuperEngine.$anonfun$runTestsInBranch$1(Engine.scala:413)
	at scala.collection.immutable.List.foreach(List.scala:334)
	at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:401)
	at org.scalatest.SuperEngine.runTestsInBranch(Engine.scala:396)
	at org.scalatest.SuperEngine.runTestsImpl(Engine.scala:475)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTests(AnyFunSuiteLike.scala:269)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTests$(AnyFunSuiteLike.scala:268)
	at org.scalatest.funsuite.AnyFunSuite.runTests(AnyFunSuite.scala:1564)
	at org.scalatest.Suite.run(Suite.scala:1114)
	at org.scalatest.Suite.run$(Suite.scala:1096)
	at org.scalatest.funsuite.AnyFunSuite.org$scalatest$funsuite$AnyFunSuiteLike$$super$run(AnyFunSuite.scala:1564)
	at org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$run$1(AnyFunSuiteLike.scala:273)
	at org.scalatest.SuperEngine.runImpl(Engine.scala:535)
	at org.scalatest.funsuite.AnyFunSuiteLike.run(AnyFunSuiteLike.scala:273)
	at org.scalatest.funsuite.AnyFunSuiteLike.run$(AnyFunSuiteLike.scala:272)
	at org.scalatest.funsuite.AnyFunSuite.run(AnyFunSuite.scala:1564)
	at org.scalatest.tools.Framework.org$scalatest$tools$Framework$$runSuite(Framework.scala:321)
	at org.scalatest.tools.Framework$ScalaTestTask.execute(Framework.scala:517)
	at sbt.TestRunner.runTest$1(TestFramework.scala:153)
	at sbt.TestRunner.run(TestFramework.scala:168)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.$anonfun$apply$1(TestFramework.scala:336)
	at sbt.TestFramework$.sbt$TestFramework$$withContextLoader(TestFramework.scala:296)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.apply(TestFramework.scala:336)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.apply(TestFramework.scala:336)
	at sbt.TestFunction.apply(TestFramework.scala:348)
	at sbt.Tests$.processRunnable$1(Tests.scala:475)
	at sbt.Tests$.$anonfun$makeSerial$1(Tests.scala:481)
	at sbt.std.Transform$$anon$3.$anonfun$apply$2(Transform.scala:47)
	at sbt.std.Transform$$anon$4.work(Transform.scala:69)
	at sbt.Execute.$anonfun$submit$2(Execute.scala:283)
	at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:24)
	at sbt.Execute.work(Execute.scala:292)
	at sbt.Execute.$anonfun$submit$1(Execute.scala:283)
	at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
	at sbt.CompletionService$$anon$2.call(CompletionService.scala:65)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:829)
}

test("should support summary and name for single example") {
Expand Down
11 changes: 11 additions & 0 deletions tests/src/main/scala/sttp/tapir/tests/data/FruitConst.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package sttp.tapir.tests.data

import sttp.tapir.Schema

case class FruitConst(fruitType: String)

object FruitConst {
implicit val schemw: Schema[FruitConst] = Schema
.derived[FruitConst]
.modifyUnsafe[String]("fruitType")(s => s.copy(const = Some(("Golden Delicious", Some("Golden Delicious")))))
}
Loading