API gap: multi-collections have no REST endpoints (blocks full #140 parity / #91) #151

Closed
opened 2026-07-07 12:22:35 +02:00 by timothy · 2 comments
Owner

Recon for #140 (2026-07-07): multi-collections are Blazor-only — MediatR handlers exist (MultiCollectionViewModel, PagedMultiCollectionsViewModel, create/update items with ScheduleAsGroup + PlaybackOrder) but there is no API controller. Note GET /api/collections returns ONLY manual collections (GetAllCollectionsHandler queries the Collection table and the mapper hardcodes CollectionType.Collection), so the SPA cannot even list multi-collections read-only.

Needed for full Blazor parity (#91 phase b): CRUD controller following the CollectionController/SmartCollectionController pattern (ApiResults, 404 pre-checks, OpenApiErrorResponseContractTests entries, file-scoped #nullable enable DTOs), then a SPA editor (Blazor reference: MultiCollectionEditor.razor, composes existing collections/smart collections).

Part of epic #59; blocks #91 phase (b) alongside #140.

Recon for #140 (2026-07-07): multi-collections are Blazor-only — MediatR handlers exist (`MultiCollectionViewModel`, `PagedMultiCollectionsViewModel`, create/update items with `ScheduleAsGroup` + `PlaybackOrder`) but there is no API controller. Note `GET /api/collections` returns ONLY manual collections (`GetAllCollectionsHandler` queries the `Collection` table and the mapper hardcodes `CollectionType.Collection`), so the SPA cannot even list multi-collections read-only. Needed for full Blazor parity (#91 phase b): CRUD controller following the CollectionController/SmartCollectionController pattern (ApiResults, 404 pre-checks, OpenApiErrorResponseContractTests entries, file-scoped `#nullable enable` DTOs), then a SPA editor (Blazor reference: `MultiCollectionEditor.razor`, composes existing collections/smart collections). Part of epic #59; blocks #91 phase (b) alongside #140.
Author
Owner

Backend REST API landed via PR #188 (merged to main 1c035cc9) — issue kept OPEN pending the SPA editor screen.

Done: MultiCollectionController (/api/multi-collections) CRUD (list paged/get/create/update/delete) over the existing MediatR handlers, following the CollectionController pattern (ApiResults, 404 pre-checks, #nullable enable DTOs, OpenApiErrorResponseContractTests entries). Thin api-client web/src/api/multiCollections.ts added. OpenAPI + TS types regenerated. docs/blazor-route-parity.md updated.

Still pending (this issue): the ChicoryTV SPA editor screen mirroring the Blazor Multi-collectionEditor.razor (composition of collections/smart-collections; rerun adds the CollectionType autocomplete + first-run/rerun playback orders). Next-session work.

Deferred follow-up (from adversarial review): the rerun create/update path does not existence-check the selected entity id (a non-existent SelectedId can produce a dangling-FK row or a 500 on SaveChanges) — consistent with api-conventions §3's deliberate deep-FK precedent, but the API now exposes it as a single top-level FK. Worth a small hardening issue. (The invalid-CollectionType hole — Playlist/internal types persisting a wrong-kind id — WAS fixed in this PR: both verbs now 422 via IsSupportedSelectionType.)

**Backend REST API landed** via PR #188 (merged to main 1c035cc9) — issue kept OPEN pending the SPA editor screen. **Done:** `MultiCollectionController (/api/multi-collections)` CRUD (list paged/get/create/update/delete) over the existing MediatR handlers, following the CollectionController pattern (ApiResults, 404 pre-checks, `#nullable enable` DTOs, OpenApiErrorResponseContractTests entries). Thin api-client `web/src/api/multiCollections.ts` added. OpenAPI + TS types regenerated. `docs/blazor-route-parity.md` updated. **Still pending (this issue):** the ChicoryTV **SPA editor screen** mirroring the Blazor `Multi-collectionEditor.razor` (composition of collections/smart-collections; rerun adds the CollectionType autocomplete + first-run/rerun playback orders). Next-session work. **Deferred follow-up (from adversarial review):** the rerun create/update path does not existence-check the selected entity id (a non-existent SelectedId can produce a dangling-FK row or a 500 on SaveChanges) — consistent with api-conventions §3's deliberate deep-FK precedent, but the API now exposes it as a single top-level FK. Worth a small hardening issue. (The invalid-CollectionType hole — Playlist/internal types persisting a wrong-kind id — WAS fixed in this PR: both verbs now 422 via IsSupportedSelectionType.)
Author
Owner

Closed via PR #194 (merged to main 636b680a).

What was done — the SPA multi-collection editor screen, completing #151 (the REST API landed earlier in PR #188). New screen MultiCollectionsScreen at /app/multi-collections: list + in-screen editor with a Name field, two add-pickers (over getCollections() / getSmartCollections(), already-present options filtered out), an items list with a per-item Schedule As Group toggle and remove, and delete via ConfirmDialog. Per-item playbackOrder is fixed to "Chronological" on save (matches the legacy MultiCollectionEditor.razor).

Files: web/src/screens/MultiCollectionsScreen.tsx (+ .test.tsx), web/src/api/multiCollections.ts (added messageFromMultiCollectionError) + multiCollections.test.ts, web/src/api/index.ts, web/src/App.tsx (six touch points) + App.test.tsx.

Verification: 354 web tests pass, lint + build clean. Adversarial fork review of the diff — clean (no Critical/High/Medium). Live-E2E passed (built + ran locally via scripts/e2e-local.sh): created a collection, added it as an item with Schedule-As-Group on, saved, confirmed persistence — zero console errors.

Docs: docs/blazor-route-parity.md #151 row → SPA DONE; docs/domain-model.md multi-collection row now points at /app/multi-collections.

Unblocks #91 phase (b) (one of the remaining SPA-editor gates).

Closed via PR #194 (merged to main `636b680a`). **What was done** — the SPA multi-collection editor screen, completing #151 (the REST API landed earlier in PR #188). New screen **`MultiCollectionsScreen`** at `/app/multi-collections`: list + in-screen editor with a Name field, two add-pickers (over `getCollections()` / `getSmartCollections()`, already-present options filtered out), an items list with a per-item **Schedule As Group** toggle and remove, and delete via `ConfirmDialog`. Per-item `playbackOrder` is fixed to `"Chronological"` on save (matches the legacy `MultiCollectionEditor.razor`). **Files**: `web/src/screens/MultiCollectionsScreen.tsx` (+ `.test.tsx`), `web/src/api/multiCollections.ts` (added `messageFromMultiCollectionError`) + `multiCollections.test.ts`, `web/src/api/index.ts`, `web/src/App.tsx` (six touch points) + `App.test.tsx`. **Verification**: 354 web tests pass, lint + build clean. Adversarial fork review of the diff — clean (no Critical/High/Medium). **Live-E2E passed** (built + ran locally via `scripts/e2e-local.sh`): created a collection, added it as an item with Schedule-As-Group on, saved, confirmed persistence — zero console errors. **Docs**: `docs/blazor-route-parity.md` #151 row → **SPA DONE**; `docs/domain-model.md` multi-collection row now points at `/app/multi-collections`. Unblocks #91 phase (b) (one of the remaining SPA-editor gates).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: timothy/ersatztv#151