# common/utils/cache/license_cache.py from typing import Dict, Any, Optional from datetime import datetime as dt, timezone as tz from flask import current_app from sqlalchemy import and_ from sqlalchemy.inspection import inspect from common.utils.cache.base import CacheHandler from common.models.entitlements import License class LicenseCacheHandler(CacheHandler[License]): """Handles caching of active licenses for tenants""" handler_name = 'license_cache' def __init__(self, region): super().__init__(region, 'active_license') self.configure_keys('tenant_id') def _to_cache_data(self, instance: License) -> Dict[str, Any]: """Convert License instance to cache data using SQLAlchemy inspection""" if not instance: return {} # Get all column attributes from the SQLAlchemy model mapper = inspect(License) data = {} for column in mapper.columns: value = getattr(instance, column.name) # Handle date serialization if isinstance(value, dt): data[column.name] = value.isoformat() else: data[column.name] = value return data def _from_cache_data(self, data: Dict[str, Any], **kwargs) -> License: """Create License instance from cache data using SQLAlchemy inspection""" if not data: return None # Create a new License instance license = License() mapper = inspect(License) # Set all attributes dynamically for column in mapper.columns: if column.name in data: value = data[column.name] # Handle date deserialization if column.name.endswith('_date') and value: if isinstance(value, str): value = dt.fromisoformat(value).date() setattr(license, column.name, value) return license def _should_cache(self, value: License) -> bool: """Validate if the license should be cached""" return value is not None and value.id is not None def get_active_license(self, tenant_id: int) -> Optional[License]: """ Get the currently active license for a tenant Args: tenant_id: ID of the tenant Returns: License instance if found, None otherwise """ def creator_func(tenant_id: int) -> Optional[License]: from common.extensions import db current_date = dt.now(tz=tz.utc).date() # TODO --> Active License via active Period? return (db.session.query(License) .filter_by(tenant_id=tenant_id) .filter(License.start_date <= current_date) .last()) return self.get(creator_func, tenant_id=tenant_id) def invalidate_tenant_license(self, tenant_id: int): """Invalidate cached license for specific tenant""" self.invalidate(tenant_id=tenant_id) def register_license_cache_handlers(cache_manager) -> None: """Register license cache handlers with cache manager""" cache_manager.register_handler( LicenseCacheHandler, 'eveai_model' # Use existing eveai_model region )