Skip to content

Commit 2fa263b

Browse files
committed
fix(list): read a capped total as a floor and trust has_next_page past it
The incident, alert and alert-event list endpoints stop counting at 1000: past that they keep returning rows but report total=1000, so notes like "showing 100 of 1000 total results" understated the set, and pages past the cap printed no hint at all. Those commands now mark their totals as capped. A total that reached the cap reads "at least 1000", and whether rows remain is taken from the response's has_next_page instead of the page offset. Uncapped lists are unchanged, including a real total of exactly 1000.
1 parent 9fc6373 commit 2fa263b

5 files changed

Lines changed: 81 additions & 14 deletions

File tree

‎internal/cli/alert.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func newAlertListCmd() *cobra.Command {
9696
if err != nil {
9797
return err
9898
}
99-
return ctx.PrintList(proj, nil, len(result.Items), page, limit, int(result.Total))
99+
return ctx.PrintCappedList(proj, nil, len(result.Items), page, limit, int(result.Total), result.HasNextPage)
100100
}
101101

102102
cols := []output.Column{
@@ -109,7 +109,7 @@ func newAlertListCmd() *cobra.Command {
109109
{Header: "STARTED", Field: func(v any) string { return output.FormatTime(v.(flashduty.AlertItem).StartTime) }},
110110
}
111111

112-
return ctx.PrintList(result.Items, cols, len(result.Items), page, limit, int(result.Total))
112+
return ctx.PrintCappedList(result.Items, cols, len(result.Items), page, limit, int(result.Total), result.HasNextPage)
113113
})
114114
},
115115
}
@@ -236,7 +236,7 @@ func newAlertEventsCmd() *cobra.Command {
236236
defaults := listDefaultsFor("Alerts", "ReadEventList")
237237
win := requestWindow(ctx.Cmd, nil, defaults)
238238
return ctx.PrintPage(result.Items, cols, listPage{
239-
Count: len(result.Items), Page: win.page, Base: win.base, Limit: win.limit, Total: int(result.Total), More: moreOf(result.HasNextPage),
239+
Count: len(result.Items), Page: win.page, Base: win.base, Limit: win.limit, Total: int(result.Total), More: moreOf(result.HasNextPage), TotalCapped: true,
240240
})
241241
})
242242
},

‎internal/cli/alert_event.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,10 @@ func newAlertEventListCmd() *cobra.Command {
104104
if len(proj) < len(result.Items) {
105105
effectiveLimit = len(proj)
106106
}
107-
return ctx.PrintList(proj, nil, len(proj), page, effectiveLimit, int(result.Total))
107+
return ctx.PrintCappedList(proj, nil, len(proj), page, effectiveLimit, int(result.Total), result.HasNextPage)
108108
}
109109

110-
return ctx.PrintList(result.Items, cols, len(result.Items), page, limit, int(result.Total))
110+
return ctx.PrintCappedList(result.Items, cols, len(result.Items), page, limit, int(result.Total), result.HasNextPage)
111111
})
112112
},
113113
}

‎internal/cli/command.go‎

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cli
33
import (
44
"fmt"
55
"io"
6+
"strconv"
67

78
"github.com/flashcatcloud/go-flashduty"
89
"github.com/spf13/cobra"
@@ -98,12 +99,24 @@ type listPage struct {
9899
Limit int // effective page size: requested, else the schema default; 0 when unknown
99100
Total int // total matching rows; negative when the API returned none
100101
More moreState // has_next_page / next cursor, when the API returned one
102+
// TotalCapped marks an endpoint that stops counting at serverCountCap; its
103+
// callers must also set More, the only "rows remain" signal past the cap.
104+
TotalCapped bool
101105
}
102106

103107
// totalKnown reports whether Total is a usable server total. A total below the
104108
// shown count is an absent field, not a real total.
105109
func (p listPage) totalKnown() bool { return p.Total >= 0 && p.Total >= p.Count }
106110

111+
// serverCountCap is where the incident, alert and alert-event list endpoints
112+
// stop counting: past it they report this value as the total while rows keep
113+
// coming (the alert and alert-event API reference says "capped at 1000").
114+
const serverCountCap = 1000
115+
116+
// totalIsFloor reports whether Total is a capped count, which says only that at
117+
// least that many rows match.
118+
func (p listPage) totalIsFloor() bool { return p.TotalCapped && p.Total >= serverCountCap }
119+
107120
// offset returns how many rows precede this page, when that is knowable: always
108121
// for the first page, and for later pages only with a known page size and base.
109122
func (p listPage) offset() (int, bool) {
@@ -143,6 +156,8 @@ func results(n int) string {
143156
// footer and the structured-mode stderr note ("" when no warning applies):
144157
//
145158
// a. total known: "showing N of T", plus a next-page hint when rows remain;
159+
// a capped total reads "at least T", and has_next_page alone decides
160+
// whether rows remain;
146161
// b. else has_next_page/cursor known: true warns, false is complete;
147162
// c. else a full page (N == limit) cannot tell: warn that more may exist;
148163
// d. else only the plain count.
@@ -179,13 +194,18 @@ func (p listPage) statement(cmd *cobra.Command) (footer, note string) {
179194
}
180195
switch {
181196
case p.totalKnown():
182-
footer = fmt.Sprintf("Showing %s%s.", results(p.Count), paren(fmt.Sprintf("total %d", p.Total)))
183-
if off, ok := p.offset(); ok && off+p.Count < p.Total {
184-
body := fmt.Sprintf("showing %d of %d total results%s", p.Count, p.Total, paren(""))
185-
if hint != "" {
186-
body += ": " + hint
187-
}
188-
note = "note: " + body
197+
total := strconv.Itoa(p.Total)
198+
if p.totalIsFloor() {
199+
total = "at least " + total
200+
}
201+
footer = fmt.Sprintf("Showing %s%s.", results(p.Count), paren("total "+total))
202+
off, offKnown := p.offset()
203+
remain := offKnown && off+p.Count < p.Total
204+
if p.totalIsFloor() {
205+
remain = p.More == moreYes
206+
}
207+
if remain {
208+
_, note = warn(fmt.Sprintf("%d of %s total results%s", p.Count, total, paren("")), "")
189209
}
190210
return footer, note
191211
case p.More == moreYes:
@@ -257,6 +277,14 @@ func (ctx *RunContext) PrintList(items any, cols []output.Column, count, page, l
257277
return ctx.PrintPage(items, cols, listPage{Count: count, Page: page, Base: pageOne, Limit: limit, Total: total})
258278
}
259279

280+
// PrintCappedList is PrintList for an endpoint whose total stops at
281+
// serverCountCap; hasNext is the response's has_next_page.
282+
func (ctx *RunContext) PrintCappedList(items any, cols []output.Column, count, page, limit, total int, hasNext bool) error {
283+
return ctx.PrintPage(items, cols, listPage{
284+
Count: count, Page: page, Base: pageOne, Limit: limit, Total: total, More: moreOf(hasNext), TotalCapped: true,
285+
})
286+
}
287+
260288
// WriteResult prints a success message as plain text or JSON.
261289
func (ctx *RunContext) WriteResult(message string) {
262290
writeResult(ctx.Writer, message)

‎internal/cli/incident.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,10 @@ func newIncidentListCmd() *cobra.Command {
173173
if len(proj) < len(result.Items) {
174174
effectiveLimit = len(proj)
175175
}
176-
return ctx.PrintList(proj, nil, len(proj), page, effectiveLimit, int(result.Total))
176+
return ctx.PrintCappedList(proj, nil, len(proj), page, effectiveLimit, int(result.Total), result.HasNextPage)
177177
}
178178

179-
return ctx.PrintList(result.Items, incidentColumns(), len(result.Items), page, limit, int(result.Total))
179+
return ctx.PrintCappedList(result.Items, incidentColumns(), len(result.Items), page, limit, int(result.Total), result.HasNextPage)
180180
})
181181
},
182182
}

‎internal/cli/list_completeness_test.go‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,45 @@ func TestListPageStatement(t *testing.T) {
9999
wantFooter: "Showing 5 results (page 1, total 45).",
100100
wantNote: "note: showing 5 of 45 total results (page 1): raise --limit or use --page 2",
101101
},
102+
{
103+
name: "a: capped total on page 1 is a floor",
104+
cmd: pagedCmd(),
105+
pg: listPage{Count: 100, Page: 1, Base: pageOne, Limit: 100, Total: 1000, More: moreYes, TotalCapped: true},
106+
wantFooter: "Showing 100 results (page 1, total at least 1000).",
107+
wantNote: "note: showing 100 of at least 1000 total results (page 1): raise --limit or use --page 2",
108+
},
109+
{
110+
name: "a: capped total past the cap, has-more true",
111+
cmd: pagedCmd(),
112+
pg: listPage{Count: 39, Page: 11, Base: pageOne, Limit: 100, Total: 1000, More: moreYes, TotalCapped: true},
113+
wantFooter: "Showing 39 results (page 11, total at least 1000).",
114+
wantNote: "note: showing 39 of at least 1000 total results (page 11): raise --limit or use --page 12",
115+
},
116+
{
117+
name: "a: capped total past the cap, has-more false is the end",
118+
cmd: pagedCmd(),
119+
pg: listPage{Count: 100, Page: 11, Base: pageOne, Limit: 100, Total: 1000, More: moreNo, TotalCapped: true},
120+
wantFooter: "Showing 100 results (page 11, total at least 1000).",
121+
},
122+
{
123+
name: "a: capped total, has-more false beats the page offset",
124+
cmd: pagedCmd(),
125+
pg: listPage{Count: 100, Page: 1, Base: pageOne, Limit: 100, Total: 1000, More: moreNo, TotalCapped: true},
126+
wantFooter: "Showing 100 results (page 1, total at least 1000).",
127+
},
128+
{
129+
name: "a: capped endpoint, a total below the cap is exact",
130+
cmd: pagedCmd(),
131+
pg: listPage{Count: 100, Page: 1, Base: pageOne, Limit: 100, Total: 999, More: moreYes, TotalCapped: true},
132+
wantFooter: "Showing 100 results (page 1, total 999).",
133+
wantNote: "note: showing 100 of 999 total results (page 1): raise --limit or use --page 2",
134+
},
135+
{
136+
name: "a: uncapped endpoint, a total of 1000 is exact",
137+
cmd: pagedCmd(),
138+
pg: listPage{Count: 100, Page: 10, Base: pageOne, Limit: 100, Total: 1000},
139+
wantFooter: "Showing 100 results (page 10, total 1000).",
140+
},
102141
{
103142
name: "b: has-more true, singular noun",
104143
cmd: pagedCmd(),

0 commit comments

Comments
 (0)