Skip to content

Commit

Permalink
Merge pull request #2467 from hongwei1/develop
Browse files Browse the repository at this point in the history
feature/V510 new getTransactionRequests with attributes
  • Loading branch information
simonredfern authored Dec 17, 2024
2 parents f09d2e5 + 5994555 commit 1279a98
Show file tree
Hide file tree
Showing 26 changed files with 594 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2293,12 +2293,19 @@ object SwaggerDefinitionsJSON {
transaction_request_types = List(transactionRequestTypeJSONV210)
)

val transactionRequestAttributeJsonV400 = TransactionRequestAttributeJsonV400(
name = transactionRequestAttributeNameExample.value,
attribute_type = transactionRequestAttributeTypeExample.value,
value = transactionRequestAttributeValueExample.value
)

val transactionRequestBodyCounterpartyJSON = TransactionRequestBodyCounterpartyJSON(
counterpartyIdJson,
amountOfMoneyJsonV121,
"A description for the transaction to the counterparty",
description = "A description for the transaction to the counterparty",
chargePolicyExample.value,
Some(futureDateExample.value)
Some(futureDateExample.value),
Some(List(transactionRequestAttributeJsonV400))
)

val transactionRequestBodySEPAJSON = TransactionRequestBodySEPAJSON(
Expand Down Expand Up @@ -4752,12 +4759,6 @@ object SwaggerDefinitionsJSON {
`type` = transactionRequestAttributeTypeExample.value,
value = transactionRequestAttributeValueExample.value
)

val transactionRequestAttributeJsonV400 = TransactionRequestAttributeJsonV400(
name = transactionRequestAttributeNameExample.value,
`type` = transactionRequestAttributeTypeExample.value,
value = transactionRequestAttributeValueExample.value
)

val transactionRequestAttributesResponseJson = TransactionRequestAttributesResponseJson(
transaction_request_attributes = List(transactionRequestAttributeResponseJson)
Expand Down Expand Up @@ -4863,7 +4864,8 @@ object SwaggerDefinitionsJSON {
start_date = DateWithDayExampleObject,
end_date = DateWithDayExampleObject,
challenges = List(challengeJsonV400),
charge = transactionRequestChargeJsonV200
charge = transactionRequestChargeJsonV200,
attributes=Some(List(bankAttributeBankResponseJsonV400)),
)

val postSimpleCounterpartyJson400 = PostSimpleCounterpartyJson400(
Expand Down
2 changes: 1 addition & 1 deletion obp-api/src/main/scala/code/api/util/ExampleValue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ object ExampleValue {
lazy val transactionRequestAttributeNameExample = ConnectorField("HOUSE_RENT", s"Transaction Request attribute name")
glossaryItems += makeGlossaryItem("Transaction Requests.attributeName", transactionRequestAttributeNameExample)

lazy val transactionRequestAttributeTypeExample = ConnectorField("DATE_WITH_DAY", s"Transaction Request attribute type.")
lazy val transactionRequestAttributeTypeExample = ConnectorField("STRING", s"Transaction Request attribute type.")
glossaryItems += makeGlossaryItem("Transaction Requests.attributeType", transactionRequestAttributeTypeExample)

lazy val transactionRequestAttributeValueExample = ConnectorField("123456789", s"Transaction Request attribute value.")
Expand Down
32 changes: 28 additions & 4 deletions obp-api/src/main/scala/code/api/util/NewStyle.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3174,17 +3174,39 @@ object NewStyle extends MdcLoggable{
}
}

def getTransactionRequestIdsByAttributeNameValues(bankId: BankId, params: Map[String, List[String]],
callContext: Option[CallContext]): OBPReturnType[List[String]] = {
def getTransactionRequestIdsByAttributeNameValues(
bankId: BankId,
params: Map[String, List[String]],
isPersonal: Boolean,
callContext: Option[CallContext]
): OBPReturnType[List[String]] = {
Connector.connector.vend.getTransactionRequestIdsByAttributeNameValues(
bankId: BankId,
params: Map[String, List[String]],
isPersonal,
callContext: Option[CallContext]
) map {
i => (connectorEmptyResponse(i._1, callContext), i._2)
}
}


def getByAttributeNameValues(
bankId: BankId,
params: Map[String, List[String]],
isPersonal: Boolean,
callContext: Option[CallContext]
): OBPReturnType[List[TransactionRequestAttributeTrait]] = {
Connector.connector.vend.getByAttributeNameValues(
bankId: BankId,
params: Map[String, List[String]],
isPersonal,
callContext
) map {
i => (connectorEmptyResponse(i._1, callContext), i._2)
}
}

def createOrUpdateTransactionRequestAttribute(bankId: BankId,
transactionRequestId: TransactionRequestId,
transactionRequestAttributeId: Option[String],
Expand All @@ -3207,12 +3229,14 @@ object NewStyle extends MdcLoggable{

def createTransactionRequestAttributes(bankId: BankId,
transactionRequestId: TransactionRequestId,
transactionRequestAttributes: List[TransactionRequestAttributeTrait],
transactionRequestAttributes: List[TransactionRequestAttributeJsonV400],
isPersonal: Boolean,
callContext: Option[CallContext]): OBPReturnType[List[TransactionRequestAttributeTrait]] = {
Connector.connector.vend.createTransactionRequestAttributes(
bankId: BankId,
transactionRequestId: TransactionRequestId,
transactionRequestAttributes: List[TransactionRequestAttributeTrait],
transactionRequestAttributes: List[TransactionRequestAttributeJsonV400],
isPersonal: Boolean,
callContext: Option[CallContext]
) map {
i => (connectorEmptyResponse(i._1, callContext), i._2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ case class TransactionRequestBodyCounterpartyJSON(
value: AmountOfMoneyJsonV121,
description: String,
charge_policy: String,
future_date: Option[String] = None
future_date: Option[String] = None,
attributes: Option[List[TransactionRequestAttributeJsonV400]]= None,
) extends TransactionRequestCommonBodyJSON

// the data from endpoint, extract as valid JSON
Expand Down
46 changes: 41 additions & 5 deletions obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1206,9 +1206,11 @@ trait APIMethods400 extends MdcLoggable {
}
} yield (transactionRequest, callContext)
}

(transactionRequestAttribute, callContext) <- NewStyle.function.getTransactionRequestAttributes(bankId, transactionRequest.id, callContext)
} yield {

(JSONFactory400.createTransactionRequestWithChargeJSON(transactionRequest, challenges), HttpCode.`202`(callContext))
(JSONFactory400.createTransactionRequestWithChargeJSON(transactionRequest, challenges, transactionRequestAttribute), HttpCode.`202`(callContext))
}
}
}
Expand Down Expand Up @@ -1253,7 +1255,7 @@ trait APIMethods400 extends MdcLoggable {
failMsg = s"$InvalidJsonFormat The `Type` field can only accept the following field: " +
s"${TransactionRequestAttributeType.DOUBLE}(12.1234), ${TransactionRequestAttributeType.STRING}(TAX_NUMBER), ${TransactionRequestAttributeType.INTEGER}(123) and ${TransactionRequestAttributeType.DATE_WITH_DAY}(2012-04-23)"
transactionRequestAttributeType <- NewStyle.function.tryons(failMsg, 400, callContext) {
TransactionRequestAttributeType.withName(postedData.`type`)
TransactionRequestAttributeType.withName(postedData.attribute_type)
}
(transactionRequestAttribute, callContext) <- NewStyle.function.createOrUpdateTransactionRequestAttribute(
bankId,
Expand Down Expand Up @@ -1391,7 +1393,7 @@ trait APIMethods400 extends MdcLoggable {
failMsg = s"$InvalidJsonFormat The `Type` field can only accept the following field: " +
s"${TransactionRequestAttributeType.DOUBLE}(12.1234), ${TransactionRequestAttributeType.STRING}(TAX_NUMBER), ${TransactionRequestAttributeType.INTEGER}(123) and ${TransactionRequestAttributeType.DATE_WITH_DAY}(2012-04-23)"
transactionRequestAttributeType <- NewStyle.function.tryons(failMsg, 400, callContext) {
TransactionRequestAttributeType.withName(postedData.`type`)
TransactionRequestAttributeType.withName(postedData.attribute_type)
}
(_, callContext) <- NewStyle.function.getTransactionRequestAttributeById(transactionRequestAttributeId, callContext)
(transactionRequestAttribute, callContext) <- NewStyle.function.createOrUpdateTransactionRequestAttribute(
Expand Down Expand Up @@ -12413,6 +12415,28 @@ object APIMethods400 extends RestHelper with APIMethods400 {
toCounterpartyId = transactionRequestBodyCounterparty.to.counterparty_id
(toCounterparty, callContext) <- NewStyle.function.getCounterpartyByCounterpartyId(CounterpartyId(toCounterpartyId), callContext)

transactionRequestAttributes <- if(transactionRequestBodyCounterparty.attributes.isDefined && transactionRequestBodyCounterparty.attributes.head.length > 0 ) {

val attributes = transactionRequestBodyCounterparty.attributes.head

val failMsg = s"$InvalidJsonFormat The attribute `type` field can only accept the following field: " +
s"${TransactionRequestAttributeType.DOUBLE}(12.1234)," +
s" ${TransactionRequestAttributeType.STRING}(TAX_NUMBER), " +
s"${TransactionRequestAttributeType.INTEGER}(123) and " +
s"${TransactionRequestAttributeType.DATE_WITH_DAY}(2012-04-23)"

for{
_ <- NewStyle.function.tryons(failMsg, 400, callContext) {
attributes.map(attribute => TransactionRequestAttributeType.withName(attribute.attribute_type))
}
}yield{
attributes
}

} else {
Future.successful(List.empty[TransactionRequestAttributeJsonV400])
}

(counterpartyLimitBox, callContext) <- Connector.connector.vend.getCounterpartyLimit(
bankId.value,
accountId.value,
Expand All @@ -12422,7 +12446,6 @@ object APIMethods400 extends RestHelper with APIMethods400 {
)
_<- if(counterpartyLimitBox.isDefined){
for{

counterpartyLimit <- Future.successful(counterpartyLimitBox.head)
maxSingleAmount = counterpartyLimit.maxSingleAmount
maxMonthlyAmount = counterpartyLimit.maxMonthlyAmount
Expand Down Expand Up @@ -12576,6 +12599,14 @@ object APIMethods400 extends RestHelper with APIMethods400 {
getScaMethodAtInstance(transactionRequestType.value).toOption,
None,
callContext)

_ <- NewStyle.function.createTransactionRequestAttributes(
bankId: BankId,
createdTransactionRequest.id,
transactionRequestAttributes,
true,
callContext: Option[CallContext]
)
} yield (createdTransactionRequest, callContext)
}
case AGENT_CASH_WITHDRAWAL => {
Expand Down Expand Up @@ -12758,8 +12789,13 @@ object APIMethods400 extends RestHelper with APIMethods400 {
}
}
(challenges, callContext) <- NewStyle.function.getChallengesByTransactionRequestId(createdTransactionRequest.id.value, callContext)
(transactionRequestAttributes, callContext) <- NewStyle.function.getTransactionRequestAttributes(
bankId,
createdTransactionRequest.id,
callContext
)
} yield {
(JSONFactory400.createTransactionRequestWithChargeJSON(createdTransactionRequest, challenges), HttpCode.`201`(callContext))
(JSONFactory400.createTransactionRequestWithChargeJSON(createdTransactionRequest, challenges, transactionRequestAttributes), HttpCode.`201`(callContext))
}
}

Expand Down
20 changes: 10 additions & 10 deletions obp-api/src/main/scala/code/api/v4_0_0/JSONFactory4.0.0.scala
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ case class TransactionRequestWithChargeJSON400(
start_date: Date,
end_date: Date,
challenges: List[ChallengeJsonV400],
charge : TransactionRequestChargeJsonV200
charge : TransactionRequestChargeJsonV200,
attributes: Option[List[BankAttributeBankResponseJsonV400]]
)
case class PostHistoricalTransactionAtBankJson(
from_account_id: String,
Expand Down Expand Up @@ -549,12 +550,6 @@ case class TransactionAttributesResponseJson(
transaction_attributes: List[TransactionAttributeResponseJson]
)

case class TransactionRequestAttributeJsonV400(
name: String,
`type`: String,
value: String,
)

case class TransactionRequestAttributeResponseJson(
transaction_request_attribute_id: String,
name: String,
Expand Down Expand Up @@ -1260,8 +1255,8 @@ object JSONFactory400 {
account_attributes = accountAttributes.map(createAccountAttributeJson)
)

def createTransactionRequestWithChargeJSON(tr : TransactionRequest, challenges: List[ChallengeTrait]) : TransactionRequestWithChargeJSON400 = {
new TransactionRequestWithChargeJSON400(
def createTransactionRequestWithChargeJSON(tr : TransactionRequest, challenges: List[ChallengeTrait], transactionRequestAttribute: List[TransactionRequestAttributeTrait]) : TransactionRequestWithChargeJSON400 = {
TransactionRequestWithChargeJSON400(
id = stringOrNull(tr.id.value),
`type` = stringOrNull(tr.`type`),
from = try{TransactionRequestAccountJsonV140 (
Expand Down Expand Up @@ -1322,7 +1317,12 @@ object JSONFactory400 {
charge = try {TransactionRequestChargeJsonV200 (summary = stringOrNull(tr.charge.summary),
value = AmountOfMoneyJsonV121(currency = stringOrNull(tr.charge.value.currency),
amount = stringOrNull(tr.charge.value.amount))
)} catch {case _ : Throwable => null}
)} catch {case _ : Throwable => null},
attributes = if(transactionRequestAttribute.isEmpty) None else Some(transactionRequestAttribute
.map(attribute =>BankAttributeBankResponseJsonV400(
attribute.name,
attribute.value
)))
)
}

Expand Down
76 changes: 76 additions & 0 deletions obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2795,7 +2795,83 @@ trait APIMethods510 {
}
}

resourceDocs += ResourceDoc(
getTransactionRequests,
implementedInApiVersion,
nameOf(getTransactionRequests),
"GET",
"/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transaction-requests",
"Get Transaction Requests." ,
"""Returns transaction requests for account specified by ACCOUNT_ID at bank specified by BANK_ID.
|
|The VIEW_ID specified must be 'owner' and the user must have access to this view.
|
|Version 2.0.0 now returns charge information.
|
|Transaction Requests serve to initiate transactions that may or may not proceed. They contain information including:
|
|* Transaction Request Id
|* Type
|* Status (INITIATED, COMPLETED)
|* Challenge (in order to confirm the request)
|* From Bank / Account
|* Details including Currency, Value, Description and other initiation information specific to each type. (Could potentialy include a list of future transactions.)
|* Related Transactions
|
|PSD2 Context: PSD2 requires transparency of charges to the customer.
|This endpoint provides the charge that would be applied if the Transaction Request proceeds - and a record of that charge there after.
|The customer can proceed with the Transaction by answering the security challenge.
|
|We support query transaction request by attribute
|URL params example:/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transaction-requests?invoiceNumber=123&referenceNumber=456
|
""".stripMargin,
EmptyBody,
transactionRequestWithChargeJSONs210,
List(
UserNotLoggedIn,
BankNotFound,
BankAccountNotFound,
UserNoPermissionAccessView,
ViewDoesNotPermitAccess,
GetTransactionRequestsException,
UnknownError
),
List(apiTagTransactionRequest, apiTagPSD2PIS))

lazy val getTransactionRequests: OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transaction-requests" :: Nil JsonGet req => {
cc => implicit val ec = EndpointContext(Some(cc))
for {
(Full(u), callContext) <- authenticatedAccess(cc)
_ <- NewStyle.function.isEnabledTransactionRequests(callContext)
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
(fromAccount, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext)
view <- NewStyle.function.checkAccountAccessAndGetView(viewId, BankIdAccountId(bankId, accountId), Full(u), callContext)
_ <- Helper.booleanToFuture(
s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(nameOf(ViewDefinition.canSeeTransactionRequests_)).dropRight(1)}` permission on the View(${viewId.value})",
cc=callContext){
view.canSeeTransactionRequests
}
(transactionRequests, callContext) <- Future(Connector.connector.vend.getTransactionRequests210(u, fromAccount, callContext)) map {
unboxFullOrFail(_, callContext, GetTransactionRequestsException)
}
(transactionRequestAttributes, callContext) <- NewStyle.function.getByAttributeNameValues(bankId, req.params, true, callContext)
transactionRequestIds = transactionRequestAttributes.map(_.transactionRequestId)

transactionRequestsFiltered = if(req.params.isEmpty)
transactionRequests
else
transactionRequests.filter(transactionRequest => transactionRequestIds.contains(transactionRequest.id))

} yield {
val json = JSONFactory510.createTransactionRequestJSONs(transactionRequestsFiltered, transactionRequestAttributes)

(json, HttpCode.`200`(callContext))
}
}
}

staticResourceDocs += ResourceDoc(
getAccountAccessByUserId,
implementedInApiVersion,
Expand Down
Loading

0 comments on commit 1279a98

Please sign in to comment.