Refactor dependency dirty marking to use shared helper (bd-56)

Replace duplicated dirty-marking logic in AddDependency and
RemoveDependency with a new markIssuesDirtyTx helper function.
This improves code maintainability and ensures consistent behavior.

The Problem:
- AddDependency and RemoveDependency had ~20 lines of duplicated code
- Each manually prepared statements and marked issues dirty
- Violation of DRY principle
- Pattern was fragile if preparation failed

The Fix:
- Created markIssuesDirtyTx() helper in dirty.go
- Takes existing transaction and issue IDs
- Both functions now use: markIssuesDirtyTx(ctx, tx, []string{id1, id2})
- Reduced from 20 lines to 3 lines per function

Benefits:
- Eliminates code duplication (DRY)
- Single source of truth for transaction-based dirty marking
- More readable and maintainable
- Easier to modify behavior in future
- Consistent error messages

Changes:
- internal/storage/sqlite/dirty.go:129-154
  * Add markIssuesDirtyTx() helper function
- internal/storage/sqlite/dependencies.go:115-117, 147-149
  * Replace duplicated code with helper call in both functions

Testing:
- All existing tests pass ✓
- Verified both issues marked dirty with same timestamp ✓
- Dependency add/remove works correctly ✓

Impact:
- Cleaner, more maintainable codebase
- No functional changes, pure refactor
- Foundation for future improvements

Closes bd-56

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-10-14 00:35:43 -07:00
parent 3aeeeb752c
commit 81ab3c3d1b
2 changed files with 31 additions and 32 deletions

View File

@@ -112,22 +112,8 @@ func (s *SQLiteStorage) AddDependency(ctx context.Context, dep *types.Dependency
// Mark both issues as dirty for incremental export // Mark both issues as dirty for incremental export
// (dependencies are exported with each issue, so both need updating) // (dependencies are exported with each issue, so both need updating)
now := time.Now() if err := markIssuesDirtyTx(ctx, tx, []string{dep.IssueID, dep.DependsOnID}); err != nil {
stmt, err := tx.PrepareContext(ctx, ` return err
INSERT INTO dirty_issues (issue_id, marked_at)
VALUES (?, ?)
ON CONFLICT (issue_id) DO UPDATE SET marked_at = excluded.marked_at
`)
if err != nil {
return fmt.Errorf("failed to prepare dirty statement: %w", err)
}
defer stmt.Close()
if _, err := stmt.ExecContext(ctx, dep.IssueID, now); err != nil {
return fmt.Errorf("failed to mark issue dirty: %w", err)
}
if _, err := stmt.ExecContext(ctx, dep.DependsOnID, now); err != nil {
return fmt.Errorf("failed to mark dependency target dirty: %w", err)
} }
return tx.Commit() return tx.Commit()
@@ -158,22 +144,8 @@ func (s *SQLiteStorage) RemoveDependency(ctx context.Context, issueID, dependsOn
} }
// Mark both issues as dirty for incremental export // Mark both issues as dirty for incremental export
now := time.Now() if err := markIssuesDirtyTx(ctx, tx, []string{issueID, dependsOnID}); err != nil {
stmt, err := tx.PrepareContext(ctx, ` return err
INSERT INTO dirty_issues (issue_id, marked_at)
VALUES (?, ?)
ON CONFLICT (issue_id) DO UPDATE SET marked_at = excluded.marked_at
`)
if err != nil {
return fmt.Errorf("failed to prepare dirty statement: %w", err)
}
defer stmt.Close()
if _, err := stmt.ExecContext(ctx, issueID, now); err != nil {
return fmt.Errorf("failed to mark issue dirty: %w", err)
}
if _, err := stmt.ExecContext(ctx, dependsOnID, now); err != nil {
return fmt.Errorf("failed to mark dependency target dirty: %w", err)
} }
return tx.Commit() return tx.Commit()

View File

@@ -125,3 +125,30 @@ func (s *SQLiteStorage) GetDirtyIssueCount(ctx context.Context) (int, error) {
} }
return count, nil return count, nil
} }
// markIssuesDirtyTx marks multiple issues as dirty within an existing transaction
// This is a helper for operations that need to mark issues dirty as part of a larger transaction
func markIssuesDirtyTx(ctx context.Context, tx *sql.Tx, issueIDs []string) error {
if len(issueIDs) == 0 {
return nil
}
now := time.Now()
stmt, err := tx.PrepareContext(ctx, `
INSERT INTO dirty_issues (issue_id, marked_at)
VALUES (?, ?)
ON CONFLICT (issue_id) DO UPDATE SET marked_at = excluded.marked_at
`)
if err != nil {
return fmt.Errorf("failed to prepare dirty statement: %w", err)
}
defer stmt.Close()
for _, issueID := range issueIDs {
if _, err := stmt.ExecContext(ctx, issueID, now); err != nil {
return fmt.Errorf("failed to mark issue %s dirty: %w", issueID, err)
}
}
return nil
}