mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 18:46:46 +00:00
Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
commit
cb683064f6
@ -423,6 +423,9 @@ webui_dummy_customer_logins = Customer Logins\
|
||||
\
|
||||
Please ask a member of the Open Bank Project team for more logins if you require. You can use this [application](https://sofit.openbankproject.com) which also uses OAuth to browse your transaction data (use the above username/password).\
|
||||
|
||||
# when this value is set to true and webui_dummy_customer_logins value not empty, the register consumer key success page will show dummy customers Direct Login tokens.
|
||||
webui_show_dummy_customer_tokens=false
|
||||
|
||||
# when developer register the consumer successfully, it will show this message to developer on the webpage or email.
|
||||
webui_register_consumer_success_message_webpage = Thanks for registering your consumer with the Open Bank Project API! Here is your developer information. Please save it in a secure location.
|
||||
webui_register_consumer_success_message_email = Thank you for registering to use the Open Bank Project API.
|
||||
|
||||
@ -267,7 +267,7 @@ trait OBPRestHelper extends RestHelper with MdcLoggable {
|
||||
fn(cc.copy(user = Full(u), consumer=consumer))
|
||||
}// Authentication is successful
|
||||
case _ => {
|
||||
var (httpCode, message, directLoginParameters) = DirectLogin.validator("protectedResource", DirectLogin.getHttpMethod)
|
||||
var (httpCode, message, directLoginParameters) = DirectLogin.validator("protectedResource")
|
||||
Full(errorJsonResponse(message, httpCode))
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,17 +245,17 @@ object SwaggerDefinitionsJSON {
|
||||
)
|
||||
)
|
||||
|
||||
val transactionTypeId = TransactionTypeId(value = "123")
|
||||
val transactionTypeIdSwagger = TransactionTypeId(value = "123")
|
||||
|
||||
val bankId = BankId(value = "gh.uk.9j")
|
||||
val bankIdSwagger = BankId(value = "gh.uk.9j")
|
||||
|
||||
val transactionRequestId = TransactionRequestId(value = "123")
|
||||
val transactionRequestIdSwagger = TransactionRequestId(value = "123")
|
||||
|
||||
val counterpartyId = CounterpartyId(value = "123")
|
||||
val counterpartyIdSwagger = CounterpartyId(value = "123")
|
||||
|
||||
val accountId = model.AccountId(value = "123")
|
||||
val accountIdSwagger = model.AccountId(value = "123")
|
||||
|
||||
val viewId = ViewId(value = "owner")
|
||||
val viewIdSwagger = ViewId(value = "owner")
|
||||
|
||||
|
||||
// from code.TransactionTypes.TransactionType, not from normal version Factory
|
||||
@ -264,8 +264,8 @@ object SwaggerDefinitionsJSON {
|
||||
import code.TransactionTypes.TransactionType._
|
||||
|
||||
val transactionType = TransactionType(
|
||||
id = transactionTypeId,
|
||||
bankId = bankId,
|
||||
id = transactionTypeIdSwagger,
|
||||
bankId = bankIdSwagger,
|
||||
shortCode = "80080",
|
||||
summary = SANDBOX_TAN.toString,
|
||||
description = "This is the sandbox mode, charging litter money.",
|
||||
@ -386,7 +386,7 @@ object SwaggerDefinitionsJSON {
|
||||
)
|
||||
|
||||
val transactionRequest = TransactionRequest(
|
||||
id= transactionRequestId,
|
||||
id= transactionRequestIdSwagger,
|
||||
`type`= "String",
|
||||
from= transactionRequestAccount,
|
||||
body= transactionRequestBodyAllTypes,
|
||||
@ -397,11 +397,11 @@ object SwaggerDefinitionsJSON {
|
||||
challenge= transactionRequestChallenge,
|
||||
charge= transactionRequestCharge,
|
||||
charge_policy= "String",
|
||||
counterparty_id= counterpartyId,
|
||||
counterparty_id= counterpartyIdSwagger,
|
||||
name= "String",
|
||||
this_bank_id= bankId,
|
||||
this_account_id= accountId,
|
||||
this_view_id= viewId,
|
||||
this_bank_id= bankIdSwagger,
|
||||
this_account_id= accountIdSwagger,
|
||||
this_view_id= viewIdSwagger,
|
||||
other_account_routing_scheme= "String",
|
||||
other_account_routing_address= "String",
|
||||
other_bank_routing_scheme= "String",
|
||||
@ -1044,7 +1044,7 @@ object SwaggerDefinitionsJSON {
|
||||
)
|
||||
|
||||
val transactionRequestJson = TransactionRequestJson(
|
||||
id = transactionRequestId,
|
||||
id = transactionRequestIdSwagger,
|
||||
`type` = "String",
|
||||
from = transactionRequestAccount,
|
||||
details = transactionRequestBodyJson,
|
||||
@ -1056,11 +1056,11 @@ object SwaggerDefinitionsJSON {
|
||||
challenge = transactionRequestChallenge,
|
||||
charge = transactionRequestCharge,
|
||||
charge_policy = "String",
|
||||
counterparty_id = counterpartyId,
|
||||
counterparty_id = counterpartyIdSwagger,
|
||||
name = "String",
|
||||
this_bank_id = bankId,
|
||||
this_account_id = accountId,
|
||||
this_view_id = viewId,
|
||||
this_bank_id = bankIdSwagger,
|
||||
this_account_id = accountIdSwagger,
|
||||
this_view_id = viewIdSwagger,
|
||||
other_account_routing_scheme = "String",
|
||||
other_account_routing_address = "String",
|
||||
other_bank_routing_scheme = "String",
|
||||
@ -1671,7 +1671,7 @@ object SwaggerDefinitionsJSON {
|
||||
)
|
||||
|
||||
val transactionTypeJsonV200 = TransactionTypeJsonV200(
|
||||
id = transactionTypeId,
|
||||
id = transactionTypeIdSwagger,
|
||||
bank_id = bankIdExample.value,
|
||||
short_code = "PlaceholderString",
|
||||
summary = "PlaceholderString",
|
||||
@ -2841,7 +2841,7 @@ object SwaggerDefinitionsJSON {
|
||||
)
|
||||
|
||||
val transactionInnerJson = TransactionInnerJson(
|
||||
AccountId = accountId.value,
|
||||
AccountId = accountIdSwagger.value,
|
||||
TransactionId = "123",
|
||||
TransactionReference = "Ref 1",
|
||||
Amount = amountOfMoneyJsonV121,
|
||||
|
||||
@ -94,36 +94,7 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
//Handling get request for a token
|
||||
case Req("my" :: "logins" :: "direct" :: Nil,_ , PostRequest) => {
|
||||
|
||||
//Extract the directLogin parameters from the header and test if the request is valid
|
||||
var (httpCode, message, directLoginParameters) = validator("authorizationToken", getHttpMethod)
|
||||
|
||||
if (httpCode == 200) {
|
||||
val userId:Long = (for {id <- getUserId(directLoginParameters)} yield id).getOrElse(0)
|
||||
|
||||
if (userId == 0) {
|
||||
message = ErrorMessages.InvalidLoginCredentials
|
||||
httpCode = 401
|
||||
} else if (userId == AuthUser.usernameLockedStateCode) {
|
||||
message = ErrorMessages.UsernameHasBeenLocked
|
||||
httpCode = 401
|
||||
} else {
|
||||
val jwtPayloadAsJson =
|
||||
"""{
|
||||
"":""
|
||||
}"""
|
||||
|
||||
val jwtClaims: JWTClaimsSet = JWTClaimsSet.parse(jwtPayloadAsJson)
|
||||
val (token:String, secret:String) = generateTokenAndSecret(jwtClaims)
|
||||
|
||||
//Save the token that we have generated
|
||||
if (saveAuthorizationToken(directLoginParameters, token, secret, userId)) {
|
||||
message = token
|
||||
} else {
|
||||
httpCode = 500
|
||||
message = "invalid"
|
||||
}
|
||||
}
|
||||
}
|
||||
val (httpCode: Int, message: String) = createToken(getAllParameters)
|
||||
|
||||
if (httpCode == 200)
|
||||
successJsonResponse(Extraction.decompose(JSONFactory.createTokenJSON(message)), 201)
|
||||
@ -132,6 +103,45 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* according username, password, consumer_key to generate a DirectLogin token
|
||||
* @param allParameters map {"username": "some_username", "password": "some_password", "consumer_key": "some_consumer_key"}
|
||||
* @return httpCode and token value
|
||||
*/
|
||||
def createToken(allParameters: Map[String, String]) = {
|
||||
//Extract the directLogin parameters from the header and test if the request is valid
|
||||
var (httpCode, message, directLoginParameters) = validator("authorizationToken", allParameters)
|
||||
|
||||
if (httpCode == 200) {
|
||||
val userId: Long = (for {id <- getUserId(directLoginParameters)} yield id).getOrElse(0)
|
||||
|
||||
if (userId == 0) {
|
||||
message = ErrorMessages.InvalidLoginCredentials
|
||||
httpCode = 401
|
||||
} else if (userId == AuthUser.usernameLockedStateCode) {
|
||||
message = ErrorMessages.UsernameHasBeenLocked
|
||||
httpCode = 401
|
||||
} else {
|
||||
val jwtPayloadAsJson =
|
||||
"""{
|
||||
"":""
|
||||
}"""
|
||||
|
||||
val jwtClaims: JWTClaimsSet = JWTClaimsSet.parse(jwtPayloadAsJson)
|
||||
val (token: String, secret: String) = generateTokenAndSecret(jwtClaims)
|
||||
|
||||
//Save the token that we have generated
|
||||
if (saveAuthorizationToken(directLoginParameters, token, secret, userId)) {
|
||||
message = token
|
||||
} else {
|
||||
httpCode = 500
|
||||
message = "invalid"
|
||||
}
|
||||
}
|
||||
}
|
||||
(httpCode, message)
|
||||
}
|
||||
|
||||
def getHttpMethod = S.request match {
|
||||
case Full(s) => s.post_? match {
|
||||
case true => "POST"
|
||||
@ -217,7 +227,7 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
|
||||
|
||||
//Check if the request (access token or request token) is valid and return a tuple
|
||||
def validator(requestType : String, httpMethod : String) : (Int, String, Map[String,String]) = {
|
||||
def validator(requestType : String, allParameters: Map[String, String] = getAllParameters) : (Int, String, Map[String,String]) = {
|
||||
|
||||
def validAccessToken(tokenKey: String) = {
|
||||
Tokens.tokens.vend.getTokenByKeyAndType(tokenKey, TokenType.Access) match {
|
||||
@ -229,12 +239,10 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
var message = ""
|
||||
var httpCode: Int = 500
|
||||
|
||||
val parameters = getAllParameters
|
||||
|
||||
//are all the necessary directLogin parameters present?
|
||||
val missingParams = missingDirectLoginParameters(parameters, requestType)
|
||||
val missingParams = missingDirectLoginParameters(allParameters, requestType)
|
||||
//guard maximum length and content of strings (a-z, 0-9 etc.) for parameters
|
||||
val validParams = validDirectLoginParameters(parameters)
|
||||
val validParams = validDirectLoginParameters(allParameters)
|
||||
|
||||
if (missingParams.nonEmpty) {
|
||||
message = ErrorMessages.DirectLoginMissingParameters + missingParams.mkString(", ")
|
||||
@ -246,18 +254,18 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
}
|
||||
else if (
|
||||
requestType == "protectedResource" &&
|
||||
! validAccessToken(parameters.getOrElse("token", ""))
|
||||
! validAccessToken(allParameters.getOrElse("token", ""))
|
||||
) {
|
||||
message = ErrorMessages.DirectLoginInvalidToken + parameters.getOrElse("token", "")
|
||||
message = ErrorMessages.DirectLoginInvalidToken + allParameters.getOrElse("token", "")
|
||||
httpCode = 401
|
||||
}
|
||||
//check if the application is registered and active
|
||||
else if (
|
||||
requestType == "authorizationToken" &&
|
||||
APIUtil.getPropsAsBoolValue("direct_login_consumer_key_mandatory", true) &&
|
||||
! APIUtil.registeredApplication(parameters.getOrElse("consumer_key", ""))) {
|
||||
! APIUtil.registeredApplication(allParameters.getOrElse("consumer_key", ""))) {
|
||||
|
||||
logger.error("application: " + parameters.getOrElse("consumer_key", "") + " not found")
|
||||
logger.error("application: " + allParameters.getOrElse("consumer_key", "") + " not found")
|
||||
message = ErrorMessages.InvalidConsumerKey
|
||||
httpCode = 401
|
||||
}
|
||||
@ -265,7 +273,7 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
httpCode = 200
|
||||
if(message.nonEmpty)
|
||||
logger.error("error message : " + message)
|
||||
(httpCode, message, parameters)
|
||||
(httpCode, message, allParameters)
|
||||
}
|
||||
|
||||
|
||||
@ -377,7 +385,7 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
case Full(r) => r.request.method
|
||||
case _ => "GET"
|
||||
}
|
||||
val (httpCode, message, directLoginParameters) = validator("protectedResource", httpMethod)
|
||||
val (httpCode, message, directLoginParameters) = validator("protectedResource")
|
||||
|
||||
if (httpCode == 400 || httpCode == 401)
|
||||
ParamFailure(message, Empty, Empty, APIFailure(message, httpCode))
|
||||
@ -450,7 +458,7 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
case _ => "GET"
|
||||
}
|
||||
|
||||
val (httpCode, message, directLoginParameters) = validator("protectedResource", httpMethod)
|
||||
val (httpCode, message, directLoginParameters) = validator("protectedResource")
|
||||
|
||||
val consumer: Option[Consumer] = for {
|
||||
tokenId: String <- directLoginParameters.get("token")
|
||||
|
||||
@ -381,17 +381,17 @@ object ApiRole {
|
||||
case class CanAddKycStatus(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canAddKycStatus = CanAddKycStatus()
|
||||
|
||||
case class CanGetKycChecks(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetKycChecks = CanGetKycChecks()
|
||||
case class CanGetAnyKycChecks(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canGetAnyKycChecks = CanGetAnyKycChecks()
|
||||
|
||||
case class CanGetKycDocuments(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetKycDocuments = CanGetKycDocuments()
|
||||
case class CanGetAnyKycDocuments(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canGetAnyKycDocuments = CanGetAnyKycDocuments()
|
||||
|
||||
case class CanGetKycMedia(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetKycMedia = CanGetKycMedia()
|
||||
case class CanGetAnyKycMedia(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canGetAnyKycMedia = CanGetAnyKycMedia()
|
||||
|
||||
case class CanGetKycStatuses(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetKycStatuses = CanGetKycStatuses()
|
||||
case class CanGetAnyKycStatuses(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canGetAnyKycStatuses = CanGetAnyKycStatuses()
|
||||
|
||||
private val roles =
|
||||
canSearchAllTransactions ::
|
||||
@ -512,10 +512,10 @@ object ApiRole {
|
||||
canAddKycDocument ::
|
||||
canAddKycMedia ::
|
||||
canAddKycStatus ::
|
||||
canGetKycChecks ::
|
||||
canGetKycDocuments ::
|
||||
canGetKycMedia ::
|
||||
canGetKycStatuses ::
|
||||
canGetAnyKycChecks ::
|
||||
canGetAnyKycDocuments ::
|
||||
canGetAnyKycMedia ::
|
||||
canGetAnyKycStatuses ::
|
||||
Nil
|
||||
|
||||
lazy val rolesMappedToClasses = roles.map(_.getClass)
|
||||
|
||||
@ -36,9 +36,10 @@ object ErrorMessages {
|
||||
|
||||
// DynamicEntity Exceptions (OBP-09XXX)
|
||||
val DynamicEntityNotFoundByDynamicEntityId = "OBP-09001: DynamicEntity not found. Please specify a valid value for dynamic_entity_id."
|
||||
val DynamicEntityEntityNameAlreadyExists = "OBP-09002: DynamicEntity's entityName already exists. Please specify a different value for entityName."
|
||||
val DynamicEntityEntityNotExists = "OBP-09003: DynamicEntity not exists. Please check entityName."
|
||||
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."
|
||||
val EntityNotFoundByEntityId = "OBP-09005: Entity not found. Please specify a valid value for entityId."
|
||||
|
||||
// General messages (OBP-10XXX)
|
||||
val InvalidJsonFormat = "OBP-10001: Incorrect json format."
|
||||
|
||||
@ -32,7 +32,7 @@ import com.openbankproject.commons.model.enums.DynamicEntityOperation.{CREATE, U
|
||||
import com.openbankproject.commons.model.enums.{AccountAttributeType, CardAttributeType, DynamicEntityOperation, ProductAttributeType}
|
||||
import com.openbankproject.commons.model.{AccountApplication, Bank, Customer, CustomerAddress, Product, ProductCollection, ProductCollectionItem, TaxResidence, UserAuthContext, UserAuthContextUpdate, _}
|
||||
import com.tesobe.CacheKeyFromArguments
|
||||
import net.liftweb.common.{Box, Empty, Full}
|
||||
import net.liftweb.common.{Box, Empty, Full, ParamFailure}
|
||||
import net.liftweb.http.provider.HTTPParam
|
||||
import net.liftweb.json
|
||||
import net.liftweb.json.{JArray, JBool, JDouble, JInt, JObject, JString, JValue}
|
||||
@ -1400,27 +1400,51 @@ object NewStyle {
|
||||
}
|
||||
}
|
||||
|
||||
def createOrUpdateDynamicEntity(dynamicEntity: DynamicEntityT, callContext: Option[CallContext]): Future[Box[DynamicEntityT]] = {
|
||||
private def createDynamicEntity(dynamicEntity: DynamicEntityT, callContext: Option[CallContext]): Future[Box[DynamicEntityT]] = {
|
||||
val existsDynamicEntity = DynamicEntityProvider.connectorMethodProvider.vend.getByEntityName(dynamicEntity.entityName)
|
||||
|
||||
val isEntityNameNotChange = existsDynamicEntity.isEmpty ||
|
||||
existsDynamicEntity.filter(_.dynamicEntityId == dynamicEntity.dynamicEntityId).isDefined
|
||||
if(existsDynamicEntity.isDefined) {
|
||||
val errorMsg = s"$DynamicEntityNameAlreadyExists current entityName is '${dynamicEntity.entityName}'."
|
||||
return Helper.booleanToFuture(errorMsg)(existsDynamicEntity.isEmpty).map(_.asInstanceOf[Box[DynamicEntityT]])
|
||||
}
|
||||
|
||||
isEntityNameNotChange match {
|
||||
case true => Future {
|
||||
DynamicEntityProvider.connectorMethodProvider.vend.createOrUpdate(dynamicEntity)
|
||||
}
|
||||
case false => {
|
||||
val entityNameExists = existsDynamicEntity.isDefined
|
||||
// validate whether entityName is exists
|
||||
val errorMsg = s"$DynamicEntityEntityNameAlreadyExists current entityName is '${dynamicEntity.entityName}'."
|
||||
Helper.booleanToFuture(errorMsg)(!entityNameExists).map { _ =>
|
||||
DynamicEntityProvider.connectorMethodProvider.vend.createOrUpdate(dynamicEntity)
|
||||
}
|
||||
}
|
||||
Future {
|
||||
DynamicEntityProvider.connectorMethodProvider.vend.createOrUpdate(dynamicEntity)
|
||||
}
|
||||
}
|
||||
|
||||
private def updateDynamicEntity(dynamicEntity: DynamicEntityT, dynamicEntityId: String , callContext: Option[CallContext]): Future[Box[DynamicEntityT]] = {
|
||||
val originEntity = DynamicEntityProvider.connectorMethodProvider.vend.getById(dynamicEntityId)
|
||||
// if can't find by id, return 404 error
|
||||
val idNotExistsMsg = s"$DynamicEntityNotFoundByDynamicEntityId dynamicEntityId = ${dynamicEntity.dynamicEntityId.get}."
|
||||
|
||||
if (originEntity.isEmpty) {
|
||||
return Helper.booleanToFuture(idNotExistsMsg, 404)(originEntity.isDefined).map(_.asInstanceOf[Box[DynamicEntityT]])
|
||||
}
|
||||
|
||||
val originEntityName = originEntity.map(_.entityName).orNull
|
||||
// if entityName changed and the new entityName already exists, return error message
|
||||
if(dynamicEntity.entityName != originEntityName) {
|
||||
val existsDynamicEntity = DynamicEntityProvider.connectorMethodProvider.vend.getByEntityName(dynamicEntity.entityName)
|
||||
|
||||
if(existsDynamicEntity.isDefined) {
|
||||
val errorMsg = s"$DynamicEntityNameAlreadyExists current entityName is '${dynamicEntity.entityName}'."
|
||||
return Helper.booleanToFuture(errorMsg)(existsDynamicEntity.isEmpty).map(_.asInstanceOf[Box[DynamicEntityT]])
|
||||
}
|
||||
}
|
||||
|
||||
Future {
|
||||
DynamicEntityProvider.connectorMethodProvider.vend.createOrUpdate(dynamicEntity)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def createOrUpdateDynamicEntity(dynamicEntity: DynamicEntityT, callContext: Option[CallContext]): Future[Box[DynamicEntityT]] =
|
||||
dynamicEntity.dynamicEntityId match {
|
||||
case Some(dynamicEntityId) => updateDynamicEntity(dynamicEntity, dynamicEntityId, callContext)
|
||||
case None => createDynamicEntity(dynamicEntity, callContext)
|
||||
}
|
||||
|
||||
def deleteDynamicEntity(dynamicEntityId: String): Future[Box[Boolean]] = Future {
|
||||
DynamicEntityProvider.connectorMethodProvider.vend.delete(dynamicEntityId)
|
||||
}
|
||||
|
||||
@ -1398,7 +1398,7 @@ trait APIMethods121 {
|
||||
BankAccountNotFound,
|
||||
InvalidJsonFormat,
|
||||
NoViewPermission,
|
||||
"the view " + viewId + "does not allow adding more info",
|
||||
"the view " + viewIdSwagger + "does not allow adding more info",
|
||||
"More Info cannot be added",
|
||||
UnknownError
|
||||
),
|
||||
|
||||
@ -350,7 +350,7 @@ trait APIMethods200 {
|
||||
(Full(u), callContext) <- authorizedAccess(cc)
|
||||
(bank, callContext) <- NewStyle.function.getBank(BankId(defaultBankId), callContext)
|
||||
} yield {
|
||||
val privateViewsUserCanAccessAtOneBank = Views.views.vend.privateViewsUserCanAccess(u).filter(_.bankId == bankId)
|
||||
val privateViewsUserCanAccessAtOneBank = Views.views.vend.privateViewsUserCanAccess(u).filter(_.bankId == BankId(defaultBankId))
|
||||
val privateAaccountsForOneBank = bank.privateAccounts(privateViewsUserCanAccessAtOneBank)
|
||||
val result = corePrivateAccountsAtOneBankResult(CallerContext(corePrivateAccountsAtOneBank), codeContext, u, privateAaccountsForOneBank, privateViewsUserCanAccessAtOneBank)
|
||||
(result, HttpCode.`200`(callContext))
|
||||
@ -453,7 +453,7 @@ trait APIMethods200 {
|
||||
List(UserNotLoggedIn, CustomerNotFoundByCustomerId, UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagKyc, apiTagCustomer),
|
||||
Some(List(canGetKycDocuments))
|
||||
Some(List(canGetAnyKycDocuments))
|
||||
)
|
||||
|
||||
// TODO Add Role
|
||||
@ -463,7 +463,7 @@ trait APIMethods200 {
|
||||
cc => {
|
||||
for {
|
||||
(Full(u), callContext) <- authorizedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement(bankId.value, u.userId, ApiRole.canGetKycDocuments, callContext)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, ApiRole.canGetAnyKycDocuments, callContext)
|
||||
(customer, callContext) <- NewStyle.function.getCustomerByCustomerId(customerId, callContext)
|
||||
(kycDocuments, callContxt) <- NewStyle.function.getKycDocuments(customerId, callContext)
|
||||
} yield {
|
||||
@ -490,14 +490,14 @@ trait APIMethods200 {
|
||||
List(UserNotLoggedIn, CustomerNotFoundByCustomerId, UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagKyc, apiTagCustomer),
|
||||
Some(List(canGetKycMedia)))
|
||||
Some(List(canGetAnyKycMedia)))
|
||||
|
||||
lazy val getKycMedia : OBPEndpoint = {
|
||||
case "customers" :: customerId :: "kyc_media" :: Nil JsonGet _ => {
|
||||
cc => {
|
||||
for {
|
||||
(Full(u), callContext) <- authorizedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement(bankId.value, u.userId, ApiRole.canGetKycMedia, callContext)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, ApiRole.canGetAnyKycMedia, callContext)
|
||||
(customer, callContext) <- NewStyle.function.getCustomerByCustomerId(customerId, callContext)
|
||||
(kycMedias, callContxt) <- NewStyle.function.getKycMedias(customerId, callContext)
|
||||
} yield {
|
||||
@ -523,17 +523,15 @@ trait APIMethods200 {
|
||||
List(UserNotLoggedIn, CustomerNotFoundByCustomerId, UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagKyc, apiTagCustomer),
|
||||
Some(List(canGetKycChecks))
|
||||
Some(List(canGetAnyKycChecks))
|
||||
)
|
||||
|
||||
// TODO Add Role
|
||||
|
||||
lazy val getKycChecks : OBPEndpoint = {
|
||||
case "customers" :: customerId :: "kyc_checks" :: Nil JsonGet _ => {
|
||||
cc => {
|
||||
for {
|
||||
(Full(u), callContext) <- authorizedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement(bankId.value, u.userId, ApiRole.canGetKycChecks, callContext)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, ApiRole.canGetAnyKycChecks, callContext)
|
||||
(customer, callContext) <- NewStyle.function.getCustomerByCustomerId(customerId, callContext)
|
||||
(kycChecks, callContxt) <- NewStyle.function.getKycChecks(customerId, callContext)
|
||||
} yield {
|
||||
@ -558,7 +556,7 @@ trait APIMethods200 {
|
||||
List(UserNotLoggedIn, CustomerNotFoundByCustomerId, UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagKyc, apiTagCustomer),
|
||||
Some(List(canGetKycStatuses))
|
||||
Some(List(canGetAnyKycStatuses))
|
||||
)
|
||||
|
||||
lazy val getKycStatuses : OBPEndpoint = {
|
||||
@ -566,7 +564,7 @@ trait APIMethods200 {
|
||||
cc => {
|
||||
for {
|
||||
(Full(u), callContext) <- authorizedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement(bankId.value, u.userId, ApiRole.canGetKycStatuses, callContext)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, ApiRole.canGetAnyKycStatuses, callContext)
|
||||
(customer, callContext) <- NewStyle.function.getCustomerByCustomerId(customerId, callContext)
|
||||
(kycStatuses, callContxt) <- NewStyle.function.getKycStatuses(customerId, callContext)
|
||||
} yield {
|
||||
|
||||
@ -483,7 +483,7 @@ trait APIMethods300 {
|
||||
moderatedCoreAccountsJsonV300,
|
||||
List(UserNotLoggedIn,UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagAccountFirehose, apiTagAccount, apiTagFirehoseData, apiTagNewStyle),
|
||||
List(apiTagAccount, apiTagAccountFirehose, apiTagFirehoseData, apiTagNewStyle),
|
||||
Some(List(canUseFirehoseAtAnyBank))
|
||||
)
|
||||
|
||||
@ -550,7 +550,7 @@ trait APIMethods300 {
|
||||
transactionsJsonV300,
|
||||
List(UserNotLoggedIn, FirehoseViewsNotAllowedOnThisInstance, UserHasMissingRoles, UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagAccountFirehose, apiTagAccount, apiTagFirehoseData, apiTagNewStyle),
|
||||
List(apiTagTransaction, apiTagAccountFirehose, apiTagTransactionFirehose, apiTagFirehoseData, apiTagNewStyle),
|
||||
Some(List(canUseFirehoseAtAnyBank)))
|
||||
|
||||
lazy val getFirehoseTransactionsForBankAccount : OBPEndpoint = {
|
||||
@ -1776,7 +1776,7 @@ trait APIMethods300 {
|
||||
val msg = s"$InvalidJsonFormat The Json body should be the $CreateEntitlementRequestJSON "
|
||||
x => unboxFullOrFail(x, callContext, msg)
|
||||
}
|
||||
_ <- Future { if (postedData.bank_id == "") Full() else NewStyle.function.getBank(bankId, callContext)}
|
||||
_ <- Future { if (postedData.bank_id == "") Full() else NewStyle.function.getBank(BankId(postedData.bank_id), callContext)}
|
||||
|
||||
_ <- Helper.booleanToFuture(failMsg = IncorrectRoleName + postedData.role_name + ". Possible roles are " + ApiRole.availableRoles.sorted.mkString(", ")) {
|
||||
availableRoles.exists(_ == postedData.role_name)
|
||||
|
||||
@ -470,7 +470,7 @@ trait APIMethods310 {
|
||||
transactionsJsonV300,
|
||||
List(UserNotLoggedIn, FirehoseViewsNotAllowedOnThisInstance, UserHasMissingRoles, UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagAccountFirehose, apiTagAccount, apiTagFirehoseData, apiTagNewStyle),
|
||||
List(apiTagCustomer, apiTagFirehoseData, apiTagNewStyle),
|
||||
Some(List(canUseFirehoseAtAnyBank)))
|
||||
|
||||
lazy val getFirehoseCustomers : OBPEndpoint = {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package code.api.v4_0_0
|
||||
|
||||
import code.api.ChargePolicy
|
||||
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
|
||||
import code.api.util.APIUtil._
|
||||
import code.api.util.ApiRole._
|
||||
@ -13,7 +12,8 @@ import code.api.v1_4_0.JSONFactory1_4_0.{ChallengeAnswerJSON, TransactionRequest
|
||||
import code.api.v2_1_0._
|
||||
import code.api.v3_0_0.JSONFactory300
|
||||
import code.api.v3_1_0.ListResult
|
||||
import code.dynamicEntity.{DynamicEntityCommons, DynamicEntityDefinition}
|
||||
import code.api.{APIFailureNewStyle, ChargePolicy}
|
||||
import code.dynamicEntity.DynamicEntityCommons
|
||||
import code.model.dataAccess.AuthUser
|
||||
import code.model.toUserExtended
|
||||
import code.transactionrequests.TransactionRequests.TransactionChallengeTypes._
|
||||
@ -25,9 +25,8 @@ import com.github.dwickern.macros.NameOf.nameOf
|
||||
import com.openbankproject.commons.model._
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityFieldType
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityOperation._
|
||||
import net.liftweb.common.{Box, Failure, Full}
|
||||
import net.liftweb.common.{Box, Full, ParamFailure}
|
||||
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.util.StringHelpers
|
||||
@ -720,13 +719,6 @@ trait APIMethods400 {
|
||||
}
|
||||
}
|
||||
|
||||
private def validateDynamicEntityJson(metadataJson: JValue) = {
|
||||
val jFields = metadataJson.asInstanceOf[JObject].obj
|
||||
require(jFields.size == 1, "json format for create or update DynamicEntity is not correct, it should have a single key value for structure definition")
|
||||
val JField(_, definition) = jFields.head
|
||||
definition.extract[DynamicEntityDefinition]
|
||||
}
|
||||
|
||||
resourceDocs += ResourceDoc(
|
||||
createDynamicEntity,
|
||||
implementedInApiVersion,
|
||||
@ -763,17 +755,12 @@ trait APIMethods400 {
|
||||
for {
|
||||
(Full(u), callContext) <- authorizedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, canCreateDynamicEntity, callContext)
|
||||
failMsg = s"$InvalidJsonFormat The Json body should be the same structure as request body example."
|
||||
postedData <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
validateDynamicEntityJson(json)
|
||||
|
||||
val JField(entityName, _) = json.asInstanceOf[JObject].obj.head
|
||||
DynamicEntityCommons(entityName, compactRender(json))
|
||||
}
|
||||
|
||||
Full(dynamicEntity) <- NewStyle.function.createOrUpdateDynamicEntity(postedData, callContext)
|
||||
jsonObject = json.asInstanceOf[JObject]
|
||||
dynamicEntity = DynamicEntityCommons(jsonObject, None)
|
||||
Full(result) <- NewStyle.function.createOrUpdateDynamicEntity(dynamicEntity, callContext)
|
||||
} yield {
|
||||
val commonsData: DynamicEntityCommons = dynamicEntity
|
||||
val commonsData: DynamicEntityCommons = result
|
||||
(commonsData.jValue, HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
@ -817,18 +804,11 @@ trait APIMethods400 {
|
||||
(Full(u), callContext) <- authorizedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, canUpdateDynamicEntity, callContext)
|
||||
|
||||
failMsg = s"$InvalidJsonFormat The Json body should be the same structure as request body example."
|
||||
putData <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
validateDynamicEntityJson(json)
|
||||
val JField(name, _) = json.asInstanceOf[JObject].obj.head
|
||||
DynamicEntityCommons(name, compactRender(json), Some(dynamicEntityId))
|
||||
}
|
||||
|
||||
(_, _) <- NewStyle.function.getDynamicEntityById(dynamicEntityId, callContext)
|
||||
|
||||
Full(dynamicEntity) <- NewStyle.function.createOrUpdateDynamicEntity(putData, callContext)
|
||||
jsonObject = json.asInstanceOf[JObject]
|
||||
dynamicEntity = DynamicEntityCommons(jsonObject, Some(dynamicEntityId))
|
||||
Full(result) <- NewStyle.function.createOrUpdateDynamicEntity(dynamicEntity, callContext)
|
||||
} yield {
|
||||
val commonsData: DynamicEntityCommons = dynamicEntity
|
||||
val commonsData: DynamicEntityCommons = result
|
||||
(commonsData.jValue, HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
@ -870,9 +850,11 @@ trait APIMethods400 {
|
||||
}
|
||||
}
|
||||
}
|
||||
private def unboxResult[T](box: Box[T]): T = {
|
||||
if(box.isInstanceOf[Failure]) {
|
||||
throw new Exception(box.asInstanceOf[Failure].msg)
|
||||
|
||||
|
||||
private def unboxResult[T: Manifest](box: Box[T]): T = {
|
||||
if(box.isInstanceOf[ParamFailure[_]]) {
|
||||
fullBoxOrException[T](box)
|
||||
}
|
||||
|
||||
box.openOrThrowException("impossible error")
|
||||
@ -881,9 +863,8 @@ trait APIMethods400 {
|
||||
case EntityName(entityName) :: Nil JsonGet req => { cc =>
|
||||
val listName = StringHelpers.snakify(English.plural(entityName))
|
||||
for {
|
||||
(box: Box[JArray], _) <- NewStyle.function.invokeDynamicConnector(GET_ALL, entityName, None, None, Some(cc))
|
||||
// resultList = APIUtil.unboxFullOrFail(box, Some(cc))
|
||||
resultList = unboxResult(box)
|
||||
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ALL, entityName, None, None, Some(cc))
|
||||
resultList: JArray = unboxResult(box.asInstanceOf[Box[JArray]])
|
||||
} yield {
|
||||
import net.liftweb.json.JsonDSL._
|
||||
val jValue: JObject = listName -> resultList
|
||||
@ -892,36 +873,32 @@ trait APIMethods400 {
|
||||
}
|
||||
case EntityName(entityName, id) JsonGet req => {cc =>
|
||||
for {
|
||||
(box: Box[JObject], _) <- NewStyle.function.invokeDynamicConnector(GET_ONE, entityName, None, Some(id), Some(cc))
|
||||
// entity = APIUtil.unboxFullOrFail(box, Some(cc))
|
||||
entity = unboxResult(box)
|
||||
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ONE, entityName, None, Some(id), Some(cc))
|
||||
entity: JValue = unboxResult(box.asInstanceOf[Box[JValue]])
|
||||
} yield {
|
||||
(entity, HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
}
|
||||
case EntityName(entityName) :: Nil JsonPost json -> _ => {cc =>
|
||||
for {
|
||||
(box: Box[JObject], _) <- NewStyle.function.invokeDynamicConnector(CREATE, entityName, Some(json.asInstanceOf[JObject]), None, Some(cc))
|
||||
// entity = APIUtil.unboxFullOrFail(box, Some(cc))
|
||||
entity = unboxResult(box)
|
||||
(box, _) <- NewStyle.function.invokeDynamicConnector(CREATE, entityName, Some(json.asInstanceOf[JObject]), None, Some(cc))
|
||||
entity: JValue = unboxResult(box.asInstanceOf[Box[JValue]])
|
||||
} yield {
|
||||
(entity, HttpCode.`201`(Some(cc)))
|
||||
}
|
||||
}
|
||||
case EntityName(entityName, id) JsonPut json -> _ => { cc =>
|
||||
for {
|
||||
(box: Box[JObject], _) <- NewStyle.function.invokeDynamicConnector(UPDATE, entityName, Some(json.asInstanceOf[JObject]), Some(id), Some(cc))
|
||||
// entity = APIUtil.unboxFullOrFail(box, Some(cc))
|
||||
entity = unboxResult(box)
|
||||
(box: Box[JValue], _) <- NewStyle.function.invokeDynamicConnector(UPDATE, entityName, Some(json.asInstanceOf[JObject]), Some(id), Some(cc))
|
||||
entity: JValue = unboxResult(box.asInstanceOf[Box[JValue]])
|
||||
} yield {
|
||||
(entity, HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
}
|
||||
case EntityName(entityName, id) JsonDelete req => { cc =>
|
||||
for {
|
||||
(box: Box[JValue], _) <- NewStyle.function.invokeDynamicConnector(DELETE, entityName, None, Some(id), Some(cc))
|
||||
// deleteResult = APIUtil.unboxFullOrFail(box, Some(cc))
|
||||
deleteResult = unboxResult(box)
|
||||
(box, _) <- NewStyle.function.invokeDynamicConnector(DELETE, entityName, None, Some(id), Some(cc))
|
||||
deleteResult: JBool = unboxResult(box.asInstanceOf[Box[JBool]])
|
||||
} yield {
|
||||
(deleteResult, HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
|
||||
@ -220,16 +220,8 @@ case class DynamicEntityInfo(definition: String, entityName: String) {
|
||||
val fieldNameToType: Map[String, Class[_]] = fieldNameToTypeName
|
||||
.mapValues(jsonTypeMap(_))
|
||||
|
||||
val requiredFieldNames: Set[String] = (entity \ "required").asInstanceOf[JArray].arr.map(_.asInstanceOf[JString].s).toSet
|
||||
|
||||
val fields = result.obj.filter(it => fieldNameToType.keySet.contains(it.name))
|
||||
|
||||
def check(v: Boolean, msg: String) = if (!v) throw new RuntimeException(msg)
|
||||
// if there are field type are not match the definitions, there must be bug.
|
||||
fields.foreach(it => check(fieldNameToType(it.name).isInstance(it.value), s"""$InvalidJsonFormat "${it.name}" required type is "${fieldNameToTypeName(it.name)}"."""))
|
||||
// if there are required field not presented, must be some bug.
|
||||
requiredFieldNames.foreach(it => check(fields.exists(_.name == it), s"""$InvalidJsonFormat required field "$it" not presented."""))
|
||||
|
||||
(id, fields.exists(_.name == idName)) match {
|
||||
case (Some(idValue), false) => JObject(JField(idName, JString(idValue)) :: fields)
|
||||
case _ => JObject(fields)
|
||||
|
||||
@ -17,6 +17,7 @@ import net.liftweb.json.JValue
|
||||
import net.liftweb.json.JsonAST.JNothing
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
import scala.language.postfixOps
|
||||
@ -55,16 +56,14 @@ object ConnectorEndpoints extends RestHelper{
|
||||
val value = invokeMethod(methodSymbol, paramValues :_*)
|
||||
|
||||
// convert any to Future[(Box[_], Option[CallContext])] type
|
||||
val futureValue: Future[(Box[_], Option[CallContext])] = toStandaredFuture(value)
|
||||
val futureValue: Future[(Box[_], Option[CallContext])] = toStandardFuture(value)
|
||||
|
||||
for {
|
||||
(Full(data), callContext) <- futureValue.map {it =>
|
||||
APIUtil.fullBoxOrException(it._1 ~> APIFailureNewStyle("", 400, optionCC.map(_.toLight)))
|
||||
it
|
||||
}
|
||||
(boxedData, _) <- futureValue
|
||||
data = APIUtil.fullBoxOrException(boxedData ~> APIFailureNewStyle("", 400, optionCC.map(_.toLight)))
|
||||
inboundAdapterCallContext = nameOf(InboundAdapterCallContext)
|
||||
//convert first letter to small case
|
||||
inboundAdapterCallContextKey = Character.toLowerCase(inboundAdapterCallContext.charAt(0)) + inboundAdapterCallContext.substring(1)
|
||||
inboundAdapterCallContextKey = StringUtils.uncapitalize(inboundAdapterCallContext)
|
||||
inboundAdapterCallContextValue = InboundAdapterCallContext(cc.correlationId)
|
||||
} yield {
|
||||
// NOTE: if any filed type is BigDecimal, it is can't be serialized by lift json
|
||||
@ -184,7 +183,8 @@ object ConnectorEndpoints extends RestHelper{
|
||||
mirrorObj.reflectMethod(method).apply(args :_*)
|
||||
}
|
||||
|
||||
def toStandaredFuture(obj: Any): Future[(Box[_], Option[CallContext])] = {
|
||||
@tailrec
|
||||
def toStandardFuture(obj: Any): Future[(Box[_], Option[CallContext])] = {
|
||||
obj match {
|
||||
case null => Future((Empty, None))
|
||||
case future: Future[_] => {
|
||||
@ -202,10 +202,10 @@ object ConnectorEndpoints extends RestHelper{
|
||||
}
|
||||
case Full(data) => {
|
||||
data match {
|
||||
case _: (_, _) => toStandaredFuture(Future(obj))
|
||||
case _: (_, _) => toStandardFuture(Future(obj))
|
||||
case _ => {
|
||||
val fillCallContext = obj.asInstanceOf[Box[_]].map((_, None))
|
||||
toStandaredFuture(Future(fillCallContext))
|
||||
toStandardFuture(Future(fillCallContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import code.cards.MappedPhysicalCard
|
||||
import code.context.{UserAuthContextProvider, UserAuthContextUpdateProvider}
|
||||
import code.customer._
|
||||
import code.customeraddress.CustomerAddressX
|
||||
import code.dynamicEntity.DynamicEntityProvider
|
||||
import code.dynamicEntity.{DynamicEntityProvider, DynamicEntityT}
|
||||
import code.fx.{FXRate, MappedFXRate, fx}
|
||||
import code.kycchecks.KycChecks
|
||||
import code.kycdocuments.KycDocuments
|
||||
@ -60,8 +60,7 @@ import com.openbankproject.commons.model.{AccountApplication, AccountAttribute,
|
||||
import com.tesobe.CacheKeyFromArguments
|
||||
import com.tesobe.model.UpdateBankAccount
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.json
|
||||
import net.liftweb.json.{JArray, JBool, JDouble, JInt, JObject, JString, JValue}
|
||||
import net.liftweb.json.{JArray, JBool, JObject, JValue}
|
||||
import net.liftweb.mapper.{By, _}
|
||||
import net.liftweb.util.Helpers.{tryo, _}
|
||||
import net.liftweb.util.Mailer
|
||||
@ -196,10 +195,8 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
)
|
||||
} yield true
|
||||
}
|
||||
val errorMessage = sendingResult map {
|
||||
case f: Failure => f.msg
|
||||
case Empty => ""
|
||||
}
|
||||
val errorMessage = sendingResult.filter(_.isInstanceOf[Failure]).map(_.asInstanceOf[Failure].msg)
|
||||
|
||||
if(sendingResult.forall(_ == Full(true))) hashedPassword else (Failure(errorMessage.toSet.mkString(" <- ")), callContext)
|
||||
case None => // All versions which precede v4.0.0 i.e. to keep backward compatibility
|
||||
createHashedPassword("123")
|
||||
@ -2801,50 +2798,30 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
val dynamicEntityBox = DynamicEntityProvider.connectorMethodProvider.vend.getByEntityName(entityName)
|
||||
// do validate, any validate process fail will return immediately
|
||||
if(dynamicEntityBox.isEmpty) {
|
||||
return Helper.booleanToFuture(s"$DynamicEntityEntityNotExists entity's name is '$entityName'")(dynamicEntityBox.isDefined)
|
||||
return Helper.booleanToFuture(s"$DynamicEntityNotExists entity's name is '$entityName'")(false)
|
||||
.map(it => (it.map(_.asInstanceOf[JValue]), callContext))
|
||||
} else if(entityId.isDefined && !persistedEntities.contains(entityId.get -> entityName)) {
|
||||
val id = entityId.get
|
||||
val idName = StringUtils.uncapitalize(entityName) + "Id"
|
||||
}
|
||||
|
||||
return Helper.booleanToFuture(s"$InvalidUrl not exists $entityName of $idName = $id")(false)
|
||||
.map(it => (it.map(_.asInstanceOf[JValue]), callContext))
|
||||
} else if(requestBody.isDefined) {
|
||||
val dynamicEntity = dynamicEntityBox.openOrThrowException(DynamicEntityEntityNotExists)
|
||||
|
||||
val jsonTypeMap = Map[String, Class[_]](
|
||||
("boolean", classOf[JBool]),
|
||||
("string", classOf[JString]),
|
||||
("array", classOf[JArray]),
|
||||
("integer", classOf[JInt]),
|
||||
("number", classOf[JDouble]),
|
||||
)
|
||||
val definitionJson = json.parse(dynamicEntity.metadataJson).asInstanceOf[JObject]
|
||||
val entity = (definitionJson \ entityName).asInstanceOf[JObject]
|
||||
val requiredFieldNames: Set[String] = (entity \ "required").asInstanceOf[JArray].arr.map(_.asInstanceOf[JString].s).toSet
|
||||
|
||||
val fieldNameToTypeName: Map[String, String] = (entity \ "properties")
|
||||
.asInstanceOf[JObject]
|
||||
.obj
|
||||
.map(field => (field.name, (field.value \ "type").asInstanceOf[JString].s))
|
||||
.toMap
|
||||
|
||||
val fieldNameToType: Map[String, Class[_]] = fieldNameToTypeName
|
||||
.mapValues(jsonTypeMap(_))
|
||||
val bodyJson = requestBody.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument please supply the requestBody."))
|
||||
val fields = bodyJson.obj.filter(it => fieldNameToType.keySet.contains(it.name))
|
||||
|
||||
// if there are field type are not match the definitions, there must be bug.
|
||||
val invalidTypes = fields.filterNot(it => fieldNameToType(it.name).isInstance(it.value))
|
||||
val invalidTypeNames = invalidTypes.map(_.name).mkString("[", ",", "]")
|
||||
val missingRequiredFields = requiredFieldNames.filterNot(it => fields.exists(_.name == it))
|
||||
val missingFieldNames = missingRequiredFields.mkString("[", ",", "]")
|
||||
|
||||
if(invalidTypes.nonEmpty) {
|
||||
return Helper.booleanToFuture(s"$InvalidJsonFormat these field type not correct: $invalidTypeNames")(invalidTypes.isEmpty)
|
||||
if(operation == CREATE || operation == UPDATE) {
|
||||
if(requestBody.isEmpty) {
|
||||
return Helper.booleanToFuture(s"$InvalidJsonFormat requestBody is required for $operation operation.")(false)
|
||||
.map(it => (it.map(_.asInstanceOf[JValue]), callContext))
|
||||
} else if(missingRequiredFields.nonEmpty) {
|
||||
return Helper.booleanToFuture(s"$InvalidJsonFormat some required fields are missing: $missingFieldNames")(missingRequiredFields.isEmpty)
|
||||
}
|
||||
val dynamicEntity: DynamicEntityT = dynamicEntityBox.openOrThrowException(DynamicEntityNotExists)
|
||||
val validateResult: Either[String, Unit] = dynamicEntity.validateEntityJson(requestBody.get)
|
||||
if(validateResult.isLeft) {
|
||||
return Helper.booleanToFuture(s"$InvalidJsonFormat details: ${validateResult.left.get}")(validateResult.isRight)
|
||||
.map(it => (it.map(_.asInstanceOf[JValue]), callContext))
|
||||
}
|
||||
}
|
||||
if(operation == GET_ONE || operation == UPDATE || operation == DELETE) {
|
||||
if (entityId.isEmpty) {
|
||||
return Helper.booleanToFuture(s"$InvalidJsonFormat entityId is required for $operation operation.")(entityId.isEmpty || StringUtils.isBlank(entityId.get))
|
||||
.map(it => (it.map(_.asInstanceOf[JValue]), callContext))
|
||||
}
|
||||
if (!persistedEntities.contains(entityId.get -> entityName)) {
|
||||
val id = entityId.get
|
||||
return Helper.booleanToFuture(s"$EntityNotFoundByEntityId please check: entityId = $id", 404)(false)
|
||||
.map(it => (it.map(_.asInstanceOf[JValue]), callContext))
|
||||
}
|
||||
}
|
||||
@ -2860,7 +2837,8 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
}
|
||||
case CREATE | UPDATE => {
|
||||
val body = requestBody.getOrElse(throw new RuntimeException(s"$DynamicEntityMissArgument please supply the requestBody."))
|
||||
val persistedEntity = MockerConnector.persist(entityName, body, entityId)
|
||||
val id = if(operation == CREATE) None else entityId
|
||||
val persistedEntity = MockerConnector.persist(entityName, body, id)
|
||||
Full(persistedEntity)
|
||||
}
|
||||
case DELETE => {
|
||||
|
||||
@ -9242,6 +9242,63 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
|
||||
}
|
||||
|
||||
//---------------- dynamic end ---------------------please don't modify this line
|
||||
|
||||
private val availableOperation = DynamicEntityOperation.values.map(it => s""""$it"""").mkString("[", ", ", "]")
|
||||
|
||||
messageDocs += dynamicEntityProcessDoc
|
||||
def dynamicEntityProcessDoc = MessageDoc(
|
||||
process = "obp.dynamicEntityProcess",
|
||||
messageFormat = messageFormat,
|
||||
description = s"operate committed dynamic entity data, the available value of 'operation' can be: ${availableOperation}",
|
||||
outboundTopic = None,
|
||||
inboundTopic = None,
|
||||
exampleOutboundMessage = (
|
||||
OutBoundDynamicEntityProcessDoc(outboundAdapterCallContext = OutboundAdapterCallContext(correlationId=correlationIdExample.value,
|
||||
sessionId=Some(sessionIdExample.value),
|
||||
consumerId=Some(consumerIdExample.value),
|
||||
generalContext=Some(List( BasicGeneralContext(key=keyExample.value,
|
||||
value=valueExample.value))),
|
||||
outboundAdapterAuthInfo=Some( OutboundAdapterAuthInfo(userId=Some(userIdExample.value),
|
||||
username=Some(usernameExample.value),
|
||||
linkedCustomers=Some(List( BasicLinkedCustomer(customerId=customerIdExample.value,
|
||||
customerNumber=customerNumberExample.value,
|
||||
legalName=legalNameExample.value))),
|
||||
userAuthContext=Some(List( BasicUserAuthContext(key=keyExample.value,
|
||||
value=valueExample.value))),
|
||||
authViews=Some(List( AuthView(view= ViewBasic(id=viewIdExample.value,
|
||||
name=viewNameExample.value,
|
||||
description=viewDescriptionExample.value),
|
||||
account= AccountBasic(id=accountIdExample.value,
|
||||
accountRoutings=List( AccountRouting(scheme=accountRoutingSchemeExample.value,
|
||||
address=accountRoutingAddressExample.value)),
|
||||
customerOwners=List( InternalBasicCustomer(bankId=bankIdExample.value,
|
||||
customerId=customerIdExample.value,
|
||||
customerNumber=customerNumberExample.value,
|
||||
legalName=legalNameExample.value,
|
||||
dateOfBirth=parseDate(dateOfBirthExample.value).getOrElse(sys.error("dateOfBirthExample.value is not validate date format.")))),
|
||||
userOwners=List( InternalBasicUser(userId=userIdExample.value,
|
||||
emailAddress=emailExample.value,
|
||||
name=usernameExample.value))))))))),
|
||||
operation = DynamicEntityOperation.UPDATE,
|
||||
entityName = "FooBar",
|
||||
requestBody = Some(FooBar(name = "James Brown", number = 1234567890)),
|
||||
entityId = Some("foobar-id-value"))
|
||||
),
|
||||
exampleInboundMessage = (
|
||||
InBoundDynamicEntityProcessDoc(inboundAdapterCallContext= InboundAdapterCallContext(correlationId=correlationIdExample.value,
|
||||
sessionId=Some(sessionIdExample.value),
|
||||
generalContext=Some(List( BasicGeneralContext(key=keyExample.value,
|
||||
value=valueExample.value)))),
|
||||
status= Status(errorCode=statusErrorCodeExample.value,
|
||||
backendMessages=List( InboundStatusMessage(source=sourceExample.value,
|
||||
status=inboundStatusMessageStatusExample.value,
|
||||
errorCode=inboundStatusMessageErrorCodeExample.value,
|
||||
text=inboundStatusMessageTextExample.value))),
|
||||
data=FooBar(name = "James Brown", number = 1234567890, fooBarId = Some("foobar-id-value")))
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("- Core", 1))
|
||||
)
|
||||
|
||||
override def dynamicEntityProcess(operation: DynamicEntityOperation,
|
||||
entityName: String,
|
||||
requestBody: Option[JObject],
|
||||
@ -9365,7 +9422,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
|
||||
val future: Future[Box[Box[T]]] = extractBody(entity) map { msg =>
|
||||
tryo {
|
||||
val errorMsg = parse(msg).extract[ErrorMessage]
|
||||
val failure: Box[T] = ParamFailure(errorMsg.message, "")
|
||||
val failure: Box[T] = ParamFailure("", APIFailureNewStyle(errorMsg.message, status.intValue()))
|
||||
failure
|
||||
} ~> APIFailureNewStyle(msg, status.intValue())
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
package code.dynamicEntity
|
||||
|
||||
import code.api.util.ErrorMessages.InvalidJsonFormat
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityFieldType
|
||||
import com.openbankproject.commons.model.{Converter, JsonFieldReName}
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.json.JsonAST.JString
|
||||
import net.liftweb.json.JsonDSL._
|
||||
import net.liftweb.json.{JField, JObject, JsonAST}
|
||||
import net.liftweb.json.{JArray, JBool, JDouble, JField, JInt, JNothing, JNull, JObject, JString, compactRender, parse}
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
object DynamicEntityProvider extends SimpleInjector {
|
||||
@ -19,13 +19,11 @@ trait DynamicEntityT {
|
||||
def dynamicEntityId: Option[String]
|
||||
def entityName: String
|
||||
def metadataJson: String
|
||||
}
|
||||
|
||||
case class DynamicEntityCommons(entityName: String,
|
||||
metadataJson: String,
|
||||
dynamicEntityId: Option[String] = None,
|
||||
) extends DynamicEntityT with JsonFieldReName {
|
||||
private val definition: JObject = net.liftweb.json.parse(metadataJson).asInstanceOf[JObject]
|
||||
|
||||
//---------util methods
|
||||
|
||||
private lazy val definition: JObject = parse(metadataJson).asInstanceOf[JObject]
|
||||
//convert metadataJson to JValue, so the final json field metadataJson have no escaped " to \", have good readable
|
||||
lazy val jValue = dynamicEntityId match {
|
||||
case Some(id) => {
|
||||
@ -35,18 +33,165 @@ case class DynamicEntityCommons(entityName: String,
|
||||
}
|
||||
case None => definition
|
||||
}
|
||||
|
||||
/**
|
||||
* validate the commit json whether fulfil DynamicEntity schema
|
||||
* @param entityJson commit json object to add new instance of given dynamic entity
|
||||
* @return return Success[Unit], or return Left[String] error message
|
||||
*/
|
||||
def validateEntityJson(entityJson: JObject): Either[String, Unit] = {
|
||||
val required: List[String] = (definition \ entityName \ "required").asInstanceOf[JArray].arr.map(_.asInstanceOf[JString].s)
|
||||
|
||||
val missingProperties = required diff entityJson.obj.map(_.name)
|
||||
|
||||
if(missingProperties.nonEmpty) {
|
||||
return Left(s"$InvalidJsonFormat The 'required' field's not be fulfilled, missing properties: ${missingProperties.mkString(", ")}")
|
||||
}
|
||||
|
||||
val invalidPropertyMsg = (definition \ entityName \ "properties").asInstanceOf[JObject].obj
|
||||
.map(it => {
|
||||
val JField(propertyName, propertyDef: JObject) = it
|
||||
val propertyTypeName = (propertyDef \ "type").asInstanceOf[JString].s
|
||||
(propertyName, propertyTypeName)
|
||||
})
|
||||
.map(it => {
|
||||
val (propertyName, propertyType) = it
|
||||
val propertyValue = entityJson \ propertyName
|
||||
propertyType match {
|
||||
case _ if propertyValue == JNothing || propertyValue == JNull => "" // required properties already checked.
|
||||
case "string" if !propertyValue.isInstanceOf[JString] => s"$InvalidJsonFormat The type of '$propertyName' should be string"
|
||||
case "number" if !propertyValue.isInstanceOf[JDouble] => s"$InvalidJsonFormat The type of '$propertyName' should be number"
|
||||
case "integer" if !propertyValue.isInstanceOf[JInt] => s"$InvalidJsonFormat The type of '$propertyName' should be integer"
|
||||
case "boolean" if !propertyValue.isInstanceOf[JBool] => s"$InvalidJsonFormat The type of '$propertyName' should be boolean"
|
||||
case "array" if !propertyValue.isInstanceOf[JArray] => s"$InvalidJsonFormat The type of '$propertyName' should be array"
|
||||
case "object" if !propertyValue.isInstanceOf[JObject] => s"$InvalidJsonFormat The type of '$propertyName' should be object"
|
||||
case _ => ""
|
||||
}
|
||||
})
|
||||
.filter(_.nonEmpty)
|
||||
.mkString("; ")
|
||||
if(invalidPropertyMsg.nonEmpty) {
|
||||
Left(invalidPropertyMsg)
|
||||
} else {
|
||||
Right(Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class DynamicEntityCommons(entityName: String,
|
||||
metadataJson: String,
|
||||
dynamicEntityId: Option[String] = None
|
||||
) extends DynamicEntityT with JsonFieldReName
|
||||
|
||||
object DynamicEntityCommons extends Converter[DynamicEntityT, DynamicEntityCommons] {
|
||||
|
||||
/**
|
||||
* create DynamicEntityCommons object, and do validation
|
||||
*
|
||||
* @param jsonObject the follow schema json:
|
||||
* {{{
|
||||
* {
|
||||
* "FooBar": {
|
||||
* "required": [
|
||||
* "name"
|
||||
* ],
|
||||
* "properties": {
|
||||
* "name": {
|
||||
* "type": "string",
|
||||
* "example": "James Brown"
|
||||
* },
|
||||
* "number": {
|
||||
* "type": "integer",
|
||||
* "example": "698761728934"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }}}
|
||||
* @param dynamicEntityId
|
||||
* @return object of DynamicEntityCommons
|
||||
*/
|
||||
def apply(jsonObject: JObject, dynamicEntityId: Option[String]): DynamicEntityCommons = {
|
||||
|
||||
def checkFormat(requirement: Boolean, message: String) = {
|
||||
if (!requirement) throw new IllegalArgumentException(message)
|
||||
}
|
||||
|
||||
val fields = jsonObject.obj
|
||||
|
||||
// validate whether json is object and have a single field, currently support one entity definition
|
||||
checkFormat(fields.nonEmpty, s"$InvalidJsonFormat The Json root object should have a single entity, but current have none.")
|
||||
checkFormat(fields.size == 1, s"$InvalidJsonFormat The Json root object should have a single entity, but current entityNames: ${fields.map(_.name).mkString(", ")}")
|
||||
|
||||
val JField(entityName, metadataJson) = fields.head
|
||||
|
||||
// validate entityName corresponding value is json object
|
||||
val metadataStr = compactRender(metadataJson)
|
||||
checkFormat(metadataJson.isInstanceOf[JObject], s"$InvalidJsonFormat The $entityName should have an object value, but current value is: $metadataStr")
|
||||
|
||||
val required = metadataJson \ "required"
|
||||
|
||||
// validate 'required' field exists and is a json array[string]
|
||||
checkFormat(required != JNothing , s"$InvalidJsonFormat There must be 'required' field in $entityName, and type is json array[string]")
|
||||
checkFormat(required.isInstanceOf[JArray] && required.asInstanceOf[JArray].arr.forall(_.isInstanceOf[JString]), s"$InvalidJsonFormat The 'required' field's type of $entityName should be array[string]")
|
||||
|
||||
val properties = metadataJson \ "properties"
|
||||
|
||||
// validate 'properties' field exists and is json object
|
||||
checkFormat(properties != JNothing , s"$InvalidJsonFormat There must be 'required' field in $entityName, and type is array[string]")
|
||||
checkFormat(properties.isInstanceOf[JObject], s"$InvalidJsonFormat The 'properties' field's type of $entityName should be json object")
|
||||
|
||||
val propertiesObj = properties.asInstanceOf[JObject]
|
||||
|
||||
val requiredFields = required.asInstanceOf[JArray].arr.map(_.asInstanceOf[JString].s)
|
||||
|
||||
val allFields = propertiesObj.obj
|
||||
|
||||
val missingRequiredFields = requiredFields diff allFields.map(_.name)
|
||||
|
||||
checkFormat(missingRequiredFields.isEmpty , s"$InvalidJsonFormat missing properties: ${missingRequiredFields.mkString(", ")}")
|
||||
|
||||
// validate there is no required field missing in properties
|
||||
val notFoundRequiredField = requiredFields.diff(allFields.map(_.name))
|
||||
checkFormat(metadataJson.isInstanceOf[JObject], s"$InvalidJsonFormat In the $entityName, all 'required' fields should be present, these are missing: ${notFoundRequiredField.mkString(", ")}")
|
||||
|
||||
// validate all properties have a type and example
|
||||
allFields.foreach(field => {
|
||||
val JField(fieldName, value) = field
|
||||
checkFormat(value.isInstanceOf[JObject], s"$InvalidJsonFormat The property of $fieldName's type should be json object")
|
||||
|
||||
// 'type' exists and value should be one of allowed type
|
||||
val fieldType = value \ "type"
|
||||
checkFormat(fieldType.isInstanceOf[JString] && fieldType.asInstanceOf[JString].s.nonEmpty, s"$InvalidJsonFormat The property of $fieldName's 'type' field should be exists and type is json string")
|
||||
checkFormat(allowedFieldType.contains(fieldType.asInstanceOf[JString].s), s"$InvalidJsonFormat The property of $fieldName's 'type' field should be json string and value should be one of: ${allowedFieldType.mkString(", ")}")
|
||||
|
||||
// example is exists
|
||||
val fieldExample = value \ "example"
|
||||
checkFormat(fieldExample != JNothing, s"$InvalidJsonFormat The property of $fieldName's 'example' field should be exists")
|
||||
})
|
||||
|
||||
DynamicEntityCommons(entityName, compactRender(jsonObject), dynamicEntityId)
|
||||
}
|
||||
|
||||
private val allowedFieldType: Set[String] = Set(
|
||||
"string",
|
||||
"number",
|
||||
"integer",
|
||||
"boolean",
|
||||
"array",
|
||||
// "object",
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* an example schema of DynamicEntity, this is as request body example usage
|
||||
* example case classes, as an example schema of DynamicEntity, for request body example usage
|
||||
* @param FooBar
|
||||
*/
|
||||
case class DynamicEntityFooBar(FooBar: DynamicEntityDefinition, dynamicEntityId: Option[String] = None)
|
||||
case class DynamicEntityDefinition(required: List[String],properties: DynamicEntityFullBarFields)
|
||||
case class DynamicEntityFullBarFields(name: DynamicEntityTypeExample, number: DynamicEntityTypeExample)
|
||||
case class DynamicEntityTypeExample(`type`: DynamicEntityFieldType, example: String)
|
||||
|
||||
object DynamicEntityCommons extends Converter[DynamicEntityT, DynamicEntityCommons]
|
||||
//-------------------example case class end
|
||||
|
||||
|
||||
trait DynamicEntityProvider {
|
||||
|
||||
@ -26,6 +26,7 @@ TESOBE (http://www.tesobe.com/)
|
||||
*/
|
||||
package code.snippet
|
||||
|
||||
import code.api.DirectLogin
|
||||
import code.api.util.{APIUtil, ErrorMessages}
|
||||
import code.consumer.Consumers
|
||||
import code.model._
|
||||
@ -37,6 +38,8 @@ import net.liftweb.util.Helpers._
|
||||
import net.liftweb.util.{FieldError, Helpers}
|
||||
import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
class ConsumerRegistration extends MdcLoggable {
|
||||
|
||||
private object nameVar extends RequestVar("")
|
||||
@ -89,6 +92,29 @@ class ConsumerRegistration extends MdcLoggable {
|
||||
val urlOAuthEndpoint = APIUtil.getPropsValue("hostname", "") + "/oauth/initiate"
|
||||
val urlDirectLoginEndpoint = APIUtil.getPropsValue("hostname", "") + "/my/logins/direct"
|
||||
val createDirectLoginToken = getWebUiPropsValue("webui_create_directlogin_token_url", "")
|
||||
val dummyCustomersInfo = getWebUiPropsValue("webui_dummy_customer_logins", "")
|
||||
val isShowDummyCustomerTokens = getWebUiPropsValue("webui_show_dummy_customer_tokens", "false").toBoolean
|
||||
val dummyUsersTokens: String = (isShowDummyCustomerTokens, dummyCustomersInfo) match {
|
||||
case(true, v) if v.nonEmpty => {
|
||||
val regex = """\{"user_name"\s*:\s*"(.+?)".+?"password"\s*:\s*"(.+?)".+?\}""".r
|
||||
val matcher = regex.pattern.matcher(v)
|
||||
val tokens = ListBuffer[String]()
|
||||
while(matcher.find()) {
|
||||
val userName = matcher.group(1)
|
||||
val password = matcher.group(2)
|
||||
val consumerKey = consumer.key.get
|
||||
val (code, token) = DirectLogin.createToken(Map(("username", userName), ("password", password), ("consumer_key", consumerKey)))
|
||||
val authHeader = code match {
|
||||
case 200 => s"""$userName auth header --> Authorization: DirectLogin token="$token""""
|
||||
case _ => s"""$userName - -> username or password is invalid, generate token fail"""
|
||||
}
|
||||
tokens += authHeader
|
||||
}
|
||||
tokens.mkString(""" | """)
|
||||
}
|
||||
case _ => ""
|
||||
}
|
||||
|
||||
val registerConsumerSuccessMessageWebpage = getWebUiPropsValue(
|
||||
"webui_register_consumer_success_message_webpage",
|
||||
"Thanks for registering your consumer with the Open Bank Project API! Here is your developer information. Please save it in a secure location.")
|
||||
@ -111,7 +137,13 @@ class ConsumerRegistration extends MdcLoggable {
|
||||
"#directlogin-endpoint a [href]" #> urlDirectLoginEndpoint &
|
||||
"#post-consumer-registration-more-info-link a *" #> registrationMoreInfoText &
|
||||
"#post-consumer-registration-more-info-link a [href]" #> registrationMoreInfoUrl &
|
||||
"#register-consumer-input" #> ""
|
||||
"#register-consumer-input" #> "" & {
|
||||
if(dummyUsersTokens.isEmpty) {
|
||||
".preparedTokens" #> dummyUsersTokens
|
||||
} else {
|
||||
"#preparedTokens *" #> dummyUsersTokens
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def showRegistrationResults(result : Consumer) = {
|
||||
|
||||
@ -134,10 +134,14 @@ Berlin 13359, Germany
|
||||
<div class="col-xs-12 col-sm-4">Direct Login Endpoint</div>
|
||||
<div class="col-xs-12 col-sm-8"><span id="directlogin-endpoint"><a href="#">endpoint</a></span></div>
|
||||
</div>
|
||||
<div class="row preparedTokens">
|
||||
<div class="col-xs-12 col-sm-4">Prepared user tokens</div>
|
||||
<div class="col-xs-12 col-sm-8" id="preparedTokens"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-4">Direct Login Documentation</div>
|
||||
<div class="col-xs-12 col-sm-8"><a href="https://github.com/OpenBankProject/OBP-API/wiki/Direct-Login">How to use Direct Login</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-4"></div>
|
||||
|
||||
@ -159,6 +159,13 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
val response = makePostRequest(request, write(rightEntity))
|
||||
Then("We should get a 201")
|
||||
response.code should equal(201)
|
||||
|
||||
{ // create duplicated entityName FooBar, cause 400
|
||||
val response400 = makePostRequest(request, write(rightEntity))
|
||||
response400.code should equal(400)
|
||||
response400.body.extract[ErrorMessage].message should startWith (DynamicEntityNameAlreadyExists)
|
||||
}
|
||||
|
||||
val responseJson = response.body
|
||||
val dynamicEntityId = (responseJson \ "dynamicEntityId").asInstanceOf[JString].s
|
||||
val dynamicEntityIdJObject: JObject = "dynamicEntityId" -> dynamicEntityId
|
||||
@ -193,11 +200,11 @@ class DynamicEntityTest extends V400ServerSetup {
|
||||
|
||||
{
|
||||
// update a not exists DynamicEntity
|
||||
val request400 = (v4_0_0_Request / "management" / "dynamic_entities" / "not-exists-id" ).PUT <@(user1)
|
||||
val response400 = makePutRequest(request400, compactRender(updateRequest))
|
||||
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 400")
|
||||
response400.code should equal(400)
|
||||
response400.body.extract[ErrorMessage].message should startWith (DynamicEntityNotFoundByDynamicEntityId)
|
||||
response404.code should equal(404)
|
||||
response404.body.extract[ErrorMessage].message should startWith (DynamicEntityNotFoundByDynamicEntityId)
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@ -1165,4 +1165,13 @@ case class OutBoundDynamicEntityProcess (outboundAdapterCallContext: OutboundAda
|
||||
entityName: String,
|
||||
requestBody: Option[JObject],
|
||||
entityId: Option[String]) extends TopicTrait
|
||||
case class InBoundDynamicEntityProcess (inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: JValue) extends InBoundTrait[JValue]
|
||||
case class InBoundDynamicEntityProcess (inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: JValue) extends InBoundTrait[JValue]
|
||||
|
||||
// because swagger generate not support JValue type, so here supply too xxxDoc TO generate correct request and response body example
|
||||
case class FooBar(name: String, number: Int, fooBarId: Option[String] = None)
|
||||
case class OutBoundDynamicEntityProcessDoc (outboundAdapterCallContext: OutboundAdapterCallContext,
|
||||
operation: DynamicEntityOperation,
|
||||
entityName: String,
|
||||
requestBody: Option[FooBar],
|
||||
entityId: Option[String]) extends TopicTrait
|
||||
case class InBoundDynamicEntityProcessDoc (inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: FooBar) extends InBoundTrait[FooBar]
|
||||
@ -1,6 +1,6 @@
|
||||
package com.openbankproject.commons.util
|
||||
|
||||
import net.liftweb.common.{Box, Empty, Full}
|
||||
import net.liftweb.common.{Box, Empty, Failure, Full}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.annotation.tailrec
|
||||
@ -10,6 +10,7 @@ import scala.language.postfixOps
|
||||
import scala.reflect.runtime.universe._
|
||||
import scala.reflect.runtime.{universe => ru}
|
||||
import scala.util.Success
|
||||
import net.liftweb.json.JValue
|
||||
|
||||
object ReflectUtils {
|
||||
private[this] val mirror: ru.Mirror = ru.runtimeMirror(getClass().getClassLoader)
|
||||
@ -506,9 +507,18 @@ object ReflectUtils {
|
||||
def toValueObject(t: Any): Any = {
|
||||
t match {
|
||||
case null => null
|
||||
case v: JValue => v
|
||||
case Some(v) => toValueObject(v)
|
||||
case Full(v) => toValueObject(v)
|
||||
case None|Empty => null
|
||||
case v: Failure => v
|
||||
case Left(v) => Left(toValueObject(v))
|
||||
case v: Right[_, _] => v.map(toValueObject)
|
||||
case v: Success[_]=> v.map(toValueObject)
|
||||
case scala.util.Failure(v) => v
|
||||
case it: Iterable[_] => it.map(toValueObject)
|
||||
case array: Array[_] => array.map(toValueObject)
|
||||
case v if(getType(v).typeSymbol.asClass.isCaseClass) => v
|
||||
case v if getType(v).typeSymbol.asClass.isCaseClass => v
|
||||
case other => {
|
||||
val mirrorObj = mirror.reflect(other)
|
||||
mirrorObj.symbol.info.decls
|
||||
|
||||
Loading…
Reference in New Issue
Block a user