2.7 KiB
2.7 KiB
Refactor ImportWatchlist 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: Refactor ImportWatchlist to use a database transaction for the import loop and improve logging.
Architecture: Use db.BeginTx to wrap the loop. ensureAnimeExists continues to use s.db to avoid long-running transactions during external API calls.
Tech Stack: Go, database/sql, csv.
Task 1: Refactor ImportWatchlist with Transaction
Files:
-
Modify:
api/watchlist/service.go -
Step 1: Update ImportWatchlist implementation
func (s *Service) ImportWatchlist(ctx context.Context, userID string, r io.Reader) error {
txQueries, tx, err := db.BeginTx(ctx, s.sqlDB)
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
reader := csv.NewReader(r)
if _, err := reader.Read(); err != nil {
return fmt.Errorf("failed to read csv header: %w", err)
}
records, err := reader.ReadAll()
if err != nil {
return fmt.Errorf("failed to read csv records: %w", err)
}
for i, record := range records {
if len(record) < 4 {
log.Printf("skipping row %d: insufficient columns", i+2) // i+2 because i is 0-indexed record after header
continue
}
animeID, err := strconv.ParseInt(record[0], 10, 64)
if err != nil {
return fmt.Errorf("row %d: invalid anime id: %w", i+2, err)
}
status := record[1]
if _, ok := validStatuses[status]; !ok {
status = "plan_to_watch"
}
currentEpisode, _ := strconv.ParseInt(record[2], 10, 64)
currentTimeSeconds, _ := strconv.ParseFloat(record[3], 64)
if err := s.ensureAnimeExists(ctx, animeID); err != nil {
return fmt.Errorf("row %d: failed to ensure anime: %w", i+2, err)
}
_, err = txQueries.UpsertWatchListEntry(ctx, db.UpsertWatchListEntryParams{
ID: uuid.New().String(),
UserID: userID,
AnimeID: animeID,
Status: status,
CurrentEpisode: sql.NullInt64{Int64: currentEpisode, Valid: currentEpisode > 0},
CurrentTimeSeconds: currentTimeSeconds,
})
if err != nil {
return fmt.Errorf("row %d: failed to upsert entry: %w", i+2, err)
}
}
return tx.Commit()
}
-
Step 2: Add missing import
log -
Step 3: Verify compilation
Run: go build ./api/watchlist/...
Expected: Success (no output)
- Step 4: Verify tests
Run: go test ./api/watchlist/...
Expected: Success
- Step 5: Commit
git add api/watchlist/service.go
git commit -m "refactor: wrap watchlist import in transaction"