diff --git a/README.md b/README.md index 8e17a57348..d5a6c20885 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ tapir documentation is available at [tapir.softwaremill.com](http://tapir.softwa Add the following dependency: ```sbt -"com.softwaremill.sttp.tapir" %% "tapir-core" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-core" % "1.10.12" ``` Then, import: diff --git a/generated-doc/out/client/http4s.md b/generated-doc/out/client/http4s.md index 1f7e09e227..ad1ae61791 100644 --- a/generated-doc/out/client/http4s.md +++ b/generated-doc/out/client/http4s.md @@ -3,7 +3,7 @@ Add the dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-http4s-client" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-http4s-client" % "1.10.12" ``` To interpret an endpoint definition as an `org.http4s.Request[F]`, import: diff --git a/generated-doc/out/client/play.md b/generated-doc/out/client/play.md index 4577f69c4c..ba23b48d1c 100644 --- a/generated-doc/out/client/play.md +++ b/generated-doc/out/client/play.md @@ -6,13 +6,13 @@ See the [Play framework documentation](https://www.playframework.com/documentati For **Play 3.0**, add the dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-play-client" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-play-client" % "1.10.12" ``` For **Play 2.9**, add ```scala -"com.softwaremill.sttp.tapir" %% "tapir-play29-client" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-play29-client" % "1.10.12" ``` instead. Furthermore, replace all uses of `sttp.capabilities.pekko.PekkoStreams` in the following code snippets with `sttp.capabilities.akka.AkkaStreams`. diff --git a/generated-doc/out/client/sttp.md b/generated-doc/out/client/sttp.md index 0018d64465..42eb9cd895 100644 --- a/generated-doc/out/client/sttp.md +++ b/generated-doc/out/client/sttp.md @@ -3,7 +3,7 @@ Add the dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-sttp-client" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-sttp-client" % "1.10.12" ``` To make requests using an endpoint definition using the [sttp client](https://github.com/softwaremill/sttp), import: @@ -101,7 +101,7 @@ In this case add the following dependencies (note the [`%%%`](https://www.scala- instead of the usual `%%`): ```scala -"com.softwaremill.sttp.tapir" %%% "tapir-sttp-client" % "1.10.11" +"com.softwaremill.sttp.tapir" %%% "tapir-sttp-client" % "1.10.12" "io.github.cquiroz" %%% "scala-java-time" % "2.2.0" // implementations of java.time classes for Scala.JS ``` diff --git a/generated-doc/out/docs/asyncapi.md b/generated-doc/out/docs/asyncapi.md index d243eb37b9..8eacc0d08b 100644 --- a/generated-doc/out/docs/asyncapi.md +++ b/generated-doc/out/docs/asyncapi.md @@ -3,7 +3,7 @@ To use, add the following dependencies: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-asyncapi-docs" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-asyncapi-docs" % "1.10.12" "com.softwaremill.sttp.apispec" %% "asyncapi-circe-yaml" % "..." // see https://github.com/softwaremill/sttp-apispec ``` diff --git a/generated-doc/out/docs/json-schema.md b/generated-doc/out/docs/json-schema.md index fc7516d8ab..51165a54cb 100644 --- a/generated-doc/out/docs/json-schema.md +++ b/generated-doc/out/docs/json-schema.md @@ -3,7 +3,7 @@ You can conveniently generate JSON schema from Tapir schema, which can be derived from your Scala types. Use `TapirSchemaToJsonSchema`: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-apispec-docs" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-apispec-docs" % "1.10.12" ``` Schema generation can now be performed like in the following example: diff --git a/generated-doc/out/docs/openapi.md b/generated-doc/out/docs/openapi.md index c1977a0c4a..59e1b38fbd 100644 --- a/generated-doc/out/docs/openapi.md +++ b/generated-doc/out/docs/openapi.md @@ -13,7 +13,7 @@ these steps can be done separately, giving you complete control over the process To generate OpenAPI documentation and expose it using the Swagger UI in a single step, first add the dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-swagger-ui-bundle" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-swagger-ui-bundle" % "1.10.12" ``` Then, you can interpret a list of endpoints using `SwaggerInterpreter`. The result will be a list of file-serving @@ -55,7 +55,7 @@ for details. Similarly as above, you'll need the following dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-redoc-bundle" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-redoc-bundle" % "1.10.12" ``` And the server endpoints can be generated using the `sttp.tapir.redoc.bundle.RedocInterpreter` class. @@ -65,7 +65,7 @@ And the server endpoints can be generated using the `sttp.tapir.redoc.bundle.Red To generate the docs in the OpenAPI yaml format, add the following dependencies: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-openapi-docs" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-openapi-docs" % "1.10.12" "com.softwaremill.sttp.apispec" %% "openapi-circe-yaml" % "..." // see https://github.com/softwaremill/sttp-apispec ``` @@ -133,7 +133,7 @@ For example, generating the OpenAPI 3.0.3 YAML string can be achieved by perform Firstly add dependencies: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-openapi-docs" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-openapi-docs" % "1.10.12" "com.softwaremill.sttp.apispec" %% "openapi-circe-yaml" % "..." // see https://github.com/softwaremill/sttp-apispec ``` @@ -163,12 +163,12 @@ The modules `tapir-swagger-ui` and `tapir-redoc` contain server endpoint definit yaml format, will expose it using the given context path. To use, add as a dependency either `tapir-swagger-ui`: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-swagger-ui" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-swagger-ui" % "1.10.12" ``` or `tapir-redoc`: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-redoc" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-redoc" % "1.10.12" ``` Then, you'll need to pass the server endpoints to your server interpreter. For example, using akka-http: diff --git a/generated-doc/out/endpoint/integrations.md b/generated-doc/out/endpoint/integrations.md index b193676db1..5ad0bd039d 100644 --- a/generated-doc/out/endpoint/integrations.md +++ b/generated-doc/out/endpoint/integrations.md @@ -12,7 +12,7 @@ The `tapir-cats` module contains additional instances for some [cats](https://ty datatypes as well as additional syntax: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-cats" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-cats" % "1.10.12" ``` - `import sttp.tapir.integ.cats.codec._` - brings schema, validator and codec instances @@ -22,7 +22,7 @@ Additionally, the `tapir-cats-effect` module contains an implementation of the ` between the sttp-internal `MonadError` and the cats-effect `Sync` typeclass: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-cats-effect" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-cats-effect" % "1.10.12" ``` ## Refined integration @@ -31,7 +31,7 @@ If you use [refined](https://github.com/fthomas/refined), the `tapir-refined` mo validators for `T Refined P` as long as a codec for `T` already exists: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-refined" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-refined" % "1.10.12" ``` You'll need to extend the `sttp.tapir.codec.refined.TapirCodecRefined` @@ -52,7 +52,7 @@ If you use [iron](https://github.com/Iltotore/iron), the `tapir-iron` module wil validators for `T :| P` as long as a codec for `T` already exists: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-iron" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-iron" % "1.10.12" ``` The module is only available for Scala 3 since iron is not designed to work with Scala 2. @@ -144,7 +144,7 @@ The `tapir-enumeratum` module provides schemas, validators and codecs for [Enume enumerations. To use, add the following dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-enumeratum" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-enumeratum" % "1.10.12" ``` Then, `import sttp.tapir.codec.enumeratum._`, or extends the `sttp.tapir.codec.enumeratum.TapirCodecEnumeratum` trait. @@ -157,7 +157,7 @@ If you use [scala-newtype](https://github.com/estatico/scala-newtype), the `tapi schemas for types with a `@newtype` and `@newsubtype` annotations as long as a codec and schema for its underlying value already exists: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-newtype" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-newtype" % "1.10.12" ``` Then, `import sttp.tapir.codec.newtype._`, or extend the `sttp.tapir.codec.newtype.TapirCodecNewType` trait to bring the implicit values into scope. @@ -168,7 +168,7 @@ If you use [monix newtypes](https://github.com/monix/newtypes), the `tapir-monix schemas for types which extend `NewtypeWrapped` and `NewsubtypeWrapped` annotations as long as a codec and schema for its underlying value already exists: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-monix-newtype" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-monix-newtype" % "1.10.12" ``` Then, `import sttp.tapir.codec.monix.newtype._`, or extend the `sttp.tapir.codec.monix.newtype.TapirCodecMonixNewType` trait to bring the implicit values into scope. @@ -179,7 +179,7 @@ If you use [ZIO Prelude Newtypes](https://zio.github.io/zio-prelude/docs/newtype schemas for types defined using `Newtype` and `Subtype` as long as a codec and a schema for the underlying type already exists: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-zio-prelude" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-zio-prelude" % "1.10.12" ``` Then, mix in `sttp.tapir.codec.zio.prelude.newtype.TapirNewtypeSupport` into your newtype to bring the implicit values into scope: @@ -218,7 +218,7 @@ For details refer to [derevo documentation](https://github.com/tofu-tf/derevo#in To use, add the following dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-derevo" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-derevo" % "1.10.12" ``` Then you can derive schema for your ADT along with other typeclasses besides ADT declaration itself: diff --git a/generated-doc/out/endpoint/json.md b/generated-doc/out/endpoint/json.md index c8a15bd38f..0186fb4e37 100644 --- a/generated-doc/out/endpoint/json.md +++ b/generated-doc/out/endpoint/json.md @@ -50,7 +50,7 @@ stringJsonBody.schema(implicitly[Schema[MyBody]].as[String]) To use [Circe](https://github.com/circe/circe), add the following dependency to your project: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-json-circe" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-json-circe" % "1.10.12" ``` Next, import the package (or extend the `TapirJsonCirce` trait, see [MyTapir](../mytapir.md)): @@ -123,7 +123,7 @@ Now the above JSON object will render as To use [µPickle](http://www.lihaoyi.com/upickle/) add the following dependency to your project: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-json-upickle" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-json-upickle" % "1.10.12" ``` Next, import the package (or extend the `TapirJsonuPickle` trait, see [MyTapir](../mytapir.md) and add `TapirJsonuPickle` not `TapirCirceJson`): @@ -158,13 +158,13 @@ For more examples, including making a custom encoder/decoder, see [TapirJsonuPic To use [Play JSON](https://github.com/playframework/play-json) for **Play 3.0**, add the following dependency to your project: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-json-play" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-json-play" % "1.10.12" ``` For **Play 2.9** use: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-json-play29" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-json-play29" % "1.10.12" ``` Next, import the package (or extend the `TapirJsonPlay` trait, see [MyTapir](../mytapir.md) and add `TapirJsonPlay` not `TapirCirceJson`): @@ -180,7 +180,7 @@ Play JSON requires `Reads` and `Writes` implicit values in scope for each type y To use [Spray JSON](https://github.com/spray/spray-json) add the following dependency to your project: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-json-spray" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-json-spray" % "1.10.12" ``` Next, import the package (or extend the `TapirJsonSpray` trait, see [MyTapir](../mytapir.md) and add `TapirJsonSpray` not `TapirCirceJson`): @@ -196,7 +196,7 @@ Spray JSON requires a `JsonFormat` implicit value in scope for each type you wan To use [Tethys JSON](https://github.com/tethys-json/tethys) add the following dependency to your project: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-json-tethys" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-json-tethys" % "1.10.12" ``` Next, import the package (or extend the `TapirJsonTethys` trait, see [MyTapir](../mytapir.md) and add `TapirJsonTethys` not `TapirCirceJson`): @@ -212,7 +212,7 @@ Tethys JSON requires `JsonReader` and `JsonWriter` implicit values in scope for To use [Jsoniter-scala](https://github.com/plokhotnyuk/jsoniter-scala) add the following dependency to your project: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-jsoniter-scala" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-jsoniter-scala" % "1.10.12" ``` Next, import the package (or extend the `TapirJsonJsoniter` trait, see [MyTapir](../mytapir.md) and add `TapirJsonJsoniter` not `TapirCirceJson`): @@ -228,7 +228,7 @@ Jsoniter Scala requires `JsonValueCodec` implicit value in scope for each type y To use [json4s](https://github.com/json4s/json4s) add the following dependencies to your project: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-json-json4s" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-json-json4s" % "1.10.12" ``` And one of the implementations: @@ -259,7 +259,7 @@ implicit val formats: Formats = org.json4s.jackson.Serialization.formats(NoTypeH To use [zio-json](https://github.com/zio/zio-json), add the following dependency to your project: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-json-zio" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-json-zio" % "1.10.12" ``` Next, import the package (or extend the `TapirJsonZio` trait, see [MyTapir](../mytapir.md) and add `TapirJsonZio` instead of `TapirCirceJson`): diff --git a/generated-doc/out/endpoint/oneof.md b/generated-doc/out/endpoint/oneof.md index 4478ca4045..3baa38247e 100644 --- a/generated-doc/out/endpoint/oneof.md +++ b/generated-doc/out/endpoint/oneof.md @@ -136,6 +136,14 @@ val baseEndpoint = endpoint.errorOut( ) ``` +### One-of-variant and singleton types + +One-of variants can also be created so that they are used only for specific values. This is a specialisation of the +`oneOfVariantValueMatcher` methods, which allows for a more convenient and compact description. + +There are two methods which allows working with multiple or single specific values: `oneOfVariantExactMatcher` and +`oneOfVariantSingletonMatcher`. + ### Error outputs Error outputs can be extended with new variants, which is especially useful for partial server endpoints, when the diff --git a/generated-doc/out/endpoint/pickler.md b/generated-doc/out/endpoint/pickler.md index c5e778c6b5..28b9f53c2a 100644 --- a/generated-doc/out/endpoint/pickler.md +++ b/generated-doc/out/endpoint/pickler.md @@ -9,7 +9,7 @@ In [other](json.md) tapir-JSON integrations, you have to keep the `Schema` (whic To use pickler, add the following dependency to your project: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-json-pickler" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-json-pickler" % "1.10.12" ``` Please note that it is available only for Scala 3 and Scala.JS 3. diff --git a/generated-doc/out/endpoint/static.md b/generated-doc/out/endpoint/static.md index 45238f398c..878cfc070c 100644 --- a/generated-doc/out/endpoint/static.md +++ b/generated-doc/out/endpoint/static.md @@ -11,7 +11,7 @@ the API documentation of the old static content API, switch documentation to an In order to use static content endpoints, add the module to your dependencies: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-files" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-files" % "1.10.12" ``` ## Files diff --git a/generated-doc/out/generator/sbt-openapi-codegen.md b/generated-doc/out/generator/sbt-openapi-codegen.md index e416cc80bf..b67442d70f 100644 --- a/generated-doc/out/generator/sbt-openapi-codegen.md +++ b/generated-doc/out/generator/sbt-openapi-codegen.md @@ -9,7 +9,7 @@ This is a really early alpha implementation. Add the sbt plugin to the `project/plugins.sbt`: ```scala -addSbtPlugin("com.softwaremill.sttp.tapir" % "sbt-openapi-codegen" % "1.10.11") +addSbtPlugin("com.softwaremill.sttp.tapir" % "sbt-openapi-codegen" % "1.10.12") ``` Enable the plugin for your project in the `build.sbt`: diff --git a/generated-doc/out/index.md b/generated-doc/out/index.md index d7f60400d4..cba78b3e12 100644 --- a/generated-doc/out/index.md +++ b/generated-doc/out/index.md @@ -218,6 +218,7 @@ We offer commercial support for sttp and related technologies, as well as develo tutorials/03_json tutorials/04_errors tutorials/05_multiple_inputs_outputs + tutorials/06_error_variants .. toctree:: :maxdepth: 2 diff --git a/generated-doc/out/quickstart.md b/generated-doc/out/quickstart.md index 2e7dcabbfa..01925674d4 100644 --- a/generated-doc/out/quickstart.md +++ b/generated-doc/out/quickstart.md @@ -3,7 +3,7 @@ To use tapir, add the following dependency to your project: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-core" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-core" % "1.10.12" ``` This will import only the core classes needed to create endpoint descriptions. To generate a server or a client, you diff --git a/generated-doc/out/server/akkahttp.md b/generated-doc/out/server/akkahttp.md index 3fb091f9ab..c2f3a21e3f 100644 --- a/generated-doc/out/server/akkahttp.md +++ b/generated-doc/out/server/akkahttp.md @@ -4,14 +4,14 @@ To expose an endpoint as an [akka-http](https://doc.akka.io/docs/akka-http/curre dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-akka-http-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-akka-http-server" % "1.10.12" ``` This will transitively pull some Akka modules in version 2.6. If you want to force your own Akka version (for example 2.5), use sbt exclusion. Mind the Scala version in artifact name: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-akka-http-server" % "1.10.11" exclude("com.typesafe.akka", "akka-stream_2.12") +"com.softwaremill.sttp.tapir" %% "tapir-akka-http-server" % "1.10.12" exclude("com.typesafe.akka", "akka-stream_2.12") ``` Now import the object: diff --git a/generated-doc/out/server/armeria.md b/generated-doc/out/server/armeria.md index 1225ea6c01..d1a7b7c35b 100644 --- a/generated-doc/out/server/armeria.md +++ b/generated-doc/out/server/armeria.md @@ -8,7 +8,7 @@ Armeria interpreter can be used with different effect systems (cats-effect, ZIO) Add the following dependency ```scala -"com.softwaremill.sttp.tapir" %% "tapir-armeria-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-armeria-server" % "1.10.12" ``` and import the object: @@ -75,7 +75,7 @@ Note that Armeria automatically injects an `ExecutionContext` on top of Armeria' Add the following dependency ```scala -"com.softwaremill.sttp.tapir" %% "tapir-armeria-server-cats" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-armeria-server-cats" % "1.10.12" ``` to use this interpreter with Cats Effect typeclasses. @@ -154,7 +154,7 @@ val tapirService = ArmeriaCatsServerInterpreter(dispatcher).toService(streamingR Add the following dependency ```scala -"com.softwaremill.sttp.tapir" %% "tapir-armeria-server-zio" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-armeria-server-zio" % "1.10.12" ``` to use this interpreter with ZIO. diff --git a/generated-doc/out/server/aws.md b/generated-doc/out/server/aws.md index e0847ee0f2..0c4ba33f64 100644 --- a/generated-doc/out/server/aws.md +++ b/generated-doc/out/server/aws.md @@ -33,7 +33,7 @@ These are corresponding classes for each of the supported runtime: To start using any of the above add the following dependency: ```sbt -"com.softwaremill.sttp.tapir" %% "tapir-aws-lambda" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-aws-lambda" % "1.10.12" ``` ## Deployment @@ -44,9 +44,9 @@ Tapir leverages ways of doing it provided by AWS, you can choose from: AWS SAM t You can start by adding one of the following dependencies to your project, and then follow examples: ```sbt -"com.softwaremill.sttp.tapir" %% "tapir-aws-sam" % "1.10.11" -"com.softwaremill.sttp.tapir" %% "tapir-aws-terraform" % "1.10.11" -"com.softwaremill.sttp.tapir" %% "tapir-aws-cdk" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-aws-sam" % "1.10.12" +"com.softwaremill.sttp.tapir" %% "tapir-aws-terraform" % "1.10.12" +"com.softwaremill.sttp.tapir" %% "tapir-aws-cdk" % "1.10.12" ``` ### Examples diff --git a/generated-doc/out/server/finatra.md b/generated-doc/out/server/finatra.md index a3b17944db..c5cb18197b 100644 --- a/generated-doc/out/server/finatra.md +++ b/generated-doc/out/server/finatra.md @@ -4,7 +4,7 @@ To expose an endpoint as an [finatra](https://twitter.github.io/finatra/) server dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-finatra-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-finatra-server" % "1.10.12" ``` and import the object: @@ -17,7 +17,7 @@ This interpreter supports the twitter `Future`. Or, if you would like to use cats-effect project, you can add the following dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-finatra-server-cats" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-finatra-server-cats" % "1.10.12" ``` and import the object: diff --git a/generated-doc/out/server/http4s.md b/generated-doc/out/server/http4s.md index 9c251c6a8a..8158ea3aad 100644 --- a/generated-doc/out/server/http4s.md +++ b/generated-doc/out/server/http4s.md @@ -4,7 +4,7 @@ To expose an endpoint as an [http4s](https://http4s.org) server, first add the f dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-http4s-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-http4s-server" % "1.10.12" ``` and import the object: diff --git a/generated-doc/out/server/jdkhttp.md b/generated-doc/out/server/jdkhttp.md index b5f4a05722..290ea9da9a 100644 --- a/generated-doc/out/server/jdkhttp.md +++ b/generated-doc/out/server/jdkhttp.md @@ -5,7 +5,7 @@ To expose endpoints using the (`com.sun.net.httpserver`), first add the following dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-jdkhttp-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-jdkhttp-server" % "1.10.12" ``` Then, import the package: diff --git a/generated-doc/out/server/netty.md b/generated-doc/out/server/netty.md index 201879c650..5555c4d324 100644 --- a/generated-doc/out/server/netty.md +++ b/generated-doc/out/server/netty.md @@ -4,16 +4,16 @@ To expose an endpoint using a [Netty](https://netty.io)-based server, first add ```scala // if you are using Future or just exploring: -"com.softwaremill.sttp.tapir" %% "tapir-netty-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-netty-server" % "1.10.12" // if you want to use Java 21 Loom virtual threads in direct style: -"com.softwaremill.sttp.tapir" %% "tapir-netty-server-sync" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-netty-server-sync" % "1.10.12" // if you are using cats-effect: -"com.softwaremill.sttp.tapir" %% "tapir-netty-server-cats" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-netty-server-cats" % "1.10.12" // if you are using zio: -"com.softwaremill.sttp.tapir" %% "tapir-netty-server-zio" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-netty-server-zio" % "1.10.12" ``` Then, use: diff --git a/generated-doc/out/server/nima.md b/generated-doc/out/server/nima.md index 64940c0c8e..e2688c2f2a 100644 --- a/generated-doc/out/server/nima.md +++ b/generated-doc/out/server/nima.md @@ -8,7 +8,7 @@ To expose an endpoint as a [Helidon Níma](https://helidon.io/nima) server, firs dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-nima-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-nima-server" % "1.10.12" ``` Loom-managed concurrency uses direct style instead of effect wrappers like `Future[T]` or `IO[T]`. Because of this, diff --git a/generated-doc/out/server/observability.md b/generated-doc/out/server/observability.md index b67883bdc1..e16a79ef8c 100644 --- a/generated-doc/out/server/observability.md +++ b/generated-doc/out/server/observability.md @@ -49,7 +49,7 @@ val labels = MetricLabels( Add the following dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-prometheus-metrics" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-prometheus-metrics" % "1.10.12" ``` `PrometheusMetrics` encapsulates `PrometheusReqistry` and `Metric` instances. It provides several ready to use metrics as @@ -128,7 +128,7 @@ val prometheusMetrics = PrometheusMetrics[Future]("tapir", PrometheusRegistry.de Add the following dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-opentelemetry-metrics" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-opentelemetry-metrics" % "1.10.12" ``` OpenTelemetry metrics are vendor-agnostic and can be exported using one @@ -155,7 +155,7 @@ val metricsInterceptor = metrics.metricsInterceptor() // add to your server opti Add the following dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-datadog-metrics" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-datadog-metrics" % "1.10.12" ``` Datadog metrics are sent as Datadog custom metrics through @@ -222,7 +222,7 @@ val datadogMetrics = DatadogMetrics.default[Future](statsdClient) Add the following dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-zio-metrics" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-zio-metrics" % "1.10.12" ``` Metrics have been integrated into ZIO core in ZIO2. diff --git a/generated-doc/out/server/pekkohttp.md b/generated-doc/out/server/pekkohttp.md index 027f2b2ff1..be9fdcdf0a 100644 --- a/generated-doc/out/server/pekkohttp.md +++ b/generated-doc/out/server/pekkohttp.md @@ -4,14 +4,14 @@ To expose an endpoint as a [pekko-http](https://pekko.apache.org/docs/pekko-http dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-pekko-http-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-pekko-http-server" % "1.10.12" ``` This will transitively pull some Pekko modules. If you want to force your own Pekko version, use sbt exclusion. Mind the Scala version in artifact name: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-pekko-http-server" % "1.10.11" exclude("org.apache.pekko", "pekko-stream_2.12") +"com.softwaremill.sttp.tapir" %% "tapir-pekko-http-server" % "1.10.12" exclude("org.apache.pekko", "pekko-stream_2.12") ``` Now import the object: diff --git a/generated-doc/out/server/play.md b/generated-doc/out/server/play.md index 6a5b0c0580..a6e84a5856 100644 --- a/generated-doc/out/server/play.md +++ b/generated-doc/out/server/play.md @@ -6,7 +6,7 @@ See the [Play framework documentation](https://www.playframework.com/documentati To expose an endpoint as a [play-server](https://www.playframework.com/), using **Play 2.9 with Akka**, add the following dependencies: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-play29-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-play29-server" % "1.10.12" ``` and (if you don't already depend on Play) @@ -26,7 +26,7 @@ depending on whether you want to use netty or Akka based http-server under the h To expose an endpoint as a [play-server](https://www.playframework.com/), using **Play 3.0 with Pekko**, add the following dependencies: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-play-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-play-server" % "1.10.12" ``` and (if you don't already depend on Play) diff --git a/generated-doc/out/server/vertx.md b/generated-doc/out/server/vertx.md index 4a3bab7e32..1d3e54db42 100644 --- a/generated-doc/out/server/vertx.md +++ b/generated-doc/out/server/vertx.md @@ -8,7 +8,7 @@ Vert.x interpreter can be used with different effect systems (cats-effect, ZIO) Add the following dependency ```scala -"com.softwaremill.sttp.tapir" %% "tapir-vertx-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-vertx-server" % "1.10.12" ``` to use this interpreter with `Future`. @@ -63,7 +63,7 @@ It's also possible to define an endpoint together with the server logic in a sin Add the following dependency ```scala -"com.softwaremill.sttp.tapir" %% "tapir-vertx-server-cats" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-vertx-server-cats" % "1.10.12" ``` to use this interpreter with Cats Effect typeclasses. @@ -145,7 +145,7 @@ val attach = VertxCatsServerInterpreter(dispatcher).route(streamedResponse.serve Add the following dependency ```scala -"com.softwaremill.sttp.tapir" %% "tapir-vertx-server-zio" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-vertx-server-zio" % "1.10.12" ``` to use this interpreter with ZIO. diff --git a/generated-doc/out/server/zio-http4s.md b/generated-doc/out/server/zio-http4s.md index 523c655c18..6a25225217 100644 --- a/generated-doc/out/server/zio-http4s.md +++ b/generated-doc/out/server/zio-http4s.md @@ -8,13 +8,13 @@ The `*-zio` modules depend on ZIO 2.x. You'll need the following dependency for the `ZServerEndpoint` type alias and helper classes: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-zio" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-zio" % "1.10.12" ``` or just add the zio-http4s integration which already depends on `tapir-zio`: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-http4s-server-zio" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-http4s-server-zio" % "1.10.12" ``` Next, instead of the usual `import sttp.tapir._`, you should import (or extend the `ZTapir` trait, see [MyTapir](../mytapir.md)): diff --git a/generated-doc/out/server/ziohttp.md b/generated-doc/out/server/ziohttp.md index 850d4c8075..16d8e85ec4 100644 --- a/generated-doc/out/server/ziohttp.md +++ b/generated-doc/out/server/ziohttp.md @@ -8,13 +8,13 @@ The `*-zio` modules depend on ZIO 2.x. You'll need the following dependency for the `ZServerEndpoint` type alias and helper classes: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-zio" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-zio" % "1.10.12" ``` or just add the zio-http integration which already depends on `tapir-zio`: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-zio-http-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-zio-http-server" % "1.10.12" ``` Next, instead of the usual `import sttp.tapir._`, you should import (or extend the `ZTapir` trait, see [MyTapir](../mytapir.md)): diff --git a/generated-doc/out/testing.md b/generated-doc/out/testing.md index 22498d3001..a04281f40c 100644 --- a/generated-doc/out/testing.md +++ b/generated-doc/out/testing.md @@ -23,7 +23,7 @@ Tapir builds upon the `SttpBackendStub` to enable stubbing using `Endpoint`s or dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-sttp-stub-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-sttp-stub-server" % "1.10.12" ``` Let's assume you are using the [akka http](server/akkahttp.md) interpreter. Given the following server endpoint: @@ -142,7 +142,7 @@ requests matching an endpoint, you can use the tapir `SttpBackendStub` extension Similarly as when testing server interpreters, add the dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-sttp-stub-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-sttp-stub-server" % "1.10.12" ``` And the following imports: @@ -197,7 +197,7 @@ with [mock-server](https://www.mock-server.com/) Add the following dependency: ```scala -"com.softwaremill.sttp.tapir" %% "sttp-mock-server" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "sttp-mock-server" % "1.10.12" ``` Imports: @@ -268,7 +268,7 @@ result == out To use, add the following dependency: ```scala -"com.softwaremill.sttp.tapir" %% "tapir-testing" % "1.10.11" +"com.softwaremill.sttp.tapir" %% "tapir-testing" % "1.10.12" ``` ### Shadowed endpoints @@ -294,7 +294,7 @@ Results in: ```scala res.toString -// res2: String = "Set(GET /x/y/x, is shadowed by: GET /x/*, GET /x, is shadowed by: GET /x/*)" +// res2: String = "Set(GET /x, is shadowed by: GET /x/*, GET /x/y/x, is shadowed by: GET /x/*)" ``` Example 2: diff --git a/generated-doc/out/tutorials/01_hello_world.md b/generated-doc/out/tutorials/01_hello_world.md index 9634cdeb31..dd27966074 100644 --- a/generated-doc/out/tutorials/01_hello_world.md +++ b/generated-doc/out/tutorials/01_hello_world.md @@ -22,8 +22,8 @@ multiple servers, but we'll choose the simplest (and also one of the fastest!), available through the `tapir-netty-server-sync` module: ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 ``` ## Endpoint description @@ -43,8 +43,8 @@ Let's start by defining the method and path of our endpoint: {emphasize-lines="4-11"} ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 import sttp.tapir.* @@ -72,8 +72,8 @@ of requiring it to be a fixed value (a constant): {emphasize-lines="10"} ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 import sttp.tapir.* @@ -98,8 +98,8 @@ Finally, let's add an output to the endpoint. We'll return the response as a str {emphasize-lines="11"} ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 import sttp.tapir.* @@ -124,8 +124,8 @@ will be sent as a response: {emphasize-lines="12"} ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 import sttp.tapir.* @@ -153,8 +153,8 @@ example, we'll bind to `localhost` (which is the default), and to the port 8080: {emphasize-lines="5, 15-18"} ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 import sttp.tapir.* import sttp.tapir.server.netty.sync.NettySyncServer diff --git a/generated-doc/out/tutorials/02_openapi_docs.md b/generated-doc/out/tutorials/02_openapi_docs.md index 6701b6b7ab..5e8da7a2b7 100644 --- a/generated-doc/out/tutorials/02_openapi_docs.md +++ b/generated-doc/out/tutorials/02_openapi_docs.md @@ -17,16 +17,16 @@ use a bundle, which first interprets the provided tapir endpoints into OpenAPI a endpoints, which expose the UI together with the generated specification. We'll need to add a dependency: ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.12 ``` We'll also define and expose two endpoints as an HTTP server, as described in the previous tutorial. Hence, our starting setup of `docs.scala` is as follows: ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.12 import sttp.tapir.* import sttp.tapir.server.netty.sync.NettySyncServer @@ -108,9 +108,9 @@ And that's almost all the code changes that we need to introduce! We only need t {emphasize-lines="3, 5, 8, 24-25, 29"} ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.12 import sttp.shared.Identity import sttp.tapir.* diff --git a/generated-doc/out/tutorials/03_json.md b/generated-doc/out/tutorials/03_json.md index 3738db4c9a..cfe4175582 100644 --- a/generated-doc/out/tutorials/03_json.md +++ b/generated-doc/out/tutorials/03_json.md @@ -83,7 +83,7 @@ In our case, deriving the schemas will amount to adding a `... derives Schema` c {emphasize-lines="1, 7, 10, 12, 16"} ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 //> using dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.30.1 import com.github.plokhotnyuk.jsoniter_scala.core.* // needed for `writeToString` @@ -127,10 +127,10 @@ how the `jsonBody[T]` method is used in the endpoint definition. We'll also expo {emphasize-lines="2-4, 10-15, 23-39"} ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-jsoniter-scala:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-jsoniter-scala:1.10.12 //> using dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.30.1 import com.github.plokhotnyuk.jsoniter_scala.macros.* // needed for ... derives diff --git a/generated-doc/out/tutorials/04_errors.md b/generated-doc/out/tutorials/04_errors.md index 7e6f0b763b..4404103b24 100644 --- a/generated-doc/out/tutorials/04_errors.md +++ b/generated-doc/out/tutorials/04_errors.md @@ -36,8 +36,8 @@ schemas both for the `Result` and `Error` classes, to represent them properly in describing the endpoint: ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-jsoniter-scala:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-jsoniter-scala:1.10.12 //> using dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.30.1 import com.github.plokhotnyuk.jsoniter_scala.macros.* @@ -72,10 +72,10 @@ We'll also add code to expose the endpoint as a server, along with its OpenAPI d {emphasize-lines="2-3, 11-13, 24-28, 30-36"} ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-jsoniter-scala:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-jsoniter-scala:1.10.12 //> using dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.30.1 import com.github.plokhotnyuk.jsoniter_scala.macros.* @@ -148,10 +148,10 @@ you'll also get `ERROR` logs when unhandled exceptions happen: {emphasize-lines="6, 26"} ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-jsoniter-scala:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-jsoniter-scala:1.10.12 //> using dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.30.1 //> using dep ch.qos.logback:logback-classic:1.5.6 diff --git a/generated-doc/out/tutorials/05_multiple_inputs_outputs.md b/generated-doc/out/tutorials/05_multiple_inputs_outputs.md index f67ccb4ec3..d18a1cedc4 100644 --- a/generated-doc/out/tutorials/05_multiple_inputs_outputs.md +++ b/generated-doc/out/tutorials/05_multiple_inputs_outputs.md @@ -3,15 +3,15 @@ In the tutorials so far we've seen how to use endpoints which have a single input and a single output, optionally with an additional single error output. However, most commonly you'll have multiple inputs and outputs. This can include multiple path, query parameters and headers, accompanied by a body as inputs, along with multiple output -headers, accompanied by a status code and body output. +headers, accompanied by a status code and body output. That's why in this tutorial we'll examine how to describe endpoints with multiple inputs/outputs and map them to high-level types. ## Describing the endpoint -Adding multiple inputs/outputs is simply a matter of calling `.in` or `.out` on an endpoint description multiple -times. +Adding multiple inputs/outputs is simply a matter of calling `.in` or `.out` on an endpoint description multiple +times. To demonstrate how this works, let's describe an `/operation/{opName}?value1=...&value2=...` endpoint, where `opName` can be either `add` or `sub`, and `value1` and `value2` should be numbers. The result should be returned in the @@ -20,7 +20,7 @@ body, but additionally the hash of the result should be included in the `X-Resul Below is the endpoint description; we'll be editing the `multiple.scala` file: ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 import sttp.tapir.* @@ -36,7 +36,7 @@ import sttp.tapir.* In our endpoint, we have: -* 5 inputs: 1 constant method input (`.get`), 1 constant path input (`"operation"`), 1 path-segment-capturing input +* 5 inputs: 1 constant method input (`.get`), 1 constant path input (`"operation"`), 1 path-segment-capturing input (`opName`), 2 query parameter inputs * 2 outputs: a body and a header * 1 error output: a string body, which we'll use in case an invalid operation name is provided @@ -47,9 +47,9 @@ When we provide the server logic for our endpoint, the values of the inputs are values from the output are mapped to the HTTP response. When there are multiple inputs, their values are by default extracted as a tuple. Conversely, the server logic must return a tuple, if there are multiple outputs. -Only values of inputs which are non-constant contribute to the input tuple. That is, in our case, a 3-tuple will be -extracted from the HTTP request: `(String, Int, Int)`, corresponding to the path segment and query parameters. The -constant method & path inputs are used when matching an endpoint with an incoming request, but do not contribute to the +Only values of inputs which are non-constant contribute to the input tuple. That is, in our case, a 3-tuple will be +extracted from the HTTP request: `(String, Int, Int)`, corresponding to the path segment and query parameters. The +constant method & path inputs are used when matching an endpoint with an incoming request, but do not contribute to the extracted values. Here's the code with the server logic provided, transforming a `(String, Int, Int)` tuple to a `(String, String)` tuple. @@ -57,8 +57,8 @@ The output tuple is then mapped to the response body & header: {emphasize-lines="5, 8-9, 18-29"} ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 import sttp.tapir.* import sttp.tapir.server.netty.sync.NettySyncServer @@ -88,7 +88,7 @@ import sttp.tapir.server.netty.sync.NettySyncServer .startAndWait() ``` -The user's input might be incorrect - that is, specify an unsupported operation name - in that case we return an error. +The user's input might be incorrect - that is, specify an unsupported operation name - in that case we return an error. That's why we need to wrap the result of the server logic either in a `Left` or `Right`, to use the error or success output. @@ -126,14 +126,14 @@ case class Input(opName: String, value1: Int, value2: Int) case class Output(result: String, hash: String) ``` -Our goal now is to change the endpoint's description so that the server logic has the shape +Our goal now is to change the endpoint's description so that the server logic has the shape `Input => Either[String, Output]`. -This can be done by mapping the inputs and outputs, that are defined on an endpoint, to our high-level types. +This can be done by mapping the inputs and outputs, that are defined on an endpoint, to our high-level types. -For inputs, we can use the `.mapIn` method. We need to provide two-way conversions, between `(String, Int, Int)` and +For inputs, we can use the `.mapIn` method. We need to provide two-way conversions, between `(String, Int, Int)` and `Input`. The tuple => `Input` conversion is used for incoming requests: first the values are extracted, and then the -mapping function is applied, yielding an `Input` instance, which is provided to the server logic. +mapping function is applied, yielding an `Input` instance, which is provided to the server logic. However, you also need to provide the `Input` => tuple conversion, in case the endpoint is interpreted as a client. We haven't covered this yet in the tutorials, but in the client-interpreter case such conversion is needed, when a request @@ -143,8 +143,8 @@ The mapping functions are simple, but quite boring to write: {emphasize-lines="8, 17-18, 23-27"} ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 import sttp.tapir.* import sttp.tapir.server.netty.sync.NettySyncServer @@ -178,23 +178,23 @@ import sttp.tapir.server.netty.sync.NettySyncServer .startAndWait() ``` -The `.mapIn` method covers all inputs defined so far, hence we're calling it only after all inputs are defined. If we -add more inputs later, the server logic will once again be parametrised by a tuple consisting of `Input` and the new +The `.mapIn` method covers all inputs defined so far, hence we're calling it only after all inputs are defined. If we +add more inputs later, the server logic will once again be parametrised by a tuple consisting of `Input` and the new inputs. ## Better mapping to case classes There is a better way of mapping multiple inputs and outputs to cases classes - Tapir can generate the "boring" mapping code for you. This can be done using `.mapInTo[]` and `.mapOutTo[]`. Both of these are macros, which take the target -type as a parameter. The mapping code is generated at compile-time, verifying also at compile-time that the types of the +type as a parameter. The mapping code is generated at compile-time, verifying also at compile-time that the types of the inputs/outputs and the types specified as the case class parameters line up. Here's the modified code using `.mapInTo`, which additionally maps outputs to the `Output` class: {emphasize-lines="9, 11-13, 19, 22"} ```scala -//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.11 -//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.11 +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 import sttp.tapir.* import sttp.tapir.server.netty.sync.NettySyncServer @@ -204,7 +204,7 @@ import sttp.tapir.server.netty.sync.NettySyncServer case class Output(result: String, hash: String) def hash(result: Int): Output = - Output(result.toString, + Output(result.toString, scala.util.hashing.MurmurHash3.stringHash(result.toString).toString) val opEndpoint = endpoint.get @@ -230,7 +230,7 @@ import sttp.tapir.server.netty.sync.NettySyncServer .startAndWait() ``` -Now our server logic has the shape `Input => Either[String, Output]`. Much better! +Now our server logic has the shape `Input => Either[String, Output]`. Much better! ## Further reading diff --git a/generated-doc/out/tutorials/06_error_variants.md b/generated-doc/out/tutorials/06_error_variants.md new file mode 100644 index 0000000000..32188cb02c --- /dev/null +++ b/generated-doc/out/tutorials/06_error_variants.md @@ -0,0 +1,282 @@ +# 6. Error variants + +Quite often, there's more than one thing that might go wrong. On the other hand, success can also have many facets. + +In the previous tutorials we've seen that Tapir includes built-in support for differentiating between successful and +error outputs. That's because in most cases the response that is returned in case of an error is totally different +from a response returned in case of success. + +Hence, Tapir has built-in, top-level response variants: either error, or success. It's also possible to introduce +more response variants, on lower levels, which further differentiate error and success scenarios. + +## `oneOf` outputs + +Such differentiation of both success and error output can be achieved using `oneOf` output descriptions. As the name +suggests, such outputs describe responses, which can take the shape of one of the given variants. Each variant is a +description of an output, such as the ones that we've seen so far. + +We've also seen that each output describes a mapping between a high-level Scala type and the HTTP response. +The same is true for `oneOf` outputs. Because `oneOf` has variants, we need a high-level type which also has variants. +Each variant of the Scala type will correspond to one output variant. + +```{note} +For error and successful outputs we also have variants in the high-level type, `Either[E, O]`. There are two variants: +`Left` and `Right`, corresponding to error and success outputs. +``` + +To represent various output variants on the Scala-value level, we'll typically use an `enum`. Each enum has a number of +variants: exactly what we need. Mind that using an enum is not required when using `oneOf` outputs, just convenient. + +## High-level response representation + +Let's start coding! We'll try to describe an endpoint, which fetches the avatar of the user. Here's a list of things +that might go wrong: + +* unauthorized, in case the avatar of the requested user is not public +* not found, in case there's no user with the provided id +* other, in case the server logic would like to respond with a generic error + +And there's also a "list of things that might go right", meaning success variants: + +* found, with an array of bytes, containing the avatar +* redirect, with an address where the avatar is located + +We'll represent both of these as an enum. We'll be editing the `variants.scala` file: + +```scala +enum AvatarError: + case Unauthorized + case NotFound + case Other(msg: String) + +enum AvatarSuccess: + case Found(bytes: Array[Byte]) + case Redirect(location: String) +``` + +## An output for a single variant + +Now that we have the high-level model in place, let's describe an output for a single variant; `AvatarSuccess.Redirect` +is the most complicated one (we won't be using `oneOf` just yet!). + +In case of a redirect, we want the response to contain: + +* the `307 Temporary Redirect` status code +* the `Location` header, with a value pointing to the avatar's location + +Endpoint outputs are described as instances of the `EndpointOutput` type. We've already seen output descriptions in +previous tutorials; `stringBody` is an `EndpointOutput[String]`, and `jsonBody[Nutrition]` is an +`EndpointOutput[Nutrition]`. Similarly, here, our goal is to obtain a value of type +`EndpointOutput[AvatarSuccess.Redirect]`, which will be mapped to the status code & header described above. + +For the status code, we can use the constant status code output: `statusCode(StatusCode.TemporaryRedirect)`. It takes a +`StatusCode` instance from the `sttp.model` package, and has the type `EndpointOutput[Unit]`. The `Unit` means that +it doesn't map any high-level values to the response: it's a constant, and it always describes the same 307 status code. + +For the header, we have the `header[String](HeaderNames.Location)` output. Just as with query and path parameters that +we've seen before, the `String` type parameter specifies that we'd like to serialize the header from a string. We can't +request serializing `AvatarSuccess.Redirect` instances, as Tapir knows nothing about that type. Hence, here we'll have +an `EndpointOutput[String]`: + +```scala +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 + +import sttp.model.{HeaderNames, StatusCode} +import sttp.tapir.* + +enum AvatarSuccess: + case Found(bytes: Array[Byte]) + case Redirect(location: String) + +val o1: EndpointOutput[Unit] = statusCode(StatusCode.TemporaryRedirect) +val o2: EndpointOutput[String] = header[String](HeaderNames.Location) +``` + +We can combine these outputs into a composite output using the `EndpointOutput.and` method. This is similar to adding +multiple outputs to an endpoint description using multiple `Endpoint.out` invocations. In fact, `Endpoint.out` +internally using `EndpointOutput.and` to combine the endpoints defined so far. + +The type of the composite output corresponds to the values, that are mapped to the response. As `o1` doesn't map any +values (it's a constant), the composite output will also have the type `EndpintOutput[String]`. Finally, we can map +this output to the `AvatarSuccess.Redirect` type using `.mapTo`, which we've learned about last time: + +{emphasize-lines="12-13"} +```scala +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 + +import sttp.model.{HeaderNames, StatusCode} +import sttp.tapir.* + +enum AvatarSuccess: + case Found(bytes: Array[Byte]) + case Redirect(location: String) + +val o1: EndpointOutput[Unit] = statusCode(StatusCode.TemporaryRedirect) +val o2: EndpointOutput[String] = header[String](HeaderNames.Location) +val o3: EndpointOutput[String] = o1.and(o2) +val o3mapped: EndpointOutput[AvatarSuccess.Redirect] = o3.mapTo[AvatarSuccess.Redirect] +``` + +## Picking the right variant + +We're almost ready to define the `oneOf` output with variants. Each variant consists of two parts: the output, and a +function determining (at run-time) if the variant should be used for a given high-level type. That is, when server logic +returns an instance of the high-level type, we need to determine, which variant should be used to map it to the +HTTP response. + +The default way to create variants is using the `oneOfVariant(EndpointOutput[T])` method. It creates a description of +a variant, which will match all instances of the `T` type. This check is done by inspecting the run-time class of the +`T` instance. This often works, but not always, as full type information is not always available at run-time, e.g. if +`T` is a generic type. If that's the case, you'll get a compile-time error. + +However, for `AvatarSuccess`, this default way of creating variants works just fine, as we are dealing with enum cases, +each of which translates to a separate class. Our one-of successful output takes the following form: + +{emphasize-lines="13-16"} +```scala +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 + +import sttp.model.{HeaderNames, StatusCode} +import sttp.tapir.* + +enum AvatarSuccess: + case Found(bytes: Array[Byte]) + case Redirect(location: String) + +val o1: EndpointOutput[Unit] = statusCode(StatusCode.TemporaryRedirect) +val o2: EndpointOutput[String] = header[String](HeaderNames.Location) + +val successOutput: EndpointOutput[AvatarSuccess] = oneOf( + oneOfVariant(o1.and(o2).mapTo[AvatarSuccess.Redirect]), + oneOfVariant(byteArrayBody.mapTo[AvatarSuccess.Found]) +) +``` + +The `oneOf` output can be typed using the common parent of both variants, which is `AvatarSuccess`. The server logic +will then have to return an instance of `AvatarSuccess`, in case of successful completion. + +```{warn} +Unfortunately, Tapir is not able to verify at compile-time that the variants are exhaustive, that is that every variant +of the high-level type has a corresponding output-variant. +``` + +## Dealing with singleton enum cases + +The output for `AvatarError` can be created similarly, with one caveat. It has two no-parameter cases (`Unauthorized` +and `NotFound`), which are not translated into separate classes by the compiler. Hence, the run-time checks done by +`oneOfVariant` would fail, or more precisely, any no-parameter case would be determined to match the first +no-parameter-case output variant, yielding incorrect responses. + +To fix this, we can use the `oneOfVariantSingletonMatcher` method. It takes a unit-typed output, along with an exact +value, to which the high-level output must be equal, for the variant to be chosen: + +```scala +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 + +import sttp.model.{HeaderNames, StatusCode} +import sttp.tapir.* + +enum AvatarError: + case Unauthorized + case NotFound + case Other(msg: String) + +val errorOutput: EndpointOutput[AvatarError] = oneOf( + oneOfVariantSingletonMatcher(statusCode(StatusCode.Unauthorized))(AvatarError.Unauthorized), + oneOfVariantSingletonMatcher(statusCode(StatusCode.NotFound))(AvatarError.NotFound), + oneOfVariant(stringBody.mapTo[AvatarError.Other]) +) +``` + +## Describing the entire endpoint + +Equipped with `oneOf` outputs, we can now fully describe and test our endpoint: + +```scala +//> using dep com.softwaremill.sttp.tapir::tapir-core:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.12 +//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.12 + +import sttp.model.{HeaderNames, StatusCode} +import sttp.tapir.* +import sttp.tapir.server.netty.sync.NettySyncServer +import sttp.tapir.swagger.bundle.SwaggerInterpreter +import sttp.shared.Identity + +enum AvatarError: + case Unauthorized + case NotFound + case Other(msg: String) + +enum AvatarSuccess: + case Found(bytes: Array[Byte]) + case Redirect(location: String) + +val o1: EndpointOutput[Unit] = statusCode(StatusCode.TemporaryRedirect) +val o2: EndpointOutput[String] = header[String](HeaderNames.Location) + +val successOutput: EndpointOutput[AvatarSuccess] = oneOf( + oneOfVariant(o1.and(o2).mapTo[AvatarSuccess.Redirect]), + oneOfVariant(byteArrayBody.mapTo[AvatarSuccess.Found]) +) + +val errorOutput: EndpointOutput[AvatarError] = oneOf( + oneOfVariantSingletonMatcher(statusCode(StatusCode.Unauthorized))(AvatarError.Unauthorized), + oneOfVariantSingletonMatcher(statusCode(StatusCode.NotFound))(AvatarError.NotFound), + oneOfVariant(stringBody.mapTo[AvatarError.Other]) +) + +@main def tapirErrorVariants(): Unit = + val avatarEndpoint = endpoint.get + .in("user" / "avatar") + .in(query[Int]("id")) + .out(successOutput) + .errorOut(errorOutput) + // Int => Either[AvatarError, AvatarSuccess] + .handle { + case 1 => Right(AvatarSuccess.Found(":-)".getBytes)) + case 2 => Right(AvatarSuccess.Redirect("https://example.org/me.jpg")) + case 3 => Left(AvatarError.Unauthorized) + case 4 => Left(AvatarError.Other("We don't like this user.")) + case _ => Left(AvatarError.NotFound) + } + + val swaggerEndpoints = SwaggerInterpreter().fromServerEndpoints[Identity]( + List(avatarEndpoint), "My App", "1.0") + + NettySyncServer() + .port(8080) + .addEndpoint(avatarEndpoint) + .addEndpoints(swaggerEndpoints) + .startAndWait() +``` + +As you can see, the server logic needs to return either an `AvatarError`, or a `AvatarSuccess`. This corresponds to the +outputs that we have defined. + +Let's run a couple of tests: + +```bash +# first console +% scala-cli variants.scala + +# second console +% curl -v "http://localhost:8080/user/avatar?id=2" +< HTTP/1.1 307 Temporary Redirect +< server: tapir/1.10.10 +< Location: https://example.org/me.jpg + +% curl -v "http://localhost:8080/user/avatar?id=7" +< HTTP/1.1 404 Not Found + +% curl -v "http://localhost:8080/user/avatar?id=3" +< HTTP/1.1 401 Unauthorized +``` + +We're also generating documentation. If you take a look at the [`http://localhost:8080/docs`](http://localhost:8080/docs), +you'll see that each status code is properly documented. + +## Further reading + +There's more ways to define `oneOf` variants, these are described in more detail on the reference page: +[](../endpoint/oneof.md).