diff --git a/backend/internal/http/handlers.go b/backend/internal/http/handlers.go index 665c652..ce972d4 100644 --- a/backend/internal/http/handlers.go +++ b/backend/internal/http/handlers.go @@ -98,5 +98,14 @@ func NewRouter(svc service.EpisodeService) *gin.Engine { }) }) + // GET /v1/shows — list all rows from "current" + v1.GET("/shows", func(c *gin.Context) { + items, err := svc.ListAll(c.Request.Context()) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "list failed"}) + return + } + c.JSON(http.StatusOK, items) + }) return r } diff --git a/backend/internal/http/handlers_test.go b/backend/internal/http/handlers_test.go index 0b1a92a..97e56eb 100644 --- a/backend/internal/http/handlers_test.go +++ b/backend/internal/http/handlers_test.go @@ -41,6 +41,14 @@ func (f *fakeSvc) MoveToArchive(ctx context.Context, ids []int64) (repo.MoveResu f.lastMove = append([]int64(nil), ids...) return f.moveRes, f.moveErr } +func (f *fakeSvc) ListAll(ctx context.Context) ([]repo.Episode, error) { + if f.moveErr != nil { + return nil, f.moveErr + } + return []repo.Episode{ + {Id: 10, EpTitle: "X"}, + }, nil +} // ---- helpers ---- diff --git a/backend/internal/repo/episode_repo.go b/backend/internal/repo/episode_repo.go index 519df9f..e2b7295 100644 --- a/backend/internal/repo/episode_repo.go +++ b/backend/internal/repo/episode_repo.go @@ -12,6 +12,7 @@ import ( var ErrNotFound = errors.New("not found") type Episode struct { + Id int `json:"id"` EpNum int `json:"ep_num"` EpTitle string `json:"ep_title"` SeasonName string `json:"season_name"` @@ -30,6 +31,7 @@ type EpisodeRepository interface { GetCurrent(ctx context.Context) (Episode, error) SetCurrent(ctx context.Context, id int64, startHHMMSS string) error MoveToArchive(ctx context.Context, ids []int64) (MoveResult, error) + ListAll(ctx context.Context) ([]Episode, error) } type pgxEpisodeRepo struct { @@ -40,9 +42,40 @@ func NewEpisodeRepo(pool *pgxpool.Pool) EpisodeRepository { return &pgxEpisodeRepo{pool: pool} } +func (r *pgxEpisodeRepo) ListAll(ctx context.Context) ([]Episode, error) { + const q = ` +SELECT + id, + ep_num, + ep_title, + season_name, + to_char(start_time, 'HH24:MI:SS') AS start_time, + to_char(playback_length, 'HH24:MI:SS') AS playback_length, + date_created +FROM current +ORDER BY id DESC; +` + rows, err := r.pool.Query(ctx, q) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []Episode + for rows.Next() { + var e Episode + if err := rows.Scan(&e.Id, &e.EpNum, &e.EpTitle, &e.SeasonName, &e.StartTime, &e.PlaybackLength, &e.DateCreated); err != nil { + return nil, err + } + out = append(out, e) + } + return out, rows.Err() +} + func (r *pgxEpisodeRepo) GetCurrent(ctx context.Context) (Episode, error) { const q = ` SELECT + id, ep_num, ep_title, season_name, @@ -56,7 +89,7 @@ LIMIT 1; ` var e Episode err := r.pool.QueryRow(ctx, q).Scan( - &e.EpNum, &e.EpTitle, &e.SeasonName, &e.StartTime, &e.PlaybackLength, &e.DateCreated, + &e.Id, &e.EpNum, &e.EpTitle, &e.SeasonName, &e.StartTime, &e.PlaybackLength, &e.DateCreated, ) if err != nil { if errors.Is(err, pgx.ErrNoRows) { diff --git a/backend/internal/service/episode_service.go b/backend/internal/service/episode_service.go index dac38b3..ab50e05 100644 --- a/backend/internal/service/episode_service.go +++ b/backend/internal/service/episode_service.go @@ -18,6 +18,7 @@ type EpisodeService interface { GetCurrent(ctx context.Context) (repo.Episode, error) SetCurrent(ctx context.Context, id int64, start string) (repo.Episode, error) MoveToArchive(ctx context.Context, ids []int64) (repo.MoveResult, error) + ListAll(ctx context.Context) ([]repo.Episode, error) } type episodeService struct { @@ -28,6 +29,12 @@ func NewEpisodeService(r repo.EpisodeRepository) EpisodeService { return &episodeService{repo: r} } +func (s *episodeService) ListAll(ctx context.Context) ([]repo.Episode, error) { + c, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + return s.repo.ListAll(c) +} + func (s *episodeService) GetCurrent(ctx context.Context) (repo.Episode, error) { c, cancel := context.WithTimeout(ctx, 3*time.Second) defer cancel() diff --git a/backend/internal/service/episode_service_test.go b/backend/internal/service/episode_service_test.go index 0f9b96d..4122992 100644 --- a/backend/internal/service/episode_service_test.go +++ b/backend/internal/service/episode_service_test.go @@ -23,6 +23,8 @@ type fakeRepo struct { start string } moveCalls [][]int64 + listRes []repo.Episode + listErr error } func (f *fakeRepo) GetCurrent(ctx context.Context) (repo.Episode, error) { @@ -39,6 +41,9 @@ func (f *fakeRepo) MoveToArchive(ctx context.Context, ids []int64) (repo.MoveRes f.moveCalls = append(f.moveCalls, ids) return f.moveRes, f.moveErr } +func (f *fakeRepo) ListAll(ctx context.Context) ([]repo.Episode, error) { + return f.listRes, f.listErr +} // ---- tests ---- @@ -141,3 +146,23 @@ func TestEpisodeService_MoveToArchive_DedupAndOK(t *testing.T) { t.Fatalf("unexpected response: %+v", res) } } + +func TestEpisodeService_ListAll_OK(t *testing.T) { + fr := &fakeRepo{listRes: []repo.Episode{{Id: 3}, {Id: 1}}} + svc := service.NewEpisodeService(fr) + got, err := svc.ListAll(context.Background()) + if err != nil { + t.Fatalf("unexpected: %v", err) + } + if len(got) != 2 || got[0].Id != 3 { + t.Fatalf("bad: %+v", got) + } +} + +func TestEpisodeService_ListAll_Error(t *testing.T) { + fr := &fakeRepo{listErr: errors.New("boom")} + svc := service.NewEpisodeService(fr) + if _, err := svc.ListAll(context.Background()); err == nil { + t.Fatal("expected error") + } +}