Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ marketing_diagram_generation/outputs/*
.specstory
project/project
coursier
metals.sbt
3 changes: 2 additions & 1 deletion obp-api/src/main/resources/props/sample.props.template
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ transactionRequests_enabled=false
transactionRequests_connector=mapped

## Transaction Request Types that are supported on this server. Possible values might include SANDBOX_TAN, COUNTERPARTY, SEPA, FREE_FORM
transactionRequests_supported_types=SANDBOX_TAN,COUNTERPARTY,SEPA,ACCOUNT_OTP,ACCOUNT,SIMPLE
transactionRequests_supported_types=SANDBOX_TAN,COUNTERPARTY,SEPA,ACCOUNT_OTP,ACCOUNT,SIMPLE,HOLD

## Transaction request challenge threshold. Level at which challenge is created and needs to be answered.
## The Currency is EUR unless set with transactionRequests_challenge_currency.
Expand Down Expand Up @@ -1075,6 +1075,7 @@ database_messages_scheduler_interval=3600
# -- SCA (Strong Customer Authentication) method for OTP challenge-------
# ACCOUNT_OTP_INSTRUCTION_TRANSPORT=DUMMY
# SIMPLE_OTP_INSTRUCTION_TRANSPORT=DUMMY
# HOLD_OTP_INSTRUCTION_TRANSPORT=DUMMY
# SEPA_OTP_INSTRUCTION_TRANSPORT=DUMMY
# FREE_FORM_OTP_INSTRUCTION_TRANSPORT=DUMMY
# COUNTERPARTY_OTP_INSTRUCTION_TRANSPORT=DUMMY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5820,6 +5820,12 @@ object SwaggerDefinitionsJSON {
description = descriptionExample.value
)

// HOLD sample (V600)
lazy val transactionRequestBodyHoldJsonV600 = TransactionRequestBodyHoldJsonV600(
value = amountOfMoneyJsonV121,
description = descriptionExample.value
)

//The common error or success format.
//Just some helper format to use in Json
case class NotSupportedYet()
Expand Down
12 changes: 12 additions & 0 deletions obp-api/src/main/scala/code/api/util/migration/Migration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ object Migration extends MdcLoggable {
populateTheFieldDeletedAtResourceUser(startedBeforeSchemifier)
populateTheFieldIsActiveAtProductAttribute(startedBeforeSchemifier)
alterColumnUsernameProviderFirstnameAndLastnameAtAuthUser(startedBeforeSchemifier)
populateMissingProviderAtAuthUser(startedBeforeSchemifier)
alterColumnEmailAtResourceUser(startedBeforeSchemifier)
alterColumnNameAtProductFee(startedBeforeSchemifier)
addFastFirehoseAccountsView(startedBeforeSchemifier)
Expand Down Expand Up @@ -347,6 +348,17 @@ object Migration extends MdcLoggable {
}
}
}
private def populateMissingProviderAtAuthUser(startedBeforeSchemifier: Boolean): Boolean = {
if(startedBeforeSchemifier == true) {
logger.warn(s"Migration.database.populateMissingProviderAtAuthUser(true) cannot be run before Schemifier.")
true
} else {
val name = nameOf(populateMissingProviderAtAuthUser(startedBeforeSchemifier))
runOnce(name) {
MigrationOfAuthUser.populateMissingProviderWithLocalIdentity(name)
}
}
}
private def alterColumnEmailAtResourceUser(startedBeforeSchemifier: Boolean): Boolean = {
if(startedBeforeSchemifier == true) {
logger.warn(s"Migration.database.alterColumnEmailAtResourceUser(true) cannot be run before Schemifier.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package code.api.util.migration
import java.time.format.DateTimeFormatter
import java.time.{ZoneId, ZonedDateTime}

import code.api.Constant
import code.api.util.APIUtil
import code.api.util.migration.Migration.{DbFunction, saveLog}
import code.util.Helper
Expand Down Expand Up @@ -74,6 +75,45 @@ object MigrationOfAuthUser {
}
}

def populateMissingProviderWithLocalIdentity(name: String): Boolean = {
DbFunction.tableExists(AuthUser) match {
case true =>
val startDate = System.currentTimeMillis()
val commitId: String = APIUtil.gitCommit
var isSuccessful = false

// Make back up
DbFunction.makeBackUpOfTable(AuthUser)

val updatedRows =
for {
user <- AuthUser.findAll()
providerValue = Option(user.provider.get).map(_.trim).getOrElse("") if providerValue.isEmpty
} yield {
user.provider(Constant.localIdentityProvider).saveMe()
}

val endDate = System.currentTimeMillis()
val comment: String =
s"""Updated number of rows:
|${updatedRows.size}
|""".stripMargin
isSuccessful = true
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
isSuccessful

case false =>
val startDate = System.currentTimeMillis()
val commitId: String = APIUtil.gitCommit
val isSuccessful = false
val endDate = System.currentTimeMillis()
val comment: String =
s"""${AuthUser._dbTableNameLC} table does not exist""".stripMargin
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
isSuccessful
}
}

def dropIndexAtColumnUsername(name: String): Boolean = {
DbFunction.tableExists(AuthUser) match {
case true =>
Expand Down
111 changes: 104 additions & 7 deletions obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
package code.api.v6_0_0

import code.api.{APIFailureNewStyle, DirectLogin, ObpApiFailure}
import code.api.v6_0_0.JSONFactory600
import code.accountattribute.AccountAttributeX
import code.api.{DirectLogin, ObpApiFailure}
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
import code.api.util.APIUtil._
import code.api.util.ApiRole.{CanCreateEntitlementAtOneBank, CanReadDynamicResourceDocsAtOneBank, canCreateBank, canDeleteRateLimiting, canReadCallLimits, canSetCallLimits}
import code.api.util.ApiRole._
import code.api.util.ApiTag._
import code.api.util.ErrorMessages.{$UserNotLoggedIn, InvalidDateFormat, InvalidJsonFormat, UnknownError, _}
import code.api.util.FutureUtil.EndpointContext
import code.api.util.{APIUtil, ErrorMessages, NewStyle, RateLimitingUtil}
import code.api.util.NewStyle.HttpCode
import code.api.v5_0_0.{JSONFactory500, PostBankJson500}
import code.api.util.{APIUtil, ErrorMessages, NewStyle, RateLimitingUtil}
import code.api.v3_0_0.JSONFactory300
import code.api.v5_0_0.JSONFactory500
import code.api.v6_0_0.JSONFactory600.{createActiveCallLimitsJsonV600, createCallLimitJsonV600, createCurrentUsageJson}
import code.bankconnectors.LocalMappedConnectorInternal
import code.bankconnectors.LocalMappedConnectorInternal._
import code.entitlement.Entitlement
import code.model._
import code.ratelimiting.RateLimitingDI
import code.util.Helper
import code.util.Helper.SILENCE_IS_GOLDEN
Expand All @@ -23,11 +25,10 @@ import com.github.dwickern.macros.NameOf.nameOf
import com.openbankproject.commons.ExecutionContext.Implicits.global
import com.openbankproject.commons.model._
import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion}
import net.liftweb.common.{Box, Empty, Full}
import net.liftweb.common.{Empty, Full}
import net.liftweb.http.rest.RestHelper

import java.text.SimpleDateFormat
import java.util.Date
import scala.collection.immutable.{List, Nil}
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.Future
Expand All @@ -49,6 +50,102 @@ trait APIMethods600 {
val codeContext = CodeContext(staticResourceDocs, apiRelations)


staticResourceDocs += ResourceDoc(
createTransactionRequestHold,
implementedInApiVersion,
nameOf(createTransactionRequestHold),
"POST",
"/banks/BANK_ID/accounts/ACCOUNT_ID/owner/transaction-request-types/HOLD/transaction-requests",
"Create Transaction Request (HOLD)",
s"""
|
|Create a transaction request to move funds from the account to its Holding Account.
|If the Holding Account does not exist, it will be created automatically.
|
|${transactionRequestGeneralText}
|
""".stripMargin,
transactionRequestBodyHoldJsonV600,
transactionRequestWithChargeJSON400,
List(
$UserNotLoggedIn,
$BankNotFound,
$BankAccountNotFound,
InsufficientAuthorisationToCreateTransactionRequest,
InvalidTransactionRequestType,
InvalidJsonFormat,
NotPositiveAmount,
InvalidTransactionRequestCurrency,
TransactionDisabled,
UnknownError
),
List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2)
)

lazy val createTransactionRequestHold: OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transaction-request-types" ::
"HOLD" :: "transaction-requests" :: Nil JsonPost json -> _ =>
cc => implicit val ec = EndpointContext(Some(cc))
val transactionRequestType = TransactionRequestType("HOLD")
LocalMappedConnectorInternal.createTransactionRequest(bankId, accountId, viewId , transactionRequestType, json)
}

// --- GET Holding Account by Parent ---
staticResourceDocs += ResourceDoc(
getHoldingAccountByReleaser,
implementedInApiVersion,
nameOf(getHoldingAccountByReleaser),
"GET",
"/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/holding-accounts",
"Get Holding Accounts By Releaser",
s"""
|
|Return the first Holding Account linked to the given releaser account via account attribute `RELEASER_ACCOUNT_ID`.
|Response is wrapped in a list and includes account attributes.
|
""".stripMargin,
EmptyBody,
moderatedCoreAccountsJsonV300,
List(
$UserNotLoggedIn,
$BankNotFound,
$BankAccountNotFound,
$UserNoPermissionAccessView,
UnknownError
),
List(apiTagAccount)
)

lazy val getHoldingAccountByReleaser: OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "holding-accounts" :: Nil JsonGet _ =>
cc => implicit val ec = EndpointContext(Some(cc))
for {
(user @Full(u), _, _, view, callContext) <- SS.userBankAccountView
// Find accounts by attribute RELEASER_ACCOUNT_ID
(accountIdsBox, callContext) <- AccountAttributeX.accountAttributeProvider.vend.getAccountIdsByParams(bankId, Map("RELEASER_ACCOUNT_ID" -> List(accountId.value))) map { ids => (ids, callContext) }
accountIds = accountIdsBox.getOrElse(Nil)
// load the first holding account
holdingOpt <- {
def firstHolding(ids: List[String]): Future[Option[BankAccount]] = ids match {
case Nil => Future.successful(None)
case id :: tail =>
NewStyle.function.getBankAccount(bankId, AccountId(id), callContext).flatMap { case (acc, cc) =>
if (acc.accountType == "HOLDING") Future.successful(Some(acc)) else firstHolding(tail)
}
}
firstHolding(accountIds)
}
holding <- NewStyle.function.tryons($BankAccountNotFound, 404, callContext) { holdingOpt.get }
moderatedAccount <- Future { holding.moderatedBankAccount(view, BankIdAccountId(holding.bankId, holding.accountId), user, callContext) } map {
x => unboxFullOrFail(x, callContext, UnknownError)
}
(attributes, callContext) <- NewStyle.function.getAccountAttributesByAccount(bankId, holding.accountId, callContext)
} yield {
val accountsJson = JSONFactory300.createFirehoseCoreBankAccountJSON(List(moderatedAccount), Some(attributes))
(accountsJson, HttpCode.`200`(callContext))
}
}

staticResourceDocs += ResourceDoc(
getCurrentCallsLimit,
implementedInApiVersion,
Expand Down
6 changes: 6 additions & 0 deletions obp-api/src/main/scala/code/api/v6_0_0/JSONFactory6.0.0.scala
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ case class TransactionRequestBodyEthSendRawTransactionJsonV600(
description: String
)

// ---------------- HOLD models (V600) ----------------
case class TransactionRequestBodyHoldJsonV600(
value: AmountOfMoneyJsonV121,
description: String
) extends TransactionRequestCommonBodyJSON

case class UserJsonV600(
user_id: String,
email : String,
Expand Down
Loading