Skip to content

Commit b06b915

Browse files
ericfitzclaude
andcommitted
fix(api): exclude soft-deleted records from pagination Count queries
The Count methods in note, document, asset, and repository GORM stores did not filter out soft-deleted records (deleted_at IS NULL), while their corresponding List methods did. This mismatch caused the total count in pagination responses to be higher than the actual returnable items, which could cause clients to paginate infinitely when offset >= total. Fixes #222 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a553b8a commit b06b915

6 files changed

Lines changed: 30 additions & 14 deletions

File tree

.version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"major": 1,
33
"minor": 3,
4-
"patch": 2,
4+
"patch": 3,
55
"prerelease": ""
66
}

api/asset_store_gorm.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -569,9 +569,13 @@ func (s *GormAssetStore) Count(ctx context.Context, threatModelID string) (int,
569569
logger.Debug("Counting assets for threat model %s", threatModelID)
570570

571571
var count int64
572-
result := s.db.WithContext(ctx).Model(&models.Asset{}).
573-
Where("threat_model_id = ?", threatModelID).
574-
Count(&count)
572+
query := s.db.WithContext(ctx).Model(&models.Asset{})
573+
if includeDeletedFromContext(ctx) {
574+
query = query.Where("threat_model_id = ?", threatModelID)
575+
} else {
576+
query = query.Where("threat_model_id = ? AND deleted_at IS NULL", threatModelID)
577+
}
578+
result := query.Count(&count)
575579

576580
if result.Error != nil {
577581
logger.Error("Failed to count assets: %v", result.Error)

api/document_store_gorm.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -456,9 +456,13 @@ func (s *GormDocumentStore) Count(ctx context.Context, threatModelID string) (in
456456
logger.Debug("Counting documents for threat model %s", threatModelID)
457457

458458
var count int64
459-
result := s.db.WithContext(ctx).Model(&models.Document{}).
460-
Where("threat_model_id = ?", threatModelID).
461-
Count(&count)
459+
query := s.db.WithContext(ctx).Model(&models.Document{})
460+
if includeDeletedFromContext(ctx) {
461+
query = query.Where("threat_model_id = ?", threatModelID)
462+
} else {
463+
query = query.Where("threat_model_id = ? AND deleted_at IS NULL", threatModelID)
464+
}
465+
result := query.Count(&count)
462466

463467
if result.Error != nil {
464468
logger.Error("Failed to count documents: %v", result.Error)

api/note_store_gorm.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -397,9 +397,13 @@ func (s *GormNoteStore) Count(ctx context.Context, threatModelID string) (int, e
397397
logger.Debug("Counting notes for threat model %s", threatModelID)
398398

399399
var count int64
400-
result := s.db.WithContext(ctx).Model(&models.Note{}).
401-
Where("threat_model_id = ?", threatModelID).
402-
Count(&count)
400+
query := s.db.WithContext(ctx).Model(&models.Note{})
401+
if includeDeletedFromContext(ctx) {
402+
query = query.Where("threat_model_id = ?", threatModelID)
403+
} else {
404+
query = query.Where("threat_model_id = ? AND deleted_at IS NULL", threatModelID)
405+
}
406+
result := query.Count(&count)
403407

404408
if result.Error != nil {
405409
logger.Error("Failed to count notes: %v", result.Error)

api/repository_store_gorm.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -510,9 +510,13 @@ func (s *GormRepositoryStore) Count(ctx context.Context, threatModelID string) (
510510
logger.Debug("Counting repositories for threat model %s", threatModelID)
511511

512512
var count int64
513-
result := s.db.WithContext(ctx).Model(&models.Repository{}).
514-
Where("threat_model_id = ?", threatModelID).
515-
Count(&count)
513+
query := s.db.WithContext(ctx).Model(&models.Repository{})
514+
if includeDeletedFromContext(ctx) {
515+
query = query.Where("threat_model_id = ?", threatModelID)
516+
} else {
517+
query = query.Where("threat_model_id = ? AND deleted_at IS NULL", threatModelID)
518+
}
519+
result := query.Count(&count)
516520

517521
if result.Error != nil {
518522
logger.Error("Failed to count repositories: %v", result.Error)

api/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ var (
5050
// Minor version number
5151
VersionMinor = "3"
5252
// Patch version number
53-
VersionPatch = "2"
53+
VersionPatch = "3"
5454
// VersionPreRelease is the pre-release label (e.g., "rc.0", "beta.1"), empty for stable releases
5555
VersionPreRelease = ""
5656
// GitCommit is the git commit hash from build

0 commit comments

Comments
 (0)