From 18380489a6bd251280fdb1666aab6409c3a315ca Mon Sep 17 00:00:00 2001 From: Dmitry Ivankov Date: Thu, 27 Nov 2025 13:43:02 +0100 Subject: [PATCH] CDF-26312: GenericClient: add wrapSttpBackend argument In addition to baseSttpBackend allow settign wrapSttpClient. GenericClient inserts auth wrapper in between and before setting retrying wrapper as base client would mean each retry is using same auth headers, which could expire for long and numerous retries. With new argument caller can move retrying wrapper from base client to wrapping argument and get up-to-date auth on each attempt. [CDF-26312] --- build.sbt | 2 +- .../cognite/sdk/scala/v1/GenericClient.scala | 32 ++++++++++++++----- .../cognite/sdk/scala/v1/RequestSession.scala | 3 +- .../common/OAuth2ClientCredentialsTest.scala | 1 + .../cognite/sdk/scala/common/TokenTest.scala | 2 +- .../scala/v1/CommonDataModelTestHelper.scala | 3 +- .../sdk/scala/v1/TransformationsTest.scala | 4 ++- 7 files changed, 34 insertions(+), 13 deletions(-) diff --git a/build.sbt b/build.sbt index 06c87807f..869166532 100644 --- a/build.sbt +++ b/build.sbt @@ -44,7 +44,7 @@ lazy val commonSettings = Seq( organization := "com.cognite", organizationName := "Cognite", organizationHomepage := Some(url("https://cognite.com")), - version := "2.34." + patchVersion, + version := "2.35." + patchVersion, isSnapshot := patchVersion.endsWith("-SNAPSHOT"), scalaVersion := scala213, // use 2.13 by default // handle cross plugin https://github.com/stringbean/sbt-dependency-lock/issues/13 diff --git a/src/main/scala/com/cognite/sdk/scala/v1/GenericClient.scala b/src/main/scala/com/cognite/sdk/scala/v1/GenericClient.scala index 903dbb040..3e2315a74 100644 --- a/src/main/scala/com/cognite/sdk/scala/v1/GenericClient.scala +++ b/src/main/scala/com/cognite/sdk/scala/v1/GenericClient.scala @@ -34,7 +34,8 @@ class GenericClient[F[_]: Trace]( apiVersion: Option[String], clientTag: Option[String], cdfVersion: Option[String], - sttpBackend: SttpBackend[F, Any] + sttpBackend: SttpBackend[F, Any], + wrapSttpBackend: SttpBackend[F, Any] => SttpBackend[F, Any] )(implicit monad: CMonadError[F, Throwable]) { def this( applicationName: String, @@ -44,7 +45,8 @@ class GenericClient[F[_]: Trace]( apiVersion: Option[String] = None, clientTag: Option[String] = None, cdfVersion: Option[String] = None, - sttpBackend: SttpBackend[F, Any] + sttpBackend: SttpBackend[F, Any], + wrapSttpBackend: SttpBackend[F, Any] => SttpBackend[F, Any] = identity[SttpBackend[F, Any]](_) )(implicit monad: CMonadError[F, Throwable]) = this( applicationName, @@ -54,7 +56,8 @@ class GenericClient[F[_]: Trace]( apiVersion, clientTag, cdfVersion, - sttpBackend + sttpBackend, + wrapSttpBackend ) import GenericClient._ @@ -66,12 +69,15 @@ class GenericClient[F[_]: Trace]( applicationName, uri"$uri/api/${apiVersion.getOrElse("v1")}/projects/$projectName", sttpBackend, + wrapSttpBackend, authProvider, clientTag, cdfVersion ) lazy val token = - new Token[F](RequestSession(applicationName, uri, sttpBackend, authProvider, clientTag)) + new Token[F]( + RequestSession(applicationName, uri, sttpBackend, wrapSttpBackend, authProvider, clientTag) + ) lazy val assets = new Assets[F](requestSession.withResourceType(ASSETS)) lazy val events = new Events[F](requestSession.withResourceType(EVENTS)) lazy val files = new Files[F](requestSession.withResourceType(FILES)) @@ -175,9 +181,17 @@ object GenericClient { projectName: String, baseUrl: String, auth: Auth, - sttpBackend: SttpBackend[F, Any] + sttpBackend: SttpBackend[F, Any], + wrapSttpBackend: SttpBackend[F, Any] => SttpBackend[F, Any] = identity[SttpBackend[F, Any]](_) )(implicit F: CMonadError[F, Throwable]): GenericClient[F] = - new GenericClient(applicationName, projectName, baseUrl, auth, sttpBackend = sttpBackend) + new GenericClient( + applicationName, + projectName, + baseUrl, + auth, + sttpBackend = sttpBackend, + wrapSttpBackend = wrapSttpBackend + ) def parseBaseUrlOrThrow(baseUrl: String): Uri = try { @@ -235,7 +249,8 @@ object GenericClient { apiVersion: Option[String] = None, clientTag: Option[String] = None, cdfVersion: Option[String] = None, - sttpBackend: SttpBackend[F, Any] + sttpBackend: SttpBackend[F, Any], + wrapSttpBackend: SttpBackend[F, Any] => SttpBackend[F, Any] = identity[SttpBackend[F, Any]](_) )(implicit F: CMonadError[F, Throwable]): F[GenericClient[F]] = if (projectName.isEmpty) { F.raiseError(InvalidAuthentication()) @@ -249,7 +264,8 @@ object GenericClient { apiVersion, clientTag, cdfVersion, - sttpBackend + sttpBackend, + wrapSttpBackend ) ) } diff --git a/src/main/scala/com/cognite/sdk/scala/v1/RequestSession.scala b/src/main/scala/com/cognite/sdk/scala/v1/RequestSession.scala index 4b9a81b3f..4e05f7b28 100644 --- a/src/main/scala/com/cognite/sdk/scala/v1/RequestSession.scala +++ b/src/main/scala/com/cognite/sdk/scala/v1/RequestSession.scala @@ -29,6 +29,7 @@ final case class RequestSession[F[_]: Trace]( applicationName: String, baseUrl: Uri, baseSttpBackend: SttpBackend[F, _], + wrapSttpBackend: SttpBackend[F, Any] => SttpBackend[F, Any], auth: AuthProvider[F], clientTag: Option[String] = None, cdfVersion: Option[String] = None, @@ -39,7 +40,7 @@ final case class RequestSession[F[_]: Trace]( this.copy(tags = this.tags + (GenericClient.RESOURCE_TYPE_TAG -> resourceType)) val sttpBackend: SttpBackend[F, _] = - new AuthSttpBackend(new TraceSttpBackend(baseSttpBackend), auth) + wrapSttpBackend(new AuthSttpBackend(new TraceSttpBackend(baseSttpBackend), auth)) def send[R]( r: RequestT[Empty, Either[String, String], Any] => RequestT[Id, R, Any] diff --git a/src/test/scala/com/cognite/sdk/scala/common/OAuth2ClientCredentialsTest.scala b/src/test/scala/com/cognite/sdk/scala/common/OAuth2ClientCredentialsTest.scala index a6a03116e..6fd3f9421 100644 --- a/src/test/scala/com/cognite/sdk/scala/common/OAuth2ClientCredentialsTest.scala +++ b/src/test/scala/com/cognite/sdk/scala/common/OAuth2ClientCredentialsTest.scala @@ -62,6 +62,7 @@ class OAuth2ClientCredentialsTest extends AnyFlatSpec with Matchers with OptionV clientTag = None, cdfVersion = None, sttpBackend = sttpBackend, + wrapSttpBackend = identity[SttpBackend[IO, Any]], ) noException shouldBe thrownBy { diff --git a/src/test/scala/com/cognite/sdk/scala/common/TokenTest.scala b/src/test/scala/com/cognite/sdk/scala/common/TokenTest.scala index 3c9dc6277..bdedb304a 100644 --- a/src/test/scala/com/cognite/sdk/scala/common/TokenTest.scala +++ b/src/test/scala/com/cognite/sdk/scala/common/TokenTest.scala @@ -21,7 +21,7 @@ class TokenTest extends SdkTestSpec with OptionValues { val token = new Token(RequestSession[IO]("CogniteScalaSDK-OAuth-Test", uri"${baseUrl}", sttpBackend, - authProvider)) + identity, authProvider)) val status = token.inspect().unsafeRunTimed(10.seconds).value assert(status.subject !== "") } diff --git a/src/test/scala/com/cognite/sdk/scala/v1/CommonDataModelTestHelper.scala b/src/test/scala/com/cognite/sdk/scala/v1/CommonDataModelTestHelper.scala index aba872810..f56f3479a 100644 --- a/src/test/scala/com/cognite/sdk/scala/v1/CommonDataModelTestHelper.scala +++ b/src/test/scala/com/cognite/sdk/scala/v1/CommonDataModelTestHelper.scala @@ -58,6 +58,7 @@ trait CommonDataModelTestHelper extends AnyFlatSpec with Matchers { None, None, Some("alpha"), - new RetryingBackend[IO, Any](implicitly) + implicitly[SttpBackend[IO, Any]], + new RetryingBackend[IO, Any](_: SttpBackend[IO, Any]), ) } diff --git a/src/test/scala/com/cognite/sdk/scala/v1/TransformationsTest.scala b/src/test/scala/com/cognite/sdk/scala/v1/TransformationsTest.scala index a467e2d94..629f790a8 100644 --- a/src/test/scala/com/cognite/sdk/scala/v1/TransformationsTest.scala +++ b/src/test/scala/com/cognite/sdk/scala/v1/TransformationsTest.scala @@ -8,6 +8,7 @@ import cats.effect.unsafe.implicits.global import com.cognite.sdk.scala.common.OAuth2.ClientCredentials import com.cognite.sdk.scala.common._ import io.circe.syntax.EncoderOps +import sttp.client3.SttpBackend import sttp.model.Uri import java.time.temporal.ChronoUnit @@ -28,7 +29,8 @@ class TransformationsTest extends CommonDataModelTestHelper with RetryWhile { None, None, None, - sttpBackendAuth + sttpBackendAuth, + identity[SttpBackend[IO, Any]](_), ) def shortRandomUUID(): String = UUID.randomUUID().toString.substring(0, 8)