Files
larksuite__cli/shortcuts/common/page_all_flags.go
liangshuo-1 2a1613484a feat: add framework flag aliases and unified IM pagination (#2146)
* feat: add framework flag aliases and unified IM pagination

Introduce declarative exact-name flag aliases at the shortcut framework boundary while keeping semantic compatibility domain-owned. Add a shared, format-aware IM pagination pipeline with consistent flags, metadata, safety bounds, resumable cursors, and request throttling.

* fix: align alias attribution and pagination contracts

* fix: align alias contracts and documentation

* test: remove environment-dependent contact bot e2e

* test: restore contact bot e2e

* docs: reduce IM pagination guidance noise

---------

Co-authored-by: liangshuo-1 <266696938+liangshuo-1@users.noreply.github.com>
2026-08-03 19:20:40 +08:00

108 lines
3.4 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package common
import (
"fmt"
"time"
"github.com/larksuite/cli/errs"
)
const (
PageAllFlagName = "page-all"
pageLimitFlagName = "page-limit"
pageLimitDefault = 10
pageLimitMaximum = 1000
pageDelayFlagName = "page-delay"
pageDelayDefault = 200
pageDelayMaximum = 60_000
)
// PageAllFlags returns the shared pagination control definitions. The caller's
// page token, when present, selects the starting cursor; --page-all controls
// whether pagination continues from that cursor until exhaustion or
// --page-limit. Each call returns a fresh slice so shortcuts cannot mutate each
// other.
func PageAllFlags() []Flag {
return []Flag{
{
Name: PageAllFlagName,
Type: "bool",
Desc: "continue from --page-token (if set) until exhaustion or --page-limit",
},
{
Name: pageLimitFlagName,
Type: "int",
Default: fmt.Sprintf("%d", pageLimitDefault),
Desc: fmt.Sprintf("maximum pages fetched by --page-all (%d-%d)", 1, pageLimitMaximum),
},
{
Name: pageDelayFlagName,
Type: "int",
Default: fmt.Sprintf("%d", pageDelayDefault),
Desc: fmt.Sprintf("delay in milliseconds between pages with --page-all (%d-%d; 0 disables throttling)",
0, pageDelayMaximum),
},
}
}
// ValidatePageAllFlags validates the shared page budget and inter-page delay.
// PaginateInto repeats this check defensively for callers that invoke Execute
// directly in tests.
func ValidatePageAllFlags(runtime *RuntimeContext) error {
_, err := pageAllValues(runtime)
return err
}
type pageAllConfig struct {
enabled bool
maxPages int
delay time.Duration
}
func pageAllValues(runtime *RuntimeContext) (pageAllConfig, error) {
if runtime == nil || runtime.Cmd == nil {
return pageAllConfig{}, errs.NewInternalError(errs.SubtypeUnknown,
"pagination requires a mounted shortcut command")
}
flags := runtime.Cmd.Flags()
if flags.Lookup(PageAllFlagName) == nil || flags.Lookup(pageLimitFlagName) == nil || flags.Lookup(pageDelayFlagName) == nil {
return pageAllConfig{}, errs.NewInternalError(errs.SubtypeUnknown,
"pagination flags are not registered; append common.PageAllFlags() to the shortcut flags")
}
enabled, err := flags.GetBool(PageAllFlagName)
if err != nil {
return pageAllConfig{}, errs.NewInternalError(errs.SubtypeUnknown,
"read pagination flag --%s: %v", PageAllFlagName, err).WithCause(err)
}
limit, err := flags.GetInt(pageLimitFlagName)
if err != nil {
return pageAllConfig{}, errs.NewInternalError(errs.SubtypeUnknown,
"read pagination flag --%s: %v", pageLimitFlagName, err).WithCause(err)
}
if limit < 1 || limit > pageLimitMaximum {
return pageAllConfig{}, errs.NewValidationError(errs.SubtypeInvalidArgument,
"--%s must be an integer between 1 and %d", pageLimitFlagName, pageLimitMaximum).
WithParam("--" + pageLimitFlagName)
}
delayMillis, err := flags.GetInt(pageDelayFlagName)
if err != nil {
return pageAllConfig{}, errs.NewInternalError(errs.SubtypeUnknown,
"read pagination flag --%s: %v", pageDelayFlagName, err).WithCause(err)
}
if delayMillis < 0 || delayMillis > pageDelayMaximum {
return pageAllConfig{}, errs.NewValidationError(errs.SubtypeInvalidArgument,
"--%s must be an integer between 0 and %d", pageDelayFlagName, pageDelayMaximum).
WithParam("--" + pageDelayFlagName)
}
return pageAllConfig{
enabled: enabled,
maxPages: limit,
delay: time.Duration(delayMillis) * time.Millisecond,
}, nil
}