Files
ersatztv/web/src/api
timothy 1a8c0f60de feat(playlists): wire optimistic-concurrency contract onto Playlist (#253 PR2)
Fans the frozen ETag/If-Match/412 recipe (Block reference, #253) onto the
Playlist aggregate:

- ReplacePlaylistItems command carries ExpectedVersion; the handler runs
  CheckVersion as a standalone Either after validation (so a stale write
  survives as 412, not flattened to 422 by Apply/Join), bumps Version
  unconditionally before saving, and persists via
  SaveChangesWithConcurrencyGuard (EF concurrency-token backstop).
- PlaylistViewModel carries Version; the items GET sets a strong ETag and
  the PUT parses If-Match, threads it into the command, and returns the
  refreshed ETag on success (400 on a malformed If-Match).
- Sibling item-adding handlers (AddItemsToPlaylist, AddMovie/Episode/
  Season/ShowToPlaylist) bump Version too, since they mutate the same
  editor-visible item list.
- SPA: playlists.ts exposes getPlaylistItemsWithMeta and an
  If-Match-aware updatePlaylist; PlaylistEditor holds the ETag in a ref,
  round-trips it on save, and opens a "changed elsewhere" ConfirmDialog on
  412 (mirrors BlockEditor).

Tests: new ReplacePlaylistItemsHandlerConcurrencyTests (stale/match/
force-write/no-op-bump/racing-save), new PlaylistController tests
(ETag on GET items, 400/412/thread-version/force-write on PUT), and a
vitest 412-conflict-dialog test for PlaylistsScreen. dotnet test:
1304/1304 green. web: npm run typecheck clean, npm run build clean,
vitest 664/664 green.

Ref #253 PR2.
2026-07-11 18:33:40 +02:00
..
2026-07-02 18:36:56 +02:00
2026-07-02 18:36:56 +02:00