11import json
2+ import logging
3+ import os
4+ import time
25from collections .abc import Generator
36from typing import Annotated , NewType
47
710from wireup import Inject , service
811
912from eligibility_signposting_api .model .campaign_config import CampaignConfig , Rules
13+ from eligibility_signposting_api .config .constants import ttl
1014
1115BucketName = NewType ("BucketName" , str )
1216
17+ logger = logging .getLogger (__name__ )
1318
1419@service
1520class CampaignRepo :
@@ -25,13 +30,55 @@ def __init__(
2530 super ().__init__ ()
2631 self .s3_client = s3_client
2732 self .bucket_name = bucket_name
33+ self ._campaign_configs_cache : list [CampaignConfig ] | None = None
34+ self ._cache_expiry_epoch : float = 0.0
35+ self ._cache_ttl_seconds : int = int (ttl .get (os .getenv ("ENVIRONMENT" ), 0 ))
36+
37+ def get_campaign_configs (self , bypass_cache : bool = False ) -> Generator [CampaignConfig , None , None ]:
38+ now = time .time ()
39+ cache_enabled = self ._cache_ttl_seconds > 0
40+ cache_valid = (
41+ cache_enabled
42+ and not bypass_cache
43+ and self ._campaign_configs_cache is not None
44+ and now < self ._cache_expiry_epoch
45+ )
2846
29- def get_campaign_configs (self ) -> Generator [CampaignConfig ]:
3047 with xray_recorder .in_subsegment ("CampaignRepo.get_campaign_configs" ):
48+ if cache_valid :
49+ logger .info ("Using cached campaign configs" )
50+ yield from self ._campaign_configs_cache
51+ return
52+
53+ logger .info (
54+ "Refreshing campaign configs from S3 (bypass_cache=%s, ttl_seconds=%s)" ,
55+ bypass_cache ,
56+ self ._cache_ttl_seconds ,
57+ )
58+ campaign_configs = self ._load_campaign_configs_from_s3 ()
59+
60+ if cache_enabled and not bypass_cache :
61+ self ._campaign_configs_cache = campaign_configs
62+ self ._cache_expiry_epoch = now + self ._cache_ttl_seconds
63+
64+ yield from campaign_configs
65+
66+ def _load_campaign_configs_from_s3 (self ) -> list [CampaignConfig ]:
67+ campaign_configs : list [CampaignConfig ] = []
68+
69+ with xray_recorder .in_subsegment ("CampaignRepo.load_campaign_configs_from_s3" ):
3170 with xray_recorder .in_subsegment ("list_objects" ):
3271 campaign_objects = self .s3_client .list_objects (Bucket = self .bucket_name )
72+
3373 with xray_recorder .in_subsegment ("get_objects" ):
34- for campaign_object in campaign_objects ["Contents" ]:
35- response = self .s3_client .get_object (Bucket = self .bucket_name , Key = f"{ campaign_object ['Key' ]} " )
74+ for campaign_object in campaign_objects .get ("Contents" , []):
75+ response = self .s3_client .get_object (
76+ Bucket = self .bucket_name ,
77+ Key = f"{ campaign_object ['Key' ]} " ,
78+ )
3679 body = response ["Body" ].read ()
37- yield Rules .model_validate (json .loads (body )).campaign_config
80+ campaign_configs .append (
81+ Rules .model_validate (json .loads (body )).campaign_config
82+ )
83+
84+ return campaign_configs
0 commit comments