From e986af998ed9a3a6386f7c4ab3c648173d86f256 Mon Sep 17 00:00:00 2001 From: "typelevel-steward[bot]" <106827141+typelevel-steward[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:21:48 +0000 Subject: [PATCH 1/8] Update scala-library to 2.12.20 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index bb913f8..470ee11 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ ThisBuild / tlBaseVersion := "0.6" val http4sVersion = "0.23.27" val natchezVersion = "0.3.6" -val scala212Version = "2.12.19" +val scala212Version = "2.12.20" val scala213Version = "2.13.14" val scala3Version = "3.3.3" val slf4jVersion = "2.0.16" From 7b147b6e81e6f2e52e8b77b41048c926df513b7e Mon Sep 17 00:00:00 2001 From: "typelevel-steward[bot]" <106827141+typelevel-steward[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 12:26:08 +0000 Subject: [PATCH 2/8] Update sbt-typelevel to 0.7.3 --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 0a5d84b..5b8181b 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,4 @@ -addSbtPlugin("org.typelevel" % "sbt-typelevel" % "0.7.2") +addSbtPlugin("org.typelevel" % "sbt-typelevel" % "0.7.3") addSbtPlugin("com.lightbend.paradox" % "sbt-paradox" % "0.10.7") addSbtPlugin("com.github.sbt" % "sbt-site-paradox" % "1.7.0") addSbtPlugin("com.github.sbt" % "sbt-ghpages" % "0.8.0") From 1e199216747e80a8b1ce1bec13071967a5996cbc Mon Sep 17 00:00:00 2001 From: "typelevel-steward[bot]" <106827141+typelevel-steward[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 12:26:30 +0000 Subject: [PATCH 3/8] Run prePR with sbt-typelevel Executed command: sbt tlPrePrBotHook --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c315b9d..f27bcc2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -245,7 +245,7 @@ jobs: dependency-submission: name: Submit Dependencies - if: github.event_name != 'pull_request' + if: github.event.repository.fork == false && github.event_name != 'pull_request' strategy: matrix: os: [ubuntu-latest] From f9146b1493081f19ec64be144dca50a0d18ca29d Mon Sep 17 00:00:00 2001 From: Brian Holt Date: Fri, 6 Sep 2024 14:19:07 -0500 Subject: [PATCH 4/8] Add methods to override Span.Options on the span created by the client middleware This would allow callers to set the Span Creation Policy, etc., on the span created by the middleware. --- .../natchez/http4s/NatchezMiddleware.scala | 41 +++++++++- .../http4s/NatchezMiddlewareSuite.scala | 81 +++++++++++++++---- 2 files changed, 106 insertions(+), 16 deletions(-) diff --git a/modules/http4s/src/main/scala/natchez/http4s/NatchezMiddleware.scala b/modules/http4s/src/main/scala/natchez/http4s/NatchezMiddleware.scala index 6736dd8..39a0649 100644 --- a/modules/http4s/src/main/scala/natchez/http4s/NatchezMiddleware.scala +++ b/modules/http4s/src/main/scala/natchez/http4s/NatchezMiddleware.scala @@ -9,7 +9,7 @@ import cats.syntax.all._ import cats.effect.{MonadCancel, MonadCancelThrow, Outcome, Resource} import cats.effect.syntax.all._ import Outcome._ -import natchez.{Tags, Trace, TraceValue} +import natchez.{Span, Tags, Trace, TraceValue} import natchez.Span.Options.Defaults import natchez.Span.SpanKind import org.http4s.client.Client @@ -114,6 +114,42 @@ object NatchezMiddleware { (additionalAttributes: (String, TraceValue)*): Client[F] = NatchezMiddleware.client(client, (_: Request[F]) => additionalAttributes.pure[F]) + /** + * A middleware that adds the current span's kernel to outgoing requests, performs requests in + * a span called `http4s-client-request`, and adds the following fields to that span. + * + * - "client.http.method" -> "GET", "PUT", etc. + * - "client.http.uri" -> request URI + * - "client.http.status_code" -> "200", "403", etc. // why is this a string? + * + * @param client the `Client[F]` to be enhanced + * @param additionalAttributes additional attributes to be added to the span + * @tparam F An effect with instances of `Trace[F]` and `MonadCancelThrow[F]` + * @return the enhanced `Client[F]` + */ + def clientWithAttributes[F[_] : Trace : MonadCancelThrow](client: Client[F], + spanOptions: Span.Options) + (additionalAttributes: (String, TraceValue)*): Client[F] = + NatchezMiddleware.client(client, spanOptions, (_: Request[F]) => additionalAttributes.pure[F]) + + /** + * A middleware that adds the current span's kernel to outgoing requests, performs requests in + * a span called `http4s-client-request`, and adds the following fields to that span. + * + * - "client.http.method" -> "GET", "PUT", etc. + * - "client.http.uri" -> request URI + * - "client.http.status_code" -> "200", "403", etc. // why is this a string? + * + * @param client the `Client[F]` to be enhanced + * @param additionalAttributesF a function that takes the `Request[F]` and returns any additional attributes to be added to the span + * @tparam F An effect with instances of `Trace[F]` and `MonadCancelThrow[F]` + * @return the enhanced `Client[F]` + */ + def client[F[_] : Trace : MonadCancelThrow](client: Client[F], + additionalAttributesF: Request[F] => F[Seq[(String, TraceValue)]], + ): Client[F] = + NatchezMiddleware.client(client, Defaults.withSpanKind(SpanKind.Client), additionalAttributesF) + /** * A middleware that adds the current span's kernel to outgoing requests, performs requests in * a span called `http4s-client-request`, and adds the following fields to that span. @@ -128,11 +164,12 @@ object NatchezMiddleware { * @return the enhanced `Client[F]` */ def client[F[_] : Trace : MonadCancelThrow](client: Client[F], + spanOptions: Span.Options, additionalAttributesF: Request[F] => F[Seq[(String, TraceValue)]], ): Client[F] = Client { req => Resource.applyFull {poll => - Trace[F].span("http4s-client-request", Defaults.withSpanKind(SpanKind.Client)) { + Trace[F].span("http4s-client-request", spanOptions) { for { knl <- Trace[F].kernel _ <- Trace[F].put( diff --git a/modules/http4s/src/test/scala/natchez/http4s/NatchezMiddlewareSuite.scala b/modules/http4s/src/test/scala/natchez/http4s/NatchezMiddlewareSuite.scala index c0a7e50..ba573ef 100644 --- a/modules/http4s/src/test/scala/natchez/http4s/NatchezMiddlewareSuite.scala +++ b/modules/http4s/src/test/scala/natchez/http4s/NatchezMiddlewareSuite.scala @@ -8,19 +8,20 @@ import cats.Monad import cats.data.{Chain, Kleisli} import cats.effect.{IO, MonadCancelThrow, Resource} import munit.ScalaCheckEffectSuite +import natchez.Span.Options.SpanCreationPolicy import natchez.Span.SpanKind -import natchez.{InMemory, Kernel, Span, Trace, TraceValue} import natchez.TraceValue.StringValue -import natchez.http4s.syntax.entrypoint._ -import org.http4s._ -import org.http4s.headers._ +import natchez.http4s.syntax.entrypoint.* +import natchez.* +import org.http4s.* import org.http4s.client.Client -import org.http4s.dsl.request._ -import org.http4s.syntax.literals._ -import org.scalacheck.{Arbitrary, Gen} +import org.http4s.dsl.request.* +import org.http4s.headers.* +import org.http4s.syntax.literals.* import org.scalacheck.Arbitrary.arbitrary import org.scalacheck.effect.PropF -import org.typelevel.ci._ +import org.scalacheck.{Arbitrary, Gen} +import org.typelevel.ci.* class NatchezMiddlewareSuite extends InMemorySuite @@ -44,6 +45,47 @@ class NatchezMiddlewareSuite } yield key -> value } + private implicit val arbCIString: Arbitrary[CIString] = Arbitrary { + Gen.alphaLowerStr.map(CIString(_)) + } + + private implicit val arbKernel: Arbitrary[Kernel] = Arbitrary { + arbitrary[Map[CIString, String]].map(Kernel(_)) + } + + private implicit val arbSpanCreationPolicy: Arbitrary[SpanCreationPolicy] = Arbitrary { + Gen.oneOf(SpanCreationPolicy.Default, SpanCreationPolicy.Coalesce, SpanCreationPolicy.Suppress) + } + + private implicit val arbSpanKind: Arbitrary[SpanKind] = Arbitrary { + Gen.oneOf( + SpanKind.Internal, + SpanKind.Client, + SpanKind.Server, + SpanKind.Producer, + SpanKind.Consumer, + ) + } + + private implicit val arbSpanOptions: Arbitrary[Span.Options] = Arbitrary { + for { + parentKernel <- arbitrary[Option[Kernel]] + spanCreationPolicy <- arbitrary[SpanCreationPolicy] + spanKind <- arbitrary[SpanKind] + links <- arbitrary[List[Kernel]].map(Chain.fromSeq) + } yield { + links.foldLeft { + parentKernel.foldLeft { + Span + .Options + .Defaults + .withSpanKind(spanKind) + .withSpanCreationPolicy(spanCreationPolicy) + }(_.withParentKernel(_)) + }(_.withLink(_)) + } + } + test("do not leak security and payload headers to the client request") { val headers = Headers( // security @@ -76,7 +118,7 @@ class NatchezMiddlewareSuite for { ref <- IO.ref(Chain.empty[(Lineage, NatchezCommand)]) ep <- IO.pure(new InMemory.EntryPoint(ref)) - routes <- IO.pure(ep.liftT(httpRoutes[Kleisli[IO, natchez.Span[IO], *]]())) + routes <- IO.pure(ep.liftT(httpRoutes[Kleisli[IO, natchez.Span[IO], *]](None))) response <- routes.orNotFound.run(request) } yield { assertEquals(response.status.code, 200) @@ -85,7 +127,8 @@ class NatchezMiddlewareSuite } test("generate proper tracing history") { - PropF.forAllF { (userSpecifiedTags: List[(String, TraceValue)]) => + PropF.forAllF { (userSpecifiedTags: List[(String, TraceValue)], + maybeSpanOptions: Option[Span.Options]) => val request = Request[IO]( method = Method.GET, uri = uri"/hello/some-name", @@ -118,10 +161,13 @@ class NatchezMiddlewareSuite "http.status_code" -> StringValue("200") ) + val spanOptions = maybeSpanOptions.getOrElse(Span.Options.Defaults.withSpanKind(SpanKind.Client)) + val kernel = maybeSpanOptions.flatMap(_.parentKernel) + List( (Lineage.Root, NatchezCommand.CreateRootSpan("/hello/some-name", requestKernel, Span.Options.Defaults)), (Lineage.Root("/hello/some-name"), NatchezCommand.CreateSpan("call-proxy", None, Span.Options.Defaults)), - (Lineage.Root("/hello/some-name") / "call-proxy", NatchezCommand.CreateSpan("http4s-client-request", None, Span.Options.Defaults.withSpanKind(SpanKind.Client))), + (Lineage.Root("/hello/some-name") / "call-proxy", NatchezCommand.CreateSpan("http4s-client-request", kernel, spanOptions)), (Lineage.Root("/hello/some-name") / "call-proxy" / "http4s-client-request", NatchezCommand.AskKernel(requestKernel)), (Lineage.Root("/hello/some-name") / "call-proxy" / "http4s-client-request", NatchezCommand.Put(clientRequestTags)), (Lineage.Root("/hello/some-name") / "call-proxy" / "http4s-client-request", NatchezCommand.Put(userSpecifiedTags)), @@ -136,15 +182,22 @@ class NatchezMiddlewareSuite for { ep <- InMemory.EntryPoint.create[IO] - routes <- IO.pure(ep.liftT(httpRoutes[Kleisli[IO, natchez.Span[IO], *]](userSpecifiedTags: _*))) + routes <- IO.pure(ep.liftT(httpRoutes[Kleisli[IO, natchez.Span[IO], *]](maybeSpanOptions, userSpecifiedTags *))) _ <- routes.orNotFound.run(request) history <- ep.ref.get } yield assertEquals(history.toList, expectedHistory) } } - private def httpRoutes[F[_]: MonadCancelThrow: Trace](additionalAttributes: (String, TraceValue)*): HttpRoutes[F] = { - val client = NatchezMiddleware.clientWithAttributes(echoHeadersClient[F])(additionalAttributes: _*) + private def httpRoutes[F[_]: MonadCancelThrow: Trace](maybeSpanOptions: Option[Span.Options], + additionalAttributes: (String, TraceValue)*): HttpRoutes[F] = { + val client = maybeSpanOptions match { + case Some(spanOptions) => + NatchezMiddleware.clientWithAttributes(echoHeadersClient[F], spanOptions)(additionalAttributes *) + case None => + NatchezMiddleware.clientWithAttributes(echoHeadersClient[F])(additionalAttributes *) + } + val server = NatchezMiddleware.server(proxyRoutes(client)) server } From fdb979b5fada85f7b06763c933accd6c57d63fdf Mon Sep 17 00:00:00 2001 From: "typelevel-steward[bot]" <106827141+typelevel-steward[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 00:18:52 +0000 Subject: [PATCH 5/8] Update http4s-client, http4s-core, ... to 0.23.28 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index bb913f8..70a47ac 100644 --- a/build.sbt +++ b/build.sbt @@ -1,6 +1,6 @@ ThisBuild / tlBaseVersion := "0.6" -val http4sVersion = "0.23.27" +val http4sVersion = "0.23.28" val natchezVersion = "0.3.6" val scala212Version = "2.12.19" val scala213Version = "2.13.14" From 39b7e9f94e1cd590648d72f34e13d22cb2ae8459 Mon Sep 17 00:00:00 2001 From: "typelevel-steward[bot]" <106827141+typelevel-steward[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 00:32:44 +0000 Subject: [PATCH 6/8] Update sbt to 1.10.2 --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index ee4c672..0b699c3 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.1 +sbt.version=1.10.2 From 57937c318f18bc9de963b369fd2893595179ae64 Mon Sep 17 00:00:00 2001 From: "typelevel-steward[bot]" <106827141+typelevel-steward[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:05:38 +0000 Subject: [PATCH 7/8] Update mdoc, sbt-mdoc to 2.6.1 --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 0a5d84b..ed870a7 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -4,4 +4,4 @@ addSbtPlugin("com.github.sbt" % "sbt-site-paradox" % "1.7.0") addSbtPlugin("com.github.sbt" % "sbt-ghpages" % "0.8.0") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.17") -addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.8") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.6.1") From 026b2679399fd3a92675439fed3d54989f4a5d30 Mon Sep 17 00:00:00 2001 From: "typelevel-steward[bot]" <106827141+typelevel-steward[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 12:23:13 +0000 Subject: [PATCH 8/8] Update scala3-library, ... to 3.3.4 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index bb913f8..3b99e88 100644 --- a/build.sbt +++ b/build.sbt @@ -4,7 +4,7 @@ val http4sVersion = "0.23.27" val natchezVersion = "0.3.6" val scala212Version = "2.12.19" val scala213Version = "2.13.14" -val scala3Version = "3.3.3" +val scala3Version = "3.3.4" val slf4jVersion = "2.0.16" val munitCEVersion = "2.0.0" val scalacheckEffectVersion = "2.0.0-M2"