Added set current
This commit is contained in:
parent
0c4453500f
commit
7a3a732efb
@ -30,5 +30,33 @@ func NewRouter(svc service.EpisodeService) *gin.Engine {
|
|||||||
c.JSON(http.StatusOK, cur)
|
c.JSON(http.StatusOK, cur)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
type setCurrentReq struct {
|
||||||
|
ID int64 `json:"id" binding:"required"`
|
||||||
|
StartTime string `json:"start_time" binding:"required"` // HH:MM or HH:MM:SS
|
||||||
|
}
|
||||||
|
|
||||||
|
r.POST("/current", func(c *gin.Context) {
|
||||||
|
var req setCurrentReq
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cur, err := svc.SetCurrent(c.Request.Context(), req.ID, req.StartTime)
|
||||||
|
if err != nil {
|
||||||
|
switch err {
|
||||||
|
case service.ErrInvalidTime:
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
case repo.ErrNotFound:
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"error": "id not found"})
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "update failed"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, cur)
|
||||||
|
})
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ type Episode struct {
|
|||||||
|
|
||||||
type EpisodeRepository interface {
|
type EpisodeRepository interface {
|
||||||
GetCurrent(ctx context.Context) (Episode, error)
|
GetCurrent(ctx context.Context) (Episode, error)
|
||||||
|
SetCurrent(ctx context.Context, id int64, startHHMMSS string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type pgxEpisodeRepo struct {
|
type pgxEpisodeRepo struct {
|
||||||
@ -58,3 +59,27 @@ LIMIT 1;
|
|||||||
}
|
}
|
||||||
return e, nil
|
return e, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *pgxEpisodeRepo) SetCurrent(ctx context.Context, id int64, startHHMMSS string) error {
|
||||||
|
tx, err := r.pool.Begin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback(ctx) }()
|
||||||
|
|
||||||
|
// 1) clear any current episode flag
|
||||||
|
if _, err := tx.Exec(ctx, `UPDATE current SET current_ep = false WHERE current_ep = true`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) set start_time and current_ep for the id
|
||||||
|
ct, err := tx.Exec(ctx, `UPDATE current SET start_time = $1::time, current_ep = true WHERE id = $2`, startHHMMSS, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ct.RowsAffected() == 0 {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.Commit(ctx)
|
||||||
|
}
|
||||||
|
|||||||
@ -2,13 +2,18 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"watch-party-backend/internal/repo"
|
"watch-party-backend/internal/repo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrInvalidTime = errors.New("invalid start_time (expected HH:MM or HH:MM:SS)")
|
||||||
|
|
||||||
type EpisodeService interface {
|
type EpisodeService interface {
|
||||||
GetCurrent(ctx context.Context) (repo.Episode, error)
|
GetCurrent(ctx context.Context) (repo.Episode, error)
|
||||||
|
SetCurrent(ctx context.Context, id int64, start string) (repo.Episode, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type episodeService struct {
|
type episodeService struct {
|
||||||
@ -20,8 +25,38 @@ func NewEpisodeService(r repo.EpisodeRepository) EpisodeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *episodeService) GetCurrent(ctx context.Context) (repo.Episode, error) {
|
func (s *episodeService) GetCurrent(ctx context.Context) (repo.Episode, error) {
|
||||||
// Add cross-cutting concerns here (timeouts, metrics, caching, etc.)
|
|
||||||
c, cancel := context.WithTimeout(ctx, 3*time.Second)
|
c, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
return s.repo.GetCurrent(c)
|
return s.repo.GetCurrent(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *episodeService) SetCurrent(ctx context.Context, id int64, start string) (repo.Episode, error) {
|
||||||
|
// normalize and validate time as HH:MM:SS
|
||||||
|
normalized, err := normalizeHHMMSS(start)
|
||||||
|
if err != nil {
|
||||||
|
return repo.Episode{}, ErrInvalidTime
|
||||||
|
}
|
||||||
|
|
||||||
|
c, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// do the transaction
|
||||||
|
if err := s.repo.SetCurrent(c, id, normalized); err != nil {
|
||||||
|
return repo.Episode{}, err
|
||||||
|
}
|
||||||
|
// return the new current row
|
||||||
|
return s.repo.GetCurrent(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeHHMMSS(s string) (string, error) {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
if len(s) == 5 { // HH:MM → append seconds
|
||||||
|
s += ":00"
|
||||||
|
}
|
||||||
|
// Strict parse/format to 24h
|
||||||
|
t, err := time.Parse("15:04:05", s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return t.Format("15:04:05"), nil
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user