Files
manage/openspec/changes/configurable-dashboard-widgets/apply-progress.md
T
Developer 1cd8e926de feat(widgets): add backend source adapters and per-widget data endpoint
PR 2 of 4 for configurable dashboard widgets.

- Add grafana_url and prometheus_url settings (config.py + compose/env).
- Create WidgetSource protocol and adapters for jellyfin, backups, grafana,
  prometheus, ssh_task, and static sources.
- Add GET /api/widgets/instances/{id}/data endpoint.
- Extract shared dashboard helpers into domain/dashboard.py so widgets and
  the dashboard router reuse the same logic.
- Add adapter and data-endpoint tests.
- Update apply-progress.md.

Verification: ruff clean; backend pytest 200 passed; frontend lint/build green.
2026-06-21 10:09:45 +00:00

106 lines
5.1 KiB
Markdown

# Apply Progress: Configurable Dashboard Widgets
**Change:** `configurable-dashboard-widgets`
**Apply run:** PR 1 / Slice 1 — Backend CRUD and default seeding
**Date:** 2026-06-19
## Completed tasks (Slice 1)
All Slice 1 tasks are marked `- [x]` in `tasks.md`:
- [x] 1.1 Create widget Pydantic models
- [x] 1.2 Create backend widget registry
- [x] 1.3 Implement widgets router (CRUD + metadata)
- [x] 1.4 Extend `SettingsStore` for `dashboard_widgets`
- [x] 1.5 Register widgets router in `main.py`
- [x] 1.6 Add backend tests for registry, CRUD, and seeding
- [x] 1.7 Verify backend slice
## Files changed
### New files
- `backend/src/media_library_viewer_api/models/widgets.py` — Pydantic models: `WidgetInstance`, `WidgetInstanceInput`, `WidgetTypeInfo`, `WidgetDataResponse`, plus credential-key/secret-value validators.
- `backend/src/media_library_viewer_api/widgets/__init__.py` — Package marker.
- `backend/src/media_library_viewer_api/widgets/registry.py` — Closed `WIDGET_REGISTRY` for six Phase 1 widget types, source-type listing, type metadata, and lightweight config-schema validation.
- `backend/src/media_library_viewer_api/routers/widgets.py` — REST endpoints for `/api/widgets/sources`, `/types`, `/instances`, and instance CRUD.
- `backend/tests/test_widgets.py` — 12 tests covering registry, CRUD, validation, and seeding.
### Modified files
- `backend/src/media_library_viewer_api/services/settings_store.py` — Added `dashboard_widgets` table, index, CRUD helpers, default seeding, and refactored `ensure_defaults()` to seed widgets independently of machine seeding.
- `backend/src/media_library_viewer_api/main.py` — Registered `widgets_router`.
## Verification
Commands run:
```bash
cd backend
.venv/bin/python -m ruff check . # All checks passed
PYTHONPATH=src .venv/bin/python -m pytest # 185 passed, 2 warnings
cd ../frontend
npm run lint # 2 pre-existing warnings, 0 errors
npm run build # Built successfully
```
Focused widget test output: `12 passed`.
## Deviations from design
- None significant for Slice 1. The implementation follows the design's backend CRUD layout.
- Used `HTTP_422_UNPROCESSABLE_CONTENT` instead of the deprecated `HTTP_422_UNPROCESSABLE_ENTITY`.
## Completed tasks (Slice 2)
All Slice 2 tasks are marked `- [x]` in `tasks.md`:
- [x] 2.1 Add observability URL settings (`grafana_url`, `prometheus_url`)
- [x] 2.2 Create source adapters (`jellyfin`, `backups`, `grafana`, `prometheus`, `ssh_task`, `static`)
- [x] 2.3 Add per-widget data endpoint (`GET /api/widgets/instances/{id}/data`)
- [x] 2.4 Extract shared backup/Jellyfin dashboard helpers into `domain/dashboard.py`
- [x] 2.5 Add adapter + data endpoint tests
## Files changed (Slice 2)
### New files
- `backend/src/media_library_viewer_api/widgets/sources.py``WidgetSource` protocol and six source adapters.
- `backend/src/media_library_viewer_api/domain/dashboard.py` — Shared dashboard helpers (`_map_sessions_to_activity_rows`, `build_backup_dashboard_summary`).
### Modified files
- `backend/src/media_library_viewer_api/config.py` — Added `grafana_url` and `prometheus_url` settings.
- `backend/src/media_library_viewer_api/routers/widgets.py` — Added `GET /api/widgets/instances/{id}/data`.
- `backend/src/media_library_viewer_api/routers/dashboard.py` — Delegated to shared `domain/dashboard.py` helpers.
- `backend/tests/test_widgets.py` — Added adapter and data endpoint tests.
- `docker-compose.yml`, `docker-compose.dev.yml`, `.env.example` — Wired `GRAFANA_URL` and `PROMETHEUS_URL` for the new adapters.
## Verification (Slice 2)
```bash
cd backend
.venv/bin/python -m ruff check . # All checks passed
PYTHONPATH=src .venv/bin/python -m pytest # 200 passed, 2 warnings
cd ../frontend
npm run lint # 2 pre-existing warnings, 0 errors
npm run build # Built successfully
```
Focused widget test output: `27 passed`.
## Deviations from design (Slice 2)
- Adapters currently call `get_settings_store()` internally for `backups`/`ssh_task` sources. The router-level endpoint uses FastAPI DI, but adapter unit tests patch `get_settings_store` to inject a test store. A future refactor can pass `store` and `settings` explicitly into `adapter.fetch()` for cleaner testability.
## Remaining work
- Slice 3: Frontend types/API/hooks/registry/components
- Slice 4: Dashboard loop + configuration UI + addon pages
## PR boundary
This slice is **PR 1 of 4** in the approved stacked-to-main chain. It is backend-only and leaves the frontend build/lint green.
**Actual changed-line count:** ~780 added lines across production code and tests (new files: ~597 lines; modified files: ~181 insertions). This is above the nominal ~400-line review budget, but Slice 1 is the smallest coherent backend unit: removing the CRUD router, store helpers, or tests would leave the slice non-functional or unverifiable. If the reviewer prefers a smaller blast radius, the store helpers (~90 lines) could be split into a preceding PR, though that PR would not be independently user-visible.