From f47947ff8719811a5bd93f27630981ce9b74406d Mon Sep 17 00:00:00 2001 From: Kamil Kloch Date: Mon, 13 May 2024 14:24:36 +0200 Subject: [PATCH 1/5] Add `const` attribute to Schema. --- .../sttp/tapir/internal/OneOfMacro.scala | 2 +- .../sttp/tapir/macros/SchemaMacros.scala | 2 +- core/src/main/scala/sttp/tapir/Schema.scala | 106 +++++++++++++++++- .../main/scala/sttp/tapir/SchemaType.scala | 2 +- .../apispec/schema/TSchemaToASchema.scala | 10 +- .../docs/apispec/schema/ToKeyedSchemas.scala | 10 +- .../expected_single_example_with_const.yml | 28 +++++ .../docs/openapi/VerifyYamlExampleTest.scala | 15 ++- .../sttp/tapir/tests/data/FruitConst.scala | 11 ++ 9 files changed, 170 insertions(+), 16 deletions(-) create mode 100644 docs/openapi-docs/src/test/resources/example/expected_single_example_with_const.yml create mode 100644 tests/src/main/scala/sttp/tapir/tests/data/FruitConst.scala diff --git a/core/src/main/scala-2/sttp/tapir/internal/OneOfMacro.scala b/core/src/main/scala-2/sttp/tapir/internal/OneOfMacro.scala index 2b9585883a..dad3a0c1c1 100644 --- a/core/src/main/scala-2/sttp/tapir/internal/OneOfMacro.scala +++ b/core/src/main/scala-2/sttp/tapir/internal/OneOfMacro.scala @@ -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 diff --git a/core/src/main/scala-3/sttp/tapir/macros/SchemaMacros.scala b/core/src/main/scala-3/sttp/tapir/macros/SchemaMacros.scala index 5fbf497154..d36ff0be35 100644 --- a/core/src/main/scala-3/sttp/tapir/macros/SchemaMacros.scala +++ b/core/src/main/scala-3/sttp/tapir/macros/SchemaMacros.scala @@ -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) } diff --git a/core/src/main/scala/sttp/tapir/Schema.scala b/core/src/main/scala/sttp/tapir/Schema.scala index 496b00d255..5aa64bfe19 100644 --- a/core/src/main/scala/sttp/tapir/Schema.scala +++ b/core/src/main/scala/sttp/tapir/Schema.scala @@ -39,15 +39,94 @@ 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 default 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 @@ -55,10 +134,11 @@ case class Schema[T]( * - 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. */ @@ -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`. */ diff --git a/core/src/main/scala/sttp/tapir/SchemaType.scala b/core/src/main/scala/sttp/tapir/SchemaType.scala index 06b395ce9e..a43ca1c1df 100644 --- a/core/src/main/scala/sttp/tapir/SchemaType.scala +++ b/core/src/main/scala/sttp/tapir/SchemaType.scala @@ -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 diff --git a/docs/apispec-docs/src/main/scala/sttp/tapir/docs/apispec/schema/TSchemaToASchema.scala b/docs/apispec-docs/src/main/scala/sttp/tapir/docs/apispec/schema/TSchemaToASchema.scala index a8e1c004f2..82429d6eaa 100644 --- a/docs/apispec-docs/src/main/scala/sttp/tapir/docs/apispec/schema/TSchemaToASchema.scala +++ b/docs/apispec-docs/src/main/scala/sttp/tapir/docs/apispec/schema/TSchemaToASchema.scala @@ -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} @@ -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. @@ -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), @@ -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, _)) } diff --git a/docs/apispec-docs/src/main/scala/sttp/tapir/docs/apispec/schema/ToKeyedSchemas.scala b/docs/apispec-docs/src/main/scala/sttp/tapir/docs/apispec/schema/ToKeyedSchemas.scala index f809fa188f..c37fcb72f9 100644 --- a/docs/apispec-docs/src/main/scala/sttp/tapir/docs/apispec/schema/ToKeyedSchemas.scala +++ b/docs/apispec-docs/src/main/scala/sttp/tapir/docs/apispec/schema/ToKeyedSchemas.scala @@ -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 } diff --git a/docs/openapi-docs/src/test/resources/example/expected_single_example_with_const.yml b/docs/openapi-docs/src/test/resources/example/expected_single_example_with_const.yml new file mode 100644 index 0000000000..faacb0557a --- /dev/null +++ b/docs/openapi-docs/src/test/resources/example/expected_single_example_with_const.yml @@ -0,0 +1,28 @@ +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 + default: + fruitConstName: default fruit + type: object + required: + - fruitConstName + properties: + fruitConstName: + type: string + const: Red Apple Const diff --git a/docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/VerifyYamlExampleTest.scala b/docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/VerifyYamlExampleTest.scala index 7625797f50..e447fc3686 100644 --- a/docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/VerifyYamlExampleTest.scala +++ b/docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/VerifyYamlExampleTest.scala @@ -11,7 +11,7 @@ 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 @@ -20,6 +20,19 @@ import java.time.ZonedDateTime class VerifyYamlExampleTest extends AnyFunSuite with Matchers { + test("support const attribute") { + val expectedYaml = load("example/expected_single_example_with_const.yml") + val actualYaml = OpenAPIDocsInterpreter() + .toOpenAPI( + endpoint.get.out(jsonBody[FruitConst].default(FruitConst("default fruit"))), + 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() diff --git a/tests/src/main/scala/sttp/tapir/tests/data/FruitConst.scala b/tests/src/main/scala/sttp/tapir/tests/data/FruitConst.scala new file mode 100644 index 0000000000..921a5b9c0b --- /dev/null +++ b/tests/src/main/scala/sttp/tapir/tests/data/FruitConst.scala @@ -0,0 +1,11 @@ +package sttp.tapir.tests.data + +import sttp.tapir.Schema + +case class FruitConst(fruitConstName: String) + +object FruitConst { + implicit val schemw: Schema[FruitConst] = Schema + .derived[FruitConst] + .modifyUnsafe[String]("fruitConstName")(s => s.copy(const = Some("Red Apple Const", Some("Red Apple Const")))) +} From 9f579988850a3f20380c6d8ae0fb12203c69c1b0 Mon Sep 17 00:00:00 2001 From: Kamil Kloch Date: Mon, 13 May 2024 14:43:21 +0200 Subject: [PATCH 2/5] Cosmetics. --- .../example/expected_single_example_with_const.yml | 8 +++----- .../sttp/tapir/docs/openapi/VerifyYamlExampleTest.scala | 4 ++-- .../src/main/scala/sttp/tapir/tests/data/FruitConst.scala | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/openapi-docs/src/test/resources/example/expected_single_example_with_const.yml b/docs/openapi-docs/src/test/resources/example/expected_single_example_with_const.yml index faacb0557a..5dce415c42 100644 --- a/docs/openapi-docs/src/test/resources/example/expected_single_example_with_const.yml +++ b/docs/openapi-docs/src/test/resources/example/expected_single_example_with_const.yml @@ -17,12 +17,10 @@ components: schemas: FruitConst: title: FruitConst - default: - fruitConstName: default fruit type: object required: - - fruitConstName + - fruitType properties: - fruitConstName: + fruitType: type: string - const: Red Apple Const + const: Golden Delicious diff --git a/docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/VerifyYamlExampleTest.scala b/docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/VerifyYamlExampleTest.scala index e447fc3686..cb7e0a3510 100644 --- a/docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/VerifyYamlExampleTest.scala +++ b/docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/VerifyYamlExampleTest.scala @@ -3,9 +3,9 @@ package sttp.tapir.docs.openapi 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 @@ -24,7 +24,7 @@ class VerifyYamlExampleTest extends AnyFunSuite with Matchers { val expectedYaml = load("example/expected_single_example_with_const.yml") val actualYaml = OpenAPIDocsInterpreter() .toOpenAPI( - endpoint.get.out(jsonBody[FruitConst].default(FruitConst("default fruit"))), + endpoint.get.out(jsonBody[FruitConst]), Info("Entities", "1.0") ) .toYaml diff --git a/tests/src/main/scala/sttp/tapir/tests/data/FruitConst.scala b/tests/src/main/scala/sttp/tapir/tests/data/FruitConst.scala index 921a5b9c0b..b004ee166f 100644 --- a/tests/src/main/scala/sttp/tapir/tests/data/FruitConst.scala +++ b/tests/src/main/scala/sttp/tapir/tests/data/FruitConst.scala @@ -2,10 +2,10 @@ package sttp.tapir.tests.data import sttp.tapir.Schema -case class FruitConst(fruitConstName: String) +case class FruitConst(fruitType: String) object FruitConst { implicit val schemw: Schema[FruitConst] = Schema .derived[FruitConst] - .modifyUnsafe[String]("fruitConstName")(s => s.copy(const = Some("Red Apple Const", Some("Red Apple Const")))) + .modifyUnsafe[String]("fruitType")(s => s.copy(const = Some("Golden Delicious", Some("Golden Delicious")))) } From 1e852d15228e5cc180987e3317a6d6d619182173 Mon Sep 17 00:00:00 2001 From: Kamil Kloch Date: Mon, 13 May 2024 17:25:58 +0200 Subject: [PATCH 3/5] Cosmetics. --- tests/src/main/scala/sttp/tapir/tests/data/FruitConst.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/main/scala/sttp/tapir/tests/data/FruitConst.scala b/tests/src/main/scala/sttp/tapir/tests/data/FruitConst.scala index b004ee166f..341a63a1b0 100644 --- a/tests/src/main/scala/sttp/tapir/tests/data/FruitConst.scala +++ b/tests/src/main/scala/sttp/tapir/tests/data/FruitConst.scala @@ -7,5 +7,5 @@ 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")))) + .modifyUnsafe[String]("fruitType")(s => s.copy(const = Some(("Golden Delicious", Some("Golden Delicious"))))) } From 84e42a0355543564ce34c0973b3bc8c902c6341a Mon Sep 17 00:00:00 2001 From: Kamil Kloch Date: Mon, 13 May 2024 17:29:14 +0200 Subject: [PATCH 4/5] Removed methods for binary compatibility. --- core/src/main/scala/sttp/tapir/Schema.scala | 176 ++++++++++---------- 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/core/src/main/scala/sttp/tapir/Schema.scala b/core/src/main/scala/sttp/tapir/Schema.scala index 5aa64bfe19..86dbab3b4c 100644 --- a/core/src/main/scala/sttp/tapir/Schema.scala +++ b/core/src/main/scala/sttp/tapir/Schema.scala @@ -40,83 +40,83 @@ case class Schema[T]( hidden: Boolean = false, validator: Validator[T] = Validator.pass[T], attributes: AttributeMap = AttributeMap.Empty, - // The default value together with the value encoded to a raw format, which will then be directly rendered in the + // 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] { // 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 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), @@ -337,21 +337,21 @@ 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) +// // 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" @@ -405,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)) }, From bc1cd77679ff5006542b3eb1a91b93caa83119b9 Mon Sep 17 00:00:00 2001 From: Kamil Kloch Date: Tue, 14 May 2024 10:13:01 +0200 Subject: [PATCH 5/5] Re-added method for binary compatibility. --- core/src/main/scala/sttp/tapir/Schema.scala | 28 ++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/core/src/main/scala/sttp/tapir/Schema.scala b/core/src/main/scala/sttp/tapir/Schema.scala index 86dbab3b4c..c1ab5ba506 100644 --- a/core/src/main/scala/sttp/tapir/Schema.scala +++ b/core/src/main/scala/sttp/tapir/Schema.scala @@ -46,20 +46,20 @@ case class Schema[T]( ) extends SchemaMacros[T] { // 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 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,