feature/(resource-docs): include technology field in API documentation

Add `technology` field to `implemented_by` JSON to indicate whether an endpoint is implemented using lift or http4s. The field is included only when `includeTechnologyInResponse` is true, which is set for API versions 6.0.0 and 7.0.0. This helps API consumers understand the underlying implementation technology.

Update tests to verify technology field presence/absence based on API version. Also improve test setup robustness by making user and account creation idempotent, and update build dependencies to support http4s and pekko.
This commit is contained in:
hongwei 2026-01-28 14:01:29 +01:00
parent a81e208cb1
commit 558ee1d404
12 changed files with 261 additions and 67 deletions

View File

@ -17,11 +17,18 @@ ThisBuild / semanticdbVersion := "4.13.9"
// Fix dependency conflicts
ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always
ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-java8-compat" % VersionScheme.Always
lazy val liftVersion = "3.5.0"
lazy val akkaVersion = "2.5.32"
lazy val jettyVersion = "9.4.50.v20221201"
lazy val avroVersion = "1.8.2"
lazy val pekkoVersion = "1.4.0"
lazy val pekkoHttpVersion = "1.3.0"
lazy val http4sVersion = "0.23.30"
lazy val catsEffectVersion = "3.5.7"
lazy val ip4sVersion = "3.7.0"
lazy val jakartaMailVersion = "2.0.1"
lazy val commonSettings = Seq(
resolvers ++= Seq(
@ -42,8 +49,8 @@ lazy val obpCommons = (project in file("obp-commons"))
"net.liftweb" %% "lift-util" % liftVersion,
"net.liftweb" %% "lift-mapper" % liftVersion,
"org.scala-lang" % "scala-reflect" % "2.12.20",
"org.scalatest" %% "scalatest" % "3.2.15" % Test,
"org.scalactic" %% "scalactic" % "3.2.15",
"org.scalatest" %% "scalatest" % "3.0.9" % Test,
"org.scalactic" %% "scalactic" % "3.0.9",
"net.liftweb" %% "lift-json" % liftVersion,
"com.alibaba" % "transmittable-thread-local" % "2.11.5",
"org.apache.commons" % "commons-lang3" % "3.12.0",
@ -96,6 +103,20 @@ lazy val obpApi = (project in file("obp-api"))
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
"com.typesafe.akka" %% "akka-http-core" % "10.1.6",
// Pekko (ActorSystem + Pekko HTTP used by OBP runtime components)
"org.apache.pekko" %% "pekko-actor" % pekkoVersion,
"org.apache.pekko" %% "pekko-remote" % pekkoVersion,
"org.apache.pekko" %% "pekko-slf4j" % pekkoVersion,
"org.apache.pekko" %% "pekko-stream" % pekkoVersion,
"org.apache.pekko" %% "pekko-http" % pekkoHttpVersion,
// http4s (v7.0.0 experimental stack)
"org.typelevel" %% "cats-effect" % catsEffectVersion,
"com.comcast" %% "ip4s-core" % ip4sVersion,
"org.http4s" %% "http4s-core" % http4sVersion,
"org.http4s" %% "http4s-dsl" % http4sVersion,
"org.http4s" %% "http4s-ember-server" % http4sVersion,
// Avro
"com.sksamuel.avro4s" %% "avro4s-core" % avroVersion,
@ -165,6 +186,9 @@ lazy val obpApi = (project in file("obp-api"))
"com.rabbitmq" % "amqp-client" % "5.22.0",
"net.liftmodules" %% "amqp_3.1" % "1.5.0",
// Blockchain (Ethereum raw transaction decoding)
"org.web3j" % "core" % "4.14.0",
// Elasticsearch
"org.elasticsearch" % "elasticsearch" % "8.14.0",
"com.sksamuel.elastic4s" %% "elastic4s-client-esjava" % "8.5.2",
@ -175,6 +199,7 @@ lazy val obpApi = (project in file("obp-api"))
// Utilities
"cglib" % "cglib" % "3.3.0",
"com.sun.activation" % "jakarta.activation" % "1.2.2",
"com.sun.mail" % "jakarta.mail" % jakartaMailVersion,
"com.nulab-inc" % "zxcvbn" % "1.9.0",
// Testing - temporarily disabled due to version incompatibility
@ -192,7 +217,7 @@ lazy val obpApi = (project in file("obp-api"))
// Test dependencies
"junit" % "junit" % "4.13.2" % Test,
"org.scalatest" %% "scalatest" % "3.2.15" % Test,
"org.scalatest" %% "scalatest" % "3.0.9" % Test,
"org.seleniumhq.selenium" % "htmlunit-driver" % "2.36.0" % Test,
"org.testcontainers" % "rabbitmq" % "1.20.3" % Test
)

View File

@ -149,6 +149,7 @@ object ResourceDocs300 extends OBPRestHelper with ResourceDocsAPIMethods with Md
object ResourceDocs600 extends OBPRestHelper with ResourceDocsAPIMethods with MdcLoggable {
val version: ApiVersion = ApiVersion.v6_0_0
val versionStatus = ApiVersionStatus.BLEEDING_EDGE.toString
override def includeTechnologyInResponse: Boolean = true
val routes: Seq[OBPEndpoint] = List(
ImplementationsResourceDocs.getResourceDocsObpV400,
ImplementationsResourceDocs.getResourceDocsSwagger,
@ -206,7 +207,7 @@ object ResourceDocs300 extends OBPRestHelper with ResourceDocsAPIMethods with Md
case _ if (apiCollectionIdParam.isDefined) =>
val operationIds = MappedApiCollectionEndpointsProvider.getApiCollectionEndpoints(apiCollectionIdParam.getOrElse("")).map(_.operationId).map(getObpFormatOperationId)
val resourceDocs = ResourceDoc.getResourceDocs(operationIds)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale, includeTechnology = includeTechnologyInResponse)
resourceDocsJson.resource_docs
case _ =>
contentParam match {

View File

@ -65,6 +65,8 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
// We add previous APIMethods so we have access to the Resource Docs
self: OBPRestHelper =>
def includeTechnologyInResponse: Boolean = false
val ImplementationsResourceDocs = new Object() {
val localResourceDocs = ArrayBuffer[ResourceDoc]()
@ -346,7 +348,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
// Filter
val rdFiltered = ResourceDocsAPIMethodsUtil.filterResourceDocs(resourceDocs, resourceDocTags, partialFunctionNames)
// Format the data as json
JSONFactory1_4_0.createResourceDocsJson(rdFiltered, isVersion4OrHigher, locale)
JSONFactory1_4_0.createResourceDocsJson(rdFiltered, isVersion4OrHigher, locale, includeTechnology = includeTechnologyInResponse)
}
}
@ -500,7 +502,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
NewStyle.function.tryons(s"$UnknownError Can not prepare OBP resource docs.", 500, callContext) {
val operationIds = MappedApiCollectionEndpointsProvider.getApiCollectionEndpoints(apiCollectionIdParam.getOrElse("")).map(_.operationId).map(getObpFormatOperationId)
val resourceDocs = ResourceDoc.getResourceDocs(operationIds)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale, includeTechnology = includeTechnologyInResponse)
val resourceDocsJsonJValue = Full(resourceDocsJsonToJsonResponse(resourceDocsJson))
resourceDocsJsonJValue.map(successJsonResponse(_))
}
@ -709,7 +711,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
case _ if (apiCollectionIdParam.isDefined) =>
val operationIds = MappedApiCollectionEndpointsProvider.getApiCollectionEndpoints(apiCollectionIdParam.getOrElse("")).map(_.operationId).map(getObpFormatOperationId)
val resourceDocs = ResourceDoc.getResourceDocs(operationIds)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale, includeTechnology = includeTechnologyInResponse)
resourceDocsJson.resource_docs
case _ =>
contentParam match {
@ -903,7 +905,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
case _ if (apiCollectionIdParam.isDefined) =>
val operationIds = MappedApiCollectionEndpointsProvider.getApiCollectionEndpoints(apiCollectionIdParam.getOrElse("")).map(_.operationId).map(getObpFormatOperationId)
val resourceDocs = ResourceDoc.getResourceDocs(operationIds)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale)
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs, isVersion4OrHigher, locale, includeTechnology = includeTechnologyInResponse)
resourceDocsJson.resource_docs
case _ =>
contentParam match {
@ -1257,4 +1259,3 @@ so the caller must specify any required filtering by catalog explicitly.
}

View File

@ -1796,7 +1796,8 @@ object SwaggerDefinitionsJSON {
lazy val implementedByJson = ImplementedByJson(
version = "1_4_0",
function = "getBranches"
function = "getBranches",
technology = None
)
// Used to describe the OBP API calls for documentation and API discovery purposes
lazy val canCreateCustomerSwagger = CanCreateCustomer()

View File

@ -328,7 +328,8 @@ object JSONFactory1_4_0 extends MdcLoggable{
// Used to describe where an API call is implemented
case class ImplementedByJson (
version : String, // Short hand for the version e.g. "1_4_0" means Implementations1_4_0
function : String // The val / partial function that implements the call e.g. "getBranches"
function : String, // The val / partial function that implements the call e.g. "getBranches"
technology: Option[String] = None
)
case class ResourceDocMeta(
@ -525,11 +526,12 @@ object JSONFactory1_4_0 extends MdcLoggable{
locale: Option[String],// this will be in the cacheKey
resourceDocUpdatedTags: ResourceDoc,
isVersion4OrHigher:Boolean,// this will be in the cacheKey
includeTechnology: Boolean, // this will be in the cacheKey
urlParametersI18n:String ,
jsonRequestBodyFieldsI18n:String,
jsonResponseBodyFieldsI18n:String
): ResourceDocJson = {
val cacheKey = LOCALISED_RESOURCE_DOC_PREFIX + s"operationId:${operationId}-locale:$locale- isVersion4OrHigher:$isVersion4OrHigher".intern()
val cacheKey = LOCALISED_RESOURCE_DOC_PREFIX + s"operationId:${operationId}-locale:$locale- isVersion4OrHigher:$isVersion4OrHigher- includeTechnology:$includeTechnology".intern()
Caching.memoizeSyncWithImMemory(Some(cacheKey))(CREATE_LOCALISED_RESOURCE_DOC_JSON_TTL.seconds) {
val fieldsDescription =
if (resourceDocUpdatedTags.tags.toString.contains("Dynamic-Entity")
@ -564,6 +566,13 @@ object JSONFactory1_4_0 extends MdcLoggable{
val summary = resourceDocUpdatedTags.summary.replaceFirst("""\.(\s*)$""", "$1") // remove the ending dot in summary
val translatedSummary = I18NUtil.ResourceDocTranslation.translate(I18NResourceDocField.SUMMARY, resourceDocUpdatedTags.operationId, locale, summary)
val technology =
if (includeTechnology) {
Some(if (resourceDocUpdatedTags.http4sPartialFunction.isDefined) "http4s" else "lift")
} else {
None
}
val resourceDoc = ResourceDocJson(
operation_id = resourceDocUpdatedTags.operationId,
request_verb = resourceDocUpdatedTags.requestVerb,
@ -575,7 +584,11 @@ object JSONFactory1_4_0 extends MdcLoggable{
example_request_body = resourceDocUpdatedTags.exampleRequestBody,
success_response_body = resourceDocUpdatedTags.successResponseBody,
error_response_bodies = resourceDocUpdatedTags.errorResponseBodies,
implemented_by = ImplementedByJson(resourceDocUpdatedTags.implementedInApiVersion.fullyQualifiedVersion, resourceDocUpdatedTags.partialFunctionName), // was resourceDocUpdatedTags.implementedInApiVersion.noV
implemented_by = ImplementedByJson(
version = resourceDocUpdatedTags.implementedInApiVersion.fullyQualifiedVersion,
function = resourceDocUpdatedTags.partialFunctionName,
technology = technology
), // was resourceDocUpdatedTags.implementedInApiVersion.noV
tags = resourceDocUpdatedTags.tags.map(i => i.tag),
typed_request_body = createTypedBody(resourceDocUpdatedTags.exampleRequestBody),
typed_success_response_body = createTypedBody(resourceDocUpdatedTags.successResponseBody),
@ -592,7 +605,7 @@ object JSONFactory1_4_0 extends MdcLoggable{
}}
def createLocalisedResourceDocJson(rd: ResourceDoc, isVersion4OrHigher:Boolean, locale: Option[String], urlParametersI18n:String ,jsonRequestBodyFieldsI18n:String, jsonResponseBodyFieldsI18n:String) : ResourceDocJson = {
def createLocalisedResourceDocJson(rd: ResourceDoc, isVersion4OrHigher:Boolean, locale: Option[String], includeTechnology: Boolean, urlParametersI18n:String ,jsonRequestBodyFieldsI18n:String, jsonResponseBodyFieldsI18n:String) : ResourceDocJson = {
// We MUST recompute all resource doc values due to translation via Web UI props --> now need to wait $CREATE_LOCALISED_RESOURCE_DOC_JSON_TTL seconds
val userDefinedEndpointTags = getAllEndpointTagsBox(rd.operationId).map(endpointTag =>ResourceDocTag(endpointTag.tagName))
val resourceDocWithUserDefinedEndpointTags: ResourceDoc = rd.copy(tags = userDefinedEndpointTags++ rd.tags)
@ -602,6 +615,7 @@ object JSONFactory1_4_0 extends MdcLoggable{
locale: Option[String],
resourceDocWithUserDefinedEndpointTags,
isVersion4OrHigher: Boolean,
includeTechnology: Boolean,
urlParametersI18n: String,
jsonRequestBodyFieldsI18n: String,
jsonResponseBodyFieldsI18n: String
@ -609,7 +623,7 @@ object JSONFactory1_4_0 extends MdcLoggable{
}
def createResourceDocsJson(resourceDocList: List[ResourceDoc], isVersion4OrHigher:Boolean, locale: Option[String]) : ResourceDocsJson = {
def createResourceDocsJson(resourceDocList: List[ResourceDoc], isVersion4OrHigher:Boolean, locale: Option[String], includeTechnology: Boolean = false) : ResourceDocsJson = {
val urlParametersI18n = I18NUtil.ResourceDocTranslation.translate(
I18NResourceDocField.URL_PARAMETERS,
"resourceDocUrlParametersString_i180n",
@ -632,11 +646,11 @@ object JSONFactory1_4_0 extends MdcLoggable{
if(isVersion4OrHigher){
ResourceDocsJson(
resourceDocList.map(createLocalisedResourceDocJson(_,isVersion4OrHigher, locale, urlParametersI18n, jsonRequestBodyFields, jsonResponseBodyFields)),
resourceDocList.map(createLocalisedResourceDocJson(_,isVersion4OrHigher, locale, includeTechnology, urlParametersI18n, jsonRequestBodyFields, jsonResponseBodyFields)),
meta=Some(ResourceDocMeta(new Date(), resourceDocList.length))
)
} else {
ResourceDocsJson(resourceDocList.map(createLocalisedResourceDocJson(_,false, locale, urlParametersI18n, jsonRequestBodyFields, jsonResponseBodyFields)))
ResourceDocsJson(resourceDocList.map(createLocalisedResourceDocJson(_,false, locale, includeTechnology, urlParametersI18n, jsonRequestBodyFields, jsonResponseBodyFields)))
}
}

View File

@ -219,7 +219,7 @@ object Http4s700 {
requestedApiVersion <- Future(ApiVersionUtils.valueOf(requestedApiVersionString))
resourceDocs = ResourceDocs140.ImplementationsResourceDocs.getResourceDocsList(requestedApiVersion).getOrElse(Nil)
filteredDocs = ResourceDocsAPIMethodsUtil.filterResourceDocs(resourceDocs, tags, functions)
} yield JSONFactory1_4_0.createResourceDocsJson(filteredDocs, isVersion4OrHigher = true, localeParam)
} yield JSONFactory1_4_0.createResourceDocsJson(filteredDocs, isVersion4OrHigher = true, localeParam, includeTechnology = true)
}
}

View File

@ -0,0 +1,46 @@
package code.api.ResourceDocs1_4_0
import code.setup.{PropsReset, ServerSetup}
import net.liftweb.json.JsonAST.{JArray, JNothing, JNull, JString}
class ResourceDocsTechnologyTest extends ServerSetup with PropsReset {
feature("ResourceDocs implemented_by.technology") {
scenario("v6.0.0 resource-docs should include implemented_by.technology") {
setPropsValues("resource_docs_requires_role" -> "false")
val request = (baseRequest / "obp" / "v6.0.0" / "resource-docs" / "v6.0.0" / "obp").GET
val response = makeGetRequest(request)
response.code should equal(200)
(response.body \ "resource_docs") match {
case JArray(docs) =>
val technology = docs.head \ "implemented_by" \ "technology"
technology should equal(JString("lift"))
case _ =>
fail("Expected resource_docs field to be an array")
}
}
scenario("v5.0.0 resource-docs should not include implemented_by.technology") {
setPropsValues("resource_docs_requires_role" -> "false")
val request = (baseRequest / "obp" / "v5.0.0" / "resource-docs" / "v5.0.0" / "obp").GET
val response = makeGetRequest(request)
response.code should equal(200)
(response.body \ "resource_docs") match {
case JArray(docs) =>
val technology = docs.head \ "implemented_by" \ "technology"
technology match {
case JNothing | JNull => succeed
case _ => fail("Expected implemented_by.technology to be absent for v5.0.0 resource-docs")
}
case _ =>
fail("Expected resource_docs field to be an array")
}
}
}
}

View File

@ -68,7 +68,7 @@ class ResourceDocsTest extends ResourceDocsV140ServerSetup with PropsReset with
def stringToNodeSeq(html : String) : NodeSeq = {
val newHtmlString =scala.xml.XML.loadString("<div>" + html + "</div>").toString()
//Note: `parse` method: We much enclose the div, otherwise only the first element is returned.
Html5.parse(newHtmlString).head
Html5.parse(newHtmlString).headOption.getOrElse(NodeSeq.Empty)
}
@ -79,6 +79,7 @@ class ResourceDocsTest extends ResourceDocsV140ServerSetup with PropsReset with
And("We should get 200 and the response can be extract to case classes")
val responseDocs = responseGetObp.body.extract[ResourceDocsJson]
responseGetObp.code should equal(200)
responseDocs.resource_docs.head.implemented_by.technology shouldBe Some("lift")
//This should not throw any exceptions
responseDocs.resource_docs.map(responseDoc => stringToNodeSeq(responseDoc.description))
}
@ -97,6 +98,7 @@ class ResourceDocsTest extends ResourceDocsV140ServerSetup with PropsReset with
And("We should get 200 and the response can be extract to case classes")
val responseDocs = responseGetObp.body.extract[ResourceDocsJson]
responseGetObp.code should equal(200)
responseDocs.resource_docs.head.implemented_by.technology shouldBe None
//This should not throw any exceptions
responseDocs.resource_docs.map(responseDoc => stringToNodeSeq(responseDoc.description))
}

View File

@ -5,9 +5,9 @@ import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.usersJsonV400
import java.util.Date
import code.api.util.APIUtil.ResourceDoc
import code.api.util.{APIUtil, ExampleValue}
import code.api.util.CustomJsonFormats
import code.api.v1_4_0.JSONFactory1_4_0.ResourceDocJson
import code.api.v3_0_0.OBPAPI3_0_0
import code.setup.DefaultUsers
import net.liftweb.json.Extraction.decompose
import net.liftweb.json._
import org.everit.json.schema.loader.SchemaLoader
@ -44,7 +44,8 @@ case class AllCases(
jvalues: List[JValue]= List(APIUtil.defaultJValue)
)
class JSONFactory1_4_0Test extends V140ServerSetup with DefaultUsers {
class JSONFactory1_4_0Test extends code.setup.ServerSetup {
override implicit val formats: Formats = CustomJsonFormats.formats
feature("Test JSONFactory1_4_0") {
scenario("prepareDescription should work well, extract the parameters from URL") {
@ -116,7 +117,7 @@ class JSONFactory1_4_0Test extends V140ServerSetup with DefaultUsers {
scenario("createResourceDocJson should work well, no exception is good enough") {
val resourceDoc: ResourceDoc = OBPAPI3_0_0.allResourceDocs(5)
val result: ResourceDocJson = JSONFactory1_4_0.createLocalisedResourceDocJson(resourceDoc,false, None,
urlParameters, "JSON request body fields:", "JSON response body fields:")
includeTechnology = false, urlParameters, "JSON request body fields:", "JSON response body fields:")
}
scenario("createResourceDocsJson should work well, no exception is good enough") {
@ -124,6 +125,21 @@ class JSONFactory1_4_0Test extends V140ServerSetup with DefaultUsers {
val result = JSONFactory1_4_0.createResourceDocsJson(resourceDoc.toList, false, None)
}
scenario("Technology field should be None unless includeTechnology=true") {
val liftDoc: ResourceDoc = OBPAPI3_0_0.allResourceDocs(0)
val json1 = JSONFactory1_4_0.createLocalisedResourceDocJson(liftDoc, false, None, includeTechnology = false, urlParameters, "JSON request body fields:", "JSON response body fields:")
json1.implemented_by.technology shouldBe None
val json2 = JSONFactory1_4_0.createLocalisedResourceDocJson(liftDoc, false, None, includeTechnology = true, urlParameters, "JSON request body fields:", "JSON response body fields:")
json2.implemented_by.technology shouldBe Some("lift")
}
scenario("Technology field should be http4s when includeTechnology=true and doc is http4s") {
val http4sDoc: ResourceDoc = code.api.v7_0_0.Http4s700.resourceDocs.head
val json = JSONFactory1_4_0.createLocalisedResourceDocJson(http4sDoc, true, None, includeTechnology = true, urlParameters, "JSON request body fields:", "JSON response body fields:")
json.implemented_by.technology shouldBe Some("http4s")
}
scenario("createTypedBody should work well, no exception is good enough") {
val inputCaseClass = AllCases()
val result = JSONFactory1_4_0.createTypedBody(inputCaseClass)

View File

@ -246,8 +246,19 @@ class Http4s700RoutesTest extends ServerSetupWithTestData {
json match {
case JObject(fields) =>
toFieldMap(fields).get("resource_docs") match {
case Some(JArray(_)) =>
succeed
case Some(JArray(resourceDocs)) =>
resourceDocs.exists {
case JObject(rdFields) =>
toFieldMap(rdFields).get("implemented_by") match {
case Some(JObject(implFields)) =>
toFieldMap(implFields).get("technology") match {
case Some(JString(value)) => value == "http4s"
case _ => false
}
case _ => false
}
case _ => false
} shouldBe true
case _ =>
fail("Expected resource_docs field to be an array")
}

View File

@ -106,28 +106,79 @@ trait DefaultUsers {
// Create resource user, need provider
val defaultProvider = Constant.HostName
private def getOrCreateResourceUser(
provider: String,
providerId: String,
createdByConsentId: Option[String],
name: String,
email: String,
userId: Option[String],
company: String
): ResourceUser = {
UserX.findByProviderId(provider = provider, idGivenByProvider = providerId)
.map(_.asInstanceOf[ResourceUser])
.getOrElse {
try {
UserX
.createResourceUser(
provider = provider,
providerId = Some(providerId),
createdByConsentId = createdByConsentId,
name = Some(name),
email = Some(email),
userId = userId,
company = Some(company)
)
.openOrThrowException(attemptedToOpenAnEmptyBox)
} catch {
case e: Throwable =>
UserX.findByProviderId(provider = provider, idGivenByProvider = providerId)
.map(_.asInstanceOf[ResourceUser])
.getOrElse(throw e)
}
}
}
// create some resource user for test purposes
lazy val resourceUser1 = UserX.findByProviderId(provider = defaultProvider, idGivenByProvider= resourceUser1Name).map(_.asInstanceOf[ResourceUser])
.getOrElse(UserX.createResourceUser(provider = defaultProvider, providerId= Some(resourceUser1Name), createdByConsentId= None, name= Some(resourceUser1Name),
email= Some("resourceUser1@123.com"), userId= userId1, company = Some("Tesobe GmbH"))
.openOrThrowException(attemptedToOpenAnEmptyBox)
)
lazy val resourceUser1 = getOrCreateResourceUser(
provider = defaultProvider,
providerId = resourceUser1Name,
createdByConsentId = None,
name = resourceUser1Name,
email = "resourceUser1@123.com",
userId = userId1,
company = "Tesobe GmbH"
)
lazy val resourceUser2 = UserX.findByProviderId(provider = defaultProvider, idGivenByProvider= resourceUser2Name).map(_.asInstanceOf[ResourceUser])
.getOrElse(UserX.createResourceUser(provider = defaultProvider, providerId= Some(resourceUser2Name), createdByConsentId= None,
name= Some(resourceUser2Name),email= Some("resourceUser2@123.com"), userId= userId2, company = Some("Tesobe GmbH"))
.openOrThrowException(attemptedToOpenAnEmptyBox)
)
lazy val resourceUser2 = getOrCreateResourceUser(
provider = defaultProvider,
providerId = resourceUser2Name,
createdByConsentId = None,
name = resourceUser2Name,
email = "resourceUser2@123.com",
userId = userId2,
company = "Tesobe GmbH"
)
lazy val resourceUser3 = UserX.findByProviderId(provider = defaultProvider, idGivenByProvider= resourceUser3Name).map(_.asInstanceOf[ResourceUser])
.getOrElse(UserX.createResourceUser(provider = defaultProvider, providerId= Some(resourceUser3Name), createdByConsentId= None,
name= Some(resourceUser3Name),email= Some("resourceUser3@123.com"), userId= userId3, company = Some("Tesobe GmbH"))
.openOrThrowException(attemptedToOpenAnEmptyBox))
lazy val resourceUser3 = getOrCreateResourceUser(
provider = defaultProvider,
providerId = resourceUser3Name,
createdByConsentId = None,
name = resourceUser3Name,
email = "resourceUser3@123.com",
userId = userId3,
company = "Tesobe GmbH"
)
lazy val resourceUser4 = UserX.findByProviderId(provider = defaultProvider, idGivenByProvider= resourceUser4Name).map(_.asInstanceOf[ResourceUser])
.getOrElse(UserX.createResourceUser(provider = GatewayLogin.gateway, providerId = Some(resourceUser4Name), createdByConsentId= Some("simonr"), name= Some(resourceUser4Name),
email= Some("resourceUser4@123.com"), userId=userId4, company = Some("Tesobe GmbH"))
.openOrThrowException(attemptedToOpenAnEmptyBox))
lazy val resourceUser4 = getOrCreateResourceUser(
provider = GatewayLogin.gateway,
providerId = resourceUser4Name,
createdByConsentId = Some("simonr"),
name = resourceUser4Name,
email = "resourceUser4@123.com",
userId = userId4,
company = "Tesobe GmbH"
)
// create the tokens in database, we only need token-key and token-secretAllCases
lazy val testToken1 = Tokens.tokens.vend.createToken(

View File

@ -65,30 +65,56 @@ trait LocalMappedConnectorTestSetup extends TestConnectorSetupWithStandardPermis
}
override protected def createAccount(bankId: BankId, accountId : AccountId, currency : String) : BankAccount = {
BankAccountRouting.create
.BankId(bankId.value)
.AccountId(accountId.value)
.AccountRoutingScheme(AccountRoutingScheme.IBAN.toString)
.AccountRoutingAddress(iban4j.Iban.random().toString())
.saveMe
BankAccountRouting.create
.BankId(bankId.value)
.AccountId(accountId.value)
.AccountRoutingScheme("AccountId")
.AccountRoutingAddress(accountId.value)
.saveMe
MappedBankAccount.create
.bank(bankId.value)
.theAccountId(accountId.value)
.accountCurrency(currency.toUpperCase)
.accountBalance(900000000)
.holder(randomString(4))
.accountLastUpdate(now)
.accountName(randomString(4))
.accountNumber(randomString(4))
.accountLabel(randomString(4))
.mBranchId(randomString(4))
.saveMe
def getOrCreateRouting(scheme: String, address: String): Unit = {
val existing = BankAccountRouting.find(
By(BankAccountRouting.BankId, bankId.value),
By(BankAccountRouting.AccountId, accountId.value),
By(BankAccountRouting.AccountRoutingScheme, scheme)
)
if (!existing.isDefined) {
try {
BankAccountRouting.create
.BankId(bankId.value)
.AccountId(accountId.value)
.AccountRoutingScheme(scheme)
.AccountRoutingAddress(address)
.saveMe
} catch {
case _: Throwable =>
}
}
()
}
getOrCreateRouting(AccountRoutingScheme.IBAN.toString, iban4j.Iban.random().toString())
getOrCreateRouting("AccountId", accountId.value)
val existingAccount = MappedBankAccount.find(
By(MappedBankAccount.bank, bankId.value),
By(MappedBankAccount.theAccountId, accountId.value)
)
existingAccount.openOr {
try {
MappedBankAccount.create
.bank(bankId.value)
.theAccountId(accountId.value)
.accountCurrency(currency.toUpperCase)
.accountBalance(900000000)
.holder(randomString(4))
.accountLastUpdate(now)
.accountName(randomString(4))
.accountNumber(randomString(4))
.accountLabel(randomString(4))
.mBranchId(randomString(4))
.saveMe
} catch {
case _: Throwable =>
MappedBankAccount.find(
By(MappedBankAccount.bank, bankId.value),
By(MappedBankAccount.theAccountId, accountId.value)
).openOrThrowException(attemptedToOpenAnEmptyBox)
}
}
}
override protected def updateAccountCurrency(bankId: BankId, accountId : AccountId, currency : String) : BankAccount = {