mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 15:06:50 +00:00
dynamic_entity_refactor_simplify: DynamicEntity operation move to connector
This commit is contained in:
parent
d074db63b9
commit
f4966d53cb
@ -36,6 +36,9 @@ object ErrorMessages {
|
||||
|
||||
// DynamicEntity Exceptions (OBP-09XXX)
|
||||
val DynamicEntityNotFoundByDynamicEntityId = "OBP-09001: DynamicEntity not found. Please specify a valid value for dynamic_entity_id."
|
||||
val DynamicEntityEntityNameAlreadyExists = "OBP-09002: DynamicEntity's entityName already exists. Please specify a different value for entityName."
|
||||
val DynamicEntityEntityNotExists = "OBP-09003: DynamicEntity not exists. Please check entityName."
|
||||
val DynamicEntityMissArgument = "OBP-09004: DynamicEntity process related argument is missing."
|
||||
|
||||
// General messages (OBP-10XXX)
|
||||
val InvalidJsonFormat = "OBP-10001: Incorrect json format."
|
||||
|
||||
@ -28,11 +28,14 @@ import code.util.Helper
|
||||
import code.views.Views
|
||||
import code.webhook.AccountWebhook
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import com.openbankproject.commons.model.enums.{AccountAttributeType, CardAttributeType, ProductAttributeType}
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityOperation.{CREATE, UPDATE}
|
||||
import com.openbankproject.commons.model.enums.{AccountAttributeType, CardAttributeType, DynamicEntityOperation, ProductAttributeType}
|
||||
import com.openbankproject.commons.model.{AccountApplication, Bank, Customer, CustomerAddress, Product, ProductCollection, ProductCollectionItem, TaxResidence, UserAuthContext, UserAuthContextUpdate, _}
|
||||
import com.tesobe.CacheKeyFromArguments
|
||||
import net.liftweb.common.{Box, Empty, Full}
|
||||
import net.liftweb.http.provider.HTTPParam
|
||||
import net.liftweb.json
|
||||
import net.liftweb.json.{JArray, JBool, JDouble, JInt, JObject, JString, JValue}
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
@ -1397,8 +1400,25 @@ object NewStyle {
|
||||
}
|
||||
}
|
||||
|
||||
def createOrUpdateDynamicEntity(dynamicEntity: DynamicEntityT): Future[Box[DynamicEntityT]] = Future {
|
||||
DynamicEntityProvider.connectorMethodProvider.vend.createOrUpdate(dynamicEntity)
|
||||
def createOrUpdateDynamicEntity(dynamicEntity: DynamicEntityT, callContext: Option[CallContext]): Future[Box[DynamicEntityT]] = {
|
||||
val existsDynamicEntity = DynamicEntityProvider.connectorMethodProvider.vend.getByEntityName(dynamicEntity.entityName)
|
||||
|
||||
val isEntityNameNotChange = existsDynamicEntity.isEmpty ||
|
||||
existsDynamicEntity.filter(_.dynamicEntityId == dynamicEntity.dynamicEntityId).isDefined
|
||||
|
||||
isEntityNameNotChange match {
|
||||
case true => Future {
|
||||
DynamicEntityProvider.connectorMethodProvider.vend.createOrUpdate(dynamicEntity)
|
||||
}
|
||||
case false => {
|
||||
val entityNameExists = existsDynamicEntity.isDefined
|
||||
// validate whether entityName is exists
|
||||
val errorMsg = s"$DynamicEntityEntityNameAlreadyExists current entityName is '${dynamicEntity.entityName}'."
|
||||
Helper.booleanToFuture(errorMsg)(!entityNameExists).map { _ =>
|
||||
DynamicEntityProvider.connectorMethodProvider.vend.createOrUpdate(dynamicEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def deleteDynamicEntity(dynamicEntityId: String): Future[Box[Boolean]] = Future {
|
||||
@ -1413,6 +1433,11 @@ object NewStyle {
|
||||
}
|
||||
}
|
||||
|
||||
def getDynamicEntityByEntityName(entityName : String, callContext: Option[CallContext]): OBPReturnType[Box[DynamicEntityT]] = Future {
|
||||
val boxedDynamicEntity = DynamicEntityProvider.connectorMethodProvider.vend.getByEntityName(entityName)
|
||||
(boxedDynamicEntity, callContext)
|
||||
}
|
||||
|
||||
private[this] val dynamicEntityTTL = APIUtil.getPropsValue(s"dynamicEntity.cache.ttl.seconds", "0").toInt
|
||||
|
||||
def getDynamicEntities(): List[DynamicEntityT] = {
|
||||
@ -1451,6 +1476,50 @@ object NewStyle {
|
||||
i => (unboxFullOrFail(i._1, callContext, s"$CreateTransactionsException"), i._2)
|
||||
}
|
||||
|
||||
def invokeDynamicConnector(operation: DynamicEntityOperation, entityName: String, requestBody: Option[JObject], entityId: Option[String], callContext: Option[CallContext]): OBPReturnType[Box[JValue]] = {
|
||||
val dynamicEntityBox = DynamicEntityProvider.connectorMethodProvider.vend.getByEntityName(entityName)
|
||||
val dynamicEntity = unboxFullOrFail(dynamicEntityBox, callContext, DynamicEntityEntityNotExists)
|
||||
|
||||
if(operation == CREATE || operation == UPDATE) {
|
||||
val jsonTypeMap = Map[String, Class[_]](
|
||||
("boolean", classOf[JBool]),
|
||||
("string", classOf[JString]),
|
||||
("array", classOf[JArray]),
|
||||
("integer", classOf[JInt]),
|
||||
("number", classOf[JDouble]),
|
||||
)
|
||||
val definitionJson = json.parse(dynamicEntity.metadataJson).asInstanceOf[JObject]
|
||||
val entity = (definitionJson \ entityName).asInstanceOf[JObject]
|
||||
val requiredFieldNames: Set[String] = (entity \ "required").asInstanceOf[JArray].arr.map(_.asInstanceOf[JString].s).toSet
|
||||
|
||||
val fieldNameToTypeName: Map[String, String] = (entity \ "properties")
|
||||
.asInstanceOf[JObject]
|
||||
.obj
|
||||
.map(field => (field.name, (field.value \ "type").asInstanceOf[JString].s))
|
||||
.toMap
|
||||
|
||||
val fieldNameToType: Map[String, Class[_]] = fieldNameToTypeName
|
||||
.mapValues(jsonTypeMap(_))
|
||||
val bodyJson = requestBody.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument please supply the requestBody."))
|
||||
val fields = bodyJson.obj.filter(it => fieldNameToType.keySet.contains(it.name))
|
||||
|
||||
// if there are field type are not match the definitions, there must be bug.
|
||||
val invalidTypes = fields.filterNot(it => fieldNameToType(it.name).isInstance(it.value))
|
||||
val invalidTypeNames = invalidTypes.map(_.name).mkString("[", ",", "]")
|
||||
val missingRequiredFields = requiredFieldNames.filterNot(it => fields.exists(_.name == it))
|
||||
val missingFieldNames = missingRequiredFields.mkString("[", ",", "]")
|
||||
|
||||
Helper.booleanToFuture(s"$InvalidJsonFormat these field type not correct: $invalidTypeNames")(invalidTypes.isEmpty).flatMap{ _ =>
|
||||
Helper.booleanToFuture(s"$InvalidJsonFormat some required fields are missing: $missingFieldNames")(missingRequiredFields.isEmpty)
|
||||
}.flatMap{ _ =>
|
||||
Connector.connector.vend.dynamicEntityProcess(operation, entityName, requestBody, entityId, callContext)
|
||||
}
|
||||
|
||||
} else {
|
||||
Connector.connector.vend.dynamicEntityProcess(operation, entityName, requestBody, entityId, callContext)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ import net.liftweb.http.rest.RestHelper
|
||||
import net.liftweb.json.Serialization.write
|
||||
import code.api.util.ExampleValue.{dynamicEntityRequestBodyExample, dynamicEntityResponseBodyExample}
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityFieldType
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityOperation.{CREATE, DELETE, GET_ALL, GET_ONE, UPDATE}
|
||||
import net.liftweb.util.StringHelpers
|
||||
import org.atteo.evo.inflector.English
|
||||
import net.liftweb.json._
|
||||
@ -763,11 +764,11 @@ trait APIMethods400 {
|
||||
postedData <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
validateDynamicEntityJson(json)
|
||||
|
||||
val JField(name, _) = json.asInstanceOf[JObject].obj.head
|
||||
DynamicEntityCommons(name, compactRender(json))
|
||||
val JField(entityName, _) = json.asInstanceOf[JObject].obj.head
|
||||
DynamicEntityCommons(entityName, compactRender(json))
|
||||
}
|
||||
|
||||
Full(dynamicEntity) <- NewStyle.function.createOrUpdateDynamicEntity(postedData)
|
||||
Full(dynamicEntity) <- NewStyle.function.createOrUpdateDynamicEntity(postedData, callContext)
|
||||
} yield {
|
||||
val commonsData: DynamicEntityCommons = dynamicEntity
|
||||
(commonsData.jValue, HttpCode.`201`(callContext))
|
||||
@ -822,7 +823,7 @@ trait APIMethods400 {
|
||||
|
||||
(_, _) <- NewStyle.function.getDynamicEntityById(dynamicEntityId, callContext)
|
||||
|
||||
Full(dynamicEntity) <- NewStyle.function.createOrUpdateDynamicEntity(putData)
|
||||
Full(dynamicEntity) <- NewStyle.function.createOrUpdateDynamicEntity(putData, callContext)
|
||||
} yield {
|
||||
val commonsData: DynamicEntityCommons = dynamicEntity
|
||||
(commonsData.jValue, HttpCode.`200`(callContext))
|
||||
@ -869,41 +870,43 @@ trait APIMethods400 {
|
||||
|
||||
|
||||
lazy val genericEndpoint: OBPEndpoint = {
|
||||
case EntityName(entityName) :: Nil JsonGet req => {
|
||||
cc =>
|
||||
Future {
|
||||
import net.liftweb.json.JsonDSL._
|
||||
val listName = StringHelpers.snakify(English.plural(entityName))
|
||||
val resultList = MockerConnector.getAll(entityName)
|
||||
|
||||
val jValue: JValue = listName -> resultList
|
||||
|
||||
(jValue, HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
case EntityName(entityName) :: Nil JsonGet req => { cc =>
|
||||
val listName = StringHelpers.snakify(English.plural(entityName))
|
||||
for {
|
||||
(Full(resultList: JObject), _) <- NewStyle.function.invokeDynamicConnector(GET_ALL, entityName, None, None, Some(cc))
|
||||
} yield {
|
||||
import net.liftweb.json.JsonDSL._
|
||||
val jValue: JObject = listName -> resultList
|
||||
(jValue, HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
}
|
||||
case EntityName(entityName, id) JsonGet req => {
|
||||
cc =>
|
||||
Future {
|
||||
(MockerConnector.getSingle(entityName, id), HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
case EntityName(entityName, id) JsonGet req => {cc =>
|
||||
for {
|
||||
(Full(entity), _) <- NewStyle.function.invokeDynamicConnector(GET_ONE, entityName, None, Some(id), Some(cc))
|
||||
} yield {
|
||||
(entity, HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
}
|
||||
case EntityName(entityName) :: Nil JsonPost json -> _ => {
|
||||
cc =>
|
||||
Future {
|
||||
(MockerConnector.persist(entityName, json.asInstanceOf[JObject]), HttpCode.`201`(Some(cc)))
|
||||
}
|
||||
case EntityName(entityName) :: Nil JsonPost json -> _ => {cc =>
|
||||
for {
|
||||
(Full(entity), _) <- NewStyle.function.invokeDynamicConnector(CREATE, entityName, Some(json.asInstanceOf[JObject]), None, Some(cc))
|
||||
} yield {
|
||||
(entity, HttpCode.`201`(Some(cc)))
|
||||
}
|
||||
}
|
||||
case EntityName(entityName, id) JsonPut json -> _ => {
|
||||
cc =>
|
||||
Future {
|
||||
(MockerConnector.persist(entityName, json.asInstanceOf[JObject], Some(id)), HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
case EntityName(entityName, id) JsonPut json -> _ => { cc =>
|
||||
for {
|
||||
(Full(entity), _) <- NewStyle.function.invokeDynamicConnector(UPDATE, entityName, Some(json.asInstanceOf[JObject]), Some(id), Some(cc))
|
||||
} yield {
|
||||
(entity, HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
}
|
||||
case EntityName(entityName, id) JsonDelete req => {
|
||||
cc =>
|
||||
Future {
|
||||
(MockerConnector.delete(entityName, id), HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
case EntityName(entityName, id) JsonDelete req => { cc =>
|
||||
for {
|
||||
(Full(deleteResult), _) <- NewStyle.function.invokeDynamicConnector(DELETE, entityName, None, Some(id), Some(cc))
|
||||
} yield {
|
||||
(deleteResult, HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -31,12 +31,12 @@ import code.transactionrequests.{TransactionRequestTypeCharge, TransactionReques
|
||||
import code.users.Users
|
||||
import code.util.Helper._
|
||||
import code.views.Views
|
||||
import com.openbankproject.commons.model.enums.{AccountAttributeType, CardAttributeType, ProductAttributeType}
|
||||
import com.openbankproject.commons.model.enums.{AccountAttributeType, CardAttributeType, DynamicEntityOperation, ProductAttributeType}
|
||||
import com.openbankproject.commons.model.{AccountApplication, Bank, CounterpartyTrait, CustomerAddress, Product, ProductCollection, ProductCollectionItem, TaxResidence, TransactionRequestStatus, UserAuthContext, UserAuthContextUpdate, _}
|
||||
|
||||
import com.tesobe.CacheKeyFromArguments
|
||||
import net.liftweb.common.{Box, Empty, Failure, Full}
|
||||
import net.liftweb.json.Extraction.decompose
|
||||
import net.liftweb.json.JObject
|
||||
import net.liftweb.json.JsonAST.JValue
|
||||
import net.liftweb.mapper.By
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
@ -1862,4 +1862,18 @@ trait Connector extends MdcLoggable with CustomJsonFormats{
|
||||
chargePolicy: String,
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[TransactionId]] = Future{(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
/**
|
||||
* DynamicEntity process function
|
||||
* @param operation type of operation, this is an enumeration
|
||||
* @param entityName DynamicEntity's entity name
|
||||
* @param requestBody content of request
|
||||
* @param entityId id of given DynamicEntity
|
||||
* @param callContext
|
||||
* @return result DynamicEntity process
|
||||
*/
|
||||
def dynamicEntityProcess(operation: DynamicEntityOperation,
|
||||
entityName: String,
|
||||
requestBody: Option[JObject],
|
||||
entityId: Option[String],
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[JValue]] = Future{(Failure(setUnimplementedError), callContext)}
|
||||
}
|
||||
@ -12,6 +12,7 @@ import code.api.util.APIUtil.{OBPReturnType, isValidCurrencyISOCode, saveConnect
|
||||
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.atms.Atms.Atm
|
||||
import code.atms.MappedAtm
|
||||
import code.branches.Branches.Branch
|
||||
@ -51,11 +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.{AccountAttributeType, CardAttributeType, ProductAttributeType, StrongCustomerAuthentication}
|
||||
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.{AccountApplication, AccountAttribute, Product, ProductAttribute, ProductCollectionItem, TaxResidence, _}
|
||||
import com.tesobe.CacheKeyFromArguments
|
||||
import com.tesobe.model.UpdateBankAccount
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.json.JsonAST.{JArray, JBool}
|
||||
import net.liftweb.json.{JObject, JValue}
|
||||
import net.liftweb.mapper.{By, _}
|
||||
import net.liftweb.util.Helpers.{tryo, _}
|
||||
import net.liftweb.util.Mailer
|
||||
@ -2785,4 +2789,29 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
(boxedData, callContext)
|
||||
}
|
||||
|
||||
override def dynamicEntityProcess(operation: DynamicEntityOperation,
|
||||
entityName: String,
|
||||
requestBody: Option[JObject],
|
||||
entityId: Option[String],
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[JValue]] = Future {
|
||||
val processResult: Box[JValue] = operation match {
|
||||
case GET_ALL => Full {
|
||||
JArray(MockerConnector.getAll(entityName).toList)
|
||||
}
|
||||
case GET_ONE => {
|
||||
val boxedEntity: Box[JValue] = MockerConnector.getSingle(entityName, entityId.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument the entityId is required.")))
|
||||
boxedEntity
|
||||
}
|
||||
case CREATE | UPDATE => {
|
||||
val body = requestBody.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument please supply the requestBody."))
|
||||
val persistedEntity = MockerConnector.persist(entityName, body, entityId)
|
||||
Full(persistedEntity)
|
||||
}
|
||||
case DELETE => {
|
||||
val deleteResult = MockerConnector.delete(entityName, entityId.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument the entityId is required. ")))
|
||||
deleteResult.map(JBool(_))
|
||||
}
|
||||
}
|
||||
(processResult, callContext)
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,6 +52,8 @@ object DynamicEntityCommons extends Converter[DynamicEntityT, DynamicEntityCommo
|
||||
trait DynamicEntityProvider {
|
||||
def getById(dynamicEntityId: String): Box[DynamicEntityT]
|
||||
|
||||
def getByEntityName(entityName: String): Box[DynamicEntityT]
|
||||
|
||||
def getDynamicEntities(): List[DynamicEntityT]
|
||||
|
||||
def createOrUpdate(dynamicEntity: DynamicEntityT): Box[DynamicEntityT]
|
||||
|
||||
@ -13,6 +13,10 @@ object MappedDynamicEntityProvider extends DynamicEntityProvider with CustomJson
|
||||
By(DynamicEntity.DynamicEntityId, dynamicEntityId)
|
||||
)
|
||||
|
||||
override def getByEntityName(entityName: String): Box[DynamicEntityT] = DynamicEntity.find(
|
||||
By(DynamicEntity.EntityName, entityName)
|
||||
)
|
||||
|
||||
override def getDynamicEntities(): List[DynamicEntity] = {
|
||||
DynamicEntity.findAll()
|
||||
}
|
||||
|
||||
@ -45,6 +45,7 @@ object PemCertificateRole extends OBPEnumeration[PemCertificateRole] {
|
||||
object PSP_PI extends Value
|
||||
}
|
||||
//------api enumerations end ----
|
||||
|
||||
sealed trait DynamicEntityFieldType extends EnumValue
|
||||
object DynamicEntityFieldType extends OBPEnumeration[DynamicEntityFieldType]{
|
||||
object string extends Value
|
||||
@ -53,4 +54,16 @@ object DynamicEntityFieldType extends OBPEnumeration[DynamicEntityFieldType]{
|
||||
object boolean extends Value
|
||||
// object array extends Value
|
||||
// object `object` extends Value //TODO in the future, we consider support nested type
|
||||
}
|
||||
|
||||
/**
|
||||
* connector support operation type for DynamicEntity
|
||||
*/
|
||||
sealed trait DynamicEntityOperation extends EnumValue
|
||||
object DynamicEntityOperation extends OBPEnumeration[DynamicEntityOperation] {
|
||||
object GET_ALL extends Value
|
||||
object GET_ONE extends Value
|
||||
object CREATE extends Value
|
||||
object UPDATE extends Value
|
||||
object DELETE extends Value
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user