diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 10cbb89d84..07c077e9ba 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -20,3 +20,6 @@ a5e39d1f4d49c7e32a9b834ebbf429f8f5cab5ee # Scala Steward: Reformat with scalafmt 3.8.1 19891560efe4b9262b4b4079e5ca95dbbea1a906 + +# Scala Steward: Reformat with scalafmt 3.8.2 +be3cd01c8ea70ee2383d0d05ab8d129f67b70c28 diff --git a/.scalafmt.conf b/.scalafmt.conf index c319e26303..225bee0752 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = 3.8.1 +version = 3.8.2 maxColumn = 120 rewrite.rules = [RedundantBraces, RedundantParens, SortImports] runner.dialect = scala3 diff --git a/core/src/main/scala/sttp/client4/package.scala b/core/src/main/scala/sttp/client4/package.scala index 47a6ed9e3e..e536215814 100644 --- a/core/src/main/scala/sttp/client4/package.scala +++ b/core/src/main/scala/sttp/client4/package.scala @@ -1,6 +1,7 @@ package sttp package object client4 extends SttpApi { + /** Provide an implicit value of this type to serialize arbitrary classes into a request body. Backends might also * provide special logic for serializer instances which they define (e.g. to handle streaming). */ diff --git a/core/src/main/scala/sttp/client4/requestBuilder.scala b/core/src/main/scala/sttp/client4/requestBuilder.scala index 3c33096850..36d7f0d22d 100644 --- a/core/src/main/scala/sttp/client4/requestBuilder.scala +++ b/core/src/main/scala/sttp/client4/requestBuilder.scala @@ -254,17 +254,17 @@ trait PartialRequestBuilder[+PR <: PartialRequestBuilder[PR, R], +R] def followRedirects(fr: Boolean): PR = withOptions(options.copy(followRedirects = fr)) def maxRedirects(n: Int): PR = - if (n <= 0) withOptions(options.copy(followRedirects = false)) - else withOptions(options.copy(followRedirects = true, maxRedirects = n)) - - /** When a POST or PUT request is redirected, should the redirect be a POST/PUT as well (with the original body), or - * should the request be converted to a GET without a body. - * - * Note that this only affects 301 and 302 redirects. 303 redirects are always converted, while 307 and 308 - * redirects always keep the same method. - * - * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections for details. - */ + if (n <= 0) withOptions(options.copy(followRedirects = false)) + else withOptions(options.copy(followRedirects = true, maxRedirects = n)) + + /** When a POST or PUT request is redirected, should the redirect be a POST/PUT as well (with the original body), or + * should the request be converted to a GET without a body. + * + * Note that this only affects 301 and 302 redirects. 303 redirects are always converted, while 307 and 308 redirects + * always keep the same method. + * + * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections for details. + */ def redirectToGet(r: Boolean): PR = withOptions(options.copy(redirectToGet = r)) def tag(k: String, v: Any): PR = withTags(tags + (k -> v)) diff --git a/core/src/main/scala/sttp/client4/testing/ResponseStub.scala b/core/src/main/scala/sttp/client4/testing/ResponseStub.scala index 29f66f0902..89d699e1a7 100644 --- a/core/src/main/scala/sttp/client4/testing/ResponseStub.scala +++ b/core/src/main/scala/sttp/client4/testing/ResponseStub.scala @@ -13,26 +13,26 @@ object ResponseStub { } /** Convenience method to create a Response instance, mainly useful in tests using - * [[sttp.client4.testing.BackendStub]] and partial matchers. - */ + * [[sttp.client4.testing.BackendStub]] and partial matchers. + */ def apply[T](body: T, code: StatusCode): Response[T] = Response(body, code, resolveStatusText(code), Nil, Nil, emptyGet) /** Convenience method to create a Response instance, mainly useful in tests using - * [[sttp.client4.testing.BackendStub]] and partial matchers. - */ + * [[sttp.client4.testing.BackendStub]] and partial matchers. + */ def apply[T](body: T, code: StatusCode, statusText: String): Response[T] = Response(body, code, resolveStatusText(code, statusText), Nil, Nil, emptyGet) /** Convenience method to create a Response instance, mainly useful in tests using - * [[sttp.client4.testing.BackendStub]] and partial matchers. - */ + * [[sttp.client4.testing.BackendStub]] and partial matchers. + */ def apply[T](body: T, code: StatusCode, statusText: String, headers: Seq[Header]): Response[T] = Response(body, code, resolveStatusText(code, statusText), headers, Nil, emptyGet) /** Convenience method to create a Response instance, mainly useful in tests using - * [[sttp.client4.testing.BackendStub]] and partial matchers. - */ + * [[sttp.client4.testing.BackendStub]] and partial matchers. + */ def ok[T](body: T): Response[T] = apply(body, StatusCode.Ok) private def resolveStatusText(statusCode: StatusCode, provided: String = ""): String = diff --git a/core/src/main/scala/sttp/client4/wrappers/ResolveRelativeUrisBackend.scala b/core/src/main/scala/sttp/client4/wrappers/ResolveRelativeUrisBackend.scala index cee48a160e..36c2238cba 100644 --- a/core/src/main/scala/sttp/client4/wrappers/ResolveRelativeUrisBackend.scala +++ b/core/src/main/scala/sttp/client4/wrappers/ResolveRelativeUrisBackend.scala @@ -1,7 +1,16 @@ package sttp.client4.wrappers import sttp.capabilities.Effect -import sttp.client4.{Backend, GenericBackend, GenericRequest, Response, StreamBackend, SyncBackend, WebSocketBackend, WebSocketStreamBackend} +import sttp.client4.{ + Backend, + GenericBackend, + GenericRequest, + Response, + StreamBackend, + SyncBackend, + WebSocketBackend, + WebSocketStreamBackend +} import sttp.model.Uri import sttp.monad.syntax._ import sttp.shared.Identity diff --git a/core/src/main/scala/sttp/client4/wrappers/TryBackend.scala b/core/src/main/scala/sttp/client4/wrappers/TryBackend.scala index 8491a9e67d..767c893bfa 100644 --- a/core/src/main/scala/sttp/client4/wrappers/TryBackend.scala +++ b/core/src/main/scala/sttp/client4/wrappers/TryBackend.scala @@ -1,7 +1,14 @@ package sttp.client4.wrappers import sttp.client4.monad.FunctionK -import sttp.client4.{Backend, StreamBackend, SyncBackend, WebSocketBackend, WebSocketStreamBackend, WebSocketSyncBackend} +import sttp.client4.{ + Backend, + StreamBackend, + SyncBackend, + WebSocketBackend, + WebSocketStreamBackend, + WebSocketSyncBackend +} import sttp.monad.TryMonad import sttp.shared.Identity diff --git a/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientSyncBackend.scala b/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientSyncBackend.scala index 54c91da181..f5e8661138 100644 --- a/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientSyncBackend.scala +++ b/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientSyncBackend.scala @@ -5,9 +5,9 @@ import sttp.client4.httpclient.HttpClientBackend.EncodingHandler import sttp.client4.httpclient.HttpClientSyncBackend.SyncEncodingHandler import sttp.client4.internal.httpclient._ import sttp.client4.internal.ws.{SimpleQueue, SyncQueue, WebSocketEvent} -import sttp.client4.internal.{NoStreams, emptyInputStream} +import sttp.client4.internal.{emptyInputStream, NoStreams} import sttp.client4.testing.WebSocketSyncBackendStub -import sttp.client4.{BackendOptions, GenericRequest, Response, WebSocketSyncBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, GenericRequest, Response, WebSocketSyncBackend} import sttp.model.StatusCode import sttp.monad.{IdentityMonad, MonadError} import sttp.shared.Identity diff --git a/core/src/main/scalajvm/sttp/client4/httpurlconnection/HttpURLConnectionBackend.scala b/core/src/main/scalajvm/sttp/client4/httpurlconnection/HttpURLConnectionBackend.scala index 2b11137fe3..23958292fa 100644 --- a/core/src/main/scalajvm/sttp/client4/httpurlconnection/HttpURLConnectionBackend.scala +++ b/core/src/main/scalajvm/sttp/client4/httpurlconnection/HttpURLConnectionBackend.scala @@ -5,7 +5,25 @@ import sttp.client4.httpurlconnection.HttpURLConnectionBackend.EncodingHandler import sttp.client4.internal._ import sttp.client4.testing.SyncBackendStub import sttp.client4.ws.{GotAWebSocketException, NotAWebSocketException} -import sttp.client4.{BackendOptions, BasicBodyPart, BasicMultipartBody, ByteArrayBody, ByteBufferBody, FileBody, GenericRequest, GenericWebSocketResponseAs, InputStreamBody, MultipartStreamBody, NoBody, Response, StreamBody, StringBody, SttpClientException, SyncBackend, wrappers} +import sttp.client4.{ + wrappers, + BackendOptions, + BasicBodyPart, + BasicMultipartBody, + ByteArrayBody, + ByteBufferBody, + FileBody, + GenericRequest, + GenericWebSocketResponseAs, + InputStreamBody, + MultipartStreamBody, + NoBody, + Response, + StreamBody, + StringBody, + SttpClientException, + SyncBackend +} import sttp.model._ import sttp.monad.{IdentityMonad, MonadError} import sttp.shared.Identity diff --git a/core/src/test/scala/sttp/client4/testing/BackendStubTests.scala b/core/src/test/scala/sttp/client4/testing/BackendStubTests.scala index 93ecaa4b03..53a5a87acb 100644 --- a/core/src/test/scala/sttp/client4/testing/BackendStubTests.scala +++ b/core/src/test/scala/sttp/client4/testing/BackendStubTests.scala @@ -38,7 +38,11 @@ class BackendStubTests extends AnyFlatSpec with Matchers with ScalaFutures { .thenRespondF(ResponseStub(Right("OK from monad"), StatusCode.Ok, "OK")) .whenRequestMatches(_.uri.port.exists(_ == 8081)) .thenRespondF(r => - ResponseStub(Right(s"OK from request. Request was sent to host: ${r.uri.host.getOrElse("?")}"), StatusCode.Ok, "OK") + ResponseStub( + Right(s"OK from request. Request was sent to host: ${r.uri.host.getOrElse("?")}"), + StatusCode.Ok, + "OK" + ) ) .whenRequestMatches(r => r.uri.path.contains("metadata") && r.method == Method.POST) .thenRespondOk() diff --git a/core/src/test/scala/sttp/client4/testing/HttpTest.scala b/core/src/test/scala/sttp/client4/testing/HttpTest.scala index fd78e9e5b0..90c2c62732 100644 --- a/core/src/test/scala/sttp/client4/testing/HttpTest.scala +++ b/core/src/test/scala/sttp/client4/testing/HttpTest.scala @@ -451,7 +451,6 @@ trait HttpTest[F[_]] "multipart" - { def mp = basicRequest.post(uri"$endpoint/multipart").response(asStringAlways) - "not encode tilde" in { val part = multipart("v1", Map("k1" -> "v1~", "~k2" -> "v2")) part.body.show should be("string: k1=v1~&~k2=v2") diff --git a/core/src/test/scalanative/sttp/client4/testing/SyncHttpTest.scala b/core/src/test/scalanative/sttp/client4/testing/SyncHttpTest.scala index 622c16e641..26f900263c 100644 --- a/core/src/test/scalanative/sttp/client4/testing/SyncHttpTest.scala +++ b/core/src/test/scalanative/sttp/client4/testing/SyncHttpTest.scala @@ -388,7 +388,7 @@ trait SyncHttpTest .get(uri"$endpoint/timeout") .readTimeout(200.milliseconds) .response(asString) - val caught = intercept[RuntimeException] { req.send(backend) } + val caught = intercept[RuntimeException](req.send(backend)) caught.getMessage should include("TIMEDOUT") } diff --git a/effects/ox/src/test/scala/sttp/client4/impl/ox/ws/OxSseTest.scala b/effects/ox/src/test/scala/sttp/client4/impl/ox/ws/OxSseTest.scala index 07df17f912..1bacd78f37 100644 --- a/effects/ox/src/test/scala/sttp/client4/impl/ox/ws/OxSseTest.scala +++ b/effects/ox/src/test/scala/sttp/client4/impl/ox/ws/OxSseTest.scala @@ -24,12 +24,12 @@ class OxServerSentEventsTest extends AnyFlatSpec with Matchers with BeforeAndAft .post(uri"$endpoint/sse/echo3") .body(sseData) .response(asInputStreamAlways { is => - OxServerSentEvents.parse(is).take(3).toList shouldBe(expectedEvents) + OxServerSentEvents.parse(is).take(3).toList shouldBe expectedEvents () }) .send(backend) } - + override protected def afterAll(): Unit = backend.close() super.afterAll() diff --git a/effects/ox/src/test/scala/sttp/client4/impl/ox/ws/OxWebSocketsTest.scala b/effects/ox/src/test/scala/sttp/client4/impl/ox/ws/OxWebSocketsTest.scala index 3711cc2913..8f8de78a77 100644 --- a/effects/ox/src/test/scala/sttp/client4/impl/ox/ws/OxWebSocketsTest.scala +++ b/effects/ox/src/test/scala/sttp/client4/impl/ox/ws/OxWebSocketsTest.scala @@ -97,7 +97,7 @@ class OxWebSocketTest extends AnyFlatSpec with BeforeAndAfterAll with Matchers w .thenRespond { case WebSocketFrame.Pong(payload) if new String(payload) == "test-ping" => List(WebSocketFrame.text("test")) - case other => + case other => fail(s"Unexpected frame: $other") }, StatusCode.SwitchingProtocols diff --git a/effects/zio1/src/test/scalajvm/sttp/client4/httpclient/zio/BackendStubZioTests.scala b/effects/zio1/src/test/scalajvm/sttp/client4/httpclient/zio/BackendStubZioTests.scala index a2ca0583dc..92612bf61d 100644 --- a/effects/zio1/src/test/scalajvm/sttp/client4/httpclient/zio/BackendStubZioTests.scala +++ b/effects/zio1/src/test/scalajvm/sttp/client4/httpclient/zio/BackendStubZioTests.scala @@ -5,7 +5,7 @@ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import sttp.client4._ import sttp.client4.impl.zio._ -import sttp.client4.testing.{BackendStub, RawStream, StreamBackendStub, ResponseStub, TestStreams} +import sttp.client4.testing.{BackendStub, RawStream, ResponseStub, StreamBackendStub, TestStreams} import sttp.model.Method import zio.stream.ZStream import zio.{Task, ZIO} diff --git a/json/json4s/src/test/scala/sttp/client4/BackendStubJson4sTests.scala b/json/json4s/src/test/scala/sttp/client4/BackendStubJson4sTests.scala index c4de8164e7..01f4ebffe1 100644 --- a/json/json4s/src/test/scala/sttp/client4/BackendStubJson4sTests.scala +++ b/json/json4s/src/test/scala/sttp/client4/BackendStubJson4sTests.scala @@ -6,7 +6,7 @@ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import sttp.client4.testing.SyncBackendStub import sttp.model.Uri -import org.json4s.{DefaultFormats, native} +import org.json4s.{native, DefaultFormats} case class Person(name: String) diff --git a/json/json4s/src/test/scala/sttp/client4/Json4sTests.scala b/json/json4s/src/test/scala/sttp/client4/Json4sTests.scala index 5a0e190638..cf8d34c851 100644 --- a/json/json4s/src/test/scala/sttp/client4/Json4sTests.scala +++ b/json/json4s/src/test/scala/sttp/client4/Json4sTests.scala @@ -3,7 +3,7 @@ package sttp.client4 import org.json4s.JsonAST.JString import org.json4s.ParserUtil.ParseException import org.json4s.native.Serialization -import org.json4s.{DefaultFormats, JField, JObject, MappingException, native} +import org.json4s.{native, DefaultFormats, JField, JObject, MappingException} import org.scalatest._ import sttp.client4.internal._ import sttp.model._ diff --git a/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsBackend.scala b/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsBackend.scala index 742fea84cb..e4eaede436 100644 --- a/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsBackend.scala +++ b/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsBackend.scala @@ -16,7 +16,7 @@ object OpenTelemetryMetricsBackend { Metrics names and model for Open Telemetry is based on these two specifications: https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#http-client https://github.com/open-telemetry/opentelemetry-specification/blob/v1.31.0/specification/metrics/api.md#instrument - * */ + * */ val DefaultLatencyHistogramName = "http.client.request.duration" val DefaultRequestSizeHistogramName = "http.client.request.size.bytes" val DefaultResponseSizeHistogramName = "http.client.response.size.bytes" diff --git a/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsConfig.scala b/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsConfig.scala index 24d7dd70d6..b6ac102120 100644 --- a/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsConfig.scala +++ b/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsConfig.scala @@ -24,8 +24,15 @@ object OpenTelemetryMetricsConfig { openTelemetry: OpenTelemetry, meterConfig: MeterConfig = MeterConfig.Default, clock: Clock = Clock.systemUTC(), - requestToLatencyHistogramMapper: GenericRequest[_, _] => Option[HistogramCollectorConfig] = (_: GenericRequest[_, _]) => - Some(HistogramCollectorConfig(DefaultLatencyHistogramName, buckets = HistogramCollectorConfig.DefaultLatencyBuckets, unit = HistogramCollectorConfig.Milliseconds)), + requestToLatencyHistogramMapper: GenericRequest[_, _] => Option[HistogramCollectorConfig] = + (_: GenericRequest[_, _]) => + Some( + HistogramCollectorConfig( + DefaultLatencyHistogramName, + buckets = HistogramCollectorConfig.DefaultLatencyBuckets, + unit = HistogramCollectorConfig.Milliseconds + ) + ), requestToInProgressCounterMapper: GenericRequest[_, _] => Option[CollectorConfig] = (_: GenericRequest[_, _]) => Some(CollectorConfig(DefaultRequestsActiveCounterName)), responseToSuccessCounterMapper: Response[_] => Option[CollectorConfig] = (_: Response[_]) => @@ -34,10 +41,23 @@ object OpenTelemetryMetricsConfig { Some(CollectorConfig(DefaultErrorCounterName)), requestToFailureCounterMapper: (GenericRequest[_, _], Throwable) => Option[CollectorConfig] = (_: GenericRequest[_, _], _: Throwable) => Some(CollectorConfig(DefaultFailureCounterName)), - requestToSizeHistogramMapper: GenericRequest[_, _] => Option[HistogramCollectorConfig] = (_: GenericRequest[_, _]) => - Some(HistogramCollectorConfig(DefaultRequestSizeHistogramName, buckets = HistogramCollectorConfig.DefaultSizeBuckets, unit = HistogramCollectorConfig.Bytes)), + requestToSizeHistogramMapper: GenericRequest[_, _] => Option[HistogramCollectorConfig] = + (_: GenericRequest[_, _]) => + Some( + HistogramCollectorConfig( + DefaultRequestSizeHistogramName, + buckets = HistogramCollectorConfig.DefaultSizeBuckets, + unit = HistogramCollectorConfig.Bytes + ) + ), responseToSizeHistogramMapper: Response[_] => Option[HistogramCollectorConfig] = (_: Response[_]) => - Some(HistogramCollectorConfig(DefaultResponseSizeHistogramName, buckets = HistogramCollectorConfig.DefaultSizeBuckets, unit = HistogramCollectorConfig.Bytes)), + Some( + HistogramCollectorConfig( + DefaultResponseSizeHistogramName, + buckets = HistogramCollectorConfig.DefaultSizeBuckets, + unit = HistogramCollectorConfig.Bytes + ) + ), ): OpenTelemetryMetricsConfig = usingMeter( openTelemetry.meterBuilder(meterConfig.name).setInstrumentationVersion(meterConfig.version).build(), clock, @@ -53,8 +73,15 @@ object OpenTelemetryMetricsConfig { def usingMeter( meter: Meter, clock: Clock = Clock.systemUTC(), - requestToLatencyHistogramMapper: GenericRequest[_, _] => Option[HistogramCollectorConfig] = (_: GenericRequest[_, _]) => - Some(HistogramCollectorConfig(DefaultLatencyHistogramName, buckets = HistogramCollectorConfig.DefaultLatencyBuckets, unit = HistogramCollectorConfig.Milliseconds)), + requestToLatencyHistogramMapper: GenericRequest[_, _] => Option[HistogramCollectorConfig] = + (_: GenericRequest[_, _]) => + Some( + HistogramCollectorConfig( + DefaultLatencyHistogramName, + buckets = HistogramCollectorConfig.DefaultLatencyBuckets, + unit = HistogramCollectorConfig.Milliseconds + ) + ), requestToInProgressCounterMapper: GenericRequest[_, _] => Option[CollectorConfig] = (_: GenericRequest[_, _]) => Some(CollectorConfig(DefaultRequestsActiveCounterName)), responseToSuccessCounterMapper: Response[_] => Option[CollectorConfig] = (_: Response[_]) => @@ -63,10 +90,23 @@ object OpenTelemetryMetricsConfig { Some(CollectorConfig(DefaultErrorCounterName)), requestToFailureCounterMapper: (GenericRequest[_, _], Throwable) => Option[CollectorConfig] = (_: GenericRequest[_, _], _: Throwable) => Some(CollectorConfig(DefaultFailureCounterName)), - requestToSizeHistogramMapper: GenericRequest[_, _] => Option[HistogramCollectorConfig] = (_: GenericRequest[_, _]) => - Some(HistogramCollectorConfig(DefaultRequestSizeHistogramName, buckets = HistogramCollectorConfig.DefaultSizeBuckets, unit = HistogramCollectorConfig.Bytes)), + requestToSizeHistogramMapper: GenericRequest[_, _] => Option[HistogramCollectorConfig] = + (_: GenericRequest[_, _]) => + Some( + HistogramCollectorConfig( + DefaultRequestSizeHistogramName, + buckets = HistogramCollectorConfig.DefaultSizeBuckets, + unit = HistogramCollectorConfig.Bytes + ) + ), responseToSizeHistogramMapper: Response[_] => Option[HistogramCollectorConfig] = (_: Response[_]) => - Some(HistogramCollectorConfig(DefaultResponseSizeHistogramName, buckets = HistogramCollectorConfig.DefaultSizeBuckets, unit = HistogramCollectorConfig.Bytes)) + Some( + HistogramCollectorConfig( + DefaultResponseSizeHistogramName, + buckets = HistogramCollectorConfig.DefaultSizeBuckets, + unit = HistogramCollectorConfig.Bytes + ) + ) ): OpenTelemetryMetricsConfig = OpenTelemetryMetricsConfig( meter, diff --git a/observability/opentelemetry-metrics-backend/src/test/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsBackendTest.scala b/observability/opentelemetry-metrics-backend/src/test/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsBackendTest.scala index b73298cde6..995cc97f48 100644 --- a/observability/opentelemetry-metrics-backend/src/test/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsBackendTest.scala +++ b/observability/opentelemetry-metrics-backend/src/test/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsBackendTest.scala @@ -8,7 +8,7 @@ import org.scalatest.OptionValues import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import sttp.client4.testing.{ResponseStub, SyncBackendStub} -import sttp.client4.{DeserializationException, SttpClientException, UriContext, asString, basicRequest} +import sttp.client4.{asString, basicRequest, DeserializationException, SttpClientException, UriContext} import sttp.model.{Header, StatusCode} import scala.collection.JavaConverters._ diff --git a/observability/opentelemetry-tracing-zio-backend/src/test/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackendTest.scala b/observability/opentelemetry-tracing-zio-backend/src/test/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackendTest.scala index 70ed2a4b02..b52d76f4bf 100644 --- a/observability/opentelemetry-tracing-zio-backend/src/test/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackendTest.scala +++ b/observability/opentelemetry-tracing-zio-backend/src/test/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackendTest.scala @@ -8,7 +8,7 @@ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import sttp.client4.impl.zio.{RIOMonadAsyncError, ZioTestBase} import sttp.client4.testing.{BackendStub, ResponseStub} -import sttp.client4.{Backend, GenericRequest, Response, UriContext, basicRequest} +import sttp.client4.{basicRequest, Backend, GenericRequest, Response, UriContext} import sttp.model.StatusCode import zio.{Runtime, Task, Unsafe, ZIO} import zio.telemetry.opentelemetry.Tracing diff --git a/observability/prometheus-backend/src/main/scala/sttp/client4/prometheus/PrometheusBackend.scala b/observability/prometheus-backend/src/main/scala/sttp/client4/prometheus/PrometheusBackend.scala index 7961d752b9..9405ad68db 100644 --- a/observability/prometheus-backend/src/main/scala/sttp/client4/prometheus/PrometheusBackend.scala +++ b/observability/prometheus-backend/src/main/scala/sttp/client4/prometheus/PrometheusBackend.scala @@ -18,7 +18,7 @@ object PrometheusBackend { Metrics names and model for Prometheus is based on these two specifications: https://prometheus.io/docs/practices/naming/ https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels - * */ + * */ val DefaultHistogramName = "http_client_request_duration_seconds" val DefaultRequestSizeName = "http_client_request_size_bytes" val DefaultResponseSizeName = "http_client_response_size_bytes" @@ -138,7 +138,10 @@ object PrometheusBackend { Hence, we need to store a global cache o created histograms/gauges, so that we can properly re-use them. */ - private def clear[T <: Collector](prometheusRegistry: PrometheusRegistry, collectors: ConcurrentHashMap[PrometheusRegistry, ConcurrentHashMap[String, T]]): Unit = { + private def clear[T <: Collector]( + prometheusRegistry: PrometheusRegistry, + collectors: ConcurrentHashMap[PrometheusRegistry, ConcurrentHashMap[String, T]] + ): Unit = { collectors .getOrDefault(prometheusRegistry, new ConcurrentHashMap[String, T]()) .values @@ -155,12 +158,12 @@ object PrometheusBackend { cache: ConcurrentHashMap[PrometheusRegistry, ConcurrentHashMap[String, T]], prometheusRegistry: PrometheusRegistry ): ConcurrentHashMap[String, T] = - cache.computeIfAbsent( - prometheusRegistry, - new java.util.function.Function[PrometheusRegistry, ConcurrentHashMap[String, T]] { - override def apply(t: PrometheusRegistry): ConcurrentHashMap[String, T] = new ConcurrentHashMap[String, T]() - } - ) + cache.computeIfAbsent( + prometheusRegistry, + new java.util.function.Function[PrometheusRegistry, ConcurrentHashMap[String, T]] { + override def apply(t: PrometheusRegistry): ConcurrentHashMap[String, T] = new ConcurrentHashMap[String, T]() + } + ) final case class RequestCollectors(maybeTimer: Option[Timer], maybeGauge: Option[GaugeDataPoint]) } @@ -319,7 +322,11 @@ trait BaseCollectorConfig { /** Represents the name of a collector, together with label names and values. The same labels must be always returned, * and in the same order. */ -case class CollectorConfig(collectorName: String, description: Option[String] = None, labels: List[(String, String)] = Nil) extends BaseCollectorConfig { +case class CollectorConfig( + collectorName: String, + description: Option[String] = None, + labels: List[(String, String)] = Nil +) extends BaseCollectorConfig { override type T = CollectorConfig override def addLabels(lbs: List[(String, String)]): CollectorConfig = copy(labels = labels ++ lbs) override def help: String = description.getOrElse(collectorName) diff --git a/observability/prometheus-backend/src/test/scala/sttp/client4/prometheus/PrometheusBackendTest.scala b/observability/prometheus-backend/src/test/scala/sttp/client4/prometheus/PrometheusBackendTest.scala index fefa1a74f3..81cf153cc7 100644 --- a/observability/prometheus-backend/src/test/scala/sttp/client4/prometheus/PrometheusBackendTest.scala +++ b/observability/prometheus-backend/src/test/scala/sttp/client4/prometheus/PrometheusBackendTest.scala @@ -17,7 +17,7 @@ import sttp.model.{Header, StatusCode} import java.util.concurrent.CountDownLatch import java.util.stream.Collectors import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.{Future, blocking} +import scala.concurrent.{blocking, Future} import scala.collection.immutable.Seq class PrometheusBackendTest @@ -118,8 +118,12 @@ class PrometheusBackendTest // then getMetricSnapshot(s"${PrometheusBackend.DefaultHistogramName}") shouldBe empty - getMetricValue[HistogramDataPointSnapshot](s"$customHistogramName", List("method" -> "GET")).map(_.getCount).value shouldBe requestsNumber1 - getMetricValue[HistogramDataPointSnapshot](s"$customHistogramName", List("method" -> "POST")).map(_.getCount).value shouldBe requestsNumber2 + getMetricValue[HistogramDataPointSnapshot](s"$customHistogramName", List("method" -> "GET")) + .map(_.getCount) + .value shouldBe requestsNumber1 + getMetricValue[HistogramDataPointSnapshot](s"$customHistogramName", List("method" -> "POST")) + .map(_.getCount) + .value shouldBe requestsNumber2 } it should "use mapped request to gauge name with labels" in { @@ -130,9 +134,7 @@ class PrometheusBackendTest stubAlwaysOk, PrometheusConfig( requestToInProgressGaugeNameMapper = - r => Some(CollectorConfig( - collectorName = customGaugeName, - labels = List("method" -> r.method.method))) + r => Some(CollectorConfig(collectorName = customGaugeName, labels = List("method" -> r.method.method))) ) ) val requestsNumber1 = 5 @@ -145,8 +147,12 @@ class PrometheusBackendTest // then getMetricSnapshot[GaugeDataPointSnapshot](s"${PrometheusBackend.DefaultRequestsActiveGaugeName}") shouldBe empty // the gauges should be created, but set to 0 - getMetricValue[GaugeDataPointSnapshot](s"$customGaugeName", List("method" -> "GET")).map(_.getValue).value shouldBe 0.0 - getMetricValue[GaugeDataPointSnapshot](s"$customGaugeName", List("method" -> "POST")).map(_.getValue).value shouldBe 0.0 + getMetricValue[GaugeDataPointSnapshot](s"$customGaugeName", List("method" -> "GET")) + .map(_.getValue) + .value shouldBe 0.0 + getMetricValue[GaugeDataPointSnapshot](s"$customGaugeName", List("method" -> "POST")) + .map(_.getValue) + .value shouldBe 0.0 } it should "disable histograms" in { @@ -186,7 +192,9 @@ class PrometheusBackendTest countDownLatch.countDown() eventually { - getMetricValue[GaugeDataPointSnapshot](PrometheusBackend.DefaultRequestsActiveGaugeName, List("method" -> "GET")).map(_.getValue).value shouldBe 0 + getMetricValue[GaugeDataPointSnapshot](PrometheusBackend.DefaultRequestsActiveGaugeName, List("method" -> "GET")) + .map(_.getValue) + .value shouldBe 0 } } @@ -283,10 +291,12 @@ class PrometheusBackendTest backendStub, PrometheusConfig( responseToSuccessCounterMapper = (_, _) => - Some(CollectorConfig( - collectorName = PrometheusBackend.DefaultSuccessCounterName, - labels = List(("method", "foo"), ("status", "bar")) - )) + Some( + CollectorConfig( + collectorName = PrometheusBackend.DefaultSuccessCounterName, + labels = List(("method", "foo"), ("status", "bar")) + ) + ) ) ) @@ -315,8 +325,12 @@ class PrometheusBackendTest ) // then - getMetricValue[SummaryDataPointSnapshot](PrometheusBackend.DefaultRequestSizeName, List("method" -> "GET")).map(_.getCount).value shouldBe 5 - getMetricValue[SummaryDataPointSnapshot](PrometheusBackend.DefaultRequestSizeName, List("method" -> "GET")).map(_.getSum).value shouldBe 25 + getMetricValue[SummaryDataPointSnapshot](PrometheusBackend.DefaultRequestSizeName, List("method" -> "GET")) + .map(_.getCount) + .value shouldBe 5 + getMetricValue[SummaryDataPointSnapshot](PrometheusBackend.DefaultRequestSizeName, List("method" -> "GET")) + .map(_.getSum) + .value shouldBe 25 getMetricValue[SummaryDataPointSnapshot]( PrometheusBackend.DefaultResponseSizeName, List("method" -> "GET", "status" -> "2xx") @@ -345,7 +359,8 @@ class PrometheusBackendTest PrometheusBackend.DefaultSuccessCounterName, List("method" -> "GET", "status" -> "2xx") ).map(_.getValue) shouldBe None - getMetricValue[CounterDataPointSnapshot](PrometheusBackend.DefaultFailureCounterName, List("method" -> "GET")).map(_.getValue) shouldBe None + getMetricValue[CounterDataPointSnapshot](PrometheusBackend.DefaultFailureCounterName, List("method" -> "GET")) + .map(_.getValue) shouldBe None getMetricValue[CounterDataPointSnapshot]( PrometheusBackend.DefaultErrorCounterName, List("method" -> "GET", "status" -> "5xx") @@ -370,7 +385,8 @@ class PrometheusBackendTest PrometheusBackend.DefaultSuccessCounterName, List("method" -> "GET", "status" -> "2xx") ).map(_.getValue) shouldBe None - getMetricValue[CounterDataPointSnapshot](PrometheusBackend.DefaultFailureCounterName, List("method" -> "GET")).map(_.getValue) shouldBe Some(1) + getMetricValue[CounterDataPointSnapshot](PrometheusBackend.DefaultFailureCounterName, List("method" -> "GET")) + .map(_.getValue) shouldBe Some(1) getMetricValue[CounterDataPointSnapshot]( PrometheusBackend.DefaultErrorCounterName, List("method" -> "GET", "status" -> "5xx") @@ -393,7 +409,8 @@ class PrometheusBackendTest PrometheusBackend.DefaultSuccessCounterName, List("method" -> "GET", "status" -> "2xx") ).map(_.getValue) shouldBe Some(1) - getMetricValue[CounterDataPointSnapshot](PrometheusBackend.DefaultFailureCounterName, List("method" -> "GET")).map(_.getValue) shouldBe None + getMetricValue[CounterDataPointSnapshot](PrometheusBackend.DefaultFailureCounterName, List("method" -> "GET")) + .map(_.getValue) shouldBe None getMetricValue[CounterDataPointSnapshot]( PrometheusBackend.DefaultErrorCounterName, List("method" -> "GET", "status" -> "5xx") diff --git a/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpSyncBackend.scala b/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpSyncBackend.scala index c672061cc5..2b9cc3f60e 100644 --- a/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpSyncBackend.scala +++ b/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpSyncBackend.scala @@ -6,7 +6,15 @@ import sttp.client4.internal.NoStreams import sttp.client4.internal.ws.{SimpleQueue, SyncQueue, WebSocketEvent} import sttp.client4.okhttp.OkHttpBackend.EncodingHandler import sttp.client4.testing.WebSocketSyncBackendStub -import sttp.client4.{BackendOptions, DefaultReadTimeout, GenericRequest, Response, WebSocketSyncBackend, ignore, wrappers} +import sttp.client4.{ + ignore, + wrappers, + BackendOptions, + DefaultReadTimeout, + GenericRequest, + Response, + WebSocketSyncBackend +} import sttp.monad.{IdentityMonad, MonadError} import sttp.shared.Identity import sttp.ws.WebSocket @@ -15,7 +23,7 @@ import java.io.InputStream import java.util.concurrent.ArrayBlockingQueue import java.util.concurrent.atomic.AtomicBoolean import scala.concurrent.duration.Duration -import scala.concurrent.{Await, ExecutionContext, Future, blocking} +import scala.concurrent.{blocking, Await, ExecutionContext, Future} class OkHttpSyncBackend private ( client: OkHttpClient,