From 324e48e544cbe4c53373cf902beccf6b0c2f004c Mon Sep 17 00:00:00 2001 From: hongwei Date: Mon, 6 Nov 2023 15:41:33 +0100 Subject: [PATCH 01/15] refactor/use constant value for isRedisAvailable cacheKey --- obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala b/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala index 1be4e88be8..d1c36fc335 100644 --- a/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala +++ b/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala @@ -80,10 +80,10 @@ object RateLimitingUtil extends MdcLoggable { def isRedisAvailable() = { try { - val uuid = APIUtil.generateUUID() + val key = "OBP_Check_isRedisAvailable" jedis.connect() - jedis.set(uuid, "10") - jedis.exists(uuid) == true + jedis.set(key, "10") + jedis.exists(key) == true } catch { case e : Throwable => logger.warn("------------| RateLimitUtil.isRedisAvailable |------------") From e3faa500b79c5ff7d2c84c677c80231a468c2e44 Mon Sep 17 00:00:00 2001 From: hongwei Date: Mon, 6 Nov 2023 17:02:06 +0100 Subject: [PATCH 02/15] feature/use our own cache method --- .../ResourceDocsAPIMethods.scala | 56 ++++++++++++--- .../main/scala/code/api/cache/Caching.scala | 12 ++++ .../src/main/scala/code/api/cache/Redis.scala | 6 +- .../code/api/v1_4_0/JSONFactory1_4_0.scala | 71 +++++++++---------- obp-api/src/test/scala/code/RedisTest.scala | 50 +++++++++++++ 5 files changed, 146 insertions(+), 49 deletions(-) create mode 100644 obp-api/src/test/scala/code/RedisTest.scala diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala index 795e46d3f6..751f03f53e 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala @@ -581,15 +581,55 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth Future(resourceDocsJsonJValue.map(successJsonResponse(_))) case _ => contentParam match { - case Some(DYNAMIC) => - val dynamicDocs: Box[JValue] = Full (getResourceDocsObpDynamicCached(tags, partialFunctions, locale, contentParam,None, isVersion4OrHigher)) + case Some(DYNAMIC) =>{ + val cacheKey = ("getResourceDocsObpDynamicCached" + tags + partialFunctions+ locale+ contentParam+ isVersion4OrHigher).intern() + val cacheValueFromRedis = Caching.getResourceDocCache(cacheKey) + + val dynamicDocs: Box[JValue] = + if (cacheValueFromRedis != null) { + Full(json.parse(cacheValueFromRedis)) + } else { + val resourceDocJsonJValue = getResourceDocsObpDynamicCached(tags, partialFunctions, locale, contentParam, None, isVersion4OrHigher) + val jsonString = json.compactRender(resourceDocJsonJValue) + Caching.setResourceDocCache(cacheKey, jsonString) + Full(resourceDocJsonJValue) + } + Future(dynamicDocs.map(successJsonResponse(_))) - case Some(STATIC) => - val staticDocs: Box[JValue] = Full(getStaticResourceDocsObpCached(requestedApiVersionString, tags, partialFunctions, locale, contentParam, isVersion4OrHigher)) - Future(staticDocs.map(successJsonResponse(_))) - case _ => - val docs: Box[JValue] = Full(getAllResourceDocsObpCached(requestedApiVersionString, tags, partialFunctions, locale, contentParam, isVersion4OrHigher)) - Future(docs.map(successJsonResponse(_))) + } + + case Some(STATIC) => { + val cacheKey = ("getStaticResourceDocsObpCached" + tags + partialFunctions + locale + contentParam + isVersion4OrHigher).intern() + val cacheValueFromRedis = Caching.getResourceDocCache(cacheKey) + + val dynamicDocs: Box[JValue] = + if (cacheValueFromRedis != null) { + Full(json.parse(cacheValueFromRedis)) + } else { + val resourceDocJsonJValue = getStaticResourceDocsObpCached(requestedApiVersionString, tags, partialFunctions, locale, contentParam, isVersion4OrHigher) + val jsonString = json.compactRender(resourceDocJsonJValue) + Caching.setResourceDocCache(cacheKey, jsonString) + Full(resourceDocJsonJValue) + } + + Future(dynamicDocs.map(successJsonResponse(_))) + } + case _ => { + val cacheKey = ("getAllResourceDocsObpCached" + tags + partialFunctions + locale + contentParam + isVersion4OrHigher).intern() + val cacheValueFromRedis = Caching.getResourceDocCache(cacheKey) + + val dynamicDocs: Box[JValue] = + if (cacheValueFromRedis != null) { + Full(json.parse(cacheValueFromRedis)) + } else { + val resourceDocJsonJValue = getAllResourceDocsObpCached(requestedApiVersionString, tags, partialFunctions, locale, contentParam, isVersion4OrHigher) + val jsonString = json.compactRender(resourceDocJsonJValue) + Caching.setResourceDocCache(cacheKey, jsonString) + Full(resourceDocJsonJValue) + } + + Future(dynamicDocs.map(successJsonResponse(_))) + } } } } yield { diff --git a/obp-api/src/main/scala/code/api/cache/Caching.scala b/obp-api/src/main/scala/code/api/cache/Caching.scala index 5f2f23733d..8ca23ad441 100644 --- a/obp-api/src/main/scala/code/api/cache/Caching.scala +++ b/obp-api/src/main/scala/code/api/cache/Caching.scala @@ -1,5 +1,6 @@ package code.api.cache +import code.api.cache.Redis.jedis import code.api.util.RateLimitingUtil import code.util.Helper.MdcLoggable import com.softwaremill.macmemo.{Cache, MemoCacheBuilder, MemoizeParams} @@ -84,4 +85,15 @@ object Caching extends MdcLoggable { } } + final val resourceDocCacheKeyPrefix = "rd_" + + + def getResourceDocCache(key: String) = { + jedis.get(resourceDocCacheKeyPrefix + key) + } + + def setResourceDocCache(key:String, value: String)={ + jedis.set(resourceDocCacheKeyPrefix+key,value) + } + } diff --git a/obp-api/src/main/scala/code/api/cache/Redis.scala b/obp-api/src/main/scala/code/api/cache/Redis.scala index b3bb000432..060e386608 100644 --- a/obp-api/src/main/scala/code/api/cache/Redis.scala +++ b/obp-api/src/main/scala/code/api/cache/Redis.scala @@ -24,10 +24,10 @@ object Redis extends MdcLoggable { def isRedisAvailable() = { try { - val uuid = APIUtil.generateUUID() + val key = "OBP_Check_isRedisAvailable" jedis.connect() - jedis.set(uuid, "10") - jedis.exists(uuid) == true + jedis.set(key, "10") + jedis.exists(key) == true } catch { case e: Throwable => logger.warn("------------| Redis.isRedisAvailable |------------") diff --git a/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala b/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala index 6f96f5bcb4..55130ea332 100644 --- a/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala +++ b/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala @@ -2,6 +2,7 @@ package code.api.v1_4_0 import code.api.berlin.group.v1_3.JvalueCaseClass import code.api.cache.Caching + import java.util.Date import code.api.util.APIUtil.{EmptyBody, PrimaryDataBody, ResourceDoc, createLocalisedResourceDocJsonTTL} import code.api.util.ApiTag.ResourceDocTag @@ -17,13 +18,15 @@ import com.openbankproject.commons.util.{EnumValue, JsonUtils, OBPEnumeration, R import net.liftweb.common.Full import net.liftweb.json import net.liftweb.json.Extraction.decompose -import net.liftweb.json.{Formats, JDouble, JInt, JString} +import net.liftweb.json.{Extraction, Formats, JDouble, JInt, JString} import net.liftweb.json.JsonAST.{JArray, JBool, JNothing, JObject, JValue} import net.liftweb.util.StringHelpers import code.util.Helper.MdcLoggable +import com.github.dwickern.macros.NameOf.nameOf import com.tesobe.{CacheKeyFromArguments, CacheKeyOmit} import org.apache.commons.lang3.StringUtils import scalacache.memoization.cacheKeyExclude + import java.util.regex.Pattern import java.lang.reflect.Field import java.util.UUID.randomUUID @@ -517,35 +520,19 @@ object JSONFactory1_4_0 extends MdcLoggable{ jsonFieldsDescription.mkString(jsonTitleType,"","\n") } + //cache key will only contain "operationId + locale" + def createLocalisedResourceDocJsonCached( operationId: String, // this will be in the cacheKey locale: Option[String],// this will be in the cacheKey - @CacheKeyOmit resourceDocUpdatedTags: ResourceDoc, - @CacheKeyOmit isVersion4OrHigher:Boolean, - @CacheKeyOmit urlParametersI18n:String , - @CacheKeyOmit jsonRequestBodyFieldsI18n:String, - @CacheKeyOmit jsonResponseBodyFieldsI18n:String + resourceDocUpdatedTags: ResourceDoc, + isVersion4OrHigher:Boolean, + urlParametersI18n:String , + jsonRequestBodyFieldsI18n:String, + jsonResponseBodyFieldsI18n:String ): ResourceDocJson = { - /** - * Please note that "var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)" - * is just a temporary value field with UUID values in order to prevent any ambiguity. - * The real value will be assigned by Macro during compile time at this line of a code: - * https://github.com/OpenBankProject/scala-macros/blob/master/macros/src/main/scala/com/tesobe/CacheKeyFromArgumentsMacro.scala#L49 - */ - var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString) - CacheKeyFromArguments.buildCacheKey { - Caching.memoizeSyncWithProvider(Some(cacheKey.toString()))(createLocalisedResourceDocJsonTTL second) { - // There are multiple flavours of markdown. For instance, original markdown emphasises underscores (surrounds _ with ()) - // But we don't want to have to escape underscores (\_) in our documentation - // Thus we use a flavour of markdown that ignores underscores in words. (Github markdown does this too) - // We return html rather than markdown to the consumer so they don't have to bother with these questions. - - //Here area some endpoints, which should not be added the description: - // 1st: Dynamic entity endpoint, - // 2rd: Dynamic endpoint endpoints, - // 3rd: all the user created endpoints, - val fieldsDescription = + val fieldsDescription = if (resourceDocUpdatedTags.tags.toString.contains("Dynamic-Entity") || resourceDocUpdatedTags.tags.toString.contains("Dynamic-Endpoint") || resourceDocUpdatedTags.roles.toString.contains("DynamicEntity") @@ -603,26 +590,34 @@ object JSONFactory1_4_0 extends MdcLoggable{ logger.trace(s"createLocalisedResourceDocJsonCached value is $resourceDoc") resourceDoc - - } } - } + def createLocalisedResourceDocJson(rd: ResourceDoc, isVersion4OrHigher:Boolean, locale: Option[String], 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 $createLocalisedResourceDocJsonTTL seconds val userDefinedEndpointTags = getAllEndpointTagsBox(rd.operationId).map(endpointTag =>ResourceDocTag(endpointTag.tagName)) val resourceDocWithUserDefinedEndpointTags: ResourceDoc = rd.copy(tags = userDefinedEndpointTags++ rd.tags) - logger.trace(s"createLocalisedResourceDocJsonCached key is ${resourceDocWithUserDefinedEndpointTags.operationId + resourceDocWithUserDefinedEndpointTags.operationId}") - createLocalisedResourceDocJsonCached( - resourceDocWithUserDefinedEndpointTags.operationId, - locale: Option[String], - resourceDocWithUserDefinedEndpointTags, - isVersion4OrHigher: Boolean, - urlParametersI18n: String, - jsonRequestBodyFieldsI18n: String, - jsonResponseBodyFieldsI18n: String - ) + val cacheKey = ("createLocalisedResourceDocJson"+ resourceDocWithUserDefinedEndpointTags.operationId + locale).intern() + val cacheValueFromRedis = Caching.getResourceDocCache(cacheKey) + + if(cacheValueFromRedis != null){ + json.parse(cacheValueFromRedis).extract[ResourceDocJson] + }else{ + val resourceDocJson = createLocalisedResourceDocJsonCached( + resourceDocWithUserDefinedEndpointTags.operationId, + locale: Option[String], + resourceDocWithUserDefinedEndpointTags, + isVersion4OrHigher: Boolean, + urlParametersI18n: String, + jsonRequestBodyFieldsI18n: String, + jsonResponseBodyFieldsI18n: String + ) + val jsonString = json.compactRender(Extraction.decompose(resourceDocJson)) + Caching.setResourceDocCache(cacheKey,jsonString) + + resourceDocJson + } } diff --git a/obp-api/src/test/scala/code/RedisTest.scala b/obp-api/src/test/scala/code/RedisTest.scala new file mode 100644 index 0000000000..b2aea36e65 --- /dev/null +++ b/obp-api/src/test/scala/code/RedisTest.scala @@ -0,0 +1,50 @@ +package code + +import code.api.util.{APIUtil, CustomJsonFormats} +import code.util.Helper.MdcLoggable +import net.liftweb.json +import net.liftweb.json.{Extraction, Formats} +import org.scalatest.{FeatureSpec, GivenWhenThen, Matchers} +import redis.clients.jedis.Jedis + +class RedisTest extends FeatureSpec with Matchers with GivenWhenThen with MdcLoggable { + + val url = APIUtil.getPropsValue("cache.redis.url", "127.0.0.1") + val port = APIUtil.getPropsAsIntValue("cache.redis.port", 6379) + implicit def formats: Formats = CustomJsonFormats.formats + + lazy val jedis = new Jedis(url, port) + + feature("The Unit test for Redis") + { + scenario("test the simple key value") + { + jedis.set("BasicObjectKey", "this is for testing") + val result = jedis.get("BasicObjectKey") + result should equal ("this is for testing") + val result2 = jedis.get("BasicObjectKey1") + val result3 = jedis.get("BasicObjectKey1") + } + + scenario("test the basic case class") + { + case class BasicObject( + customer_number: String, + legal_name: String + ) + + val basicObject = BasicObject("customer_number_123","Tom") + val redisValue = json.compactRender(Extraction.decompose(basicObject)) + jedis.set("BasicObjectKey", redisValue) + val result = jedis.get("BasicObjectKey") + println(result) + result should equal (""""[{"$outer":{},"customer_number":"customer_number_123","legal_name":"Tom"}]"""") + } + + } + + + + + +} \ No newline at end of file From 5cd62fa914f6c57d50a473c5aed0c9824e287dc1 Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 7 Nov 2023 16:13:40 +0100 Subject: [PATCH 03/15] feature/added four obp redis cache method --- .../ResourceDocsAPIMethods.scala | 17 ++++----- .../main/scala/code/api/cache/Caching.scala | 36 ++++++++++++++++--- .../code/api/v1_4_0/JSONFactory1_4_0.scala | 4 +-- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala index 751f03f53e..4a4bf8130b 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala @@ -572,6 +572,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth } requestedApiVersion <- NewStyle.function.tryons(s"$InvalidApiVersionString $requestedApiVersionString", 400, callContext) {ApiVersionUtils.valueOf(requestedApiVersionString)} _ <- Helper.booleanToFuture(s"$ApiVersionNotSupported $requestedApiVersionString", 400, callContext)(versionIsAllowed(requestedApiVersion)) + cacheKey = (locale.toString + tags + partialFunctions+ contentParam+ isVersion4OrHigher).intern() json <- locale match { case _ if (apiCollectionIdParam.isDefined) => val operationIds = MappedApiCollectionEndpointsProvider.getApiCollectionEndpoints(apiCollectionIdParam.getOrElse("")).map(_.operationId).map(getObpFormatOperationId) @@ -582,16 +583,14 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth case _ => contentParam match { case Some(DYNAMIC) =>{ - val cacheKey = ("getResourceDocsObpDynamicCached" + tags + partialFunctions+ locale+ contentParam+ isVersion4OrHigher).intern() - val cacheValueFromRedis = Caching.getResourceDocCache(cacheKey) - + val cacheValueFromRedis = Caching.getDynamicResourceDocCache(cacheKey) val dynamicDocs: Box[JValue] = if (cacheValueFromRedis != null) { Full(json.parse(cacheValueFromRedis)) } else { val resourceDocJsonJValue = getResourceDocsObpDynamicCached(tags, partialFunctions, locale, contentParam, None, isVersion4OrHigher) val jsonString = json.compactRender(resourceDocJsonJValue) - Caching.setResourceDocCache(cacheKey, jsonString) + Caching.setDynamicResourceDocCache(cacheKey, jsonString) Full(resourceDocJsonJValue) } @@ -599,8 +598,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth } case Some(STATIC) => { - val cacheKey = ("getStaticResourceDocsObpCached" + tags + partialFunctions + locale + contentParam + isVersion4OrHigher).intern() - val cacheValueFromRedis = Caching.getResourceDocCache(cacheKey) + val cacheValueFromRedis = Caching.getStaticResourceDocCache(cacheKey) val dynamicDocs: Box[JValue] = if (cacheValueFromRedis != null) { @@ -608,15 +606,14 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth } else { val resourceDocJsonJValue = getStaticResourceDocsObpCached(requestedApiVersionString, tags, partialFunctions, locale, contentParam, isVersion4OrHigher) val jsonString = json.compactRender(resourceDocJsonJValue) - Caching.setResourceDocCache(cacheKey, jsonString) + Caching.setLocalisedResourceDocCache(cacheKey, jsonString) Full(resourceDocJsonJValue) } Future(dynamicDocs.map(successJsonResponse(_))) } case _ => { - val cacheKey = ("getAllResourceDocsObpCached" + tags + partialFunctions + locale + contentParam + isVersion4OrHigher).intern() - val cacheValueFromRedis = Caching.getResourceDocCache(cacheKey) + val cacheValueFromRedis = Caching.getAllResourceDocCache(cacheKey) val dynamicDocs: Box[JValue] = if (cacheValueFromRedis != null) { @@ -624,7 +621,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth } else { val resourceDocJsonJValue = getAllResourceDocsObpCached(requestedApiVersionString, tags, partialFunctions, locale, contentParam, isVersion4OrHigher) val jsonString = json.compactRender(resourceDocJsonJValue) - Caching.setResourceDocCache(cacheKey, jsonString) + Caching.setAllResourceDocCache(cacheKey, jsonString) Full(resourceDocJsonJValue) } diff --git a/obp-api/src/main/scala/code/api/cache/Caching.scala b/obp-api/src/main/scala/code/api/cache/Caching.scala index 8ca23ad441..4471e16e5f 100644 --- a/obp-api/src/main/scala/code/api/cache/Caching.scala +++ b/obp-api/src/main/scala/code/api/cache/Caching.scala @@ -85,15 +85,41 @@ object Caching extends MdcLoggable { } } - final val resourceDocCacheKeyPrefix = "rd_" + final val LocalisedResourceDocPrefix = "rd_local_" + final val DynamicResourceDocCacheKeyPrefix = "rd_dynamic_" + final val StaticResourceDocCacheKeyPrefix = "rd_static_" + final val AllResourceDocCacheKeyPrefix = "rd_all_" + def getLocalisedResourceDocCache(key: String) = { + jedis.get(LocalisedResourceDocPrefix + key) + } + + def setLocalisedResourceDocCache(key:String, value: String)={ + jedis.set(LocalisedResourceDocPrefix+key,value) + } + + def getDynamicResourceDocCache(key: String) = { + jedis.get(DynamicResourceDocCacheKeyPrefix + key) + } + + def setDynamicResourceDocCache(key:String, value: String)={ + jedis.set(DynamicResourceDocCacheKeyPrefix+key,value) + } + + def getStaticResourceDocCache(key: String) = { + jedis.get(StaticResourceDocCacheKeyPrefix + key) + } + + def setStaticResourceDocCache(key:String, value: String)={ + jedis.set(StaticResourceDocCacheKeyPrefix+key,value) + } - def getResourceDocCache(key: String) = { - jedis.get(resourceDocCacheKeyPrefix + key) + def getAllResourceDocCache(key: String) = { + jedis.get(AllResourceDocCacheKeyPrefix + key) } - def setResourceDocCache(key:String, value: String)={ - jedis.set(resourceDocCacheKeyPrefix+key,value) + def setAllResourceDocCache(key:String, value: String)={ + jedis.set(AllResourceDocCacheKeyPrefix+key,value) } } diff --git a/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala b/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala index 55130ea332..9c790381c2 100644 --- a/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala +++ b/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala @@ -599,7 +599,7 @@ object JSONFactory1_4_0 extends MdcLoggable{ val resourceDocWithUserDefinedEndpointTags: ResourceDoc = rd.copy(tags = userDefinedEndpointTags++ rd.tags) val cacheKey = ("createLocalisedResourceDocJson"+ resourceDocWithUserDefinedEndpointTags.operationId + locale).intern() - val cacheValueFromRedis = Caching.getResourceDocCache(cacheKey) + val cacheValueFromRedis = Caching.getLocalisedResourceDocCache(cacheKey) if(cacheValueFromRedis != null){ json.parse(cacheValueFromRedis).extract[ResourceDocJson] @@ -614,7 +614,7 @@ object JSONFactory1_4_0 extends MdcLoggable{ jsonResponseBodyFieldsI18n: String ) val jsonString = json.compactRender(Extraction.decompose(resourceDocJson)) - Caching.setResourceDocCache(cacheKey,jsonString) + Caching.setLocalisedResourceDocCache(cacheKey,jsonString) resourceDocJson } From 32d91e8abcf6515a2da61055b7b9dbb70f828c6b Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 7 Nov 2023 16:25:31 +0100 Subject: [PATCH 04/15] refactor/revert this autoCommit to false --- obp-api/src/main/scala/bootstrap/liftweb/CustomDBVendor.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obp-api/src/main/scala/bootstrap/liftweb/CustomDBVendor.scala b/obp-api/src/main/scala/bootstrap/liftweb/CustomDBVendor.scala index be5b72e564..9a0d58240a 100644 --- a/obp-api/src/main/scala/bootstrap/liftweb/CustomDBVendor.scala +++ b/obp-api/src/main/scala/bootstrap/liftweb/CustomDBVendor.scala @@ -50,7 +50,7 @@ class CustomDBVendor(driverName: String, config.setMaxLifetime(maxLifetime.head) } //Liftweb DB.scala will set all the new connections to false, so here we set default to false - config.setAutoCommit(true) + config.setAutoCommit(false) (dbUser, dbPassword) match { case (Full(user), Full(pwd)) => From bc8dde99b5248d826bd59c10b8d1a67eebb040c2 Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 7 Nov 2023 16:43:15 +0100 Subject: [PATCH 05/15] feature/use our own cache method - step2 --- .../ResourceDocsAPIMethods.scala | 2 +- .../main/scala/code/api/cache/Caching.scala | 23 ++++++++----------- .../scala/code/api/constant/constant.scala | 6 +++++ .../code/api/v1_4_0/JSONFactory1_4_0.scala | 2 +- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala index 4a4bf8130b..152c9da7ef 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala @@ -606,7 +606,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth } else { val resourceDocJsonJValue = getStaticResourceDocsObpCached(requestedApiVersionString, tags, partialFunctions, locale, contentParam, isVersion4OrHigher) val jsonString = json.compactRender(resourceDocJsonJValue) - Caching.setLocalisedResourceDocCache(cacheKey, jsonString) + Caching.setStaticResourceDocCache(cacheKey, jsonString) Full(resourceDocJsonJValue) } diff --git a/obp-api/src/main/scala/code/api/cache/Caching.scala b/obp-api/src/main/scala/code/api/cache/Caching.scala index 4471e16e5f..be58082d27 100644 --- a/obp-api/src/main/scala/code/api/cache/Caching.scala +++ b/obp-api/src/main/scala/code/api/cache/Caching.scala @@ -1,7 +1,7 @@ package code.api.cache +import code.api.Constant._ import code.api.cache.Redis.jedis -import code.api.util.RateLimitingUtil import code.util.Helper.MdcLoggable import com.softwaremill.macmemo.{Cache, MemoCacheBuilder, MemoizeParams} @@ -85,41 +85,36 @@ object Caching extends MdcLoggable { } } - final val LocalisedResourceDocPrefix = "rd_local_" - final val DynamicResourceDocCacheKeyPrefix = "rd_dynamic_" - final val StaticResourceDocCacheKeyPrefix = "rd_static_" - final val AllResourceDocCacheKeyPrefix = "rd_all_" - def getLocalisedResourceDocCache(key: String) = { - jedis.get(LocalisedResourceDocPrefix + key) + jedis.get(LOCALISED_RESOURCE_DOC_PREFIX + key) } def setLocalisedResourceDocCache(key:String, value: String)={ - jedis.set(LocalisedResourceDocPrefix+key,value) + jedis.set(LOCALISED_RESOURCE_DOC_PREFIX+key,value) } def getDynamicResourceDocCache(key: String) = { - jedis.get(DynamicResourceDocCacheKeyPrefix + key) + jedis.get(DYNAMIC_RESOURCE_DOC_CACHE_KEY_PREFIX + key) } def setDynamicResourceDocCache(key:String, value: String)={ - jedis.set(DynamicResourceDocCacheKeyPrefix+key,value) + jedis.set(DYNAMIC_RESOURCE_DOC_CACHE_KEY_PREFIX+key,value) } def getStaticResourceDocCache(key: String) = { - jedis.get(StaticResourceDocCacheKeyPrefix + key) + jedis.get(STATIC_RESOURCE_DOC_CACHE_KEY_PREFIX + key) } def setStaticResourceDocCache(key:String, value: String)={ - jedis.set(StaticResourceDocCacheKeyPrefix+key,value) + jedis.set(STATIC_RESOURCE_DOC_CACHE_KEY_PREFIX+key,value) } def getAllResourceDocCache(key: String) = { - jedis.get(AllResourceDocCacheKeyPrefix + key) + jedis.get(ALL_RESOURCE_DOC_CACHE_KEY_PREFIX + key) } def setAllResourceDocCache(key:String, value: String)={ - jedis.set(AllResourceDocCacheKeyPrefix+key,value) + jedis.set(ALL_RESOURCE_DOC_CACHE_KEY_PREFIX+key,value) } } diff --git a/obp-api/src/main/scala/code/api/constant/constant.scala b/obp-api/src/main/scala/code/api/constant/constant.scala index 6ace561278..a75408fc65 100644 --- a/obp-api/src/main/scala/code/api/constant/constant.scala +++ b/obp-api/src/main/scala/code/api/constant/constant.scala @@ -87,6 +87,12 @@ object Constant extends MdcLoggable { final val PARAM_LOCALE = "locale" final val PARAM_TIMESTAMP = "_timestamp_" + + + final val LOCALISED_RESOURCE_DOC_PREFIX = "rd_localised_" + final val DYNAMIC_RESOURCE_DOC_CACHE_KEY_PREFIX = "rd_dynamic_" + final val STATIC_RESOURCE_DOC_CACHE_KEY_PREFIX = "rd_static_" + final val ALL_RESOURCE_DOC_CACHE_KEY_PREFIX = "rd_all_" } diff --git a/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala b/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala index 9c790381c2..3ef0f9f6fe 100644 --- a/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala +++ b/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala @@ -598,7 +598,7 @@ object JSONFactory1_4_0 extends MdcLoggable{ val userDefinedEndpointTags = getAllEndpointTagsBox(rd.operationId).map(endpointTag =>ResourceDocTag(endpointTag.tagName)) val resourceDocWithUserDefinedEndpointTags: ResourceDoc = rd.copy(tags = userDefinedEndpointTags++ rd.tags) - val cacheKey = ("createLocalisedResourceDocJson"+ resourceDocWithUserDefinedEndpointTags.operationId + locale).intern() + val cacheKey = (resourceDocWithUserDefinedEndpointTags.operationId + locale).intern() val cacheValueFromRedis = Caching.getLocalisedResourceDocCache(cacheKey) if(cacheValueFromRedis != null){ From c405e019669f4515ffc8e306e0e030511636d425 Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 7 Nov 2023 16:58:01 +0100 Subject: [PATCH 06/15] refactor/set the getExampleFieldValue log level to trace --- obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala b/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala index 3ef0f9f6fe..f64fa87b98 100644 --- a/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala +++ b/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala @@ -411,7 +411,7 @@ object JSONFactory1_4_0 extends MdcLoggable{ case Some(ConnectorField(value, _)) => value case _ => //The ExampleValue are not totally finished, lots of fields are missing here. so we first hide them. and show them in the log - logger.debug(s"getExampleFieldValue: there is no $exampleValueFieldName variable in ExampleValue object") + logger.trace(s"getExampleFieldValue: there is no $exampleValueFieldName variable in ExampleValue object") parameter } } From 66014a43fc50ef7d25a3198e2169da1f96422829 Mon Sep 17 00:00:00 2001 From: hongwei Date: Wed, 8 Nov 2023 10:19:04 +0100 Subject: [PATCH 07/15] refactor/use obp simple cache for swaggerDocs --- .../resources/props/sample.props.template | 6 - .../ResourceDocsAPIMethods.scala | 281 ++++++++---------- .../main/scala/code/api/cache/Caching.scala | 8 + .../scala/code/api/constant/constant.scala | 1 + .../code/api/v1_4_0/JSONFactory1_4_0.scala | 2 +- .../scala/code/api/v4_0_0/APIMethods400.scala | 8 - 6 files changed, 142 insertions(+), 164 deletions(-) diff --git a/obp-api/src/main/resources/props/sample.props.template b/obp-api/src/main/resources/props/sample.props.template index e79ea00329..24859597c1 100644 --- a/obp-api/src/main/resources/props/sample.props.template +++ b/obp-api/src/main/resources/props/sample.props.template @@ -91,12 +91,6 @@ authTypeValidation.cache.ttl.seconds=36 ## no 0 value will cause new ConnectorMethod will works after that seconds connectorMethod.cache.ttl.seconds=40 -## swagger file should not generated for every request, this is a time-to-live in seconds for the generated swagger of OBP api, -## this value also represent how many seconds before the new endpoints will be shown after upload a new DynamicEntity. -## So if you want the new endpoints shown timely, set this value to a small number. -dynamicResourceDocsObp.cache.ttl.seconds=3600 -staticResourceDocsObp.cache.ttl.seconds=3600 -createLocalisedResourceDocJson.cache.ttl.seconds=3600 ## This can change the behavior of `Get Resource Docs`/`Get API Glossary`. If we set it to `true`, OBP will check the authentication and CanReadResourceDoc/CanReadGlossary Role # the default value is false, so the `Get Resource Docs`/`Get API Glossary` is anonymous as default. diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala index 152c9da7ef..c0eb2b03ea 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala @@ -220,13 +220,6 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth } - - //implicit val scalaCache = ScalaCache(GuavaCache(underlyingGuavaCache)) - // if upload DynamicEntity, will generate corresponding endpoints, when current cache timeout, the new endpoints will be shown. - // so if you want the new generated endpoints shown timely, set this value to a small number, or set to a big number - val getDynamicResourceDocsTTL : Int = APIUtil.getPropsValue(s"dynamicResourceDocsObp.cache.ttl.seconds", "3600").toInt - val getStaticResourceDocsTTL : Int = APIUtil.getPropsValue(s"staticResourceDocsObp.cache.ttl.seconds", "3600").toInt - /** * * @param requestedApiVersion @@ -235,30 +228,19 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth * @param contentParam if this is Some(`true`), only show dynamic endpoints, if Some(`false`), only show static. If it is None, we will show all. default is None * @return */ - private def getStaticResourceDocsObpCached(requestedApiVersionString : String, - resourceDocTags: Option[List[ResourceDocTag]], - partialFunctionNames: Option[List[String]], - locale: Option[String], - contentParam: Option[ContentParam], - isVersion4OrHigher:Boolean - ) : JValue = { - /** - * Please note that "var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)" - * is just a temporary value field with UUID values in order to prevent any ambiguity. - * The real value will be assigned by Macro during compile time at this line of a code: - * https://github.com/OpenBankProject/scala-macros/blob/master/macros/src/main/scala/com/tesobe/CacheKeyFromArgumentsMacro.scala#L49 - */ - var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString) - CacheKeyFromArguments.buildCacheKey { - Caching.memoizeSyncWithProvider (Some(cacheKey.toString())) (getStaticResourceDocsTTL second) { - logger.debug(s"Generating OBP-getStaticResourceDocsObpCached requestedApiVersion is $requestedApiVersionString") - val requestedApiVersion = ApiVersionUtils.valueOf(requestedApiVersionString) - - val resourceDocJson = resourceDocsToResourceDocJson(getResourceDocsList(requestedApiVersion), resourceDocTags, partialFunctionNames, isVersion4OrHigher, locale) - resourceDocJson.map(resourceDocsJsonToJsonResponse).head - } - } - } + private def getStaticResourceDocsObpCached( + requestedApiVersionString: String, + resourceDocTags: Option[List[ResourceDocTag]], + partialFunctionNames: Option[List[String]], + locale: Option[String], + isVersion4OrHigher: Boolean + ): JValue = { + logger.debug(s"Generating OBP-getStaticResourceDocsObpCached requestedApiVersion is $requestedApiVersionString") + val requestedApiVersion = ApiVersionUtils.valueOf(requestedApiVersionString) + + val resourceDocJson = resourceDocsToResourceDocJson(getResourceDocsList(requestedApiVersion), resourceDocTags, partialFunctionNames, isVersion4OrHigher, locale) + resourceDocJson.map(resourceDocsJsonToJsonResponse).head + } /** * @@ -268,109 +250,96 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth * @param contentParam if this is Some(`true`), only show dynamic endpoints, if Some(`false`), only show static. If it is None, we will show all. default is None * @return */ - private def getAllResourceDocsObpCached(requestedApiVersionString : String, + private def getAllResourceDocsObpCached( + requestedApiVersionString: String, resourceDocTags: Option[List[ResourceDocTag]], partialFunctionNames: Option[List[String]], locale: Option[String], contentParam: Option[ContentParam], - isVersion4OrHigher:Boolean - ) : JValue = { - /** - * Please note that "var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)" - * is just a temporary value field with UUID values in order to prevent any ambiguity. - * The real value will be assigned by Macro during compile time at this line of a code: - * https://github.com/OpenBankProject/scala-macros/blob/master/macros/src/main/scala/com/tesobe/CacheKeyFromArgumentsMacro.scala#L49 - */ - var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString) - CacheKeyFromArguments.buildCacheKey { - Caching.memoizeSyncWithProvider (Some(cacheKey.toString())) (getStaticResourceDocsTTL second) { - logger.debug(s"Generating getAllResourceDocsObpCached-Docs requestedApiVersion is $requestedApiVersionString") - val requestedApiVersion = ApiVersionUtils.valueOf(requestedApiVersionString) - - val dynamicDocs = allDynamicResourceDocs - .filter(rd => rd.implementedInApiVersion == requestedApiVersion) - .map(it => it.specifiedUrl match { - case Some(_) => it - case _ => - it.specifiedUrl = Some(s"/${it.implementedInApiVersion.urlPrefix}/${requestedApiVersion.vDottedApiVersion}${it.requestUrl}") - it - }) - .toList - - val filteredDocs = resourceDocTags match { - // We have tags - case Some(tags) => { - // This can create duplicates to use toSet below - for { - r <- dynamicDocs - t <- tags - if r.tags.contains(t) - } yield { - r - } - } - // tags param was not mentioned in url or was empty, so return all - case None => dynamicDocs + isVersion4OrHigher: Boolean + ): JValue = { + logger.debug(s"Generating getAllResourceDocsObpCached-Docs requestedApiVersion is $requestedApiVersionString") + val requestedApiVersion = ApiVersionUtils.valueOf(requestedApiVersionString) + + val dynamicDocs = allDynamicResourceDocs + .filter(rd => rd.implementedInApiVersion == requestedApiVersion) + .map(it => it.specifiedUrl match { + case Some(_) => it + case _ => + it.specifiedUrl = Some(s"/${it.implementedInApiVersion.urlPrefix}/${requestedApiVersion.vDottedApiVersion}${it.requestUrl}") + it + }) + + val filteredDocs = resourceDocTags match { + // We have tags + case Some(tags) => { + // This can create duplicates to use toSet below + for { + r <- dynamicDocs + t <- tags + if r.tags.contains(t) + } yield { + r } - - val staticDocs = getResourceDocsList(requestedApiVersion) - - val allDocs = staticDocs.map( _ ++ filteredDocs) - - val resourceDocJson = resourceDocsToResourceDocJson(allDocs, resourceDocTags, partialFunctionNames, isVersion4OrHigher, locale) - resourceDocJson.map(resourceDocsJsonToJsonResponse).head } + // tags param was not mentioned in url or was empty, so return all + case None => dynamicDocs } + + val staticDocs = getResourceDocsList(requestedApiVersion) + + val allDocs = staticDocs.map(_ ++ filteredDocs) + + val resourceDocJson = resourceDocsToResourceDocJson(allDocs, resourceDocTags, partialFunctionNames, isVersion4OrHigher, locale) + resourceDocJson.map(resourceDocsJsonToJsonResponse).head } private def getResourceDocsObpDynamicCached( - resourceDocTags: Option[List[ResourceDocTag]], - partialFunctionNames: Option[List[String]], - locale: Option[String], - contentParam: Option[ContentParam], - bankId:Option[String], - isVersion4OrHigher:Boolean - ): JValue = { - var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString) - CacheKeyFromArguments.buildCacheKey { - Caching.memoizeSyncWithProvider (Some(cacheKey.toString())) (getDynamicResourceDocsTTL second) { - val dynamicDocs = allDynamicResourceDocs - .filter(rd => if (bankId.isDefined) rd.createdByBankId == bankId else true) - .map(it => it.specifiedUrl match { - case Some(_) => it - case _ => - it.specifiedUrl = if(it.partialFunctionName.startsWith("dynamicEntity"))Some(s"/${it.implementedInApiVersion.urlPrefix}/${ApiVersion.`dynamic-entity`}${it.requestUrl}") else Some(s"/${it.implementedInApiVersion.urlPrefix}/${ApiVersion.`dynamic-endpoint`}${it.requestUrl}") - it - }) - .toList - - val filteredDocs = resourceDocTags match { - // We have tags - case Some(tags) => { - // This can create duplicates to use toSet below - for { - r <- dynamicDocs - t <- tags - if r.tags.contains(t) - } yield { - r - } - } - // tags param was not mentioned in url or was empty, so return all - case None => dynamicDocs + resourceDocTags: Option[List[ResourceDocTag]], + partialFunctionNames: Option[List[String]], + locale: Option[String], + bankId: Option[String], + isVersion4OrHigher: Boolean + ): JValue = { + val dynamicDocs = allDynamicResourceDocs + .filter(rd => if (bankId.isDefined) rd.createdByBankId == bankId else true) + .map(it => it.specifiedUrl match { + case Some(_) => it + case _ => + it.specifiedUrl = if (it.partialFunctionName.startsWith("dynamicEntity")) Some(s"/${it.implementedInApiVersion.urlPrefix}/${ApiVersion.`dynamic-entity`}${it.requestUrl}") else Some(s"/${it.implementedInApiVersion.urlPrefix}/${ApiVersion.`dynamic-endpoint`}${it.requestUrl}") + it + }) + .toList + + val filteredDocs = resourceDocTags match { + // We have tags + case Some(tags) => { + // This can create duplicates to use toSet below + for { + r <- dynamicDocs + t <- tags + if r.tags.contains(t) + } yield { + r } - - val resourceDocJson = resourceDocsToResourceDocJson(Some(filteredDocs), resourceDocTags, partialFunctionNames, isVersion4OrHigher, locale) - resourceDocJson.map(resourceDocsJsonToJsonResponse).head - }}} + } + // tags param was not mentioned in url or was empty, so return all + case None => dynamicDocs + } + val resourceDocJson = resourceDocsToResourceDocJson(Some(filteredDocs), resourceDocTags, partialFunctionNames, isVersion4OrHigher, locale) + resourceDocJson.map(resourceDocsJsonToJsonResponse).head + } - private def resourceDocsToResourceDocJson(rd: Option[List[ResourceDoc]], - resourceDocTags: Option[List[ResourceDocTag]], - partialFunctionNames: Option[List[String]], - isVersion4OrHigher:Boolean, - locale: Option[String]): Option[ResourceDocsJson] = { + + private def resourceDocsToResourceDocJson( + rd: Option[List[ResourceDoc]], + resourceDocTags: Option[List[ResourceDocTag]], + partialFunctionNames: Option[List[String]], + isVersion4OrHigher: Boolean, + locale: Option[String] + ): Option[ResourceDocsJson] = { for { resourceDocs <- rd } yield { @@ -380,11 +349,6 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth JSONFactory1_4_0.createResourceDocsJson(rdFiltered, isVersion4OrHigher, locale) } } - - def upperName(name: String): (String, String) = (name.toUpperCase(), name) - - - val exampleResourceDoc = ResourceDoc( dummy(implementedInApiVersion.toString, "DUMMY"), @@ -588,7 +552,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth if (cacheValueFromRedis != null) { Full(json.parse(cacheValueFromRedis)) } else { - val resourceDocJsonJValue = getResourceDocsObpDynamicCached(tags, partialFunctions, locale, contentParam, None, isVersion4OrHigher) + val resourceDocJsonJValue = getResourceDocsObpDynamicCached(tags, partialFunctions, locale, None, isVersion4OrHigher) val jsonString = json.compactRender(resourceDocJsonJValue) Caching.setDynamicResourceDocCache(cacheKey, jsonString) Full(resourceDocJsonJValue) @@ -604,7 +568,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth if (cacheValueFromRedis != null) { Full(json.parse(cacheValueFromRedis)) } else { - val resourceDocJsonJValue = getStaticResourceDocsObpCached(requestedApiVersionString, tags, partialFunctions, locale, contentParam, isVersion4OrHigher) + val resourceDocJsonJValue = getStaticResourceDocsObpCached(requestedApiVersionString, tags, partialFunctions, locale, isVersion4OrHigher) val jsonString = json.compactRender(resourceDocJsonJValue) Caching.setStaticResourceDocCache(cacheKey, jsonString) Full(resourceDocJsonJValue) @@ -669,8 +633,18 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth ) } requestedApiVersion <- NewStyle.function.tryons(s"$InvalidApiVersionString $requestedApiVersionString", 400, callContext) {ApiVersionUtils.valueOf(requestedApiVersionString)} + cacheKey = (locale.toString + tags + partialFunctions+ contentParam+ false).intern() + json <- NewStyle.function.tryons(s"$UnknownError Can not create dynamic resource docs.", 400, callContext) { - getResourceDocsObpDynamicCached(tags, partialFunctions, locale, contentParam, Some(bankId), false) + val cacheValueFromRedis = Caching.getDynamicResourceDocCache(cacheKey) + if (cacheValueFromRedis != null) { + json.parse(cacheValueFromRedis) + } else { + val resourceDocJsonJValue = getResourceDocsObpDynamicCached(tags, partialFunctions, locale, None, false) + val jsonString = json.compactRender(resourceDocJsonJValue) + Caching.setDynamicResourceDocCache(cacheKey, jsonString) + resourceDocJsonJValue + } } } yield { (Full(json), HttpCode.`200`(callContext)) @@ -726,7 +700,21 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth _ <- Helper.booleanToFuture(failMsg = s"$ApiVersionNotSupported Current Version is $requestedApiVersionString", cc=cc.callContext) { versionIsAllowed(requestedApiVersion) } - staticJson <- Future(getResourceDocsSwaggerCached(requestedApiVersionString, resourceDocTags, partialFunctions).getOrElse(JNull)) + cacheKey = requestedApiVersionString + resourceDocTags+ partialFunctions + staticJson <- NewStyle.function.tryons(s"$UnknownError Can not convert internal swagger file.", 400, cc.callContext) { + val cacheValueFromRedis = Caching.getStaticSwaggerDocCache(cacheKey) + + val dynamicDocs: JValue = + if (cacheValueFromRedis != null) { + json.parse(cacheValueFromRedis) + } else { + val resourceDocJsonJValue = getResourceDocsSwaggerCached(requestedApiVersionString, resourceDocTags, partialFunctions) + val jsonString = json.compactRender(resourceDocJsonJValue) + Caching.setStaticSwaggerDocCache(cacheKey, jsonString) + resourceDocJsonJValue + } + dynamicDocs + } dynamicJson <- Future(getResourceDocsSwagger(requestedApiVersionString, resourceDocTags, partialFunctions).getOrElse(JNull)) } yield { (staticJson.merge(dynamicJson), HttpCode.`200`(cc.callContext)) @@ -736,27 +724,22 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth } - private def getResourceDocsSwaggerCached(requestedApiVersionString : String, resourceDocTags: Option[List[ResourceDocTag]], partialFunctionNames: Option[List[String]]) : Box[JValue] = { - /** - * Please note that "var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)" - * is just a temporary value field with UUID values in order to prevent any ambiguity. - * The real value will be assigned by Macro during compile time at this line of a code: - * https://github.com/OpenBankProject/scala-macros/blob/master/macros/src/main/scala/com/tesobe/CacheKeyFromArgumentsMacro.scala#L49 - */ - var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString) - CacheKeyFromArguments.buildCacheKey { - Caching.memoizeSyncWithProvider (Some(cacheKey.toString())) (getStaticResourceDocsTTL second) { - logger.debug(s"Generating Swagger-getResourceDocsSwaggerCached requestedApiVersion is $requestedApiVersionString") - - Box.tryo(ApiVersionUtils.valueOf(requestedApiVersionString)) match { - case Full(requestedApiVersion) => - val resourceDocs: Option[List[ResourceDoc]] = getResourceDocsList(requestedApiVersion) - getResourceDocsSwagger(requestedApiVersionString, resourceDocTags, partialFunctionNames, resourceDocs) - case e => - (e ?~! InvalidApiVersionString).asInstanceOf[Box[JValue]] - } - } + private def getResourceDocsSwaggerCached( + requestedApiVersionString: String, + resourceDocTags: Option[List[ResourceDocTag]], + partialFunctionNames: Option[List[String]] + ) : JValue = { + logger.debug(s"Generating Swagger-getResourceDocsSwaggerCached requestedApiVersion is $requestedApiVersionString") + + val a = Box.tryo(ApiVersionUtils.valueOf(requestedApiVersionString)) match { + case Full(requestedApiVersion) => + val resourceDocs: Option[List[ResourceDoc]] = getResourceDocsList(requestedApiVersion) + getResourceDocsSwagger(requestedApiVersionString, resourceDocTags, partialFunctionNames, resourceDocs) + case e => + (e ?~! InvalidApiVersionString).asInstanceOf[Box[JValue]] } + + a.head } // if not supply resourceDocs parameter, just get dynamic ResourceDocs swagger diff --git a/obp-api/src/main/scala/code/api/cache/Caching.scala b/obp-api/src/main/scala/code/api/cache/Caching.scala index be58082d27..f9b0695776 100644 --- a/obp-api/src/main/scala/code/api/cache/Caching.scala +++ b/obp-api/src/main/scala/code/api/cache/Caching.scala @@ -116,5 +116,13 @@ object Caching extends MdcLoggable { def setAllResourceDocCache(key:String, value: String)={ jedis.set(ALL_RESOURCE_DOC_CACHE_KEY_PREFIX+key,value) } + + def getStaticSwaggerDocCache(key: String) = { + jedis.get(STATIC_SWAGGER_DOC_CACHE_KEY_PREFIX + key) + } + + def setStaticSwaggerDocCache(key:String, value: String)={ + jedis.set(STATIC_SWAGGER_DOC_CACHE_KEY_PREFIX+key,value) + } } diff --git a/obp-api/src/main/scala/code/api/constant/constant.scala b/obp-api/src/main/scala/code/api/constant/constant.scala index a75408fc65..15d7159136 100644 --- a/obp-api/src/main/scala/code/api/constant/constant.scala +++ b/obp-api/src/main/scala/code/api/constant/constant.scala @@ -93,6 +93,7 @@ object Constant extends MdcLoggable { final val DYNAMIC_RESOURCE_DOC_CACHE_KEY_PREFIX = "rd_dynamic_" final val STATIC_RESOURCE_DOC_CACHE_KEY_PREFIX = "rd_static_" final val ALL_RESOURCE_DOC_CACHE_KEY_PREFIX = "rd_all_" + final val STATIC_SWAGGER_DOC_CACHE_KEY_PREFIX = "swagger_static_" } diff --git a/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala b/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala index f64fa87b98..62003d0e6f 100644 --- a/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala +++ b/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala @@ -4,7 +4,7 @@ import code.api.berlin.group.v1_3.JvalueCaseClass import code.api.cache.Caching import java.util.Date -import code.api.util.APIUtil.{EmptyBody, PrimaryDataBody, ResourceDoc, createLocalisedResourceDocJsonTTL} +import code.api.util.APIUtil.{EmptyBody, PrimaryDataBody, ResourceDoc} import code.api.util.ApiTag.ResourceDocTag import code.api.util.Glossary.glossaryItems import code.api.util.{APIUtil, ApiRole, ConnectorField, CustomJsonFormats, ExampleValue, I18NUtil, PegdownOptions} diff --git a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala index cdb00ec12b..ab00f7d50c 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala @@ -11819,8 +11819,6 @@ trait APIMethods400 { "/management/endpoints/OPERATION_ID/tags", "Create System Level Endpoint Tag", s"""Create System Level Endpoint Tag - | - |Note: Resource Docs are cached, TTL is ${createLocalisedResourceDocJsonTTL} | |""".stripMargin, endpointTagJson400, @@ -11863,8 +11861,6 @@ trait APIMethods400 { "/management/endpoints/OPERATION_ID/tags/ENDPOINT_TAG_ID", "Update System Level Endpoint Tag", s"""Update System Level Endpoint Tag, you can only update the tag_name here, operation_id can not be updated. - | - |Note: Resource Docs are cached, TTL is ${createLocalisedResourceDocJsonTTL} | |""".stripMargin, endpointTagJson400, @@ -11971,8 +11967,6 @@ trait APIMethods400 { "/management/banks/BANK_ID/endpoints/OPERATION_ID/tags", "Create Bank Level Endpoint Tag", s"""Create Bank Level Endpoint Tag - | - |Note: Resource Docs are cached, TTL is ${createLocalisedResourceDocJsonTTL} | | |""".stripMargin, @@ -12018,8 +12012,6 @@ trait APIMethods400 { "/management/banks/BANK_ID/endpoints/OPERATION_ID/tags/ENDPOINT_TAG_ID", "Update Bank Level Endpoint Tag", s"""Update Endpoint Tag, you can only update the tag_name here, operation_id can not be updated. - | - |Note: Resource Docs are cached, TTL is ${createLocalisedResourceDocJsonTTL} | |""".stripMargin, endpointTagJson400, From a2875244d0e65fb801af4b9425531077a5e81a90 Mon Sep 17 00:00:00 2001 From: hongwei Date: Wed, 8 Nov 2023 10:50:47 +0100 Subject: [PATCH 08/15] feature/use our own cache method-removed the test first --- obp-api/src/test/scala/code/RedisTest.scala | 50 --------------------- 1 file changed, 50 deletions(-) delete mode 100644 obp-api/src/test/scala/code/RedisTest.scala diff --git a/obp-api/src/test/scala/code/RedisTest.scala b/obp-api/src/test/scala/code/RedisTest.scala deleted file mode 100644 index b2aea36e65..0000000000 --- a/obp-api/src/test/scala/code/RedisTest.scala +++ /dev/null @@ -1,50 +0,0 @@ -package code - -import code.api.util.{APIUtil, CustomJsonFormats} -import code.util.Helper.MdcLoggable -import net.liftweb.json -import net.liftweb.json.{Extraction, Formats} -import org.scalatest.{FeatureSpec, GivenWhenThen, Matchers} -import redis.clients.jedis.Jedis - -class RedisTest extends FeatureSpec with Matchers with GivenWhenThen with MdcLoggable { - - val url = APIUtil.getPropsValue("cache.redis.url", "127.0.0.1") - val port = APIUtil.getPropsAsIntValue("cache.redis.port", 6379) - implicit def formats: Formats = CustomJsonFormats.formats - - lazy val jedis = new Jedis(url, port) - - feature("The Unit test for Redis") - { - scenario("test the simple key value") - { - jedis.set("BasicObjectKey", "this is for testing") - val result = jedis.get("BasicObjectKey") - result should equal ("this is for testing") - val result2 = jedis.get("BasicObjectKey1") - val result3 = jedis.get("BasicObjectKey1") - } - - scenario("test the basic case class") - { - case class BasicObject( - customer_number: String, - legal_name: String - ) - - val basicObject = BasicObject("customer_number_123","Tom") - val redisValue = json.compactRender(Extraction.decompose(basicObject)) - jedis.set("BasicObjectKey", redisValue) - val result = jedis.get("BasicObjectKey") - println(result) - result should equal (""""[{"$outer":{},"customer_number":"customer_number_123","legal_name":"Tom"}]"""") - } - - } - - - - - -} \ No newline at end of file From d8fea2172e8a83898c1d8e40db7a468aafb4b75b Mon Sep 17 00:00:00 2001 From: hongwei Date: Mon, 6 Nov 2023 15:41:33 +0100 Subject: [PATCH 09/15] refactor/use constant value for isRedisAvailable cacheKey --- obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala b/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala index 1be4e88be8..d1c36fc335 100644 --- a/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala +++ b/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala @@ -80,10 +80,10 @@ object RateLimitingUtil extends MdcLoggable { def isRedisAvailable() = { try { - val uuid = APIUtil.generateUUID() + val key = "OBP_Check_isRedisAvailable" jedis.connect() - jedis.set(uuid, "10") - jedis.exists(uuid) == true + jedis.set(key, "10") + jedis.exists(key) == true } catch { case e : Throwable => logger.warn("------------| RateLimitUtil.isRedisAvailable |------------") From 0d818cc4627538874e4990d684eb7e8be0e892a6 Mon Sep 17 00:00:00 2001 From: hongwei Date: Mon, 6 Nov 2023 17:02:06 +0100 Subject: [PATCH 10/15] feature/use our own cache method --- .../ResourceDocsAPIMethods.scala | 56 ++++++++++++--- .../main/scala/code/api/cache/Caching.scala | 12 ++++ .../src/main/scala/code/api/cache/Redis.scala | 6 +- .../code/api/v1_4_0/JSONFactory1_4_0.scala | 71 +++++++++---------- obp-api/src/test/scala/code/RedisTest.scala | 50 +++++++++++++ 5 files changed, 146 insertions(+), 49 deletions(-) create mode 100644 obp-api/src/test/scala/code/RedisTest.scala diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala index 795e46d3f6..751f03f53e 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala @@ -581,15 +581,55 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth Future(resourceDocsJsonJValue.map(successJsonResponse(_))) case _ => contentParam match { - case Some(DYNAMIC) => - val dynamicDocs: Box[JValue] = Full (getResourceDocsObpDynamicCached(tags, partialFunctions, locale, contentParam,None, isVersion4OrHigher)) + case Some(DYNAMIC) =>{ + val cacheKey = ("getResourceDocsObpDynamicCached" + tags + partialFunctions+ locale+ contentParam+ isVersion4OrHigher).intern() + val cacheValueFromRedis = Caching.getResourceDocCache(cacheKey) + + val dynamicDocs: Box[JValue] = + if (cacheValueFromRedis != null) { + Full(json.parse(cacheValueFromRedis)) + } else { + val resourceDocJsonJValue = getResourceDocsObpDynamicCached(tags, partialFunctions, locale, contentParam, None, isVersion4OrHigher) + val jsonString = json.compactRender(resourceDocJsonJValue) + Caching.setResourceDocCache(cacheKey, jsonString) + Full(resourceDocJsonJValue) + } + Future(dynamicDocs.map(successJsonResponse(_))) - case Some(STATIC) => - val staticDocs: Box[JValue] = Full(getStaticResourceDocsObpCached(requestedApiVersionString, tags, partialFunctions, locale, contentParam, isVersion4OrHigher)) - Future(staticDocs.map(successJsonResponse(_))) - case _ => - val docs: Box[JValue] = Full(getAllResourceDocsObpCached(requestedApiVersionString, tags, partialFunctions, locale, contentParam, isVersion4OrHigher)) - Future(docs.map(successJsonResponse(_))) + } + + case Some(STATIC) => { + val cacheKey = ("getStaticResourceDocsObpCached" + tags + partialFunctions + locale + contentParam + isVersion4OrHigher).intern() + val cacheValueFromRedis = Caching.getResourceDocCache(cacheKey) + + val dynamicDocs: Box[JValue] = + if (cacheValueFromRedis != null) { + Full(json.parse(cacheValueFromRedis)) + } else { + val resourceDocJsonJValue = getStaticResourceDocsObpCached(requestedApiVersionString, tags, partialFunctions, locale, contentParam, isVersion4OrHigher) + val jsonString = json.compactRender(resourceDocJsonJValue) + Caching.setResourceDocCache(cacheKey, jsonString) + Full(resourceDocJsonJValue) + } + + Future(dynamicDocs.map(successJsonResponse(_))) + } + case _ => { + val cacheKey = ("getAllResourceDocsObpCached" + tags + partialFunctions + locale + contentParam + isVersion4OrHigher).intern() + val cacheValueFromRedis = Caching.getResourceDocCache(cacheKey) + + val dynamicDocs: Box[JValue] = + if (cacheValueFromRedis != null) { + Full(json.parse(cacheValueFromRedis)) + } else { + val resourceDocJsonJValue = getAllResourceDocsObpCached(requestedApiVersionString, tags, partialFunctions, locale, contentParam, isVersion4OrHigher) + val jsonString = json.compactRender(resourceDocJsonJValue) + Caching.setResourceDocCache(cacheKey, jsonString) + Full(resourceDocJsonJValue) + } + + Future(dynamicDocs.map(successJsonResponse(_))) + } } } } yield { diff --git a/obp-api/src/main/scala/code/api/cache/Caching.scala b/obp-api/src/main/scala/code/api/cache/Caching.scala index 5f2f23733d..8ca23ad441 100644 --- a/obp-api/src/main/scala/code/api/cache/Caching.scala +++ b/obp-api/src/main/scala/code/api/cache/Caching.scala @@ -1,5 +1,6 @@ package code.api.cache +import code.api.cache.Redis.jedis import code.api.util.RateLimitingUtil import code.util.Helper.MdcLoggable import com.softwaremill.macmemo.{Cache, MemoCacheBuilder, MemoizeParams} @@ -84,4 +85,15 @@ object Caching extends MdcLoggable { } } + final val resourceDocCacheKeyPrefix = "rd_" + + + def getResourceDocCache(key: String) = { + jedis.get(resourceDocCacheKeyPrefix + key) + } + + def setResourceDocCache(key:String, value: String)={ + jedis.set(resourceDocCacheKeyPrefix+key,value) + } + } diff --git a/obp-api/src/main/scala/code/api/cache/Redis.scala b/obp-api/src/main/scala/code/api/cache/Redis.scala index 84d6051452..d691ad48e2 100644 --- a/obp-api/src/main/scala/code/api/cache/Redis.scala +++ b/obp-api/src/main/scala/code/api/cache/Redis.scala @@ -22,10 +22,10 @@ object Redis extends MdcLoggable { def isRedisAvailable() = { try { - val uuid = APIUtil.generateUUID() + val key = "OBP_Check_isRedisAvailable" jedis.connect() - jedis.set(uuid, "10") - jedis.exists(uuid) == true + jedis.set(key, "10") + jedis.exists(key) == true } catch { case e: Throwable => logger.warn("------------| Redis.isRedisAvailable |------------") diff --git a/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala b/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala index 6f96f5bcb4..55130ea332 100644 --- a/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala +++ b/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala @@ -2,6 +2,7 @@ package code.api.v1_4_0 import code.api.berlin.group.v1_3.JvalueCaseClass import code.api.cache.Caching + import java.util.Date import code.api.util.APIUtil.{EmptyBody, PrimaryDataBody, ResourceDoc, createLocalisedResourceDocJsonTTL} import code.api.util.ApiTag.ResourceDocTag @@ -17,13 +18,15 @@ import com.openbankproject.commons.util.{EnumValue, JsonUtils, OBPEnumeration, R import net.liftweb.common.Full import net.liftweb.json import net.liftweb.json.Extraction.decompose -import net.liftweb.json.{Formats, JDouble, JInt, JString} +import net.liftweb.json.{Extraction, Formats, JDouble, JInt, JString} import net.liftweb.json.JsonAST.{JArray, JBool, JNothing, JObject, JValue} import net.liftweb.util.StringHelpers import code.util.Helper.MdcLoggable +import com.github.dwickern.macros.NameOf.nameOf import com.tesobe.{CacheKeyFromArguments, CacheKeyOmit} import org.apache.commons.lang3.StringUtils import scalacache.memoization.cacheKeyExclude + import java.util.regex.Pattern import java.lang.reflect.Field import java.util.UUID.randomUUID @@ -517,35 +520,19 @@ object JSONFactory1_4_0 extends MdcLoggable{ jsonFieldsDescription.mkString(jsonTitleType,"","\n") } + //cache key will only contain "operationId + locale" + def createLocalisedResourceDocJsonCached( operationId: String, // this will be in the cacheKey locale: Option[String],// this will be in the cacheKey - @CacheKeyOmit resourceDocUpdatedTags: ResourceDoc, - @CacheKeyOmit isVersion4OrHigher:Boolean, - @CacheKeyOmit urlParametersI18n:String , - @CacheKeyOmit jsonRequestBodyFieldsI18n:String, - @CacheKeyOmit jsonResponseBodyFieldsI18n:String + resourceDocUpdatedTags: ResourceDoc, + isVersion4OrHigher:Boolean, + urlParametersI18n:String , + jsonRequestBodyFieldsI18n:String, + jsonResponseBodyFieldsI18n:String ): ResourceDocJson = { - /** - * Please note that "var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)" - * is just a temporary value field with UUID values in order to prevent any ambiguity. - * The real value will be assigned by Macro during compile time at this line of a code: - * https://github.com/OpenBankProject/scala-macros/blob/master/macros/src/main/scala/com/tesobe/CacheKeyFromArgumentsMacro.scala#L49 - */ - var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString) - CacheKeyFromArguments.buildCacheKey { - Caching.memoizeSyncWithProvider(Some(cacheKey.toString()))(createLocalisedResourceDocJsonTTL second) { - // There are multiple flavours of markdown. For instance, original markdown emphasises underscores (surrounds _ with ()) - // But we don't want to have to escape underscores (\_) in our documentation - // Thus we use a flavour of markdown that ignores underscores in words. (Github markdown does this too) - // We return html rather than markdown to the consumer so they don't have to bother with these questions. - - //Here area some endpoints, which should not be added the description: - // 1st: Dynamic entity endpoint, - // 2rd: Dynamic endpoint endpoints, - // 3rd: all the user created endpoints, - val fieldsDescription = + val fieldsDescription = if (resourceDocUpdatedTags.tags.toString.contains("Dynamic-Entity") || resourceDocUpdatedTags.tags.toString.contains("Dynamic-Endpoint") || resourceDocUpdatedTags.roles.toString.contains("DynamicEntity") @@ -603,26 +590,34 @@ object JSONFactory1_4_0 extends MdcLoggable{ logger.trace(s"createLocalisedResourceDocJsonCached value is $resourceDoc") resourceDoc - - } } - } + def createLocalisedResourceDocJson(rd: ResourceDoc, isVersion4OrHigher:Boolean, locale: Option[String], 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 $createLocalisedResourceDocJsonTTL seconds val userDefinedEndpointTags = getAllEndpointTagsBox(rd.operationId).map(endpointTag =>ResourceDocTag(endpointTag.tagName)) val resourceDocWithUserDefinedEndpointTags: ResourceDoc = rd.copy(tags = userDefinedEndpointTags++ rd.tags) - logger.trace(s"createLocalisedResourceDocJsonCached key is ${resourceDocWithUserDefinedEndpointTags.operationId + resourceDocWithUserDefinedEndpointTags.operationId}") - createLocalisedResourceDocJsonCached( - resourceDocWithUserDefinedEndpointTags.operationId, - locale: Option[String], - resourceDocWithUserDefinedEndpointTags, - isVersion4OrHigher: Boolean, - urlParametersI18n: String, - jsonRequestBodyFieldsI18n: String, - jsonResponseBodyFieldsI18n: String - ) + val cacheKey = ("createLocalisedResourceDocJson"+ resourceDocWithUserDefinedEndpointTags.operationId + locale).intern() + val cacheValueFromRedis = Caching.getResourceDocCache(cacheKey) + + if(cacheValueFromRedis != null){ + json.parse(cacheValueFromRedis).extract[ResourceDocJson] + }else{ + val resourceDocJson = createLocalisedResourceDocJsonCached( + resourceDocWithUserDefinedEndpointTags.operationId, + locale: Option[String], + resourceDocWithUserDefinedEndpointTags, + isVersion4OrHigher: Boolean, + urlParametersI18n: String, + jsonRequestBodyFieldsI18n: String, + jsonResponseBodyFieldsI18n: String + ) + val jsonString = json.compactRender(Extraction.decompose(resourceDocJson)) + Caching.setResourceDocCache(cacheKey,jsonString) + + resourceDocJson + } } diff --git a/obp-api/src/test/scala/code/RedisTest.scala b/obp-api/src/test/scala/code/RedisTest.scala new file mode 100644 index 0000000000..b2aea36e65 --- /dev/null +++ b/obp-api/src/test/scala/code/RedisTest.scala @@ -0,0 +1,50 @@ +package code + +import code.api.util.{APIUtil, CustomJsonFormats} +import code.util.Helper.MdcLoggable +import net.liftweb.json +import net.liftweb.json.{Extraction, Formats} +import org.scalatest.{FeatureSpec, GivenWhenThen, Matchers} +import redis.clients.jedis.Jedis + +class RedisTest extends FeatureSpec with Matchers with GivenWhenThen with MdcLoggable { + + val url = APIUtil.getPropsValue("cache.redis.url", "127.0.0.1") + val port = APIUtil.getPropsAsIntValue("cache.redis.port", 6379) + implicit def formats: Formats = CustomJsonFormats.formats + + lazy val jedis = new Jedis(url, port) + + feature("The Unit test for Redis") + { + scenario("test the simple key value") + { + jedis.set("BasicObjectKey", "this is for testing") + val result = jedis.get("BasicObjectKey") + result should equal ("this is for testing") + val result2 = jedis.get("BasicObjectKey1") + val result3 = jedis.get("BasicObjectKey1") + } + + scenario("test the basic case class") + { + case class BasicObject( + customer_number: String, + legal_name: String + ) + + val basicObject = BasicObject("customer_number_123","Tom") + val redisValue = json.compactRender(Extraction.decompose(basicObject)) + jedis.set("BasicObjectKey", redisValue) + val result = jedis.get("BasicObjectKey") + println(result) + result should equal (""""[{"$outer":{},"customer_number":"customer_number_123","legal_name":"Tom"}]"""") + } + + } + + + + + +} \ No newline at end of file From a28720aa9404dd82b5a9deeaf1473d6eeebaae69 Mon Sep 17 00:00:00 2001 From: hongwei Date: Wed, 8 Nov 2023 12:48:57 +0100 Subject: [PATCH 11/15] refactor/use logger instead of println --- obp-api/src/main/scala/code/api/cache/Redis.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obp-api/src/main/scala/code/api/cache/Redis.scala b/obp-api/src/main/scala/code/api/cache/Redis.scala index d691ad48e2..803a9388e9 100644 --- a/obp-api/src/main/scala/code/api/cache/Redis.scala +++ b/obp-api/src/main/scala/code/api/cache/Redis.scala @@ -54,7 +54,7 @@ object Redis extends MdcLoggable { tryDecode match { case Success(v) => v.asInstanceOf[T] case Failure(e) => - println(e) + logger.error(e) "NONE".asInstanceOf[T] } } From 85df1a8a962842714b66cc3f43878f7ae1413c58 Mon Sep 17 00:00:00 2001 From: hongwei Date: Wed, 8 Nov 2023 13:05:22 +0100 Subject: [PATCH 12/15] feature/use our own cache method - check Redis status before use --- .../main/scala/code/api/cache/Caching.scala | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/obp-api/src/main/scala/code/api/cache/Caching.scala b/obp-api/src/main/scala/code/api/cache/Caching.scala index f9b0695776..3a03b49a0f 100644 --- a/obp-api/src/main/scala/code/api/cache/Caching.scala +++ b/obp-api/src/main/scala/code/api/cache/Caching.scala @@ -86,43 +86,73 @@ object Caching extends MdcLoggable { } def getLocalisedResourceDocCache(key: String) = { - jedis.get(LOCALISED_RESOURCE_DOC_PREFIX + key) + if(Redis.isRedisAvailable()) + jedis.get(LOCALISED_RESOURCE_DOC_PREFIX + key) + else + null } def setLocalisedResourceDocCache(key:String, value: String)={ - jedis.set(LOCALISED_RESOURCE_DOC_PREFIX+key,value) + if (Redis.isRedisAvailable()) + jedis.set(LOCALISED_RESOURCE_DOC_PREFIX+key,value) + else + null } def getDynamicResourceDocCache(key: String) = { - jedis.get(DYNAMIC_RESOURCE_DOC_CACHE_KEY_PREFIX + key) + if (Redis.isRedisAvailable()) + jedis.get(DYNAMIC_RESOURCE_DOC_CACHE_KEY_PREFIX + key) + else + null } def setDynamicResourceDocCache(key:String, value: String)={ - jedis.set(DYNAMIC_RESOURCE_DOC_CACHE_KEY_PREFIX+key,value) + if (Redis.isRedisAvailable()) + jedis.set(DYNAMIC_RESOURCE_DOC_CACHE_KEY_PREFIX+key,value) + else + null } def getStaticResourceDocCache(key: String) = { - jedis.get(STATIC_RESOURCE_DOC_CACHE_KEY_PREFIX + key) + if (Redis.isRedisAvailable()) + jedis.get(STATIC_RESOURCE_DOC_CACHE_KEY_PREFIX + key) + else + null } def setStaticResourceDocCache(key:String, value: String)={ - jedis.set(STATIC_RESOURCE_DOC_CACHE_KEY_PREFIX+key,value) + if (Redis.isRedisAvailable()) + jedis.set(STATIC_RESOURCE_DOC_CACHE_KEY_PREFIX+key,value) + else + null } def getAllResourceDocCache(key: String) = { - jedis.get(ALL_RESOURCE_DOC_CACHE_KEY_PREFIX + key) + if (Redis.isRedisAvailable()) + jedis.get(ALL_RESOURCE_DOC_CACHE_KEY_PREFIX + key) + else + null } def setAllResourceDocCache(key:String, value: String)={ - jedis.set(ALL_RESOURCE_DOC_CACHE_KEY_PREFIX+key,value) + if (Redis.isRedisAvailable()) + jedis.set(ALL_RESOURCE_DOC_CACHE_KEY_PREFIX+key,value) + else + null } def getStaticSwaggerDocCache(key: String) = { - jedis.get(STATIC_SWAGGER_DOC_CACHE_KEY_PREFIX + key) + if (Redis.isRedisAvailable()) + jedis.get(STATIC_SWAGGER_DOC_CACHE_KEY_PREFIX + key) + else + null } def setStaticSwaggerDocCache(key:String, value: String)={ - jedis.set(STATIC_SWAGGER_DOC_CACHE_KEY_PREFIX+key,value) + if (Redis.isRedisAvailable()) + jedis.set(STATIC_SWAGGER_DOC_CACHE_KEY_PREFIX+key,value) + else + null } } From 17f9ee35982755327d74b8e69986ac5c460a69f8 Mon Sep 17 00:00:00 2001 From: hongwei Date: Wed, 8 Nov 2023 13:09:59 +0100 Subject: [PATCH 13/15] feature/use our own cache method - remove RedisTest first --- obp-api/src/test/scala/code/RedisTest.scala | 50 --------------------- 1 file changed, 50 deletions(-) delete mode 100644 obp-api/src/test/scala/code/RedisTest.scala diff --git a/obp-api/src/test/scala/code/RedisTest.scala b/obp-api/src/test/scala/code/RedisTest.scala deleted file mode 100644 index b2aea36e65..0000000000 --- a/obp-api/src/test/scala/code/RedisTest.scala +++ /dev/null @@ -1,50 +0,0 @@ -package code - -import code.api.util.{APIUtil, CustomJsonFormats} -import code.util.Helper.MdcLoggable -import net.liftweb.json -import net.liftweb.json.{Extraction, Formats} -import org.scalatest.{FeatureSpec, GivenWhenThen, Matchers} -import redis.clients.jedis.Jedis - -class RedisTest extends FeatureSpec with Matchers with GivenWhenThen with MdcLoggable { - - val url = APIUtil.getPropsValue("cache.redis.url", "127.0.0.1") - val port = APIUtil.getPropsAsIntValue("cache.redis.port", 6379) - implicit def formats: Formats = CustomJsonFormats.formats - - lazy val jedis = new Jedis(url, port) - - feature("The Unit test for Redis") - { - scenario("test the simple key value") - { - jedis.set("BasicObjectKey", "this is for testing") - val result = jedis.get("BasicObjectKey") - result should equal ("this is for testing") - val result2 = jedis.get("BasicObjectKey1") - val result3 = jedis.get("BasicObjectKey1") - } - - scenario("test the basic case class") - { - case class BasicObject( - customer_number: String, - legal_name: String - ) - - val basicObject = BasicObject("customer_number_123","Tom") - val redisValue = json.compactRender(Extraction.decompose(basicObject)) - jedis.set("BasicObjectKey", redisValue) - val result = jedis.get("BasicObjectKey") - println(result) - result should equal (""""[{"$outer":{},"customer_number":"customer_number_123","legal_name":"Tom"}]"""") - } - - } - - - - - -} \ No newline at end of file From 2c3a8a732712239921c3a720779ab2b392d9f50a Mon Sep 17 00:00:00 2001 From: hongwei Date: Wed, 8 Nov 2023 13:37:45 +0100 Subject: [PATCH 14/15] feature/use our own cache method - remove the unused props in release_notes.md --- obp-api/src/main/scala/code/api/util/APIUtil.scala | 2 -- release_notes.md | 4 ---- 2 files changed, 6 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/APIUtil.scala b/obp-api/src/main/scala/code/api/util/APIUtil.scala index 71cbf62a4d..993c693413 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -4872,6 +4872,4 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ ) } - val createLocalisedResourceDocJsonTTL : Int = APIUtil.getPropsValue(s"createLocalisedResourceDocJson.cache.ttl.seconds", "3600").toInt - } diff --git a/release_notes.md b/release_notes.md index 45db4d4570..6899733419 100644 --- a/release_notes.md +++ b/release_notes.md @@ -3,7 +3,6 @@ ### Most recent changes at top of file ``` Date Commit Action -30/10/2023 4e82c66c Added createLocalisedResourceDocJson.cache.ttl.seconds, default is 3600 13/10/2023 d87c99d8 Added props hikari.connectionTimeout, default is from hikari. Added props hikari.maximumPoolSize, default is from hikari. Added props hikari.idleTimeout, default is from hikari. @@ -42,9 +41,6 @@ Date Commit Action 18/08/2021 83c3b4fa Added props: webui_favicon_link_url, default is /favicon.ico 18/08/2021 5924c820 Added props: webui_api_documentation_bottom_url, default is https://github.com/OpenBankProject/OBP-API/wiki Added props: webui_privacy_policy_url, default is https://openbankproject.com/privacy-policy -30/06/2021 cf2dd987 Changed props, static will cache 24 hours, dynamic only 1 hour as default. - dynamicResourceDocsObp.cache.ttl.seconds=3600 - staticResourceDocsObp.cache.ttl.seconds=3600 30/06/2021 cf2dd987 Added props: email_domain_to_entitlement_mappings, default is empty We can automatically grant the Entitlements required to the User has access to via their validated email domain. Entitlements are generated /refreshed both following manual locin and Direct Login token generation (POST). From baf8f9802ca67a0cfe7bc9d184cf7f6d5c1304c3 Mon Sep 17 00:00:00 2001 From: hongwei Date: Wed, 8 Nov 2023 15:13:47 +0100 Subject: [PATCH 15/15] feature/use our own cache method - use on redis and tweak the error handling --- .../src/main/scala/code/api/cache/Redis.scala | 13 ++++++------ .../code/api/util/RateLimitingUtil.scala | 20 +------------------ .../scala/code/api/v3_1_0/APIMethods310.scala | 5 +++-- .../scala/code/api/v3_1_0/RateLimitTest.scala | 14 +++++++------ .../code/api/v4_0_0/RateLimitingTest.scala | 19 +++++++++--------- 5 files changed, 29 insertions(+), 42 deletions(-) diff --git a/obp-api/src/main/scala/code/api/cache/Redis.scala b/obp-api/src/main/scala/code/api/cache/Redis.scala index 803a9388e9..d4e4a5faf1 100644 --- a/obp-api/src/main/scala/code/api/cache/Redis.scala +++ b/obp-api/src/main/scala/code/api/cache/Redis.scala @@ -22,14 +22,15 @@ object Redis extends MdcLoggable { def isRedisAvailable() = { try { - val key = "OBP_Check_isRedisAvailable" - jedis.connect() - jedis.set(key, "10") - jedis.exists(key) == true + val status = jedis.isConnected + if (!status) { + logger.warn("------------| Redis is not connected|------------") + } + status } catch { case e: Throwable => - logger.warn("------------| Redis.isRedisAvailable |------------") - logger.warn(e) + logger.error("------------| Redis throw exception|------------") + logger.error(e) false } } diff --git a/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala b/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala index d1c36fc335..5e780d9243 100644 --- a/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala +++ b/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala @@ -1,6 +1,7 @@ package code.api.util import code.api.APIFailureNewStyle +import code.api.cache.Redis.{isRedisAvailable, jedis} import code.api.util.APIUtil.fullBoxOrException import code.api.util.ErrorMessages.TooManyRequests import code.api.util.RateLimitingJson.CallLimit @@ -70,28 +71,9 @@ object RateLimitingJson { object RateLimitingUtil extends MdcLoggable { import code.api.util.RateLimitingPeriod._ - - val port = APIUtil.getPropsAsIntValue("redis_port", 6379) - val url = APIUtil.getPropsValue("redis_address", "127.0.0.1") def useConsumerLimits = APIUtil.getPropsAsBoolValue("use_consumer_limits", false) - lazy val jedis = new Jedis(url, port) - - def isRedisAvailable() = { - try { - val key = "OBP_Check_isRedisAvailable" - jedis.connect() - jedis.set(key, "10") - jedis.exists(key) == true - } catch { - case e : Throwable => - logger.warn("------------| RateLimitUtil.isRedisAvailable |------------") - logger.warn(e) - false - } - } - private def createUniqueKey(consumerKey: String, period: LimitCallPeriod) = consumerKey + RateLimitingPeriod.toString(period) private def underConsumerLimits(consumerKey: String, period: LimitCallPeriod, limit: Long): Boolean = { diff --git a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala index ba3997e138..21730c908b 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala @@ -2,12 +2,13 @@ package code.api.v3_1_0 import code.api.Constant import code.api.Constant.{SYSTEM_OWNER_VIEW_ID, localIdentityProvider} + import java.text.SimpleDateFormat import java.util.UUID import java.util.regex.Pattern - import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ import code.api.ResourceDocs1_4_0.{MessageDocsSwaggerDefinitions, ResourceDocsAPIMethodsUtil, SwaggerDefinitionsJSON, SwaggerJSONFactory} +import code.api.cache.Redis import code.api.util.APIUtil.{getWebUIPropsPairs, _} import code.api.util.ApiRole._ import code.api.util.ApiTag._ @@ -1242,7 +1243,7 @@ trait APIMethods310 { for { (_, callContext) <- anonymousAccess(cc) rateLimiting <- NewStyle.function.tryons("", 400, callContext) { - val isRedisAvailable = RateLimitingUtil.isRedisAvailable() + val isRedisAvailable = Redis.isRedisAvailable() val isActive = if (RateLimitingUtil.useConsumerLimits && isRedisAvailable) true else false RateLimiting(RateLimitingUtil.useConsumerLimits, "REDIS", isRedisAvailable, isActive) } diff --git a/obp-api/src/test/scala/code/api/v3_1_0/RateLimitTest.scala b/obp-api/src/test/scala/code/api/v3_1_0/RateLimitTest.scala index 2c0f4a832a..f89718c257 100644 --- a/obp-api/src/test/scala/code/api/v3_1_0/RateLimitTest.scala +++ b/obp-api/src/test/scala/code/api/v3_1_0/RateLimitTest.scala @@ -25,6 +25,8 @@ TESOBE (http://www.tesobe.com/) */ package code.api.v3_1_0 +import code.api.cache.Redis + import java.time.format.DateTimeFormatter import java.time.{ZoneId, ZonedDateTime} import java.util.Date @@ -176,7 +178,7 @@ class RateLimitTest extends V310ServerSetup with PropsReset { response310.body.extract[CallLimitJson] } scenario("We will set calls limit per second for a Consumer", ApiEndpoint, VersionOfApi) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") @@ -201,7 +203,7 @@ class RateLimitTest extends V310ServerSetup with PropsReset { Consumers.consumers.vend.updateConsumerCallLimits(id, Some("-1"), Some("-1"), Some("-1"), Some("-1"), Some("-1"), Some("-1")) } scenario("We will set calls limit per minute for a Consumer", ApiEndpoint, VersionOfApi) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") @@ -226,7 +228,7 @@ class RateLimitTest extends V310ServerSetup with PropsReset { Consumers.consumers.vend.updateConsumerCallLimits(id, Some("-1"), Some("-1"), Some("-1"), Some("-1"), Some("-1"), Some("-1")) } scenario("We will set calls limit per hour for a Consumer", ApiEndpoint, VersionOfApi) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") @@ -251,7 +253,7 @@ class RateLimitTest extends V310ServerSetup with PropsReset { Consumers.consumers.vend.updateConsumerCallLimits(id, Some("-1"), Some("-1"), Some("-1"), Some("-1"), Some("-1"), Some("-1")) } scenario("We will set calls limit per day for a Consumer", ApiEndpoint, VersionOfApi) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") @@ -276,7 +278,7 @@ class RateLimitTest extends V310ServerSetup with PropsReset { Consumers.consumers.vend.updateConsumerCallLimits(id, Some("-1"), Some("-1"), Some("-1"), Some("-1"), Some("-1"), Some("-1")) } scenario("We will set calls limit per week for a Consumer", ApiEndpoint, VersionOfApi) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") @@ -301,7 +303,7 @@ class RateLimitTest extends V310ServerSetup with PropsReset { Consumers.consumers.vend.updateConsumerCallLimits(id, Some("-1"), Some("-1"), Some("-1"), Some("-1"), Some("-1"), Some("-1")) } scenario("We will set calls limit per month for a Consumer", ApiEndpoint, VersionOfApi) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") diff --git a/obp-api/src/test/scala/code/api/v4_0_0/RateLimitingTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/RateLimitingTest.scala index 73ffe73e58..dc2c0b25a0 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/RateLimitingTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/RateLimitingTest.scala @@ -25,6 +25,7 @@ TESOBE (http://www.tesobe.com/) */ package code.api.v4_0_0 +import code.api.cache.Redis import code.api.util.APIUtil.OAuth._ import code.api.util.ApiRole.{CanSetCallLimits, canCreateDynamicEndpoint} import code.api.util.ErrorMessages.{UserHasMissingRoles, UserNotLoggedIn} @@ -92,7 +93,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { feature("Rate Limit - " + ApiCallsLimit + " - " + ApiVersion400) { scenario("We will try to set Rate Limiting per minute for a Consumer - unauthorized access", ApiCallsLimit, ApiVersion400) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v4.0.0") val response400 = setRateLimitingAnonymousAccess(callLimitJsonInitial) Then("We should get a 401") @@ -101,7 +102,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { response400.body.extract[ErrorMessage].message should equal (UserNotLoggedIn) } scenario("We will try to set Rate Limiting per minute without a proper Role " + ApiRole.canSetCallLimits, ApiCallsLimit, ApiVersion400) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v4.0.0 without a Role " + ApiRole.canSetCallLimits) val response400 = setRateLimitingWithoutRole(user1, callLimitJsonInitial) Then("We should get a 403") @@ -110,7 +111,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { response400.body.extract[ErrorMessage].message should equal (UserHasMissingRoles + CanSetCallLimits) } scenario("We will try to set Rate Limiting per minute with a proper Role " + ApiRole.canSetCallLimits, ApiCallsLimit, ApiVersion400) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits) val response400 = setRateLimiting(user1, callLimitJsonInitial) Then("We should get a 200") @@ -118,7 +119,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { response400.body.extract[CallLimitJsonV400] } scenario("We will set Rate Limiting per second for an Endpoint", ApiCallsLimit, ApiVersion400) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits) val response01 = setRateLimiting(user1, callLimitJsonSecond) Then("We should get a 200") @@ -141,7 +142,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { response04.code should equal(200) } scenario("We will set Rate Limiting per minute for an Endpoint", ApiCallsLimit, ApiVersion400) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits) val response01 = setRateLimiting(user1, callLimitJsonMinute) Then("We should get a 200") @@ -163,7 +164,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { response04.code should equal(200) } scenario("We will set Rate Limiting per hour for an Endpoint", ApiCallsLimit, ApiVersion400) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits) val response01 = setRateLimiting(user1, callLimitJsonHour) Then("We should get a 200") @@ -185,7 +186,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { response04.code should equal(200) } scenario("We will set Rate Limiting per week for an Endpoint", ApiCallsLimit, ApiVersion400) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits) val response01 = setRateLimiting(user1, callLimitJsonWeek) Then("We should get a 200") @@ -207,7 +208,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { response04.code should equal(200) } scenario("We will set Rate Limiting per month for an Endpoint", ApiCallsLimit, ApiVersion400) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits) val response01 = setRateLimiting(user1, callLimitJsonMonth) Then("We should get a 200") @@ -232,7 +233,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { feature(s"Dynamic Endpoint: test $ApiCreateDynamicEndpoint version $ApiVersion400 - authorized access - with role - should be success!") { scenario("We will call the endpoint with user credentials", ApiCreateDynamicEndpoint, ApiVersion400) { - assume(RateLimitingUtil.isRedisAvailable()) + assume(Redis.isRedisAvailable()) When("We make a request v4.0.0") val postDynamicEndpointRequestBodyExample = ExampleValue.dynamicEndpointRequestBodyExample When("We make a request v4.0.0")