mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 17:56:46 +00:00
Merge remote-tracking branch 'BaiShuang/feature/dynamic_entity_refactor_simplify' into develop
This commit is contained in:
commit
bbf3f9b13c
@ -12,7 +12,8 @@ import code.api.v1_4_0.{APIMethods140, JSONFactory1_4_0, OBPAPI1_4_0}
|
||||
import code.api.v2_2_0.{APIMethods220, OBPAPI2_2_0}
|
||||
import code.api.v3_0_0.OBPAPI3_0_0
|
||||
import code.api.v3_1_0.OBPAPI3_1_0
|
||||
import code.api.v4_0_0.OBPAPI4_0_0
|
||||
import code.api.v4_0_0.{APIMethods400, OBPAPI4_0_0}
|
||||
import APIMethods400.Implementations4_0_0.genericEndpoint
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.tesobe.{CacheKeyFromArguments, CacheKeyOmit}
|
||||
import net.liftweb.common.{Box, Empty, Full}
|
||||
@ -619,7 +620,7 @@ def filterResourceDocs(allResources: List[ResourceDoc], showCore: Option[Boolean
|
||||
val jsonOut = for {
|
||||
requestedApiVersion <- Full(ApiVersion.valueOf(requestedApiVersionString)) ?~! InvalidApiVersionString
|
||||
_ <- booleanToBox(versionIsAllowed(requestedApiVersion), ApiVersionNotSupported)
|
||||
rd <- getResourceDocsList(requestedApiVersion)
|
||||
rd <- getResourceDocsList(requestedApiVersion).map(_.filterNot(_.partialFunction == genericEndpoint)) // exclude all DynamicEntity endpoints
|
||||
} yield {
|
||||
// Filter
|
||||
val rdFiltered = filterResourceDocs(rd, showCore, showPSD2, showOBWG, resourceDocTags, partialFunctionNames)
|
||||
|
||||
@ -3432,29 +3432,6 @@ object SwaggerDefinitionsJSON {
|
||||
overall_balance = amountOfMoney,
|
||||
overall_balance_date = DateWithMsExampleObject
|
||||
)
|
||||
|
||||
val dynamicEntityCommons = DynamicEntityCommons(entityName = "FooBar", metadataJson =
|
||||
"""
|
||||
|{
|
||||
| \"definitions\": {
|
||||
| \"FooBar\": {
|
||||
| \"required\": [
|
||||
| \"name\"
|
||||
| ],
|
||||
| \"properties\": {
|
||||
| \"name\": {
|
||||
| \"type\": \"string\",
|
||||
| \"example\": \"James Brown\"
|
||||
| },
|
||||
| \"number\": {
|
||||
| \"type\": \"integer\",
|
||||
| \"example\": \"698761728934\"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
|}
|
||||
|""".stripMargin, dynamicEntityId = Some("dynamic-entity-id"))
|
||||
|
||||
//The common error or success format.
|
||||
//Just some helper format to use in Json
|
||||
|
||||
@ -20,12 +20,11 @@ import net.liftweb.util.StringHelpers
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import code.api.v3_1_0.ListResult
|
||||
import code.dynamicEntity.DynamicEntityCommons
|
||||
import code.api.v4_0_0.APIMethods400.Implementations4_0_0.genericEndpoint
|
||||
import net.liftweb.common.{EmptyBox, Full}
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
import scala.reflect.runtime.universe
|
||||
import scala.tools.scalap.scalax.util.StringUtil
|
||||
|
||||
object SwaggerJSONFactory {
|
||||
//Info Object
|
||||
@ -794,11 +793,10 @@ object SwaggerJSONFactory {
|
||||
|
||||
// extract uploaded DynamicEntities definitions, only when processing resourceDocList have DynamicEntity
|
||||
val dynamicEntityDefinitions = resourceDocList
|
||||
.find(_.exampleRequestBody.isInstanceOf[DynamicEntityCommons])
|
||||
.find(_.partialFunction == genericEndpoint)
|
||||
.map(_ => {
|
||||
NewStyle.function.getDynamicEntities()
|
||||
.map(it => parse(it.metadataJson) \ "definitions")
|
||||
.map(compactRender(_))
|
||||
.map(_.metadataJson)
|
||||
.map(StringUtils.substring(_, 1, -1))
|
||||
})
|
||||
.toList.flatten
|
||||
|
||||
@ -2,6 +2,8 @@ package code.api.util
|
||||
|
||||
|
||||
import code.api.util.Glossary.{glossaryItems, makeGlossaryItem}
|
||||
import code.dynamicEntity.{DynamicEntityDefinition, DynamicEntityFooBar, DynamicEntityFullBarFields, DynamicEntityTypeExample}
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityFieldType
|
||||
|
||||
case class ConnectorField(value: String, description: String) {
|
||||
|
||||
@ -283,6 +285,39 @@ object ExampleValue {
|
||||
// if yes, please rename the follow to lastOkDateExample, and delete outBoundCreateCustomerLastOkDateExample
|
||||
lazy val outBoundCreateCustomerLastOkDateExample = ConnectorField("2019-09-12", "fix me, lastOkDate Date string")
|
||||
|
||||
|
||||
//this is only for dynamicEntity post or request body example
|
||||
"""
|
||||
|{
|
||||
| "FooBar": {
|
||||
| "required": [
|
||||
| "name"
|
||||
| ],
|
||||
| "properties": {
|
||||
| "name": {
|
||||
| "type": "string",
|
||||
| "example": "James Brown"
|
||||
| },
|
||||
| "number": {
|
||||
| "type": "integer",
|
||||
| "example": "698761728934"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
|}
|
||||
|""".stripMargin
|
||||
|
||||
lazy val dynamicEntityRequestBodyExample = DynamicEntityFooBar(
|
||||
DynamicEntityDefinition(
|
||||
List("name"),
|
||||
DynamicEntityFullBarFields(
|
||||
DynamicEntityTypeExample(DynamicEntityFieldType.string, "James Brown"),
|
||||
DynamicEntityTypeExample(DynamicEntityFieldType.integer, "698761728934")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
lazy val dynamicEntityResponseBodyExample = dynamicEntityRequestBodyExample.copy(dynamicEntityId = Some("dynamic-entity-id"))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ import code.api.util._
|
||||
import code.api.v1_4_0.JSONFactory1_4_0.{ChallengeAnswerJSON, TransactionRequestAccountJsonV140}
|
||||
import code.api.v2_1_0._
|
||||
import code.api.v3_1_0.ListResult
|
||||
import code.dynamicEntity.DynamicEntityCommons
|
||||
import code.dynamicEntity.{DynamicEntityCommons, DynamicEntityDefinition}
|
||||
import code.model.dataAccess.AuthUser
|
||||
import code.model.toUserExtended
|
||||
import code.transactionrequests.TransactionRequests.TransactionChallengeTypes._
|
||||
@ -23,9 +23,11 @@ import com.openbankproject.commons.model._
|
||||
import net.liftweb.common.{Box, Full}
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
import net.liftweb.json.Serialization.write
|
||||
import net.liftweb.json._
|
||||
import code.api.util.ExampleValue.{dynamicEntityRequestBodyExample, dynamicEntityResponseBodyExample}
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityFieldType
|
||||
import net.liftweb.util.StringHelpers
|
||||
import org.atteo.evo.inflector.English
|
||||
import net.liftweb.json._
|
||||
|
||||
import scala.collection.immutable.{List, Nil}
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
@ -686,30 +688,8 @@ trait APIMethods400 {
|
||||
emptyObjectJson,
|
||||
ListResult(
|
||||
"dynamic_entities",
|
||||
(List(DynamicEntityCommons(entityName = "FooBar", metadataJson =
|
||||
"""
|
||||
|{
|
||||
| "definitions": {
|
||||
| "FooBar": {
|
||||
| "required": [
|
||||
| "name"
|
||||
| ],
|
||||
| "properties": {
|
||||
| "name": {
|
||||
| "type": "string",
|
||||
| "example": "James Brown"
|
||||
| },
|
||||
| "number": {
|
||||
| "type": "integer",
|
||||
| "example": "698761728934"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
|}
|
||||
|""".stripMargin)))
|
||||
)
|
||||
,
|
||||
List(dynamicEntityResponseBodyExample)
|
||||
),
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
@ -730,23 +710,17 @@ trait APIMethods400 {
|
||||
dynamicEntities <- Future(NewStyle.function.getDynamicEntities())
|
||||
} yield {
|
||||
val listCommons: List[DynamicEntityCommons] = dynamicEntities
|
||||
(ListResult("dynamic_entities", listCommons), HttpCode.`200`(callContext))
|
||||
val jObjects = listCommons.map(_.jValue)
|
||||
(ListResult("dynamic_entities", jObjects), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def validateDynamicEntityJson(data: DynamicEntityCommons) = {
|
||||
val metadataJson = net.liftweb.json.parse(data.metadataJson)
|
||||
|
||||
val rqs = (metadataJson \ "definitions" \ data.entityName \ "required").extract[Array[String]]
|
||||
|
||||
val propertiesFields = (metadataJson \ "definitions" \ data.entityName \ "properties").asInstanceOf[JObject].values
|
||||
require(rqs.toSet.diff(propertiesFields.keySet).isEmpty)
|
||||
propertiesFields.values.foreach(pair => {
|
||||
val map = pair.asInstanceOf[Map[String, _]]
|
||||
require(map("type").isInstanceOf[String])
|
||||
require(map("example") != null)
|
||||
})
|
||||
private def validateDynamicEntityJson(metadataJson: JValue) = {
|
||||
val jFields = metadataJson.asInstanceOf[JObject].obj
|
||||
require(jFields.size == 1, "json format for create or update DynamicEntity is not correct, it should have a single key value for structure definition")
|
||||
val JField(_, definition) = jFields.head
|
||||
definition.extract[DynamicEntityDefinition]
|
||||
}
|
||||
|
||||
resourceDocs += ResourceDoc(
|
||||
@ -755,68 +729,20 @@ trait APIMethods400 {
|
||||
nameOf(createDynamicEntity),
|
||||
"POST",
|
||||
"/management/dynamic_entities",
|
||||
"Add DynamicEntity",
|
||||
s"""Add a DynamicEntity.
|
||||
"Create DynamicEntity",
|
||||
s"""Create a DynamicEntity.
|
||||
|
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|
|
||||
|Explanation of Fields:
|
||||
|Create one DynamicEntity, after created success, the corresponding CURD endpoints will be generated automatically
|
||||
|
|
||||
|* method_name is required String value
|
||||
|* connector_name is required String value
|
||||
|* is_bank_id_exact_match is required boolean value, if bank_id_pattern is exact bank_id value, this value is true; if bank_id_pattern is null or a regex, this value is false
|
||||
|* bank_id_pattern is optional String value, it can be null, a exact bank_id or a regex
|
||||
|* parameters is optional array of key value pairs. You can set some paremeters for this method
|
||||
|Current support filed types as follow:
|
||||
|${DynamicEntityFieldType.values.map(_.toString).mkString("[", ", ", "]")}
|
||||
|
|
||||
|note:
|
||||
|
|
||||
|* if bank_id_pattern is regex, special characters need to do escape, for example: bank_id_pattern = "some\\-id_pattern_\\d+"
|
||||
|""",
|
||||
DynamicEntityCommons(entityName = "FooBar", metadataJson =
|
||||
"""
|
||||
|{
|
||||
| "definitions": {
|
||||
| "FooBar": {
|
||||
| "required": [
|
||||
| "name"
|
||||
| ],
|
||||
| "properties": {
|
||||
| "name": {
|
||||
| "type": "string",
|
||||
| "example": "James Brown"
|
||||
| },
|
||||
| "number": {
|
||||
| "type": "integer",
|
||||
| "example": "698761728934"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
|}
|
||||
|""".stripMargin),
|
||||
DynamicEntityCommons(entityName = "FooBar", metadataJson =
|
||||
"""
|
||||
|{
|
||||
| "definitions": {
|
||||
| "FooBar": {
|
||||
| "required": [
|
||||
| "name"
|
||||
| ],
|
||||
| "properties": {
|
||||
| "name": {
|
||||
| "type": "string",
|
||||
| "example": "James Brown"
|
||||
| },
|
||||
| "number": {
|
||||
| "type": "integer",
|
||||
| "example": "698761728934"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
|}
|
||||
|""".stripMargin, dynamicEntityId = Some("dynamic-entity-id")),
|
||||
dynamicEntityRequestBodyExample,
|
||||
dynamicEntityResponseBodyExample,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
@ -833,17 +759,18 @@ trait APIMethods400 {
|
||||
for {
|
||||
(Full(u), callContext) <- authorizedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, canCreateDynamicEntity, callContext)
|
||||
failMsg = s"$InvalidJsonFormat The Json body should be the ${classOf[DynamicEntityCommons]}, and metadataJson should be the same structure as document example."
|
||||
failMsg = s"$InvalidJsonFormat The Json body should be the same structure as request body example."
|
||||
postedData <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
val data = json.extract[DynamicEntityCommons]
|
||||
validateDynamicEntityJson(data)
|
||||
data
|
||||
validateDynamicEntityJson(json)
|
||||
|
||||
val JField(name, _) = json.asInstanceOf[JObject].obj.head
|
||||
DynamicEntityCommons(name, compactRender(json))
|
||||
}
|
||||
|
||||
Full(dynamicEntity) <- NewStyle.function.createOrUpdateDynamicEntity(postedData)
|
||||
} yield {
|
||||
val commonsData: DynamicEntityCommons = dynamicEntity
|
||||
(commonsData, HttpCode.`201`(callContext))
|
||||
(commonsData.jValue, HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -861,61 +788,14 @@ trait APIMethods400 {
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|
|
||||
|Explanations of Fields:
|
||||
|Update one DynamicEntity, after update finished, the corresponding CURD endpoints will be changed.
|
||||
|
|
||||
|* method_name is required String value
|
||||
|* connector_name is required String value
|
||||
|* is_bank_id_exact_match is required boolean value, if bank_id_pattern is exact bank_id value, this value is true; if bank_id_pattern is null or a regex, this value is false
|
||||
|* bank_id_pattern is optional String value, it can be null, a exact bank_id or a regex
|
||||
|* parameters is optional array of key value pairs. You can set some paremeters for this method
|
||||
|note:
|
||||
|Current support filed types as follow:
|
||||
|${DynamicEntityFieldType.values.map(_.toString).mkString("[", ", ", "]")}
|
||||
|
|
||||
|* if bank_id_pattern is regex, special characters need to do escape, for example: bank_id_pattern = "some\\-id_pattern_\\d+"
|
||||
|""",
|
||||
DynamicEntityCommons(entityName = "FooBar", metadataJson =
|
||||
"""
|
||||
|{
|
||||
| "definitions": {
|
||||
| "FooBar": {
|
||||
| "required": [
|
||||
| "name"
|
||||
| ],
|
||||
| "properties": {
|
||||
| "name": {
|
||||
| "type": "string",
|
||||
| "example": "James Brown"
|
||||
| },
|
||||
| "number": {
|
||||
| "type": "integer",
|
||||
| "example": "698761728934"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
|}
|
||||
|""".stripMargin),
|
||||
DynamicEntityCommons(entityName = "FooBar", metadataJson =
|
||||
"""
|
||||
|{
|
||||
| "definitions": {
|
||||
| "FooBar": {
|
||||
| "required": [
|
||||
| "name"
|
||||
| ],
|
||||
| "properties": {
|
||||
| "name": {
|
||||
| "type": "string",
|
||||
| "example": "James Brown"
|
||||
| },
|
||||
| "number": {
|
||||
| "type": "integer",
|
||||
| "example": "698761728934"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
|}
|
||||
|""".stripMargin, dynamicEntityId = Some("dynamic-entity-id")),
|
||||
dynamicEntityRequestBodyExample,
|
||||
dynamicEntityResponseBodyExample,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
@ -933,11 +813,11 @@ trait APIMethods400 {
|
||||
(Full(u), callContext) <- authorizedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, canUpdateDynamicEntity, callContext)
|
||||
|
||||
failMsg = s"$InvalidJsonFormat The Json body should be the ${classOf[DynamicEntityCommons]}, and metadataJson should be the same structure as document example."
|
||||
failMsg = s"$InvalidJsonFormat The Json body should be the same structure as request body example."
|
||||
putData <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
val data = json.extract[DynamicEntityCommons].copy(dynamicEntityId = Some(dynamicEntityId))
|
||||
validateDynamicEntityJson(data)
|
||||
data
|
||||
validateDynamicEntityJson(json)
|
||||
val JField(name, _) = json.asInstanceOf[JObject].obj.head
|
||||
DynamicEntityCommons(name, compactRender(json), Some(dynamicEntityId))
|
||||
}
|
||||
|
||||
(_, _) <- NewStyle.function.getDynamicEntityById(dynamicEntityId, callContext)
|
||||
@ -945,7 +825,7 @@ trait APIMethods400 {
|
||||
Full(dynamicEntity) <- NewStyle.function.createOrUpdateDynamicEntity(putData)
|
||||
} yield {
|
||||
val commonsData: DynamicEntityCommons = dynamicEntity
|
||||
(commonsData, HttpCode.`200`(callContext))
|
||||
(commonsData.jValue, HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ object MockerConnector {
|
||||
collection.mutable.ArrayBuffer(docs:_*)
|
||||
}
|
||||
|
||||
// TODO the reqestBody and responseBody is not correct ref type
|
||||
// TODO the requestBody and responseBody is not correct ref type
|
||||
def createDocs(dynamicEntityInfo: DynamicEntityInfo) = {
|
||||
val entityName = dynamicEntityInfo.entityName
|
||||
val idNameInUrl = StringHelpers.snakify(dynamicEntityInfo.idName).toUpperCase()
|
||||
@ -207,7 +207,7 @@ case class DynamicEntityInfo(definition: String, entityName: String) {
|
||||
)
|
||||
|
||||
val definitionJson = json.parse(definition).asInstanceOf[JObject]
|
||||
val entity = (definitionJson \ "definitions" \ entityName).asInstanceOf[JObject]
|
||||
val entity = (definitionJson \ entityName).asInstanceOf[JObject]
|
||||
|
||||
def toResponse(result: JObject, id: Option[String]): JObject = {
|
||||
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
package code.dynamicEntity
|
||||
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityFieldType
|
||||
import com.openbankproject.commons.model.{Converter, JsonFieldReName}
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.json.JsonAST.JString
|
||||
import net.liftweb.json.JsonDSL._
|
||||
import net.liftweb.json.{JField, JObject, JsonAST}
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
object DynamicEntityProvider extends SimpleInjector {
|
||||
@ -20,7 +24,27 @@ trait DynamicEntityT {
|
||||
case class DynamicEntityCommons(entityName: String,
|
||||
metadataJson: String,
|
||||
dynamicEntityId: Option[String] = None,
|
||||
) extends DynamicEntityT with JsonFieldReName
|
||||
) extends DynamicEntityT with JsonFieldReName {
|
||||
private val definition: JObject = net.liftweb.json.parse(metadataJson).asInstanceOf[JObject]
|
||||
//convert metadataJson to JValue, so the final json field metadataJson have no escaped " to \", have good readable
|
||||
lazy val jValue = dynamicEntityId match {
|
||||
case Some(id) => {
|
||||
val jId: JObject = "dynamicEntityId" -> id
|
||||
// add dynamicEntityId to JObject
|
||||
definition merge jId
|
||||
}
|
||||
case None => definition
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* an example schema of DynamicEntity, this is as request body example usage
|
||||
* @param FooBar
|
||||
*/
|
||||
case class DynamicEntityFooBar(FooBar: DynamicEntityDefinition, dynamicEntityId: Option[String] = None)
|
||||
case class DynamicEntityDefinition(required: List[String],properties: DynamicEntityFullBarFields)
|
||||
case class DynamicEntityFullBarFields(name: DynamicEntityTypeExample, number: DynamicEntityTypeExample)
|
||||
case class DynamicEntityTypeExample(`type`: DynamicEntityFieldType, example: String)
|
||||
|
||||
object DynamicEntityCommons extends Converter[DynamicEntityT, DynamicEntityCommons]
|
||||
|
||||
|
||||
@ -44,4 +44,13 @@ object PemCertificateRole extends OBPEnumeration[PemCertificateRole] {
|
||||
object PSP_AI extends Value
|
||||
object PSP_PI extends Value
|
||||
}
|
||||
//------api enumerations end ----
|
||||
//------api enumerations end ----
|
||||
sealed trait DynamicEntityFieldType extends EnumValue
|
||||
object DynamicEntityFieldType extends OBPEnumeration[DynamicEntityFieldType]{
|
||||
object string extends Value
|
||||
object number extends Value
|
||||
object integer extends Value
|
||||
object boolean extends Value
|
||||
// object array extends Value
|
||||
// object `object` extends Value //TODO in the future, we consider support nested type
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user