Merge pull request #2146 from oldbig/feature/customer_special_dynamic_records

Add customer special dynamic entities
This commit is contained in:
Simon Redfern 2022-12-14 17:23:37 +01:00 committed by GitHub
commit 9163c063b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1999 additions and 1220 deletions

View File

@ -4,12 +4,13 @@ import java.util.UUID.randomUUID
import code.api.OBPRestHelper
import code.api.builder.OBP_APIBuilder
import code.api.cache.Caching
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEndpoints, DynamicEntityHelper}
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEndpoints}
import code.api.dynamic.entity.helper.DynamicEntityHelper
import code.api.util.APIUtil._
import code.api.util.ApiRole.{canReadDynamicResourceDocsAtOneBank, canReadResourceDoc, canReadStaticResourceDoc}
import code.api.util.ApiTag._
import code.api.util.ExampleValue.endpointMappingRequestBodyExample
import code.api.util.{APIUtil, _}
import code.api.util._
import code.api.v1_4_0.JSONFactory1_4_0.ResourceDocsJson
import code.api.v1_4_0.{APIMethods140, JSONFactory1_4_0, OBPAPI1_4_0}
import code.api.v2_2_0.{APIMethods220, OBPAPI2_2_0}

View File

@ -1,10 +1,11 @@
package code.api.dynamic.endpoint
import code.DynamicData.{DynamicData, DynamicDataProvider}
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEntityHelper, DynamicEntityInfo, EntityName, MockResponseHolder}
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, MockResponseHolder}
import code.api.dynamic.endpoint.helper.DynamicEndpointHelper.DynamicReq
import code.api.dynamic.endpoint.helper.MockResponseHolder
import code.api.util.APIUtil.{fullBoxOrException, _}
import code.api.dynamic.entity.helper.{DynamicEntityHelper, DynamicEntityInfo, EntityName}
import code.api.util.APIUtil._
import code.api.util.ErrorMessages._
import code.api.util.NewStyle.HttpCode
import code.api.util._
@ -98,7 +99,7 @@ trait APIMethodsDynamicEndpoint {
DynamicEndpointHelper.getEntityNameKeyAndValue(responseMappingString, pathParams)
}
dynamicData <- Future {
DynamicDataProvider.connectorMethodProvider.vend.getAll(bankId, entityName)
DynamicDataProvider.connectorMethodProvider.vend.getAll(bankId, entityName, None, false)
}
dynamicJsonData = JArray(dynamicData.map(it => net.liftweb.json.parse(it.dataJson)).map(_.asInstanceOf[JObject]))
// //We only get the value, but not sure the field name of it.
@ -127,7 +128,7 @@ trait APIMethodsDynamicEndpoint {
}
//build the entity body according to the request json and mapping
entityBody = JsonUtils.buildJson(json, requestMappingJvalue)
(box, _) <- NewStyle.function.invokeDynamicConnector(CREATE, entityName, Some(entityBody.asInstanceOf[JObject]), None, None, None, Some(cc))
(box, _) <- NewStyle.function.invokeDynamicConnector(CREATE, entityName, Some(entityBody.asInstanceOf[JObject]), None, None, None, None, false, Some(cc))
singleObject: JValue = unboxResult(box.asInstanceOf[Box[JValue]], entityName)
responseBodyScheme = DynamicEndpointHelper.prepareMappingFields(responseMappingJvalue)
responseBody = JsonUtils.buildJson(singleObject, responseBodyScheme)
@ -139,13 +140,13 @@ trait APIMethodsDynamicEndpoint {
(entityName, entityIdKey, entityIdValueFromUrl) <- NewStyle.function.tryons(s"$InvalidEndpointMapping `response_mapping` must be linked to at least one valid dynamic entity!", 400, cc.callContext) {
DynamicEndpointHelper.getEntityNameKeyAndValue(responseMappingString, pathParams)
}
dynamicData = DynamicDataProvider.connectorMethodProvider.vend.getAll(bankId, entityName)
dynamicData = DynamicDataProvider.connectorMethodProvider.vend.getAll(bankId, entityName, None,false)
dynamicJsonData = JArray(dynamicData.map(it => net.liftweb.json.parse(it.dataJson)).map(_.asInstanceOf[JObject]))
entityObject = DynamicEndpointHelper.getObjectByKeyValuePair(dynamicJsonData, entityIdKey, entityIdValueFromUrl.get)
isDeleted <- NewStyle.function.tryons(s"$InvalidEndpointMapping `response_mapping` must be linked to at least one valid dynamic entity!", 400, cc.callContext) {
val entityIdName = DynamicEntityHelper.createEntityId(entityName)
val entityIdValue = (entityObject \ entityIdName).asInstanceOf[JString].s
DynamicDataProvider.connectorMethodProvider.vend.delete(bankId, entityName, entityIdValue).head
DynamicDataProvider.connectorMethodProvider.vend.delete(bankId, entityName, entityIdValue, None, false).head
}
} yield {
JBool(isDeleted)
@ -155,16 +156,16 @@ trait APIMethodsDynamicEndpoint {
(entityName, entityIdKey, entityIdValueFromUrl) <- NewStyle.function.tryons(s"$InvalidEndpointMapping `response_mapping` must be linked to at least one valid dynamic entity!", 400, cc.callContext) {
DynamicEndpointHelper.getEntityNameKeyAndValue(responseMappingString, pathParams)
}
dynamicData = DynamicDataProvider.connectorMethodProvider.vend.getAll(bankId, entityName)
dynamicData = DynamicDataProvider.connectorMethodProvider.vend.getAll(bankId, entityName, None, false)
dynamicJsonData = JArray(dynamicData.map(it => net.liftweb.json.parse(it.dataJson)).map(_.asInstanceOf[JObject]))
entityObject = DynamicEndpointHelper.getObjectByKeyValuePair(dynamicJsonData, entityIdKey, entityIdValueFromUrl.get)
_ <- NewStyle.function.tryons(s"$InvalidEndpointMapping `response_mapping` must be linked to at least one valid dynamic entity!", 400, cc.callContext) {
val entityIdName = DynamicEntityHelper.createEntityId(entityName)
val entityIdValue = (entityObject \ entityIdName).asInstanceOf[JString].s
DynamicDataProvider.connectorMethodProvider.vend.delete(bankId, entityName, entityIdValue).head
DynamicDataProvider.connectorMethodProvider.vend.delete(bankId, entityName, entityIdValue, None, false).head
}
entityBody = JsonUtils.buildJson(json, requestMappingJvalue)
(box, _) <- NewStyle.function.invokeDynamicConnector(CREATE, entityName, Some(entityBody.asInstanceOf[JObject]), None, bankId, None, Some(cc))
(box, _) <- NewStyle.function.invokeDynamicConnector(CREATE, entityName, Some(entityBody.asInstanceOf[JObject]), None, bankId, None, None, false, Some(cc))
singleObject: JValue = unboxResult(box.asInstanceOf[Box[JValue]], entityName)
responseBodyScheme = DynamicEndpointHelper.prepareMappingFields(responseMappingJvalue)
responseBody = JsonUtils.buildJson(singleObject, responseBodyScheme)

View File

@ -1043,7 +1043,7 @@ object DynamicEndpointHelper extends RestHelper {
def deleteObjectByKeyValuePair (dynamicDataList: List[DynamicDataT], jsonArray: JArray, key:String, value:String): JValue = {
val dynamicDataJson = getObjectByKeyValuePair(jsonArray: JArray, key:String, value:String)
val (dynamicEntityName, dynamicDateId) = findDynamicData(dynamicDataList, dynamicDataJson)
JBool(DynamicDataProvider.connectorMethodProvider.vend.delete(None, dynamicEntityName, dynamicDateId).getOrElse(false))
JBool(DynamicDataProvider.connectorMethodProvider.vend.delete(None, dynamicEntityName, dynamicDateId, None, false).getOrElse(false))
}
def addedBankToPath(swagger: String, bankId: Option[String]): JValue = {

View File

@ -1,449 +0,0 @@
package code.api.dynamic.endpoint.helper
import code.api.util.APIUtil.{EmptyBody, ResourceDoc, authenticationRequiredMessage, generateUUID}
import code.api.util.ApiRole.getOrCreateDynamicApiRole
import code.api.util.ApiTag._
import code.api.util.ErrorMessages.{InvalidJsonFormat, UnknownError, UserHasMissingRoles, UserNotLoggedIn}
import code.api.util._
import com.openbankproject.commons.model.enums.{DynamicEntityFieldType, DynamicEntityOperation}
import com.openbankproject.commons.util.ApiVersion
import net.liftweb.json.JsonDSL._
import net.liftweb.json._
import net.liftweb.util.StringHelpers
import org.apache.commons.lang3.StringUtils
import scala.collection.immutable.{List, Nil}
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
object EntityName {
// unapply result structure: (BankId, entityName, id)
def unapply(url: List[String]): Option[(Option[String], String, String)] = url match {
//no bank:
//eg: /FooBar21
case entityName :: Nil =>
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1._1 == None && definitionMap._1._2 == entityName && definitionMap._2.bankId.isEmpty)
.map(_ => (None, entityName, ""))
//eg: /FooBar21/FOO_BAR21_ID
case entityName :: id :: Nil =>
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1._1 == None && definitionMap._1._2 == entityName && definitionMap._2.bankId.isEmpty)
.map(_ => (None, entityName, id))
//contains Bank:
//eg: /Banks/BANK_ID/FooBar21
case "banks" :: bankId :: entityName :: Nil =>
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1._1 == Some(bankId) && definitionMap._1._2 == entityName && definitionMap._2.bankId == Some(bankId))
.map(_ => (Some(bankId), entityName, ""))
//eg: /Banks/BANK_ID/FooBar21/FOO_BAR21_ID
case "banks" :: bankId :: entityName :: id :: Nil =>
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1._1 == Some(bankId) && definitionMap._1._2 == entityName && definitionMap._2.bankId == Some(bankId))
.map(_ => (Some(bankId),entityName, id))
case _ => None
}
}
object DynamicEntityHelper {
private val implementedInApiVersion = ApiVersion.v4_0_0
// (Some(BankId), EntityName, DynamicEntityInfo)
def definitionsMap: Map[(Option[String], String), DynamicEntityInfo] = NewStyle.function.getDynamicEntities(None).map(it => ((it.bankId, it.entityName), DynamicEntityInfo(it.metadataJson, it.entityName, it.bankId))).toMap
def dynamicEntityRoles: List[String] = NewStyle.function.getDynamicEntities(None).flatMap(dEntity => DynamicEntityInfo.roleNames(dEntity.entityName, dEntity.bankId))
def doc: ArrayBuffer[ResourceDoc] = {
val docs = operationToResourceDoc.values.toList
collection.mutable.ArrayBuffer(docs:_*)
}
def createEntityId(entityName: String) = {
// (?<=[a-z0-9])(?=[A-Z]) --> mean `Positive Lookbehind (?<=[a-z0-9])` && Positive Lookahead (?=[A-Z]) --> So we can find the space to replace to `_`
val regexPattern = "(?<=[a-z0-9])(?=[A-Z])|-"
// eg: entityName = PetEntity => entityIdName = pet_entity_id
s"${entityName}_Id".replaceAll(regexPattern, "_").toLowerCase
}
def operationToResourceDoc: Map[(DynamicEntityOperation, String), ResourceDoc] = {
val addPrefix = APIUtil.getPropsAsBoolValue("dynamic_entities_have_prefix", true)
// record exists tag names, to avoid duplicated dynamic tag name.
var existsTagNames = ApiTag.staticTagNames
// match string that start with _, e.g: "_abc"
val Regex = "(_+)(.+)".r
//convert entity name to tag name, example:
// Csem-case -> Csem Case
// _Csem-case -> _Csem Case
// Csem_case -> Csem Case
// _Csem_case -> _Csem Case
// csem-case -> Csem Case
def prettyTagName(s: String) = s.capitalize.split("(?<=[^-_])[-_]+").reduceLeft(_ + " " + _.capitalize)
def apiTag(entityName: String, singularName: String): ResourceDocTag = {
val existsSameStaticEntity: Boolean = existsTagNames
.exists(it => it.equalsIgnoreCase(singularName) || it.equalsIgnoreCase(entityName))
val tagName = if(addPrefix || existsSameStaticEntity) {
var name = singularName match {
case Regex(a,b) => s"$a${b.capitalize}"
case v => s"_${v.capitalize}"
}
while(existsTagNames.exists(it => it.equalsIgnoreCase(name))) {
name = s"_$name"
}
prettyTagName(name)
} else {
prettyTagName(singularName.capitalize)
}
existsTagNames += tagName
ApiTag(tagName)
}
val fun: DynamicEntityInfo => mutable.Map[(DynamicEntityOperation, String), ResourceDoc] = createDocs(apiTag)
val docs: Iterable[((DynamicEntityOperation, String), ResourceDoc)] = definitionsMap.values.flatMap(fun)
docs.toMap
}
// TODO the requestBody and responseBody is not correct ref type
/**
*
* @param fun (singularName, entityName) => ResourceDocTag
* @param dynamicEntityInfo dynamicEntityInfo
* @return all ResourceDoc of given dynamicEntity
*/
private def createDocs(fun: (String, String) => ResourceDocTag)
(dynamicEntityInfo: DynamicEntityInfo): mutable.Map[(DynamicEntityOperation, String), ResourceDoc] = {
val entityName = dynamicEntityInfo.entityName
// e.g: "someMultiple-part_Name" -> ["Some", "Multiple", "Part", "Name"]
val capitalizedNameParts = entityName.split("(?<=[a-z0-9])(?=[A-Z])|-|_").map(_.capitalize).filterNot(_.trim.isEmpty)
val splitName = s"""${capitalizedNameParts.mkString(" ")}"""
val splitNameWithBankId = if (dynamicEntityInfo.bankId.isDefined)
s"""$splitName(${dynamicEntityInfo.bankId.getOrElse("")})"""
else
s"""$splitName"""
val idNameInUrl = StringHelpers.snakify(dynamicEntityInfo.idName).toUpperCase()
val listName = dynamicEntityInfo.listName
val bankId = dynamicEntityInfo.bankId
val resourceDocUrl = if(bankId.isDefined) s"/banks/${bankId.getOrElse("")}/$entityName" else s"/$entityName"
val endPoint = APIUtil.dynamicEndpointStub
// (operationType, entityName) -> ResourceDoc
val resourceDocs = scala.collection.mutable.Map[(DynamicEntityOperation, String),ResourceDoc]()
val apiTag: ResourceDocTag = fun(entityName,splitNameWithBankId)
resourceDocs += (DynamicEntityOperation.GET_ALL, splitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildGetAllFunctionName(bankId, entityName),
"GET",
s"$resourceDocUrl",
s"Get $splitName List",
s"""Get $splitName List.
|${dynamicEntityInfo.description}
|
|${dynamicEntityInfo.fieldsDescription}
|
|${methodRoutingExample(entityName)}
|
|${authenticationRequiredMessage(true)}
|
|Can do filter on the fields
|e.g: /${entityName}?name=James%20Brown&number=123.456&number=11.11
|Will do filter by this rule: name == "James Brown" && (number==123.456 || number=11.11)
|""".stripMargin,
EmptyBody,
dynamicEntityInfo.getExampleList,
List(
UserNotLoggedIn,
UserHasMissingRoles,
UnknownError
),
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
Some(List(dynamicEntityInfo.canGetRole)),
createdByBankId= dynamicEntityInfo.bankId
)
resourceDocs += (DynamicEntityOperation.GET_ONE, splitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildGetOneFunctionName(bankId, entityName),
"GET",
s"$resourceDocUrl/$idNameInUrl",
s"Get $splitName by id",
s"""Get $splitName by id.
|${dynamicEntityInfo.description}
|
|${dynamicEntityInfo.fieldsDescription}
|
|${methodRoutingExample(entityName)}
|
|${authenticationRequiredMessage(true)}
|""".stripMargin,
EmptyBody,
dynamicEntityInfo.getSingleExample,
List(
UserNotLoggedIn,
UserHasMissingRoles,
UnknownError
),
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
Some(List(dynamicEntityInfo.canGetRole)),
createdByBankId= dynamicEntityInfo.bankId
)
resourceDocs += (DynamicEntityOperation.CREATE, splitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildCreateFunctionName(bankId, entityName),
"POST",
s"$resourceDocUrl",
s"Create new $splitName",
s"""Create new $splitName.
|${dynamicEntityInfo.description}
|
|${dynamicEntityInfo.fieldsDescription}
|
|${methodRoutingExample(entityName)}
|
|${authenticationRequiredMessage(true)}
|
|""",
dynamicEntityInfo.getSingleExampleWithoutId,
dynamicEntityInfo.getSingleExample,
List(
UserNotLoggedIn,
UserHasMissingRoles,
InvalidJsonFormat,
UnknownError
),
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
Some(List(dynamicEntityInfo.canCreateRole)),
createdByBankId= dynamicEntityInfo.bankId
)
resourceDocs += (DynamicEntityOperation.UPDATE, splitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildUpdateFunctionName(bankId, entityName),
"PUT",
s"$resourceDocUrl/$idNameInUrl",
s"Update $splitName",
s"""Update $splitName.
|${dynamicEntityInfo.description}
|
|${dynamicEntityInfo.fieldsDescription}
|
|${methodRoutingExample(entityName)}
|
|${authenticationRequiredMessage(true)}
|
|""",
dynamicEntityInfo.getSingleExampleWithoutId,
dynamicEntityInfo.getSingleExample,
List(
UserNotLoggedIn,
UserHasMissingRoles,
InvalidJsonFormat,
UnknownError
),
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
Some(List(dynamicEntityInfo.canUpdateRole)),
createdByBankId= dynamicEntityInfo.bankId
)
resourceDocs += (DynamicEntityOperation.DELETE, splitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildDeleteFunctionName(bankId, entityName),
"DELETE",
s"$resourceDocUrl/$idNameInUrl",
s"Delete $splitName by id",
s"""Delete $splitName by id
|
|${methodRoutingExample(entityName)}
|
|${authenticationRequiredMessage(true)}
|
|""",
dynamicEntityInfo.getSingleExampleWithoutId,
dynamicEntityInfo.getSingleExample,
List(
UserNotLoggedIn,
UserHasMissingRoles,
InvalidJsonFormat,
UnknownError
),
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
Some(List(dynamicEntityInfo.canDeleteRole)),
createdByBankId= dynamicEntityInfo.bankId
)
resourceDocs
}
private def buildCreateFunctionName(bankId:Option[String], entityName: String) = s"dynamicEntity_create${entityName}_${bankId.getOrElse("")}"
private def buildUpdateFunctionName(bankId:Option[String], entityName: String) = s"dynamicEntity_update${entityName}_${bankId.getOrElse("")}"
private def buildDeleteFunctionName(bankId:Option[String], entityName: String) = s"dynamicEntity_delete${entityName}_${bankId.getOrElse("")}"
private def buildGetOneFunctionName(bankId:Option[String], entityName: String) = s"dynamicEntity_getSingle${entityName}_${bankId.getOrElse("")}"
private def buildGetAllFunctionName(bankId:Option[String], entityName: String) = s"dynamicEntity_get${entityName}List_${bankId.getOrElse("")}"
@inline
private def buildOperationId(bankId:Option[String], entityName: String, fun: (Option[String], String) => String): String = {
APIUtil.buildOperationId(implementedInApiVersion, fun(bankId, entityName))
}
def buildCreateOperationId(bankId:Option[String], entityName: String) = buildOperationId(bankId, entityName, buildCreateFunctionName)
def buildUpdateOperationId(bankId:Option[String], entityName: String) = buildOperationId(bankId, entityName, buildUpdateFunctionName)
def buildDeleteOperationId(bankId:Option[String], entityName: String) = buildOperationId(bankId, entityName, buildDeleteFunctionName)
def buildGetOneOperationId(bankId:Option[String], entityName: String) = buildOperationId(bankId, entityName, buildGetOneFunctionName)
def buildGetAllOperationId(bankId:Option[String], entityName: String) = buildOperationId(bankId, entityName, buildGetAllFunctionName)
private def methodRoutingExample(entityName: String) =
s"""
|MethodRouting settings example:
|
|<details>
|
|```
|{
| "is_bank_id_exact_match":false,
| "method_name":"dynamicEntityProcess",
| "connector_name":"rest_vMar2019",
| "bank_id_pattern":".*",
| "parameters":[
| {
| "key":"entityName",
| "value":"$entityName"
| }
| {
| "key":"url",
| "value":"http://mydomain.com/xxx"
| }
| ]
|}
|```
|
|</details>
|""".stripMargin
}
case class DynamicEntityInfo(definition: String, entityName: String, bankId: Option[String]) {
import net.liftweb.json
val subEntities: List[DynamicEntityInfo] = Nil
val idName = StringUtils.uncapitalize(entityName) + "Id"
val listName = StringHelpers.snakify(entityName).replaceFirst("[-_]*$", "_list")
val singleName = StringHelpers.snakify(entityName).replaceFirst("[-_]*$", "")
val jsonTypeMap: Map[String, Class[_]] = DynamicEntityFieldType.nameToValue.mapValues(_.jValueType)
val definitionJson = json.parse(definition).asInstanceOf[JObject]
val entity = (definitionJson \ entityName).asInstanceOf[JObject]
val description = entity \ "description" match {
case JString(s) if StringUtils.isNotBlank(s) =>
s"""
|${s.capitalize}
|""".stripMargin
case _ => ""
}
val fieldsDescription = {
val descriptions = (entity \ "properties")
.asInstanceOf[JObject]
.obj
.filter(field =>
field.value \ "description" match {
case JString(s) if StringUtils.isNotBlank(s) => true
case _ => false
}
)
if(descriptions.nonEmpty) {
descriptions
.map(field => s"""* ${field.name}: ${(field.value \ "description").asInstanceOf[JString].s}""")
.mkString("**Property List:** \n\n", "\n", "")
} else {
""
}
}
def toResponse(result: JObject, id: Option[String]): JObject = {
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 fields = result.obj.filter(it => fieldNameToType.keySet.contains(it.name))
(id, fields.exists(_.name == idName)) match {
case (Some(idValue), false) => JObject(JField(idName, JString(idValue)) :: fields)
case _ => JObject(fields)
}
}
def getSingleExampleWithoutId: JObject = {
val fields = (entity \ "properties").asInstanceOf[JObject].obj
def extractExample(typeAndExample: JValue): JValue = {
val example = typeAndExample \ "example"
(example, (typeAndExample \ "type")) match {
case (JString(s), JString("boolean")) => JBool(s.toLowerCase().toBoolean)
case (JString(s), JString("integer")) => JInt(s.toLong)
case (JString(s), JString("number")) => JDouble(s.toDouble)
case _ => example
}
}
val exampleFields = fields.map(field => JField(field.name, extractExample(field.value)))
JObject(exampleFields)
}
val bankIdJObject: JObject = ("bank-id" -> ExampleValue.bankIdExample.value)
def getSingleExample: JObject = if (bankId.isDefined){
val SingleObject: JObject = (singleName -> (JObject(JField(idName, JString(generateUUID())) :: getSingleExampleWithoutId.obj)))
bankIdJObject merge SingleObject
} else{
(singleName -> (JObject(JField(idName, JString(generateUUID())) :: getSingleExampleWithoutId.obj)))
}
def getExampleList: JObject = if (bankId.isDefined){
val objectList: JObject = (listName -> JArray(List(getSingleExample)))
bankIdJObject merge objectList
} else{
(listName -> JArray(List(getSingleExample)))
}
val canCreateRole: ApiRole = DynamicEntityInfo.canCreateRole(entityName, bankId)
val canUpdateRole: ApiRole = DynamicEntityInfo.canUpdateRole(entityName, bankId)
val canGetRole: ApiRole = DynamicEntityInfo.canGetRole(entityName, bankId)
val canDeleteRole: ApiRole = DynamicEntityInfo.canDeleteRole(entityName, bankId)
}
object DynamicEntityInfo {
def canCreateRole(entityName: String, bankId:Option[String]): ApiRole = getOrCreateDynamicApiRole("CanCreateDynamicEntity_" + entityName, bankId.isDefined)
def canUpdateRole(entityName: String, bankId:Option[String]): ApiRole = getOrCreateDynamicApiRole("CanUpdateDynamicEntity_" + entityName, bankId.isDefined)
def canGetRole(entityName: String, bankId:Option[String]): ApiRole = getOrCreateDynamicApiRole("CanGetDynamicEntity_" + entityName, bankId.isDefined)
def canDeleteRole(entityName: String, bankId:Option[String]): ApiRole = getOrCreateDynamicApiRole("CanDeleteDynamicEntity_" + entityName, bankId.isDefined)
def roleNames(entityName: String, bankId:Option[String]): List[String] = List(
canCreateRole(entityName, bankId),
canUpdateRole(entityName, bankId),
canGetRole(entityName, bankId),
canDeleteRole(entityName, bankId)
).map(_.toString())
}

View File

@ -1,10 +1,11 @@
package code.api.dynamic.entity
import code.DynamicData.{DynamicData, DynamicDataProvider}
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEntityHelper, DynamicEntityInfo, EntityName, MockResponseHolder}
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, MockResponseHolder}
import code.api.dynamic.endpoint.helper.DynamicEndpointHelper.DynamicReq
import code.api.dynamic.endpoint.helper.MockResponseHolder
import code.api.util.APIUtil.{fullBoxOrException, _}
import code.api.dynamic.entity.helper.{DynamicEntityHelper, DynamicEntityInfo, EntityName}
import code.api.util.APIUtil._
import code.api.util.ErrorMessages._
import code.api.util.NewStyle.HttpCode
import code.api.util._
@ -64,7 +65,7 @@ trait APIMethodsDynamicEntity {
case map if map.isEmpty => resultList
case params =>
val filteredWithFieldValue = resultList.arr.filter { jValue =>
params.forall { kv =>
params.filter(_._1!="locale").forall { kv =>
val (path, values) = kv
values.exists(JsonUtils.isFieldEquals(jValue, path, _))
}
@ -75,13 +76,25 @@ trait APIMethodsDynamicEntity {
}
lazy val genericEndpoint: OBPEndpoint = {
case EntityName(bankId, entityName, id) JsonGet req => { cc =>
case EntityName(bankId, entityName, id, isPersonalEntity) JsonGet req => { cc =>
val listName = StringHelpers.snakify(entityName).replaceFirst("[-_]*$", "_list")
val singleName = StringHelpers.snakify(entityName).replaceFirst("[-_]*$", "")
val isGetAll = StringUtils.isBlank(id)
// e.g: "someMultiple-part_Name" -> ["Some", "Multiple", "Part", "Name"]
val capitalizedNameParts = entityName.split("(?<=[a-z0-9])(?=[A-Z])|-|_").map(_.capitalize).filterNot(_.trim.isEmpty)
val splitName = s"""${capitalizedNameParts.mkString(" ")}"""
val splitNameWithBankId = if (bankId.isDefined)
s"""$splitName(${bankId.getOrElse("")})"""
else
s"""$splitName"""
val mySplitNameWithBankId = s"My$splitNameWithBankId"
val operation: DynamicEntityOperation = if (StringUtils.isBlank(id)) GET_ALL else GET_ONE
val resourceDoc = DynamicEntityHelper.operationToResourceDoc.get(operation -> entityName)
val resourceDoc = if(isPersonalEntity)
DynamicEntityHelper.operationToResourceDoc.get(operation -> mySplitNameWithBankId)
else
DynamicEntityHelper.operationToResourceDoc.get(operation -> splitNameWithBankId)
val operationId = resourceDoc.map(_.operationId).orNull
val callContext = cc.copy(operationId = Some(operationId), resourceDocument = resourceDoc)
// process before authentication interceptor, get intercept result
@ -99,7 +112,11 @@ trait APIMethodsDynamicEntity {
}
}
_ <- NewStyle.function.hasEntitlement(bankId.getOrElse(""), u.userId, DynamicEntityInfo.canGetRole(entityName, bankId), callContext)
_ <- if (isPersonalEntity) {
Future.successful(true)
} else {
NewStyle.function.hasEntitlement(bankId.getOrElse(""), u.userId, DynamicEntityInfo.canGetRole(entityName, bankId), callContext)
}
// process after authentication interceptor, get intercept result
jsonResponse: Box[ErrorMessage] = afterAuthenticateInterceptResult(callContext, operationId).collect({
@ -109,7 +126,11 @@ trait APIMethodsDynamicEntity {
jsonResponse.isEmpty
}
(box, _) <- NewStyle.function.invokeDynamicConnector(operation, entityName, None, Option(id).filter(StringUtils.isNotBlank), bankId, None, Some(cc))
(box, _) <- NewStyle.function.invokeDynamicConnector(operation, entityName, None, Option(id).filter(StringUtils.isNotBlank), bankId, None,
Some(u.userId),
isPersonalEntity,
Some(cc)
)
_ <- Helper.booleanToFuture(EntityNotFoundByEntityId, 404, cc = callContext) {
box.isDefined
@ -139,8 +160,7 @@ trait APIMethodsDynamicEntity {
(jValue, HttpCode.`200`(Some(cc)))
}
}
case EntityName(bankId, entityName, _) JsonPost json -> _ => { cc =>
case EntityName(bankId, entityName, _, isPersonalEntity) JsonPost json -> _ => { cc =>
val singleName = StringHelpers.snakify(entityName).replaceFirst("[-_]*$", "")
val operation: DynamicEntityOperation = CREATE
// e.g: "someMultiple-part_Name" -> ["Some", "Multiple", "Part", "Name"]
@ -150,7 +170,13 @@ trait APIMethodsDynamicEntity {
s"""$splitName(${bankId.getOrElse("")})"""
else
s"""$splitName"""
val resourceDoc = DynamicEntityHelper.operationToResourceDoc.get(operation -> splitNameWithBankId)
val mySplitNameWithBankId = s"My$splitNameWithBankId"
val resourceDoc = if(isPersonalEntity)
DynamicEntityHelper.operationToResourceDoc.get(operation -> mySplitNameWithBankId)
else
DynamicEntityHelper.operationToResourceDoc.get(operation -> splitNameWithBankId)
val operationId = resourceDoc.map(_.operationId).orNull
val callContext = cc.copy(operationId = Some(operationId), resourceDocument = resourceDoc)
@ -167,7 +193,12 @@ trait APIMethodsDynamicEntity {
("", callContext)
}
}
_ <- NewStyle.function.hasEntitlement(bankId.getOrElse(""), u.userId, DynamicEntityInfo.canCreateRole(entityName, bankId), callContext)
_ <- if (isPersonalEntity) {
Future.successful(true)
} else {
NewStyle.function.hasEntitlement(bankId.getOrElse(""), u.userId, DynamicEntityInfo.canCreateRole(entityName, bankId), callContext)
}
// process after authentication interceptor, get intercept result
jsonResponse: Box[ErrorMessage] = afterAuthenticateInterceptResult(callContext, operationId).collect({
@ -177,7 +208,7 @@ trait APIMethodsDynamicEntity {
jsonResponse.isEmpty
}
(box, _) <- NewStyle.function.invokeDynamicConnector(operation, entityName, Some(json.asInstanceOf[JObject]), None, bankId, None, Some(cc))
(box, _) <- NewStyle.function.invokeDynamicConnector(operation, entityName, Some(json.asInstanceOf[JObject]), None, bankId, None, Some(u.userId), isPersonalEntity, Some(cc))
singleObject: JValue = unboxResult(box.asInstanceOf[Box[JValue]], entityName)
} yield {
val result: JObject = (singleName -> singleObject)
@ -190,7 +221,7 @@ trait APIMethodsDynamicEntity {
(entity, HttpCode.`201`(Some(cc)))
}
}
case EntityName(bankId, entityName, id) JsonPut json -> _ => { cc =>
case EntityName(bankId, entityName, id, isPersonalEntity) JsonPut json -> _ => { cc =>
val singleName = StringHelpers.snakify(entityName).replaceFirst("[-_]*$", "")
val operation: DynamicEntityOperation = UPDATE
// e.g: "someMultiple-part_Name" -> ["Some", "Multiple", "Part", "Name"]
@ -200,7 +231,13 @@ trait APIMethodsDynamicEntity {
s"""$splitName(${bankId.getOrElse("")})"""
else
s"""$splitName"""
val resourceDoc = DynamicEntityHelper.operationToResourceDoc.get(operation -> splitNameWithBankId)
val mySplitNameWithBankId = s"My$splitNameWithBankId"
val resourceDoc = if(isPersonalEntity)
DynamicEntityHelper.operationToResourceDoc.get(operation -> mySplitNameWithBankId)
else
DynamicEntityHelper.operationToResourceDoc.get(operation -> splitNameWithBankId)
val operationId = resourceDoc.map(_.operationId).orNull
val callContext = cc.copy(operationId = Some(operationId), resourceDocument = resourceDoc)
@ -217,7 +254,11 @@ trait APIMethodsDynamicEntity {
("", callContext)
}
}
_ <- NewStyle.function.hasEntitlement(bankId.getOrElse(""), u.userId, DynamicEntityInfo.canUpdateRole(entityName, bankId), callContext)
_ <- if (isPersonalEntity) {
Future.successful(true)
} else {
NewStyle.function.hasEntitlement(bankId.getOrElse(""), u.userId, DynamicEntityInfo.canUpdateRole(entityName, bankId), callContext)
}
// process after authentication interceptor, get intercept result
jsonResponse: Box[ErrorMessage] = afterAuthenticateInterceptResult(callContext, operationId).collect({
@ -227,11 +268,17 @@ trait APIMethodsDynamicEntity {
jsonResponse.isEmpty
}
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ONE, entityName, None, Some(id), bankId, None, Some(cc))
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ONE, entityName, None, Some(id), bankId, None,
Some(u.userId),
isPersonalEntity,
Some(cc))
_ <- Helper.booleanToFuture(EntityNotFoundByEntityId, 404, cc = callContext) {
box.isDefined
}
(box: Box[JValue], _) <- NewStyle.function.invokeDynamicConnector(operation, entityName, Some(json.asInstanceOf[JObject]), Some(id), bankId, None, Some(cc))
(box: Box[JValue], _) <- NewStyle.function.invokeDynamicConnector(operation, entityName, Some(json.asInstanceOf[JObject]), Some(id), bankId, None,
Some(u.userId),
isPersonalEntity,
Some(cc))
singleObject: JValue = unboxResult(box.asInstanceOf[Box[JValue]], entityName)
} yield {
val result: JObject = (singleName -> singleObject)
@ -244,7 +291,7 @@ trait APIMethodsDynamicEntity {
(entity, HttpCode.`200`(Some(cc)))
}
}
case EntityName(bankId, entityName, id) JsonDelete _ => { cc =>
case EntityName(bankId, entityName, id, isPersonalEntity) JsonDelete _ => { cc =>
val operation: DynamicEntityOperation = DELETE
// e.g: "someMultiple-part_Name" -> ["Some", "Multiple", "Part", "Name"]
val capitalizedNameParts = entityName.split("(?<=[a-z0-9])(?=[A-Z])|-|_").map(_.capitalize).filterNot(_.trim.isEmpty)
@ -253,7 +300,13 @@ trait APIMethodsDynamicEntity {
s"""$splitName(${bankId.getOrElse("")})"""
else
s"""$splitName"""
val resourceDoc = DynamicEntityHelper.operationToResourceDoc.get(operation -> splitNameWithBankId)
val mySplitNameWithBankId = s"My$splitNameWithBankId"
val resourceDoc = if(isPersonalEntity)
DynamicEntityHelper.operationToResourceDoc.get(operation -> mySplitNameWithBankId)
else
DynamicEntityHelper.operationToResourceDoc.get(operation -> splitNameWithBankId)
val operationId = resourceDoc.map(_.operationId).orNull
val callContext = cc.copy(operationId = Some(operationId), resourceDocument = resourceDoc)
@ -270,7 +323,12 @@ trait APIMethodsDynamicEntity {
("", callContext)
}
}
_ <- NewStyle.function.hasEntitlement(bankId.getOrElse(""), u.userId, DynamicEntityInfo.canDeleteRole(entityName, bankId), callContext)
_ <- if (isPersonalEntity) {
Future.successful(true)
} else {
NewStyle.function.hasEntitlement(bankId.getOrElse(""), u.userId, DynamicEntityInfo.canDeleteRole(entityName, bankId), callContext)
}
// process after authentication interceptor, get intercept result
jsonResponse: Box[ErrorMessage] = afterAuthenticateInterceptResult(callContext, operationId).collect({
@ -280,14 +338,22 @@ trait APIMethodsDynamicEntity {
jsonResponse.isEmpty
}
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ONE, entityName, None, Some(id), bankId, None, Some(cc))
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ONE, entityName, None, Some(id), bankId, None,
Some(u.userId),
isPersonalEntity,
Some(cc)
)
_ <- Helper.booleanToFuture(EntityNotFoundByEntityId, 404, cc = callContext) {
box.isDefined
}
(box, _) <- NewStyle.function.invokeDynamicConnector(operation, entityName, None, Some(id), bankId, None, Some(cc))
(box, _) <- NewStyle.function.invokeDynamicConnector(operation, entityName, None, Some(id), bankId, None,
Some(u.userId),
isPersonalEntity,
Some(cc)
)
deleteResult: JBool = unboxResult(box.asInstanceOf[Box[JBool]], entityName)
} yield {
(deleteResult, HttpCode.`204`(Some(cc)))
(deleteResult, HttpCode.`200`(Some(cc)))
}
}
}

View File

@ -18,27 +18,46 @@ import scala.collection.mutable.ArrayBuffer
object EntityName {
// unapply result structure: (BankId, entityName, id)
def unapply(url: List[String]): Option[(Option[String], String, String)] = url match {
//no bank:
// unapply result structure: (BankId, entityName, id, isPersonalEntity)
def unapply(url: List[String]): Option[(Option[String], String, String, Boolean)] = url match {
//eg: /my/FooBar21
case "my" :: entityName :: Nil =>
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1._1 == None && definitionMap._1._2 == entityName && definitionMap._2.bankId.isEmpty && definitionMap._2.hasPersonalEntity)
.map(_ => (None, entityName, "", true))
//eg: /my/FooBar21/FOO_BAR21_ID
case "my" :: entityName :: id :: Nil =>
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1._1 == None && definitionMap._1._2 == entityName && definitionMap._2.bankId.isEmpty && definitionMap._2.hasPersonalEntity)
.map(_ => (None, entityName, id, true))
//eg: /FooBar21
case entityName :: Nil =>
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1 == entityName && definitionMap._2.bankId.isEmpty)
.map(_ => (None, entityName, ""))
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1._1 == None && definitionMap._1._2 == entityName && definitionMap._2.bankId.isEmpty)
.map(_ => (None, entityName, "", false))
//eg: /FooBar21/FOO_BAR21_ID
case entityName :: id :: Nil =>
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1 == entityName && definitionMap._2.bankId.isEmpty)
.map(_ => (None, entityName, id))
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1._1 == None && definitionMap._1._2 == entityName && definitionMap._2.bankId.isEmpty)
.map(_ => (None, entityName, id, false))
//eg: /Banks/BANK_ID/my/FooBar21
case "banks" :: bankId :: "my" :: entityName :: Nil =>
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1._1 == Some(bankId) && definitionMap._1._2 == entityName && definitionMap._2.bankId == Some(bankId) && definitionMap._2.hasPersonalEntity)
.map(_ => (Some(bankId), entityName, "", true))
//eg: /Banks/BANK_ID/my/FooBar21/FOO_BAR21_ID
case "banks" :: bankId :: "my" :: entityName :: id :: Nil =>
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1._1 == Some(bankId) && definitionMap._1._2 == entityName && definitionMap._2.bankId == Some(bankId) && definitionMap._2.hasPersonalEntity)
.map(_ => (Some(bankId),entityName, id, true))
//contains Bank:
//eg: /Banks/BANK_ID/FooBar21
case "banks" :: bankId :: entityName :: Nil =>
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1 == entityName && definitionMap._2.bankId == Some(bankId))
.map(_ => (Some(bankId), entityName, ""))
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1._1 == Some(bankId) && definitionMap._1._2 == entityName && definitionMap._2.bankId == Some(bankId))
.map(_ => (Some(bankId), entityName, "", false))
//eg: /Banks/BANK_ID/FooBar21/FOO_BAR21_ID
case "banks" :: bankId :: entityName :: id :: Nil =>
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1 == entityName && definitionMap._2.bankId == Some(bankId))
.map(_ => (Some(bankId),entityName, id))
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1._1 == Some(bankId) && definitionMap._1._2 == entityName && definitionMap._2.bankId == Some(bankId))
.map(_ => (Some(bankId),entityName, id, false))//no bank:
case _ => None
}
@ -46,10 +65,11 @@ object EntityName {
object DynamicEntityHelper {
private val implementedInApiVersion = ApiVersion.v4_0_0
// (Some(BankId), EntityName, DynamicEntityInfo)
def definitionsMap: Map[(Option[String], String), DynamicEntityInfo] = NewStyle.function.getDynamicEntities(None, true).map(it => ((it.bankId, it.entityName), DynamicEntityInfo(it.metadataJson, it.entityName, it.bankId, it.hasPersonalEntity))).toMap
def definitionsMap: Map[String, DynamicEntityInfo] = NewStyle.function.getDynamicEntities(None).map(it => (it.entityName, DynamicEntityInfo(it.metadataJson, it.entityName, it.bankId))).toMap
def dynamicEntityRoles: List[String] = NewStyle.function.getDynamicEntities(None).flatMap(dEntity => DynamicEntityInfo.roleNames(dEntity.entityName, dEntity.bankId))
def dynamicEntityRoles: List[String] = NewStyle.function.getDynamicEntities(None, true).flatMap(dEntity => DynamicEntityInfo.roleNames(dEntity.entityName, dEntity.bankId))
def doc: ArrayBuffer[ResourceDoc] = {
val docs = operationToResourceDoc.values.toList
@ -118,25 +138,34 @@ object DynamicEntityHelper {
private def createDocs(fun: (String, String) => ResourceDocTag)
(dynamicEntityInfo: DynamicEntityInfo): mutable.Map[(DynamicEntityOperation, String), ResourceDoc] = {
val entityName = dynamicEntityInfo.entityName
val hasPersonalEntity = dynamicEntityInfo.hasPersonalEntity
// e.g: "someMultiple-part_Name" -> ["Some", "Multiple", "Part", "Name"]
val capitalizedNameParts = entityName.split("(?<=[a-z0-9])(?=[A-Z])|-|_").map(_.capitalize).filterNot(_.trim.isEmpty)
val splitName = capitalizedNameParts.mkString(" ")
val splitName = s"""${capitalizedNameParts.mkString(" ")}"""
val splitNameWithBankId = if (dynamicEntityInfo.bankId.isDefined)
s"""$splitName(${dynamicEntityInfo.bankId.getOrElse("")})"""
else
s"""$splitName"""
val mySplitNameWithBankId = s"My$splitNameWithBankId"
val idNameInUrl = StringHelpers.snakify(dynamicEntityInfo.idName).toUpperCase()
val listName = dynamicEntityInfo.listName
val bankId = dynamicEntityInfo.bankId
val resourceDocUrl = if(bankId.isDefined) s"/banks/BANK_ID/$entityName" else s"/$entityName"
val resourceDocUrl = if(bankId.isDefined) s"/banks/${bankId.getOrElse("")}/$entityName" else s"/$entityName"
val myResourceDocUrl = if(bankId.isDefined) s"/banks/${bankId.getOrElse("")}/my/$entityName" else s"/my/$entityName"
val endPoint = APIUtil.dynamicEndpointStub
// (operationType, entityName) -> ResourceDoc
val resourceDocs = scala.collection.mutable.Map[(DynamicEntityOperation, String),ResourceDoc]()
val apiTag: ResourceDocTag = fun(splitName, entityName)
val apiTag: ResourceDocTag = fun(entityName,splitNameWithBankId)
resourceDocs += (DynamicEntityOperation.GET_ALL, entityName) -> ResourceDoc(
resourceDocs += (DynamicEntityOperation.GET_ALL, splitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildGetAllFunctionName(entityName),
buildGetAllFunctionName(bankId, entityName),
"GET",
s"$resourceDocUrl",
s"Get $splitName List",
@ -164,10 +193,11 @@ object DynamicEntityHelper {
Some(List(dynamicEntityInfo.canGetRole)),
createdByBankId= dynamicEntityInfo.bankId
)
resourceDocs += (DynamicEntityOperation.GET_ONE, entityName) -> ResourceDoc(
resourceDocs += (DynamicEntityOperation.GET_ONE, splitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildGetOneFunctionName(entityName),
buildGetOneFunctionName(bankId, entityName),
"GET",
s"$resourceDocUrl/$idNameInUrl",
s"Get $splitName by id",
@ -192,10 +222,10 @@ object DynamicEntityHelper {
createdByBankId= dynamicEntityInfo.bankId
)
resourceDocs += (DynamicEntityOperation.CREATE, entityName) -> ResourceDoc(
resourceDocs += (DynamicEntityOperation.CREATE, splitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildCreateFunctionName(entityName),
buildCreateFunctionName(bankId, entityName),
"POST",
s"$resourceDocUrl",
s"Create new $splitName",
@ -222,10 +252,10 @@ object DynamicEntityHelper {
createdByBankId= dynamicEntityInfo.bankId
)
resourceDocs += (DynamicEntityOperation.UPDATE, entityName) -> ResourceDoc(
resourceDocs += (DynamicEntityOperation.UPDATE, splitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildUpdateFunctionName(entityName),
buildUpdateFunctionName(bankId, entityName),
"PUT",
s"$resourceDocUrl/$idNameInUrl",
s"Update $splitName",
@ -252,10 +282,10 @@ object DynamicEntityHelper {
createdByBankId= dynamicEntityInfo.bankId
)
resourceDocs += (DynamicEntityOperation.DELETE, entityName) -> ResourceDoc(
resourceDocs += (DynamicEntityOperation.DELETE, splitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildDeleteFunctionName(entityName),
buildDeleteFunctionName(bankId, entityName),
"DELETE",
s"$resourceDocUrl/$idNameInUrl",
s"Delete $splitName by id",
@ -279,25 +309,163 @@ object DynamicEntityHelper {
createdByBankId= dynamicEntityInfo.bankId
)
if(hasPersonalEntity){ //only hasPersonalEntity == true, then create the myEndpoints
resourceDocs += (DynamicEntityOperation.GET_ALL, mySplitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildGetAllFunctionName(bankId, s"My$entityName"),
"GET",
s"$myResourceDocUrl",
s"Get My $splitName List",
s"""Get My $splitName List.
|${dynamicEntityInfo.description}
|
|${dynamicEntityInfo.fieldsDescription}
|
|${methodRoutingExample(entityName)}
|
|${authenticationRequiredMessage(true)}
|
|Can do filter on the fields
|e.g: /${entityName}?name=James%20Brown&number=123.456&number=11.11
|Will do filter by this rule: name == "James Brown" && (number==123.456 || number=11.11)
|""".stripMargin,
EmptyBody,
dynamicEntityInfo.getExampleList,
List(
UserNotLoggedIn,
UnknownError
),
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
createdByBankId= dynamicEntityInfo.bankId
)
resourceDocs += (DynamicEntityOperation.GET_ONE, mySplitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildGetOneFunctionName(bankId, s"My$entityName"),
"GET",
s"$myResourceDocUrl/$idNameInUrl",
s"Get My $splitName by id",
s"""Get My $splitName by id.
|${dynamicEntityInfo.description}
|
|${dynamicEntityInfo.fieldsDescription}
|
|${methodRoutingExample(entityName)}
|
|${authenticationRequiredMessage(true)}
|""".stripMargin,
EmptyBody,
dynamicEntityInfo.getSingleExample,
List(
UserNotLoggedIn,
UnknownError
),
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
createdByBankId= dynamicEntityInfo.bankId
)
resourceDocs += (DynamicEntityOperation.CREATE, mySplitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildCreateFunctionName(bankId, s"My$entityName"),
"POST",
s"$myResourceDocUrl",
s"Create new My $splitName",
s"""Create new My $splitName.
|${dynamicEntityInfo.description}
|
|${dynamicEntityInfo.fieldsDescription}
|
|${methodRoutingExample(entityName)}
|
|${authenticationRequiredMessage(true)}
|
|""",
dynamicEntityInfo.getSingleExampleWithoutId,
dynamicEntityInfo.getSingleExample,
List(
UserNotLoggedIn,
InvalidJsonFormat,
UnknownError
),
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
createdByBankId= dynamicEntityInfo.bankId
)
resourceDocs += (DynamicEntityOperation.UPDATE, mySplitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildUpdateFunctionName(bankId, s"My$entityName"),
"PUT",
s"$myResourceDocUrl/$idNameInUrl",
s"Update My $splitName",
s"""Update My $splitName.
|${dynamicEntityInfo.description}
|
|${dynamicEntityInfo.fieldsDescription}
|
|${methodRoutingExample(entityName)}
|
|${authenticationRequiredMessage(true)}
|
|""",
dynamicEntityInfo.getSingleExampleWithoutId,
dynamicEntityInfo.getSingleExample,
List(
UserNotLoggedIn,
InvalidJsonFormat,
UnknownError
),
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
Some(List(dynamicEntityInfo.canUpdateRole)),
createdByBankId= dynamicEntityInfo.bankId
)
resourceDocs += (DynamicEntityOperation.DELETE, mySplitNameWithBankId) -> ResourceDoc(
endPoint,
implementedInApiVersion,
buildDeleteFunctionName(bankId, s"My$entityName"),
"DELETE",
s"$myResourceDocUrl/$idNameInUrl",
s"Delete My $splitName by id",
s"""Delete My $splitName by id
|
|${methodRoutingExample(entityName)}
|
|${authenticationRequiredMessage(true)}
|
|""",
dynamicEntityInfo.getSingleExampleWithoutId,
dynamicEntityInfo.getSingleExample,
List(
UserNotLoggedIn,
UnknownError
),
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
createdByBankId= dynamicEntityInfo.bankId
)
}
resourceDocs
}
private def buildCreateFunctionName(entityName: String) = s"dynamicEntity_create$entityName"
private def buildUpdateFunctionName(entityName: String) = s"dynamicEntity_update$entityName"
private def buildDeleteFunctionName(entityName: String) = s"dynamicEntity_delete$entityName"
private def buildGetOneFunctionName(entityName: String) = s"dynamicEntity_getSingle$entityName"
private def buildGetAllFunctionName(entityName: String) = s"dynamicEntity_get${entityName}List"
private def buildCreateFunctionName(bankId:Option[String], entityName: String) = s"dynamicEntity_create${entityName}_${bankId.getOrElse("")}"
private def buildUpdateFunctionName(bankId:Option[String], entityName: String) = s"dynamicEntity_update${entityName}_${bankId.getOrElse("")}"
private def buildDeleteFunctionName(bankId:Option[String], entityName: String) = s"dynamicEntity_delete${entityName}_${bankId.getOrElse("")}"
private def buildGetOneFunctionName(bankId:Option[String], entityName: String) = s"dynamicEntity_getSingle${entityName}_${bankId.getOrElse("")}"
private def buildGetAllFunctionName(bankId:Option[String], entityName: String) = s"dynamicEntity_get${entityName}List_${bankId.getOrElse("")}"
@inline
private def buildOperationId(entityName: String, fun: String => String): String = {
APIUtil.buildOperationId(implementedInApiVersion, fun(entityName))
private def buildOperationId(bankId:Option[String], entityName: String, fun: (Option[String], String) => String): String = {
APIUtil.buildOperationId(implementedInApiVersion, fun(bankId, entityName))
}
def buildCreateOperationId(entityName: String) = buildOperationId(entityName, buildCreateFunctionName)
def buildUpdateOperationId(entityName: String) = buildOperationId(entityName, buildUpdateFunctionName)
def buildDeleteOperationId(entityName: String) = buildOperationId(entityName, buildDeleteFunctionName)
def buildGetOneOperationId(entityName: String) = buildOperationId(entityName, buildGetOneFunctionName)
def buildGetAllOperationId(entityName: String) = buildOperationId(entityName, buildGetAllFunctionName)
def buildCreateOperationId(bankId:Option[String], entityName: String) = buildOperationId(bankId, entityName, buildCreateFunctionName)
def buildUpdateOperationId(bankId:Option[String], entityName: String) = buildOperationId(bankId, entityName, buildUpdateFunctionName)
def buildDeleteOperationId(bankId:Option[String], entityName: String) = buildOperationId(bankId, entityName, buildDeleteFunctionName)
def buildGetOneOperationId(bankId:Option[String], entityName: String) = buildOperationId(bankId, entityName, buildGetOneFunctionName)
def buildGetAllOperationId(bankId:Option[String], entityName: String) = buildOperationId(bankId, entityName, buildGetAllFunctionName)
private def methodRoutingExample(entityName: String) =
s"""
@ -328,7 +496,7 @@ object DynamicEntityHelper {
|""".stripMargin
}
case class DynamicEntityInfo(definition: String, entityName: String, bankId: Option[String]) {
case class DynamicEntityInfo(definition: String, entityName: String, bankId: Option[String], hasPersonalEntity: Boolean) {
import net.liftweb.json
@ -429,10 +597,28 @@ case class DynamicEntityInfo(definition: String, entityName: String, bankId: Opt
}
object DynamicEntityInfo {
def canCreateRole(entityName: String, bankId:Option[String]): ApiRole = getOrCreateDynamicApiRole("CanCreateDynamicEntity_" + entityName, bankId.isDefined)
def canUpdateRole(entityName: String, bankId:Option[String]): ApiRole = getOrCreateDynamicApiRole("CanUpdateDynamicEntity_" + entityName, bankId.isDefined)
def canGetRole(entityName: String, bankId:Option[String]): ApiRole = getOrCreateDynamicApiRole("CanGetDynamicEntity_" + entityName, bankId.isDefined)
def canDeleteRole(entityName: String, bankId:Option[String]): ApiRole = getOrCreateDynamicApiRole("CanDeleteDynamicEntity_" + entityName, bankId.isDefined)
def canCreateRole(entityName: String, bankId:Option[String]): ApiRole =
if(bankId.isDefined)
getOrCreateDynamicApiRole("CanCreateDynamicEntity_" + entityName, true)
else
getOrCreateDynamicApiRole("CanCreateDynamicEntity_System" + entityName, false)
def canUpdateRole(entityName: String, bankId:Option[String]): ApiRole =
if(bankId.isDefined)
getOrCreateDynamicApiRole("CanUpdateDynamicEntity_" + entityName, true)
else
getOrCreateDynamicApiRole("CanUpdateDynamicEntity_System" + entityName, false)
def canGetRole(entityName: String, bankId:Option[String]): ApiRole =
if(bankId.isDefined)
getOrCreateDynamicApiRole("CanGetDynamicEntity_" + entityName, true)
else
getOrCreateDynamicApiRole("CanGetDynamicEntity_System" + entityName, false)
def canDeleteRole(entityName: String, bankId:Option[String]): ApiRole =
if(bankId.isDefined)
getOrCreateDynamicApiRole("CanDeleteDynamicEntity_" + entityName, true)
else
getOrCreateDynamicApiRole("CanDeleteDynamicEntity_System" + entityName, false)
def roleNames(entityName: String, bankId:Option[String]): List[String] = List(
canCreateRole(entityName, bankId),

View File

@ -43,7 +43,7 @@ import code.api.UKOpenBanking.v3_1_0.OBP_UKOpenBanking_310
import code.api.berlin.group.v1.OBP_BERLIN_GROUP_1
import code.api.builder.OBP_APIBuilder
import code.api.dynamic.endpoint.OBPAPIDynamicEndpoint
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEndpoints, DynamicEntityHelper}
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEndpoints}
import code.api.oauth1a.Arithmetics
import code.api.oauth1a.OauthParams._
import code.api.util.APIUtil.ResourceDoc.{findPathVariableNames, isPathVariable}
@ -55,6 +55,8 @@ import code.api.v1_2.ErrorMessage
import code.api.v2_0_0.CreateEntitlementJSON
import code.api.dynamic.endpoint.helper.DynamicEndpointHelper
import code.api.dynamic.entity.OBPAPIDynamicEntity
import code.api._
import code.api.dynamic.entity.helper.DynamicEntityHelper
import code.api.v5_0_0.OBPAPI5_0_0
import code.api.{DirectLogin, _}
import code.authtypevalidation.AuthenticationTypeValidationProvider
@ -85,7 +87,7 @@ import dispatch.url
import javassist.expr.{ExprEditor, MethodCall}
import javassist.{ClassPool, LoaderClassPath}
import net.liftweb.actor.LAFuture
import net.liftweb.common.{Empty, _}
import net.liftweb.common._
import net.liftweb.http._
import net.liftweb.http.js.JE.JsRaw
import net.liftweb.http.provider.HTTPParam

View File

@ -1,9 +1,10 @@
package code.api.util
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEntityHelper}
import code.api.dynamic.endpoint.helper.DynamicEndpointHelper
import java.util.concurrent.ConcurrentHashMap
import code.api.dynamic.endpoint.helper.DynamicEndpointHelper
import code.api.dynamic.entity.helper.DynamicEntityHelper
import com.openbankproject.commons.util.{JsonAble, ReflectUtils}
import net.liftweb.json.{Formats, JsonAST}
import net.liftweb.json.JsonDSL._
@ -566,23 +567,23 @@ object ApiRole {
case class CanDeleteWebUiProps(requiresBankId: Boolean = false) extends ApiRole
lazy val canDeleteWebUiProps = CanDeleteWebUiProps()
case class CanGetDynamicEntities(requiresBankId: Boolean = false) extends ApiRole
lazy val canGetDynamicEntities = CanGetDynamicEntities()
case class CanGetSystemLevelDynamicEntities(requiresBankId: Boolean = false) extends ApiRole
lazy val canGetSystemLevelDynamicEntities = CanGetSystemLevelDynamicEntities()
case class CanCreateDynamicEntity(requiresBankId: Boolean = false) extends ApiRole
lazy val canCreateDynamicEntity = CanCreateDynamicEntity()
case class CanCreateSystemLevelDynamicEntity(requiresBankId: Boolean = false) extends ApiRole
lazy val canCreateSystemLevelDynamicEntity = CanCreateSystemLevelDynamicEntity()
case class CanCreateBankLevelDynamicEntity(requiresBankId: Boolean = true) extends ApiRole
lazy val canCreateBankLevelDynamicEntity = CanCreateBankLevelDynamicEntity()
case class CanUpdateDynamicEntity(requiresBankId: Boolean = false) extends ApiRole
lazy val canUpdateDynamicEntity = CanUpdateDynamicEntity()
case class CanUpdateSystemLevelDynamicEntity(requiresBankId: Boolean = false) extends ApiRole
lazy val canUpdateSystemDynamicEntity = CanUpdateSystemLevelDynamicEntity()
case class CanUpdateBankLevelDynamicEntity(requiresBankId: Boolean = true) extends ApiRole
lazy val canUpdateBankLevelDynamicEntity = CanUpdateBankLevelDynamicEntity()
case class CanDeleteDynamicEntity(requiresBankId: Boolean = false) extends ApiRole
lazy val canDeleteDynamicEntity = CanDeleteDynamicEntity()
case class CanDeleteSystemLevelDynamicEntity(requiresBankId: Boolean = false) extends ApiRole
lazy val canDeleteSystemLevelDynamicEntity = CanDeleteSystemLevelDynamicEntity()
case class CanDeleteBankLevelDynamicEntity(requiresBankId: Boolean = true) extends ApiRole
lazy val canDeleteBankLevelDynamicEntity = CanDeleteBankLevelDynamicEntity()

View File

@ -63,9 +63,10 @@ import code.validation.{JsonSchemaValidationProvider, JsonValidation}
import net.liftweb.http.JsonResponse
import net.liftweb.util.Props
import code.api.JsonResponseException
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEntityHelper, DynamicEntityInfo}
import code.api.dynamic.endpoint.helper.DynamicEndpointHelper
import code.api.v4_0_0.JSONFactory400
import code.api.dynamic.endpoint.helper.DynamicEndpointHelper
import code.api.dynamic.entity.helper.{DynamicEntityHelper, DynamicEntityInfo}
import code.bankattribute.BankAttribute
import code.connectormethod.{ConnectorMethodProvider, JsonConnectorMethod}
import code.customeraccountlinks.CustomerAccountLinkTrait
@ -3104,7 +3105,7 @@ object NewStyle extends MdcLoggable{
else APIUtil.getPropsValue(s"dynamicEntity.cache.ttl.seconds", "30").toInt
}
def getDynamicEntities(bankId: Option[String]): List[DynamicEntityT] = {
def getDynamicEntities(bankId: Option[String], returnBothBankAndSystemLevel: Boolean): List[DynamicEntityT] = {
import scala.concurrent.duration._
validateBankId(bankId, None)
@ -3112,7 +3113,7 @@ object NewStyle extends MdcLoggable{
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
CacheKeyFromArguments.buildCacheKey {
Caching.memoizeSyncWithProvider(Some(cacheKey.toString()))(dynamicEntityTTL second) {
DynamicEntityProvider.connectorMethodProvider.vend.getDynamicEntities(bankId)
DynamicEntityProvider.connectorMethodProvider.vend.getDynamicEntities(bankId, returnBothBankAndSystemLevel)
}
}
}
@ -3161,6 +3162,8 @@ object NewStyle extends MdcLoggable{
entityId: Option[String],
bankId: Option[String],
queryParameters: Option[Map[String, List[String]]],
userId: Option[String],
isPersonalEntity: Boolean,
callContext: Option[CallContext]): OBPReturnType[Box[JValue]] = {
import DynamicEntityOperation._
validateBankId(bankId, callContext)
@ -3213,7 +3216,7 @@ object NewStyle extends MdcLoggable{
// @(variable-binding pattern), we can use the empty variable
// If there is not instance in requestBody, we just call the `dynamicEntityProcess` directly.
case empty @None =>
Connector.connector.vend.dynamicEntityProcess(operation, entityName, empty, entityId, bankId, queryParameters, callContext)
Connector.connector.vend.dynamicEntityProcess(operation, entityName, empty, entityId, bankId, queryParameters, userId, isPersonalEntity, callContext)
// @(variable-binding pattern), we can use both v and body variables.
case requestBody @Some(body) =>
// If the request body is existing, we need to validate the body first.
@ -3221,7 +3224,7 @@ object NewStyle extends MdcLoggable{
dynamicEntity.validateEntityJson(body, callContext).flatMap {
// If there is no error in the request body
case None =>
Connector.connector.vend.dynamicEntityProcess(operation, entityName, requestBody, entityId, bankId, queryParameters, callContext)
Connector.connector.vend.dynamicEntityProcess(operation, entityName, requestBody, entityId, bankId, queryParameters, userId, isPersonalEntity, callContext)
// If there are errors, we need to show them to end user.
case Some(errorMsg) =>
Helper.booleanToFuture(s"$DynamicEntityInstanceValidateFail details: $errorMsg", cc=callContext)(false)

View File

@ -13,10 +13,11 @@ import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.{jsonDynamicResourceDoc
import code.api.UKOpenBanking.v2_0_0.OBP_UKOpenBanking_200
import code.api.UKOpenBanking.v3_1_0.OBP_UKOpenBanking_310
import code.api.berlin.group.v1.OBP_BERLIN_GROUP_1
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
import code.api.dynamic.endpoint.helper.practise.{DynamicEndpointCodeGenerator, PractiseEndpoint}
import code.api.dynamic.endpoint.helper.{CompiledObjects, DynamicEndpointHelper, DynamicEndpoints, DynamicEntityHelper, DynamicEntityInfo}
import code.api.dynamic.endpoint.helper.{CompiledObjects, DynamicEndpointHelper, DynamicEndpoints}
import code.api.util.APIUtil.{fullBoxOrException, _}
import code.api.util.ApiRole.{canCreateEntitlementAtAnyBank, _}
import code.api.util.ApiRole._
import code.api.util.ApiTag._
import code.api.util.DynamicUtil.Validation
import code.api.util.ErrorMessages.{BankNotFound, _}
@ -41,6 +42,7 @@ import code.api.v3_1_0._
import code.api.v4_0_0.JSONFactory400._
import code.api.dynamic.endpoint.helper._
import code.api.dynamic.endpoint.helper.practise.PractiseEndpoint
import code.api.dynamic.entity.helper.{DynamicEntityHelper, DynamicEntityInfo}
import code.api.v5_0_0.OBPAPI5_0_0
import code.api.{ChargePolicy, Constant, JsonResponseException}
import code.apicollection.MappedApiCollectionsProvider
@ -58,7 +60,7 @@ import code.loginattempts.LoginAttempt
import code.metadata.counterparties.{Counterparties, MappedCounterparty}
import code.metadata.tags.Tags
import code.model.dataAccess.{AuthUser, BankAccountCreation}
import code.model.{toUserExtended, _}
import code.model._
import code.ratelimiting.RateLimitingDI
import code.scope.Scope
import code.snippet.{WebUIPlaceholder, WebUITemplate}
@ -1987,13 +1989,13 @@ trait APIMethods400 {
staticResourceDocs += ResourceDoc(
getDynamicEntities,
getSystemDynamicEntities,
implementedInApiVersion,
nameOf(getDynamicEntities),
nameOf(getSystemDynamicEntities),
"GET",
"/management/dynamic-entities",
"Get Dynamic Entities",
s"""Get the all Dynamic Entities.""",
"/management/system-dynamic-entities",
"Get System Dynamic Entities",
s"""Get all System Dynamic Entities """,
EmptyBody,
ListResult(
"dynamic_entities",
@ -2005,15 +2007,14 @@ trait APIMethods400 {
UnknownError
),
List(apiTagManageDynamicEntity, apiTagApi, apiTagNewStyle),
Some(List(canGetDynamicEntities))
Some(List(canGetSystemLevelDynamicEntities))
)
lazy val getDynamicEntities: OBPEndpoint = {
case "management" :: "dynamic-entities" :: Nil JsonGet req => {
lazy val getSystemDynamicEntities: OBPEndpoint = {
case "management" :: "system-dynamic-entities" :: Nil JsonGet req => {
cc =>
for {
dynamicEntities <- Future(NewStyle.function.getDynamicEntities(None))
dynamicEntities <- Future(NewStyle.function.getDynamicEntities(None, false))
} yield {
val listCommons: List[DynamicEntityCommons] = dynamicEntities
val jObjects = listCommons.map(_.jValue)
@ -2042,14 +2043,14 @@ trait APIMethods400 {
UnknownError
),
List(apiTagManageDynamicEntity, apiTagApi, apiTagNewStyle),
Some(List(canGetBankLevelDynamicEntities, canGetDynamicEntities))
Some(List(canGetBankLevelDynamicEntities))
)
lazy val getBankLevelDynamicEntities: OBPEndpoint = {
case "management" :: "banks" :: bankId :: "dynamic-entities" :: Nil JsonGet req => {
cc =>
for {
dynamicEntities <- Future(NewStyle.function.getDynamicEntities(Some(bankId)))
dynamicEntities <- Future(NewStyle.function.getDynamicEntities(Some(bankId),false))
} yield {
val listCommons: List[DynamicEntityCommons] = dynamicEntities
val jObjects = listCommons.map(_.jValue)
@ -2076,13 +2077,13 @@ trait APIMethods400 {
}
private def createDynamicEntityDoc = ResourceDoc(
createDynamicEntity,
createSystemDynamicEntity,
implementedInApiVersion,
nameOf(createDynamicEntity),
nameOf(createSystemDynamicEntity),
"POST",
"/management/dynamic-entities",
"Create Dynamic Entity",
s"""Create a DynamicEntity.
"/management/system-dynamic-entities",
"Create System Level Dynamic Entity",
s"""Create a system level Dynamic Entity.
|
|
|${authenticationRequiredMessage(true)}
@ -2099,12 +2100,11 @@ trait APIMethods400 {
|```
|${ReferenceType.referenceTypeAndExample.mkString("\n")}
|```
| Note: BankId filed is optional,
| if you add it, the entity will be the Bank level.
| if you omit it, the entity will be the System level.
|
|Note: if you set `hasPersonalEntity` = false, then OBP will not generate the CRUD my FooBar endpoints.
|""",
dynamicEntityRequestBodyExample,
dynamicEntityResponseBodyExample,
dynamicEntityRequestBodyExample.copy(bankId = None),
dynamicEntityResponseBodyExample.copy(bankId = None),
List(
$UserNotLoggedIn,
UserHasMissingRoles,
@ -2112,12 +2112,12 @@ trait APIMethods400 {
UnknownError
),
List(apiTagManageDynamicEntity, apiTagApi, apiTagNewStyle),
Some(List(canCreateDynamicEntity)))
Some(List(canCreateSystemLevelDynamicEntity)))
lazy val createDynamicEntity: OBPEndpoint = {
case "management" :: "dynamic-entities" :: Nil JsonPost json -> _ => {
lazy val createSystemDynamicEntity: OBPEndpoint = {
case "management" :: "system-dynamic-entities" :: Nil JsonPost json -> _ => {
cc =>
val dynamicEntity = DynamicEntityCommons(json.asInstanceOf[JObject], None, cc.userId)
val dynamicEntity = DynamicEntityCommons(json.asInstanceOf[JObject], None, cc.userId, None)
createDynamicEntityMethod(cc, dynamicEntity)
}
}
@ -2145,9 +2145,7 @@ trait APIMethods400 {
|```
|${ReferenceType.referenceTypeAndExample.mkString("\n")}
|```
| Note: BankId filed is optional,
| if you add it, the entity will be the Bank level.
| if you omit it, the entity will be the System level.
| Note: if you set `hasPersonalEntity` = false, then OBP will not generate the CRUD my FooBar endpoints.
|""",
dynamicEntityRequestBodyExample.copy(bankId = None),
dynamicEntityResponseBodyExample,
@ -2159,11 +2157,11 @@ trait APIMethods400 {
UnknownError
),
List(apiTagManageDynamicEntity, apiTagApi, apiTagNewStyle),
Some(List(canCreateBankLevelDynamicEntity, canCreateDynamicEntity)))
Some(List(canCreateBankLevelDynamicEntity)))
lazy val createBankLevelDynamicEntity: OBPEndpoint = {
case "management" ::"banks" :: BankId(bankId) :: "dynamic-entities" :: Nil JsonPost json -> _ => {
cc =>
val dynamicEntity = DynamicEntityCommons(json.asInstanceOf[JObject], None, cc.userId).copy(bankId = Some(bankId.value))
val dynamicEntity = DynamicEntityCommons(json.asInstanceOf[JObject], None, cc.userId, Some(bankId.value))
createDynamicEntityMethod(cc, dynamicEntity)
}
}
@ -2173,14 +2171,14 @@ trait APIMethods400 {
for {
// Check whether there are uploaded data, only if no uploaded data allow to update DynamicEntity.
(entity, _) <- NewStyle.function.getDynamicEntityById(bankId, dynamicEntityId, cc.callContext)
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ALL, entity.entityName, None, None, entity.bankId, None, cc.callContext)
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ALL, entity.entityName, None, None, entity.bankId, None, None, false, cc.callContext)
resultList: JArray = unboxResult(box.asInstanceOf[Box[JArray]], entity.entityName)
_ <- Helper.booleanToFuture(DynamicEntityOperationNotAllowed, cc = cc.callContext) {
resultList.arr.isEmpty
}
jsonObject = json.asInstanceOf[JObject]
dynamicEntity = DynamicEntityCommons(jsonObject, Some(dynamicEntityId), cc.userId).copy(bankId = bankId)
dynamicEntity = DynamicEntityCommons(jsonObject, Some(dynamicEntityId), cc.userId, bankId)
Full(result) <- NewStyle.function.createOrUpdateDynamicEntity(dynamicEntity, cc.callContext)
} yield {
val commonsData: DynamicEntityCommons = result
@ -2190,13 +2188,13 @@ trait APIMethods400 {
private def updateDynamicEntityDoc = ResourceDoc(
updateDynamicEntity,
updateSystemDynamicEntity,
implementedInApiVersion,
nameOf(updateDynamicEntity),
nameOf(updateSystemDynamicEntity),
"PUT",
"/management/dynamic-entities/DYNAMIC_ENTITY_ID",
"Update Dynamic Entity",
s"""Update a DynamicEntity.
"/management/system-dynamic-entities/DYNAMIC_ENTITY_ID",
"Update System Level Dynamic Entity",
s"""Update a System Level Dynamic Entity.
|
|
|${authenticationRequiredMessage(true)}
@ -2215,17 +2213,18 @@ trait APIMethods400 {
|```
|""",
dynamicEntityRequestBodyExample.copy(bankId = None),
dynamicEntityResponseBodyExample,
dynamicEntityResponseBodyExample.copy(bankId= None),
List(
$UserNotLoggedIn,
UserHasMissingRoles,
DynamicEntityNotFoundByDynamicEntityId,
InvalidJsonFormat,
UnknownError
),
List(apiTagManageDynamicEntity, apiTagApi, apiTagNewStyle),
Some(List(canUpdateDynamicEntity)))
lazy val updateDynamicEntity: OBPEndpoint = {
case "management" :: "dynamic-entities" :: dynamicEntityId :: Nil JsonPut json -> _ => {
Some(List(canUpdateSystemDynamicEntity)))
lazy val updateSystemDynamicEntity: OBPEndpoint = {
case "management" :: "system-dynamic-entities" :: dynamicEntityId :: Nil JsonPut json -> _ => {
cc =>
updateDynamicEntityMethod(None, dynamicEntityId, json, cc)
}
@ -2266,7 +2265,7 @@ trait APIMethods400 {
UnknownError
),
List(apiTagManageDynamicEntity, apiTagApi, apiTagNewStyle),
Some(List(canUpdateBankLevelDynamicEntity, canUpdateDynamicEntity)))
Some(List(canUpdateBankLevelDynamicEntity)))
lazy val updateBankLevelDynamicEntity: OBPEndpoint = {
case "management" :: "banks" :: bankId :: "dynamic-entities" :: dynamicEntityId :: Nil JsonPut json -> _ => {
cc =>
@ -2275,12 +2274,12 @@ trait APIMethods400 {
}
staticResourceDocs += ResourceDoc(
deleteDynamicEntity,
deleteSystemDynamicEntity,
implementedInApiVersion,
nameOf(deleteDynamicEntity),
nameOf(deleteSystemDynamicEntity),
"DELETE",
"/management/dynamic-entities/DYNAMIC_ENTITY_ID",
"Delete Dynamic Entity",
"/management/system-dynamic-entities/DYNAMIC_ENTITY_ID",
"Delete System Level Dynamic Entity",
s"""Delete a DynamicEntity specified by DYNAMIC_ENTITY_ID.
|
|""",
@ -2292,9 +2291,9 @@ trait APIMethods400 {
UnknownError
),
List(apiTagManageDynamicEntity, apiTagApi, apiTagNewStyle),
Some(List(canDeleteDynamicEntity)))
lazy val deleteDynamicEntity: OBPEndpoint = {
case "management" :: "dynamic-entities" :: dynamicEntityId :: Nil JsonDelete _ => {
Some(List(canDeleteSystemLevelDynamicEntity)))
lazy val deleteSystemDynamicEntity: OBPEndpoint = {
case "management" :: "system-dynamic-entities" :: dynamicEntityId :: Nil JsonDelete _ => {
cc =>
deleteDynamicEntityMethod(None, dynamicEntityId, cc)
}
@ -2304,14 +2303,14 @@ trait APIMethods400 {
for {
// Check whether there are uploaded data, only if no uploaded data allow to delete DynamicEntity.
(entity, _) <- NewStyle.function.getDynamicEntityById(bankId, dynamicEntityId, cc.callContext)
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ALL, entity.entityName, None, None, entity.bankId, None, cc.callContext)
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ALL, entity.entityName, None, None, entity.bankId, None, None, false, cc.callContext)
resultList: JArray = unboxResult(box.asInstanceOf[Box[JArray]], entity.entityName)
_ <- Helper.booleanToFuture(DynamicEntityOperationNotAllowed, cc = cc.callContext) {
resultList.arr.isEmpty
}
deleted: Box[Boolean] <- NewStyle.function.deleteDynamicEntity(bankId, dynamicEntityId)
} yield {
(deleted, HttpCode.`204`(cc.callContext))
(deleted, HttpCode.`200`(cc.callContext))
}
}
@ -2334,7 +2333,7 @@ trait APIMethods400 {
UnknownError
),
List(apiTagManageDynamicEntity, apiTagApi, apiTagNewStyle),
Some(List(canDeleteBankLevelDynamicEntity, canDeleteDynamicEntity)))
Some(List(canDeleteBankLevelDynamicEntity)))
lazy val deleteBankLevelDynamicEntity: OBPEndpoint = {
case "management" :: "banks" :: bankId :: "dynamic-entities" :: dynamicEntityId :: Nil JsonDelete _ => {
cc =>
@ -2405,6 +2404,7 @@ trait APIMethods400 {
List(
$UserNotLoggedIn,
InvalidJsonFormat,
DynamicEntityNotFoundByDynamicEntityId,
UnknownError
),
List(apiTagManageDynamicEntity, apiTagApi, apiTagNewStyle)
@ -2414,18 +2414,19 @@ trait APIMethods400 {
case "my" :: "dynamic-entities" :: dynamicEntityId :: Nil JsonPut json -> _ => {
cc =>
for {
// Check whether there are uploaded data, only if no uploaded data allow to update DynamicEntity.
(entity, _) <- NewStyle.function.getDynamicEntityById(None, dynamicEntityId, cc.callContext)
_ <- Helper.booleanToFuture(InvalidMyDynamicEntityUser, cc=cc.callContext) {
entity.userId.equals(cc.userId)
dynamicEntities <- Future(NewStyle.function.getDynamicEntitiesByUserId(cc.userId))
entityOption = dynamicEntities.find(_.dynamicEntityId.equals(Some(dynamicEntityId)))
myEntity <- NewStyle.function.tryons(InvalidMyDynamicEntityUser, 400, cc.callContext) {
entityOption.get
}
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ALL, entity.entityName, None, None, entity.bankId, None, cc.callContext)
resultList: JArray = unboxResult(box.asInstanceOf[Box[JArray]], entity.entityName)
// Check whether there are uploaded data, only if no uploaded data allow to update DynamicEntity.
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ALL, myEntity.entityName, None, myEntity.dynamicEntityId, myEntity.bankId, None, Some(myEntity.userId), false, cc.callContext)
resultList: JArray = unboxResult(box.asInstanceOf[Box[JArray]], myEntity.entityName)
_ <- Helper.booleanToFuture(DynamicEntityOperationNotAllowed, cc=cc.callContext) {
resultList.arr.isEmpty
}
jsonObject = json.asInstanceOf[JObject]
dynamicEntity = DynamicEntityCommons(jsonObject, Some(dynamicEntityId), cc.userId)
dynamicEntity = DynamicEntityCommons(jsonObject, Some(dynamicEntityId), cc.userId, myEntity.bankId)
Full(result) <- NewStyle.function.createOrUpdateDynamicEntity(dynamicEntity, cc.callContext)
} yield {
val commonsData: DynamicEntityCommons = result
@ -2457,17 +2458,18 @@ trait APIMethods400 {
case "my" :: "dynamic-entities" :: dynamicEntityId :: Nil JsonDelete _ => {
cc =>
for {
// Check whether there are uploaded data, only if no uploaded data allow to delete DynamicEntity.
(entity, _) <- NewStyle.function.getDynamicEntityById(None, dynamicEntityId, cc.callContext)
_ <- Helper.booleanToFuture(InvalidMyDynamicEntityUser, cc=cc.callContext) {
entity.userId.equals(cc.userId)
dynamicEntities <- Future(NewStyle.function.getDynamicEntitiesByUserId(cc.userId))
entityOption = dynamicEntities.find(_.dynamicEntityId.equals(Some(dynamicEntityId)))
myEntity <- NewStyle.function.tryons(InvalidMyDynamicEntityUser, 400, cc.callContext) {
entityOption.get
}
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ALL, entity.entityName, None, None, entity.bankId, None, cc.callContext)
resultList: JArray = unboxResult(box.asInstanceOf[Box[JArray]], entity.entityName)
// Check whether there are uploaded data, only if no uploaded data allow to delete DynamicEntity.
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ALL, myEntity.entityName, None, myEntity.dynamicEntityId, myEntity.bankId, None, Some(myEntity.userId), false, cc.callContext)
resultList: JArray = unboxResult(box.asInstanceOf[Box[JArray]], myEntity.entityName)
_ <- Helper.booleanToFuture(DynamicEntityOperationNotAllowed, cc=cc.callContext) {
resultList.arr.isEmpty
}
deleted: Box[Boolean] <- NewStyle.function.deleteDynamicEntity(None, dynamicEntityId)
deleted: Box[Boolean] <- NewStyle.function.deleteDynamicEntity(myEntity.bankId, dynamicEntityId)
} yield {
(deleted, HttpCode.`200`(cc.callContext))
}

View File

@ -2534,6 +2534,8 @@ trait Connector extends MdcLoggable {
entityId: Option[String],
bankId: Option[String],
queryParameters: Option[Map[String, List[String]]],
userId: Option[String],
isPersonalEntity: Boolean,
callContext: Option[CallContext]): OBPReturnType[Box[JValue]] = Future{(Failure(setUnimplementedError), callContext)}
def dynamicEndpointProcess(url: String, jValue: JValue, method: HttpMethod, params: Map[String, List[String]], pathParams: Map[String, String],

View File

@ -4420,35 +4420,37 @@ object LocalMappedConnector extends Connector with MdcLoggable {
entityId: Option[String],
bankId: Option[String],
queryParameters: Option[Map[String, List[String]]],
userId: Option[String],
isPersonalEntity: Boolean,
callContext: Option[CallContext]): OBPReturnType[Box[JValue]] = {
Future {
val processResult: Box[JValue] = operation.asInstanceOf[Any] match {
case GET_ALL => Full {
val dataList = DynamicDataProvider.connectorMethodProvider.vend.getAllDataJson(bankId, entityName)
val dataList = DynamicDataProvider.connectorMethodProvider.vend.getAllDataJson(bankId, entityName, userId, isPersonalEntity)
JArray(dataList)
}
case GET_ONE => {
val boxedEntity: Box[JValue] = DynamicDataProvider.connectorMethodProvider.vend
.get(bankId, entityName, entityId.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument the entityId is required.")))
.get(bankId, entityName, entityId.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument the entityId is required.")),userId, isPersonalEntity)
.map(it => json.parse(it.dataJson))
boxedEntity
}
case CREATE => {
val body = requestBody.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument please supply the requestBody."))
val boxedEntity: Box[JValue] = DynamicDataProvider.connectorMethodProvider.vend.save(bankId, entityName, body)
val boxedEntity: Box[JValue] = DynamicDataProvider.connectorMethodProvider.vend.save(bankId, entityName, body, userId, isPersonalEntity)
.map(it => json.parse(it.dataJson))
boxedEntity
}
case UPDATE => {
val body = requestBody.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument please supply the requestBody."))
val boxedEntity: Box[JValue] = DynamicDataProvider.connectorMethodProvider.vend.update(bankId, entityName, body, entityId.get)
val boxedEntity: Box[JValue] = DynamicDataProvider.connectorMethodProvider.vend.update(bankId, entityName, body, entityId.get, userId, isPersonalEntity)
.map(it => json.parse(it.dataJson))
boxedEntity
}
case DELETE => {
val id = entityId.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument the entityId is required. "))
val boxedEntity: Box[JValue] = DynamicDataProvider.connectorMethodProvider.vend.delete(bankId, entityName, id)
val boxedEntity: Box[JValue] = DynamicDataProvider.connectorMethodProvider.vend.delete(bankId, entityName, id, userId, isPersonalEntity)
.map(it => JBool(it))
boxedEntity
}

View File

@ -6487,10 +6487,12 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
entityId: Option[String],
bankId: Option[String],
queryParameters: Option[Map[String, List[String]]],
userId: Option[String],
isPersonalEntity: Boolean,
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, bankId, queryParameters)
val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull , operation, entityName, requestBody, entityId, bankId, queryParameters, userId, isPersonalEntity)
val result: OBPReturnType[Box[JValue]] = sendRequest[InBound](url, HttpMethods.POST, req, callContext).map(convertToTuple(callContext))
result
}

View File

@ -6470,10 +6470,12 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
entityId: Option[String],
bankId: Option[String],
queryParameters: Option[Map[String, List[String]]],
userId: Option[String],
isPersonalEntity: Boolean,
callContext: Option[CallContext]): OBPReturnType[Box[JValue]] = {
import com.openbankproject.commons.dto.{InBoundDynamicEntityProcess => InBound, OutBoundDynamicEntityProcess => OutBound}
val procedureName = StringHelpers.snakify("dynamicEntityProcess")
val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull , operation, entityName, requestBody, entityId, bankId, queryParameters)
val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull , operation, entityName, requestBody, entityId, bankId, queryParameters,userId, isPersonalEntity)
val result: OBPReturnType[Box[JValue]] = sendRequest[InBound](procedureName, req, callContext).map(convertToTuple(callContext))
result
}

View File

@ -17,25 +17,29 @@ trait DynamicDataT {
def dynamicEntityName: String
def dataJson: String
def bankId: Option[String]
def userId: Option[String]
def isPersonalEntity: Boolean
}
case class DynamicDataCommons(dynamicEntityName: String,
dataJson: String,
dynamicDataId: Option[String] = None,
bankId: Option[String]
bankId: Option[String],
userId: Option[String],
isPersonalEntity: Boolean
) extends DynamicDataT with JsonFieldReName
object DynamicDataCommons extends Converter[DynamicDataT, DynamicDataCommons]
trait DynamicDataProvider {
def save(bankId: Option[String], entityName: String, requestBody: JObject): Box[DynamicDataT]
def update(bankId: Option[String], entityName: String, requestBody: JObject, id: String): Box[DynamicDataT]
def get(bankId: Option[String], entityName: String, id: String): Box[DynamicDataT]
def getAllDataJson(bankId: Option[String], entityName: String): List[JObject]
def getAll(bankId: Option[String], entityName: String): List[DynamicDataT]
def delete(bankId: Option[String], entityName: String, id: String): Box[Boolean]
def existsData(dbankId: Option[String], ynamicEntityName: String): Boolean
def save(bankId: Option[String], entityName: String, requestBody: JObject, userId: Option[String], isPersonalEntity: Boolean): Box[DynamicDataT]
def update(bankId: Option[String], entityName: String, requestBody: JObject, id: String, userId: Option[String], isPersonalEntity: Boolean): Box[DynamicDataT]
def get(bankId: Option[String], entityName: String, id: String, userId: Option[String], isPersonalEntity: Boolean): Box[DynamicDataT]
def getAllDataJson(bankId: Option[String], entityName: String, userId: Option[String], isPersonalEntity: Boolean): List[JObject]
def getAll(bankId: Option[String], entityName: String, userId: Option[String], isPersonalEntity: Boolean): List[DynamicDataT]
def delete(bankId: Option[String], entityName: String, id: String, userId: Option[String], isPersonalEntity: Boolean): Box[Boolean]
def existsData(bankId: Option[String], dynamicEntityName: String, userId: Option[String], isPersonalEntity: Boolean): Boolean
}

View File

@ -35,6 +35,7 @@ trait DynamicEntityT {
* @return
*/
def userId: String
def hasPersonalEntity: Boolean
/**
* Add Option(bank_id) to Dynamic Entity.
@ -287,7 +288,7 @@ object ReferenceType {
)
def referenceTypeNames: List[String] = {
val dynamicRefs: List[String] = NewStyle.function.getDynamicEntities(None)
val dynamicRefs: List[String] = NewStyle.function.getDynamicEntities(None, true)
.map(entity => s"reference:${entity.entityName}")
val staticRefs: List[String] = staticRefTypeToValidateFunction.keys.toList
@ -347,7 +348,7 @@ object ReferenceType {
} else {
val dynamicEntityName = typeName.replace("reference:", "")
val errorMsg = s"""$dynamicEntityName not found by the id value '$value', propertyName is '$propertyName'"""
NewStyle.function.invokeDynamicConnector(DynamicEntityOperation.GET_ONE,dynamicEntityName, None, Some(value), None, None, callContext)
NewStyle.function.invokeDynamicConnector(DynamicEntityOperation.GET_ONE,dynamicEntityName, None, Some(value), None, None, None, false,callContext)
.recover {
case _: Throwable => errorMsg
}
@ -363,7 +364,8 @@ case class DynamicEntityCommons(entityName: String,
metadataJson: String,
dynamicEntityId: Option[String] = None,
userId: String,
bankId: Option[String]
bankId: Option[String] ,
hasPersonalEntity: Boolean
) extends DynamicEntityT with JsonFieldReName
object DynamicEntityCommons extends Converter[DynamicEntityT, DynamicEntityCommons] {
@ -374,7 +376,6 @@ object DynamicEntityCommons extends Converter[DynamicEntityT, DynamicEntityCommo
* @param jsonObject the follow schema json:
* {{{
* {
* "BankId": "gh.29.uk",
* "FooBar": {
* "description": "description of this entity, can be markdown text.",
* "required": [
@ -400,7 +401,7 @@ object DynamicEntityCommons extends Converter[DynamicEntityT, DynamicEntityCommo
* @param dynamicEntityId
* @return object of DynamicEntityCommons
*/
def apply(jsonObject: JObject, dynamicEntityId: Option[String], userId: String): DynamicEntityCommons = {
def apply(jsonObject: JObject, dynamicEntityId: Option[String], userId: String, bankId: Option[String]): DynamicEntityCommons = {
def checkFormat(requirement: Boolean, message: String) = {
if (!requirement) throw new IllegalArgumentException(message)
@ -410,11 +411,11 @@ object DynamicEntityCommons extends Converter[DynamicEntityT, DynamicEntityCommo
// validate whether json is object and have a single field, currently support one entity definition
checkFormat(fields.nonEmpty, s"$DynamicEntityInstanceValidateFail The Json root object should have a single entity, but current have none.")
checkFormat(fields.size <= 2, s"$DynamicEntityInstanceValidateFail The Json root object should at most two fields: entity and BankId, but current entityNames: ${fields.map(_.name).mkString(", ")}")
val bankId: Option[String] = fields.filter(_.name=="bankId").map(_.value.asInstanceOf[JString].values).headOption
checkFormat(fields.size <= 2, s"$DynamicEntityInstanceValidateFail The Json root object should at most two fields: entity and hasPersonalEntity, but current entityNames: ${fields.map(_.name).mkString(", ")}")
val JField(entityName, metadataJson) = fields.filter(_.name!="bankId").head
val hasPersonalEntity: Boolean = fields.filter(_.name=="hasPersonalEntity").map(_.value.asInstanceOf[JBool].values).headOption.getOrElse(true)
val JField(entityName, metadataJson) = fields.filter(_.name!="hasPersonalEntity").head
val namePattern = "[-_A-Za-z0-9]+".r.pattern
// validate entity name
@ -519,7 +520,7 @@ object DynamicEntityCommons extends Converter[DynamicEntityT, DynamicEntityCommo
}
})
DynamicEntityCommons(entityName, compactRender(jsonObject), dynamicEntityId, userId, bankId)
DynamicEntityCommons(entityName, compactRender(jsonObject), dynamicEntityId, userId, bankId, hasPersonalEntity)
}
private def allowedFieldType: List[String] = DynamicEntityFieldType.values.map(_.toString) ++: ReferenceType.referenceTypeNames
@ -529,7 +530,7 @@ object DynamicEntityCommons extends Converter[DynamicEntityT, DynamicEntityCommo
* example case classes, as an example schema of DynamicEntity, for request body example usage
* @param FooBar
*/
case class DynamicEntityFooBar(bankId: Option[String], FooBar: DynamicEntityDefinition, dynamicEntityId: Option[String] = None, userId: Option[String] = None)
case class DynamicEntityFooBar(bankId: Option[String], FooBar: DynamicEntityDefinition, dynamicEntityId: Option[String] = None, userId: Option[String] = None, hasPersonalEntity:Boolean = true)
case class DynamicEntityDefinition(description: String, required: List[String],properties: DynamicEntityFullBarFields)
case class DynamicEntityFullBarFields(name: DynamicEntityStringTypeExample, number: DynamicEntityIntTypeExample)
case class DynamicEntityStringTypeExample(`type`: DynamicEntityFieldType, minLength: Int, maxLength: Int, example: String, description: String)
@ -544,7 +545,7 @@ trait DynamicEntityProvider {
// so --> here can not use bankId as parameters:
def getByEntityName(bankId: Option[String], entityName: String): Box[DynamicEntityT]
def getDynamicEntities(bankId: Option[String]): List[DynamicEntityT]
def getDynamicEntities(bankId: Option[String], returnBothBankAndSystemLevel: Boolean): List[DynamicEntityT]
def getDynamicEntitiesByUserId(userId: String): List[DynamicEntity]

View File

@ -13,104 +13,148 @@ import net.liftweb.util.Helpers.tryo
import org.apache.commons.lang3.StringUtils
object MappedDynamicDataProvider extends DynamicDataProvider with CustomJsonFormats{
override def save(bankId: Option[String], entityName: String, requestBody: JObject): Box[DynamicDataT] = {
override def save(bankId: Option[String], entityName: String, requestBody: JObject, userId: Option[String], isPersonalEntity: Boolean): Box[DynamicDataT] = {
val idName = getIdName(entityName)
val JString(idValue) = (requestBody \ idName).asInstanceOf[JString]
val dynamicData: DynamicData = DynamicData.create.DynamicDataId(idValue)
val result = saveOrUpdate(bankId, entityName, requestBody, dynamicData)
val result = saveOrUpdate(bankId, entityName, requestBody, userId, isPersonalEntity, dynamicData)
result
}
override def update(bankId: Option[String], entityName: String, requestBody: JObject, id: String): Box[DynamicDataT] = {
val dynamicData = get(bankId, entityName, id).openOrThrowException(s"$DynamicDataNotFound dynamicEntityName=$entityName, dynameicDataId=$id").asInstanceOf[DynamicData]
saveOrUpdate(bankId, entityName, requestBody, dynamicData)
override def update(bankId: Option[String], entityName: String, requestBody: JObject, id: String, userId: Option[String], isPersonalEntity: Boolean): Box[DynamicDataT] = {
val dynamicData = get(bankId, entityName, id, userId, isPersonalEntity).openOrThrowException(s"$DynamicDataNotFound dynamicEntityName=$entityName, dynamicDataId=$id").asInstanceOf[DynamicData]
saveOrUpdate(bankId, entityName, requestBody, userId, isPersonalEntity, dynamicData)
}
override def get(bankId: Option[String],entityName: String, id: String): Box[DynamicDataT] = {
if(bankId.isEmpty){
override def get(bankId: Option[String],entityName: String, id: String, userId: Option[String], isPersonalEntity: Boolean): Box[DynamicDataT] = {
if(bankId.isEmpty && !isPersonalEntity ){ //isPersonalEntity == false, get all the data, no need for specific userId.
//forced the empty also to a error here. this is get Dynamic by Id, if it return Empty, better show the error in this level.
DynamicData.find(
By(DynamicData.DynamicDataId, id),
By(DynamicData.DynamicEntityName, entityName),
NullRef(DynamicData.BankId),
By(DynamicData.UserId, userId.getOrElse(null)),
By(DynamicData.IsPersonalEntity, false),
NullRef(DynamicData.BankId)
) match {
case Full(dynamicData) => Full(dynamicData)
case _ => Failure(s"$DynamicDataNotFound dynamicEntityName=$entityName, dynameicDataId=$id")
case _ => Failure(s"$DynamicDataNotFound dynamicEntityName=$entityName, dynamicDataId=$id")
}
} else{
} else if(bankId.isEmpty && isPersonalEntity){ //isPersonalEntity == true, get all the data for specific userId.
DynamicData.find(
By(DynamicData.DynamicDataId, id),
By(DynamicData.DynamicEntityName, entityName),
By(DynamicData.UserId, userId.getOrElse(null)),
By(DynamicData.IsPersonalEntity, true),
NullRef(DynamicData.BankId)
) match {
case Full(dynamicData) => Full(dynamicData)
case _ => Failure(s"$DynamicDataNotFound dynamicEntityName=$entityName, dynamicDataId=$id, userId = $userId")
}
} else if(bankId.isDefined && !isPersonalEntity ){ //isPersonalEntity == false, get all the data, no need for specific userId.
//forced the empty also to a error here. this is get Dynamic by Id, if it return Empty, better show the error in this level.
DynamicData.find(
By(DynamicData.DynamicDataId, id),
By(DynamicData.DynamicEntityName, entityName),
By(DynamicData.BankId, bankId.get)
By(DynamicData.IsPersonalEntity, false),
By(DynamicData.BankId, bankId.get),
) match {
case Full(dynamicData) => Full(dynamicData)
case _ => Failure(s"$DynamicDataNotFound dynamicEntityName=$entityName, dynameicDataId=$id")
case _ => Failure(s"$DynamicDataNotFound dynamicEntityName=$entityName, dynamicDataId=$id, bankId= ${bankId.get}")
}
}else{ //isPersonalEntity == true, get all the data for specific userId.
DynamicData.find(
By(DynamicData.DynamicDataId, id),
By(DynamicData.DynamicEntityName, entityName),
By(DynamicData.BankId, bankId.get),
By(DynamicData.UserId, userId.get),
By(DynamicData.IsPersonalEntity, true)
) match {
case Full(dynamicData) => Full(dynamicData)
case _ => Failure(s"$DynamicDataNotFound dynamicEntityName=$entityName, dynamicDataId=$id, bankId= ${bankId.get}, userId = ${userId.get}")
}
}
}
override def getAllDataJson(bankId: Option[String], entityName: String): List[JObject] = {
if(bankId.isEmpty){
DynamicData.findAll(
By(DynamicData.DynamicEntityName, entityName),
NullRef(DynamicData.BankId)
).map(it => json.parse(it.dataJson)).map(_.asInstanceOf[JObject])
} else {
DynamicData.findAll(
By(DynamicData.DynamicEntityName, entityName),
By(DynamicData.BankId, bankId.get)
).map(it => json.parse(it.dataJson)).map(_.asInstanceOf[JObject])
}
override def getAllDataJson(bankId: Option[String], entityName: String, userId: Option[String], isPersonalEntity: Boolean): List[JObject] = {
getAll(bankId: Option[String], entityName: String, userId: Option[String], isPersonalEntity)
.map(it => json.parse(it.dataJson))
.map(_.asInstanceOf[JObject])
}
override def getAll(bankId: Option[String], entityName: String): List[DynamicDataT] = {
if(bankId.isEmpty) {
override def getAll(bankId: Option[String], entityName: String, userId: Option[String], isPersonalEntity: Boolean): List[DynamicDataT] = {
if(bankId.isEmpty && !isPersonalEntity){ //isPersonalEntity == false, get all the data, no need for specific userId.
DynamicData.findAll(
By(DynamicData.DynamicEntityName, entityName),
By(DynamicData.IsPersonalEntity, false),
NullRef(DynamicData.BankId),
)
} else if(bankId.isEmpty && isPersonalEntity){ //isPersonalEntity == true, get all the data for specific userId.
DynamicData.findAll(
By(DynamicData.DynamicEntityName, entityName),
By(DynamicData.UserId, userId.getOrElse(null)),
By(DynamicData.IsPersonalEntity, true),
NullRef(DynamicData.BankId)
)
} else if(bankId.isDefined && !isPersonalEntity){ //isPersonalEntity == false, get all the data, no need for specific userId.
DynamicData.findAll(
By(DynamicData.DynamicEntityName, entityName),
By(DynamicData.IsPersonalEntity, false),
By(DynamicData.BankId, bankId.get),
)
}else{
DynamicData.findAll(
DynamicData.findAll(//isPersonalEntity == true, get all the data for specific userId.
By(DynamicData.DynamicEntityName, entityName),
By(DynamicData.BankId, bankId.get)
By(DynamicData.BankId, bankId.get),
By(DynamicData.UserId, userId.getOrElse(null)),
By(DynamicData.IsPersonalEntity, true)
)
}
}
override def delete(bankId: Option[String], entityName: String, id: String) = {
//forced the empty also to a error here. this is get Dynamic by Id, if it return Empty, better show the error in this level.
//Note: DynamicDataId is UniqueIndex
DynamicData.find(By(DynamicData.DynamicDataId, id), By(DynamicData.DynamicEntityName, entityName)) match {
case Full(dynamicData) => Full(dynamicData.delete_!)
case _ => Failure(s"$DynamicDataNotFound dynamicEntityName=$entityName, dynamicDataId=$id")
}
override def delete(bankId: Option[String], entityName: String, id: String, userId: Option[String], isPersonalEntity: Boolean) = {
get(bankId, entityName, id, userId, isPersonalEntity).map(_.asInstanceOf[DynamicData].delete_!)
}
override def existsData(bankId: Option[String], dynamicEntityName: String): Boolean = {
if(bankId.isEmpty){
override def existsData(bankId: Option[String], dynamicEntityName: String, userId: Option[String], isPersonalEntity: Boolean): Boolean = {
if(bankId.isEmpty && !isPersonalEntity){//isPersonalEntity == false, get all the data, no need for specific userId.
DynamicData.find(
By(DynamicData.DynamicEntityName, dynamicEntityName),
NullRef(DynamicData.BankId)
NullRef(DynamicData.BankId),
By(DynamicData.IsPersonalEntity, false)
).isDefined
} else if(bankId.isDefined && !isPersonalEntity){//isPersonalEntity == false, get all the data, no need for specific userId.
DynamicData.find(
By(DynamicData.DynamicEntityName, dynamicEntityName),
By(DynamicData.BankId, bankId.get),
By(DynamicData.IsPersonalEntity, false)
).nonEmpty
} else if(bankId.isEmpty && isPersonalEntity){ //isPersonalEntity == true, get all the data for specific userId.
DynamicData.find(
By(DynamicData.DynamicEntityName, dynamicEntityName),
NullRef(DynamicData.BankId),
By(DynamicData.IsPersonalEntity, true),
By(DynamicData.UserId, userId.getOrElse(null))
).nonEmpty
} else {
DynamicData.find(
By(DynamicData.DynamicEntityName, dynamicEntityName),
By(DynamicData.BankId, bankId.get)
By(DynamicData.BankId, bankId.get),
By(DynamicData.IsPersonalEntity, true),
By(DynamicData.UserId, userId.getOrElse(null))
).nonEmpty
}
}
private def saveOrUpdate(bankId: Option[String], entityName: String, requestBody: JObject, dynamicData: => DynamicData): Box[DynamicData] = {
private def saveOrUpdate(bankId: Option[String], entityName: String, requestBody: JObject, userId: Option[String], isPersonalEntity: Boolean, dynamicData: => DynamicData): Box[DynamicData] = {
val data: DynamicData = dynamicData
val dataStr = json.compactRender(requestBody)
tryo {
if(bankId.isDefined){
data.DataJson(dataStr).DynamicEntityName(entityName).BankId(bankId.get).saveMe()
} else{
data.DataJson(dataStr).DynamicEntityName(entityName).BankId(null).saveMe()
}
val dataStr = json.compactRender(requestBody)
data.DataJson(dataStr)
.DynamicEntityName(entityName)
.BankId(bankId.getOrElse(null))
.UserId(userId.getOrElse(null))
.IsPersonalEntity(isPersonalEntity)
.saveMe()
}
}
@ -129,11 +173,17 @@ class DynamicData extends DynamicDataT with LongKeyedMapper[DynamicData] with Id
object DataJson extends MappedText(this)
object BankId extends MappedString(this,255)
object UserId extends MappedString(this,255)
object IsPersonalEntity extends MappedBoolean(this)
override def dynamicDataId: Option[String] = Option(DynamicDataId.get)
override def dynamicEntityName: String = DynamicEntityName.get
override def dataJson: String = DataJson.get
override def bankId: Option[String] = Some(BankId.get)
override def bankId: Option[String] = Option(BankId.get)
override def userId: Option[String] = Option(UserId.get)
override def isPersonalEntity: Boolean = IsPersonalEntity.get
}
object DynamicData extends DynamicData with LongKeyedMetaMapper[DynamicData] {

View File

@ -11,17 +11,18 @@ import org.apache.commons.lang3.StringUtils
object MappedDynamicEntityProvider extends DynamicEntityProvider with CustomJsonFormats with MdcLoggable {
override def getById(bankId: Option[String], dynamicEntityId: String): Box[DynamicEntityT] = {
if (bankId.isEmpty)
DynamicEntity.find(By(DynamicEntity.DynamicEntityId, dynamicEntityId))
if (bankId.isEmpty)//If bankId is empty, we only return the system level entities
DynamicEntity.find(
By(DynamicEntity.DynamicEntityId, dynamicEntityId),
NullRef(DynamicEntity.BankId))
else
DynamicEntity.find(
By(DynamicEntity.DynamicEntityId, dynamicEntityId),
By(DynamicEntity.BankId, bankId.get
))
By(DynamicEntity.BankId, bankId.get))
}
override def getByEntityName(bankId: Option[String], entityName: String): Box[DynamicEntityT] =
if (bankId.isEmpty)
if (bankId.isEmpty)//If Bank id is empty, we only return the system level entity
DynamicEntity.find(
By(DynamicEntity.EntityName, entityName),
NullRef(DynamicEntity.BankId)
@ -33,9 +34,11 @@ object MappedDynamicEntityProvider extends DynamicEntityProvider with CustomJson
)
override def getDynamicEntities(bankId: Option[String]): List[DynamicEntity] = {
if (bankId.isEmpty)
override def getDynamicEntities(bankId: Option[String], returnBothBankAndSystemLevel: Boolean): List[DynamicEntity] = {
if(returnBothBankAndSystemLevel)
DynamicEntity.findAll()
else if (bankId.isEmpty)//If Bank id is empty, we only return the system level entity
DynamicEntity.findAll(NullRef(DynamicEntity.BankId))
else
DynamicEntity.findAll(By(DynamicEntity.BankId, bankId.get))
}
@ -63,6 +66,7 @@ object MappedDynamicEntityProvider extends DynamicEntityProvider with CustomJson
.MetadataJson(dynamicEntity.metadataJson)
.UserId(dynamicEntity.userId)
.BankId(dynamicEntity.bankId.getOrElse(null))
.HasPersonalEntity(dynamicEntity.hasPersonalEntity)
.saveMe()
} catch {
case e =>
@ -94,12 +98,14 @@ class DynamicEntity extends DynamicEntityT with LongKeyedMapper[DynamicEntity] w
object MetadataJson extends MappedText(this)
object UserId extends MappedString(this, 255)
object BankId extends MappedString(this, 255)
object HasPersonalEntity extends MappedBoolean(this)
override def dynamicEntityId: Option[String] = Option(DynamicEntityId.get)
override def entityName: String = EntityName.get
override def metadataJson: String = MetadataJson.get
override def userId: String = UserId.get
override def bankId: Option[String] = if (BankId.get == null || BankId.get.isEmpty) None else Some(BankId.get)
override def hasPersonalEntity: Boolean = HasPersonalEntity.get
}
object DynamicEntity extends DynamicEntity with LongKeyedMetaMapper[DynamicEntity] {

View File

@ -1,6 +1,6 @@
package code.entitlement
import code.api.dynamic.endpoint.helper.DynamicEntityInfo
import code.api.dynamic.entity.helper.DynamicEntityInfo
import code.api.util.ApiRole.{CanCreateEntitlementAtAnyBank, CanCreateEntitlementAtOneBank}
import code.api.util.{ErrorMessages, NotificationUtil}
import code.util.{MappedUUID, UUIDString}

View File

@ -325,12 +325,12 @@ class AuthenticationTypeValidationTest extends V400ServerSetup {
feature(s"test AuthenticationTypeValidation endpoints version $VersionOfApi - Validate dynamic entity endpoint request body") {
scenario(s"We will call the endpoint $ApiEndpoint1 with invalid FooBar", ApiEndpoint1, VersionOfApi) {
addOneAuthenticationTypeValidation(allowedDirectLogin, s"OBPv4.0.0-dynamicEntity_createFooBar_${bankId}")
addDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_FooBar", bankId)
addOneAuthenticationTypeValidation(allowedDirectLogin, s"OBPv4.0.0-dynamicEntity_createFooBar_")
addSystemDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_SystemFooBar", "")
When("We make a request v4.0.0")
val request = (dynamicEntity_Request / "banks" / bankId / "FooBar").POST <@ user1
val request = (dynamicEntity_Request / "FooBar").POST <@ user1
val response= makePostRequest(request, newFooBar)
Then("We should get a 400")
response.code should equal(400)
@ -344,11 +344,11 @@ class AuthenticationTypeValidationTest extends V400ServerSetup {
scenario(s"We will call the endpoint $ApiEndpoint1 with valid FooBar", ApiEndpoint1, VersionOfApi) {
addOneAuthenticationTypeValidation(allowedAll, s"OBPv4.0.0-dynamicEntity_createFooBar_${bankId}")
addDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_FooBar", bankId)
addSystemDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_SystemFooBar", "")
When("We make a request v4.0.0")
val request = (dynamicEntity_Request / "banks" / bankId / "FooBar").POST <@ user1
val request = (dynamicEntity_Request / "FooBar").POST <@ user1
val response= makePostRequest(request, newFooBar)
Then("We should get a 201")
response.code should equal(201)
@ -402,13 +402,12 @@ class AuthenticationTypeValidationTest extends V400ServerSetup {
response
}
// prepare one dynamic entity FooBar
private def addDynamicEntity(): APIResponse = {
grantEntitlement(canCreateDynamicEntity)
val request = (v4_0_0_Request / "management" / "dynamic-entities").POST <@ user1
private def addSystemDynamicEntity(): APIResponse = {
grantEntitlement(canCreateSystemLevelDynamicEntity)
val request = (v4_0_0_Request / "management" / "system-dynamic-entities").POST <@ user1
val fooBar =
s"""
|{
| "bankId": "$bankId",
| "FooBar": {
| "description": "description of this entity, can be markdown text.",
| "required": [

View File

@ -770,7 +770,9 @@ class DynamicEndpointHelperTest extends FlatSpec with Matchers {
dynamicDataId: Option[String],
dynamicEntityName: String,
dataJson: String,
bankId: Option[String]
bankId: Option[String],
userId: Option[String],
isPersonalEntity: Boolean
)extends DynamicDataT
val dataJsonString = """{
@ -788,7 +790,7 @@ class DynamicEndpointHelperTest extends FlatSpec with Matchers {
|}
|}""".stripMargin
val dynamicDataJson = json.parse(dataJsonString)
val dynamicDataList = List(DataTest(Some("1"),"PetEntity",dataJsonString, Some(testBankId1.value)), DataTest(Some("2"),"PetEntity2",dataJsonString2, Some(testBankId1.value)))
val dynamicDataList = List(DataTest(Some("1"),"PetEntity",dataJsonString, Some(testBankId1.value), None, false), DataTest(Some("2"),"PetEntity2",dataJsonString2, Some(testBankId1.value), None, false))
val expectedResult = ("PetEntity", "1")

File diff suppressed because it is too large Load Diff

View File

@ -88,10 +88,10 @@ class ForceErrorValidationTest extends V400ServerSetup with PropsReset {
}
scenario(s"We will call the dynamic entity endpoint without authentication", VersionOfApi) {
addDynamicEntity()
addSystemDynamicEntity()
When("We make a request v4.0.0")
val request = (dynamicEntity_Request / "banks" / bankId / "FooBar").POST
val request = (dynamicEntity_Request / "FooBar").POST
val response = makePostRequest(request, correctFooBar, ("Force-Error", "OBP-20006"))
Then("We should get a 401")
@ -410,11 +410,11 @@ class ForceErrorValidationTest extends V400ServerSetup with PropsReset {
////// dynamic entity
feature(s"test dynamic entity endpoints Force-Error, version $VersionOfApi - authenticated access") {
scenario(s"We will call the endpoint $ApiEndpoint3 with Force-Error have wrong format header", VersionOfApi) {
addDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_FooBar", bankId)
addSystemDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_SystemFooBar", "")
When("We make a request v4.0.0")
val request = (dynamicEntity_Request / "banks" / bankId / "FooBar").POST <@ user1
val request = (dynamicEntity_Request / "FooBar").POST <@ user1
val response = makePostRequest(request, correctFooBar, ("Force-Error" -> "OBP-xxxx"))
Then("We should get a 400")
@ -426,11 +426,11 @@ class ForceErrorValidationTest extends V400ServerSetup with PropsReset {
}
scenario(s"We will call the endpoint $ApiEndpoint3 with Force-Error header value not support by current endpoint", VersionOfApi) {
addDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_FooBar", bankId)
addSystemDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_SystemFooBar", "")
When("We make a request v4.0.0")
val request = (dynamicEntity_Request / "banks" / bankId / "FooBar").POST <@ user1
val request = (dynamicEntity_Request / "FooBar").POST <@ user1
val response = makePostRequest(request, correctFooBar, ("Force-Error" -> "OBP-20009"))
Then("We should get a 400")
response.code should equal(400)
@ -441,11 +441,11 @@ class ForceErrorValidationTest extends V400ServerSetup with PropsReset {
}
scenario(s"We will call the endpoint $ApiEndpoint3 with Response-Code header value is not Int", VersionOfApi) {
addDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_FooBar", bankId)
addSystemDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_SystemFooBar", "")
When("We make a request v4.0.0")
val request = (dynamicEntity_Request / "banks" / bankId / "FooBar").POST <@ user1
val request = (dynamicEntity_Request / "FooBar").POST <@ user1
val response = makePostRequest(request, correctFooBar, ("Force-Error" -> "OBP-20006"), ("Response-Code" -> "not_integer"))
Then("We should get a 400")
response.code should equal(400)
@ -456,11 +456,11 @@ class ForceErrorValidationTest extends V400ServerSetup with PropsReset {
}
scenario(s"We will call the endpoint $ApiEndpoint3 with correct Force-Error header value", VersionOfApi) {
addDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_FooBar", bankId)
addSystemDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_SystemFooBar", "")
When("We make a request v4.0.0")
val request = (dynamicEntity_Request / "banks" / bankId / "FooBar").POST <@ user1
val request = (dynamicEntity_Request / "FooBar").POST <@ user1
val response = makePostRequest(request, correctFooBar, ("Force-Error" -> "OBP-20006"))
Then("We should get a 403")
response.code should equal(403)
@ -473,11 +473,11 @@ class ForceErrorValidationTest extends V400ServerSetup with PropsReset {
}
scenario(s"We will call the endpoint $ApiEndpoint3 with correct Force-Error header value and Response-Code value", VersionOfApi) {
addDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_FooBar", bankId)
addSystemDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_SystemFooBar", "")
When("We make a request v4.0.0")
val request = (dynamicEntity_Request / "banks" / bankId / "FooBar").POST <@ user1
val request = (dynamicEntity_Request / "FooBar").POST <@ user1
val response = makePostRequest(request, correctFooBar, ("Force-Error" -> "OBP-20006"), ("Response-Code" -> "444"))
Then("We should get a 444")
response.code should equal(444)
@ -491,11 +491,11 @@ class ForceErrorValidationTest extends V400ServerSetup with PropsReset {
scenario(s"We will call the endpoint $ApiEndpoint3 with correct Force-Error header value, but 'enable.force_error=false'", VersionOfApi) {
setPropsValues("enable.force_error"->"false")
addDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_FooBar", bankId)
addSystemDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_SystemFooBar", "")
When("We make a request v4.0.0")
val request = (dynamicEntity_Request / "banks" / bankId / "FooBar").POST <@ user1
val request = (dynamicEntity_Request / "FooBar").POST <@ user1
val response = makePostRequest(request, correctFooBar, ("Force-Error" -> "OBP-20006"))
Then("We should not get a 403")
response.code should not equal(403)
@ -630,13 +630,12 @@ class ForceErrorValidationTest extends V400ServerSetup with PropsReset {
}
// prepare one dynamic entity FooBar
private def addDynamicEntity(): APIResponse = {
addEntitlement(canCreateDynamicEntity)
val request = (v4_0_0_Request / "management" / "dynamic-entities").POST <@ user1
private def addSystemDynamicEntity(): APIResponse = {
addEntitlement(canCreateSystemLevelDynamicEntity)
val request = (v4_0_0_Request / "management" / "system-dynamic-entities").POST <@ user1
val fooBar =
s"""
|{
| "bankId": "$bankId",
| "FooBar": {
| "description": "description of this entity, can be markdown text.",
| "required": [

View File

@ -49,7 +49,7 @@ class GetScannedApiVersionsTest extends V400ServerSetup {
feature("Get all scanned API versions should works") {
scenario("We get all the scanned API versions", ApiEndpoint, VersionOfApi) {
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateDynamicEntity.toString)
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateSystemLevelDynamicEntity.toString)
When("We make a request v4.0.0")
val request = (v4_0_0_Request / "api" / "versions").GET

View File

@ -327,12 +327,12 @@ class JsonSchemaValidationTest extends V400ServerSetup {
feature(s"test JSON Schema Validation endpoints version $VersionOfApi - Validate dynamic entity endpoint request body") {
scenario(s"We will call the endpoint $ApiEndpoint1 with invalid FooBar", ApiEndpoint1, VersionOfApi) {
addOneValidation(jsonSchemaFooBar, s"OBPv4.0.0-dynamicEntity_createFooBar_${bankId}")
addDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_FooBar", bankId)
addOneValidation(jsonSchemaFooBar, s"OBPv4.0.0-dynamicEntity_createFooBar_")
addSystemDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_SystemFooBar", "")
When("We make a request v4.0.0")
val request = (dynamicEntity_Request / "banks" / bankId / "FooBar").POST <@ user1
val request = (dynamicEntity_Request / "FooBar").POST <@ user1
val response= makePostRequest(request, wrongFooBar)
Then("We should get a 400")
response.code should equal(400)
@ -345,11 +345,11 @@ class JsonSchemaValidationTest extends V400ServerSetup {
scenario(s"We will call the endpoint $ApiEndpoint1 with valid FooBar", ApiEndpoint1, VersionOfApi) {
addOneValidation(jsonSchemaFooBar, s"OBPv4.0.0-dynamicEntity_createFooBar_${bankId}")
addDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_FooBar", bankId)
addSystemDynamicEntity()
addStringEntitlement("CanCreateDynamicEntity_SystemFooBar", "")
When("We make a request v4.0.0")
val request = (dynamicEntity_Request / "banks" / bankId / "FooBar").POST <@ user1
val request = (dynamicEntity_Request / "FooBar").POST <@ user1
val response= makePostRequest(request, correctFooBar)
Then("We should get a 201")
response.code should equal(201)
@ -404,13 +404,12 @@ class JsonSchemaValidationTest extends V400ServerSetup {
response
}
// prepare one dynamic entity FooBar
private def addDynamicEntity(): APIResponse = {
addEntitlement(canCreateDynamicEntity)
val request = (v4_0_0_Request / "management" / "dynamic-entities").POST <@ user1
private def addSystemDynamicEntity(): APIResponse = {
addEntitlement(canCreateSystemLevelDynamicEntity)
val request = (v4_0_0_Request / "management" / "system-dynamic-entities").POST <@ user1
val fooBar =
s"""
|{
| "bankId": "$bankId",
| "FooBar": {
| "description": "description of this entity, can be markdown text.",
| "required": [

View File

@ -986,7 +986,9 @@ case class OutBoundDynamicEntityProcess (outboundAdapterCallContext: OutboundAda
requestBody: Option[JObject],
entityId: Option[String],
bankId: Option[String],
queryParameters: Option[Map[String, List[String]]]) extends TopicTrait
queryParameters: Option[Map[String, List[String]]],
userId: Option[String],
isPersonalEntity: Boolean)extends TopicTrait
case class InBoundDynamicEntityProcess (inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: JValue) extends InBoundTrait[JValue]
// because swagger generate not support JValue type, so here supply too xxxDoc TO generate correct request and response body example