diff --git a/server/backend/internal/store/store.go b/server/backend/internal/store/store.go index 51d2418..1d70f8e 100644 --- a/server/backend/internal/store/store.go +++ b/server/backend/internal/store/store.go @@ -3,12 +3,17 @@ package store import ( "context" + "errors" "fmt" "time" "github.com/jackc/pgx/v5/pgxpool" ) +// 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") + // ------------------------------------------------------------------ // Domain types // ------------------------------------------------------------------ @@ -531,20 +536,46 @@ func (s *PlaylistStore) ScreenSlugByItemID(ctx context.Context, itemID string) ( } // 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 { + seen := make(map[string]struct{}, len(itemIDs)) + for _, id := range itemIDs { + if _, dup := seen[id]; dup { + return fmt.Errorf("%w: duplicate id %s", ErrReorderMismatch, id) + } + seen[id] = struct{}{} + } + 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 { - if _, err := tx.Exec(ctx, + tag, err := tx.Exec(ctx, `update playlist_items set order_index=$1 where id=$2 and playlist_id=$3`, i, id, playlistID, - ); err != nil { + ) + 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) }