Files
kandev/apps/backend/internal/github/service_watch_reset.go
T
2026-06-25 14:13:39 +01:00

126 lines
4.0 KiB
Go

package github
import (
"context"
"errors"
"go.uber.org/zap"
"github.com/kandev/kandev/internal/watchreset"
)
// --- Issue watch reset ---
// githubIssueWatchResetter is the watchreset.Resetter adapter for a single
// GitHub issue watch. Closes over the store and watch ID so the shared
// watchreset.Run helper stays integration-agnostic.
type githubIssueWatchResetter struct {
store *Store
watchID string
}
func (r *githubIssueWatchResetter) ListTaskIDs(ctx context.Context) ([]string, error) {
return r.store.ListIssueWatchTaskIDsByWatch(ctx, r.watchID)
}
func (r *githubIssueWatchResetter) Clear(ctx context.Context) error {
return r.store.ResetIssueWatchState(ctx, r.watchID)
}
// PreviewResetIssueWatch returns how many tasks ResetIssueWatch would
// cascade-delete. Used by the frontend to populate the confirmation dialog.
func (s *Service) PreviewResetIssueWatch(ctx context.Context, watchID string) (int, error) {
if s.store == nil {
return 0, errStoreUnavailable
}
return watchreset.Preview(ctx, &githubIssueWatchResetter{store: s.store, watchID: watchID})
}
// ResetIssueWatch is destructive: cascade-deletes every task previously
// created by the issue watch (including archived), wipes the per-watch
// dedup rows, and nulls last_polled_at so the next poll re-imports every
// currently-matching issue. Returns the count of tasks deleted.
func (s *Service) ResetIssueWatch(ctx context.Context, watchID string) (int, error) {
if s.store == nil {
return 0, errStoreUnavailable
}
s.mu.Lock()
td := s.cascadeTaskDeleter
s.mu.Unlock()
if td == nil {
return 0, errors.New("github: cascade task deleter not wired; reset unavailable")
}
res, err := watchreset.Run(ctx,
&githubIssueWatchResetter{store: s.store, watchID: watchID},
td, s.logger)
return res.TasksDeleted, err
}
// --- Review watch reset ---
// githubReviewWatchResetter is the watchreset.Resetter adapter for a single
// GitHub review watch.
type githubReviewWatchResetter struct {
store *Store
watchID string
}
func (r *githubReviewWatchResetter) ListTaskIDs(ctx context.Context) ([]string, error) {
return r.store.ListReviewPRTaskIDsByWatch(ctx, r.watchID)
}
func (r *githubReviewWatchResetter) Clear(ctx context.Context) error {
return r.store.ResetReviewWatchState(ctx, r.watchID)
}
// PreviewResetReviewWatch returns how many tasks ResetReviewWatch would
// cascade-delete. Used by the frontend to populate the confirmation dialog.
func (s *Service) PreviewResetReviewWatch(ctx context.Context, watchID string) (int, error) {
if s.store == nil {
return 0, errStoreUnavailable
}
return watchreset.Preview(ctx, &githubReviewWatchResetter{store: s.store, watchID: watchID})
}
// ResetReviewWatch is destructive: cascade-deletes every task previously
// created by the review watch (including archived), wipes the per-watch
// dedup rows, and then schedules the watch to re-run so currently-matching
// PRs are published for task creation. Returns the count of tasks deleted.
func (s *Service) ResetReviewWatch(ctx context.Context, watchID string) (int, error) {
if s.store == nil {
return 0, errStoreUnavailable
}
s.mu.Lock()
td := s.cascadeTaskDeleter
s.mu.Unlock()
if td == nil {
return 0, errors.New("github: cascade task deleter not wired; reset unavailable")
}
res, err := watchreset.Run(ctx,
&githubReviewWatchResetter{store: s.store, watchID: watchID},
td, s.logger)
if err != nil {
return res.TasksDeleted, err
}
go s.reimportReviewWatchAfterReset(context.Background(), watchID)
return res.TasksDeleted, nil
}
func (s *Service) reimportReviewWatchAfterReset(ctx context.Context, watchID string) {
watch, err := s.GetReviewWatch(ctx, watchID)
if err != nil {
s.logger.Warn("reset review watch: could not load watch for re-import",
zap.String("watch_id", watchID),
zap.Error(err))
return
}
if watch == nil || !watch.Enabled {
return
}
if _, err := s.TriggerReviewWatch(ctx, watch); err != nil {
s.logger.Warn("reset review watch: re-import failed",
zap.String("watch_id", watch.ID),
zap.Error(err))
}
}