from typing import Any, Self from fansifter_common.constants import PROD_ENVIRONMENT, QA_ENVIRONMENT from fansifter_common.utils.functional import lazy_proxy from pydantic import Field, ValidationError, model_validator from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): environment: str debug: bool = Field(validation_alias="APP_DEBUG", default=False) service_name: str = "lambda-audience-songwhip-fan-consent-handler" service_version: str = "0.1.0" aws_region_name: str = "us-east-1" # Redis redis_host: str = "localhost" redis_port: int = 6379 redis_ssl: bool = True cache_backend: str = Field("redis", validation_alias="CACHE_BACKEND") cache_redis_db: int = 0 cache_timeout: int = 3600 @property def cache_key_prefix(self) -> str: return f"{self.service_name}:" @property def m2m_token_secret_name(self) -> str: return f"{self.environment}/lambda-audience-refresh-m2m-token/m2m_token" @property def m2m_token_expiration_secret_name(self) -> str: return ( f"{self.environment}/lambda-audience-refresh-m2m-token/m2m_token_expiration" ) email_sender_name: str = "Songwhip" email_title_templates: dict[str, str] = { "en": "Confirm your {artist_name} subscription", "de": "Bitte bestätige deine Anmeldung zum Newsletter von {artist_name}", "at": "Bitte bestätige deine Anmeldung zum Newsletter von {artist_name}", "ch": "Bitte bestätige deine Anmeldung zum Newsletter von {artist_name}", } email_content_wording: dict[str, dict[str, str]] = { "en": { "title": "Confirm your subscription", "body": "Thanks for signing up to {{ artist_name }} email list. Please click the button below " "to confirm you want to be notified about the latest content from {{ artist_name }}.", "button_text": "Yes, I want to subscribe", "notification_message": "This is one-off notification email. You received it because " "you signed-up for notifications from {{ artist_name }}", }, "de": { "title": "Fast geschafft!
Bitte bestätige deine Anmeldung", "body": "Vielen Dank für dein Interesse! Damit wir dich ab sofort mit den neuesten Inhalten " "von {{ artist_name }} versorgen können, bestätige bitte die Anmeldung zum Newsletter.", "button_text": "Anmeldung bestätigen", "notification_message": "Du hast dich nicht für den Newsletter angemeldet? Dann können " "Sie diese E-Mail einfach ignorieren. Wir respektieren Sie und Ihre Daten.", }, "at": { "title": "Fast geschafft!
Bitte bestätige deine Anmeldung", "body": "Vielen Dank für dein Interesse! Damit wir dich ab sofort mit den neuesten Inhalten " "von {{ artist_name }} versorgen können, bestätige bitte die Anmeldung zum Newsletter.", "button_text": "Anmeldung bestätigen", "notification_message": "Du hast dich nicht für den Newsletter angemeldet? Dann können " "Sie diese E-Mail einfach ignorieren. Wir respektieren Sie und Ihre Daten.", }, "ch": { "title": "Fast geschafft!
Bitte bestätige deine Anmeldung", "body": "Vielen Dank für dein Interesse! Damit wir dich ab sofort mit den neuesten Inhalten " "von {{ artist_name }} versorgen können, bestätige bitte die Anmeldung zum Newsletter.", "button_text": "Anmeldung bestätigen", "notification_message": "Du hast dich nicht für den Newsletter angemeldet? Dann können " "Sie diese E-Mail einfach ignorieren. Wir respektieren Sie und Ihre Daten.", }, } songwhip_aws_account_id: str = "619719722105" @property def email_source_arn(self) -> str: return f"arn:aws:ses:us-east-1:{self.songwhip_aws_account_id}:identity/songwhip.com" @property def email_sender(self) -> str: if self.environment == PROD_ENVIRONMENT: return "contact@songwhip.com" return "qa-contact@songwhip.com" @property def preference_center_url(self) -> str: if self.environment == PROD_ENVIRONMENT: return "https://fan-preferences.sonymusic.com/en/opt-in" return "https://qa-preferences.theorchard.io/en/opt-in" # Logging logging_debug: bool = Field(validation_alias="LOGGING_DEBUG", default=False) @property def logging_config(self) -> dict[str, Any]: return { "version": 1, "disable_existing_loggers": True, "formatters": { "json": { "()": "owslogger.logger.DDJsonFormatter", "service_name": self.service_name, "service_version": self.service_version, "env": self.environment, }, "console": { "()": "fansifter_common.logging.ConsoleFormatter", }, }, "filters": { "require_debug_true": { "()": "fansifter_common.logging.RequireDebugTrueFilter", "value": self.logging_debug, }, "require_debug_false": { "()": "fansifter_common.logging.RequireDebugFalseFilter", "value": self.logging_debug, }, }, "handlers": { "stream": { "level": "INFO", "class": "logging.StreamHandler", "formatter": "json", "filters": ["require_debug_false"], }, "rich": { "level": "INFO", "class": "rich.logging.RichHandler", "filters": ["require_debug_true"], "formatter": "console", }, }, "loggers": { "app": { "handlers": ["stream", "rich"], "level": "INFO", "propagate": False, }, "httpx": { "handlers": ["stream", "rich"], "level": "WARNING", "propagate": False, }, }, } # Sentry sentry_dsn: str | None = None @model_validator(mode="after") def _setup_sentry_dsn(self) -> Self: if not self.sentry_dsn and self.environment in [ QA_ENVIRONMENT, PROD_ENVIRONMENT, ]: raise ValueError(f"Sentry is not configured in {self.environment}.") return self model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", case_sensitive=False, extra="allow", ) def initialize_settings(**defaults: Any) -> Settings: try: return Settings(**defaults) except ValidationError as exc: errors = "\n".join( [ f" * {'.'.join(map(str, error['loc']))} - {error['msg']}" for error in exc.errors() ] ) raise RuntimeError(f"Failed to initialize settings:\n {errors}") from exc # Lazy proxy for settings settings = lazy_proxy(initialize_settings)