From 81d57c91bbbfb1e32956693e0628a5712316078d Mon Sep 17 00:00:00 2001 From: Marko Milic Date: Fri, 3 Jun 2016 09:46:02 +0200 Subject: [PATCH] Add API call to Link User to Customer #35 - without script to migrate the data and modification of getCustomer in MappedCustomerProvider --- src/main/scala/bootstrap/liftweb/Boot.scala | 4 +- .../scala/code/api/v2_0_0/APIMethods200.scala | 40 ++++++++++- .../code/api/v2_0_0/JSONFactory2.0.0.scala | 17 +++++ .../scala/code/api/v2_0_0/OBPAPI2_0_0.scala | 3 +- src/main/scala/code/model/User.scala | 3 + .../MappedUserCustomerLinkProvider.scala | 66 +++++++++++++++++++ .../usercustomerlinks/UserCustomerLink.scala | 29 ++++++++ src/main/scala/code/users/LiftUsers.scala | 4 ++ src/main/scala/code/users/Users.scala | 2 + 9 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 src/main/scala/code/usercustomerlinks/MappedUserCustomerLinkProvider.scala create mode 100644 src/main/scala/code/usercustomerlinks/UserCustomerLink.scala diff --git a/src/main/scala/bootstrap/liftweb/Boot.scala b/src/main/scala/bootstrap/liftweb/Boot.scala index f030311d4..7cef09f5e 100755 --- a/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/src/main/scala/bootstrap/liftweb/Boot.scala @@ -63,6 +63,7 @@ import code.transaction_types.MappedTransactionType import code.snippet.{OAuthAuthorisation, OAuthWorkedThanks} import code.tesobe.CashAccountAPI import code.transactionrequests.MappedTransactionRequest +import code.usercustomerlinks.MappedUserCustomerLink import net.liftweb.common._ import net.liftweb.http._ import net.liftweb.mapper._ @@ -417,5 +418,6 @@ object ToSchemify { MappedKycStatus, MappedSocialMedia, MappedTransactionType, - MappedMeeting) + MappedMeeting, + MappedUserCustomerLink) } diff --git a/src/main/scala/code/api/v2_0_0/APIMethods200.scala b/src/main/scala/code/api/v2_0_0/APIMethods200.scala index e3958957d..76cf063de 100644 --- a/src/main/scala/code/api/v2_0_0/APIMethods200.scala +++ b/src/main/scala/code/api/v2_0_0/APIMethods200.scala @@ -29,6 +29,7 @@ import code.socialmedia.SocialMediaHandle import code.transactionrequests.TransactionRequests import code.meetings.Meeting +import code.usercustomerlinks.UserCustomerLink import net.liftweb.common.{Full, _} import net.liftweb.http.rest.RestHelper @@ -1515,8 +1516,7 @@ trait APIMethods200 { postedData <- tryo{json.extract[CreateCustomerJson]} ?~! ErrorMessages.InvalidJsonFormat checkAvailable <- tryo(assert(Customer.customerProvider.vend.checkCustomerNumberAvailable(bankId, postedData.customer_number) == true)) ?~! ErrorMessages.CustomerNumberAlreadyExists // TODO The user id we expose should be a uuid . For now we have a long direct from the database. - user_id <- tryo {postedData.user_id.toLong} ?~ ErrorMessages.InvalidNumber - customer_user <- User.findByApiId(user_id) ?~! ErrorMessages.UserNotFoundById + customer_user <- User.findByUserId(postedData.user_id) ?~! ErrorMessages.UserNotFoundById customer <- booleanToBox(Customer.customerProvider.vend.getCustomer(bankId, customer_user).isEmpty) ?~ ErrorMessages.CustomerAlreadyExistsForUser customer <- Customer.customerProvider.vend.addCustomer(bankId, customer_user, @@ -1576,7 +1576,43 @@ trait APIMethods200 { } } + resourceDocs += ResourceDoc( + createUserCustomerLinks, + apiVersion, + "createUserCustomerLinks", + "POST", + "/banks/BANK_ID/UserCustomerLinks", + "Create user customer link.", + """Link a customer and an user + |This call may require additional permissions/role in the future. + |For now the authenticated user can create at most one linked customer. + |OAuth authentication is required. + |""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + false, + List(apiTagCustomer)) + lazy val createUserCustomerLinks : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "banks" :: BankId(bankId) :: "UserCustomerLinks" :: Nil JsonPost json -> _ => { + user => + for { + u <- user ?~! "User must be logged in to post Customer" + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} + user_id <- tryo(u.userId.isEmpty) ?~! "Field user_id is not defined for the logged user!" + customer_user <- User.findByUserId(u.userId) ?~! ErrorMessages.UserNotFoundById + customer <- tryo(Customer.customerProvider.vend.getCustomer(bankId, customer_user).get) ?~! ErrorMessages.CustomerNotFound + userCustomerLink <- booleanToBox(UserCustomerLink.userCustomerLinkProvider.vend.getUserCustomerLink(u.userId, customer.customerId).isEmpty == true) ?~ ErrorMessages.CustomerAlreadyExistsForUser + userCustomerLink <- UserCustomerLink.userCustomerLinkProvider.vend.createUserCustomerLink(u.userId, customer.customerId, bankId.value.toString, exampleDate, true) ?~! "Could not create userCustomerLink" + } yield { + val successJson = Extraction.decompose(code.api.v2_0_0.JSONFactory200.createUserCustomerLinkJSON(userCustomerLink)) + successJsonResponse(successJson, 201) + } + } + } diff --git a/src/main/scala/code/api/v2_0_0/JSONFactory2.0.0.scala b/src/main/scala/code/api/v2_0_0/JSONFactory2.0.0.scala index f462dbfac..927df331b 100644 --- a/src/main/scala/code/api/v2_0_0/JSONFactory2.0.0.scala +++ b/src/main/scala/code/api/v2_0_0/JSONFactory2.0.0.scala @@ -111,6 +111,12 @@ case class MeetingPresentJSON( ) +case class UserCustomerLinkJSON(customer_id: String, + user_id: String, + bank_id: String, + date_inserted: Date, + is_active: Boolean) +case class UserCustomerLinkJSONs(l: List[UserCustomerLinkJSON]) class BasicViewJSON( val id: String, @@ -748,7 +754,18 @@ def createTransactionTypeJSON(transactionType : TransactionType) : TransactionTy MeetingJSONs(meetings.map(createMeetingJSON)) } + def createUserCustomerLinkJSON(ucl: code.usercustomerlinks.UserCustomerLink) = { + UserCustomerLinkJSON(customer_id = ucl.customerId, + user_id = ucl.userId, + bank_id = ucl.bankId, + date_inserted = ucl.dateInserted, + is_active = ucl.isActive + ) + } + def createUserCustomerLinkJSONs(ucls: List[code.usercustomerlinks.UserCustomerLink]): UserCustomerLinkJSONs = { + UserCustomerLinkJSONs(ucls.map(createUserCustomerLinkJSON)) + } // Copied from 1.2.1 (import just this def instead?) def stringOrNull(text : String) = diff --git a/src/main/scala/code/api/v2_0_0/OBPAPI2_0_0.scala b/src/main/scala/code/api/v2_0_0/OBPAPI2_0_0.scala index 2cda1148e..100ad03fe 100644 --- a/src/main/scala/code/api/v2_0_0/OBPAPI2_0_0.scala +++ b/src/main/scala/code/api/v2_0_0/OBPAPI2_0_0.scala @@ -169,7 +169,8 @@ object OBPAPI2_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w Implementations2_0_0.getMeetings, Implementations2_0_0.getMeeting, Implementations2_0_0.createCustomer, - Implementations2_0_0.getCurrentUser + Implementations2_0_0.getCurrentUser, + Implementations2_0_0.createUserCustomerLinks ) routes.foreach(route => { diff --git a/src/main/scala/code/model/User.scala b/src/main/scala/code/model/User.scala index 25309f3d0..b1dcdb4a9 100644 --- a/src/main/scala/code/model/User.scala +++ b/src/main/scala/code/model/User.scala @@ -100,4 +100,7 @@ object User { //versions of the API return this failure message, so if you change it, make sure //that all stable versions retain the same behavior Users.users.vend.getUserByProviderId(provider, idGivenByProvider) ~> UserNotFound(provider, idGivenByProvider) + + def findByUserId(userId : String) = + Users.users.vend.getUserByUserId(userId) } \ No newline at end of file diff --git a/src/main/scala/code/usercustomerlinks/MappedUserCustomerLinkProvider.scala b/src/main/scala/code/usercustomerlinks/MappedUserCustomerLinkProvider.scala new file mode 100644 index 000000000..e65c1e9b0 --- /dev/null +++ b/src/main/scala/code/usercustomerlinks/MappedUserCustomerLinkProvider.scala @@ -0,0 +1,66 @@ +package code.usercustomerlinks + +import java.util.Date + +import code.customer.Customer +import code.model.{User, BankId} +import code.util.{DefaultStringField} +import net.liftweb.common.Box +import net.liftweb.mapper._ + +/** + * Created by markom on 5/30/16. + */ +object MappedUserCustomerLinkProvider extends UserCustomerLinkProvider { + + override def createUserCustomerLink(userId: String, customerId: String, bankId: String, dateInserted: Date, isActive: Boolean): Box[UserCustomerLink] = { + + val createUserCustomerLink = MappedUserCustomerLink.create + .mUserId(userId) + .mCustomerId(customerId) + .mBankId(bankId) + .mDateInserted(new Date()) + .mIsActive(isActive) + .saveMe() + + Some(createUserCustomerLink) + } + + override def getUserCustomerLink(userId : String, customerId: String): Box[UserCustomerLink] = { + MappedUserCustomerLink.find( + By(MappedUserCustomerLink.mUserId, userId), + By(MappedUserCustomerLink.mCustomerId, customerId)) + } + + override def getUserCustomerLinks: Box[List[UserCustomerLink]] = { + //MappedUserCustomerLink.bulkDelete_!!() + Some(MappedUserCustomerLink.findAll()) + } + +} + +class MappedUserCustomerLink extends UserCustomerLink with LongKeyedMapper[MappedUserCustomerLink] with IdPK with CreatedUpdated { + + def getSingleton = MappedUserCustomerLink + + // Name the objects m* so that we can give the overridden methods nice names. + // Assume we'll have to override all fields so name them all m* + object mCustomerId extends DefaultStringField(this) + object mBankId extends DefaultStringField(this) + object mUserId extends DefaultStringField(this) + object mDateInserted extends MappedDateTime(this) + object mIsActive extends MappedBoolean(this) + + override def customerId: String = mCustomerId.get // id.toString + override def userId: String = mUserId.get + override def bankId : String = mBankId.get + override def dateInserted: Date = mDateInserted.get + override def isActive: Boolean = mIsActive + + + +} + +object MappedUserCustomerLink extends MappedUserCustomerLink with LongKeyedMetaMapper[MappedUserCustomerLink] { + override def dbIndexes = UniqueIndex(mUserId, mCustomerId) :: super.dbIndexes +} diff --git a/src/main/scala/code/usercustomerlinks/UserCustomerLink.scala b/src/main/scala/code/usercustomerlinks/UserCustomerLink.scala new file mode 100644 index 000000000..20975b09e --- /dev/null +++ b/src/main/scala/code/usercustomerlinks/UserCustomerLink.scala @@ -0,0 +1,29 @@ +package code.usercustomerlinks + +import java.util.Date +import code.model.{User, BankId} +import net.liftweb.common.Box +import net.liftweb.util.SimpleInjector + +trait UserCustomerLink { + def userId: String + def customerId: String + def bankId: String + def dateInserted: Date + def isActive: Boolean +} + + +object UserCustomerLink extends SimpleInjector { + + val userCustomerLinkProvider = new Inject(buildOne _) {} + + def buildOne: UserCustomerLinkProvider = MappedUserCustomerLinkProvider + +} + +trait UserCustomerLinkProvider { + def createUserCustomerLink(userId: String, customerId: String, bankId: String, dateInserted: Date, isActive: Boolean): Box[UserCustomerLink] + def getUserCustomerLink(userId: String, customerId: String): Box[UserCustomerLink] + def getUserCustomerLinks: Box[List[UserCustomerLink]] +} \ No newline at end of file diff --git a/src/main/scala/code/users/LiftUsers.scala b/src/main/scala/code/users/LiftUsers.scala index 3bfa3b0a0..70dd773a0 100644 --- a/src/main/scala/code/users/LiftUsers.scala +++ b/src/main/scala/code/users/LiftUsers.scala @@ -15,5 +15,9 @@ private object LiftUsers extends Users { def getUserByProviderId(provider : String, idGivenByProvider : String) : Box[User] = { APIUser.find(By(APIUser.provider_, provider), By(APIUser.providerId, idGivenByProvider)) } + + def getUserByUserId(userId : String) : Box[User] = { + APIUser.find(By(APIUser.userId_, userId)) + } } \ No newline at end of file diff --git a/src/main/scala/code/users/Users.scala b/src/main/scala/code/users/Users.scala index ec099a4d4..a72951c00 100644 --- a/src/main/scala/code/users/Users.scala +++ b/src/main/scala/code/users/Users.scala @@ -16,4 +16,6 @@ trait Users { def getUserByApiId(id : Long) : Box[User] def getUserByProviderId(provider : String, idGivenByProvider : String) : Box[User] + + def getUserByUserId(userId : String) : Box[User] } \ No newline at end of file