from collections import defaultdict from functools import partial from threading import RLock import cachetools from flask import current_app from notifications_utils.clients.redis import RequestCache from notifications_utils.serialised_model import ( SerialisedModel, SerialisedModelCollection, ) from werkzeug.utils import cached_property from app import db, redis_store from app.dao.api_key_dao import get_model_api_keys from app.dao.services_dao import dao_fetch_service_by_id caches = defaultdict(partial(cachetools.TTLCache, maxsize=1024, ttl=2)) locks = defaultdict(RLock) redis_cache = RequestCache(redis_store) def memory_cache(func): @cachetools.cached( cache=caches[func.__qualname__], lock=locks[func.__qualname__], key=ignore_first_argument_cache_key, ) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper def ignore_first_argument_cache_key(cls, *args, **kwargs): return cachetools.keys.hashkey(*args, **kwargs) class SerialisedTemplate(SerialisedModel): ALLOWED_PROPERTIES = { 'archived', 'content', 'id', 'process_type', 'reply_to_text', 'subject', 'template_type', 'version', } @classmethod @memory_cache def from_id_and_service_id(cls, template_id, service_id, version=None): return cls(cls.get_dict(template_id, service_id, version)['data']) @staticmethod @redis_cache.set('service-{service_id}-template-{template_id}-version-{version}') def get_dict(template_id, service_id, version): from app.dao import templates_dao from app.schemas import template_schema fetched_template = templates_dao.dao_get_template_by_id_and_service_id( template_id=template_id, service_id=service_id, version=version, ) template_dict = template_schema.dump(fetched_template) db.session.commit() return {'data': template_dict} class SerialisedService(SerialisedModel): ALLOWED_PROPERTIES = { 'id', 'name', 'active', 'contact_link', 'email_from', 'message_limit', 'permissions', 'rate_limit', 'restricted', 'prefix_sms', 'email_branding' } @classmethod @memory_cache def from_id(cls, service_id): return cls(cls.get_dict(service_id)['data']) @staticmethod @redis_cache.set('service-{service_id}') def get_dict(service_id): from app.schemas import service_schema service_dict = service_schema.dump(dao_fetch_service_by_id(service_id)) db.session.commit() return {'data': service_dict} @cached_property def api_keys(self): return SerialisedAPIKeyCollection.from_service_id(self.id) @property def high_volume(self): return self.id in current_app.config['HIGH_VOLUME_SERVICE'] class SerialisedAPIKey(SerialisedModel): ALLOWED_PROPERTIES = { 'id', 'secret', 'expiry_date', 'key_type', } class SerialisedAPIKeyCollection(SerialisedModelCollection): model = SerialisedAPIKey @classmethod @memory_cache def from_service_id(cls, service_id): keys = [ {k: getattr(key, k) for k in SerialisedAPIKey.ALLOWED_PROPERTIES} for key in get_model_api_keys(service_id) ] db.session.commit() return cls(keys)