FastAPI skeleton: health, OpenAPI, core wiring (task 042dfc5c) #147

Member

FastAPI skeleton for Feature 16, first task (042dfc5c): health, OpenAPI, core wiring.

Done-condition: A FastAPI app in api/ imports core/, starts against a SQLite path from configuration, and serves GET /health returning 200 with a status body. The OpenAPI document is served at /docs. A test starts the app with httpx and asserts both.

What this adds

  • api/nestquest_core.py — transitional coupling: bootstraps the integration directory onto sys.path and imports core as a top-level package, so custom_components/nestquest/__init__.py (which imports homeassistant) never executes. Encapsulated in one documented helper to revisit in Feature 18.
  • api/config.py — reads NESTQUEST_DB_PATH (env var, documented temp default).
  • api/database.py — opens+migrates the SQLite database via the bundled NestQuestDatabase / apply_migrations, using an asyncio.to_thread executor mirroring hass.async_add_executor_job.
  • api/app.py — FastAPI app with a lifespan that opens+migrates on startup and closes on shutdown; serves GET /health (200, status: ok + resolved db_path) and the auto OpenAPI at /docs and /openapi.json.
  • tests/test_api_health.py — httpx against the ASGI app asserts /health (200 + body), /openapi.json, and /docs; a hermetic test re-opens the temp db and asserts migrations ran (schema version stamped); a subprocess test proves the app imports core WITHOUT importing homeassistant or registering custom_components (the suite conftest stubs HA, so the honest proof is a clean subprocess).
  • pyproject.toml — add fastapi, uvicorn, httpx to the dev dependency group; uv lock.

Verification

  • uv run pytest -q green at 1581 (1576 baseline + 5 new API tests).
  • No-HA invariant: homeassistant and custom_components are absent from sys.modules after building the app in a clean subprocess.
  • No lint/typecheck tool is configured for this repo (pyproject has only pytest); py_compile and ast.parse clean on all new files.

Task: NestQuest tracker 042dfc5c-9082-4db9-9539-790cd21f5308 (Feature 16).

FastAPI skeleton for Feature 16, first task (042dfc5c): health, OpenAPI, core wiring. **Done-condition:** A FastAPI app in `api/` imports `core/`, starts against a SQLite path from configuration, and serves `GET /health` returning 200 with a status body. The OpenAPI document is served at `/docs`. A test starts the app with httpx and asserts both. **What this adds** - `api/nestquest_core.py` — transitional coupling: bootstraps the integration directory onto `sys.path` and imports `core` as a top-level package, so `custom_components/nestquest/__init__.py` (which imports `homeassistant`) never executes. Encapsulated in one documented helper to revisit in Feature 18. - `api/config.py` — reads `NESTQUEST_DB_PATH` (env var, documented temp default). - `api/database.py` — opens+migrates the SQLite database via the bundled `NestQuestDatabase` / `apply_migrations`, using an `asyncio.to_thread` executor mirroring `hass.async_add_executor_job`. - `api/app.py` — FastAPI app with a lifespan that opens+migrates on startup and closes on shutdown; serves `GET /health` (200, `status: ok` + resolved `db_path`) and the auto OpenAPI at `/docs` and `/openapi.json`. - `tests/test_api_health.py` — httpx against the ASGI app asserts `/health` (200 + body), `/openapi.json`, and `/docs`; a hermetic test re-opens the temp db and asserts migrations ran (schema version stamped); a subprocess test proves the app imports `core` WITHOUT importing `homeassistant` or registering `custom_components` (the suite conftest stubs HA, so the honest proof is a clean subprocess). - `pyproject.toml` — add `fastapi`, `uvicorn`, `httpx` to the dev dependency group; `uv lock`. **Verification** - `uv run pytest -q` green at 1581 (1576 baseline + 5 new API tests). - No-HA invariant: `homeassistant` and `custom_components` are absent from `sys.modules` after building the app in a clean subprocess. - No lint/typecheck tool is configured for this repo (pyproject has only pytest); `py_compile` and `ast.parse` clean on all new files. Task: NestQuest tracker 042dfc5c-9082-4db9-9539-790cd21f5308 (Feature 16).
dev_agent added 1 commit 2026-09-21 16:06:57 -04:00
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).
overseer added 1 commit 2026-09-21 16:11:59 -04:00
P2-1 (containment): api/nestquest_core.py no longer mutates sys.path.
Load core by file location with importlib.util.spec_from_file_location,
registering it under the distinct top-level name 'nestquest_core' with
submodule_search_locations pointing at the bundled core directory. No
generic top-level names (db, const, schema, ...) are published that
could shadow a future dependency, and the integration's HA-coupled
__init__ is never executed. Use importlib.import_module for submodules;
drop the redundant _BOOTSTRAPPED flag. Document that two core copies
(nestquest_core.* and custom_components.nestquest.core.*) coexist in
the pytest process so later tasks must not compare class/exception
identity across them.

P2-2 (fail fast): ApiConfig.from_env now RAISES ConfigError when
NESTQUEST_DB_PATH is unset or empty — no silent fallback to a
predictable world-writable temp path that loses data at reboot. Remove
the module-level 'app = create_app()' (it read env at import time and
would break fail-fast); document serving with 'uvicorn --factory
api.app:create_app'.

P3 cleanups:
- /health drops db_path from the body (no filesystem path leak on an
  unauthenticated route) and runs a cheap SELECT 1 to reflect database
  reachability.
- Remove dead code: resolve_db_path in api/config.py; unused LOGGER in
  api/app.py.
- tests/test_api_health.py: add ApiConfig.from_env tests (set value,
  empty raises, missing raises); add startup-failure tests (unwritable
  dir, non-SQLite file); assert shutdown closes the connection
  (connected is False); make _AppRunner.__aexit__ use try/finally so a
  client error still shuts down the lifespan; reuse api.database._executor
  instead of a duplicated local copy; in the no-HA subprocess test
  assert core.__file__ points at the bundled directory and stop passing
  the unused ':memory:'.
- pyproject.toml: comment that fastapi/uvicorn are dev-group only so the
  monorepo test venv can exercise the app; the API deployable's runtime
  dependencies must be declared when it is packaged (Feature 17).
dev_agent merged commit 7fec3c76ef into feature/cd03735b-2498-4b34-b84c-c1cb2b271ba7-nestquest-api-service 2026-09-21 16:13:42 -04:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Maniacs_Mansion/NestQuest#147