.
This commit is contained in:
parent
d57355a9c9
commit
41ebca932a
@ -1,114 +1,22 @@
|
|||||||
import os
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
class AppSettings:
|
class AppSettings:
|
||||||
def __init__(self) -> None:
|
# --------------------
|
||||||
# --------------------
|
# Application
|
||||||
# Application
|
# --------------------
|
||||||
# --------------------
|
app_env: str
|
||||||
self.app_env = self._get_env(
|
log_level: str
|
||||||
"APP_ENV",
|
|
||||||
default="local",
|
|
||||||
normalize=self._normalize_lower,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.log_level = self._get_env(
|
# --------------------
|
||||||
"LOG_LEVEL",
|
# Secret manager
|
||||||
default="INFO",
|
# --------------------
|
||||||
normalize=self._normalize_upper,
|
secret_provider: str
|
||||||
)
|
gcp_project_id: str | None
|
||||||
|
|
||||||
# --------------------
|
# --------------------
|
||||||
# Secret manager
|
# PDF signing
|
||||||
# --------------------
|
# --------------------
|
||||||
self.secret_provider = self._get_env(
|
pdf_cert_secret_name: str
|
||||||
"SECRET_PROVIDER",
|
pdf_cert_password_secret_name: str
|
||||||
default="fake",
|
|
||||||
normalize=self._normalize_lower,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.gcp_project_id = self._get_env(
|
|
||||||
"GCP_PROJECT_ID",
|
|
||||||
default=None,
|
|
||||||
required=self.secret_provider == "google",
|
|
||||||
)
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
# PDF signing
|
|
||||||
# --------------------
|
|
||||||
self.pdf_cert_secret_name = self._get_env(
|
|
||||||
"PDF_CERT_SECRET_NAME",
|
|
||||||
required=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.pdf_cert_password_secret_name = self._get_env(
|
|
||||||
"PDF_CERT_PASSWORD_SECRET_NAME",
|
|
||||||
required=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
# Cross-field validation
|
|
||||||
# --------------------
|
|
||||||
self._validate()
|
|
||||||
|
|
||||||
# ======================================================
|
|
||||||
# Helpers
|
|
||||||
# ======================================================
|
|
||||||
|
|
||||||
def _get_env(
|
|
||||||
self,
|
|
||||||
name: str,
|
|
||||||
*,
|
|
||||||
default: str | None = None,
|
|
||||||
required: bool = False,
|
|
||||||
normalize=None,
|
|
||||||
) -> str | None:
|
|
||||||
"""
|
|
||||||
Read and validate an environment variable.
|
|
||||||
"""
|
|
||||||
raw = os.environ.get(name, default)
|
|
||||||
|
|
||||||
if raw is None:
|
|
||||||
if required:
|
|
||||||
raise ValueError(f"Environment variable {name} is required")
|
|
||||||
return None
|
|
||||||
|
|
||||||
if not isinstance(raw, str):
|
|
||||||
raise ValueError(f"Environment variable {name} must be a string")
|
|
||||||
|
|
||||||
value = raw.strip()
|
|
||||||
|
|
||||||
if required and value == "":
|
|
||||||
raise ValueError(f"Environment variable {name} cannot be empty")
|
|
||||||
|
|
||||||
if normalize:
|
|
||||||
value = normalize(value)
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _normalize_lower(value: str) -> str:
|
|
||||||
return value.strip().lower()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _normalize_upper(value: str) -> str:
|
|
||||||
return value.strip().upper()
|
|
||||||
|
|
||||||
# ======================================================
|
|
||||||
# Validations
|
|
||||||
# ======================================================
|
|
||||||
|
|
||||||
def _validate(self) -> None:
|
|
||||||
allowed_providers = {"fake", "infisical", "google"}
|
|
||||||
|
|
||||||
if self.secret_provider not in allowed_providers:
|
|
||||||
raise ValueError(
|
|
||||||
f"Invalid SECRET_PROVIDER: {self.secret_provider}. "
|
|
||||||
f"Allowed values: {allowed_providers}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.app_env not in {"local", "prod"}:
|
|
||||||
raise ValueError(
|
|
||||||
f"Invalid APP_ENV: {self.app_env}. "
|
|
||||||
"Allowed values: local | prod"
|
|
||||||
)
|
|
||||||
|
|||||||
@ -0,0 +1,92 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from signing_service.application.settings.app_settings import AppSettings
|
||||||
|
|
||||||
|
|
||||||
|
def load_app_settings() -> AppSettings:
|
||||||
|
def get_env(
|
||||||
|
name: str,
|
||||||
|
*,
|
||||||
|
default: str | None = None,
|
||||||
|
required: bool = False,
|
||||||
|
normalize=None,
|
||||||
|
) -> str | None:
|
||||||
|
raw = os.environ.get(name, default)
|
||||||
|
|
||||||
|
if raw is None:
|
||||||
|
if required:
|
||||||
|
raise ValueError(f"Environment variable {name} is required")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not isinstance(raw, str):
|
||||||
|
raise ValueError(f"Environment variable {name} must be a string")
|
||||||
|
|
||||||
|
value = raw.strip()
|
||||||
|
|
||||||
|
if required and value == "":
|
||||||
|
raise ValueError(f"Environment variable {name} cannot be empty")
|
||||||
|
|
||||||
|
if normalize:
|
||||||
|
value = normalize(value)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
# --------------------
|
||||||
|
# Application
|
||||||
|
# --------------------
|
||||||
|
app_env = get_env(
|
||||||
|
"APP_ENV",
|
||||||
|
default="local",
|
||||||
|
normalize=str.lower,
|
||||||
|
)
|
||||||
|
|
||||||
|
log_level = get_env(
|
||||||
|
"LOG_LEVEL",
|
||||||
|
default="INFO",
|
||||||
|
normalize=str.upper,
|
||||||
|
)
|
||||||
|
|
||||||
|
# --------------------
|
||||||
|
# Secret manager
|
||||||
|
# --------------------
|
||||||
|
secret_provider = get_env(
|
||||||
|
"SECRET_PROVIDER",
|
||||||
|
default="fake",
|
||||||
|
normalize=str.lower,
|
||||||
|
)
|
||||||
|
|
||||||
|
if secret_provider not in {"fake", "google", "infisical"}:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid SECRET_PROVIDER: {secret_provider}. "
|
||||||
|
"Allowed values: fake | google | infisical"
|
||||||
|
)
|
||||||
|
|
||||||
|
gcp_project_id = get_env(
|
||||||
|
"GCP_PROJECT_ID",
|
||||||
|
required=secret_provider == "google",
|
||||||
|
)
|
||||||
|
|
||||||
|
# --------------------
|
||||||
|
# PDF signing
|
||||||
|
# --------------------
|
||||||
|
pdf_cert_secret_name = get_env(
|
||||||
|
"PDF_CERT_SECRET_NAME",
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
pdf_cert_password_secret_name = get_env(
|
||||||
|
"PDF_CERT_PASSWORD_SECRET_NAME",
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# --------------------
|
||||||
|
# Build immutable settings object
|
||||||
|
# --------------------
|
||||||
|
return AppSettings(
|
||||||
|
app_env=app_env,
|
||||||
|
log_level=log_level,
|
||||||
|
secret_provider=secret_provider,
|
||||||
|
gcp_project_id=gcp_project_id,
|
||||||
|
pdf_cert_secret_name=pdf_cert_secret_name,
|
||||||
|
pdf_cert_password_secret_name=pdf_cert_password_secret_name,
|
||||||
|
)
|
||||||
@ -1,7 +1,9 @@
|
|||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
|
||||||
from signing_service.application.settings.app_settings import AppSettings
|
from signing_service.application.settings.app_settings import AppSettings
|
||||||
|
from signing_service.application.settings.app_settings_loader import load_app_settings
|
||||||
|
|
||||||
|
|
||||||
@lru_cache
|
@lru_cache
|
||||||
def get_settings() -> AppSettings:
|
def get_settings() -> AppSettings:
|
||||||
return AppSettings()
|
return load_app_settings()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user