mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 17:17:09 +00:00
commit
0f4a2ac40f
@ -766,4 +766,7 @@ stored_procedure_connector.poolFactoryName=commons-dbcp2
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Set whether DynamicEntity display name starts with underscore, default is true
|
||||
dynamic_entities_have_prefix=true
|
||||
dynamic_entities_have_prefix=true
|
||||
|
||||
# Url prefix of dynamic endpoints, default is dynamic. e.g if set to foobar, one url can be /obp/v4.0.0/foobar/Address
|
||||
dynamic_endpoints_url_prefix=dynamic
|
||||
@ -31,6 +31,7 @@ import java.util.{Locale, TimeZone}
|
||||
|
||||
import code.CustomerDependants.MappedCustomerDependant
|
||||
import code.DynamicData.DynamicData
|
||||
import code.DynamicEndpoint.DynamicEndpoint
|
||||
import code.accountapplication.MappedAccountApplication
|
||||
import code.accountattribute.MappedAccountAttribute
|
||||
import code.accountholders.MapperAccountHolders
|
||||
@ -712,6 +713,7 @@ object ToSchemify {
|
||||
Authorisation,
|
||||
DynamicEntity,
|
||||
DynamicData,
|
||||
DynamicEndpoint,
|
||||
AccountIdMapping,
|
||||
DirectDebit,
|
||||
StandingOrder
|
||||
|
||||
@ -2019,34 +2019,8 @@ object SwaggerDefinitionsJSON {
|
||||
branch_id = ExampleValue.branchIdExample.value,
|
||||
name_suffix = ExampleValue.nameSuffixExample.value
|
||||
)
|
||||
|
||||
val customersJsonV300 = code.api.v3_0_0.CustomerJSONs(List(customerJsonV300))
|
||||
|
||||
val customerWithAttributesJsonV300 = CustomerWithAttributesJsonV300(
|
||||
bank_id = bankIdExample.value,
|
||||
customer_id = ExampleValue.customerIdExample.value,
|
||||
customer_number = ExampleValue.customerNumberExample.value,
|
||||
legal_name = ExampleValue.legalNameExample.value,
|
||||
mobile_phone_number = ExampleValue.mobileNumberExample.value,
|
||||
email = ExampleValue.emailExample.value,
|
||||
face_image = customerFaceImageJson,
|
||||
date_of_birth = "19900101",
|
||||
relationship_status = ExampleValue.relationshipStatusExample.value,
|
||||
dependants = ExampleValue.dependentsExample.value.toInt,
|
||||
dob_of_dependants = List("19900101"),
|
||||
credit_rating = Option(customerCreditRatingJSON),
|
||||
credit_limit = Option(amountOfMoneyJsonV121),
|
||||
highest_education_attained = ExampleValue.highestEducationAttainedExample.value,
|
||||
employment_status = ExampleValue.employmentStatusExample.value,
|
||||
kyc_status = ExampleValue.kycStatusExample.value.toBoolean,
|
||||
last_ok_date = DateWithDayExampleObject,
|
||||
title = ExampleValue.titleExample.value,
|
||||
branch_id = ExampleValue.branchIdExample.value,
|
||||
name_suffix = ExampleValue.nameSuffixExample.value,
|
||||
customer_attributes = List(customerAttributeResponseJson)
|
||||
)
|
||||
|
||||
val customersWithAttributesJsonV300 = CustomersWithAttributesJsonV300(List(customerWithAttributesJsonV300))
|
||||
val customersJsonV300 = code.api.v3_0_0.CustomerJSONs(List(customerJsonV300))
|
||||
|
||||
val postCustomerJsonV310 =
|
||||
PostCustomerJsonV310(
|
||||
@ -2123,6 +2097,32 @@ object SwaggerDefinitionsJSON {
|
||||
customer_attributes = List(customerAttributeResponseJson)
|
||||
)
|
||||
|
||||
val customerWithAttributesJsonV300 = CustomerWithAttributesJsonV300(
|
||||
bank_id = bankIdExample.value,
|
||||
customer_id = ExampleValue.customerIdExample.value,
|
||||
customer_number = ExampleValue.customerNumberExample.value,
|
||||
legal_name = ExampleValue.legalNameExample.value,
|
||||
mobile_phone_number = ExampleValue.mobileNumberExample.value,
|
||||
email = ExampleValue.emailExample.value,
|
||||
face_image = customerFaceImageJson,
|
||||
date_of_birth = "19900101",
|
||||
relationship_status = ExampleValue.relationshipStatusExample.value,
|
||||
dependants = ExampleValue.dependentsExample.value.toInt,
|
||||
dob_of_dependants = List("19900101"),
|
||||
credit_rating = Option(customerCreditRatingJSON),
|
||||
credit_limit = Option(amountOfMoneyJsonV121),
|
||||
highest_education_attained = ExampleValue.highestEducationAttainedExample.value,
|
||||
employment_status = ExampleValue.employmentStatusExample.value,
|
||||
kyc_status = ExampleValue.kycStatusExample.value.toBoolean,
|
||||
last_ok_date = DateWithDayExampleObject,
|
||||
title = ExampleValue.titleExample.value,
|
||||
branch_id = ExampleValue.branchIdExample.value,
|
||||
name_suffix = ExampleValue.nameSuffixExample.value,
|
||||
customer_attributes = List(customerAttributeResponseJson)
|
||||
)
|
||||
|
||||
val customersWithAttributesJsonV300 = CustomersWithAttributesJsonV300(List(customerWithAttributesJsonV300))
|
||||
|
||||
val putUpdateCustomerDataJsonV310 = PutUpdateCustomerDataJsonV310(
|
||||
face_image = customerFaceImageJson,
|
||||
relationship_status = ExampleValue.relationshipStatusExample.value,
|
||||
@ -3631,8 +3631,7 @@ object SwaggerDefinitionsJSON {
|
||||
product_code = accountTypeExample.value,
|
||||
balance = amountOfMoneyJsonV121,
|
||||
branch_id = branchIdExample.value,
|
||||
account_routing = accountRoutingJsonV121,
|
||||
account_attributes= List(accountAttributeResponseJson)
|
||||
account_routing = accountRoutingJsonV121
|
||||
)
|
||||
|
||||
val postAccountAccessJsonV400 = PostAccountAccessJsonV400(userIdExample.value, PostViewJsonV400(ExampleValue.viewIdExample.value, true))
|
||||
|
||||
@ -81,7 +81,7 @@ import scala.collection.JavaConverters._
|
||||
import scala.collection.immutable.{List, Nil}
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import com.openbankproject.commons.util.{ApiVersion, ReflectUtils, ScannedApiVersion}
|
||||
import com.openbankproject.commons.util.{ApiVersion, JsonAble, ReflectUtils, ScannedApiVersion}
|
||||
import com.openbankproject.commons.util.Functions.Implicits._
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
|
||||
@ -406,6 +406,21 @@ object ApiRole {
|
||||
|
||||
case class CanDeleteDynamicEntity(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canDeleteDynamicEntity = CanDeleteDynamicEntity()
|
||||
|
||||
case class CanGetDynamicEndpoint(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canGetDynamicEndpoint = CanGetDynamicEndpoint()
|
||||
|
||||
case class CanGetDynamicEndpoints(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canGetDynamicEndpoints = CanGetDynamicEndpoints()
|
||||
|
||||
case class CanCreateDynamicEndpoint(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canCreateDynamicEndpoint = CanCreateDynamicEndpoint()
|
||||
|
||||
case class CanUpdateDynamicEndpoint(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canUpdateDynamicEndpoint = CanUpdateDynamicEndpoint()
|
||||
|
||||
case class CanDeleteDynamicEndpoint(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canDeleteDynamicEndpoint = CanDeleteDynamicEndpoint()
|
||||
|
||||
case class CanCreateResetPasswordUrl(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canCreateResetPasswordUrl = CanCreateResetPasswordUrl()
|
||||
|
||||
@ -68,6 +68,7 @@ object ApiTag {
|
||||
val apiTagMethodRouting = ResourceDocTag("Method-Routing")
|
||||
val apiTagWebUiProps = ResourceDocTag("WebUi-Props")
|
||||
val apiTagDynamicEntity= ResourceDocTag("Dynamic-Entity")
|
||||
val apiTagDynamicEndpoint= ResourceDocTag("Dynamic-Endpoint")
|
||||
|
||||
// To mark the Berlin Group APIs suggested order of implementation
|
||||
val apiTagBerlinGroupM = ResourceDocTag("Berlin-Group-M")
|
||||
|
||||
@ -41,7 +41,7 @@ object ErrorMessages {
|
||||
val WebUiPropsNotFound = "OBP-08002: WebUi props not found. Please specify a valid value for WEB_UI_PROPS_ID."
|
||||
|
||||
// DynamicEntity Exceptions (OBP-09XXX)
|
||||
val DynamicEntityNotFoundByDynamicEntityId = "OBP-09001: DynamicEntity not found. Please specify a valid value for dynamic_entity_id."
|
||||
val DynamicEntityNotFoundByDynamicEntityId = "OBP-09001: DynamicEntity not found. Please specify a valid value for DYNAMIC_ENTITY_ID."
|
||||
val DynamicEntityNameAlreadyExists = "OBP-09002: DynamicEntity's entityName already exists. Please specify a different value for entityName."
|
||||
val DynamicEntityNotExists = "OBP-09003: DynamicEntity not exists. Please check entityName."
|
||||
val DynamicEntityMissArgument = "OBP-09004: DynamicEntity process related argument is missing."
|
||||
@ -49,6 +49,9 @@ object ErrorMessages {
|
||||
val DynamicEntityOperationNotAllowed = "OBP-09006: Operation is not allowed, because Current DynamicEntity have upload data, must to delete all the data before this operation."
|
||||
val DynamicEntityInstanceValidateFail = "OBP-09007: DynamicEntity data validation failure."
|
||||
|
||||
val DynamicEndpointExists = "OBP-09008: DynamicEndpoint already exists."
|
||||
val DynamicEndpointNotFoundByDynamicEndpointId = "OBP-09009: DynamicEndpoint not found. Please specify a valid value for DYNAMIC_ENDPOINT_ID."
|
||||
|
||||
|
||||
// General messages (OBP-10XXX)
|
||||
val InvalidJsonFormat = "OBP-10001: Incorrect json format."
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
package code.api.util
|
||||
|
||||
|
||||
import net.liftweb.json.JsonDSL._
|
||||
import code.api.util.Glossary.{glossaryItems, makeGlossaryItem}
|
||||
import code.dynamicEntity.{DynamicEntityDefinition, DynamicEntityFooBar, DynamicEntityFullBarFields, DynamicEntityIntTypeExample, DynamicEntityStringTypeExample}
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityFieldType
|
||||
import net.liftweb.json
|
||||
import net.liftweb.json.JObject
|
||||
|
||||
case class ConnectorField(value: String, description: String) {
|
||||
|
||||
@ -369,6 +372,251 @@ object ExampleValue {
|
||||
)
|
||||
|
||||
lazy val dynamicEntityResponseBodyExample = dynamicEntityRequestBodyExample.copy(dynamicEntityId = Some("dynamic-entity-id"))
|
||||
|
||||
private val dynamicEndpointSwagger =
|
||||
"""{
|
||||
| "swagger": "2.0",
|
||||
| "info": {
|
||||
| "version": "0.0.1",
|
||||
| "title": "Example Title",
|
||||
| "description": "Example Description",
|
||||
| "contact": {
|
||||
| "name": "Example Company",
|
||||
| "email": " simon@example.com",
|
||||
| "url": "https://www.tesobe.com/"
|
||||
| }
|
||||
| },
|
||||
| "host": "localhost:8080",
|
||||
| "basePath": "/user",
|
||||
| "schemes": [
|
||||
| "http"
|
||||
| ],
|
||||
| "consumes": [
|
||||
| "application/json"
|
||||
| ],
|
||||
| "produces": [
|
||||
| "application/json"
|
||||
| ],
|
||||
| "paths": {
|
||||
| "/save": {
|
||||
| "post": {
|
||||
| "parameters": [
|
||||
| {
|
||||
| "name": "body",
|
||||
| "in": "body",
|
||||
| "required": true,
|
||||
| "schema": {
|
||||
| "$ref": "#/definitions/user"
|
||||
| }
|
||||
| }
|
||||
| ],
|
||||
| "responses": {
|
||||
| "201": {
|
||||
| "description": "create user successful and return created user object",
|
||||
| "schema": {
|
||||
| "$ref": "#/definitions/user"
|
||||
| }
|
||||
| },
|
||||
| "500": {
|
||||
| "description": "unexpected error",
|
||||
| "schema": {
|
||||
| "$ref": "#/responses/unexpectedError"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| },
|
||||
| "/getById/{userId}": {
|
||||
| "get": {
|
||||
| "description": "get reuested user by user ID",
|
||||
| "parameters": [
|
||||
| {
|
||||
| "$ref": "#/parameters/userId"
|
||||
| }
|
||||
| ],
|
||||
| "consumes": [],
|
||||
| "responses": {
|
||||
| "200": {
|
||||
| "description": "the successful get requested user by user ID",
|
||||
| "schema": {
|
||||
| "$ref": "#/definitions/user"
|
||||
| }
|
||||
| },
|
||||
| "400": {
|
||||
| "description": "bad request",
|
||||
| "schema": {
|
||||
| "$ref": "#/responses/invalidRequest"
|
||||
| }
|
||||
| },
|
||||
| "404": {
|
||||
| "description": "user not found",
|
||||
| "schema": {
|
||||
| "$ref": "#/definitions/APIError"
|
||||
| }
|
||||
| },
|
||||
| "500": {
|
||||
| "description": "unexpected error",
|
||||
| "schema": {
|
||||
| "$ref": "#/responses/unexpectedError"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| },
|
||||
| "/listUsers": {
|
||||
| "get": {
|
||||
| "description": "get list of users",
|
||||
| "consumes": [],
|
||||
| "responses": {
|
||||
| "200": {
|
||||
| "description": "get all users",
|
||||
| "schema": {
|
||||
| "$ref": "#/definitions/users"
|
||||
| }
|
||||
| },
|
||||
| "404": {
|
||||
| "description": "user not found",
|
||||
| "schema": {
|
||||
| "$ref": "#/definitions/APIError"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| },
|
||||
| "/updateUser": {
|
||||
| "put": {
|
||||
| "parameters": [
|
||||
| {
|
||||
| "name": "body",
|
||||
| "in": "body",
|
||||
| "required": true,
|
||||
| "schema": {
|
||||
| "$ref": "#/definitions/user"
|
||||
| }
|
||||
| }
|
||||
| ],
|
||||
| "responses": {
|
||||
| "200": {
|
||||
| "description": "create user successful and return created user object",
|
||||
| "schema": {
|
||||
| "$ref": "#/definitions/user"
|
||||
| }
|
||||
| },
|
||||
| "500": {
|
||||
| "description": "unexpected error",
|
||||
| "schema": {
|
||||
| "$ref": "#/responses/unexpectedError"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| },
|
||||
| "/delete/{userId}": {
|
||||
| "delete": {
|
||||
| "description": "delete user by user ID",
|
||||
| "parameters": [
|
||||
| {
|
||||
| "$ref": "#/parameters/userId"
|
||||
| }
|
||||
| ],
|
||||
| "consumes": [],
|
||||
| "responses": {
|
||||
| "204": {
|
||||
| "description": "the successful delete user by user ID"
|
||||
| },
|
||||
| "400": {
|
||||
| "description": "bad request",
|
||||
| "schema": {
|
||||
| "$ref": "#/responses/invalidRequest"
|
||||
| }
|
||||
| },
|
||||
| "500": {
|
||||
| "description": "unexpected error",
|
||||
| "schema": {
|
||||
| "$ref": "#/responses/unexpectedError"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| },
|
||||
| "definitions": {
|
||||
| "user": {
|
||||
| "type": "object",
|
||||
| "properties": {
|
||||
| "id": {
|
||||
| "type": "integer",
|
||||
| "description": "user ID"
|
||||
| },
|
||||
| "first_name": {
|
||||
| "type": "string"
|
||||
| },
|
||||
| "last_name": {
|
||||
| "type": "string"
|
||||
| },
|
||||
| "age": {
|
||||
| "type": "integer"
|
||||
| },
|
||||
| "career": {
|
||||
| "type": "string"
|
||||
| }
|
||||
| },
|
||||
| "required": [
|
||||
| "first_name",
|
||||
| "last_name",
|
||||
| "age"
|
||||
| ]
|
||||
| },
|
||||
| "users": {
|
||||
| "description": "array of users",
|
||||
| "type": "array",
|
||||
| "items": {
|
||||
| "$ref": "#/definitions/user"
|
||||
| }
|
||||
| },
|
||||
| "APIError": {
|
||||
| "description": "content any error from API",
|
||||
| "type": "object",
|
||||
| "properties": {
|
||||
| "errorCode": {
|
||||
| "description": "content error code relate to API",
|
||||
| "type": "string"
|
||||
| },
|
||||
| "errorMessage": {
|
||||
| "description": "content user-friendly error message",
|
||||
| "type": "string"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| },
|
||||
| "responses": {
|
||||
| "unexpectedError": {
|
||||
| "description": "unexpected error",
|
||||
| "schema": {
|
||||
| "$ref": "#/definitions/APIError"
|
||||
| }
|
||||
| },
|
||||
| "invalidRequest": {
|
||||
| "description": "invalid request",
|
||||
| "schema": {
|
||||
| "$ref": "#/definitions/APIError"
|
||||
| }
|
||||
| }
|
||||
| },
|
||||
| "parameters": {
|
||||
| "userId": {
|
||||
| "name": "userId",
|
||||
| "in": "path",
|
||||
| "required": true,
|
||||
| "type": "string",
|
||||
| "description": "user ID"
|
||||
| }
|
||||
| }
|
||||
|}
|
||||
|""".stripMargin
|
||||
lazy val dynamicEndpointRequestBodyExample = json.parse(dynamicEndpointSwagger).asInstanceOf[JObject]
|
||||
lazy val dynamicEndpointResponseBodyExample = ("dynamic_endpoint_id", "dynamic-endpoint-id") ~ ("swagger_string", dynamicEndpointRequestBodyExample)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -3,7 +3,9 @@ package code.api.util
|
||||
import java.util.Date
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
import akka.http.scaladsl.model.HttpMethod
|
||||
import code.DynamicData.DynamicDataProvider
|
||||
import code.DynamicEndpoint.{DynamicEndpointProvider, DynamicEndpointT}
|
||||
import code.api.APIFailureNewStyle
|
||||
import code.api.cache.Caching
|
||||
import code.api.util.APIUtil.{OBPReturnType, canGrantAccessToViewCommon, canRevokeAccessToViewCommon, connectorEmptyResponse, createHttpParamsByUrlFuture, createQueriesByHttpParamsFuture, fullBoxOrException, generateUUID, unboxFull, unboxFullOrFail}
|
||||
@ -13,8 +15,9 @@ import code.api.v1_4_0.OBPAPI1_4_0.Implementations1_4_0
|
||||
import code.api.v2_0_0.OBPAPI2_0_0.Implementations2_0_0
|
||||
import code.api.v2_1_0.OBPAPI2_1_0.Implementations2_1_0
|
||||
import code.api.v2_2_0.OBPAPI2_2_0.Implementations2_2_0
|
||||
import code.api.v4_0_0.DynamicEntityInfo
|
||||
import code.api.v4_0_0.{DynamicEndpointHelper, DynamicEntityInfo}
|
||||
import code.bankconnectors.Connector
|
||||
import code.bankconnectors.rest.RestConnector_vMar2019
|
||||
import code.branches.Branches.{Branch, DriveUpString, LobbyString}
|
||||
import code.consumer.Consumers
|
||||
import code.directdebit.DirectDebitTrait
|
||||
@ -1919,6 +1922,10 @@ object NewStyle {
|
||||
}
|
||||
}
|
||||
}
|
||||
def dynamicEndpointProcess(url: String, jValue: JValue, method: HttpMethod, params: Map[String, List[String]], pathParams: Map[String, String],
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[JValue]] = {
|
||||
Connector.connector.vend.dynamicEndpointProcess(url, jValue, method, params, pathParams, callContext)
|
||||
}
|
||||
|
||||
|
||||
def createDirectDebit(bankId : String,
|
||||
@ -1996,6 +2003,54 @@ object NewStyle {
|
||||
getConnectorByName(connectorName).flatMap(_.implementedMethods.get(methodName))
|
||||
}
|
||||
|
||||
def createDynamicEndpoint(swaggerString: String, callContext: Option[CallContext]): OBPReturnType[DynamicEndpointT] = {
|
||||
Connector.connector.vend.createDynamicEndpoint(
|
||||
swaggerString,
|
||||
callContext
|
||||
) map {
|
||||
i => (connectorEmptyResponse(i._1, callContext), i._2)
|
||||
}
|
||||
}
|
||||
|
||||
def getDynamicEndpoint(dynamicEndpointId: String, callContext: Option[CallContext]): OBPReturnType[DynamicEndpointT] = {
|
||||
val dynamicEndpointBox: Box[DynamicEndpointT] = DynamicEndpointProvider.connectorMethodProvider.vend.get(dynamicEndpointId)
|
||||
val dynamicEndpoint = unboxFullOrFail(dynamicEndpointBox, callContext, DynamicEndpointNotFoundByDynamicEndpointId, 404)
|
||||
Future{
|
||||
(dynamicEndpoint, callContext)
|
||||
}
|
||||
}
|
||||
|
||||
def getDynamicEndpoints(callContext: Option[CallContext]): OBPReturnType[List[DynamicEndpointT]] = {
|
||||
Connector.connector.vend.getDynamicEndpoints(
|
||||
callContext
|
||||
)
|
||||
}
|
||||
/**
|
||||
* delete one DynamicEndpoint and corresponding entitlement and dynamic entitlement
|
||||
* @param dynamicEndpointId
|
||||
* @param callContext
|
||||
* @return
|
||||
*/
|
||||
def deleteDynamicEndpoint(dynamicEndpointId: String, callContext: Option[CallContext]): Future[Box[Boolean]] = {
|
||||
val dynamicEndpoint: OBPReturnType[DynamicEndpointT] = this.getDynamicEndpoint(dynamicEndpointId, callContext)
|
||||
for {
|
||||
(entity, _) <- dynamicEndpoint
|
||||
deleteSuccess = DynamicEndpointProvider.connectorMethodProvider.vend.delete(dynamicEndpointId)
|
||||
|
||||
deleteEndpointResult: Box[Boolean] = if(deleteSuccess) {
|
||||
val roles = DynamicEndpointHelper.getRoles(dynamicEndpointId).map(_.toString())
|
||||
DynamicEndpointHelper.removeEndpoint(dynamicEndpointId)
|
||||
val rolesDeleteResult: Box[Boolean] = Entitlement.entitlement.vend.deleteEntitlements(roles)
|
||||
|
||||
Box !! (rolesDeleteResult == Full(true))
|
||||
} else {
|
||||
Box !! false
|
||||
}
|
||||
} yield {
|
||||
deleteEndpointResult
|
||||
}
|
||||
}
|
||||
|
||||
def deleteCustomerAttribute(customerAttributeId : String, callContext: Option[CallContext]): OBPReturnType[Boolean] = {
|
||||
Connector.connector.vend.deleteCustomerAttribute(customerAttributeId, callContext) map {
|
||||
i => (connectorEmptyResponse(i._1, callContext), i._2)
|
||||
|
||||
@ -2227,6 +2227,7 @@ trait APIMethods310 {
|
||||
emptyObjectJson,
|
||||
List(
|
||||
UserHasMissingRoles,
|
||||
BankNotFound,
|
||||
UnknownError
|
||||
),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
|
||||
@ -664,8 +664,7 @@ case class CreateAccountRequestJsonV310(
|
||||
product_code : String,
|
||||
balance : AmountOfMoneyJsonV121,
|
||||
branch_id : String,
|
||||
account_routing: AccountRoutingJsonV121,
|
||||
account_attributes: List[AccountAttributeResponseJson]
|
||||
account_routing: AccountRoutingJsonV121
|
||||
)
|
||||
|
||||
case class CreateAccountResponseJsonV310(
|
||||
|
||||
@ -3,6 +3,7 @@ package code.api.v4_0_0
|
||||
import java.util.Date
|
||||
|
||||
import code.DynamicData.DynamicData
|
||||
import code.DynamicEndpoint.{DynamicEndpointCommons, DynamicEndpointSwagger}
|
||||
import code.accountattribute.AccountAttributeX
|
||||
import code.api.ChargePolicy
|
||||
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
|
||||
@ -10,7 +11,7 @@ import code.api.util.APIUtil.{fullBoxOrException, _}
|
||||
import code.api.util.ApiRole._
|
||||
import code.api.util.ApiTag._
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.ExampleValue.{dynamicEntityRequestBodyExample, dynamicEntityResponseBodyExample}
|
||||
import code.api.util.ExampleValue.{dynamicEndpointRequestBodyExample, dynamicEndpointResponseBodyExample, dynamicEntityRequestBodyExample, dynamicEntityResponseBodyExample}
|
||||
import code.api.util.NewStyle.HttpCode
|
||||
import code.api.util.newstyle.AttributeDefinition._
|
||||
import code.api.util.newstyle.Consumer._
|
||||
@ -23,6 +24,7 @@ import code.api.v2_1_0._
|
||||
import code.api.v2_2_0.{BankJSONV220, JSONFactory220}
|
||||
import code.api.v3_0_0.JSONFactory300
|
||||
import code.api.v3_1_0.{CreateAccountRequestJsonV310, CustomerWithAttributesJsonV310, JSONFactory310, ListResult}
|
||||
import code.api.v4_0_0.DynamicEndpointHelper.DynamicReq
|
||||
import code.api.v4_0_0.JSONFactory400.{createBankAccountJSON, createNewCoreBankAccountJson}
|
||||
import code.bankconnectors.Connector
|
||||
import code.dynamicEntity.{DynamicEntityCommons, ReferenceType}
|
||||
@ -50,6 +52,7 @@ import net.liftweb.http.rest.RestHelper
|
||||
import net.liftweb.json.JsonAST.JValue
|
||||
import net.liftweb.json.Serialization.write
|
||||
import net.liftweb.json._
|
||||
import net.liftweb.json.JsonDSL._
|
||||
import net.liftweb.mapper.By
|
||||
import net.liftweb.util.Helpers.now
|
||||
import net.liftweb.util.{Helpers, StringHelpers}
|
||||
@ -60,6 +63,7 @@ import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.Future
|
||||
import code.model._
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import net.liftweb.json.compactRender
|
||||
|
||||
trait APIMethods400 {
|
||||
self: RestHelper =>
|
||||
@ -131,11 +135,11 @@ trait APIMethods400 {
|
||||
| INITIATED => COMPLETED
|
||||
|In case n persons needs to answer security challenge we have next flow of state of an `transaction request`:
|
||||
| INITIATED => NEXT_CHALLENGE_PENDING => ... => NEXT_CHALLENGE_PENDING => COMPLETED
|
||||
|
|
||||
|
|
||||
|The security challenge is bound to a user i.e. in case of right answer and the user is different than expected one the challenge will fail.
|
||||
|
|
||||
|Rule for calculating number of security challenges:
|
||||
|If product Account attribute REQUIRED_CHALLENGE_ANSWERS=N then create N challenges
|
||||
|If product Account attribute REQUIRED_CHALLENGE_ANSWERS=N then create N challenges
|
||||
|(one for every user that has a View where permission "can_add_transaction_request_to_any_account"=true)
|
||||
|In case REQUIRED_CHALLENGE_ANSWERS is not defined as an account attribute default value is 1.
|
||||
|
|
||||
@ -363,7 +367,7 @@ trait APIMethods400 {
|
||||
),
|
||||
Catalogs(Core, PSD2, OBWG),
|
||||
List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagNewStyle))
|
||||
|
||||
|
||||
// FREE_FORM.
|
||||
resourceDocs += ResourceDoc(
|
||||
createTransactionRequestFreeForm,
|
||||
@ -424,7 +428,7 @@ trait APIMethods400 {
|
||||
|
||||
account = BankIdAccountId(bankId, accountId)
|
||||
_ <- NewStyle.function.checkAuthorisationToCreateTransactionRequest(viewId, account, u, cc.callContext)
|
||||
|
||||
|
||||
_ <- Helper.booleanToFuture(InsufficientAuthorisationToCreateTransactionRequest) {
|
||||
u.hasOwnerViewAccess(BankIdAccountId(bankId, accountId)) ||
|
||||
hasEntitlement(bankId.value, u.userId, ApiRole.canCreateAnyTransactionRequest)
|
||||
@ -467,7 +471,7 @@ trait APIMethods400 {
|
||||
transactionRequestBodyRefundJson <- NewStyle.function.tryons(s"${InvalidJsonFormat}, it should be $ACCOUNT json format", 400, cc.callContext) {
|
||||
json.extract[TransactionRequestBodyRefundJsonV400]
|
||||
}
|
||||
|
||||
|
||||
transactionId = TransactionId(transactionRequestBodyRefundJson.refund.transaction_id)
|
||||
toBankId = BankId(transactionRequestBodyRefundJson.to.bank_id)
|
||||
toAccountId = AccountId(transactionRequestBodyRefundJson.to.account_id)
|
||||
@ -476,28 +480,28 @@ trait APIMethods400 {
|
||||
transDetailsSerialized <- NewStyle.function.tryons(UnknownError, 400, callContext) {
|
||||
write(transactionRequestBodyRefundJson)(Serialization.formats(NoTypeHints))
|
||||
}
|
||||
|
||||
|
||||
_ <- Helper.booleanToFuture(s"${RefundedTransaction} Current input amount is: '${transDetailsJson.value.amount}'. It can not be more than the original amount(${(transaction.amount).abs})") {
|
||||
(transaction.amount).abs >= transactionAmountNumber
|
||||
}
|
||||
//TODO, we need additional field to guarantee the transaction is refunded...
|
||||
//TODO, we need additional field to guarantee the transaction is refunded...
|
||||
// _ <- Helper.booleanToFuture(s"${RefundedTransaction}") {
|
||||
// !((transaction.description.toString contains(" Refund to ")) && (transaction.description.toString contains(" and transaction_id(")))
|
||||
// }
|
||||
|
||||
|
||||
//we add the extro info (counterparty name + transaction_id) for this special Refund endpoint.
|
||||
newDescription = s"${transactionRequestBodyRefundJson.description} - Refund for transaction_id: (${transactionId.value}) to ${transaction.otherAccount.counterpartyName}"
|
||||
|
||||
//This is the refund endpoint, the original fromAccount is the `toAccount` which will receive money.
|
||||
|
||||
//This is the refund endpoint, the original fromAccount is the `toAccount` which will receive money.
|
||||
refundToAccount = fromAccount
|
||||
//This is the refund endpoint, the original toAccount is the `fromAccount` which will lose money.
|
||||
//This is the refund endpoint, the original toAccount is the `fromAccount` which will lose money.
|
||||
refundFromAccount = toAccount
|
||||
|
||||
|
||||
(createdTransactionRequest, callContext) <- NewStyle.function.createTransactionRequestv400(u,
|
||||
viewId,
|
||||
refundFromAccount,
|
||||
refundToAccount,
|
||||
transactionRequestType,
|
||||
transactionRequestType,
|
||||
transactionRequestBodyRefundJson.copy(description = newDescription),
|
||||
transDetailsSerialized,
|
||||
sharedChargePolicy.toString,
|
||||
@ -782,7 +786,7 @@ trait APIMethods400 {
|
||||
.findAll(By(MappedExpectedChallengeAnswer.mTransactionRequestId, transReqId.value))
|
||||
.count(_.successful == true) match {
|
||||
case number if number >= quorum => true
|
||||
case _ =>
|
||||
case _ =>
|
||||
MappedTransactionRequestProvider.saveTransactionRequestStatusImpl(transReqId, TransactionRequestStatus.NEXT_CHALLENGE_PENDING.toString)
|
||||
false
|
||||
}
|
||||
@ -813,9 +817,9 @@ trait APIMethods400 {
|
||||
implementedInApiVersion,
|
||||
nameOf(getDynamicEntities),
|
||||
"GET",
|
||||
"/management/dynamic_entities",
|
||||
"Get DynamicEntities",
|
||||
s"""Get the all DynamicEntities.""",
|
||||
"/management/dynamic-entities",
|
||||
"Get Dynamic Entities",
|
||||
s"""Get the all Dynamic Entities.""",
|
||||
emptyObjectJson,
|
||||
ListResult(
|
||||
"dynamic_entities",
|
||||
@ -833,7 +837,7 @@ trait APIMethods400 {
|
||||
|
||||
|
||||
lazy val getDynamicEntities: OBPEndpoint = {
|
||||
case "management" :: "dynamic_entities" :: Nil JsonGet req => {
|
||||
case "management" :: "dynamic-entities" :: Nil JsonGet req => {
|
||||
cc =>
|
||||
for {
|
||||
dynamicEntities <- Future(NewStyle.function.getDynamicEntities())
|
||||
@ -850,8 +854,8 @@ trait APIMethods400 {
|
||||
implementedInApiVersion,
|
||||
nameOf(createDynamicEntity),
|
||||
"POST",
|
||||
"/management/dynamic_entities",
|
||||
"Create DynamicEntity",
|
||||
"/management/dynamic-entities",
|
||||
"Create Dynamic Entity",
|
||||
s"""Create a DynamicEntity.
|
||||
|
|
||||
|
|
||||
@ -883,7 +887,7 @@ trait APIMethods400 {
|
||||
Some(List(canCreateDynamicEntity)))
|
||||
|
||||
lazy val createDynamicEntity: OBPEndpoint = {
|
||||
case "management" :: "dynamic_entities" :: Nil JsonPost json -> _ => {
|
||||
case "management" :: "dynamic-entities" :: Nil JsonPost json -> _ => {
|
||||
cc =>
|
||||
val dynamicEntity = DynamicEntityCommons(json.asInstanceOf[JObject], None)
|
||||
for {
|
||||
@ -901,8 +905,8 @@ trait APIMethods400 {
|
||||
implementedInApiVersion,
|
||||
nameOf(updateDynamicEntity),
|
||||
"PUT",
|
||||
"/management/dynamic_entities/DYNAMIC_ENTITY_ID",
|
||||
"Update DynamicEntity",
|
||||
"/management/dynamic-entities/DYNAMIC_ENTITY_ID",
|
||||
"Update Dynamic Entity",
|
||||
s"""Update a DynamicEntity.
|
||||
|
|
||||
|
|
||||
@ -934,7 +938,7 @@ trait APIMethods400 {
|
||||
Some(List(canUpdateDynamicEntity)))
|
||||
|
||||
lazy val updateDynamicEntity: OBPEndpoint = {
|
||||
case "management" :: "dynamic_entities" :: dynamicEntityId :: Nil JsonPut json -> _ => {
|
||||
case "management" :: "dynamic-entities" :: dynamicEntityId :: Nil JsonPut json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
// Check whether there are uploaded data, only if no uploaded data allow to update DynamicEntity.
|
||||
@ -959,8 +963,8 @@ trait APIMethods400 {
|
||||
implementedInApiVersion,
|
||||
nameOf(deleteDynamicEntity),
|
||||
"DELETE",
|
||||
"/management/dynamic_entities/DYNAMIC_ENTITY_ID",
|
||||
"Delete DynamicEntity",
|
||||
"/management/dynamic-entities/DYNAMIC_ENTITY_ID",
|
||||
"Delete Dynamic Entity",
|
||||
s"""Delete a DynamicEntity specified by DYNAMIC_ENTITY_ID.
|
||||
|
|
||||
|""",
|
||||
@ -976,7 +980,7 @@ trait APIMethods400 {
|
||||
Some(List(canDeleteDynamicEntity)))
|
||||
|
||||
lazy val deleteDynamicEntity: OBPEndpoint = {
|
||||
case "management" :: "dynamic_entities" :: dynamicEntityId :: Nil JsonDelete _ => {
|
||||
case "management" :: "dynamic-entities" :: dynamicEntityId :: Nil JsonDelete _ => {
|
||||
cc =>
|
||||
for {
|
||||
// Check whether there are uploaded data, only if no uploaded data allow to delete DynamicEntity.
|
||||
@ -1114,7 +1118,7 @@ trait APIMethods400 {
|
||||
json.extract[PostResetPasswordUrlJsonV400]
|
||||
}
|
||||
} yield {
|
||||
val resetLink = AuthUser.passwordResetUrl(postedData.username, postedData.email, postedData.user_id)
|
||||
val resetLink = AuthUser.passwordResetUrl(postedData.username, postedData.email, postedData.user_id)
|
||||
(ResetPasswordUrlJsonV400(resetLink), HttpCode.`201`(cc.callContext))
|
||||
}
|
||||
}
|
||||
@ -2181,7 +2185,7 @@ trait APIMethods400 {
|
||||
CustomerAttributeType.withName(postedData.`type`)
|
||||
}
|
||||
(customer, callContext) <- NewStyle.function.getCustomerByCustomerId(customerId, cc.callContext)
|
||||
_ <- Helper.booleanToFuture(InvalidCustomerBankId.replaceAll("Bank Id.",s"Bank Id ($bankId).").replaceAll("The Customer",s"The Customer($customerId)")){customer.bankId == bankId}
|
||||
_ <- Helper.booleanToFuture(InvalidCustomerBankId.replaceAll("Bank Id.",s"Bank Id ($bankId).").replaceAll("The Customer",s"The Customer($customerId)")){customer.bankId == bankId}
|
||||
(accountAttribute, callContext) <- NewStyle.function.getCustomerAttributeById(
|
||||
customerAttributeId,
|
||||
callContext
|
||||
@ -2768,7 +2772,188 @@ trait APIMethods400 {
|
||||
}
|
||||
}
|
||||
|
||||
resourceDocs += ResourceDoc(
|
||||
createDynamicEndpoint,
|
||||
implementedInApiVersion,
|
||||
nameOf(createDynamicEndpoint),
|
||||
"POST",
|
||||
"/management/dynamic-endpoints",
|
||||
" Create DynamicEndpoint",
|
||||
s"""Create a DynamicEndpoint.
|
||||
|
|
||||
|Create one DynamicEndpoint,
|
||||
|
|
||||
|""",
|
||||
dynamicEndpointRequestBodyExample,
|
||||
dynamicEndpointResponseBodyExample,
|
||||
List(
|
||||
$UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
DynamicEndpointExists,
|
||||
InvalidJsonFormat,
|
||||
UnknownError
|
||||
),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagDynamicEndpoint, apiTagApi, apiTagNewStyle),
|
||||
Some(List(canCreateDynamicEndpoint)))
|
||||
|
||||
lazy val createDynamicEndpoint: OBPEndpoint = {
|
||||
case "management" :: "dynamic-endpoints" :: Nil JsonPost json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
(postedJson, openAPI) <- NewStyle.function.tryons(InvalidJsonFormat, 400, cc.callContext) {
|
||||
val swaggerContent = compactRender(json)
|
||||
|
||||
(DynamicEndpointSwagger(swaggerContent), DynamicEndpointHelper.parseSwaggerContent(swaggerContent))
|
||||
}
|
||||
duplicatedUrl = DynamicEndpointHelper.findExistsEndpoints(openAPI).map(kv => s"${kv._1}:${kv._2}")
|
||||
errorMsg = s"""$DynamicEndpointExists Duplicated ${if(duplicatedUrl.size > 1) "endpoints" else "endpoint"}: ${duplicatedUrl.mkString("; ")}"""
|
||||
_ <- Helper.booleanToFuture(errorMsg) {
|
||||
duplicatedUrl.isEmpty
|
||||
}
|
||||
(dynamicEndpoint, callContext) <- NewStyle.function.createDynamicEndpoint(postedJson.swaggerString, cc.callContext)
|
||||
_ = DynamicEndpointHelper.addEndpoint(openAPI, dynamicEndpoint.dynamicEndpointId.get)
|
||||
} yield {
|
||||
val swaggerJson = parse(dynamicEndpoint.swaggerString)
|
||||
val responseJson: JObject = ("dynamic_endpoint_id", dynamicEndpoint.dynamicEndpointId) ~ ("swagger_string", swaggerJson)
|
||||
(responseJson, HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resourceDocs += ResourceDoc(
|
||||
getDynamicEndpoint,
|
||||
implementedInApiVersion,
|
||||
nameOf(getDynamicEndpoint),
|
||||
"GET",
|
||||
"/management/dynamic-endpoints/DYNAMIC_ENDPOINT_ID",
|
||||
" Get DynamicEndpoint",
|
||||
s"""Get a DynamicEndpoint.
|
||||
|
|
||||
|
|
||||
|Get one DynamicEndpoint,
|
||||
|
|
||||
|""",
|
||||
emptyObjectJson,
|
||||
dynamicEndpointResponseBodyExample,
|
||||
List(
|
||||
$UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
DynamicEndpointNotFoundByDynamicEndpointId,
|
||||
InvalidJsonFormat,
|
||||
UnknownError
|
||||
),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagDynamicEndpoint, apiTagApi, apiTagNewStyle),
|
||||
Some(List(canGetDynamicEndpoint)))
|
||||
|
||||
lazy val getDynamicEndpoint: OBPEndpoint = {
|
||||
case "management" :: "dynamic-endpoints" :: dynamicEndpointId :: Nil JsonGet req => {
|
||||
cc =>
|
||||
for {
|
||||
(dynamicEndpoint, callContext) <- NewStyle.function.getDynamicEndpoint(dynamicEndpointId, cc.callContext)
|
||||
} yield {
|
||||
val swaggerJson = parse(dynamicEndpoint.swaggerString)
|
||||
val responseJson: JObject = ("dynamic_endpoint_id", dynamicEndpoint.dynamicEndpointId) ~ ("swagger_string", swaggerJson)
|
||||
(responseJson, HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resourceDocs += ResourceDoc(
|
||||
getDynamicEndpoints,
|
||||
implementedInApiVersion,
|
||||
nameOf(getDynamicEndpoints),
|
||||
"GET",
|
||||
"/management/dynamic-endpoints",
|
||||
" Get DynamicEndpoints",
|
||||
s"""
|
||||
|
|
||||
|Get DynamicEndpoints.
|
||||
|
|
||||
|""",
|
||||
emptyObjectJson,
|
||||
ListResult(
|
||||
"dynamic_endpoints",
|
||||
List(dynamicEndpointResponseBodyExample)
|
||||
),
|
||||
List(
|
||||
$UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
InvalidJsonFormat,
|
||||
UnknownError
|
||||
),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagDynamicEndpoint, apiTagApi, apiTagNewStyle),
|
||||
Some(List(canGetDynamicEndpoints)))
|
||||
|
||||
lazy val getDynamicEndpoints: OBPEndpoint = {
|
||||
case "management" :: "dynamic-endpoints" :: Nil JsonGet _ => {
|
||||
cc =>
|
||||
for {
|
||||
(dynamicEndpoints, _) <- NewStyle.function.getDynamicEndpoints(cc.callContext)
|
||||
} yield {
|
||||
val resultList = dynamicEndpoints.map[JObject, List[JObject]] { dynamicEndpoint=>
|
||||
val swaggerJson = parse(dynamicEndpoint.swaggerString)
|
||||
("dynamic_endpoint_id", dynamicEndpoint.dynamicEndpointId) ~ ("swagger_string", swaggerJson)
|
||||
}
|
||||
(ListResult("dynamic_endpoints", resultList), HttpCode.`200`(cc.callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resourceDocs += ResourceDoc(
|
||||
deleteDynamicEndpoint,
|
||||
implementedInApiVersion,
|
||||
nameOf(deleteDynamicEndpoint),
|
||||
"DELETE",
|
||||
"/management/dynamic-endpoints/DYNAMIC_ENDPOINT_ID",
|
||||
" Delete Dynamic Endpoint",
|
||||
s"""Delete a DynamicEndpoint specified by DYNAMIC_ENDPOINT_ID.
|
||||
|
|
||||
|""",
|
||||
emptyObjectJson,
|
||||
emptyObjectJson,
|
||||
List(
|
||||
$UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
DynamicEndpointNotFoundByDynamicEndpointId,
|
||||
UnknownError
|
||||
),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagDynamicEndpoint, apiTagApi, apiTagNewStyle),
|
||||
Some(List(canDeleteDynamicEndpoint)))
|
||||
|
||||
lazy val deleteDynamicEndpoint : OBPEndpoint = {
|
||||
case "management" :: "dynamic-endpoints" :: dynamicEndpointId :: Nil JsonDelete _ => {
|
||||
cc =>
|
||||
for {
|
||||
deleted <- NewStyle.function.deleteDynamicEndpoint(dynamicEndpointId, cc.callContext)
|
||||
} yield {
|
||||
(deleted, HttpCode.`204`(cc.callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lazy val dynamicEndpoint: OBPEndpoint = {
|
||||
case DynamicReq(url, json, method, params, pathParams, role) => { cc =>
|
||||
for {
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, role, callContext)
|
||||
|
||||
(box, _) <- NewStyle.function.dynamicEndpointProcess(url, json, method, params, pathParams, callContext)
|
||||
} yield {
|
||||
box match {
|
||||
case Full(v) => (v, HttpCode.`200`(Some(cc)))
|
||||
case e: Failure => (e.messageChain, HttpCode.`200`(Some(cc))) // TODO code need change
|
||||
case _ => ("fail", HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resourceDocs += ResourceDoc(
|
||||
createOrUpdateCustomerAttributeAttributeDefinition,
|
||||
|
||||
@ -0,0 +1,454 @@
|
||||
package code.api.v4_0_0
|
||||
|
||||
import java.io.File
|
||||
import java.nio.charset.Charset
|
||||
import java.util
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.regex.Pattern
|
||||
import java.util.{Date, Optional}
|
||||
|
||||
import akka.http.scaladsl.model.HttpMethods
|
||||
import code.DynamicEndpoint.{DynamicEndpointProvider, DynamicEndpointT}
|
||||
import code.api.util.APIUtil.{Catalogs, OBPEndpoint, ResourceDoc, authenticationRequiredMessage, emptyObjectJson, generateUUID, notCore, notOBWG, notPSD2}
|
||||
import code.api.util.ApiTag.{ResourceDocTag, apiTagApi, apiTagNewStyle}
|
||||
import code.api.util.ErrorMessages.{InvalidJsonFormat, UnknownError, UserHasMissingRoles, UserNotLoggedIn}
|
||||
import code.api.util.{APIUtil, ApiRole, ApiTag, CustomJsonFormats, NewStyle}
|
||||
import code.api.util.ApiRole.getOrCreateDynamicApiRole
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityFieldType
|
||||
import com.openbankproject.commons.util.{ApiVersion, Functions}
|
||||
import io.swagger.v3.oas.models.{OpenAPI, Operation, PathItem}
|
||||
import io.swagger.v3.oas.models.PathItem.HttpMethod
|
||||
import akka.http.scaladsl.model.{HttpMethod => AkkaHttpMethod}
|
||||
import io.swagger.v3.oas.models.media.{ArraySchema, BooleanSchema, Content, DateSchema, DateTimeSchema, IntegerSchema, NumberSchema, ObjectSchema, Schema, StringSchema}
|
||||
import io.swagger.v3.oas.models.parameters.RequestBody
|
||||
import io.swagger.v3.oas.models.responses.ApiResponses
|
||||
import io.swagger.v3.parser.OpenAPIV3Parser
|
||||
import net.liftweb.http.Req
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
import net.liftweb.json.JsonAST.{JArray, JField, JNothing, JObject}
|
||||
import net.liftweb.json.JsonDSL._
|
||||
import net.liftweb.json
|
||||
import net.liftweb.json.JValue
|
||||
import net.liftweb.util.StringHelpers
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
import scala.collection.immutable.{List, Nil}
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.compat.java8.OptionConverters._
|
||||
|
||||
|
||||
object DynamicEndpointHelper extends RestHelper {
|
||||
|
||||
/**
|
||||
* dynamic endpoints url prefix
|
||||
*/
|
||||
val urlPrefix = APIUtil.getPropsValue("dynamic_endpoints_url_prefix", "dynamic")
|
||||
|
||||
private lazy val dynamicEndpointInfos: CopyOnWriteArrayList[DynamicEndpointInfo] = {
|
||||
val dynamicEndpoints: List[DynamicEndpointT] = DynamicEndpointProvider.connectorMethodProvider.vend.getAll()
|
||||
val infos = dynamicEndpoints.map(it => swaggerToResourceDocs(it.swaggerString, it.dynamicEndpointId.get))
|
||||
new CopyOnWriteArrayList(infos.asJava)
|
||||
}
|
||||
|
||||
def getRoles(dynamicEndpointId: String): List[ApiRole] = {
|
||||
val foundInfos: Option[DynamicEndpointInfo] = dynamicEndpointInfos.asScala
|
||||
.find(_.id == dynamicEndpointId)
|
||||
|
||||
val roles = foundInfos.toList
|
||||
.flatMap(_.resourceDocs)
|
||||
.map(_.roles)
|
||||
.collect {
|
||||
case Some(role :: _) => role
|
||||
}
|
||||
|
||||
roles
|
||||
}
|
||||
/**
|
||||
* extract request body, no matter GET, POST, PUT or DELETE method
|
||||
*/
|
||||
object DynamicReq extends JsonTest with JsonBody {
|
||||
|
||||
private val ExpressionRegx = """\{(.+?)\}""".r
|
||||
/**
|
||||
* unapply Request to (request url, json, http method, request parameters, path parameters, role)
|
||||
* request url is current request url
|
||||
* json is request body
|
||||
* http method is request http method
|
||||
* request parameters is http request parameters
|
||||
* path parameters: /banks/{bankId}/users/{userId} bankId and userId corresponding key to value
|
||||
* role is current endpoint required entitlement
|
||||
* @param r HttpRequest
|
||||
* @return
|
||||
*/
|
||||
def unapply(r: Req): Option[(String, JValue, AkkaHttpMethod, Map[String, List[String]], Map[String, String], ApiRole)] = {
|
||||
val partPath = r.path.partPath
|
||||
if (!testResponse_?(r) || partPath.headOption != Option(urlPrefix))
|
||||
None
|
||||
else {
|
||||
val akkaHttpMethod = HttpMethods.getForKeyCaseInsensitive(r.requestType.method).get
|
||||
val httpMethod = HttpMethod.valueOf(r.requestType.method)
|
||||
// url that match original swagger endpoint.
|
||||
val url = partPath.tail.mkString("/", "/", "")
|
||||
val foundDynamicEndpoint: Optional[(DynamicEndpointInfo, ResourceDoc, String)] = dynamicEndpointInfos.stream()
|
||||
.map[Option[(DynamicEndpointInfo, ResourceDoc, String)]](_.findDynamicEndpoint(httpMethod, url))
|
||||
.filter(_.isDefined)
|
||||
.findFirst()
|
||||
.map(_.get)
|
||||
|
||||
foundDynamicEndpoint.asScala
|
||||
.flatMap[(String, JValue, AkkaHttpMethod, Map[String, List[String]], Map[String, String], ApiRole)] { it =>
|
||||
val (dynamicEndpointInfo, doc, originalUrl) = it
|
||||
|
||||
val pathParams: Map[String, String] = if(originalUrl == url) {
|
||||
Map.empty[String, String]
|
||||
} else {
|
||||
val tuples: Array[(String, String)] = StringUtils.split(originalUrl, "/").zip(partPath.tail)
|
||||
tuples.collect {
|
||||
case (ExpressionRegx(name), value) => name->value
|
||||
}.toMap
|
||||
}
|
||||
|
||||
val Some(role::_) = doc.roles
|
||||
body(r).toOption
|
||||
.orElse(Some(JNothing))
|
||||
.map(t => (dynamicEndpointInfo.targetUrl(url), t, akkaHttpMethod, r.params, pathParams, role))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def addEndpoint(openAPI: OpenAPI, id: String): Boolean = {
|
||||
val endpointInfo = swaggerToResourceDocs(openAPI, id)
|
||||
dynamicEndpointInfos.add(endpointInfo)
|
||||
}
|
||||
|
||||
def removeEndpoint(id: String): Boolean = {
|
||||
dynamicEndpointInfos.asScala.find(_.id == id) match {
|
||||
case Some(v) => dynamicEndpointInfos.remove(v)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
def findExistsEndpoints(openAPI: OpenAPI): List[(HttpMethod, String)] = {
|
||||
for {
|
||||
(path, pathItem) <- openAPI.getPaths.asScala.toList
|
||||
(method: HttpMethod, _) <- pathItem.readOperationsMap.asScala
|
||||
if dynamicEndpointInfos.stream().anyMatch(_.existsEndpoint(method, path))
|
||||
} yield (method, path)
|
||||
|
||||
}
|
||||
|
||||
private def swaggerToResourceDocs(content: String, id: String): DynamicEndpointInfo = {
|
||||
val openAPI: OpenAPI = parseSwaggerContent(content)
|
||||
swaggerToResourceDocs(openAPI, id)
|
||||
}
|
||||
|
||||
private def swaggerToResourceDocs(openAPI: OpenAPI, id: String): DynamicEndpointInfo = {
|
||||
val tags: List[ResourceDocTag] = List(ApiTag.apiTagDynamicEndpoint, apiTagApi, apiTagNewStyle)
|
||||
|
||||
val serverUrl = {
|
||||
val servers = openAPI.getServers
|
||||
assert(!servers.isEmpty, s"swagger host is mandatory, but current swagger host is empty, id=$id")
|
||||
servers.get(0).getUrl
|
||||
}
|
||||
|
||||
val paths: mutable.Map[String, PathItem] = openAPI.getPaths.asScala
|
||||
def entitlementSuffix(path: String) = Math.abs(path.hashCode).toString.substring(0, 3) // to avoid different swagger have same entitlement
|
||||
val docs: mutable.Iterable[(ResourceDoc, String)] = for {
|
||||
(path, pathItem) <- paths
|
||||
(method: HttpMethod, op: Operation) <- pathItem.readOperationsMap.asScala
|
||||
} yield {
|
||||
val implementedInApiVersion = ApiVersion.v4_0_0
|
||||
val partialFunction: OBPEndpoint = APIMethods400.Implementations4_0_0.genericEndpoint // this function is just placeholder, not need a real value.
|
||||
val partialFunctionName: String = s"$method-$path".replaceAll("\\W", "_")
|
||||
val requestVerb: String = method.name()
|
||||
val requestUrl: String = buildRequestUrl(path)
|
||||
val summary: String = Option(pathItem.getSummary)
|
||||
.filter(StringUtils.isNotBlank)
|
||||
.getOrElse(buildSummary(method, op, path))
|
||||
val description: String = Option(pathItem.getDescription)
|
||||
.filter(StringUtils.isNotBlank)
|
||||
.orElse(Option(op.getDescription))
|
||||
.filter(StringUtils.isNotBlank)
|
||||
.map(_.capitalize)
|
||||
.getOrElse(summary) +
|
||||
s"""
|
||||
|
|
||||
|MethodRouting settings example:
|
||||
|```
|
||||
|{
|
||||
| "is_bank_id_exact_match":false,
|
||||
| "method_name":"dynamicEndpointProcess",
|
||||
| "connector_name":"rest_vMar2019",
|
||||
| "bank_id_pattern":".*",
|
||||
| "parameters":[
|
||||
| {
|
||||
| "key":"url_pattern",
|
||||
| "value":"$serverUrl$path"
|
||||
| },
|
||||
| {
|
||||
| "key":"http_method",
|
||||
| "value":"$requestVerb"
|
||||
| }
|
||||
| {
|
||||
| "key":"url",
|
||||
| "value":"http://mydomain.com/xxx"
|
||||
| }
|
||||
| ]
|
||||
|}
|
||||
|```
|
||||
|
|
||||
|""".stripMargin
|
||||
val exampleRequestBody: Product = getRequestExample(openAPI, op.getRequestBody)
|
||||
val successResponseBody: Product = getResponseExample(openAPI, op.getResponses)
|
||||
val errorResponseBodies: List[String] = List(
|
||||
UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
UnknownError
|
||||
)
|
||||
val catalogs: Catalogs = Catalogs(notCore, notPSD2, notOBWG)
|
||||
|
||||
val roles: Option[List[ApiRole]] = {
|
||||
val roleName = s"Can$summary${entitlementSuffix(path)}"
|
||||
.replaceFirst("Can(Create|Update|Get|Delete)", "Can$1Dynamic")
|
||||
.replace(" ", "")
|
||||
Some(List(
|
||||
ApiRole.getOrCreateDynamicApiRole(roleName)
|
||||
))
|
||||
}
|
||||
val connectorMethods = Some(List("dynamicEndpointProcess"))
|
||||
val doc = ResourceDoc(
|
||||
partialFunction,
|
||||
implementedInApiVersion,
|
||||
partialFunctionName,
|
||||
requestVerb,
|
||||
requestUrl,
|
||||
summary,
|
||||
description,
|
||||
exampleRequestBody,
|
||||
successResponseBody,
|
||||
errorResponseBodies,
|
||||
catalogs,
|
||||
tags,
|
||||
roles,
|
||||
connectorMethods = connectorMethods
|
||||
)
|
||||
(doc, path)
|
||||
}
|
||||
|
||||
DynamicEndpointInfo(id, docs, serverUrl)
|
||||
}
|
||||
|
||||
private val PathParamRegx = """\{(.+?)\}""".r
|
||||
private val WordBoundPattern = Pattern.compile("(?<=[a-z])(?=[A-Z])|-")
|
||||
|
||||
private def buildRequestUrl(path: String): String = {
|
||||
val url = StringUtils.split(s"$urlPrefix/$path", "/")
|
||||
url.map {
|
||||
case PathParamRegx(param) => WordBoundPattern.matcher(param).replaceAll("_").toUpperCase()
|
||||
case v => v
|
||||
}.mkString("/", "/", "")
|
||||
}
|
||||
|
||||
def parseSwaggerContent(content: String): OpenAPI = {
|
||||
val tempSwaggerFile = File.createTempFile("temp", ".swagger")
|
||||
FileUtils.write(tempSwaggerFile, content, Charset.forName("utf-8"))
|
||||
val openAPI: OpenAPI = new OpenAPIV3Parser().read(tempSwaggerFile.getAbsolutePath)
|
||||
// Delete temp file when program exits, only if delete fail.
|
||||
if(!FileUtils.deleteQuietly(tempSwaggerFile)){
|
||||
tempSwaggerFile.deleteOnExit()
|
||||
}
|
||||
openAPI
|
||||
}
|
||||
|
||||
def doc: ArrayBuffer[ResourceDoc] = {
|
||||
val docs = ArrayBuffer[ResourceDoc]()
|
||||
dynamicEndpointInfos.forEach { info =>
|
||||
info.resourceDocs.foreach { doc =>
|
||||
docs += doc
|
||||
}
|
||||
}
|
||||
docs
|
||||
}
|
||||
|
||||
private def buildSummary(method: HttpMethod, op: Operation, path: String): String = method match {
|
||||
case _ if StringUtils.isNotBlank(op.getSummary) => op.getSummary
|
||||
case HttpMethod.GET | HttpMethod.DELETE =>
|
||||
val opName = if(method == HttpMethod.GET) "Get" else "Delete"
|
||||
op.getResponses.asScala
|
||||
.find(_._1.startsWith("20"))
|
||||
.flatMap(it => getRef(it._2.getContent, it._2.get$ref()) )
|
||||
.map(StringUtils.substringAfterLast(_, "/"))
|
||||
.map(entityName => s"$opName $entityName")
|
||||
.orElse(Option(op.getDescription))
|
||||
.filter(StringUtils.isNotBlank)
|
||||
.orElse(Option(s"$opName $path"))
|
||||
.map(_.replaceFirst("(?i)((get|delete)\\s+\\S+).*", "$1"))
|
||||
.map(capitalize)
|
||||
.get
|
||||
|
||||
case m@(HttpMethod.POST | HttpMethod.PUT) =>
|
||||
val opName = if(m == HttpMethod.POST) "Create" else "Update"
|
||||
|
||||
getRef(op.getRequestBody.getContent, op.getRequestBody.get$ref())
|
||||
.map(StringUtils.substringAfterLast(_, "/"))
|
||||
.map(entityName => s"$opName $entityName")
|
||||
.orElse(Option(op.getDescription))
|
||||
.filter(StringUtils.isNotBlank)
|
||||
.orElse(Option(s"$method $path"))
|
||||
.map(capitalize)
|
||||
.get
|
||||
case _ => throw new RuntimeException(s"Support HTTP METHOD: GET, POST, PUT, DELETE, current method is $method")
|
||||
}
|
||||
private def capitalize(str: String): String =
|
||||
StringUtils.split(str, " ").map(_.capitalize).mkString(" ")
|
||||
|
||||
private def getRequestExample(openAPI: OpenAPI, body: RequestBody): Product = {
|
||||
if(body == null || body.getContent == null) {
|
||||
JObject()
|
||||
} else {
|
||||
getExample(openAPI, getRef(body.getContent, body.get$ref()).orNull)
|
||||
}
|
||||
}
|
||||
private def getResponseExample(openAPI: OpenAPI, apiResponses: ApiResponses): Product = {
|
||||
if(apiResponses == null || apiResponses.isEmpty) {
|
||||
JObject()
|
||||
} else {
|
||||
val ref: Option[String] = apiResponses.asScala
|
||||
.find(_._1.startsWith("20"))
|
||||
.flatMap(it => getRef(it._2.getContent, it._2.get$ref()))
|
||||
getExample(openAPI, ref.orNull)
|
||||
}
|
||||
}
|
||||
|
||||
private def getRef(content: Content, $ref: String): Option[String] = {
|
||||
if(StringUtils.isNoneBlank($ref)) {
|
||||
Option($ref)
|
||||
} else {
|
||||
val schemaRef: Option[String] = Option(content.get("application/json"))
|
||||
.flatMap(it => Option[Schema[_]](it.getSchema))
|
||||
.map(_.get$ref())
|
||||
.filter(StringUtils.isNoneBlank(_))
|
||||
|
||||
if(schemaRef.isDefined) {
|
||||
Option(schemaRef.get)
|
||||
} else {
|
||||
val supportMediaTypes = content.values().asScala
|
||||
supportMediaTypes.collectFirst {
|
||||
case mediaType if mediaType.getSchema != null && StringUtils.isNotBlank(mediaType.getSchema.get$ref()) =>
|
||||
mediaType.getSchema.get$ref()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private val RegexDefinitions = """(?:#/components/schemas(?:/#definitions)?/)(.+)""".r
|
||||
private val RegexResponse = """#/responses/(.+)""".r
|
||||
|
||||
private def getExample(openAPI: OpenAPI, ref: String): Product = {
|
||||
implicit val formats = CustomJsonFormats.formats
|
||||
ref match {
|
||||
case null => JObject()
|
||||
|
||||
case RegexResponse(refName) =>
|
||||
val response = openAPI.getComponents.getResponses.get(refName)
|
||||
val ref = getRef(response.getContent, response.get$ref())
|
||||
getExample(openAPI, ref.get)
|
||||
|
||||
case RegexDefinitions(refName) =>
|
||||
openAPI.getComponents.getSchemas.get(refName) match {
|
||||
case o: ObjectSchema =>
|
||||
val properties: util.Map[String, Schema[_]] = o.getProperties
|
||||
|
||||
val jFields: mutable.Iterable[JField] = properties.asScala.map { kv =>
|
||||
val (name, value) = kv
|
||||
val valueExample = if(value.getClass == classOf[Schema[_]]) getExample(openAPI, value.get$ref()) else getPropertyExample(value)
|
||||
JField(name, json.Extraction.decompose(valueExample))
|
||||
}
|
||||
JObject(jFields.toList)
|
||||
|
||||
case a: ArraySchema =>
|
||||
Option(a.getExample)
|
||||
.map(json.Extraction.decompose(_).asInstanceOf[JObject])
|
||||
.getOrElse {
|
||||
val schema: Schema[_] = a.getItems
|
||||
val singleItem: Any = if(schema.getClass == classOf[Schema[_]]) getExample(openAPI, schema.get$ref()) else getPropertyExample(schema)
|
||||
val jItem = json.Extraction.decompose(singleItem)
|
||||
jItem :: Nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private def getPropertyExample(schema: Schema[_]) = schema match {
|
||||
case b: BooleanSchema => Option(b.getExample).getOrElse(true)
|
||||
case d: DateSchema => Option(d.getExample).getOrElse {
|
||||
APIUtil.DateWithDayFormat.format(new Date())
|
||||
}
|
||||
case t: DateTimeSchema => Option(t.getExample).getOrElse {
|
||||
APIUtil.DateWithSecondsFormat.format(new Date())
|
||||
}
|
||||
case i: IntegerSchema => Option(i.getExample).getOrElse(1)
|
||||
case n: NumberSchema => Option(n.getExample).getOrElse(1.2)
|
||||
case s: StringSchema => Option(s.getExample).getOrElse("string")
|
||||
case _ => throw new RuntimeException(s"Not support type $schema, please support it if necessary.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param id DynamicEntity id value
|
||||
* @param docsToUrl ResourceDoc to url that defined in swagger content
|
||||
* @param serverUrl base url that defined in swagger content
|
||||
*/
|
||||
case class DynamicEndpointInfo(id: String, docsToUrl: mutable.Iterable[(ResourceDoc, String)], serverUrl: String) {
|
||||
val resourceDocs: mutable.Iterable[ResourceDoc] = docsToUrl.map(_._1)
|
||||
|
||||
private val existsUrlToMethod: mutable.Iterable[(HttpMethod, String, ResourceDoc)] =
|
||||
docsToUrl
|
||||
.map(it => {
|
||||
val (doc, path) = it
|
||||
(HttpMethod.valueOf(doc.requestVerb), path, doc)
|
||||
})
|
||||
|
||||
def findDynamicEndpoint(newMethod: HttpMethod, newUrl: String): Option[(DynamicEndpointInfo, ResourceDoc, String)] = existsUrlToMethod.find(it => {
|
||||
val (method, url, _) = it
|
||||
isSameUrl(newUrl, url) && newMethod == method
|
||||
}).map(it => (this, it._3, it._2))
|
||||
|
||||
def existsEndpoint(newMethod: HttpMethod, newUrl: String): Boolean = findDynamicEndpoint(newMethod, newUrl).isDefined
|
||||
|
||||
def targetUrl(url: String): String = s"""$serverUrl$url"""
|
||||
|
||||
/**
|
||||
* check whether two url is the same:
|
||||
* isSameUrl("/abc/efg", "/abc/efg") == true
|
||||
* isSameUrl("/abc/efg", "/abc/{id}") == true
|
||||
* isSameUrl("/abc/{userId}", "/abc/{id}") == true
|
||||
* isSameUrl("/abc/{userId}/", "/abc/{id}") == true
|
||||
* isSameUrl("/def/abc/", "/abc/{id}") == false
|
||||
* @param pathX
|
||||
* @param pathY
|
||||
* @return
|
||||
*/
|
||||
private def isSameUrl(pathX: String, pathY: String) = {
|
||||
val splitPathX = StringUtils.split(pathX, '/')
|
||||
val splitPathY = StringUtils.split(pathY, '/')
|
||||
if(splitPathX.size != splitPathY.size) {
|
||||
false
|
||||
} else {
|
||||
splitPathX.zip(splitPathY).forall {kv =>
|
||||
val (partX, partY) = kv
|
||||
partX == partY ||
|
||||
(partX.startsWith("{") && partX.endsWith("}")) ||
|
||||
(partY.startsWith("{") && partY.endsWith("}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -32,7 +32,7 @@ object MockerConnector {
|
||||
|
||||
def definitionsMap = NewStyle.function.getDynamicEntities().map(it => (it.entityName, DynamicEntityInfo(it.metadataJson, it.entityName))).toMap
|
||||
|
||||
def doc = {
|
||||
def doc: ArrayBuffer[ResourceDoc] = {
|
||||
val docs: Seq[ResourceDoc] = definitionsMap.values.flatMap(createDocs).toSeq
|
||||
collection.mutable.ArrayBuffer(docs:_*)
|
||||
}
|
||||
|
||||
@ -36,18 +36,16 @@ import code.api.v1_2_1.{BankRoutingJsonV121, JSONFactory, UserJSONV121, ViewJSON
|
||||
import code.api.v1_4_0.JSONFactory1_4_0.TransactionRequestAccountJsonV140
|
||||
import code.api.v2_0_0.TransactionRequestChargeJsonV200
|
||||
import code.api.v2_1_0.ResourceUserJSON
|
||||
import code.api.v2_2_0.ConsumerJson
|
||||
import code.api.v3_0_0.JSONFactory300.createAccountRoutingsJSON
|
||||
import code.api.v3_0_0.{CustomerAttributeResponseJsonV300, ViewBasicV300}
|
||||
import code.api.v3_1_0.AccountAttributeResponseJson
|
||||
import code.api.v3_1_0.JSONFactory310.createAccountAttributeJson
|
||||
import code.directdebit.DirectDebitTrait
|
||||
import code.entitlement.Entitlement
|
||||
import code.model.{Consumer, ModeratedBankAccount, ModeratedBankAccountCore}
|
||||
import code.model.{Consumer, ModeratedBankAccountCore}
|
||||
import code.standingorders.StandingOrderTrait
|
||||
import code.transactionChallenge.MappedExpectedChallengeAnswer
|
||||
import code.transactionrequests.TransactionRequests.TransactionChallengeTypes
|
||||
import code.users.Users
|
||||
import com.openbankproject.commons.model._
|
||||
import net.liftweb.common.{Box, Full}
|
||||
|
||||
|
||||
@ -55,7 +55,7 @@ object OBPAPI4_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w
|
||||
|
||||
// Possible Endpoints from 4.0.0, exclude one endpoint use - method,exclude multiple endpoints use -- method,
|
||||
// e.g getEndpoints(Implementations4_0_0) -- List(Implementations4_0_0.genericEndpoint, Implementations4_0_0.root)
|
||||
val endpointsOf4_0_0 = getEndpoints(Implementations4_0_0) - Implementations4_0_0.genericEndpoint
|
||||
val endpointsOf4_0_0 = getEndpoints(Implementations4_0_0) - Implementations4_0_0.genericEndpoint - Implementations4_0_0.dynamicEndpoint
|
||||
|
||||
lazy val excludeEndpoints =
|
||||
nameOf(Implementations1_2_1.addPermissionForUserForBankAccountForMultipleViews) ::
|
||||
@ -67,7 +67,7 @@ object OBPAPI4_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w
|
||||
// if old version ResourceDoc objects have the same name endpoint with new version, omit old version ResourceDoc.
|
||||
def allResourceDocs = collectResourceDocs(OBPAPI3_1_0.allResourceDocs,
|
||||
Implementations4_0_0.resourceDocs,
|
||||
MockerConnector.doc)
|
||||
MockerConnector.doc, DynamicEndpointHelper.doc)
|
||||
.filterNot(it => it.partialFunctionName.matches(excludeEndpoints.mkString("|")))
|
||||
//TODO exclude two endpoints, after training we need add logic to exclude endpoints
|
||||
|
||||
@ -85,6 +85,8 @@ object OBPAPI4_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w
|
||||
registerRoutes(routes, allResourceDocs, apiPrefix, true)
|
||||
|
||||
oauthServe(apiPrefix{Implementations4_0_0.genericEndpoint}, None)
|
||||
oauthServe(apiPrefix{Implementations4_0_0.dynamicEndpoint}, None)
|
||||
|
||||
logger.info(s"version $version has been run! There are ${routes.length} routes.")
|
||||
|
||||
// specified response for OPTIONS request.
|
||||
|
||||
@ -3,6 +3,7 @@ package code.bankconnectors
|
||||
import java.util.Date
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
import code.DynamicEndpoint.DynamicEndpointT
|
||||
import code.accountholders.{AccountHolders, MapperAccountHolders}
|
||||
import code.api.attributedefinition.AttributeDefinition
|
||||
import code.api.{APIFailure, APIFailureNewStyle}
|
||||
@ -59,6 +60,7 @@ import scala.concurrent.duration._
|
||||
import scala.math.{BigDecimal, BigInt}
|
||||
import scala.util.Random
|
||||
import scala.reflect.runtime.universe.{MethodSymbol, typeOf}
|
||||
import _root_.akka.http.scaladsl.model.HttpMethod
|
||||
|
||||
/*
|
||||
So we can switch between different sources of resources e.g.
|
||||
@ -2181,6 +2183,9 @@ trait Connector extends MdcLoggable {
|
||||
requestBody: Option[JObject],
|
||||
entityId: Option[String],
|
||||
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],
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[JValue]] = Future{(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
def createDirectDebit(bankId: String,
|
||||
accountId: String,
|
||||
@ -2210,4 +2215,16 @@ trait Connector extends MdcLoggable {
|
||||
|
||||
def deleteCustomerAttribute(customerAttributeId: String,
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[Boolean]] = Future{(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
def createDynamicEndpoint(swaggerString: String, callContext: Option[CallContext]): OBPReturnType[Box[DynamicEndpointT]] = Future {
|
||||
(Failure(setUnimplementedError), callContext)
|
||||
}
|
||||
|
||||
def getDynamicEndpoint(dynamicEndpointId: String, callContext: Option[CallContext]): OBPReturnType[Box[DynamicEndpointT]] = Future {
|
||||
(Failure(setUnimplementedError), callContext)
|
||||
}
|
||||
|
||||
def getDynamicEndpoints(callContext: Option[CallContext]): OBPReturnType[List[DynamicEndpointT]] = Future {
|
||||
(List.empty[DynamicEndpointT], callContext)
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import java.util.UUID.randomUUID
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import code.DynamicData.DynamicDataProvider
|
||||
import code.DynamicEndpoint.{DynamicEndpointProvider, DynamicEndpointT}
|
||||
import code.accountapplication.AccountApplicationX
|
||||
import code.accountattribute.AccountAttributeX
|
||||
import code.accountholders.{AccountHolders, MapperAccountHolders}
|
||||
@ -78,7 +79,6 @@ import net.liftweb.mapper.{By, _}
|
||||
import net.liftweb.util.Helpers.{hours, now, time, tryo}
|
||||
import net.liftweb.util.Mailer
|
||||
import net.liftweb.util.Mailer.{From, PlainMailBodyType, Subject, To}
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.mindrot.jbcrypt.BCrypt
|
||||
import scalacache.ScalaCache
|
||||
import scalacache.guava.GuavaCache
|
||||
@ -91,6 +91,7 @@ import scala.language.postfixOps
|
||||
import scala.math.{BigDecimal, BigInt}
|
||||
import scala.util.Random
|
||||
|
||||
import _root_.akka.http.scaladsl.model.HttpMethod
|
||||
|
||||
object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
|
||||
@ -3158,6 +3159,13 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
}
|
||||
}
|
||||
|
||||
/* delegate to rest connector
|
||||
*/
|
||||
override def dynamicEndpointProcess(url: String, jValue: JValue, method: HttpMethod, params: Map[String, List[String]], pathParams: Map[String, String],
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[JValue]] = {
|
||||
Connector.getConnectorInstance("rest_vMar2019").dynamicEndpointProcess(url,jValue, method, params, pathParams, callContext)
|
||||
}
|
||||
|
||||
override def createDirectDebit(bankId: String,
|
||||
accountId: String,
|
||||
customerId: String,
|
||||
@ -4144,6 +4152,18 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
}
|
||||
Full(res)
|
||||
}
|
||||
|
||||
override def createDynamicEndpoint(swaggerString: String, callContext: Option[CallContext]): OBPReturnType[Box[DynamicEndpointT]] = Future {
|
||||
(DynamicEndpointProvider.connectorMethodProvider.vend.create(swaggerString), callContext)
|
||||
}
|
||||
|
||||
override def getDynamicEndpoint(dynamicEndpointId: String, callContext: Option[CallContext]): OBPReturnType[Box[DynamicEndpointT]] = Future {
|
||||
(DynamicEndpointProvider.connectorMethodProvider.vend.get(dynamicEndpointId), callContext)
|
||||
}
|
||||
|
||||
override def getDynamicEndpoints(callContext: Option[CallContext]): OBPReturnType[List[DynamicEndpointT]] = Future {
|
||||
(DynamicEndpointProvider.connectorMethodProvider.vend.getAll(), callContext)
|
||||
}
|
||||
|
||||
override def deleteCustomerAttribute(customerAttributeId: String, callContext: Option[CallContext] ): OBPReturnType[Box[Boolean]] = {
|
||||
CustomerAttributeX.customerAttributeProvider.vend.deleteCustomerAttribute(customerAttributeId) map { ( _, callContext) }
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package code
|
||||
|
||||
import java.lang.reflect.Method
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import akka.http.scaladsl.model.HttpMethod
|
||||
import code.api.{APIFailureNewStyle, ApiVersionHolder}
|
||||
import code.api.util.{CallContext, NewStyle}
|
||||
import code.methodrouting.{MethodRouting, MethodRoutingT}
|
||||
@ -17,7 +19,7 @@ import scala.collection.mutable.ArrayBuffer
|
||||
import scala.reflect.runtime.universe.{MethodSymbol, Type, typeOf}
|
||||
import code.api.util.ErrorMessages.InvalidConnectorResponseForMissingRequiredValues
|
||||
import code.api.util.APIUtil.fullBoxOrException
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import com.openbankproject.commons.util.{ApiVersion, ReflectUtils}
|
||||
import com.openbankproject.commons.util.ReflectUtils._
|
||||
import com.openbankproject.commons.util.Functions.Implicits._
|
||||
import net.liftweb.util.ThreadGlobal
|
||||
@ -97,6 +99,18 @@ package object bankconnectors extends MdcLoggable {
|
||||
NewStyle.function.getMethodRoutings(Some(methodName))
|
||||
.find(_.parameters.exists(it => it.key == "entityName" && it.value == entityName))
|
||||
}
|
||||
case _ if methodName == "dynamicEndpointProcess" => {
|
||||
val Array(url: String, _, method: HttpMethod, _*) = args
|
||||
NewStyle.function.getMethodRoutings(Some(methodName))
|
||||
.find(routing => {
|
||||
routing.parameters.exists(it => it.key == "http_method" && it.value.equalsIgnoreCase(method.value)) &&
|
||||
routing.parameters.exists(it => it.key == "url")&&
|
||||
routing.parameters.exists(
|
||||
it => it.key == "url_pattern" &&
|
||||
(it.value == url || Pattern.compile(it.value).matcher(url).matches())
|
||||
)
|
||||
})
|
||||
}
|
||||
case None => NewStyle.function.getMethodRoutings(Some(methodName), Some(false))
|
||||
.find {routing =>
|
||||
val bankIdPattern = routing.bankIdPattern
|
||||
@ -123,7 +137,7 @@ package object bankconnectors extends MdcLoggable {
|
||||
case name => Connector.getConnectorInstance(name)
|
||||
}
|
||||
val methodSymbol = connector.implementedMethods(methodName).alternatives match {
|
||||
case m::Nil if m.isInstanceOf[MethodSymbol] => m.asMethod
|
||||
case m::Nil if m.isMethod => m.asMethod
|
||||
case _ =>
|
||||
findMethodByArgs(connector, methodName, args:_*)
|
||||
.getOrElse(sys.error(s"not found matched method, method name: ${methodName}, params: ${args.mkString(",")}"))
|
||||
@ -179,7 +193,8 @@ package object bankconnectors extends MdcLoggable {
|
||||
|
||||
processObj match {
|
||||
case None => None
|
||||
case Some(value) => {
|
||||
|
||||
case Some(value) if ReflectUtils.isObpObject(value) => {
|
||||
val argNameToValues: Map[String, Any] = getConstructorArgs(value)
|
||||
//find from current object constructor args
|
||||
// orElse: if current object constructor args not found value, recursive search args
|
||||
@ -192,6 +207,8 @@ package object bankconnectors extends MdcLoggable {
|
||||
.find(it => it.isDefined)
|
||||
}
|
||||
}
|
||||
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -61,8 +61,10 @@ import code.model.dataAccess.internalMapping.MappedAccountIdMappingProvider
|
||||
import code.util.{Helper, JsonUtils}
|
||||
import com.openbankproject.commons.model.enums.{AccountAttributeType, CardAttributeType, DynamicEntityOperation, ProductAttributeType}
|
||||
import com.openbankproject.commons.util.{ReflectUtils, RequiredFieldValidation}
|
||||
import net.liftweb.json._
|
||||
import net.liftweb.json
|
||||
import net.liftweb.json.{JValue, _}
|
||||
import net.liftweb.json.Extraction.decompose
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable {
|
||||
//this one import is for implicit convert, don't delete
|
||||
@ -9324,6 +9326,81 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
|
||||
val result: OBPReturnType[Box[JValue]] = sendRequest[InBound](url, HttpMethods.POST, req, callContext).map(convertToTuple(callContext))
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
override def dynamicEndpointProcess(url: String, jValue: JValue, method: HttpMethod, params: Map[String, List[String]], pathParams: Map[String, String],
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[JValue]] = {
|
||||
val urlInMethodRouting: Option[String] = MethodRoutingHolder.methodRouting match {
|
||||
case _: EmptyBox => None
|
||||
case Full(routing) => routing.parameters.find(_.key == "url").map(_.value)
|
||||
}
|
||||
val pathVariableRex = """\{:(.+?)\}""".r
|
||||
val targetUrl = urlInMethodRouting.map { urlInRouting =>
|
||||
val tuples: Iterator[(String, String)] = pathVariableRex.findAllMatchIn(urlInRouting).map{ regex =>
|
||||
val expression = regex.group(0)
|
||||
val paramName = regex.group(1)
|
||||
val paramValue =
|
||||
if(paramName.startsWith("body.")) {
|
||||
val path = StringUtils.substringAfter(paramName, "body.")
|
||||
val value = JsonUtils.getValueByPath(jValue, path)
|
||||
JsonUtils.toString(value)
|
||||
} else {
|
||||
pathParams.get(paramName)
|
||||
.orElse(params.get(paramName).flatMap(_.headOption)).getOrElse(throw new RuntimeException(s"param $paramName not exists."))
|
||||
}
|
||||
expression -> paramValue
|
||||
}
|
||||
|
||||
(urlInRouting /: tuples) {(pre, kv)=>
|
||||
pre.replace(kv._1, kv._2)
|
||||
}
|
||||
}.getOrElse(url)
|
||||
|
||||
val paramNameToValue = for {
|
||||
(name, values) <- params
|
||||
value <- values
|
||||
param = s"$name=$value"
|
||||
} yield param
|
||||
|
||||
val paramUrl: String =
|
||||
if(params.isEmpty){
|
||||
targetUrl
|
||||
} else if(targetUrl.contains("?")) {
|
||||
targetUrl + "&" + paramNameToValue.mkString("&")
|
||||
} else {
|
||||
targetUrl + "?" + paramNameToValue.mkString("&")
|
||||
}
|
||||
|
||||
val jsonToSend = if(jValue == JNothing) "" else compactRender(jValue)
|
||||
val request = prepareHttpRequest(paramUrl, method, HttpProtocol("HTTP/1.1"), jsonToSend).withHeaders(callContext)
|
||||
logger.debug(s"RestConnector_vMar2019 request is : $request")
|
||||
val responseFuture = makeHttpRequest(request)
|
||||
|
||||
val result: Future[(Box[JValue], Option[CallContext])] = responseFuture.map {
|
||||
case response@HttpResponse(status, _, entity@_, _) => (status, entity)
|
||||
}.flatMap {
|
||||
case (status, entity) if status.isSuccess() =>
|
||||
this.extractBody(entity)
|
||||
.map{
|
||||
case v if StringUtils.isBlank(v) => (Empty, callContext)
|
||||
case v => (Full(json.parse(v)), callContext)
|
||||
}
|
||||
case (status, entity) => {
|
||||
val future: Future[Box[Box[JValue]]] = extractBody(entity) map { msg =>
|
||||
tryo {
|
||||
val failure: Box[JValue] = ParamFailure(msg, APIFailureNewStyle(msg, status.intValue()))
|
||||
failure
|
||||
} ~> APIFailureNewStyle(msg, status.intValue())
|
||||
}
|
||||
future.map{
|
||||
case Full(v) => (v, callContext)
|
||||
case e: EmptyBox => (e, callContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
//In RestConnector, we use the headers to propagate the parameters to Adapter. The parameters come from the CallContext.outboundAdapterAuthInfo.userAuthContext
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
package code.DynamicEndpoint
|
||||
|
||||
import com.openbankproject.commons.model.{Converter, JsonFieldReName}
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
object DynamicEndpointProvider extends SimpleInjector {
|
||||
|
||||
val connectorMethodProvider = new Inject(buildOne _) {}
|
||||
|
||||
def buildOne: MappedDynamicEndpointProvider.type = MappedDynamicEndpointProvider
|
||||
}
|
||||
|
||||
trait DynamicEndpointT {
|
||||
def dynamicEndpointId: Option[String]
|
||||
def swaggerString: String
|
||||
}
|
||||
|
||||
case class DynamicEndpointCommons(
|
||||
dynamicEndpointId: Option[String] = None,
|
||||
swaggerString: String
|
||||
) extends DynamicEndpointT with JsonFieldReName
|
||||
|
||||
object DynamicEndpointCommons extends Converter[DynamicEndpointT, DynamicEndpointCommons]
|
||||
|
||||
case class DynamicEndpointSwagger(swaggerString: String, dynamicEndpointId: Option[String] = None)
|
||||
|
||||
trait DynamicEndpointProvider {
|
||||
def create(swaggerString: String): Box[DynamicEndpointT]
|
||||
def update(dynamicEndpointId: String, swaggerString: String): Box[DynamicEndpointT]
|
||||
def get(dynamicEndpointId: String): Box[DynamicEndpointT]
|
||||
def getAll(): List[DynamicEndpointT]
|
||||
def delete(dynamicEndpointId: String): Boolean
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package code.DynamicEndpoint
|
||||
|
||||
import code.api.util.CustomJsonFormats
|
||||
import code.util.MappedUUID
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.mapper._
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
|
||||
object MappedDynamicEndpointProvider extends DynamicEndpointProvider with CustomJsonFormats{
|
||||
override def create(swaggerString: String): Box[DynamicEndpointT] = {
|
||||
tryo{DynamicEndpoint.create.SwaggerString(swaggerString).saveMe()}
|
||||
}
|
||||
override def update(dynamicEndpointId: String, swaggerString: String): Box[DynamicEndpointT] = {
|
||||
DynamicEndpoint.find(By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId)).map(_.SwaggerString(swaggerString).saveMe())
|
||||
}
|
||||
|
||||
override def get(dynamicEndpointId: String): Box[DynamicEndpointT] = DynamicEndpoint.find(By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId))
|
||||
|
||||
override def getAll(): List[DynamicEndpointT] = DynamicEndpoint.findAll()
|
||||
|
||||
override def delete(dynamicEndpointId: String): Boolean = DynamicEndpoint.bulkDelete_!!(By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId))
|
||||
|
||||
}
|
||||
|
||||
class DynamicEndpoint extends DynamicEndpointT with LongKeyedMapper[DynamicEndpoint] with IdPK {
|
||||
|
||||
override def getSingleton = DynamicEndpoint
|
||||
|
||||
object DynamicEndpointId extends MappedUUID(this)
|
||||
|
||||
object SwaggerString extends MappedText(this)
|
||||
|
||||
override def dynamicEndpointId: Option[String] = Option(DynamicEndpointId.get)
|
||||
override def swaggerString: String = SwaggerString.get
|
||||
}
|
||||
|
||||
object DynamicEndpoint extends DynamicEndpoint with LongKeyedMetaMapper[DynamicEndpoint] {
|
||||
override def dbIndexes = UniqueIndex(DynamicEndpointId) :: super.dbIndexes
|
||||
}
|
||||
|
||||
@ -32,6 +32,7 @@ trait EntitlementProvider {
|
||||
def getEntitlementsByRoleFuture(roleName: String) : Future[Box[List[Entitlement]]]
|
||||
def addEntitlement(bankId: String, userId: String, roleName: String) : Box[Entitlement]
|
||||
def deleteDynamicEntityEntitlement(entityName: String) : Box[Boolean]
|
||||
def deleteEntitlements(entityNames: List[String]) : Box[Boolean]
|
||||
}
|
||||
|
||||
trait Entitlement {
|
||||
@ -54,6 +55,7 @@ class RemotedataEntitlementsCaseClasses {
|
||||
case class getEntitlementsByRoleFuture(roleName: String)
|
||||
case class addEntitlement(bankId: String, userId: String, roleName: String)
|
||||
case class deleteDynamicEntityEntitlement(entityName: String)
|
||||
case class deleteEntitlements(entityNames: List[String])
|
||||
}
|
||||
|
||||
object RemotedataEntitlementsCaseClasses extends RemotedataEntitlementsCaseClasses
|
||||
@ -94,8 +94,12 @@ object MappedEntitlementsProvider extends EntitlementProvider {
|
||||
|
||||
override def deleteDynamicEntityEntitlement(entityName: String): Box[Boolean] = {
|
||||
val roleNames = DynamicEntityInfo.roleNames(entityName)
|
||||
deleteEntitlements(roleNames)
|
||||
}
|
||||
|
||||
override def deleteEntitlements(entityNames: List[String]) : Box[Boolean] = {
|
||||
Box.tryo{
|
||||
MappedEntitlement.bulkDelete_!!(ByList(MappedEntitlement.mRoleName, roleNames))
|
||||
MappedEntitlement.bulkDelete_!!(ByList(MappedEntitlement.mRoleName, entityNames))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -56,4 +56,8 @@ object RemotedataEntitlements extends ObpActorInit with EntitlementProvider {
|
||||
(actor ? cc.deleteDynamicEntityEntitlement(entityName)).mapTo[Box[Boolean]]
|
||||
)
|
||||
|
||||
override def deleteEntitlements(entityNames: List[String]) : Box[Boolean] = getValueFromFuture(
|
||||
(actor ? cc.deleteEntitlements(entityNames)).mapTo[Box[Boolean]]
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@ -63,6 +63,10 @@ class RemotedataEntitlementsActor extends Actor with ObpActorHelper with MdcLogg
|
||||
logger.debug(s"deleteDynamicEntityEntitlement($entityName)")
|
||||
sender ! (mapper.deleteDynamicEntityEntitlement(entityName))
|
||||
|
||||
case cc.deleteEntitlements(entityNames) =>
|
||||
logger.debug(s"deleteEntitlements($entityNames)")
|
||||
sender ! (mapper.deleteEntitlements(entityNames))
|
||||
|
||||
case message => logger.warn("[AKKA ACTOR ERROR - REQUEST NOT RECOGNIZED] " + message)
|
||||
|
||||
}
|
||||
|
||||
@ -163,6 +163,7 @@ object JsonUtils {
|
||||
} else {
|
||||
JField(newName, jObj)
|
||||
}
|
||||
case _ => throw new RuntimeException(s"Not support json value type, value is: $jValue")
|
||||
}
|
||||
|
||||
}
|
||||
@ -420,7 +421,7 @@ object JsonUtils {
|
||||
* @param pathExpress path, can be prefix by - or !, e.g: "-result.count" "!value.isDeleted"
|
||||
* @return given nested field value
|
||||
*/
|
||||
private def getValueByPath(jValue: JValue, pathExpress: String): JValue = {
|
||||
def getValueByPath(jValue: JValue, pathExpress: String): JValue = {
|
||||
pathExpress match {
|
||||
case str if str.trim == "$root" || str.trim.isEmpty => jValue // if path is "$root" or "", return whole original json
|
||||
case RegexBoolean(b) => JBool(b.toBoolean)
|
||||
@ -479,4 +480,14 @@ object JsonUtils {
|
||||
case v => v.values.toString == expectValue
|
||||
}
|
||||
}
|
||||
|
||||
def toString(jValue: JValue) = jValue match{
|
||||
case JString(s) => s
|
||||
case JInt(num) => num.toString()
|
||||
case JDouble(num) => num.toString()
|
||||
case JBool(b) => b.toString()
|
||||
case JNothing => ""
|
||||
case JNull => "null"
|
||||
case v => json.compactRender(v)
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,7 +152,7 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
feature("Add a DynamicEntity v4.0.4- Unauthorized access") {
|
||||
scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) {
|
||||
When("We make a request v4.0.0")
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic_entities").POST
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-entities").POST
|
||||
val response400 = makePostRequest(request400, write(rightEntity))
|
||||
Then("We should get a 400")
|
||||
response400.code should equal(400)
|
||||
@ -163,7 +163,7 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
feature("Update a DynamicEntity v4.0.4- Unauthorized access") {
|
||||
scenario("We will call the endpoint without user credentials", ApiEndpoint2, VersionOfApi) {
|
||||
When("We make a request v4.0.0")
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic_entities"/ "some-method-routing-id").PUT
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-entities"/ "some-method-routing-id").PUT
|
||||
val response400 = makePutRequest(request400, write(rightEntity))
|
||||
Then("We should get a 400")
|
||||
response400.code should equal(400)
|
||||
@ -174,7 +174,7 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
feature("Get DynamicEntities v4.0.4- Unauthorized access") {
|
||||
scenario("We will call the endpoint without user credentials", ApiEndpoint3, VersionOfApi) {
|
||||
When("We make a request v4.0.0")
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic_entities").GET
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-entities").GET
|
||||
val response400 = makeGetRequest(request400)
|
||||
Then("We should get a 400")
|
||||
response400.code should equal(400)
|
||||
@ -185,7 +185,7 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
feature("Delete the DynamicEntity specified by METHOD_ROUTING_ID v4.0.4- Unauthorized access") {
|
||||
scenario("We will call the endpoint without user credentials", ApiEndpoint4, VersionOfApi) {
|
||||
When("We make a request v4.0.0")
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic_entities" / "METHOD_ROUTING_ID").DELETE
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-entities" / "METHOD_ROUTING_ID").DELETE
|
||||
val response400 = makeDeleteRequest(request400)
|
||||
Then("We should get a 400")
|
||||
response400.code should equal(400)
|
||||
@ -198,7 +198,7 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
feature("Add a DynamicEntity v4.0.4- Unauthorized access - Authorized access") {
|
||||
scenario("We will call the endpoint without the proper Role " + canCreateDynamicEntity, ApiEndpoint1, VersionOfApi) {
|
||||
When("We make a request v4.0.0 without a Role " + canCreateDynamicEntity)
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic_entities").POST <@(user1)
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-entities").POST <@(user1)
|
||||
val response400 = makePostRequest(request400, write(rightEntity))
|
||||
Then("We should get a 403")
|
||||
response400.code should equal(403)
|
||||
@ -209,7 +209,7 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
scenario("We will call the endpoint with the proper Role " + canCreateDynamicEntity , ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, VersionOfApi) {
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateDynamicEntity.toString)
|
||||
When("We make a request v4.0.0")
|
||||
val request = (v4_0_0_Request / "management" / "dynamic_entities").POST <@(user1)
|
||||
val request = (v4_0_0_Request / "management" / "dynamic-entities").POST <@(user1)
|
||||
val response = makePostRequest(request, write(rightEntity))
|
||||
Then("We should get a 201")
|
||||
response.code should equal(201)
|
||||
@ -244,7 +244,7 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
|
||||
{
|
||||
// update success
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic_entities" / dynamicEntityId ).PUT <@(user1)
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-entities" / dynamicEntityId ).PUT <@(user1)
|
||||
val response400 = makePutRequest(request400, compactRender(updateRequest))
|
||||
Then("We should get a 200")
|
||||
response400.code should equal(200)
|
||||
@ -254,7 +254,7 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
|
||||
{
|
||||
// update a not exists DynamicEntity
|
||||
val request404 = (v4_0_0_Request / "management" / "dynamic_entities" / "not-exists-id" ).PUT <@(user1)
|
||||
val request404 = (v4_0_0_Request / "management" / "dynamic-entities" / "not-exists-id" ).PUT <@(user1)
|
||||
val response404 = makePutRequest(request404, compactRender(updateRequest))
|
||||
Then("We should get a 404")
|
||||
response404.code should equal(404)
|
||||
@ -263,7 +263,7 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
|
||||
{
|
||||
// update a DynamicEntity with wrong required field name
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic_entities" / dynamicEntityId ).PUT <@(user1)
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-entities" / dynamicEntityId ).PUT <@(user1)
|
||||
val response400 = makePutRequest(request400, compactRender(wrongRequiredEntity))
|
||||
Then("We should get a 400")
|
||||
|
||||
@ -273,7 +273,7 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
|
||||
{
|
||||
// update a DynamicEntity with wrong type of description
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic_entities" / dynamicEntityId ).PUT <@(user1)
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-entities" / dynamicEntityId ).PUT <@(user1)
|
||||
val response400 = makePutRequest(request400, compactRender(wrongDescriptionEntity))
|
||||
Then("We should get a 400")
|
||||
|
||||
@ -283,7 +283,7 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
|
||||
{
|
||||
// update a DynamicEntity with wrong type of property description
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic_entities" / dynamicEntityId ).PUT <@(user1)
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-entities" / dynamicEntityId ).PUT <@(user1)
|
||||
val response400 = makePutRequest(request400, compactRender(wrongPropertyDescriptionEntity))
|
||||
Then("We should get a 400")
|
||||
|
||||
@ -293,7 +293,7 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanGetDynamicEntities.toString)
|
||||
When("We make a request v4.0.0 with the Role " + canGetDynamicEntities)
|
||||
val requestGet = (v4_0_0_Request / "management" / "dynamic_entities").GET <@(user1)
|
||||
val requestGet = (v4_0_0_Request / "management" / "dynamic-entities").GET <@(user1)
|
||||
val responseGet = makeGetRequest(requestGet)
|
||||
Then("We should get a 200")
|
||||
responseGet.code should equal(200)
|
||||
@ -308,7 +308,7 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanDeleteDynamicEntity.toString)
|
||||
When("We make a request v4.0.0 with the Role " + canDeleteDynamicEntity)
|
||||
val requestDelete400 = (v4_0_0_Request / "management" / "dynamic_entities" / dynamicEntityId).DELETE <@(user1)
|
||||
val requestDelete400 = (v4_0_0_Request / "management" / "dynamic-entities" / dynamicEntityId).DELETE <@(user1)
|
||||
val responseDelete400 = makeDeleteRequest(requestDelete400)
|
||||
Then("We should get a 200")
|
||||
responseDelete400.code should equal(200)
|
||||
|
||||
@ -0,0 +1,284 @@
|
||||
package code.api.v4_0_0
|
||||
|
||||
import code.api.util.APIUtil.OAuth._
|
||||
import code.api.util.ApiRole._
|
||||
import code.api.util.ErrorMessages.{DynamicEndpointExists, UserHasMissingRoles, UserNotLoggedIn}
|
||||
import code.api.util.ExampleValue
|
||||
import code.api.v4_0_0.OBPAPI4_0_0.Implementations4_0_0
|
||||
import code.entitlement.Entitlement
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import com.openbankproject.commons.model.ErrorMessage
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import net.liftweb.json.JsonAST.JField
|
||||
import net.liftweb.json.Serialization.write
|
||||
import org.scalatest.Tag
|
||||
|
||||
class DynamicEndpointsTest extends V400ServerSetup {
|
||||
/**
|
||||
* Test tags
|
||||
* Example: To run tests with tag "getPermissions":
|
||||
* mvn test -D tagsToInclude
|
||||
*
|
||||
* This is made possible by the scalatest maven plugin
|
||||
*/
|
||||
object VersionOfApi extends Tag(ApiVersion.v4_0_0.toString)
|
||||
object ApiEndpoint1 extends Tag(nameOf(Implementations4_0_0.createDynamicEndpoint))
|
||||
object ApiEndpoint2 extends Tag(nameOf(Implementations4_0_0.getDynamicEndpoints))
|
||||
object ApiEndpoint3 extends Tag(nameOf(Implementations4_0_0.getDynamicEndpoint))
|
||||
object ApiEndpoint4 extends Tag(nameOf(Implementations4_0_0.deleteDynamicEndpoint))
|
||||
|
||||
|
||||
feature(s"test $ApiEndpoint1 version $VersionOfApi - Unauthorized access") {
|
||||
scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) {
|
||||
val postDynamicEndpointRequestBodyExample = ExampleValue.dynamicEndpointRequestBodyExample
|
||||
|
||||
When("We make a request v4.0.0")
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-endpoints").POST
|
||||
val response400 = makePostRequest(request400, write(postDynamicEndpointRequestBodyExample))
|
||||
Then("We should get a 400")
|
||||
response400.code should equal(400)
|
||||
response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"test $ApiEndpoint1 version $VersionOfApi - authorized access- missing role") {
|
||||
scenario("We will call the endpoint with user credentials", ApiEndpoint1, VersionOfApi) {
|
||||
val postDynamicEndpointRequestBodyExample = ExampleValue.dynamicEndpointRequestBodyExample
|
||||
|
||||
When("We make a request v4.0.0")
|
||||
val request = (v4_0_0_Request / "management" / "dynamic-endpoints").POST<@ (user1)
|
||||
val response = makePostRequest(request, write(postDynamicEndpointRequestBodyExample))
|
||||
Then("We should get a 403")
|
||||
response.code should equal(403)
|
||||
response.body.extract[ErrorMessage].message.toString contains (UserHasMissingRoles) should be (true)
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"test $ApiEndpoint1 version $VersionOfApi - authorized access - with role - should be success!") {
|
||||
scenario("We will call the endpoint with user credentials", ApiEndpoint1, VersionOfApi) {
|
||||
When("We make a request v4.0.0")
|
||||
val postDynamicEndpointRequestBodyExample = ExampleValue.dynamicEndpointRequestBodyExample
|
||||
|
||||
When("We make a request v4.0.0")
|
||||
val request = (v4_0_0_Request / "management" / "dynamic-endpoints").POST<@ (user1)
|
||||
val response = makePostRequest(request, write(postDynamicEndpointRequestBodyExample))
|
||||
Then("We should get a 403")
|
||||
response.code should equal(403)
|
||||
response.body.extract[ErrorMessage].message.toString contains (UserHasMissingRoles) should be (true)
|
||||
|
||||
Then("We grant the role to the user1")
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, canCreateDynamicEndpoint.toString)
|
||||
|
||||
val responseWithRole = makePostRequest(request, write(postDynamicEndpointRequestBodyExample))
|
||||
Then("We should get a 201")
|
||||
responseWithRole.code should equal(201)
|
||||
responseWithRole.body.toString contains("dynamic_endpoint_id") should be (true)
|
||||
responseWithRole.body.toString contains("swagger_string") should be (true)
|
||||
responseWithRole.body.toString contains("Example Title") should be (true)
|
||||
responseWithRole.body.toString contains("Example Description") should be (true)
|
||||
responseWithRole.body.toString contains("Example Company") should be (true)
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"test $ApiEndpoint2 version $VersionOfApi - Unauthorized access") {
|
||||
scenario("We will call the endpoint without user credentials", ApiEndpoint2, VersionOfApi) {
|
||||
When("We make a request v4.0.0")
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-endpoints").GET
|
||||
val response400 = makeGetRequest(request400)
|
||||
Then("We should get a 400")
|
||||
response400.code should equal(400)
|
||||
response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"test $ApiEndpoint2 version $VersionOfApi - authorized access- missing role") {
|
||||
scenario("We will call the endpoint with user credentials", ApiEndpoint2, VersionOfApi) {
|
||||
When("We make a request v4.0.0")
|
||||
val request = (v4_0_0_Request / "management" / "dynamic-endpoints").GET<@ (user1)
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 400")
|
||||
response.code should equal(403)
|
||||
response.body.extract[ErrorMessage].message.toString contains (UserHasMissingRoles) should be (true)
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"test $ApiEndpoint2 version $VersionOfApi - authorized access - with role - should be success!") {
|
||||
scenario("We will call the endpoint with user credentials", ApiEndpoint2, VersionOfApi) {
|
||||
When("We make a request v4.0.0")
|
||||
val postDynamicEndpointRequestBodyExample = ExampleValue.dynamicEndpointRequestBodyExample
|
||||
|
||||
When("We make a request v4.0.0")
|
||||
val request = (v4_0_0_Request / "management" / "dynamic-endpoints").POST<@ (user1)
|
||||
val response = makePostRequest(request, write(postDynamicEndpointRequestBodyExample))
|
||||
Then("We should get a 403")
|
||||
response.code should equal(403)
|
||||
response.body.extract[ErrorMessage].message.toString contains (UserHasMissingRoles) should be (true)
|
||||
|
||||
Then("We grant the role to the user1")
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanGetDynamicEndpoints.toString)
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateDynamicEndpoint.toString)
|
||||
|
||||
|
||||
val newSwagger = postDynamicEndpointRequestBodyExample.transformField {
|
||||
case JField(name, value) if name.startsWith("/") => JField(s"$name/abc", value)
|
||||
}
|
||||
|
||||
val responseWithRole = makePostRequest(request, write(newSwagger))
|
||||
Then("We should get a 201")
|
||||
responseWithRole.code should equal(201)
|
||||
|
||||
|
||||
val duplicatedRequest = makePostRequest(request, write(newSwagger))
|
||||
Then("We should get a 400")
|
||||
duplicatedRequest.code should equal(400)
|
||||
duplicatedRequest.body.extract[ErrorMessage].message.toString contains (DynamicEndpointExists) should be (true)
|
||||
|
||||
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-endpoints").GET<@ (user1)
|
||||
val response400 = makeGetRequest(request400)
|
||||
response400.code should be (200)
|
||||
response400.body.toString contains("Example Title") should be (true)
|
||||
response400.body.toString contains("Example Description") should be (true)
|
||||
response400.body.toString contains("Example Company") should be (true)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"test $ApiEndpoint3 version $VersionOfApi - Unauthorized access") {
|
||||
scenario("We will call the endpoint without user credentials", ApiEndpoint3, VersionOfApi) {
|
||||
When("We make a request v4.0.0")
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-endpoints"/ "some-id").GET
|
||||
val response400 = makeGetRequest(request400)
|
||||
Then("We should get a 400")
|
||||
response400.code should equal(400)
|
||||
response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"test $ApiEndpoint3 version $VersionOfApi - authorized access- missing role") {
|
||||
scenario("We will call the endpoint with user credentials", ApiEndpoint3, VersionOfApi) {
|
||||
When("We make a request v4.0.0")
|
||||
val request = (v4_0_0_Request / "management" / "dynamic-endpoints" /"some-id").GET<@ (user1)
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 400")
|
||||
response.code should equal(403)
|
||||
response.body.extract[ErrorMessage].message.toString contains (UserHasMissingRoles) should be (true)
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"test $ApiEndpoint3 version $VersionOfApi - authorized access - with role - should be success!") {
|
||||
scenario("We will call the endpoint with user credentials", ApiEndpoint3, VersionOfApi) {
|
||||
When("We make a request v4.0.0")
|
||||
val postDynamicEndpointRequestBodyExample = ExampleValue.dynamicEndpointRequestBodyExample
|
||||
|
||||
When("We make a request v4.0.0")
|
||||
val request = (v4_0_0_Request / "management" / "dynamic-endpoints").POST<@ (user1)
|
||||
val response = makePostRequest(request, write(postDynamicEndpointRequestBodyExample))
|
||||
Then("We should get a 403")
|
||||
response.code should equal(403)
|
||||
response.body.extract[ErrorMessage].message.toString contains (UserHasMissingRoles) should be (true)
|
||||
|
||||
Then("We grant the role to the user1")
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanGetDynamicEndpoint.toString)
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateDynamicEndpoint.toString)
|
||||
|
||||
val newSwagger = postDynamicEndpointRequestBodyExample.transformField {
|
||||
case JField(name, value) if name.startsWith("/") => JField(s"$name/def", value)
|
||||
}
|
||||
|
||||
val responseWithRole = makePostRequest(request, write(newSwagger))
|
||||
Then("We should get a 201")
|
||||
responseWithRole.code should equal(201)
|
||||
|
||||
|
||||
val duplicatedRequest = makePostRequest(request, write(newSwagger))
|
||||
Then("We should get a 400")
|
||||
duplicatedRequest.code should equal(400)
|
||||
duplicatedRequest.body.extract[ErrorMessage].message.toString contains (DynamicEndpointExists) should be (true)
|
||||
|
||||
|
||||
val id = responseWithRole.body.\\("dynamic_endpoint_id").values.get("dynamic_endpoint_id").head.toString
|
||||
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-endpoints" /id).GET<@ (user1)
|
||||
val response400 = makeGetRequest(request400)
|
||||
response400.code should be (200)
|
||||
response400.body.toString contains("dynamic_endpoint_id") should be (true)
|
||||
response400.body.toString contains("swagger_string") should be (true)
|
||||
response400.body.toString contains("Example Title") should be (true)
|
||||
response400.body.toString contains("Example Description") should be (true)
|
||||
response400.body.toString contains("Example Company") should be (true)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"test $ApiEndpoint4 version $VersionOfApi - Unauthorized access") {
|
||||
scenario("We will call the endpoint without user credentials", ApiEndpoint4, VersionOfApi) {
|
||||
When("We make a request v4.0.0")
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-endpoints"/ "some-id").DELETE
|
||||
val response400 = makeDeleteRequest(request400)
|
||||
Then("We should get a 400")
|
||||
response400.code should equal(400)
|
||||
response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"test $ApiEndpoint4 version $VersionOfApi - authorized access- missing role") {
|
||||
scenario("We will call the endpoint with user credentials", ApiEndpoint4, VersionOfApi) {
|
||||
When("We make a request v4.0.0")
|
||||
val request = (v4_0_0_Request / "management" / "dynamic-endpoints" /"some-id").DELETE<@ (user1)
|
||||
val response = makeDeleteRequest(request)
|
||||
Then("We should get a 400")
|
||||
response.code should equal(403)
|
||||
response.body.extract[ErrorMessage].message.toString contains (UserHasMissingRoles) should be (true)
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"test $ApiEndpoint4 version $VersionOfApi - authorized access - with role - should be success!") {
|
||||
scenario("We will call the endpoint with user credentials", ApiEndpoint4, VersionOfApi) {
|
||||
When("We make a request v4.0.0")
|
||||
val postDynamicEndpointRequestBodyExample = ExampleValue.dynamicEndpointRequestBodyExample
|
||||
|
||||
When("We make a request v4.0.0")
|
||||
val request = (v4_0_0_Request / "management" / "dynamic-endpoints").POST<@ (user1)
|
||||
val response = makePostRequest(request, write(postDynamicEndpointRequestBodyExample))
|
||||
Then("We should get a 403")
|
||||
response.code should equal(403)
|
||||
response.body.extract[ErrorMessage].message.toString contains (UserHasMissingRoles) should be (true)
|
||||
|
||||
Then("We grant the role to the user1")
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanGetDynamicEndpoint.toString)
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateDynamicEndpoint.toString)
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanDeleteDynamicEndpoint.toString)
|
||||
|
||||
val newSwagger = postDynamicEndpointRequestBodyExample.transformField {
|
||||
case JField(name, value) if name.startsWith("/") => JField(s"$name/def2", value)
|
||||
}
|
||||
|
||||
val responseWithRole = makePostRequest(request, write(newSwagger))
|
||||
Then("We should get a 201")
|
||||
responseWithRole.code should equal(201)
|
||||
|
||||
|
||||
val id = responseWithRole.body.\\("dynamic_endpoint_id").values.get("dynamic_endpoint_id").head.toString
|
||||
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic-endpoints" /id).GET<@ (user1)
|
||||
val response400 = makeGetRequest(request400)
|
||||
response400.code should be (200)
|
||||
response400.body.toString contains("dynamic_endpoint_id") should be (true)
|
||||
response400.body.toString contains("swagger_string") should be (true)
|
||||
response400.body.toString contains("Example Title") should be (true)
|
||||
response400.body.toString contains("Example Description") should be (true)
|
||||
response400.body.toString contains("Example Company") should be (true)
|
||||
|
||||
|
||||
val requestDelete = (v4_0_0_Request / "management" / "dynamic-endpoints" /id).DELETE<@ (user1)
|
||||
val responseDelete = makeDeleteRequest(requestDelete)
|
||||
responseDelete.code should be (204)
|
||||
|
||||
val responseGetAgain = makeGetRequest(request400)
|
||||
responseGetAgain.code should be (404)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ import java.net.URI
|
||||
import code.bankconnectors.rest.RestConnector_vMar2019
|
||||
import code.connector.RestConnector_vMar2019_FrozenUtil.{connectorMethodNames, persistFilePath, typeNameToFieldsInfo}
|
||||
import com.openbankproject.commons.util.ReflectUtils
|
||||
import net.liftweb.common.Logger
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.scalatest.matchers.{MatchResult, Matcher}
|
||||
import org.scalatest.{BeforeAndAfter, FlatSpec, Matchers, Tag}
|
||||
@ -19,14 +20,19 @@ import scala.reflect.runtime.universe._
|
||||
class RestConnector_vMar2019_FrozenTest extends FlatSpec with Matchers with BeforeAndAfter {
|
||||
private var connectorMethodNamesPersisted: List[String] = _
|
||||
private var typeNameToFieldsInfoPersisted: Map[String, Map[String, String]] = _
|
||||
private val logger = Logger(classOf[RestConnector_vMar2019_FrozenTest])
|
||||
|
||||
before {
|
||||
val in = new ObjectInputStream(new FileInputStream(persistFilePath))
|
||||
var in: ObjectInputStream = null
|
||||
try {
|
||||
in = new ObjectInputStream(new FileInputStream(persistFilePath))
|
||||
in.readUTF()
|
||||
connectorMethodNamesPersisted = in.readObject().asInstanceOf[List[String]]
|
||||
typeNameToFieldsInfoPersisted = in.readObject().asInstanceOf[Map[String, Map[String, String]]]
|
||||
} finally {
|
||||
} catch {
|
||||
case e: Throwable =>
|
||||
logger.error("read frozen file fail.", e)
|
||||
}finally {
|
||||
IOUtils.closeQuietly(in)
|
||||
}
|
||||
}
|
||||
@ -85,7 +91,7 @@ object RestConnector_vMar2019_FrozenUtil {
|
||||
.filter(_.overrides.nonEmpty)
|
||||
.filter(_.paramLists.flatten.nonEmpty)
|
||||
.map(_.name.toString)
|
||||
.toList
|
||||
.toList.filterNot(_ == "dynamicEndpointProcess")
|
||||
|
||||
// typeNameToFieldsInfo sturcture is: (typeFullName, Map(fieldName->fieldTypeName))
|
||||
val typeNameToFieldsInfo: Map[String, Map[String, String]] = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user