mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 07:56:48 +00:00
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:
parent
a81e208cb1
commit
558ee1d404
31
build.sbt
31
build.sbt
@ -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",
|
||||
@ -95,6 +102,20 @@ lazy val obpApi = (project in file("obp-api"))
|
||||
"com.typesafe.akka" %% "akka-remote" % akkaVersion,
|
||||
"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,
|
||||
@ -164,6 +185,9 @@ lazy val obpApi = (project in file("obp-api"))
|
||||
// RabbitMQ
|
||||
"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",
|
||||
@ -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
|
||||
)
|
||||
|
||||
@ -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 {
|
||||
@ -247,4 +248,4 @@ object ResourceDocs300 extends OBPRestHelper with ResourceDocsAPIMethods with Md
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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 = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user