feature/OBPv510 added deleteUserAttribute endpoint

This commit is contained in:
hongwei 2023-05-23 17:21:04 +08:00
parent f77e9f41a5
commit 1a4eefaf9d
8 changed files with 121 additions and 13 deletions

View File

@ -406,6 +406,9 @@ object ApiRole {
case class CanCreateUserAttribute (requiresBankId: Boolean = false) extends ApiRole
lazy val canCreateUserAttribute = CanCreateUserAttribute()
case class CanDeleteUserAttribute (requiresBankId: Boolean = false) extends ApiRole
lazy val canDeleteUserAttribute = CanDeleteUserAttribute()
case class CanReadUserLockedStatus(requiresBankId: Boolean = false) extends ApiRole
lazy val canReadUserLockedStatus = CanReadUserLockedStatus()

View File

@ -15,6 +15,7 @@ import code.api.v3_1_0.ConsentJsonV310
import code.api.v3_1_0.JSONFactory310.createBadLoginStatusJson
import code.api.v4_0_0.{JSONFactory400, PostApiCollectionJson400, UserAttributeJsonV400}
import code.atmattribute.AtmAttribute
import code.bankconnectors.Connector
import code.consent.Consents
import code.loginattempts.LoginAttempt
import code.metrics.APIMetrics
@ -132,6 +133,7 @@ trait APIMethods510 {
userAttributeResponseJson,
List(
$UserNotLoggedIn,
UserHasMissingRoles,
InvalidJsonFormat,
UnknownError
),
@ -144,7 +146,7 @@ trait APIMethods510 {
cc =>
val failMsg = s"$InvalidJsonFormat The Json body should be the $UserAttributeJsonV400 "
for {
(attributes, callContext) <- NewStyle.function.getUserAttributes(userId, cc.callContext)
(user, callContext) <- NewStyle.function.getUserByUserId(userId, cc.callContext)
postedData <- NewStyle.function.tryons(failMsg, 400, callContext) {
json.extract[UserAttributeJsonV400]
}
@ -154,7 +156,7 @@ trait APIMethods510 {
UserAttributeType.withName(postedData.`type`)
}
(userAttribute, callContext) <- NewStyle.function.createOrUpdateUserAttribute(
userId,
user.userId,
None,
postedData.name,
userAttributeType,
@ -165,7 +167,48 @@ trait APIMethods510 {
}
}
}
resourceDocs += ResourceDoc(
deleteUserAttribute,
implementedInApiVersion,
nameOf(deleteUserAttribute),
"DELETE",
"/users/USER_ID/attributes/USER_ATTRIBUTE_ID",
"Delete User Attribute",
s"""Delete the User Attribute specified by ENTITLEMENT_REQUEST_ID for a user specified by USER_ID
|
|${authenticationRequiredMessage(true)}
|""".stripMargin,
EmptyBody,
EmptyBody,
List(
UserNotLoggedIn,
UserHasMissingRoles,
InvalidConnectorResponse,
UnknownError
),
List(apiTagUser, apiTagNewStyle),
Some(List(canDeleteUserAttribute)))
lazy val deleteUserAttribute: OBPEndpoint = {
case "users" :: userId :: "attributes" :: userAttributeId :: Nil JsonDelete _ => {
cc =>
for {
(_, callContext) <- authenticatedAccess(cc)
(_, callContext) <- NewStyle.function.getUserByUserId(userId, callContext)
(deleted,callContext) <- Connector.connector.vend.deleteUserAttribute(
userAttributeId: String,
callContext: Option[CallContext]
) map {
i => (connectorEmptyResponse (i._1, callContext), i._2)
}
} yield {
(Full(deleted), HttpCode.`204`(callContext))
}
}
}
staticResourceDocs += ResourceDoc(
customViewNamesCheck,
implementedInApiVersion,

View File

@ -2272,6 +2272,11 @@ trait Connector extends MdcLoggable {
callContext: Option[CallContext]
): OBPReturnType[Box[UserAttribute]] = Future{(Failure(setUnimplementedError), callContext)}
def deleteUserAttribute(
userAttributeId: String,
callContext: Option[CallContext]
): OBPReturnType[Box[Boolean]] = Future{(Failure(setUnimplementedError), callContext)}
def createOrUpdateTransactionAttribute(
bankId: BankId,
transactionId: TransactionId,

View File

@ -4080,6 +4080,9 @@ object LocalMappedConnector extends Connector with MdcLoggable {
override def getUserAttributesByUsers(userIds: List[String], callContext: Option[CallContext]): OBPReturnType[Box[List[UserAttribute]]] = {
UserAttributeProvider.userAttributeProvider.vend.getUserAttributesByUsers(userIds) map {(_, callContext)}
}
override def deleteUserAttribute(userAttributeId: String, callContext: Option[CallContext]): OBPReturnType[Box[Boolean]] = {
UserAttributeProvider.userAttributeProvider.vend.deleteUserAttribute(userAttributeId) map {(_, callContext)}
}
override def createOrUpdateUserAttribute(
userId: String,
userAttributeId: Option[String],

View File

@ -22,6 +22,9 @@ object RemotedataUserAttribute extends ObpActorInit with UserAttributeProvider {
override def getUserAttributesByUsers(userIds: List[String]): Future[Box[List[UserAttribute]]] =
(actor ? cc.getUserAttributesByUsers(userIds)).mapTo[Box[List[UserAttribute]]]
override def deleteUserAttribute(userAttributeId: String): Future[Box[Boolean]] =
(actor ? cc.deleteUserAttribute(userAttributeId: String)).mapTo[Box[Boolean]]
override def createOrUpdateUserAttribute(userId: String,
userAttributeId: Option[String],
name: String,

View File

@ -1,7 +1,8 @@
package code.users
import java.util.Date
import code.api.util.ErrorMessages
import java.util.Date
import code.util.MappedUUID
import com.openbankproject.commons.ExecutionContext.Implicits.global
import com.openbankproject.commons.model.UserAttributeTrait
@ -25,6 +26,16 @@ object MappedUserAttributeProvider extends UserAttributeProvider {
UserAttribute.findAll(ByList(UserAttribute.UserId, userIds))
)
}
override def deleteUserAttribute(userAttributeId: String): Future[Box[Boolean]] = {
Future {
UserAttribute.find(By(UserAttribute.UserAttributeId, userAttributeId)) match {
case Full(t) => Full(t.delete_!)
case Empty => Empty ?~! ErrorMessages.UserAttributeNotFound
case _ => Full(false)
}
}
}
override def createOrUpdateUserAttribute(userId: String,
userAttributeId: Option[String],

View File

@ -39,6 +39,7 @@ trait UserAttributeProvider {
def getUserAttributesByUser(userId: String): Future[Box[List[UserAttribute]]]
def getUserAttributesByUsers(userIds: List[String]): Future[Box[List[UserAttribute]]]
def deleteUserAttribute(userAttributeId: String): Future[Box[Boolean]]
def createOrUpdateUserAttribute(userId: String,
userAttributeId: Option[String],
name: String,
@ -49,6 +50,7 @@ trait UserAttributeProvider {
class RemotedataUserAttributeCaseClasses {
case class getUserAttributesByUser(userId: String)
case class deleteUserAttribute(userAttributeId: String)
case class getUserAttributesByUsers(userIds: List[String])
case class createOrUpdateUserAttribute(userId: String,
userAttributeId: Option[String],

View File

@ -11,7 +11,6 @@ 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.common.Box
import net.liftweb.json.Serialization.write
import org.scalatest.Tag
@ -26,6 +25,7 @@ class UserAttributesTest extends V510ServerSetup {
*/
object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString)
object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.createUserAttribute))
object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.deleteUserAttribute))
lazy val bankId = testBankId1.value
@ -36,24 +36,32 @@ class UserAttributesTest extends V510ServerSetup {
feature(s"test $ApiEndpoint1 version $VersionOfApi - Unauthorized access") {
scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) {
feature(s"test $ApiEndpoint1 $ApiEndpoint2 version $VersionOfApi - Unauthorized access") {
scenario(s"We will call the end $ApiEndpoint1 without user credentials", ApiEndpoint1, VersionOfApi) {
When("We make a request v5.1.0")
val request510 = (v5_1_0_Request / "users" /"testuserId"/ "attributes").POST
val request510 = (v5_1_0_Request / "users" /"testUserId"/ "attributes").POST
val response510 = makePostRequest(request510, write(postUserAttributeJsonV510))
Then("We should get a 401")
response510.code should equal(401)
response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
}
scenario(s"We will call the $ApiEndpoint2 without user credentials", ApiEndpoint1, VersionOfApi) {
When("We make a request v5.1.0")
val request510 = (v5_1_0_Request / "users" /"testUserId"/ "attributes"/"testUserAttributeId").POST
val response510 = makeDeleteRequest(request510)
Then("We should get a 401")
response510.code should equal(401)
response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
}
}
feature(s"test $ApiEndpoint1 version $VersionOfApi - authorized access") {
scenario("We will call the endpoint with user credentials, but missing role", ApiEndpoint1, VersionOfApi) {
feature(s"test $ApiEndpoint1 $ApiEndpoint2 version $VersionOfApi - authorized access") {
scenario(s"We will call the $ApiEndpoint1 $ApiEndpoint2 with user credentials", ApiEndpoint1, ApiEndpoint2,VersionOfApi) {
When("We make a request v5.1.0, we need to prepare the roles and users")
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanGetAnyUser.toString)
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanCreateUserAttribute.toString)
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanDeleteUserAttribute.toString)
val requestGetUsers = (v5_1_0_Request / "users").GET <@ (user1)
val responseGetUsers = makeGetRequest(requestGetUsers)
@ -65,10 +73,23 @@ class UserAttributesTest extends V510ServerSetup {
Then("We should get a 201")
response510.code should equal(201)
val jsonResponse = response510.body.extract[UserAttributeResponseJsonV400]
jsonResponse.name should be(batteryLevel)
val userAttributeId = jsonResponse.user_attribute_id
val requestDeleteUserAttribute = (v5_1_0_Request / "users"/ userId/"attributes"/userAttributeId).DELETE <@ (user1)
val responseDeleteUserAttribute = makeDeleteRequest(requestDeleteUserAttribute)
Then("We should get a 204")
responseDeleteUserAttribute.code should equal(204)
Then("We delete it again, we should get the can not find error")
val responseDeleteUserAttributeAgain = makeDeleteRequest(requestDeleteUserAttribute)
Then("We should get a 400")
responseDeleteUserAttributeAgain.code should equal(400)
responseDeleteUserAttributeAgain.body.extract[ErrorMessage].message contains (UserAttributeNotFound) shouldBe( true)
}
scenario("We will call the endpoint with user credentials, but missing roles", ApiEndpoint1, VersionOfApi) {
scenario(s"We will call the $ApiEndpoint1 with user credentials, but missing roles", ApiEndpoint1, ApiEndpoint2, VersionOfApi) {
When("We make a request v5.1.0, we need to prepare the roles and users")
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanGetAnyUser.toString)
@ -84,6 +105,23 @@ class UserAttributesTest extends V510ServerSetup {
response510.body.extract[ErrorMessage].message contains (UserHasMissingRoles) shouldBe (true)
response510.body.extract[ErrorMessage].message contains (ApiRole.CanCreateUserAttribute.toString()) shouldBe (true)
}
scenario(s"We will call the $ApiEndpoint2 with user credentials, but missing roles", ApiEndpoint1, ApiEndpoint2, VersionOfApi) {
When("We make a request v5.1.0, we need to prepare the roles and users")
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanGetAnyUser.toString)
val requestGetUsers = (v5_1_0_Request / "users").GET <@ (user1)
val responseGetUsers = makeGetRequest(requestGetUsers)
val userIds = responseGetUsers.body.extract[UsersJsonV400].users.map(_.user_id)
val userId = userIds(scala.util.Random.nextInt(userIds.size))
val request510 = (v5_1_0_Request / "users" / userId / "attributes" / "attributeId").DELETE <@ (user1)
val response510 = makeDeleteRequest(request510)
Then("We should get a 403")
response510.code should equal(403)
response510.body.extract[ErrorMessage].message contains (UserHasMissingRoles) shouldBe (true)
response510.body.extract[ErrorMessage].message contains (ApiRole.CanDeleteUserAttribute.toString()) shouldBe (true)
}
}
}