Merge remote-tracking branch 'Simon/develop' into develop

This commit is contained in:
hongwei 2023-01-26 14:41:41 +01:00
commit 4a21b9f64e
8 changed files with 209 additions and 31 deletions

View File

@ -93,7 +93,7 @@ import code.metadata.tags.MappedTag
import code.metadata.transactionimages.MappedTransactionImage
import code.metadata.wheretags.MappedWhereTag
import code.methodrouting.MethodRouting
import code.metrics.{MappedConnectorMetric, MappedMetric, MetricsArchive}
import code.metrics.{MappedConnectorMetric, MappedMetric, MetricArchive}
import code.migration.MigrationScriptLog
import code.model.dataAccess._
import code.model.dataAccess.internalMapping.AccountIdMapping
@ -955,7 +955,7 @@ object ToSchemify {
MappedTransactionRequest,
TransactionRequestAttribute,
MappedMetric,
MetricsArchive,
MetricArchive,
MapperAccountHolders,
MappedEntitlement,
MappedConnectorMetric,

View File

@ -1802,7 +1802,7 @@ object ExampleValue {
lazy val canSeeBankRoutingAddressExample = ConnectorField(booleanTrue,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_bank_routing_address", canSeeBankRoutingAddressExample)
lazy val usersExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
lazy val usersExample = ConnectorField("user list", "Please refer to the user object.")
glossaryItems += makeGlossaryItem("users", usersExample)
lazy val staffNameExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)

View File

@ -26,6 +26,8 @@ import java.util.regex.Pattern
import com.openbankproject.commons.model.enums.LanguageParam
import java.lang.reflect.Field
object JSONFactory1_4_0 extends MdcLoggable{
implicit def formats: Formats = CustomJsonFormats.formats
case class PostCustomerJson(
@ -450,7 +452,7 @@ object JSONFactory1_4_0 extends MdcLoggable{
def boldIfMandatory() = {
optionalTypeFields.exists(i => i._1 == parameter && i._2 == false) match {
case true =>
s"***$parameter**"
s"**$parameter**"
case false =>
s"$parameter"
}
@ -460,33 +462,55 @@ object JSONFactory1_4_0 extends MdcLoggable{
} else {
s"""
|
|* [${boldIfMandatory()}](/glossary#$glossaryItemTitle): $exampleFieldValue
|[${boldIfMandatory()}](/glossary#$glossaryItemTitle): $exampleFieldValue
|
|""".stripMargin
}
}
def prepareJsonFieldDescription(jsonBody: scala.Product, jsonType: String): String = {
jsonBody.productIterator
val (jsonBodyJValue: json.JValue, optionalTypeFields) = jsonBody match {
case JvalueCaseClass(jValue) =>
val types = Nil
(jValue, types)
case _ =>
val types = jsonBody.getClass()
.getDeclaredFields().toList
.map(f => (f.getName(), f.getType().getCanonicalName().contains("Option")))
(decompose(jsonBody), types)
def getAllFields(jsonBody: scala.Product): List[Field] = {
def loopAllFields(rootFields: List[Field]) = {
val fields = for {
field <- jsonBody.productIterator.toList if (field.isInstanceOf[scala.Product] && field != jsonBody)
fields = getAllFields(field.asInstanceOf[scala.Product])
} yield
fields
(rootFields ++ fields.flatten).toSet.toList
}
//The root level is a list: eg: List[Users]
if(jsonBody.isInstanceOf[List[Any]] && jsonBody.productIterator.toList.nonEmpty){
val rootFields: List[Field] = jsonBody.productIterator.toSet.head.getClass.getDeclaredFields.toList
loopAllFields(rootFields)
}else {
jsonBody match {
case JvalueCaseClass(jValue) =>
val types = Nil
types
case _ =>
val rootFields: List[Field] = jsonBody.getClass().getDeclaredFields().toSet.toList
loopAllFields(rootFields)
}
}
}
def checkFieldOption(jsonBody: scala.Product, rootFields: List[Field]) = {
val types = rootFields.map(f => (f.getName(), f.getType().getCanonicalName().contains("Option")))
(decompose(jsonBody), types)
}
def prepareJsonFieldDescription(jsonBody: scala.Product, jsonType: String): String = {
val allFields = getAllFields(jsonBody)
val (jsonBodyJValue: json.JValue, allFieldsAndOptionStatus) = checkFieldOption(jsonBody, allFields)
// Group by is mandatory criteria and sort those 2 groups by name of the field
val jsonBodyFieldsOptional = JsonUtils.collectFieldNames(jsonBodyJValue).keySet.toList
.filter(x => optionalTypeFields.exists(i => i._1 == x && i._2 == true)).sorted
.filter(x => allFieldsAndOptionStatus.exists(i => i._1 == x && i._2 == true)).sorted
val jsonBodyFieldsMandatory = JsonUtils.collectFieldNames(jsonBodyJValue).keySet.toList
.filter(x => optionalTypeFields.exists(i => i._1 == x && i._2 == false)).sorted
.filter(x => allFieldsAndOptionStatus.exists(i => i._1 == x && i._2 == false)).sorted
val jsonBodyFields = jsonBodyFieldsMandatory ::: jsonBodyFieldsOptional
val jsonFieldsDescription = jsonBodyFields.map(i => prepareDescription(i, optionalTypeFields))
val jsonFieldsDescription = jsonBodyFields.map(i => prepareDescription(i, allFieldsAndOptionStatus))
val jsonTitleType = if (jsonType.contains("request")) "\n\n\n**JSON request body fields:**\n\n" else "\n\n\n**JSON response body fields:**\n\n"
@ -527,6 +551,7 @@ object JSONFactory1_4_0 extends MdcLoggable{
""
}
//3rd: get the fields description from the response body:
//response body can be a nest class, need to loop all the fields.
val responseFieldsDescription = prepareJsonFieldDescription(resourceDocUpdatedTags.successResponseBody,"response")
urlParametersDescription ++ exampleRequestBodyFieldsDescription ++ responseFieldsDescription
}

View File

@ -65,7 +65,7 @@ object MappedMetrics extends APIMetrics with MdcLoggable{
metric.save
}
override def saveMetricsArchive(primaryKey: Long, userId: String, url: String, date: Date, duration: Long, userName: String, appName: String, developerEmail: String, consumerId: String, implementedByPartialFunction: String, implementedInVersion: String, verb: String, httpCode: Option[Int], correlationId: String): Unit = {
val metric = MetricsArchive.find(By(MetricsArchive.id, primaryKey)).getOrElse(MetricsArchive.create)
val metric = MetricArchive.find(By(MetricArchive.id, primaryKey)).getOrElse(MetricArchive.create)
metric
.metricId(primaryKey)
.userId(userId)
@ -548,8 +548,8 @@ object MappedMetric extends MappedMetric with LongKeyedMetaMapper[MappedMetric]
}
class MetricsArchive extends APIMetric with LongKeyedMapper[MetricsArchive] with IdPK {
override def getSingleton = MetricsArchive
class MetricArchive extends APIMetric with LongKeyedMapper[MetricArchive] with IdPK {
override def getSingleton = MetricArchive
object metricId extends MappedLong(this)
object userId extends UUIDString(this)
@ -589,7 +589,7 @@ class MetricsArchive extends APIMetric with LongKeyedMapper[MetricsArchive] with
override def getHttpCode(): Int = httpCode.get
override def getCorrelationId(): String = correlationId.get
}
object MetricsArchive extends MetricsArchive with LongKeyedMetaMapper[MetricsArchive] {
object MetricArchive extends MetricArchive with LongKeyedMetaMapper[MetricArchive] {
override def dbIndexes =
Index(userId) :: Index(consumerId) :: Index(url) :: Index(date) :: Index(userName) ::
Index(appName) :: Index(developerEmail) :: super.dbIndexes

View File

@ -5,7 +5,7 @@ import java.util.{Calendar, Date}
import code.actorsystem.ObpLookupSystem
import code.api.util.{APIUtil, OBPToDate}
import code.metrics.{APIMetric, APIMetrics, MappedMetric, MetricsArchive}
import code.metrics.{APIMetric, APIMetrics, MappedMetric, MetricArchive}
import code.util.Helper.MdcLoggable
import net.liftweb.common.Full
import net.liftweb.mapper.{By, By_<=}
@ -42,7 +42,7 @@ object MetricsArchiveScheduler extends MdcLoggable {
}
val someYearsAgo: Date = new Date(currentTime.getTime - (oneDayInMillis * days))
// Delete the outdated rows from the table "MetricsArchive"
MetricsArchive.bulkDelete_!!(By_<=(MetricsArchive.date, someYearsAgo))
MetricArchive.bulkDelete_!!(By_<=(MetricArchive.date, someYearsAgo))
}
def conditionalDeleteMetricsRow() = {
@ -60,7 +60,7 @@ object MetricsArchiveScheduler extends MdcLoggable {
}
val maybeDeletedRows: List[(Boolean, Long)] = canditateMetricRowsToMove map { i =>
// and delete it after successful coping
MetricsArchive.find(By(MetricsArchive.metricId, i.getMetricId())) match {
MetricArchive.find(By(MetricArchive.metricId, i.getMetricId())) match {
case Full(_) => (MappedMetric.bulkDelete_!!(By(MappedMetric.id, i.getMetricId())), i.getMetricId())
case _ => (false, i.getMetricId())
}

View File

@ -1,7 +1,8 @@
package code.api.v1_4_0
import java.util.Date
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.v1_4_0.JSONFactory1_4_0.ResourceDocJson
@ -51,6 +52,40 @@ class JSONFactory1_4_0Test extends V140ServerSetup with DefaultUsers {
description.contains("[BANK_ID](/glossary#Bank.bank_id): gh.29.uk") should be (true)
}
scenario("prepareJsonFieldDescription should work well - users object") {
val usersJson = usersJsonV400
val description = JSONFactory1_4_0.prepareJsonFieldDescription(usersJson, "response")
description.contains(
"""
|JSON response body fields:
|
|*user_id = 9ca9a7e4-6d02-40e3-a129-0b2bf89de9b1,
|
|*email = felixsmith@example.com,
|
|*provider_id = ,
|
|*provider = ,
|
|*username = felixsmith,
|
|*entitlements = ,
|
|*views = ,
|
|*agreements = ,
|
|*is_deleted = false,
|
|*last_marketing_agreement_signed_date = ,
|
|*is_locked = false
|
|""".stripMargin
) should be (false)
println(description)
}
scenario("PrepareUrlParameterDescription should work well, extract the parameters from URL") {
val requestUrl1 = "/obp/v4.0.0/banks/BANK_ID/accounts/account_ids/private"
val requestUrl1Description = JSONFactory1_4_0.prepareUrlParameterDescription(requestUrl1)

View File

@ -0,0 +1,115 @@
package code.api.v1_4_0
import code.api.util.CustomJsonFormats
import code.util.Helper.MdcLoggable
import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, FeatureSpec, GivenWhenThen, Matchers}
import java.lang.reflect.Field
import java.util.Date
class JSONFactory1_4_0_LightTest extends FeatureSpec
with BeforeAndAfterEach
with GivenWhenThen
with BeforeAndAfterAll
with Matchers
with MdcLoggable
with CustomJsonFormats {
feature("Test JSONFactory1_4_0.getJValueAndAllFields method") {
case class ClassOne(
string1: String = "1"
)
case class ClassTwo(
string2: String = "2",
strings2: List[String] = List("List-2")
)
val oneObject = ClassOne()
case class NestedClass(
classes: List[ClassOne] = List(oneObject)
)
val twoObject = ClassTwo()
case class NestedListClass(
classes1: List[ClassOne] = List(oneObject)
)
val nestedClass = NestedClass()
val nestedListClass = NestedListClass()
case class ComplexNestedClass(
complexNestedClassString: String = "ComplexNestedClass1",
complexNestedClassInt: Int = 1,
complexNestedClassDate: Date = new Date(),
complexNestedClassOptionSomeInt: Option[Int] = Some(1),
complexNestedClassOptionNoneInt: Option[Int] = None,
classes1: List[ClassOne] = List(oneObject),
classes2: List[ClassTwo] = List(twoObject),
)
val complexNestedClass = ComplexNestedClass()
scenario("getJValueAndAllFields -input is the oneObject, basic no nested, no List inside") {
val listFields: List[Field] = JSONFactory1_4_0.getAllFields(oneObject)
val expectedListFieldsString = "List(private final java.lang.String code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassOne$1.string1, " +
"private final code.api.v1_4_0.JSONFactory1_4_0_LightTest code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassOne$1.$outer)"
listFields.toString shouldBe (expectedListFieldsString)
// println(listFields)
}
scenario("getJValueAndAllFields -input it the nestedClass") {
val listFields: List[Field] = JSONFactory1_4_0.getAllFields(nestedClass)
val expectedListFieldsString = "List(" +
"public static final long scala.collection.immutable.Nil$.serialVersionUID, public static scala.collection.immutable.Nil$ scala.collection.immutable.Nil$.MODULE$, " +
"private final code.api.v1_4_0.JSONFactory1_4_0_LightTest code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassOne$1.$outer, " +
"private final code.api.v1_4_0.JSONFactory1_4_0_LightTest code.api.v1_4_0.JSONFactory1_4_0_LightTest$NestedClass$1.$outer, " +
"private final java.lang.String code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassOne$1.string1, " +
"private final scala.collection.immutable.List code.api.v1_4_0.JSONFactory1_4_0_LightTest$NestedClass$1.classes)"
listFields.toString shouldBe (expectedListFieldsString)
// println(listFields)
}
scenario("getJValueAndAllFields - input is the List[nestedClass]") {
val listFields: List[Field] = JSONFactory1_4_0.getAllFields(List(oneObject))
// it should return all the fields in the List
val expectedListFieldsString = "List(private final java.lang.String code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassOne$1.string1, " +
"private final code.api.v1_4_0.JSONFactory1_4_0_LightTest code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassOne$1.$outer, " +
"public static scala.collection.immutable.Nil$ scala.collection.immutable.Nil$.MODULE$, " +
"public static final long scala.collection.immutable.Nil$.serialVersionUID)"
listFields.toString shouldBe (expectedListFieldsString)
// println(listFields)
}
scenario("getJValueAndAllFields -input it the complexNestedClass") {
val listFields: List[Field] = JSONFactory1_4_0.getAllFields(complexNestedClass)
listFields.toString contains ("private final java.lang.String code.api.v1_4_0.JSONFactory1_4_0_LightTest$ComplexNestedClass$1.complexNestedClassString, ") shouldBe (true)
listFields.toString contains ("private final int code.api.v1_4_0.JSONFactory1_4_0_LightTest$ComplexNestedClass$1.complexNestedClassInt, ") shouldBe (true)
listFields.toString contains ("private final code.api.v1_4_0.JSONFactory1_4_0_LightTest code.api.v1_4_0.JSONFactory1_4_0_LightTest$ComplexNestedClass$1.$outer,") shouldBe (true)
listFields.toString contains ("public static final long scala.collection.immutable.Nil$.serialVersionUID, ") shouldBe (true)
listFields.toString contains ("private final scala.collection.immutable.List code.api.v1_4_0.JSONFactory1_4_0_LightTest$ComplexNestedClass$1.classes2, ") shouldBe (true)
listFields.toString contains ("private final java.lang.String code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassTwo$1.string2, ") shouldBe (true)
listFields.toString contains ("public static scala.collection.immutable.Nil$ scala.collection.immutable.Nil$.MODULE$, public static final long scala.None$.serialVersionUID, ") shouldBe (true)
listFields.toString contains ("private final code.api.v1_4_0.JSONFactory1_4_0_LightTest code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassOne$1.$outer, ") shouldBe (true)
listFields.toString contains ("private final scala.Option code.api.v1_4_0.JSONFactory1_4_0_LightTest$ComplexNestedClass$1.complexNestedClassOptionSomeInt") shouldBe (true)
listFields.toString contains ("public static scala.None$ scala.None$.MODULE$, private final java.lang.Object scala.Some.value, private final scala.collection.immutable.List code.api.v1_4_0.JSONFactory1_4_0_LightTest$ComplexNestedClass$1.classes1, ") shouldBe (true)
listFields.toString contains ("private final java.util.Date code.api.v1_4_0.JSONFactory1_4_0_LightTest$ComplexNestedClass$1.complexNestedClassDate, ") shouldBe (true)
listFields.toString contains ("private final scala.collection.immutable.List code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassTwo$1.strings2, ") shouldBe (true)
listFields.toString contains ("private int java.lang.String.hash, private final java.lang.String code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassOne$1.string1, ") shouldBe (true)
listFields.toString contains ("private static final long java.lang.String.serialVersionUID, private final code.api.v1_4_0.JSONFactory1_4_0_LightTest code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassTwo$1.$outer, ") shouldBe (true)
listFields.toString contains ("private final scala.Option code.api.v1_4_0.JSONFactory1_4_0_LightTest$ComplexNestedClass$1.complexNestedClassOptionNoneIn") shouldBe (true)
// println(listFields)
}
}
}

View File

@ -37,10 +37,11 @@ class IndexPageTest extends V510ServerSetup {
*
* This is made possible by the scalatest maven plugin
*/
/*
feature("Test the response of the page /index.html ") {
scenario("We create the apiCollection get All API collections back") {
When("We make a request ")
feature(s"Test the response of the page http://${server.host}:${server.port}/index.html") {
scenario(s"We try to load the page at http://${server.host}:${server.port}/index.html") {
When("We make the request")
val client = new OkHttpClient
val request = new Request.Builder().url(s"http://${server.host}:${server.port}/index.html").build
val responseFirst = client.newCall(request).execute
@ -51,8 +52,10 @@ class IndexPageTest extends V510ServerSetup {
responseSecond.code should equal(200)
val duration: Long = endTime - startTime
And(s"And duration($duration) is less than 1000 ms")
duration should be <= 1000L
println(s"Duration of the loading the page http://${server.host}:${server.port}/index.html is: $duration ms")
// duration should be <= 1000L // TODO Make this check adjustable
}
}
*/
}