Added GET system cache config and GET system cache info

This commit is contained in:
simonredfern 2025-12-31 17:18:08 +01:00
parent 423a6000b0
commit 4a20168da7
3 changed files with 257 additions and 1 deletions

View File

@ -412,6 +412,12 @@ object ApiRole extends MdcLoggable{
lazy val canGetMetricsAtOneBank = CanGetMetricsAtOneBank()
case class CanGetConfig(requiresBankId: Boolean = false) extends ApiRole
case class CanGetCacheConfig(requiresBankId: Boolean = false) extends ApiRole
lazy val canGetCacheConfig = CanGetCacheConfig()
case class CanGetCacheInfo(requiresBankId: Boolean = false) extends ApiRole
lazy val canGetCacheInfo = CanGetCacheInfo()
case class CanGetCacheNamespaces(requiresBankId: Boolean = false) extends ApiRole
lazy val canGetCacheNamespaces = CanGetCacheNamespaces()

View File

@ -27,7 +27,7 @@ import code.api.v5_0_0.{ViewJsonV500, ViewsJsonV500}
import code.api.v5_1_0.{JSONFactory510, PostCustomerLegalNameJsonV510}
import code.api.dynamic.entity.helper.{DynamicEntityHelper, DynamicEntityInfo}
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}
import code.api.v6_0_0.{AbacRuleJsonV600, AbacRuleResultJsonV600, AbacRulesJsonV600, CreateAbacRuleJsonV600, CurrentConsumerJsonV600, ExecuteAbacRuleJsonV600, UpdateAbacRuleJsonV600}
import code.api.v6_0_0.{AbacRuleJsonV600, AbacRuleResultJsonV600, AbacRulesJsonV600, CacheConfigJsonV600, CacheInfoJsonV600, CacheNamespaceInfoJsonV600, CacheProviderConfigJsonV600, CreateAbacRuleJsonV600, CurrentConsumerJsonV600, ExecuteAbacRuleJsonV600, UpdateAbacRuleJsonV600}
import code.api.v6_0_0.OBPAPI6_0_0
import code.abacrule.{AbacRuleEngine, MappedAbacRuleProvider}
import code.metrics.APIMetrics
@ -658,6 +658,133 @@ trait APIMethods600 {
}
}
staticResourceDocs += ResourceDoc(
getCacheConfig,
implementedInApiVersion,
nameOf(getCacheConfig),
"GET",
"/system/cache/config",
"Get Cache Configuration",
"""Returns cache configuration information including:
|
|- Available cache providers (Redis, In-Memory)
|- Redis connection details (URL, port, SSL)
|- Instance ID and environment
|- Global cache namespace prefix
|
|This helps understand what cache backend is being used and how it's configured.
|
|Authentication is Required
|""",
EmptyBody,
CacheConfigJsonV600(
providers = List(
CacheProviderConfigJsonV600(
provider = "redis",
enabled = true,
url = Some("127.0.0.1"),
port = Some(6379),
use_ssl = Some(false)
),
CacheProviderConfigJsonV600(
provider = "in_memory",
enabled = true,
url = None,
port = None,
use_ssl = None
)
),
instance_id = "obp",
environment = "dev",
global_prefix = "obp_dev_"
),
List(
UserNotLoggedIn,
UserHasMissingRoles,
UnknownError
),
List(apiTagCache, apiTagSystem, apiTagApi),
Some(List(canGetCacheConfig))
)
lazy val getCacheConfig: OBPEndpoint = {
case "system" :: "cache" :: "config" :: Nil JsonGet _ => {
cc => implicit val ec = EndpointContext(Some(cc))
for {
(Full(u), callContext) <- authenticatedAccess(cc)
_ <- NewStyle.function.hasEntitlement("", u.userId, canGetCacheConfig, callContext)
} yield {
val result = JSONFactory600.createCacheConfigJsonV600()
(result, HttpCode.`200`(callContext))
}
}
}
staticResourceDocs += ResourceDoc(
getCacheInfo,
implementedInApiVersion,
nameOf(getCacheInfo),
"GET",
"/system/cache/info",
"Get Cache Information",
"""Returns detailed cache information for all namespaces:
|
|- Namespace ID and versioned prefix
|- Current version counter
|- Number of keys in each namespace
|- Description and category
|- Total key count across all namespaces
|- Redis availability status
|
|This endpoint helps monitor cache usage and identify which namespaces contain the most data.
|
|Authentication is Required
|""",
EmptyBody,
CacheInfoJsonV600(
namespaces = List(
CacheNamespaceInfoJsonV600(
namespace_id = "call_counter",
prefix = "obp_dev_call_counter_1_",
current_version = 1,
key_count = 42,
description = "Rate limit call counters",
category = "Rate Limiting"
),
CacheNamespaceInfoJsonV600(
namespace_id = "rd_localised",
prefix = "obp_dev_rd_localised_1_",
current_version = 1,
key_count = 128,
description = "Localized resource docs",
category = "API Documentation"
)
),
total_keys = 170,
redis_available = true
),
List(
UserNotLoggedIn,
UserHasMissingRoles,
UnknownError
),
List(apiTagCache, apiTagSystem, apiTagApi),
Some(List(canGetCacheInfo))
)
lazy val getCacheInfo: OBPEndpoint = {
case "system" :: "cache" :: "info" :: Nil JsonGet _ => {
cc => implicit val ec = EndpointContext(Some(cc))
for {
(Full(u), callContext) <- authenticatedAccess(cc)
_ <- NewStyle.function.hasEntitlement("", u.userId, canGetCacheInfo, callContext)
} yield {
val result = JSONFactory600.createCacheInfoJsonV600()
(result, HttpCode.`200`(callContext))
}
}
}
lazy val getCurrentConsumer: OBPEndpoint = {
case "consumers" :: "current" :: Nil JsonGet _ => {
cc => {

View File

@ -268,6 +268,36 @@ case class InvalidatedCacheNamespaceJsonV600(
status: String
)
case class CacheProviderConfigJsonV600(
provider: String,
enabled: Boolean,
url: Option[String],
port: Option[Int],
use_ssl: Option[Boolean]
)
case class CacheConfigJsonV600(
providers: List[CacheProviderConfigJsonV600],
instance_id: String,
environment: String,
global_prefix: String
)
case class CacheNamespaceInfoJsonV600(
namespace_id: String,
prefix: String,
current_version: Long,
key_count: Int,
description: String,
category: String
)
case class CacheInfoJsonV600(
namespaces: List[CacheNamespaceInfoJsonV600],
total_keys: Int,
redis_available: Boolean
)
case class PostCustomerJsonV600(
legal_name: String,
customer_number: Option[String] = None,
@ -1083,4 +1113,97 @@ object JSONFactory600 extends CustomJsonFormats with MdcLoggable {
): CacheNamespacesJsonV600 = {
CacheNamespacesJsonV600(namespaces)
}
def createCacheConfigJsonV600(): CacheConfigJsonV600 = {
import code.api.cache.{Redis, InMemory}
import code.api.Constant
import net.liftweb.util.Props
val redisProvider = CacheProviderConfigJsonV600(
provider = "redis",
enabled = true,
url = Some(Redis.url),
port = Some(Redis.port),
use_ssl = Some(Redis.useSsl)
)
val inMemoryProvider = CacheProviderConfigJsonV600(
provider = "in_memory",
enabled = true,
url = None,
port = None,
use_ssl = None
)
val instanceId = code.api.util.APIUtil.getPropsValue("api_instance_id").getOrElse("obp")
val environment = Props.mode match {
case Props.RunModes.Production => "prod"
case Props.RunModes.Staging => "staging"
case Props.RunModes.Development => "dev"
case Props.RunModes.Test => "test"
case _ => "unknown"
}
CacheConfigJsonV600(
providers = List(redisProvider, inMemoryProvider),
instance_id = instanceId,
environment = environment,
global_prefix = Constant.getGlobalCacheNamespacePrefix
)
}
def createCacheInfoJsonV600(): CacheInfoJsonV600 = {
import code.api.cache.Redis
import code.api.Constant
val namespaceDescriptions = Map(
Constant.CALL_COUNTER_NAMESPACE -> ("Rate limit call counters", "Rate Limiting"),
Constant.RL_ACTIVE_NAMESPACE -> ("Active rate limit states", "Rate Limiting"),
Constant.RD_LOCALISED_NAMESPACE -> ("Localized resource docs", "API Documentation"),
Constant.RD_DYNAMIC_NAMESPACE -> ("Dynamic resource docs", "API Documentation"),
Constant.RD_STATIC_NAMESPACE -> ("Static resource docs", "API Documentation"),
Constant.RD_ALL_NAMESPACE -> ("All resource docs", "API Documentation"),
Constant.SWAGGER_STATIC_NAMESPACE -> ("Static Swagger docs", "API Documentation"),
Constant.CONNECTOR_NAMESPACE -> ("Connector cache", "Connector"),
Constant.METRICS_STABLE_NAMESPACE -> ("Stable metrics data", "Metrics"),
Constant.METRICS_RECENT_NAMESPACE -> ("Recent metrics data", "Metrics"),
Constant.ABAC_RULE_NAMESPACE -> ("ABAC rule cache", "Authorization")
)
var redisAvailable = true
var totalKeys = 0
val namespaces = Constant.ALL_CACHE_NAMESPACES.map { namespaceId =>
val version = Constant.getCacheNamespaceVersion(namespaceId)
val prefix = Constant.getVersionedCachePrefix(namespaceId)
val pattern = s"${prefix}*"
val keyCount = try {
val count = Redis.countKeys(pattern)
totalKeys += count
count
} catch {
case _: Throwable =>
redisAvailable = false
0
}
val (description, category) = namespaceDescriptions.getOrElse(namespaceId, ("Unknown namespace", "Other"))
CacheNamespaceInfoJsonV600(
namespace_id = namespaceId,
prefix = prefix,
current_version = version,
key_count = keyCount,
description = description,
category = category
)
}
CacheInfoJsonV600(
namespaces = namespaces,
total_keys = totalKeys,
redis_available = redisAvailable
)
}
}