mirror of
https://github.com/larksuite/cli.git
synced 2026-07-11 03:43:42 +08:00
Merge pull request #1083 from zhengzhijiej-tech/fix/dropdown-get-sheet-id-prefix
fix(sheets): +dropdown-get accepts --sheet-id/--sheet-name + bare --range
This commit is contained in:
@@ -1180,7 +1180,7 @@
|
||||
"kind": "own",
|
||||
"type": "string",
|
||||
"required": "required",
|
||||
"desc": "A1 range, e.g. `Sheet1!A1:F10`"
|
||||
"desc": "A1 range, e.g. `A1:F10` (no sheet prefix — use `--sheet-id` / `--sheet-name` to select the sheet)"
|
||||
},
|
||||
{
|
||||
"name": "include",
|
||||
@@ -1238,12 +1238,26 @@
|
||||
"required": "xor",
|
||||
"desc": "Spreadsheet token (XOR with `--url`)"
|
||||
},
|
||||
{
|
||||
"name": "sheet-id",
|
||||
"kind": "public",
|
||||
"type": "string",
|
||||
"required": "xor",
|
||||
"desc": "Sheet reference_id (XOR with `--sheet-name`)"
|
||||
},
|
||||
{
|
||||
"name": "sheet-name",
|
||||
"kind": "public",
|
||||
"type": "string",
|
||||
"required": "xor",
|
||||
"desc": "Sheet name (XOR with `--sheet-id`)"
|
||||
},
|
||||
{
|
||||
"name": "range",
|
||||
"kind": "own",
|
||||
"type": "string",
|
||||
"required": "required",
|
||||
"desc": "Target range (A1 notation; must include the sheet prefix, e.g. `sheet1!A2:A100`)"
|
||||
"desc": "Target range in A1 notation, e.g. `A2:A100` (no sheet prefix — use `--sheet-id` / `--sheet-name` to select the sheet)"
|
||||
},
|
||||
{
|
||||
"name": "dry-run",
|
||||
@@ -1290,7 +1304,7 @@
|
||||
"kind": "own",
|
||||
"type": "string",
|
||||
"required": "required",
|
||||
"desc": "A1 range, e.g. `Sheet1!A1:F30`"
|
||||
"desc": "A1 range, e.g. `A1:F30` (no sheet prefix — use `--sheet-id` / `--sheet-name` to select the sheet)"
|
||||
},
|
||||
{
|
||||
"name": "value-render-option",
|
||||
@@ -1951,7 +1965,7 @@
|
||||
"kind": "own",
|
||||
"type": "string",
|
||||
"required": "required",
|
||||
"desc": "Top-left A1 anchor (e.g. `Sheet1!A1`); the bottom-right is inferred from CSV row/column counts",
|
||||
"desc": "Top-left A1 anchor (e.g. `A1`, `B5`; no sheet prefix — use `--sheet-id` / `--sheet-name` to select the sheet); must be a single cell, range notation not accepted; the bottom-right is inferred from CSV row/column counts",
|
||||
"default": "A1"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -215,16 +215,16 @@ func stripRowPrefixFromCsvOutput(out interface{}) interface{} {
|
||||
}
|
||||
|
||||
// DropdownGet wraps get_cell_ranges scoped to data_validation: read the
|
||||
// dropdown configuration on a range. The CLI accepts the range in the
|
||||
// sheet-prefixed form (e.g. "sheet1!A2:A100") for convenience; the
|
||||
// prefix is split client-side into sheet_name + bare A1 because the
|
||||
// get_cell_ranges tool wants sheet selector and ranges as separate
|
||||
// fields (ranges with the "sheet!" prefix gets the empty-sheet_id
|
||||
// rejection from the server).
|
||||
// dropdown configuration on a range. Aligned with its sibling +cells-get
|
||||
// — sheet selection is via --sheet-id / --sheet-name (XOR), and --range
|
||||
// is a bare A1 reference. The earlier "must include a sheet prefix"
|
||||
// shape was the odd one out among the get_cell_ranges wrappers and made
|
||||
// callers treat the prefix as either name or id; folding it into the
|
||||
// canonical --sheet-id selector removes that ambiguity.
|
||||
var DropdownGet = common.Shortcut{
|
||||
Service: "sheets",
|
||||
Command: "+dropdown-get",
|
||||
Description: "Read the dropdown / data-validation configuration on a sheet-prefixed range.",
|
||||
Description: "Read the dropdown / data-validation configuration on a range.",
|
||||
Risk: "read",
|
||||
Scopes: []string{"sheets:spreadsheet:read"},
|
||||
AuthTypes: []string{"user", "bot"},
|
||||
@@ -234,24 +234,29 @@ var DropdownGet = common.Shortcut{
|
||||
if _, err := resolveSpreadsheetToken(runtime); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, _, err := resolveSheetSelector(runtime); err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.TrimSpace(runtime.Str("range")) == "" {
|
||||
return common.FlagErrorf("--range is required")
|
||||
}
|
||||
if !strings.Contains(runtime.Str("range"), "!") {
|
||||
return common.FlagErrorf("--range must include a sheet prefix (e.g. sheet1!A2:A100)")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
|
||||
token, _ := resolveSpreadsheetToken(runtime)
|
||||
return invokeToolDryRun(token, ToolKindRead, "get_cell_ranges", dropdownGetInput(runtime, token))
|
||||
sheetID, sheetName, _ := resolveSheetSelector(runtime)
|
||||
return invokeToolDryRun(token, ToolKindRead, "get_cell_ranges", dropdownGetInput(runtime, token, sheetID, sheetName))
|
||||
},
|
||||
Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
|
||||
token, err := resolveSpreadsheetToken(runtime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := callTool(ctx, runtime, token, ToolKindRead, "get_cell_ranges", dropdownGetInput(runtime, token))
|
||||
sheetID, sheetName, err := resolveSheetSelector(runtime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := callTool(ctx, runtime, token, ToolKindRead, "get_cell_ranges", dropdownGetInput(runtime, token, sheetID, sheetName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -260,16 +265,13 @@ var DropdownGet = common.Shortcut{
|
||||
},
|
||||
}
|
||||
|
||||
func dropdownGetInput(runtime *common.RuntimeContext, token string) map[string]interface{} {
|
||||
// Validate already enforced the "Sheet!range" prefix, so the
|
||||
// split error path can't be reached here in practice.
|
||||
sheetName, bareRange, _ := splitSheetPrefixedRange(strings.TrimSpace(runtime.Str("range")))
|
||||
func dropdownGetInput(runtime *common.RuntimeContext, token, sheetID, sheetName string) map[string]interface{} {
|
||||
input := map[string]interface{}{
|
||||
"excel_id": token,
|
||||
"ranges": []string{bareRange},
|
||||
"ranges": []string{strings.TrimSpace(runtime.Str("range"))},
|
||||
"include_styles": false,
|
||||
"value_render_option": "formatted_value",
|
||||
}
|
||||
sheetSelectorForToolInput(input, "", sheetName)
|
||||
sheetSelectorForToolInput(input, sheetID, sheetName)
|
||||
return input
|
||||
}
|
||||
|
||||
@@ -48,14 +48,30 @@ func TestReadDataShortcuts_DryRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "+dropdown-get range with sheet prefix only",
|
||||
// Canonical form: --sheet-id + bare --range. Aligned with
|
||||
// +cells-get / +csv-get; before the e2e BUG-019 fix this
|
||||
// shortcut was the odd one out (range-prefix required).
|
||||
name: "+dropdown-get with --sheet-id",
|
||||
sc: DropdownGet,
|
||||
args: []string{"--url", testURL, "--range", "sheet1!A2:A100"},
|
||||
args: []string{"--url", testURL, "--sheet-id", testSheetID, "--range", "C2:C6"},
|
||||
toolName: "get_cell_ranges",
|
||||
wantInput: map[string]interface{}{
|
||||
"excel_id": testToken,
|
||||
"sheet_name": "sheet1",
|
||||
"ranges": []interface{}{"A2:A100"},
|
||||
"sheet_id": testSheetID,
|
||||
"ranges": []interface{}{"C2:C6"},
|
||||
"include_styles": false,
|
||||
"value_render_option": "formatted_value",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "+dropdown-get with --sheet-name",
|
||||
sc: DropdownGet,
|
||||
args: []string{"--url", testURL, "--sheet-name", "Sheet1", "--range", "C2:C6"},
|
||||
toolName: "get_cell_ranges",
|
||||
wantInput: map[string]interface{}{
|
||||
"excel_id": testToken,
|
||||
"sheet_name": "Sheet1",
|
||||
"ranges": []interface{}{"C2:C6"},
|
||||
"include_styles": false,
|
||||
"value_render_option": "formatted_value",
|
||||
},
|
||||
@@ -72,7 +88,12 @@ func TestReadDataShortcuts_DryRun(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropdownGet_RequiresSheetPrefix(t *testing.T) {
|
||||
// TestDropdownGet_RequiresSheetSelector locks the +cells-get-style
|
||||
// selector contract: at least one of --sheet-id / --sheet-name must be
|
||||
// supplied. Before BUG-019 fix this shortcut required a "Sheet!A1"
|
||||
// prefix inside --range instead; the canonical selector pair is what
|
||||
// every other get_cell_ranges wrapper uses.
|
||||
func TestDropdownGet_RequiresSheetSelector(t *testing.T) {
|
||||
t.Parallel()
|
||||
stdout, stderr, err := runShortcutCapturingErr(t, DropdownGet, []string{
|
||||
"--url", testURL, "--range", "A2:A100", "--dry-run",
|
||||
@@ -80,8 +101,9 @@ func TestDropdownGet_RequiresSheetPrefix(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatalf("expected validation error; stdout=%s stderr=%s", stdout, stderr)
|
||||
}
|
||||
if !strings.Contains(stdout+stderr+err.Error(), "must include a sheet prefix") {
|
||||
t.Errorf("expected sheet-prefix guard; got=%s|%s|%v", stdout, stderr, err)
|
||||
combined := stdout + stderr + err.Error()
|
||||
if !strings.Contains(combined, "sheet-id") && !strings.Contains(combined, "sheet-name") {
|
||||
t.Errorf("expected --sheet-id/--sheet-name guard; got=%s|%s|%v", stdout, stderr, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +118,7 @@ func TestReadData_RequiresRange(t *testing.T) {
|
||||
}{
|
||||
{"+cells-get", CellsGet},
|
||||
{"+csv-get", CsvGet},
|
||||
{"+dropdown-get", DropdownGet},
|
||||
}
|
||||
for _, c := range cases {
|
||||
c := c
|
||||
|
||||
@@ -90,18 +90,18 @@ _公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | A1 范围,如 `Sheet1!A1:F10` |
|
||||
| `--range` | string | required | A1 范围,如 `A1:F10`(不带 sheet 前缀;用 `--sheet-id` / `--sheet-name` 指定 sheet) |
|
||||
| `--include` | string_slice | optional | 要返回的信息类别,逗号分隔多个(可选值:`value` / `formula` / `style` / `comment` / `data_validation`) |
|
||||
| `--max-chars` | int | optional | 防爆,默认 200000(隐藏 flag:不在 `--help` 列出,但可正常传入) |
|
||||
| `--skip-hidden` | bool | optional | 跳过隐藏行列,默认 `false` |
|
||||
|
||||
### `+dropdown-get`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--dry-run`_
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | 目标范围(A1 格式,必须带 sheet 前缀,如 `sheet1!A2:A100`) |
|
||||
| `--range` | string | required | A1 范围,如 `A2:A100`(不带 sheet 前缀;用 `--sheet-id` / `--sheet-name` 指定 sheet) |
|
||||
|
||||
### `+csv-get`
|
||||
|
||||
@@ -109,7 +109,7 @@ _公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | A1 范围,如 `Sheet1!A1:F30` |
|
||||
| `--range` | string | required | A1 范围,如 `A1:F30`(不带 sheet 前缀;用 `--sheet-id` / `--sheet-name` 指定 sheet) |
|
||||
| `--value-render-option` | string | optional | 单元格取值模式(可选值:`formatted_value` / `raw_value` / `formula`)(默认 `formatted_value`) |
|
||||
| `--max-chars` | int | optional | 防爆,默认 200000(隐藏 flag:不在 `--help` 列出,但可正常传入) |
|
||||
| `--include-row-prefix` | bool | optional | 是否在每行前加 `[row=N]` 前缀,默认 `true` |
|
||||
|
||||
@@ -286,7 +286,7 @@ _公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--start-cell` | string | required | 目标区域起点 A1(如 `Sheet1!A1`);终点按 CSV 实际行列数自动推断 |
|
||||
| `--start-cell` | string | required | 目标区域起点 A1(如 `A1`、`B5`,不带 sheet 前缀;用 `--sheet-id` / `--sheet-name` 指定 sheet);必须是单个单元格,不接受范围写法;终点按 CSV 实际行列数自动推断 |
|
||||
| `--csv` | string + File + Stdin(非 JSON 文本) | required | RFC 4180 CSV 文本;只写纯值,不带公式/样式/批注 |
|
||||
| `--allow-overwrite` | bool | optional | 允许覆盖(默认 true);设为 false 时若目标非空报错 |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user