Files
NestQuest/api/config.py
T
overseer 70dce8a536 FastAPI skeleton: health, OpenAPI, core wiring (task 042dfc5c)
Add api/ with a FastAPI app that imports the bundled NestQuest core
(Feature 15) WITHOUT importing homeassistant: api/nestquest_core.py
bootstraps the integration directory onto sys.path and imports core as
a top-level package, so custom_components/nestquest/__init__.py (which
imports HA) never executes. This transitional coupling is encapsulated
in one documented helper to revisit in Feature 18.

The app reads NESTQUEST_DB_PATH (env var, documented temp default),
opens+migrates the SQLite database in a FastAPI lifespan, and serves
GET /health (200, status ok + resolved db_path) and the OpenAPI doc at
/docs and /openapi.json. Tests start the app with httpx against the
ASGI app and assert all three; a subprocess test proves the app
imports core without importing homeassistant or registering
custom_components.

Add fastapi, uvicorn, httpx to the dev dependency group; uv lock.

Suite green at 1581 (1576 baseline + 5 new API tests).
2026-09-21 16:06:31 -04:00

62 lines
2.2 KiB
Python

"""Configuration for the NestQuest API service.
The API reads its single configuration knob — the SQLite database path —
from the ``NESTQUEST_DB_PATH`` environment variable, with a documented
default inside the OS temp directory. This mirrors the integration's
"explicit settings object" discipline (Feature 15's
``core.settings``): the API never reads HA config; callers pass the
resolved path into the database wiring.
"""
from __future__ import annotations
import os
import tempfile
from dataclasses import dataclass
from pathlib import Path
#: Environment variable holding the SQLite file path. The API opens
#: this file on startup (creating it if absent) and applies migrations.
DB_PATH_ENV = "NESTQUEST_DB_PATH"
#: The default database location when the env var is unset. Lives in
#: the OS temp directory so a dev run without configuration does not
#: litter the repo, and so tests that forget to set the env var still
#: get a hermetic-ish path. Production deployments ALWAYS set
#: ``NESTQUEST_DB_PATH`` to a persistent volume path.
_DEFAULT_DB_NAME = "nestquest.db"
def _default_db_path() -> str:
return str(Path(tempfile.gettempdir()) / _DEFAULT_DB_NAME)
@dataclass(frozen=True)
class ApiConfig:
"""Resolved API configuration.
Currently a single field — the SQLite path — but kept as a dataclass
so later tasks (auth, SSE, settings persistence) can extend it
without touching the call sites that read ``db_path``.
"""
db_path: str
@classmethod
def from_env(cls, env: dict[str, str] | None = None) -> "ApiConfig":
"""Build configuration from the process environment.
``env`` defaults to ``os.environ``; tests pass an explicit dict
so they are hermetic (no monkeypatch of the global environ).
"""
source = env if env is not None else os.environ
raw = source.get(DB_PATH_ENV, "").strip()
return cls(db_path=raw if raw else _default_db_path())
def resolve_db_path(env: dict[str, str] | None = None) -> str:
"""Return the resolved SQLite path (env override or documented default)."""
return ApiConfig.from_env(env).db_path
__all__ = ["ApiConfig", "DB_PATH_ENV", "resolve_db_path"]