Skip to content

Commit 5c7100e

Browse files
fix(sheets): migrate +table-put to typed error contract
The merge from main brought in #1449 (retire legacy error envelopes), which removed output.ExitError / output.ErrDetail and forbids constructing them. Port tablePutPartial off the legacy envelope: - no sheets written -> typed errs.APIError (plain failure) - some sheets written -> ok:false result via runtime.OutPartialFailure carrying written_sheets, returning the partial-failure exit signal Also fix two drifts the same merge introduced: - regenerate flag_defs_gen.go to match the committed flag-defs.json - update the --max-chars flag test to assert visible (no longer hidden)
1 parent 3ef3a9d commit 5c7100e

4 files changed

Lines changed: 40 additions & 43 deletions

File tree

shortcuts/sheets/flag_defs_gen.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

shortcuts/sheets/flag_defs_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ func TestFlagsFor_MapsAllFields(t *testing.T) {
6565
if url == nil || url.Required {
6666
t.Errorf("+sheet-create --url should not be cobra-required: %+v", url)
6767
}
68-
// hidden + int default
68+
// visible + int default
6969
cap := byName("+cells-get", "max-chars")
70-
if cap == nil || !cap.Hidden || cap.Default != "500000" {
70+
if cap == nil || cap.Hidden || cap.Default != "500000" {
7171
t.Errorf("+cells-get --max-chars not mapped: %+v", cap)
7272
}
7373
// input sources

shortcuts/sheets/lark_sheet_table_io.go

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"time"
1414

1515
"github.com/larksuite/cli/errs"
16-
"github.com/larksuite/cli/internal/output"
1716
"github.com/larksuite/cli/internal/util"
1817
"github.com/larksuite/cli/shortcuts/common"
1918
)
@@ -818,7 +817,7 @@ func renameSheet(ctx context.Context, runtime *common.RuntimeContext, token, she
818817
func tablePutWrite(ctx context.Context, runtime *common.RuntimeContext, token string, payload *tablePayload, styles *workbookCreateSheetStyles) error {
819818
written, err := writeTypedSheets(ctx, runtime, token, payload, "", styles)
820819
if err != nil {
821-
return tablePutPartial(token, nil, written, err.Error())
820+
return tablePutPartial(runtime, token, nil, written, err.Error())
822821
}
823822
runtime.Out(map[string]interface{}{
824823
"spreadsheet_token": token,
@@ -1005,39 +1004,28 @@ func listSheetIDsByName(ctx context.Context, runtime *common.RuntimeContext, tok
10051004
return byName, dimsByName, nil
10061005
}
10071006

1008-
// tablePutPartial builds a structured error for a multi-sheet write that failed
1009-
// partway. When some sheets already landed it's a partial_success (their
1010-
// summaries are surfaced so callers can retry the rest or delete the workbook);
1011-
// when nothing landed — the first or only sheet failed — it's a plain failure,
1012-
// so we don't misleadingly claim "some sheets were written".
1013-
func tablePutPartial(token string, spreadsheet interface{}, written []interface{}, reason string) error {
1014-
detail := map[string]interface{}{
1007+
// tablePutPartial reports a multi-sheet write that failed partway. When some
1008+
// sheets already landed it is a partial_success: their summaries are the primary
1009+
// machine-readable output, so we emit an ok:false result envelope on stdout (via
1010+
// OutPartialFailure) carrying written_sheets, and return the partial-failure exit
1011+
// signal — callers can retry the rest or delete the workbook. When nothing landed
1012+
// — the first or only sheet failed — it is a plain failure, so we return a typed
1013+
// errs.APIError rather than misleadingly claiming "some sheets were written".
1014+
func tablePutPartial(runtime *common.RuntimeContext, token string, spreadsheet interface{}, written []interface{}, reason string) error {
1015+
if len(written) == 0 {
1016+
return errs.NewAPIError(errs.SubtypeServerError, "table-put failed on %s: %s", token, reason).
1017+
WithHint("no sheets were written; fix the cause and retry")
1018+
}
1019+
data := map[string]interface{}{
10151020
"spreadsheet_token": token,
10161021
"written_sheets": written,
1022+
"reason": reason,
1023+
"hint": "some sheets were written; inspect written_sheets, then retry the remaining sheets or delete the spreadsheet",
10171024
}
10181025
if spreadsheet != nil {
1019-
detail["spreadsheet"] = spreadsheet
1020-
}
1021-
if len(written) == 0 {
1022-
return &output.ExitError{
1023-
Code: output.ExitAPI,
1024-
Detail: &output.ErrDetail{
1025-
Type: "api_error",
1026-
Message: fmt.Sprintf("table-put failed on %s: %s", token, reason),
1027-
Hint: "no sheets were written; fix the cause and retry",
1028-
Detail: detail,
1029-
},
1030-
}
1031-
}
1032-
return &output.ExitError{
1033-
Code: output.ExitAPI,
1034-
Detail: &output.ErrDetail{
1035-
Type: "partial_success",
1036-
Message: fmt.Sprintf("table-put partially applied to %s: %s", token, reason),
1037-
Hint: "some sheets were written; inspect written_sheets, then retry the remaining sheets or delete the spreadsheet",
1038-
Detail: detail,
1039-
},
1026+
data["spreadsheet"] = spreadsheet
10401027
}
1028+
return runtime.OutPartialFailure(data, nil)
10411029
}
10421030

10431031
// ─── dry-run ──────────────────────────────────────────────────────────

shortcuts/sheets/lark_sheet_table_io_test.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ package sheets
55

66
import (
77
"encoding/json"
8+
"errors"
89
"fmt"
910
"strings"
1011
"testing"
1112

1213
"github.com/larksuite/cli/internal/httpmock"
14+
"github.com/larksuite/cli/internal/output"
1315
)
1416

1517
// ─── pure helpers: date serial, typed cell mapping ────────────────────
@@ -654,15 +656,22 @@ func TestTablePut_ExecutePartialFailure(t *testing.T) {
654656
`{"sheets":[{"name":"汇总","columns":["a"],"data":[["x"]]},{"name":"明细","columns":["a"],"data":[["y"]]}]}`},
655657
structure, writeOK, writeErr)
656658
if err == nil {
657-
t.Fatalf("expected partial-success error; got nil. out=%s", out)
658-
}
659-
if !strings.Contains(err.Error(), "partially applied") && !strings.Contains(out, "partially applied") {
660-
t.Errorf("expected partial_success (not total failure); got err=%v out=%s", err, out)
661-
}
662-
// The failing sheet is named in the message; the written one lives in the
663-
// structured written_sheets detail.
664-
if !strings.Contains(err.Error(), "明细") {
665-
t.Errorf("partial_success should name the failed sheet 明细; got err=%v", err)
659+
t.Fatalf("expected partial-failure exit signal; got nil. out=%s", out)
660+
}
661+
// Per the typed-error contract, a partial success is a result, not an error
662+
// envelope: the ok:false result (with written_sheets and the failing sheet's
663+
// name) lands on stdout, and err is the bare partial-failure exit signal.
664+
var pfErr *output.PartialFailureError
665+
if !errors.As(err, &pfErr) {
666+
t.Errorf("expected *output.PartialFailureError exit signal; got %T %v", err, err)
667+
}
668+
if !strings.Contains(out, "written_sheets") {
669+
t.Errorf("expected written_sheets in the partial-success result; got out=%s", out)
670+
}
671+
// The failing sheet is named in the reason; the written one lives in
672+
// written_sheets — both on stdout.
673+
if !strings.Contains(out, "明细") {
674+
t.Errorf("partial_success should name the failed sheet 明细; got out=%s", out)
666675
}
667676
}
668677

0 commit comments

Comments
 (0)