morz-infoboard/docs/superpowers/plans/2026-03-26-reorder-validation.md
Jesko Anschütz b463aeeae1 docs: Implementierungsplan für Reorder-Validierung
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 22:20:15 +01:00

252 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Reorder-Validierung Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** `PlaylistStore.Reorder` soll mit `ErrReorderMismatch` abbrechen, wenn die übergebene ID-Liste nicht vollständig oder nicht korrekt ist; beide Handler geben dann 400 zurück.
**Architecture:** Sentinel-Fehler im store-Package; Vollständigkeitsprüfung per COUNT + RowsAffected-Check im Store; beide HTTP-Handler unterscheiden Validierungsfehler von DB-Fehlern via `errors.Is`.
**Tech Stack:** Go, pgx/v5, net/http
---
### Task 1: Sentinel-Fehler + Validierung in `store.go`
**Files:**
- Modify: `server/backend/internal/store/store.go:4-10` (imports)
- Modify: `server/backend/internal/store/store.go:533-550` (Reorder)
- [ ] **Schritt 1: `errors` zu den Imports hinzufügen**
Datei: `server/backend/internal/store/store.go`, Zeilen 410.
Vorher:
```go
import (
"context"
"fmt"
"time"
"github.com/jackc/pgx/v5/pgxpool"
)
```
Nachher:
```go
import (
"context"
"errors"
"fmt"
"time"
"github.com/jackc/pgx/v5/pgxpool"
)
```
- [ ] **Schritt 2: Sentinel-Variable nach den Imports einfügen**
Direkt nach dem Import-Block (vor der ersten Typdefinition) einfügen:
```go
// ErrReorderMismatch wird von Reorder zurückgegeben, wenn die übergebene
// ID-Liste nicht mit den tatsächlichen Items der Playlist übereinstimmt.
var ErrReorderMismatch = errors.New("reorder: item list does not match playlist")
```
- [ ] **Schritt 3: `Reorder`-Funktion ersetzen**
Datei: `server/backend/internal/store/store.go`, die gesamte `Reorder`-Funktion (aktuell Zeilen 533550) ersetzen durch:
```go
// Reorder sets order_index for each item ID in the given slice order.
// Returns ErrReorderMismatch if the number of provided IDs does not match
// the number of items in the playlist, or if any ID does not belong to it.
func (s *PlaylistStore) Reorder(ctx context.Context, playlistID string, itemIDs []string) error {
tx, err := s.pool.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx) //nolint:errcheck
var count int
if err := tx.QueryRow(ctx,
`select count(*) from playlist_items where playlist_id=$1`, playlistID,
).Scan(&count); err != nil {
return err
}
if count != len(itemIDs) {
return fmt.Errorf("%w: got %d ids, playlist has %d items",
ErrReorderMismatch, len(itemIDs), count)
}
for i, id := range itemIDs {
tag, err := tx.Exec(ctx,
`update playlist_items set order_index=$1 where id=$2 and playlist_id=$3`,
i, id, playlistID,
)
if err != nil {
return err
}
if tag.RowsAffected() != 1 {
return fmt.Errorf("%w: id %s not found in playlist %s",
ErrReorderMismatch, id, playlistID)
}
}
return tx.Commit(ctx)
}
```
- [ ] **Schritt 4: Kompilieren**
```bash
cd server/backend && go build ./...
```
Erwartet: keine Ausgabe, Exit 0.
- [ ] **Schritt 5: Committen**
```bash
git add server/backend/internal/store/store.go
git commit -m "fix(store): Reorder validiert Vollständigkeit und RowsAffected"
```
---
### Task 2: `HandleReorderUI` gibt 400 bei Mismatch zurück
**Files:**
- Modify: `server/backend/internal/httpapi/manage/ui.go:1-20` (imports)
- Modify: `server/backend/internal/httpapi/manage/ui.go:743-746` (Fehlerbehandlung)
- [ ] **Schritt 1: `errors` zu den Imports hinzufügen**
Datei: `server/backend/internal/httpapi/manage/ui.go`. Im import-Block `"errors"` ergänzen (alphabetisch vor `"encoding/json"`):
```go
import (
"bytes"
"encoding/json"
"errors"
"html/template"
"log/slog"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"git.az-it.net/az/morz-infoboard/server/backend/internal/config"
"git.az-it.net/az/morz-infoboard/server/backend/internal/fileutil"
"git.az-it.net/az/morz-infoboard/server/backend/internal/mqttnotifier"
"git.az-it.net/az/morz-infoboard/server/backend/internal/reqcontext"
"git.az-it.net/az/morz-infoboard/server/backend/internal/store"
)
```
- [ ] **Schritt 2: Fehlerbehandlung in `HandleReorderUI` ersetzen**
Datei: `server/backend/internal/httpapi/manage/ui.go`, Zeilen 743746. Den Block:
```go
if err := playlists.Reorder(r.Context(), playlist.ID, ids); err != nil {
http.Error(w, "db error", http.StatusInternalServerError)
return
}
```
ersetzen durch:
```go
if err := playlists.Reorder(r.Context(), playlist.ID, ids); err != nil {
if errors.Is(err, store.ErrReorderMismatch) {
http.Error(w, "item list mismatch", http.StatusBadRequest)
} else {
http.Error(w, "db error", http.StatusInternalServerError)
}
return
}
```
- [ ] **Schritt 3: Kompilieren**
```bash
cd server/backend && go build ./...
```
Erwartet: keine Ausgabe, Exit 0.
- [ ] **Schritt 4: Committen**
```bash
git add server/backend/internal/httpapi/manage/ui.go
git commit -m "fix(manage): HandleReorderUI gibt 400 bei Mismatch zurück"
```
---
### Task 3: `HandleReorder` gibt 400 bei Mismatch zurück
**Files:**
- Modify: `server/backend/internal/httpapi/manage/playlist.go:1-14` (imports)
- Modify: `server/backend/internal/httpapi/manage/playlist.go:244-247` (Fehlerbehandlung)
- [ ] **Schritt 1: `errors` zu den Imports hinzufügen**
Datei: `server/backend/internal/httpapi/manage/playlist.go`. Im import-Block `"errors"` ergänzen:
```go
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"git.az-it.net/az/morz-infoboard/server/backend/internal/mqttnotifier"
"git.az-it.net/az/morz-infoboard/server/backend/internal/reqcontext"
"git.az-it.net/az/morz-infoboard/server/backend/internal/store"
)
```
- [ ] **Schritt 2: Fehlerbehandlung in `HandleReorder` ersetzen**
Datei: `server/backend/internal/httpapi/manage/playlist.go`, Zeilen 244247. Den Block:
```go
if err := playlists.Reorder(r.Context(), playlistID, ids); err != nil {
http.Error(w, "db error", http.StatusInternalServerError)
return
}
```
ersetzen durch:
```go
if err := playlists.Reorder(r.Context(), playlistID, ids); err != nil {
if errors.Is(err, store.ErrReorderMismatch) {
http.Error(w, "item list mismatch", http.StatusBadRequest)
} else {
http.Error(w, "db error", http.StatusInternalServerError)
}
return
}
```
- [ ] **Schritt 3: Kompilieren und Tests ausführen**
```bash
cd server/backend && go build ./... && go test ./...
```
Erwartet: keine Ausgabe bei build, alle Tests grün.
- [ ] **Schritt 4: Committen**
```bash
git add server/backend/internal/httpapi/manage/playlist.go
git commit -m "fix(manage): HandleReorder gibt 400 bei Mismatch zurück"
```