diff --git a/README.md b/README.md index 4ef4f59..d774f13 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Tokens are stored in your user config directory with strict file permissions (`0 ## Core Commands ```bash -rollbaz # default: list active issues for active project +rollbaz # default: list recent active issues for active project rollbaz active --limit 20 rollbaz recent --limit 20 rollbaz show 274 diff --git a/internal/app/service.go b/internal/app/service.go index 8dd4a55..86dd9e1 100644 --- a/internal/app/service.go +++ b/internal/app/service.go @@ -82,8 +82,8 @@ func (s *Service) Recent(ctx context.Context, limit int, filters IssueFilters) ( return leftTS > rightTS } - leftOccurrence := uint64Value(items[i].LastOccurrenceID) - rightOccurrence := uint64Value(items[j].LastOccurrenceID) + leftOccurrence := totalOccurrences(items[i]) + rightOccurrence := totalOccurrences(items[j]) return leftOccurrence > rightOccurrence }) @@ -225,10 +225,7 @@ func matchesTimeFilter(timestamp *uint64, sinceUnix *int64, untilUnix *int64) bo } func matchesOccurrenceFilter(item rollbar.Item, minOccurrences *uint64, maxOccurrences *uint64) bool { - occurrences := uint64Value(item.TotalOccurrences) - if occurrences == 0 { - occurrences = uint64Value(item.Occurrences) - } + occurrences := totalOccurrences(item) if minOccurrences != nil && occurrences < *minOccurrences { return false } @@ -257,6 +254,14 @@ func mapSummary(item rollbar.Item) IssueSummary { } } +func totalOccurrences(item rollbar.Item) uint64 { + if item.TotalOccurrences != nil { + return *item.TotalOccurrences + } + + return uint64Value(item.Occurrences) +} + func uint64Value(value *uint64) uint64 { if value == nil { return 0 diff --git a/internal/app/service_test.go b/internal/app/service_test.go index b31c46c..86391ea 100644 --- a/internal/app/service_test.go +++ b/internal/app/service_test.go @@ -70,17 +70,36 @@ func TestServiceActive(t *testing.T) { } } -func TestServiceRecentSortsByTimestampThenOccurrence(t *testing.T) { +func TestServiceRecentSortsByTimestamp(t *testing.T) { t.Parallel() ts1 := uint64(100) ts2 := uint64(200) - o1 := uint64(10) - o2 := uint64(20) service := NewService(fakeAPI{listItems: []rollbar.Item{ - {ID: 1, Counter: 1, LastOccurrenceTimestamp: &ts1, LastOccurrenceID: &o1}, - {ID: 2, Counter: 2, LastOccurrenceTimestamp: &ts2, LastOccurrenceID: &o2}, + {ID: 1, Counter: 1, LastOccurrenceTimestamp: &ts1}, + {ID: 2, Counter: 2, LastOccurrenceTimestamp: &ts2}, + }}) + + issues, err := service.Recent(context.Background(), 10, IssueFilters{}) + if err != nil { + t.Fatalf("Recent() error = %v", err) + } + if len(issues) < 2 || issues[0].Counter != 2 { + t.Fatalf("unexpected sort order: %+v", issues) + } +} + +func TestServiceRecentSortsByTimestampThenTotalOccurrences(t *testing.T) { + t.Parallel() + + ts := uint64(100) + totalLow := uint64(10) + totalHigh := uint64(20) + + service := NewService(fakeAPI{listItems: []rollbar.Item{ + {ID: 1, Counter: 1, LastOccurrenceTimestamp: &ts, TotalOccurrences: &totalLow}, + {ID: 2, Counter: 2, LastOccurrenceTimestamp: &ts, TotalOccurrences: &totalHigh}, }}) issues, err := service.Recent(context.Background(), 10, IssueFilters{}) @@ -95,19 +114,21 @@ func TestServiceRecentSortsByTimestampThenOccurrence(t *testing.T) { func TestServiceRecentLimitAndOccurrenceFallback(t *testing.T) { t.Parallel() - o1 := uint64(10) - o2 := uint64(20) + ts := uint64(100) + totalLow := uint64(10) + fallbackHigh := uint64(20) + ignoredOccurrence := uint64(999) service := NewService(fakeAPI{listItems: []rollbar.Item{ - {ID: 1, Counter: 1, LastOccurrenceID: &o1}, - {ID: 2, Counter: 2, LastOccurrenceID: &o2}, + {ID: 1, Counter: 1, LastOccurrenceTimestamp: &ts, Occurrences: &fallbackHigh}, + {ID: 2, Counter: 2, LastOccurrenceTimestamp: &ts, TotalOccurrences: &totalLow, Occurrences: &ignoredOccurrence}, }}) issues, err := service.Recent(context.Background(), 1, IssueFilters{}) if err != nil { t.Fatalf("Recent() error = %v", err) } - if len(issues) != 1 || issues[0].Counter != 2 { + if len(issues) != 1 || issues[0].Counter != 1 { t.Fatalf("unexpected fallback sort/limit: %+v", issues) } } diff --git a/internal/cli/root.go b/internal/cli/root.go index 30d992d..a0d566d 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -57,7 +57,7 @@ func NewRootCmd() *cobra.Command { Short: "Fast Rollbar triage from your terminal", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { - return runActive(cmd.Context(), *flags) + return runRecent(cmd.Context(), *flags) }, } cmd.Version = version diff --git a/internal/cli/root_test.go b/internal/cli/root_test.go index 53ecad9..4adfe44 100644 --- a/internal/cli/root_test.go +++ b/internal/cli/root_test.go @@ -219,11 +219,11 @@ func TestProjectRemoveValidation(t *testing.T) { } } -func TestRootCommandDefaultRunsActive(t *testing.T) { +func TestRootCommandDefaultRunsRecent(t *testing.T) { stdout := setupServerAndStdout(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { - case "/api/1/reports/top_active_items": - _, _ = fmt.Fprint(w, `{"err":0,"result":[{"item":{"id":1,"counter":2,"title":"x","status":"active","environment":"production"}}]}`) + case "/api/1/items": + _, _ = fmt.Fprint(w, `{"err":0,"result":{"items":[{"id":1,"counter":3,"title":"y","status":"active","environment":"production"}]}}`) default: t.Fatalf("unexpected path: %s", r.URL.Path) } @@ -234,8 +234,8 @@ func TestRootCommandDefaultRunsActive(t *testing.T) { if err := cmd.Execute(); err != nil { t.Fatalf("root execute error = %v", err) } - if !strings.Contains(stdout.String(), "COUNTER") || !strings.Contains(stdout.String(), "x") { - t.Fatalf("expected active list output, got %q", stdout.String()) + if !strings.Contains(stdout.String(), "COUNTER") || !strings.Contains(stdout.String(), "y") { + t.Fatalf("expected recent list output, got %q", stdout.String()) } }