Merge pull request #2020 from hongwei1/feature/added_update_delete_to_dynamicEndpoint

feature/added the support for update/delete functions for dynamicEndpoint
This commit is contained in:
Simon Redfern 2022-02-28 10:52:21 -06:00 committed by GitHub
commit 60ea682a3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 217 additions and 155 deletions

View File

@ -64,7 +64,7 @@ import code.validation.{JsonSchemaValidationProvider, JsonValidation}
import net.liftweb.http.JsonResponse
import net.liftweb.util.Props
import code.api.JsonResponseException
import code.api.v4_0_0.dynamic.{DynamicEndpointHelper, DynamicEntityInfo}
import code.api.v4_0_0.dynamic.{DynamicEndpointHelper, DynamicEntityHelper, DynamicEntityInfo}
import code.bankattribute.BankAttribute
import code.connectormethod.{ConnectorMethodProvider, JsonConnectorMethod}
import code.dynamicMessageDoc.{DynamicMessageDocProvider, JsonDynamicMessageDoc}
@ -2895,10 +2895,7 @@ object NewStyle extends MdcLoggable{
// check if there is (entityIdName, entityIdValue) pair in the requestBody, if no, we will add it.
val requestBodyDynamicInstance: Option[JObject] = requestBody.map { requestJsonJObject =>
// (?<=[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
val entityIdName = s"${entityName}_Id".replaceAll(regexPattern, "_").toLowerCase
val entityIdName = DynamicEntityHelper.createEntityId(entityName)
val entityIdValue = requestJsonJObject \ entityIdName

View File

@ -5716,21 +5716,42 @@ trait APIMethods400 {
responseBody
}
} else if (method.value.equalsIgnoreCase("delete")) {
// for {
// (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)
// }
// isDeleted <- NewStyle.function.tryons(s"$InvalidEndpointMapping `response_mapping` must be linked to at least one valid dynamic entity!", 400, cc.callContext) {
//We need to delete the record by the the request key and value,
// DynamicDataProvider.connectorMethodProvider.vend.delete(entityName, entityIdValueFromUrl.head).head
// }
// }yield{
// JBool(isDeleted)
// }
throw new RuntimeException(s"$NotImplemented We only support the Http Methods GET and POST . The current method is: ${method.value}")
for {
(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(entityName)
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(entityName, entityIdValue).head
}
}yield{
JBool(isDeleted)
}
} else if (method.value.equalsIgnoreCase("put")) {
throw new RuntimeException(s"$NotImplemented We only support the Http Methods GET and POST . The current method is: ${method.value}")
for {
(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(entityName)
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(entityName, entityIdValue).head
}
entityBody = JsonUtils.buildJson(json,requestMappingJvalue)
(box, _) <- NewStyle.function.invokeDynamicConnector(CREATE, entityName, Some(entityBody.asInstanceOf[JObject]), None, None, None, Some(cc))
singleObject: JValue = unboxResult(box.asInstanceOf[Box[JValue]], entityName)
responseBodyScheme = DynamicEndpointHelper.prepareMappingFields(responseMappingJvalue)
responseBody = JsonUtils.buildJson(singleObject, responseBodyScheme)
}yield{
responseBody
}
}else {
NewStyle.function.tryons(s"$InvalidEndpointMapping `request_mapping` must be linked to at least one valid dynamic entity!", 400, cc.callContext) {
DynamicEndpointHelper.getEntityNameKeyAndValue(responseMappingString, pathParams)

View File

@ -56,6 +56,13 @@ object DynamicEntityHelper {
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)

View File

@ -23,6 +23,7 @@ import net.liftweb.json.Serialization.write
import org.scalatest.Tag
import java.util.UUID
import java.util.concurrent.TimeUnit
import scala.util.Random
class AccountTest extends V310ServerSetup with DefaultUsers {
@ -205,6 +206,10 @@ class AccountTest extends V310ServerSetup with DefaultUsers {
account.label should be (putCreateAccountJSONV310.label)
account.account_routings should be (putCreateAccountJSONV310.account_routings)
//We need to waite some time for the account creation, because we introduce `AuthUser.refreshUser(user, callContext)`
//It may not finished when we call the get accounts directly.
TimeUnit.SECONDS.sleep(2)
Then(s"we call $ApiEndpoint4 to get the account back")
val requestApiEndpoint4 = (v3_1_0_Request / "my" / "accounts" ).PUT <@(user1)
@ -213,8 +218,7 @@ class AccountTest extends V310ServerSetup with DefaultUsers {
responseApiEndpoint4.code should equal(200)
val accounts = responseApiEndpoint4.body.extract[CoreAccountsJsonV300].accounts
accounts.map(_.id).toList.toString() contains(account.account_id) should be (true)
Then(s"we call $ApiEndpoint5 to get the account back")
val requestApiEndpoint5 = (v3_1_0_Request /"banks" / testBankId.value / "accounts").GET <@ (user1)
val responseApiEndpoint5 = makeGetRequest(requestApiEndpoint5)

View File

@ -9,128 +9,130 @@ import net.liftweb.json.JsonAST.{JNothing, JValue}
class JsonUtilsTest extends FlatSpec with Matchers {
object JsonUtilsTag extends Tag("JsonUtils")
val zson = json.parse(
"""
|{
| "level": 3,
| "banks":[{
| "id":"dmo.01.uk.uk",
| "short_name":"uk",
| "full_name":"uk",
| "is_deleted": true,
| "is_new": true,
| "score": 10,
| "logo":"http://www.example.com",
| "website":"http://www.example.com",
| "bank_routings":[{
| "scheme":"OBP",
| "address":"dmo.01.uk.uk"
| }]
| },{
| "id":"dmo.02.uk.uk",
| "short_name":"uk",
| "full_name":"uk",
| "is_deleted": false,
| "is_new": true,
| "score": 5.3,
| "logo":"https://static.openbankproject.com/images/sandbox/bank_y.png",
| "website":"http://www.example.com"
| },{
| "id":"dmo.02.de.de",
| "short_name":"de",
| "full_name":"de",
| "is_deleted": false,
| "is_new": false,
| "score": 2,
| "logo":"https://static.openbankproject.com/images/sandbox/bank_z.png",
| "website":"http://www.example.com",
| "bank_routings":[{
| "scheme":"OBP",
| "address":"dmo.02.de.de"
| }]
| }]
|}
|""".stripMargin)
val schema = json.parse(
"""
|{
| "value": " 'number: 1.0' + 'int:1' * level",
| "code": 200,
| "meta$default": {
| "count": 10,
| "classInfo": {
| "someInfo": "hello"
| }
| },
| "result[]": {
| "bkId": "banks.id",
| "bkName": "'hello:' + banks.short_name+ ' + ' + banks.full_name",
| "is_exists": "!banks.is_deleted",
| "newBank": "!banks.is_deleted & banks.is_new",
| "routing": "banks.bank_routings[0]"
| },
| "secondBank[1]": {
| "id": "banks.id",
| "name": "banks.short_name",
| "count": "banks.score * 'int: 2'"
| }
|}
|""".stripMargin)
val expectedJson = json.parse(
"""
|{
| "value":6.0,
| "code":200,
| "meta":{
| "count":10,
| "classInfo":{
| "someInfo":"hello"
| }
| },
| "result":[
| {
| "bkId":"dmo.01.uk.uk",
| "bkName":"hello:uk + uk",
| "is_exists":false,
| "newBank":false,
| "routing":{
| "scheme":"OBP",
| "address":"dmo.01.uk.uk"
| }
| },
| {
| "bkId":"dmo.02.uk.uk",
| "bkName":"hello:uk + uk",
| "is_exists":true,
| "newBank":true
| },
| {
| "bkId":"dmo.02.de.de",
| "bkName":"hello:de + de",
| "is_exists":true,
| "newBank":false,
| "routing":{
| "scheme":"OBP",
| "address":"dmo.02.de.de"
| }
| }
| ],
| "secondBank": {
| "id":"dmo.02.uk.uk",
| "name":"uk",
| "count":10.6
| }
|}
|""".stripMargin)
"buildJson" should "generate JValue according schema" taggedAs JsonUtilsTag in {
val zson = json.parse(
"""
|{
| "level": 3,
| "banks":[{
| "id":"dmo.01.uk.uk",
| "short_name":"uk",
| "full_name":"uk",
| "is_deleted": true,
| "is_new": true,
| "score": 10,
| "logo":"http://www.example.com",
| "website":"http://www.example.com",
| "bank_routings":[{
| "scheme":"OBP",
| "address":"dmo.01.uk.uk"
| }]
| },{
| "id":"dmo.02.uk.uk",
| "short_name":"uk",
| "full_name":"uk",
| "is_deleted": false,
| "is_new": true,
| "score": 5.3,
| "logo":"https://static.openbankproject.com/images/sandbox/bank_y.png",
| "website":"http://www.example.com"
| },{
| "id":"dmo.02.de.de",
| "short_name":"de",
| "full_name":"de",
| "is_deleted": false,
| "is_new": false,
| "score": 2,
| "logo":"https://static.openbankproject.com/images/sandbox/bank_z.png",
| "website":"http://www.example.com",
| "bank_routings":[{
| "scheme":"OBP",
| "address":"dmo.02.de.de"
| }]
| }]
|}
|""".stripMargin)
val schema = json.parse(
"""
|{
| "value": " 'number: 1.0' + 'int:1' * level",
| "code": 200,
| "meta$default": {
| "count": 10,
| "classInfo": {
| "someInfo": "hello"
| }
| },
| "result[]": {
| "bkId": "banks.id",
| "bkName": "'hello:' + banks.short_name+ ' + ' + banks.full_name",
| "is_exists": "!banks.is_deleted",
| "newBank": "!banks.is_deleted & banks.is_new",
| "routing": "banks.bank_routings[0]"
| },
| "secondBank[1]": {
| "id": "banks.id",
| "name": "banks.short_name",
| "count": "banks.score * 'int: 2'"
| }
|}
|""".stripMargin)
val expectedJson = json.parse(
"""
|{
| "value":6.0,
| "code":200,
| "meta":{
| "count":10,
| "classInfo":{
| "someInfo":"hello"
| }
| },
| "result":[
| {
| "bkId":"dmo.01.uk.uk",
| "bkName":"hello:uk + uk",
| "is_exists":false,
| "newBank":false,
| "routing":{
| "scheme":"OBP",
| "address":"dmo.01.uk.uk"
| }
| },
| {
| "bkId":"dmo.02.uk.uk",
| "bkName":"hello:uk + uk",
| "is_exists":true,
| "newBank":true
| },
| {
| "bkId":"dmo.02.de.de",
| "bkName":"hello:de + de",
| "is_exists":true,
| "newBank":false,
| "routing":{
| "scheme":"OBP",
| "address":"dmo.02.de.de"
| }
| }
| ],
| "secondBank": {
| "id":"dmo.02.uk.uk",
| "name":"uk",
| "count":10.6
| }
|}
|""".stripMargin)
val resultJson = buildJson(zson, schema)
val str1 = json.prettyRender(resultJson)
// println(str1)
val str2 = json.prettyRender(expectedJson)
// println(str2)
str1 shouldEqual str2
}
@ -160,12 +162,11 @@ class JsonUtilsTest extends FlatSpec with Matchers {
val resultJson = buildJson(zson, schema)
val str1 = json.prettyRender(resultJson)
println(str1)
val str2 = json.prettyRender(expectedJson)
str1 shouldEqual str2
}
"""buildJson-request single{}, mapping is {"photoUrls[]":"field5"}""" should "generate JValue according schema3" taggedAs JsonUtilsTag in {
val zson = (
"""{
@ -220,7 +221,7 @@ class JsonUtilsTest extends FlatSpec with Matchers {
val resultJson = buildJson(zson, mapping)
val str1 = json.prettyRender(resultJson)
println(str1)
val str2 = json.prettyRender(expectedJson)
str1 shouldEqual str2
}
@ -254,7 +255,7 @@ class JsonUtilsTest extends FlatSpec with Matchers {
val resultJson = buildJson(requestJson, mapping)
val str1 = json.prettyRender(resultJson)
println(str1)
val str2 = json.prettyRender(expectedJson)
str1 shouldEqual str2
}
@ -308,7 +309,7 @@ class JsonUtilsTest extends FlatSpec with Matchers {
val resultJson = buildJson(zson, schema)
val str1 = json.prettyRender(resultJson)
println(str1)
val str2 = json.prettyRender(expectedJson)
str1 shouldEqual str2
}
@ -368,9 +369,9 @@ class JsonUtilsTest extends FlatSpec with Matchers {
val resultJson = buildJson(requestJson, mapping)
val str1 = json.prettyRender(resultJson)
println(str1)
val str2 = json.prettyRender(expectedJson)
println(str2)
str1 shouldEqual str2
}
@ -394,7 +395,6 @@ class JsonUtilsTest extends FlatSpec with Matchers {
val resultJson = buildJson(zson, schema)
val str1 = json.prettyRender(resultJson)
println(str1)
val str2 = json.prettyRender(expectedJson)
str1 shouldEqual str2
@ -494,7 +494,7 @@ class JsonUtilsTest extends FlatSpec with Matchers {
val resultJson = buildJson(requestJson, mapping)
val str1 = json.prettyRender(resultJson)
println(str1)
val str2 = json.prettyRender(expectedJson)
str1 shouldEqual str2
}
@ -516,7 +516,6 @@ class JsonUtilsTest extends FlatSpec with Matchers {
val resultJson = buildJson(requestJson, mapping)
val str1 = json.prettyRender(resultJson)
println(str1)
val str2 = json.prettyRender(expectedJson)
str1 shouldEqual str2
}
@ -545,7 +544,7 @@ class JsonUtilsTest extends FlatSpec with Matchers {
val resultJson = buildJson(requestJson, mapping)
val str1 = json.prettyRender(resultJson)
println(str1)
val expectedJson = json.parse(
"""
@ -689,7 +688,6 @@ class JsonUtilsTest extends FlatSpec with Matchers {
val resultJson = buildJson(jsonList, jsonListSchema)
val str1 = json.prettyRender(resultJson)
println(str1)
val str2 = json.prettyRender(expectListResult)
str1 shouldEqual str2
}
@ -730,6 +728,7 @@ class JsonUtilsTest extends FlatSpec with Matchers {
str1 shouldEqual expectedJson
}
"$root[] name field and subField[][] type field" should "properly be converted" taggedAs JsonUtilsTag in {
val jsonList = json.parse(
"""
@ -802,7 +801,6 @@ class JsonUtilsTest extends FlatSpec with Matchers {
|]""".stripMargin)
val resultJson = buildJson(jsonList, schema)
val str1 = json.prettyRender(resultJson)
// println(str1)
val str2 = json.prettyRender(expectedJson)
str1 shouldEqual str2
@ -843,7 +841,6 @@ class JsonUtilsTest extends FlatSpec with Matchers {
val resultJson = buildJson(requestJson, mapping)
val str1 = json.prettyRender(resultJson)
println(str1)
val str2 = json.prettyRender(expectedJson)
str1 shouldEqual str2
}
@ -876,7 +873,44 @@ class JsonUtilsTest extends FlatSpec with Matchers {
val resultJson = buildJson(requestJson, mapping)
val str1 = json.prettyRender(resultJson)
println(str1)
val str2 = json.prettyRender(expectedJson)
str1 shouldEqual str2
}
"buildJson - request is Array1, mapping is []object" should "work well" taggedAs JsonUtilsTag in {
val requestJson = (
"""[
| {
| "name": "family account200",
| "number": 200,
| "sample_entity_id": "9b344781-32f5-4afb-a4f1-2c93087e6e71"
| },
| {
| "name": "family account201",
| "number": 201,
| "sample_entity_id": "38ff936d-6780-444f-81b9-ac7ab8565035"
| }
|]""".stripMargin)
val mapping = ("""{
| "$root[]": {
| "name": "name",
| "balance": "number"
| }
|}""".stripMargin)
val expectedJson = json.parse(
"""[
| {
| "name":"family account200",
| "balance":200,
| },
| {
| "name":"family account201",
| "balance":201
| }
|]""".stripMargin)
val resultJson = buildJson(requestJson, mapping)
val str1 = json.prettyRender(resultJson)
val str2 = json.prettyRender(expectedJson)
str1 shouldEqual str2
}
@ -888,7 +922,6 @@ class JsonUtilsTest extends FlatSpec with Matchers {
val resultJson = buildJson(requestJson, mapping)
val str1 = json.prettyRender(resultJson)
println(str1)
val str2 = json.prettyRender(expectedJson)
str1 shouldEqual str2
}