mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 14:02:43 +08:00
feat(pagination): preserve pagination state on truncation and natural… (#659)
* feat(pagination): preserve pagination state on truncation and natural end * feat(pagination): drop page_token from merged output to reflect aggregate view
This commit is contained in:
@@ -208,7 +208,7 @@ func TestPaginateAll_PageLimitStopsPagination(t *testing.T) {
|
||||
|
||||
ac, errBuf := newTestAPIClient(t, rt)
|
||||
|
||||
_, err := ac.PaginateAll(context.Background(), RawApiRequest{
|
||||
result, err := ac.PaginateAll(context.Background(), RawApiRequest{
|
||||
Method: "GET",
|
||||
URL: "/open-apis/test",
|
||||
As: "bot",
|
||||
@@ -223,6 +223,57 @@ func TestPaginateAll_PageLimitStopsPagination(t *testing.T) {
|
||||
if !strings.Contains(errBuf.String(), "reached page limit (2), stopping. Use --page-all --page-limit 0 to fetch all pages.") {
|
||||
t.Errorf("expected page limit log, got: %s", errBuf.String())
|
||||
}
|
||||
|
||||
// Truncation must surface in the merged output: has_more stays true so
|
||||
// callers can detect loss. page_token is intentionally dropped from the
|
||||
// aggregate view — to fetch more, re-run with a larger --page-limit.
|
||||
resultMap, _ := result.(map[string]interface{})
|
||||
data, _ := resultMap["data"].(map[string]interface{})
|
||||
if hasMore, _ := data["has_more"].(bool); !hasMore {
|
||||
t.Errorf("expected has_more=true when page limit truncates, got false")
|
||||
}
|
||||
if _, exists := data["page_token"]; exists {
|
||||
t.Errorf("expected page_token to be dropped from merged output, got %v", data["page_token"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestPaginateAll_NaturalEndClearsPageToken(t *testing.T) {
|
||||
apiCalls := 0
|
||||
rt := roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||
apiCalls++
|
||||
hasMore := apiCalls < 2
|
||||
body := map[string]interface{}{
|
||||
"code": 0, "msg": "ok",
|
||||
"data": map[string]interface{}{
|
||||
"items": []interface{}{map[string]interface{}{"id": apiCalls}},
|
||||
"has_more": hasMore,
|
||||
},
|
||||
}
|
||||
if hasMore {
|
||||
body["data"].(map[string]interface{})["page_token"] = "next"
|
||||
}
|
||||
return jsonResponse(body), nil
|
||||
})
|
||||
|
||||
ac, _ := newTestAPIClient(t, rt)
|
||||
|
||||
result, err := ac.PaginateAll(context.Background(), RawApiRequest{
|
||||
Method: "GET",
|
||||
URL: "/open-apis/test",
|
||||
As: "bot",
|
||||
}, PaginationOptions{PageLimit: 10, PageDelay: 0})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
resultMap, _ := result.(map[string]interface{})
|
||||
data, _ := resultMap["data"].(map[string]interface{})
|
||||
if hasMore, _ := data["has_more"].(bool); hasMore {
|
||||
t.Errorf("expected has_more=false at natural end, got true")
|
||||
}
|
||||
if _, exists := data["page_token"]; exists {
|
||||
t.Errorf("expected page_token absent at natural end, got %v", data["page_token"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildApiReq_QueryParams(t *testing.T) {
|
||||
|
||||
@@ -71,7 +71,18 @@ func mergePagedResults(w io.Writer, results []interface{}) interface{} {
|
||||
mergedData[k] = v
|
||||
}
|
||||
mergedData[arrayField] = merged
|
||||
mergedData["has_more"] = false
|
||||
|
||||
// Surface the last page's real has_more so callers can detect truncation
|
||||
// when --page-limit stops the loop before the API is exhausted. Page tokens
|
||||
// are intentionally dropped: the merged view is an aggregate, not a resume
|
||||
// cursor — to fetch more, re-run with a larger --page-limit.
|
||||
lastHasMore := false
|
||||
if lastMap, ok := results[len(results)-1].(map[string]interface{}); ok {
|
||||
if lastData, ok := lastMap["data"].(map[string]interface{}); ok {
|
||||
lastHasMore, _ = lastData["has_more"].(bool)
|
||||
}
|
||||
}
|
||||
mergedData["has_more"] = lastHasMore
|
||||
delete(mergedData, "page_token")
|
||||
delete(mergedData, "next_page_token")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user