From 568105990a02487f418124ec3cdee31d86e1ac51 Mon Sep 17 00:00:00 2001 From: adamw Date: Fri, 13 Dec 2024 10:59:39 +0100 Subject: [PATCH] Release 4.0.0-M20 --- README.md | 10 ++-- generated-docs/out/backends/akka.md | 2 +- generated-docs/out/backends/catseffect.md | 6 +- generated-docs/out/backends/finagle.md | 2 +- generated-docs/out/backends/fs2.md | 8 +-- generated-docs/out/backends/future.md | 6 +- generated-docs/out/backends/http4s.md | 4 +- .../out/backends/javascript/fetch.md | 12 ++-- generated-docs/out/backends/monix.md | 6 +- generated-docs/out/backends/native/curl.md | 2 +- generated-docs/out/backends/pekko.md | 2 +- generated-docs/out/backends/scalaz.md | 2 +- generated-docs/out/backends/synchronous.md | 8 +-- .../out/backends/wrappers/custom.md | 23 +++++--- .../out/backends/wrappers/logging.md | 4 +- .../out/backends/wrappers/opentelemetry.md | 4 +- .../out/backends/wrappers/prometheus.md | 2 +- generated-docs/out/backends/zio.md | 8 +-- generated-docs/out/examples.md | 48 ++++++++-------- generated-docs/out/json.md | 57 +++++++++++-------- generated-docs/out/openapi.md | 8 +-- generated-docs/out/quickstart.md | 12 ++-- generated-docs/out/requests/body.md | 18 +++--- generated-docs/out/responses/basics.md | 5 +- generated-docs/out/responses/body.md | 34 +++++------ generated-docs/out/responses/exceptions.md | 2 +- generated-docs/out/testing.md | 4 +- generated-docs/out/websockets.md | 2 +- generated-docs/out/xml.md | 13 +++-- 29 files changed, 162 insertions(+), 152 deletions(-) diff --git a/README.md b/README.md index bbea82185d..8d5eeab293 100755 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ sttp (v2) documentation is available at [sttp.softwaremill.com/en/v2](https://st sttp (v1) documentation is available at [sttp.softwaremill.com/en/v1](https://sttp.softwaremill.com/en/v1). -scaladoc is available at [https://www.javadoc.io](https://www.javadoc.io/doc/com.softwaremill.sttp.client4/core_2.12/4.0.0-M19) +scaladoc is available at [https://www.javadoc.io](https://www.javadoc.io/doc/com.softwaremill.sttp.client4/core_2.12/4.0.0-M20) ## Quickstart with scala-cli @@ -56,7 +56,7 @@ Add the following directive to the top of your scala file to add the core sttp d If you are using [scala-cli](https://scala-cli.virtuslab.org), you can quickly start experimenting with sttp by copy-pasting the following: ``` -//> using dep "com.softwaremill.sttp.client4::core:4.0.0-M19" +//> using dep "com.softwaremill.sttp.client4::core:4.0.0-M20" import sttp.client4.quick._ quickRequest.get(uri"http://httpbin.org/ip").send() ``` @@ -68,7 +68,7 @@ The `quick` package import brings in the sttp API and a pre-configured, global s Similarly, using [Ammonite](http://ammonite.io): ```scala -import $ivy.`com.softwaremill.sttp.client4::core:4.0.0-M19` +import $ivy.`com.softwaremill.sttp.client4::core:4.0.0-M20` import sttp.client4.quick._ quickRequest.get(uri"http://httpbin.org/ip").send() ``` @@ -78,7 +78,7 @@ quickRequest.get(uri"http://httpbin.org/ip").send() Add the following dependency: ```scala -"com.softwaremill.sttp.client4" %% "core" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "core" % "4.0.0-M20" ``` Then, import: @@ -135,7 +135,7 @@ The documentation is typechecked using [mdoc](https://scalameta.org/mdoc/). The When generating documentation, it's best to set the version to the current one, so that the generated doc files don't include modifications with the current snapshot version. -That is, in sbt run: `set version := "4.0.0-M19"`, before running `mdoc` in `docs`. +That is, in sbt run: `set version := "4.0.0-M20"`, before running `mdoc` in `docs`. ### Testing the Scala.JS backend diff --git a/generated-docs/out/backends/akka.md b/generated-docs/out/backends/akka.md index de8c36816f..fbb4d3e841 100644 --- a/generated-docs/out/backends/akka.md +++ b/generated-docs/out/backends/akka.md @@ -3,7 +3,7 @@ This backend is based on [akka-http](http://doc.akka.io/docs/akka-http/current/scala/http/). To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "akka-http-backend" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "akka-http-backend" % "4.0.0-M20" ``` A fully **asynchronous** backend. Uses the `Future` effect to return responses. There are also [other `Future`-based backends](future.md), which don't depend on Akka. diff --git a/generated-docs/out/backends/catseffect.md b/generated-docs/out/backends/catseffect.md index 7e0c916ffc..6c9d40eb08 100644 --- a/generated-docs/out/backends/catseffect.md +++ b/generated-docs/out/backends/catseffect.md @@ -14,7 +14,7 @@ Also note that the [http4s](http4s.md) backend can also be created for a type im Firstly, add the following dependency to your project: ```scala -"com.softwaremill.sttp.client4" %% "cats" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "cats" % "4.0.0-M20" ``` Obtain a cats-effect `Resource` which creates the backend, and closes the thread pool after the resource is no longer used: @@ -82,9 +82,9 @@ Creation of the backend can be done in two basic ways: Firstly, add the following dependency to your project: ```scala -"com.softwaremill.sttp.client4" %% "armeria-backend-cats" % "4.0.0-M19" // for cats-effect 3.x +"com.softwaremill.sttp.client4" %% "armeria-backend-cats" % "4.0.0-M20" // for cats-effect 3.x // or -"com.softwaremill.sttp.client4" %% "armeria-backend-cats-ce2" % "4.0.0-M19" // for cats-effect 2.x +"com.softwaremill.sttp.client4" %% "armeria-backend-cats-ce2" % "4.0.0-M20" // for cats-effect 2.x ``` create client: diff --git a/generated-docs/out/backends/finagle.md b/generated-docs/out/backends/finagle.md index 2840dc130d..1d316ff326 100644 --- a/generated-docs/out/backends/finagle.md +++ b/generated-docs/out/backends/finagle.md @@ -3,7 +3,7 @@ To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "finagle-backend" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "finagle-backend" % "4.0.0-M20" ``` Next you'll need to add an implicit value: diff --git a/generated-docs/out/backends/fs2.md b/generated-docs/out/backends/fs2.md index eca42ebe49..9f272c1eeb 100644 --- a/generated-docs/out/backends/fs2.md +++ b/generated-docs/out/backends/fs2.md @@ -12,9 +12,9 @@ Creation of the backend can be done in two basic ways: Firstly, add the following dependency to your project: ```scala -"com.softwaremill.sttp.client4" %% "fs2" % "4.0.0-M19" // for cats-effect 3.x & fs2 3.x +"com.softwaremill.sttp.client4" %% "fs2" % "4.0.0-M20" // for cats-effect 3.x & fs2 3.x // or -"com.softwaremill.sttp.client4" %% "fs2ce2" % "4.0.0-M19" // for cats-effect 2.x & fs2 2.x +"com.softwaremill.sttp.client4" %% "fs2ce2" % "4.0.0-M20" // for cats-effect 2.x & fs2 2.x ``` Obtain a cats-effect `Resource` which creates the backend, and closes the thread pool after the resource is no longer used: @@ -78,9 +78,9 @@ Host header override is supported in environments running Java 12 onwards, but i To use, add the following dependency to your project: ```scala -"com.softwaremill.sttp.client4" %% "armeria-backend-fs2" % "4.0.0-M19" // for cats-effect 3.x & fs2 3.x +"com.softwaremill.sttp.client4" %% "armeria-backend-fs2" % "4.0.0-M20" // for cats-effect 3.x & fs2 3.x // or -"com.softwaremill.sttp.client4" %% "armeria-backend-fs2" % "4.0.0-M19" // for cats-effect 2.x & fs2 2.x +"com.softwaremill.sttp.client4" %% "armeria-backend-fs2" % "4.0.0-M20" // for cats-effect 2.x & fs2 2.x ``` create client: diff --git a/generated-docs/out/backends/future.md b/generated-docs/out/backends/future.md index 65d22719f8..b65ecd3be9 100644 --- a/generated-docs/out/backends/future.md +++ b/generated-docs/out/backends/future.md @@ -20,7 +20,7 @@ Class Supported stream type To use, you don't need any extra dependencies, `core` is enough: ``` -"com.softwaremill.sttp.client4" %% "core" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "core" % "4.0.0-M20" ``` You'll need the following imports: @@ -59,7 +59,7 @@ Host header override is supported in environments running Java 12 onwards, but i To use, add the following dependency to your project: ```scala -"com.softwaremill.sttp.client4" %% "okhttp-backend" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "okhttp-backend" % "4.0.0-M20" ``` and some imports: @@ -91,7 +91,7 @@ This backend depends on [OkHttp](http://square.github.io/okhttp/) and fully supp To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "armeria-backend" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "armeria-backend" % "4.0.0-M20" ``` add imports: diff --git a/generated-docs/out/backends/http4s.md b/generated-docs/out/backends/http4s.md index bedfc9a11a..df05f9b676 100644 --- a/generated-docs/out/backends/http4s.md +++ b/generated-docs/out/backends/http4s.md @@ -3,9 +3,9 @@ This backend is based on [http4s](https://http4s.org) (client) and is **asynchronous**. To use, add the following dependency to your project: ```scala -"com.softwaremill.sttp.client4" %% "http4s-backend" % "4.0.0-M19" // for cats-effect 3.x & http4s 1.0.0-Mx +"com.softwaremill.sttp.client4" %% "http4s-backend" % "4.0.0-M20" // for cats-effect 3.x & http4s 1.0.0-Mx // or -"com.softwaremill.sttp.client4" %% "http4s-ce2-backend" % "4.0.0-M19" // for cats-effect 2.x & http4s 0.21.x +"com.softwaremill.sttp.client4" %% "http4s-ce2-backend" % "4.0.0-M20" // for cats-effect 2.x & http4s 0.21.x ``` The backend can be created in a couple of ways, e.g.: diff --git a/generated-docs/out/backends/javascript/fetch.md b/generated-docs/out/backends/javascript/fetch.md index 6d0a12b89f..1faf56f902 100644 --- a/generated-docs/out/backends/javascript/fetch.md +++ b/generated-docs/out/backends/javascript/fetch.md @@ -7,7 +7,7 @@ A JavaScript backend with web socket support. Implemented using the [Fetch API]( This is the default backend, available in the main jar for JS. To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %%% "core" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %%% "core" % "4.0.0-M20" ``` And create the backend instance: @@ -26,7 +26,7 @@ Note that `Fetch` does not pass cookies by default. If your request needs cookie To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %%% "monix" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %%% "monix" % "4.0.0-M20" ``` And create the backend instance: @@ -40,7 +40,7 @@ val backend = FetchMonixBackend() To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %%% "zio" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %%% "zio" % "4.0.0-M20" ``` And create the backend instance: @@ -55,13 +55,13 @@ Any effect implementing the cats-effect `Concurrent` typeclass can be used. To u your project: ``` -"com.softwaremill.sttp.client4" %%% "cats" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %%% "cats" % "4.0.0-M20" ``` If you are on Cats Effect 2 (CE2) you will need to add the CE2 specific dependency instead: ``` -"com.softwaremill.sttp.client4" %%% "catsce2 % "4.0.0-M19" +"com.softwaremill.sttp.client4" %%% "catsce2 % "4.0.0-M20" ``` And create the backend instance: @@ -129,7 +129,7 @@ Streaming support is provided via `FetchMonixBackend`. Note that streaming suppo To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %%% "monix" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %%% "monix" % "4.0.0-M20" ``` An example of streaming a response: diff --git a/generated-docs/out/backends/monix.md b/generated-docs/out/backends/monix.md index 18457b3785..080fedaf78 100644 --- a/generated-docs/out/backends/monix.md +++ b/generated-docs/out/backends/monix.md @@ -12,7 +12,7 @@ Creation of the backend can be done in two basic ways: Firstly, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "monix" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "monix" % "4.0.0-M20" ``` and create the backend using: @@ -50,7 +50,7 @@ Host header override is supported in environments running Java 12 onwards, but i To use, add the following dependency to your project: ```scala -"com.softwaremill.sttp.client4" %% "okhttp-backend-monix" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "okhttp-backend-monix" % "4.0.0-M20" ``` Create the backend using: @@ -76,7 +76,7 @@ This backend depends on [OkHttp](http://square.github.io/okhttp/) and fully supp To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "armeria-backend-monix" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "armeria-backend-monix" % "4.0.0-M20" ``` add imports: diff --git a/generated-docs/out/backends/native/curl.md b/generated-docs/out/backends/native/curl.md index 92157693b0..d9df8a7921 100644 --- a/generated-docs/out/backends/native/curl.md +++ b/generated-docs/out/backends/native/curl.md @@ -5,7 +5,7 @@ A Scala Native backend implemented using [Curl](https://github.com/curl/curl/blo To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %%% "core" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %%% "core" % "4.0.0-M20" ``` and initialize one of the backends: diff --git a/generated-docs/out/backends/pekko.md b/generated-docs/out/backends/pekko.md index 71edbfd868..f75c8e0a8f 100644 --- a/generated-docs/out/backends/pekko.md +++ b/generated-docs/out/backends/pekko.md @@ -3,7 +3,7 @@ This backend is based on [pekko-http](https://pekko.apache.org/docs/pekko-http/current/). To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "pekko-http-backend" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "pekko-http-backend" % "4.0.0-M20" ``` A fully **asynchronous** backend. Uses the `Future` effect to return responses. There are also [other `Future`-based backends](future.md), which don't depend on Pekko. diff --git a/generated-docs/out/backends/scalaz.md b/generated-docs/out/backends/scalaz.md index 23ed895acf..4167765d84 100644 --- a/generated-docs/out/backends/scalaz.md +++ b/generated-docs/out/backends/scalaz.md @@ -8,7 +8,7 @@ The [Scalaz](https://github.com/scalaz/scalaz) backend is **asynchronous**. Send To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "armeria-backend-scalaz" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "armeria-backend-scalaz" % "4.0.0-M20" ``` add imports: diff --git a/generated-docs/out/backends/synchronous.md b/generated-docs/out/backends/synchronous.md index ed3c03149e..d1d6dc7b80 100644 --- a/generated-docs/out/backends/synchronous.md +++ b/generated-docs/out/backends/synchronous.md @@ -7,7 +7,7 @@ There are several synchronous backend implementations. Sending a request using t The default **synchronous** backend. To use, you don't need any extra dependencies, `core` is enough: ``` -"com.softwaremill.sttp.client4" %% "core" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "core" % "4.0.0-M20" ``` Create the backend using: @@ -40,7 +40,7 @@ Host header override is supported in environments running Java 12 onwards, but i To use, you don't need any extra dependencies, `core` is enough: ``` -"com.softwaremill.sttp.client4" %% "core" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "core" % "4.0.0-M20" ``` Create the backend using: @@ -62,7 +62,7 @@ This backend supports host header override, but it has to be enabled by system p To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "okhttp-backend" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "okhttp-backend" % "4.0.0-M20" ``` Create the backend using: @@ -99,7 +99,7 @@ Both HttpClient and OkHttp backends support regular [websockets](../websockets.m ``` // sbt dependency -"com.softwaremill.sttp.client4" %% "ox" % "4.0.0-M19", +"com.softwaremill.sttp.client4" %% "ox" % "4.0.0-M20", ``` ```scala diff --git a/generated-docs/out/backends/wrappers/custom.md b/generated-docs/out/backends/wrappers/custom.md index 670f440705..1e77f2d675 100644 --- a/generated-docs/out/backends/wrappers/custom.md +++ b/generated-docs/out/backends/wrappers/custom.md @@ -10,13 +10,13 @@ Possible use-cases for wrapper-backend include: See also the section on [resilience](../../resilience.md) which covers topics such as retries, circuit breaking and rate limiting. -## Request tagging +## Request attributes -Each request contains a `tags: Map[String, Any]` map. This map can be used to tag the request with any backend-specific information, and isn't used in any way by sttp itself. +Each request contains a `attributes: AttributeMap` type-safe map. This map can be used to tag the request with any backend-specific information, and isn't used in any way by sttp itself. -Tags can be added to a request using the `def tag(k: String, v: Any)` method, and read using the `def tag(k: String): Option[Any]` method. +Attributes can be added to a request using the `def attribute[T](k: AttributeKey[T], v: T)` method, and read using the `def attribute[T](k: Attribute[T]): Option[T]` method. -Backends, or backend wrappers can use tags e.g. for logging, passing a metric name, using different connection pools, or even different delegate backends. +Backends, or backend wrappers can use attributes e.g. for logging, passing a metric name, using different connection pools, or even different delegate backends. ## Listener backend @@ -42,6 +42,7 @@ Below is an example on how to implement a backend wrapper, which sends metrics for completed requests and wraps any `Future`-based backend: ```scala +import sttp.attributes.AttributeKey import sttp.capabilities.Effect import sttp.client4._ import sttp.client4.akkahttp._ @@ -49,6 +50,7 @@ import sttp.client4.wrappers.DelegateBackend import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global import scala.util._ + // the metrics infrastructure trait MetricsServer { def reportDuration(name: String, duration: Long): Unit @@ -58,6 +60,9 @@ class CloudMetricsServer extends MetricsServer { override def reportDuration(name: String, duration: Long): Unit = ??? } +case class MetricPrefix(prefix: String) +val MetricPrefixAttributeKey = AttributeKey[MetricPrefix] + // the backend wrapper abstract class MetricWrapper[P](delegate: GenericBackend[Future, P], metrics: MetricsServer) @@ -67,7 +72,7 @@ abstract class MetricWrapper[P](delegate: GenericBackend[Future, P], val start = System.currentTimeMillis() def report(metricSuffix: String): Unit = { - val metricPrefix = request.tag("metric").getOrElse("?") + val metricPrefix = request.attribute(MetricPrefixAttributeKey).getOrElse(MetricPrefix("?")) val end = System.currentTimeMillis() metrics.reportDuration(metricPrefix + "-" + metricSuffix, end - start) } @@ -93,7 +98,7 @@ val backend = MetricWrapper(AkkaHttpBackend(), new CloudMetricsServer()) basicRequest .get(uri"http://company.com/api/service1") - .tag("metric", "service1") + .attribute(MetricPrefixAttributeKey, MetricPrefix("service1")) .send(backend) ``` @@ -260,7 +265,7 @@ object RateLimitingSttpBackend { Implementing a new backend is made easy as the tests are published in the `core` jar file under the `tests` classifier. Simply add the follow dependencies to your `build.sbt`: ``` -"com.softwaremill.sttp.client4" %% "core" % "4.0.0-M19" % Test classifier "tests" +"com.softwaremill.sttp.client4" %% "core" % "4.0.0-M20" % Test classifier "tests" ``` Implement your backend and extend the `HttpTest` class: @@ -288,9 +293,9 @@ import sttp.client4.impl.cats.implicits._ from the cats integration module. The module should be available on the classpath after adding following dependency: ```scala -"com.softwaremill.sttp.client4" %% "cats" % "4.0.0-M19" // for cats-effect 3.x +"com.softwaremill.sttp.client4" %% "cats" % "4.0.0-M20" // for cats-effect 3.x // or -"com.softwaremill.sttp.client4" %% "catsce2" % "4.0.0-M19" // for cats-effect 2.x +"com.softwaremill.sttp.client4" %% "catsce2" % "4.0.0-M20" // for cats-effect 2.x ``` The object contains implicits to convert a cats `MonadError` into the sttp `MonadError`, diff --git a/generated-docs/out/backends/wrappers/logging.md b/generated-docs/out/backends/wrappers/logging.md index c7deb594c2..80890923c6 100644 --- a/generated-docs/out/backends/wrappers/logging.md +++ b/generated-docs/out/backends/wrappers/logging.md @@ -28,7 +28,7 @@ Log levels can be configured when creating the `LoggingBackend`, or specified in To use the [slf4j](http://www.slf4j.org) logging backend wrapper, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "slf4j-backend" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "slf4j-backend" % "4.0.0-M20" ``` There are three backend wrappers available, which log request & response information using a slf4j `Logger`. To see the logs, you'll need to use an slf4j-compatible logger implementation, e.g. [logback](http://logback.qos.ch), or use a binding, e.g. [log4j-slf4j](https://logging.apache.org/log4j/2.x/log4j-slf4j-impl.html). @@ -53,5 +53,5 @@ To create a customised logging backend, see the section on [custom backends](cus To use the [scribe](https://github.com/outr/scribe) logging backend wrapper, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "scribe-backend" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "scribe-backend" % "4.0.0-M20" ``` \ No newline at end of file diff --git a/generated-docs/out/backends/wrappers/opentelemetry.md b/generated-docs/out/backends/wrappers/opentelemetry.md index 20234dcdb8..9e1560fa6c 100644 --- a/generated-docs/out/backends/wrappers/opentelemetry.md +++ b/generated-docs/out/backends/wrappers/opentelemetry.md @@ -12,7 +12,7 @@ The backend depends only on [opentelemetry-api](https://github.com/open-telemetr following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "opentelemetry-metrics-backend" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "opentelemetry-metrics-backend" % "4.0.0-M20" ``` Then an instance can be obtained as follows: @@ -55,7 +55,7 @@ OpenTelemetryMetricsBackend( To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "opentelemetry-tracing-zio-backend" % "4.0.0-M19" // for ZIO 2.x +"com.softwaremill.sttp.client4" %% "opentelemetry-tracing-zio-backend" % "4.0.0-M20" // for ZIO 2.x ``` This backend depends on [zio-opentelemetry](https://github.com/zio/zio-telemetry). diff --git a/generated-docs/out/backends/wrappers/prometheus.md b/generated-docs/out/backends/wrappers/prometheus.md index 41f854df70..1f4ea53b98 100644 --- a/generated-docs/out/backends/wrappers/prometheus.md +++ b/generated-docs/out/backends/wrappers/prometheus.md @@ -3,7 +3,7 @@ To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "prometheus-backend" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "prometheus-backend" % "4.0.0-M20" ``` and some imports: diff --git a/generated-docs/out/backends/zio.md b/generated-docs/out/backends/zio.md index 30d7be6db4..0d0e3a323f 100644 --- a/generated-docs/out/backends/zio.md +++ b/generated-docs/out/backends/zio.md @@ -9,8 +9,8 @@ The `*-zio` modules depend on ZIO 2.x. For ZIO 1.x support, use modules with the To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "zio" % "4.0.0-M19" // for ZIO 2.x -"com.softwaremill.sttp.client4" %% "zio1" % "4.0.0-M19" // for ZIO 1.x +"com.softwaremill.sttp.client4" %% "zio" % "4.0.0-M20" // for ZIO 2.x +"com.softwaremill.sttp.client4" %% "zio1" % "4.0.0-M20" // for ZIO 1.x ``` Create the backend using: @@ -45,8 +45,8 @@ Host header override is supported in environments running Java 12 onwards, but i To use, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "armeria-backend-zio" % "4.0.0-M19" // for ZIO 2.x -"com.softwaremill.sttp.client4" %% "armeria-backend-zio1" % "4.0.0-M19" // for ZIO 1.x +"com.softwaremill.sttp.client4" %% "armeria-backend-zio" % "4.0.0-M20" // for ZIO 2.x +"com.softwaremill.sttp.client4" %% "armeria-backend-zio1" % "4.0.0-M20" // for ZIO 1.x ``` add imports: diff --git a/generated-docs/out/examples.md b/generated-docs/out/examples.md index 23b92e046f..8a0c6b632d 100644 --- a/generated-docs/out/examples.md +++ b/generated-docs/out/examples.md @@ -7,7 +7,7 @@ All of the examples are available [in the sources](https://github.com/softwaremi Required dependencies: ```scala -libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "core" % "4.0.0-M19") +libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "core" % "4.0.0-M20") ``` Example code: @@ -22,7 +22,7 @@ Example code: Required dependencies: ```scala -libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "core" % "4.0.0-M19") +libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "core" % "4.0.0-M20") ``` Example code: @@ -38,8 +38,8 @@ Required dependencies: ```scala libraryDependencies ++= List( - "com.softwaremill.sttp.client4" %% "akka-http-backend" % "4.0.0-M19", - "com.softwaremill.sttp.client4" %% "json4s" % "4.0.0-M19", + "com.softwaremill.sttp.client4" %% "akka-http-backend" % "4.0.0-M20", + "com.softwaremill.sttp.client4" %% "json4s" % "4.0.0-M20", "org.json4s" %% "json4s-native" % "3.6.0" ) ``` @@ -57,8 +57,8 @@ Required dependencies: ```scala libraryDependencies ++= List( - "com.softwaremill.sttp.client4" %% "zio" % "4.0.0-M19", - "com.softwaremill.sttp.client4" %% "circe" % "4.0.0-M19", + "com.softwaremill.sttp.client4" %% "zio" % "4.0.0-M20", + "com.softwaremill.sttp.client4" %% "circe" % "4.0.0-M20", "io.circe" %% "circe-generic" % "0.14.10" ) ``` @@ -76,8 +76,8 @@ Required dependencies: ```scala libraryDependencies ++= List( - "com.softwaremill.sttp.client4" %% "monix" % "4.0.0-M19", - "com.softwaremill.sttp.client4" %% "circe" % "4.0.0-M19", + "com.softwaremill.sttp.client4" %% "monix" % "4.0.0-M20", + "com.softwaremill.sttp.client4" %% "circe" % "4.0.0-M20", "io.circe" %% "circe-generic" % "0.14.10" ) ``` @@ -85,7 +85,7 @@ libraryDependencies ++= List( Example code: ```eval_rst -.. literalinclude:: ../../examples-ce2/src/main/scala/sttp/client4/examples/GetAndParseJsonOrFailMonixCirce.scala +.. literalinclude:: ../../examples-ce2/src/main/scala/sttp/client4/examples/GetAndParseJsonGetRightMonixCirce.scala :language: scala ``` @@ -95,8 +95,8 @@ Required dependencies: ```scala libraryDependencies ++= List( - "com.softwaremill.sttp.client4" %% "slf4j-backend" % "4.0.0-M19", - "com.softwaremill.sttp.client4" %% "circe" % "4.0.0-M19", + "com.softwaremill.sttp.client4" %% "slf4j-backend" % "4.0.0-M20", + "com.softwaremill.sttp.client4" %% "circe" % "4.0.0-M20", "io.circe" %% "circe-generic" % "0.14.10" ) ``` @@ -114,8 +114,8 @@ Required dependencies: ```scala libraryDependencies ++= List( - "com.softwaremill.sttp.client4" %% "monix" % "4.0.0-M19", - "com.softwaremill.sttp.client4" %% "circe" % "4.0.0-M19", + "com.softwaremill.sttp.client4" %% "monix" % "4.0.0-M20", + "com.softwaremill.sttp.client4" %% "circe" % "4.0.0-M20", "io.circe" %% "circe-generic" % "0.14.10" ) ``` @@ -132,7 +132,7 @@ Example code: Required dependencies: ```scala -libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "core" % "4.0.0-M19") +libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "core" % "4.0.0-M20") ``` Example code: @@ -146,7 +146,7 @@ Example code: Required dependencies: ```scala -libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "zio" % "4.0.0-M19") +libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "zio" % "4.0.0-M20") ``` Example code: @@ -161,7 +161,7 @@ Example code: Required dependencies: ```scala -libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "fs2" % "4.0.0-M19") +libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "fs2" % "4.0.0-M20") ``` Example code: @@ -176,7 +176,7 @@ Example code: Required dependencies: ```scala -libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "monix" % "4.0.0-M19") +libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "monix" % "4.0.0-M20") ``` Example code: @@ -191,7 +191,7 @@ Example code: Required dependencies: ```scala -libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "akka-http-backend" % "4.0.0-M19") +libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "akka-http-backend" % "4.0.0-M20") ``` Example code: @@ -206,7 +206,7 @@ Example code: Required dependencies: ```scala -libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "pekko-http-backend" % "4.0.0-M19") +libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "pekko-http-backend" % "4.0.0-M20") ``` Example code: @@ -221,7 +221,7 @@ Example code: Required dependencies: ```scala -libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "monix" % "4.0.0-M19") +libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "monix" % "4.0.0-M20") ``` Example code: @@ -236,7 +236,7 @@ Example code: Required dependencies: ```scala -libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "fs2" % "4.0.0-M19") +libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "fs2" % "4.0.0-M20") ``` Example code: @@ -251,7 +251,7 @@ Example code: Required dependencies: ```scala -libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "zio" % "4.0.0-M19") +libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "zio" % "4.0.0-M20") ``` Example code: @@ -266,7 +266,7 @@ Example code: Required dependencies: ```scala -libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "zio" % "4.0.0-M19") +libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "zio" % "4.0.0-M20") ``` Example code: @@ -281,7 +281,7 @@ Example code: Required dependencies: ```scala -libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "core" % "4.0.0-M19") +libraryDependencies ++= List("com.softwaremill.sttp.client4" %% "core" % "4.0.0-M20") ``` Example code: diff --git a/generated-docs/out/json.md b/generated-docs/out/json.md index d84dba3848..f91ce9fe81 100644 --- a/generated-docs/out/json.md +++ b/generated-docs/out/json.md @@ -2,25 +2,34 @@ Adding support for JSON (or other format) bodies in requests/responses is a matter of providing a [body serializer](requests/body.md) and/or a [response body specification](responses/body.md). Both are quite straightforward to implement, so integrating with your favorite JSON library shouldn't be a problem. However, there are some integrations available out-of-the-box. -Each integration is available as an import, which brings the implicit `BodySerializer`s and `asJson` methods into scope. Alternatively, these values are grouped intro traits (e.g. `sttp.client4.circe.SttpCirceApi`), which can be extended to group multiple integrations in one object, and thus reduce the number of necessary imports. +Each integration is available as an import, which brings `asJson` methods into scope. Alternatively, these values are grouped intro traits (e.g. `sttp.client4.circe.SttpCirceApi`), which can be extended to group multiple integrations in one object, and thus reduce the number of necessary imports. The following variants of `asJson` methods are available: -* regular - deserializes the body to json, only if the response is successful (2xx) -* `always` - deserializes the body to json regardless of the status code -* `either` - uses different deserializers for error and successful (2xx) responses +* `asJson(b: B)` - to be used when specifying the body of a request: serializes the body so that it can be used as a request's body, e.g. using `basicRequest.body(asJson(myValue))` +* `asJson[B]` - to be used when specifying how the response body should be handled: specifies that the body should be deserialized to json, but only if the response is successful (2xx); otherwise, a `Left` is returned, with body as a string +* `asJsonOrFail[B]` - specifies that the body should be deserialized to json, if the response is successful (2xx); throws an exception/returns a failed effect if the response code is other than 2xx, or if deserialization fails +* `asJsonAlways[B]` - specifies that the body should be deserialized to json, regardless of the status code +* `asJsonEither[E, B]` - specifies that the body should be deserialized to json, using different deserializers for error and successful (2xx) responses +* `asJsonEitherOrFail[E, B]` - specifies that the body should be deserialized to json, using different deserializers for error and successful (2xx) responses; throws an exception/returns a failed effect, if deserialization fails The type signatures vary depending on the underlying library (required implicits and error representation differs), but they obey the following pattern: ```scala import sttp.client4._ +// request bodies +def asJson[B](b: B): StringBody = ??? + +// response handling description def asJson[B]: ResponseAs[Either[ResponseException[String, Exception], B]] = ??? +def asJsonOrFail[B]: ResponseAs[B] = ??? def asJsonAlways[B]: ResponseAs[Either[DeserializationException[Exception], B]] = ??? def asJsonEither[E, B]: ResponseAs[Either[ResponseException[E, Exception], B]] = ??? +def asJsonEitherOrFail[E, B]: ResponseAs[Either[E, B]] = ??? ``` -The response specifications can be further refined using `.getRight` and `.getEither`, see [response body specifications](responses/body.md). +The response specifications can be further refined using `.orFail` and `.orFailDeserialization`, see [response body specifications](responses/body.md). Following data class will be used through the next few examples: @@ -34,7 +43,7 @@ case class ResponsePayload(data: String) JSON encoding of bodies and decoding of responses can be handled using [Circe](https://circe.github.io/circe/) by the `circe` module. To use add the following dependency to your project: ```scala -"com.softwaremill.sttp.client4" %% "circe" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "circe" % "4.0.0-M20" ``` This module adds a body serialized, so that json payloads can be sent as request bodies. To send a payload of type `T` as json, a `io.circe.Encoder[T]` implicit value must be available in scope. @@ -54,7 +63,7 @@ val requestPayload = RequestPayload("some data") val response: Response[Either[ResponseException[String, io.circe.Error], ResponsePayload]] = basicRequest .post(uri"...") - .body(requestPayload) + .body(asJson(requestPayload)) .response(asJson[ResponsePayload]) .send(backend) ``` @@ -66,7 +75,7 @@ Arbitrary JSON structures can be traversed by parsing the result as `io.circe.Js To encode and decode json using json4s, add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "json4s" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "json4s" % "4.0.0-M20" "org.json4s" %% "json4s-native" % "3.6.0" ``` @@ -90,7 +99,7 @@ implicit val formats = org.json4s.DefaultFormats val response: Response[Either[ResponseException[String, Exception], ResponsePayload]] = basicRequest .post(uri"...") - .body(requestPayload) + .body(asJson(requestPayload)) .response(asJson[ResponsePayload]) .send(backend) ``` @@ -100,7 +109,7 @@ val response: Response[Either[ResponseException[String, Exception], ResponsePayl To encode and decode JSON using [spray-json](https://github.com/spray/spray-json), add the following dependency to your project: ``` -"com.softwaremill.sttp.client4" %% "spray-json" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "spray-json" % "4.0.0-M20" ``` Using this module it is possible to set request bodies and read response bodies as your custom types, using the implicitly available instances of `spray.json.JsonWriter` / `spray.json.JsonReader` or `spray.json.JsonFormat`. @@ -122,7 +131,7 @@ val requestPayload = RequestPayload("some data") val response: Response[Either[ResponseException[String, Exception], ResponsePayload]] = basicRequest .post(uri"...") - .body(requestPayload) + .body(asJson(requestPayload)) .response(asJson[ResponsePayload]) .send(backend) ``` @@ -132,13 +141,13 @@ val response: Response[Either[ResponseException[String, Exception], ResponsePayl To encode and decode JSON using [play-json](https://www.playframework.com), add the following dependency to your project: ```scala -"com.softwaremill.sttp.client4" %% "play-json" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "play-json" % "4.0.0-M20" ``` If you use older version of play (2.9.x), add the following dependency to your project: ```scala -"com.softwaremill.sttp.client4" %% "play29-json" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "play29-json" % "4.0.0-M20" ``` To use, add an import: `import sttp.client4.playJson._`. @@ -150,13 +159,13 @@ To encode and decode JSON using the high-performance [zio-json](https://zio.gith The `zio-json` module depends on ZIO 2.x. For ZIO 1.x support, use `zio1-json`. ```scala -"com.softwaremill.sttp.client4" %% "zio-json" % "4.0.0-M19" // for ZIO 2.x -"com.softwaremill.sttp.client4" %% "zio1-json" % "4.0.0-M19" // for ZIO 1.x +"com.softwaremill.sttp.client4" %% "zio-json" % "4.0.0-M20" // for ZIO 2.x +"com.softwaremill.sttp.client4" %% "zio1-json" % "4.0.0-M20" // for ZIO 1.x ``` or for ScalaJS (cross build) projects: ```scala -"com.softwaremill.sttp.client4" %%% "zio-json" % "4.0.0-M19" // for ZIO 2.x -"com.softwaremill.sttp.client4" %%% "zio1-json" % "4.0.0-M19" // for ZIO 1.x +"com.softwaremill.sttp.client4" %%% "zio-json" % "4.0.0-M20" // for ZIO 2.x +"com.softwaremill.sttp.client4" %%% "zio1-json" % "4.0.0-M20" // for ZIO 1.x ``` To use, add an import: `import sttp.client4.ziojson._` (or extend `SttpZioJsonApi`), define an implicit `JsonCodec`, or `JsonDecoder`/`JsonEncoder` for your datatype. @@ -178,7 +187,7 @@ val requestPayload = RequestPayload("some data") val response: Response[Either[ResponseException[String, String], ResponsePayload]] = basicRequest .post(uri"...") - .body(requestPayload) + .body(asJson(requestPayload)) .response(asJson[ResponsePayload]) .send(backend) ``` @@ -188,13 +197,13 @@ basicRequest To encode and decode JSON using the [high(est)-performant](https://plokhotnyuk.github.io/jsoniter-scala/) [jsoniter-scala](https://github.com/plokhotnyuk/jsoniter-scala) library, one add the following dependency to your project. ```scala -"com.softwaremill.sttp.client4" %% "jsoniter" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "jsoniter" % "4.0.0-M20" ``` or for ScalaJS (cross build) projects: ```scala -"com.softwaremill.sttp.client4" %%% "jsoniter" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %%% "jsoniter" % "4.0.0-M20" ``` To use, add an import: `import sttp.client4.jsoniter._` (or extend `SttpJsonIterJsonApi`), define an implicit `JsonCodec`, or `JsonDecoder`/`JsonEncoder` for your datatype. @@ -218,7 +227,7 @@ val requestPayload = RequestPayload("some data") val response: Response[Either[ResponseException[String, Exception], ResponsePayload]] = basicRequest .post(uri"...") - .body(requestPayload) + .body(asJson(requestPayload)) .response(asJson[ResponsePayload]) .send(backend) ``` @@ -228,13 +237,13 @@ basicRequest To encode and decode JSON using the [uPickle](https://github.com/com-lihaoyi/upickle) library, add the following dependency to your project: ```scala -"com.softwaremill.sttp.client4" %% "upickle" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "upickle" % "4.0.0-M20" ``` or for ScalaJS (cross build) projects: ```scala -"com.softwaremill.sttp.client4" %%% "upickle" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %%% "upickle" % "4.0.0-M20" ``` To use, add an import: `import sttp.client4.upicklejson.default._` and define an implicit `ReadWriter` (or separately `Reader` and `Writer`) for your datatype. @@ -255,7 +264,7 @@ val requestPayload = RequestPayload("some data") val response: Response[Either[ResponseException[String, Exception], ResponsePayload]] = basicRequest .post(uri"...") - .body(requestPayload) + .body(asJson(requestPayload)) .response(asJson[ResponsePayload]) .send(backend) ``` diff --git a/generated-docs/out/openapi.md b/generated-docs/out/openapi.md index 1935a3b2a9..62917b525d 100644 --- a/generated-docs/out/openapi.md +++ b/generated-docs/out/openapi.md @@ -43,8 +43,8 @@ lazy val petstoreApi: Project = project openApiGeneratorName := "scala-sttp", openApiOutputDir := baseDirectory.value.name, libraryDependencies ++= Seq( - "com.softwaremill.sttp.client4" %% "core" % "4.0.0-M19", - "com.softwaremill.sttp.client4" %% "json4s" % "4.0.0-M19", + "com.softwaremill.sttp.client4" %% "core" % "4.0.0-M20", + "com.softwaremill.sttp.client4" %% "json4s" % "4.0.0-M20", "org.json4s" %% "json4s-jackson" % "3.6.8" ) ) @@ -94,8 +94,8 @@ lazy val petstoreApi: Project = project openApiOutputDir := baseDirectory.value.name, openApiIgnoreFileOverride := s"${baseDirectory.in(ThisBuild).value.getPath}/openapi-ignore-file", libraryDependencies ++= Seq( - "com.softwaremill.sttp.client4" %% "core" % "4.0.0-M19", - "com.softwaremill.sttp.client4" %% "json4s" % "4.0.0-M19", + "com.softwaremill.sttp.client4" %% "core" % "4.0.0-M20", + "com.softwaremill.sttp.client4" %% "json4s" % "4.0.0-M20", "org.json4s" %% "json4s-jackson" % "3.6.8" ), (compile in Compile) := ((compile in Compile) dependsOn openApiGenerate).value, diff --git a/generated-docs/out/quickstart.md b/generated-docs/out/quickstart.md index c50ed5d575..cbb04ea916 100644 --- a/generated-docs/out/quickstart.md +++ b/generated-docs/out/quickstart.md @@ -15,7 +15,7 @@ platforms, and that each has its own dedicated set of backends. The basic dependency which provides the API, together with a synchronous and `Future`-based backends, is: ```scala -"com.softwaremill.sttp.client4" %% "core" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "core" % "4.0.0-M20" ``` ## Using scala-cli @@ -23,7 +23,7 @@ The basic dependency which provides the API, together with a synchronous and `Fu Add the following directive to the top of your scala file to add the core sttp dependency: ``` -//> using dep "com.softwaremill.sttp.client4::core:4.0.0-M19" +//> using dep "com.softwaremill.sttp.client4::core:4.0.0-M20" ``` ## Using Ammonite @@ -31,7 +31,7 @@ Add the following directive to the top of your scala file to add the core sttp d If you are an [Ammonite](https://ammonite.io) user, you can quickly start experimenting with sttp by copy-pasting the following: ```scala -import $ivy.`com.softwaremill.sttp.client4::core:4.0.0-M19` +import $ivy.`com.softwaremill.sttp.client4::core:4.0.0-M20` ``` ## Imports @@ -72,7 +72,7 @@ As an example, to integrate with the [uPickle](https://github.com/com-lihaoyi/up dependency: ```scala -"com.softwaremill.sttp.client4" %% "upickle" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "upickle" % "4.0.0-M20" ``` Your code might then look as follows: @@ -93,7 +93,7 @@ implicit val responseRW: ReadWriter[HttpBinResponse] = macroRW[HttpBinResponse] val request = basicRequest .post(uri"https://httpbin.org/post") - .body(MyRequest("test", 42)) + .body(asJson(MyRequest("test", 42))) .response(asJson[HttpBinResponse]) val response = request.send(backend) @@ -109,7 +109,7 @@ Logging can be added using the [logging backend wrapper](backends/wrappers/loggi use slf4j, you'll need the following dependency: ``` -"com.softwaremill.sttp.client4" %% "slf4j-backend" % "4.0.0-M19" +"com.softwaremill.sttp.client4" %% "slf4j-backend" % "4.0.0-M20" ``` Then, you'll need to configure your client: diff --git a/generated-docs/out/requests/body.md b/generated-docs/out/requests/body.md index 7f05de54ef..037581877f 100644 --- a/generated-docs/out/requests/body.md +++ b/generated-docs/out/requests/body.md @@ -83,15 +83,11 @@ basicRequest.body("k1" -> "v1", "k2" -> "v2") basicRequest.body(Seq("k1" -> "v1", "k2" -> "v2"), "utf-8") ``` -## Custom body serializers +## Custom serializers -It is also possible to set custom types as request bodies, as long as there's an implicit `BodySerializer[B]` value in scope, which is simply an alias for a function: - -```scala -type BodySerializer[B] = B => BasicRequestBody -``` - -A `BasicRequestBody` is a wrapper for one of the supported request body types: a `String`/byte array or an input stream. +It is also possible to write custom serializers, which return arbitrary body representations. These should be +methods/functions which return instances of `BasicBody`, which is a wrapper for one of the supported request body +types: a `String`, byte array, an input stream, etc. For example, here's how to write a custom serializer for a case class, with serializer-specific default content type: @@ -101,12 +97,12 @@ import sttp.model.MediaType case class Person(name: String, surname: String, age: Int) // for this example, assuming names/surnames can't contain commas -implicit val personSerializer: BodySerializer[Person] = { p: Person => +def serializePerson(p: Person): BasicBody = { val serialized = s"${p.name},${p.surname},${p.age}" StringBody(serialized, "UTF-8", MediaType.TextCsv) } -basicRequest.body(Person("mary", "smith", 67)) +basicRequest.body(serializePerson(Person("mary", "smith", 67))) ``` -See the implementations of the `BasicRequestBody` trait for more options. +See the implementations of the `BasicBody` trait for more options. diff --git a/generated-docs/out/responses/basics.md b/generated-docs/out/responses/basics.md index a98cc7f171..caf8c04759 100644 --- a/generated-docs/out/responses/basics.md +++ b/generated-docs/out/responses/basics.md @@ -1,6 +1,6 @@ # Responses -Responses are represented as instances of the case class `Response[T]`, where `T` is the type of the response body. When sending a request, an effect containing the response will be returned. For example, for asynchronous backends, we can get a `Future[Response[T]]`, while for the default synchronous backend, the wrapper will be a no-op, `Identity`, which is the same as no wrapper at all. +Responses are represented as instances of the case class `Response[T]`, where `T` is the type of the response body. When sending a request, the response might be return directly, or wrapped with an effect containing the response. For example, for asynchronous backends, we can get a `Future[Response[T]]`, while for the default synchronous backend, there's no wrapper at all. If sending the request fails, either due to client or connection errors, an exception will be thrown (synchronous backends), or a failed effect will be returned (e.g. a failed future). @@ -50,5 +50,4 @@ If the cookies from a response should be set without changes on the request, thi ## Obtaining the response body -The response body can be obtained through the `.body: T` property. `T` is the body deserialized as specified in the request description - see -the next section on [response body specifications](body.md). +The response body can be obtained through the `.body: T` property. `T` is the type of the body to which it's deserialized, as specified in the request description - see the next section on [response body specifications](body.md). diff --git a/generated-docs/out/responses/body.md b/generated-docs/out/responses/body.md index d2da065e5e..e39cacb274 100644 --- a/generated-docs/out/responses/body.md +++ b/generated-docs/out/responses/body.md @@ -1,4 +1,4 @@ -# Response body specification +# Response body descriptions By default, the received response body will be read as a `Either[String, String]`, using the encoding specified in the `Content-Type` response header (and if none is specified, using `UTF-8`). This is of course configurable: response bodies can be ignored, deserialized into custom types, received as a stream or saved to a file. @@ -7,9 +7,9 @@ The default `response.body` will be a: * `Left(errorMessage)` if the request is successful, but response code is not 2xx. * `Right(body)` if the request is successful, and the response code is 2xx. -How the response body will be read is part of the request description, as already when sending the request, the backend needs to know what to do with the response. The type to which the response body should be deserialized is the second type parameter of `RequestT`, and stored in the request definition as the `request.response: ResponseAs[T, R]` property. +How the response body will be read is part of the request description, as already when sending the request, the backend needs to know what to do with the response. The type to which the response body should be deserialized is a type parameter of `Request`. It's used in request definition in the `request.response: ResponseAs[T]` property. -## Basic response specifications +## Basic response descriptions To conveniently specify how to deserialize the response body, a number of `as(...Type...)` methods are available. They can be used to provide a value for the request description's `response` property: @@ -21,7 +21,7 @@ basicRequest.response(asByteArray) When the above request is completely described and sent, it will result in a `Response[Either[String, Array[Byte]]]` (where the left and right correspond to non-2xx and 2xx status codes, as above). -Other possible response descriptions include (the first type parameter of `ResponseAs` specifies the type returned as the response body, the second - the capabilities that the backend is required to support to send the request; `Any` means no special requirements): +Other possible response descriptions include: ```scala import sttp.client4._ @@ -45,9 +45,9 @@ def asPath(path: Path): ResponseAs[Either[String, Path]] = ??? def asPathAlways(path: Path): ResponseAs[Path] = ??? def asEither[A, B](onError: ResponseAs[A], - onSuccess: ResponseAs[B]): ResponseAs[Either[A, B]] = ??? + onSuccess: ResponseAs[B]): ResponseAs[Either[A, B]] = ??? def fromMetadata[T](default: ResponseAs[T], - conditions: ConditionalResponseAs[T]*): ResponseAs[T] = ??? + conditions: ConditionalResponseAs[T]*): ResponseAs[T] = ??? def asBoth[A, B](l: ResponseAs[A], r: ResponseAs[B]): ResponseAs[(A, B)] = ??? def asBothOption[A, B](l: ResponseAs[A], r: ResponseAs[B]): ResponseAs[(A, Option[B])] = ??? @@ -79,12 +79,12 @@ basicRequest.response(asFile(someFile)) ## Failing when the response code is not 2xx -Sometimes it's convenient to get a failed effect (or an exception thrown) when the response status code is not successful. In such cases, the response specification can be modified using the `.getRight` combinator: +Sometimes it's convenient to get a failed effect (or an exception thrown) when the response status code is not successful. In such cases, the response description can be modified using the `.orFail` combinator: ```scala import sttp.client4._ -basicRequest.response(asString.getRight): PartialRequest[String] +basicRequest.response(asString.orFail): PartialRequest[String] ``` The combinator works in all cases where the response body is specified to be deserialized as an `Either`. If the left is already an exception, it will be thrown unchanged. Otherwise, the left-value will be wrapped in an `HttpError`. @@ -92,7 +92,7 @@ The combinator works in all cases where the response body is specified to be des ```eval_rst .. note:: - While both ``asStringAlways`` and ``asString.getRight`` have the type ``ResponseAs[String, Any]``, they are different. The first will return the response body as a string always, regardless of the responses' status code. The second will return a failed effect / throw a ``HttpError`` exception for non-2xx status codes, and the string as body only for 2xx status codes. + While both ``asStringAlways`` and ``asString.orFail`` have the type ``ResponseAs[String, Any]``, they are different. The first will return the response body as a string always, regardless of the responses' status code. The second will return a failed effect / throw a ``HttpError`` exception for non-2xx status codes, and the string as body only for 2xx status codes. ``` There's also a variant of the combinator, `.getEither`, which can be used to extract typed errors and fail the effect if there's a deserialization error. @@ -110,14 +110,14 @@ As an example, to read the response body as an int, the following response descr ```scala import sttp.client4._ -val asInt: ResponseAs[Either[String, Int]] = asString.mapRight(_.toInt) +val asInt: ResponseAs[Either[String, Int]] = asString.mapRight((_: String).toInt) basicRequest .get(uri"http://example.com") .response(asInt) ``` -To integrate with a third-party JSON library, and always parse the response as a json (regardless of the status code): +To integrate with a third-party JSON library, and always parse the response as JSON (regardless of the status code): ```scala import sttp.client4._ @@ -170,6 +170,7 @@ import io.circe._ import io.circe.generic.auto._ case class MyModel(p1: Int) + sealed trait MyErrorModel case class Conflict(message: String) extends MyErrorModel case class BadRequest(message: String) extends MyErrorModel @@ -184,13 +185,13 @@ basicRequest ### Blocking streaming (InputStream) -Some backends on the JVM support receiving the response body as a `java.io.InputStream`. This is possible either using the safe `asInputStream(f)` specification, where the entire stream has to be consumed by the provided `f` function, and is then closed by sttp client. Alternatively, there's `asInputStreamUnsafe`, which returns the stream directly to the user, who is then responsible for closing it. +Some backends on the JVM support receiving the response body as a `java.io.InputStream`. This is possible either using the safe `asInputStream(f)` description, where the entire stream has to be consumed by the provided `f` function, and is then closed by sttp client. Alternatively, there's `asInputStreamUnsafe`, which returns the stream directly to the user, who is then responsible for closing it. -`InputStream`s have two major limitations. First, they operate on the relatively low `byte`-level. The consumer is responsible for any decoding, chunking etc. Moreover, all `InputStream` operations are blocking, hence using them in a non-virtual-threads environment may severely limit performance. If you're using a functional effect system, see below on how to use non-blocking streams instead. +`InputStream`s have two limitations. First, they operate on the relatively low `byte`-level. The consumer is responsible for any decoding, chunking etc. Moreover, all `InputStream` operations are blocking, hence using them in a non-virtual-threads environment may severely limit performance. If you're using a functional effect system, see below on how to use non-blocking streams instead. ### Non-blocking streaming -If the backend used supports non-blocking streaming (see "Supported stream type" in the [backends summary](../backends/summary.md)), it's possible to receive responses as a stream. This can be described using the following methods: +If the backend used supports non-blocking, asynchronous streaming (see "Supported stream type" in the [backends summary](../backends/summary.md)), it's possible to receive responses as a stream. This can be described using the following methods: ```scala import sttp.capabilities.{Effect, Streams} @@ -218,7 +219,7 @@ def asStreamUnsafeAlways[S](s: Streams[S]): StreamResponseAs[s.BinaryStream, S] = ??? ``` -All of these specifications require the streaming capability to be passed as a parameter, an implementation of `Streams[S]`. This is used to determine the type of binary streams that are supported, and to require that the backend used to send the request supports the given type of streams. These implementations are provided by the backend implementations, e.g. `AkkaStreams` or `Fs2Streams[F]`. +All of these descriptions require the streaming capability to be passed as a parameter, an implementation of `Streams[S]`. This is used to determine the type of binary streams that are supported, and to require that the backend used to send the request supports the given type of streams. These implementations are provided by the backend implementations, e.g. `AkkaStreams` or `Fs2Streams[F]`. The first two "safe" variants pass the response stream to the user-provided function, which should consume the stream entirely. Once the effect returned by the function is complete, the backend will try to close the stream (if the streaming implementation allows it). @@ -243,5 +244,4 @@ val response: Future[Response[Either[String, Source[ByteString, Any]]]] = .send(backend) ``` -It's also possible to parse the received stream as server-sent events (SSE), using an implementation-specific mapping -function. Refer to the documentation for particular backends for more details. +It's also possible to parse the received stream as server-sent events (SSE), using an implementation-specific mapping function. Refer to the documentation for particular backends for more details. diff --git a/generated-docs/out/responses/exceptions.md b/generated-docs/out/responses/exceptions.md index dbf07da8fc..ec206538b2 100644 --- a/generated-docs/out/responses/exceptions.md +++ b/generated-docs/out/responses/exceptions.md @@ -28,7 +28,7 @@ import sttp.client4._ def asJson[T]: ResponseAs[Either[ResponseException[String, Exception], T]] = ??? ``` -There are also the `.getRight` and `.getEither` methods on eligible response specifications, which convert http errors or deserialization exceptions as failed effects. +There are also the `.orFail` and `.orFailDeserialization` methods on eligible response specifications, which convert http errors or deserialization exceptions as failed effects. ## Possible outcomes diff --git a/generated-docs/out/testing.md b/generated-docs/out/testing.md index 99fc243fb4..37f236022b 100644 --- a/generated-docs/out/testing.md +++ b/generated-docs/out/testing.md @@ -285,12 +285,12 @@ backend.whenAnyRequest.thenRespond(webSocketStub) There is a possiblity to add error responses as well. If this is not enough, using a custom implementation of the `WebSocket` trait is recommended. -## Verifying, that a request was sent +## Verifying that a request was sent Using `RecordingSttpBackend` it's possible to capture all interactions in which a backend has been involved. The recording backend is a [backend wrapper](backends/wrappers/custom.md), and it can wrap any backend, but it's most -useful when combine with the backend stub. +useful when combined with the backend stub. Example usage: diff --git a/generated-docs/out/websockets.md b/generated-docs/out/websockets.md index 9fd5ee5a35..6da0bf5566 100644 --- a/generated-docs/out/websockets.md +++ b/generated-docs/out/websockets.md @@ -104,7 +104,7 @@ as Ox `Source` and `Sink`: ``` // sbt dependency -"com.softwaremill.sttp.client4" %% "ox" % "4.0.0-M19", +"com.softwaremill.sttp.client4" %% "ox" % "4.0.0-M20", ``` ```scala diff --git a/generated-docs/out/xml.md b/generated-docs/out/xml.md index c5286377b9..96bea7e2e1 100644 --- a/generated-docs/out/xml.md +++ b/generated-docs/out/xml.md @@ -12,7 +12,7 @@ After code generation, create the `SttpScalaxbApi` trait (or trait with another import generated.defaultScope // import may differ depending on location of generated code import scalaxb.`package`.{fromXML, toXML} // import may differ depending on location of generated code import scalaxb.{CanWriteXML, XMLFormat} // import may differ depending on location of generated code -import sttp.client4.{BodySerializer, ResponseAs, ResponseException, StringBody, asString} +import sttp.client4.{ResponseAs, ResponseException, StringBody, asString} import sttp.model.MediaType import scala.xml.{NodeSeq, XML} @@ -20,7 +20,7 @@ import scala.xml.{NodeSeq, XML} trait SttpScalaxbApi { case class XmlElementLabel(label: String) - implicit def scalaxbBodySerializer[B](implicit format: CanWriteXML[B], label: XmlElementLabel): BodySerializer[B] = { (b: B) => + def asXml[B](b: B)(implicit format: CanWriteXML[B], label: XmlElementLabel): StringBody = { val nodeSeq: NodeSeq = toXML[B](obj = b, elementLabel = label.label, scope = defaultScope) StringBody(nodeSeq.toString(), "utf-8", MediaType.ApplicationXml) } @@ -38,11 +38,11 @@ trait SttpScalaxbApi { .showAs("either(as string, as xml)") } ``` -This would add `BodySerializer` needed for serialization and `asXml` method needed for deserialization. Please notice, that `fromXML`, `toXML`, `CanWriteXML`, `XMLFormat` and `defaultScope` are members of code generated by scalaxb. +This would add `asXml` methods needed for serialization and deserialization. Please notice, that `fromXML`, `toXML`, `CanWriteXML`, `XMLFormat` and `defaultScope` are members of code generated by scalaxb. + +Next to this trait, you might want to introduce `sttpScalaxb` package object to simplify imports. -Next to this trait, you might want to introduce `sttpScalaxb` -package object to simplify imports. ```scala package object sttpScalaxb extends SttpScalaxbApi ``` @@ -50,6 +50,7 @@ package object sttpScalaxb extends SttpScalaxbApi From now on, XML serialization/deserialization would work for all classes generated from `.xsd` file as long as `XMLFormat` for the type in the question and `XmlElementLabel` for the top XML node would be implicitly provided in the scope. Usage example: + ```scala val backend: SyncBackend = DefaultSyncBackend() val requestPayload = Outer(Inner(42, b = true, "horses"), "cats") // `Outer` and `Inner` classes are generated by scalaxb from xsd file @@ -61,7 +62,7 @@ import generated.Generated_OuterFormat // imports member of code generated by sc val response: Response[Either[ResponseException[String, Exception], Outer]] = basicRequest .post(uri"...") - .body(requestPayload) + .body(asXml(requestPayload)) .response(asXml[Outer]) .send(backend) ``` \ No newline at end of file