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

This commit is contained in:
hongwei 2019-09-16 16:18:24 +02:00
commit bbf3f9b13c
8 changed files with 114 additions and 190 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = {

View File

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

View File

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