mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 16:56:56 +00:00
Merge remote-tracking branch 'Hongwei/feature/dynamic_entity_refactor_simplify' into develop
This commit is contained in:
commit
45b28acafd
@ -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,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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(_))
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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]
|
||||
@ -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