from typing import Any, Self from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from fansifter_common.adapters.aws.secretsmanager import SecretsManager from fansifter_common.constants import PROD_ENVIRONMENT, QA_ENVIRONMENT from fansifter_common.logging import LogFormat from fansifter_common.logging.utils import update_logging_config from pydantic import Field, model_validator from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): environment: str debug: bool = Field(validation_alias="APP_DEBUG", default=False) aws_region_name: str = "us-east-1" service_name: str = "lambda-audience-validate-email-domains" service_version: str = "0.1.0" domains_chunk_size: int = 100 domain_resolving_timeout: int = 5 threads_count: int = 10 domains_queue_size: int = 100 # Database snowflake_account: str = "account" snowflake_database: str = "database" snowflake_schema: str = "schema" snowflake_warehouse: str = "warehouse" snowflake_role: str = "role" snowflake_user: str = "user" snowflake_host: str = "localhost" snowflake_port: int = 443 snowflake_private_key_secret_name: str = "private_key_secret_name" snowflake_key_passphrase_secret_name: str = "key_passphrase_secret_name" # For local development only snowflake_private_key_location: str = "snowflake_private_key_location" snowflake_private_key_password: str = "snowflake_private_key_password" @property def snowflake_private_key(self) -> bytes: if self.environment in [QA_ENVIRONMENT, PROD_ENVIRONMENT]: secrets_manager = SecretsManager(region_name=self.aws_region_name) private_key = secrets_manager.get_secret( self.snowflake_private_key_secret_name ) private_key_passphrase = secrets_manager.get_secret( self.snowflake_key_passphrase_secret_name ) else: # For local development only with open(self.snowflake_private_key_location) as fo: private_key = fo.read() private_key_passphrase = self.snowflake_private_key_password pem_private_key = serialization.load_pem_private_key( private_key.encode(), password=private_key_passphrase.encode() if private_key_passphrase else None, backend=default_backend(), ) private_bytes = pem_private_key.private_bytes( encoding=serialization.Encoding.DER, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption(), ) return private_bytes # Logging log_format: LogFormat = "json" logging_config: dict[str, Any] = { "version": 1, "disable_existing_loggers": True, "handlers": { "stream": { "level": "INFO", "class": "fansifter_common.logging.OwsStreamHandler", }, }, "loggers": { "app": { "handlers": ["stream"], "level": "INFO", "propagate": False, }, "httpx": { "handlers": ["stream"], "level": "WARNING", "propagate": False, }, }, } @model_validator(mode="after") def _setup_logging_config(self) -> Self: self.logging_config = update_logging_config( logging_config=self.logging_config, environment=self.environment, service_name=self.service_name, service_version=self.service_version, log_format=self.log_format, debug=self.debug, ) return self # 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", )