mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 17:56:46 +00:00
feature/persist_DynaicEntity_to_DB
This commit is contained in:
parent
fcee7bd010
commit
49aa1bbe98
@ -29,6 +29,7 @@ package bootstrap.liftweb
|
||||
import java.io.{File, FileInputStream}
|
||||
import java.util.{Locale, TimeZone}
|
||||
|
||||
import code.DynamicData.DynamicData
|
||||
import code.accountapplication.MappedAccountApplication
|
||||
import code.accountattribute.MappedAccountAttribute
|
||||
import code.accountholders.MapperAccountHolders
|
||||
@ -660,6 +661,7 @@ object ToSchemify {
|
||||
WebUiProps,
|
||||
Authorisation,
|
||||
DynamicEntity,
|
||||
DynamicData,
|
||||
AccountIdMapping,
|
||||
)++ APIBuilder_Connector.allAPIBuilderModels
|
||||
}
|
||||
|
||||
@ -40,6 +40,7 @@ object ErrorMessages {
|
||||
val DynamicEntityNotExists = "OBP-09003: DynamicEntity not exists. Please check entityName."
|
||||
val DynamicEntityMissArgument = "OBP-09004: DynamicEntity process related argument is missing."
|
||||
val EntityNotFoundByEntityId = "OBP-09005: Entity not found. Please specify a valid value for entityId."
|
||||
val DynamicEntityOperationNotAllowed = "OBP-09006: Operation is not allowed, because Current DynamicEntity have upload data, must to delete all the data before this operation."
|
||||
|
||||
// General messages (OBP-10XXX)
|
||||
val InvalidJsonFormat = "OBP-10001: Incorrect json format."
|
||||
|
||||
@ -1452,7 +1452,7 @@ object NewStyle {
|
||||
|
||||
def getDynamicEntityById(dynamicEntityId : String, callContext: Option[CallContext]): OBPReturnType[DynamicEntityT] = {
|
||||
val dynamicEntityBox: Box[DynamicEntityT] = DynamicEntityProvider.connectorMethodProvider.vend.getById(dynamicEntityId)
|
||||
val dynamicEntity = unboxFullOrFail(dynamicEntityBox, callContext, DynamicEntityNotFoundByDynamicEntityId)
|
||||
val dynamicEntity = unboxFullOrFail(dynamicEntityBox, callContext, DynamicEntityNotFoundByDynamicEntityId, 404)
|
||||
Future{
|
||||
(dynamicEntity, callContext)
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
|
||||
import code.api.util.APIUtil._
|
||||
import code.api.util.ApiRole._
|
||||
import code.api.util.ApiTag._
|
||||
import code.api.util.ErrorMessages.{AccountNotFound, AllowedAttemptsUsedUp, BankAccountNotFound, BankNotFound, CounterpartyBeneficiaryPermit, InsufficientAuthorisationToCreateTransactionRequest, InvalidAccountIdFormat, InvalidBankIdFormat, InvalidChallengeAnswer, InvalidChallengeType, InvalidChargePolicy, InvalidISOCurrencyCode, InvalidJsonFormat, InvalidNumber, InvalidTransactionRequesChallengeId, InvalidTransactionRequestCurrency, InvalidTransactionRequestType, NoViewPermission, NotPositiveAmount, TransactionDisabled, TransactionRequestStatusNotInitiated, TransactionRequestTypeHasChanged, UnknownError, UserHasMissingRoles, UserNoPermissionAccessView, UserNotLoggedIn, ViewNotFound}
|
||||
import code.api.util.ErrorMessages.{AccountNotFound, AllowedAttemptsUsedUp, BankAccountNotFound, BankNotFound, CounterpartyBeneficiaryPermit, InsufficientAuthorisationToCreateTransactionRequest, InvalidAccountIdFormat, InvalidBankIdFormat, InvalidChallengeAnswer, InvalidChallengeType, InvalidChargePolicy, InvalidISOCurrencyCode, InvalidJsonFormat, InvalidNumber, InvalidTransactionRequesChallengeId, InvalidTransactionRequestCurrency, InvalidTransactionRequestType, NoViewPermission, NotPositiveAmount, TransactionDisabled, TransactionRequestStatusNotInitiated, TransactionRequestTypeHasChanged, UnknownError, UserHasMissingRoles, UserNoPermissionAccessView, UserNotLoggedIn, ViewNotFound, DynamicEntityOperationNotAllowed}
|
||||
import code.api.util.ExampleValue.{dynamicEntityRequestBodyExample, dynamicEntityResponseBodyExample}
|
||||
import code.api.util.NewStyle.HttpCode
|
||||
import code.api.util._
|
||||
@ -808,6 +808,13 @@ trait APIMethods400 {
|
||||
(Full(u), callContext) <- authorizedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, canUpdateDynamicEntity, callContext)
|
||||
|
||||
// Check whether there are uploaded data, only if no uploaded data allow to update DynamicEntity.
|
||||
(entity, _) <- NewStyle.function.getDynamicEntityById(dynamicEntityId, callContext)
|
||||
(isExists, _) <- NewStyle.function.invokeDynamicConnector(IS_EXISTS_DATA, entity.entityName, None, None, callContext)
|
||||
_ <- Helper.booleanToFuture(DynamicEntityOperationNotAllowed) {
|
||||
isExists.isDefined && isExists.contains(JBool(false))
|
||||
}
|
||||
|
||||
jsonObject = json.asInstanceOf[JObject]
|
||||
dynamicEntity = DynamicEntityCommons(jsonObject, Some(dynamicEntityId))
|
||||
Full(result) <- NewStyle.function.createOrUpdateDynamicEntity(dynamicEntity, callContext)
|
||||
@ -848,6 +855,12 @@ trait APIMethods400 {
|
||||
for {
|
||||
(Full(u), callContext) <- authorizedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, canDeleteDynamicEntity, callContext)
|
||||
// Check whether there are uploaded data, only if no uploaded data allow to delete DynamicEntity.
|
||||
(entity, _) <- NewStyle.function.getDynamicEntityById(dynamicEntityId, callContext)
|
||||
(isExists, _) <- NewStyle.function.invokeDynamicConnector(IS_EXISTS_DATA, entity.entityName, None, None, callContext)
|
||||
_ <- Helper.booleanToFuture(DynamicEntityOperationNotAllowed) {
|
||||
isExists.isDefined && isExists.contains(JBool(false))
|
||||
}
|
||||
deleted: Box[Boolean] <- NewStyle.function.deleteDynamicEntity(dynamicEntityId)
|
||||
} yield {
|
||||
(deleted, HttpCode.`200`(callContext))
|
||||
|
||||
@ -2,9 +2,8 @@ package code.api.v4_0_0
|
||||
|
||||
import code.api.util.APIUtil.{Catalogs, ResourceDoc, authenticationRequiredMessage, emptyObjectJson, generateUUID, notCore, notOBWG, notPSD2}
|
||||
import code.api.util.ApiTag.{apiTagApi, apiTagNewStyle}
|
||||
import code.api.util.ErrorMessages.{InvalidJsonFormat, InvalidUrl, UnknownError, UserHasMissingRoles, UserNotLoggedIn}
|
||||
import code.api.util.ErrorMessages.{InvalidJsonFormat, UnknownError, UserHasMissingRoles, UserNotLoggedIn}
|
||||
import code.api.util.{ApiTag, ApiVersion, NewStyle}
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.json.JsonDSL._
|
||||
import net.liftweb.json._
|
||||
import net.liftweb.util.StringHelpers
|
||||
@ -30,31 +29,6 @@ object MockerConnector {
|
||||
|
||||
def definitionsMap = NewStyle.function.getDynamicEntities().map(it => (it.entityName, DynamicEntityInfo(it.metadataJson, it.entityName))).toMap
|
||||
|
||||
//(id, entityName) -> entity
|
||||
val persistedEntities = scala.collection.mutable.Map[(String, String), JObject]()
|
||||
|
||||
def persist(entityName: String, requestBody: JObject, id: Option[String] = None) = {
|
||||
val idValue = id.orElse(Some(generateUUID()))
|
||||
val idName = StringUtils.uncapitalize(entityName) + "Id"
|
||||
val entityToPersist = this.definitionsMap(entityName).toResponse(requestBody, id)
|
||||
val haveIdEntity = (entityToPersist \ idName) match {
|
||||
case JNothing => JObject(JField(idName, JString(idValue.get)) :: entityToPersist.obj)
|
||||
case _ => entityToPersist
|
||||
}
|
||||
persistedEntities.put((idValue.get, entityName), haveIdEntity)
|
||||
haveIdEntity
|
||||
}
|
||||
|
||||
def getSingle(entityName: String, id: String) = {
|
||||
persistedEntities.get(id, entityName)
|
||||
}
|
||||
|
||||
def getAll(entityName: String) = persistedEntities.filter(pair => pair._1._2 == entityName).values
|
||||
|
||||
def delete(entityName: String, id: String): Box[Boolean] = {
|
||||
persistedEntities.remove(id -> entityName).map(_ => true)
|
||||
}
|
||||
|
||||
def doc = {
|
||||
val docs: Seq[ResourceDoc] = definitionsMap.values.flatMap(createDocs).toSeq
|
||||
collection.mutable.ArrayBuffer(docs:_*)
|
||||
|
||||
@ -56,7 +56,7 @@ object OBPAPI4_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w
|
||||
val endpointsOf4_0_0 = getEndpoints(Implementations4_0_0) - Implementations4_0_0.genericEndpoint
|
||||
|
||||
// if old version ResourceDoc objects have the same name endpoint with new version, omit old version ResourceDoc.
|
||||
val allResourceDocs = collectResourceDocs(OBPAPI3_1_0.allResourceDocs,
|
||||
def allResourceDocs = collectResourceDocs(OBPAPI3_1_0.allResourceDocs,
|
||||
Implementations4_0_0.resourceDocs,
|
||||
MockerConnector.doc)
|
||||
// all endpoints
|
||||
@ -68,6 +68,7 @@ object OBPAPI4_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w
|
||||
|
||||
// Filter the possible endpoints by the disabled / enabled Props settings and add them together
|
||||
val routes : List[OBPEndpoint] =
|
||||
Implementations4_0_0.genericEndpoint::
|
||||
Implementations4_0_0.root :: // For now we make this mandatory
|
||||
getAllowedEndpoints(endpoints, allResourceDocs)
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ package code.bankconnectors
|
||||
import java.util.Date
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
import code.DynamicData.DynamicDataProvider
|
||||
import code.accountapplication.AccountApplicationX
|
||||
import code.accountattribute.AccountAttributeX
|
||||
import code.accountholders.AccountHolders
|
||||
@ -10,10 +11,7 @@ import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
|
||||
import code.api.cache.Caching
|
||||
import code.api.util.APIUtil.{OBPReturnType, isValidCurrencyISOCode, saveConnectorMetric, stringOrNull}
|
||||
import code.api.util.ErrorMessages._
|
||||
import com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SCA
|
||||
import code.api.util._
|
||||
import code.api.v4_0_0.MockerConnector
|
||||
import code.api.v4_0_0.MockerConnector.persistedEntities
|
||||
import code.atms.Atms.Atm
|
||||
import code.atms.MappedAtm
|
||||
import code.branches.Branches.Branch
|
||||
@ -54,12 +52,14 @@ import code.views.Views
|
||||
import com.google.common.cache.CacheBuilder
|
||||
import com.nexmo.client.NexmoClient
|
||||
import com.nexmo.client.sms.messages.TextMessage
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityOperation.{CREATE, DELETE, GET_ALL, GET_ONE, UPDATE}
|
||||
import com.openbankproject.commons.model.enums.{AccountAttributeType, CardAttributeType, DynamicEntityOperation, ProductAttributeType, StrongCustomerAuthentication}
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityOperation._
|
||||
import com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SCA
|
||||
import com.openbankproject.commons.model.enums._
|
||||
import com.openbankproject.commons.model.{AccountApplication, AccountAttribute, Product, ProductAttribute, ProductCollectionItem, TaxResidence, _}
|
||||
import com.tesobe.CacheKeyFromArguments
|
||||
import com.tesobe.model.UpdateBankAccount
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.json
|
||||
import net.liftweb.json.{JArray, JBool, JObject, JValue}
|
||||
import net.liftweb.mapper.{By, _}
|
||||
import net.liftweb.util.Helpers.{tryo, _}
|
||||
@ -2819,31 +2819,41 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
return Helper.booleanToFuture(s"$InvalidJsonFormat entityId is required for $operation operation.")(entityId.isEmpty || StringUtils.isBlank(entityId.get))
|
||||
.map(it => (it.map(_.asInstanceOf[JValue]), callContext))
|
||||
}
|
||||
if (!persistedEntities.contains(entityId.get -> entityName)) {
|
||||
val id = entityId.get
|
||||
val id = entityId.get
|
||||
val value = DynamicDataProvider.connectorMethodProvider.vend.get(entityName, id)
|
||||
if (value.isEmpty) {
|
||||
return Helper.booleanToFuture(s"$EntityNotFoundByEntityId please check: entityId = $id", 404)(false)
|
||||
.map(it => (it.map(_.asInstanceOf[JValue]), callContext))
|
||||
}
|
||||
}
|
||||
|
||||
val op: Any = operation
|
||||
Future {
|
||||
val processResult: Box[JValue] = operation match {
|
||||
val processResult: Box[JValue] = op match {
|
||||
case GET_ALL => Full {
|
||||
JArray(MockerConnector.getAll(entityName).toList)
|
||||
val dataList = DynamicDataProvider.connectorMethodProvider.vend.getAll(entityName)
|
||||
JArray(dataList)
|
||||
}
|
||||
case GET_ONE => {
|
||||
val boxedEntity: Box[JValue] = MockerConnector.getSingle(entityName, entityId.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument the entityId is required.")))
|
||||
val boxedEntity: Box[JValue] = DynamicDataProvider.connectorMethodProvider.vend
|
||||
.get(entityName, entityId.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument the entityId is required.")))
|
||||
.map(it => json.parse(it.dataJson))
|
||||
boxedEntity
|
||||
}
|
||||
case CREATE | UPDATE => {
|
||||
val body = requestBody.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument please supply the requestBody."))
|
||||
val id = if(operation == CREATE) None else entityId
|
||||
val persistedEntity = MockerConnector.persist(entityName, body, id)
|
||||
Full(persistedEntity)
|
||||
val boxedEntity: Box[JValue] = DynamicDataProvider.connectorMethodProvider.vend.saveOrUpdate(entityName, body, id)
|
||||
.map(it => json.parse(it.dataJson))
|
||||
boxedEntity
|
||||
}
|
||||
case DELETE => {
|
||||
val deleteResult = MockerConnector.delete(entityName, entityId.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument the entityId is required. ")))
|
||||
deleteResult.map(JBool(_))
|
||||
val id = entityId.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument the entityId is required. "))
|
||||
val deleteResult: Boolean = DynamicDataProvider.connectorMethodProvider.vend.delete(entityName, id)
|
||||
Full(JBool(deleteResult))
|
||||
}
|
||||
case IS_EXISTS_DATA => {
|
||||
val isExistsData: Boolean = DynamicDataProvider.connectorMethodProvider.vend.existsData(entityName)
|
||||
Full(JBool(isExistsData))
|
||||
}
|
||||
}
|
||||
(processResult, callContext)
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
package code.DynamicData
|
||||
|
||||
import com.openbankproject.commons.model.{Converter, JsonFieldReName}
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.json.JObject
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
object DynamicDataProvider extends SimpleInjector {
|
||||
|
||||
val connectorMethodProvider = new Inject(buildOne _) {}
|
||||
|
||||
def buildOne: MappedDynamicDataProvider.type = MappedDynamicDataProvider
|
||||
}
|
||||
|
||||
trait DynamicDataT {
|
||||
def dynamicDataId: Option[String]
|
||||
def dynamicEntityName: String
|
||||
def dataJson: String
|
||||
}
|
||||
|
||||
case class DynamicDataCommons(dynamicEntityName: String,
|
||||
dataJson: String,
|
||||
dynamicDataId: Option[String] = None
|
||||
) extends DynamicDataT with JsonFieldReName
|
||||
|
||||
object DynamicDataCommons extends Converter[DynamicDataT, DynamicDataCommons]
|
||||
|
||||
|
||||
trait DynamicDataProvider {
|
||||
def saveOrUpdate(entityName: String, requestBody: JObject, id: Option[String] = None): Box[DynamicData]
|
||||
def get(entityName: String, id: String): Box[DynamicData]
|
||||
def getAll(entityName: String): List[JObject]
|
||||
def delete(entityName: String, id: String): Boolean
|
||||
def existsData(dynamicEntityName: String): Boolean
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
package code.DynamicData
|
||||
|
||||
import code.api.util.APIUtil.generateUUID
|
||||
import code.api.util.CustomJsonFormats
|
||||
import code.util.MappedUUID
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.json
|
||||
import net.liftweb.json.JObject
|
||||
import net.liftweb.json.JsonDSL._
|
||||
import net.liftweb.mapper._
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
object MappedDynamicDataProvider extends DynamicDataProvider with CustomJsonFormats{
|
||||
override def saveOrUpdate(entityName: String, requestBody: JObject, id: Option[String]): Box[DynamicData] = {
|
||||
val idValue: String = id.getOrElse(generateUUID())
|
||||
val dynamicData: DynamicData = id match{
|
||||
case Some(i) => get(entityName, i).openOrThrowException(s"not exists DynamicData's data of dynamicEntityName=$entityName, dynameicDataId=$i")
|
||||
case _ => DynamicData.create.DynamicDataId(idValue).DynamicEntityName(entityName)
|
||||
}
|
||||
val idName = StringUtils.uncapitalize(entityName) + "Id"
|
||||
|
||||
val dataToPersist: JObject = requestBody ~ (idName -> idValue)
|
||||
val dataStr = json.compactRender(dataToPersist)
|
||||
tryo {
|
||||
dynamicData.DataJson(dataStr).saveMe()
|
||||
}
|
||||
}
|
||||
|
||||
override def get(entityName: String, id: String): Box[DynamicData] = DynamicData.find(By(DynamicData.DynamicDataId, id))
|
||||
|
||||
override def getAll(entityName: String): List[JObject] = DynamicData.findAll(By(DynamicData.DynamicEntityName, entityName))
|
||||
.map(it => json.parse(it.dataJson)).map(_.asInstanceOf[JObject])
|
||||
|
||||
override def delete(entityName: String, id: String): Boolean = DynamicData.bulkDelete_!!(By(DynamicData.DynamicDataId, id))
|
||||
|
||||
override def existsData(dynamicEntityName: String): Boolean = {
|
||||
DynamicData.findAll(By(DynamicData.DynamicEntityName, dynamicEntityName), MaxRows(1))
|
||||
.nonEmpty
|
||||
}
|
||||
}
|
||||
|
||||
class DynamicData extends DynamicDataT with LongKeyedMapper[DynamicData] with IdPK {
|
||||
|
||||
override def getSingleton = DynamicData
|
||||
|
||||
object DynamicDataId extends MappedUUID(this)
|
||||
object DynamicEntityName extends MappedString(this, 255)
|
||||
|
||||
object DataJson extends MappedText(this)
|
||||
|
||||
override def dynamicDataId: Option[String] = Option(DynamicDataId.get)
|
||||
override def dynamicEntityName: String = DynamicEntityName.get
|
||||
override def dataJson: String = DataJson.get
|
||||
}
|
||||
|
||||
object DynamicData extends DynamicData with LongKeyedMetaMapper[DynamicData] {
|
||||
override def dbIndexes = UniqueIndex(DynamicDataId) :: super.dbIndexes
|
||||
}
|
||||
|
||||
@ -202,7 +202,7 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
// update a not exists DynamicEntity
|
||||
val request404 = (v4_0_0_Request / "management" / "dynamic_entities" / "not-exists-id" ).PUT <@(user1)
|
||||
val response404 = makePutRequest(request404, compactRender(updateRequest))
|
||||
Then("We should get a 400")
|
||||
Then("We should get a 404")
|
||||
response404.code should equal(404)
|
||||
response404.body.extract[ErrorMessage].message should startWith (DynamicEntityNotFoundByDynamicEntityId)
|
||||
}
|
||||
|
||||
@ -74,4 +74,5 @@ object DynamicEntityOperation extends OBPEnumeration[DynamicEntityOperation] {
|
||||
object CREATE extends Value
|
||||
object UPDATE extends Value
|
||||
object DELETE extends Value
|
||||
object IS_EXISTS_DATA extends Value
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user