Merge remote-tracking branch 'Hongwei/feature/dynamic_entity_refactor_simplify' into develop

This commit is contained in:
hongwei 2019-09-18 08:00:52 +02:00
commit 45b28acafd
15 changed files with 269 additions and 63 deletions

View File

@ -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."

View File

@ -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,10 @@ 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]] = {
Connector.connector.vend.dynamicEntityProcess(operation, entityName, requestBody, entityId, callContext)
}
}
}

View File

@ -20,11 +20,12 @@ import code.transactionrequests.TransactionRequests.TransactionRequestTypes.{app
import code.util.Helper
import com.github.dwickern.macros.NameOf.nameOf
import com.openbankproject.commons.model._
import net.liftweb.common.{Box, Full}
import net.liftweb.common.{Box, Failure, Full}
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))
@ -866,44 +867,61 @@ trait APIMethods400 {
}
}
}
private def unboxResult[T](box: Box[T]): T = {
if(box.isInstanceOf[Failure]) {
throw new Exception(box.asInstanceOf[Failure].msg)
}
box.openOrThrowException("impossible error")
}
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 {
(box: Box[JArray], _) <- NewStyle.function.invokeDynamicConnector(GET_ALL, entityName, None, None, Some(cc))
// resultList = APIUtil.unboxFullOrFail(box, Some(cc))
resultList = unboxResult(box)
} 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 {
(box: Box[JObject], _) <- NewStyle.function.invokeDynamicConnector(GET_ONE, entityName, None, Some(id), Some(cc))
// entity = APIUtil.unboxFullOrFail(box, Some(cc))
entity = unboxResult(box)
} 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 {
(box: Box[JObject], _) <- NewStyle.function.invokeDynamicConnector(CREATE, entityName, Some(json.asInstanceOf[JObject]), None, Some(cc))
// entity = APIUtil.unboxFullOrFail(box, Some(cc))
entity = unboxResult(box)
} 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 {
(box: Box[JObject], _) <- NewStyle.function.invokeDynamicConnector(UPDATE, entityName, Some(json.asInstanceOf[JObject]), Some(id), Some(cc))
// entity = APIUtil.unboxFullOrFail(box, Some(cc))
entity = unboxResult(box)
} 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 {
(box: Box[JValue], _) <- NewStyle.function.invokeDynamicConnector(DELETE, entityName, None, Some(id), Some(cc))
// deleteResult = APIUtil.unboxFullOrFail(box, Some(cc))
deleteResult = unboxResult(box)
} yield {
(deleteResult, HttpCode.`200`(Some(cc)))
}
}
}

View File

@ -46,14 +46,14 @@ object MockerConnector {
}
def getSingle(entityName: String, id: String) = {
val idName = StringUtils.uncapitalize(entityName) + "Id"
require(persistedEntities.contains(id -> entityName), s"$InvalidUrl not exists ${entityName} of ${idName} = $id")
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 delete(entityName: String, id: String): Box[Boolean] = {
persistedEntities.remove(id -> entityName).map(_ => true)
}
def doc = {
val docs: Seq[ResourceDoc] = definitionsMap.values.flatMap(createDocs).toSeq

View File

@ -31,13 +31,13 @@ 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.JsonAST.JValue
import net.liftweb.json.JObject
import net.liftweb.json.JValue
import net.liftweb.mapper.By
import net.liftweb.util.Helpers.tryo
import net.liftweb.util.SimpleInjector
@ -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)}
}

View File

@ -144,8 +144,8 @@ object ConnectorEndpoints extends RestHelper{
// it is impossible to get the type of OBPQueryParam*, ru.typeOf[OBPQueryParam*] not work, it is Seq type indeed
private val paramsType = ru.typeOf[Seq[OBPQueryParam]]
// (methodName, paramNames, method, allParamNames)
lazy val allMethods: List[(String, List[String], ru.MethodSymbol, List[String])] = {
// (methodName, paramNames, method, allParamNames, fn: paramName => isOption)
lazy val allMethods: List[(String, List[String], ru.MethodSymbol, List[String], String => Boolean)] = {
val mirror: ru.Mirror = ru.runtimeMirror(this.getClass.getClassLoader)
val objMirror = mirror.reflect(LocalMappedConnector)
@ -161,14 +161,17 @@ object ConnectorEndpoints extends RestHelper{
val names = allParams
.filterNot(symbol => isCallContextOrQueryParams(symbol.info))
.map(_.name.toString.trim)
(it.name.toString, names, it.asMethod, allNames)
val paramNameToIsOption: Map[String, Boolean] = allParams.map(it => (it.name.toString.trim, it.info <:< ru.typeOf[Option[_]])).toMap
val isParamOption: String => Boolean = name => paramNameToIsOption.get(name).filter(true ==).isDefined
(it.name.toString, names, it.asMethod, allNames, isParamOption)
})
.toList
}
def getMethod(methodName: String, json: JValue): Option[ru.MethodSymbol] = {
this.allMethods.filter { triple =>
triple._1 == methodName && triple._2.forall(paramName => (json \ paramName) != JNothing)
this.allMethods.filter { quadruple =>
val (mName, paramNames, _, _, isParamOption) = quadruple
mName == methodName && paramNames.forall(paramName => isParamOption(paramName) || (json \ paramName) != JNothing)
}
.sortBy(_._2.size)
.lastOption

View File

@ -12,6 +12,8 @@ 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.api.v4_0_0.MockerConnector.persistedEntities
import code.atms.Atms.Atm
import code.atms.MappedAtm
import code.branches.Branches.Branch
@ -21,6 +23,7 @@ import code.cards.MappedPhysicalCard
import code.context.{UserAuthContextProvider, UserAuthContextUpdateProvider}
import code.customer._
import code.customeraddress.CustomerAddressX
import code.dynamicEntity.DynamicEntityProvider
import code.fx.{FXRate, MappedFXRate, fx}
import code.kycchecks.KycChecks
import code.kycdocuments.KycDocuments
@ -51,15 +54,19 @@ 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
import net.liftweb.json.{JArray, JBool, JDouble, JInt, JObject, JString, JValue}
import net.liftweb.mapper.{By, _}
import net.liftweb.util.Helpers.{tryo, _}
import net.liftweb.util.Mailer
import net.liftweb.util.Mailer.{From, PlainMailBodyType, Subject, To}
import org.apache.commons.lang3.StringUtils
import org.mindrot.jbcrypt.BCrypt
import scalacache.ScalaCache
import scalacache.guava.GuavaCache
@ -2785,4 +2792,83 @@ 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]] = {
val dynamicEntityBox = DynamicEntityProvider.connectorMethodProvider.vend.getByEntityName(entityName)
// do validate, any validate process fail will return immediately
if(dynamicEntityBox.isEmpty) {
return Helper.booleanToFuture(s"$DynamicEntityEntityNotExists entity's name is '$entityName'")(dynamicEntityBox.isDefined)
.map(it => (it.map(_.asInstanceOf[JValue]), callContext))
} else if(entityId.isDefined && !persistedEntities.contains(entityId.get -> entityName)) {
val id = entityId.get
val idName = StringUtils.uncapitalize(entityName) + "Id"
return Helper.booleanToFuture(s"$InvalidUrl not exists $entityName of $idName = $id")(false)
.map(it => (it.map(_.asInstanceOf[JValue]), callContext))
} else if(requestBody.isDefined) {
val dynamicEntity = dynamicEntityBox.openOrThrowException(DynamicEntityEntityNotExists)
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("[", ",", "]")
if(invalidTypes.nonEmpty) {
return Helper.booleanToFuture(s"$InvalidJsonFormat these field type not correct: $invalidTypeNames")(invalidTypes.isEmpty)
.map(it => (it.map(_.asInstanceOf[JValue]), callContext))
} else if(missingRequiredFields.nonEmpty) {
return Helper.booleanToFuture(s"$InvalidJsonFormat some required fields are missing: $missingFieldNames")(missingRequiredFields.isEmpty)
.map(it => (it.map(_.asInstanceOf[JValue]), callContext))
}
}
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)
}
}
}

View File

@ -71,6 +71,12 @@ package object bankconnectors {
}
val connectorName: Box[String] = bankId match {
case None if methodName == "dynamicEntityProcess" => {
val entityName = args.tail.head
NewStyle.function.getMethodRoutings(Some(methodName))
.find(_.parameters.exists(it => it.key == "entityName" && it.value == entityName))
.map(_.connectorName)
}
case None => NewStyle.function.getMethodRoutings(Some(methodName), Some(false))
.find {routing =>
val bankIdPattern = routing.bankIdPattern

View File

@ -30,7 +30,7 @@ import java.util.Date
import akka.http.scaladsl.model.{HttpProtocol, _}
import akka.http.scaladsl.model.headers.RawHeader
import akka.util.ByteString
import code.api.APIFailureNewStyle
import code.api.{APIFailureNewStyle, ErrorMessage}
import code.api.cache.Caching
import code.api.util.APIUtil.{AdapterImplementation, MessageDoc, OBPReturnType, saveConnectorMetric}
import code.api.util.ErrorMessages._
@ -57,7 +57,7 @@ import code.api.util.APIUtil._
import com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SCA
import code.customer.internalMapping.MappedCustomerIdMappingProvider
import code.model.dataAccess.internalMapping.MappedAccountIdMappingProvider
import com.openbankproject.commons.model.enums.{AccountAttributeType, CardAttributeType, ProductAttributeType}
import com.openbankproject.commons.model.enums.{AccountAttributeType, CardAttributeType, DynamicEntityOperation, ProductAttributeType}
import com.openbankproject.commons.util.ReflectUtils
import net.liftweb.json._
@ -9240,9 +9240,19 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
val result: OBPReturnType[Box[TransactionId]] = sendRequest[InBound](url, HttpMethods.POST, req, callContext).map(convertToTuple(callContext))
result
}
//---------------- dynamic end ---------------------please don't modify this line
//---------------- dynamic end ---------------------please don't modify this line
override def dynamicEntityProcess(operation: DynamicEntityOperation,
entityName: String,
requestBody: Option[JObject],
entityId: Option[String],
callContext: Option[CallContext]): OBPReturnType[Box[JValue]] = {
import com.openbankproject.commons.dto.{OutBoundDynamicEntityProcess => OutBound, InBoundDynamicEntityProcess => InBound}
val url = getUrl(callContext, "dynamicEntityProcess")
val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull , operation, entityName, requestBody, entityId)
val result: OBPReturnType[Box[JValue]] = sendRequest[InBound](url, HttpMethods.POST, req, callContext).map(convertToTuple(callContext))
result
}
//In RestConnector, we use the headers to propagate the parameters to Adapter. The parameters come from the CallContext.outboundAdapterAuthInfo.userAuthContext
@ -9351,9 +9361,15 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
case response@HttpResponse(status, _, entity@_, _) => (status, entity)
}.flatMap {
case (status, entity) if status.isSuccess() => extractEntity[T](entity)
case (status, entity) => extractBody(entity) map { msg => tryo {
parse(msg).extract[T]
} ~> APIFailureNewStyle(msg, status.intValue())
case (status, entity) => {
val future: Future[Box[Box[T]]] = extractBody(entity) map { msg =>
tryo {
val errorMsg = parse(msg).extract[ErrorMessage]
val failure: Box[T] = ParamFailure(errorMsg.message, "")
failure
} ~> APIFailureNewStyle(msg, status.intValue())
}
future.map(_.flatten)
}
}.map(convertToId(_))
}

View File

@ -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]

View File

@ -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()
}

View File

@ -36,7 +36,6 @@ import com.github.dwickern.macros.NameOf.nameOf
import net.liftweb.json.JsonDSL._
import net.liftweb.json.Serialization.write
import net.liftweb.json._
import org.scalatest.LoneElement._
import org.scalatest.Tag
class DynamicEntityTest extends V400ServerSetup {

View File

@ -40,6 +40,11 @@
<groupId>org.scalactic</groupId>
<artifactId>scalactic_${scala.version}</artifactId>
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-json_${scala.version}</artifactId>
<version>${lift.version}</version>
</dependency>
</dependencies>
<build>

View File

@ -28,9 +28,10 @@ package com.openbankproject.commons.dto
import java.util.Date
import com.openbankproject.commons.model.enums.CardAttributeType
import com.openbankproject.commons.model.enums.{CardAttributeType, DynamicEntityOperation}
import com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SCA
import com.openbankproject.commons.model.{enums, _}
import net.liftweb.json.{JObject, JValue}
import scala.collection.immutable.List
@ -1157,4 +1158,11 @@ case class InBoundGetBankAccountByIban (inboundAdapterCallContext: InboundAdapte
case class OutBoundGetBankAccounts (outboundAdapterCallContext: OutboundAdapterCallContext,
bankIdAccountIds: List[BankIdAccountId]) extends TopicTrait
case class InBoundGetBankAccounts (inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: List[BankAccountCommons]) extends InBoundTrait[List[BankAccountCommons]]
case class InBoundGetBankAccounts (inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: List[BankAccountCommons]) extends InBoundTrait[List[BankAccountCommons]]
case class OutBoundDynamicEntityProcess (outboundAdapterCallContext: OutboundAdapterCallContext,
operation: DynamicEntityOperation,
entityName: String,
requestBody: Option[JObject],
entityId: Option[String]) extends TopicTrait
case class InBoundDynamicEntityProcess (inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: JValue) extends InBoundTrait[JValue]

View File

@ -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
}