mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 17:56:46 +00:00
feature/add_dynamic_endpoints_with_swagger: process delete DynamicEndpoint and related roles.
This commit is contained in:
parent
ec51a12053
commit
9ac32017c0
@ -50,6 +50,7 @@ object ErrorMessages {
|
||||
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)
|
||||
|
||||
@ -3,8 +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.DynamicEndpointT
|
||||
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}
|
||||
@ -14,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
|
||||
@ -1920,6 +1922,10 @@ object NewStyle {
|
||||
}
|
||||
}
|
||||
}
|
||||
def dynamicEndpointProcess(url: String, jValue: JValue, method: HttpMethod, params: Map[String, List[String]],
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[JValue]] = {
|
||||
RestConnector_vMar2019.dynamicEndpointProcess(url, jValue, method, params, callContext)
|
||||
}
|
||||
|
||||
|
||||
def createDirectDebit(bankId : String,
|
||||
@ -2007,11 +2013,10 @@ object NewStyle {
|
||||
}
|
||||
|
||||
def getDynamicEndpoint(dynamicEndpointId: String, callContext: Option[CallContext]): OBPReturnType[DynamicEndpointT] = {
|
||||
Connector.connector.vend.getDynamicEndpoint(
|
||||
dynamicEndpointId,
|
||||
callContext
|
||||
) map {
|
||||
i => (connectorEmptyResponse(i._1, callContext), i._2)
|
||||
val dynamicEndpointBox: Box[DynamicEndpointT] = DynamicEndpointProvider.connectorMethodProvider.vend.get(dynamicEndpointId)
|
||||
val dynamicEndpoint = unboxFullOrFail(dynamicEndpointBox, callContext, DynamicEndpointNotFoundByDynamicEndpointId, 404)
|
||||
Future{
|
||||
(dynamicEndpoint, callContext)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2020,6 +2025,31 @@ object NewStyle {
|
||||
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 {
|
||||
|
||||
@ -2838,6 +2838,7 @@ trait APIMethods400 {
|
||||
List(
|
||||
$UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
DynamicEndpointNotFoundByDynamicEndpointId,
|
||||
InvalidJsonFormat,
|
||||
UnknownError
|
||||
),
|
||||
@ -2900,6 +2901,39 @@ trait APIMethods400 {
|
||||
}
|
||||
}
|
||||
|
||||
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.`200`(cc.callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lazy val dynamicEndpoint: OBPEndpoint = {
|
||||
case DynamicReq(url, json, method, params, role) => { cc =>
|
||||
@ -2907,10 +2941,14 @@ trait APIMethods400 {
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, role, callContext)
|
||||
|
||||
jValue = JObject(JField("name", "hello"))
|
||||
(box, _) <- NewStyle.function.dynamicEndpointProcess(url, json, method, params, 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)))
|
||||
}
|
||||
|
||||
(jValue, HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,8 +5,9 @@ import java.nio.charset.Charset
|
||||
import java.util
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.regex.Pattern
|
||||
import java.util.{Date, Objects, Optional, stream}
|
||||
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}
|
||||
@ -17,6 +18,7 @@ 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
|
||||
@ -50,6 +52,20 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
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
|
||||
*/
|
||||
@ -59,27 +75,28 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
* @param r HttpRequest
|
||||
* @return
|
||||
*/
|
||||
def unapply(r: Req): Option[(String, JValue, HttpMethod, Map[String, List[String]], ApiRole)] = {
|
||||
def unapply(r: Req): Option[(String, JValue, AkkaHttpMethod, Map[String, List[String]], ApiRole)] = {
|
||||
val partPath = r.path.partPath
|
||||
if (!testResponse_?(r) || partPath.headOption != Option(urlPrefix))
|
||||
None
|
||||
else {
|
||||
val method = HttpMethod.valueOf(r.requestType.method)
|
||||
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)] = dynamicEndpointInfos.stream()
|
||||
.map[Option[(DynamicEndpointInfo, ResourceDoc)]](_.findDynamicEndpoint(method, url))
|
||||
.map[Option[(DynamicEndpointInfo, ResourceDoc)]](_.findDynamicEndpoint(httpMethod, url))
|
||||
.filter(_.isDefined)
|
||||
.findFirst()
|
||||
.map(_.get)
|
||||
|
||||
foundDynamicEndpoint.asScala
|
||||
.flatMap[(String, JValue, HttpMethod, Map[String, List[String]], ApiRole)] { it =>
|
||||
.flatMap[(String, JValue, AkkaHttpMethod, Map[String, List[String]], ApiRole)] { it =>
|
||||
val (dynamicEndpointInfo, doc) = it
|
||||
val Some(role::_) = doc.roles
|
||||
body(r).toOption
|
||||
.orElse(Some(JNothing))
|
||||
.map(t => (dynamicEndpointInfo.targetUrl(url), t, method, r.params, role))
|
||||
.map(t => (dynamicEndpointInfo.targetUrl(url), t, akkaHttpMethod, r.params, role))
|
||||
}
|
||||
|
||||
}
|
||||
@ -363,7 +380,7 @@ case class DynamicEndpointInfo(id: String, docsToUrl: mutable.Iterable[(Resource
|
||||
|
||||
def existsEndpoint(newMethod: HttpMethod, newUrl: String): Boolean = findDynamicEndpoint(newMethod, newUrl).isDefined
|
||||
|
||||
def targetUrl(url: String): String = s"""${serverUrl.getOrElse("/")}/$url""".replaceAll("/{2,}", "/")
|
||||
def targetUrl(url: String): String = s"""${serverUrl.get}$url"""
|
||||
|
||||
/**
|
||||
* check whether two url is the same:
|
||||
|
||||
@ -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,45 @@ 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
|
||||
}
|
||||
|
||||
//TODO params process
|
||||
def dynamicEndpointProcess(url: String, jValue: JValue, method: HttpMethod, params: Map[String, List[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 targetUrl = urlInMethodRouting.getOrElse(url)
|
||||
val jsonToSend = if(jValue == JNothing) "" else compactRender(jValue)
|
||||
val request = prepareHttpRequest(url, 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
|
||||
|
||||
@ -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)
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user