Merge in changes from develop

This commit is contained in:
Everett Sochowski 2015-02-24 11:46:45 +01:00
commit 50067e5632
20 changed files with 1139 additions and 48 deletions

View File

@ -39,6 +39,8 @@ import code.metadata.tags.MappedTag
import code.metadata.transactionimages.MappedTransactionImage
import code.metadata.wheretags.MappedWhereTag
import code.metrics.MappedMetric
import code.bankbranches.{MappedBankBranch, MappedDataLicense}
import code.customerinfo.{MappedCustomerMessage, MappedCustomerInfo}
import net.liftweb._
import util._
import common._
@ -176,6 +178,7 @@ class Boot extends Loggable{
LiftRules.statelessDispatch.append(v1_2.OBPAPI1_2)
LiftRules.statelessDispatch.append(v1_2_1.OBPAPI1_2_1)
LiftRules.statelessDispatch.append(v1_3_0.OBPAPI1_3_0)
LiftRules.statelessDispatch.append(v1_4_0.OBPAPI1_4_0)
// add other apis
LiftRules.statelessDispatch.append(BankMockAPI)
@ -327,5 +330,7 @@ object ToSchemify {
ViewPrivileges, ViewImpl, APIUser, MappedAccountHolder,
MappedComment, MappedNarrative, MappedTag,
MappedTransactionImage, MappedWhereTag, MappedCounterpartyMetadata,
MappedCounterpartyWhereTag, MappedBank, MappedBankAccount, MappedTransaction, MappedMetric)
MappedCounterpartyWhereTag, MappedBank, MappedBankAccount, MappedTransaction,
MappedMetric, MappedCustomerInfo, MappedCustomerMessage,
MappedBankBranch, MappedDataLicense)
}

View File

@ -0,0 +1,86 @@
package code.api.v1_4_0
import code.api.APIFailure
import code.api.v1_4_0.JSONFactory1_4_0.AddCustomerMessageJson
import code.bankbranches.BankBranches
import code.customerinfo.{CustomerMessages, CustomerInfo}
import code.model.{BankId, User}
import net.liftweb.common.Box
import net.liftweb.http.js.JE.JsRaw
import net.liftweb.http.{JsonResponse, Req}
import net.liftweb.http.rest.RestHelper
import code.api.util.APIUtil._
import net.liftweb.json.Extraction
import net.liftweb.json.JsonAST.JObject
import net.liftweb.util.Helpers.tryo
import code.util.Helper._
trait APIMethods140 {
//needs to be a RestHelper to get access to JsonGet, JsonPost, etc.
self: RestHelper =>
val Implementations1_4_0 = new Object(){
lazy val getCustomerInfo : PartialFunction[Req, Box[User] => Box[JsonResponse]] = {
case "banks" :: BankId(bankId) :: "customer" :: Nil JsonGet _ => {
user => {
for {
u <- user ?~! "User must be logged in to retrieve customer info"
info <- CustomerInfo.customerInfoProvider.vend.getInfo(bankId, u) ~> APIFailure("No customer info found", 404)
} yield {
val json = JSONFactory1_4_0.createCustomerInfoJson(info)
successJsonResponse(Extraction.decompose(json))
}
}
}
}
lazy val getCustomerMessages : PartialFunction[Req, Box[User] => Box[JsonResponse]] = {
case "banks" :: BankId(bankId) :: "customer" :: "messages" :: Nil JsonGet _ => {
user => {
for {
u <- user ?~! "User must be logged in to retrieve customer messages"
} yield {
val messages = CustomerMessages.customerMessageProvider.vend.getMessages(u, bankId)
val json = JSONFactory1_4_0.createCustomerMessagesJson(messages)
successJsonResponse(Extraction.decompose(json))
}
}
}
}
lazy val addCustomerMessage : PartialFunction[Req, Box[User] => Box[JsonResponse]] = {
case "banks" :: BankId(bankId) :: "customer" :: customerNumber :: "messages" :: Nil JsonPost json -> _ => {
user => {
for {
postedData <- tryo{json.extract[AddCustomerMessageJson]} ?~! "Incorrect json format"
customer <- CustomerInfo.customerInfoProvider.vend.getUser(bankId, customerNumber) ?~! "No customer found"
messageCreated <- booleanToBox(
CustomerMessages.customerMessageProvider.vend.addMessage(
customer, bankId, postedData.message, postedData.from_department, postedData.from_person),
"Server error: could not add message")
} yield {
successJsonResponse(JsRaw("{}"), 201)
}
}
}
}
lazy val getBranches : PartialFunction[Req, Box[User] => Box[JsonResponse]] = {
case "banks" :: BankId(bankId) :: "branches" :: Nil JsonGet _ => {
user => {
for {
branches <- Box(BankBranches.bankBranchesProvider.vend.getBranches(bankId)) ~> APIFailure("No branch data available", 404)
} yield {
val json = JSONFactory1_4_0.createBranchesJson(branches)
successJsonResponse(Extraction.decompose(json))
}
}
}
}
}
}

View File

@ -0,0 +1,63 @@
package code.api.v1_4_0
import java.util.Date
import code.bankbranches.BankBranches
import code.bankbranches.BankBranches.{BankBranch, DataLicense, BranchData}
import code.customerinfo.{CustomerMessage, CustomerInfo}
object JSONFactory1_4_0 {
case class CustomerInfoJson(customer_number : String,
legal_name : String,
mobile_phone_number : String,
email : String,
face_image : CustomerFaceImageJson)
case class CustomerFaceImageJson(url : String, date : Date)
case class CustomerMessagesJson(messages : List[CustomerMessageJson])
case class CustomerMessageJson(id : String, date : Date, message : String, from_department : String, from_person : String)
case class AddCustomerMessageJson(message : String, from_department : String, from_person : String)
case class BranchDataJson(license : DataLicenseJson, branches : List[BranchJson])
case class DataLicenseJson(name : String, url : String)
case class BranchJson(id : String, name : String, address : AddressJson)
case class AddressJson(line_1 : String, line_2 : String, line_3 : String, line_4 : String, line_5 : String, postcode_zip : String, country : String)
def createCustomerInfoJson(cInfo : CustomerInfo) : CustomerInfoJson = {
CustomerInfoJson(customer_number = cInfo.number,
legal_name = cInfo.legalName, mobile_phone_number = cInfo.mobileNumber,
email = cInfo.email, face_image = CustomerFaceImageJson(url = cInfo.faceImage.url, date = cInfo.faceImage.date))
}
def createCustomerMessageJson(cMessage : CustomerMessage) : CustomerMessageJson = {
CustomerMessageJson(id = cMessage.messageId, date = cMessage.date,
message = cMessage.message, from_department = cMessage.fromDepartment,
from_person = cMessage.fromPerson)
}
def createCustomerMessagesJson(messages : List[CustomerMessage]) : CustomerMessagesJson = {
CustomerMessagesJson(messages.map(createCustomerMessageJson))
}
def createDataLicenseJson(dataLicense : DataLicense) : DataLicenseJson = {
DataLicenseJson(dataLicense.name, dataLicense.url)
}
def createAddressJson(address : BankBranches.Address) : AddressJson = {
AddressJson(address.line1, address.line2, address.line3, address.line4, address.line5, address.postCode, address.countryCode)
}
def createBranchJson(bankBranch: BankBranch) : BranchJson = {
BranchJson(bankBranch.branchId.value, bankBranch.name, createAddressJson(bankBranch.address))
}
def createBranchesJson(branchData : BranchData) : BranchDataJson = {
BranchDataJson(createDataLicenseJson(branchData.license), branchData.branches.map(createBranchJson))
}
}

View File

@ -0,0 +1,20 @@
package code.api.v1_4_0
import code.api.OBPRestHelper
import net.liftweb.common.Loggable
object OBPAPI1_4_0 extends OBPRestHelper with APIMethods140 with Loggable {
val VERSION = "1.4.0"
val routes = List(
Implementations1_4_0.getCustomerInfo,
Implementations1_4_0.getCustomerMessages,
Implementations1_4_0.addCustomerMessage,
Implementations1_4_0.getBranches)
routes.foreach(route => {
oauthServe(apiPrefix{route})
})
}

View File

@ -0,0 +1,60 @@
package code.bankbranches
import code.bankbranches.BankBranches.{BankBranch, DataLicense, BranchData}
import code.model.BankId
import net.liftweb.common.Logger
import net.liftweb.util.SimpleInjector
object BankBranches extends SimpleInjector {
case class BankBranchId(value : String)
case class BranchData(branches : List[BankBranch], license : DataLicense)
trait DataLicense {
def name : String
def url : String
}
trait BankBranch {
def branchId : BankBranchId
def name : String
def address : Address
}
trait Address {
def line1 : String
def line2 : String
def line3 : String
def line4 : String
def line5 : String
def postCode : String
//ISO_3166-1_alpha-2
def countryCode : String
}
val bankBranchesProvider = new Inject(buildOne _) {}
def buildOne: BankBranchesProvider = MappedBankBranchesProvider
}
trait BankBranchesProvider {
private val logger = Logger(classOf[BankBranchesProvider])
final def getBranches(bank : BankId) : Option[BranchData] = {
branchDataLicense(bank) match {
case Some(license) =>
Some(BranchData(branchData(bank), license))
case None => {
logger.info(s"No branch data license found for bank ${bank.value}")
None
}
}
}
protected def branchData(bank : BankId) : List[BankBranch]
protected def branchDataLicense(bank : BankId) : Option[DataLicense]
}

View File

@ -0,0 +1,65 @@
package code.bankbranches
import code.bankbranches.BankBranches.{DataLicense, BankBranchId, Address, BankBranch}
import code.model.BankId
import net.liftweb.mapper._
object MappedBankBranchesProvider extends BankBranchesProvider {
override protected def branchData(bank: BankId): List[BankBranch] =
MappedBankBranch.findAll(By(MappedBankBranch.mBankId, bank.value))
override protected def branchDataLicense(bank: BankId): Option[DataLicense] =
MappedDataLicense.find(By(MappedDataLicense.mBankId, bank.value))
}
class MappedBankBranch extends BankBranch with LongKeyedMapper[MappedBankBranch] with IdPK {
override def getSingleton = MappedBankBranch
object mBankId extends MappedText(this)
object mName extends MappedText(this)
object mBranchId extends MappedText(this)
object mLine1 extends MappedText(this)
object mLine2 extends MappedText(this)
object mLine3 extends MappedText(this)
object mLine4 extends MappedText(this)
object mLine5 extends MappedText(this)
object mCountryCode extends MappedString(this, 2)
object mPostCode extends MappedText(this)
override def branchId: BankBranchId = BankBranchId(mBranchId.get)
override def name: String = mName.get
override def address: Address = new Address {
override def line1: String = mLine1.get
override def line2: String = mLine2.get
override def line3: String = mLine3.get
override def line4: String = mLine4.get
override def line5: String = mLine5.get
override def countryCode: String = mCountryCode.get
override def postCode: String = mPostCode.get
}
}
object MappedBankBranch extends MappedBankBranch with LongKeyedMetaMapper[MappedBankBranch] {
override def dbIndexes = UniqueIndex(mBankId, mBranchId) :: Index(mBankId) :: super.dbIndexes
}
class MappedDataLicense extends DataLicense with LongKeyedMapper[MappedDataLicense] with IdPK {
override def getSingleton = MappedDataLicense
object mBankId extends MappedText(this)
object mName extends MappedText(this)
object mUrl extends MappedText(this)
override def name: String = mName.get
override def url: String = mUrl.get
}
object MappedDataLicense extends MappedDataLicense with LongKeyedMetaMapper[MappedDataLicense] {
override def dbIndexes = Index(mBankId) :: super.dbIndexes
}

View File

@ -0,0 +1,34 @@
package code.customerinfo
import java.util.Date
import code.model.{BankId, User}
import net.liftweb.common.Box
import net.liftweb.util.SimpleInjector
object CustomerInfo extends SimpleInjector {
val customerInfoProvider = new Inject(buildOne _) {}
def buildOne: CustomerInfoProvider = MappedCustomerInfoProvider
}
trait CustomerInfoProvider {
def getInfo(bankId : BankId, user : User) : Box[CustomerInfo]
def getUser(bankId : BankId, customerId : String) : Box[User]
}
trait CustomerInfo {
def number : String
def legalName : String
def mobileNumber : String
def email : String
def faceImage : CustomerFaceImage
}
trait CustomerFaceImage {
def url : String
def date : Date
}

View File

@ -0,0 +1,33 @@
package code.customerinfo
import java.util.Date
import code.model.{BankId, User}
import net.liftweb.util.SimpleInjector
object CustomerMessages extends SimpleInjector {
val customerMessageProvider = new Inject(buildOne _) {}
def buildOne: CustomerMessageProvider = MappedCustomerMessageProvider
}
trait CustomerMessageProvider {
//TODO: pagination? is this sorted by date?
def getMessages(user : User, bankId : BankId) : List[CustomerMessage]
def addMessage(user : User, bankId : BankId, message : String, fromDepartment : String, fromPerson : String) : Boolean
}
trait CustomerMessage {
//TODO: message language?
def messageId : String
def date : Date
def message : String
def fromDepartment : String
def fromPerson : String
}

View File

@ -0,0 +1,52 @@
package code.customerinfo
import java.util.Date
import code.model.{BankId, User}
import code.model.dataAccess.APIUser
import net.liftweb.common.Box
import net.liftweb.mapper._
object MappedCustomerInfoProvider extends CustomerInfoProvider {
override def getInfo(bankId : BankId, user: User): Box[CustomerInfo] = {
MappedCustomerInfo.find(
By(MappedCustomerInfo.mUser, user.apiId.value),
By(MappedCustomerInfo.mBank, bankId.value))
}
override def getUser(bankId: BankId, customerNumber: String): Box[User] = {
MappedCustomerInfo.find(
By(MappedCustomerInfo.mBank, bankId.value),
By(MappedCustomerInfo.mNumber, customerNumber)
).flatMap(_.mUser.obj)
}
}
class MappedCustomerInfo extends CustomerInfo with LongKeyedMapper[MappedCustomerInfo] with IdPK with CreatedUpdated {
def getSingleton = MappedCustomerInfo
object mUser extends MappedLongForeignKey(this, APIUser)
object mBank extends MappedText(this)
object mNumber extends MappedText(this)
object mMobileNumber extends MappedText(this)
object mLegalName extends MappedText(this)
object mEmail extends MappedEmail(this, 200)
object mFaceImageUrl extends MappedText(this)
object mFaceImageTime extends MappedDateTime(this)
override def number: String = mNumber.get
override def mobileNumber: String = mMobileNumber.get
override def legalName: String = mLegalName.get
override def email: String = mEmail.get
override def faceImage: CustomerFaceImage = new CustomerFaceImage {
override def date: Date = mFaceImageTime.get
override def url: String = mFaceImageUrl.get
}
}
object MappedCustomerInfo extends MappedCustomerInfo with LongKeyedMetaMapper[MappedCustomerInfo] {
//one customer info per bank for each api user
override def dbIndexes = UniqueIndex(mBank, mNumber) :: UniqueIndex(mUser, mBank) :: super.dbIndexes
}

View File

@ -0,0 +1,53 @@
package code.customerinfo
import java.util.Date
import code.model.{BankId, User}
import code.model.dataAccess.APIUser
import code.util.MappedUUID
import net.liftweb.mapper._
object MappedCustomerMessageProvider extends CustomerMessageProvider {
override def getMessages(user: User, bankId : BankId): List[CustomerMessage] = {
MappedCustomerMessage.findAll(
By(MappedCustomerMessage.user, user.apiId.value),
By(MappedCustomerMessage.bank, bankId.value),
OrderBy(MappedCustomerMessage.updatedAt, Descending))
}
override def addMessage(user: User, bankId: BankId, message: String, fromDepartment: String, fromPerson: String): Boolean = {
MappedCustomerMessage.create
.mFromDepartment(fromDepartment)
.mFromPerson(fromPerson)
.mMessage(message)
.user(user.apiId.value)
.bank(bankId.value).save()
}
}
class MappedCustomerMessage extends CustomerMessage
with LongKeyedMapper[MappedCustomerMessage] with IdPK with CreatedUpdated {
def getSingleton = MappedCustomerMessage
object user extends MappedLongForeignKey(this, APIUser)
object bank extends MappedText(this)
object mFromPerson extends MappedText(this)
object mFromDepartment extends MappedText(this)
object mMessage extends MappedText(this)
object mMessageId extends MappedUUID(this)
override def messageId: String = mMessageId.get
override def date: Date = createdAt.get
override def fromPerson: String = mFromPerson.get
override def fromDepartment: String = mFromDepartment.get
override def message: String = mMessage.get
}
object MappedCustomerMessage extends MappedCustomerMessage with LongKeyedMetaMapper[MappedCustomerMessage] {
override def dbIndexes = UniqueIndex(mMessageId) :: Index(user, bank, updatedAt) :: super.dbIndexes
}

View File

@ -55,7 +55,8 @@ package code.model.dataAccess {
import java.util.UUID
import code.model.{AccountId, BankId}
import code.model.{User, AccountId, BankId}
import code.users.Users
import com.rabbitmq.client.{ConnectionFactory,Channel}
import net.liftmodules.amqp.{
AMQPDispatcher,
@ -114,7 +115,7 @@ import scala.util.Random
}
}
private def createAccount(bankAccountNumber: BankAccountNumber, accountId : AccountId, bank : HostedBank, u: APIUser) : Account = {
private def createAccount(bankAccountNumber: BankAccountNumber, accountId : AccountId, bank : HostedBank, u: User) : Account = {
//TODO: fill these fields using the HBCI library.
import net.liftweb.mongodb.BsonDSL._
Account.find(
@ -148,11 +149,11 @@ import scala.util.Random
}
}
def createAccount(bankAccountNumber: BankAccountNumber, bank: HostedBank, u: APIUser): Account = {
def createAccount(bankAccountNumber: BankAccountNumber, bank: HostedBank, u: User): Account = {
createAccount(bankAccountNumber, AccountId(UUID.randomUUID().toString), bank, u)
}
def createAccount(accountId: AccountId, bank: HostedBank, u: APIUser): Account = {
def createAccount(accountId: AccountId, bank: HostedBank, u: User): Account = {
import net.liftweb.mongodb.BsonDSL._
val uniqueAccountNumber = {
def exists(number : String) = Account.count((Account.accountNumber.name -> number) ~ (Account.bankID.name -> bank.id.get)) > 0
@ -173,20 +174,20 @@ import scala.util.Random
}, accountId, bank, u)
}
def setAsOwner(bankId : BankId, accountId : AccountId, user: APIUser): Unit = {
def setAsOwner(bankId : BankId, accountId : AccountId, user: User): Unit = {
createOwnerView(bankId, accountId, user)
setAsAccountOwner(bankId, accountId, user)
}
private def setAsAccountOwner(bankId : BankId, accountId : AccountId, user : APIUser) : Unit = {
private def setAsAccountOwner(bankId : BankId, accountId : AccountId, user : User) : Unit = {
MappedAccountHolder.create
.accountBankPermalink(bankId.value)
.accountPermalink(accountId.value)
.user(user)
.user(user.apiId.value)
.save
}
private def createOwnerView(bankId : BankId, accountId : AccountId, user: APIUser): Unit = {
private def createOwnerView(bankId : BankId, accountId : AccountId, user: User): Unit = {
val existingOwnerView = ViewImpl.find(
By(ViewImpl.permalink_, "owner") ::
@ -195,16 +196,16 @@ import scala.util.Random
existingOwnerView match {
case Full(v) => {
logger.info(s"account $accountId at bank $bankId has already an owner view")
v.users_.toList.find(_.id == user.id) match {
v.users_.toList.find(_.id == user.apiId.value) match {
case Some(u) => {
logger.info(s"user ${user.email.get} has already an owner view access on account $accountId at bank $bankId")
logger.info(s"user ${user.emailAddress} has already an owner view access on account $accountId at bank $bankId")
}
case _ =>{
//TODO: When can this case occur?
logger.info(s"creating owner view access to user ${user.email.get}")
logger.info(s"creating owner view access to user ${user.emailAddress}")
ViewPrivileges
.create
.user(user)
.user(user.apiId.value)
.view(v)
.save
}
@ -216,10 +217,10 @@ import scala.util.Random
logger.info(s"creating owner view on account account $accountId at bank $bankId")
val view = ViewImpl.createAndSaveOwnerView(bankId, accountId, "")
logger.info(s"creating owner view access to user ${user.email.get}")
logger.info(s"creating owner view access to user ${user.emailAddress}")
ViewPrivileges
.create
.user(user)
.user(user.apiId.value)
.view(view)
.save
}
@ -246,19 +247,18 @@ import scala.util.Random
case msg@AMQPMessage(message: CreateBankAccount) => {
logger.info(s"got message to create account/bank: ${message.accountNumber} / ${message.bankIdentifier}")
APIUser.find(
By(APIUser.provider_, message.accountOwnerProvider),
By(APIUser.providerId, message.accountOwnerId)
).map{ user => {
val foundUser = Users.users.vend.getUserByProviderId(message.accountOwnerProvider, message.accountOwnerId)
foundUser.map{ user => {
logger.info("user found for owner view")
val bank: HostedBank = BankAccountCreation.createBank(message)
val bankAccount = BankAccountCreation.createAccount(message, bank, user)
BankAccountCreation.setAsOwner(BankId(bank.permalink.get), AccountId(message.accountNumber), user)
BankAccountCreation.setAsOwner(BankId(bank.permalink.get), bankAccount.accountId, user)
logger.info(s"created account ${message.accountNumber} at ${message.bankIdentifier}")
logger.info(s"created account with id ${bankAccount.bankId.value} with number ${bankAccount.number} at bank with identifier ${message.bankIdentifier}")
logger.info(s"Send message to get updates for the account ${message.accountNumber} at ${message.bankIdentifier}")
logger.info(s"Send message to get updates for the account with id ${bankAccount.accountId.value}" +
s" with number ${bankAccount.number} at bank with identifier ${message.bankIdentifier}")
UpdatesRequestSender.sendMsg(UpdateBankAccount(message.accountNumber, message.bankIdentifier))
}
}.getOrElse(

View File

@ -0,0 +1,4 @@
package code.api
//Set the default connector setup here by extending it
trait DefaultConnectorTestSetup extends LocalMappedConnectorTestSetup

View File

@ -33,10 +33,10 @@ Berlin 13359, Germany
package code.api.test
import code.TestServer
import code.api.LocalMappedConnectorTestSetup
import code.api.{DefaultConnectorTestSetup, TestConnectorSetup, LocalConnectorTestSetup}
import org.scalatest._
import dispatch._
import net.liftweb.json.{Serialization, NoTypeHints}
import net.liftweb.json.{DefaultFormats, Serialization, NoTypeHints}
import net.liftweb.common._
trait ServerSetup extends FeatureSpec with SendServerRequests
@ -45,13 +45,13 @@ trait ServerSetup extends FeatureSpec with SendServerRequests
with ShouldMatchers with Loggable {
var server = TestServer
implicit val formats = Serialization.formats(NoTypeHints)
implicit val formats = DefaultFormats
val h = Http
def baseRequest = host(server.host, server.port)
}
trait ServerSetupWithTestData extends ServerSetup with LocalMappedConnectorTestSetup {
trait ServerSetupWithTestData extends ServerSetup with DefaultConnectorTestSetup {
override def beforeEach() = {
super.beforeEach()

View File

@ -0,0 +1,117 @@
package code.api.v1_4_0
import code.api.v1_4_0.JSONFactory1_4_0.{BranchJson, BranchDataJson}
import dispatch._
import code.bankbranches.BankBranches.{Address, BankBranchId, BankBranch, DataLicense}
import code.bankbranches.{BankBranches, BankBranchesProvider}
import code.model.BankId
class BankBranchesTest extends V140ServerSetup {
val BankWithLicense = BankId("bank-with-license")
val BankWithoutLicense = BankId("bank-without-license")
case class BankBranchImpl(branchId : BankBranchId, name : String, address : Address) extends BankBranch
case class AddressImpl(line1 : String, line2 : String, line3 : String, line4 : String,
line5 : String, postCode : String, countryCode : String) extends Address
val fakeAddress1 = AddressImpl("134", "32432", "fff", "fsfsfs", "mvmvmv", "C4SF5", "DE")
val fakeAddress2 = fakeAddress1.copy(line1 = "00000")
val fakeBranch1 = BankBranchImpl(BankBranchId("branch1"), "Branch 1", fakeAddress1)
val fakeBranch2 = BankBranchImpl(BankBranchId("branch2"), "Branch 2", fakeAddress2)
val fakeLicense = new DataLicense {
override def name: String = "sample-license"
override def url: String = "http://example.com/license"
}
val mockConnector = new BankBranchesProvider {
override protected def branchData(bank: BankId): List[BankBranch] = {
bank match {
// have it return branches even for the bank without a license so we can test the connector does not return them
case BankWithLicense | BankWithoutLicense=> List(fakeBranch1, fakeBranch2)
case _ => Nil
}
}
override protected def branchDataLicense(bank: BankId): Option[DataLicense] = {
bank match {
case BankWithLicense => Some(fakeLicense)
case _ => None
}
}
}
def verifySameData(branch: BankBranch, branchJson : BranchJson) = {
branch.name should equal (branchJson.name)
branch.branchId should equal(BankBranchId(branchJson.id))
branch.address.line1 should equal(branchJson.address.line_1)
branch.address.line2 should equal(branchJson.address.line_2)
branch.address.line3 should equal(branchJson.address.line_3)
branch.address.line4 should equal(branchJson.address.line_4)
branch.address.line5 should equal(branchJson.address.line_5)
branch.address.countryCode should equal(branchJson.address.country)
branch.address.postCode should equal(branchJson.address.postcode_zip)
}
override def beforeAll() {
super.beforeAll()
//use the mock connector
BankBranches.bankBranchesProvider.default.set(mockConnector)
}
override def afterAll() {
super.afterAll()
//reset the default connector
BankBranches.bankBranchesProvider.default.set(BankBranches.buildOne)
}
feature("Getting bank branches") {
scenario("We try to get bank branches for a bank without a data license for branch information") {
When("We make a request")
val request = (v1_4Request / "banks" / BankWithoutLicense.value / "branches").GET
val response = makeGetRequest(request)
Then("We should get a 404")
response.code should equal(404)
}
scenario("We try to get bank branches for a bank with a data license for branch information") {
When("We make a request")
val request = (v1_4Request / "banks" / BankWithLicense.value / "branches").GET
val response = makeGetRequest(request)
Then("We should get a 200")
response.code should equal(200)
And("We should get the right json format")
val responseBodyOpt = response.body.extractOpt[BranchDataJson]
responseBodyOpt.isDefined should equal(true)
val responseBody = responseBodyOpt.get
And("We should get the right license")
val license = responseBody.license
license.name should equal(fakeLicense.name)
license.url should equal(fakeLicense.url)
And("We should get the right branches")
val branches = responseBody.branches
branches.size should equal(2)
val first = branches(0)
if(first.id == fakeBranch1.branchId.value) {
verifySameData(fakeBranch1, first)
verifySameData(fakeBranch2, branches(1))
} else if (first.id == fakeBranch2.branchId.value) {
verifySameData(fakeBranch2, first)
verifySameData(fakeBranch1, branches(1))
} else {
fail("incorrect branches")
}
}
}
}

View File

@ -0,0 +1,105 @@
package code.api.v1_4_0
import java.util.Date
import code.api.DefaultUsers
import code.api.util.APIUtil
import code.api.v1_4_0.JSONFactory1_4_0.CustomerInfoJson
import code.customerinfo.{CustomerFaceImage, CustomerInfo, CustomerInfoProvider}
import code.model.{User, BankId}
import net.liftweb.common.{Full, Empty, Box}
import dispatch._
import code.api.util.APIUtil.OAuth._
class CustomerInfoTest extends V140ServerSetup with DefaultUsers {
val mockBankId = BankId("mockbank1")
case class MockFaceImage(date : Date, url : String) extends CustomerFaceImage
case class MockCustomerInfo(number : String, mobileNumber : String,
legalName : String, email : String,
faceImage : MockFaceImage) extends CustomerInfo
val mockCustomerFaceImage = MockFaceImage(new Date(1234000), "http://example.com/image1")
val mockCustomerInfo = MockCustomerInfo("123", "3939", "Bob", "bob@example.com", mockCustomerFaceImage)
object MockedCustomerInfoProvider extends CustomerInfoProvider {
override def getInfo(bankId: BankId, user: User): Box[CustomerInfo] = {
if(bankId == mockBankId) Full(mockCustomerInfo)
else Empty
}
override def getUser(bankId: BankId, customerId: String): Box[User] = Empty
}
override def beforeAll() {
super.beforeAll()
//use the mock connector
CustomerInfo.customerInfoProvider.default.set(MockedCustomerInfoProvider)
}
override def afterAll() {
super.afterAll()
//reset the default connector
CustomerInfo.customerInfoProvider.default.set(CustomerInfo.buildOne)
}
feature("Getting a bank's customer info of the current user") {
scenario("There is no current user") {
Given("There is no logged in user")
When("We make the request")
val request = (v1_4Request / "banks" / mockBankId.value / "customer").GET
val response = makeGetRequest(request)
Then("We should get a 400")
response.code should equal(400)
}
scenario("There is a user, but the bank in questions has no customer info") {
Given("The bank in question has no customer info")
val testBank = BankId("test-bank")
val user = obpuser1
CustomerInfo.customerInfoProvider.vend.getInfo(testBank, user).isEmpty should equal(true)
When("We make the request")
//TODO: need stronger link between obpuser1 and user1
val request = (v1_4Request / "banks" / testBank.value / "customer").GET <@(user1)
val response = makeGetRequest(request)
Then("We should get a 404")
response.code should equal(404)
}
scenario("There is a user, and the bank in questions has customer info for that user") {
Given("The bank in question has customer info")
val testBank = mockBankId
val user = obpuser1
CustomerInfo.customerInfoProvider.vend.getInfo(testBank, user).isEmpty should equal(false)
When("We make the request")
//TODO: need stronger link between obpuser1 and user1
val request = (v1_4Request / "banks" / testBank.value / "customer").GET <@(user1)
val response = makeGetRequest(request)
Then("We should get a 200")
response.code should equal(200)
And("We should get the right information back")
val info = response.body.extract[CustomerInfoJson]
val received = MockCustomerInfo(info.customer_number, info.mobile_phone_number,
info.legal_name, info.email, MockFaceImage(info.face_image.date, info.face_image.url))
received should equal(mockCustomerInfo)
}
}
}

View File

@ -0,0 +1,77 @@
package code.api.v1_4_0
import code.api.DefaultUsers
import code.api.util.APIUtil
import code.api.v1_4_0.JSONFactory1_4_0.{AddCustomerMessageJson, CustomerMessagesJson}
import code.customerinfo.{MappedCustomerMessage, MappedCustomerInfo, CustomerInfo}
import code.model.BankId
import dispatch._
import code.api.util.APIUtil.OAuth._
import net.liftweb.json.Serialization.{read, write}
//TODO: API test should be independent of CustomerMessages implementation
class MappedCustomerMessagesTest extends V140ServerSetup with DefaultUsers {
implicit val format = APIUtil.formats
val mockBankId = BankId("mockbank1")
val mockCustomerNumber = "9393490320"
//TODO: need better tests
feature("Customer messages") {
scenario("Getting messages when none exist") {
Given("No messages exist")
MappedCustomerMessage.count() should equal(0)
When("We get the messages")
val request = (v1_4Request / "banks" / mockBankId.value / "customer" / "messages").GET <@ user1
val response = makeGetRequest(request)
Then("We should get a 200")
response.code should equal(200)
And("We should get no messages")
val json = response.body.extract[CustomerMessagesJson]
json.messages.size should equal(0)
}
scenario("Adding a message") {
When("We add a message")
val request = (v1_4Request / "banks" / mockBankId.value / "customer" / mockCustomerNumber / "messages").POST
val messageJson = AddCustomerMessageJson("some message", "some department", "some person")
val response = makePostRequest(request, write(messageJson))
Then("We should get a 201")
response.code should equal(201)
And("We should get that message when we do a get messages request ")
val getMessagesRequest = (v1_4Request / "banks" / mockBankId.value / "customer" / "messages").GET <@ user1
val getMessagesResponse = makeGetRequest(getMessagesRequest)
val json = getMessagesResponse.body.extract[CustomerMessagesJson]
json.messages.size should equal(1)
val msg = json.messages(0)
msg.message should equal(messageJson.message)
msg.from_department should equal(messageJson.from_department)
msg.from_person should equal(messageJson.from_person)
msg.id.nonEmpty should equal(true)
}
}
override def beforeAll(): Unit = {
super.beforeAll()
//TODO: this shouldn't be tied to an implementation
//need to create a customer info obj since the customer messages call needs to find user by customer number
MappedCustomerInfo.create
.mBank(mockBankId.value)
.mUser(obpuser1)
.mNumber(mockCustomerNumber).save()
}
override def beforeEach(): Unit = {
super.beforeEach()
MappedCustomerMessage.bulkDelete_!!()
}
}

View File

@ -0,0 +1,10 @@
package code.api.v1_4_0
import code.api.test.ServerSetup
import dispatch._
trait V140ServerSetup extends ServerSetup {
def v1_4Request = baseRequest / "obp" / "v1.4.0"
}

View File

@ -1,49 +1,113 @@
package code.bankaccountcreation
import code.api.DefaultConnectorTestSetup
import code.api.test.ServerSetup
import code.model.{Consumer => OBPConsumer, Token => OBPToken, AccountId, BankId}
import code.model.{User, BankId}
import code.views.Views
import net.liftweb.common.Full
import net.liftweb.mapper.By
import org.scalatest.Tag
import com.tesobe.model.CreateBankAccount
import code.model.dataAccess.{HostedBank, APIUser, BankAccountCreationListener}
import code.model.dataAccess.{APIUser, BankAccountCreationListener}
import net.liftmodules.amqp.AMQPMessage
import net.liftweb.mapper.By
import code.bankconnectors.Connector
class BankAccountCreationListenerTest extends ServerSetup {
class BankAccountCreationListenerTest extends ServerSetup with DefaultConnectorTestSetup {
object AccountHolderSetup extends Tag("account_holder_setup")
object BankAccountCreationListenerTag extends Tag("bank_account_creation_listener")
feature("The account holder gets properly set when a bank account is created"){
scenario("a bank account is created", AccountHolderSetup) {
override def beforeEach() = {
super.beforeEach()
wipeTestData()
}
When("We create a bank account")
val userId = "foo"
val userProvider = "bar"
val accountNumber = "123456"
val expectedAccountId = AccountId(accountNumber)
val bankIdentifier = "qux"
val expectedBankId = "quxbank"
override def afterEach() = {
super.afterEach()
wipeTestData()
}
//need to create the user for the bank accout creation process to work
val user =
feature("Bank account creation via AMQP messages"){
val userId = "foo"
val userProvider = "bar"
//need to create the user for the bank accout creation process to work
def getTestUser() =
APIUser.find(By(APIUser.provider_, userProvider), By(APIUser.providerId, userId)).getOrElse{
APIUser.create.
provider_(userProvider).
providerId(userId).
saveMe
}
val expectedBankId = "quxbank"
val accountNumber = "123456"
def thenCheckAccountCreated(user : User) = {
Then("An account with the proper parameters should be created")
val userAccounts = Views.views.vend.getAllAccountsUserCanSee(Full(user))
userAccounts.size should equal(1)
val createdAccount = userAccounts(0)
//the account id should be randomly generated
createdAccount.accountId.value.nonEmpty should be(true)
createdAccount.bankId.value should equal(expectedBankId)
createdAccount.number should equal(accountNumber)
And("The account holder should be set correctly")
Connector.connector.vend.getAccountHolders(BankId(expectedBankId), createdAccount.accountId) should equal(Set(user))
}
scenario("a bank account is created at a bank that does not yet exist", BankAccountCreationListenerTag) {
val bankIdentifier = "qux"
val user = getTestUser()
Given("The account doesn't already exist")
Views.views.vend.getAllAccountsUserCanSee(Full(user)).size should equal(0)
And("The bank in question doesn't already exist")
Connector.connector.vend.getBank(BankId(expectedBankId)).isDefined should equal(false)
When("We create a bank account")
//using expectedBankId as the bank name should be okay as the behaviour should be to slugify the bank name to get the id
//what to do if this slugification results in an id collision has not been determined yet
val msgContent = CreateBankAccount(userId, userProvider, accountNumber, bankIdentifier, expectedBankId)
//before the bank account is created, it should obviously have no holders
Connector.connector.vend.getAccountHolders(BankId(expectedBankId), expectedAccountId) should equal (Set.empty)
BankAccountCreationListener.createBankAccountListener ! AMQPMessage(msgContent)
//sleep to give the actor time to process the message
Thread.sleep(5000)
Then("The should be considered the account holder")
Connector.connector.vend.getAccountHolders(BankId(expectedBankId), expectedAccountId) should equal(Set(user))
thenCheckAccountCreated(user)
And("A bank should be created")
val createdBankBox = Connector.connector.vend.getBank(BankId(expectedBankId))
createdBankBox.isDefined should equal(true)
val createdBank = createdBankBox.get
createdBank.nationalIdentifier should equal(bankIdentifier)
}
scenario("a bank account is created at a bank that already exists", BankAccountCreationListenerTag) {
val user = getTestUser()
Given("The account doesn't already exist")
Views.views.vend.getAllAccountsUserCanSee(Full(user)).size should equal(0)
And("The bank in question already exists")
val createdBank = createBank(expectedBankId)
When("We create a bank account")
val msgContent = CreateBankAccount(userId, userProvider, accountNumber, createdBank.nationalIdentifier, createdBank.bankId.value)
BankAccountCreationListener.createBankAccountListener ! AMQPMessage(msgContent)
//sleep to give the actor time to process the message
Thread.sleep(5000)
thenCheckAccountCreated(user)
}
}

View File

@ -0,0 +1,133 @@
package code.bankbranches
import code.api.test.ServerSetup
import code.model.BankId
import net.liftweb.mapper.By
class MappedBankBranchesProviderTest extends ServerSetup {
private def delete(): Unit = {
MappedBankBranch.bulkDelete_!!()
MappedDataLicense.bulkDelete_!!()
}
override def beforeAll() = {
super.beforeAll()
delete()
}
override def afterEach() = {
super.afterEach()
delete()
}
def defaultSetup() =
new {
val bankIdWithLicenseAndData = "some-bank"
val bankIdWithNoLicense = "unlicensed-bank"
val license = MappedDataLicense.create
.mBankId(bankIdWithLicenseAndData)
.mName("some-license")
.mUrl("http://www.example.com/license").saveMe()
val unlicensedBranch = MappedBankBranch.create
.mBankId(bankIdWithNoLicense)
.mName("unlicensed")
.mBranchId("unlicensed")
.mCountryCode("es")
.mPostCode("4444")
.mLine1("a4")
.mLine2("b4")
.mLine3("c4")
.mLine4("d4")
.mLine5("e4").saveMe()
val branch1 = MappedBankBranch.create
.mBankId(bankIdWithLicenseAndData)
.mName("branch 1")
.mBranchId("branch1")
.mCountryCode("de")
.mPostCode("123213213")
.mLine1("a")
.mLine2("b")
.mLine3("c")
.mLine4("d")
.mLine5("e").saveMe()
val branch2 = MappedBankBranch.create
.mBankId(bankIdWithLicenseAndData)
.mName("branch 2")
.mBranchId("branch2")
.mCountryCode("fr")
.mPostCode("898989")
.mLine1("a2")
.mLine2("b2")
.mLine3("c2")
.mLine4("d2")
.mLine5("e2").saveMe()
}
feature("MappedBankBranchesProvider") {
scenario("We try to get branch data for a bank which does not have a data license set") {
val fixture = defaultSetup()
Given("The bank in question has no data license")
MappedDataLicense.count(By(MappedDataLicense.mBankId, fixture.bankIdWithNoLicense)) should equal(0)
And("The bank in question has branches")
MappedBankBranch.find(By(MappedBankBranch.mBankId, fixture.bankIdWithNoLicense)).isDefined should equal(true)
When("We try to get the branch data for that bank")
val branchData = MappedBankBranchesProvider.getBranches(BankId(fixture.bankIdWithNoLicense))
Then("We should get an empty option")
branchData should equal(None)
}
scenario("We try to get branch data for a bank which does have a data license set") {
val fixture = defaultSetup()
val expectedBranches = Set(fixture.branch1, fixture.branch2)
Given("We have a data license and branches for a bank")
MappedDataLicense.count(By(MappedDataLicense.mBankId, fixture.bankIdWithLicenseAndData)) should equal(1)
MappedBankBranch.findAll(By(MappedBankBranch.mBankId, fixture.bankIdWithLicenseAndData)).toSet should equal(expectedBranches)
When("We try to get the branch data for that bank")
val branchDataOpt = MappedBankBranchesProvider.getBranches(BankId(fixture.bankIdWithLicenseAndData))
Then("We should get back the data license and the branches")
branchDataOpt.isDefined should equal(true)
val branchData = branchDataOpt.get
branchData.license should equal(fixture.license)
branchData.branches.toSet should equal(expectedBranches)
}
scenario("We try to get branch data for a bank with a data license, but no branches") {
Given("We have a data license for a bank, but no branches")
val bankWithNoBranches = "bank-with-no-branches"
val license = MappedDataLicense.create
.mBankId(bankWithNoBranches)
.mName("some-license")
.mUrl("http://www.example.com/license").saveMe()
MappedBankBranch.find(By(MappedBankBranch.mBankId, bankWithNoBranches)).isDefined should equal(false)
When("We try to get the branch data for that bank")
val branchDataOpt = MappedBankBranchesProvider.getBranches(BankId(bankWithNoBranches))
Then("We should get back the data license, and a list branches of size 0")
branchDataOpt.isDefined should equal(true)
val branchData = branchDataOpt.get
branchData.license should equal(license)
branchData.branches should equal(Nil)
}
}
}

View File

@ -0,0 +1,110 @@
package code.customerinfo
import java.util.Date
import code.api.DefaultUsers
import code.api.test.ServerSetup
import code.model.BankId
import net.liftweb.mapper.By
class MappedCustomerInfoProviderTest extends ServerSetup with DefaultUsers {
val testBankId = BankId("bank")
def createCustomerInfo1() = MappedCustomerInfo.create
.mBank(testBankId.value).mEmail("bob@example.com").mFaceImageTime(new Date(12340000))
.mFaceImageUrl("http://example.com/image.jpg").mLegalName("John Johnson")
.mMobileNumber("12343434").mNumber("343").mUser(obpuser1).saveMe()
feature("Getting customer info") {
scenario("No customer info exists for user and we try to get it") {
Given("No MappedCustomerInfo exists for a user")
MappedCustomerInfo.find(By(MappedCustomerInfo.mUser, obpuser2)).isDefined should equal(false)
When("We try to get it")
val found = MappedCustomerInfoProvider.getInfo(testBankId, obpuser2)
Then("We don't")
found.isDefined should equal(false)
}
scenario("Customer info exists and we try to get it") {
val customerInfo1 = createCustomerInfo1()
Given("MappedCustomerInfo exists for a user")
MappedCustomerInfo.find(By(MappedCustomerInfo.mUser, obpuser1.apiId.value)).isDefined should equal(true)
When("We try to get it")
val foundOpt = MappedCustomerInfoProvider.getInfo(testBankId, obpuser1)
Then("We do")
foundOpt.isDefined should equal(true)
And("It is the right info")
val found = foundOpt.get
found should equal(customerInfo1)
}
}
feature("Getting a user from a bankId and customer number") {
scenario("We try to get a user from a customer number that doesn't exist") {
val customerNumber = "123213213213213"
Given("No customer info exists for a certain customer number")
MappedCustomerInfo.find(By(MappedCustomerInfo.mNumber, customerNumber)).isDefined should equal(false)
When("We try to get the user for a bank with that customer number")
val found = MappedCustomerInfoProvider.getUser(BankId("some-bank"), customerNumber)
Then("We should not find a user")
found.isDefined should equal(false)
}
scenario("We try to get a user from a customer number that doesn't exist at the bank in question") {
val customerNumber = "123213213213213"
val bankId = BankId("a-bank")
Given("Customer info exists for a different bank")
MappedCustomerInfo.create.mNumber(customerNumber).mBank(bankId.value).mUser(obpuser1).saveMe()
MappedCustomerInfo.count(By(MappedCustomerInfo.mNumber, customerNumber),
By(MappedCustomerInfo.mBank, bankId.value)) should equal({
MappedCustomerInfo.count(By(MappedCustomerInfo.mNumber, customerNumber))
})
When("We try to get the user for a different bank")
val found = MappedCustomerInfoProvider.getUser(BankId(bankId.value + "asdsad"), customerNumber)
Then("We should not find a user")
found.isDefined should equal(false)
}
scenario("We try to get a user from a customer number that does exist at the bank in question") {
val customerNumber = "123213213213213"
val bankId = BankId("a-bank")
Given("Customer info exists for that bank")
MappedCustomerInfo.create.mNumber(customerNumber).mBank(bankId.value).mUser(obpuser1).saveMe()
MappedCustomerInfo.count(By(MappedCustomerInfo.mNumber, customerNumber),
By(MappedCustomerInfo.mBank, bankId.value)) should equal(1)
When("We try to get the user for that bank")
val found = MappedCustomerInfoProvider.getUser(bankId, customerNumber)
Then("We should not find a user")
found.isDefined should equal(true)
}
}
override def beforeAll() = {
super.beforeAll()
MappedCustomerInfo.bulkDelete_!!()
}
override def afterEach() = {
super.afterEach()
MappedCustomerInfo.bulkDelete_!!()
}
}