Skip to content

Commit b675eab

Browse files
committed
Use pydantic-settings for app settings object
* Already using pydantic via FastAPI * Less to maintain * Typed settings - catches config errors
1 parent 510861e commit b675eab

File tree

17 files changed

+330
-230
lines changed

17 files changed

+330
-230
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ else
9696
endif
9797

9898

99+
.PHONY: migrate-and-run
100+
migrate-and-run: migrations run
101+
102+
99103
## docker-build: Build a Docker image
100104
.PHONY: docker-build
101105
docker-build: clean

client/src/GovSingleConsent.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ export class GovSingleConsent {
8383
request(
8484
getConsentsUrl,
8585
{ timeout: 1000 },
86-
({ status: consents }: { status: Consents }) => {
87-
this.updateBrowserConsents(consents)
86+
({consent}) => {
87+
this.updateBrowserConsents(consent)
8888
this._consentsUpdateCallback(
89-
consents,
89+
consent,
9090
GovSingleConsent.isConsentPreferencesSet(),
9191
null
9292
)

consent_api/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from fastapi_etag import add_exception_handler
77
from starlette.middleware.sessions import SessionMiddleware
88

9-
from consent_api import config
9+
from consent_api.config import settings
1010
from consent_api.routers import consent
1111
from consent_api.routers import dummy_service
1212
from consent_api.routers import healthcheck
@@ -18,7 +18,7 @@
1818

1919
@app.on_event("startup")
2020
async def startup_event():
21-
if config.FLAG_FIXTURES:
21+
if settings.flag_fixtures:
2222
await register_origins_for_e2e_testing()
2323

2424

@@ -28,7 +28,7 @@ async def startup_event():
2828
)
2929
app.add_middleware(
3030
SessionMiddleware,
31-
secret_key=config.SECRET_KEY,
31+
secret_key=settings.secret_key,
3232
)
3333
app.include_router(consent.router)
3434
app.include_router(healthcheck.router)

consent_api/commands/delete_expired_consent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from sqlalchemy.sql.expression import delete
88
from sqlmodel import col
99

10-
from consent_api import config
10+
from consent_api.config import settings
1111
from consent_api.database import engine
1212
from consent_api.models import UserConsent
1313

@@ -18,7 +18,7 @@ async def delete_expired_consent():
1818
await conn.execute(
1919
delete(UserConsent).where(
2020
col(UserConsent.updated_at)
21-
< (datetime.now() - timedelta(days=config.CONSENT_EXPIRY_DAYS)),
21+
< (datetime.now() - timedelta(days=settings.consent_expiry_days)),
2222
)
2323
)
2424
await engine.dispose()

consent_api/config.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""App configuration."""
2+
import os
3+
4+
from pydantic import Field
5+
from pydantic_settings import BaseSettings
6+
from pydantic_settings import SettingsConfigDict
7+
8+
9+
class Settings(BaseSettings):
10+
secret_key: bytes = os.urandom(24)
11+
sqlalchemy_database_uri: str = Field(
12+
alias="DATABASE_URL",
13+
default="postgresql+asyncpg://localhost:5432/consent_api",
14+
)
15+
consent_expiry_days: int = 7
16+
consent_api_origin: str | None = None
17+
other_service_origin: str | None = None
18+
flag_fixtures: bool = True
19+
20+
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
21+
22+
23+
class Production(Settings):
24+
...
25+
26+
27+
class Staging(Settings):
28+
consent_api_origin: str = "https://gds-single-consent-staging.app"
29+
30+
31+
class Testing(Settings):
32+
consent_api_origin: str = "http://consent-api"
33+
other_service_origin: str = "http://dummy-service-1"
34+
35+
36+
class Development(Settings):
37+
...
38+
39+
40+
def get_settings():
41+
env = os.getenv("ENV", "development")
42+
try:
43+
return {subclass.__name__: subclass for subclass in Settings.__subclasses__()}[
44+
env.title()
45+
]()
46+
except KeyError as err:
47+
raise ValueError(f"Invalid ENV={env}") from err
48+
49+
50+
settings = get_settings()
51+
52+
print(f"Environment: {settings.__class__.__name__}")

consent_api/config/__init__.py

Lines changed: 0 additions & 69 deletions
This file was deleted.

consent_api/config/defaults.py

Lines changed: 0 additions & 13 deletions
This file was deleted.

consent_api/database.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
from sqlalchemy.ext.asyncio import create_async_engine
88
from sqlalchemy.orm import sessionmaker
99

10-
from consent_api import config
10+
from consent_api.config import settings
1111

1212
engine = create_async_engine(
13-
config.SQLALCHEMY_DATABASE_URI,
13+
str(settings.sqlalchemy_database_uri),
1414
json_serializer=FriendlyEncoder().encode,
1515
pool_size=20,
1616
max_overflow=20,

consent_api/jinja.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import jinja2
55
from starlette.templating import Jinja2Templates
66

7-
from consent_api.config import CONSENT_API_URL
7+
from consent_api.config import settings
88

99
templates = Jinja2Templates(
1010
directory="consent_api/templates", # required but overridden by loader arg
@@ -18,5 +18,5 @@
1818
),
1919
)
2020
templates.env.globals.update(
21-
{"CONSENT_API_URL": CONSENT_API_URL},
21+
{"CONSENT_API_URL": f"{settings.consent_api_origin}/api/v1/consent/"},
2222
)

consent_api/routers/dummy_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
from fastapi import Request
66
from starlette.responses import Response
77

8-
from consent_api import config
8+
from consent_api.config import settings
99
from consent_api.jinja import templates
1010

1111
router = APIRouter(include_in_schema=False)
1212
get = router.get
1313

1414
DUMMY_SERVICE_PREFIX = "/dummy-service"
15-
OTHER_SERVICE_URL = f"{config.OTHER_SERVICE_ORIGIN}{DUMMY_SERVICE_PREFIX}/"
15+
OTHER_SERVICE_URL = f"{settings.other_service_origin}{DUMMY_SERVICE_PREFIX}/"
1616

1717

1818
@get("/")

0 commit comments

Comments
 (0)