Skip to content

Commit 4a20168

Browse files
committed
Added GET system cache config and GET system cache info
1 parent 423a600 commit 4a20168

File tree

3 files changed

+257
-1
lines changed

3 files changed

+257
-1
lines changed

obp-api/src/main/scala/code/api/util/ApiRole.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,12 @@ object ApiRole extends MdcLoggable{
412412
lazy val canGetMetricsAtOneBank = CanGetMetricsAtOneBank()
413413

414414
case class CanGetConfig(requiresBankId: Boolean = false) extends ApiRole
415+
case class CanGetCacheConfig(requiresBankId: Boolean = false) extends ApiRole
416+
lazy val canGetCacheConfig = CanGetCacheConfig()
417+
418+
case class CanGetCacheInfo(requiresBankId: Boolean = false) extends ApiRole
419+
lazy val canGetCacheInfo = CanGetCacheInfo()
420+
415421

416422
case class CanGetCacheNamespaces(requiresBankId: Boolean = false) extends ApiRole
417423
lazy val canGetCacheNamespaces = CanGetCacheNamespaces()

obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import code.api.v5_0_0.{ViewJsonV500, ViewsJsonV500}
2727
import code.api.v5_1_0.{JSONFactory510, PostCustomerLegalNameJsonV510}
2828
import code.api.dynamic.entity.helper.{DynamicEntityHelper, DynamicEntityInfo}
2929
import code.api.v6_0_0.JSONFactory600.{AddUserToGroupResponseJsonV600, DynamicEntityDiagnosticsJsonV600, DynamicEntityIssueJsonV600, GroupEntitlementJsonV600, GroupEntitlementsJsonV600, GroupJsonV600, GroupsJsonV600, PostGroupJsonV600, PostGroupMembershipJsonV600, PostResetPasswordUrlJsonV600, PutGroupJsonV600, ReferenceTypeJsonV600, ReferenceTypesJsonV600, ResetPasswordUrlJsonV600, RoleWithEntitlementCountJsonV600, RolesWithEntitlementCountsJsonV600, ScannedApiVersionJsonV600, UpdateViewJsonV600, UserGroupMembershipJsonV600, UserGroupMembershipsJsonV600, ValidateUserEmailJsonV600, ValidateUserEmailResponseJsonV600, ViewJsonV600, ViewPermissionJsonV600, ViewPermissionsJsonV600, ViewsJsonV600, createAbacRuleJsonV600, createAbacRulesJsonV600, createActiveRateLimitsJsonV600, createCallLimitJsonV600, createRedisCallCountersJson}
30-
import code.api.v6_0_0.{AbacRuleJsonV600, AbacRuleResultJsonV600, AbacRulesJsonV600, CreateAbacRuleJsonV600, CurrentConsumerJsonV600, ExecuteAbacRuleJsonV600, UpdateAbacRuleJsonV600}
30+
import code.api.v6_0_0.{AbacRuleJsonV600, AbacRuleResultJsonV600, AbacRulesJsonV600, CacheConfigJsonV600, CacheInfoJsonV600, CacheNamespaceInfoJsonV600, CacheProviderConfigJsonV600, CreateAbacRuleJsonV600, CurrentConsumerJsonV600, ExecuteAbacRuleJsonV600, UpdateAbacRuleJsonV600}
3131
import code.api.v6_0_0.OBPAPI6_0_0
3232
import code.abacrule.{AbacRuleEngine, MappedAbacRuleProvider}
3333
import code.metrics.APIMetrics
@@ -658,6 +658,133 @@ trait APIMethods600 {
658658
}
659659
}
660660

661+
staticResourceDocs += ResourceDoc(
662+
getCacheConfig,
663+
implementedInApiVersion,
664+
nameOf(getCacheConfig),
665+
"GET",
666+
"/system/cache/config",
667+
"Get Cache Configuration",
668+
"""Returns cache configuration information including:
669+
|
670+
|- Available cache providers (Redis, In-Memory)
671+
|- Redis connection details (URL, port, SSL)
672+
|- Instance ID and environment
673+
|- Global cache namespace prefix
674+
|
675+
|This helps understand what cache backend is being used and how it's configured.
676+
|
677+
|Authentication is Required
678+
|""",
679+
EmptyBody,
680+
CacheConfigJsonV600(
681+
providers = List(
682+
CacheProviderConfigJsonV600(
683+
provider = "redis",
684+
enabled = true,
685+
url = Some("127.0.0.1"),
686+
port = Some(6379),
687+
use_ssl = Some(false)
688+
),
689+
CacheProviderConfigJsonV600(
690+
provider = "in_memory",
691+
enabled = true,
692+
url = None,
693+
port = None,
694+
use_ssl = None
695+
)
696+
),
697+
instance_id = "obp",
698+
environment = "dev",
699+
global_prefix = "obp_dev_"
700+
),
701+
List(
702+
UserNotLoggedIn,
703+
UserHasMissingRoles,
704+
UnknownError
705+
),
706+
List(apiTagCache, apiTagSystem, apiTagApi),
707+
Some(List(canGetCacheConfig))
708+
)
709+
710+
lazy val getCacheConfig: OBPEndpoint = {
711+
case "system" :: "cache" :: "config" :: Nil JsonGet _ => {
712+
cc => implicit val ec = EndpointContext(Some(cc))
713+
for {
714+
(Full(u), callContext) <- authenticatedAccess(cc)
715+
_ <- NewStyle.function.hasEntitlement("", u.userId, canGetCacheConfig, callContext)
716+
} yield {
717+
val result = JSONFactory600.createCacheConfigJsonV600()
718+
(result, HttpCode.`200`(callContext))
719+
}
720+
}
721+
}
722+
723+
staticResourceDocs += ResourceDoc(
724+
getCacheInfo,
725+
implementedInApiVersion,
726+
nameOf(getCacheInfo),
727+
"GET",
728+
"/system/cache/info",
729+
"Get Cache Information",
730+
"""Returns detailed cache information for all namespaces:
731+
|
732+
|- Namespace ID and versioned prefix
733+
|- Current version counter
734+
|- Number of keys in each namespace
735+
|- Description and category
736+
|- Total key count across all namespaces
737+
|- Redis availability status
738+
|
739+
|This endpoint helps monitor cache usage and identify which namespaces contain the most data.
740+
|
741+
|Authentication is Required
742+
|""",
743+
EmptyBody,
744+
CacheInfoJsonV600(
745+
namespaces = List(
746+
CacheNamespaceInfoJsonV600(
747+
namespace_id = "call_counter",
748+
prefix = "obp_dev_call_counter_1_",
749+
current_version = 1,
750+
key_count = 42,
751+
description = "Rate limit call counters",
752+
category = "Rate Limiting"
753+
),
754+
CacheNamespaceInfoJsonV600(
755+
namespace_id = "rd_localised",
756+
prefix = "obp_dev_rd_localised_1_",
757+
current_version = 1,
758+
key_count = 128,
759+
description = "Localized resource docs",
760+
category = "API Documentation"
761+
)
762+
),
763+
total_keys = 170,
764+
redis_available = true
765+
),
766+
List(
767+
UserNotLoggedIn,
768+
UserHasMissingRoles,
769+
UnknownError
770+
),
771+
List(apiTagCache, apiTagSystem, apiTagApi),
772+
Some(List(canGetCacheInfo))
773+
)
774+
775+
lazy val getCacheInfo: OBPEndpoint = {
776+
case "system" :: "cache" :: "info" :: Nil JsonGet _ => {
777+
cc => implicit val ec = EndpointContext(Some(cc))
778+
for {
779+
(Full(u), callContext) <- authenticatedAccess(cc)
780+
_ <- NewStyle.function.hasEntitlement("", u.userId, canGetCacheInfo, callContext)
781+
} yield {
782+
val result = JSONFactory600.createCacheInfoJsonV600()
783+
(result, HttpCode.`200`(callContext))
784+
}
785+
}
786+
}
787+
661788
lazy val getCurrentConsumer: OBPEndpoint = {
662789
case "consumers" :: "current" :: Nil JsonGet _ => {
663790
cc => {

obp-api/src/main/scala/code/api/v6_0_0/JSONFactory6.0.0.scala

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,36 @@ case class InvalidatedCacheNamespaceJsonV600(
268268
status: String
269269
)
270270

271+
case class CacheProviderConfigJsonV600(
272+
provider: String,
273+
enabled: Boolean,
274+
url: Option[String],
275+
port: Option[Int],
276+
use_ssl: Option[Boolean]
277+
)
278+
279+
case class CacheConfigJsonV600(
280+
providers: List[CacheProviderConfigJsonV600],
281+
instance_id: String,
282+
environment: String,
283+
global_prefix: String
284+
)
285+
286+
case class CacheNamespaceInfoJsonV600(
287+
namespace_id: String,
288+
prefix: String,
289+
current_version: Long,
290+
key_count: Int,
291+
description: String,
292+
category: String
293+
)
294+
295+
case class CacheInfoJsonV600(
296+
namespaces: List[CacheNamespaceInfoJsonV600],
297+
total_keys: Int,
298+
redis_available: Boolean
299+
)
300+
271301
case class PostCustomerJsonV600(
272302
legal_name: String,
273303
customer_number: Option[String] = None,
@@ -1083,4 +1113,97 @@ object JSONFactory600 extends CustomJsonFormats with MdcLoggable {
10831113
): CacheNamespacesJsonV600 = {
10841114
CacheNamespacesJsonV600(namespaces)
10851115
}
1116+
1117+
def createCacheConfigJsonV600(): CacheConfigJsonV600 = {
1118+
import code.api.cache.{Redis, InMemory}
1119+
import code.api.Constant
1120+
import net.liftweb.util.Props
1121+
1122+
val redisProvider = CacheProviderConfigJsonV600(
1123+
provider = "redis",
1124+
enabled = true,
1125+
url = Some(Redis.url),
1126+
port = Some(Redis.port),
1127+
use_ssl = Some(Redis.useSsl)
1128+
)
1129+
1130+
val inMemoryProvider = CacheProviderConfigJsonV600(
1131+
provider = "in_memory",
1132+
enabled = true,
1133+
url = None,
1134+
port = None,
1135+
use_ssl = None
1136+
)
1137+
1138+
val instanceId = code.api.util.APIUtil.getPropsValue("api_instance_id").getOrElse("obp")
1139+
val environment = Props.mode match {
1140+
case Props.RunModes.Production => "prod"
1141+
case Props.RunModes.Staging => "staging"
1142+
case Props.RunModes.Development => "dev"
1143+
case Props.RunModes.Test => "test"
1144+
case _ => "unknown"
1145+
}
1146+
1147+
CacheConfigJsonV600(
1148+
providers = List(redisProvider, inMemoryProvider),
1149+
instance_id = instanceId,
1150+
environment = environment,
1151+
global_prefix = Constant.getGlobalCacheNamespacePrefix
1152+
)
1153+
}
1154+
1155+
def createCacheInfoJsonV600(): CacheInfoJsonV600 = {
1156+
import code.api.cache.Redis
1157+
import code.api.Constant
1158+
1159+
val namespaceDescriptions = Map(
1160+
Constant.CALL_COUNTER_NAMESPACE -> ("Rate limit call counters", "Rate Limiting"),
1161+
Constant.RL_ACTIVE_NAMESPACE -> ("Active rate limit states", "Rate Limiting"),
1162+
Constant.RD_LOCALISED_NAMESPACE -> ("Localized resource docs", "API Documentation"),
1163+
Constant.RD_DYNAMIC_NAMESPACE -> ("Dynamic resource docs", "API Documentation"),
1164+
Constant.RD_STATIC_NAMESPACE -> ("Static resource docs", "API Documentation"),
1165+
Constant.RD_ALL_NAMESPACE -> ("All resource docs", "API Documentation"),
1166+
Constant.SWAGGER_STATIC_NAMESPACE -> ("Static Swagger docs", "API Documentation"),
1167+
Constant.CONNECTOR_NAMESPACE -> ("Connector cache", "Connector"),
1168+
Constant.METRICS_STABLE_NAMESPACE -> ("Stable metrics data", "Metrics"),
1169+
Constant.METRICS_RECENT_NAMESPACE -> ("Recent metrics data", "Metrics"),
1170+
Constant.ABAC_RULE_NAMESPACE -> ("ABAC rule cache", "Authorization")
1171+
)
1172+
1173+
var redisAvailable = true
1174+
var totalKeys = 0
1175+
1176+
val namespaces = Constant.ALL_CACHE_NAMESPACES.map { namespaceId =>
1177+
val version = Constant.getCacheNamespaceVersion(namespaceId)
1178+
val prefix = Constant.getVersionedCachePrefix(namespaceId)
1179+
val pattern = s"${prefix}*"
1180+
1181+
val keyCount = try {
1182+
val count = Redis.countKeys(pattern)
1183+
totalKeys += count
1184+
count
1185+
} catch {
1186+
case _: Throwable =>
1187+
redisAvailable = false
1188+
0
1189+
}
1190+
1191+
val (description, category) = namespaceDescriptions.getOrElse(namespaceId, ("Unknown namespace", "Other"))
1192+
1193+
CacheNamespaceInfoJsonV600(
1194+
namespace_id = namespaceId,
1195+
prefix = prefix,
1196+
current_version = version,
1197+
key_count = keyCount,
1198+
description = description,
1199+
category = category
1200+
)
1201+
}
1202+
1203+
CacheInfoJsonV600(
1204+
namespaces = namespaces,
1205+
total_keys = totalKeys,
1206+
redis_available = redisAvailable
1207+
)
1208+
}
10861209
}

0 commit comments

Comments
 (0)