mirror of
https://github.com/GSA/notifications-api.git
synced 2025-12-16 10:12:32 -05:00
ALLOWED_PROPERTIES is a list containing fields that it will attempt to deserialize into the returned object. If a field isn't present on the underlying data source (whether that's a dump from a marshmallow schema, a blob retrieved from redis, or anything else), then SerialisedModel will raise an exception. However, SerialisedModel isn't involved when setting the cache, so with that said the correct flow for adding a column to a cached database model: PR #1 * add new column to DB * add new column to model, and to template_schema (because that schema is used to create the dict that goes in to redis). New redis keys start getting populated. Deploy that through, and then clear redis PR #2 * add new column to SerialisedTemplate.ALLOWED_PROPERTIES. this means that it'll start reading that value from redis * now instances of the app will always have the new field in their template objects, whether they came from database directly or from the cache This commit removes the field from ALLOWED_PROPERTIES. After it's deployed we'll be able to clear redis, observe redis being populated with the new field, and then we'll be able to re-add it to ALLOWED_PROPERTIES, ready to use.
128 lines
3.2 KiB
Python
128 lines
3.2 KiB
Python
from collections import defaultdict
|
|
from functools import partial
|
|
from threading import RLock
|
|
|
|
import cachetools
|
|
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',
|
|
'postage',
|
|
'process_type',
|
|
'reply_to_text',
|
|
'subject',
|
|
'template_type',
|
|
'version',
|
|
}
|
|
|
|
@classmethod
|
|
@memory_cache
|
|
def from_id_and_service_id(cls, template_id, service_id):
|
|
return cls(cls.get_dict(template_id, service_id)['data'])
|
|
|
|
@staticmethod
|
|
@redis_cache.set('template-{template_id}-version-None')
|
|
def get_dict(template_id, service_id):
|
|
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
|
|
)
|
|
|
|
template_dict = template_schema.dump(fetched_template).data
|
|
db.session.commit()
|
|
|
|
return {'data': template_dict}
|
|
|
|
|
|
class SerialisedService(SerialisedModel):
|
|
ALLOWED_PROPERTIES = {
|
|
'id',
|
|
'active',
|
|
'contact_link',
|
|
'email_from',
|
|
'message_limit',
|
|
'permissions',
|
|
'rate_limit',
|
|
'research_mode',
|
|
'restricted',
|
|
}
|
|
|
|
@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)).data
|
|
db.session.commit()
|
|
|
|
return {'data': service_dict}
|
|
|
|
@cached_property
|
|
def api_keys(self):
|
|
return SerialisedAPIKeyCollection.from_service_id(self.id)
|
|
|
|
|
|
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)
|