feat(web+api): block-playout history + sequential-YAML validator (#145, #158 items 4–5) #182

Merged
timothy merged 5 commits from feat/145-block-history into main 2026-07-07 22:32:32 +02:00
Owner

Closes out #158 items 4–5 (do not auto-close; manual close after verification). Part of #145 / phase (b) gates on #91.

Endpoints

  • GET /api/playouts/{id}/blocks — blocks used by a block playout (wraps GetAllBlocksForPlayout; 404 unknown playout).
  • GET /api/playouts/{id}/blocks/{blockId}/history?pageNum&pageSize — paged playout history (clamped paging; 404 unknown playout).
  • GET /api/playouts/history/{id} — decoded history details by row id (server-side lookup; 404 unknown row, 422 malformed Key/Details JSON). Decode core extracted into shared PlayoutHistoryDecoder, reused by the existing Blazor-path handler.
  • POST /api/troubleshoot/validate-schedule — {yaml, isImport} → {isValid, messages, json}; 400 empty yaml; validator/parse exceptions become messages, never 500 (Blazor parity).
  • Block-playout list reuses existing GET /api/playouts (scheduleKind filter client-side) — no new endpoint.

SPA: /app/troubleshooting/blocks (playout select → grouped blocks table with name filter → paged history → row-click decode panel) and /app/troubleshooting/yaml (textarea + import toggle + messages/JSON output), both in the System nav group. Internal drill-down state (no sub-paths). OpenAPI v1.json + v1.d.ts regenerated.

Tests: ErsatzTV.Tests 806 green (+14 controller/handler), Core.Tests 493 green, web 318 green (+9), lint clean. Post-#181 merge reconciled conflict-free, artifacts regen byte-identical.

🤖 Generated with Claude Code

Closes out #158 items 4–5 (do not auto-close; manual close after verification). Part of #145 / phase (b) gates on #91. **Endpoints** - `GET /api/playouts/{id}/blocks` — blocks used by a block playout (wraps GetAllBlocksForPlayout; 404 unknown playout). - `GET /api/playouts/{id}/blocks/{blockId}/history?pageNum&pageSize` — paged playout history (clamped paging; 404 unknown playout). - `GET /api/playouts/history/{id}` — decoded history details by row id (server-side lookup; 404 unknown row, 422 malformed Key/Details JSON). Decode core extracted into shared `PlayoutHistoryDecoder`, reused by the existing Blazor-path handler. - `POST /api/troubleshoot/validate-schedule` — `{yaml, isImport}` → `{isValid, messages, json}`; 400 empty yaml; validator/parse exceptions become messages, never 500 (Blazor parity). - Block-playout list reuses existing `GET /api/playouts` (scheduleKind filter client-side) — no new endpoint. **SPA**: `/app/troubleshooting/blocks` (playout select → grouped blocks table with name filter → paged history → row-click decode panel) and `/app/troubleshooting/yaml` (textarea + import toggle + messages/JSON output), both in the System nav group. Internal drill-down state (no sub-paths). OpenAPI v1.json + v1.d.ts regenerated. **Tests**: ErsatzTV.Tests 806 green (+14 controller/handler), Core.Tests 493 green, web 318 green (+9), lint clean. Post-#181 merge reconciled conflict-free, artifacts regen byte-identical. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
timothy added 5 commits 2026-07-07 22:26:40 +02:00
Adds the REST surface for the #145 troubleshooting leftovers (#158 items 4-5):

- GET /api/playouts/{id}/blocks - blocks a block playout schedules
- GET /api/playouts/{id}/blocks/{blockId}/history - paged block history
- GET /api/playouts/history/{id} - decode a history row by id
- POST /api/troubleshoot/validate-schedule - validate sequential YAML

New MediatR queries GetPlayoutHistoryDetails (Either, 404 unknown row /
422 malformed JSON) and ValidateSequentialSchedule (wraps
ISequentialScheduleValidator, never throws). DecodePlayoutHistoryHandler
and the new by-id handler now share PlayoutHistoryDecoder. Regenerated
openapi/v1.json.

Refs #145 #158

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Route assertions, paging clamp, 404/422/400 paths and mediator interaction
for the new PlayoutController/TroubleshootController endpoints; handler tests
for ValidateSequentialSchedule (valid/invalid/throwing) and
GetPlayoutHistoryDetails (found/not-found/malformed JSON via in-memory SQLite).

Refs #145 #158

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two new System-area screens over the new endpoints:

- /app/troubleshooting/blocks: pick a Block playout, browse its blocks
  (grouped, name filter), drill into paged block history, click a row to
  decode its details.
- /app/troubleshooting/yaml: paste YAML, toggle import schema, validate;
  shows messages + JSON conversion.

Adds api/playoutHistory client + validateSequentialSchedule; regenerated
generated/v1.d.ts. Async loads follow the LogsScreen seq-guard pattern
(no synchronous setState inside effects).

Refs #145 #158

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Client param-mapping tests for playoutHistory + validateSequentialSchedule,
and screen-level flow tests (Block playout filtering + playout->block->
history->decode drilldown; YAML validate valid/invalid/import-toggle).

Refs #145 #158

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
merge main into feat/145-block-history (post-#181); regenerate OpenAPI artifacts
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 4m45s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m52s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
d2096c0675
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
timothy merged commit 1c883ac601 into main 2026-07-07 22:32:32 +02:00
timothy deleted branch feat/145-block-history 2026-07-07 22:32:32 +02:00
Sign in to join this conversation.