mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 17:56:46 +00:00
Merge remote-tracking branch 'origin/develop' into TOBI-develop
This commit is contained in:
commit
d07002fd81
@ -19,6 +19,59 @@ Please comment your code ! :-) Imagine an engineer is trying to fix a production
|
||||
|
||||
When naming variables use strict camel case e.g. use myUrl not myURL. This is so we can automatically convert from camelCase to snake_case for JSON output.
|
||||
|
||||
## Writing an API endpoint
|
||||
|
||||
```scala
|
||||
resourceDocs += ResourceDoc(
|
||||
getCustomersForUser,
|
||||
implementedInApiVersion,
|
||||
nameOf(getCustomersForUser),
|
||||
"GET",
|
||||
"/users/current/customers",
|
||||
"Get Customers for Current User",
|
||||
s"""Gets all Customers that are linked to a User.
|
||||
|
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|
|
||||
|""",
|
||||
emptyObjectJson,
|
||||
customerJsonV300,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
UserCustomerLinksNotFoundForUser,
|
||||
UnknownError
|
||||
),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagCustomer, apiTagUser, apiTagNewStyle))
|
||||
|
||||
|
||||
|
||||
// This can be considered a reference new style endpoint.
|
||||
// This is a partial function. The lazy value should have a meaningful name.
|
||||
lazy val getCustomersForUser : OBPEndpoint = {
|
||||
// This defines the URL path and method (GET) for which this partial function will accept the call.
|
||||
case "users" :: "current" :: "customers" :: Nil JsonGet _ => {
|
||||
// We have the Call Context (cc) object (provided through the OBPEndpoint type)
|
||||
// The Call Context contains the authorisation headers etc.
|
||||
cc => {
|
||||
for {
|
||||
// Extract the user from the headers and get an updated callContext
|
||||
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
|
||||
// Now here is the business logic.
|
||||
// Get The customers related to a user. Process the resonse which might be an Exception
|
||||
(customers,callContext) <- Connector.connector.vend.getCustomersByUserIdFuture(u.userId, callContext) map {
|
||||
unboxFullOrFail(_, callContext, ConnectorEmptyResponse, 400)
|
||||
}
|
||||
} yield {
|
||||
// Create the JSON to return. We also return the callContext
|
||||
(JSONFactory300.createCustomersJson(customers), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Writing tests
|
||||
|
||||
When you write a test for an endpoint please tag it with a version and the endpoint.
|
||||
@ -55,6 +108,33 @@ class FundsAvailableTest extends V310ServerSetup {
|
||||
}
|
||||
```
|
||||
|
||||
## Code Generation
|
||||
We support to generate the OBP-API code from the following three types of json. You can choose one of them as your own requirements.
|
||||
|
||||
1 Choose one of the following types: type1 or type2 or type3
|
||||
2 Modify the json file your selected,
|
||||
3 Run the Main method according to your json file
|
||||
4 Run/Restart OBP-API project.
|
||||
5 Run API_Exploer project to test your new APIs. (click the Tag `APIBuilder B1)
|
||||
|
||||
Here are the three types:
|
||||
|
||||
Type1: If you use `modelSource.json`, please run `APIBuilderModel.scala` main method
|
||||
```
|
||||
OBP-API/src/main/scala/code/api/APIBuilder/APIModelSource.json
|
||||
OBP-API/src/main/scala/code/api/APIBuilder/APIBuilderModel.scala
|
||||
```
|
||||
Type2: If you use `apisResource.json`, please run `APIBuilder.scala` main method
|
||||
```
|
||||
OBP-API/src/main/scala/code/api/APIBuilder/apiResourceDoc/apisResource.json
|
||||
OBP-API/src/main/scala/code/api/APIBuilder/apiResourceDoc/APIBuilder.scala
|
||||
```
|
||||
Type3: If you use `swaggerResource.json`, please run `APIBuilderSwagger.scala` main method
|
||||
```
|
||||
OBP-API/src/main/scala/code/api/APIBuilder/swagger/swaggerResource.json
|
||||
OBP-API/src/main/scala/code/api/APIBuilder/swagger/APIBuilderSwagger.scala
|
||||
```
|
||||
|
||||
## Issues
|
||||
|
||||
If would like to report an issue or suggest any kind of improvement please use Github Issues.
|
||||
|
||||
54
README.md
54
README.md
@ -452,26 +452,7 @@ You can obfuscate passwords in the props file the same way as for jetty:
|
||||
* db.url=OBF:fdsafdsakwaetcetcetc
|
||||
|
||||
## Code Generation
|
||||
We support to generate the OBP-API code from the following two types of json. You can choose one of them as your own requirements.
|
||||
|
||||
1 Choose one of the following types: type1 or type2
|
||||
2 Modify the json file your selected,
|
||||
3 Run the Main method according to your json file
|
||||
4 Run/Restart OBP-API project.
|
||||
5 Run API_Exploer project to test your new APIs. (click the Tag `APIBuilder B1)
|
||||
|
||||
Here are the two types:
|
||||
|
||||
Type1: If you use `modelSource.json`, please run `APIBuilderModel.scala` main method
|
||||
```
|
||||
OBP-API/src/main/scala/code/api/APIBuilder/modelSource.json
|
||||
OBP-API/src/main/scala/code/api/APIBuilder/APIBuilderModel.scala
|
||||
```
|
||||
Type2: If you use `apisResource.json`, please run `APIBuilder.scala` main method
|
||||
```
|
||||
OBP-API/src/main/scala/code/api/APIBuilder/apisResource.json
|
||||
OBP-API/src/main/scala/code/api/APIBuilder/APIBuilder.scala
|
||||
```
|
||||
Please refer to the [Code Generation](https://github.com/OpenBankProject/OBP-API/blob/develop/CONTRIBUTING.md##code-generation) for links
|
||||
|
||||
## Using jetty password obfuscation with props file
|
||||
|
||||
@ -531,6 +512,39 @@ Info about rate limiting availibility at some instance can be found over next AP
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Webhooks
|
||||
Webhooks are used to call external URLs when certain events happen.
|
||||
Account Webhooks focus on events around accounts.
|
||||
For instance, a webhook could be used to notify an external service if a balance changes on an account.
|
||||
This functionality is work in progress!
|
||||
|
||||
There are 3 API's endpoint related to webhooks:
|
||||
1. `POST ../banks/BANK_ID/account-web-hooks` - Create an Account Webhook
|
||||
2. `PUT ../banks/BANK_ID/account-web-hooks` - Enable/Disable an Account Webhook
|
||||
3. `GET ../management/banks/BANK_ID/account-web-hooks` - Get Account Webhooks
|
||||
---
|
||||
## OAuth 2.0
|
||||
In order to enable an OAuth2 workflow at an instance of OBP-API backend app you need to setup next props:
|
||||
```
|
||||
# -- OAuth 2 ---------------------------------------------------------------
|
||||
# Enable/Disable OAuth 2 workflow at a server instance
|
||||
# In case isn't defined default value is false
|
||||
# allow_oauth2_login=false
|
||||
# URL of Public server JWK set used for validating bearer JWT access tokens
|
||||
# oauth2.jwk_set.url=http://localhost:8080/jwk.json
|
||||
# ----------------------------------------------------------- OAuth 2 ------
|
||||
|
||||
OpenID Connect is supported.
|
||||
Tested Identity providers: Google, MITREId.
|
||||
|
||||
```
|
||||
### Example for Google's OAuth 2.0 implementation for authentication, which conforms to the OpenID Connect specification
|
||||
```
|
||||
allow_oauth2_login=true
|
||||
oauth2.jwk_set.url=https://www.googleapis.com/oauth2/v3/certs
|
||||
```
|
||||
---
|
||||
|
||||
## Scala / Lift
|
||||
|
||||
|
||||
37
cheat_sheet.md
Normal file
37
cheat_sheet.md
Normal file
@ -0,0 +1,37 @@
|
||||
#OBP Cheat Sheet.
|
||||
|
||||
|
||||
## A selection of links to get you started using the Open Bank Project platform and surrounding tools.
|
||||
|
||||
## Note! *An alternative version of this document is available at the glossary* [here](https://apiexplorersandbox.openbankproject.com/glossary#OBP.Cheat-Sheet)
|
||||
|
||||
|
||||
[OBP API Installation](https://github.com/OpenBankProject/OBP-API/blob/develop/README.md)
|
||||
|
||||
[OBP API Contributing](https://github.com/OpenBankProject/OBP-API/blob/develop/CONTRIBUTING.md)
|
||||
|
||||
[Access Control](https://apiexplorersandbox.openbankproject.com/glossary#API.Access-Control)
|
||||
|
||||
[Authentication](https://github.com/OpenBankProject/OBP-API/wiki/Authentication)
|
||||
|
||||
[Interfaces](https://apiexplorersandbox.openbankproject.com/glossary#API.Interfaces)
|
||||
|
||||
[Endpoints](https://apiexplorersandbox.openbankproject.com)
|
||||
|
||||
[Versioning](https://github.com/OpenBankProject/OBP-API/wiki/API-Versioning)
|
||||
|
||||
[Glossary](https://apiexplorersandbox.openbankproject.com/glossary)
|
||||
|
||||
[Access Control](https://apiexplorersandbox.openbankproject.com/glossary#API.Access-Control)
|
||||
|
||||
[OBP Kafka](https://apiexplorersandbox.openbankproject.com/glossary#Adapter.Kafka.Intro)
|
||||
|
||||
[OBP Akka](https://apiexplorersandbox.openbankproject.com/glossary#Adapter.Akka.Intro)
|
||||
|
||||
[API Explorer](https://github.com/OpenBankProject/API-Explorer/blob/develop/README.md)
|
||||
|
||||
[API Manager](https://github.com/OpenBankProject/API-Manager/blob/master/README.md)
|
||||
|
||||
[API Tester](https://github.com/OpenBankProject/API-Tester/blob/master/README.md)
|
||||
|
||||
|
||||
10
credits.md
Normal file
10
credits.md
Normal file
@ -0,0 +1,10 @@
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
Marko Milić: marko AT tesobe DOT com
|
||||
Hongwei Zhang: hongwei AT tesobe DOT com
|
||||
Tobias Woelk: tobias.woelk AT tesobe DOT com
|
||||
26
pom.xml
26
pom.xml
@ -13,7 +13,7 @@
|
||||
<properties>
|
||||
<scala.version>2.12</scala.version>
|
||||
<scala.compiler>2.12.4</scala.compiler>
|
||||
<akka.version>2.5.13</akka.version>
|
||||
<akka.version>2.5.19</akka.version>
|
||||
<akka-streams-kafka.version>0.22</akka-streams-kafka.version>
|
||||
<kafka.version>1.1.0</kafka.version>
|
||||
<avro.version>1.8.3</avro.version>
|
||||
@ -91,11 +91,6 @@
|
||||
<artifactId>dispatch-lift-json_${scala.version}</artifactId>
|
||||
<version>0.13.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.databinder.dispatch</groupId>
|
||||
<artifactId>dispatch-core_${scala.version}</artifactId>
|
||||
<version>0.13.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
@ -220,11 +215,6 @@
|
||||
<artifactId>pegdown</artifactId>
|
||||
<version>1.6.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jason-goodwin</groupId>
|
||||
<artifactId>authentikat-jwt_${scala.version}</artifactId>
|
||||
<version>0.4.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.tokbox</groupId>
|
||||
<artifactId>opentok-server-sdk</artifactId>
|
||||
@ -293,6 +283,12 @@
|
||||
<artifactId>obp-ri-transport</artifactId>
|
||||
<version>${obp-ri.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.typesafe.akka/akka-http-core -->
|
||||
<dependency>
|
||||
<groupId>com.typesafe.akka</groupId>
|
||||
<artifactId>akka-http-core_${scala.version}</artifactId>
|
||||
<version>10.1.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.typesafe.akka</groupId>
|
||||
<artifactId>akka-actor_${scala.version}</artifactId>
|
||||
@ -352,7 +348,7 @@
|
||||
<dependency>
|
||||
<groupId>com.nimbusds</groupId>
|
||||
<artifactId>nimbus-jose-jwt</artifactId>
|
||||
<version>4.23</version>
|
||||
<version>6.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.OpenBankProject</groupId>
|
||||
@ -371,6 +367,12 @@
|
||||
<version>0.1.6</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.nimbusds/oauth2-oidc-sdk -->
|
||||
<dependency>
|
||||
<groupId>com.nimbusds</groupId>
|
||||
<artifactId>oauth2-oidc-sdk</artifactId>
|
||||
<version>5.17.1</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ connector=mapped
|
||||
#connector=mongodb
|
||||
#connector=kafka
|
||||
#conenctor=kafka_vMar2017
|
||||
#connector=akka_vDec2018
|
||||
#connector=obpjvm
|
||||
#connector=...
|
||||
|
||||
@ -238,6 +239,19 @@ BankMockKey=change_me
|
||||
|
||||
|
||||
## Web interface configuration
|
||||
|
||||
## IMPLEMENTING BANK SPECIFIC BRANDING ON ONE OBP INSTANCE ########################
|
||||
# Note, you can specify bank specific branding by appending _FOR_BRAND_<BANK_ID> to the standard props names
|
||||
# e.g.
|
||||
#webui_header_logo_left_url_FOR_BRAND_banku = https://static.openbankproject.com/images/sandbox/bank_u.png
|
||||
#webui_header_logo_left_url_FOR_BRAND_banky = https://static.openbankproject.com/images/sandbox/bank_y.png
|
||||
# And invoke by calling index etc with ?brand=<BANK_ID>
|
||||
# e.g. http://127.0.0.1:8080/?brand=x
|
||||
# For any properties that don't have a bank specific flavour, the standard props name will be used.
|
||||
####################################################################################
|
||||
|
||||
|
||||
|
||||
webui_header_logo_left_url = /media/images/logo.png
|
||||
webui_header_logo_right_url =
|
||||
webui_index_page_about_section_background_image_url = /media/images/about-background.jpg
|
||||
@ -335,7 +349,7 @@ webui_agree_terms_url =
|
||||
|
||||
|
||||
|
||||
# If we want to gather more informaiton about an Applicaiton / Startup fill this url and text
|
||||
# If we want to gather more information about an Application / Startup fill this url and text
|
||||
# Will be displayed on the post Consumer Registration page.
|
||||
#webui_post_consumer_registration_more_info_url =
|
||||
#webui_post_consumer_registration_more_info_text = Please tell us more your Application and / or Startup using this link.
|
||||
@ -393,7 +407,7 @@ super_admin_user_ids=USER_ID1,USER_ID2,
|
||||
##########################
|
||||
|
||||
|
||||
## OpenId Connect can be used to retrieve User informaiton from an
|
||||
## OpenId Connect can be used to retrieve User information from an
|
||||
## external OpenID Connect server.
|
||||
## To use an external OpenID Connect server,
|
||||
## you will need to change these values.
|
||||
@ -463,18 +477,13 @@ display_internal_errors=false
|
||||
# -------------------------------------- Display internal errors --
|
||||
|
||||
|
||||
# -- OAuth 2 --------------------------------------------------------------------
|
||||
# -- OAuth 2 ---------------------------------------------------------------
|
||||
# Enable/Disable OAuth 2 workflow at a server instance
|
||||
# In case isn't defined default value is false
|
||||
# allow_oauth2_login=false
|
||||
# oauth2.token_secret=secret
|
||||
# Enable SSL for JWT
|
||||
# If set to true must set paths for the keystore locations
|
||||
# In case isn't defined default value is false
|
||||
# oauth2.jwt.use.ssl=false
|
||||
# URL of Public server JWK set used for validating bearer JWT access tokens
|
||||
# oauth2.jwk_set.url=http://localhost:8080/jwk.json
|
||||
# ----------------------------------------------------------- OAuth 2 -----------
|
||||
# ----------------------------------------------------------- OAuth 2 ------
|
||||
|
||||
## This property is used for documenting at Resource Doc. It may include the port also (but not /obp)
|
||||
## (this needs to be a URL)
|
||||
@ -522,4 +531,28 @@ featured_apis=elasticSearchWarehouseV300
|
||||
# Define mapper rules
|
||||
# In case isn't defined default value is "false"
|
||||
# mapper_rules.create_foreign_keys=false
|
||||
# -----------------------------------------------
|
||||
# -----------------------------------------------
|
||||
|
||||
# -- Akka connector --------------------------------------------
|
||||
# Define mapper rules
|
||||
# In case isn't defined default value is "127.0.0.1"
|
||||
# akka_connector.hostname=127.0.0.1
|
||||
# In case isn't defined default value is "2662"
|
||||
# akka_connector.port=2662
|
||||
# In case isn't defined default value is "INFO"
|
||||
# akka_connector.loglevel=INFO/DEBUG etc.
|
||||
# In case isn't defined default value is "akka-connector-actor"
|
||||
# akka_connector.name_of_actor=SOME_ACTOR_NAME
|
||||
# --------------------------------------------------------------
|
||||
|
||||
# -- Scopes ---------------------------------------------
|
||||
# Scopes are a way to limit the APIs a Consumer can call.
|
||||
# In case isn't defined default value is "false"
|
||||
# require_scopes=false
|
||||
# -------------------------------------------------------
|
||||
|
||||
# -- Database scheduler -----------------------------
|
||||
# Database scheduler interval in seconds.
|
||||
# Scheduler would not be started if delay is not set.
|
||||
database_messages_scheduler_interval=3600
|
||||
# ---------------------------------------------------
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,24 +16,20 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
package bootstrap.liftweb
|
||||
|
||||
import java.io.{File, FileInputStream}
|
||||
import java.util.{Locale, TimeZone}
|
||||
|
||||
import code.accountapplication.MappedAccountApplication
|
||||
import code.accountholder.MapperAccountHolders
|
||||
import code.actorsystem.ObpActorSystem
|
||||
import code.api.Constant._
|
||||
@ -42,7 +38,7 @@ import code.api.ResourceDocs1_4_0._
|
||||
import code.api._
|
||||
import code.api.builder.APIBuilder_Connector
|
||||
import code.api.sandbox.SandboxApiCalls
|
||||
import code.api.util.APIUtil.enableVersionIfAllowed
|
||||
import code.api.util.APIUtil.{enableVersionIfAllowed, errorJsonResponse}
|
||||
import code.api.util.{APIUtil, ApiVersion, ErrorMessages, Migration}
|
||||
import code.atms.MappedAtm
|
||||
import code.bankconnectors.Connector
|
||||
@ -57,7 +53,7 @@ import code.customeraddress.MappedCustomerAddress
|
||||
import code.entitlement.MappedEntitlement
|
||||
import code.entitlementrequest.MappedEntitlementRequest
|
||||
import code.fx.{MappedCurrency, MappedFXRate}
|
||||
import code.kafka.{KafkaConsumer, KafkaHelperActors}
|
||||
import code.kafka.{KafkaHelperActors, OBPKafkaConsumer}
|
||||
import code.kycchecks.MappedKycCheck
|
||||
import code.kycdocuments.MappedKycDocument
|
||||
import code.kycmedias.MappedKycMedia
|
||||
@ -77,6 +73,7 @@ import code.model.dataAccess._
|
||||
import code.productAttributeattribute.MappedProductAttribute
|
||||
import code.products.MappedProduct
|
||||
import code.remotedata.RemotedataActors
|
||||
import code.scheduler.DatabaseDriverScheduler
|
||||
import code.scope.{MappedScope, MappedUserScope}
|
||||
import code.snippet.{OAuthAuthorisation, OAuthWorkedThanks}
|
||||
import code.socialmedia.MappedSocialMedia
|
||||
@ -93,6 +90,7 @@ import javax.mail.internet.MimeMessage
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.db.DBLogEntry
|
||||
import net.liftweb.http._
|
||||
import net.liftweb.json.Extraction
|
||||
import net.liftweb.mapper._
|
||||
import net.liftweb.sitemap.Loc._
|
||||
import net.liftweb.sitemap._
|
||||
@ -233,6 +231,12 @@ class Boot extends MdcLoggable {
|
||||
logger.debug(s"If you can read this, logging level is debug")
|
||||
|
||||
val actorSystem = ObpActorSystem.startLocalActorSystem()
|
||||
connector match {
|
||||
case "akka_vDec2018" =>
|
||||
// Start Actor system of Akka connector
|
||||
ObpActorSystem.startNorthSideAkkaConnectorActorSystem()
|
||||
case _ => // Do nothing
|
||||
}
|
||||
|
||||
// where to search snippets
|
||||
LiftRules.addToPackages("code")
|
||||
@ -326,12 +330,12 @@ class Boot extends MdcLoggable {
|
||||
logger.info(s"KafkaHelperActors.startLocalKafkaHelperWorkers( ${actorSystem} ) starting")
|
||||
KafkaHelperActors.startLocalKafkaHelperWorkers(actorSystem)
|
||||
// Start North Side Consumer if it's not already started
|
||||
KafkaConsumer.primaryConsumer.start()
|
||||
OBPKafkaConsumer.primaryConsumer.start()
|
||||
}
|
||||
|
||||
if (!APIUtil.getPropsAsBoolValue("remotedata.enable", false)) {
|
||||
if (APIUtil.getPropsAsBoolValue("use_akka", false) == true) {
|
||||
try {
|
||||
logger.info(s"RemotedataActors.startLocalRemotedataWorkers( ${actorSystem} ) starting")
|
||||
logger.info(s"RemotedataActors.startActors( ${actorSystem} ) starting")
|
||||
RemotedataActors.startActors(actorSystem)
|
||||
} catch {
|
||||
case ex: Exception => logger.warn(s"RemotedataActors.startLocalRemotedataWorkers( ${actorSystem} ) could not start: $ex")
|
||||
@ -430,25 +434,41 @@ class Boot extends MdcLoggable {
|
||||
logger.info("Would have sent email if not in dev mode: " + m.getContent)
|
||||
})
|
||||
|
||||
implicit val formats = net.liftweb.json.DefaultFormats
|
||||
LiftRules.exceptionHandler.prepend{
|
||||
//same as default LiftRules.exceptionHandler
|
||||
case(Props.RunModes.Development, r, e) => {
|
||||
logger.error("Exception being returned to browser when processing " + r.uri.toString, e)
|
||||
XhtmlResponse((<html> <body>Exception occured while processing {r.uri}<pre>{showException(e)}</pre> </body> </html>), S.htmlProperties.docType, List("Content-Type" -> "text/html; charset=utf-8"), Nil, 500, S.legacyIeCompatibilityMode)
|
||||
|
||||
JsonResponse(
|
||||
Extraction.decompose(ErrorMessage(code = 500, message = s"${ErrorMessages.InternalServerError} ${showExceptionAtJson(e)}")),
|
||||
500
|
||||
)
|
||||
}
|
||||
//same as default LiftRules.exceptionHandler, except that it also send an email notification
|
||||
case (_, r , e) => {
|
||||
sendExceptionEmail(e)
|
||||
logger.error("Exception being returned to browser when processing " + r.uri.toString, e)
|
||||
XhtmlResponse((<html> <body>Something unexpected happened while serving the page at {r.uri}</body> </html>), S.htmlProperties.docType, List("Content-Type" -> "text/html; charset=utf-8"), Nil, 500, S.legacyIeCompatibilityMode)
|
||||
JsonResponse(
|
||||
Extraction.decompose(ErrorMessage(code = 500, message = s"${ErrorMessages.InternalServerError}")),
|
||||
500
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LiftRules.uriNotFound.prepend{
|
||||
case (r, _) => NotFoundAsResponse(errorJsonResponse(
|
||||
s"${ErrorMessages.InvalidUri}Current Url is (${r.uri.toString}), Current Content-Type Header is (${r.headers.find(_._1.equals("Content-Type")).map(_._2).getOrElse("")})",
|
||||
404)
|
||||
)
|
||||
}
|
||||
|
||||
if ( !APIUtil.getPropsAsLongValue("transaction_status_scheduler_delay").isEmpty ) {
|
||||
val delay = APIUtil.getPropsAsLongValue("transaction_status_scheduler_delay").openOrThrowException("Incorrect value for transaction_status_scheduler_delay, please provide number of seconds.")
|
||||
TransactionStatusScheduler.start(delay)
|
||||
}
|
||||
APIUtil.getPropsAsLongValue("database_messages_scheduler_interval") match {
|
||||
case Full(i) => DatabaseDriverScheduler.start(i)
|
||||
case _ => // Do not start it
|
||||
}
|
||||
|
||||
|
||||
APIUtil.akkaSanityCheck() match {
|
||||
case Full(c) if c == true => logger.info(s"remotedata.secret matched = $c")
|
||||
@ -460,24 +480,26 @@ class Boot extends MdcLoggable {
|
||||
case _ => throw new Exception(s"Unexpected error occurs during Akka sanity check!")
|
||||
}
|
||||
|
||||
Migration.database.generateAndPopulateMissingCustomerUUIDs()
|
||||
Migration.database.generateAndPopulateMissingConsumersUUIDs()
|
||||
|
||||
}
|
||||
|
||||
def schemifyAll() = {
|
||||
Schemifier.schemify(true, Schemifier.infoF _, ToSchemify.models: _*)
|
||||
if (APIUtil.getPropsAsBoolValue("remotedata.enable", false) == false) {
|
||||
Schemifier.schemify(true, Schemifier.infoF _, ToSchemify.modelsRemotedata: _*)
|
||||
}
|
||||
}
|
||||
|
||||
private def showException(le: Throwable): String = {
|
||||
val ret = "Message: " + le.toString + "\n\t" +
|
||||
le.getStackTrace.map(_.toString).mkString("\n\t") + "\n"
|
||||
private def showExceptionAtJson(error: Throwable): String = {
|
||||
val formattedError = "Message: " + error.toString + error.getStackTrace.map(_.toString).mkString(" ")
|
||||
|
||||
val also = le.getCause match {
|
||||
val formattedCause = error.getCause match {
|
||||
case null => ""
|
||||
case sub: Throwable => "\nCaught and thrown by:\n" + showException(sub)
|
||||
case cause: Throwable => "Caught and thrown by: " + showExceptionAtJson(cause)
|
||||
}
|
||||
|
||||
ret + also
|
||||
formattedError + formattedCause
|
||||
}
|
||||
|
||||
private def sendExceptionEmail(exception: Throwable): Unit = {
|
||||
@ -548,7 +570,8 @@ object ToSchemify {
|
||||
MappedUserScope,
|
||||
MappedTaxResidence,
|
||||
MappedCustomerAddress,
|
||||
MappedUserAuthContext
|
||||
MappedUserAuthContext,
|
||||
MappedAccountApplication
|
||||
)
|
||||
|
||||
// The following tables are accessed directly via Mapper / JDBC
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
package code.accountapplication
|
||||
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import code.products.Products.ProductCode
|
||||
import code.remotedata.RemotedataAccountApplication
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
object AccountApplication extends SimpleInjector {
|
||||
|
||||
val accountApplication = new Inject(buildOne _) {}
|
||||
|
||||
def buildOne: AccountApplicationProvider =
|
||||
APIUtil.getPropsAsBoolValue("use_akka", false) match {
|
||||
case false => MappedAccountApplicationProvider
|
||||
case true => RemotedataAccountApplication
|
||||
}
|
||||
}
|
||||
|
||||
trait AccountApplicationProvider {
|
||||
def getAll(): Future[Box[List[AccountApplication]]]
|
||||
def getById(accountApplicationId: String): Future[Box[AccountApplication]]
|
||||
def createAccountApplication(productCode: ProductCode, userId: Option[String], customerId: Option[String]): Future[Box[AccountApplication]]
|
||||
def updateStatus(accountApplicationId:String, status: String): Future[Box[AccountApplication]]
|
||||
}
|
||||
|
||||
trait AccountApplication {
|
||||
def accountApplicationId: String
|
||||
def productCode: ProductCode
|
||||
def userId: String
|
||||
def customerId: String
|
||||
def dateOfApplication: Date
|
||||
def status: String
|
||||
}
|
||||
|
||||
|
||||
class RemotedataAccountApplicationCaseClasses {
|
||||
case class getAll()
|
||||
case class getById(accountApplicationId: String)
|
||||
case class createAccountApplication(productCode: ProductCode, userId: Option[String], customerId: Option[String])
|
||||
case class updateStatus(accountApplicationId:String, status: String)
|
||||
}
|
||||
|
||||
object RemotedataAccountApplicationCaseClasses extends RemotedataAccountApplicationCaseClasses
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
package code.accountapplication
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import code.api.util.ErrorMessages
|
||||
import code.customer.{Customer, MappedCustomer}
|
||||
import code.model.dataAccess.ResourceUser
|
||||
import code.products.Products.ProductCode
|
||||
import code.util.MappedUUID
|
||||
import net.liftweb.common.{Box, Empty, Failure, Full}
|
||||
import net.liftweb.mapper._
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
|
||||
object MappedAccountApplicationProvider extends AccountApplicationProvider {
|
||||
|
||||
override def getAll(): Future[Box[List[AccountApplication]]] = Future {
|
||||
tryo{MappedAccountApplication.findAll()}
|
||||
}
|
||||
|
||||
override def getById(accountApplicationId: String): Future[Box[AccountApplication]] = Future {
|
||||
MappedAccountApplication.find(By(MappedAccountApplication.mAccountApplicationId, accountApplicationId))
|
||||
}
|
||||
|
||||
override def createAccountApplication(productCode: ProductCode, userId: Option[String], customerId: Option[String]): Future[Box[AccountApplication]] =
|
||||
Future {
|
||||
tryo {
|
||||
MappedAccountApplication.create.mCode(productCode.value).mUserId(userId.orNull).mCustomerId(customerId.orNull).mStatus("REQUESTED").saveMe()
|
||||
}
|
||||
}
|
||||
|
||||
override def updateStatus(accountApplicationId:String, status: String): Future[Box[AccountApplication]] =
|
||||
Future{
|
||||
MappedAccountApplication.find(By(MappedAccountApplication.mAccountApplicationId, accountApplicationId))
|
||||
match {
|
||||
case Full(accountApplication) if(accountApplication.status == "ACCEPTED") =>
|
||||
Failure(s"${ErrorMessages.AccountApplicationAlreadyAccepted} Current Account-Application-Id($accountApplicationId)")
|
||||
case Full(accountApplication) => tryo{accountApplication.mStatus(status).saveMe()}
|
||||
case Empty => Failure(s"${ErrorMessages.AccountApplicationNotFound} Current Account-Application-Id($accountApplicationId)")
|
||||
case _ => Failure(ErrorMessages.UnknownError)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MappedAccountApplication extends AccountApplication with LongKeyedMapper[MappedAccountApplication] with IdPK with CreatedUpdated {
|
||||
|
||||
def getSingleton = MappedAccountApplication
|
||||
|
||||
object mAccountApplicationId extends MappedUUID(this)
|
||||
object mCode extends MappedString(this, 50)
|
||||
object mCustomerId extends MappedUUID(this)
|
||||
object mUserId extends MappedUUID(this) //resourceUser
|
||||
object mStatus extends MappedString(this, 255)
|
||||
|
||||
override def accountApplicationId: String = mAccountApplicationId.get
|
||||
|
||||
override def productCode: ProductCode = ProductCode(mCode.get)
|
||||
override def userId: String = mUserId.get
|
||||
override def customerId: String = mCustomerId.get
|
||||
override def dateOfApplication: Date = createdAt.get
|
||||
override def status: String = mStatus.get
|
||||
|
||||
}
|
||||
|
||||
object MappedAccountApplication extends MappedAccountApplication with LongKeyedMetaMapper[MappedAccountApplication] {
|
||||
override def dbIndexes = UniqueIndex(mAccountApplicationId) :: super.dbIndexes
|
||||
}
|
||||
@ -16,8 +16,8 @@ trait ObpActorHelper {
|
||||
}
|
||||
case af: APIFailure => af
|
||||
case f: Failure => f
|
||||
case l: List[T] => l
|
||||
case s: Set[T] => s
|
||||
case l: List[_] => l.asInstanceOf[List[T]]
|
||||
case s: Set[_] => s.asInstanceOf[Set[T]]
|
||||
case Full(r) => r
|
||||
case j: JValue => j
|
||||
case t: T => t
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
package code.actorsystem
|
||||
|
||||
import akka.util.Timeout
|
||||
import code.api.APIFailure
|
||||
import code.api.util.APIUtil
|
||||
import code.util.Helper.MdcLoggable
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.util.Props
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{Await, Future}
|
||||
|
||||
@ -20,29 +16,9 @@ trait ObpActorInit extends MdcLoggable{
|
||||
logger.debug(s"Create this Actor: $actorName: ${actor}")
|
||||
val TIMEOUT = (ACTOR_TIMEOUT seconds)
|
||||
implicit val timeout = Timeout(ACTOR_TIMEOUT * (1000 milliseconds))
|
||||
|
||||
def extractFuture[T](f: Future[Any]): T = {
|
||||
val r = f.map {
|
||||
case s: Set[T] => s
|
||||
case l: List[T] => l
|
||||
case t: T => t
|
||||
case _ => Empty ~> APIFailure(s"future extraction failed", 501)
|
||||
}
|
||||
Await.result(r, TIMEOUT).asInstanceOf[T]
|
||||
}
|
||||
|
||||
def extractFutureToBox[T](f: Future[Any]): Box[T] = {
|
||||
val r = f.map {
|
||||
case pf: ParamFailure[_] => Empty ~> pf
|
||||
case af: APIFailure => Empty ~> af
|
||||
case f: Failure => f
|
||||
case Empty => Empty
|
||||
case t: T => Full(t)
|
||||
case _ => Empty ~> APIFailure(s"future extraction to box failed", 501)
|
||||
}
|
||||
|
||||
Await.result(r, TIMEOUT)
|
||||
|
||||
|
||||
def getValueFromFuture[T](f: Future[T]): T = {
|
||||
Await.result(f, TIMEOUT)
|
||||
}
|
||||
|
||||
def CreateActorNameFromClassName(c: String): String = {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package code.actorsystem
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import code.bankconnectors.akka.actor.AkkaConnectorActorConfig
|
||||
import code.util.Helper
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.typesafe.config.ConfigFactory
|
||||
@ -10,11 +11,21 @@ object ObpActorSystem extends MdcLoggable {
|
||||
|
||||
val props_hostname = Helper.getHostname
|
||||
var obpActorSystem: ActorSystem = _
|
||||
var northSideAkkaConnectorActorSystem: ActorSystem = _
|
||||
|
||||
def startLocalActorSystem(): ActorSystem = {
|
||||
logger.info("Starting local actor system")
|
||||
logger.info(ObpActorConfig.localConf)
|
||||
obpActorSystem = ActorSystem.create(s"ObpActorSystem_${props_hostname}", ConfigFactory.load(ConfigFactory.parseString(ObpActorConfig.localConf)))
|
||||
val localConf = ObpActorConfig.localConf
|
||||
logger.info(localConf)
|
||||
obpActorSystem = ActorSystem.create(s"ObpActorSystem_${props_hostname}", ConfigFactory.load(ConfigFactory.parseString(localConf)))
|
||||
obpActorSystem
|
||||
}
|
||||
|
||||
def startNorthSideAkkaConnectorActorSystem(): ActorSystem = {
|
||||
logger.info("Starting North Side Akka Connector actor system")
|
||||
val localConf = AkkaConnectorActorConfig.localConf
|
||||
logger.info(localConf)
|
||||
northSideAkkaConnectorActorSystem = ActorSystem.create(s"SouthSideAkkaConnector_${props_hostname}", ConfigFactory.load(ConfigFactory.parseString(localConf)))
|
||||
northSideAkkaConnectorActorSystem
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,13 @@
|
||||
package code.actorsystem
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.actor.{ActorSelection, ActorSystem}
|
||||
import code.api.util.APIUtil
|
||||
import code.bankconnectors.akka.actor.{AkkaConnectorActorConfig, AkkaConnectorHelperActor}
|
||||
import code.util.Helper
|
||||
import code.util.Helper.MdcLoggable
|
||||
import code.webhook.WebhookHelperActors
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import net.liftweb.util.Props
|
||||
import net.liftweb.common.Full
|
||||
|
||||
|
||||
object ObpLookupSystem extends ObpLookupSystem {
|
||||
@ -76,19 +77,53 @@ trait ObpLookupSystem extends MdcLoggable {
|
||||
this.obpLookupSystem.actorSelection(actorPath)
|
||||
}
|
||||
|
||||
|
||||
def getWebhookActor() = {
|
||||
/**
|
||||
* This function is a Single Point Of Entry for Webhook's Actor
|
||||
* I.e. we can obtain te Actor all over the code in next way:
|
||||
* {
|
||||
* val actor: ActorSelection = ObpLookupSystem.getWebhookActor()
|
||||
* }
|
||||
*
|
||||
* @return An ActorSelection which is a logical view of a section of an ActorSystem's tree of Actors,
|
||||
* allowing for broadcasting of messages to that section.
|
||||
*/
|
||||
def getWebhookActor(): ActorSelection = {
|
||||
val name = WebhookHelperActors.actorName
|
||||
val actorPath: String = {
|
||||
val hostname = ObpActorConfig.localHostname
|
||||
val port = ObpActorConfig.localPort
|
||||
val props_hostname = Helper.getHostname
|
||||
if (port == 0) {
|
||||
logger.error("Failed to connect to local Web Hook actor")
|
||||
logger.error("Failed to connect to local Webhook's actor")
|
||||
}
|
||||
s"akka.tcp://ObpActorSystem_${props_hostname}@${hostname}:${port}/user/${name}"
|
||||
}
|
||||
this.obpLookupSystem.actorSelection(actorPath)
|
||||
}
|
||||
|
||||
|
||||
def getAkkaConnectorActor(actorName: String) = {
|
||||
|
||||
val hostname = APIUtil.getPropsValue("akka_connector.hostname")
|
||||
val port = APIUtil.getPropsValue("akka_connector.port")
|
||||
val actorPath: String = (hostname, port) match {
|
||||
case (Full(h), Full(p)) =>
|
||||
val hostname = h
|
||||
val port = p
|
||||
val akka_connector_hostname = Helper.getAkkaConnectorHostname
|
||||
s"akka.tcp://SouthSideAkkaConnector_${akka_connector_hostname}@${hostname}:${port}/user/${actorName}"
|
||||
|
||||
case _ =>
|
||||
val hostname = AkkaConnectorActorConfig.localHostname
|
||||
val port = AkkaConnectorActorConfig.localPort
|
||||
val props_hostname = Helper.getHostname
|
||||
if (port == 0) {
|
||||
logger.error("Failed to find an available port.")
|
||||
}
|
||||
AkkaConnectorHelperActor.startAkkaConnectorHelperActors(ObpActorSystem.northSideAkkaConnectorActorSystem)
|
||||
s"akka.tcp://SouthSideAkkaConnector_${props_hostname}@${hostname}:${port}/user/${actorName}"
|
||||
}
|
||||
this.obpLookupSystem.actorSelection(actorPath)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -265,7 +265,7 @@ object APIBuilderModel
|
||||
println("Congratulations! You make the new APIs. Please restart OBP-API server!")
|
||||
}
|
||||
|
||||
val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("src/main/scala/code/api/APIBuilder/modelSource.json")
|
||||
val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("src/main/scala/code/api/APIBuilder/APIModelSource.json")
|
||||
|
||||
//"/templates"
|
||||
val apiUrl= getApiUrl(jsonJValueFromFile)
|
||||
|
||||
@ -23,9 +23,10 @@ Berlin 13359, Germany
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
*/
|
||||
package code.api.APIBuilder;
|
||||
package code.api.APIBuilder.apiResourceDoc;
|
||||
|
||||
import code.api.APIBuilder.APIBuilderModel._
|
||||
import code.api.APIBuilder.APIBuilderModel
|
||||
import code.api.util.APIUtil
|
||||
import scala.meta._
|
||||
import net.liftweb.json.JsonAST.{JObject, JString}
|
||||
@ -35,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/apisResource.json")
|
||||
val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("src/main/scala/code/api/APIBuilder/apiResourceDoc/apisResource.json")
|
||||
|
||||
val resourceDocsJObject= jsonJValueFromFile.\("resource_docs").children.asInstanceOf[List[JObject]]
|
||||
|
||||
@ -505,4 +506,4 @@ object JsonFactory_APIBuilder{
|
||||
}
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,457 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
*/
|
||||
package code.api.APIBuilder.swagger
|
||||
|
||||
import code.api.APIBuilder.APIBuilderModel._
|
||||
import code.api.APIBuilder.APIBuilderModel
|
||||
import code.api.util.APIUtil
|
||||
import net.liftweb.json.JsonAST.{JObject, JString}
|
||||
import net.liftweb.json.{JArray, JValue}
|
||||
|
||||
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 getSingleApiResponseBody: JValue = jsonJValueFromFile \\("foo")\"foo"\"value"
|
||||
//"template"
|
||||
val modelName = getModelName(getSingleApiResponseBody)
|
||||
//All the fields in the template object.
|
||||
val modelFieldsJValue: JValue = (getSingleApiResponseBody \modelName).children.head
|
||||
|
||||
//TEMPLATE
|
||||
val modelNameUpperCase = modelName.toUpperCase
|
||||
//template
|
||||
val modelNameLowerCase = modelName.toLowerCase
|
||||
//Template
|
||||
val modelNameCapitalized = modelNameLowerCase.capitalize
|
||||
//MappedTemplate_123123
|
||||
val modelMappedName = s"Mapped${modelNameCapitalized}_"+Math.abs(scala.util.Random.nextLong())
|
||||
val modelTypeName = Type.Name(modelMappedName)
|
||||
val modelTermName = Term.Name(modelMappedName)
|
||||
val modelInit =Init.apply(Type.Name(modelMappedName), Term.Name(modelMappedName), Nil)
|
||||
|
||||
|
||||
val getMultipleApiSummary: String = ((jsonJValueFromFile \\"get"\\ "summary")\("summary")).asInstanceOf[JArray].children(0).asInstanceOf[JString].values
|
||||
val getSingleApiSummary: String = ((jsonJValueFromFile \\"get"\\ "summary")\("summary")).asInstanceOf[JArray].children(1).asInstanceOf[JString].values
|
||||
val deleteSingleApiSummary: String = ((jsonJValueFromFile \\"delete"\\ "summary")\("summary")).asInstanceOf[JString].values
|
||||
val createSingleApiSummary: String = ((jsonJValueFromFile \\"post"\\ "summary")\("summary")).asInstanceOf[JString].values
|
||||
|
||||
val getApiDescription: String = ((jsonJValueFromFile \\"get"\\ "description").obj.head.value).asInstanceOf[JString].values
|
||||
val getSingleDescription: String = ((jsonJValueFromFile \\"get"\\ "description").obj(3).value).asInstanceOf[JString].values
|
||||
val createApiDescription: String = ((jsonJValueFromFile \\"post"\\ "description").obj.head.value).asInstanceOf[JString].values
|
||||
val deleteApiDescription: String = ((jsonJValueFromFile \\"delete"\\ "description").obj.head.value).asInstanceOf[JString].values
|
||||
|
||||
val getMultipleApiAuthenticationStatement: Term.ApplyInfix = getAuthenticationStatement(true)
|
||||
val getSingleApiAuthenticationStatement: Term.ApplyInfix = getAuthenticationStatement(true)
|
||||
val deleteSingleApiAuthenticationStatement: Term.ApplyInfix = getAuthenticationStatement(true)
|
||||
val createSingleApiAuthenticationStatement: Term.ApplyInfix = getAuthenticationStatement(true)
|
||||
|
||||
val getMultipleApiUrl: String = (jsonJValueFromFile \\("paths")\"paths").asInstanceOf[JObject].obj(0).name
|
||||
val getSingleApiUrl: String = (jsonJValueFromFile \\("paths")\"paths").asInstanceOf[JObject].obj(1).name
|
||||
|
||||
val getMultipleApiUrlVal = Lit.String(s"$getMultipleApiUrl")
|
||||
val createSingleApiUrlVal = getMultipleApiUrl
|
||||
val getApiUrlLiftFormat = getMultipleApiUrl.replaceFirst("/", "").split("/").mkString("""""",""" :: ""","""""")
|
||||
val getApiUrlLiftweb: Lit.String = Lit.String(getApiUrlLiftFormat)
|
||||
val createApiUrlLiftweb: Lit.String = Lit.String(getApiUrlLiftFormat)
|
||||
|
||||
val getSingleApiUrlVal = Lit.String(s"$getSingleApiUrl")
|
||||
val deleteSingleApiUrlVal = Lit.String(s"$getSingleApiUrl")
|
||||
val getSingleApiUrlLiftFormat = getSingleApiUrl.replaceFirst("/", "").split("/").dropRight(1).mkString("""""",""" :: ""","""""")
|
||||
val getSingleApiUrlLiftweb: Lit.String = Lit.String(getSingleApiUrlLiftFormat)
|
||||
val deleteApiUrlLiftweb: Lit.String = Lit.String(getSingleApiUrlLiftFormat)
|
||||
|
||||
val getMultipleApiSummaryVal = Lit.String(s"$getMultipleApiSummary")
|
||||
val getSingleApiSummaryVal = Lit.String(s"$getSingleApiSummary")
|
||||
val deleteSingleApiSummaryVal = Lit.String(s"$deleteSingleApiSummary")
|
||||
val createSingleApiSummaryVal = Lit.String(s"$createSingleApiSummary")
|
||||
|
||||
val getMultipleApiDescriptionVal = Lit.String(s"$getApiDescription")
|
||||
val createSingleApiDescriptionVal = Lit.String(s"$createApiDescription")
|
||||
val getSingleApiDescriptionVal = Lit.String(s"$getSingleDescription")
|
||||
val deleteSingleApiDescriptionVal = Lit.String(s"$deleteApiDescription")
|
||||
|
||||
val errorMessageBody: Lit.String = Lit.String(s"OBP-31001: ${modelNameCapitalized} not found. Please specify a valid value for ${modelNameUpperCase}_ID.")
|
||||
val errorMessageName: Pat.Var = Pat.Var(Term.Name(s"${modelNameCapitalized}NotFound"))
|
||||
val errorMessageVal: Defn.Val = q"""val TemplateNotFound = $errorMessageBody""".copy(pats = List(errorMessageName))
|
||||
val errorMessage: Term.Name = Term.Name(errorMessageVal.pats.head.toString())
|
||||
|
||||
val getTemplatesResourceCode: Term.ApplyInfix =q"""
|
||||
resourceDocs += ResourceDoc(
|
||||
getTemplates,
|
||||
apiVersion,
|
||||
"getTemplates",
|
||||
"GET",
|
||||
$getMultipleApiUrlVal,
|
||||
$getMultipleApiSummaryVal,
|
||||
$getMultipleApiDescriptionVal,
|
||||
emptyObjectJson,
|
||||
templatesJson,
|
||||
List(UserNotLoggedIn, UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
apiTagApiBuilder :: Nil
|
||||
)"""
|
||||
val getTemplatesPartialFunction: Defn.Val = q"""
|
||||
lazy val getTemplates: OBPEndpoint ={
|
||||
case ($getApiUrlLiftweb:: Nil) JsonGet req =>
|
||||
cc =>
|
||||
{
|
||||
for{
|
||||
u <- $getMultipleApiAuthenticationStatement
|
||||
templates <- APIBuilder_Connector.getTemplates
|
||||
templatesJson = JsonFactory_APIBuilder.createTemplates(templates)
|
||||
jsonObject:JValue = decompose(templatesJson)
|
||||
}yield{
|
||||
successJsonResponse(jsonObject)
|
||||
}
|
||||
}
|
||||
}"""
|
||||
|
||||
|
||||
val createTemplateResourceCode: Term.ApplyInfix =q"""
|
||||
resourceDocs += ResourceDoc(
|
||||
createTemplate,
|
||||
apiVersion,
|
||||
"createTemplate",
|
||||
"POST",
|
||||
$createSingleApiUrlVal,
|
||||
$createSingleApiSummaryVal,
|
||||
$createSingleApiDescriptionVal,
|
||||
createTemplateJson,
|
||||
templateJson,
|
||||
List(UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
apiTagApiBuilder :: Nil
|
||||
)"""
|
||||
|
||||
val createTemplatePartialFunction: Defn.Val = q"""
|
||||
lazy val createTemplate: OBPEndpoint ={
|
||||
case ($createApiUrlLiftweb:: Nil) JsonPost json -> _ => {
|
||||
cc =>
|
||||
{
|
||||
for{
|
||||
createTemplateJson <- tryo(json.extract[CreateTemplateJson]) ?~! InvalidJsonFormat
|
||||
u <- $createSingleApiAuthenticationStatement
|
||||
template <- APIBuilder_Connector.createTemplate(createTemplateJson)
|
||||
templateJson = JsonFactory_APIBuilder.createTemplate(template)
|
||||
jsonObject:JValue = decompose(templateJson)
|
||||
}yield{
|
||||
successJsonResponse(jsonObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
val getTemplateResourceCode: Term.ApplyInfix =q"""
|
||||
resourceDocs += ResourceDoc(
|
||||
getTemplate,
|
||||
apiVersion,
|
||||
"getTemplate",
|
||||
"GET",
|
||||
$getSingleApiUrlVal,
|
||||
$getSingleApiSummaryVal,
|
||||
$getSingleApiDescriptionVal,
|
||||
emptyObjectJson,
|
||||
templateJson,
|
||||
List(UserNotLoggedIn, UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
apiTagApiBuilder :: Nil
|
||||
)"""
|
||||
|
||||
val getTemplatePartialFunction: Defn.Val = q"""
|
||||
lazy val getTemplate: OBPEndpoint ={
|
||||
case ($getSingleApiUrlLiftweb :: templateId :: Nil) JsonGet _ => {
|
||||
cc =>
|
||||
{
|
||||
for{
|
||||
u <- $getSingleApiAuthenticationStatement
|
||||
template <- APIBuilder_Connector.getTemplateById(templateId) ?~! $errorMessage
|
||||
templateJson = JsonFactory_APIBuilder.createTemplate(template)
|
||||
jsonObject:JValue = decompose(templateJson)
|
||||
}yield{
|
||||
successJsonResponse(jsonObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
}"""
|
||||
|
||||
val deleteTemplateResourceCode: Term.ApplyInfix = q"""
|
||||
resourceDocs += ResourceDoc(
|
||||
deleteTemplate,
|
||||
apiVersion,
|
||||
"deleteTemplate",
|
||||
"DELETE",
|
||||
$deleteSingleApiUrlVal,
|
||||
$deleteSingleApiSummaryVal,
|
||||
$deleteSingleApiDescriptionVal,
|
||||
emptyObjectJson,
|
||||
emptyObjectJson.copy("true"),
|
||||
List(UserNotLoggedIn, UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
apiTagApiBuilder :: Nil
|
||||
)"""
|
||||
|
||||
val deleteTemplatePartialFunction: Defn.Val = q"""
|
||||
lazy val deleteTemplate: OBPEndpoint ={
|
||||
case ($deleteApiUrlLiftweb :: templateId :: Nil) JsonDelete _ => {
|
||||
cc =>
|
||||
{
|
||||
for{
|
||||
u <- $deleteSingleApiAuthenticationStatement
|
||||
template <- APIBuilder_Connector.getTemplateById(templateId) ?~! $errorMessage
|
||||
deleted <- APIBuilder_Connector.deleteTemplate(templateId)
|
||||
}yield{
|
||||
if(deleted)
|
||||
noContentJsonResponse
|
||||
else
|
||||
errorJsonResponse("Delete not completed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
//List(author, pages, points)
|
||||
val modelFieldsNames: List[String] = getModelFieldsNames(modelFieldsJValue)
|
||||
//List(String, Int, Double)
|
||||
val modelFieldsTypes: List[String] = getModelFieldsTypes(modelFieldsNames, modelFieldsJValue)
|
||||
//List(Chinua Achebe, 209, 1.3)
|
||||
val modelFieldsDefaultValues: List[Any] = getModelFieldDefaultValues(modelFieldsNames, modelFieldsJValue)
|
||||
|
||||
//List(author: String = `Chinua Achebe`, tutor: String = `1123123 1312`, pages: Int = 209, points: Double = 1.3)
|
||||
val modelCaseClassParams: List[Term.Param] = getModelCaseClassParams(modelFieldsNames, modelFieldsTypes, modelFieldsDefaultValues)
|
||||
|
||||
//def createTemplate(createTemplateJson: CreateTemplateJson) = Full(
|
||||
// MappedTemplate_6099750036365020434.create
|
||||
// .mTemplateId(UUID.randomUUID().toString)
|
||||
// .mAuthor(createTemplateJson.author)
|
||||
// .mPages(createTemplateJson.pages)
|
||||
// .mPoints(createTemplateJson.points)
|
||||
// .saveMe())
|
||||
val createModelJsonMethod: Defn.Def = generateCreateModelJsonMethod(modelFieldsNames, modelMappedName)
|
||||
|
||||
//trait Template { `_` =>
|
||||
// def author: String
|
||||
// def tutor: String
|
||||
// def pages: Int
|
||||
// def points: Double
|
||||
// def templateId: String
|
||||
//}
|
||||
val modelTrait: Defn.Trait = getModelTrait(modelFieldsNames, modelFieldsTypes)
|
||||
|
||||
//class MappedTemplate extends Template with LongKeyedMapper[MappedTemplate] 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
|
||||
// object mTemplateId extends MappedString(this, 100)
|
||||
// override def templateId: String = mTemplateId.get
|
||||
//}
|
||||
val modelClass = getModelClass(modelTypeName, modelTermName, modelFieldsNames, modelFieldsTypes)
|
||||
|
||||
val apiSource: Source = source"""
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
*/
|
||||
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
|
||||
import code.api.util.ErrorMessages._
|
||||
import net.liftweb.common.Full
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
import net.liftweb.json
|
||||
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 =>
|
||||
|
||||
val ImplementationsBuilderAPI = new Object()
|
||||
{
|
||||
val apiVersion: ApiVersion = ApiVersion.apiBuilder
|
||||
val resourceDocs = ArrayBuffer[ResourceDoc]()
|
||||
val apiRelations = ArrayBuffer[ApiRelation]()
|
||||
val codeContext = CodeContext(resourceDocs, apiRelations)
|
||||
implicit val formats = net.liftweb.json.DefaultFormats
|
||||
|
||||
$errorMessageVal;
|
||||
def endpointsOfBuilderAPI = getTemplates :: createTemplate :: getTemplate :: deleteTemplate:: Nil
|
||||
|
||||
|
||||
$getTemplatesResourceCode
|
||||
$getTemplatesPartialFunction
|
||||
|
||||
$createTemplateResourceCode
|
||||
$createTemplatePartialFunction
|
||||
|
||||
$getTemplateResourceCode
|
||||
$getTemplatePartialFunction
|
||||
|
||||
$deleteTemplateResourceCode
|
||||
$deleteTemplatePartialFunction
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
object APIBuilder_Connector
|
||||
{
|
||||
val allAPIBuilderModels = List($modelTermName)
|
||||
|
||||
$createModelJsonMethod;
|
||||
|
||||
def getTemplates()= Full($modelTermName.findAll())
|
||||
|
||||
def getTemplateById(templateId: String)= $modelTermName.find(By($modelTermName.mTemplateId, templateId))
|
||||
|
||||
def deleteTemplate(templateId: String)= $modelTermName.find(By($modelTermName.mTemplateId, templateId)).map(_.delete_!)
|
||||
|
||||
}
|
||||
|
||||
import net.liftweb.mapper._
|
||||
|
||||
$modelClass
|
||||
|
||||
object $modelTermName extends $modelInit with LongKeyedMetaMapper[$modelTypeName] {}
|
||||
|
||||
$modelTrait
|
||||
"""
|
||||
|
||||
/*
|
||||
* ######################################JsonFactory_APIBuilder.scala###################################################
|
||||
* */
|
||||
|
||||
//List(templateId:String = "11231231312" ,author: String = `Chinua Achebe`, tutor: String = `11231231312`, pages: Int = 209, points: Double = 1.3)
|
||||
//Added the templatedId to `modelCaseClassParams`
|
||||
val templateJsonClassParams = List(APIBuilderModel.templateIdField)++ modelCaseClassParams
|
||||
|
||||
//case class TemplateJson(templateId: String = """1123123 1312""", author: String = """Chinua Achebe""", tutor: String = """1123123 1312""", pages: Int = 209, points: Double = 1.3)
|
||||
val TemplateJsonClass: Defn.Class = q"""case class TemplateJson(..$templateJsonClassParams) """
|
||||
|
||||
//case class Template(author: String = `Chinua Achebe`, pages: Int = 209, points: Double = 1.3)
|
||||
//Note: No `templateId` in this class, the bank no need provide it, obp create a uuid for it.
|
||||
val createTemplateJsonClass: Defn.Class = q"""case class CreateTemplateJson(..$modelCaseClassParams) """
|
||||
|
||||
//TemplateJson(template.templateId, template.author, template.tutor, template.pages, template.points)
|
||||
val createTemplateJsonApply: Term.Apply = generateCreateTemplateJsonApply(modelFieldsNames)
|
||||
|
||||
//def createTemplate(template: Template) = TemplateJson(template.templateId, template.author, template.tutor, template.pages, template.points)
|
||||
val createTemplateDef: Defn.Def =q"""def createTemplate(template: Template) = $createTemplateJsonApply"""
|
||||
|
||||
//def createTemplates(templates: List[Template]) = templates.map(template => TemplateJson(template.templateId, template.author, template.tutor, template.pages, template.points))
|
||||
val createTemplatesDef: Defn.Def = q"""def createTemplates(templates: List[Template])= templates.map(template => $createTemplateJsonApply)"""
|
||||
|
||||
val jsonFactorySource: Source =source"""
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
*/
|
||||
package code.api.builder
|
||||
import code.api.util.APIUtil
|
||||
|
||||
$TemplateJsonClass
|
||||
$createTemplateJsonClass
|
||||
|
||||
object JsonFactory_APIBuilder{
|
||||
|
||||
val templateJson = TemplateJson()
|
||||
val templatesJson = List(templateJson)
|
||||
val createTemplateJson = CreateTemplateJson()
|
||||
|
||||
$createTemplateDef;
|
||||
$createTemplatesDef;
|
||||
|
||||
val allFields =
|
||||
for (
|
||||
v <- this.getClass.getDeclaredFields
|
||||
//add guard, ignore the SwaggerJSONsV220.this and allFieldsAndValues fields
|
||||
if (APIUtil.notExstingBaseClass(v.getName()))
|
||||
)
|
||||
yield {
|
||||
v.setAccessible(true)
|
||||
v.get(this)
|
||||
}
|
||||
}
|
||||
"""
|
||||
}
|
||||
213
src/main/scala/code/api/APIBuilder/swagger/swaggerResource.json
Normal file
213
src/main/scala/code/api/APIBuilder/swagger/swaggerResource.json
Normal file
@ -0,0 +1,213 @@
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"title": "Open Bank Project API",
|
||||
"description": "An Open Source API for Banks. (c) TESOBE Ltd. 2011 - 2018. Licensed under the AGPL and commercial licences.",
|
||||
"contact": {
|
||||
"name": "TESOBE Ltd. / Open Bank Project",
|
||||
"url": "https://openbankproject.com",
|
||||
"email": "contact@tesobe.com"
|
||||
},
|
||||
"version": "b1"
|
||||
},
|
||||
"paths": {
|
||||
"/templates": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"API-Builder"
|
||||
],
|
||||
"summary": "Get Templates",
|
||||
"description": "Return All my templates. Authentication is Mandatory.",
|
||||
"operationId": "vb1-GetTemplates",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Templates"
|
||||
},
|
||||
"examples": {
|
||||
"foo": {
|
||||
"value": {
|
||||
"templates": [
|
||||
{
|
||||
"author": "Chinua Achebe",
|
||||
"pages": 209,
|
||||
"points": 1.3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorUserNotLoggedIn"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"API-Builder"
|
||||
],
|
||||
"summary": "Create Template",
|
||||
"description": "Create template. Authentication is Mandatory.",
|
||||
"operationId": "vb1-CreateTemplate",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Template"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorUserNotLoggedIn"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/templates/{template_id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"API-Builder"
|
||||
],
|
||||
"summary": "Get Template",
|
||||
"description": "Return one template. Authentication is Mandatory.",
|
||||
"operationId": "vb1-GetTemplate",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "template_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Template"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorUserNotLoggedIn"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"API-Builder"
|
||||
],
|
||||
"summary": "Delete Template",
|
||||
"description": "Delete template. Authentication is Mandatory.",
|
||||
"operationId": "vb1-DeleteTemplate",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "template_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success"
|
||||
},
|
||||
"400": {
|
||||
"description": "Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorUserNotLoggedIn"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ErrorUserNotLoggedIn": {
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string",
|
||||
"example": "OBP-20001: User not logged in. Authentication is required!"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Templates": {
|
||||
"required": [
|
||||
"templates"
|
||||
],
|
||||
"properties": {
|
||||
"templates": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Template"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Template": {
|
||||
"required": [
|
||||
"author",
|
||||
"pages",
|
||||
"points"
|
||||
],
|
||||
"properties": {
|
||||
"author": {
|
||||
"type": "string",
|
||||
"example": "Chinua Achebe"
|
||||
},
|
||||
"pages": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"example": "209"
|
||||
},
|
||||
"points": {
|
||||
"type": "number",
|
||||
"format": "double",
|
||||
"example": "2.99"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,22 +16,17 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
package code.api
|
||||
|
||||
case class ErrorMessage(
|
||||
error : String
|
||||
)
|
||||
case class ErrorMessage(code: Int,
|
||||
message: String
|
||||
)
|
||||
|
||||
@ -304,7 +304,7 @@ object GatewayLogin extends RestHelper with MdcLoggable {
|
||||
}
|
||||
case Full((s, accounts, callContextNew)) if getErrors(s).forall(_.equalsIgnoreCase("")) => // CBS returned response without any error
|
||||
logger.debug("CBS returned proper response")
|
||||
Users.users.vend.getOrCreateUserByProviderIdFuture(provider = gateway, idGivenByProvider = username) map {
|
||||
Users.users.vend.getOrCreateUserByProviderIdFuture(provider = gateway, idGivenByProvider = username, name = None, email = None) map {
|
||||
case Full(u) =>
|
||||
val isFirst = getFieldFromPayloadJson(jwtPayload, "is_first")
|
||||
// Update user account views, only when is_first == true in the GatewayLogin token's payload .
|
||||
|
||||
@ -30,6 +30,8 @@ import code.api.util.{APIUtil, CallContext, ErrorMessages, JwtUtil}
|
||||
import code.model.User
|
||||
import code.users.Users
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.nimbusds.jwt.JWTClaimsSet
|
||||
import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
|
||||
@ -51,77 +53,209 @@ object OAuth2Handshake extends RestHelper with MdcLoggable {
|
||||
valueOfAuthReqHeaderField
|
||||
}
|
||||
|
||||
private def verifyJwt(jwt: String) = {
|
||||
APIUtil.getPropsAsBoolValue("oauth2.jwt.use.ssl", false) match {
|
||||
case true =>
|
||||
JwtUtil.verifyRsaSignedJwt(jwt)
|
||||
case false =>
|
||||
JwtUtil.verifyHmacSignedJwt(jwt)
|
||||
}
|
||||
}
|
||||
|
||||
private def validateAccessToken(accessToken: String) = {
|
||||
APIUtil.getPropsValue("oauth2.jwk_set.url") match {
|
||||
case Full(url) =>
|
||||
JwtUtil.validateAccessToken(accessToken, url)
|
||||
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
|
||||
ParamFailure(a, b, c, apiFailure : APIFailure)
|
||||
case Failure(msg, t, c) =>
|
||||
Failure(msg, t, c)
|
||||
case _ =>
|
||||
Failure(ErrorMessages.Oauth2ThereIsNoUrlOfJwkSet)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Method for Old Style Endpoints
|
||||
*/
|
||||
def getUserFromOAuth2Header(sc: CallContext): (Box[User], Option[CallContext]) = {
|
||||
def getUserFromOAuth2Header(cc: CallContext): (Box[User], Option[CallContext]) = {
|
||||
APIUtil.getPropsAsBoolValue("allow_oauth2_login", true) match {
|
||||
case true =>
|
||||
val value = getValueOfOAuh2HeaderField(sc)
|
||||
validateAccessToken(value) match {
|
||||
case Full(_) =>
|
||||
val username = JwtUtil.getSubject(value).getOrElse("")
|
||||
(Users.users.vend.getUserByUserName(username), Some(sc))
|
||||
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
|
||||
(ParamFailure(a, b, c, apiFailure : APIFailure), Some(sc))
|
||||
case Failure(msg, t, c) =>
|
||||
(Failure(msg, t, c), Some(sc))
|
||||
case _ =>
|
||||
(Failure(ErrorMessages.Oauth2IJwtCannotBeVerified), Some(sc))
|
||||
val value = getValueOfOAuh2HeaderField(cc)
|
||||
if (Google.isIssuer(value)) {
|
||||
Google.applyRules(value, cc)
|
||||
} else {
|
||||
MITREId.applyRules(value, cc)
|
||||
}
|
||||
case false =>
|
||||
(Failure(ErrorMessages.Oauth2IsNotAllowed), Some(sc))
|
||||
(Failure(ErrorMessages.Oauth2IsNotAllowed), Some(cc))
|
||||
}
|
||||
}
|
||||
/*
|
||||
Method for New Style Endpoints
|
||||
*/
|
||||
def getUserFromOAuth2HeaderFuture(sc: CallContext): Future[(Box[User], Option[CallContext])] = {
|
||||
def getUserFromOAuth2HeaderFuture(cc: CallContext): Future[(Box[User], Option[CallContext])] = {
|
||||
APIUtil.getPropsAsBoolValue("allow_oauth2_login", true) match {
|
||||
case true =>
|
||||
val value = getValueOfOAuh2HeaderField(sc)
|
||||
validateAccessToken(value) match {
|
||||
case Full(_) =>
|
||||
val username = JwtUtil.getSubject(value).getOrElse("")
|
||||
(Users.users.vend.getUserByUserName(username), Some(sc))
|
||||
for {
|
||||
user <- Users.users.vend.getUserByUserNameFuture(username)
|
||||
} yield {
|
||||
(user, Some(sc))
|
||||
}
|
||||
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
|
||||
Future((ParamFailure(a, b, c, apiFailure : APIFailure), Some(sc)))
|
||||
case Failure(msg, t, c) =>
|
||||
Future((Failure(msg, t, c), Some(sc)))
|
||||
case _ =>
|
||||
Future((Failure(ErrorMessages.Oauth2IJwtCannotBeVerified), Some(sc)))
|
||||
val value = getValueOfOAuh2HeaderField(cc)
|
||||
if (Google.isIssuer(value)) {
|
||||
Google.applyRulesFuture(value, cc)
|
||||
} else {
|
||||
MITREId.applyRulesFuture(value, cc)
|
||||
}
|
||||
case false =>
|
||||
Future((Failure(ErrorMessages.Oauth2IsNotAllowed), Some(sc)))
|
||||
Future((Failure(ErrorMessages.Oauth2IsNotAllowed), Some(cc)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object MITREId {
|
||||
def validateAccessToken(accessToken: String): Box[JWTClaimsSet] = {
|
||||
APIUtil.getPropsValue("oauth2.jwk_set.url") match {
|
||||
case Full(url) =>
|
||||
JwtUtil.validateAccessToken(accessToken, url)
|
||||
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
|
||||
ParamFailure(a, b, c, apiFailure : APIFailure)
|
||||
case Failure(msg, t, c) =>
|
||||
Failure(msg, t, c)
|
||||
case _ =>
|
||||
Failure(ErrorMessages.Oauth2ThereIsNoUrlOfJwkSet)
|
||||
}
|
||||
}
|
||||
def applyRules(value: String, cc: CallContext): (Box[User], Some[CallContext]) = {
|
||||
validateAccessToken(value) match {
|
||||
case Full(_) =>
|
||||
val username = JwtUtil.getSubject(value).getOrElse("")
|
||||
(Users.users.vend.getUserByUserName(username), Some(cc))
|
||||
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
|
||||
(ParamFailure(a, b, c, apiFailure : APIFailure), Some(cc))
|
||||
case Failure(msg, t, c) =>
|
||||
(Failure(msg, t, c), Some(cc))
|
||||
case _ =>
|
||||
(Failure(ErrorMessages.Oauth2IJwtCannotBeVerified), Some(cc))
|
||||
}
|
||||
}
|
||||
def applyRulesFuture(value: String, cc: CallContext): Future[(Box[User], Some[CallContext])] = {
|
||||
validateAccessToken(value) match {
|
||||
case Full(_) =>
|
||||
val username = JwtUtil.getSubject(value).getOrElse("")
|
||||
for {
|
||||
user <- Users.users.vend.getUserByUserNameFuture(username)
|
||||
} yield {
|
||||
(user, Some(cc))
|
||||
}
|
||||
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
|
||||
Future((ParamFailure(a, b, c, apiFailure : APIFailure), Some(cc)))
|
||||
case Failure(msg, t, c) =>
|
||||
Future((Failure(msg, t, c), Some(cc)))
|
||||
case _ =>
|
||||
Future((Failure(ErrorMessages.Oauth2IJwtCannotBeVerified), Some(cc)))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
trait OAuth2Util {
|
||||
|
||||
def urlOfJwkSets: Box[String] = APIUtil.getPropsValue(nameOfProperty = "oauth2.jwk_set.url")
|
||||
|
||||
private def getClaim(name: String, idToken: String): Option[String] = {
|
||||
val claim = JwtUtil.getClaim(name = name, jwtToken = idToken).asString()
|
||||
claim match {
|
||||
case null => None
|
||||
case string => Some(string)
|
||||
}
|
||||
}
|
||||
def isIssuer(jwtToken: String): Boolean = {
|
||||
JwtUtil.getIssuer(jwtToken).map(_.contains("accounts.google.com")).getOrElse(false)
|
||||
}
|
||||
def validateIdToken(idToken: String): Box[IDTokenClaimsSet] = {
|
||||
urlOfJwkSets match {
|
||||
case Full(url) =>
|
||||
JwtUtil.validateIdToken(idToken, url)
|
||||
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
|
||||
ParamFailure(a, b, c, apiFailure : APIFailure)
|
||||
case Failure(msg, t, c) =>
|
||||
Failure(msg, t, c)
|
||||
case _ =>
|
||||
Failure(ErrorMessages.Oauth2ThereIsNoUrlOfJwkSet)
|
||||
}
|
||||
}
|
||||
/** New Style Endpoints
|
||||
* This function creates user based on "iss" and "sub" fields
|
||||
* It is mapped in next way:
|
||||
* iss => ResourceUser.provider_
|
||||
* sub => ResourceUser.providerId
|
||||
* @param idToken Google's response example:
|
||||
* {
|
||||
* "access_token": "ya29.GluUBg5DflrJciFikW5hqeKEp9r1whWnU5x2JXCm9rKkRMs2WseXX8O5UugFMDsIKuKCZlE7tTm1fMII_YYpvcMX6quyR5DXNHH8Lbx5TrZN__fA92kszHJEVqPc",
|
||||
* "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjA4ZDMyNDVjNjJmODZiNjM2MmFmY2JiZmZlMWQwNjk4MjZkZDFkYzEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM5NjY4NTQyNDU3ODA4OTI5NTkiLCJlbWFpbCI6Im1hcmtvLm1pbGljLnNyYmlqYUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6Im5HS1JUb0tOblZBMjhINk1od1hCeHciLCJuYW1lIjoiTWFya28gTWlsacSHIiwicGljdHVyZSI6Imh0dHBzOi8vbGg1Lmdvb2dsZXVzZXJjb250ZW50LmNvbS8tWGQ0NGhuSjZURG8vQUFBQUFBQUFBQUkvQUFBQUFBQUFBQUEvQUt4cndjYWR3emhtNE40dFdrNUU4QXZ4aS1aSzZrczRxZy9zOTYtYy9waG90by5qcGciLCJnaXZlbl9uYW1lIjoiTWFya28iLCJmYW1pbHlfbmFtZSI6Ik1pbGnEhyIsImxvY2FsZSI6ImVuIiwiaWF0IjoxNTQ3NzA1NjkxLCJleHAiOjE1NDc3MDkyOTF9.iUxhF_SU2vi76zPuRqAKJvFOzpb_EeP3lc5u9FO9o5xoXzVq3QooXexTfK2f1YAcWEy9LSftA34PB0QTuCZpkQChZVM359n3a3hplf6oWWkBXZN2_IG10NwEH4g0VVBCsjWBDMp6lvepN_Zn15x8opUB7272m4-smAou_WmUPTeivXRF8yPcp4J55DigcY31YP59dMQr2X-6Rr1vCRnJ6niqqJ1UDldfsgt4L7dXmUCnkDdXHwEQAZwbKbR4dUoEha3QeylCiBErmLdpIyqfKECphC6piGXZB-rRRqLz41WNfuF-3fswQvGmIkzTJDR7lQaletMp7ivsfVw8N5jFxg",
|
||||
* "expires_in": 3600,
|
||||
* "token_type": "Bearer",
|
||||
* "scope": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
|
||||
* "refresh_token": "1/HkTtUahtUTdG7D6urpPNz6g-_qufF-Y1YppcBf0v3Cs"
|
||||
* }
|
||||
* @return an existing or a new user
|
||||
*/
|
||||
def getOrCreateResourceUserFuture(idToken: String): Future[Box[User]] = {
|
||||
val subject = JwtUtil.getSubject(idToken).getOrElse("")
|
||||
val issuer = JwtUtil.getIssuer(idToken).getOrElse("")
|
||||
Users.users.vend.getOrCreateUserByProviderIdFuture(
|
||||
provider = issuer,
|
||||
idGivenByProvider = subject,
|
||||
name = getClaim(name = "name", idToken = idToken).orElse(Some(subject)),
|
||||
email = getClaim(name = "email", idToken = idToken)
|
||||
)
|
||||
}
|
||||
/** Old Style Endpoints
|
||||
* This function creates user based on "iss" and "sub" fields
|
||||
* It is mapped in next way:
|
||||
* iss => ResourceUser.provider_
|
||||
* sub => ResourceUser.providerId
|
||||
* @param idToken Google's response example:
|
||||
* {
|
||||
* "access_token": "ya29.GluUBg5DflrJciFikW5hqeKEp9r1whWnU5x2JXCm9rKkRMs2WseXX8O5UugFMDsIKuKCZlE7tTm1fMII_YYpvcMX6quyR5DXNHH8Lbx5TrZN__fA92kszHJEVqPc",
|
||||
* "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjA4ZDMyNDVjNjJmODZiNjM2MmFmY2JiZmZlMWQwNjk4MjZkZDFkYzEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM5NjY4NTQyNDU3ODA4OTI5NTkiLCJlbWFpbCI6Im1hcmtvLm1pbGljLnNyYmlqYUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6Im5HS1JUb0tOblZBMjhINk1od1hCeHciLCJuYW1lIjoiTWFya28gTWlsacSHIiwicGljdHVyZSI6Imh0dHBzOi8vbGg1Lmdvb2dsZXVzZXJjb250ZW50LmNvbS8tWGQ0NGhuSjZURG8vQUFBQUFBQUFBQUkvQUFBQUFBQUFBQUEvQUt4cndjYWR3emhtNE40dFdrNUU4QXZ4aS1aSzZrczRxZy9zOTYtYy9waG90by5qcGciLCJnaXZlbl9uYW1lIjoiTWFya28iLCJmYW1pbHlfbmFtZSI6Ik1pbGnEhyIsImxvY2FsZSI6ImVuIiwiaWF0IjoxNTQ3NzA1NjkxLCJleHAiOjE1NDc3MDkyOTF9.iUxhF_SU2vi76zPuRqAKJvFOzpb_EeP3lc5u9FO9o5xoXzVq3QooXexTfK2f1YAcWEy9LSftA34PB0QTuCZpkQChZVM359n3a3hplf6oWWkBXZN2_IG10NwEH4g0VVBCsjWBDMp6lvepN_Zn15x8opUB7272m4-smAou_WmUPTeivXRF8yPcp4J55DigcY31YP59dMQr2X-6Rr1vCRnJ6niqqJ1UDldfsgt4L7dXmUCnkDdXHwEQAZwbKbR4dUoEha3QeylCiBErmLdpIyqfKECphC6piGXZB-rRRqLz41WNfuF-3fswQvGmIkzTJDR7lQaletMp7ivsfVw8N5jFxg",
|
||||
* "expires_in": 3600,
|
||||
* "token_type": "Bearer",
|
||||
* "scope": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
|
||||
* "refresh_token": "1/HkTtUahtUTdG7D6urpPNz6g-_qufF-Y1YppcBf0v3Cs"
|
||||
* }
|
||||
* @return an existing or a new user
|
||||
*/
|
||||
def getOrCreateResourceUser(idToken: String): Box[User] = {
|
||||
val subject = JwtUtil.getSubject(idToken).getOrElse("")
|
||||
val issuer = JwtUtil.getIssuer(idToken).getOrElse("")
|
||||
Users.users.vend.getUserByProviderId(provider = issuer, idGivenByProvider = subject).or { // Find a user
|
||||
Users.users.vend.createResourceUser( // Otherwise create a new one
|
||||
provider = issuer,
|
||||
providerId = Some(subject),
|
||||
name = getClaim(name = "name", idToken = idToken).orElse(Some(subject)),
|
||||
email = getClaim(name = "email", idToken = idToken),
|
||||
userId = None
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def applyRules(value: String, cc: CallContext): (Box[User], Some[CallContext]) = {
|
||||
validateIdToken(value) match {
|
||||
case Full(_) =>
|
||||
val user = Google.getOrCreateResourceUser(value)
|
||||
(user, Some(cc))
|
||||
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
|
||||
(ParamFailure(a, b, c, apiFailure : APIFailure), Some(cc))
|
||||
case Failure(msg, t, c) =>
|
||||
(Failure(msg, t, c), Some(cc))
|
||||
case _ =>
|
||||
(Failure(ErrorMessages.Oauth2IJwtCannotBeVerified), Some(cc))
|
||||
}
|
||||
}
|
||||
def applyRulesFuture(value: String, cc: CallContext): Future[(Box[User], Some[CallContext])] = {
|
||||
validateIdToken(value) match {
|
||||
case Full(_) =>
|
||||
for {
|
||||
user <- Google.getOrCreateResourceUserFuture(value)
|
||||
} yield {
|
||||
(user, Some(cc))
|
||||
}
|
||||
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
|
||||
Future((ParamFailure(a, b, c, apiFailure : APIFailure), Some(cc)))
|
||||
case Failure(msg, t, c) =>
|
||||
Future((Failure(msg, t, c), Some(cc)))
|
||||
case _ =>
|
||||
Future((Failure(ErrorMessages.Oauth2IJwtCannotBeVerified), Some(cc)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Google extends OAuth2Util {
|
||||
override def urlOfJwkSets: Box[String] = {
|
||||
val url = APIUtil.getPropsValue(nameOfProperty = "oauth2.jwk_set.url")
|
||||
url.map(_.toLowerCase()).map(_.contains("google")).getOrElse(false) match {
|
||||
case true => url
|
||||
case false => Failure(ErrorMessages.Oauth2CannotMatchIssuerAndJwksUriException)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,17 +16,12 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ import net.liftweb.http.rest.RestHelper
|
||||
import net.liftweb.http.{JsonResponse, LiftRules, S}
|
||||
import net.liftweb.json.JsonAST.{JField, JString, JValue}
|
||||
import net.liftweb.json._
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
import net.liftweb.util.Props
|
||||
|
||||
import scala.collection.immutable.Nil
|
||||
@ -65,6 +66,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
{
|
||||
|
||||
// Determine if the partialFunctionName is due to be "featured" in API Explorer etc.
|
||||
// "Featured" means shown at the top of the list or so.
|
||||
def getIsFeaturedApi(partialFunctionName: String) : Boolean = {
|
||||
val partialFunctionNames = APIUtil.getPropsValue("featured_apis") match {
|
||||
case Full(v) =>
|
||||
@ -121,6 +123,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
case ApiVersion.v1_4_0 => Implementations1_4_0.resourceDocs ++ Implementations1_3_0.resourceDocs ++ Implementations1_2_1.resourceDocs
|
||||
case ApiVersion.v1_3_0 => Implementations1_3_0.resourceDocs ++ Implementations1_2_1.resourceDocs
|
||||
case ApiVersion.v1_2_1 => Implementations1_2_1.resourceDocs
|
||||
case _ => ArrayBuffer.empty[ResourceDoc]
|
||||
}
|
||||
|
||||
logger.debug(s"There are ${resourceDocs.length} resource docs available to $requestedApiVersion")
|
||||
@ -137,6 +140,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
case ApiVersion.v1_4_0 => OBPAPI1_4_0.routes
|
||||
case ApiVersion.v1_3_0 => OBPAPI1_3_0.routes
|
||||
case ApiVersion.v1_2_1 => OBPAPI1_2_1.routes
|
||||
case _ => Nil
|
||||
}
|
||||
|
||||
logger.debug(s"There are ${versionRoutes.length} routes available to $requestedApiVersion")
|
||||
@ -160,6 +164,10 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
|
||||
// Add any featured status and special instructions from Props
|
||||
// Overwrite the requestUrl adding /obp
|
||||
// TODO We ideally need to return two urls here:
|
||||
// 1) the implemented in url i.e. the earliest version under which this endpoint is available
|
||||
// 2) the called url (contains the version we are calling)
|
||||
|
||||
val theResourceDocs = for {
|
||||
x <- activePlusLocalResourceDocs
|
||||
// This is the "implemented in" url
|
||||
@ -172,7 +180,17 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
}
|
||||
y = x.copy(isFeatured = getIsFeaturedApi(x.partialFunctionName),
|
||||
specialInstructions = getSpecialInstructions(x.partialFunctionName),
|
||||
requestUrl = url)
|
||||
requestUrl = url,
|
||||
specifiedUrl = x.implementedInApiVersion match {
|
||||
case ApiVersion.`berlinGroupV1` => Some(url)
|
||||
case ApiVersion.`ukOpenBankingV200` => Some(url)
|
||||
case ApiVersion.`apiBuilder` => Some(url)
|
||||
// We add the /obp/vX prefix here - but this is the requested API version by the resource docs endpoint. i.e. we know this endpoint
|
||||
// is also available here as well as the requestUrl. See the resource doc for resource doc!
|
||||
case _ => Some(s"/obp/${requestedApiVersion.vDottedApiVersion}${x.requestUrl}")
|
||||
}
|
||||
|
||||
)
|
||||
} yield y
|
||||
|
||||
|
||||
@ -215,10 +233,10 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
Caching.memoizeSyncWithProvider (Some(cacheKey.toString())) (getResourceDocsTTL millisecond) {
|
||||
logger.debug(s"Generating OBP Resource Docs showCore is $showCore showPSD2 is $showPSD2 showOBWG is $showOBWG requestedApiVersion is $requestedApiVersion")
|
||||
val obpResourceDocJson = for {
|
||||
rd <- getResourceDocsList(requestedApiVersion)
|
||||
resourceDocs <- getResourceDocsList(requestedApiVersion)
|
||||
} yield {
|
||||
// Filter
|
||||
val rdFiltered = filterResourceDocs(rd, showCore, showPSD2, showOBWG, resourceDocTags, partialFunctionNames)
|
||||
val rdFiltered = filterResourceDocs(resourceDocs, showCore, showPSD2, showOBWG, resourceDocTags, partialFunctionNames)
|
||||
// Format the data as json
|
||||
val innerJson = JSONFactory1_4_0.createResourceDocsJson(rdFiltered)
|
||||
// Return
|
||||
@ -392,7 +410,8 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
|<li> operation_id is concatenation of "v", version and function and should be unique (used for DOM element IDs etc. maybe used to link to source code) </li>
|
||||
|<li> version references the version that the API call is defined in.</li>
|
||||
|<li> function is the (scala) partial function that implements this endpoint. It is unique per version of the API.</li>
|
||||
|<li> request_url is empty for the root call, else the path.</li>
|
||||
|<li> request_url is empty for the root call, else the path. It contains the standard prefix (e.g. /obp) and the implemented version (the version where this endpoint was defined) e.g. /obp/v1.2.0/resource</li>
|
||||
|<li> specified_url (recommended to use) is empty for the root call, else the path. It contains the standard prefix (e.g. /obp) and the version specified in the call e.g. /obp/v3.1.0/resource. In OBP, endpoints are first made available at the request_url, but the same resource (function call) is often made available under later versions (specified_url). To access the latest version of all endpoints use the latest version available on your OBP instance e.g. /obp/v3.1.0 - To get the original version use the request_url. We recommend to use the specified_url since non semantic improvements are more likely to be applied to later implementations of the call.</li>
|
||||
|<li> summary is a short description inline with the swagger terminology. </li>
|
||||
|<li> description may contain html markup (generated from markdown on the server).</li>
|
||||
|</ul>
|
||||
@ -409,15 +428,13 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
|
||||
def getResourceDocsObp : OBPEndpoint = {
|
||||
case "resource-docs" :: requestedApiVersionString :: "obp" :: Nil JsonGet _ => {
|
||||
val (showCore, showPSD2, showOBWG, tags, partialFunctions) = getParams()
|
||||
cc =>{
|
||||
|
||||
for {
|
||||
requestedApiVersion <- Full(ApiVersion.valueOf(requestedApiVersionString)) ?~! InvalidApiVersionString
|
||||
_ <- booleanToBox(versionIsAllowed(requestedApiVersion), ApiVersionNotSupported)
|
||||
(showCore, showPSD2, showOBWG, tags, partialFunctions) <- Full(getParams())
|
||||
requestedApiVersion <- tryo {ApiVersion.valueOf(requestedApiVersionString)} ?~! s"$InvalidApiVersionString Current Version is $requestedApiVersionString"
|
||||
_ <- booleanToBox(versionIsAllowed(requestedApiVersion), s"$ApiVersionNotSupported Current Version is $requestedApiVersionString")
|
||||
json <- getResourceDocsObpCached(showCore, showPSD2, showOBWG, requestedApiVersion, tags, partialFunctions)
|
||||
}
|
||||
yield {
|
||||
} yield {
|
||||
json
|
||||
}
|
||||
}
|
||||
@ -456,9 +473,17 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
|
||||
|
||||
def getResourceDocsSwagger : OBPEndpoint = {
|
||||
case "resource-docs" :: requestedApiVersion :: "swagger" :: Nil JsonGet _ => {
|
||||
val (showCore, showPSD2, showOBWG, resourceDocTags, partialFunctions) = getParams()
|
||||
cc =>getResourceDocsSwaggerCached(showCore, showPSD2, showOBWG, requestedApiVersion, resourceDocTags, partialFunctions)
|
||||
case "resource-docs" :: requestedApiVersionString :: "swagger" :: Nil JsonGet _ => {
|
||||
cc =>{
|
||||
for {
|
||||
(showCore, showPSD2, showOBWG, resourceDocTags, partialFunctions) <- tryo(getParams())
|
||||
requestedApiVersion <- tryo(ApiVersion.valueOf(requestedApiVersionString)) ?~! s"$InvalidApiVersionString Current Version is $requestedApiVersionString"
|
||||
_ <- booleanToBox(versionIsAllowed(requestedApiVersion), s"$ApiVersionNotSupported Current Version is $requestedApiVersionString")
|
||||
json <- getResourceDocsSwaggerCached(showCore, showPSD2, showOBWG, requestedApiVersionString, resourceDocTags, partialFunctions)
|
||||
} yield {
|
||||
json
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -398,7 +398,8 @@ object SwaggerDefinitionsJSON {
|
||||
other_account_routing_address= "String",
|
||||
other_bank_routing_scheme= "String",
|
||||
other_bank_routing_address= "String",
|
||||
is_beneficiary= true
|
||||
is_beneficiary= true,
|
||||
future_date = Some("20881230")
|
||||
)
|
||||
|
||||
|
||||
@ -457,7 +458,8 @@ object SwaggerDefinitionsJSON {
|
||||
)*/
|
||||
|
||||
val errorMessage = ErrorMessage(
|
||||
error = "String"
|
||||
code = 500,
|
||||
message = "Internal Server Error"
|
||||
)
|
||||
|
||||
val postTransactionImageJSON = PostTransactionImageJSON(
|
||||
@ -1418,7 +1420,8 @@ object SwaggerDefinitionsJSON {
|
||||
typed_success_response_body = json.parse("""{"response": { "type" :"string" }}"""),
|
||||
roles = Some(List(canCreateCustomerSwagger)),
|
||||
is_featured = false,
|
||||
special_instructions = ""
|
||||
special_instructions = "",
|
||||
specified_url = ""
|
||||
)
|
||||
|
||||
val resourceDocsJson = ResourceDocsJson(resource_docs = List(resourceDocJson))
|
||||
@ -1863,14 +1866,16 @@ object SwaggerDefinitionsJSON {
|
||||
counterpartyIdJson,
|
||||
amountOfMoneyJsonV121,
|
||||
"A description for the transaction to the counterparty",
|
||||
"SHARED"
|
||||
"SHARED",
|
||||
Some("20881230")
|
||||
)
|
||||
|
||||
val transactionRequestBodySEPAJSON = TransactionRequestBodySEPAJSON(
|
||||
amountOfMoneyJsonV121,
|
||||
ibanJson,
|
||||
"This is a SEPA Transaction Request",
|
||||
"SHARED"
|
||||
"SHARED",
|
||||
Some("20881230")
|
||||
)
|
||||
|
||||
val customerCreditRatingJSON = CustomerCreditRatingJSON(
|
||||
@ -2931,6 +2936,7 @@ object SwaggerDefinitionsJSON {
|
||||
trigger_name = ApiTrigger.onBalanceChange.toString(),
|
||||
url = "https://localhost.openbankproject.com",
|
||||
http_method = "POST",
|
||||
http_protocol = "HTTP/1.1",
|
||||
is_active = "true"
|
||||
)
|
||||
val accountWebhookPutJson = AccountWebhookPutJson(
|
||||
@ -2944,6 +2950,7 @@ object SwaggerDefinitionsJSON {
|
||||
trigger_name = ApiTrigger.onBalanceChange.toString(),
|
||||
url = "https://localhost.openbankproject.com",
|
||||
http_method = "POST",
|
||||
http_protocol = "HTTP/1.1",
|
||||
created_by_user_id = "b1fd9b29-659d-4838-a300-ea65b65b5fb6",
|
||||
is_active = true
|
||||
)
|
||||
@ -2985,6 +2992,26 @@ object SwaggerDefinitionsJSON {
|
||||
value = "2012-04-23"
|
||||
)
|
||||
|
||||
val accountApplicationJson = AccountApplicationJson(
|
||||
product_code = "saveing1",
|
||||
user_id = Some("123"),
|
||||
customer_id = Some("123")
|
||||
)
|
||||
|
||||
val accountApplicationResponseJson = AccountApplicationResponseJson (
|
||||
account_application_id = "gc23a7e2-7dd2-4bdf-a0b4-ae31232a4763",
|
||||
product_code = "saveing1",
|
||||
user = resourceUserJSON,
|
||||
customer = customerJsonV310,
|
||||
date_of_application = DateWithDayExampleObject,
|
||||
status = "REQUESTED"
|
||||
)
|
||||
val accountApplicationUpdateStatusJson = AccountApplicationUpdateStatusJson(
|
||||
status = "ACCEPTED"
|
||||
)
|
||||
|
||||
val accountApplicationsJsonV310 = AccountApplicationsJsonV310(List(accountApplicationResponseJson))
|
||||
|
||||
//The common error or success format.
|
||||
//Just some helper format to use in Json
|
||||
case class NoSupportYet()
|
||||
|
||||
@ -213,7 +213,7 @@ object SwaggerJSONFactory {
|
||||
// "400": {
|
||||
// "description": "Error",
|
||||
// "schema": {"$ref": "#/definitions/Error"
|
||||
val paths: ListMap[String, Map[String, OperationObjectJson]] = resourceDocList.groupBy(x => x.requestUrl).toSeq.sortBy(x => x._1).map { mrd =>
|
||||
val paths: ListMap[String, Map[String, OperationObjectJson]] = resourceDocList.groupBy(x => x.specifiedUrl.getOrElse(x.requestUrl)).toSeq.sortBy(x => x._1).map { mrd =>
|
||||
|
||||
//`/banks/BANK_ID` --> `/obp/v3.0.0/banks/BANK_ID`
|
||||
val pathAddedObpandVersion = mrd._1
|
||||
|
||||
@ -1,33 +1,28 @@
|
||||
/**
|
||||
* Open Bank Project - API
|
||||
* Copyright (C) 2011-2018, TESOBE Ltd
|
||||
**
|
||||
*This program is free software: you can redistribute it and/or modify
|
||||
*it under the terms of the GNU Affero General Public License as published by
|
||||
*the Free Software Foundation, either version 3 of the License, or
|
||||
*(at your option) any later version.
|
||||
**
|
||||
*This program is distributed in the hope that it will be useful,
|
||||
*but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
*GNU Affero General Public License for more details.
|
||||
**
|
||||
*You should have received a copy of the GNU Affero General Public License
|
||||
*along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
**
|
||||
*Email: contact@tesobe.com
|
||||
*TESOBE Ltd
|
||||
*Osloerstrasse 16/17
|
||||
*Berlin 13359, Germany
|
||||
**
|
||||
*This product includes software developed at
|
||||
*TESOBE (http://www.tesobe.com/)
|
||||
* by
|
||||
*Simon Redfern : simon AT tesobe DOT com
|
||||
*Stefan Bethge : stefan AT tesobe DOT com
|
||||
*Everett Sochowski : everett AT tesobe DOT com
|
||||
*Ayoub Benali: ayoub AT tesobe DOT com
|
||||
*
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
package code.api.UKOpenBanking.v2_0_0
|
||||
|
||||
|
||||
@ -1,33 +1,28 @@
|
||||
/**
|
||||
* Open Bank Project - API
|
||||
* Copyright (C) 2011-2018, TESOBE Ltd
|
||||
**
|
||||
*This program is free software: you can redistribute it and/or modify
|
||||
*it under the terms of the GNU Affero General Public License as published by
|
||||
*the Free Software Foundation, either version 3 of the License, or
|
||||
*(at your option) any later version.
|
||||
**
|
||||
*This program is distributed in the hope that it will be useful,
|
||||
*but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
*GNU Affero General Public License for more details.
|
||||
**
|
||||
*You should have received a copy of the GNU Affero General Public License
|
||||
*along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
**
|
||||
*Email: contact@tesobe.com
|
||||
*TESOBE Ltd
|
||||
*Osloerstrasse 16/17
|
||||
*Berlin 13359, Germany
|
||||
**
|
||||
*This product includes software developed at
|
||||
*TESOBE (http://www.tesobe.com/)
|
||||
* by
|
||||
*Simon Redfern : simon AT tesobe DOT com
|
||||
*Stefan Bethge : stefan AT tesobe DOT com
|
||||
*Everett Sochowski : everett AT tesobe DOT com
|
||||
*Ayoub Benali: ayoub AT tesobe DOT com
|
||||
*
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
package code.api.berlin.group.v1
|
||||
|
||||
|
||||
@ -28,14 +28,14 @@ package code.api
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import authentikat.jwt.{JsonWebToken, JwtClaimsSet, JwtHeader}
|
||||
import code.api.util.APIUtil._
|
||||
import code.api.util.{APIUtil, CallContext, ErrorMessages}
|
||||
import code.api.util._
|
||||
import code.consumer.Consumers._
|
||||
import code.model.dataAccess.AuthUser
|
||||
import code.model.{Consumer, Token, TokenType, User}
|
||||
import code.token.Tokens
|
||||
import code.util.Helper.{MdcLoggable, SILENCE_IS_GOLDEN}
|
||||
import com.nimbusds.jwt.JWTClaimsSet
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.http._
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
@ -106,8 +106,13 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
message = ErrorMessages.UsernameHasBeenLocked
|
||||
httpCode = 401
|
||||
} else {
|
||||
val claims = Map("" -> "")
|
||||
val (token:String, secret:String) = generateTokenAndSecret(claims)
|
||||
val jwtPayloadAsJson =
|
||||
"""{
|
||||
"":""
|
||||
}"""
|
||||
|
||||
val jwtClaims: JWTClaimsSet = JWTClaimsSet.parse(jwtPayloadAsJson)
|
||||
val (token:String, secret:String) = generateTokenAndSecret(jwtClaims)
|
||||
|
||||
//Save the token that we have generated
|
||||
if (saveAuthorizationToken(directLoginParameters, token, secret, userId)) {
|
||||
@ -406,14 +411,12 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
|
||||
}
|
||||
|
||||
private def generateTokenAndSecret(claims: Map[String,String]) =
|
||||
private def generateTokenAndSecret(claims: JWTClaimsSet): (String, String) =
|
||||
{
|
||||
// generate random string
|
||||
val secret_message = Helpers.randomString(40)
|
||||
// jwt header
|
||||
val header = JwtHeader("HS256")
|
||||
// generate jwt token
|
||||
val token_message = JsonWebToken(header, JwtClaimsSet(claims), secret_message)
|
||||
val token_message = CertificateUtil.jwtWithHmacProtection(claims, secret_message)
|
||||
(token_message, secret_message)
|
||||
}
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ import code.api.Constant._
|
||||
import code.api.oauth1a.Arithmetics
|
||||
import code.api.oauth1a.OauthParams._
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.{APIUtil, CallContext, ErrorMessages}
|
||||
import code.api.util._
|
||||
import code.consumer.Consumers
|
||||
import code.model.{Consumer, TokenType, User}
|
||||
import code.nonce.Nonces
|
||||
|
||||
@ -1,34 +1,29 @@
|
||||
/**
|
||||
* Open Bank Project - API
|
||||
* Copyright (C) 2011-2018, TESOBE Ltd
|
||||
**
|
||||
*This program is free software: you can redistribute it and/or modify
|
||||
*it under the terms of the GNU Affero General Public License as published by
|
||||
*the Free Software Foundation, either version 3 of the License, or
|
||||
*(at your option) any later version.
|
||||
**
|
||||
*This program is distributed in the hope that it will be useful,
|
||||
*but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
*GNU Affero General Public License for more details.
|
||||
**
|
||||
*You should have received a copy of the GNU Affero General Public License
|
||||
*along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
**
|
||||
*Email: contact@tesobe.com
|
||||
*TESOBE Ltd
|
||||
*Osloerstrasse 16/17
|
||||
*Berlin 13359, Germany
|
||||
**
|
||||
*This product includes software developed at
|
||||
*TESOBE (http://www.tesobe.com/)
|
||||
* by
|
||||
*Simon Redfern : simon AT tesobe DOT com
|
||||
*Stefan Bethge : stefan AT tesobe DOT com
|
||||
*Everett Sochowski : everett AT tesobe DOT com
|
||||
*Ayoub Benali: ayoub AT tesobe DOT com
|
||||
*
|
||||
*/
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
|
||||
package code.api.util
|
||||
|
||||
@ -79,6 +74,7 @@ import scala.collection.immutable.Nil
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
import scala.reflect.ClassTag
|
||||
import scala.xml.{Elem, XML}
|
||||
|
||||
object APIUtil extends MdcLoggable {
|
||||
@ -427,15 +423,15 @@ object APIUtil extends MdcLoggable {
|
||||
case _ =>
|
||||
httpCode
|
||||
}
|
||||
JsonResponse(Extraction.decompose(ErrorMessage(message)), getHeaders() ::: headers.list, Nil, code)
|
||||
JsonResponse(Extraction.decompose(ErrorMessage(message = message, code = code)), getHeaders() ::: headers.list, Nil, code)
|
||||
}
|
||||
|
||||
def notImplementedJsonResponse(message : String = ErrorMessages.NotImplemented, httpCode : Int = 501)(implicit headers: CustomResponseHeaders = CustomResponseHeaders(Nil)) : JsonResponse =
|
||||
JsonResponse(Extraction.decompose(ErrorMessage(message)), getHeaders() ::: headers.list, Nil, httpCode)
|
||||
JsonResponse(Extraction.decompose(ErrorMessage(message = message, code = httpCode)), getHeaders() ::: headers.list, Nil, httpCode)
|
||||
|
||||
|
||||
def oauthHeaderRequiredJsonResponse(implicit headers: CustomResponseHeaders = CustomResponseHeaders(Nil)) : JsonResponse =
|
||||
JsonResponse(Extraction.decompose(ErrorMessage("Authentication via OAuth is required")), getHeaders() ::: headers.list, Nil, 400)
|
||||
JsonResponse(Extraction.decompose(ErrorMessage(message = "Authentication via OAuth is required", code = 400)), getHeaders() ::: headers.list, Nil, 400)
|
||||
|
||||
/** check the currency ISO code from the ISOCurrencyCodes.xml file */
|
||||
def isValidCurrencyISOCode(currencyCode: String): Boolean = {
|
||||
@ -458,6 +454,10 @@ object APIUtil extends MdcLoggable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** enforce the password.
|
||||
* The rules :
|
||||
* 1) length is >16 characters without validations
|
||||
@ -1032,7 +1032,7 @@ object APIUtil extends MdcLoggable {
|
||||
implementedInApiVersion: ApiVersion, // TODO: Use ApiVersion enumeration instead of string
|
||||
partialFunctionName: String, // The string name of the partial function that implements this resource. Could use it to link to the source code that implements the call
|
||||
requestVerb: String, // GET, POST etc. TODO: Constrain to GET, POST etc.
|
||||
requestUrl: String, // The URL (includes implemented in prefix e.g. /obp/vX.X). Starts with / No trailing slash. TODO Constrain the string?
|
||||
requestUrl: String, // The URL. THIS GETS MODIFIED TO include the implemented in prefix e.g. /obp/vX.X). Starts with / No trailing slash.
|
||||
summary: String, // A summary of the call (originally taken from code comment) SHOULD be under 120 chars to be inline with Swagger
|
||||
description: String, // Longer description (originally taken from github wiki)
|
||||
exampleRequestBody: scala.Product, // An example of the body required (maybe empty)
|
||||
@ -1042,7 +1042,8 @@ object APIUtil extends MdcLoggable {
|
||||
tags: List[ResourceDocTag],
|
||||
roles: Option[List[ApiRole]] = None,
|
||||
isFeatured: Boolean = false,
|
||||
specialInstructions: Option[String] = None
|
||||
specialInstructions: Option[String] = None,
|
||||
specifiedUrl: Option[String] = None // A derived value: Contains the called version (added at run time). See the resource doc for resource doc!
|
||||
)
|
||||
|
||||
|
||||
@ -1297,6 +1298,20 @@ Returns a string showed to the developer
|
||||
def hasScope(bankId: String, consumerId: String, role: ApiRole): Boolean = {
|
||||
!Scope.scope.vend.getScope(bankId, consumerId, role.toString).isEmpty
|
||||
}
|
||||
def getConsumerPrimaryKey(callContext: Option[CallContext]): String = {
|
||||
callContext match {
|
||||
case Some(cc) =>
|
||||
cc.consumer.map(_.id.get.toString).getOrElse("")
|
||||
case _ =>
|
||||
""
|
||||
}
|
||||
}
|
||||
def checkScope(bankId: String, consumerId: String, role: ApiRole): Boolean = {
|
||||
REQUIRE_SCOPES match {
|
||||
case false => true // if the props require_scopes == false, we do not need to check the Scope stuff..
|
||||
case true => !Scope.scope.vend.getScope(bankId, consumerId, role.toString).isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
// Function checks does a consumer specified by a parameter consumerId has at least one role provided by a parameter roles at a bank specified by a parameter bankId
|
||||
// i.e. does consumer has assigned at least one role from the list
|
||||
@ -1388,11 +1403,12 @@ Returns a string showed to the developer
|
||||
|
||||
|
||||
def saveConnectorMetric[R](blockOfCode: => R)(nameOfFunction: String = "")(implicit nameOfConnector: String): R = {
|
||||
val t0 = System.currentTimeMillis()
|
||||
// call-by-name
|
||||
val result = blockOfCode
|
||||
val t1 = System.currentTimeMillis()
|
||||
|
||||
if (getPropsAsBoolValue("write_connector_metrics", false)){
|
||||
val t0 = System.currentTimeMillis()
|
||||
// call-by-name
|
||||
val t1 = System.currentTimeMillis()
|
||||
val correlationId = getCorrelationId()
|
||||
Future {
|
||||
ConnectorMetricsProvider.metrics.vend.saveConnectorMetric(nameOfConnector, nameOfFunction, correlationId, now, t1 - t0)
|
||||
@ -1641,6 +1657,7 @@ Returns a string showed to the developer
|
||||
case ApiVersion.`berlinGroupV1` => LiftRules.statelessDispatch.append(OBP_BERLIN_GROUP_1)
|
||||
case ApiVersion.`ukOpenBankingV200` => LiftRules.statelessDispatch.append(OBP_UKOpenBanking_200)
|
||||
case ApiVersion.`apiBuilder` => LiftRules.statelessDispatch.append(OBP_APIBuilder)
|
||||
case _ => logger.info(s"There is no ${version.toString}")
|
||||
}
|
||||
|
||||
logger.info(s"${version.toString} was ENABLED")
|
||||
@ -1793,6 +1810,7 @@ Returns a string showed to the developer
|
||||
t => Full(logEndpointTiming(t._2.map(_.toLight))(reply.apply(successJsonResponseNewStyle(t._1, t._2)(getHeadersNewStyle(t._2.map(_.toLight))))))
|
||||
)
|
||||
in.onFail {
|
||||
case Failure(null, _, _) => Full(reply.apply(errorJsonResponse(UnknownError)))
|
||||
case Failure(msg, _, _) =>
|
||||
extractAPIFailureNewStyle(msg) match {
|
||||
case Some(af) =>
|
||||
@ -1801,7 +1819,7 @@ Returns a string showed to the developer
|
||||
Full((reply.apply(errorJsonResponse(msg))))
|
||||
}
|
||||
case _ =>
|
||||
Full(reply.apply(errorJsonResponse("Error")))
|
||||
Full(reply.apply(errorJsonResponse(UnknownError)))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -1935,6 +1953,20 @@ Returns a string showed to the developer
|
||||
}
|
||||
}
|
||||
|
||||
def unboxFuture[T](box: Box[Future[T]]): Future[Box[T]] = box match {
|
||||
case Full(v) => v.map(Box !! _)
|
||||
case other => Future(other.asInstanceOf[Box[T]])
|
||||
}
|
||||
|
||||
def unboxOBPReturnType[T](box: Box[OBPReturnType[T]]): Future[Box[T]] = box match {
|
||||
case Full(v) => v.map(Box !! _._1)
|
||||
case other => Future(other.asInstanceOf[Box[T]])
|
||||
}
|
||||
|
||||
def unboxOptionFuture[T](option: Option[Future[T]]): Future[Box[T]] = unboxFuture(Box(option))
|
||||
|
||||
def unboxOptionOBPReturnType[T](option: Option[OBPReturnType[T]]): Future[Box[T]] = unboxOBPReturnType(Box(option))
|
||||
|
||||
|
||||
/**
|
||||
* This function checks rate limiting for a Consumer.
|
||||
@ -2167,7 +2199,13 @@ Returns a string showed to the developer
|
||||
* @return Decrypted value of a property
|
||||
*/
|
||||
def getPropsValue(nameOfProperty: String): Box[String] = {
|
||||
(Props.get(nameOfProperty), Props.get(nameOfProperty + ".is_encrypted"), Props.get(nameOfProperty + ".is_obfuscated") ) match {
|
||||
|
||||
val brandSpecificPropertyName = getBrandSpecificPropertyName(nameOfProperty)
|
||||
|
||||
logger.debug(s"Standard property $nameOfProperty has bankSpecificPropertyName: $brandSpecificPropertyName")
|
||||
|
||||
|
||||
(Props.get(brandSpecificPropertyName), Props.get(brandSpecificPropertyName + ".is_encrypted"), Props.get(brandSpecificPropertyName + ".is_obfuscated") ) match {
|
||||
case (Full(base64PropsValue), Full(isEncrypted), Empty) if isEncrypted == "true" =>
|
||||
val decryptedValueAsString = RSAUtil.decrypt(base64PropsValue)
|
||||
Full(decryptedValueAsString)
|
||||
@ -2182,8 +2220,8 @@ Returns a string showed to the developer
|
||||
case (Empty, Empty, Empty) =>
|
||||
Empty
|
||||
case _ =>
|
||||
logger.error(cannotDecryptValueOfProperty + nameOfProperty)
|
||||
Failure(cannotDecryptValueOfProperty + nameOfProperty)
|
||||
logger.error(cannotDecryptValueOfProperty + brandSpecificPropertyName)
|
||||
Failure(cannotDecryptValueOfProperty + brandSpecificPropertyName)
|
||||
}
|
||||
}
|
||||
def getPropsValue(nameOfProperty: String, defaultValue: String): String = {
|
||||
@ -2206,6 +2244,58 @@ Returns a string showed to the developer
|
||||
getPropsAsLongValue(nameOfProperty) openOr(defaultValue)
|
||||
}
|
||||
|
||||
/*
|
||||
Get any brand specified in url parameter or form field, validate it, and if all good, set the session
|
||||
Else just return the session
|
||||
Note there are Read and Write side effects here!
|
||||
*/
|
||||
def activeBrand() : Option[String] = {
|
||||
|
||||
val brandParameter = "brand"
|
||||
|
||||
// Use brand in parameter (query or form)
|
||||
val brand : Option[String] = S.param(brandParameter) match {
|
||||
case Full(value) => {
|
||||
// If found, and has a valid format, set the session.
|
||||
if (isValidID(value)) {
|
||||
S.setSessionAttribute(brandParameter, value)
|
||||
logger.debug(s"activeBrand says: I found a $brandParameter param. $brandParameter session has been set to: ${S.getSessionAttribute(brandParameter)}")
|
||||
Some(value)
|
||||
} else {
|
||||
logger.warn (s"activeBrand says: ${ErrorMessages.InvalidBankIdFormat}")
|
||||
None
|
||||
}
|
||||
}
|
||||
case _ => {
|
||||
// Else look in the session
|
||||
S.getSessionAttribute(brandParameter)
|
||||
}
|
||||
}
|
||||
brand
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
For bank specific branding and possibly other customisations, if we have an active brand (in url param, form field, session),
|
||||
we will look for property_FOR_BRAND_<BANK_ID>
|
||||
We also check that the property exists, else return the standard property name.
|
||||
*/
|
||||
def getBrandSpecificPropertyName(nameOfProperty: String) : String = {
|
||||
// If we have an active brand, construct a target property name to look for.
|
||||
val brandSpecificPropertyName = activeBrand() match {
|
||||
case Some(brand) => s"${nameOfProperty}_FOR_BRAND_${brand}"
|
||||
case _ => nameOfProperty
|
||||
}
|
||||
|
||||
// Check if the property actually exits, if not, return the default / standard property name
|
||||
val propertyToUse = Props.get(brandSpecificPropertyName) match {
|
||||
case Full(value) => brandSpecificPropertyName
|
||||
case _ => nameOfProperty
|
||||
}
|
||||
|
||||
propertyToUse
|
||||
}
|
||||
|
||||
|
||||
|
||||
val ALLOW_PUBLIC_VIEWS: Boolean = getPropsAsBoolValue("allow_public_views", false)
|
||||
|
||||
@ -213,7 +213,16 @@ object ApiRole {
|
||||
|
||||
case class CanRefreshUser(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canRefreshUser = CanRefreshUser()
|
||||
|
||||
case class CanGetAccountApplications(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canGetAccountApplications = CanGetAccountApplications()
|
||||
|
||||
case class CanUpdateAccountApplications(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canUpdateAccountApplications = CanUpdateAccountApplications()
|
||||
|
||||
case class CanReadFx(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canReadFx = CanReadFx()
|
||||
|
||||
private val roles =
|
||||
canSearchAllTransactions ::
|
||||
canSearchAllAccounts ::
|
||||
@ -284,6 +293,9 @@ object ApiRole {
|
||||
canCreateTaxResidence ::
|
||||
canDeleteTaxResidence ::
|
||||
canRefreshUser ::
|
||||
canGetAccountApplications::
|
||||
canUpdateAccountApplications::
|
||||
canReadFx::
|
||||
Nil
|
||||
|
||||
lazy val rolesMappedToClasses = roles.map(_.getClass)
|
||||
|
||||
@ -34,6 +34,17 @@ case class CallContext(
|
||||
`X-Rate-Limit-Remaining` : Long = -1,
|
||||
`X-Rate-Limit-Reset` : Long = -1
|
||||
) {
|
||||
|
||||
def removeResourceDocument: CallContext = this.copy(resourceDocument = None)
|
||||
|
||||
def toCallContextAkka: CallContextAkka =
|
||||
CallContextAkka(
|
||||
userId = this.user.map(_.userId).toOption,
|
||||
consumerId = this.consumer.map(_.consumerId.get).toOption,
|
||||
correlationId = this.correlationId,
|
||||
sessionId = this.sessionId
|
||||
)
|
||||
|
||||
def toLight: CallContextLight = {
|
||||
CallContextLight(
|
||||
gatewayLoginRequestPayload = this.gatewayLoginRequestPayload,
|
||||
@ -86,6 +97,12 @@ case class CallContextLight(gatewayLoginRequestPayload: Option[PayloadOfJwtJSON]
|
||||
`X-Rate-Limit-Reset` : Long = -1
|
||||
)
|
||||
|
||||
case class CallContextAkka(userId: Option[String] = None,
|
||||
consumerId: Option[String] = None,
|
||||
correlationId: String = "",
|
||||
sessionId: Option[String] = None,
|
||||
)
|
||||
|
||||
trait GatewayLoginParam
|
||||
case class GatewayLoginRequestPayload(jwtPayload: Option[PayloadOfJwtJSON]) extends GatewayLoginParam
|
||||
case class GatewayLoginResponseHeader(jwt: Option[String]) extends GatewayLoginParam
|
||||
|
||||
@ -10,6 +10,7 @@ object ApiTag {
|
||||
val apiTagApi = ResourceDocTag("API")
|
||||
val apiTagBank = ResourceDocTag("Bank")
|
||||
val apiTagAccount = ResourceDocTag("Account")
|
||||
val apiTagAccountApplication = ResourceDocTag("Account-Application")
|
||||
val apiTagAccountPublic = ResourceDocTag("Account-Public")
|
||||
val apiTagAccountFirehose = ResourceDocTag("Account-Firehose")
|
||||
val apiTagFirehoseData = ResourceDocTag("FirehoseData")
|
||||
@ -19,9 +20,10 @@ object ApiTag {
|
||||
val apiTagTransactionFirehose = ResourceDocTag("Transaction-Firehose")
|
||||
val apiTagCounterpartyMetaData = ResourceDocTag("Counterparty-Metadata")
|
||||
val apiTagTransactionMetaData = ResourceDocTag("Transaction-Metadata")
|
||||
val apiTagView = ResourceDocTag("Account-View")
|
||||
val apiTagView = ResourceDocTag("View")
|
||||
val apiTagEntitlement = ResourceDocTag("Entitlement")
|
||||
val apiTagRole = ResourceDocTag("API-Role")
|
||||
val apiTagRole = ResourceDocTag("Role")
|
||||
val apiTagScope = ResourceDocTag("Scope")
|
||||
val apiTagOwnerRequired = ResourceDocTag("OwnerViewRequired")
|
||||
val apiTagCounterparty = ResourceDocTag("Counterparty")
|
||||
val apiTagKyc = ResourceDocTag("KYC")
|
||||
@ -32,20 +34,20 @@ object ApiTag {
|
||||
val apiTagExperimental = ResourceDocTag("Experimental")
|
||||
val apiTagPerson = ResourceDocTag("Person")
|
||||
val apiTagCard = ResourceDocTag("Card")
|
||||
val apiTagSandbox = ResourceDocTag("API-Sandbox")
|
||||
val apiTagSandbox = ResourceDocTag("Sandbox")
|
||||
val apiTagBranch = ResourceDocTag("Branch")
|
||||
val apiTagATM = ResourceDocTag("ATM")
|
||||
val apiTagProduct = ResourceDocTag("Product")
|
||||
val apiTagOpenData = ResourceDocTag("Open-Data")
|
||||
val apiTagConsumer = ResourceDocTag("API-Consumer")
|
||||
val apiTagConsumer = ResourceDocTag("Consumer")
|
||||
val apiTagSearchWarehouse = ResourceDocTag("Data-Warehouse")
|
||||
val apiTagFx = ResourceDocTag("FX")
|
||||
val apiTagMessage = ResourceDocTag("Customer-Message")
|
||||
val apiTagMetric = ResourceDocTag("API-Metric")
|
||||
val apiTagDocumentation = ResourceDocTag("API-Documentation")
|
||||
val apiTagMetric = ResourceDocTag("Metric")
|
||||
val apiTagDocumentation = ResourceDocTag("Documentation")
|
||||
val apiTagBerlinGroup = ResourceDocTag("Berlin-Group")
|
||||
val apiTagUKOpenBanking = ResourceDocTag("UKOpenBanking")
|
||||
val apiTagApiBuilder = ResourceDocTag("API_Builder")
|
||||
val apiTagApiBuilder = ResourceDocTag("API-Builder")
|
||||
val apiTagAggregateMetrics = ResourceDocTag("Aggregate-Metrics")
|
||||
val apiTagNewStyle = ResourceDocTag("New-Style")
|
||||
val apiTagWebhook = ResourceDocTag("Webhook")
|
||||
|
||||
@ -9,7 +9,6 @@ import code.util.Helper.MdcLoggable
|
||||
import com.nimbusds.jose._
|
||||
import com.nimbusds.jose.crypto.{MACSigner, RSAEncrypter}
|
||||
import com.nimbusds.jwt.{EncryptedJWT, JWTClaimsSet}
|
||||
import javax.crypto.Cipher
|
||||
|
||||
|
||||
object CryptoSystem extends Enumeration {
|
||||
@ -65,7 +64,22 @@ object CertificateUtil extends MdcLoggable {
|
||||
keyPairGenerator.genKeyPair
|
||||
}
|
||||
|
||||
def jwtWithHmacProtection(claimsSet: JWTClaimsSet) = {
|
||||
def jwtWithHmacProtection(claimsSet: JWTClaimsSet): String = {
|
||||
// Create HMAC signer
|
||||
val signer: JWSSigner = new MACSigner(sharedSecret)
|
||||
import com.nimbusds.jose.{JWSAlgorithm, JWSHeader}
|
||||
import com.nimbusds.jwt.SignedJWT
|
||||
val signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet)
|
||||
// Apply the HMAC protection
|
||||
signedJWT.sign(signer)
|
||||
// Serialize to compact form, produces something like
|
||||
// eyJhbGciOiJIUzI1NiJ9.SGVsbG8sIHdvcmxkIQ.onO9Ihudz3WkiauDO2Uhyuz0Y18UASXlSc1eS0NkWyA
|
||||
val s: String = signedJWT.serialize()
|
||||
// logger.info("jwtWithHmacProtection: " + s)
|
||||
s
|
||||
}
|
||||
|
||||
def jwtWithHmacProtection(claimsSet: JWTClaimsSet, sharedSecret: String): String = {
|
||||
// Create HMAC signer
|
||||
val signer: JWSSigner = new MACSigner(sharedSecret)
|
||||
import com.nimbusds.jose.{JWSAlgorithm, JWSHeader}
|
||||
@ -80,7 +94,16 @@ object CertificateUtil extends MdcLoggable {
|
||||
s
|
||||
}
|
||||
|
||||
def verifywtWithHmacProtection(jwt: String) = {
|
||||
def verifywtWithHmacProtection(jwt: String): Boolean = {
|
||||
import com.nimbusds.jose.crypto.MACVerifier
|
||||
import com.nimbusds.jwt.SignedJWT
|
||||
val signedJWT: SignedJWT = SignedJWT.parse(jwt)
|
||||
// your-at-least-256-bit-secret
|
||||
val verifier = new MACVerifier(sharedSecret)
|
||||
signedJWT.verify(verifier)
|
||||
}
|
||||
|
||||
def verifywtWithHmacProtection(jwt: String, sharedSecret: String): Boolean = {
|
||||
import com.nimbusds.jose.crypto.MACVerifier
|
||||
import com.nimbusds.jwt.SignedJWT
|
||||
val signedJWT: SignedJWT = SignedJWT.parse(jwt)
|
||||
|
||||
@ -51,6 +51,8 @@ object ErrorMessages {
|
||||
val InvalidUrl = "OBP-10017: Incorrect URL Format. "
|
||||
val TooManyRequests = "OBP-10018: Too Many Requests."
|
||||
val InvalidBoolean = "OBP-10019: Invalid Boolean. Could not convert value to a boolean type."
|
||||
val InvalidUri = "OBP-404: 404 Not Found. The server has not found anything matching the Request-URI.Check your URL and the headers. " +
|
||||
"NOTE: when it is POST or PUT api, the Content-Type must be `application/json`. OBP only support the json format body."
|
||||
|
||||
// General Sort and Paging
|
||||
val FilterSortDirectionError = "OBP-10023: obp_sort_direction parameter can only take two values: DESC or ASC!" // was OBP-20023
|
||||
@ -131,6 +133,9 @@ object ErrorMessages {
|
||||
val Oauth2ThereIsNoUrlOfJwkSet = "OBP-20203: There is no an URL of OAuth 2.0 server's JWK set, published at a well-known URL."
|
||||
val Oauth2BadJWTException = "OBP-20204: Bad JWT error. "
|
||||
val Oauth2ParseException = "OBP-20205: Parse error. "
|
||||
val Oauth2BadJOSEException = "OBP-20206: Bad JSON Object Signing and Encryption (JOSE) exception. "
|
||||
val Oauth2JOSEException = "OBP-20207: Javascript Object Signing and Encryption (JOSE) exception. "
|
||||
val Oauth2CannotMatchIssuerAndJwksUriException = "OBP-20208: Cannot match the issuer and JWKS URI at this server instance. "
|
||||
|
||||
val InvalidAmount = "OBP-20054: Invalid amount. Please specify a valid value for amount."
|
||||
val MissingQueryParams = "OBP-20055: These query parameters are missing: "
|
||||
@ -236,7 +241,7 @@ object ErrorMessages {
|
||||
|
||||
val InvalidStrongPasswordFormat = "OBP-30207: Invalid Password Format. Your password should EITHER be at least 10 characters long and contain mixed numbers and both upper and lower case letters and at least one special character, OR be longer than 16 characters."
|
||||
|
||||
val AccountIdAlreadyExsits = "OBP-30208: Account_ID already exists at the Bank."
|
||||
val AccountIdAlreadyExists = "OBP-30208: Account_ID already exists at the Bank."
|
||||
|
||||
|
||||
val InsufficientAuthorisationToCreateBranch = "OBP-30209: Insufficient authorisation to Create Branch. You do not have the role CanCreateBranch." // was OBP-20019
|
||||
@ -253,6 +258,12 @@ object ErrorMessages {
|
||||
|
||||
val TaxResidenceNotFound = "OBP-30300: Tax Residence not found by TAX_RESIDENCE_ID. "
|
||||
val CustomerAddressNotFound = "OBP-30310: Customer's Address not found by CUSTOMER_ADDRESS_ID. "
|
||||
val AccountApplicationNotFound = "OBP-30311: AccountApplication not found by ACCOUNT_APPLICATION_ID. "
|
||||
val ResourceUserNotFound = "OBP-30312: ResourceUser not found by USER_ID. "
|
||||
val UserIdAndCustomerIdNotPresent = "OBP-30313: userId and customerId not present. "
|
||||
val AccountApplicationAlreadyAccepted = "OBP-30314: AccountApplication has already been accepted. "
|
||||
val UpdateAccountApplicationStatusError = "OBP-30315: AccountApplication Status can not be updated. "
|
||||
val CreateAccountApplicationError = "OBP-30316: AccountApplication Status can not be created. "
|
||||
|
||||
// Branch related messages
|
||||
val branchesNotFoundLicense = "OBP-32001: No branches available. License may not be set."
|
||||
@ -303,6 +314,8 @@ object ErrorMessages {
|
||||
val NoCallContext = "OBP-50012: Can not get the CallContext object here."
|
||||
val UnspecifiedCbsError = "OBP-50013: The Core Banking System returned an unspecified error or response."
|
||||
val RefreshUserError = "OBP-50014: Can not refresh User."
|
||||
val InternalServerError = "OBP-50015: The server encountered an unexpected condition which prevented it from fulfilling the request."
|
||||
|
||||
|
||||
// Connector Data Exceptions (OBP-502XX)
|
||||
val ConnectorEmptyResponse = "OBP-50200: Connector cannot return the data we requested." // was OBP-30200
|
||||
|
||||
@ -30,7 +30,7 @@ object ExampleValue {
|
||||
val usernameExample = ConnectorField("felixsmith", s"The username the user uses to authenticate.")
|
||||
glossaryItems += makeGlossaryItem("Adapter.username", usernameExample)
|
||||
|
||||
val correlationIdExample = ConnectorField("2ba9a7e4-6d02-40e3-a129-0b2bf89de92b", s"A UUID string generated by OBP-API that MUST uniquely identify the API call recieved by OBP-API. Used for debugging and logging purposes. It is returned in header to the caller.")
|
||||
val correlationIdExample = ConnectorField("1flssoftxq0cr1nssr68u0mioj", s"A string generated by OBP-API that MUST uniquely identify the API call received by OBP-API. Used for debugging and logging purposes. It is returned in header to the caller.")
|
||||
glossaryItems += makeGlossaryItem("API.correlation_id", correlationIdExample)
|
||||
|
||||
|
||||
@ -77,6 +77,8 @@ object ExampleValue {
|
||||
val accountTypeExample = ConnectorField("AC","A short code that represents the type of the account as provided by the bank.")
|
||||
|
||||
val balanceAmountExample = ConnectorField("50.89", "The balance on the account.")
|
||||
|
||||
val creditLimitAmountExample = ConnectorField("1000.00", "The credit limit on the accounts of a customer.")
|
||||
|
||||
val transactionAmountExample = ConnectorField("19.64", "A Transaction Amount.")
|
||||
|
||||
|
||||
@ -85,101 +85,205 @@ object Glossary {
|
||||
|
||||
|
||||
|
||||
|
||||
glossaryItems += GlossaryItem(
|
||||
title = "Cheat Sheet",
|
||||
description =
|
||||
s"""
|
||||
|## A selection of links to get you started using the Open Bank Project API platform, applications and tools.
|
||||
|
|
||||
|[OBP API Installation](https://github.com/OpenBankProject/OBP-API/blob/develop/README.md)
|
||||
|
|
||||
|[OBP API Contributing](https://github.com/OpenBankProject/OBP-API/blob/develop/CONTRIBUTING.md)
|
||||
|
|
||||
|[Access Control](/glossary#API.Access-Control)
|
||||
|
|
||||
|[Versioning](https://github.com/OpenBankProject/OBP-API/wiki/API-Versioning)
|
||||
|
|
||||
|[Authentication](https://github.com/OpenBankProject/OBP-API/wiki/Authentication)
|
||||
|
|
||||
|[Interfaces](/glossary#API.Interfaces)
|
||||
|
|
||||
|[Endpoints](https://apiexplorersandbox.openbankproject.com)
|
||||
|
|
||||
|[Glossary](/glossary)
|
||||
|
|
||||
|[Access Control](/glossary#API.Access-Control)
|
||||
|
|
||||
|[OBP Kafka](/glossary#Adapter.Kafka.Intro)
|
||||
|
|
||||
|[OBP Akka](/glossary#Adapter.Akka.Intro)
|
||||
|
|
||||
|[API Explorer](https://github.com/OpenBankProject/API-Explorer/blob/develop/README.md)
|
||||
|
|
||||
|[API Manager](https://github.com/OpenBankProject/API-Manager/blob/master/README.md)
|
||||
|
|
||||
|[API Tester](https://github.com/OpenBankProject/API-Tester/blob/master/README.md)
|
||||
|
|
||||
|
||||
|
|
||||
""")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
glossaryItems += GlossaryItem(
|
||||
title = "Adapter.Akka.Intro",
|
||||
description =
|
||||
s"""
|
||||
|## Use Akka as an interface between OBP and your Core Banking System (CBS).
|
||||
|
|
||||
|For an introduction to Akka see [here](https://akka.io/)
|
||||
|
|
||||
|The OBP Akka interface allows integrators to write Java or Scala Adapters (any JVM language with Akka support)
|
||||
|respond to requests for data and services from OBP.
|
||||
|
|
||||
|For the message definitions see [here](/message-docs?connector=akka_vDec2018)
|
||||
|
|
||||
|This glossary item is Work In Progress.
|
||||
|
|
||||
""")
|
||||
|
||||
|
||||
|
||||
glossaryItems += GlossaryItem(
|
||||
title = "Adapter.Kafka.Intro",
|
||||
description =
|
||||
s"""
|
||||
|## Brief introduction to using Kafka as the interface layer between OBP and your Core Banking System (CBS).
|
||||
|### Prerequesites
|
||||
|## Use Kafka as an interface between OBP and your Core Banking System (CBS).
|
||||
|
|
||||
|
|
||||
|For an introduction to Kafka see [here](https://kafka.apache.org/)
|
||||
|
|
||||
|### Installation Prerequisites
|
||||
|
|
||||
|
|
||||
|* We assume you have OBP-API running and it is connected to a working Kafka installation.
|
||||
| You can check OBP -> Kafka connectivity using the <a href="/#vv3_1_0-getObpApiLoopback">"loopback" endpoint</a>.
|
||||
|* You have OBP-API running and it is connected to a Kafka installation.
|
||||
| You can check OBP -> Kafka connectivity using the <a href="/#vv3_1_0-getObpApiLoopback">"loopback" endpoint</a>.
|
||||
|
|
||||
|* Ideally you have API Explorer running (the application serving this page) but its not necessary - you could use any other REST client.
|
||||
|* You might want to also run API Manager as it makes it easier to grant yourself roles, but its not necessary - you could use the API Explorer / any REST client instead.
|
||||
|
|
||||
|### Create a Customer User and an Admin User
|
||||
|
|
||||
|* We assume you have API Explorer running (the application serving this page) but its not necessary - (you could use a REST client)
|
||||
|* You might want to also run API Manager as it makes it easier to grant yourself roles, but its not nessessary (you could use a REST client / API Explorer instead).
|
||||
|* You should register a User that you want to use to call the API. Let's call this user Jane.
|
||||
|* You will need another user that will have the roles required for the following steps. Let's call this user CarolaAdmin.
|
||||
|* Use <a href="/index#vv3_1_0-createUserAuthContext">Create Auth Context</a> to add a “token” to the User who while request accounts.
|
||||
|This token which could be a CUSTOMER_NUMBER is sent inside the AuthInfo object to Kafka
|
||||
|* OR Use Create Customer and Create User Customer Link (note that Create Auth Context is preferred)
|
||||
|
|
||||
|Then its time to configure or program your Adapter to consume, and respond to, the messages OBP will send to Kafka.
|
||||
|* Register a User who will use the API as a Customer.
|
||||
|* Register another User that will use the API as an Admin. The Admin user will need some Roles. See [here](/index#vv2_0_0-addEntitlement). You can bootstrap an Admin user by editing the Props file. See the README for that.
|
||||
|
|
||||
| We suggest they are implemented in the following order:
|
||||
|### Add some authentication context to the Customer User
|
||||
|
|
||||
|* As the Admin User, use the [Create Auth Context](/index#vv3_1_0-createUserAuthContext) endpoint to add one or more attributes to the Customer User.
|
||||
|For instance you could add the name/value pair CUSTOMER_NUMBER/889763 and this will be sent to the Adapter / CBS inside the AuthInfo object.
|
||||
|
|
||||
|
|
||||
|Now you should be able to use the [Get Auth Contexts](/index#vv3_1_0-getUserAuthContexts) endpoint to see the data you added.
|
||||
|
|
||||
|### Write or Build an Adapter to respond to the following messages.
|
||||
|
|
||||
| When getting started, we suggest that you implement the messages in the following order:
|
||||
|
|
||||
|1) Core (Prerequisites) - Get Adapter, Get Banks, Get Bank)
|
||||
|
|
||||
|* ${messageDocLink("obp.get.AdapterInfo")}
|
||||
|* ${messageDocLink("obp.get.Banks")}
|
||||
|* ${messageDocLink("obp.get.Bank")}
|
||||
|
|
||||
|2) Get Accounts
|
||||
|* ${messageDocLink("obp.get.AdapterInfo")}
|
||||
|
|
||||
|* ${messageDocLink("obp.get.CustomersByUserIdBox")}
|
||||
|* ${messageDocLink("obp.get.coreBankAccounts")}
|
||||
|* ${messageDocLink("obp.check.BankAccountExists")}
|
||||
|* ${messageDocLink("obp.get.Accounts")}
|
||||
|* ${messageDocLink("obp.get.Account")}
|
||||
|
|
||||
|3) Get Transactions
|
||||
|Now you should be able to use the [Adapter Info](/index#vv3_0_0-getAdapter) endpoint
|
||||
|
|
||||
|* ${messageDocLink("obp.get.Banks")}
|
||||
|
|
||||
|Now you should be able to use the [Get Banks](/index#vv3_0_0-getBanks) endpoint
|
||||
|
|
||||
|* ${messageDocLink("obp.get.Bank")}
|
||||
|
|
||||
|Now you should be able to use the [Get Bank](/index#vv3_0_0-bankById) endpoint
|
||||
|
|
||||
|
|
||||
|2) Customers for logged in User
|
||||
|
|
||||
|* ${messageDocLink("obp.get.CustomersByUserIdBox")}
|
||||
|
|
||||
|Now you should be able to use the [Get Customers](/index#vv3_0_0-getCustomersForUser) endpoint.
|
||||
|
|
||||
|
|
||||
|3) Get Accounts
|
||||
|
|
||||
|* ${messageDocLink("obp.check.BankAccountExists")}
|
||||
|* ${messageDocLink("obp.get.coreBankAccounts")}
|
||||
|* ${messageDocLink("obp.get.Accounts")}
|
||||
|
|
||||
| The above messages should enable at least the following endpoints:
|
||||
|
|
||||
|* [Get Accounts Held](http://localhost:8082/index#vv3_0_0-getAccountsHeld)
|
||||
|* [Get Account IDs](/index#vv3_0_0-getPrivateAccountIdsbyBankId)
|
||||
|* [Get Accounts - Minimal](index#vv3_0_0-privateAccountsAtOneBank)
|
||||
|
|
||||
|4) Get Account
|
||||
|
|
||||
|* ${messageDocLink("obp.get.Account")}
|
||||
|
|
||||
| The above message should enable at least the following endpoints:
|
||||
|
|
||||
|* [Get Account by Id - Core](/index#vv3_0_0-getCoreAccountById)
|
||||
|* [Get Account by Id - Full](/index#vv3_0_0-getPrivateAccountById)
|
||||
|
|
||||
|5) Get Transactions
|
||||
|
|
||||
|* ${messageDocLink("obp.get.Transactions")}
|
||||
|* ${messageDocLink("obp.get.Transaction")}
|
||||
|
|
||||
|4) Manage Counterparties
|
||||
|6) Manage Counterparties
|
||||
|
|
||||
|* ${messageDocLink("obp.get.counterparties")}
|
||||
|* ${messageDocLink("obp.get.CounterpartyByCounterpartyId")}
|
||||
|* ${messageDocLink("obp.create.Counterparty")}
|
||||
|
|
||||
|5) Get Transaction Request Types
|
||||
|7) Get Transaction Request Types
|
||||
|
|
||||
|* This is configured using OBP Props - No messages required
|
||||
|
|
||||
|6) Get Challenge Threshold (CBS)
|
||||
|8) Get Challenge Threshold (CBS)
|
||||
|
|
||||
|* ${messageDocLink("obp.get.getChallengeThreshold")}
|
||||
|
|
||||
|7) Make Payment (used by Create Transaction Request)
|
||||
|9) Make Payment (used by Create Transaction Request)
|
||||
|
|
||||
|* ${messageDocLink("obp.get.makePaymentv210")}
|
||||
|* This also requires 8,9,10 for high value payments.
|
||||
|
|
||||
|8) Get Transaction Requests.
|
||||
|10) Get Transaction Requests.
|
||||
|
|
||||
|* ${messageDocLink("obp.get.transactionRequests210")}
|
||||
|
|
||||
|9) Generate Security Challenges (CBS)
|
||||
|11) Generate Security Challenges (CBS)
|
||||
|
|
||||
|* ${messageDocLink("obp.create.Challenge")}
|
||||
|
|
||||
|10) Answer Security Challenges (Validate)
|
||||
|12) Answer Security Challenges (Validate)
|
||||
|
|
||||
|* Optional / Internal OBP (No additional messages required)
|
||||
|
|
||||
|11) Manage Counterparty Metadata
|
||||
|13) Manage Counterparty Metadata
|
||||
|
|
||||
|* Internal OBP (No additional messages required)
|
||||
|
|
||||
|12) Get Entitlements
|
||||
|14) Get Entitlements
|
||||
|
|
||||
|* Internal OBP (No additional messages required)
|
||||
|
|
||||
|13) Manage Roles
|
||||
|15) Manage Roles
|
||||
|
|
||||
|* Internal OBP (No additional messages required)
|
||||
|
|
||||
|14) Manage Entitlements
|
||||
|16) Manage Entitlements
|
||||
|
|
||||
|* Internal OBP (No additional messages required)
|
||||
|
|
||||
|15) Manage Views
|
||||
|17) Manage Views
|
||||
|
|
||||
|* Internal OBP (No additional messages required)
|
||||
|
|
||||
|16) Manage Transaction Metadata
|
||||
|18) Manage Transaction Metadata
|
||||
|
|
||||
|* Internal OBP (No additional messages required)
|
||||
|
|
||||
@ -224,13 +328,65 @@ object Glossary {
|
||||
title = "API.Interfaces",
|
||||
description =
|
||||
s"""
|
||||
|<img width="468" alt="authinfo_annotated_1" src="https://user-images.githubusercontent.com/485218/48845997-413e0780-ed9e-11e8-86c5-e5ce510c140c.png"></img>
|
||||
|<img width="468" alt="OBP Interfaces Image" src="https://user-images.githubusercontent.com/485218/49711990-9ef99d00-fc42-11e8-8cb4-cc68bab74703.png"></img>
|
||||
|
|
||||
|
|
||||
|
|
||||
|"""
|
||||
)
|
||||
|
||||
glossaryItems += GlossaryItem(
|
||||
title = "API.Timeouts",
|
||||
description =
|
||||
s"""
|
||||
|<img width="1403" alt="OBP Timeouts Image" src="https://user-images.githubusercontent.com/29032407/50471858-b52f8900-09b6-11e9-9888-454e6d41907c.png"></img>
|
||||
|
|
||||
|
|
||||
|
|
||||
|"""
|
||||
)
|
||||
|
||||
|
||||
glossaryItems += GlossaryItem(
|
||||
title = "API.Access Control",
|
||||
description =
|
||||
s"""
|
||||
|
|
||||
|Access Control is achieved via the following mechanisms in OBP:
|
||||
|
|
||||
|* APIs are enabled in Props. See the README.md
|
||||
|
|
||||
|* Consumers (Apps) are granted access to Roles and Views via Scopes (WIP)
|
||||
|
|
||||
|See [here](/index#group-Scope) for related endpoints and documentation.
|
||||
|
|
||||
|* Users are granted access to System or Bank Roles via Entitlements.
|
||||
|
|
||||
|See [here](/index#group-Role) for related endpoints and documentation.
|
||||
|
|
||||
|Users may request Entitlement Requests [here](/index#vv3_0_0-addEntitlementRequest)
|
||||
|
|
||||
|Entitlements and Entitlement Requests can be managed in the OBP API Manager.
|
||||
|
|
||||
|* Users are granted access to Customer Accounts, Transactions and Payments via Views.
|
||||
|
|
||||
|See [here](/index#group-View) for related endpoints and documentation.
|
||||
|
|
||||
|User Views can be managed via the OBP Sofit Consent App.
|
||||
|
|
||||
|
|
||||
|<img width="468" alt="OBP Access Control Image" src="https://user-images.githubusercontent.com/485218/49863122-e6795800-fdff-11e8-9b05-bba99e2c72da.png"></img>
|
||||
|
|
||||
|
|
||||
|
|
||||
|"""
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
glossaryItems += GlossaryItem(
|
||||
title =
|
||||
@ -357,7 +513,7 @@ object Glossary {
|
||||
title = "User.provider_id",
|
||||
description =
|
||||
"""
|
||||
|The id of the user given by the authenticaiton provider.
|
||||
|The id of the user given by the authentication provider.
|
||||
""")
|
||||
|
||||
glossaryItems += GlossaryItem(
|
||||
@ -1173,6 +1329,104 @@ object Glossary {
|
||||
""")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
glossaryItems += GlossaryItem(
|
||||
title = "OAuth 2 with Google",
|
||||
description =
|
||||
s"""
|
||||
|
|
||||
|$oauth2EnabledMessage
|
||||
|
|
||||
|## OpenID Connect with Google
|
||||
|
|
||||
|### Introduction
|
||||
|Google's OAuth 2.0 APIs can be used for both authentication and authorization. This document describes our OAuth 2.0 implementation for authentication, which conforms to the OpenID Connect specification, and is OpenID Certified.
|
||||
|For complete documentation please refer to the official doc's page: [OpenID Connect](https://developers.google.com/identity/protocols/OpenIDConnect)
|
||||
|
|
||||
|### Obtain OAuth 2.0 credentials
|
||||
|Please refer to the official doc's page: [OpenID Connect](https://developers.google.com/identity/protocols/OpenIDConnect)
|
||||
|
|
||||
|### An ID token's payload
|
||||
|
|
||||
|
|
||||
| {
|
||||
| "iss": "https://accounts.google.com",
|
||||
| "azp": "407408718192.apps.googleusercontent.com",
|
||||
| "aud": "407408718192.apps.googleusercontent.com",
|
||||
| "sub": "113966854245780892959",
|
||||
| "email": "marko.milic.srbija@gmail.com",
|
||||
| "email_verified": true,
|
||||
| "at_hash": "nGKRToKNnVA28H6MhwXBxw",
|
||||
| "name": "Marko Milić",
|
||||
| "picture": "https://lh5.googleusercontent.com/-Xd44hnJ6TDo/AAAAAAAAAAI/AAAAAAAAAAA/AKxrwcadwzhm4N4tWk5E8Avxi-ZK6ks4qg/s96-c/photo.jpg",
|
||||
| "given_name": "Marko",
|
||||
| "family_name": "Milić",
|
||||
| "locale": "en",
|
||||
| "iat": 1547705691,
|
||||
| "exp": 1547709291
|
||||
| }
|
||||
|
|
||||
|
|
||||
|### Try a REST call using the authorization's header
|
||||
| Using your favorite http client:
|
||||
|
|
||||
| GET /obp/v3.0.0/users/current
|
||||
|
|
||||
|Body
|
||||
|
|
||||
|Leave Empty!
|
||||
|
|
||||
|Headers:
|
||||
|
|
||||
|
|
||||
| Authorization: Bearer ID_TOKEN
|
||||
|
|
||||
|
|
||||
|Here is it all together:
|
||||
|
|
||||
|
|
||||
|
|
||||
| GET /obp/v3.0.0/users/current HTTP/1.1
|
||||
| Host: $getServerUrl
|
||||
| Authorization: Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjA4ZDMyNDVjNjJmODZiNjM2MmFmY2JiZmZlMWQwNjk4MjZkZDFkYzEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM5NjY4NTQyNDU3ODA4OTI5NTkiLCJlbWFpbCI6Im1hcmtvLm1pbGljLnNyYmlqYUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6IkFvYVNGQTlVTTdCSGg3YWZYNGp2TmciLCJuYW1lIjoiTWFya28gTWlsacSHIiwicGljdHVyZSI6Imh0dHBzOi8vbGg1Lmdvb2dsZXVzZXJjb250ZW50LmNvbS8tWGQ0NGhuSjZURG8vQUFBQUFBQUFBQUkvQUFBQUFBQUFBQUEvQUt4cndjYWR3emhtNE40dFdrNUU4QXZ4aS1aSzZrczRxZy9zOTYtYy9waG90by5qcGciLCJnaXZlbl9uYW1lIjoiTWFya28iLCJmYW1pbHlfbmFtZSI6Ik1pbGnEhyIsImxvY2FsZSI6ImVuIiwiaWF0IjoxNTQ3NzExMTE1LCJleHAiOjE1NDc3MTQ3MTV9.MKsyecCSKS4Y0C8R4JP0J0d2Oa-xahvMAbtfFrGHncTm8xBgeaNb50XSJn20ak1YyA8hZiRP2M3el0f4eIVQZsMMa22MrwaiL8pLb1zGfawDLPb1RvOmoCWTDJGc_s1qQMlyc21Wenr9rjuu1bQCerGTYM6M0Aq-Uu_GT0lCEjz5WVDI5xDUf4Mhdi8HYq7UQ1kGz1gQFiBm5nI3_xtYm75EfXFeDg3TejaMmy36NpgtwN_vwpHByoHE5BoTl2J55rJ2creZZ7CmtZttm-9HsT6v1vxT8zi0RXObFrZSk-LgfF0tJQcGZ5LXQZL0yMKXPQVFIMCg8J0Gg7l_QACkCA
|
||||
| Cache-Control: no-cache
|
||||
|
|
||||
|
|
||||
|
|
||||
|CURL example:
|
||||
|
|
||||
|
|
||||
| curl -X GET
|
||||
| $getServerUrl/obp/v3.0.0/users/current
|
||||
| -H 'Authorization: Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjA4ZDMyNDVjNjJmODZiNjM2MmFmY2JiZmZlMWQwNjk4MjZkZDFkYzEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM5NjY4NTQyNDU3ODA4OTI5NTkiLCJlbWFpbCI6Im1hcmtvLm1pbGljLnNyYmlqYUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6IkFvYVNGQTlVTTdCSGg3YWZYNGp2TmciLCJuYW1lIjoiTWFya28gTWlsacSHIiwicGljdHVyZSI6Imh0dHBzOi8vbGg1Lmdvb2dsZXVzZXJjb250ZW50LmNvbS8tWGQ0NGhuSjZURG8vQUFBQUFBQUFBQUkvQUFBQUFBQUFBQUEvQUt4cndjYWR3emhtNE40dFdrNUU4QXZ4aS1aSzZrczRxZy9zOTYtYy9waG90by5qcGciLCJnaXZlbl9uYW1lIjoiTWFya28iLCJmYW1pbHlfbmFtZSI6Ik1pbGnEhyIsImxvY2FsZSI6ImVuIiwiaWF0IjoxNTQ3NzExMTE1LCJleHAiOjE1NDc3MTQ3MTV9.MKsyecCSKS4Y0C8R4JP0J0d2Oa-xahvMAbtfFrGHncTm8xBgeaNb50XSJn20ak1YyA8hZiRP2M3el0f4eIVQZsMMa22MrwaiL8pLb1zGfawDLPb1RvOmoCWTDJGc_s1qQMlyc21Wenr9rjuu1bQCerGTYM6M0Aq-Uu_GT0lCEjz5WVDI5xDUf4Mhdi8HYq7UQ1kGz1gQFiBm5nI3_xtYm75EfXFeDg3TejaMmy36NpgtwN_vwpHByoHE5BoTl2J55rJ2creZZ7CmtZttm-9HsT6v1vxT8zi0RXObFrZSk-LgfF0tJQcGZ5LXQZL0yMKXPQVFIMCg8J0Gg7l_QACkCA'
|
||||
| -H 'Cache-Control: no-cache'
|
||||
| -H 'Postman-Token: aa812d04-eddd-4752-adb7-4d56b3a98f36'
|
||||
|
|
||||
|
|
||||
|
|
||||
|And we get the response:
|
||||
|
|
||||
|
|
||||
| {
|
||||
| "user_id": "6d411bce-50c1-4eb8-b8b0-3953e4211773",
|
||||
| "email": "marko.milic.srbija@gmail.com",
|
||||
| "provider_id": "113966854245780892959",
|
||||
| "provider": "https://accounts.google.com",
|
||||
| "username": "Marko Milić",
|
||||
| "entitlements": {
|
||||
| "list": []
|
||||
| }
|
||||
| }
|
||||
|
|
||||
|
|
||||
|""")
|
||||
|
||||
|
||||
|
||||
|
||||
val gatewayLoginEnabledMessage : String = if (APIUtil.getPropsAsBoolValue("allow_gateway_login", false))
|
||||
{"Note: Gateway Login is enabled."} else {"Note: *Gateway Login is NOT enabled on this instance!*"}
|
||||
|
||||
|
||||
@ -1,17 +1,19 @@
|
||||
package code.api.util
|
||||
|
||||
import java.net.URL
|
||||
import java.text.ParseException
|
||||
|
||||
import com.auth0.jwt.JWT
|
||||
import com.auth0.jwt.interfaces.Claim
|
||||
import com.nimbusds.jose.JWSAlgorithm
|
||||
import com.nimbusds.jose.crypto.{MACVerifier, RSASSAVerifier}
|
||||
import com.nimbusds.jose.jwk.source.{JWKSource, RemoteJWKSet}
|
||||
import com.nimbusds.jose.proc.{JWSVerificationKeySelector, SecurityContext}
|
||||
import com.nimbusds.jwt.{JWTClaimsSet, SignedJWT}
|
||||
import com.nimbusds.jose.util.DefaultResourceRetriever
|
||||
import com.nimbusds.jwt.proc.{BadJWTException, DefaultJWTProcessor}
|
||||
import com.nimbusds.jwt.{JWTClaimsSet, SignedJWT}
|
||||
import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet
|
||||
import net.liftweb.common.{Box, Empty, Failure, Full}
|
||||
import net.liftweb.util.Props
|
||||
import java.text.ParseException
|
||||
|
||||
object JwtUtil {
|
||||
|
||||
@ -64,9 +66,8 @@ object JwtUtil {
|
||||
*
|
||||
* @return True or False
|
||||
*/
|
||||
def verifyHmacSignedJwt(jwtToken: String): Boolean = {
|
||||
def verifyHmacSignedJwt(jwtToken: String, sharedSecret: String): Boolean = {
|
||||
val signedJWT = SignedJWT.parse(jwtToken)
|
||||
val sharedSecret = APIUtil.getPropsValue("oauth2.token_secret", "")
|
||||
val verifier = new MACVerifier(sharedSecret)
|
||||
signedJWT.verify(verifier)
|
||||
}
|
||||
@ -74,7 +75,7 @@ object JwtUtil {
|
||||
/**
|
||||
* Get the value of the "sub" claim, or None if it's not available.
|
||||
*
|
||||
* @return the Subject value or None.
|
||||
* @return the Subject's value or None.
|
||||
*/
|
||||
def getSubject(jwtToken: String): Option[String] = {
|
||||
val jwtDecoded = JWT.decode(jwtToken)
|
||||
@ -83,9 +84,47 @@ object JwtUtil {
|
||||
case value => Some(value)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The Issuer Identifier for the Issuer of the response.
|
||||
* Get the value of the "iss" claim, or None if it's not available.
|
||||
*
|
||||
* @return the Issuer's value or None.
|
||||
*/
|
||||
def getIssuer(jwtToken: String): Option[String] = {
|
||||
val jwtDecoded = JWT.decode(jwtToken)
|
||||
jwtDecoded.getIssuer() match {
|
||||
case null => None
|
||||
case value => Some(value)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The Audience Identifier for the Issuer of the response.
|
||||
* Get the value of the "aud" claim.
|
||||
*
|
||||
* @return the Issuer's value. In case if it's not available the value is empty list.
|
||||
*/
|
||||
def getAudience(jwtToken: String): List[String] = {
|
||||
import scala.collection.JavaConverters._
|
||||
val jwtDecoded = JWT.decode(jwtToken)
|
||||
jwtDecoded.getAudience() match {
|
||||
case null => Nil
|
||||
case value => value.asScala.toList
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function which validating bearer JWT access tokens
|
||||
* This fuction gets an arbitrary claim
|
||||
* @param name The name of the claim we want to get
|
||||
* @param jwtToken JSON Web Token (JWT) as a String value
|
||||
* @return The claim we requested
|
||||
*/
|
||||
def getClaim(name: String, jwtToken: String): Claim = {
|
||||
val jwtDecoded = JWT.decode(jwtToken)
|
||||
jwtDecoded.getClaim(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* This function validates Access Token
|
||||
* @param accessToken The access token to validate, typically submitted with a HTTP header like
|
||||
* Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6InMxIn0.eyJzY3A...
|
||||
* @param remoteJWKSetUrl The URL of OAuth 2.0 server's JWK set, published at a well-known URL
|
||||
@ -124,17 +163,70 @@ object JwtUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function validates ID Token
|
||||
* @param idToken The access token to validate, typically submitted with a HTTP header like
|
||||
* Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6InMxIn0.eyJzY3A...
|
||||
* @param remoteJWKSetUrl The URL of OAuth 2.0 server's JWK set, published at a well-known URL
|
||||
* @return The boxed token claims set or Failure
|
||||
*/
|
||||
def validateIdToken(idToken: String, remoteJWKSetUrl: String): Box[IDTokenClaimsSet] = {
|
||||
import java.net._
|
||||
|
||||
import com.nimbusds.jose._
|
||||
import com.nimbusds.oauth2.sdk.id._
|
||||
import com.nimbusds.openid.connect.sdk.validators._
|
||||
|
||||
val resourceRetriever = new DefaultResourceRetriever(1000, 1000, 50 * 1024)
|
||||
|
||||
// The required parameters
|
||||
val iss: Issuer = new Issuer(getIssuer(idToken).getOrElse(""))
|
||||
val azp = getClaim("azp", idToken).asString()
|
||||
val clientID: ClientID = new ClientID(azp)
|
||||
val jwsAlg: JWSAlgorithm = JWSAlgorithm.RS256
|
||||
//val jwkSetURL: URL = new URL("https://www.googleapis.com/oauth2/v3/certs")
|
||||
val jwkSetURL: URL = new URL(remoteJWKSetUrl)
|
||||
|
||||
// Create validator for signed ID tokens
|
||||
val validator: IDTokenValidator = new IDTokenValidator(iss, clientID, jwsAlg, jwkSetURL, resourceRetriever)
|
||||
|
||||
import com.nimbusds.jose.JOSEException
|
||||
import com.nimbusds.jose.proc.BadJOSEException
|
||||
import com.nimbusds.jwt.{JWT, JWTParser}
|
||||
import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet
|
||||
|
||||
// Parse the ID token// Parse the ID token
|
||||
val idTokenAsJWT: JWT = JWTParser.parse(idToken)
|
||||
|
||||
// Set the expected nonce, leave null if none
|
||||
val expectedNonce = null // new Nonce("xyz...") or null
|
||||
|
||||
try {
|
||||
val claims: IDTokenClaimsSet = validator.validate(idTokenAsJWT, expectedNonce)
|
||||
Full(claims)
|
||||
} catch {
|
||||
case e: BadJOSEException =>
|
||||
// Invalid signature or claims (iss, aud, exp...)
|
||||
Failure(ErrorMessages.Oauth2BadJOSEException + e.getMessage, Full(e), Empty)
|
||||
case e: JOSEException =>
|
||||
// Internal processing exception
|
||||
Failure(ErrorMessages.Oauth2JOSEException + e.getMessage, Full(e), Empty)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
// val jwtToken = "eyJraWQiOiJyc2ExIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJhZG1pbiIsImF6cCI6ImNsaWVudCIsImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo4MDgwXC9vcGVuaWQtY29ubmVjdC1zZXJ2ZXItd2ViYXBwXC8iLCJleHAiOjE1MTk1MDMxODAsImlhdCI6MTUxOTQ5OTU4MCwianRpIjoiMmFmZjNhNGMtZjY5Zi00ZWM1LWE2MzEtYWUzMGYyYzQ4MjZiIn0.NwlK2EJKutaybB4YyEhuwb231ZNkD-BEwhScadcWWn8PFftjVyjqjD5_BwSiWHHa_QaESNPdZugAnF4I2DxtXmpir_x2fB2ch888AzXw6CgTT482I16m1jpL-2iSlQk1D-ZW6fJ2Qemdi3x2V13Xgt9PBvk5CsUukJ8SSqTPbSNNER9Nq2dlS-qQfg61TzhPkuuXDlmCQ3b8QHgUf6UnCfee1jRaohHQoCvJJJubmUI3dY0Df1ynTodTTZm4J1TV6Wp6ZhsPkQVmdBAUsE5kIFqADaE179lldh86-97bVHGU5a4aTYRRKoTPDltt1NvY5XJrjLCgZH8AEW7mOHz9mw"
|
||||
val jwtToken = "eyJraWQiOiJyc2ExIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJtYXJrby5taWxpYyIsImF6cCI6ImNsaWVudCIsImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo4MDgwXC9vcGVuaWQtY29ubmVjdC1zZXJ2ZXItd2ViYXBwXC8iLCJleHAiOjE1MTk3MTc2MDUsImlhdCI6MTUxOTcxNDAwNSwianRpIjoiY2RiNThmNTctZTI2OC00MzZhLWIzMDQtNWE0MWFiYTg0NDFhIn0.XiZKY8A_mXZz6zjCgtXaj0bHI5klmQGnEQcX_b9lBlhfh6IruUwiHuYW0DHXDpKHdKA3Uuqcubj68aT8r5FGyrEGRy4AmzHbzCcwly-MYIElAK4trjSwUJh9VmGwDdr1OFtWC5HrTfsTGfiLQrhNjBGePCy2bGy0pG7pjBNQ3TVOkiAFUVYnCJOiFGLdWcHvEHnPYoYOdvRBLa072qDFbNFiWXqfKcdXdYGXZD5SGMMlA6J6l3NKKiy4t53yE3LjHs5pIclG5OdSV3uB8wGTTACN44CMVUFpWaL6_7_Zlzr-swq_jXYuxHesWGoCWaZKzlbtHsOqpvolgQJlTgdAgA"
|
||||
val jwtToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhhYWQ2NmJkZWZjMWI0M2Q4ZGIyN2U2NWUyZTJlZjMwMTg3OWQzZTgiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM5NjY4NTQyNDU3ODA4OTI5NTkiLCJhdF9oYXNoIjoiWGlpckZ1cnJ2X0ZxN3RHd25rLWt1QSIsIm5hbWUiOiJNYXJrbyBNaWxpxIciLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDUuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy1YZDQ0aG5KNlREby9BQUFBQUFBQUFBSS9BQUFBQUFBQUFBQS9BS3hyd2NhZHd6aG00TjR0V2s1RThBdnhpLVpLNmtzNHFnL3M5Ni1jL3Bob3RvLmpwZyIsImdpdmVuX25hbWUiOiJNYXJrbyIsImZhbWlseV9uYW1lIjoiTWlsacSHIiwibG9jYWxlIjoiZW4iLCJpYXQiOjE1NDczMTE3NjAsImV4cCI6MTU0NzMxNTM2MH0.UyOmM0rsO0-G_ibDH3DFogS94GcsNd9GtYVw7j3vSMjO1rZdIraV-N2HUtQN3yHopwdf35A2FEJaag6X8dbvEkJC7_GAynyLIpodoaHNtaLbww6XQSYuQYyF27aPMpROoGZUYkMpB_82LF3PbD4ecDPC2IA5oSyDF4Eya4yn-MzxYmXS7usVWvanREg8iNQSxpu7zZqj4UwhvSIv7wH0vskr_M-PnefQzNTrdUx74i-v9lVqC4E_bF5jWeDGO8k5dqWqg55QuZdyJdSh89KNiIjJXGZDWUBzGfsbetWRnObIgX264fuOW4SpRglUc8fzv41Sc7SSqjqRAFm05t60kg"
|
||||
println("Header: " + getHeader(jwtToken))
|
||||
println("Payload: " + getPayload(jwtToken))
|
||||
println("Subject: " + getSubject(jwtToken))
|
||||
println("Signature :" + getSignature(jwtToken))
|
||||
println("Verify JWT :" + verifyRsaSignedJwt(jwtToken))
|
||||
println("Signature: " + getSignature(jwtToken))
|
||||
println("Verify JWT: " + verifyRsaSignedJwt(jwtToken))
|
||||
|
||||
val idToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhhYWQ2NmJkZWZjMWI0M2Q4ZGIyN2U2NWUyZTJlZjMwMTg3OWQzZTgiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM5NjY4NTQyNDU3ODA4OTI5NTkiLCJhdF9oYXNoIjoiWVVuME9EYlVsUU4xQ1VUOThSVmE3USIsIm5hbWUiOiJNYXJrbyBNaWxpxIciLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDUuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy1YZDQ0aG5KNlREby9BQUFBQUFBQUFBSS9BQUFBQUFBQUFBQS9BS3hyd2NhZHd6aG00TjR0V2s1RThBdnhpLVpLNmtzNHFnL3M5Ni1jL3Bob3RvLmpwZyIsImdpdmVuX25hbWUiOiJNYXJrbyIsImZhbWlseV9uYW1lIjoiTWlsacSHIiwibG9jYWxlIjoiZW4iLCJpYXQiOjE1NDc0NTQ3MTIsImV4cCI6MTU0NzQ1ODMxMn0.GImeYoPuOgitxpS59XvEd93nxRYKbWl9vHvuMIXYJWFQ5bF_LcnX_PdXRA3w-cBrAZZ3FCAtY0nrE8f7pb6-oQnqpJXYl6PwCe_oZV5rUzMnWUyWauk752_Et-hSxCypAyf7zvW3xcunQUdeKLt_b5dIIs80d8vDpnSlR4SkXx9iduOQ84ktvHMgwIb7ymws6LenstJH864TMvmUNokFgVGOcVeJRKKiGmcoIhIYdh9j1z4J0_gCPs-UsJhJTdmVQgtNQFqMUt8KPEYvFd0gI3Cdvd9gQM5cq9OSUs3D9sI0DLEhBCoEHanBinUrII8B7JE2HkPTEMdM2ZN-2Ecq5A"
|
||||
println("validateIdToken: " + validateIdToken(idToken = idToken, remoteJWKSetUrl = "https://www.googleapis.com/oauth2/v3/certs").map("Logged in user: " + _.getSubject))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package code.api.util
|
||||
|
||||
import code.api.util.APIUtil.getPropsAsBoolValue
|
||||
import code.consumer.Consumers
|
||||
import code.customer.Customer
|
||||
|
||||
object Migration {
|
||||
@ -17,6 +18,10 @@ object Migration {
|
||||
Customer.customerProvider.vend.populateMissingUUIDs()
|
||||
}
|
||||
|
||||
def generateAndPopulateMissingConsumersUUIDs(): Boolean = executeScript {
|
||||
Consumers.consumers.vend.populateMissingUUIDs()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package code.api.util
|
||||
|
||||
import code.accountapplication.AccountApplication
|
||||
import code.api.APIFailureNewStyle
|
||||
import code.api.util.APIUtil.{OBPReturnType, createHttpParamsByUrlFuture, createQueriesByHttpParamsFuture, fullBoxOrException, unboxFull, unboxFullOrFail}
|
||||
import code.api.util.ErrorMessages._
|
||||
@ -13,7 +14,7 @@ import code.api.v3_1_0.OBPAPI3_1_0.Implementations3_1_0
|
||||
import code.atms.Atms
|
||||
import code.atms.Atms.AtmId
|
||||
import code.bankconnectors.vMar2017.InboundAdapterInfoInternal
|
||||
import code.bankconnectors.{Connector, OBPQueryParam, ObpApiLoopback}
|
||||
import code.bankconnectors.{Connector, ObpApiLoopback}
|
||||
import code.branches.Branches
|
||||
import code.branches.Branches.BranchId
|
||||
import code.consumer.Consumers
|
||||
@ -32,9 +33,10 @@ import code.util.Helper
|
||||
import code.views.Views
|
||||
import code.webhook.AccountWebhook
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.common.{Box, Full}
|
||||
import net.liftweb.http.provider.HTTPParam
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
import scala.collection.immutable.List
|
||||
import scala.concurrent.Future
|
||||
@ -43,6 +45,10 @@ object NewStyle {
|
||||
lazy val endpoints: List[(String, String)] = List(
|
||||
(nameOf(Implementations1_4_0.getTransactionRequestTypes), ApiVersion.v1_4_0.toString),
|
||||
(nameOf(Implementations2_0_0.getAllEntitlements), ApiVersion.v2_0_0.toString),
|
||||
(nameOf(Implementations2_0_0.publicAccountsAtOneBank), ApiVersion.v2_0_0.toString),
|
||||
(nameOf(Implementations2_0_0.privateAccountsAtOneBank), ApiVersion.v2_0_0.toString),
|
||||
(nameOf(Implementations2_0_0.corePrivateAccountsAtOneBank), ApiVersion.v2_0_0.toString),
|
||||
(nameOf(Implementations2_0_0.getPrivateAccountsAtOneBank), ApiVersion.v2_0_0.toString),
|
||||
(nameOf(Implementations2_1_0.getRoles), ApiVersion.v3_1_0.toString),
|
||||
(nameOf(Implementations2_2_0.config), ApiVersion.v2_2_0.toString),
|
||||
(nameOf(Implementations2_2_0.getViewsForBankAccount), ApiVersion.v2_2_0.toString),
|
||||
@ -131,7 +137,11 @@ object NewStyle {
|
||||
(nameOf(Implementations3_1_0.createProductAttribute), ApiVersion.v3_1_0.toString),
|
||||
(nameOf(Implementations3_1_0.getProductAttribute), ApiVersion.v3_1_0.toString),
|
||||
(nameOf(Implementations3_1_0.updateProductAttribute), ApiVersion.v3_1_0.toString),
|
||||
(nameOf(Implementations3_1_0.deleteProductAttribute), ApiVersion.v3_1_0.toString)
|
||||
(nameOf(Implementations3_1_0.deleteProductAttribute), ApiVersion.v3_1_0.toString),
|
||||
(nameOf(Implementations3_1_0.createAccountApplication), ApiVersion.v3_1_0.toString),
|
||||
(nameOf(Implementations3_1_0.getAccountApplications), ApiVersion.v3_1_0.toString),
|
||||
(nameOf(Implementations3_1_0.getAccountApplication), ApiVersion.v3_1_0.toString),
|
||||
(nameOf(Implementations3_1_0.updateAccountApplicationStatus), ApiVersion.v3_1_0.toString)
|
||||
)
|
||||
|
||||
object HttpCode {
|
||||
@ -187,10 +197,9 @@ object NewStyle {
|
||||
}
|
||||
|
||||
def checkBankAccountExists(bankId : BankId, accountId : AccountId, callContext: Option[CallContext]) : OBPReturnType[BankAccount] = {
|
||||
Future { Connector.connector.vend.checkBankAccountExists(bankId, accountId, callContext) } map {
|
||||
Connector.connector.vend.checkBankAccountExistsFuture(bankId, accountId, callContext) } map {
|
||||
unboxFullOrFail(_, callContext, s"$BankAccountNotFound Current BankId is $bankId and Current AccountId is $accountId", 400)
|
||||
}
|
||||
}
|
||||
|
||||
def moderatedBankAccount(account: BankAccount, view: View, user: Box[User]) = Future {
|
||||
account.moderatedBankAccount(view, user)
|
||||
@ -291,11 +300,9 @@ object NewStyle {
|
||||
}
|
||||
|
||||
def getAdapterInfo(callContext: Option[CallContext]): OBPReturnType[InboundAdapterInfoInternal] = {
|
||||
Future {
|
||||
Connector.connector.vend.getAdapterInfo(callContext)
|
||||
} map {
|
||||
unboxFullOrFail(_, callContext, ConnectorEmptyResponse, 400)
|
||||
}
|
||||
Connector.connector.vend.getAdapterInfoFuture(callContext) map {
|
||||
unboxFullOrFail(_, callContext, ConnectorEmptyResponse, 400)
|
||||
}
|
||||
}
|
||||
|
||||
def getEntitlementsByUserId(userId: String, callContext: Option[CallContext]): Future[List[Entitlement]] = {
|
||||
@ -305,9 +312,9 @@ object NewStyle {
|
||||
}
|
||||
|
||||
def getCounterparties(bankId : BankId, accountId : AccountId, viewId : ViewId, callContext: Option[CallContext]): OBPReturnType[List[CounterpartyTrait]] = {
|
||||
Future(Connector.connector.vend.getCounterparties(bankId,accountId,viewId, callContext)) map {
|
||||
x => fullBoxOrException(x ~> APIFailureNewStyle(ConnectorEmptyResponse, 400, callContext.map(_.toLight)))
|
||||
} map { unboxFull(_) }
|
||||
Connector.connector.vend.getCounterpartiesFuture(bankId,accountId,viewId, callContext) map { i=>
|
||||
(unboxFullOrFail(i._1, callContext, ConnectorEmptyResponse, 400), i._2)
|
||||
}
|
||||
}
|
||||
|
||||
def getMetadata(bankId : BankId, accountId : AccountId, counterpartyId : String, callContext: Option[CallContext]): Future[CounterpartyMetadata] = {
|
||||
@ -572,7 +579,80 @@ object NewStyle {
|
||||
i => (unboxFullOrFail(i._1, callContext, ConnectorEmptyResponse, 400), i._2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def createAccountApplication(
|
||||
productCode: ProductCode,
|
||||
userId: Option[String],
|
||||
customerId: Option[String],
|
||||
callContext: Option[CallContext]
|
||||
): OBPReturnType[AccountApplication] =
|
||||
Connector.connector.vend.createAccountApplication(productCode, userId, customerId, callContext) map {
|
||||
i => (unboxFullOrFail(i._1, callContext, CreateAccountApplicationError, 400), i._2)
|
||||
}
|
||||
|
||||
|
||||
def getAllAccountApplication(callContext: Option[CallContext]): OBPReturnType[List[AccountApplication]] =
|
||||
Connector.connector.vend.getAllAccountApplication(callContext) map {
|
||||
i => (unboxFullOrFail(i._1, callContext, UnknownError, 400), i._2)
|
||||
}
|
||||
|
||||
def getAccountApplicationById(accountApplicationId: String, callContext: Option[CallContext]): OBPReturnType[AccountApplication] =
|
||||
Connector.connector.vend.getAccountApplicationById(accountApplicationId, callContext) map {
|
||||
i => (unboxFullOrFail(i._1, callContext, s"$AccountApplicationNotFound Current Account-Application-Id($accountApplicationId)", 400), i._2)
|
||||
}
|
||||
|
||||
def updateAccountApplicationStatus(accountApplicationId:String, status: String, callContext: Option[CallContext]): OBPReturnType[AccountApplication] =
|
||||
Connector.connector.vend.updateAccountApplicationStatus(accountApplicationId, status, callContext) map {
|
||||
i => (unboxFullOrFail(i._1, callContext, s"$UpdateAccountApplicationStatusError Current Account-Application-Id($accountApplicationId)", 400), i._2)
|
||||
}
|
||||
|
||||
def findUsers(userIds: List[String], callContext: Option[CallContext]): OBPReturnType[List[User]] = Future {
|
||||
val userList = userIds.filterNot(StringUtils.isBlank).distinct.map(User.findByUserId).collect {
|
||||
case Full(user) => user
|
||||
}
|
||||
(userList, callContext)
|
||||
}
|
||||
|
||||
def createSandboxBankAccount(
|
||||
bankId: BankId,
|
||||
accountId: AccountId,
|
||||
accountType: String,
|
||||
accountLabel: String,
|
||||
currency: String,
|
||||
initialBalance: BigDecimal,
|
||||
accountHolderName: String,
|
||||
branchId: String,
|
||||
accountRoutingScheme: String,
|
||||
accountRoutingAddress: String,
|
||||
callContext: Option[CallContext]
|
||||
): OBPReturnType[BankAccount] = Future {
|
||||
Connector.connector.vend.createSandboxBankAccount(
|
||||
bankId: BankId,
|
||||
accountId: AccountId,
|
||||
accountType: String,
|
||||
accountLabel: String,
|
||||
currency: String,
|
||||
initialBalance: BigDecimal,
|
||||
accountHolderName: String,
|
||||
branchId: String,
|
||||
accountRoutingScheme: String,
|
||||
accountRoutingAddress: String
|
||||
)} map {
|
||||
i => (unboxFullOrFail(i, callContext, UnknownError, 400), callContext)
|
||||
}
|
||||
|
||||
def findCustomers(customerIds: List[String], callContext: Option[CallContext]): OBPReturnType[List[Customer]] = {
|
||||
val customerList = customerIds.filterNot(StringUtils.isBlank).distinct
|
||||
.map(Consumers.consumers.vend.getConsumerByConsumerIdFuture)
|
||||
Future.foldLeft(customerList)(List.empty[Customer]) { (prev, next) =>
|
||||
next match {
|
||||
case Full(customer:Customer) => customer :: prev
|
||||
case _ => prev
|
||||
}
|
||||
} map {
|
||||
(_, callContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
40
src/main/scala/code/api/util/OBPParam.scala
Normal file
40
src/main/scala/code/api/util/OBPParam.scala
Normal file
@ -0,0 +1,40 @@
|
||||
package code.api.util
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import scala.collection.immutable.List
|
||||
|
||||
class OBPQueryParam
|
||||
trait OBPOrder { def orderValue : Int }
|
||||
object OBPOrder {
|
||||
def apply(s: Option[String]): OBPOrder = s match {
|
||||
case Some("asc") => OBPAscending
|
||||
case Some("ASC")=> OBPAscending
|
||||
case _ => OBPDescending
|
||||
}
|
||||
}
|
||||
object OBPAscending extends OBPOrder { def orderValue = 1 }
|
||||
object OBPDescending extends OBPOrder { def orderValue = -1}
|
||||
case class OBPLimit(value: Int) extends OBPQueryParam
|
||||
case class OBPOffset(value: Int) extends OBPQueryParam
|
||||
case class OBPFromDate(value: Date) extends OBPQueryParam
|
||||
case class OBPToDate(value: Date) extends OBPQueryParam
|
||||
case class OBPOrdering(field: Option[String], order: OBPOrder) extends OBPQueryParam
|
||||
case class OBPConsumerId(value: String) extends OBPQueryParam
|
||||
case class OBPUserId(value: String) extends OBPQueryParam
|
||||
case class OBPBankId(value: String) extends OBPQueryParam
|
||||
case class OBPAccountId(value: String) extends OBPQueryParam
|
||||
case class OBPUrl(value: String) extends OBPQueryParam
|
||||
case class OBPAppName(value: String) extends OBPQueryParam
|
||||
case class OBPExcludeAppNames(values: List[String]) extends OBPQueryParam
|
||||
case class OBPImplementedByPartialFunction(value: String) extends OBPQueryParam
|
||||
case class OBPImplementedInVersion(value: String) extends OBPQueryParam
|
||||
case class OBPVerb(value: String) extends OBPQueryParam
|
||||
case class OBPAnon(value: Boolean) extends OBPQueryParam
|
||||
case class OBPCorrelationId(value: String) extends OBPQueryParam
|
||||
case class OBPDuration(value: Long) extends OBPQueryParam
|
||||
case class OBPExcludeUrlPatterns(values: List[String]) extends OBPQueryParam
|
||||
case class OBPExcludeImplementedByPartialFunctions(value: List[String]) extends OBPQueryParam
|
||||
case class OBPFunctionName(value: String) extends OBPQueryParam
|
||||
case class OBPConnectorName(value: String) extends OBPQueryParam
|
||||
case class OBPEmpty() extends OBPQueryParam
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,19 +16,14 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
package code.api.v1_2
|
||||
|
||||
import java.util.Date
|
||||
@ -45,9 +40,10 @@ case class HostedBy(
|
||||
email : String,
|
||||
phone : String
|
||||
)
|
||||
case class ErrorMessage(
|
||||
error : String
|
||||
)
|
||||
|
||||
case class ErrorMessage(code: Int,
|
||||
message: String
|
||||
)
|
||||
case class SuccessMessage(
|
||||
success : String
|
||||
)
|
||||
|
||||
@ -1859,7 +1859,7 @@ trait APIMethods121 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "other_accounts" :: other_account_id :: "metadata" :: "corporate_location" :: Nil JsonPost json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user
|
||||
u <- cc.user ?~ UserNotLoggedIn
|
||||
account <- BankAccount(bankId, accountId) ?~! BankAccountNotFound
|
||||
view <- Views.views.vend.view(viewId, BankIdAccountId(account.bankId, account.accountId))
|
||||
otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, cc.user)
|
||||
@ -1903,7 +1903,7 @@ trait APIMethods121 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "other_accounts":: other_account_id :: "metadata" :: "corporate_location" :: Nil JsonPut json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user
|
||||
u <- cc.user ?~ UserNotLoggedIn
|
||||
account <- BankAccount(bankId, accountId) ?~! BankAccountNotFound
|
||||
view <- Views.views.vend.view(viewId, BankIdAccountId(account.bankId, account.accountId))
|
||||
otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, cc.user)
|
||||
@ -1945,7 +1945,7 @@ trait APIMethods121 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "other_accounts":: other_account_id :: "metadata" :: "corporate_location" :: Nil JsonDelete _ => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user
|
||||
u <- cc.user ?~ UserNotLoggedIn
|
||||
account <- BankAccount(bankId, accountId) ?~! BankAccountNotFound
|
||||
view <- Views.views.vend.view(viewId, BankIdAccountId(account.bankId, account.accountId))
|
||||
otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, cc.user)
|
||||
@ -1989,7 +1989,7 @@ trait APIMethods121 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "other_accounts" :: other_account_id :: "metadata" :: "physical_location" :: Nil JsonPost json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user
|
||||
u <- cc.user ?~ UserNotLoggedIn
|
||||
account <- BankAccount(bankId, accountId) ?~! BankAccountNotFound
|
||||
view <- Views.views.vend.view(viewId, BankIdAccountId(account.bankId, account.accountId))
|
||||
otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, cc.user)
|
||||
@ -2034,7 +2034,7 @@ trait APIMethods121 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "other_accounts":: other_account_id :: "metadata" :: "physical_location" :: Nil JsonPut json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user
|
||||
u <- cc.user ?~ UserNotLoggedIn
|
||||
account <- BankAccount(bankId, accountId) ?~! BankAccountNotFound
|
||||
view <- Views.views.vend.view(viewId, BankIdAccountId(account.bankId, account.accountId))
|
||||
otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, cc.user)
|
||||
@ -2077,7 +2077,7 @@ trait APIMethods121 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "other_accounts":: other_account_id :: "metadata" :: "physical_location" :: Nil JsonDelete _ => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user
|
||||
u <- cc.user ?~ UserNotLoggedIn
|
||||
account <- BankAccount(bankId, accountId) ?~! BankAccountNotFound
|
||||
view <- Views.views.vend.view(viewId, BankIdAccountId(account.bankId, account.accountId))
|
||||
otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, cc.user)
|
||||
@ -2265,7 +2265,7 @@ trait APIMethods121 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: TransactionId(transactionId) :: "metadata" :: "narrative" :: Nil JsonPost json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user
|
||||
u <- cc.user ?~ UserNotLoggedIn
|
||||
narrativeJson <- tryo{json.extract[TransactionNarrativeJSON]} ?~ {InvalidJsonFormat}
|
||||
metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, Full(u), Some(cc))
|
||||
addNarrative <- Box(metadata.addOwnerComment) ?~ { s"$NoViewPermission can_add_owner_comment. Current ViewId($viewId)" }
|
||||
@ -2302,7 +2302,7 @@ trait APIMethods121 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: TransactionId(transactionId) :: "metadata" :: "narrative" :: Nil JsonPut json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user
|
||||
u <- cc.user ?~ UserNotLoggedIn
|
||||
narrativeJson <- tryo{json.extract[TransactionNarrativeJSON]} ?~ {InvalidJsonFormat}
|
||||
metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, Full(u), Some(cc))
|
||||
addNarrative <- Box(metadata.addOwnerComment) ?~ { s"$NoViewPermission can_add_owner_comment. Current ViewId($viewId)" }
|
||||
@ -2412,7 +2412,7 @@ trait APIMethods121 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: TransactionId(transactionId) :: "metadata" :: "comments" :: Nil JsonPost json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user
|
||||
u <- cc.user ?~ UserNotLoggedIn
|
||||
commentJson <- tryo{json.extract[PostTransactionCommentJSON]} ?~ {InvalidJsonFormat}
|
||||
metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, Full(u), Some(cc))
|
||||
addCommentFunc <- Box(metadata.addComment) ?~ { s"$NoViewPermission can_add_comment. Current ViewId($viewId)" }
|
||||
@ -2524,7 +2524,7 @@ trait APIMethods121 {
|
||||
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user
|
||||
u <- cc.user ?~ UserNotLoggedIn
|
||||
tagJson <- tryo{json.extract[PostTransactionTagJSON]} ?~ { s"$InvalidJsonFormat Check your Post Json Body." }
|
||||
metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, Full(u), Some(cc))
|
||||
addTagFunc <- Box(metadata.addTag) ?~ { s"$NoViewPermission can_add_tag. Current ViewId($viewId)" }
|
||||
@ -2635,7 +2635,7 @@ trait APIMethods121 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: TransactionId(transactionId) :: "metadata" :: "images" :: Nil JsonPost json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user
|
||||
u <- cc.user ?~ UserNotLoggedIn
|
||||
imageJson <- tryo{json.extract[PostTransactionImageJSON]} ?~! InvalidJsonFormat
|
||||
metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, Full(u), Some(cc))
|
||||
addImageFunc <- Box(metadata.addImage) ?~ { s"$NoViewPermission can_add_image. Current ViewId($viewId)" }
|
||||
@ -2750,7 +2750,7 @@ trait APIMethods121 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: TransactionId(transactionId) :: "metadata" :: "where" :: Nil JsonPost json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user
|
||||
u <- cc.user ?~ UserNotLoggedIn
|
||||
view <- Views.views.vend.view(viewId, BankIdAccountId(bankId, accountId))
|
||||
metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, cc.user, Some(cc))
|
||||
addWhereTag <- Box(metadata.addWhereTag) ?~ { s"$NoViewPermission can_add_where_tag. Current ViewId($viewId)" }
|
||||
@ -2794,7 +2794,7 @@ trait APIMethods121 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: TransactionId(transactionId) :: "metadata" :: "where" :: Nil JsonPut json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user
|
||||
u <- cc.user ?~ UserNotLoggedIn
|
||||
view <- Views.views.vend.view(viewId, BankIdAccountId(bankId, accountId))
|
||||
metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, cc.user, Some(cc))
|
||||
addWhereTag <- Box(metadata.addWhereTag) ?~ { s"$NoViewPermission can_add_where_tag. Current ViewId($viewId)" }
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,19 +16,14 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
package code.api.v1_2_1
|
||||
|
||||
import java.util.Date
|
||||
@ -51,8 +46,8 @@ case class HostedBy(
|
||||
)
|
||||
case class RateLimiting(enabled: Boolean, technology: String, service_available: Boolean, is_active: Boolean)
|
||||
|
||||
case class ErrorMessage(
|
||||
error : String
|
||||
case class ErrorMessage(code: Int,
|
||||
message : String
|
||||
)
|
||||
case class SuccessMessage(
|
||||
success : String
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,17 +16,12 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
package code.api.v1_2_1
|
||||
|
||||
@ -335,23 +335,25 @@ object JSONFactory1_4_0 {
|
||||
|
||||
// Used to describe the OBP API calls for documentation and API discovery purposes
|
||||
case class ResourceDocJson(operation_id: String,
|
||||
implemented_by: ImplementedByJson,
|
||||
request_verb: String,
|
||||
request_url: String,
|
||||
summary: String,
|
||||
description: String,
|
||||
example_request_body: scala.Product,
|
||||
success_response_body: scala.Product,
|
||||
error_response_bodies: List[String],
|
||||
is_core: Boolean,
|
||||
is_psd2: Boolean,
|
||||
is_obwg: Boolean,
|
||||
tags: List[String],
|
||||
typed_request_body: JValue, //JSON Schema --> https://spacetelescope.github.io/understanding-json-schema/index.html
|
||||
typed_success_response_body: JValue, //JSON Schema --> https://spacetelescope.github.io/understanding-json-schema/index.html
|
||||
roles: Option[List[ApiRole]] = None,
|
||||
is_featured: Boolean,
|
||||
special_instructions: String)
|
||||
implemented_by: ImplementedByJson,
|
||||
request_verb: String,
|
||||
request_url: String,
|
||||
summary: String,
|
||||
description: String,
|
||||
example_request_body: scala.Product,
|
||||
success_response_body: scala.Product,
|
||||
error_response_bodies: List[String],
|
||||
is_core: Boolean,
|
||||
is_psd2: Boolean,
|
||||
is_obwg: Boolean,
|
||||
tags: List[String],
|
||||
typed_request_body: JValue, //JSON Schema --> https://spacetelescope.github.io/understanding-json-schema/index.html
|
||||
typed_success_response_body: JValue, //JSON Schema --> https://spacetelescope.github.io/understanding-json-schema/index.html
|
||||
roles: Option[List[ApiRole]] = None,
|
||||
is_featured: Boolean,
|
||||
special_instructions: String,
|
||||
specified_url: String // Derived value. The Url when called under a certain version.
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -367,7 +369,7 @@ object JSONFactory1_4_0 {
|
||||
|
||||
// We return html rather than markdown to the consumer so they don't have to bother with these questions.
|
||||
// Set the timeout: https://github.com/sirthias/pegdown#parsing-timeouts
|
||||
val PegDownProcessorTimeout: Long = 1000*20
|
||||
val PegDownProcessorTimeout: Long = 1000*20
|
||||
val pegDownProcessor : PegDownProcessor = new PegDownProcessor(PegDownProcessorTimeout)
|
||||
|
||||
ResourceDocJson(
|
||||
@ -389,7 +391,8 @@ object JSONFactory1_4_0 {
|
||||
typed_success_response_body = createTypedBody(rd.successResponseBody),
|
||||
roles = rd.roles,
|
||||
is_featured = rd.isFeatured,
|
||||
special_instructions = pegDownProcessor.markdownToHtml(rd.specialInstructions.getOrElse("").stripMargin)
|
||||
special_instructions = pegDownProcessor.markdownToHtml(rd.specialInstructions.getOrElse("").stripMargin),
|
||||
specified_url = rd.specifiedUrl.getOrElse("")
|
||||
)
|
||||
}
|
||||
|
||||
@ -433,10 +436,10 @@ object JSONFactory1_4_0 {
|
||||
|
||||
//String --> Some field calleds `date`, we will treat the filed as a `date` object.
|
||||
//String --> But for some are not, eg: `date_of_birth` and `future_date` in V300Custom
|
||||
case i: String if(key.contains("date")&& value.toString.length != "20181230".length) => "\"" + key + """": {"type": "string","format": "date-time"}"""
|
||||
case Some(i: String) if(key.contains("date")&& value.toString.length != "20181230".length) => "\"" + key + """": {"type": "string","format": "date-time"}"""
|
||||
case List(i: String, _*) if(key.contains("date")&& value.toString.length != "20181230".length) => "\"" + key + """": {"type": "array","items": {"type": "string","format": "date-time"}}"""
|
||||
case Some(List(i: String, _*)) if(key.contains("date")&& value.toString.length != "20181230".length) => "\"" + key + """": {"type": "array","items": {"type": "string","format": "date-time"}}"""
|
||||
case i: String if(key.contains("date")&& i.length != "20181230".length) => "\"" + key + """": {"type": "string","format": "date-time"}"""
|
||||
case Some(i: String) if(key.contains("date")&& i.length != "20181230".length) => "\"" + key + """": {"type": "string","format": "date-time"}"""
|
||||
case List(i: String, _*) if(key.contains("date")&& i.length != "20181230".length) => "\"" + key + """": {"type": "array","items": {"type": "string","format": "date-time"}}"""
|
||||
case Some(List(i: String, _*)) if(key.contains("date")&& i.length != "20181230".length) => "\"" + key + """": {"type": "array","items": {"type": "string","format": "date-time"}}"""
|
||||
|
||||
//String-->
|
||||
case i: String => "\"" + key + """": {"type":"string"}"""
|
||||
|
||||
@ -8,7 +8,8 @@ import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
|
||||
import code.api.util.APIUtil._
|
||||
import code.api.util.ApiTag._
|
||||
import code.api.util.ErrorMessages.UserNotLoggedIn
|
||||
import code.api.util.{APIUtil, ApiRole, ApiVersion, ErrorMessages}
|
||||
import code.api.util.NewStyle.HttpCode
|
||||
import code.api.util._
|
||||
import code.api.v1_2_1.OBPAPI1_2_1._
|
||||
import code.api.v1_2_1.{AmountOfMoneyJsonV121 => AmountOfMoneyJSON121, JSONFactory => JSONFactory121}
|
||||
import code.api.v1_4_0.JSONFactory1_4_0
|
||||
@ -265,7 +266,7 @@ trait APIMethods200 {
|
||||
basicAccountsJSON,
|
||||
List(BankNotFound, UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagAccount, apiTagPrivateData, apiTagPublicData)
|
||||
List(apiTagAccount, apiTagPrivateData, apiTagPublicData, apiTagNewStyle)
|
||||
)
|
||||
|
||||
//TODO, double check with `lazy val privateAccountsAtOneBank`, they are the same accounts, only different json body.
|
||||
@ -274,12 +275,12 @@ trait APIMethods200 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: Nil JsonGet req => {
|
||||
cc =>
|
||||
for{
|
||||
u <- cc.user ?~! ErrorMessages.UserNotLoggedIn
|
||||
(bank, callContext) <- Bank(bankId, Some(cc)) ?~! BankNotFound
|
||||
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
|
||||
(bank, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
} yield {
|
||||
val privateViewsUserCanAccessAtOneBank = Views.views.vend.privateViewsUserCanAccess(u).filter(_.bankId == bankId)
|
||||
val availablePrivateAccounts = bank.privateAccounts(privateViewsUserCanAccessAtOneBank)
|
||||
successJsonResponse(privateBankAccountBasicListToJson(availablePrivateAccounts, privateViewsUserCanAccessAtOneBank))
|
||||
(privateBankAccountBasicListToJson(availablePrivateAccounts, privateViewsUserCanAccessAtOneBank), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -309,7 +310,7 @@ trait APIMethods200 {
|
||||
coreAccountsJSON,
|
||||
List(UserNotLoggedIn, UnknownError),
|
||||
Catalogs(Core, PSD2, OBWG),
|
||||
List(apiTagAccount, apiTagPrivateData))
|
||||
List(apiTagAccount, apiTagPrivateData, apiTagNewStyle))
|
||||
|
||||
apiRelations += ApiRelation(corePrivateAccountsAtOneBank, createAccount, "new")
|
||||
apiRelations += ApiRelation(corePrivateAccountsAtOneBank, corePrivateAccountsAtOneBank, "self")
|
||||
@ -322,38 +323,40 @@ trait APIMethods200 {
|
||||
case "my" :: "banks" :: BankId(bankId) :: "accounts" :: Nil JsonGet req => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user ?~! ErrorMessages.UserNotLoggedIn
|
||||
(bank, callContext) <- Bank(bankId, Some(cc))
|
||||
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
|
||||
(bank, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
|
||||
} yield {
|
||||
val privateViewsUserCanAccessAtOneBank = Views.views.vend.privateViewsUserCanAccess(u).filter(_.bankId == bankId)
|
||||
val privateAaccountsForOneBank = bank.privateAccounts(privateViewsUserCanAccessAtOneBank)
|
||||
corePrivateAccountsAtOneBankResult(CallerContext(corePrivateAccountsAtOneBank), codeContext, u, privateAaccountsForOneBank, privateViewsUserCanAccessAtOneBank)
|
||||
val result = corePrivateAccountsAtOneBankResult(CallerContext(corePrivateAccountsAtOneBank), codeContext, u, privateAaccountsForOneBank, privateViewsUserCanAccessAtOneBank)
|
||||
(result, HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
// Also we support accounts/private to maintain compatibility with 1.4.0
|
||||
case "my" :: "banks" :: BankId(bankId) :: "accounts" :: "private" :: Nil JsonGet req => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user ?~! ErrorMessages.UserNotLoggedIn
|
||||
(bank, callContext) <- Bank(bankId, Some(cc))
|
||||
|
||||
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
|
||||
(bank, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
} yield {
|
||||
val privateViewsUserCanAccessAtOneBank = Views.views.vend.privateViewsUserCanAccess(u).filter(_.bankId == bankId)
|
||||
val privateAaccountsForOneBank = bank.privateAccounts(privateViewsUserCanAccessAtOneBank)
|
||||
corePrivateAccountsAtOneBankResult(CallerContext(corePrivateAccountsAtOneBank), codeContext, u, privateAaccountsForOneBank, privateViewsUserCanAccessAtOneBank)
|
||||
val result = corePrivateAccountsAtOneBankResult(CallerContext(corePrivateAccountsAtOneBank), codeContext, u, privateAaccountsForOneBank, privateViewsUserCanAccessAtOneBank)
|
||||
(result, HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
// Supports idea of default bank
|
||||
case "bank" :: "accounts" :: Nil JsonGet req => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user ?~! ErrorMessages.UserNotLoggedIn
|
||||
(bank, callContext) <- Bank(BankId(defaultBankId), Some(cc)) ?~! ErrorMessages.DefaultBankIdNotSet
|
||||
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
|
||||
(bank, callContext) <- NewStyle.function.getBank(BankId(defaultBankId), callContext)
|
||||
} yield {
|
||||
val privateViewsUserCanAccessAtOneBank = Views.views.vend.privateViewsUserCanAccess(u).filter(_.bankId == bankId)
|
||||
val privateAaccountsForOneBank = bank.privateAccounts(privateViewsUserCanAccessAtOneBank)
|
||||
corePrivateAccountsAtOneBankResult(CallerContext(corePrivateAccountsAtOneBank), codeContext, u, privateAaccountsForOneBank, privateViewsUserCanAccessAtOneBank)
|
||||
val result = corePrivateAccountsAtOneBankResult(CallerContext(corePrivateAccountsAtOneBank), codeContext, u, privateAaccountsForOneBank, privateViewsUserCanAccessAtOneBank)
|
||||
(result, HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
|
||||
@ -381,7 +384,7 @@ trait APIMethods200 {
|
||||
basicAccountsJSON,
|
||||
List(UserNotLoggedIn, BankNotFound, UnknownError),
|
||||
Catalogs(Core, PSD2, OBWG),
|
||||
List(apiTagAccount)
|
||||
List(apiTagAccount, apiTagNewStyle)
|
||||
)
|
||||
|
||||
lazy val privateAccountsAtOneBank : OBPEndpoint = {
|
||||
@ -389,12 +392,12 @@ trait APIMethods200 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: "private" :: Nil JsonGet req => {
|
||||
cc =>
|
||||
for {
|
||||
u <- cc.user ?~! ErrorMessages.UserNotLoggedIn
|
||||
(bank, callContext) <- Bank(bankId, Some(cc)) ?~! BankNotFound
|
||||
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
|
||||
(bank, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
} yield {
|
||||
val privateViewsUserCanAccessAtOneBank = Views.views.vend.privateViewsUserCanAccess(u).filter(_.bankId == bankId)
|
||||
val availablePrivateAccounts = bank.privateAccounts(privateViewsUserCanAccessAtOneBank)
|
||||
successJsonResponse(privateBankAccountsListToJson(availablePrivateAccounts, privateViewsUserCanAccessAtOneBank))
|
||||
(privateBankAccountsListToJson(availablePrivateAccounts, privateViewsUserCanAccessAtOneBank), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -420,18 +423,18 @@ trait APIMethods200 {
|
||||
basicAccountsJSON,
|
||||
List(UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagAccountPublic, apiTagAccount, apiTagPublicData))
|
||||
List(apiTagAccountPublic, apiTagAccount, apiTagPublicData, apiTagNewStyle))
|
||||
|
||||
lazy val publicAccountsAtOneBank : OBPEndpoint = {
|
||||
//get public accounts for a single bank
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: "public" :: Nil JsonGet req => {
|
||||
cc =>
|
||||
for {
|
||||
(bank, callContext) <- Bank(bankId, Some(cc))
|
||||
publicViewsForBank <- Full(Views.views.vend.publicViewsForBank(bank.bankId))
|
||||
(bank, callContext) <- NewStyle.function.getBank(bankId, Some(cc))
|
||||
} yield {
|
||||
val publicViewsForBank = Views.views.vend.publicViewsForBank(bank.bankId)
|
||||
val publicAccountsJson = publicBankAccountBasicListToJson(bank.publicAccounts(publicViewsForBank), publicViewsForBank)
|
||||
successJsonResponse(publicAccountsJson)
|
||||
(publicAccountsJson, HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -942,7 +945,7 @@ trait APIMethods200 {
|
||||
|More details about the data moderation by the view [here](#1_2_1-getViewsForBankAccount).
|
||||
|
|
||||
|PSD2 Context: PSD2 requires customers to have access to their account information via third party applications.
|
||||
|This call provides balance and other account information via delegated authenticaiton using OAuth.
|
||||
|This call provides balance and other account information via delegated authentication using OAuth.
|
||||
|
|
||||
|${authenticationRequiredMessage(true)} if the 'is_public' field in view (VIEW_ID) is not set to `true`.
|
||||
|
|
||||
@ -1171,7 +1174,7 @@ trait APIMethods200 {
|
||||
)
|
||||
|
||||
lazy val getTransactionTypes : OBPEndpoint = {
|
||||
case "banks" :: BankId(bankId) :: "transaction-types" :: Nil JsonGet _ => {
|
||||
case "banks" :: BankId(bankId) :: "" :: Nil JsonGet _ => {
|
||||
cc => {
|
||||
for {
|
||||
// Get Transaction Types from the active provider
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,19 +16,14 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
package code.api.v2_0_0
|
||||
|
||||
import java.util.Date
|
||||
|
||||
@ -1,34 +1,29 @@
|
||||
/**
|
||||
* Open Bank Project - API
|
||||
* Copyright (C) 2011-2018, TESOBE Ltd
|
||||
**
|
||||
*This program is free software: you can redistribute it and/or modify
|
||||
*it under the terms of the GNU Affero General Public License as published by
|
||||
*the Free Software Foundation, either version 3 of the License, or
|
||||
*(at your option) any later version.
|
||||
**
|
||||
*This program is distributed in the hope that it will be useful,
|
||||
*but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
*GNU Affero General Public License for more details.
|
||||
**
|
||||
*You should have received a copy of the GNU Affero General Public License
|
||||
*along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
**
|
||||
*Email: contact@tesobe.com
|
||||
*TESOBE Ltd
|
||||
*Osloerstrasse 16/17
|
||||
*Berlin 13359, Germany
|
||||
**
|
||||
*This product includes software developed at
|
||||
*TESOBE (http://www.tesobe.com/)
|
||||
* by
|
||||
*Simon Redfern : simon AT tesobe DOT com
|
||||
*Stefan Bethge : stefan AT tesobe DOT com
|
||||
*Everett Sochowski : everett AT tesobe DOT com
|
||||
*Ayoub Benali: ayoub AT tesobe DOT com
|
||||
*
|
||||
*/
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
package code.api.v2_0_0
|
||||
|
||||
import code.api.OBPRestHelper
|
||||
|
||||
@ -1016,7 +1016,7 @@ trait APIMethods210 {
|
||||
)
|
||||
} yield {
|
||||
val cardJson = JSONFactory1_3_0.createPhysicalCardJSON(card, u)
|
||||
successJsonResponse(Extraction.decompose(cardJson))
|
||||
successJsonResponse(Extraction.decompose(cardJson), 201)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1520,7 +1520,7 @@ trait APIMethods210 {
|
||||
success <- Connector.connector.vend.createOrUpdateBranch(branch)
|
||||
} yield {
|
||||
val json = JSONFactory1_4_0.createBranchJson(success)
|
||||
createdJsonResponse(Extraction.decompose(json))
|
||||
createdJsonResponse(Extraction.decompose(json),201)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1560,7 +1560,7 @@ trait APIMethods210 {
|
||||
success <- Connector.connector.vend.createOrUpdateBranch(branch)
|
||||
} yield {
|
||||
val json = JSONFactory1_4_0.createBranchJson(success)
|
||||
createdJsonResponse(Extraction.decompose(json))
|
||||
createdJsonResponse(Extraction.decompose(json), 201)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,19 +16,14 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
package code.api.v2_1_0
|
||||
|
||||
import java.lang
|
||||
@ -101,7 +96,8 @@ case class TransactionRequestBodyCounterpartyJSON(
|
||||
to: CounterpartyIdJson,
|
||||
value: AmountOfMoneyJsonV121,
|
||||
description: String,
|
||||
charge_policy: String
|
||||
charge_policy: String,
|
||||
future_date: Option[String] = None
|
||||
) extends TransactionRequestCommonBodyJSON
|
||||
|
||||
// the data from endpoint, extract as valid JSON
|
||||
@ -109,7 +105,8 @@ case class TransactionRequestBodySEPAJSON(
|
||||
value: AmountOfMoneyJsonV121,
|
||||
to: IbanJson,
|
||||
description: String,
|
||||
charge_policy: String
|
||||
charge_policy: String,
|
||||
future_date: Option[String] = None
|
||||
) extends TransactionRequestCommonBodyJSON
|
||||
|
||||
// Note: FreeForm is not used yet, the format maybe changed latter. the data from endpoint, extract as valid JSON
|
||||
|
||||
@ -1,33 +1,28 @@
|
||||
/**
|
||||
* Open Bank Project - API
|
||||
* Copyright (C) 2011-2018, TESOBE Ltd
|
||||
**
|
||||
*This program is free software: you can redistribute it and/or modify
|
||||
*it under the terms of the GNU Affero General Public License as published by
|
||||
*the Free Software Foundation, either version 3 of the License, or
|
||||
*(at your option) any later version.
|
||||
**
|
||||
*This program is distributed in the hope that it will be useful,
|
||||
*but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
*GNU Affero General Public License for more details.
|
||||
**
|
||||
*You should have received a copy of the GNU Affero General Public License
|
||||
*along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
**
|
||||
*Email: contact@tesobe.com
|
||||
*TESOBE Ltd
|
||||
*Osloerstrasse 16/17
|
||||
*Berlin 13359, Germany
|
||||
**
|
||||
*This product includes software developed at
|
||||
*TESOBE (http://www.tesobe.com/)
|
||||
* by
|
||||
*Simon Redfern : simon AT tesobe DOT com
|
||||
*Stefan Bethge : stefan AT tesobe DOT com
|
||||
*Everett Sochowski : everett AT tesobe DOT com
|
||||
*Ayoub Benali: ayoub AT tesobe DOT com
|
||||
*
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
package code.api.v2_1_0
|
||||
|
||||
|
||||
@ -247,6 +247,9 @@ trait APIMethods220 {
|
||||
cc =>
|
||||
for {
|
||||
(_, callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
|
||||
_ <- Helper.booleanToFuture(failMsg = ConsumerHasMissingRoles + CanReadFx) {
|
||||
checkScope(bankId.value, getConsumerPrimaryKey(callContext), ApiRole.canReadFx)
|
||||
}
|
||||
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
_ <- NewStyle.function.tryons(failMsg = InvalidISOCurrencyCode,400, callContext) {
|
||||
assert(isValidCurrencyISOCode(fromCurrencyCode))
|
||||
@ -374,6 +377,8 @@ trait APIMethods220 {
|
||||
"Get Message Docs",
|
||||
"""These message docs provide example messages sent by OBP to the (Kafka) message queue for processing by the Core Banking / Payment system Adapter - together with an example expected response and possible error codes.
|
||||
| Integrators can use these messages to build Adapters that provide core banking services to OBP.
|
||||
|
|
||||
| Note: API Explorer provides a Message Docs page where these messages are displayed.
|
||||
|
|
||||
| `CONNECTOR`:kafka_vMar2017 , kafka_vJune2017, kafka_vSept2018 ...
|
||||
""".stripMargin,
|
||||
@ -721,7 +726,7 @@ trait APIMethods220 {
|
||||
InvalidAccountInitialBalance,
|
||||
InitialBalanceMustBeZero,
|
||||
InvalidAccountBalanceCurrency,
|
||||
AccountIdAlreadyExsits,
|
||||
AccountIdAlreadyExists,
|
||||
UnknownError
|
||||
),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
@ -751,7 +756,7 @@ trait APIMethods220 {
|
||||
initialBalanceAsNumber <- tryo {BigDecimal(initialBalanceAsString)} ?~! InvalidAccountInitialBalance
|
||||
_ <- booleanToBox(0 == initialBalanceAsNumber) ?~! InitialBalanceMustBeZero
|
||||
currency <- tryo (jsonBody.balance.currency) ?~!ErrorMessages.InvalidAccountBalanceCurrency
|
||||
_ <- booleanToBox(BankAccount(bankId, accountId).isEmpty, AccountIdAlreadyExsits)
|
||||
_ <- booleanToBox(BankAccount(bankId, accountId).isEmpty, AccountIdAlreadyExists)
|
||||
bankAccount <- Connector.connector.vend.createSandboxBankAccount(
|
||||
bankId,
|
||||
accountId,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,19 +16,14 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
package code.api.v2_2_0
|
||||
|
||||
//import code.api.v1_2_1.JSONFactory
|
||||
|
||||
@ -1,34 +1,4 @@
|
||||
/**
|
||||
* Open Bank Project - API
|
||||
* Copyright (C) 2011-2018, TESOBE Ltd
|
||||
**
|
||||
*This program is free software: you can redistribute it and/or modify
|
||||
*it under the terms of the GNU Affero General Public License as published by
|
||||
*the Free Software Foundation, either version 3 of the License, or
|
||||
*(at your option) any later version.
|
||||
**
|
||||
*This program is distributed in the hope that it will be useful,
|
||||
*but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
*GNU Affero General Public License for more details.
|
||||
**
|
||||
*You should have received a copy of the GNU Affero General Public License
|
||||
*along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
**
|
||||
*Email: contact@tesobe.com
|
||||
*TESOBE Ltd
|
||||
*Osloerstrasse 16/17
|
||||
*Berlin 13359, Germany
|
||||
**
|
||||
*This product includes software developed at
|
||||
*TESOBE (http://www.tesobe.com/)
|
||||
* by
|
||||
*Simon Redfern : simon AT tesobe DOT com
|
||||
*Stefan Bethge : stefan AT tesobe DOT com
|
||||
*Everett Sochowski : everett AT tesobe DOT com
|
||||
*Ayoub Benali: ayoub AT tesobe DOT com
|
||||
*
|
||||
*/
|
||||
|
||||
package code.api.v2_2_0
|
||||
|
||||
import code.api.OBPRestHelper
|
||||
|
||||
@ -29,14 +29,14 @@ import code.util.Helper
|
||||
import code.util.Helper.booleanToBox
|
||||
import code.views.Views
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import net.liftweb.common.{Box, Empty, Full}
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.http.S
|
||||
import net.liftweb.http.js.JE.JsRaw
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
import net.liftweb.json.{Extraction, compactRender}
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
|
||||
import scala.collection.immutable.Nil
|
||||
import scala.collection.immutable.{List, Nil}
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
@ -296,7 +296,7 @@ trait APIMethods300 {
|
||||
|More details about the data moderation by the view [here](#1_2_1-getViewsForBankAccount).
|
||||
|
|
||||
|PSD2 Context: PSD2 requires customers to have access to their account information via third party applications.
|
||||
|This call provides balance and other account information via delegated authenticaiton using OAuth.
|
||||
|This call provides balance and other account information via delegated authentication using OAuth.
|
||||
|
|
||||
|Authentication is required if the 'is_public' field in view (VIEW_ID) is not set to `true`.
|
||||
|""".stripMargin,
|
||||
@ -342,7 +342,7 @@ trait APIMethods300 {
|
||||
|
|
||||
|
|
||||
|PSD2 Context: PSD2 requires customers to have access to their account information via third party applications.
|
||||
|This call provides balance and other account information via delegated authenticaiton using OAuth.
|
||||
|This call provides balance and other account information via delegated authentication using OAuth.
|
||||
|
|
||||
|${authenticationRequiredMessage(false)}
|
||||
|
|
||||
@ -660,21 +660,20 @@ trait APIMethods300 {
|
||||
lazy val getTransactionsForBankAccount: OBPEndpoint = {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: Nil JsonGet req => {
|
||||
cc =>
|
||||
val res =
|
||||
for {
|
||||
(user, callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
|
||||
(bankAccount, callContext) <- NewStyle.function.getBankAccount(bankId, accountId, callContext)
|
||||
view <- NewStyle.function.view(viewId, BankIdAccountId(bankAccount.bankId, bankAccount.accountId), callContext)
|
||||
} yield {
|
||||
for {
|
||||
//Note: error handling and messages for getTransactionParams are in the sub method
|
||||
params <- createQueriesByHttpParams(callContext.get.requestHeaders)
|
||||
(transactions, callContext) <- bankAccount.getModeratedTransactions(user, view, callContext, params: _*)
|
||||
} yield {
|
||||
(createTransactionsJson(transactions), HttpCode.`200`(callContext))
|
||||
}
|
||||
for {
|
||||
(user, callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
|
||||
(bankAccount, callContext) <- NewStyle.function.getBankAccount(bankId, accountId, callContext)
|
||||
view <- NewStyle.function.view(viewId, BankIdAccountId(bankAccount.bankId, bankAccount.accountId), callContext)
|
||||
params <- createQueriesByHttpParamsFuture(callContext.get.requestHeaders)map {
|
||||
unboxFullOrFail(_, callContext, InvalidFilterParameterFormat, 400)
|
||||
}
|
||||
res map { fullBoxOrException(_) } map { unboxFull(_) }
|
||||
//Note: error handling and messages for getTransactionParams are in the sub method
|
||||
(transactions, callContext) <- bankAccount.getModeratedTransactionsFuture(user, view, callContext, params: _*) map {
|
||||
unboxFullOrFail(_, callContext, ConnectorEmptyResponse, 400)
|
||||
}
|
||||
} yield {
|
||||
(createTransactionsJson(transactions), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -740,7 +739,7 @@ trait APIMethods300 {
|
||||
} map { unboxFull(_) }
|
||||
result: esw.APIResponse <- esw.searchProxyAsyncV300(u.userId, indexPart, bodyPart)
|
||||
} yield {
|
||||
(esw.parseResponse(result), HttpCode.`200`(callContext))
|
||||
(esw.parseResponse(result), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -809,7 +808,7 @@ trait APIMethods300 {
|
||||
} map { unboxFull(_) }
|
||||
result <- esw.searchProxyStatsAsyncV300(u.userId, indexPart, bodyPart, field)
|
||||
} yield {
|
||||
(esw.parseResponse(result), HttpCode.`200`(callContext))
|
||||
(esw.parseResponse(result), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1011,7 +1010,7 @@ trait APIMethods300 {
|
||||
success: Branches.BranchT <- Connector.connector.vend.createOrUpdateBranch(branch) ?~! {ErrorMessages.CountNotSaveOrUpdateResource + " Branch"}
|
||||
} yield {
|
||||
val json = JSONFactory300.createBranchJsonV300(success)
|
||||
createdJsonResponse(Extraction.decompose(json))
|
||||
createdJsonResponse(Extraction.decompose(json), 201)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1066,7 +1065,7 @@ trait APIMethods300 {
|
||||
success <- Connector.connector.vend.createOrUpdateAtm(atm)
|
||||
} yield {
|
||||
val json = JSONFactory300.createAtmJsonV300(success)
|
||||
createdJsonResponse(Extraction.decompose(json))
|
||||
createdJsonResponse(Extraction.decompose(json), 201)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1189,6 +1188,8 @@ trait APIMethods300 {
|
||||
val branchesWithLicense = for { branch <- list if branch.meta.license.name.size > 3 } yield branch
|
||||
if (branchesWithLicense.size == 0) fullBoxOrException(Empty ?~! branchesNotFoundLicense)
|
||||
else Full(branchesWithLicense)
|
||||
case Failure(msg, t, c) => Failure(msg, t, c)
|
||||
case ParamFailure(x,y,z,q) => ParamFailure(x,y,z,q)
|
||||
} map { unboxFull(_) } map {
|
||||
branches =>
|
||||
// Before we slice we need to sort in order to keep consistent results
|
||||
@ -1310,6 +1311,8 @@ trait APIMethods300 {
|
||||
val branchesWithLicense = for { branch <- list if branch.meta.license.name.size > 3 } yield branch
|
||||
if (branchesWithLicense.size == 0) fullBoxOrException(Empty ?~! atmsNotFoundLicense)
|
||||
else Full(branchesWithLicense)
|
||||
case Failure(msg, t, c) => Failure(msg, t, c)
|
||||
case ParamFailure(x,y,z,q) => ParamFailure(x,y,z,q)
|
||||
} map { unboxFull(_) } map {
|
||||
branch =>
|
||||
// Before we slice we need to sort in order to keep consistent results
|
||||
@ -1612,7 +1615,7 @@ trait APIMethods300 {
|
||||
nameOf(addEntitlementRequest),
|
||||
"POST",
|
||||
"/entitlement-requests",
|
||||
"Add Entitlement Request for a Logged User.",
|
||||
"Add Entitlement Request for current User.",
|
||||
s"""Create Entitlement Request.
|
||||
|
|
||||
|Any logged in User can use this endpoint to request an Entitlement
|
||||
@ -1668,7 +1671,7 @@ trait APIMethods300 {
|
||||
x => fullBoxOrException(x ~> APIFailureNewStyle(EntitlementRequestCannotBeAdded, 400, callContext.map(_.toLight)))
|
||||
} map { unboxFull(_) }
|
||||
} yield {
|
||||
(JSONFactory300.createEntitlementRequestJSON(addedEntitlementRequest), HttpCode.`200`(callContext))
|
||||
(JSONFactory300.createEntitlementRequestJSON(addedEntitlementRequest), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2056,7 +2059,7 @@ trait APIMethods300 {
|
||||
UnknownError
|
||||
),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagRole, apiTagEntitlement, apiTagUser, apiTagNewStyle),
|
||||
List(apiTagScope, apiTagRole, apiTagNewStyle),
|
||||
Some(List(canCreateScopeAtOneBank, canCreateScopeAtAnyBank)))
|
||||
|
||||
lazy val addScope : OBPEndpoint = {
|
||||
@ -2105,7 +2108,7 @@ trait APIMethods300 {
|
||||
addedEntitlement <- Future {Scope.scope.vend.addScope(postedData.bank_id, consumerId, postedData.role_name)} map { unboxFull(_) }
|
||||
|
||||
} yield {
|
||||
(JSONFactory300.createScopeJson(addedEntitlement), HttpCode.`200`(callContext))
|
||||
(JSONFactory300.createScopeJson(addedEntitlement), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2128,7 +2131,7 @@ trait APIMethods300 {
|
||||
emptyObjectJson,
|
||||
List(UserNotLoggedIn, UserNotSuperAdmin, EntitlementNotFound, UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagRole, apiTagUser, apiTagEntitlement, apiTagNewStyle))
|
||||
List(apiTagScope, apiTagRole, apiTagEntitlement, apiTagNewStyle))
|
||||
|
||||
lazy val deleteScope: OBPEndpoint = {
|
||||
case "consumers" :: consumerId :: "scope" :: scopeId :: Nil JsonDelete _ => {
|
||||
@ -2170,7 +2173,7 @@ trait APIMethods300 {
|
||||
scopeJsons,
|
||||
List(UserNotLoggedIn, UserNotSuperAdmin, EntitlementNotFound, UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagRole, apiTagUser, apiTagEntitlement, apiTagNewStyle))
|
||||
List(apiTagScope, apiTagRole, apiTagEntitlement, apiTagNewStyle))
|
||||
|
||||
lazy val getScopes: OBPEndpoint = {
|
||||
case "consumers" :: consumerId :: "scopes" :: Nil JsonGet _ => {
|
||||
|
||||
@ -1,33 +1,28 @@
|
||||
/**
|
||||
* Open Bank Project - API
|
||||
* Copyright (C) 2011-2018, TESOBE Ltd
|
||||
**
|
||||
*This program is free software: you can redistribute it and/or modify
|
||||
*it under the terms of the GNU Affero General Public License as published by
|
||||
*the Free Software Foundation, either version 3 of the License, or
|
||||
*(at your option) any later version.
|
||||
**
|
||||
*This program is distributed in the hope that it will be useful,
|
||||
*but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
*GNU Affero General Public License for more details.
|
||||
**
|
||||
*You should have received a copy of the GNU Affero General Public License
|
||||
*along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
**
|
||||
*Email: contact@tesobe.com
|
||||
*TESOBE Ltd
|
||||
*Osloerstrasse 16/17
|
||||
*Berlin 13359, Germany
|
||||
**
|
||||
*This product includes software developed at
|
||||
*TESOBE (http://www.tesobe.com/)
|
||||
* by
|
||||
*Simon Redfern : simon AT tesobe DOT com
|
||||
*Stefan Bethge : stefan AT tesobe DOT com
|
||||
*Everett Sochowski : everett AT tesobe DOT com
|
||||
*Ayoub Benali: ayoub AT tesobe DOT com
|
||||
*
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
package code.api.v3_0_0
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package code.api.v3_1_0
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
|
||||
import code.api.util.APIUtil._
|
||||
import code.api.util.ApiRole._
|
||||
@ -12,14 +14,14 @@ import code.api.v2_1_0.JSONFactory210
|
||||
import code.api.v3_0_0.JSONFactory300
|
||||
import code.api.v3_0_0.JSONFactory300.createAdapterInfoJson
|
||||
import code.api.v3_1_0.JSONFactory310._
|
||||
import code.bankconnectors.{Connector, OBPBankId}
|
||||
import code.bankconnectors.Connector
|
||||
import code.consumer.Consumers
|
||||
import code.customer.{CreditLimit, CreditRating, CustomerFaceImage}
|
||||
import code.entitlement.Entitlement
|
||||
import code.loginattempts.LoginAttempt
|
||||
import code.metrics.APIMetrics
|
||||
import code.model._
|
||||
import code.model.dataAccess.AuthUser
|
||||
import code.model.dataAccess.{AuthUser, BankAccountCreation}
|
||||
import code.productattribute.ProductAttribute.ProductAttributeType
|
||||
import code.products.Products.ProductCode
|
||||
import code.users.Users
|
||||
@ -32,6 +34,7 @@ import net.liftweb.http.provider.HTTPParam
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
import net.liftweb.util.Helpers
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
import org.apache.commons.lang3.Validate
|
||||
|
||||
import scala.collection.immutable.{List, Nil}
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
@ -41,7 +44,9 @@ import scala.concurrent.Future
|
||||
trait APIMethods310 {
|
||||
self: RestHelper =>
|
||||
|
||||
val Implementations3_1_0 = new Object() {
|
||||
val Implementations3_1_0 = new Implementations310()
|
||||
|
||||
class Implementations310 {
|
||||
|
||||
val implementedInApiVersion: ApiVersion = ApiVersion.v3_1_0
|
||||
|
||||
@ -155,7 +160,7 @@ trait APIMethods310 {
|
||||
// banksBox <- Connector.connector.vend.getBanksFuture()
|
||||
// banks <- unboxFullAndWrapIntoFuture{ banksBox }
|
||||
// } yield
|
||||
Future{ (JSONFactory310.createCreditLimitOrderResponseJson(), HttpCode.`200`(cc))}
|
||||
Future{ (JSONFactory310.createCreditLimitOrderResponseJson(), HttpCode.`201`(Some(cc)))}
|
||||
}
|
||||
}
|
||||
|
||||
@ -494,7 +499,10 @@ trait APIMethods310 {
|
||||
"/users/USERNAME/lock-status",
|
||||
"Unlock the user",
|
||||
s"""
|
||||
|Get Customers that has a firehose View.
|
||||
|Unlock a User.
|
||||
|
|
||||
|(Perhaps the user was locked due to multiple failed login attempts)
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|
|
||||
|""".stripMargin,
|
||||
@ -821,13 +829,13 @@ trait APIMethods310 {
|
||||
}
|
||||
|
||||
|
||||
val accountWebHookInfo = """Webhooks are used to call external URLs when certain events happen.
|
||||
val accountWebHookInfo = s"""Webhooks are used to call external URLs when certain events happen.
|
||||
|
|
||||
|Account Webhooks focus on events around accounts.
|
||||
|
|
||||
|For instance, a webhook could be used to notify an external serivce if a balance changes on an account.
|
||||
|For instance, a webhook could be used to notify an external service if a balance changes on an account.
|
||||
|
|
||||
|This functionality is work in progress! Although you can create and modify Webhooks, they do not yet fire on triggers."""
|
||||
|This functionality is work in progress! Please note that only implemented trigger is: ${ApiTrigger.onBalanceChange}"""
|
||||
|
||||
|
||||
resourceDocs += ResourceDoc(
|
||||
@ -875,12 +883,13 @@ trait APIMethods310 {
|
||||
triggerName = postJson.trigger_name,
|
||||
url = postJson.url,
|
||||
httpMethod = postJson.http_method,
|
||||
httpProtocol = postJson.http_protocol,
|
||||
isActive = isActive
|
||||
) map {
|
||||
unboxFullOrFail(_, callContext, CreateWebhookError, 400)
|
||||
}
|
||||
} yield {
|
||||
(createAccountWebhookJson(wh), HttpCode.`200`(callContext))
|
||||
(createAccountWebhookJson(wh), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -892,8 +901,8 @@ trait APIMethods310 {
|
||||
nameOf(enableDisableAccountWebhook),
|
||||
"PUT",
|
||||
"/banks/BANK_ID/account-web-hooks",
|
||||
"Update an Account Webhook",
|
||||
s"""Update an Account Webhook
|
||||
"Enable/Disable an Account Webhook",
|
||||
s"""Enable/Disable an Account Webhook
|
||||
|
|
||||
|
|
||||
|$accountWebHookInfo
|
||||
@ -903,7 +912,7 @@ trait APIMethods310 {
|
||||
List(UnknownError),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
apiTagWebhook :: apiTagBank :: apiTagNewStyle :: Nil,
|
||||
Some(List(canCreateWebhook))
|
||||
Some(List(canUpdateWebhook))
|
||||
)
|
||||
|
||||
lazy val enableDisableAccountWebhook : OBPEndpoint = {
|
||||
@ -1080,7 +1089,7 @@ trait APIMethods310 {
|
||||
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
(account, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext)
|
||||
view <- NewStyle.function.view(viewId, BankIdAccountId(account.bankId, account.accountId), callContext)
|
||||
(moderatedTransaction, callContext) <- Future(account.moderatedTransaction(transactionId, view, user, callContext)) map {
|
||||
(moderatedTransaction, callContext) <- account.moderatedTransactionFuture(bankId, accountId, transactionId, view, user, callContext) map {
|
||||
unboxFullOrFail(_, callContext, GetTransactionsException)
|
||||
}
|
||||
} yield {
|
||||
@ -1226,7 +1235,7 @@ trait APIMethods310 {
|
||||
unboxFullOrFail(_, callContext, CreateCustomerError, 400)
|
||||
}
|
||||
} yield {
|
||||
(JSONFactory310.createCustomerJson(customer), HttpCode.`200`(callContext))
|
||||
(JSONFactory310.createCustomerJson(customer), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1347,7 +1356,7 @@ trait APIMethods310 {
|
||||
}
|
||||
(customer, callContext) <- NewStyle.function.getCustomerByCustomerNumber(postedData.customer_number, bank.bankId, callContext)
|
||||
} yield {
|
||||
(JSONFactory310.createCustomerJson(customer), HttpCode.`200`(callContext))
|
||||
(JSONFactory310.createCustomerJson(customer), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1386,7 +1395,7 @@ trait APIMethods310 {
|
||||
(_, callContext) <- NewStyle.function.findByUserId(userId, callContext)
|
||||
(userAuthContext, callContext) <- NewStyle.function.createUserAuthContext(userId, postedData.key, postedData.value, callContext)
|
||||
} yield {
|
||||
(JSONFactory310.createUserAuthContextJson(userAuthContext), HttpCode.`200`(callContext))
|
||||
(JSONFactory310.createUserAuthContextJson(userAuthContext), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1543,7 +1552,7 @@ trait APIMethods310 {
|
||||
(_, callContext) <- NewStyle.function.getCustomerByCustomerId(customerId, callContext)
|
||||
(taxResidence, callContext) <- NewStyle.function.createTaxResidence(customerId, postedData.domain, postedData.tax_number, callContext)
|
||||
} yield {
|
||||
(JSONFactory310.createTaxResidence(List(taxResidence)), HttpCode.`200`(callContext))
|
||||
(JSONFactory310.createTaxResidence(List(taxResidence)), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1719,7 +1728,7 @@ trait APIMethods310 {
|
||||
postedData.state,
|
||||
callContext)
|
||||
} yield {
|
||||
(JSONFactory310.createAddress(address), HttpCode.`200`(callContext))
|
||||
(JSONFactory310.createAddress(address), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1868,7 +1877,7 @@ trait APIMethods310 {
|
||||
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
|
||||
_ <- NewStyle.function.hasEntitlement(failMsg = UserHasMissingRoles + CanRefreshUser)("", userId, canRefreshUser)
|
||||
startTime <- Future{Helpers.now}
|
||||
_ <- NewStyle.function.findByUserId(userId, Some(cc))
|
||||
_ <- NewStyle.function.findByUserId(userId, callContext)
|
||||
_ <- if (APIUtil.isSandboxMode) Future{} else Future{ tryo {AuthUser.updateUserAccountViews(u, callContext)}} map {
|
||||
unboxFullOrFail(_, callContext, RefreshUserError, 400)
|
||||
}
|
||||
@ -2062,6 +2071,238 @@ trait APIMethods310 {
|
||||
}
|
||||
}
|
||||
|
||||
resourceDocs += ResourceDoc(
|
||||
createAccountApplication,
|
||||
implementedInApiVersion,
|
||||
nameOf(createAccountApplication),
|
||||
"POST",
|
||||
"/banks/BANK_ID/account-applications",
|
||||
"Create Account Application",
|
||||
s""" Create Account Application
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|
|
||||
|""",
|
||||
accountApplicationJson,
|
||||
accountApplicationResponseJson,
|
||||
List(
|
||||
InvalidJsonFormat,
|
||||
UnknownError
|
||||
),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagAccountApplication, apiTagAccount, apiTagNewStyle))
|
||||
|
||||
lazy val createAccountApplication : OBPEndpoint = {
|
||||
case "banks" :: BankId(bankId) :: "account-applications" :: Nil JsonPost json -> _=> {
|
||||
cc =>
|
||||
for {
|
||||
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
|
||||
|
||||
failMsg = s"$InvalidJsonFormat The Json body should be the $AccountApplicationJson "
|
||||
postedData <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
json.extract[AccountApplicationJson]
|
||||
}
|
||||
|
||||
illegalProductCodeMsg = s"$InvalidJsonFormat product_code should not be empty."
|
||||
_ <- NewStyle.function.tryons(illegalProductCodeMsg, 400, callContext) {
|
||||
Validate.notBlank(postedData.product_code)
|
||||
}
|
||||
|
||||
illegalUserIdOrCustomerIdMsg = s"$InvalidJsonFormat User_id and customer_id should not both are empty."
|
||||
_ <- NewStyle.function.tryons(illegalUserIdOrCustomerIdMsg, 400, callContext) {
|
||||
Validate.isTrue(postedData.user_id.isDefined || postedData.customer_id.isDefined)
|
||||
}
|
||||
|
||||
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
|
||||
user <- unboxOptionOBPReturnType(postedData.user_id.map(NewStyle.function.findByUserId(_, callContext)))
|
||||
|
||||
customer <- unboxOptionOBPReturnType(postedData.customer_id.map(NewStyle.function.getCustomerByCustomerId(_, callContext)))
|
||||
|
||||
(productAttribute, callContext) <- NewStyle.function.createAccountApplication(
|
||||
productCode = ProductCode(postedData.product_code),
|
||||
userId = postedData.user_id,
|
||||
customerId = postedData.customer_id,
|
||||
callContext = callContext
|
||||
)
|
||||
} yield {
|
||||
(createAccountApplicationJson(productAttribute, user, customer), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resourceDocs += ResourceDoc(
|
||||
getAccountApplications,
|
||||
implementedInApiVersion,
|
||||
nameOf(getAccountApplications),
|
||||
"GET",
|
||||
"/banks/BANK_ID/account-applications",
|
||||
"Get Account Applications",
|
||||
s"""Get the Account Applications.
|
||||
|
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|
|
||||
|""",
|
||||
emptyObjectJson,
|
||||
accountApplicationsJsonV310,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
UnknownError
|
||||
),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagAccountApplication, apiTagAccount, apiTagNewStyle))
|
||||
|
||||
lazy val getAccountApplications : OBPEndpoint = {
|
||||
case "banks" :: BankId(bankId) ::"account-applications" :: Nil JsonGet _ => {
|
||||
cc =>
|
||||
for {
|
||||
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
|
||||
|
||||
_ <- NewStyle.function.hasEntitlement(failMsg = UserHasMissingRoles + CanGetAccountApplications)("", u.userId, canGetAccountApplications)
|
||||
|
||||
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
|
||||
(accountApplications, _) <- NewStyle.function.getAllAccountApplication(callContext)
|
||||
(users, _) <- NewStyle.function.findUsers(accountApplications.map(_.userId), callContext)
|
||||
(customers, _) <- NewStyle.function.findCustomers(accountApplications.map(_.customerId), callContext)
|
||||
} yield {
|
||||
(JSONFactory310.createAccountApplications(accountApplications, users, customers), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
resourceDocs += ResourceDoc(
|
||||
getAccountApplication,
|
||||
implementedInApiVersion,
|
||||
nameOf(getAccountApplication),
|
||||
"GET",
|
||||
"/banks/BANK_ID/account-applications/ACCOUNT_APPLICATION_ID",
|
||||
"Get Account Application by Id",
|
||||
s"""Get the Account Application.
|
||||
|
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|
|
||||
|""",
|
||||
emptyObjectJson,
|
||||
accountApplicationResponseJson,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
UnknownError
|
||||
),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagAccountApplication, apiTagAccount, apiTagNewStyle))
|
||||
|
||||
lazy val getAccountApplication : OBPEndpoint = {
|
||||
case "banks" :: BankId(bankId) ::"account-applications":: accountApplicationId :: Nil JsonGet _ => {
|
||||
cc =>
|
||||
for {
|
||||
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
|
||||
|
||||
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
|
||||
(accountApplication, _) <- NewStyle.function.getAccountApplicationById(accountApplicationId, callContext)
|
||||
|
||||
userId = Option(accountApplication.userId)
|
||||
customerId = Option(accountApplication.customerId)
|
||||
|
||||
user <- unboxOptionOBPReturnType(userId.map(NewStyle.function.findByUserId(_, callContext)))
|
||||
customer <- unboxOptionOBPReturnType(customerId.map(NewStyle.function.getCustomerByCustomerId(_, callContext)))
|
||||
|
||||
} yield {
|
||||
(createAccountApplicationJson(accountApplication, user, customer), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
resourceDocs += ResourceDoc(
|
||||
updateAccountApplicationStatus,
|
||||
implementedInApiVersion,
|
||||
nameOf(updateAccountApplicationStatus),
|
||||
"PUT",
|
||||
"/banks/BANK_ID/account-applications/ACCOUNT_APPLICATION_ID",
|
||||
"Update Account Application Status",
|
||||
s"""Update an Account Application status
|
||||
|
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|
|
||||
|""",
|
||||
accountApplicationUpdateStatusJson,
|
||||
accountApplicationResponseJson,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
UnknownError
|
||||
),
|
||||
Catalogs(notCore, notPSD2, notOBWG),
|
||||
List(apiTagAccountApplication, apiTagAccount, apiTagNewStyle)
|
||||
)
|
||||
|
||||
lazy val updateAccountApplicationStatus : OBPEndpoint = {
|
||||
case "banks" :: BankId(bankId) ::"account-applications" :: accountApplicationId :: Nil JsonPut json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
|
||||
|
||||
_ <- NewStyle.function.hasEntitlement(failMsg = UserHasMissingRoles + CanUpdateAccountApplications)("", u.userId, ApiRole.canUpdateAccountApplications)
|
||||
failMsg = s"$InvalidJsonFormat The Json body should be the $AccountApplicationUpdateStatusJson "
|
||||
putJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
json.extract[AccountApplicationUpdateStatusJson]
|
||||
}
|
||||
|
||||
failMsg = s"$InvalidJsonFormat status should not be blank."
|
||||
status <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
Validate.notBlank(putJson.status)
|
||||
}
|
||||
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
|
||||
(accountApplication, _) <- NewStyle.function.getAccountApplicationById(accountApplicationId, callContext)
|
||||
|
||||
(accountApplication, _) <- NewStyle.function.updateAccountApplicationStatus(accountApplicationId, status, callContext)
|
||||
|
||||
userId = Option(accountApplication.userId)
|
||||
customerId = Option(accountApplication.customerId)
|
||||
|
||||
user <- unboxOptionOBPReturnType(userId.map(NewStyle.function.findByUserId(_, callContext)))
|
||||
customer <- unboxOptionOBPReturnType(customerId.map(NewStyle.function.getCustomerByCustomerId(_, callContext)))
|
||||
|
||||
_ <- status match {
|
||||
case "ACCEPTED" =>
|
||||
for{
|
||||
accountId <- Future{AccountId(UUID.randomUUID().toString)}
|
||||
(_, callContext) <- NewStyle.function.createSandboxBankAccount(
|
||||
bankId,
|
||||
accountId,
|
||||
accountApplication.productCode.value,
|
||||
"",
|
||||
"EUR",
|
||||
BigDecimal("0"),
|
||||
u.name,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
callContext)
|
||||
}yield {
|
||||
BankAccountCreation.setAsOwner(bankId, accountId, u)
|
||||
}
|
||||
case _ => Future{""}
|
||||
}
|
||||
|
||||
} yield {
|
||||
(createAccountApplicationJson(accountApplication, user, customer), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,8 +27,9 @@ Berlin 13359, Germany
|
||||
package code.api.v3_1_0
|
||||
|
||||
import java.lang
|
||||
import java.util.Date
|
||||
import java.util.{Date, Objects}
|
||||
|
||||
import code.accountapplication.AccountApplication
|
||||
import code.customeraddress.CustomerAddress
|
||||
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
|
||||
import code.api.util.RateLimitPeriod.LimitCallPeriod
|
||||
@ -49,6 +50,7 @@ import scala.collection.immutable.List
|
||||
import code.customer.Customer
|
||||
import code.context.UserAuthContext
|
||||
import code.entitlement.Entitlement
|
||||
import code.model.dataAccess.ResourceUser
|
||||
import code.productattribute.ProductAttribute.ProductAttribute
|
||||
import code.taxresidence.TaxResidence
|
||||
|
||||
@ -181,6 +183,7 @@ case class AccountWebhookJson(account_webhook_id: String,
|
||||
trigger_name: String,
|
||||
url: String,
|
||||
http_method: String,
|
||||
http_protocol: String,
|
||||
created_by_user_id: String,
|
||||
is_active: Boolean
|
||||
)
|
||||
@ -189,6 +192,7 @@ case class AccountWebhookPostJson(account_id: String,
|
||||
trigger_name: String,
|
||||
url: String,
|
||||
http_method: String,
|
||||
http_protocol: String,
|
||||
is_active: String
|
||||
)
|
||||
case class AccountWebhookPutJson(account_webhook_id: String,
|
||||
@ -326,7 +330,24 @@ case class ProductAttributeResponseJson(
|
||||
value: String,
|
||||
)
|
||||
|
||||
case class AccountApplicationJson(
|
||||
product_code: String,
|
||||
user_id: Option[String],
|
||||
customer_id: Option[String]
|
||||
)
|
||||
|
||||
case class AccountApplicationResponseJson(
|
||||
account_application_id: String,
|
||||
product_code: String,
|
||||
user: ResourceUserJSON,
|
||||
customer: CustomerJsonV310,
|
||||
date_of_application: Date,
|
||||
status: String
|
||||
)
|
||||
|
||||
case class AccountApplicationUpdateStatusJson(status: String)
|
||||
|
||||
case class AccountApplicationsJsonV310(account_applications: List[AccountApplicationResponseJson])
|
||||
|
||||
|
||||
case class RateLimitingInfoV310(enabled: Boolean, technology: String, service_available: Boolean, is_active: Boolean)
|
||||
@ -442,6 +463,7 @@ object JSONFactory310{
|
||||
trigger_name = wh.triggerName,
|
||||
url = wh.url,
|
||||
http_method = wh.httpMethod,
|
||||
http_protocol = wh.httpProtocol,
|
||||
created_by_user_id = wh.createdByUserId,
|
||||
is_active = wh.isActive()
|
||||
)
|
||||
@ -570,5 +592,35 @@ object JSONFactory310{
|
||||
`type` = productAttribute.attributeType.toString,
|
||||
value = productAttribute.value,
|
||||
)
|
||||
def createAccountApplicationJson(accountApplication: AccountApplication, user: Box[User], customer: Box[Customer]): AccountApplicationResponseJson = {
|
||||
|
||||
val userJson = user.map(u => ResourceUserJSON(
|
||||
user_id = u.userId,
|
||||
email = u.emailAddress,
|
||||
provider_id = u.idGivenByProvider,
|
||||
provider = u.provider,
|
||||
username = u.name
|
||||
)).orNull
|
||||
|
||||
val customerJson = customer.map(createCustomerJson).orNull
|
||||
|
||||
AccountApplicationResponseJson(
|
||||
account_application_id = accountApplication.accountApplicationId,
|
||||
product_code = accountApplication.productCode.value,
|
||||
user = userJson,
|
||||
customer = customerJson,
|
||||
date_of_application =accountApplication.dateOfApplication,
|
||||
status = accountApplication.status
|
||||
)
|
||||
}
|
||||
|
||||
def createAccountApplications(accountApplications: List[AccountApplication], users: List[User], customers: List[Customer]): AccountApplicationsJsonV310 = {
|
||||
val applicationList = accountApplications.map { x =>
|
||||
val user = Box(users.find(it => it.userId == x.userId))
|
||||
val customer = Box(customers.find(it => it.customerId == x.customerId))
|
||||
createAccountApplicationJson(x, user, customer)
|
||||
}
|
||||
AccountApplicationsJsonV310(applicationList)
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,33 +1,28 @@
|
||||
/**
|
||||
* Open Bank Project - API
|
||||
* Copyright (C) 2011-2018, TESOBE Ltd
|
||||
**
|
||||
*This program is free software: you can redistribute it and/or modify
|
||||
*it under the terms of the GNU Affero General Public License as published by
|
||||
*the Free Software Foundation, either version 3 of the License, or
|
||||
*(at your option) any later version.
|
||||
**
|
||||
*This program is distributed in the hope that it will be useful,
|
||||
*but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
*GNU Affero General Public License for more details.
|
||||
**
|
||||
*You should have received a copy of the GNU Affero General Public License
|
||||
*along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
**
|
||||
*Email: contact@tesobe.com
|
||||
*TESOBE Ltd
|
||||
*Osloerstrasse 16/17
|
||||
*Berlin 13359, Germany
|
||||
**
|
||||
*This product includes software developed at
|
||||
*TESOBE (http://www.tesobe.com/)
|
||||
* by
|
||||
*Simon Redfern : simon AT tesobe DOT com
|
||||
*Stefan Bethge : stefan AT tesobe DOT com
|
||||
*Everett Sochowski : everett AT tesobe DOT com
|
||||
*Ayoub Benali: ayoub AT tesobe DOT com
|
||||
*
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
package code.api.v3_1_0
|
||||
|
||||
@ -324,6 +319,10 @@ object OBPAPI3_1_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w
|
||||
Implementations3_1_0.getProductAttribute ::
|
||||
Implementations3_1_0.updateProductAttribute ::
|
||||
Implementations3_1_0.deleteProductAttribute ::
|
||||
Implementations3_1_0.createAccountApplication ::
|
||||
Implementations3_1_0.getAccountApplications ::
|
||||
Implementations3_1_0.getAccountApplication ::
|
||||
Implementations3_1_0.updateAccountApplicationStatus ::
|
||||
Nil
|
||||
|
||||
val allResourceDocs = Implementations3_1_0.resourceDocs ++
|
||||
|
||||
@ -4,10 +4,10 @@ package code.atms
|
||||
|
||||
// Need to import these one by one because in same package!
|
||||
|
||||
import code.api.util.OBPQueryParam
|
||||
import code.atms.Atms.{AtmId, AtmT}
|
||||
import code.bankconnectors.OBPQueryParam
|
||||
import code.model.BankId
|
||||
import code.common._
|
||||
import code.model.BankId
|
||||
import net.liftweb.common.Logger
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package code.atms
|
||||
|
||||
import code.api.util.{OBPLimit, OBPOffset, OBPQueryParam}
|
||||
import code.atms.Atms._
|
||||
import code.bankconnectors.{OBPLimit, OBPOffset, OBPQueryParam}
|
||||
import code.common._
|
||||
import code.model.BankId
|
||||
import code.util.{TwentyFourHourClockString, UUIDString}
|
||||
|
||||
@ -1,26 +1,27 @@
|
||||
package code.bankconnectors
|
||||
|
||||
import java.util.{Date, UUID}
|
||||
import java.util.Date
|
||||
|
||||
import code.accountapplication.AccountApplication
|
||||
import code.accountholder.{AccountHolders, MapperAccountHolders}
|
||||
import code.customeraddress.CustomerAddress
|
||||
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.accountId
|
||||
import code.api.util.APIUtil._
|
||||
import code.api.util.ApiRole._
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.{APIUtil, CallContext, ErrorMessages, NewStyle}
|
||||
import code.api.util._
|
||||
import code.api.v1_2_1.AmountOfMoneyJsonV121
|
||||
import code.api.v1_4_0.JSONFactory1_4_0.TransactionRequestAccountJsonV140
|
||||
import code.api.v2_1_0.{TransactionRequestCommonBodyJSON, _}
|
||||
import code.api.v3_1_0._
|
||||
import code.atms.Atms
|
||||
import code.atms.Atms.{AtmId, AtmT}
|
||||
import code.bankconnectors.akka.AkkaConnector_vDec2018
|
||||
import code.bankconnectors.vJune2017.KafkaMappedConnector_vJune2017
|
||||
import code.bankconnectors.vMar2017.{InboundAdapterInfoInternal, KafkaMappedConnector_vMar2017}
|
||||
import code.bankconnectors.vSept2018.KafkaMappedConnector_vSept2018
|
||||
import code.branches.Branches.{Branch, BranchId, BranchT}
|
||||
import code.context.UserAuthContext
|
||||
import code.customer._
|
||||
import code.customeraddress.CustomerAddress
|
||||
import code.fx.FXRate
|
||||
import code.kafka.Topics.TopicTrait
|
||||
import code.management.ImporterAPI.ImporterTransaction
|
||||
@ -40,15 +41,14 @@ import code.views.Views
|
||||
import net.liftweb.common.{Box, Empty, Failure, Full}
|
||||
import net.liftweb.mapper.By
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
import net.liftweb.util.{BCrypt, Helpers, Props, SimpleInjector}
|
||||
import net.liftweb.util.{BCrypt, Helpers, SimpleInjector}
|
||||
|
||||
import scala.collection.immutable.List
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.{Await, Future}
|
||||
import scala.concurrent.Future
|
||||
import scala.math.BigInt
|
||||
import scala.util.Random
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/*
|
||||
So we can switch between different sources of resources e.g.
|
||||
@ -65,11 +65,10 @@ Could consider a Map of ("resourceType" -> "provider") - this could tell us whic
|
||||
*/
|
||||
|
||||
object Connector extends SimpleInjector {
|
||||
|
||||
import scala.reflect.runtime.universe._
|
||||
def getConnectorInstance(connectorVersion: String):Connector = {
|
||||
connectorVersion match {
|
||||
case "mapped" => LocalMappedConnector
|
||||
case "akka_vDec2018" => AkkaConnector_vDec2018
|
||||
case "mongodb" => LocalRecordConnector
|
||||
case "obpjvm" => ObpJvmMappedConnector
|
||||
case "kafka" => KafkaMappedConnector
|
||||
@ -91,41 +90,6 @@ object Connector extends SimpleInjector {
|
||||
|
||||
}
|
||||
|
||||
class OBPQueryParam
|
||||
trait OBPOrder { def orderValue : Int }
|
||||
object OBPOrder {
|
||||
def apply(s: Option[String]): OBPOrder = s match {
|
||||
case Some("asc") => OBPAscending
|
||||
case Some("ASC")=> OBPAscending
|
||||
case _ => OBPDescending
|
||||
}
|
||||
}
|
||||
object OBPAscending extends OBPOrder { def orderValue = 1 }
|
||||
object OBPDescending extends OBPOrder { def orderValue = -1}
|
||||
case class OBPLimit(value: Int) extends OBPQueryParam
|
||||
case class OBPOffset(value: Int) extends OBPQueryParam
|
||||
case class OBPFromDate(value: Date) extends OBPQueryParam
|
||||
case class OBPToDate(value: Date) extends OBPQueryParam
|
||||
case class OBPOrdering(field: Option[String], order: OBPOrder) extends OBPQueryParam
|
||||
case class OBPConsumerId(value: String) extends OBPQueryParam
|
||||
case class OBPUserId(value: String) extends OBPQueryParam
|
||||
case class OBPBankId(value: String) extends OBPQueryParam
|
||||
case class OBPAccountId(value: String) extends OBPQueryParam
|
||||
case class OBPUrl(value: String) extends OBPQueryParam
|
||||
case class OBPAppName(value: String) extends OBPQueryParam
|
||||
case class OBPExcludeAppNames(values: List[String]) extends OBPQueryParam
|
||||
case class OBPImplementedByPartialFunction(value: String) extends OBPQueryParam
|
||||
case class OBPImplementedInVersion(value: String) extends OBPQueryParam
|
||||
case class OBPVerb(value: String) extends OBPQueryParam
|
||||
case class OBPAnon(value: Boolean) extends OBPQueryParam
|
||||
case class OBPCorrelationId(value: String) extends OBPQueryParam
|
||||
case class OBPDuration(value: Long) extends OBPQueryParam
|
||||
case class OBPExcludeUrlPatterns(values: List[String]) extends OBPQueryParam
|
||||
case class OBPExcludeImplementedByPartialFunctions(value: List[String]) extends OBPQueryParam
|
||||
case class OBPFunctionName(value: String) extends OBPQueryParam
|
||||
case class OBPConnectorName(value: String) extends OBPQueryParam
|
||||
case class OBPEmpty() extends OBPQueryParam
|
||||
|
||||
//Note: this is used for connector method: 'def getUser(name: String, password: String): Box[InboundUser]'
|
||||
case class InboundUser(
|
||||
email: String,
|
||||
@ -217,6 +181,7 @@ trait Connector extends MdcLoggable{
|
||||
}
|
||||
|
||||
def getAdapterInfo(callContext: Option[CallContext]) : Box[(InboundAdapterInfoInternal, Option[CallContext])] = Failure(NotImplemented + currentMethodName)
|
||||
def getAdapterInfoFuture(callContext: Option[CallContext]) : Future[Box[(InboundAdapterInfoInternal, Option[CallContext])]] = Future(Failure(NotImplemented + "getAdapterInfoFuture"))
|
||||
|
||||
// Gets current challenge level for transaction request
|
||||
// Transaction request challenge threshold. Level at which challenge is created and needs to be answered
|
||||
@ -350,6 +315,7 @@ trait Connector extends MdcLoggable{
|
||||
def getCoreBankAccountsHeldFuture(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) : Future[Box[List[AccountHeld]]]= Future {Failure(NotImplemented + currentMethodName)}
|
||||
|
||||
def checkBankAccountExists(bankId : BankId, accountId : AccountId, callContext: Option[CallContext] = None) : Box[(BankAccount, Option[CallContext])]= Failure(NotImplemented + currentMethodName)
|
||||
def checkBankAccountExistsFuture(bankId : BankId, accountId : AccountId, callContext: Option[CallContext] = None) : Future[Box[(BankAccount, Option[CallContext])]] = Future {Failure(NotImplemented + currentMethodName)}
|
||||
|
||||
/**
|
||||
* This method is just return an empty account to AccountType.
|
||||
@ -400,6 +366,8 @@ trait Connector extends MdcLoggable{
|
||||
|
||||
def getCounterparties(thisBankId: BankId, thisAccountId: AccountId,viewId :ViewId, callContext: Option[CallContext] = None): Box[(List[CounterpartyTrait], Option[CallContext])]= Failure(NotImplemented + currentMethodName)
|
||||
|
||||
def getCounterpartiesFuture(thisBankId: BankId, thisAccountId: AccountId,viewId: ViewId, callContext: Option[CallContext] = None): OBPReturnType[Box[List[CounterpartyTrait]]] = Future {(Failure(NotImplemented + currentMethodName), callContext)}
|
||||
|
||||
def getTransactions(bankId: BankId, accountID: AccountId, queryParams: OBPQueryParam*): Box[List[Transaction]]= {
|
||||
getTransactions(bankId, accountID, None, queryParams: _*).map(_._1)
|
||||
}
|
||||
@ -407,9 +375,17 @@ trait Connector extends MdcLoggable{
|
||||
//TODO, here is a problem for return value `List[Transaction]`, this is a normal class, not a trait. It is a big class,
|
||||
// it contains thisAccount(BankAccount object) and otherAccount(Counterparty object)
|
||||
def getTransactions(bankId: BankId, accountID: AccountId, callContext: Option[CallContext], queryParams: OBPQueryParam*): Box[(List[Transaction], Option[CallContext])]= Failure(NotImplemented + currentMethodName)
|
||||
def getTransactionsFuture(bankId: BankId, accountID: AccountId, callContext: Option[CallContext], queryParams: OBPQueryParam*): OBPReturnType[Box[List[Transaction]]] = {
|
||||
val result: Box[(List[Transaction], Option[CallContext])] = getTransactions(bankId, accountID, callContext, queryParams: _*)
|
||||
Future(result.map(_._1), result.map(_._2).getOrElse(callContext))
|
||||
}
|
||||
def getTransactionsCore(bankId: BankId, accountID: AccountId, callContext: Option[CallContext], queryParams: OBPQueryParam*): Box[(List[TransactionCore], Option[CallContext])]= Failure(NotImplemented + currentMethodName)
|
||||
|
||||
def getTransaction(bankId: BankId, accountID : AccountId, transactionId : TransactionId, callContext: Option[CallContext] = None): Box[(Transaction, Option[CallContext])] = Failure(NotImplemented + currentMethodName)
|
||||
def getTransactionFuture(bankId: BankId, accountID : AccountId, transactionId : TransactionId, callContext: Option[CallContext] = None): OBPReturnType[Box[Transaction]] = {
|
||||
val result: Box[(Transaction, Option[CallContext])] = getTransaction(bankId, accountID, transactionId, callContext)
|
||||
Future(result.map(_._1), result.map(_._2).getOrElse(callContext))
|
||||
}
|
||||
|
||||
def getPhysicalCards(user : User) : Box[List[PhysicalCard]] = Failure(NotImplemented + currentMethodName)
|
||||
|
||||
@ -1025,7 +1001,8 @@ trait Connector extends MdcLoggable{
|
||||
to = CounterpartyIdJson(counterpartyId.value),
|
||||
value = AmountOfMoneyJsonV121(body.value.currency, body.value.amount),
|
||||
description = body.description,
|
||||
charge_policy = transactionRequest.charge_policy)
|
||||
charge_policy = transactionRequest.charge_policy,
|
||||
future_date = transactionRequest.future_date)
|
||||
|
||||
(transactionId, callContext) <- NewStyle.function.makePaymentv210(
|
||||
fromAccount,
|
||||
@ -1052,7 +1029,8 @@ trait Connector extends MdcLoggable{
|
||||
to = IbanJson(toCounterpartyIBan),
|
||||
value = AmountOfMoneyJsonV121(body.value.currency, body.value.amount),
|
||||
description = body.description,
|
||||
charge_policy = transactionRequest.charge_policy
|
||||
charge_policy = transactionRequest.charge_policy,
|
||||
future_date = transactionRequest.future_date
|
||||
)
|
||||
(transactionId, callContext) <- NewStyle.function.makePaymentv210(
|
||||
fromAccount,
|
||||
@ -1545,4 +1523,20 @@ trait Connector extends MdcLoggable{
|
||||
productAttributeId: String,
|
||||
callContext: Option[CallContext]
|
||||
): OBPReturnType[Box[Boolean]] = Future{(Failure(NotImplemented + currentMethodName), callContext)}
|
||||
|
||||
def createAccountApplication(
|
||||
productCode: ProductCode,
|
||||
userId: Option[String],
|
||||
customerId: Option[String],
|
||||
callContext: Option[CallContext]
|
||||
): OBPReturnType[Box[AccountApplication]] = Future{(Failure(NotImplemented + currentMethodName), callContext)}
|
||||
|
||||
def getAllAccountApplication(callContext: Option[CallContext]): OBPReturnType[Box[List[AccountApplication]]] =
|
||||
Future{(Failure(NotImplemented + currentMethodName), callContext)}
|
||||
|
||||
def getAccountApplicationById(accountApplicationId: String, callContext: Option[CallContext]): OBPReturnType[Box[AccountApplication]] =
|
||||
Future{(Failure(NotImplemented + currentMethodName), callContext)}
|
||||
|
||||
def updateAccountApplicationStatus(accountApplicationId:String, status: String, callContext: Option[CallContext]): OBPReturnType[Box[AccountApplication]] =
|
||||
Future{(Failure(NotImplemented + currentMethodName), callContext)}
|
||||
}
|
||||
@ -28,7 +28,7 @@ import java.util.{Date, Locale, UUID}
|
||||
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.accountholder.AccountHolders
|
||||
import code.api.util.{APIUtil, CallContext, ErrorMessages}
|
||||
import code.api.util._
|
||||
import code.api.v2_1_0.TransactionRequestCommonBodyJSON
|
||||
import code.bankconnectors.vMar2017.{InboundAdapterInfoInternal, KafkaMappedConnector_vMar2017}
|
||||
import code.branches.Branches.{Branch, BranchT}
|
||||
|
||||
@ -27,18 +27,20 @@ import java.text.SimpleDateFormat
|
||||
import java.time.ZoneOffset.UTC
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.{Date, Locale, UUID}
|
||||
import java.util.Date
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
import code.accountholder.AccountHolders
|
||||
import code.api.cache.Caching
|
||||
import code.api.util.APIUtil.saveConnectorMetric
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.{APIUtil, CallContext, ErrorMessages}
|
||||
import code.api.util._
|
||||
import code.api.v2_1_0.TransactionRequestCommonBodyJSON
|
||||
import code.atms.Atms.{AtmId, AtmT}
|
||||
import code.atms.{Atms, MappedAtm}
|
||||
import code.bankconnectors.vMar2017.{InboundAdapterInfoInternal, KafkaMappedConnector_vMar2017}
|
||||
import code.branches.Branches.{Branch, BranchId, BranchT}
|
||||
import code.fx.{FXRate, fx}
|
||||
import code.bankconnectors.vMar2017.KafkaMappedConnector_vMar2017
|
||||
import code.branches.Branches.{Branch, BranchT}
|
||||
import code.fx.FXRate
|
||||
import code.kafka.KafkaHelper
|
||||
import code.management.ImporterAPI.ImporterTransaction
|
||||
import code.metadata.comments.Comments
|
||||
@ -57,7 +59,7 @@ import code.transactionrequests.{MappedTransactionRequestTypeCharge, Transaction
|
||||
import code.util.Helper
|
||||
import code.util.Helper.MdcLoggable
|
||||
import code.views.Views
|
||||
import com.google.common.cache.CacheBuilder
|
||||
import com.tesobe.CacheKeyFromArguments
|
||||
import com.tesobe.obp.transport.Pager
|
||||
import com.tesobe.obp.transport.spi.{DefaultPager, DefaultSorter, TimestampFilter}
|
||||
import net.liftweb.common._
|
||||
@ -67,15 +69,9 @@ import net.liftweb.mapper._
|
||||
import net.liftweb.util.Helpers._
|
||||
|
||||
import scala.collection.immutable.{List, Seq}
|
||||
import scala.concurrent.TimeoutException
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{Future, TimeoutException}
|
||||
import scala.language.postfixOps
|
||||
import scalacache.ScalaCache
|
||||
import scalacache.guava.GuavaCache
|
||||
import scalacache.memoization._
|
||||
import java.util.UUID.randomUUID
|
||||
import code.api.cache.Caching
|
||||
import com.tesobe.CacheKeyFromArguments
|
||||
|
||||
object KafkaMappedConnector_JVMcompatible extends Connector with KafkaHelper with MdcLoggable {
|
||||
|
||||
@ -330,7 +326,7 @@ object KafkaMappedConnector_JVMcompatible extends Connector with KafkaHelper wit
|
||||
} yield {
|
||||
val res = tryExtract[List[KafkaInboundAccount]](process(req)) match {
|
||||
case Full(a) => a
|
||||
case Empty => List.empty
|
||||
case _ => List.empty
|
||||
}
|
||||
logger.debug(s"JVMCompatible updateUserAccountViews got response ${res}")
|
||||
res
|
||||
|
||||
@ -3,12 +3,13 @@ package code.bankconnectors
|
||||
import java.util.UUID.randomUUID
|
||||
import java.util.{Date, UUID}
|
||||
|
||||
import code.accountapplication.AccountApplication
|
||||
import code.customeraddress.{CustomerAddress, MappedCustomerAddress}
|
||||
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
|
||||
import code.api.cache.Caching
|
||||
import code.api.util.APIUtil.{OBPReturnType, saveConnectorMetric, stringOrNull}
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.{APIUtil, CallContext, ErrorMessages, NewStyle}
|
||||
import code.api.util._
|
||||
import code.api.v2_1_0.TransactionRequestCommonBodyJSON
|
||||
import code.api.v3_1_0.{CardObjectJson, CheckbookOrdersJson, PostCustomerJsonV310, TaxResidenceV310}
|
||||
import code.atms.Atms.{AtmId, AtmT}
|
||||
@ -371,15 +372,19 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
}
|
||||
|
||||
override def getBankAccount(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]): Box[(BankAccount, Option[CallContext])] = {
|
||||
getBankAccountCommon(bankId, accountId, callContext)
|
||||
}
|
||||
|
||||
def getBankAccountCommon(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]) = {
|
||||
MappedBankAccount
|
||||
.find(By(MappedBankAccount.bank, bankId.value),
|
||||
By(MappedBankAccount.theAccountId, accountId.value))
|
||||
.map(
|
||||
account =>
|
||||
account
|
||||
.mAccountRoutingScheme(APIUtil.ValueOrOBP(account.accountRoutingScheme))
|
||||
.mAccountRoutingAddress(APIUtil.ValueOrOBPId(account.accountRoutingAddress,account.accountId.value))
|
||||
).map(bankAccount => (bankAccount,callContext))
|
||||
account
|
||||
.mAccountRoutingScheme(APIUtil.ValueOrOBP(account.accountRoutingScheme))
|
||||
.mAccountRoutingAddress(APIUtil.ValueOrOBPId(account.accountRoutingAddress, account.accountId.value))
|
||||
).map(bankAccount => (bankAccount, callContext))
|
||||
}
|
||||
|
||||
override def getBankAccountsFuture(bankIdAcountIds: List[BankIdAccountId], callContext: Option[CallContext]) : Future[Box[List[BankAccount]]] = {
|
||||
@ -397,7 +402,11 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
|
||||
override def checkBankAccountExists(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]) = {
|
||||
getBankAccount(bankId: BankId, accountId: AccountId, callContext)
|
||||
}
|
||||
}
|
||||
override def checkBankAccountExistsFuture(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]): Future[Box[(BankAccount, Option[CallContext])]] =
|
||||
Future {
|
||||
getBankAccount(bankId: BankId, accountId: AccountId, callContext)
|
||||
}
|
||||
|
||||
override def getCoreBankAccounts(bankIdAcountIds: List[BankIdAccountId], callContext: Option[CallContext]) : Box[(List[CoreAccount], Option[CallContext])]= {
|
||||
Full(
|
||||
@ -1666,6 +1675,9 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
override def getCounterparties(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId, callContext: Option[CallContext] = None): Box[(List[CounterpartyTrait], Option[CallContext])] = {
|
||||
Counterparties.counterparties.vend.getCounterparties(thisBankId, thisAccountId, viewId).map(counterparties =>(counterparties, callContext))
|
||||
}
|
||||
override def getCounterpartiesFuture(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId, callContext: Option[CallContext] = None): OBPReturnType[Box[List[CounterpartyTrait]]] = Future {
|
||||
(getCounterparties(thisBankId, thisAccountId, viewId, callContext) map (i => i._1), callContext)
|
||||
}
|
||||
|
||||
override def createOrUpdateBank(
|
||||
bankId: String,
|
||||
@ -1797,6 +1809,10 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
)
|
||||
}
|
||||
|
||||
def getCustomersByUserId(userId: String, callContext: Option[CallContext]): Box[(List[Customer], Option[CallContext])] = {
|
||||
Full((Customer.customerProvider.vend.getCustomersByUserId(userId), callContext))
|
||||
}
|
||||
|
||||
override def getCustomersByUserIdFuture(userId: String, callContext: Option[CallContext]): Future[Box[(List[Customer],Option[CallContext])]]=
|
||||
Customer.customerProvider.vend.getCustomersByUserIdFuture(userId) map {
|
||||
customersBox =>(customersBox.map(customers=>(customers,callContext)))
|
||||
@ -1946,6 +1962,30 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
ProductAttribute.productAttributeProvider.vend.deleteProductAttribute(productAttributeId: String) map {
|
||||
(_, callContext)
|
||||
}
|
||||
|
||||
|
||||
override def createAccountApplication(
|
||||
productCode: ProductCode,
|
||||
userId: Option[String],
|
||||
customerId: Option[String],
|
||||
callContext: Option[CallContext]
|
||||
): OBPReturnType[Box[AccountApplication]] =
|
||||
AccountApplication.accountApplication.vend.createAccountApplication(productCode, userId, customerId) map {
|
||||
(_, callContext)
|
||||
}
|
||||
|
||||
override def getAllAccountApplication(callContext: Option[CallContext]): OBPReturnType[Box[List[AccountApplication]]] =
|
||||
AccountApplication.accountApplication.vend.getAll() map {
|
||||
(_, callContext)
|
||||
}
|
||||
|
||||
override def getAccountApplicationById(accountApplicationId: String, callContext: Option[CallContext]): OBPReturnType[Box[AccountApplication]] =
|
||||
AccountApplication.accountApplication.vend.getById(accountApplicationId) map {
|
||||
(_, callContext)
|
||||
}
|
||||
|
||||
override def updateAccountApplicationStatus(accountApplicationId:String, status: String, callContext: Option[CallContext]): OBPReturnType[Box[AccountApplication]] =
|
||||
AccountApplication.accountApplication.vend.updateStatus(accountApplicationId, status) map {
|
||||
(_, callContext)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,21 +1,14 @@
|
||||
package code.bankconnectors
|
||||
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.{Date, TimeZone, UUID}
|
||||
import java.util.{Date, TimeZone}
|
||||
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.{APIUtil, CallContext}
|
||||
import code.api.util.{APIUtil, CallContext, OBPQueryParam}
|
||||
import code.api.v2_1_0.TransactionRequestCommonBodyJSON
|
||||
import code.bankconnectors.vMar2017.InboundAdapterInfoInternal
|
||||
import code.branches.Branches.{Branch, BranchT}
|
||||
import code.fx.{FXRate, fx}
|
||||
import code.management.ImporterAPI.ImporterTransaction
|
||||
import code.metadata.counterparties.{Counterparties, CounterpartyTrait, Metadata, MongoCounterparties}
|
||||
import code.metadata.counterparties.{Counterparties, Metadata, MongoCounterparties}
|
||||
import code.model._
|
||||
import code.model.dataAccess._
|
||||
import code.products.Products.{Product, ProductCode}
|
||||
import code.transactionrequests.TransactionRequestTypeCharge
|
||||
import code.transactionrequests.TransactionRequests._
|
||||
import code.util.Helper
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.mongodb.QueryBuilder
|
||||
@ -25,7 +18,6 @@ import net.liftweb.json.Extraction
|
||||
import net.liftweb.json.JsonAST.JValue
|
||||
import net.liftweb.mongodb.BsonDSL._
|
||||
import net.liftweb.util.Helpers._
|
||||
import net.liftweb.util.Props
|
||||
import org.bson.types.ObjectId
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
@ -8,7 +8,7 @@ import java.util.{Date, Locale, Optional, UUID}
|
||||
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.accountholder.{AccountHolders, MapperAccountHolders}
|
||||
import code.api.util.{APIUtil, CallContext, ErrorMessages}
|
||||
import code.api.util._
|
||||
import code.api.v2_1_0.TransactionRequestCommonBodyJSON
|
||||
import code.bankconnectors.vMar2017.InboundAdapterInfoInternal
|
||||
import code.branches.Branches.{Branch, BranchT}
|
||||
|
||||
@ -0,0 +1,426 @@
|
||||
package code.bankconnectors.akka
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import akka.pattern.ask
|
||||
import code.actorsystem.ObpLookupSystem
|
||||
import code.api.util.APIUtil.{AdapterImplementation, MessageDoc, OBPReturnType}
|
||||
import code.api.util.ExampleValue._
|
||||
import code.api.util._
|
||||
import code.bankconnectors._
|
||||
import code.bankconnectors.akka.InboundTransformerDec2018._
|
||||
import code.bankconnectors.akka.actor.{AkkaConnectorActorInit, AkkaConnectorHelperActor}
|
||||
import code.bankconnectors.vMar2017.InboundAdapterInfoInternal
|
||||
import code.customer.{CreditLimit, CreditRating, Customer, CustomerFaceImage}
|
||||
import code.metadata.counterparties.CounterpartyTrait
|
||||
import code.model.{AccountId, AccountRouting, BankAccount, BankId, BankIdAccountId, CoreAccount, CounterpartyBespoke, Transaction, TransactionId, ViewId, Bank => BankTrait}
|
||||
import com.sksamuel.avro4s.SchemaFor
|
||||
import net.liftweb.common.{Box, Full}
|
||||
import net.liftweb.json.Extraction.decompose
|
||||
import net.liftweb.json.JsonAST.JValue
|
||||
import net.liftweb.json.parse
|
||||
|
||||
import scala.collection.immutable.{List, Nil}
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
|
||||
object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit {
|
||||
|
||||
implicit override val nameOfConnector = AkkaConnector_vDec2018.toString
|
||||
|
||||
val messageFormat: String = "Dec2018"
|
||||
implicit val formats = net.liftweb.json.DefaultFormats
|
||||
override val messageDocs = ArrayBuffer[MessageDoc]()
|
||||
val emptyObjectJson: JValue = decompose(Nil)
|
||||
|
||||
lazy val southSideActor = ObpLookupSystem.getAkkaConnectorActor(AkkaConnectorHelperActor.actorName)
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.AdapterInfo",
|
||||
messageFormat = messageFormat,
|
||||
description = "Gets information about the active general (non bank specific) Adapter that is responding to messages sent by OBP.",
|
||||
outboundTopic = Some(OutboundGetAdapterInfo.getClass.getSimpleName.replace("$", "")),
|
||||
inboundTopic = Some(InboundAdapterInfo.getClass.getSimpleName.replace("$", "")),
|
||||
exampleOutboundMessage = decompose(
|
||||
OutboundGetAdapterInfo(
|
||||
APIUtil.DateWithMsFormat.format(new Date()),
|
||||
Examples.callContextAkka)
|
||||
),
|
||||
exampleInboundMessage = decompose(
|
||||
InboundAdapterInfo(
|
||||
name = "The south side of Akka connector",
|
||||
version = messageFormat,
|
||||
git_commit = gitCommitExample.value,
|
||||
date = APIUtil.DateWithMsFormat.format(new Date()),
|
||||
Examples.callContextAkka)
|
||||
),
|
||||
outboundAvroSchema = Some(parse(SchemaFor[OutboundGetAdapterInfo]().toString(true))),
|
||||
inboundAvroSchema = Some(parse(SchemaFor[InboundAdapterInfo]().toString(true))),
|
||||
adapterImplementation = Some(AdapterImplementation("- Core", 1))
|
||||
)
|
||||
override def getAdapterInfoFuture(callContext: Option[CallContext]): Future[Box[(InboundAdapterInfoInternal, Option[CallContext])]] = {
|
||||
val req = OutboundGetAdapterInfo((new Date()).toString, callContext.map(_.toCallContextAkka))
|
||||
val response = (southSideActor ? req).mapTo[InboundAdapterInfo]
|
||||
response.map(r =>
|
||||
Full(
|
||||
(
|
||||
InboundAdapterInfoInternal(
|
||||
errorCode = "",
|
||||
backendMessages = Nil,
|
||||
name = r.name,
|
||||
version = r.version,
|
||||
git_commit = r.git_commit,
|
||||
date = r.date
|
||||
)
|
||||
,
|
||||
callContext
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.Banks",
|
||||
messageFormat = messageFormat,
|
||||
description = "Gets the banks list on this OBP installation.",
|
||||
outboundTopic = Some(OutboundGetBanks.getClass.getSimpleName.replace("$", "")),
|
||||
inboundTopic = Some(InboundGetBanks.getClass.getSimpleName.replace("$", "")),
|
||||
exampleOutboundMessage = decompose(
|
||||
OutboundGetBanks(Examples.callContextAkka)
|
||||
),
|
||||
exampleInboundMessage = decompose(
|
||||
InboundGetBanks(
|
||||
Some(List(Examples.bank)),
|
||||
Examples.callContextAkka)
|
||||
),
|
||||
outboundAvroSchema = Some(parse(SchemaFor[OutboundGetBanks]().toString(true))),
|
||||
inboundAvroSchema = Some(parse(SchemaFor[InboundGetBanks]().toString(true))),
|
||||
adapterImplementation = Some(AdapterImplementation("- Core", 2))
|
||||
)
|
||||
override def getBanksFuture(callContext: Option[CallContext]): Future[Box[(List[BankTrait], Option[CallContext])]] = {
|
||||
val req = OutboundGetBanks(callContext.map(_.toCallContextAkka))
|
||||
val response: Future[InboundGetBanks] = (southSideActor ? req).mapTo[InboundGetBanks]
|
||||
response.map(_.payload.map(r => (r.map(toBank(_)), callContext)))
|
||||
}
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.Bank",
|
||||
messageFormat = messageFormat,
|
||||
description = "Get a specific Bank as specified by bankId",
|
||||
outboundTopic = Some(OutboundGetBank.getClass.getSimpleName.replace("$", "")),
|
||||
inboundTopic = Some(InboundGetBank.getClass.getSimpleName.replace("$", "")),
|
||||
exampleOutboundMessage = decompose(
|
||||
OutboundGetBank(
|
||||
bankIdExample.value,
|
||||
Examples.callContextAkka)
|
||||
),
|
||||
exampleInboundMessage = decompose(
|
||||
InboundGetBank(
|
||||
Some(Examples.bank),
|
||||
Examples.callContextAkka)
|
||||
),
|
||||
outboundAvroSchema = Some(parse(SchemaFor[OutboundGetBank]().toString(true))),
|
||||
inboundAvroSchema = Some(parse(SchemaFor[InboundGetBank]().toString(true))),
|
||||
adapterImplementation = Some(AdapterImplementation("- Core", 5))
|
||||
)
|
||||
override def getBankFuture(bankId : BankId, callContext: Option[CallContext]): Future[Box[(BankTrait, Option[CallContext])]] = {
|
||||
val req = OutboundGetBank(bankId.value, callContext.map(_.toCallContextAkka))
|
||||
val response: Future[InboundGetBank] = (southSideActor ? req).mapTo[InboundGetBank]
|
||||
response.map(_.payload.map(r => (toBank(r), callContext)))
|
||||
}
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.check.BankAccountExists",
|
||||
messageFormat = messageFormat,
|
||||
description = "Check a bank Account exists - as specified by bankId and accountId.",
|
||||
outboundTopic = Some(OutboundCheckBankAccountExists.getClass.getSimpleName.replace("$", "")),
|
||||
inboundTopic = Some(InboundCheckBankAccountExists.getClass.getSimpleName.replace("$", "")),
|
||||
exampleOutboundMessage = decompose(
|
||||
OutboundCheckBankAccountExists(
|
||||
bankIdExample.value,
|
||||
accountIdExample.value,
|
||||
Examples.callContextAkka
|
||||
)
|
||||
),
|
||||
exampleInboundMessage = decompose(
|
||||
InboundCheckBankAccountExists(
|
||||
Some(Examples.inboundAccountDec2018Example),
|
||||
Examples.callContextAkka
|
||||
)
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("Accounts", 4))
|
||||
)
|
||||
override def checkBankAccountExistsFuture(bankId : BankId, accountId : AccountId, callContext: Option[CallContext] = None): Future[Box[(BankAccount, Option[CallContext])]] = {
|
||||
val req = OutboundCheckBankAccountExists(bankId.value, accountId.value, callContext.map(_.toCallContextAkka))
|
||||
val response: Future[InboundCheckBankAccountExists] = (southSideActor ? req).mapTo[InboundCheckBankAccountExists]
|
||||
response.map(_.payload.map(r => (toAccount(r), callContext)))
|
||||
}
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.Account",
|
||||
messageFormat = messageFormat,
|
||||
description = "Get a single Account as specified by the bankId and accountId.",
|
||||
outboundTopic = Some(OutboundGetAccount.getClass.getSimpleName.replace("$", "")),
|
||||
inboundTopic = Some(InboundGetAccount.getClass.getSimpleName.replace("$", "")),
|
||||
exampleOutboundMessage = decompose(
|
||||
OutboundGetAccount(
|
||||
bankIdExample.value,
|
||||
accountIdExample.value,
|
||||
Examples.callContextAkka
|
||||
)
|
||||
),
|
||||
exampleInboundMessage = decompose(
|
||||
InboundGetAccount(
|
||||
Some(Examples.inboundAccountDec2018Example),
|
||||
Examples.callContextAkka
|
||||
)
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("Accounts", 7))
|
||||
)
|
||||
override def getBankAccountFuture(bankId : BankId, accountId : AccountId, callContext: Option[CallContext]): OBPReturnType[Box[BankAccount]] = {
|
||||
val req = OutboundGetAccount(bankId.value, accountId.value, callContext.map(_.toCallContextAkka))
|
||||
val response = (southSideActor ? req).mapTo[InboundGetAccount]
|
||||
response.map(a => (a.payload.map(toAccount(_)), callContext))
|
||||
}
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.coreBankAccounts",
|
||||
messageFormat = messageFormat,
|
||||
description = "Get bank Accounts available to the User (without Metadata)",
|
||||
outboundTopic = Some(OutboundGetCoreBankAccounts.getClass.getSimpleName.replace("$", "")),
|
||||
inboundTopic = Some(InboundGetCoreBankAccounts.getClass.getSimpleName.replace("$", "")),
|
||||
exampleOutboundMessage = decompose(
|
||||
OutboundGetCoreBankAccounts(
|
||||
List(BankIdAccountId(BankId(bankIdExample.value), AccountId(accountIdExample.value))),
|
||||
Examples.callContextAkka
|
||||
)
|
||||
),
|
||||
exampleInboundMessage = decompose(
|
||||
InboundGetCoreBankAccounts(
|
||||
List(
|
||||
InboundCoreAccount(
|
||||
accountIdExample.value,
|
||||
"My private account for Uber",
|
||||
bankIdExample.value,
|
||||
accountTypeExample.value,
|
||||
List(AccountRouting(accountRoutingSchemeExample.value, accountRoutingAddressExample.value)
|
||||
)
|
||||
)
|
||||
),
|
||||
Examples.callContextAkka
|
||||
)),
|
||||
adapterImplementation = Some(AdapterImplementation("Accounts", 1))
|
||||
)
|
||||
override def getCoreBankAccountsFuture(BankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) : Future[Box[(List[CoreAccount], Option[CallContext])]] = {
|
||||
val req = OutboundGetCoreBankAccounts(BankIdAccountIds, callContext.map(_.toCallContextAkka))
|
||||
val response: Future[InboundGetCoreBankAccounts] = (southSideActor ? req).mapTo[InboundGetCoreBankAccounts]
|
||||
response.map(a => Full(a.payload.map( x => CoreAccount(x.id,x.label,x.bankId,x.accountType, x.accountRoutings)), callContext))
|
||||
}
|
||||
|
||||
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.CustomersByUserId",
|
||||
messageFormat = messageFormat,
|
||||
description = "Get Customers represented by the User.",
|
||||
outboundTopic = Some(OutboundGetCustomersByUserId.getClass.getSimpleName.replace("$", "")),
|
||||
inboundTopic = Some(InboundGetCustomersByUserId.getClass.getSimpleName.replace("$", "")),
|
||||
exampleOutboundMessage = decompose(
|
||||
OutboundGetCustomersByUserId(
|
||||
userIdExample.value,
|
||||
Examples.callContextAkka
|
||||
)
|
||||
),
|
||||
exampleInboundMessage = decompose(
|
||||
InboundGetCustomersByUserId(
|
||||
InboundCustomer(
|
||||
customerId = customerIdExample.value,
|
||||
bankId = bankIdExample.value,
|
||||
number = customerNumberExample.value,
|
||||
legalName = legalNameExample.value,
|
||||
mobileNumber = "String",
|
||||
email = "String",
|
||||
faceImage = CustomerFaceImage(date = APIUtil.DateWithSecondsExampleObject, url = "String"),
|
||||
dateOfBirth = APIUtil.DateWithSecondsExampleObject, relationshipStatus = "String",
|
||||
dependents = 1,
|
||||
dobOfDependents = List(APIUtil.DateWithSecondsExampleObject),
|
||||
highestEducationAttained = "String",
|
||||
employmentStatus = "String",
|
||||
creditRating = CreditRating(rating = "String", source = "String"),
|
||||
creditLimit = CreditLimit(currency = currencyExample.value, amount = creditLimitAmountExample.value),
|
||||
kycStatus = false,
|
||||
lastOkDate = APIUtil.DateWithSecondsExampleObject
|
||||
) :: Nil,
|
||||
Examples.callContextAkka
|
||||
)
|
||||
),
|
||||
outboundAvroSchema = None,
|
||||
inboundAvroSchema = None,
|
||||
adapterImplementation = Some(AdapterImplementation("Accounts", 0))
|
||||
)
|
||||
override def getCustomersByUserIdFuture(userId: String , callContext: Option[CallContext]): Future[Box[(List[Customer], Option[CallContext])]] = {
|
||||
val req = OutboundGetCustomersByUserId(userId, callContext.map(_.toCallContextAkka))
|
||||
val response: Future[InboundGetCustomersByUserId] = (southSideActor ? req).mapTo[InboundGetCustomersByUserId]
|
||||
response.map(a => Full(toCustomers(a.payload), callContext))
|
||||
}
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.counterparties",
|
||||
messageFormat = messageFormat,
|
||||
description = "Get Counterparties available to the View on the Account specified by thisBankId, thisAccountId and viewId.",
|
||||
outboundTopic = Some(OutboundGetCounterparties.getClass.getSimpleName.replace("$", "")),
|
||||
inboundTopic = Some(InboundGetCounterparties.getClass.getSimpleName.replace("$", "")),
|
||||
exampleOutboundMessage = decompose(
|
||||
OutboundGetCounterparties(
|
||||
thisBankId = bankIdExample.value,
|
||||
accountIdExample.value,
|
||||
viewId = "Auditor",
|
||||
Examples.callContextAkka
|
||||
)
|
||||
),
|
||||
exampleInboundMessage = decompose(
|
||||
InboundGetCounterparties(
|
||||
InboundCounterparty(
|
||||
createdByUserId = userIdExample.value,
|
||||
name = "",
|
||||
thisBankId = bankIdExample.value,
|
||||
thisAccountId = accountIdExample.value,
|
||||
thisViewId = "Auditor",
|
||||
counterpartyId = counterpartyIdExample.value,
|
||||
otherAccountRoutingScheme = accountRoutingSchemeExample.value,
|
||||
otherAccountRoutingAddress = accountRoutingAddressExample.value,
|
||||
otherBankRoutingScheme = bankRoutingSchemeExample.value,
|
||||
otherBankRoutingAddress = bankRoutingAddressExample.value,
|
||||
otherBranchRoutingScheme = accountRoutingSchemeExample.value,
|
||||
otherBranchRoutingAddress = accountRoutingAddressExample.value,
|
||||
isBeneficiary = true,
|
||||
description = "",
|
||||
otherAccountSecondaryRoutingScheme = accountRoutingSchemeExample.value,
|
||||
otherAccountSecondaryRoutingAddress = accountRoutingAddressExample.value,
|
||||
bespoke = List(
|
||||
CounterpartyBespoke(key = "key", value = "value"))
|
||||
) :: Nil,
|
||||
Examples.callContextAkka
|
||||
)
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("Payments", 0))
|
||||
)
|
||||
override def getCounterpartiesFuture(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId, callContext: Option[CallContext] = None): OBPReturnType[Box[List[CounterpartyTrait]]] = {
|
||||
val req = OutboundGetCounterparties(thisBankId.value, thisAccountId.value, viewId.value, callContext.map(_.toCallContextAkka))
|
||||
val response: Future[InboundGetCounterparties] = (southSideActor ? req).mapTo[InboundGetCounterparties]
|
||||
response.map(a => (Full(a.payload), callContext))
|
||||
}
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.Transactions",
|
||||
messageFormat = messageFormat,
|
||||
description = "Get Transactions for an Account specified by bankId and accountId. Pagination is achieved with limit, fromDate and toDate.",
|
||||
outboundTopic = Some(OutboundGetTransactions.getClass.getSimpleName.replace("$", "")),
|
||||
inboundTopic = Some(InboundGetTransactions.getClass.getSimpleName.replace("$", "")),
|
||||
exampleOutboundMessage = decompose(
|
||||
OutboundGetTransactions(
|
||||
bankId = bankIdExample.value,
|
||||
accountId = accountIdExample.value,
|
||||
limit = 100,
|
||||
fromDate=APIUtil.DateWithSecondsExampleString,
|
||||
toDate=APIUtil.DateWithSecondsExampleString,
|
||||
Examples.callContextAkka
|
||||
)
|
||||
),
|
||||
exampleInboundMessage = decompose(
|
||||
InboundGetTransactions(
|
||||
Nil,
|
||||
Examples.callContextAkka
|
||||
)
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("Transactions", 10))
|
||||
)
|
||||
override def getTransactionsFuture(bankId: BankId, accountId: AccountId, callContext: Option[CallContext], queryParams: OBPQueryParam*): OBPReturnType[Box[List[Transaction]]] = {
|
||||
val limit = queryParams.collect { case OBPLimit(value) => value }.headOption.getOrElse(100)
|
||||
val fromDate = queryParams.collect { case OBPFromDate(date) => APIUtil.DateWithMsFormat.format(date) }.headOption.getOrElse(APIUtil.DefaultFromDate.toString)
|
||||
val toDate = queryParams.collect { case OBPToDate(date) => APIUtil.DateWithMsFormat.format(date) }.headOption.getOrElse(APIUtil.DefaultToDate.toString)
|
||||
|
||||
val req = OutboundGetTransactions(bankId.value, accountId.value, limit, fromDate, toDate, callContext.map(_.toCallContextAkka))
|
||||
val response: Future[InboundGetTransactions] = (southSideActor ? req).mapTo[InboundGetTransactions]
|
||||
response.map(a => (Full(toTransactions(a.payload)), callContext))
|
||||
}
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.Transaction",
|
||||
messageFormat = messageFormat,
|
||||
description = "Get a single Transaction specified by bankId, accountId and transactionId",
|
||||
outboundTopic = Some(OutboundGetTransaction.getClass.getSimpleName.replace("$", "")),
|
||||
inboundTopic = Some(InboundGetTransaction.getClass.getSimpleName.replace("$", "")),
|
||||
exampleOutboundMessage = decompose(
|
||||
OutboundGetTransaction(
|
||||
bankId = bankIdExample.value,
|
||||
accountId = accountIdExample.value,
|
||||
transactionId = transactionIdExample.value,
|
||||
Examples.callContextAkka
|
||||
)
|
||||
),
|
||||
exampleInboundMessage = decompose(
|
||||
InboundGetTransaction(
|
||||
None,
|
||||
Examples.callContextAkka
|
||||
)
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("Transactions", 11))
|
||||
)
|
||||
override def getTransactionFuture(bankId: BankId, accountId: AccountId, transactionId: TransactionId, callContext: Option[CallContext]): OBPReturnType[Box[Transaction]] = {
|
||||
val req = OutboundGetTransaction(bankId.value, accountId.value, transactionId.value, callContext.map(_.toCallContextAkka))
|
||||
val response: Future[InboundGetTransaction] = (southSideActor ? req).mapTo[InboundGetTransaction]
|
||||
response.map(a => (a.payload.map(toTransaction(_)), callContext))
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
object Examples {
|
||||
val inboundAccountDec2018Example =
|
||||
InboundAccount(
|
||||
bankId = bankIdExample.value,
|
||||
branchId = branchIdExample.value,
|
||||
accountId = accountIdExample.value,
|
||||
accountNumber = accountNumberExample.value,
|
||||
accountType = accountTypeExample.value,
|
||||
balanceAmount = balanceAmountExample.value,
|
||||
balanceCurrency = currencyExample.value,
|
||||
owners = owner1Example.value :: owner1Example.value :: Nil,
|
||||
viewsToGenerate = "Public" :: "Accountant" :: "Auditor" :: Nil,
|
||||
bankRoutingScheme = bankRoutingSchemeExample.value,
|
||||
bankRoutingAddress = bankRoutingAddressExample.value,
|
||||
branchRoutingScheme = branchRoutingSchemeExample.value,
|
||||
branchRoutingAddress = branchRoutingAddressExample.value,
|
||||
accountRoutingScheme = accountRoutingSchemeExample.value,
|
||||
accountRoutingAddress = accountRoutingAddressExample.value,
|
||||
accountRouting = Nil,
|
||||
accountRules = Nil
|
||||
)
|
||||
|
||||
val callContextAkka = Some(
|
||||
CallContextAkka(
|
||||
Some(userIdExample.value),
|
||||
Some("9ddb6507-9cec-4e5e-b09a-ef1cb203825a"),
|
||||
correlationIdExample.value,
|
||||
Some(sessionIdExample.value)
|
||||
)
|
||||
)
|
||||
|
||||
val bank =
|
||||
InboundBank(
|
||||
bankId = bankIdExample.value,
|
||||
shortName = "The Royal Bank of Scotland",
|
||||
fullName = "The Royal Bank of Scotland",
|
||||
logoUrl = "http://www.red-bank-shoreditch.com/logo.gif",
|
||||
websiteUrl = "http://www.red-bank-shoreditch.com",
|
||||
bankRoutingScheme = "OBP",
|
||||
bankRoutingAddress = "rbs"
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,344 @@
|
||||
package code.bankconnectors.akka
|
||||
|
||||
import java.lang
|
||||
import java.util.Date
|
||||
|
||||
import code.api.util.{APIUtil, CallContextAkka}
|
||||
import code.customer.{CreditLimit, CreditRating, Customer, CustomerFaceImage}
|
||||
import code.metadata.counterparties.CounterpartyTrait
|
||||
import code.model.dataAccess.MappedBankAccountData
|
||||
import code.model.{AccountId, AccountRouting, AccountRule, BankAccount, BankId, BankIdAccountId, Counterparty, CounterpartyBespoke, Transaction, TransactionId, Bank => BankTrait}
|
||||
import net.liftweb.mapper.By
|
||||
import net.liftweb.util.Helpers.today
|
||||
|
||||
import scala.collection.immutable.List
|
||||
import scala.math.BigDecimal
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* case classes used to define outbound Akka messages
|
||||
*
|
||||
*/
|
||||
case class OutboundGetAdapterInfo(date: String,
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class OutboundGetBanks(callContext: Option[CallContextAkka])
|
||||
|
||||
case class OutboundGetBank(bankId: String,
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class OutboundCheckBankAccountExists(bankId: String,
|
||||
ccountId: String,
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class OutboundGetAccount(bankId: String,
|
||||
accountId: String,
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class OutboundGetCoreBankAccounts(bankIdAccountIds: List[BankIdAccountId],
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class OutboundGetCustomersByUserId(userId: String, callContext: Option[CallContextAkka])
|
||||
|
||||
case class OutboundGetCounterparties(thisBankId: String,
|
||||
thisAccountId: String,
|
||||
viewId: String,
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class OutboundGetTransactions(bankId: String,
|
||||
accountId: String,
|
||||
limit: Int,
|
||||
fromDate: String,
|
||||
toDate: String,
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class OutboundGetTransaction(bankId: String,
|
||||
accountId: String,
|
||||
transactionId: String,
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
/**
|
||||
*
|
||||
* case classes used to define inbound Akka messages
|
||||
*
|
||||
*/
|
||||
case class InboundAdapterInfo(
|
||||
name: String,
|
||||
version: String,
|
||||
git_commit: String,
|
||||
date: String,
|
||||
callContext: Option[CallContextAkka]
|
||||
)
|
||||
|
||||
case class InboundGetBanks(payload: Option[List[InboundBank]],
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class InboundGetBank(payload: Option[InboundBank],
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class InboundCheckBankAccountExists(payload: Option[InboundAccount],
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class InboundGetAccount(payload: Option[InboundAccount],
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class InboundGetCoreBankAccounts(payload: List[InboundCoreAccount],
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class InboundGetCustomersByUserId(payload: List[InboundCustomer],
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class InboundGetCounterparties(payload: List[InboundCounterparty],
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class InboundGetTransactions(payload: List[InboundTransaction],
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
case class InboundGetTransaction(payload: Option[InboundTransaction],
|
||||
callContext: Option[CallContextAkka])
|
||||
|
||||
|
||||
case class InboundBank(
|
||||
bankId: String,
|
||||
shortName: String,
|
||||
fullName: String,
|
||||
logoUrl: String,
|
||||
websiteUrl: String,
|
||||
bankRoutingScheme: String,
|
||||
bankRoutingAddress: String
|
||||
)
|
||||
|
||||
case class InboundAccount(
|
||||
bankId: String,
|
||||
branchId: String,
|
||||
accountId: String,
|
||||
accountNumber: String,
|
||||
accountType: String,
|
||||
balanceAmount: String,
|
||||
balanceCurrency: String,
|
||||
owners: List[String],
|
||||
viewsToGenerate: List[String],
|
||||
bankRoutingScheme: String,
|
||||
bankRoutingAddress: String,
|
||||
branchRoutingScheme: String,
|
||||
branchRoutingAddress: String,
|
||||
accountRoutingScheme: String,
|
||||
accountRoutingAddress: String,
|
||||
accountRouting: List[AccountRouting],
|
||||
accountRules: List[AccountRule]
|
||||
)
|
||||
|
||||
|
||||
case class InboundCoreAccount(
|
||||
id: String,
|
||||
label: String,
|
||||
bankId: String,
|
||||
accountType: String,
|
||||
accountRoutings: List[AccountRouting]
|
||||
)
|
||||
|
||||
case class InboundCustomer(
|
||||
customerId: String,
|
||||
bankId: String,
|
||||
number: String,
|
||||
legalName: String,
|
||||
mobileNumber: String,
|
||||
email: String,
|
||||
faceImage: CustomerFaceImage,
|
||||
dateOfBirth: Date,
|
||||
relationshipStatus: String,
|
||||
dependents: Integer,
|
||||
dobOfDependents: List[Date],
|
||||
highestEducationAttained: String,
|
||||
employmentStatus: String,
|
||||
creditRating: CreditRating,
|
||||
creditLimit: CreditLimit,
|
||||
kycStatus: lang.Boolean,
|
||||
lastOkDate: Date
|
||||
)
|
||||
|
||||
case class InboundTransaction(
|
||||
uuid: String,
|
||||
id: TransactionId,
|
||||
thisAccount: BankAccount,
|
||||
otherAccount: Counterparty,
|
||||
transactionType: String,
|
||||
amount: BigDecimal,
|
||||
currency: String,
|
||||
description: Option[String],
|
||||
startDate: Date,
|
||||
finishDate: Date,
|
||||
balance: BigDecimal
|
||||
)
|
||||
|
||||
|
||||
case class InboundCounterparty(
|
||||
createdByUserId: String,
|
||||
name: String,
|
||||
thisBankId: String,
|
||||
thisAccountId: String,
|
||||
thisViewId: String,
|
||||
counterpartyId: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String,
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
isBeneficiary: Boolean,
|
||||
description: String,
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String,
|
||||
bespoke: List[CounterpartyBespoke]) extends CounterpartyTrait
|
||||
|
||||
object InboundTransformerDec2018 {
|
||||
|
||||
/*
|
||||
* Customer
|
||||
*/
|
||||
case class AkkaDec2018Customer(
|
||||
customerId: String,
|
||||
bankId: String,
|
||||
number: String,
|
||||
legalName: String,
|
||||
mobileNumber: String,
|
||||
email: String,
|
||||
faceImage: CustomerFaceImage,
|
||||
dateOfBirth: Date,
|
||||
relationshipStatus: String,
|
||||
dependents: Integer,
|
||||
dobOfDependents: List[Date],
|
||||
highestEducationAttained: String,
|
||||
employmentStatus: String,
|
||||
creditRating: CreditRating,
|
||||
creditLimit: CreditLimit,
|
||||
kycStatus: lang.Boolean,
|
||||
lastOkDate: Date,
|
||||
title: String = "", //These new fields for V310, not from Connector for now.
|
||||
branchId: String = "", //These new fields for V310, not from Connector for now.
|
||||
nameSuffix: String = "", //These new fields for V310, not from Connector for now.
|
||||
) extends Customer
|
||||
|
||||
def toCustomer(customer: InboundCustomer): Customer = {
|
||||
AkkaDec2018Customer(
|
||||
customerId = customer.customerId,
|
||||
bankId = customer.bankId,
|
||||
number = customer.number,
|
||||
legalName = customer.legalName,
|
||||
mobileNumber = customer.mobileNumber,
|
||||
email = customer.email,
|
||||
faceImage = customer.faceImage,
|
||||
dateOfBirth = customer.dateOfBirth,
|
||||
relationshipStatus = customer.relationshipStatus,
|
||||
dependents = customer.dependents,
|
||||
dobOfDependents = customer.dobOfDependents,
|
||||
highestEducationAttained = customer.highestEducationAttained,
|
||||
employmentStatus = customer.employmentStatus,
|
||||
creditRating = customer.creditRating,
|
||||
creditLimit = customer.creditLimit,
|
||||
kycStatus = customer.kycStatus,
|
||||
lastOkDate = customer.lastOkDate,
|
||||
)
|
||||
}
|
||||
|
||||
def toCustomers(customers: List[InboundCustomer]): List[Customer] = {
|
||||
customers.map(toCustomer)
|
||||
}
|
||||
|
||||
/*
|
||||
* Transaction
|
||||
*/
|
||||
def toTransaction(t: InboundTransaction): Transaction = {
|
||||
new Transaction(
|
||||
uuid = t.uuid,
|
||||
id = t.id,
|
||||
thisAccount = t.thisAccount,
|
||||
otherAccount = t.otherAccount,
|
||||
transactionType = t.transactionType,
|
||||
amount = t.amount,
|
||||
currency = t.currency,
|
||||
description = t.description,
|
||||
startDate = t.startDate,
|
||||
finishDate = t.finishDate,
|
||||
balance = t.balance
|
||||
)
|
||||
}
|
||||
|
||||
def toTransactions(t: List[InboundTransaction]): List[Transaction] = {
|
||||
t.map(toTransaction)
|
||||
}
|
||||
|
||||
/*
|
||||
* Bank
|
||||
*/
|
||||
case class BankAkka(b: InboundBank) extends BankTrait {
|
||||
override def bankId = BankId(b.bankId)
|
||||
|
||||
override def fullName = b.fullName
|
||||
|
||||
override def shortName = b.shortName
|
||||
|
||||
override def logoUrl = b.logoUrl
|
||||
|
||||
override def websiteUrl = b.websiteUrl
|
||||
|
||||
override def bankRoutingScheme = b.bankRoutingScheme
|
||||
|
||||
override def bankRoutingAddress = b.bankRoutingAddress
|
||||
|
||||
override def swiftBic = ""
|
||||
|
||||
override def nationalIdentifier: String = ""
|
||||
}
|
||||
|
||||
def toBank(b: InboundBank): BankTrait = BankAkka(b)
|
||||
|
||||
/*
|
||||
* Account
|
||||
*/
|
||||
case class BankAccountDec2018(a: InboundAccount) extends BankAccount {
|
||||
override def accountId: AccountId = AccountId(a.accountId)
|
||||
|
||||
override def accountType: String = a.accountType
|
||||
|
||||
override def balance: BigDecimal = BigDecimal(a.balanceAmount)
|
||||
|
||||
override def currency: String = a.balanceCurrency
|
||||
|
||||
override def name: String = a.owners.head
|
||||
|
||||
override def swift_bic: Option[String] = Some("swift_bic")
|
||||
|
||||
override def iban: Option[String] = Some("iban")
|
||||
|
||||
override def number: String = a.accountNumber
|
||||
|
||||
override def bankId: BankId = BankId(a.bankId)
|
||||
|
||||
override def lastUpdate: Date = APIUtil.DateWithMsFormat.parse(today.getTime.toString)
|
||||
|
||||
override def accountHolder: String = a.owners.head
|
||||
|
||||
override
|
||||
def label: String = (for {
|
||||
d <- MappedBankAccountData.find(By(MappedBankAccountData.accountId, a.accountId))
|
||||
} yield {
|
||||
d.getLabel
|
||||
}).getOrElse(a.accountNumber)
|
||||
|
||||
override def accountRoutingScheme: String = a.accountRoutingScheme
|
||||
|
||||
override def accountRoutingAddress: String = a.accountRoutingAddress
|
||||
|
||||
override def accountRoutings: List[AccountRouting] = List()
|
||||
|
||||
override def branchId: String = a.branchId
|
||||
|
||||
override def accountRules: List[AccountRule] = a.accountRules
|
||||
}
|
||||
|
||||
def toAccount(account: InboundAccount): BankAccount = BankAccountDec2018(account)
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
package code.bankconnectors.akka.actor
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import code.util.Helper
|
||||
|
||||
|
||||
object AkkaConnectorActorConfig {
|
||||
|
||||
val remoteHostname = APIUtil.getPropsValue("akka_connector.hostname").openOr("127.0.0.1")
|
||||
val remotePort = APIUtil.getPropsValue("akka_connector.port").openOr("2662")
|
||||
|
||||
val localHostname = "127.0.0.1"
|
||||
val localPort = Helper.findAvailablePort()
|
||||
|
||||
val akka_loglevel = APIUtil.getPropsValue("akka_connector.loglevel").openOr("INFO")
|
||||
|
||||
val commonConf =
|
||||
"""
|
||||
akka {
|
||||
loggers = ["akka.event.slf4j.Slf4jLogger"]
|
||||
loglevel = """ + akka_loglevel + """
|
||||
actor {
|
||||
provider = "akka.remote.RemoteActorRefProvider"
|
||||
allow-java-serialization = off
|
||||
kryo {
|
||||
type = "graph"
|
||||
idstrategy = "default"
|
||||
buffer-size = 65536
|
||||
max-buffer-size = -1
|
||||
use-manifests = false
|
||||
use-unsafe = true
|
||||
post-serialization-transformations = "off"
|
||||
#post-serialization-transformations = "lz4,aes"
|
||||
#encryption {
|
||||
# aes {
|
||||
# mode = "AES/CBC/PKCS5Padding"
|
||||
# key = j68KkRjq21ykRGAQ
|
||||
# IV-length = 16
|
||||
# }
|
||||
#}
|
||||
implicit-registration-logging = false
|
||||
kryo-trace = false
|
||||
resolve-subclasses = true
|
||||
}
|
||||
serializers {
|
||||
kryo = "com.twitter.chill.akka.AkkaSerializer"
|
||||
}
|
||||
serialization-bindings {
|
||||
"net.liftweb.common.Full" = kryo,
|
||||
"net.liftweb.common.Empty" = kryo,
|
||||
"net.liftweb.common.Box" = kryo,
|
||||
"net.liftweb.common.ParamFailure" = kryo,
|
||||
"code.api.APIFailure" = kryo,
|
||||
"code.model.BankAccount" = kryo,
|
||||
"code.model.View" = kryo,
|
||||
"code.model.dataAccess.ViewImpl" = kryo,
|
||||
"code.model.User" = kryo,
|
||||
"code.model.ViewId" = kryo,
|
||||
"code.model.ViewIdBankIdAccountId" = kryo,
|
||||
"code.model.Permission" = kryo,
|
||||
"scala.Unit" = kryo,
|
||||
"scala.Boolean" = kryo,
|
||||
"java.io.Serializable" = kryo,
|
||||
"scala.collection.immutable.List" = kryo,
|
||||
"akka.actor.ActorSelectionMessage" = kryo,
|
||||
"code.model.Consumer" = kryo,
|
||||
"code.model.AppType" = kryo
|
||||
}
|
||||
}
|
||||
remote {
|
||||
enabled-transports = ["akka.remote.netty.tcp"]
|
||||
netty {
|
||||
tcp {
|
||||
send-buffer-size = 50000000
|
||||
receive-buffer-size = 50000000
|
||||
maximum-frame-size = 52428800
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
val lookupConf =
|
||||
s"""
|
||||
${commonConf}
|
||||
akka {
|
||||
remote.netty.tcp.hostname = ${localHostname}
|
||||
remote.netty.tcp.port = 0
|
||||
}
|
||||
"""
|
||||
|
||||
val localConf =
|
||||
s"""
|
||||
${commonConf}
|
||||
akka {
|
||||
remote.netty.tcp.hostname = ${localHostname}
|
||||
remote.netty.tcp.port = ${localPort}
|
||||
}
|
||||
"""
|
||||
|
||||
val remoteConf =
|
||||
s"""
|
||||
${commonConf}
|
||||
akka {
|
||||
remote.netty.tcp.hostname = ${remoteHostname}
|
||||
remote.netty.tcp.port = ${remotePort}
|
||||
}
|
||||
"""
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package code.bankconnectors.akka.actor
|
||||
|
||||
import akka.util.Timeout
|
||||
import code.api.util.APIUtil
|
||||
import code.util.Helper.MdcLoggable
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
trait AkkaConnectorActorInit extends MdcLoggable{
|
||||
// Default is 3 seconds, which should be more than enough for slower systems
|
||||
val ACTOR_TIMEOUT: Long = APIUtil.getPropsAsLongValue("akka_connector.timeout").openOr(3)
|
||||
implicit val timeout = Timeout(ACTOR_TIMEOUT * (1000 milliseconds))
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package code.bankconnectors.akka.actor
|
||||
|
||||
import akka.actor.{ActorSystem, Props}
|
||||
import code.api.util.APIUtil
|
||||
import code.util.Helper.MdcLoggable
|
||||
|
||||
object AkkaConnectorHelperActor extends MdcLoggable {
|
||||
|
||||
val actorName = APIUtil.getPropsValue("akka_connector.name_of_actor", "akka-connector-actor")
|
||||
|
||||
//This method is called in Boot.scala
|
||||
def startAkkaConnectorHelperActors(actorSystem: ActorSystem): Unit = {
|
||||
logger.info("***** Starting " + actorName + " at the North side *****")
|
||||
val actorsHelper = Map(
|
||||
Props[SouthSideActorOfAkkaConnector] -> actorName
|
||||
)
|
||||
actorsHelper.foreach { a => logger.info(actorSystem.actorOf(a._1, name = a._2)) }
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,188 @@
|
||||
package code.bankconnectors.akka.actor
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import akka.actor.{Actor, ActorLogging}
|
||||
import code.api.util.{APIUtil, OBPFromDate, OBPLimit, OBPToDate}
|
||||
import code.bankconnectors.LocalMappedConnector._
|
||||
import code.bankconnectors.akka._
|
||||
import code.customer.{CreditLimit, CreditRating, Customer, CustomerFaceImage}
|
||||
import code.metadata.counterparties.CounterpartyTrait
|
||||
import code.model.dataAccess.MappedBank
|
||||
import code.model.{Bank => _, _}
|
||||
import code.util.Helper.MdcLoggable
|
||||
import net.liftweb.common.Box
|
||||
|
||||
import scala.collection.immutable.List
|
||||
|
||||
|
||||
/**
|
||||
* This Actor acts in next way:
|
||||
*/
|
||||
class SouthSideActorOfAkkaConnector extends Actor with ActorLogging with MdcLoggable {
|
||||
|
||||
def receive: Receive = waitingForRequest
|
||||
|
||||
private def waitingForRequest: Receive = {
|
||||
case OutboundGetAdapterInfo(_, cc) =>
|
||||
val result =
|
||||
InboundAdapterInfo(
|
||||
"The south side of Akka connector",
|
||||
"Dec2018",
|
||||
APIUtil.gitCommit,
|
||||
APIUtil.DateWithMsFormat.format(new Date()),
|
||||
cc
|
||||
)
|
||||
sender ! result
|
||||
|
||||
case OutboundGetBanks(cc) =>
|
||||
val result: Box[List[MappedBank]] = getBanks(None).map(r => r._1)
|
||||
sender ! InboundGetBanks(result.map(l => l.map(Transformer.bank(_))).toOption, cc)
|
||||
|
||||
case OutboundGetBank(bankId, cc) =>
|
||||
val result: Box[MappedBank] = getBank(BankId(bankId), None).map(r => r._1)
|
||||
sender ! InboundGetBank(result.map(Transformer.bank(_)).toOption, cc)
|
||||
|
||||
case OutboundCheckBankAccountExists(bankId, accountId, cc) =>
|
||||
val result: Box[BankAccount] = checkBankAccountExists(BankId(bankId), AccountId(accountId), None).map(r => r._1)
|
||||
sender ! InboundCheckBankAccountExists(result.map(Transformer.bankAccount(_)).toOption, cc)
|
||||
|
||||
case OutboundGetAccount(bankId, accountId, cc) =>
|
||||
val result: Box[BankAccount] = getBankAccount(BankId(bankId), AccountId(accountId), None).map(r => r._1)
|
||||
org.scalameta.logger.elem(result)
|
||||
sender ! InboundGetAccount(result.map(Transformer.bankAccount(_)).toOption, cc)
|
||||
|
||||
case OutboundGetCoreBankAccounts(bankIdAccountIds, cc) =>
|
||||
val result: Box[List[CoreAccount]] = getCoreBankAccounts(bankIdAccountIds, None).map(r => r._1)
|
||||
sender ! InboundGetCoreBankAccounts(result.getOrElse(Nil).map(Transformer.coreAccount(_)), cc)
|
||||
|
||||
case OutboundGetCustomersByUserId(userId, cc) =>
|
||||
val result: Box[List[Customer]] = getCustomersByUserId(userId, None).map(r => r._1)
|
||||
sender ! InboundGetCustomersByUserId(result.getOrElse(Nil).map(Transformer.toInternalCustomer(_)), cc)
|
||||
|
||||
case OutboundGetCounterparties(thisBankId, thisAccountId, viewId, cc) =>
|
||||
val result: Box[List[CounterpartyTrait]] = getCounterparties(BankId(thisBankId), AccountId(thisAccountId), ViewId(viewId), None).map(r => r._1)
|
||||
sender ! InboundGetCounterparties(result.getOrElse(Nil).map(Transformer.toInternalCounterparty(_)), cc)
|
||||
|
||||
case OutboundGetTransactions(bankId, accountId, limit, fromDate, toDate, cc) =>
|
||||
val from = APIUtil.DateWithMsFormat.parse(fromDate)
|
||||
val to = APIUtil.DateWithMsFormat.parse(toDate)
|
||||
val result = getTransactions(BankId(bankId), AccountId(accountId), None, List(OBPLimit(limit), OBPFromDate(from), OBPToDate(to)): _*).map(r => r._1)
|
||||
sender ! InboundGetTransactions(result.getOrElse(Nil).map(Transformer.toInternalTransaction(_)), cc)
|
||||
|
||||
case OutboundGetTransaction(bankId, accountId, transactionId, cc) =>
|
||||
val result = getTransaction(BankId(bankId), AccountId(accountId), TransactionId(transactionId), None).map(r => r._1)
|
||||
sender ! InboundGetTransaction(result.map(Transformer.toInternalTransaction(_)), cc)
|
||||
|
||||
case message =>
|
||||
logger.warn("[AKKA ACTOR ERROR - REQUEST NOT RECOGNIZED] " + message)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
object Transformer {
|
||||
def bank(mb: MappedBank): InboundBank =
|
||||
InboundBank(
|
||||
bankId=mb.bankId.value,
|
||||
shortName=mb.shortName,
|
||||
fullName=mb.fullName,
|
||||
logoUrl=mb.logoUrl,
|
||||
websiteUrl=mb.websiteUrl,
|
||||
bankRoutingScheme=mb.bankRoutingScheme,
|
||||
bankRoutingAddress=mb.bankRoutingAddress
|
||||
)
|
||||
|
||||
def bankAccount(acc: BankAccount) =
|
||||
InboundAccount(
|
||||
bankId = acc.bankId.value,
|
||||
branchId = acc.branchId,
|
||||
accountId = acc.accountId.value,
|
||||
accountNumber = acc.number,
|
||||
accountType = acc.accountType,
|
||||
balanceAmount = acc.balance.toString(),
|
||||
balanceCurrency = acc.currency,
|
||||
owners = acc.customerOwners.map(_.customerId).toList,
|
||||
viewsToGenerate = Nil,
|
||||
bankRoutingScheme = acc.bankRoutingScheme,
|
||||
bankRoutingAddress = acc.bankRoutingAddress,
|
||||
branchRoutingScheme = "",
|
||||
branchRoutingAddress = "",
|
||||
accountRoutingScheme = acc.accountRoutingScheme,
|
||||
accountRoutingAddress = acc.accountRoutingAddress,
|
||||
accountRouting = Nil,
|
||||
accountRules = Nil
|
||||
)
|
||||
|
||||
def coreAccount(a: CoreAccount) =
|
||||
InboundCoreAccount(
|
||||
id = a.id,
|
||||
label = a.label,
|
||||
bankId = a.bankId,
|
||||
accountType = a.accountType,
|
||||
accountRoutings = a.accountRoutings
|
||||
)
|
||||
|
||||
def toInternalCustomer(customer: Customer): InboundCustomer = {
|
||||
InboundCustomer(
|
||||
customerId = customer.customerId,
|
||||
bankId = customer.bankId,
|
||||
number = customer.number,
|
||||
legalName = customer.legalName,
|
||||
mobileNumber = customer.mobileNumber,
|
||||
email = customer.email,
|
||||
faceImage = CustomerFaceImage(customer.faceImage.date,customer.faceImage.url),
|
||||
dateOfBirth = customer.dateOfBirth,
|
||||
relationshipStatus = customer.relationshipStatus,
|
||||
dependents = customer.dependents,
|
||||
dobOfDependents = customer.dobOfDependents,
|
||||
highestEducationAttained = customer.highestEducationAttained,
|
||||
employmentStatus = customer.employmentStatus,
|
||||
creditRating = CreditRating(customer.creditRating.rating, customer.creditRating.source),
|
||||
creditLimit = CreditLimit(customer.creditLimit.amount,customer.creditLimit.currency),
|
||||
kycStatus = customer.kycStatus,
|
||||
lastOkDate = customer.lastOkDate,
|
||||
)
|
||||
}
|
||||
|
||||
def toInternalCounterparty(c: CounterpartyTrait) = {
|
||||
InboundCounterparty(
|
||||
createdByUserId=c.createdByUserId,
|
||||
name=c.name,
|
||||
thisBankId=c.thisBankId,
|
||||
thisAccountId=c.thisAccountId,
|
||||
thisViewId=c.thisViewId,
|
||||
counterpartyId=c.counterpartyId,
|
||||
otherAccountRoutingScheme=c.otherAccountRoutingScheme,
|
||||
otherAccountRoutingAddress=c.otherAccountRoutingAddress,
|
||||
otherBankRoutingScheme=c.otherBankRoutingScheme,
|
||||
otherBankRoutingAddress=c.otherBankRoutingAddress,
|
||||
otherBranchRoutingScheme=c.otherBankRoutingScheme,
|
||||
otherBranchRoutingAddress=c.otherBranchRoutingAddress,
|
||||
isBeneficiary=c.isBeneficiary,
|
||||
description=c.description,
|
||||
otherAccountSecondaryRoutingScheme=c.otherAccountSecondaryRoutingScheme,
|
||||
otherAccountSecondaryRoutingAddress=c.otherAccountSecondaryRoutingAddress,
|
||||
bespoke=c.bespoke
|
||||
)
|
||||
}
|
||||
|
||||
def toInternalTransaction(t: Transaction): InboundTransaction = {
|
||||
InboundTransaction(
|
||||
uuid = t.uuid ,
|
||||
id = t.id ,
|
||||
thisAccount = t.thisAccount ,
|
||||
otherAccount = t.otherAccount ,
|
||||
transactionType = t.transactionType ,
|
||||
amount = t.amount ,
|
||||
currency = t.currency ,
|
||||
description = t.description ,
|
||||
startDate = t.startDate ,
|
||||
finishDate = t.finishDate ,
|
||||
balance = t.balance
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
|
||||
import code.api.cache.Caching
|
||||
import code.api.util.APIUtil.{MessageDoc, getSecondsCache, saveConnectorMetric}
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.{APIUtil, ApiSession, CallContext, ErrorMessages}
|
||||
import code.api.util._
|
||||
import code.api.util.APIUtil._
|
||||
import code.api.v3_1_0.{AccountV310Json, CardObjectJson, CheckbookOrdersJson}
|
||||
import code.atms.Atms.{AtmId, AtmT}
|
||||
@ -50,6 +50,7 @@ import com.google.common.cache.CacheBuilder
|
||||
import com.sksamuel.avro4s.SchemaFor
|
||||
import com.tesobe.{CacheKeyFromArguments, CacheKeyOmit}
|
||||
import net.liftweb.common.{Box, _}
|
||||
import net.liftweb.json
|
||||
import net.liftweb.json.Extraction._
|
||||
import net.liftweb.json.JsonAST.JValue
|
||||
import net.liftweb.json.{Extraction, MappingException, parse}
|
||||
@ -165,6 +166,47 @@ trait KafkaMappedConnector_vJune2017 extends Connector with KafkaHelper with Mdc
|
||||
|
||||
res
|
||||
}
|
||||
override def getAdapterInfoFuture(callContext: Option[CallContext]): Future[Box[(InboundAdapterInfoInternal, Option[CallContext])]] = {
|
||||
val req = OutboundGetAdapterInfo(
|
||||
AuthInfo(sessionId = callContext.get.correlationId),
|
||||
DateWithSecondsExampleString
|
||||
)
|
||||
|
||||
logger.debug(s"Kafka getAdapterInfoFuture Req says: is: $req")
|
||||
|
||||
val future = for {
|
||||
res <- processToFuture[OutboundGetAdapterInfo](req) map {
|
||||
f =>
|
||||
try {
|
||||
f.extract[InboundAdapterInfo]
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
val received = json.compactRender(f)
|
||||
val expected = SchemaFor[InboundAdapterInfo]().toString(false)
|
||||
val err = s"Extraction Failed: You received this ($received). We expected this ($expected)"
|
||||
sendOutboundAdapterError(err)
|
||||
throw new MappingException(err, e)
|
||||
}
|
||||
} map {
|
||||
x => x.data
|
||||
}
|
||||
} yield {
|
||||
Full(res)
|
||||
}
|
||||
|
||||
val res = future map {
|
||||
case Full(list) if (list.errorCode=="") =>
|
||||
Full(list, callContext)
|
||||
case Full(list) if (list.errorCode!="") =>
|
||||
Failure("INTERNAL-"+ list.errorCode+". + CoreBank-Status:"+ list.backendMessages)
|
||||
case _ =>
|
||||
Failure(ErrorMessages.UnknownError)
|
||||
}
|
||||
logger.debug(s"Kafka getAdapterInfoFuture says res is $res")
|
||||
res
|
||||
}
|
||||
|
||||
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.Banks",
|
||||
@ -645,6 +687,11 @@ trait KafkaMappedConnector_vJune2017 extends Connector with KafkaHelper with Mdc
|
||||
}
|
||||
}
|
||||
}("getBankAccount")
|
||||
|
||||
override def checkBankAccountExistsFuture(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]) =
|
||||
Future {
|
||||
checkBankAccountExists(bankId, accountId, callContext)
|
||||
}
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.coreBankAccounts",
|
||||
@ -1327,14 +1374,14 @@ trait KafkaMappedConnector_vJune2017 extends Connector with KafkaHelper with Mdc
|
||||
val res = box match {
|
||||
case Full((data, status, callContext)) if (status.errorCode=="") =>
|
||||
//For consistency with sandbox mode, we need combine obp transactions in database and adapter transactions
|
||||
val transancitonRequests = for{
|
||||
val transactionRequests = for{
|
||||
adapterTransactionRequests <- Full(data)
|
||||
//TODO, this will cause performance issue, we need limit the number of transaction requests.
|
||||
obpTransactionRequests <- LocalMappedConnector.getTransactionRequestsImpl210(fromAccount) ?~! s"$ConnectorEmptyResponse, error on LocalMappedConnector.getTransactionRequestsImpl210"
|
||||
} yield {
|
||||
adapterTransactionRequests ::: obpTransactionRequests
|
||||
}
|
||||
transancitonRequests.map(transactionRequests =>(transactionRequests, callContext))
|
||||
transactionRequests.map(transactionRequests =>(transactionRequests, callContext))
|
||||
case Full((data, status, _)) if (status.errorCode!="") =>
|
||||
Failure("INTERNAL-"+ status.errorCode+". + CoreBank-Status:"+ status.backendMessages)
|
||||
case Empty =>
|
||||
@ -1434,6 +1481,9 @@ trait KafkaMappedConnector_vJune2017 extends Connector with KafkaHelper with Mdc
|
||||
}
|
||||
}
|
||||
}("getCounterparties")
|
||||
override def getCounterpartiesFuture(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId, callContext: Option[CallContext] = None): OBPReturnType[Box[List[CounterpartyTrait]]] = Future {
|
||||
(getCounterparties(thisBankId, thisAccountId, viewId, callContext) map (i => i._1), callContext)
|
||||
}
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.CounterpartyByCounterpartyId",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,19 +16,14 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
package code.bankconnectors.vMar2017
|
||||
|
||||
import java.util.Date
|
||||
|
||||
@ -29,7 +29,7 @@ import java.util.{Date, Locale, UUID}
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.accountholder.AccountHolders
|
||||
import code.api.util.APIUtil.MessageDoc
|
||||
import code.api.util.{APIUtil, CallContext, ErrorMessages}
|
||||
import code.api.util._
|
||||
import code.api.v2_1_0._
|
||||
import code.bankconnectors._
|
||||
import code.branches.Branches.{Branch, BranchT}
|
||||
|
||||
@ -25,12 +25,13 @@ Berlin 13359, Germany
|
||||
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
import code.api.JSONFactoryGateway.PayloadOfJwtJSON
|
||||
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
|
||||
import code.api.cache.Caching
|
||||
import code.api.util.APIUtil.{MessageDoc, saveConnectorMetric, _}
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.{APIUtil, CallContext, ErrorMessages}
|
||||
import code.api.util._
|
||||
import code.api.v3_1_0.CardObjectJson
|
||||
import code.atms.Atms.AtmId
|
||||
import code.bankconnectors._
|
||||
@ -52,6 +53,7 @@ import net.liftweb.json.Extraction._
|
||||
import net.liftweb.json.JsonAST.JValue
|
||||
import net.liftweb.json.{Extraction, MappingException, parse}
|
||||
import net.liftweb.util.Helpers.{now, tryo}
|
||||
|
||||
import scala.collection.immutable.{List, Nil}
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
@ -62,6 +64,7 @@ import code.api.util.ExampleValue._
|
||||
import code.api.v1_2_1.AmountOfMoneyJsonV121
|
||||
import code.api.v2_1_0.{TransactionRequestBodyCommonJSON, TransactionRequestCommonBodyJSON}
|
||||
import code.context.UserAuthContextProvider
|
||||
import code.metadata.counterparties.CounterpartyTrait
|
||||
import code.users.Users
|
||||
import net.liftweb
|
||||
import net.liftweb.json
|
||||
@ -288,6 +291,42 @@ trait KafkaMappedConnector_vSept2018 extends Connector with KafkaHelper with Mdc
|
||||
|
||||
res
|
||||
}
|
||||
override def getAdapterInfoFuture(callContext: Option[CallContext]): Future[Box[(InboundAdapterInfoInternal, Option[CallContext])]] = {
|
||||
val req = OutboundGetAdapterInfo(DateWithSecondsExampleString)
|
||||
|
||||
logger.debug(s"Kafka getAdapterInfoFuture Req says: is: $req")
|
||||
|
||||
val future = for {
|
||||
res <- processToFuture[OutboundGetAdapterInfo](req) map {
|
||||
f =>
|
||||
try {
|
||||
f.extract[InboundAdapterInfo]
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
val received = liftweb.json.compactRender(f)
|
||||
val expected = SchemaFor[InboundAdapterInfo]().toString(false)
|
||||
val err = s"Extraction Failed: You received this ($received). We expected this ($expected)"
|
||||
sendOutboundAdapterError(err)
|
||||
throw new MappingException(err, e)
|
||||
}
|
||||
} map {
|
||||
x => x.data
|
||||
}
|
||||
} yield {
|
||||
Full(res)
|
||||
}
|
||||
|
||||
val res = future map {
|
||||
case Full(list) if (list.errorCode=="") =>
|
||||
Full(list, callContext)
|
||||
case Full(list) if (list.errorCode!="") =>
|
||||
Failure("INTERNAL-"+ list.errorCode+". + CoreBank-Status:"+ list.backendMessages)
|
||||
case _ =>
|
||||
Failure(ErrorMessages.UnknownError)
|
||||
}
|
||||
logger.debug(s"Kafka getAdapterInfoFuture says res is $res")
|
||||
res
|
||||
}
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.User",
|
||||
@ -871,6 +910,11 @@ trait KafkaMappedConnector_vSept2018 extends Connector with KafkaHelper with Mdc
|
||||
}
|
||||
}
|
||||
}("getBankAccount")
|
||||
|
||||
override def checkBankAccountExistsFuture(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]) =
|
||||
Future {
|
||||
checkBankAccountExists(bankId, accountId, callContext)
|
||||
}
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.coreBankAccounts",
|
||||
@ -1653,6 +1697,9 @@ trait KafkaMappedConnector_vSept2018 extends Connector with KafkaHelper with Mdc
|
||||
}
|
||||
}
|
||||
}("getCounterparties")
|
||||
override def getCounterpartiesFuture(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId, callContext: Option[CallContext] = None): OBPReturnType[Box[List[CounterpartyTrait]]] = Future {
|
||||
(getCounterparties(thisBankId, thisAccountId, viewId, callContext) map (i => i._1), callContext)
|
||||
}
|
||||
|
||||
messageDocs += MessageDoc(
|
||||
process = "obp.get.CounterpartyByCounterpartyId",
|
||||
|
||||
@ -4,11 +4,11 @@ package code.branches
|
||||
/* For branches */
|
||||
|
||||
// Need to import these one by one because in same package!
|
||||
import code.bankconnectors.OBPQueryParam
|
||||
import code.branches.Branches.{Branch, BranchId, BranchT}
|
||||
import code.api.util.OBPQueryParam
|
||||
import code.branches.Branches.{BranchId, BranchT}
|
||||
import code.common._
|
||||
import code.model.BankId
|
||||
import net.liftweb.common.{Box, Logger}
|
||||
import net.liftweb.common.Logger
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
object Branches extends SimpleInjector {
|
||||
@ -232,9 +232,6 @@ object Branches extends SimpleInjector {
|
||||
)
|
||||
|
||||
|
||||
import code.common.Routing
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
package code.branches
|
||||
|
||||
import code.bankconnectors._
|
||||
import code.api.util.{OBPLimit, OBPOffset, OBPQueryParam}
|
||||
import code.branches.Branches._
|
||||
import code.common._
|
||||
import code.model.BankId
|
||||
import code.util.{TwentyFourHourClockString, UUIDString}
|
||||
import net.liftweb.common.Logger
|
||||
import net.liftweb.mapper.{By, _}
|
||||
import net.liftweb.common.{Box, Logger}
|
||||
|
||||
object MappedBranchesProvider extends BranchesProvider {
|
||||
|
||||
|
||||
@ -34,6 +34,7 @@ trait ConsumersProvider {
|
||||
def updateConsumer(id: Long, key: Option[String], secret: Option[String], isActive: Option[Boolean], name: Option[String], appType: Option[AppType], description: Option[String], developerEmail: Option[String], redirectURL: Option[String], createdByUserId: Option[String]): Box[Consumer]
|
||||
def updateConsumerCallLimits(id: Long, perMinute: Option[String], perHour: Option[String], perDay: Option[String], perWeek: Option[String], perMonth: Option[String]): Future[Box[Consumer]]
|
||||
def getOrCreateConsumer(consumerId: Option[String], key: Option[String], secret: Option[String], isActive: Option[Boolean], name: Option[String], appType: Option[AppType], description: Option[String], developerEmail: Option[String], redirectURL: Option[String], createdByUserId: Option[String]): Box[Consumer]
|
||||
def populateMissingUUIDs(): Boolean
|
||||
}
|
||||
|
||||
|
||||
@ -50,6 +51,7 @@ class RemotedataConsumersCaseClasses {
|
||||
case class updateConsumer(id: Long, key: Option[String], secret: Option[String], isActive: Option[Boolean], name: Option[String], appType: Option[AppType], description: Option[String], developerEmail: Option[String], redirectURL: Option[String], createdByUserId: Option[String])
|
||||
case class updateConsumerCallLimits(id: Long, perMinute: Option[String], perHour: Option[String], perDay: Option[String], perWeek: Option[String], perMonth: Option[String])
|
||||
case class getOrCreateConsumer(consumerId: Option[String], key: Option[String], secret: Option[String], isActive: Option[Boolean], name: Option[String], appType: Option[AppType], description: Option[String], developerEmail: Option[String], redirectURL: Option[String], createdByUserId: Option[String])
|
||||
case class populateMissingUUIDs()
|
||||
}
|
||||
|
||||
object RemotedataConsumersCaseClasses extends RemotedataConsumersCaseClasses
|
||||
|
||||
@ -3,12 +3,11 @@ package code.customer
|
||||
import java.lang
|
||||
import java.util.Date
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import code.bankconnectors.OBPQueryParam
|
||||
import code.api.util.{APIUtil, OBPQueryParam}
|
||||
import code.model.{BankId, User}
|
||||
import code.remotedata.RemotedataCustomers
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.util.{Props, SimpleInjector}
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
import scala.collection.immutable.List
|
||||
import scala.concurrent.Future
|
||||
|
||||
@ -3,10 +3,9 @@ package code.customer
|
||||
import java.lang
|
||||
import java.util.Date
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import code.bankconnectors._
|
||||
import code.api.util._
|
||||
import code.model.{BankId, User}
|
||||
import code.usercustomerlinks.{MappedUserCustomerLink, MappedUserCustomerLinkProvider, UserCustomerLink}
|
||||
import code.usercustomerlinks.{MappedUserCustomerLinkProvider, UserCustomerLink}
|
||||
import code.users.Users
|
||||
import code.util.Helper.MdcLoggable
|
||||
import code.util.{MappedUUID, UUIDString}
|
||||
@ -16,8 +15,8 @@ import net.liftweb.mapper.{By, _}
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
|
||||
import scala.collection.immutable.List
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
|
||||
|
||||
object MappedCustomerProvider extends CustomerProvider with MdcLoggable {
|
||||
@ -186,7 +185,7 @@ object MappedCustomerProvider extends CustomerProvider with MdcLoggable {
|
||||
for {
|
||||
customer <- MappedCustomer.findAll(NullRef(MappedCustomer.mCustomerId))
|
||||
} yield {
|
||||
customer.mTitle(APIUtil.generateUUID()).save()
|
||||
customer.mCustomerId(APIUtil.generateUUID()).save()
|
||||
}
|
||||
}.forall(_ == true)
|
||||
|
||||
|
||||
@ -35,13 +35,13 @@ trait KafkaHelper extends ObpActorInit with MdcLoggable {
|
||||
process(mapRequest)
|
||||
}
|
||||
|
||||
def process (request: Map[String, String]): JValue ={
|
||||
extractFuture(actor ? request)
|
||||
}
|
||||
def process (request: Map[String, String]): JValue = getValueFromFuture(
|
||||
(actor ? request).mapTo[JValue]
|
||||
)
|
||||
|
||||
def processToBox[T](request: T): Box[JValue] = {
|
||||
extractFutureToBox(actor ? request)
|
||||
}
|
||||
def processToBox[T](request: T): Box[JValue] = getValueFromFuture(
|
||||
(actor ? request).mapTo[Box[JValue]]
|
||||
)
|
||||
|
||||
def processToFuture[T](request: T): Future[JValue] = {
|
||||
(actor ? request).mapTo[JValue]
|
||||
|
||||
@ -45,7 +45,7 @@ object NorthSideConsumer {
|
||||
Map[String, String](
|
||||
ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> brokers,
|
||||
ConsumerConfig.GROUP_ID_CONFIG -> group,
|
||||
ConsumerConfig.AUTO_OFFSET_RESET_CONFIG -> KafkaConsumer.autoOffsetResetConfig,
|
||||
ConsumerConfig.AUTO_OFFSET_RESET_CONFIG -> OBPKafkaConsumer.autoOffsetResetConfig,
|
||||
ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> keyDeserealizer,
|
||||
ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG -> valueDeserealizer,
|
||||
"security.protocol" -> "SSL",
|
||||
@ -58,7 +58,7 @@ object NorthSideConsumer {
|
||||
Map[String, String](
|
||||
ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> brokers,
|
||||
ConsumerConfig.GROUP_ID_CONFIG -> group,
|
||||
ConsumerConfig.AUTO_OFFSET_RESET_CONFIG -> KafkaConsumer.autoOffsetResetConfig,
|
||||
ConsumerConfig.AUTO_OFFSET_RESET_CONFIG -> OBPKafkaConsumer.autoOffsetResetConfig,
|
||||
ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> keyDeserealizer,
|
||||
ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG -> valueDeserealizer
|
||||
)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
package code.kafka
|
||||
|
||||
object KafkaConsumer extends KafkaConfig {
|
||||
object OBPKafkaConsumer extends KafkaConfig {
|
||||
lazy val primaryConsumer = NorthSideConsumer(bootstrapServers, "", groupId + "-north.side.consumer", new NorthSideConsumerMessageProcessor())
|
||||
}
|
||||
@ -2,11 +2,10 @@ package code.metrics
|
||||
|
||||
import java.util.{Calendar, Date}
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import code.bankconnectors.OBPQueryParam
|
||||
import code.api.util.{APIUtil, OBPQueryParam}
|
||||
import code.remotedata.RemotedataMetrics
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.util.{Props, SimpleInjector}
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
|
||||
@ -2,8 +2,8 @@ package code.metrics
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import code.bankconnectors._
|
||||
import code.util.{MappedUUID}
|
||||
import code.api.util._
|
||||
import code.util.MappedUUID
|
||||
import net.liftweb.mapper._
|
||||
|
||||
object ConnectorMetrics extends ConnectorMetricsProvider {
|
||||
|
||||
@ -2,10 +2,9 @@ package code.metrics
|
||||
|
||||
import java.util.{Calendar, Date}
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import code.bankconnectors.OBPQueryParam
|
||||
import code.api.util.{APIUtil, OBPQueryParam}
|
||||
import code.remotedata.RemotedataConnectorMetrics
|
||||
import net.liftweb.util.{Props, SimpleInjector}
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
object ConnectorMetricsProvider extends SimpleInjector {
|
||||
|
||||
|
||||
@ -2,12 +2,10 @@ package code.metrics
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import code.bankconnectors._
|
||||
import code.api.util._
|
||||
import code.search.elasticsearchMetrics
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.mapper._
|
||||
import net.liftweb.util.Props
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
|
||||
@ -1,19 +1,18 @@
|
||||
package code.metrics
|
||||
|
||||
import java.sql.{PreparedStatement, Time, Timestamp}
|
||||
import java.sql.{PreparedStatement, Timestamp}
|
||||
import java.util.Date
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.bankconnectors.{OBPImplementedByPartialFunction, _}
|
||||
import code.api.util._
|
||||
import code.util.Helper.MdcLoggable
|
||||
import code.util.{MappedUUID, UUIDString}
|
||||
import net.liftweb.common.{Box, Empty, Full}
|
||||
import net.liftweb.common.{Box, Full}
|
||||
import net.liftweb.mapper.{Index, _}
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.collection.immutable.List
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
|
||||
object MappedMetrics extends APIMetrics with MdcLoggable{
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,25 +16,20 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
package code.metrics
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import code.bankconnectors.OBPQueryParam
|
||||
import code.api.util.OBPQueryParam
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.mongodb.record.field.{DateField, ObjectIdPk}
|
||||
import net.liftweb.mongodb.record.{MongoMetaRecord, MongoRecord}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,28 +16,23 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
package code.model
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.accountholder.AccountHolders
|
||||
import code.api.util.APIUtil.hasEntitlement
|
||||
import code.api.util.{APIUtil, ApiRole, CallContext, ErrorMessages}
|
||||
import code.bankconnectors.{Connector, OBPQueryParam}
|
||||
import code.api.util.APIUtil.unboxFullOrFail
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.{APIUtil, CallContext, ErrorMessages, OBPQueryParam}
|
||||
import code.bankconnectors.Connector
|
||||
import code.customer.Customer
|
||||
import code.metadata.comments.Comments
|
||||
import code.metadata.counterparties.{Counterparties, CounterpartyTrait}
|
||||
@ -47,14 +42,14 @@ import code.metadata.transactionimages.TransactionImages
|
||||
import code.metadata.wheretags.WhereTags
|
||||
import code.util.Helper
|
||||
import code.util.Helper.MdcLoggable
|
||||
import code.views.{MapperViews, Views}
|
||||
import code.views.Views
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.json.JObject
|
||||
import net.liftweb.json.JsonAST.JArray
|
||||
import net.liftweb.json.JsonDSL._
|
||||
import net.liftweb.util.Props
|
||||
|
||||
import scala.collection.immutable.{List, Set}
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
import scala.math.BigDecimal
|
||||
|
||||
@ -524,6 +519,21 @@ trait BankAccount extends MdcLoggable {
|
||||
else
|
||||
viewNotAllowed(view)
|
||||
}
|
||||
final def moderatedTransactionFuture(bankId: BankId, accountId: AccountId, transactionId: TransactionId, view: View, user: Box[User], callContext: Option[CallContext] = None) : Future[Box[(ModeratedTransaction, Option[CallContext])]] = {
|
||||
if(APIUtil.hasAccess(view, user))
|
||||
for{
|
||||
(transaction, callContext)<-Connector.connector.vend.getTransactionFuture(bankId, accountId, transactionId, callContext) map {
|
||||
x => (unboxFullOrFail(x._1, callContext, ConnectorEmptyResponse, 400), x._2)
|
||||
}
|
||||
} yield {
|
||||
view.moderateTransaction(transaction) match {
|
||||
case Full(m) => Full((m, callContext))
|
||||
case _ => Failure("Server error - moderateTransactionsWithSameAccount")
|
||||
}
|
||||
}
|
||||
else
|
||||
Future(viewNotAllowed(view))
|
||||
}
|
||||
|
||||
/*
|
||||
end views
|
||||
@ -538,6 +548,21 @@ trait BankAccount extends MdcLoggable {
|
||||
} yield (moderated, callContext)
|
||||
}
|
||||
else viewNotAllowed(view)
|
||||
}
|
||||
final def getModeratedTransactionsFuture(user : Box[User], view : View, callContext: Option[CallContext], queryParams: OBPQueryParam* ): Future[Box[(List[ModeratedTransaction],Option[CallContext])]] = {
|
||||
if(APIUtil.hasAccess(view, user)) {
|
||||
for {
|
||||
(transactions, callContext) <- Connector.connector.vend.getTransactionsFuture(bankId, accountId, callContext, queryParams: _*) map {
|
||||
x => (unboxFullOrFail(x._1, callContext, ConnectorEmptyResponse, 400), x._2)
|
||||
}
|
||||
} yield {
|
||||
view.moderateTransactionsWithSameAccount(transactions) match {
|
||||
case Full(m) => Full((m, callContext))
|
||||
case _ => Failure("Server error - moderateTransactionsWithSameAccount")
|
||||
}
|
||||
}
|
||||
}
|
||||
else Future(viewNotAllowed(view))
|
||||
}
|
||||
|
||||
// TODO We should extract params (and their defaults) prior to this call, so this whole function can be cached.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,19 +16,14 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
package code.model
|
||||
import java.util.Date
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,19 +16,14 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
package code.model
|
||||
import java.util.Date
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,19 +16,14 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
package code.model
|
||||
import java.util.{Date, UUID}
|
||||
|
||||
@ -46,6 +41,7 @@ import net.liftweb.mapper.{LongKeyedMetaMapper, _}
|
||||
import net.liftweb.util.Helpers.{now, _}
|
||||
import net.liftweb.util.{FieldError, Helpers, Props}
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.github.dwickern.macros.NameOf
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
@ -71,7 +67,7 @@ object TokenType {
|
||||
}
|
||||
|
||||
|
||||
object MappedConsumersProvider extends ConsumersProvider {
|
||||
object MappedConsumersProvider extends ConsumersProvider with MdcLoggable {
|
||||
|
||||
override def getConsumerByPrimaryIdFuture(id: Long): Future[Box[Consumer]] = {
|
||||
Future(
|
||||
@ -339,6 +335,15 @@ object MappedConsumersProvider extends ConsumersProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override def populateMissingUUIDs(): Boolean = {
|
||||
logger.warn("Executed script: MappedConsumersProvider." + NameOf.nameOf(populateMissingUUIDs))
|
||||
for {
|
||||
consumer <- Consumer.findAll(NullRef(Consumer.consumerId))
|
||||
} yield {
|
||||
consumer.consumerId(APIUtil.generateUUID()).save()
|
||||
}
|
||||
}.forall(_ == true)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,19 +16,14 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
package code.model
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,19 +16,14 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
package code.model
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2018, TESOBE Ltd
|
||||
Copyright (C) 2011-2018, TESOBE Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
@ -16,35 +16,30 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE Ltd
|
||||
Osloerstrasse 16/17
|
||||
TESOBE Ltd.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Stefan Bethge : stefan AT tesobe DOT com
|
||||
Everett Sochowski : everett AT tesobe DOT com
|
||||
Ayoub Benali: ayoub AT tesobe DOT com
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
package code.model.dataAccess
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import code.bankconnectors.{OBPLimit, OBPOffset, OBPOrdering, _}
|
||||
import code.api.util._
|
||||
import code.model._
|
||||
import code.util.Helper
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.mongodb.QueryBuilder
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.mongodb.BsonDSL._
|
||||
import net.liftweb.mongodb.{Limit, Skip}
|
||||
import net.liftweb.mongodb.record.{MongoMetaRecord, MongoRecord}
|
||||
import net.liftweb.mongodb.record.field.{DateField, ObjectIdPk, ObjectIdRefField}
|
||||
import net.liftweb.mongodb.record.{MongoMetaRecord, MongoRecord}
|
||||
import net.liftweb.mongodb.{Limit, Skip}
|
||||
import net.liftweb.record.field.{DecimalField, StringField}
|
||||
import code.util.Helper.MdcLoggable
|
||||
|
||||
import scala.collection.immutable.List
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user