diff --git a/obp-api/src/main/scala/code/api/util/ApiSession.scala b/obp-api/src/main/scala/code/api/util/ApiSession.scala index 30946d18c..e7426ed7b 100644 --- a/obp-api/src/main/scala/code/api/util/ApiSession.scala +++ b/obp-api/src/main/scala/code/api/util/ApiSession.scala @@ -55,7 +55,12 @@ case class CallContext( xRateLimitRemaining : Long = -1, xRateLimitReset : Long = -1, paginationOffset : Option[String] = None, - paginationLimit : Option[String] = None + paginationLimit : Option[String] = None, + // Validated entities from ResourceDoc middleware (http4s) + bank: Option[Bank] = None, + bankAccount: Option[BankAccount] = None, + view: Option[View] = None, + counterparty: Option[CounterpartyTrait] = None ) extends MdcLoggable { override def toString: String = SecureLogging.maskSensitive( s"${this.getClass.getSimpleName}(${this.productIterator.mkString(", ")})" diff --git a/obp-api/src/main/scala/code/api/util/http4s/Http4sSupport.scala b/obp-api/src/main/scala/code/api/util/http4s/Http4sSupport.scala index 4b826df41..d17126bc6 100644 --- a/obp-api/src/main/scala/code/api/util/http4s/Http4sSupport.scala +++ b/obp-api/src/main/scala/code/api/util/http4s/Http4sSupport.scala @@ -24,63 +24,28 @@ import scala.language.higherKinds * * This file contains: * - Http4sCallContextBuilder: Builds shared CallContext from http4s Request[IO] - * - Http4sRequestAttributes: Request attribute keys for storing validated objects + * - Http4sRequestAttributes: Request attribute key for storing CallContext * - ResourceDocMatcher: Matches http4s requests to ResourceDoc entries - * - ResourceDocMiddleware: Validation chain middleware for http4s - * - ErrorResponseConverter: Converts OBP errors to http4s Response[IO] + * + * Validated entities (User, Bank, BankAccount, View, Counterparty) are stored + * directly in CallContext fields, making them available throughout the call chain. */ /** - * Vault keys for storing validated objects in http4s request attributes. - * These keys allow middleware to pass validated objects to endpoint handlers. - * WIP - */ -/** - * Request attribute keys for storing validated objects in http4s requests. - * These keys allow middleware to pass validated objects to endpoint handlers. + * Request attribute keys for storing CallContext in http4s requests. * - * Note: Uses http4s Vault (org.typelevel.vault.Key) for type-safe request attributes. */ object Http4sRequestAttributes { - // Use shared CallContext from code.api.util.ApiSession + // CallContext contains all request data and validated entities val callContextKey: Key[CallContext] = Key.newKey[IO, CallContext].unsafeRunSync()(cats.effect.unsafe.IORuntime.global) - val userKey: Key[User] = - Key.newKey[IO, User].unsafeRunSync()(cats.effect.unsafe.IORuntime.global) - - val bankKey: Key[Bank] = - Key.newKey[IO, Bank].unsafeRunSync()(cats.effect.unsafe.IORuntime.global) - - val bankAccountKey: Key[BankAccount] = - Key.newKey[IO, BankAccount].unsafeRunSync()(cats.effect.unsafe.IORuntime.global) - - val viewKey: Key[View] = - Key.newKey[IO, View].unsafeRunSync()(cats.effect.unsafe.IORuntime.global) - - val counterpartyKey: Key[CounterpartyTrait] = - Key.newKey[IO, CounterpartyTrait].unsafeRunSync()(cats.effect.unsafe.IORuntime.global) - /** - * Helper methods for accessing validated objects from request attributes + * Get CallContext from request attributes. + * CallContext contains validated entities: bank, bankAccount, view, counterparty */ def getCallContext(req: Request[IO]): Option[CallContext] = req.attributes.lookup(callContextKey) - - def getUser(req: Request[IO]): Option[User] = - req.attributes.lookup(userKey) - - def getBank(req: Request[IO]): Option[Bank] = - req.attributes.lookup(bankKey) - - def getBankAccount(req: Request[IO]): Option[BankAccount] = - req.attributes.lookup(bankAccountKey) - - def getView(req: Request[IO]): Option[View] = - req.attributes.lookup(viewKey) - - def getCounterparty(req: Request[IO]): Option[CounterpartyTrait] = - req.attributes.lookup(counterpartyKey) } /** @@ -331,16 +296,3 @@ object ResourceDocMatcher { ) } } - -/** - * Validated context containing all validated objects from the middleware chain. - * This is passed to endpoint handlers after successful validation. - */ -case class ValidatedContext( - user: Option[User], - bank: Option[Bank], - bankAccount: Option[BankAccount], - view: Option[View], - counterparty: Option[CounterpartyTrait], - callContext: CallContext -) diff --git a/obp-api/src/main/scala/code/api/util/http4s/ResourceDocMiddleware.scala b/obp-api/src/main/scala/code/api/util/http4s/ResourceDocMiddleware.scala index fbffba49c..74d11eef5 100644 --- a/obp-api/src/main/scala/code/api/util/http4s/ResourceDocMiddleware.scala +++ b/obp-api/src/main/scala/code/api/util/http4s/ResourceDocMiddleware.scala @@ -243,13 +243,16 @@ object ResourceDocMiddleware extends MdcLoggable{ counterpartyResult.flatMap { case Left(errorResponse) => IO.pure(errorResponse) case Right((counterpartyOpt, finalCC)) => - // All validations passed - store validated context and invoke route - var updatedReq = req.withAttribute(Http4sRequestAttributes.callContextKey, finalCC) - boxUser.toOption.foreach { user => updatedReq = updatedReq.withAttribute(Http4sRequestAttributes.userKey, user) } - bankOpt.foreach { bank => updatedReq = updatedReq.withAttribute(Http4sRequestAttributes.bankKey, bank) } - accountOpt.foreach { account => updatedReq = updatedReq.withAttribute(Http4sRequestAttributes.bankAccountKey, account) } - viewOpt.foreach { view => updatedReq = updatedReq.withAttribute(Http4sRequestAttributes.viewKey, view) } - counterpartyOpt.foreach { counterparty => updatedReq = updatedReq.withAttribute(Http4sRequestAttributes.counterpartyKey, counterparty) } + // All validations passed - update CallContext with validated entities + val enrichedCC = finalCC.copy( + bank = bankOpt, + bankAccount = accountOpt, + view = viewOpt, + counterparty = counterpartyOpt + ) + + // Store enriched CallContext in request attributes + val updatedReq = req.withAttribute(Http4sRequestAttributes.callContextKey, enrichedCC) routes.run(updatedReq).getOrElseF(IO.pure(Response[IO](org.http4s.Status.NotFound))) } } diff --git a/obp-api/src/main/scala/code/api/v7_0_0/Http4s700.scala b/obp-api/src/main/scala/code/api/v7_0_0/Http4s700.scala index 35fafb839..09fc31bad 100644 --- a/obp-api/src/main/scala/code/api/v7_0_0/Http4s700.scala +++ b/obp-api/src/main/scala/code/api/v7_0_0/Http4s700.scala @@ -137,7 +137,7 @@ object Http4s700 { case req @ GET -> `prefixPath` / "cards" => val response = for { cc <- IO.fromOption(req.attributes.lookup(Http4sRequestAttributes.callContextKey))(new RuntimeException("CallContext not found in request attributes")) - user <- IO.fromOption(req.attributes.lookup(Http4sRequestAttributes.userKey))(new RuntimeException("User not found in request attributes")) + user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext")) result <- IO.fromFuture(IO { for { (cards, callContext) <- NewStyle.function.getPhysicalCardsForUser(user, Some(cc)) @@ -169,8 +169,8 @@ object Http4s700 { case req @ GET -> `prefixPath` / "banks" / bankId / "cards" => val response = for { cc <- IO.fromOption(req.attributes.lookup(Http4sRequestAttributes.callContextKey))(new RuntimeException("CallContext not found in request attributes")) - user <- IO.fromOption(req.attributes.lookup(Http4sRequestAttributes.userKey))(new RuntimeException("User not found in request attributes")) - bank <- IO.fromOption(req.attributes.lookup(Http4sRequestAttributes.bankKey))(new RuntimeException("Bank not found in request attributes")) + user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext")) + bank <- IO.fromOption(cc.bank)(new RuntimeException("Bank not found in CallContext")) result <- IO.fromFuture(IO { for { httpParams <- NewStyle.function.extractHttpParamsFromUrl(req.uri.renderString)