Skip to content

Commit 4d60b8a

Browse files
authored
Merge pull request #2601 from hongwei1/feature/cardanoTest
Feature/cardano test
2 parents 09639a5 + 196cc8c commit 4d60b8a

27 files changed

Lines changed: 2103 additions & 799 deletions

‎.gitignore‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,10 @@ obp-api/src/main/scala/code/api/v3_0_0/custom/
2828

2929
# Marketing diagram generation outputs
3030
marketing_diagram_generation/outputs/*
31+
32+
.bloop
33+
.bsp
34+
.specstory
35+
project/project
36+
coursier
37+
*.code-workspace

‎obp-api/src/main/scala/bootstrap/liftweb/Boot.scala‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import code.accountattribute.MappedAccountAttribute
3535
import code.accountholders.MapperAccountHolders
3636
import code.actorsystem.ObpActorSystem
3737
import code.api.Constant._
38-
import code.api.ResourceDocs1_4_0.ResourceDocs300.{ResourceDocs310, ResourceDocs400, ResourceDocs500, ResourceDocs510}
38+
import code.api.ResourceDocs1_4_0.ResourceDocs300.{ResourceDocs310, ResourceDocs400, ResourceDocs500, ResourceDocs510, ResourceDocs600}
3939
import code.api.ResourceDocs1_4_0._
4040
import code.api._
4141
import code.api.attributedefinition.AttributeDefinition
@@ -46,7 +46,6 @@ import code.api.util.ApiRole.CanCreateEntitlementAtAnyBank
4646
import code.api.util.ErrorMessages.MandatoryPropertyIsNotSet
4747
import code.api.util._
4848
import code.api.util.migration.Migration
49-
import code.api.util.CommonsEmailWrapper
5049
import code.api.util.migration.Migration.DbFunction
5150
import code.apicollection.ApiCollection
5251
import code.apicollectionendpoint.ApiCollectionEndpoint
@@ -467,6 +466,7 @@ class Boot extends MdcLoggable {
467466
enableVersionIfAllowed(ApiVersion.v4_0_0)
468467
enableVersionIfAllowed(ApiVersion.v5_0_0)
469468
enableVersionIfAllowed(ApiVersion.v5_1_0)
469+
enableVersionIfAllowed(ApiVersion.v6_0_0)
470470
enableVersionIfAllowed(ApiVersion.`dynamic-endpoint`)
471471
enableVersionIfAllowed(ApiVersion.`dynamic-entity`)
472472

@@ -525,6 +525,7 @@ class Boot extends MdcLoggable {
525525
LiftRules.statelessDispatch.append(ResourceDocs400)
526526
LiftRules.statelessDispatch.append(ResourceDocs500)
527527
LiftRules.statelessDispatch.append(ResourceDocs510)
528+
LiftRules.statelessDispatch.append(ResourceDocs600)
528529
////////////////////////////////////////////////////
529530

530531

‎obp-api/src/main/scala/code/api/OBPRestHelper.scala‎

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,36 +27,32 @@ TESOBE (http://www.tesobe.com/)
2727

2828
package code.api
2929

30-
import java.net.URLDecoder
3130
import code.api.Constant._
3231
import code.api.OAuthHandshake._
33-
import code.api.builder.AccountInformationServiceAISApi.APIMethods_AccountInformationServiceAISApi
34-
import code.api.util.APIUtil.{getClass, _}
32+
import code.api.util.APIUtil._
3533
import code.api.util.ErrorMessages.{InvalidDAuthHeaderToken, UserIsDeleted, UsernameHasBeenLocked, attemptedToOpenAnEmptyBox}
3634
import code.api.util._
37-
import code.api.v3_0_0.APIMethods300
38-
import code.api.v3_1_0.APIMethods310
39-
import code.api.v4_0_0.{APIMethods400, OBPAPI4_0_0}
35+
import code.api.v4_0_0.OBPAPI4_0_0
4036
import code.api.v5_0_0.OBPAPI5_0_0
4137
import code.api.v5_1_0.OBPAPI5_1_0
38+
import code.api.v6_0_0.OBPAPI6_0_0
4239
import code.loginattempts.LoginAttempt
4340
import code.model.dataAccess.AuthUser
4441
import code.util.Helper.{MdcLoggable, ObpS}
4542
import com.alibaba.ttl.TransmittableThreadLocal
4643
import com.openbankproject.commons.model.ErrorMessage
4744
import com.openbankproject.commons.util.{ApiVersion, ReflectUtils, ScannedApiVersion}
48-
import net.liftweb.common.{Box, Full, _}
45+
import net.liftweb.common._
4946
import net.liftweb.http.rest.RestHelper
5047
import net.liftweb.http.{JsonResponse, LiftResponse, LiftRules, Req, S, TransientRequestMemoize}
5148
import net.liftweb.json.Extraction
5249
import net.liftweb.json.JsonAST.JValue
53-
import net.liftweb.util.{Helpers, NamedPF, Props, ThreadGlobal}
5450
import net.liftweb.util.Helpers.tryo
51+
import net.liftweb.util.{Helpers, NamedPF, Props, ThreadGlobal}
5552

53+
import java.net.URLDecoder
5654
import java.util.{Locale, ResourceBundle}
57-
import scala.collection.immutable.List
5855
import scala.collection.mutable.ArrayBuffer
59-
import scala.math.Ordering
6056
import scala.util.control.NoStackTrace
6157
import scala.xml.{Node, NodeSeq}
6258

@@ -642,23 +638,40 @@ trait OBPRestHelper extends RestHelper with MdcLoggable {
642638
result
643639
}
644640

641+
def isAutoValidate(doc: ResourceDoc, autoValidateAll: Boolean): Boolean = { //note: auto support v4.0.0 and later versions
642+
doc.isValidateEnabled || (autoValidateAll && !doc.isValidateDisabled && {
643+
// Auto support v4.0.0 and all later versions
644+
val docVersion = doc.implementedInApiVersion
645+
// Check if the version is v4.0.0 or later by comparing the version string
646+
docVersion match {
647+
case v: ScannedApiVersion =>
648+
// Extract version numbers and compare
649+
val versionStr = v.apiShortVersion.replace("v", "")
650+
val parts = versionStr.split("\\.")
651+
if (parts.length >= 2) {
652+
val major = parts(0).toInt
653+
val minor = parts(1).toInt
654+
major > 4 || (major == 4 && minor >= 0)
655+
} else {
656+
false
657+
}
658+
case _ => false
659+
}
660+
})
661+
}
662+
645663
protected def registerRoutes(routes: List[OBPEndpoint],
646664
allResourceDocs: ArrayBuffer[ResourceDoc],
647665
apiPrefix:OBPEndpoint => OBPEndpoint,
648666
autoValidateAll: Boolean = false): Unit = {
649-
650-
def isAutoValidate(doc: ResourceDoc): Boolean = { //note: only support v5.1.0, v5.0.0 and v4.0.0 at the moment.
651-
doc.isValidateEnabled || (autoValidateAll && !doc.isValidateDisabled && List(OBPAPI5_1_0.version,OBPAPI5_0_0.version,OBPAPI4_0_0.version).contains(doc.implementedInApiVersion))
652-
}
653-
654667
for(route <- routes) {
655668
// one endpoint can have multiple ResourceDocs, so here use filter instead of find, e.g APIMethods400.Implementations400.createTransactionRequest
656669
val resourceDocs = allResourceDocs.filter(_.partialFunction == route)
657670

658671
if(resourceDocs.isEmpty) {
659672
oauthServe(apiPrefix(route), None)
660673
} else {
661-
val (autoValidateDocs, other) = resourceDocs.partition(isAutoValidate)
674+
val (autoValidateDocs, other) = resourceDocs.partition(isAutoValidate(_, autoValidateAll))
662675
// autoValidateAll or doc isAutoValidate, just wrapped to auth check endpoint
663676
autoValidateDocs.foreach { doc =>
664677
val wrappedEndpoint = doc.wrappedWithAuthCheck(route)

‎obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocs140.scala‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package code.api.ResourceDocs1_4_0
22

33
import code.api.OBPRestHelper
4-
import com.openbankproject.commons.util.{ApiVersion,ApiVersionStatus}
54
import code.util.Helper.MdcLoggable
5+
import com.openbankproject.commons.util.{ApiVersion, ApiVersionStatus}
66

77

88
object ResourceDocs140 extends OBPRestHelper with ResourceDocsAPIMethods with MdcLoggable {
@@ -136,5 +136,21 @@ object ResourceDocs300 extends OBPRestHelper with ResourceDocsAPIMethods with Md
136136
})
137137
})
138138
}
139+
140+
object ResourceDocs600 extends OBPRestHelper with ResourceDocsAPIMethods with MdcLoggable {
141+
val version: ApiVersion = ApiVersion.v6_0_0
142+
val versionStatus = ApiVersionStatus.BLEEDING_EDGE.toString
143+
val routes = List(
144+
ImplementationsResourceDocs.getResourceDocsObpV400,
145+
ImplementationsResourceDocs.getResourceDocsSwagger,
146+
ImplementationsResourceDocs.getBankLevelDynamicResourceDocsObp,
147+
// ImplementationsResourceDocs.getStaticResourceDocsObp
148+
)
149+
routes.foreach(route => {
150+
oauthServe(apiPrefix {
151+
route
152+
})
153+
})
154+
}
139155

140156
}

‎obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import code.api.v3_1_0._
1616
import code.api.v4_0_0._
1717
import code.api.v5_0_0._
1818
import code.api.v5_1_0._
19+
import code.api.v6_0_0._
1920
import code.branches.Branches.{Branch, DriveUpString, LobbyString}
2021
import code.connectormethod.{JsonConnectorMethod, JsonConnectorMethodMethodBody}
2122
import code.consent.ConsentStatus
@@ -5697,6 +5698,47 @@ object SwaggerDefinitionsJSON {
56975698
permission_name = CAN_GRANT_ACCESS_TO_VIEWS,
56985699
extra_data = Some(List(SYSTEM_ACCOUNTANT_VIEW_ID, SYSTEM_AUDITOR_VIEW_ID))
56995700
)
5701+
5702+
5703+
lazy val cardanoPaymentJsonV600 = CardanoPaymentJsonV600(
5704+
address = "addr_test1qpv3se9ghq87ud29l0a8asy8nlqwd765e5zt4rc2z4mktqulwagn832cuzcjknfyxwzxz2p2kumx6n58tskugny6mrqs7fd12",
5705+
amount = CardanoAmountJsonV600(
5706+
quantity = 1000000,
5707+
unit = "lovelace"
5708+
),
5709+
assets = Some(List(CardanoAssetJsonV600(
5710+
policy_id = "policy1234567890abcdef",
5711+
asset_name = "4f47435241",
5712+
quantity = 10
5713+
)))
5714+
)
5715+
5716+
// Example for Send ADA with Token only (no ADA amount)
5717+
lazy val cardanoPaymentTokenOnlyJsonV510 = CardanoPaymentJsonV600(
5718+
address = "addr_test1qpv3se9ghq87ud29l0a8asy8nlqwd765e5zt4rc2z4mktqulwagn832cuzcjknfyxwzxz2p2kumx6n58tskugny6mrqs7fd12",
5719+
amount = CardanoAmountJsonV600(
5720+
quantity = 0,
5721+
unit = "lovelace"
5722+
),
5723+
assets = Some(List(CardanoAssetJsonV600(
5724+
policy_id = "policy1234567890abcdef",
5725+
asset_name = "4f47435241",
5726+
quantity = 10
5727+
)))
5728+
)
5729+
5730+
lazy val cardanoMetadataStringJsonV600 = CardanoMetadataStringJsonV600(
5731+
string = "Hello Cardano"
5732+
)
5733+
5734+
lazy val transactionRequestBodyCardanoJsonV600 = TransactionRequestBodyCardanoJsonV600(
5735+
to = cardanoPaymentJsonV600,
5736+
value = amountOfMoneyJsonV121,
5737+
passphrase = "password1234!",
5738+
description = descriptionExample.value,
5739+
metadata = Some(Map("202507022319" -> cardanoMetadataStringJsonV600))
5740+
)
5741+
57005742
//The common error or success format.
57015743
//Just some helper format to use in Json
57025744
case class NotSupportedYet()

‎obp-api/src/main/scala/code/api/util/APIUtil.scala‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ import code.util.{Helper, JsonSchemaUtil}
7171
import code.views.system.AccountAccess
7272
import code.views.{MapperViews, Views}
7373
import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue
74-
import javassist.CannotCompileException
7574
import com.github.dwickern.macros.NameOf.{nameOf, nameOfType}
7675
import com.openbankproject.commons.ExecutionContext.Implicits.global
7776
import com.openbankproject.commons.model._
@@ -81,7 +80,7 @@ import com.openbankproject.commons.util.Functions.Implicits._
8180
import com.openbankproject.commons.util._
8281
import dispatch.url
8382
import javassist.expr.{ExprEditor, MethodCall}
84-
import javassist.{ClassPool, LoaderClassPath}
83+
import javassist.{CannotCompileException, ClassPool, LoaderClassPath}
8584
import net.liftweb.actor.LAFuture
8685
import net.liftweb.common._
8786
import net.liftweb.http._
@@ -2747,6 +2746,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
27472746
case ApiVersion.v4_0_0 => LiftRules.statelessDispatch.append(v4_0_0.OBPAPI4_0_0)
27482747
case ApiVersion.v5_0_0 => LiftRules.statelessDispatch.append(v5_0_0.OBPAPI5_0_0)
27492748
case ApiVersion.v5_1_0 => LiftRules.statelessDispatch.append(v5_1_0.OBPAPI5_1_0)
2749+
case ApiVersion.v6_0_0 => LiftRules.statelessDispatch.append(v6_0_0.OBPAPI6_0_0)
27502750
case ApiVersion.`dynamic-endpoint` => LiftRules.statelessDispatch.append(OBPAPIDynamicEndpoint)
27512751
case ApiVersion.`dynamic-entity` => LiftRules.statelessDispatch.append(OBPAPIDynamicEntity)
27522752
case version: ScannedApiVersion => LiftRules.statelessDispatch.append(ScannedApis.versionMapScannedApis(version))

‎obp-api/src/main/scala/code/api/util/ErrorMessages.scala‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ object ErrorMessages {
7979
// General messages (OBP-10XXX)
8080
val InvalidJsonFormat = "OBP-10001: Incorrect json format."
8181
val InvalidNumber = "OBP-10002: Invalid Number. Could not convert value to a number."
82-
val InvalidISOCurrencyCode = "OBP-10003: Invalid Currency Value. It should be three letters ISO Currency Code. "
82+
val InvalidISOCurrencyCode = """OBP-10003: Invalid Currency Value. Expected a 3-letter ISO Currency Code (e.g., 'USD', 'EUR') or 'lovelace' for Cardano transactions.""".stripMargin
8383
val FXCurrencyCodeCombinationsNotSupported = "OBP-10004: ISO Currency code combination not supported for FX. Please modify the FROM_CURRENCY_CODE or TO_CURRENCY_CODE. "
8484
val InvalidDateFormat = "OBP-10005: Invalid Date Format. Could not convert value to a Date."
8585
val InvalidCurrency = "OBP-10006: Invalid Currency Value."

‎obp-api/src/main/scala/code/api/util/NewStyle.scala‎

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -769,17 +769,37 @@ object NewStyle extends MdcLoggable{
769769
def isEnabledTransactionRequests(callContext: Option[CallContext]): Future[Box[Unit]] = Helper.booleanToFuture(failMsg = TransactionRequestsNotEnabled, cc=callContext)(APIUtil.getPropsAsBoolValue("transactionRequests_enabled", false))
770770

771771
/**
772-
* Wraps a Future("try") block around the function f and
773-
* @param f - the block of code to evaluate
774-
* @return <ul>
775-
* <li>Future(result of the evaluation of f) if f doesn't throw any exception
776-
* <li>a Failure if f throws an exception with message = failMsg and code = failCode
777-
* </ul>
778-
*/
779-
def tryons[T](failMsg: String, failCode: Int = 400, callContext: Option[CallContext])(f: => T)(implicit m: Manifest[T]): Future[T]= {
772+
* Wraps a computation `f` in a Future, capturing exceptions and returning detailed error messages.
773+
*
774+
* @param failMsg Base error message to return if the computation fails.
775+
* @param failCode HTTP status code to return on failure (default: 400).
776+
* @param callContext Optional call context for logging or metadata.
777+
* @param f The computation to execute (call-by-name to defer evaluation).
778+
* @param m Implicit Manifest for type `T` (handled by Scala compiler).
779+
* @return Future[T] Success: Result of `f`; Failure: Detailed error message.
780+
*/
781+
def tryons[T](
782+
failMsg: String,
783+
failCode: Int = 400,
784+
callContext: Option[CallContext]
785+
)(f: => T)(implicit m: Manifest[T]): Future[T] = {
780786
Future {
781-
tryo {
782-
f
787+
try {
788+
// Attempt to execute `f` and wrap the result in `Full` (success) or `Failure` (error)
789+
tryo(f) match {
790+
case Full(result) =>
791+
Full(result) // Success: Forward the result
792+
case Failure(msg, _, _) =>
793+
// `tryo` encountered an exception (e.g., validation error)
794+
Failure(s"$failMsg. Details: $msg", Empty, Empty)
795+
case Empty =>
796+
// Edge case: Empty result (unlikely but handled defensively)
797+
Failure(s"$failMsg. Details: Empty result", Empty, Empty)
798+
}
799+
} catch {
800+
case e: Exception =>
801+
// Directly caught exception (e.g., JSON parsing error)
802+
Failure(s"$failMsg. Details: ${e.getMessage}", Full(e), Empty)
783803
}
784804
} map {
785805
x => unboxFullOrFail(x, callContext, failMsg, failCode)

‎obp-api/src/main/scala/code/api/util/migration/Migration.scala‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ object Migration extends MdcLoggable {
8686
addFastFirehoseAccountsView(startedBeforeSchemifier)
8787
addFastFirehoseAccountsMaterializedView(startedBeforeSchemifier)
8888
alterUserAuthContextColumnKeyAndValueLength(startedBeforeSchemifier)
89+
alterMappedTransactionRequestFieldsLengthMigration(startedBeforeSchemifier)
8990
dropIndexAtColumnUsernameAtTableAuthUser(startedBeforeSchemifier)
9091
dropIndexAtUserAuthContext()
9192
alterWebhookColumnUrlLength()
@@ -403,6 +404,19 @@ object Migration extends MdcLoggable {
403404
}
404405
}
405406
}
407+
408+
private def alterMappedTransactionRequestFieldsLengthMigration(startedBeforeSchemifier: Boolean): Boolean = {
409+
if(startedBeforeSchemifier == true) {
410+
logger.warn(s"Migration.database.alterMappedTransactionRequestFieldsLengthMigration(true) cannot be run before Schemifier.")
411+
true
412+
} else {
413+
val name = nameOf(alterMappedTransactionRequestFieldsLengthMigration(startedBeforeSchemifier))
414+
runOnce(name) {
415+
MigrationOfMappedTransactionRequestFieldsLength.alterMappedTransactionRequestFieldsLength(name)
416+
}
417+
}
418+
}
419+
406420
private def dropIndexAtColumnUsernameAtTableAuthUser(startedBeforeSchemifier: Boolean): Boolean = {
407421
if(startedBeforeSchemifier == true) {
408422
logger.warn(s"Migration.database.dropIndexAtColumnUsernameAtTableAuthUser(true) cannot be run before Schemifier.")

0 commit comments

Comments
 (0)