mirror of
https://github.com/larksuite/cli.git
synced 2026-08-03 08:32:46 +08:00
refactor(client): PaginateToOutput takes an options struct
PaginateToOutput had 11 positional parameters (writers, callbacks, pagination knobs), which read poorly at the call sites and invited positional-argument mistakes. Introduce PaginateOutputOptions and reduce the signature to (ctx, opts). The two production call sites (cmd/api, cmd/service) now pass named fields; the pagination tests use a small adapter so each case stays one statement. No behavior change.
This commit is contained in:
@@ -280,8 +280,18 @@ func apiRun(opts *APIOptions) error {
|
||||
out := f.IOStreams.Out
|
||||
|
||||
if opts.PageAll {
|
||||
return client.PaginateToOutput(opts.Ctx, ac, request, format, opts.JqExpr, out, f.IOStreams.ErrOut, opts.Cmd.CommandPath(),
|
||||
client.PaginationOptions{PageLimit: opts.PageLimit, PageDelay: opts.PageDelay}, ac.CheckResponse, errs.MarkRaw)
|
||||
return client.PaginateToOutput(opts.Ctx, client.PaginateOutputOptions{
|
||||
Client: ac,
|
||||
Request: request,
|
||||
Format: format,
|
||||
JqExpr: opts.JqExpr,
|
||||
Out: out,
|
||||
ErrOut: f.IOStreams.ErrOut,
|
||||
CommandPath: opts.Cmd.CommandPath(),
|
||||
Pagination: client.PaginationOptions{PageLimit: opts.PageLimit, PageDelay: opts.PageDelay},
|
||||
CheckErr: ac.CheckResponse,
|
||||
MarkErr: errs.MarkRaw,
|
||||
})
|
||||
}
|
||||
|
||||
resp, err := ac.DoAPI(opts.Ctx, request)
|
||||
|
||||
@@ -66,6 +66,23 @@ func apiPaginateRequest() client.RawApiRequest {
|
||||
}
|
||||
}
|
||||
|
||||
// apiPaginate adapts the positional test calls to PaginateToOutput's options
|
||||
// struct so each test case stays a single readable statement.
|
||||
func apiPaginate(ctx context.Context, ac *client.APIClient, request client.RawApiRequest, format output.Format, jqExpr string, out, errOut io.Writer, commandPath string, pag client.PaginationOptions, checkErr func(interface{}, core.Identity) error, markErr func(error) error) error {
|
||||
return client.PaginateToOutput(ctx, client.PaginateOutputOptions{
|
||||
Client: ac,
|
||||
Request: request,
|
||||
Format: format,
|
||||
JqExpr: jqExpr,
|
||||
Out: out,
|
||||
ErrOut: errOut,
|
||||
CommandPath: commandPath,
|
||||
Pagination: pag,
|
||||
CheckErr: checkErr,
|
||||
MarkErr: markErr,
|
||||
})
|
||||
}
|
||||
|
||||
func assertAPIPaginateJSONBytes(t *testing.T, got []byte, want interface{}) {
|
||||
t.Helper()
|
||||
wantBytes, err := json.MarshalIndent(want, "", " ")
|
||||
@@ -108,7 +125,7 @@ func TestAPIPaginate_DefaultAggregatesAllPages(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
err := client.PaginateToOutput(context.Background(), ac, apiPaginateRequest(),
|
||||
err := apiPaginate(context.Background(), ac, apiPaginateRequest(),
|
||||
output.FormatJSON, "", out, errOut, "lark-cli api GET", client.PaginationOptions{
|
||||
PageLimit: 10,
|
||||
PageDelay: -1,
|
||||
@@ -191,7 +208,7 @@ func TestAPIPaginate_StreamingFormatsEmitExactMultiPageBytes(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
err := client.PaginateToOutput(context.Background(), ac, apiPaginateRequest(),
|
||||
err := apiPaginate(context.Background(), ac, apiPaginateRequest(),
|
||||
tt.format, "", out, errOut, "lark-cli api GET", client.PaginationOptions{
|
||||
PageLimit: 10,
|
||||
PageDelay: -1,
|
||||
@@ -237,7 +254,7 @@ func TestAPIPaginate_StreamingWriteFailureStopsFurtherPages(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
err := client.PaginateToOutput(context.Background(), ac, apiPaginateRequest(),
|
||||
err := apiPaginate(context.Background(), ac, apiPaginateRequest(),
|
||||
output.FormatNDJSON, "", out, errOut, "lark-cli api GET",
|
||||
client.PaginationOptions{PageLimit: 10, PageDelay: -1}, ac.CheckResponse, errs.MarkRaw)
|
||||
|
||||
@@ -270,7 +287,7 @@ func TestAPIPaginate_StreamingFormatHonorsNDJSONWithoutList(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
err := client.PaginateToOutput(context.Background(), ac, apiPaginateRequest(),
|
||||
err := apiPaginate(context.Background(), ac, apiPaginateRequest(),
|
||||
output.FormatNDJSON, "", out, errOut, "lark-cli api GET", client.PaginationOptions{PageDelay: -1}, ac.CheckResponse, errs.MarkRaw)
|
||||
|
||||
if err != nil {
|
||||
@@ -308,7 +325,7 @@ func TestAPIPaginate_BusinessErrorsWriteRawAndAreMarkedRaw(t *testing.T) {
|
||||
Body: businessResponse,
|
||||
})
|
||||
|
||||
err := client.PaginateToOutput(context.Background(), ac, apiPaginateRequest(),
|
||||
err := apiPaginate(context.Background(), ac, apiPaginateRequest(),
|
||||
tt.format, tt.jqExpr, out, errOut, "lark-cli api GET", client.PaginationOptions{PageDelay: -1}, ac.CheckResponse, errs.MarkRaw)
|
||||
|
||||
if err == nil {
|
||||
@@ -343,7 +360,7 @@ func TestAPIPaginate_TransportErrorsAreMarkedRaw(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ac, out, errOut, _ := newAPIPaginateTestHarness(t)
|
||||
|
||||
err := client.PaginateToOutput(context.Background(), ac, apiPaginateRequest(),
|
||||
err := apiPaginate(context.Background(), ac, apiPaginateRequest(),
|
||||
tt.format, tt.jqExpr, out, errOut, "lark-cli api GET", client.PaginationOptions{PageDelay: -1}, ac.CheckResponse, errs.MarkRaw)
|
||||
|
||||
if err == nil {
|
||||
@@ -373,7 +390,7 @@ func TestAPIPaginate_StreamBusinessErrorIsMarkedRaw(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
err := client.PaginateToOutput(context.Background(), ac, apiPaginateRequest(),
|
||||
err := apiPaginate(context.Background(), ac, apiPaginateRequest(),
|
||||
output.FormatNDJSON, "", out, errOut, "lark-cli api GET", client.PaginationOptions{PageDelay: -1}, ac.CheckResponse, errs.MarkRaw)
|
||||
|
||||
if err == nil {
|
||||
|
||||
@@ -444,8 +444,18 @@ func serviceMethodRun(opts *ServiceMethodOptions) error {
|
||||
checkErr := ac.CheckResponse
|
||||
|
||||
if opts.PageAll {
|
||||
return client.PaginateToOutput(opts.Ctx, ac, request, format, opts.JqExpr, out, f.IOStreams.ErrOut, opts.Cmd.CommandPath(),
|
||||
client.PaginationOptions{PageLimit: opts.PageLimit, PageDelay: opts.PageDelay}, checkErr, nil)
|
||||
return client.PaginateToOutput(opts.Ctx, client.PaginateOutputOptions{
|
||||
Client: ac,
|
||||
Request: request,
|
||||
Format: format,
|
||||
JqExpr: opts.JqExpr,
|
||||
Out: out,
|
||||
ErrOut: f.IOStreams.ErrOut,
|
||||
CommandPath: opts.Cmd.CommandPath(),
|
||||
Pagination: client.PaginationOptions{PageLimit: opts.PageLimit, PageDelay: opts.PageDelay},
|
||||
CheckErr: checkErr,
|
||||
MarkErr: nil,
|
||||
})
|
||||
}
|
||||
|
||||
resp, err := ac.DoAPI(opts.Ctx, request)
|
||||
|
||||
@@ -66,6 +66,23 @@ func servicePaginateRequest() client.RawApiRequest {
|
||||
}
|
||||
}
|
||||
|
||||
// servicePaginate adapts the positional test calls to PaginateToOutput's options
|
||||
// struct so each test case stays a single readable statement.
|
||||
func servicePaginate(ctx context.Context, ac *client.APIClient, request client.RawApiRequest, format output.Format, jqExpr string, out, errOut io.Writer, commandPath string, pag client.PaginationOptions, checkErr func(interface{}, core.Identity) error, markErr func(error) error) error {
|
||||
return client.PaginateToOutput(ctx, client.PaginateOutputOptions{
|
||||
Client: ac,
|
||||
Request: request,
|
||||
Format: format,
|
||||
JqExpr: jqExpr,
|
||||
Out: out,
|
||||
ErrOut: errOut,
|
||||
CommandPath: commandPath,
|
||||
Pagination: pag,
|
||||
CheckErr: checkErr,
|
||||
MarkErr: markErr,
|
||||
})
|
||||
}
|
||||
|
||||
func assertServicePaginateJSONBytes(t *testing.T, got []byte, want interface{}) {
|
||||
t.Helper()
|
||||
wantBytes, err := json.MarshalIndent(want, "", " ")
|
||||
@@ -108,7 +125,7 @@ func TestServicePaginate_DefaultAggregatesAllPages(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
err := client.PaginateToOutput(context.Background(), ac, servicePaginateRequest(),
|
||||
err := servicePaginate(context.Background(), ac, servicePaginateRequest(),
|
||||
output.FormatJSON, "", out, errOut, "lark-cli test items list", client.PaginationOptions{
|
||||
PageLimit: 10,
|
||||
PageDelay: -1,
|
||||
@@ -191,7 +208,7 @@ func TestServicePaginate_StreamingFormatsEmitExactMultiPageBytes(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
err := client.PaginateToOutput(context.Background(), ac, servicePaginateRequest(),
|
||||
err := servicePaginate(context.Background(), ac, servicePaginateRequest(),
|
||||
tt.format, "", out, errOut, "lark-cli test items list", client.PaginationOptions{
|
||||
PageLimit: 10,
|
||||
PageDelay: -1,
|
||||
@@ -237,7 +254,7 @@ func TestServicePaginate_StreamingWriteFailureStopsFurtherPages(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
err := client.PaginateToOutput(context.Background(), ac, servicePaginateRequest(),
|
||||
err := servicePaginate(context.Background(), ac, servicePaginateRequest(),
|
||||
output.FormatNDJSON, "", out, errOut, "lark-cli test items list",
|
||||
client.PaginationOptions{PageLimit: 10, PageDelay: -1}, ac.CheckResponse, nil)
|
||||
|
||||
@@ -270,7 +287,7 @@ func TestServicePaginate_StreamingFormatHonorsNDJSONWithoutList(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
err := client.PaginateToOutput(context.Background(), ac, servicePaginateRequest(),
|
||||
err := servicePaginate(context.Background(), ac, servicePaginateRequest(),
|
||||
output.FormatNDJSON, "", out, errOut, "lark-cli test items get",
|
||||
client.PaginationOptions{PageDelay: -1}, ac.CheckResponse, nil)
|
||||
|
||||
@@ -309,7 +326,7 @@ func TestServicePaginate_BusinessErrorsWriteRawAndRemainUnmarked(t *testing.T) {
|
||||
Body: businessResponse,
|
||||
})
|
||||
|
||||
err := client.PaginateToOutput(context.Background(), ac, servicePaginateRequest(),
|
||||
err := servicePaginate(context.Background(), ac, servicePaginateRequest(),
|
||||
tt.format, tt.jqExpr, out, errOut, "lark-cli test items list",
|
||||
client.PaginationOptions{PageDelay: -1}, ac.CheckResponse, nil)
|
||||
|
||||
@@ -345,7 +362,7 @@ func TestServicePaginate_TransportErrorsRemainUnmarked(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ac, out, errOut, _ := newServicePaginateTestHarness(t)
|
||||
|
||||
err := client.PaginateToOutput(context.Background(), ac, servicePaginateRequest(),
|
||||
err := servicePaginate(context.Background(), ac, servicePaginateRequest(),
|
||||
tt.format, tt.jqExpr, out, errOut, "lark-cli test items list",
|
||||
client.PaginationOptions{PageDelay: -1}, ac.CheckResponse, nil)
|
||||
|
||||
@@ -376,7 +393,7 @@ func TestServicePaginate_StreamBusinessErrorRemainsUnmarked(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
err := client.PaginateToOutput(context.Background(), ac, servicePaginateRequest(),
|
||||
err := servicePaginate(context.Background(), ac, servicePaginateRequest(),
|
||||
output.FormatNDJSON, "", out, errOut, "lark-cli test items list",
|
||||
client.PaginationOptions{PageDelay: -1}, ac.CheckResponse, nil)
|
||||
|
||||
|
||||
@@ -714,19 +714,13 @@ func TestCallAPI_ParseJSONFailureWrapsAsAPI(t *testing.T) {
|
||||
|
||||
func TestPaginateToOutputRejectsUnsupportedInternalFormat(t *testing.T) {
|
||||
for _, format := range []output.Format{output.FormatPretty, output.Format(99)} {
|
||||
err := PaginateToOutput(
|
||||
context.Background(),
|
||||
nil,
|
||||
RawApiRequest{},
|
||||
format,
|
||||
"",
|
||||
io.Discard,
|
||||
io.Discard,
|
||||
"lark-cli fixture",
|
||||
PaginationOptions{},
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
err := PaginateToOutput(context.Background(), PaginateOutputOptions{
|
||||
Request: RawApiRequest{},
|
||||
Format: format,
|
||||
Out: io.Discard,
|
||||
ErrOut: io.Discard,
|
||||
CommandPath: "lark-cli fixture",
|
||||
})
|
||||
var internalErr *errs.InternalError
|
||||
if !errors.As(err, &internalErr) {
|
||||
t.Fatalf("format %q error = %T, want *errs.InternalError", format, err)
|
||||
|
||||
@@ -12,8 +12,34 @@ import (
|
||||
"github.com/larksuite/cli/internal/output"
|
||||
)
|
||||
|
||||
// PaginateOutputOptions bundles the inputs for PaginateToOutput. Grouping the
|
||||
// writers, callbacks, and pagination knobs into one struct keeps the call sites
|
||||
// readable and avoids positional-argument mistakes across the many parameters.
|
||||
type PaginateOutputOptions struct {
|
||||
Client *APIClient
|
||||
Request RawApiRequest
|
||||
Format output.Format
|
||||
JqExpr string
|
||||
Out io.Writer
|
||||
ErrOut io.Writer
|
||||
CommandPath string
|
||||
Pagination PaginationOptions
|
||||
CheckErr func(interface{}, core.Identity) error
|
||||
MarkErr func(error) error
|
||||
}
|
||||
|
||||
// PaginateToOutput fetches all requested pages and emits them in the selected format.
|
||||
func PaginateToOutput(ctx context.Context, ac *APIClient, request RawApiRequest, format output.Format, jqExpr string, out, errOut io.Writer, commandPath string, pagOpts PaginationOptions, checkErr func(interface{}, core.Identity) error, markErr func(error) error) error {
|
||||
func PaginateToOutput(ctx context.Context, opts PaginateOutputOptions) error {
|
||||
ac := opts.Client
|
||||
request := opts.Request
|
||||
format := opts.Format
|
||||
jqExpr := opts.JqExpr
|
||||
out := opts.Out
|
||||
errOut := opts.ErrOut
|
||||
commandPath := opts.CommandPath
|
||||
pagOpts := opts.Pagination
|
||||
checkErr := opts.CheckErr
|
||||
markErr := opts.MarkErr
|
||||
if !format.Valid() || format == output.FormatPretty {
|
||||
return errs.NewInternalError(errs.SubtypeUnknown,
|
||||
"internal: unsupported pagination output format %q", format)
|
||||
|
||||
Reference in New Issue
Block a user