feature/User Attributes; Add endpoint createCurrentUserAttribute v4.0.0

This commit is contained in:
Marko Milić 2022-01-21 11:45:16 +01:00
parent 3fea748c4e
commit 2b07808438
12 changed files with 219 additions and 17 deletions

View File

@ -4130,6 +4130,22 @@ object SwaggerDefinitionsJSON {
value = customerAttributeValueExample.value
)
val userAttributeResponseJson = UserAttributeResponseJsonV400 (
user_attribute_id = userAttributeIdExample.value,
name = userAttributeNameExample.value,
`type` = userAttributeTypeExample.value,
value = userAttributeValueExample.value
)
val userAttributesResponseJson = UserAttributesResponseJson (
user_attributes = List(userAttributeResponseJson)
)
val userAttributeJsonV400 = UserAttributeJsonV400(
name = userAttributeNameExample.value,
`type` = userAttributeTypeExample.value,
value = userAttributeValueExample.value
)
val transactionAttributeResponseJson = TransactionAttributeResponseJson(
transaction_attribute_id = transactionAttributeIdExample.value,
name = transactionAttributeNameExample.value,

View File

@ -111,14 +111,23 @@ object ExampleValue {
lazy val customerAttributeIdExample = ConnectorField("7uy8a7e4-6d02-40e3-a129-0b2bf89de8uh", s"Customer attribute id")
glossaryItems += makeGlossaryItem("Customer.attributeId", customerAttributeIdExample)
lazy val userAttributeIdExample = ConnectorField("7uy8a7e4-6d02-40e3-a129-0b2bf89de8uh", s"User attribute id")
glossaryItems += makeGlossaryItem("Customer.attributeId", userAttributeIdExample)
lazy val customerAttributeNameExample = ConnectorField("SPECIAL_TAX_NUMBER", s"Customer attribute name")
glossaryItems += makeGlossaryItem("Customer.attributeName", customerAttributeNameExample)
lazy val userAttributeNameExample = ConnectorField("ROLE", s"User attribute name")
glossaryItems += makeGlossaryItem("User.attributeName", userAttributeNameExample)
lazy val templateAttributeNameExample = ConnectorField("SPECIAL_TAX_NUMBER", s"Attribute name")
glossaryItems += makeGlossaryItem("Template.attributeName", templateAttributeNameExample)
lazy val customerAttributeTypeExample = ConnectorField("STRING", s"Customer attribute type.")
glossaryItems += makeGlossaryItem("Customer.attributeType", customerAttributeTypeExample)
lazy val userAttributeTypeExample = ConnectorField("STRING", s"User attribute type.")
glossaryItems += makeGlossaryItem("User.attributeType", userAttributeTypeExample)
lazy val templateAttributeTypeExample = ConnectorField("STRING", s"Attribute type.")
glossaryItems += makeGlossaryItem("Template.attributeType", templateAttributeTypeExample)
@ -128,6 +137,9 @@ object ExampleValue {
lazy val customerAttributeValueExample = ConnectorField("123456789", s"Customer attribute value.")
glossaryItems += makeGlossaryItem("Customer.attributeValue", customerAttributeValueExample)
lazy val userAttributeValueExample = ConnectorField("123456789", s"Uset attribute value.")
glossaryItems += makeGlossaryItem("User.attributeValue", userAttributeValueExample)
lazy val labelExample = ConnectorField("My Account", s"A lable that describes the Account")
lazy val legalNameExample = ConnectorField("Eveline Tripman", s"The legal name of the Customer.")

View File

@ -36,7 +36,7 @@ import code.apicollection.{ApiCollectionTrait, MappedApiCollectionsProvider}
import code.model.dataAccess.{AuthUser, BankAccountRouting}
import code.standingorders.StandingOrderTrait
import code.usercustomerlinks.UserCustomerLink
import code.users.{UserAgreement, UserAgreementProvider, UserInvitation, UserInvitationProvider, Users}
import code.users.{UserAgreement, UserAgreementProvider, UserAttribute, UserInvitation, UserInvitationProvider, Users}
import code.util.Helper
import com.openbankproject.commons.util.{ApiVersion, JsonUtils}
import code.views.Views
@ -1596,6 +1596,33 @@ object NewStyle {
}
}
def getUserAttributes(userId: String, callContext: Option[CallContext]): OBPReturnType[List[UserAttribute]] = {
Connector.connector.vend.getUserAttributes(
userId: String, callContext: Option[CallContext]
) map {
i => (connectorEmptyResponse(i._1, callContext), i._2)
}
}
def createOrUpdateUserAttribute(
userId: String,
userAttributeId: Option[String],
name: String,
attributeType: UserAttributeType.Value,
value: String,
callContext: Option[CallContext]
): OBPReturnType[UserAttribute] = {
Connector.connector.vend.createOrUpdateUserAttribute(
userId: String,
userAttributeId: Option[String],
name: String,
attributeType: UserAttributeType.Value,
value: String,
callContext: Option[CallContext]
) map {
i => (connectorEmptyResponse(i._1, callContext), i._2)
}
}
def createAccountAttributes(bankId: BankId,
accountId: AccountId,
productCode: ProductCode,

View File

@ -8105,6 +8105,59 @@ trait APIMethods400 {
}
}
}
staticResourceDocs += ResourceDoc(
createCurrentUserAttribute,
implementedInApiVersion,
nameOf(createCurrentUserAttribute),
"POST",
"/my/user/attribute",
"Create User Attribute for current user",
s""" Create User Attribute forcurrent user
|
|The type field must be one of "STRING", "INTEGER", "DOUBLE" or DATE_WITH_DAY"
|
|${authenticationRequiredMessage(true)}
|
|""",
userAttributeJsonV400,
userAttributeResponseJson,
List(
$UserNotLoggedIn,
InvalidJsonFormat,
UnknownError
),
List(apiTagUser, apiTagNewStyle),
Some(List()))
lazy val createCurrentUserAttribute : OBPEndpoint = {
case "my" :: "user" :: "attribute" :: Nil JsonPost json -> _=> {
cc =>
val failMsg = s"$InvalidJsonFormat The Json body should be the $TransactionAttributeJsonV400 "
for {
(attributes, callContext) <- NewStyle.function.getUserAttributes(cc.userId, cc.callContext)
postedData <- NewStyle.function.tryons(failMsg, 400, callContext) {
json.extract[TransactionAttributeJsonV400]
}
failMsg = s"$InvalidJsonFormat The `Type` field can only accept the following field: " +
s"${TransactionAttributeType.DOUBLE}(12.1234), ${TransactionAttributeType.STRING}(TAX_NUMBER), ${TransactionAttributeType.INTEGER} (123)and ${TransactionAttributeType.DATE_WITH_DAY}(2012-04-23)"
userAttributeType <- NewStyle.function.tryons(failMsg, 400, callContext) {
UserAttributeType.withName(postedData.`type`)
}
(userAttribute, callContext) <- NewStyle.function.createOrUpdateUserAttribute(
cc.userId,
None,
postedData.name,
userAttributeType,
postedData.value,
callContext
)
} yield {
(JSONFactory400.createUserAttributeJson(userAttribute), HttpCode.`201`(callContext))
}
}
}
staticResourceDocs += ResourceDoc(

View File

@ -56,7 +56,7 @@ import code.ratelimiting.RateLimiting
import code.standingorders.StandingOrderTrait
import code.transactionrequests.TransactionRequests.TransactionChallengeTypes
import code.userlocks.UserLocks
import code.users.{UserAgreement, UserInvitation}
import code.users.{UserAgreement, UserAttribute, UserInvitation}
import com.openbankproject.commons.model.{DirectDebitTrait, ProductFeeTrait, _}
import net.liftweb.common.{Box, Full}
import net.liftweb.json.JValue
@ -425,6 +425,22 @@ case class RefundJson(
reason_code: String
)
case class UserAttributeJsonV400(
name: String,
`type`: String,
value: String,
)
case class UserAttributeResponseJsonV400(
user_attribute_id: String,
name: String,
`type`: String,
value: String
)
case class UserAttributesResponseJson(
user_attributes: List[UserAttributeResponseJsonV400]
)
case class CustomerAttributeJsonV400(
name: String,
`type`: String,
@ -1281,6 +1297,15 @@ object JSONFactory400 {
value = transactionAttribute.value
)
}
def createUserAttributeJson(userAttribute: UserAttribute) : UserAttributeResponseJsonV400 = {
UserAttributeResponseJsonV400(
user_attribute_id = userAttribute.userAttributeId,
name = userAttribute.name,
`type` = userAttribute.attributeType.toString,
value = userAttribute.value
)
}
def createTransactionAttributesJson(transactionAttributes: List[TransactionAttribute]) : TransactionAttributesResponseJson = {
TransactionAttributesResponseJson (transactionAttributes.map( transactionAttribute => TransactionAttributeResponseJson(

View File

@ -2,6 +2,7 @@ package code.bankconnectors
import java.util.Date
import java.util.UUID.randomUUID
import _root_.akka.http.scaladsl.model.HttpMethod
import code.accountholders.{AccountHolders, MapperAccountHolders}
import code.api.attributedefinition.AttributeDefinition
@ -33,7 +34,7 @@ import code.standingorders.StandingOrderTrait
import code.transactionrequests.TransactionRequests
import code.transactionrequests.TransactionRequests.TransactionRequestTypes._
import code.transactionrequests.TransactionRequests._
import code.users.Users
import code.users.{UserAttribute, Users}
import code.util.Helper._
import code.views.Views
import com.openbankproject.commons.ExecutionContext.Implicits.global
@ -2123,6 +2124,18 @@ trait Connector extends MdcLoggable {
(Failure(setUnimplementedError), callContext)
}
def getUserAttributes(userId: String, callContext: Option[CallContext]): OBPReturnType[Box[List[UserAttribute]]] =
Future{(Failure(setUnimplementedError), callContext)}
def createOrUpdateUserAttribute(
userId: String,
userAttributeId: Option[String],
name: String,
attributeType: UserAttributeType.Value,
value: String,
callContext: Option[CallContext]
): OBPReturnType[Box[UserAttribute]] = Future{(Failure(setUnimplementedError), callContext)}
def createOrUpdateTransactionAttribute(
bankId: BankId,
transactionId: TransactionId,

View File

@ -2,6 +2,7 @@ package code.bankconnectors
import java.util.Date
import java.util.UUID.randomUUID
import _root_.akka.http.scaladsl.model.HttpMethod
import code.DynamicData.DynamicDataProvider
import code.DynamicEndpoint.{DynamicEndpointProvider, DynamicEndpointT}
@ -66,7 +67,7 @@ import code.transactionattribute.TransactionAttributeX
import code.transactionrequests.TransactionRequests.TransactionRequestTypes._
import code.transactionrequests.TransactionRequests.{TransactionChallengeTypes, TransactionRequestTypes}
import code.transactionrequests._
import code.users.Users
import code.users.{UserAttribute, UserAttributeProvider, Users}
import code.util.Helper
import code.util.Helper.{MdcLoggable, _}
import code.views.Views
@ -3721,6 +3722,28 @@ object LocalMappedConnector extends Connector with MdcLoggable {
}
}
override def getUserAttributes(userId: String, callContext: Option[CallContext]): OBPReturnType[Box[List[UserAttribute]]] = {
UserAttributeProvider.userAttributeProvider.vend.getUserAttributesByUser(userId: String) map {(_, callContext)}
}
override def createOrUpdateUserAttribute(
userId: String,
userAttributeId: Option[String],
name: String,
attributeType: UserAttributeType.Value,
value: String,
callContext: Option[CallContext]
): OBPReturnType[Box[UserAttribute]] = {
UserAttributeProvider.userAttributeProvider.vend.createOrUpdateUserAttribute(
userId: String,
userAttributeId: Option[String],
name: String,
attributeType: UserAttributeType.Value,
value: String
) map {
(_, callContext)
}
}
override def createOrUpdateTransactionAttribute(
bankId: BankId,
transactionId: TransactionId,

View File

@ -16,8 +16,8 @@ object RemotedataUserAttribute extends ObpActorInit with UserAttributeProvider {
val cc = RemotedataUserAttributeCaseClasses
override def getAccountAttributesByUser(userId: String): Future[Box[List[UserAttribute]]] =
(actor ? cc.getAccountAttributesByUser(userId)).mapTo[Box[List[UserAttribute]]]
override def getUserAttributesByUser(userId: String): Future[Box[List[UserAttribute]]] =
(actor ? cc.getUserAttributesByUser(userId)).mapTo[Box[List[UserAttribute]]]
override def createOrUpdateUserAttribute(userId: String,
userAttributeId: Option[String],

View File

@ -15,9 +15,9 @@ class RemotedataUserAttributeActor extends Actor with ObpActorHelper with MdcLog
def receive: PartialFunction[Any, Unit] = {
case cc.getAccountAttributesByUser(userId: String) =>
logger.debug(s"getAccountAttributesByUser(${userId})")
mapper.getAccountAttributesByUser(userId) pipeTo sender
case cc.getUserAttributesByUser(userId: String) =>
logger.debug(s"getUserAttributesByUser(${userId})")
mapper.getUserAttributesByUser(userId) pipeTo sender
case cc.createOrUpdateUserAttribute(userId: String, userAttributeId: Option[String], name: String, attributeType: UserAttributeType.Value, value: String) =>
logger.debug(s"createOrUpdateUserAttribute(${userId}, ${userAttributeId}, ${name}, ${attributeType}, ${value})")

View File

@ -1,34 +1,66 @@
package code.users
import code.util.MappedUUID
import com.openbankproject.commons.model.enums.{AccountAttributeType, UserAttributeType}
import com.openbankproject.commons.model.{AccountAttribute, UserAttributeTrait}
import net.liftweb.common.Box
import com.openbankproject.commons.ExecutionContext.Implicits.global
import com.openbankproject.commons.model.UserAttributeTrait
import com.openbankproject.commons.model.enums.UserAttributeType
import net.liftweb.common.{Box, Empty, Full}
import net.liftweb.mapper._
import net.liftweb.util.Helpers.tryo
import scala.collection.immutable.List
import scala.concurrent.Future
object MappedUserAttributeProvider extends UserAttributeProvider {
override def getAccountAttributesByUser(userId: String): Future[Box[List[UserAttribute]]] = ???
override def getUserAttributesByUser(userId: String): Future[Box[List[UserAttribute]]] = Future {
tryo(
UserAttribute.findAll(By(UserAttribute.UserId, userId))
)
}
override def createOrUpdateUserAttribute(userId: String,
userAttributeId: Option[String],
name: String,
attributeType: UserAttributeType.Value,
value: String): Future[Box[UserAttribute]] = ???
value: String): Future[Box[UserAttribute]] = {
userAttributeId match {
case Some(id) => Future {
UserAttribute.find(By(UserAttribute.UserAttributeId, id)) match {
case Full(attribute) => tryo {
attribute
.Name(name)
.Type(attributeType.toString)
.Value(value)
.saveMe()
}
case _ => Empty
}
}
case None => Future {
Full {
UserAttribute.create
.Name(name)
.Type(attributeType.toString())
.Value(value)
.saveMe()
}
}
}
}
}
class UserAttribute extends UserAttributeTrait with LongKeyedMapper[UserAttribute] with IdPK {
override def getSingleton = UserAttribute
object UserAttributeId extends MappedUUID(this)
object UserId extends MappedUUID(this)
object Name extends MappedString(this, 50)
object Type extends MappedString(this, 50)
object Value extends MappedString(this, 255)
override def userAttributeId: String = UserAttributeId.get
override def userId: String = UserId.get
override def name: String = Name.get
override def attributeType: UserAttributeType.Value = UserAttributeType.withName(Type.get)
override def value: String = Value.get

View File

@ -37,7 +37,7 @@ trait UserAttributeProvider {
private val logger = Logger(classOf[UserAttributeProvider])
def getAccountAttributesByUser(userId: String): Future[Box[List[UserAttribute]]]
def getUserAttributesByUser(userId: String): Future[Box[List[UserAttribute]]]
def createOrUpdateUserAttribute(userId: String,
userAttributeId: Option[String],
name: String,
@ -47,7 +47,7 @@ trait UserAttributeProvider {
}
class RemotedataUserAttributeCaseClasses {
case class getAccountAttributesByUser(userId: String)
case class getUserAttributesByUser(userId: String)
case class createOrUpdateUserAttribute(userId: String,
userAttributeId: Option[String],
name: String,

View File

@ -97,6 +97,7 @@ trait AccountApplication {
trait UserAttributeTrait {
def userAttributeId: String
def userId: String
def name: String
def attributeType: UserAttributeType.Value
def value: String