"""Cached secret retrieval via aws-secretsmanager-caching.""" from aws_secretsmanager_caching import SecretCache, SecretCacheConfig # 15-min TTL balances rotation latency against Secrets Manager RPS. # Shorter than the lib default (3600s) because our 401 flow still calls # invalidate() on immediate rotation; TTL is the fallback window. _SECRET_REFRESH_INTERVAL = 900 # Lazy — SecretCache() builds a boto3 client in __init__, which needs a # region. Building at import time breaks CI test collection (no AWS_REGION) # and any process that imports this module before AWS env is set. _cache: SecretCache | None = None def _get_cache() -> SecretCache: global _cache if _cache is None: _cache = SecretCache( config=SecretCacheConfig(secret_refresh_interval=_SECRET_REFRESH_INTERVAL) ) return _cache def get_secret(arn: str) -> str: """Fetch a secret by ARN, cached per container lifetime.""" return str(_get_cache().get_secret_string(arn)) def invalidate(arn: str) -> None: """Re-fetch the secret now, replacing the cached value. Blocks on the Secrets Manager API call (via the lib's `refresh_secret_now`) and returns only once the cache holds the fresh value. Used by the Payoneer 401-rotation flow where the subsequent request must see the new token synchronously. """ _get_cache().refresh_secret_now(arn)