Merge pull request #1383 from hongwei1/develop

added the new endpoint --getBankAccountsBalances, added tests for swagger
This commit is contained in:
Simon Redfern 2019-08-08 11:35:58 +02:00 committed by GitHub
commit ad119f52dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 711 additions and 209 deletions

View File

@ -360,6 +360,17 @@
<artifactId>client</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser</artifactId>
<version>2.0.13</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
</dependencies>
<build>

View File

@ -6,7 +6,7 @@
### Base configuration
## Which data connector to use, if config star as connector, should make sure other connector config appropriate and works
## Which data connector to use, if config `star` as connector, please also check `starConnector_supported_types`
connector=mapped
#connector=mongodb
#connector=kafka
@ -17,6 +17,9 @@ connector=mapped
#connector=star
#connector=...
## if connector = star, then need to set which connectors will be used. For now, obp support rest, akka, kafka. If you set kafka, then you need to start the kafka server.
#starConnector_supported_types=rest,akka,kafka
## whether export LocalMappedConnector methods as endpoints, it is just for develop, default is false
#connector.export.LocalMappedConnector=false
@ -32,9 +35,9 @@ connector=mapped
#connector.cache.ttl.seconds.getCounterpartiesFromTransaction=10
#connector.cache.ttl.seconds.APIMethods121.getTransactions=10
## MethodRouting cache time-to-live in seconds, caching defaults to 30 seconds
## MethodRouting cache time-to-live in seconds
#methodRouting.cache.ttl.seconds=30
## webui props cache time-to-live in seconds, caching defaults to 20 seconds
## webui props cache time-to-live in seconds
#webui.props.cache.ttl.seconds=20
## enable logging all the database queries in log file

View File

@ -220,7 +220,7 @@ class Boot extends MdcLoggable {
// ensure our relational database's tables are created/fit the schema
val connector = APIUtil.getPropsValue("connector").openOrThrowException("no connector set")
if(connector != "mongodb" || connector == "star")
if(connector != "mongodb")
schemifyAll()
// This sets up MongoDB config (for the mongodb connector)
@ -242,9 +242,11 @@ class Boot extends MdcLoggable {
val actorSystem = ObpActorSystem.startLocalActorSystem()
connector match {
case ("akka_vDec2018"| "star") =>
case "akka_vDec2018" =>
// Start Actor system of Akka connector
ObpActorSystem.startNorthSideAkkaConnectorActorSystem()
case "star" if (APIUtil.getPropsValue("starConnector_supported_types","").split(",").contains("akka")) =>
ObpActorSystem.startNorthSideAkkaConnectorActorSystem()
case _ => // Do nothing
}
@ -336,7 +338,7 @@ class Boot extends MdcLoggable {
WebhookHelperActors.startLocalWebhookHelperWorkers(actorSystem)
if (connector.startsWith("kafka") || connector == "star") {
if (connector.startsWith("kafka") || (connector == "star" && APIUtil.getPropsValue("starConnector_supported_types","").split(",").contains("kafka"))) {
logger.info(s"KafkaHelperActors.startLocalKafkaHelperWorkers( ${actorSystem} ) starting")
KafkaHelperActors.startLocalKafkaHelperWorkers(actorSystem)
// Start North Side Consumer if it's not already started

View File

@ -257,15 +257,15 @@ object APIBuilderModel
def overwriteApiCode(apiSource: Source, jsonFactorySource:Source =jsonFactorySource) = {
//APIMethods_APIBuilder.scala
overwriteCurrentFile(apiSource,"src/main/scala/code/api/builder/APIMethods_APIBuilder.scala")
overwriteCurrentFile(apiSource,"obp-api/src/main/scala/code/api/builder/APIMethods_APIBuilder.scala")
//JsonFactory_APIBuilder.scala
overwriteCurrentFile(jsonFactorySource, "src/main/scala/code/api/builder/JsonFactory_APIBuilder.scala")
overwriteCurrentFile(jsonFactorySource, "obp-api/src/main/scala/code/api/builder/JsonFactory_APIBuilder.scala")
println("Congratulations! You make the new APIs. Please restart OBP-API server!")
}
val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("src/main/scala/code/api/APIBuilder/APIModelSource.json")
val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("obp-api/src/main/scala/code/api/APIBuilder/APIModelSource.json")
//"/templates"
val apiUrl= getApiUrl(jsonJValueFromFile)

View File

@ -36,7 +36,7 @@ object APIBuilder
{
def main(args: Array[String]): Unit = overwriteApiCode(apiSource,jsonFactorySource)
val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("src/main/scala/code/api/APIBuilder/apiResourceDoc/apisResource.json")
val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("obp-api/src/main/scala/code/api/APIBuilder/apiResourceDoc/apisResource.json")
val resourceDocsJObject= jsonJValueFromFile.\("resource_docs").children.asInstanceOf[List[JObject]]
@ -210,7 +210,7 @@ object APIBuilder
cc => {
for {
u <- $getMultipleAuthenticationStatement
jsonStringFromFile = scala.io.Source.fromFile("src/main/scala/code/api/APIBuilder/apisResource.json").mkString
jsonStringFromFile = scala.io.Source.fromFile("obp-api/src/main/scala/code/api/APIBuilder/apisResource.json").mkString
jsonJValueFromFile = json.parse(jsonStringFromFile)
resourceDocsJObject= jsonJValueFromFile.\("resource_docs").children.asInstanceOf[List[JObject]]
getMethodJValue = resourceDocsJObject.filter(jObject => jObject.\("request_verb") == JString("GET")&& !jObject.\("request_url").asInstanceOf[JString].values.contains("_ID")).head
@ -380,7 +380,7 @@ trait APIMethods_APIBuilder
val ImplementationsBuilderAPI = new Object()
{
val apiVersion: ApiVersion = ApiVersion.apiBuilder
val apiVersion = ApiVersion.apiBuilder
val resourceDocs = ArrayBuffer[ResourceDoc]()
val apiRelations = ArrayBuffer[ApiRelation]()
val codeContext = CodeContext(resourceDocs, apiRelations)

View File

@ -35,7 +35,7 @@ import scala.meta._
object APIBuilderSwagger {
def main(args: Array[String]): Unit = overwriteApiCode(apiSource,jsonFactorySource)
val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("src/main/scala/code/api/APIBuilder/swagger/swaggerResource.json")
val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("obp-api/src/main/scala/code/api/APIBuilder/swagger/swaggerResource.json")
val getSingleApiResponseBody: JValue = jsonJValueFromFile \\("foo")\"foo"\"value"
//"template"
@ -328,7 +328,7 @@ trait APIMethods_APIBuilder
val ImplementationsBuilderAPI = new Object()
{
val apiVersion: ApiVersion = ApiVersion.apiBuilder
val apiVersion = ApiVersion.apiBuilder
val resourceDocs = ArrayBuffer[ResourceDoc]()
val apiRelations = ArrayBuffer[ApiRelation]()
val codeContext = CodeContext(resourceDocs, apiRelations)

View File

@ -5,7 +5,7 @@ import java.util.Date
import code.api.Constant
import code.api.UKOpenBanking.v2_0_0.JSONFactory_UKOpenBanking_200
import code.api.UKOpenBanking.v2_0_0.JSONFactory_UKOpenBanking_200.{Account, AccountBalancesUKV200, AccountInner, AccountList, Accounts, BalanceJsonUKV200, BalanceUKOpenBankingJson, BankTransactionCodeJson, CreditLineJson, DataJsonUKV200, Links, MetaBisJson, MetaInnerJson, TransactionCodeJson, TransactionInnerJson, TransactionsInnerJson, TransactionsJsonUKV200}
import code.api.berlin.group.v1.JSONFactory_BERLIN_GROUP_1.{AccountBalance, AccountBalances, AmountOfMoneyV1, ClosingBookedBody, ExpectedBody, TransactionJsonV1, TransactionsJsonV1, ViewAccount}
import code.api.berlin.group.v1.JSONFactory_BERLIN_GROUP_1.{AccountBalanceV1, AccountBalances, AmountOfMoneyV1, Balances, ClosingBookedBody, CoreAccountJsonV1, CoreAccountsJsonV1, ExpectedBody, TransactionJsonV1, Transactions, TransactionsJsonV1, ViewAccount}
import code.api.util.APIUtil.{defaultJValue, _}
import code.api.util.ApiRole._
import code.api.util.{ApiTrigger, ExampleValue}
@ -15,6 +15,8 @@ import code.api.v3_0_0.JSONFactory300.createBranchJsonV300
import code.api.v3_0_0.custom.JSONFactoryCustom300
import code.api.v3_0_0.{LobbyJsonV330, _}
import code.api.v3_1_0.{BadLoginStatusJson, ContactDetailsJson, InviteeJson, ObpApiLoopbackJson, PhysicalCardWithAttributesJsonV310, PutUpdateCustomerEmailJsonV310, _}
import code.api.v3_0_0.{EmptyElasticSearch, LobbyJsonV330, NewModeratedCoreAccountJsonV300, _}
import code.api.v3_1_0.{AccountBalanceV310, AccountsBalancesV310Json, BadLoginStatusJson, ContactDetailsJson, InviteeJson, ObpApiLoopbackJson, PhysicalCardWithAttributesJsonV310, PutUpdateCustomerEmailJsonV310, _}
import code.branches.Branches.{Branch, DriveUpString, LobbyString}
import code.sandbox.SandboxData
import code.transactionrequests.TransactionRequests.TransactionRequestTypes._
@ -25,6 +27,8 @@ import com.openbankproject.commons.model
import com.openbankproject.commons.model.PinResetReason.{FORGOT, GOOD_SECURITY_PRACTICE}
import com.openbankproject.commons.model.{ViewBasic, _}
import com.openbankproject.commons.util.ReflectUtils
import com.openbankproject.commons.model.{AccountBalance, GeoTag, ViewBasic, _}
import net.liftweb.json
import scala.collection.immutable.List
@ -2651,13 +2655,13 @@ object SwaggerDefinitionsJSON {
lastActionDateTime = DateWithDayExampleObject
)
val accountBalance = AccountBalance(
val accountBalanceV1 = AccountBalanceV1(
closingBooked = closingBookedBody,
expected = expectedBody
)
val accountBalances = AccountBalances(
`balances` = List(accountBalance)
`balances` = List(accountBalanceV1)
)
val transactionJsonV1 = TransactionJsonV1(
@ -3414,7 +3418,21 @@ object SwaggerDefinitionsJSON {
val elasticSearchQuery = ElasticSearchQuery(emptyElasticSearch)
val elasticSearchJsonV300 = ElasticSearchJsonV300(elasticSearchQuery)
val accountBalanceV310 = AccountBalanceV310(
id = accountIdExample.value,
label = labelExample.value,
bank_id = bankIdExample.value,
account_routings = List(accountRouting),
balance = amountOfMoney
)
val accountBalancesV310Json = AccountsBalancesV310Json(
accounts = List(accountBalanceV310),
overall_balance = amountOfMoney,
overall_balance_date = DateWithMsExampleObject
)
//The common error or success format.
//Just some helper format to use in Json
case class NoSupportYet()

View File

@ -209,8 +209,8 @@ The TPP sends a request to the ASPSP for retrieving the list of the PSU payment
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u, bankId)
Full(accounts) <- {Connector.connector.vend.getBankAccounts(availablePrivateAccounts,callContext)}
(accounts, callContext)<- NewStyle.function.getBankAccounts(availablePrivateAccounts, callContext)
} yield {
(createTransactionListJSON(accounts), callContext)

View File

@ -57,9 +57,9 @@ object APIMethods_UKOpenBanking_200 extends RestHelper{
for {
(Full(u), callContext) <- authorizedAccess(cc)
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u)
accounts <- {Connector.connector.vend.getBankAccounts(availablePrivateAccounts, callContext)}
(accounts, callContext)<- NewStyle.function.getBankAccounts(availablePrivateAccounts, callContext)
} yield {
(JSONFactory_UKOpenBanking_200.createAccountsListJSON(accounts.getOrElse(Nil)), callContext)
(JSONFactory_UKOpenBanking_200.createAccountsListJSON(accounts), callContext)
}
}
}
@ -144,9 +144,9 @@ object APIMethods_UKOpenBanking_200 extends RestHelper{
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u) map {
_.filter(_.accountId.value == accountId.value)
}
accounts <- {Connector.connector.vend.getBankAccounts(availablePrivateAccounts, callContext)}
(accounts, callContext)<- NewStyle.function.getBankAccounts(availablePrivateAccounts, callContext)
} yield {
(JSONFactory_UKOpenBanking_200.createAccountJSON(accounts.getOrElse(Nil)), callContext)
(JSONFactory_UKOpenBanking_200.createAccountJSON(accounts), callContext)
}
}
}
@ -229,10 +229,10 @@ object APIMethods_UKOpenBanking_200 extends RestHelper{
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u)
accounts <- {Connector.connector.vend.getBankAccounts(availablePrivateAccounts, callContext)}
(accounts, callContext)<- NewStyle.function.getBankAccounts(availablePrivateAccounts, callContext)
} yield {
(JSONFactory_UKOpenBanking_200.createBalancesJSON(accounts.getOrElse(Nil)), callContext)
(JSONFactory_UKOpenBanking_200.createBalancesJSON(accounts), callContext)
}
}
}

View File

@ -2,7 +2,7 @@ package code.api.UKOpenBanking.v3_1_0
import code.api.berlin.group.v1_3.JvalueCaseClass
import code.api.util.APIUtil._
import code.api.util.ApiTag
import code.api.util.{ApiTag, NewStyle}
import code.api.util.ApiTag._
import code.api.util.ErrorMessages._
import code.bankconnectors.Connector
@ -110,9 +110,9 @@ object APIMethods_AccountsApi extends RestHelper {
for {
(Full(u), callContext) <- authorizedAccess(cc)
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u)
accounts <- {Connector.connector.vend.getBankAccounts(availablePrivateAccounts, callContext)}
(accounts, callContext)<- NewStyle.function.getBankAccounts(availablePrivateAccounts, callContext)
} yield {
(JSONFactory_UKOpenBanking_310.createAccountsListJSON(accounts.getOrElse(Nil)), callContext)
(JSONFactory_UKOpenBanking_310.createAccountsListJSON(accounts), callContext)
}
}
@ -202,9 +202,9 @@ object APIMethods_AccountsApi extends RestHelper {
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u) map {
_.filter(_.accountId.value == accountId.value)
}
accounts <- {Connector.connector.vend.getBankAccounts(availablePrivateAccounts, callContext)}
(accounts, callContext)<- NewStyle.function.getBankAccounts(availablePrivateAccounts, callContext)
} yield {
(JSONFactory_UKOpenBanking_310.createAccountJSON(accounts.getOrElse(Nil)), callContext)
(JSONFactory_UKOpenBanking_310.createAccountJSON(accounts), callContext)
}
}
}

View File

@ -222,10 +222,10 @@ object APIMethods_BalancesApi extends RestHelper {
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u)
accounts <- {Connector.connector.vend.getBankAccounts(availablePrivateAccounts, callContext)}
(accounts, callContext)<- NewStyle.function.getBankAccounts(availablePrivateAccounts, callContext)
} yield {
(JSONFactory_UKOpenBanking_310.createBalancesJSON(accounts.getOrElse(Nil)), callContext)
(JSONFactory_UKOpenBanking_310.createBalancesJSON(accounts), callContext)
}
}
}

View File

@ -1022,7 +1022,7 @@ object APIMethods_TransactionsApi extends RestHelper {
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u)
Full(accounts) <- Connector.connector.vend.getBankAccounts(availablePrivateAccounts, callContext)
(accounts, callContext)<- NewStyle.function.getBankAccounts(availablePrivateAccounts, callContext)
transactionAndTransactionRequestTuple = for{
bankAccount <- accounts

View File

@ -44,11 +44,11 @@ object JSONFactory_BERLIN_GROUP_1 extends CustomJsonFormats {
amount : AmountOfMoneyV1,
lastActionDateTime: Date
)
case class AccountBalance(
case class AccountBalanceV1(
closingBooked: ClosingBookedBody,
expected: ExpectedBody
)
case class AccountBalances(`balances`: List[AccountBalance])
case class AccountBalances(`balances`: List[AccountBalanceV1])
case class TransactionsJsonV1(
transactions_booked: List[TransactionJsonV1],
@ -93,7 +93,7 @@ object JSONFactory_BERLIN_GROUP_1 extends CustomJsonFormats {
val sumOfAll = (BigDecimal(moderatedAccount.balance) + sumOfAllUncompletedTransactionrequests).toString()
AccountBalances(
AccountBalance(
AccountBalanceV1(
closingBooked = ClosingBookedBody(
amount = AmountOfMoneyV1(currency = moderatedAccount.currency.getOrElse(""), content = moderatedAccount.balance),
date = APIUtil.DateWithDayFormat.format(latestCompletedEndDate)

View File

@ -270,10 +270,10 @@ of the PSU at this ASPSP.
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u, bankId)
Full((coreAccounts)) <- {Connector.connector.vend.getBankAccounts(availablePrivateAccounts, callContext)}
(accounts, callContext)<- NewStyle.function.getBankAccounts(availablePrivateAccounts, callContext)
} yield {
(JSONFactory_BERLIN_GROUP_1_3.createAccountListJson(coreAccounts, u), callContext)
(JSONFactory_BERLIN_GROUP_1_3.createAccountListJson(accounts, u), callContext)
}
}
}

View File

@ -1,10 +1,9 @@
package code.api.builder
import java.util.UUID
import code.api.builder.JsonFactory_APIBuilder._
import code.api.util.APIUtil._
import code.api.util.ApiTag._
import code.api.util.{ApiVersion, CustomJsonFormats}
import code.api.util.ApiVersion
import code.api.util.ErrorMessages._
import net.liftweb.common.Full
import net.liftweb.http.rest.RestHelper
@ -13,7 +12,6 @@ import net.liftweb.json.Extraction._
import net.liftweb.json._
import net.liftweb.mapper.By
import net.liftweb.util.Helpers.tryo
import scala.collection.immutable.Nil
import scala.collection.mutable.ArrayBuffer
trait APIMethods_APIBuilder { self: RestHelper =>
@ -22,10 +20,10 @@ trait APIMethods_APIBuilder { self: RestHelper =>
val resourceDocs = ArrayBuffer[ResourceDoc]()
val apiRelations = ArrayBuffer[ApiRelation]()
val codeContext = CodeContext(resourceDocs, apiRelations)
implicit val formats = CustomJsonFormats.formats
val TemplateNotFound = "OBP-31001: Template not found. Please specify a valid value for TEMPLATE_ID."
def endpointsOfBuilderAPI = getTemplates :: getTemplate :: createTemplate :: deleteTemplate :: Nil
resourceDocs += ResourceDoc(getTemplates, apiVersion, "getTemplates", "GET", "/templates", "Get Templates", "Return All Templates", emptyObjectJson, templatesJson, List(UserNotLoggedIn, UnknownError), Catalogs(notCore, notPSD2, notOBWG), apiTagApiBuilder :: Nil)
implicit val formats = code.api.util.CustomJsonFormats.formats
val TemplatesNotFound = "OBP-31001: Templates not found. Please specify a valid value for TEMPLATES_ID."
def endpointsOfBuilderAPI = getTemplates :: createTemplate :: getTemplate :: deleteTemplate :: Nil
resourceDocs += ResourceDoc(getTemplates, apiVersion, "getTemplates", "GET", "/templates", "Get Templates", "Return All my templates. Authentication is Mandatory.", emptyObjectJson, templatesJson, List(UserNotLoggedIn, UnknownError), Catalogs(notCore, notPSD2, notOBWG), apiTagApiBuilder :: Nil)
lazy val getTemplates: OBPEndpoint = {
case ("templates" :: Nil) JsonGet req =>
cc => {
@ -34,16 +32,7 @@ trait APIMethods_APIBuilder { self: RestHelper =>
}
}
}
resourceDocs += ResourceDoc(getTemplate, apiVersion, "getTemplate", "GET", "/templates/TEMPLATE_ID", "Get Template", "Return One Template By Id", emptyObjectJson, templateJson, List(UserNotLoggedIn, UnknownError), Catalogs(notCore, notPSD2, notOBWG), apiTagApiBuilder :: Nil)
lazy val getTemplate: OBPEndpoint = {
case ("templates" :: templateId :: Nil) JsonGet _ =>
cc => {
for (u <- cc.user ?~ UserNotLoggedIn; template <- APIBuilder_Connector.getTemplateById(templateId) ?~! TemplateNotFound; templateJson = JsonFactory_APIBuilder.createTemplate(template); jsonObject: JValue = decompose(templateJson)) yield {
successJsonResponse(jsonObject)
}
}
}
resourceDocs += ResourceDoc(createTemplate, apiVersion, "createTemplate", "POST", "/templates", "Create Template", "Create One Template", createTemplateJson, templateJson, List(UnknownError), Catalogs(notCore, notPSD2, notOBWG), apiTagApiBuilder :: Nil)
resourceDocs += ResourceDoc(createTemplate, apiVersion, "createTemplate", "POST", "/templates", "Create Template", "Create template. Authentication is Mandatory.", createTemplateJson, templateJson, List(UnknownError), Catalogs(notCore, notPSD2, notOBWG), apiTagApiBuilder :: Nil)
lazy val createTemplate: OBPEndpoint = {
case ("templates" :: Nil) JsonPost json -> _ =>
cc => {
@ -52,15 +41,20 @@ trait APIMethods_APIBuilder { self: RestHelper =>
}
}
}
resourceDocs += ResourceDoc(deleteTemplate, apiVersion, "deleteTemplate", "DELETE", "/templates/TEMPLATE_ID", "Delete Template", "Delete One Template", emptyObjectJson, emptyObjectJson.copy("true"), List(UserNotLoggedIn, UnknownError), Catalogs(notCore, notPSD2, notOBWG), apiTagApiBuilder :: Nil)
resourceDocs += ResourceDoc(getTemplate, apiVersion, "getTemplate", "GET", "/templates/{template_id}", "Get Template", "Return one template. Authentication is Mandatory.", emptyObjectJson, templateJson, List(UserNotLoggedIn, UnknownError), Catalogs(notCore, notPSD2, notOBWG), apiTagApiBuilder :: Nil)
lazy val getTemplate: OBPEndpoint = {
case ("templates" :: templateId :: Nil) JsonGet _ =>
cc => {
for (u <- cc.user ?~ UserNotLoggedIn; template <- APIBuilder_Connector.getTemplateById(templateId) ?~! TemplatesNotFound; templateJson = JsonFactory_APIBuilder.createTemplate(template); jsonObject: JValue = decompose(templateJson)) yield {
successJsonResponse(jsonObject)
}
}
}
resourceDocs += ResourceDoc(deleteTemplate, apiVersion, "deleteTemplate", "DELETE", "/templates/{template_id}", "Delete Template", "Delete template. Authentication is Mandatory.", emptyObjectJson, emptyObjectJson.copy("true"), List(UserNotLoggedIn, UnknownError), Catalogs(notCore, notPSD2, notOBWG), apiTagApiBuilder :: Nil)
lazy val deleteTemplate: OBPEndpoint = {
case ("templates" :: templateId :: Nil) JsonDelete _ =>
cc => {
for (
u <- cc.user ?~ UserNotLoggedIn;
template <- APIBuilder_Connector.getTemplateById(templateId) ?~! TemplateNotFound;
deleted <- APIBuilder_Connector.deleteTemplate(templateId)
) yield {
for (u <- cc.user ?~ UserNotLoggedIn; template <- APIBuilder_Connector.getTemplateById(templateId) ?~! TemplatesNotFound; deleted <- APIBuilder_Connector.deleteTemplate(templateId)) yield {
if (deleted) noContentJsonResponse else errorJsonResponse("Delete not completed")
}
}
@ -68,25 +62,25 @@ trait APIMethods_APIBuilder { self: RestHelper =>
}
}
object APIBuilder_Connector {
val allAPIBuilderModels = List(MappedTemplate_4824100653501473508)
def createTemplate(createTemplateJson: CreateTemplateJson) = Full(MappedTemplate_4824100653501473508.create.mTemplateId(generateUUID()).mAuthor(createTemplateJson.author).mPages(createTemplateJson.pages).mPoints(createTemplateJson.points).saveMe())
def getTemplates() = Full(MappedTemplate_4824100653501473508.findAll())
def getTemplateById(templateId: String) = MappedTemplate_4824100653501473508.find(By(MappedTemplate_4824100653501473508.mTemplateId, templateId))
def deleteTemplate(templateId: String) = MappedTemplate_4824100653501473508.find(By(MappedTemplate_4824100653501473508.mTemplateId, templateId)).map(_.delete_!)
val allAPIBuilderModels = List(MappedTemplates_430794570390706560)
def createTemplate(createTemplateJson: CreateTemplateJson) = Full(MappedTemplates_430794570390706560.create.mTemplateId(UUID.randomUUID().toString).mAuthor(createTemplateJson.author).mPages(createTemplateJson.pages).mPoints(createTemplateJson.points).saveMe())
def getTemplates() = Full(MappedTemplates_430794570390706560.findAll())
def getTemplateById(templateId: String) = MappedTemplates_430794570390706560.find(By(MappedTemplates_430794570390706560.mTemplateId, templateId))
def deleteTemplate(templateId: String) = MappedTemplates_430794570390706560.find(By(MappedTemplates_430794570390706560.mTemplateId, templateId)).map(_.delete_!)
}
import net.liftweb.mapper._
class MappedTemplate_4824100653501473508 extends Template with LongKeyedMapper[MappedTemplate_4824100653501473508] with IdPK {
class MappedTemplates_430794570390706560 extends Template with LongKeyedMapper[MappedTemplates_430794570390706560] with IdPK {
object mAuthor extends MappedString(this, 100)
override def author: String = mAuthor.get
object mPages extends MappedInt(this)
override def pages: Int = mPages.get
object mPoints extends MappedDouble(this)
override def points: Double = mPoints.get
def getSingleton = MappedTemplate_4824100653501473508
def getSingleton = MappedTemplates_430794570390706560
object mTemplateId extends MappedString(this, 100)
override def templateId: String = mTemplateId.get
}
object MappedTemplate_4824100653501473508 extends MappedTemplate_4824100653501473508 with LongKeyedMetaMapper[MappedTemplate_4824100653501473508]
object MappedTemplates_430794570390706560 extends MappedTemplates_430794570390706560 with LongKeyedMetaMapper[MappedTemplates_430794570390706560]
trait Template { `_` =>
def author: String
def pages: Int

View File

@ -1,7 +1,7 @@
package code.api.builder
import code.api.util.APIUtil
case class TemplateJson(id: String = """11231231312""", author: String = """Chinua Achebe""", pages: Int = 209, points: Double = 1.3)
case class CreateTemplateJson(author: String = """Chinua Achebe""", pages: Int = 209, points: Double = 1.3)
case class TemplateJson(template_id: String = """11231231312""", author: String = """Chinua Achebe""", pages: Int = 209, points: Double = 1.3)
object JsonFactory_APIBuilder {
val templateJson = TemplateJson()
val templatesJson = List(templateJson)

View File

@ -2496,6 +2496,18 @@ Returns a string showed to the developer
def createBasicUserAuthContextJson(userAuthContexts : List[UserAuthContext]) : List[BasicUserAuthContext] = {
userAuthContexts.map(createBasicUserAuthContext)
}
def createBasicUserAuthContextJsonFromCallContext(callContext : CallContext) : List[BasicGeneralContext] = {
val requestHeaders: List[HTTPParam] = callContext.requestHeaders
//Only these Pass-Through headers can be propagated to Adapter side.
val passThroughHeaders: List[HTTPParam] = requestHeaders
.filter(requestHeader => requestHeader.name.startsWith("Pass-Through"))
passThroughHeaders.map(header => BasicGeneralContext(
key = header.name,
value = if (header.values.isEmpty) "" else header.values.head))
}
def createAuthInfoCustomerJson(customer : Customer) : InternalBasicCustomer = {
InternalBasicCustomer(

View File

@ -57,8 +57,10 @@ case class CallContext(
views <- tryo(permission.views)
linkedCustomers <- tryo(CustomerX.customerProvider.vend.getCustomersByUserId(user.userId))
likedCustomersBasic = if (linkedCustomers.isEmpty) None else Some(createInternalLinkedBasicCustomersJson(linkedCustomers))
userAuthContexts<- UserAuthContextProvider.userAuthContextProvider.vend.getUserAuthContextsBox(user.userId)
basicUserAuthContexts = if (userAuthContexts.isEmpty) None else Some(createBasicUserAuthContextJson(userAuthContexts))
userAuthContexts<- UserAuthContextProvider.userAuthContextProvider.vend.getUserAuthContextsBox(user.userId)
basicUserAuthContextsFromDatabase = if (userAuthContexts.isEmpty) None else Some(createBasicUserAuthContextJson(userAuthContexts))
generalContextFromPassThroughHeaders = createBasicUserAuthContextJsonFromCallContext(this)
basicUserAuthContexts = Some(basicUserAuthContextsFromDatabase.getOrElse(List.empty[BasicUserAuthContext]))
authViews<- tryo(
for{
view <- views
@ -79,7 +81,7 @@ case class CallContext(
correlationId = this.correlationId,
sessionId = this.sessionId,
consumerId = Some(consumerId),
generalContext = None,
generalContext = Some(generalContextFromPassThroughHeaders),
outboundAdapterAuthInfo = Some(OutboundAdapterAuthInfo(
userId = currentResourceUserId,
username = username,

View File

@ -204,6 +204,7 @@ object NewStyle {
(nameOf(Implementations3_1_0.createAccount), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.saveHistoricalTransaction), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.getPrivateAccountByIdFull), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.getBankAccountsBalances), ApiVersion.v3_1_0.toString),
(nameOf(Implementations4_0_0.getBanks), ApiVersion.v4_0_0.toString)
)
@ -286,12 +287,27 @@ object NewStyle {
connectorEmptyResponse(_, callContext)
}
}
def getBalances(callContext: Option[CallContext]) : OBPReturnType[List[Bank]] = {
Connector.connector.vend.getBanks(callContext: Option[CallContext]) map {
connectorEmptyResponse(_, callContext)
}
}
def getBankAccount(bankId : BankId, accountId : AccountId, callContext: Option[CallContext]): OBPReturnType[BankAccount] = {
Connector.connector.vend.getBankAccount(bankId, accountId, callContext) map { i =>
(unboxFullOrFail(i._1, callContext,s"$BankAccountNotFound Current BankId is $bankId and Current AccountId is $accountId", 400 ), i._2)
}
}
def getBankAccounts(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]): OBPReturnType[List[BankAccount]] = {
Connector.connector.vend.getBankAccounts(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) map { i =>
(unboxFullOrFail(i._1, callContext,s"$InvalidConnectorResponseForGetBankAccounts", 400 ), i._2)
}
}
def getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]): OBPReturnType[AccountsBalances] = {
Connector.connector.vend.getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) map { i =>
(unboxFullOrFail(i._1, callContext,s"$InvalidConnectorResponseForGetBankAccounts", 400 ), i._2)
}
}
def getBankAccountByIban(iban : String, callContext: Option[CallContext]) : OBPReturnType[BankAccount] = {
Connector.connector.vend.getBankAccountByIban(iban : String, callContext: Option[CallContext]) map { i =>
@ -1507,7 +1523,7 @@ object NewStyle {
}
}
private[this] val methodRoutingTTL = APIUtil.getPropsValue(s"methodRouting.cache.ttl.seconds", "30").toInt // default 30 seconds
private[this] val methodRoutingTTL = APIUtil.getPropsValue(s"methodRouting.cache.ttl.seconds", "0").toInt
def getMethodRoutings(methodName: Option[String], isBankIdExactMatch: Option[Boolean] = None, bankIdPattern: Option[String] = None): List[MethodRoutingT] = {
import scala.concurrent.duration._

View File

@ -1,11 +1,9 @@
package code.api.v3_1_0
import java.text.SimpleDateFormat
import java.util.{Date, UUID}
import java.util.UUID
import java.util.regex.Pattern
import code.accountholders.AccountHolders
import code.api.{APIFailureNewStyle, ChargePolicy}
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
import code.api.ResourceDocs1_4_0.{MessageDocsSwaggerDefinitions, SwaggerDefinitionsJSON, SwaggerJSONFactory}
import code.api.util.APIUtil._
@ -16,16 +14,13 @@ import code.api.util.ExampleValue._
import code.api.util.NewStyle.HttpCode
import code.api.util._
import code.api.v1_2_1.{JSONFactory, RateLimiting}
import code.api.v1_3_0.{JSONFactory1_3_0, PostPhysicalCardJSON}
import code.api.v1_4_0.JSONFactory1_4_0.TransactionRequestAccountJsonV140
import code.api.v2_0_0.CreateMeetingJson
import code.api.v2_1_0._
import code.api.v2_2_0.{CreateAccountJSONV220, JSONFactory220}
import code.api.v3_0_0.JSONFactory300
import code.api.v3_0_0.JSONFactory300.{createAdapterInfoJson, createCoreBankAccountJSON}
import code.api.v3_0_0.JSONFactory300.{createAdapterInfoJson}
import code.api.v3_1_0.JSONFactory310._
import code.bankconnectors.Connector
import code.bankconnectors.akka.AkkaConnector_vDec2018
import code.bankconnectors.rest.RestConnector_vMar2019
import code.consent.{ConsentStatus, Consents}
import code.consumer.Consumers
@ -33,31 +28,29 @@ import code.context.{UserAuthContextUpdateProvider, UserAuthContextUpdateStatus}
import code.entitlement.Entitlement
import code.kafka.KafkaHelper
import code.loginattempts.LoginAttempt
import code.methodrouting.MethodRoutingCommons
import code.methodrouting.{MethodRoutingCommons, MethodRoutingParam}
import code.metrics.APIMetrics
import code.model._
import code.model.dataAccess.{AuthUser, BankAccountCreation}
import code.transactionrequests.TransactionRequests.TransactionRequestTypes
import code.transactionrequests.TransactionRequests.TransactionRequestTypes.{COUNTERPARTY, FREE_FORM, SANDBOX_TAN, SEPA}
import com.openbankproject.commons.model.Product
import code.users.Users
import code.util.Helper
import code.views.Views
import code.webhook.AccountWebhook
import code.webuiprops.{MappedWebUiPropsProvider, WebUiPropsCommons, WebUiPropsProvider}
import code.webuiprops.{MappedWebUiPropsProvider, WebUiPropsCommons}
import com.github.dwickern.macros.NameOf.nameOf
import com.nexmo.client.NexmoClient
import com.nexmo.client.sms.messages.TextMessage
import com.openbankproject.commons.model.{CreditLimit, _}
import net.liftweb.common.{Box, Empty, Failure, Full}
import net.liftweb.common.{Box, Empty, Full}
import net.liftweb.http.provider.HTTPParam
import net.liftweb.http.rest.RestHelper
import net.liftweb.json.Serialization.write
import net.liftweb.json._
import net.liftweb.util.Helpers.tryo
import net.liftweb.util.Mailer.{From, PlainMailBodyType, Subject, To}
import net.liftweb.util.{Helpers, Mailer, Props}
import org.apache.commons.lang3.Validate
import net.liftweb.util.{Helpers, Mailer}
import org.apache.commons.lang3.{StringUtils, Validate}
import scala.collection.immutable.{List, Nil}
import scala.collection.mutable.ArrayBuffer
@ -3930,7 +3923,7 @@ trait APIMethods310 {
emptyObjectJson,
ListResult(
"method_routings",
(List(MethodRoutingCommons("getBanks", "rest_vMar2019", false, Some("some_bank_.*"), Some("method-routing-id"))))
(List(MethodRoutingCommons("getBanks", "rest_vMar2019", false, Some("some_bank_.*"), Some(List(MethodRoutingParam("url", "http://mydomain.com/xxx"))), Some("method-routing-id"))))
)
,
List(
@ -3976,12 +3969,17 @@ trait APIMethods310 {
|* connector_name is required String value
|* is_bank_id_exact_match is required boolean value, if bank_id_pattern is exact bank_id value, this value is true; if bank_id_pattern is null or a regex, this value is false
|* bank_id_pattern is optional String value, it can be null, a exact bank_id or a regex
|* parameters is optional array of key value pairs. You can set some paremeters for this method
|
|note: if bank_id_pattern is regex, special characters need to do escape, for example:
|bank_id_pattern = "some\\-id_pattern_\\d+"
|note:
|
|* if bank_id_pattern is regex, special characters need to do escape, for example: bank_id_pattern = "some\\-id_pattern_\\d+"
|""",
MethodRoutingCommons("getBank", "rest_vMar2019", false, Some("some_bankId_.*")),
MethodRoutingCommons("getBank", "rest_vMar2019", false, Some("some_bankId_.*"), Some("this-method-routing-Id")),
MethodRoutingCommons("getBank", "rest_vMar2019", false, Some("some_bankId_.*"), Some(List(MethodRoutingParam("url", "http://mydomain.com/xxx")))),
MethodRoutingCommons("getBank", "rest_vMar2019", false, Some("some_bankId_.*"),
Some(List(MethodRoutingParam("url", "http://mydomain.com/xxx"))),
Some("this-method-routing-Id")
),
List(
UserNotLoggedIn,
UserHasMissingRoles,
@ -4036,13 +4034,13 @@ trait APIMethods310 {
|* connector_name is required String value
|* is_bank_id_exact_match is required boolean value, if bank_id_pattern is exact bank_id value, this value is true; if bank_id_pattern is null or a regex, this value is false
|* bank_id_pattern is optional String value, it can be null, a exact bank_id or a regex
|* parameters is optional array of key value pairs. You can set some paremeters for this method
|note:
|
|note: if bank_id_pattern is regex, special characters need to do escape, for example:
|bank_id_pattern = "some\\-id_pattern_\\d+"
|
|* if bank_id_pattern is regex, special characters need to do escape, for example: bank_id_pattern = "some\\-id_pattern_\\d+"
|""",
MethodRoutingCommons("getBank", "rest_vMar2019", true, Some("some_bankId"), None),
MethodRoutingCommons("getBank", "rest_vMar2019", true, Some("some_bankId"), None),
MethodRoutingCommons("getBank", "rest_vMar2019", true, Some("some_bankId"), Some(List(MethodRoutingParam("url", "http://mydomain.com/xxx")))),
MethodRoutingCommons("getBank", "rest_vMar2019", true, Some("some_bankId"),Some(List(MethodRoutingParam("url", "http://mydomain.com/xxx"))), Some("this-method-routing-Id")),
List(
UserNotLoggedIn,
UserHasMissingRoles,
@ -4061,21 +4059,21 @@ trait APIMethods310 {
_ <- NewStyle.function.hasEntitlement("", u.userId, canUpdateMethodRouting, callContext)
failMsg = s"$InvalidJsonFormat The Json body should be the ${classOf[MethodRoutingCommons]} "
postedData <- NewStyle.function.tryons(failMsg, 400, callContext) {
putData <- NewStyle.function.tryons(failMsg, 400, callContext) {
json.extract[MethodRoutingCommons].copy(methodRoutingId = Some(methodRoutingId))
}
(_, _) <- NewStyle.function.getMethodRoutingById(methodRoutingId, callContext)
invalidRegexMsg = s"$InvalidBankIdRegex The bankIdPattern is invalid regex, bankIdPatten: ${postedData.bankIdPattern.orNull} "
invalidRegexMsg = s"$InvalidBankIdRegex The bankIdPattern is invalid regex, bankIdPatten: ${putData.bankIdPattern.orNull} "
_ <- NewStyle.function.tryons(invalidRegexMsg, 400, callContext) {
// if do fuzzy match and bankIdPattern not empty, do check the regex is valid
if(!postedData.isBankIdExactMatch && postedData.bankIdPattern.isDefined) {
Pattern.compile(postedData.bankIdPattern.get)
if(!putData.isBankIdExactMatch && putData.bankIdPattern.isDefined) {
Pattern.compile(putData.bankIdPattern.get)
}
}
Full(methodRouting) <- NewStyle.function.createOrUpdateMethodRouting(postedData)
Full(methodRouting) <- NewStyle.function.createOrUpdateMethodRouting(putData)
} yield {
val commonsData: MethodRoutingCommons = methodRouting
(commonsData, HttpCode.`200`(callContext))
@ -5450,6 +5448,35 @@ trait APIMethods310 {
}
}
resourceDocs += ResourceDoc(
getBankAccountsBalances,
implementedInApiVersion,
nameOf(getBankAccountsBalances),
"GET",
"/banks/BANK_ID/balances",
"Get Accounts Balances",
"""Get the Balances for the Accounts of the current User at one bank.""",
emptyObjectJson,
accountBalancesV310Json,
List(UnknownError),
Catalogs(Core, PSD2, OBWG),
apiTagAccount :: apiTagPSD2AIS :: apiTagNewStyle :: Nil
)
lazy val getBankAccountsBalances : OBPEndpoint = {
case "banks" :: BankId(bankId) :: "balances" :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc)
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u, bankId)
(accountsBalances, callContext)<- NewStyle.function.getBankAccountsBalances(availablePrivateAccounts, callContext)
} yield{
(createBalancesJson(accountsBalances), HttpCode.`200`(callContext))
}
}
}
}
}

View File

@ -34,7 +34,7 @@ import code.api.util.APIUtil.{stringOptionOrNull, stringOrNull}
import code.api.util.RateLimitPeriod.LimitCallPeriod
import code.api.util.{APIUtil, RateLimitPeriod}
import code.api.v1_2_1.JSONFactory.{createAmountOfMoneyJSON, createOwnersJSON}
import code.api.v1_2_1.{RateLimiting, UserJSONV121, ViewJSONV121}
import code.api.v1_2_1.{BankJSON, RateLimiting, UserJSONV121, ViewJSONV121}
import code.api.v1_3_0.JSONFactory1_3_0._
import code.api.v1_3_0.{PinResetJSON, ReplacementJSON}
import code.api.v1_4_0.JSONFactory1_4_0.{CustomerFaceImageJson, MetaJsonV140, TransactionRequestAccountJsonV140}
@ -42,7 +42,7 @@ import code.api.v2_0_0.{MeetingKeysJson, MeetingPresentJson}
import code.api.v2_1_0.JSONFactory210.createLicenseJson
import code.api.v2_1_0.{CustomerCreditRatingJSON, ResourceUserJSON}
import code.api.v2_2_0._
import code.api.v3_0_0.AccountRuleJsonV300
import code.api.v3_0_0.{AccountRuleJsonV300, ViewBasicV300}
import code.api.v3_0_0.JSONFactory300.{createAccountRoutingsJSON, createAccountRulesJSON}
import code.consent.MappedConsent
import code.context.UserAuthContextUpdate
@ -701,6 +701,24 @@ case class PostHistoricalTransactionResponseJson(
charge_policy: String
)
case class AccountsBalancesV310Json(
accounts:List[AccountBalanceV310],
overall_balance: AmountOfMoney,
overall_balance_date: Date
)
case class AccountBalanceV310(
id: String,
label: String,
bank_id: String,
account_routings: List[AccountRouting],
balance: AmountOfMoney
)
case class AccountBalancesJson(
accounts: List[AccountBalanceV310],
overall_balance: AmountOfMoney,
overall_balance_date: Date
)
object JSONFactory310{
def createCheckbookOrdersJson(checkbookOrders: CheckbookOrdersJson): CheckbookOrdersJson =
@ -1309,5 +1327,19 @@ object JSONFactory310{
)
}
def createBalancesJson(accountsBalances: AccountsBalances) = {
AccountsBalancesV310Json(
accounts = accountsBalances.accounts.map(account => AccountBalanceV310(
account.id,
account.label,
account.bankId,
account.accountRoutings,
account.balance)
),
overall_balance = accountsBalances.overallBalance,
overall_balance_date = accountsBalances.overallBalanceDate
)
}
}

View File

@ -376,6 +376,7 @@ object OBPAPI3_1_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w
Implementations3_1_0.getWebUiProps ::
Implementations3_1_0.createWebUiProps ::
Implementations3_1_0.deleteWebUiProps ::
Implementations3_1_0.getBankAccountsBalances ::
Nil
val allResourceDocs = Implementations3_1_0.resourceDocs ++

View File

@ -377,6 +377,7 @@ object OBPAPI4_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w
Implementations3_1_0.getWebUiProps ::
Implementations3_1_0.createWebUiProps ::
Implementations3_1_0.deleteWebUiProps ::
Implementations3_1_0.getBankAccountsBalances ::
Nil

View File

@ -273,7 +273,9 @@ trait Connector extends MdcLoggable with CustomJsonFormats{
def getBankAccountByIban(iban : String, callContext: Option[CallContext]) : OBPReturnType[Box[BankAccount]]= Future{(Failure(setUnimplementedError),callContext)}
def getBankAccountByRouting(scheme : String, address : String, callContext: Option[CallContext]) : Box[(BankAccount, Option[CallContext])]= Failure(setUnimplementedError)
def getBankAccounts(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) : Future[Box[List[BankAccount]]]= Future{Failure(setUnimplementedError)}
def getBankAccounts(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) : OBPReturnType[Box[List[BankAccount]]]= Future{(Failure(setUnimplementedError), callContext)}
def getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) : OBPReturnType[Box[AccountsBalances]]= Future{(Failure(setUnimplementedError), callContext)}
def getCoreBankAccountsLegacy(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) : Box[(List[CoreAccount], Option[CallContext])] =
Failure(setUnimplementedError)

View File

@ -5,7 +5,7 @@ import code.api.util.APIUtil.{OBPEndpoint, _}
import code.api.util.NewStyle.HttpCode
import code.api.util.{APIUtil, CallContext, OBPQueryParam}
import code.api.v3_1_0.OBPAPI3_1_0.oauthServe
import com.openbankproject.commons.model.{AccountId, BankId, BankIdAccountId, InboundAdapterCallContext}
import com.openbankproject.commons.model._
import com.openbankproject.commons.util.ReflectUtils
import com.openbankproject.commons.util.ReflectUtils.{getType, toValueObject}
import net.liftweb.common.{Box, Empty, Failure, Full}
@ -55,7 +55,7 @@ object ConnectorEndpoints extends RestHelper{
inboundAdapterCallContextValue = InboundAdapterCallContext(cc.correlationId)
} yield {
// NOTE: if any filed type is BigDecimal, it is can't be serialized by lift json
val json = Map((inboundAdapterCallContextKey, inboundAdapterCallContextValue),("data", toValueObject(data)))
val json = Map((inboundAdapterCallContextKey, inboundAdapterCallContextValue),("status", Status("",List(InboundStatusMessage("","","","")))),("data", toValueObject(data)))
(json, HttpCode.`200`(cc))
}
}

View File

@ -11,6 +11,7 @@ import code.api.cache.Caching
import code.api.util.APIUtil.{OBPReturnType, isValidCurrencyISOCode, saveConnectorMetric, stringOrNull}
import code.api.util.ErrorMessages._
import code.api.util._
import code.api.v3_1_0.AccountBalanceV310
import code.atms.Atms.Atm
import code.atms.MappedAtm
import code.bankconnectors.vJune2017.InboundAccountJune2017
@ -437,19 +438,61 @@ object LocalMappedConnector extends Connector with MdcLoggable {
).map(bankAccount => (bankAccount, callContext))
}
override def getBankAccounts(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) : Future[Box[List[BankAccount]]] = {
override def getBankAccounts(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) = {
Future {
Full(
(Full(
bankIdAccountIds.map(
bankIdAccountId =>
getBankAccount(
bankIdAccountId.bankId,
bankIdAccountId.accountId
).openOrThrowException(s"${ErrorMessages.BankAccountNotFound} current BANK_ID(${bankIdAccountId.bankId}) and ACCOUNT_ID(${bankIdAccountId.accountId})"))
)
),callContext)
}
}
override def getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) =
Future {
val accountsBalances = for{
bankIdAccountId <- bankIdAccountIds
bankAccount <- getBankAccount(bankIdAccountId.bankId, bankIdAccountId.accountId) ?~! s"${ErrorMessages.BankAccountNotFound} current BANK_ID(${bankIdAccountId.bankId}) and ACCOUNT_ID(${bankIdAccountId.accountId})"
accountBalance = AccountBalance(
id = bankAccount.accountId.value,
label = bankAccount.label,
bankId = bankAccount.bankId.value,
accountRoutings = bankAccount.accountRoutings.map(accountRounting => AccountRouting(accountRounting.scheme, accountRounting.address)),
balance = AmountOfMoney(bankAccount.currency, bankAccount.balance.toString())
)
} yield {
(accountBalance)
}
val allCurrencies = accountsBalances.map(_.balance.currency)
val mostCommonCurrency = if (allCurrencies.isEmpty) "EUR" else allCurrencies.groupBy(identity).mapValues(_.size).maxBy(_._2)._1
val allCommonCurrencyBalances = for {
accountBalance <- accountsBalances
requestAccountCurrency = accountBalance.balance.currency
requestAccountAmount = BigDecimal(accountBalance.balance.amount)
//From change from requestAccount Currency to mostCommon Currency
rate <- fx.exchangeRate(requestAccountCurrency, mostCommonCurrency)
requestChangedCurrencyAmount = fx.convert(requestAccountAmount, Some(rate))
}yield {
requestChangedCurrencyAmount
}
val overallBalance = allCommonCurrencyBalances.sum
(Full(AccountsBalances(
accounts = accountsBalances,
overallBalance = AmountOfMoney(
mostCommonCurrency,
overallBalance.toString
),
overallBalanceDate = now
)), callContext)
}
override def checkBankAccountExistsLegacy(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]) = {
getBankAccountLegacy(bankId: BankId, accountId: AccountId, callContext)
}

View File

@ -17,24 +17,22 @@ import code.api.util.CodeGenerateUtils.createDocExample
object RestConnectorBuilder extends App {
val genMethodNames1 = List(
val genMethodNames = List(
// "getAdapterInfo",
"getAdapterInfo",
// "getAdapterInfo",
// "getUser", // have problem, return type not common
// "getBanks",
"getBanksFuture",
// "getBank",
"getBankFuture",
// "getBankAccountsForUser",
"getBankAccountsForUserFuture",
"getCustomersByUserIdFuture",
// "getBankAccount",
// "checkBankAccountExists",
"checkBankAccountExistsFuture",
"getBankAccountsBalances",
// "getBanksLegacy",
// "getBank",
// "getBankLegacy",
// "getBankAccountsForUser",
// "getCustomersByUserIdFuture",
// "getBankAccount",
// "checkBankAccountExists",
// "getCoreBankAccounts",
"getCoreBankAccountsFuture",
// "getCoreBankAccountsFuture",
// "getTransactions",
"getTransactionsCore",
// "getTransactionsCore",
// "getTransaction",
// "getTransactionRequests210", //have problem params are not simple object
// "getCounterparties",
@ -54,7 +52,7 @@ object RestConnectorBuilder extends App {
// "createCounterparty" // not support
)
//For vSept2018
val genMethodNames = List(
val genMethodNames2 = List(
// "createOrUpdateKycCheck",
// "createOrUpdateKycDocument",
// "createOrUpdateKycMedia",
@ -64,7 +62,7 @@ object RestConnectorBuilder extends App {
// "getKycMedias",
// "getKycStatuses",
// "createBankAccount",
"createCustomer",
// "createCustomer",
// "createMeeting",
// "createMessage"
)

View File

@ -28,14 +28,13 @@ import java.util.UUID.randomUUID
import java.util.Date
import akka.http.scaladsl.model.{HttpProtocol, _}
import akka.http.scaladsl.model.headers.RawHeader
import akka.util.ByteString
import code.api.APIFailureNewStyle
import code.api.ResourceDocs1_4_0.MessageDocsSwaggerDefinitions.inboundStatus
import code.api.cache.Caching
import code.api.util.APIUtil.{AdapterImplementation, MessageDoc, OBPReturnType, saveConnectorMetric}
import code.api.util.ErrorMessages._
import code.api.util.ExampleValue._
import code.api.util.{CallContext, OBPQueryParam}
import code.api.util.{CallContext, NewStyle, OBPQueryParam}
import code.bankconnectors._
import code.bankconnectors.vJune2017.AuthInfo
import code.kafka.{KafkaHelper, Topics}
@ -43,9 +42,8 @@ import code.util.AkkaHttpClient._
import code.util.Helper.MdcLoggable
import com.openbankproject.commons.dto._
import com.openbankproject.commons.model._
import com.tesobe.CacheKeyFromArguments
import com.tesobe.{CacheKeyFromArguments, CacheKeyOmit}
import net.liftweb.common.{Box, Empty, _}
import net.liftweb.json._
import net.liftweb.util.Helpers.tryo
import scala.collection.immutable.{List, Nil}
@ -54,10 +52,11 @@ import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.language.postfixOps
import scala.reflect.runtime.universe._
import code.api.util.ExampleValue._
import code.api.util.APIUtil._
import code.methodrouting.MethodRoutingParam
import org.apache.commons.lang3.StringUtils
import net.liftweb.json._
trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable {
//this one import is for implicit convert, don't delete
@ -79,6 +78,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
val authInfoExample = AuthInfo(userId = "userId", username = "username", cbsToken = "cbsToken")
val errorCodeExample = "INTERNAL-OBP-ADAPTER-6001: ..."
val connectorName = "rest_vMar2019"
/*
All the following code is created automatclly.
@ -166,16 +166,16 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
//---------------- dynamic start -------------------please don't modify this line
// ---------- create on Thu Jun 13 11:07:28 CST 2019
// ---------- create on Tue Jul 23 18:38:46 CEST 2019
messageDocs += MessageDoc(
process = "obp.createCustomer",
process = "obp.getBankAccountsBalances",
messageFormat = messageFormat,
description = "Create Customer",
outboundTopic = Some(Topics.createTopicByClassName(OutBoundCreateCustomer.getClass.getSimpleName).request),
inboundTopic = Some(Topics.createTopicByClassName(OutBoundCreateCustomer.getClass.getSimpleName).response),
description = "Get Bank Accounts Balances",
outboundTopic = None,
inboundTopic = None,
exampleOutboundMessage = (
OutBoundCreateCustomer(outboundAdapterCallContext= OutboundAdapterCallContext(correlationId=correlationIdExample.value,
OutBoundGetBankAccountsBalances(outboundAdapterCallContext= OutboundAdapterCallContext(correlationId=correlationIdExample.value,
sessionId=Some(sessionIdExample.value),
consumerId=Some(consumerIdExample.value),
generalContext=Some(List( BasicGeneralContext(key=keyExample.value,
@ -201,30 +201,11 @@ messageDocs += MessageDoc(
userOwners=List( InternalBasicUser(userId=userIdExample.value,
emailAddress=emailExample.value,
name=usernameExample.value))))))))),
bankId=BankId(bankIdExample.value),
legalName=legalNameExample.value,
mobileNumber=mobileNumberExample.value,
email=emailExample.value,
faceImage= CustomerFaceImage(date=parseDate(customerFaceImageDateExample.value).getOrElse(sys.error("customerFaceImageDateExample.value is not validate date format.")),
url=urlExample.value),
dateOfBirth=parseDate(dateOfBirthExample.value).getOrElse(sys.error("dateOfBirthExample.value is not validate date format.")),
relationshipStatus=relationshipStatusExample.value,
dependents=dependentsExample.value.toInt,
dobOfDependents=dobOfDependentsExample.value.split("[,;]").map(parseDate).flatMap(_.toSeq).toList,
highestEducationAttained=highestEducationAttainedExample.value,
employmentStatus=employmentStatusExample.value,
kycStatus=kycStatusExample.value.toBoolean,
lastOkDate=parseDate(outBoundCreateCustomerLastOkDateExample.value).getOrElse(sys.error("outBoundCreateCustomerLastOkDateExample.value is not validate date format.")),
creditRating=Some( CreditRating(rating=ratingExample.value,
source=sourceExample.value)),
creditLimit=Some( AmountOfMoney(currency=currencyExample.value,
amount=creditLimitAmountExample.value)),
title=titleExample.value,
branchId=branchIdExample.value,
nameSuffix=nameSuffixExample.value)
bankIdAccountIds=List( BankIdAccountId(bankId=BankId(bankIdExample.value),
accountId=AccountId(accountIdExample.value))))
),
exampleInboundMessage = (
InBoundCreateCustomer(inboundAdapterCallContext= InboundAdapterCallContext(correlationId=correlationIdExample.value,
InBoundGetBankAccountsBalances(inboundAdapterCallContext= InboundAdapterCallContext(correlationId=correlationIdExample.value,
sessionId=Some(sessionIdExample.value),
generalContext=Some(List( BasicGeneralContext(key=keyExample.value,
value=valueExample.value)))),
@ -233,48 +214,42 @@ messageDocs += MessageDoc(
status=inboundStatusMessageStatusExample.value,
errorCode=inboundStatusMessageErrorCodeExample.value,
text=inboundStatusMessageTextExample.value))),
data= CustomerCommons(customerId=customerIdExample.value,
data= AccountsBalances(accounts=List( AccountBalance(id=accountIdExample.value,
label=labelExample.value,
bankId=bankIdExample.value,
number=customerNumberExample.value,
legalName=legalNameExample.value,
mobileNumber=mobileNumberExample.value,
email=emailExample.value,
faceImage= CustomerFaceImage(date=parseDate(customerFaceImageDateExample.value).getOrElse(sys.error("customerFaceImageDateExample.value is not validate date format.")),
url=urlExample.value),
dateOfBirth=parseDate(dateOfBirthExample.value).getOrElse(sys.error("dateOfBirthExample.value is not validate date format.")),
relationshipStatus=relationshipStatusExample.value,
dependents=dependentsExample.value.toInt,
dobOfDependents=dobOfDependentsExample.value.split("[,;]").map(parseDate).flatMap(_.toSeq).toList,
highestEducationAttained=highestEducationAttainedExample.value,
employmentStatus=employmentStatusExample.value,
creditRating= CreditRating(rating=ratingExample.value,
source=sourceExample.value),
creditLimit= CreditLimit(currency=currencyExample.value,
amount=creditLimitAmountExample.value),
kycStatus=kycStatusExample.value.toBoolean,
lastOkDate=parseDate(customerLastOkDateExample.value).getOrElse(sys.error("customerLastOkDateExample.value is not validate date format.")),
title=customerTitleExample.value,
branchId=branchIdExample.value,
nameSuffix=nameSuffixExample.value))
accountRoutings=List( AccountRouting(scheme=accountRoutingSchemeExample.value,
address=accountRoutingAddressExample.value)),
balance= AmountOfMoney(currency=balanceCurrencyExample.value,
amount=balanceAmountExample.value))),
overallBalance= AmountOfMoney(currency=currencyExample.value,
amount="string"),
overallBalanceDate=new Date()))
),
adapterImplementation = Some(AdapterImplementation("- Core", 1))
)
// url example: /createCustomer
override def createCustomer(bankId: BankId, legalName: String, mobileNumber: String, email: String, faceImage: CustomerFaceImageTrait, dateOfBirth: Date, relationshipStatus: String, dependents: Int, dobOfDependents: List[Date], highestEducationAttained: String, employmentStatus: String, kycStatus: Boolean, lastOkDate: Date, creditRating: Option[CreditRatingTrait], creditLimit: Option[AmountOfMoneyTrait], title: String, branchId: String, nameSuffix: String, callContext: Option[CallContext]): OBPReturnType[Box[Customer]] = {
import net.liftweb.json.Serialization.write
val url = getUrl("createCustomer")
val outboundAdapterCallContext = Box(callContext.map(_.toOutboundAdapterCallContext)).openOrThrowException(NoCallContext)
val jsonStr = write(OutBoundCreateCustomer(outboundAdapterCallContext , bankId, legalName, mobileNumber, email, faceImage, dateOfBirth, relationshipStatus, dependents, dobOfDependents, highestEducationAttained, employmentStatus, kycStatus, lastOkDate, creditRating, creditLimit, title, branchId, nameSuffix))
sendPostRequest[InBoundCreateCustomer](url, callContext, jsonStr)
.map{ boxedResult =>
boxedResult match {
case Full(result) => (Full(result.data), buildCallContext(result.inboundAdapterCallContext, callContext))
case result: EmptyBox => (result, callContext) // Empty and Failure all match this case
}
// url example: /getBankAccountsBalances/bankIdAccountIds/{bankIdAccountIds}
override def getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], @CacheKeyOmit callContext: Option[CallContext]): OBPReturnType[Box[AccountsBalances]] = saveConnectorMetric {
/**
* Please note that "var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)"
* is just a temporary value filed with UUID values in order to prevent any ambiguity.
* The real value will be assigned by Macro during compile time at this line of a code:
* https://github.com/OpenBankProject/scala-macros/blob/master/macros/src/main/scala/com/tesobe/CacheKeyFromArgumentsMacro.scala#L49
*/
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
CacheKeyFromArguments.buildCacheKey {
Caching.memoizeWithProvider(Some(cacheKey.toString()))(banksTTL second){
val url = getUrl("getBankAccountsBalances" , ("bankIdAccountIds", bankIdAccountIds))
sendGetRequest[InBoundGetBankAccountsBalances](url, callContext)
.map { boxedResult =>
boxedResult match {
case Full(result) => (Full(result.data), buildCallContext(result.inboundAdapterCallContext, callContext))
case result: EmptyBox => (result, callContext) // Empty and Failure all match this case
}
}
}
}
}
}("getBankAccountsBalances")
//---------------- dynamic end ---------------------please don't modify this line
@ -282,6 +257,7 @@ messageDocs += MessageDoc(
private[this] def sendGetRequest[T: TypeTag : Manifest](url: String, callContext: Option[CallContext]) =
@ -296,8 +272,12 @@ messageDocs += MessageDoc(
private[this] def sendDelteRequest[T: TypeTag : Manifest](url: String, callContext: Option[CallContext]) =
sendRequest[T](url, callContext, HttpMethods.DELETE)
//TODO every connector should implement this method to build authorization headers with callContext
private[this] implicit def buildHeaders(callContext: Option[CallContext]): List[HttpHeader] = Nil
//In RestConnector, we use the headers to propagate the parameters to Adapter. The parameters come from the CallContext.outboundAdapterAuthInfo.userAuthContext
//We can set them from UserOauthContext or the http request headers.
private[this] implicit def buildHeaders(callContext: Option[CallContext]): List[HttpHeader] = {
val generalContext = callContext.flatMap(_.toOutboundAdapterCallContext.generalContext).getOrElse(List.empty[BasicGeneralContext])
generalContext.map(generalContext => RawHeader(generalContext.key,generalContext.value))
}
private[this] def buildAdapterCallContext(callContext: Option[CallContext]): OutboundAdapterCallContext = callContext.map(_.toOutboundAdapterCallContext).orNull
@ -314,6 +294,17 @@ messageDocs += MessageDoc(
private[this] val baseUrl = "http://localhost:8080/restConnector"
private[this] def getUrl(methodName: String, variables: (String, Any)*): String = {
// rest connector can have url value in the parameters, key is url
val urlInMethodRouting = NewStyle.function.getMethodRoutings(Some(methodName))
.flatMap(_.parameters).flatten
.find(_.key == "url")
.map(_.value)
if(urlInMethodRouting.isDefined) {
return urlInMethodRouting.get
}
// convert any type value to string, to fill in the url
def urlValueConverter(obj: Any):String = {
val value = obj match {
@ -341,6 +332,7 @@ messageDocs += MessageDoc(
private[this] def sendRequest[T: TypeTag : Manifest](url: String, callContext: Option[CallContext], method: HttpMethod, entityJsonString: String = ""): Future[Box[T]] = {
val request = prepareHttpRequest(url, method, HttpProtocol("HTTP/1.1"), entityJsonString).withHeaders(callContext)
logger.debug(s"RestConnector_vMar2019 request is : $request")
val responseFuture = makeHttpRequest(request)
val jsonType = typeOf[T]
responseFuture.map {

View File

@ -1,12 +1,15 @@
package code.methodrouting
import code.api.util.CustomJsonFormats
import code.util.MappedUUID
import net.liftweb.common.{Box, Empty, EmptyBox, Full}
import net.liftweb.json
import net.liftweb.mapper._
import net.liftweb.util.Helpers.tryo
import org.apache.commons.lang3.StringUtils
import net.liftweb.json.Serialization.write
object MappedMethodRoutingProvider extends MethodRoutingProvider {
object MappedMethodRoutingProvider extends MethodRoutingProvider with CustomJsonFormats{
override def getById(methodRoutingId: String): Box[MethodRoutingT] = MethodRouting.find(
By(MethodRouting.MethodRoutingId, methodRoutingId)
@ -42,12 +45,18 @@ object MappedMethodRoutingProvider extends MethodRoutingProvider {
// if not supply bankIdPattern, isExactMatch must be false
val isExactMatch = if(bankIdPattern.isDefined) methodRouting.isBankIdExactMatch else false
val existsMethodRoutingParameters = methodRouting.parameters match {
case Some(parameters) if (parameters.nonEmpty) => parameters
case _ => List.empty[MethodRoutingParam]
}
tryo{
entityToPersist
.MethodName(methodRouting.methodName)
.BankIdPattern(bankIdPattern.orNull)
.IsBankIdExactMatch(isExactMatch)
.ConnectorName(methodRouting.connectorName)
.Parameters(write(existsMethodRoutingParameters))
.saveMe()
}
}
@ -59,7 +68,7 @@ object MappedMethodRoutingProvider extends MethodRoutingProvider {
}
class MethodRouting extends MethodRoutingT with LongKeyedMapper[MethodRouting] with IdPK {
class MethodRouting extends MethodRoutingT with LongKeyedMapper[MethodRouting] with IdPK with CustomJsonFormats{
override def getSingleton = MethodRouting
@ -70,12 +79,16 @@ class MethodRouting extends MethodRoutingT with LongKeyedMapper[MethodRouting] w
}
object IsBankIdExactMatch extends MappedBoolean(this)
object ConnectorName extends MappedString(this, 255)
object Parameters extends MappedString(this, 5000)
override def methodRoutingId: Option[String] = Option(MethodRoutingId.get)
override def methodName: String = MethodName.get
override def bankIdPattern: Option[String] = Option(BankIdPattern.get)
override def isBankIdExactMatch: Boolean = IsBankIdExactMatch.get
override def connectorName: String = ConnectorName.get
//Here we store all the key-value paris in one big String filed in database.
override def parameters: Option[List[MethodRoutingParam]] = Option(json.parse(if (Parameters.get != null) Parameters.get else "[]").extract[List[MethodRoutingParam]])
}
object MethodRouting extends MethodRouting with LongKeyedMetaMapper[MethodRouting] {

View File

@ -24,17 +24,21 @@ trait MethodRoutingT {
*/
def isBankIdExactMatch: Boolean
def connectorName: String
def parameters: Option[List[MethodRoutingParam]]
}
case class MethodRoutingCommons(methodName: String,
connectorName: String,
isBankIdExactMatch: Boolean,
bankIdPattern: Option[String],
methodRoutingId: Option[String] = None
parameters: Option[List[MethodRoutingParam]] = None,
methodRoutingId: Option[String] = None,
) extends MethodRoutingT with JsonFieldReName
object MethodRoutingCommons extends Converter[MethodRoutingT, MethodRoutingCommons]
case class MethodRoutingParam(key: String, value: String)
trait MethodRoutingProvider {
def getById(methodRoutingId: String): Box[MethodRoutingT]

View File

@ -31,7 +31,8 @@ class MappedBankAccount extends BankAccount with LongKeyedMapper[MappedBankAccou
object accountLabel extends MappedString(this, 255)
//the last time this account was updated via hbci
//the last time this account was updated via hbci [when transaction data was refreshed from the bank.]
//It means last transaction refresh date only used for HBCI now.
object accountLastUpdate extends MappedDateTime(this)
object mAccountRoutingScheme extends MappedString(this, 32)

View File

@ -15,7 +15,7 @@ import net.liftweb.mapper._
*/
object MappedWebUiPropsProvider extends WebUiPropsProvider {
// default webUiProps value cached seconds
private val webUiPropsTTL = APIUtil.getPropsAsIntValue("webui.props.cache.ttl.seconds", 20)
private val webUiPropsTTL = APIUtil.getPropsAsIntValue("webui.props.cache.ttl.seconds", 0)
override def getAll(): List[WebUiPropsT] = WebUiProps.findAll()

View File

@ -0,0 +1 @@
ALTER TABLE "methodrouting" ADD "parameters" varchar(5000) NULL;

View File

@ -0,0 +1,277 @@
package code.api.v3_1_0
import net.liftweb.json.JValue
import org.scalatest.Tag
import io.swagger.parser.OpenAPIParser
import java.util
import code.api.ResourceDocs1_4_0.ResourceDocsV140ServerSetup
import code.api.util.{ApiVersion, CustomJsonFormats}
import code.api.v1_4_0.JSONFactory1_4_0.ImplementedByJson
import net.liftweb.json
import scala.collection.immutable.List
class ResourceDocsTest extends ResourceDocsV140ServerSetup with CustomJsonFormats{
object VersionOfApi extends Tag(ApiVersion.v1_4_0.toString)
object ApiEndpoint1 extends Tag("Get Swagger ResourceDoc")
object ApiEndpoint2 extends Tag("Get OBP ResourceDoc ")
feature(s"test ${ApiEndpoint1.name} ") {
case class RoleJson (
role: String,
requires_bank_id: Boolean
)
//This case class is for API_Explorer, it should make sure api_explorer can get proper doc.
case class ResourceDocJson(operation_id: String,
request_verb: String,
request_url: String,
summary: String, // Summary of call should be 120 characters max
description: String, // Description of call in markdown
example_request_body: JValue, // An example request body
success_response_body: JValue, // Success response body
error_response_bodies: List[String],
implemented_by: ImplementedByJson,
is_core : Boolean,
is_psd2 : Boolean,
is_obwg : Boolean, // This may be tracking isCore
tags : List[String],
roles: List[RoleJson],
is_featured: Boolean,
special_instructions: String,
specified_url: String // This is the URL that we want people to call.
)
case class ResourceDocsJson (resource_docs : List[ResourceDocJson])
scenario(s"We will test ${ApiEndpoint1.name} Api -v4.0.0", ApiEndpoint1, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v4.0.0" / "obp").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
responseGetObp.body.extract[ResourceDocsJson]
}
scenario(s"We will test ${ApiEndpoint1.name} Api -v3.1.0", ApiEndpoint1, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v3.1.0" / "obp").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
responseGetObp.body.extract[ResourceDocsJson]
}
scenario(s"We will test ${ApiEndpoint1.name} Api -v3.0.0", ApiEndpoint1, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v3.0.0" / "obp").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
responseGetObp.body.extract[ResourceDocsJson]
}
scenario(s"We will test ${ApiEndpoint1.name} Api -v2.2.0", ApiEndpoint1, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v2.2.0" / "obp").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
responseGetObp.body.extract[ResourceDocsJson]
}
scenario(s"We will test ${ApiEndpoint1.name} Api -v2.1.0", ApiEndpoint1, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v2.1.0" / "obp").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
responseGetObp.body.extract[ResourceDocsJson]
}
scenario(s"We will test ${ApiEndpoint1.name} Api -v2.0.0", ApiEndpoint1, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v2.0.0" / "obp").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
responseGetObp.body.extract[ResourceDocsJson]
}
scenario(s"We will test ${ApiEndpoint1.name} Api -v1.4.0", ApiEndpoint1, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v1.4.0" / "obp").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
responseGetObp.body.extract[ResourceDocsJson]
}
scenario(s"We will test ${ApiEndpoint1.name} Api -v1.3.0", ApiEndpoint1, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v1.3.0" / "obp").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
responseGetObp.body.extract[ResourceDocsJson]
}
scenario(s"We will test ${ApiEndpoint1.name} Api -v1.2.1", ApiEndpoint1, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v1.2.1" / "obp").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
responseGetObp.body.extract[ResourceDocsJson]
}
}
feature(s"test ${ApiEndpoint2.name} ") {
scenario(s"We will test ${ApiEndpoint2.name} Api - v4.0.0", ApiEndpoint2, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v4.0.0" / "swagger").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
val swaggerJsonString = json.compactRender(responseGetObp.body)
val validatedSwaggerResult = ValidateSwaggerString(swaggerJsonString)
val errors = validatedSwaggerResult._1
if (!errors.isEmpty) logger.info(s"Here is the wrong swagger json: $swaggerJsonString")
errors.isEmpty should be (true)
}
scenario(s"We will test ${ApiEndpoint2.name} Api - v3.1.1", ApiEndpoint2, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v3.1.0" / "swagger").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
val swaggerJsonString = json.compactRender(responseGetObp.body)
val validatedSwaggerResult = ValidateSwaggerString(swaggerJsonString)
val errors = validatedSwaggerResult._1
errors.isEmpty should be (true)
}
scenario(s"We will test ${ApiEndpoint2.name} Api - v3.0.0", ApiEndpoint2, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v3.0.0" / "swagger").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
val swaggerJsonString = json.compactRender(responseGetObp.body)
val validatedSwaggerResult = ValidateSwaggerString(swaggerJsonString)
val errors = validatedSwaggerResult._1
errors.isEmpty should be (true)
}
scenario(s"We will test ${ApiEndpoint2.name} Api - v2.2.0", ApiEndpoint2, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v2.2.0" / "swagger").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
val swaggerJsonString = json.compactRender(responseGetObp.body)
val validatedSwaggerResult = ValidateSwaggerString(swaggerJsonString)
val errors = validatedSwaggerResult._1
errors.isEmpty should be (true)
}
scenario(s"We will test ${ApiEndpoint2.name} Api - v2.1.0", ApiEndpoint2, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v2.1.0" / "swagger").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
val swaggerJsonString = json.compactRender(responseGetObp.body)
val validatedSwaggerResult = ValidateSwaggerString(swaggerJsonString)
val errors = validatedSwaggerResult._1
errors.isEmpty should be (true)
}
scenario(s"We will test ${ApiEndpoint2.name} Api - v2.0.0", ApiEndpoint2, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v2.0.0" / "swagger").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
val swaggerJsonString = json.compactRender(responseGetObp.body)
val validatedSwaggerResult = ValidateSwaggerString(swaggerJsonString)
val errors = validatedSwaggerResult._1
errors.isEmpty should be (true)
}
scenario(s"We will test ${ApiEndpoint2.name} Api - v1.4.0", ApiEndpoint2, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v1.4.0" / "swagger").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
val swaggerJsonString = json.compactRender(responseGetObp.body)
val validatedSwaggerResult = ValidateSwaggerString(swaggerJsonString)
val errors = validatedSwaggerResult._1
errors.isEmpty should be (true)
}
scenario(s"We will test ${ApiEndpoint2.name} Api - v1.3.0", ApiEndpoint2, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v1.3.0" / "swagger").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
val swaggerJsonString = json.compactRender(responseGetObp.body)
val validatedSwaggerResult = ValidateSwaggerString(swaggerJsonString)
val errors = validatedSwaggerResult._1
errors.isEmpty should be (true)
}
scenario(s"We will test ${ApiEndpoint2.name} Api - v1.2.1", ApiEndpoint2, VersionOfApi) {
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v1.2.1" / "swagger").GET
val responseGetObp = makeGetRequest(requestGetObp)
And("We should get 200 and the response can be extract to case classes")
responseGetObp.code should equal(200)
val swaggerJsonString = json.compactRender(responseGetObp.body)
val validatedSwaggerResult = ValidateSwaggerString(swaggerJsonString)
val errors = validatedSwaggerResult._1
errors.isEmpty should be (true)
}
}
//Note: it is tricky to validate the swagger string, I just find this : https://github.com/swagger-api/swagger-parser/issues/718
//So follow it to call the `Validate` method:
//https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Validate.java#L46
def ValidateSwaggerString (swaggerJsonString: String)= {
val result = new OpenAPIParser().readContents(swaggerJsonString, null, null)
val messageList: util.List[String] = result.getMessages()
val errors = new util.HashSet[String](messageList)
val warnings = new util.HashSet[String]
val sb = new StringBuilder
if (!errors.isEmpty) {
sb.append("Errors:").append(System.lineSeparator)
errors.forEach((msg: String) => sb.append("\t-").append(msg).append(System.lineSeparator))
}
if (!warnings.isEmpty) {
sb.append("Warnings: ").append(System.lineSeparator)
warnings.forEach((msg: String) => sb.append("\t-").append(msg).append(System.lineSeparator))
}
if (!errors.isEmpty) {
sb.append(System.lineSeparator)
sb.append("[error] Spec has ").append(errors.size).append(" errors.")
System.err.println(sb.toString)
System.exit(1)
}
else if (!warnings.isEmpty) {
sb.append(System.lineSeparator)
sb.append("[info] Spec has ").append(warnings.size).append(" recommendation(s).")
}
else { // we say "issues" here rather than "errors" to account for both errors and issues.
sb.append("No validation issues detected.")
}
val allMessages = sb.toString
logger.info(s"validatedSwaggerResult.errors $errors")
logger.info(s"validatedSwaggerResult.warnings $warnings")
logger.info(s"validatedSwaggerResult.allMessages $allMessages")
(errors, warnings, allMessages)
}
}

View File

@ -0,0 +1,15 @@
package code.api.ResourceDocs1_4_0
import code.setup.ServerSetupWithTestData
trait ResourceDocsV140ServerSetup extends ServerSetupWithTestData {
def ResourceDocsV1_4Request = baseRequest / "obp" / "v1.4.0"
def ResourceDocsV2_0Request = baseRequest / "obp" / "v2.0.0"
def ResourceDocsV2_1Request = baseRequest / "obp" / "v2.1.0"
def ResourceDocsV2_2Request = baseRequest / "obp" / "v2.2.0"
def ResourceDocsV3_0Request = baseRequest / "obp" / "v3.0.0"
def ResourceDocsV3_1Request = baseRequest / "obp" / "v3.1.0"
def ResourceDocsV4_0Request = baseRequest / "obp" / "v4.0.0"
}

View File

@ -20,6 +20,7 @@ class AccountTest extends V310ServerSetup with DefaultUsers {
object VersionOfApi extends Tag(ApiVersion.v3_1_0.toString)
object ApiEndpoint1 extends Tag(nameOf(Implementations3_1_0.updateAccount))
object ApiEndpoint2 extends Tag(nameOf(Implementations3_1_0.createAccount))
object ApiEndpoint3 extends Tag(nameOf(Implementations3_1_0.getBankAccountsBalances))
lazy val testBankId = testBankId1
lazy val putCreateAccountJSONV310 = SwaggerDefinitionsJSON.createAccountJSONV220.copy(user_id = resourceUser1.userId)
@ -119,4 +120,19 @@ class AccountTest extends V310ServerSetup with DefaultUsers {
}
feature(s"test ${ApiEndpoint3.name}") {
scenario("We will test ${ApiEndpoint3.name}", ApiEndpoint3, VersionOfApi) {
Given("The test bank and test accounts")
val requestGet = (v3_1_0_Request / "banks" / testBankId.value / "balances").GET <@ (user1)
val responseGet = makeGetRequest(requestGet)
responseGet.code should equal(200)
responseGet.body.extract[AccountsBalancesV310Json].accounts.size > 0 should be (true)
responseGet.body.extract[AccountsBalancesV310Json].overall_balance.currency.nonEmpty should be (true)
responseGet.body.extract[AccountsBalancesV310Json].overall_balance.amount.nonEmpty should be (true)
responseGet.body.extract[AccountsBalancesV310Json].overall_balance_date.getTime >0 should be (true)
}
}
}

View File

@ -32,11 +32,13 @@ import code.api.util.ApiVersion
import code.api.util.ErrorMessages._
import code.api.v3_1_0.OBPAPI3_1_0.Implementations3_1_0
import code.entitlement.Entitlement
import code.methodrouting.MethodRoutingCommons
import code.methodrouting.{MethodRoutingCommons, MethodRoutingParam}
import com.github.dwickern.macros.NameOf.nameOf
import net.liftweb.json.Serialization.write
import org.scalatest.Tag
import scala.collection.immutable.List
class MethodRoutingTest extends V310ServerSetup {
/**
@ -52,7 +54,7 @@ class MethodRoutingTest extends V310ServerSetup {
object ApiEndpoint3 extends Tag(nameOf(Implementations3_1_0.getMethodRoutings))
object ApiEndpoint4 extends Tag(nameOf(Implementations3_1_0.deleteMethodRouting))
val rightEntity = MethodRoutingCommons("getBank", "rest_vMar2019", false, Some("some_bankId_.*"))
val rightEntity = MethodRoutingCommons("getBank", "rest_vMar2019", false, Some("some_bankId_.*"), Some(List(MethodRoutingParam("url", "http://mydomain.com/xxx"))))
val wrongEntity = MethodRoutingCommons("getBank", "rest_vMar2019", false, Some("some_bankId_([")) // wrong regex

View File

@ -84,6 +84,10 @@ case class OutBoundGetCoreBankAccounts(outboundAdapterCallContext: OutboundAdapt
bankIdAccountIds: List[BankIdAccountId]) extends TopicTrait
case class InBoundGetCoreBankAccounts(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: List[CoreAccount]) extends InBoundTrait[List[CoreAccount]]
case class OutBoundGetBankAccountsBalances(outboundAdapterCallContext: OutboundAdapterCallContext,
bankIdAccountIds: List[BankIdAccountId]) extends TopicTrait
case class InBoundGetBankAccountsBalances(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: AccountsBalances) extends InBoundTrait[AccountsBalances]
case class OutBoundGetCoreBankAccountsHeld(outboundAdapterCallContext: OutboundAdapterCallContext,
bankIdAccountIds: List[BankIdAccountId]) extends TopicTrait

View File

@ -184,6 +184,7 @@ trait BankAccount{
def iban : Option[String]
def number : String
def bankId : BankId
//It means last transaction refresh date only used for HBCI now.
def lastUpdate : Date
def branchId: String
def accountRoutingScheme: String
@ -298,6 +299,20 @@ case class CoreAccount(
accountRoutings: List[AccountRouting]
)
case class AccountBalance(
id: String,
label: String,
bankId: String,
accountRoutings: List[AccountRouting],
balance: AmountOfMoney
)
case class AccountsBalances(
accounts: List[AccountBalance],
overallBalance: AmountOfMoney,
overallBalanceDate: Date
)
case class AccountHeld(
id: String,
bankId: String,