Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,18 @@ ThisBuild / semanticdbVersion := "4.13.9"

// Fix dependency conflicts
ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always
ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-java8-compat" % VersionScheme.Always

lazy val liftVersion = "3.5.0"
lazy val akkaVersion = "2.5.32"
lazy val jettyVersion = "9.4.50.v20221201"
lazy val avroVersion = "1.8.2"
lazy val pekkoVersion = "1.4.0"
lazy val pekkoHttpVersion = "1.3.0"
lazy val http4sVersion = "0.23.30"
lazy val catsEffectVersion = "3.5.7"
lazy val ip4sVersion = "3.7.0"
lazy val jakartaMailVersion = "2.0.1"

lazy val commonSettings = Seq(
resolvers ++= Seq(
Expand All @@ -42,8 +49,8 @@ lazy val obpCommons = (project in file("obp-commons"))
"net.liftweb" %% "lift-util" % liftVersion,
"net.liftweb" %% "lift-mapper" % liftVersion,
"org.scala-lang" % "scala-reflect" % "2.12.20",
"org.scalatest" %% "scalatest" % "3.2.15" % Test,
"org.scalactic" %% "scalactic" % "3.2.15",
"org.scalatest" %% "scalatest" % "3.0.9" % Test,
"org.scalactic" %% "scalactic" % "3.0.9",
"net.liftweb" %% "lift-json" % liftVersion,
"com.alibaba" % "transmittable-thread-local" % "2.11.5",
"org.apache.commons" % "commons-lang3" % "3.12.0",
Expand Down Expand Up @@ -95,6 +102,20 @@ lazy val obpApi = (project in file("obp-api"))
"com.typesafe.akka" %% "akka-remote" % akkaVersion,
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
"com.typesafe.akka" %% "akka-http-core" % "10.1.6",

// Pekko (ActorSystem + Pekko HTTP used by OBP runtime components)
"org.apache.pekko" %% "pekko-actor" % pekkoVersion,
"org.apache.pekko" %% "pekko-remote" % pekkoVersion,
"org.apache.pekko" %% "pekko-slf4j" % pekkoVersion,
"org.apache.pekko" %% "pekko-stream" % pekkoVersion,
"org.apache.pekko" %% "pekko-http" % pekkoHttpVersion,

// http4s (v7.0.0 experimental stack)
"org.typelevel" %% "cats-effect" % catsEffectVersion,
"com.comcast" %% "ip4s-core" % ip4sVersion,
"org.http4s" %% "http4s-core" % http4sVersion,
"org.http4s" %% "http4s-dsl" % http4sVersion,
"org.http4s" %% "http4s-ember-server" % http4sVersion,

// Avro
"com.sksamuel.avro4s" %% "avro4s-core" % avroVersion,
Expand Down Expand Up @@ -164,6 +185,9 @@ lazy val obpApi = (project in file("obp-api"))
// RabbitMQ
"com.rabbitmq" % "amqp-client" % "5.22.0",
"net.liftmodules" %% "amqp_3.1" % "1.5.0",

// Blockchain (Ethereum raw transaction decoding)
"org.web3j" % "core" % "4.14.0",

// Elasticsearch
"org.elasticsearch" % "elasticsearch" % "8.14.0",
Expand All @@ -175,6 +199,7 @@ lazy val obpApi = (project in file("obp-api"))
// Utilities
"cglib" % "cglib" % "3.3.0",
"com.sun.activation" % "jakarta.activation" % "1.2.2",
"com.sun.mail" % "jakarta.mail" % jakartaMailVersion,
"com.nulab-inc" % "zxcvbn" % "1.9.0",

// Testing - temporarily disabled due to version incompatibility
Expand All @@ -192,7 +217,7 @@ lazy val obpApi = (project in file("obp-api"))

// Test dependencies
"junit" % "junit" % "4.13.2" % Test,
"org.scalatest" %% "scalatest" % "3.2.15" % Test,
"org.scalatest" %% "scalatest" % "3.0.9" % Test,
"org.seleniumhq.selenium" % "htmlunit-driver" % "2.36.0" % Test,
"org.testcontainers" % "rabbitmq" % "1.20.3" % Test
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import code.api.OBPRestHelper
import code.api.util.APIUtil.{OBPEndpoint, ResourceDoc, getAllowedEndpoints}
import code.api.util.ScannedApis
import code.util.Helper.MdcLoggable
import com.openbankproject.commons.util.{ApiVersionStatus, ScannedApiVersion}
import com.openbankproject.commons.util.{ApiVersion, ApiVersionStatus}

import scala.collection.mutable.ArrayBuffer

Expand All @@ -47,7 +47,7 @@ This file defines which endpoints from all the versions are available in v1
*/
object ApiCollector extends OBPRestHelper with MdcLoggable with ScannedApis {
//please modify these three parameter if it is not correct.
override val apiVersion = ScannedApiVersion("cds-au", "AU", "v1.0.0")
override val apiVersion = ApiVersion.auOpenBankingV100
val versionStatus = ApiVersionStatus.DRAFT.toString

private[this] val endpoints =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import code.api.OBPRestHelper
import code.api.util.APIUtil.{OBPEndpoint, ResourceDoc, getAllowedEndpoints}
import code.api.util.{ScannedApis}
import code.util.Helper.MdcLoggable
import com.openbankproject.commons.util.{ApiVersionStatus, ScannedApiVersion}
import com.openbankproject.commons.util.{ApiVersion, ApiVersionStatus}
import scala.collection.mutable.ArrayBuffer


Expand All @@ -45,7 +45,7 @@ import scala.collection.mutable.ArrayBuffer
This file defines which endpoints from all the versions are available in v1
*/
object ApiCollector extends OBPRestHelper with MdcLoggable with ScannedApis {
override val apiVersion = ScannedApiVersion("BAHRAIN-OBF", "BAHRAIN-OBF", "v1.0.0")
override val apiVersion = ApiVersion.bahrainObfV100
val versionStatus = ApiVersionStatus.DRAFT.toString

private[this] val endpoints =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ object ResourceDocs300 extends OBPRestHelper with ResourceDocsAPIMethods with Md
object ResourceDocs600 extends OBPRestHelper with ResourceDocsAPIMethods with MdcLoggable {
val version: ApiVersion = ApiVersion.v6_0_0
val versionStatus = ApiVersionStatus.BLEEDING_EDGE.toString
override def includeTechnologyInResponse: Boolean = true
val routes: Seq[OBPEndpoint] = List(
ImplementationsResourceDocs.getResourceDocsObpV400,
ImplementationsResourceDocs.getResourceDocsSwagger,
Expand Down Expand Up @@ -206,7 +207,7 @@ object ResourceDocs300 extends OBPRestHelper with ResourceDocsAPIMethods with Md
case _ if (apiCollectionIdParam.isDefined) =>
val operationIds = MappedApiCollectionEndpointsProvider.getApiCollectionEndpoints(apiCollectionIdParam.getOrElse("")).map(_.operationId).map(getObpFormatOperationId)
val resourceDocs = ResourceDoc.getResourceDocs(operationIds)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale, includeTechnology = includeTechnologyInResponse)
resourceDocsJson.resource_docs
case _ =>
contentParam match {
Expand Down Expand Up @@ -247,4 +248,4 @@ object ResourceDocs300 extends OBPRestHelper with ResourceDocsAPIMethods with Md
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
// We add previous APIMethods so we have access to the Resource Docs
self: OBPRestHelper =>

def includeTechnologyInResponse: Boolean = false

val ImplementationsResourceDocs = new Object() {

val localResourceDocs = ArrayBuffer[ResourceDoc]()
Expand Down Expand Up @@ -346,7 +348,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
// Filter
val rdFiltered = ResourceDocsAPIMethodsUtil.filterResourceDocs(resourceDocs, resourceDocTags, partialFunctionNames)
// Format the data as json
JSONFactory1_4_0.createResourceDocsJson(rdFiltered, isVersion4OrHigher, locale)
JSONFactory1_4_0.createResourceDocsJson(rdFiltered, isVersion4OrHigher, locale, includeTechnology = includeTechnologyInResponse)
}
}

Expand Down Expand Up @@ -500,7 +502,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
NewStyle.function.tryons(s"$UnknownError Can not prepare OBP resource docs.", 500, callContext) {
val operationIds = MappedApiCollectionEndpointsProvider.getApiCollectionEndpoints(apiCollectionIdParam.getOrElse("")).map(_.operationId).map(getObpFormatOperationId)
val resourceDocs = ResourceDoc.getResourceDocs(operationIds)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale, includeTechnology = includeTechnologyInResponse)
val resourceDocsJsonJValue = Full(resourceDocsJsonToJsonResponse(resourceDocsJson))
resourceDocsJsonJValue.map(successJsonResponse(_))
}
Expand Down Expand Up @@ -709,7 +711,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
case _ if (apiCollectionIdParam.isDefined) =>
val operationIds = MappedApiCollectionEndpointsProvider.getApiCollectionEndpoints(apiCollectionIdParam.getOrElse("")).map(_.operationId).map(getObpFormatOperationId)
val resourceDocs = ResourceDoc.getResourceDocs(operationIds)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale, includeTechnology = includeTechnologyInResponse)
resourceDocsJson.resource_docs
case _ =>
contentParam match {
Expand Down Expand Up @@ -903,7 +905,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
case _ if (apiCollectionIdParam.isDefined) =>
val operationIds = MappedApiCollectionEndpointsProvider.getApiCollectionEndpoints(apiCollectionIdParam.getOrElse("")).map(_.operationId).map(getObpFormatOperationId)
val resourceDocs = ResourceDoc.getResourceDocs(operationIds)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale, includeTechnology = includeTechnologyInResponse)
resourceDocsJson.resource_docs
case _ =>
contentParam match {
Expand Down Expand Up @@ -1257,4 +1259,3 @@ so the caller must specify any required filtering by catalog explicitly.


}

Original file line number Diff line number Diff line change
Expand Up @@ -1796,7 +1796,8 @@ object SwaggerDefinitionsJSON {

lazy val implementedByJson = ImplementedByJson(
version = "1_4_0",
function = "getBranches"
function = "getBranches",
technology = None
)
// Used to describe the OBP API calls for documentation and API discovery purposes
lazy val canCreateCustomerSwagger = CanCreateCustomer()
Expand Down Expand Up @@ -4018,7 +4019,7 @@ object SwaggerDefinitionsJSON {
lazy val topApiJson = TopApiJson(
count = 7076,
Implemented_by_partial_function = "getBanks",
implemented_in_version = "v1.2.1"
implemented_in_version = ApiVersion.v1_2_1.toString
)

lazy val topApisJson = TopApisJson(List(topApiJson))
Expand Down Expand Up @@ -4129,7 +4130,7 @@ object SwaggerDefinitionsJSON {
lazy val callLimitPostJsonV600 = CallLimitPostJsonV600(
from_date = DateWithDayExampleObject,
to_date = DateWithDayExampleObject,
api_version = Some("v6.0.0"),
api_version = Some(ApiVersion.v6_0_0.toString),
api_name = Some("getConsumerCallLimits"),
bank_id = None,
per_second_call_limit = "100",
Expand All @@ -4144,7 +4145,7 @@ object SwaggerDefinitionsJSON {
rate_limiting_id = "80e1e0b2-d8bf-4f85-a579-e69ef36e3305",
from_date = DateWithDayExampleObject,
to_date = DateWithDayExampleObject,
api_version = Some("v6.0.0"),
api_version = Some(ApiVersion.v6_0_0.toString),
api_name = Some("getConsumerCallLimits"),
bank_id = None,
per_second_call_limit = "100",
Expand Down Expand Up @@ -5128,7 +5129,7 @@ object SwaggerDefinitionsJSON {
user_id = userIdExample.value,
allowed_attempts =3,
challenge_type = ChallengeType.OBP_TRANSACTION_REQUEST_CHALLENGE.toString,
link = "/obp/v4.0.0/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transaction-request-types/TRANSACTION_REQUEST_TYPE/transaction-requests/TRANSACTION_REQUEST_ID/challenge"
link = s"/obp/${ApiVersion.v4_0_0}/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transaction-request-types/TRANSACTION_REQUEST_TYPE/transaction-requests/TRANSACTION_REQUEST_ID/challenge"
)
lazy val transactionRequestWithChargeJSON400 = TransactionRequestWithChargeJSON400(
id = "4050046c-63b3-4868-8a22-14b4181d33a6",
Expand Down
5 changes: 5 additions & 0 deletions obp-api/src/main/scala/code/api/constant/constant.scala
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,11 @@ object Constant extends MdcLoggable {
CAN_GRANT_ACCESS_TO_VIEWS,
CAN_REVOKE_ACCESS_TO_VIEWS,
)


final val TECHNOLOGY_LIFTWEB = "liftweb"
final val TECHNOLOGY_HTTP4S = "http4s"

}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package code.api.util.http4s

import cats.effect._
import code.api.APIFailureNewStyle
import code.api.util.ErrorMessages._
import code.api.util.CallContext
import net.liftweb.common.{Failure => LiftFailure}
import net.liftweb.json.{JInt, JString, parseOpt}
import net.liftweb.json.compactRender
import net.liftweb.json.JsonDSL._
import org.http4s._
Expand All @@ -30,6 +29,8 @@ object ErrorResponseConverter {

implicit val formats: Formats = CustomJsonFormats.formats
private val jsonContentType: `Content-Type` = `Content-Type`(MediaType.application.json)
private val internalFieldsFailCode = "failCode"
private val internalFieldsFailMsg = "failMsg"

/**
* OBP standard error response format.
Expand All @@ -51,47 +52,28 @@ object ErrorResponseConverter {
* Convert any error to http4s Response[IO].
*/
def toHttp4sResponse(error: Throwable, callContext: CallContext): IO[Response[IO]] = {
error match {
case e: APIFailureNewStyle => apiFailureToResponse(e, callContext)
case _ => unknownErrorToResponse(error, callContext)
parseApiFailureFromExceptionMessage(error).map { failure =>
createErrorResponse(failure.code, failure.message, callContext)
}.getOrElse {
unknownErrorToResponse(error, callContext)
}
}

/**
* Convert APIFailureNewStyle to http4s Response.
* Uses failCode as HTTP status and failMsg as error message.
*/
def apiFailureToResponse(failure: APIFailureNewStyle, callContext: CallContext): IO[Response[IO]] = {
val errorJson = OBPErrorResponse(failure.failCode, failure.failMsg)
val status = org.http4s.Status.fromInt(failure.failCode).getOrElse(org.http4s.Status.BadRequest)
IO.pure(
Response[IO](status)
.withEntity(toJsonString(errorJson))
.withContentType(jsonContentType)
.putHeaders(org.http4s.Header.Raw(CIString("Correlation-Id"), callContext.correlationId))
)
}

/**
* Convert Lift Box Failure to http4s Response.
* Returns 400 Bad Request with failure message.
*/
def boxFailureToResponse(failure: LiftFailure, callContext: CallContext): IO[Response[IO]] = {
val errorJson = OBPErrorResponse(400, failure.msg)
IO.pure(
Response[IO](org.http4s.Status.BadRequest)
.withEntity(toJsonString(errorJson))
.withContentType(jsonContentType)
.putHeaders(org.http4s.Header.Raw(CIString("Correlation-Id"), callContext.correlationId))
)
private def parseApiFailureFromExceptionMessage(error: Throwable): Option[OBPErrorResponse] = {
Option(error.getMessage).flatMap(parseOpt).flatMap { json =>
(json \ internalFieldsFailCode, json \ internalFieldsFailMsg) match {
case (JInt(code), JString(message)) => Some(OBPErrorResponse(code.toInt, message))
case _ => None
}
}
}

/**
* Convert unknown error to http4s Response.
* Returns 500 Internal Server Error.
*/
def unknownErrorToResponse(e: Throwable, callContext: CallContext): IO[Response[IO]] = {
val errorJson = OBPErrorResponse(500, s"$UnknownError: ${e.getMessage}")
val errorJson = OBPErrorResponse(500, UnknownError)
IO.pure(
Response[IO](org.http4s.Status.InternalServerError)
.withEntity(toJsonString(errorJson))
Expand Down
11 changes: 8 additions & 3 deletions obp-api/src/main/scala/code/api/util/http4s/Http4sSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,14 @@ object Http4sRequestAttributes {
def executeAndRespond[A](req: Request[IO])(f: CallContext => Future[A])(implicit formats: Formats): IO[Response[IO]] = {
implicit val cc: CallContext = req.callContext
for {
result <- IO.fromFuture(IO(f(cc)))
jsonString = prettyRender(Extraction.decompose(result))
response <- Ok(jsonString)
attempted <- IO.fromFuture(IO(f(cc))).attempt
response <- attempted match {
case Right(result) =>
val jsonString = prettyRender(Extraction.decompose(result))
Ok(jsonString)
case Left(error) =>
ErrorResponseConverter.toHttp4sResponse(error, cc)
}
} yield response
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import code.api.util.newstyle.ViewNewStyle
import code.api.util.{APIUtil, ApiRole, CallContext, NewStyle}
import code.util.Helper.MdcLoggable
import com.openbankproject.commons.model._
import com.openbankproject.commons.util.ApiShortVersions
import com.github.dwickern.macros.NameOf.nameOf
import net.liftweb.common.{Box, Empty, Full}
import org.http4s._
Expand Down Expand Up @@ -86,7 +87,7 @@ object ResourceDocMiddleware extends MdcLoggable {
def apply(resourceDocs: ArrayBuffer[ResourceDoc]): HttpRoutes[IO] => HttpRoutes[IO] = { routes =>
Kleisli[HttpF, Request[IO], Response[IO]] { req: Request[IO] =>
// Build initial CallContext from request
OptionT.liftF(Http4sCallContextBuilder.fromRequest(req, "v7.0.0")).flatMap { cc =>
OptionT.liftF(Http4sCallContextBuilder.fromRequest(req, ApiShortVersions.`v7.0.0`.toString)).flatMap { cc =>
ResourceDocMatcher.findResourceDoc(req.method.name, req.uri.path, resourceDocs) match {
case Some(resourceDoc) =>
val ccWithDoc = ResourceDocMatcher.attachToCallContext(cc, resourceDoc)
Expand Down
Loading