From e291ee078ee334a8da43ba7c93175c58745f5a81 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Thu, 6 Nov 2025 19:17:06 -0800 Subject: [PATCH] Fix storage backend extensibility by adding DeleteIssue to Storage interface - Added DeleteIssue to Storage interface - Implemented DeleteIssue in MemoryStorage backend - Removed brittle type assertion from deletion_tracking.go - Closes bd-1fkr --- cmd/bd/deletion_tracking.go | 12 +----------- internal/storage/memory/memory.go | 23 +++++++++++++++++++++++ internal/storage/storage.go | 1 + 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/cmd/bd/deletion_tracking.go b/cmd/bd/deletion_tracking.go index 8cb5168c..e57f36f9 100644 --- a/cmd/bd/deletion_tracking.go +++ b/cmd/bd/deletion_tracking.go @@ -99,20 +99,10 @@ func merge3WayAndPruneDeletions(ctx context.Context, store storage.Storage, json } // Prune accepted deletions from the database - // Use type assertion to access DeleteIssue method (available in concrete SQLiteStorage) - type deleter interface { - DeleteIssue(context.Context, string) error - } - - d, ok := store.(deleter) - if !ok { - return false, fmt.Errorf("storage backend does not support DeleteIssue") - } - // Collect all deletion errors - fail the operation if any delete fails var deletionErrors []error for _, id := range acceptedDeletions { - if err := d.DeleteIssue(ctx, id); err != nil { + if err := store.DeleteIssue(ctx, id); err != nil { deletionErrors = append(deletionErrors, fmt.Errorf("issue %s: %w", id, err)) } } diff --git a/internal/storage/memory/memory.go b/internal/storage/memory/memory.go index c6dcc9a4..2d916321 100644 --- a/internal/storage/memory/memory.go +++ b/internal/storage/memory/memory.go @@ -406,6 +406,29 @@ func (m *MemoryStorage) CloseIssue(ctx context.Context, id string, reason string }, actor) } +// DeleteIssue permanently deletes an issue and all associated data +func (m *MemoryStorage) DeleteIssue(ctx context.Context, id string) error { + m.mu.Lock() + defer m.mu.Unlock() + + // Check if issue exists + if _, ok := m.issues[id]; !ok { + return fmt.Errorf("issue not found: %s", id) + } + + // Delete the issue + delete(m.issues, id) + + // Delete associated data + delete(m.dependencies, id) + delete(m.labels, id) + delete(m.events, id) + delete(m.comments, id) + delete(m.dirty, id) + + return nil +} + // SearchIssues finds issues matching query and filters func (m *MemoryStorage) SearchIssues(ctx context.Context, query string, filter types.IssueFilter) ([]*types.Issue, error) { m.mu.RLock() diff --git a/internal/storage/storage.go b/internal/storage/storage.go index c422ea3f..3f5d50fd 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -17,6 +17,7 @@ type Storage interface { GetIssueByExternalRef(ctx context.Context, externalRef string) (*types.Issue, error) UpdateIssue(ctx context.Context, id string, updates map[string]interface{}, actor string) error CloseIssue(ctx context.Context, id string, reason string, actor string) error + DeleteIssue(ctx context.Context, id string) error SearchIssues(ctx context.Context, query string, filter types.IssueFilter) ([]*types.Issue, error) // Dependencies