Skip to content

Commit

Permalink
Release 0.19.0-M13
Browse files Browse the repository at this point in the history
  • Loading branch information
adamw committed Oct 22, 2021
1 parent 603db28 commit 5fc7f8a
Show file tree
Hide file tree
Showing 26 changed files with 197 additions and 52 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ tapir documentation is available at [tapir.softwaremill.com](http://tapir.softwa
Add the following dependency:

```sbt
"com.softwaremill.sttp.tapir" %% "tapir-core" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-core" % "0.19.0-M13"
```

You'll need partial unification enabled in the compiler (alternatively, you'll need to manually provide type arguments in some cases):
Expand Down
2 changes: 1 addition & 1 deletion generated-doc/out/client/http4s.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Add the dependency:

```scala
"com.softwaremill.sttp.tapir" %% "tapir-http4s-client" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-http4s-client" % "0.19.0-M13"
```

To interpret an endpoint definition as an `org.http4s.Request[F]`, import:
Expand Down
2 changes: 1 addition & 1 deletion generated-doc/out/client/play.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Add the dependency:

```scala
"com.softwaremill.sttp.tapir" %% "tapir-play-client" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-play-client" % "0.19.0-M13"
```

To make requests using an endpoint definition using the [play client](https://github.com/playframework/play-ws), import:
Expand Down
4 changes: 2 additions & 2 deletions generated-doc/out/client/sttp.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Add the dependency:

```scala
"com.softwaremill.sttp.tapir" %% "tapir-sttp-client" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-sttp-client" % "0.19.0-M13"
```

To make requests using an endpoint definition using the [sttp client](https://github.com/softwaremill/sttp), import:
Expand Down Expand Up @@ -57,7 +57,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" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %%% "tapir-sttp-client" % "0.19.0-M13"
"io.github.cquiroz" %%% "scala-java-time" % "2.2.0" // implementations of java.time classes for Scala.JS
```

Expand Down
4 changes: 2 additions & 2 deletions generated-doc/out/docs/asyncapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
To use, add the following dependencies:

```scala
"com.softwaremill.sttp.tapir" %% "tapir-asyncapi-docs" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-asyncapi-circe-yaml" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-asyncapi-docs" % "0.19.0-M13"
"com.softwaremill.sttp.tapir" %% "tapir-asyncapi-circe-yaml" % "0.19.0-M13"
```

Tapir contains a case class-based model of the asyncapi data structures in the `asyncapi/asyncapi-model` subproject (the
Expand Down
8 changes: 4 additions & 4 deletions generated-doc/out/docs/openapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
To use, add the following dependencies:

```scala
"com.softwaremill.sttp.tapir" %% "tapir-openapi-docs" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-openapi-circe-yaml" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-openapi-docs" % "0.19.0-M13"
"com.softwaremill.sttp.tapir" %% "tapir-openapi-circe-yaml" % "0.19.0-M13"
```

Tapir contains a case class-based model of the openapi data structures in the `openapi/openapi-model` subproject (the
Expand Down Expand Up @@ -133,8 +133,8 @@ definitions, which given the documentation in yaml format, will expose it using
as a dependency either:

```scala
"com.softwaremill.sttp.tapir" %% "tapir-swagger-ui" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-redoc" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-swagger-ui" % "0.19.0-M13"
"com.softwaremill.sttp.tapir" %% "tapir-redoc" % "0.19.0-M13"
```

Then, you'll need to pass the server endpoints to your server interpreter. For example, using akka-http:
Expand Down
3 changes: 1 addition & 2 deletions generated-doc/out/endpoint/contenttype.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ user-provided value will override this default:

## Multiple content types

Multiple, alternative content types can be specified using `oneOf`, similarly as when specifying [status code](statuscodes.md)
mappings.
Multiple, alternative content types can be specified using [`oneOf`](oneof.md).

On the server side, the appropriate mapping will be chosen using content negotiation, via the `Accept` header, using
the [configurable](../server/options.md) `ContentTypeInterceptor`. Note that both the base media type, and the charset
Expand Down
10 changes: 5 additions & 5 deletions generated-doc/out/endpoint/integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,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" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-cats" % "0.19.0-M13"
```

- `import sttp.tapir.integ.cats.codec._` - brings schema, validator and codec instances
Expand All @@ -19,7 +19,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" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-refined" % "0.19.0-M13"
```

You'll need to extend the `sttp.tapir.codec.refined.TapirCodecRefined`
Expand All @@ -40,7 +40,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" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-enumeratum" % "0.19.0-M13"
```

Then, `import sttp.tapir.codec.enumeratum`, or extends the `sttp.tapir.codec.enumeratum.TapirCodecEnumeratum` trait.
Expand Down Expand Up @@ -78,7 +78,7 @@ If you use [scala-newtype](https://github.com/estatico/scala-newtype), the `tapi
schemas for a 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" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-newtype" % "0.19.0-M13"
```

Then, `import sttp.tapir.codec.newtype._`, or extend the `sttp.tapir.codec.enumeratum.TapirCodecNewType` trait to bring the implicit values into scope.
Expand All @@ -90,7 +90,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" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-derevo" % "0.19.0-M13"
```

Then you can derive schema for your ADT along with other typeclasses besides ADT declaration itself:
Expand Down
22 changes: 21 additions & 1 deletion generated-doc/out/endpoint/ios.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ For inputs, these are:
For both inputs/outputs:

* `header[T](name)` captures a header with the given name
* `header[T](name, value)` maps to a fixed header with the given name and value
* `headers` captures all headers, represented as `List[Header]`
* `cookies` captures cookies from the `Cookie` header and represents them as `List[Cookie]`
* `setCookie(name)` captures the value & metadata of the a `Set-Cookie` header with a matching name
Expand Down Expand Up @@ -219,6 +220,25 @@ To match only the root path, use an empty string: `endpoint.in("")` will match `
To match a path prefix, first define inputs which match the path prefix, and then capture any remaining part using
`paths`, e.g.: `endpoint.in("api" / "download").in(paths)"`.

## Status codes

### Arbitrary status codes

To provide a (varying) status code of a server response, use the `statusCode` output, which maps to a value of type
`sttp.model.StatusCode`. The companion object contains known status codes as constants. This type of output is used only
when interpreting the endpoint as a server. If your endpoint returns varying status codes which you would like to have
listed in documentation use `statusCode.description(code1, "code1 description").description(code2, "code2 description")`
output.

### Fixed status code

A fixed status code can be specified using the `statusCode(code)` output.

### In server interpreters

Unless specified otherwise, successful responses are returned with the `200 OK` status code, and errors with
`400 Bad Request`. For exception and decode failure handling, see [error handling](../server/errors.md).

## Next

Read on about [status codes](statuscodes.md).
Read on about [one-of mappings](oneof.md).
16 changes: 8 additions & 8 deletions generated-doc/out/endpoint/json.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ the json codec that is in scope.
To use Circe, add the following dependency to your project:

```scala
"com.softwaremill.sttp.tapir" %% "tapir-json-circe" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-json-circe" % "0.19.0-M13"
```

Next, import the package (or extend the `TapirJsonCirce` trait, see [MyTapir](../mytapir.md)):
Expand Down Expand Up @@ -95,7 +95,7 @@ Now the above JSON object will render as
To use µPickle add the following dependency to your project:

```scala
"com.softwaremill.sttp.tapir" %% "tapir-json-upickle" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-json-upickle" % "0.19.0-M13"
```

Next, import the package (or extend the `TapirJsonuPickle` trait, see [MyTapir](../mytapir.md) and add `TapirJsonuPickle` not `TapirCirceJson`):
Expand Down Expand Up @@ -130,7 +130,7 @@ For more examples, including making a custom encoder/decoder, see [TapirJsonuPic
To use Play JSON add the following dependency to your project:

```scala
"com.softwaremill.sttp.tapir" %% "tapir-json-play" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-json-play" % "0.19.0-M13"
```

Next, import the package (or extend the `TapirJsonPlay` trait, see [MyTapir](../mytapir.md) and add `TapirJsonPlay` not `TapirCirceJson`):
Expand All @@ -146,7 +146,7 @@ Play JSON requires `Reads` and `Writes` implicit values in scope for each type y
To use Spray JSON add the following dependency to your project:

```scala
"com.softwaremill.sttp.tapir" %% "tapir-json-spray" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-json-spray" % "0.19.0-M13"
```

Next, import the package (or extend the `TapirJsonSpray` trait, see [MyTapir](../mytapir.md) and add `TapirJsonSpray` not `TapirCirceJson`):
Expand All @@ -162,7 +162,7 @@ Spray JSON requires a `JsonFormat` implicit value in scope for each type you wan
To use Tethys JSON add the following dependency to your project:

```scala
"com.softwaremill.sttp.tapir" %% "tapir-json-tethys" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-json-tethys" % "0.19.0-M13"
```

Next, import the package (or extend the `TapirJsonTethys` trait, see [MyTapir](../mytapir.md) and add `TapirJsonTethys` not `TapirCirceJson`):
Expand All @@ -178,7 +178,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" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-jsoniter-scala" % "0.19.0-M13"
```

Next, import the package (or extend the `TapirJsonJsoniter` trait, see [MyTapir](../mytapir.md) and add `TapirJsonJsoniter` not `TapirCirceJson`):
Expand All @@ -195,7 +195,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" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-json-json4s" % "0.19.0-M13"
```

And one of the implementations:
Expand Down Expand Up @@ -226,7 +226,7 @@ implicit val formats: Formats = org.json4s.jackson.Serialization.formats(NoTypeH
To use Zio JSON, add the following dependency to your project:

```scala
"com.softwaremill.sttp.tapir" %% "tapir-json-zio" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-json-zio" % "0.19.0-M13"
```
Next, import the package (or extend the `TapirJsonZio` trait, see [MyTapir](../mytapir.md) and add `TapirJsonZio` instead of `TapirCirceJson`):

Expand Down
125 changes: 125 additions & 0 deletions generated-doc/out/endpoint/oneof.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# One-of mappings

Outputs with multiple variants can be specified using the `oneOf` output. Each variant is defined using a one-of
mapping. All possible outputs must have a common supertype. Typically, the supertype is a sealed trait, and the mappings
are implementing case classes.

Each one-of mapping needs an `appliesTo` function to determine at run-time, if the variant should be used for a given
value. This function is inferred at compile time when using `oneOfMapping`, but can also be provided by hand, or if
the compile-time inference fails, using one of the other factory methods (see below). A catch-all mapping can be defined
using `oneOfDefaultMapping`, and should be placed as the last mapping in the list of possible variants.

When encoding such an output to a response, the first matching output is chosen, using the following rules:
1. the mappings `appliesTo` method, applied to the output value (as returned by the server logic) must return `true`.
2. when a fixed content type is specified by the output, it must match the request's `Accept` header (if present).
This implements content negotiation.

When decoding from a response, the first output which decodes successfully is chosen.

The outputs might vary in status codes, headers (e.g. different content types), and body implementations. However, for
bodies, only replayable ones can be used, and they need to have the same raw representation (e.g. all byte-array-base,
or all file-based).

Note that exhaustiveness of the mappings (that all subtypes of `T` are covered) is not checked.

For example, below is a specification for an endpoint where the error output is a sealed trait `ErrorInfo`;
such a specification can then be refined and reused for other endpoints:

```scala
import sttp.tapir._
import sttp.tapir.json.circe._
import sttp.tapir.generic.auto._
import sttp.model.StatusCode
import io.circe.generic.auto._

sealed trait ErrorInfo
case class NotFound(what: String) extends ErrorInfo
case class Unauthorized(realm: String) extends ErrorInfo
case class Unknown(code: Int, msg: String) extends ErrorInfo
case object NoContent extends ErrorInfo

// here we are defining an error output, but the same can be done for regular outputs
val baseEndpoint = endpoint.errorOut(
oneOf[ErrorInfo](
oneOfMapping(statusCode(StatusCode.NotFound).and(jsonBody[NotFound].description("not found"))),
oneOfMapping(statusCode(StatusCode.Unauthorized.and(jsonBody[Unauthorized].description("unauthorized")))),
oneOfMapping(statusCode(StatusCode.NoContent.and(emptyOutputAs(NoContent)))),
oneOfDefaultMapping(jsonBody[Unknown].description("unknown"))
)
)
```

## One-of-mapping and type erasure

Type erasure may prevent a one-of-mapping from working properly. The following example will fail at compile time because `Right[NotFound]` and `Right[BadRequest]` will become `Right[Any]`:

```scala
import sttp.tapir._
import sttp.tapir.json.circe._
import sttp.tapir.generic.auto._
import sttp.model.StatusCode
import io.circe.generic.auto._

case class ServerError(what: String)

sealed trait UserError
case class BadRequest(what: String) extends UserError
case class NotFound(what: String) extends UserError

val baseEndpoint = endpoint.errorOut(
oneOf[Either[ServerError, UserError]](
oneOfMapping(StatusCode.NotFound, jsonBody[Right[ServerError, NotFound]].description("not found")),
oneOfMapping(StatusCode.BadRequest, jsonBody[Right[ServerError, BadRequest]].description("unauthorized")),
oneOfMapping(StatusCode.InternalServerError, jsonBody[Left[ServerError, UserError]].description("unauthorized")),
)
)
// error: Constructing oneOfMapping of type scala.util.Right[repl.MdocSession.App.ServerError,repl.MdocSession.App.NotFound] is not allowed because of type erasure. Using a runtime-class-based check it isn't possible to verify that the input matches the desired class. Please use oneOfMappingClassMatcher, oneOfMappingValueMatcher or oneOfMappingFromMatchType instead
// oneOfMappingValueMatcher(StatusCode.NotFound, jsonBody[Right[ServerError, NotFound]].description("not found")) {
// ^
// error: Constructing oneOfMapping of type scala.util.Right[repl.MdocSession.App.ServerError,repl.MdocSession.App.BadRequest] is not allowed because of type erasure. Using a runtime-class-based check it isn't possible to verify that the input matches the desired class. Please use oneOfMappingClassMatcher, oneOfMappingValueMatcher or oneOfMappingFromMatchType instead
// oneOfMappingValueMatcher(StatusCode.BadRequest, jsonBody[Right[ServerError, BadRequest]].description("unauthorized")) {
// ^
// error: Constructing oneOfMapping of type scala.util.Left[repl.MdocSession.App.ServerError,repl.MdocSession.App.UserError] is not allowed because of type erasure. Using a runtime-class-based check it isn't possible to verify that the input matches the desired class. Please use oneOfMappingClassMatcher, oneOfMappingValueMatcher or oneOfMappingFromMatchType instead
// oneOfMappingValueMatcher(StatusCode.InternalServerError, jsonBody[Left[ServerError, UserError]].description("unauthorized")) {
// ^
```

The solution is therefore to handwrite a function checking that a value (of type `Any`) is of the correct type:


```scala
val baseEndpoint = endpoint.errorOut(
oneOf[Either[ServerError, UserError]](
oneOfMappingValueMatcher(StatusCode.NotFound, jsonBody[Right[ServerError, NotFound]].description("not found")) {
case Right(NotFound(_)) => true
},
oneOfMappingValueMatcher(StatusCode.BadRequest, jsonBody[Right[ServerError, BadRequest]].description("unauthorized")) {
case Right(BadRequest(_)) => true
},
oneOfMappingValueMatcher(StatusCode.InternalServerError, jsonBody[Left[ServerError, UserError]].description("unauthorized")) {
case Left(ServerError(_)) => true
}
)
)
```

Of course, you could use `oneOfMappingValueMatcher` to do runtime filtering for other purpose than solving type erasure.

In the case of solving type erasure, writing by hand partial function to match value against composition of case class and sealed trait can be repetitive.
To make that more easy, we provide an **experimental** typeclass - `MatchType` - so you can automatically derive that partial function:

```scala
import sttp.tapir.typelevel.MatchType

val baseEndpoint = endpoint.errorOut(
oneOf[Either[ServerError, UserError]](
oneOfMappingFromMatchType(StatusCode.NotFound, jsonBody[Right[ServerError, NotFound]].description("not found")),
oneOfMappingFromMatchType(StatusCode.BadRequest, jsonBody[Right[ServerError, BadRequest]].description("unauthorized")),
oneOfMappingFromMatchType(StatusCode.InternalServerError, jsonBody[Left[ServerError, UserError]].description("unauthorized"))
)
)
```

## Next

Read on about [codecs](codecs.md).
1 change: 1 addition & 0 deletions generated-doc/out/endpoint/schemas.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ field of a case class. One way the automatic & semi-automatic derivation can be
* `@encodedExample` sets example value for a case class field which is used in the documentation in the encoded form
* `@format` sets the format for a case class field
* `@deprecated` marks a case class's field as deprecated
* `@validate` will add the given validator to a case class field

These annotations will adjust schemas, after they are looked up using the normal implicit mechanisms.

Expand Down
2 changes: 1 addition & 1 deletion generated-doc/out/generator/sbt-openapi-codegen.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
Add the sbt plugin to the `project/plugins.sbt`:

```scala
addSbtPlugin("com.softwaremill.sttp.tapir" % "sbt-openapi-codegen" % "0.19.0-M12")
addSbtPlugin("com.softwaremill.sttp.tapir" % "sbt-openapi-codegen" % "0.19.0-M13")
```

Enable the plugin for your project in the `build.sbt`:
Expand Down
2 changes: 1 addition & 1 deletion generated-doc/out/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
To use tapir, add the following dependency to your project:

```scala
"com.softwaremill.sttp.tapir" %% "tapir-core" % "0.19.0-M12"
"com.softwaremill.sttp.tapir" %% "tapir-core" % "0.19.0-M13"
```

This will import only the core classes needed to create endpoint descriptions. To generate a server or a client, you
Expand Down
Loading

0 comments on commit 5fc7f8a

Please sign in to comment.