Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var SheetAddDimension = common.Shortcut{ Service: "sheets", Command: "+add-dimension", Description: "Add rows or columns at the end of a sheet", Risk: "write", Scopes: []string{"sheets:spreadsheet:write_only", "sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "sheet-id", Desc: "worksheet ID", Required: true}, {Name: "dimension", Desc: "ROWS or COLUMNS", Required: true, Enum: []string{"ROWS", "COLUMNS"}}, {Name: "length", Type: "int", Desc: "number of rows/columns to add (1-5000)", Required: true}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } length := runtime.Int("length") if length < 1 || length > 5000 { return common.FlagErrorf("--length must be between 1 and 5000, got %d", length) } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } return common.NewDryRunAPI(). POST("/open-apis/sheets/v2/spreadsheets/:token/dimension_range"). Body(map[string]interface{}{ "dimension": map[string]interface{}{ "sheetId": runtime.Str("sheet-id"), "majorDimension": runtime.Str("dimension"), "length": runtime.Int("length"), }, }). Set("token", token) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } data, err := runtime.CallAPI("POST", fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/dimension_range", validate.EncodePathSegment(token)), nil, map[string]interface{}{ "dimension": map[string]interface{}{ "sheetId": runtime.Str("sheet-id"), "majorDimension": runtime.Str("dimension"), "length": runtime.Int("length"), }, }, ) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var SheetAppend = common.Shortcut{ Service: "sheets", Command: "+append", Description: "Append rows to a spreadsheet", Risk: "write", Scopes: []string{"sheets:spreadsheet:write_only", "sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "range", Desc: "append range (<sheetId>!A1:D10, A1:D10 with --sheet-id, or a single cell like C2)"}, {Name: "sheet-id", Desc: "sheet ID"}, {Name: "values", Desc: "2D array JSON", Required: true}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } var values interface{} if err := json.Unmarshal([]byte(runtime.Str("values")), &values); err != nil { return common.FlagErrorf("--values invalid JSON, must be a 2D array") } if err := validateSheetRangeInput(runtime.Str("sheet-id"), runtime.Str("range")); err != nil { return err } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } appendRange := runtime.Str("range") if appendRange == "" && runtime.Str("sheet-id") != "" { appendRange = runtime.Str("sheet-id") } var values interface{} json.Unmarshal([]byte(runtime.Str("values")), &values) appendRange = normalizePointRange(runtime.Str("sheet-id"), appendRange) return common.NewDryRunAPI(). POST("/open-apis/sheets/v2/spreadsheets/:token/values_append"). Body(map[string]interface{}{"valueRange": map[string]interface{}{"range": appendRange, "values": values}}). Set("token", token) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } var values interface{} json.Unmarshal([]byte(runtime.Str("values")), &values) appendRange := runtime.Str("range") if appendRange == "" && runtime.Str("sheet-id") != "" { appendRange = runtime.Str("sheet-id") } if appendRange == "" { var err error appendRange, err = getFirstSheetID(runtime, token) if err != nil { return err } } appendRange = normalizePointRange(runtime.Str("sheet-id"), appendRange) data, err := runtime.CallAPI("POST", fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/values_append", validate.EncodePathSegment(token)), nil, map[string]interface{}{ "valueRange": map[string]interface{}{ "range": appendRange, "values": values, }, }) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var SheetBatchSetStyle = common.Shortcut{ Service: "sheets", Command: "+batch-set-style", Description: "Batch set cell styles for multiple ranges", Risk: "write", Scopes: []string{"sheets:spreadsheet:write_only", "sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "data", Desc: "JSON array of {ranges, style} objects", Required: true}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } var data interface{} if err := json.Unmarshal([]byte(runtime.Str("data")), &data); err != nil { return common.FlagErrorf("--data must be valid JSON: %v", err) } arr, ok := data.([]interface{}) if !ok || len(arr) == 0 { return common.FlagErrorf("--data must be a non-empty JSON array") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } var data interface{} json.Unmarshal([]byte(runtime.Str("data")), &data) return common.NewDryRunAPI(). PUT("/open-apis/sheets/v2/spreadsheets/:token/styles_batch_update"). Body(map[string]interface{}{ "data": data, }). Set("token", token) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } var data interface{} if err := json.Unmarshal([]byte(runtime.Str("data")), &data); err != nil { return common.FlagErrorf("--data must be valid JSON: %v", err) } result, err := runtime.CallAPI("PUT", fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/styles_batch_update", validate.EncodePathSegment(token)), nil, map[string]interface{}{ "data": data, }, ) if err != nil { return err } runtime.Out(result, nil) return nil }, }
View Source
var SheetCreate = common.Shortcut{ Service: "sheets", Command: "+create", Description: "Create a spreadsheet (optional header row and initial data)", Risk: "write", Scopes: []string{"sheets:spreadsheet:create", "sheets:spreadsheet:write_only"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "title", Desc: "spreadsheet title", Required: true}, {Name: "folder-token", Desc: "target folder token"}, {Name: "headers", Desc: "header row JSON array"}, {Name: "data", Desc: "initial data JSON 2D array"}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if headersStr := runtime.Str("headers"); headersStr != "" { var headers []interface{} if err := json.Unmarshal([]byte(headersStr), &headers); err != nil { return common.FlagErrorf("--headers invalid JSON, must be a 1D array") } } if dataStr := runtime.Str("data"); dataStr != "" { var rows [][]interface{} if err := json.Unmarshal([]byte(dataStr), &rows); err != nil { return common.FlagErrorf("--data invalid JSON, must be a 2D array") } } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { d := common.NewDryRunAPI(). POST("/open-apis/sheets/v3/spreadsheets"). Body(map[string]interface{}{"title": runtime.Str("title")}) if runtime.IsBot() { d.Desc("After spreadsheet creation succeeds in bot mode, the CLI will also try to grant the current CLI user full_access (可管理权限) on the new spreadsheet.") } return d }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { title := runtime.Str("title") folderToken := runtime.Str("folder-token") headersStr := runtime.Str("headers") dataStr := runtime.Str("data") var allRows []interface{} if headersStr != "" { var headers []interface{} if err := json.Unmarshal([]byte(headersStr), &headers); err != nil { return common.FlagErrorf("--headers invalid JSON, must be a 1D array") } if len(headers) > 0 { allRows = append(allRows, headers) } } if dataStr != "" { var rows []interface{} if err := json.Unmarshal([]byte(dataStr), &rows); err != nil { return common.FlagErrorf("--data invalid JSON, must be a 2D array") } if len(rows) > 0 { allRows = append(allRows, rows...) } } createData := map[string]interface{}{"title": title} if folderToken != "" { createData["folder_token"] = folderToken } data, err := runtime.CallAPI("POST", "/open-apis/sheets/v3/spreadsheets", nil, createData) if err != nil { return err } spreadsheet, _ := data["spreadsheet"].(map[string]interface{}) token, _ := spreadsheet["spreadsheet_token"].(string) if len(allRows) > 0 && token != "" { appendRange, err := getFirstSheetID(runtime, token) if err != nil { return err } if _, err := runtime.CallAPI("POST", fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/values_append", validate.EncodePathSegment(token)), nil, map[string]interface{}{ "valueRange": map[string]interface{}{ "range": appendRange, "values": allRows, }, }); err != nil { return err } } out := map[string]interface{}{ "spreadsheet_token": token, "title": title, "url": spreadsheet["url"], } if grant := common.AutoGrantCurrentUserDrivePermission(runtime, token, "sheet"); grant != nil { out["permission_grant"] = grant } runtime.Out(out, nil) return nil }, }
View Source
var SheetDeleteDimension = common.Shortcut{ Service: "sheets", Command: "+delete-dimension", Description: "Delete rows or columns", Risk: "write", Scopes: []string{"sheets:spreadsheet:write_only", "sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "sheet-id", Desc: "worksheet ID", Required: true}, {Name: "dimension", Desc: "ROWS or COLUMNS", Required: true, Enum: []string{"ROWS", "COLUMNS"}}, {Name: "start-index", Type: "int", Desc: "start position (1-indexed, inclusive)", Required: true}, {Name: "end-index", Type: "int", Desc: "end position (1-indexed, inclusive)", Required: true}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } if runtime.Int("start-index") < 1 { return common.FlagErrorf("--start-index must be >= 1") } if runtime.Int("end-index") < runtime.Int("start-index") { return common.FlagErrorf("--end-index must be >= --start-index") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } return common.NewDryRunAPI(). DELETE("/open-apis/sheets/v2/spreadsheets/:token/dimension_range"). Body(map[string]interface{}{ "dimension": map[string]interface{}{ "sheetId": runtime.Str("sheet-id"), "majorDimension": runtime.Str("dimension"), "startIndex": runtime.Int("start-index"), "endIndex": runtime.Int("end-index"), }, }). Set("token", token) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } data, err := runtime.CallAPI("DELETE", fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/dimension_range", validate.EncodePathSegment(token)), nil, map[string]interface{}{ "dimension": map[string]interface{}{ "sheetId": runtime.Str("sheet-id"), "majorDimension": runtime.Str("dimension"), "startIndex": runtime.Int("start-index"), "endIndex": runtime.Int("end-index"), }, }, ) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var SheetExport = common.Shortcut{ Service: "sheets", Command: "+export", Description: "Export a spreadsheet (async task polling + optional download)", Risk: "read", Scopes: []string{"docs:document:export", "drive:file:download"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "file-extension", Desc: "export format: xlsx | csv", Required: true}, {Name: "output-path", Desc: "local save path"}, {Name: "sheet-id", Desc: "sheet ID (required for CSV)"}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } return common.NewDryRunAPI(). POST("/open-apis/drive/v1/export_tasks"). Body(map[string]interface{}{"token": token, "type": "sheet", "file_extension": runtime.Str("file-extension")}). Set("token", token).Set("ext", runtime.Str("file-extension")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } fileExt := runtime.Str("file-extension") outputPath := runtime.Str("output-path") sheetIdFlag := runtime.Str("sheet-id") if outputPath != "" { if _, err := runtime.ResolveSavePath(outputPath); err != nil { return output.ErrValidation("unsafe output path: %s", err) } } exportData := map[string]interface{}{ "token": token, "type": "sheet", "file_extension": fileExt, } if sheetIdFlag != "" { exportData["sub_id"] = sheetIdFlag } data, err := runtime.CallAPI("POST", "/open-apis/drive/v1/export_tasks", nil, exportData) if err != nil { return err } ticket, _ := data["ticket"].(string) fmt.Fprintf(runtime.IO().ErrOut, "Waiting for export task to complete...\n") var fileToken string for i := 0; i < 50; i++ { time.Sleep(600 * time.Millisecond) pollResult, err := runtime.RawAPI("GET", "/open-apis/drive/v1/export_tasks/"+ticket, map[string]interface{}{"token": token}, nil) if err != nil { continue } pollMap, _ := pollResult.(map[string]interface{}) pollData, _ := pollMap["data"].(map[string]interface{}) pollResult2, _ := pollData["result"].(map[string]interface{}) if pollResult2 != nil { ft, _ := pollResult2["file_token"].(string) if ft != "" { fileToken = ft break } } } if fileToken == "" { return output.Errorf(output.ExitAPI, "api_error", "export task timed out") } fmt.Fprintf(runtime.IO().ErrOut, "Export complete: file_token=%s\n", fileToken) if outputPath == "" { runtime.Out(map[string]interface{}{ "file_token": fileToken, "ticket": ticket, }, nil) } resp, err := runtime.DoAPIStream(ctx, &larkcore.ApiReq{ HttpMethod: http.MethodGet, ApiPath: fmt.Sprintf("/open-apis/drive/v1/export_tasks/file/%s/download", validate.EncodePathSegment(fileToken)), }) if err != nil { return output.ErrNetwork("download failed: %s", err) } defer resp.Body.Close() result, err := runtime.FileIO().Save(outputPath, fileio.SaveOptions{ ContentType: resp.Header.Get("Content-Type"), ContentLength: resp.ContentLength, }, resp.Body) if err != nil { return common.WrapSaveErrorByCategory(err, "io") } savedPath, _ := runtime.ResolveSavePath(outputPath) if savedPath == "" { savedPath = outputPath } runtime.Out(map[string]interface{}{ "saved_path": savedPath, "size_bytes": result.Size(), }, nil) return nil }, }
View Source
var SheetFind = common.Shortcut{ Service: "sheets", Command: "+find", Description: "Find cells in a spreadsheet", Risk: "read", Scopes: []string{"sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "sheet-id", Desc: "sheet ID", Required: true}, {Name: "find", Desc: "search text", Required: true}, {Name: "range", Desc: "search range (<sheetId>!A1:D10, or A1:D10 / C2 with --sheet-id)"}, {Name: "ignore-case", Type: "bool", Desc: "case-insensitive search"}, {Name: "match-entire-cell", Type: "bool", Desc: "match entire cell"}, {Name: "search-by-regex", Type: "bool", Desc: "regex search"}, {Name: "include-formulas", Type: "bool", Desc: "search formulas"}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } if err := validateSheetRangeInput(runtime.Str("sheet-id"), runtime.Str("range")); err != nil { return err } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } sheetIdFlag := runtime.Str("sheet-id") findCondition := map[string]interface{}{ "range": sheetIdFlag, "match_case": !runtime.Bool("ignore-case"), "match_entire_cell": runtime.Bool("match-entire-cell"), "search_by_regex": runtime.Bool("search-by-regex"), "include_formulas": runtime.Bool("include-formulas"), } if runtime.Str("range") != "" { findCondition["range"] = normalizePointRange(sheetIdFlag, runtime.Str("range")) } return common.NewDryRunAPI(). POST("/open-apis/sheets/v3/spreadsheets/:token/sheets/:sheet_id/find"). Body(map[string]interface{}{ "find": runtime.Str("find"), "find_condition": findCondition, }). Set("token", token).Set("sheet_id", sheetIdFlag).Set("find", runtime.Str("find")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } sheetIdFlag := runtime.Str("sheet-id") findText := runtime.Str("find") findCondition := map[string]interface{}{ "range": sheetIdFlag, "match_case": !runtime.Bool("ignore-case"), "match_entire_cell": runtime.Bool("match-entire-cell"), "search_by_regex": runtime.Bool("search-by-regex"), "include_formulas": runtime.Bool("include-formulas"), } if runtime.Str("range") != "" { findCondition["range"] = normalizePointRange(sheetIdFlag, runtime.Str("range")) } reqData := map[string]interface{}{ "find_condition": findCondition, "find": findText, } data, err := runtime.CallAPI("POST", fmt.Sprintf("/open-apis/sheets/v3/spreadsheets/%s/sheets/%s/find", validate.EncodePathSegment(token), validate.EncodePathSegment(sheetIdFlag)), nil, reqData) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var SheetInfo = common.Shortcut{ Service: "sheets", Command: "+info", Description: "View spreadsheet and sheet information", Risk: "read", Scopes: []string{"sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } return common.NewDryRunAPI(). GET("/open-apis/sheets/v3/spreadsheets/:token"). Set("token", token) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } spreadsheetData, err := runtime.CallAPI("GET", fmt.Sprintf("/open-apis/sheets/v3/spreadsheets/%s", validate.EncodePathSegment(token)), nil, nil) if err != nil { return err } // Get sheets info (best-effort) var sheetsData interface{} sheetsResult, sheetsErr := runtime.RawAPI("GET", fmt.Sprintf("/open-apis/sheets/v3/spreadsheets/%s/sheets/query", validate.EncodePathSegment(token)), nil, nil) if sheetsErr == nil { if sheetsMap, ok := sheetsResult.(map[string]interface{}); ok { if d, ok := sheetsMap["data"].(map[string]interface{}); ok { sheetsData = d } } } runtime.Out(map[string]interface{}{ "spreadsheet": spreadsheetData, "sheets": sheetsData, }, nil) return nil }, }
View Source
var SheetInsertDimension = common.Shortcut{ Service: "sheets", Command: "+insert-dimension", Description: "Insert rows or columns at a specified position", Risk: "write", Scopes: []string{"sheets:spreadsheet:write_only", "sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "sheet-id", Desc: "worksheet ID", Required: true}, {Name: "dimension", Desc: "ROWS or COLUMNS", Required: true, Enum: []string{"ROWS", "COLUMNS"}}, {Name: "start-index", Type: "int", Desc: "start position (0-indexed)", Required: true}, {Name: "end-index", Type: "int", Desc: "end position (0-indexed, exclusive)", Required: true}, {Name: "inherit-style", Desc: "style inheritance: BEFORE or AFTER", Enum: []string{"BEFORE", "AFTER"}}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } if runtime.Int("start-index") < 0 { return common.FlagErrorf("--start-index must be >= 0") } if runtime.Int("end-index") <= runtime.Int("start-index") { return common.FlagErrorf("--end-index must be greater than --start-index") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } body := map[string]interface{}{ "dimension": map[string]interface{}{ "sheetId": runtime.Str("sheet-id"), "majorDimension": runtime.Str("dimension"), "startIndex": runtime.Int("start-index"), "endIndex": runtime.Int("end-index"), }, } if s := runtime.Str("inherit-style"); s != "" { body["inheritStyle"] = s } return common.NewDryRunAPI(). POST("/open-apis/sheets/v2/spreadsheets/:token/insert_dimension_range"). Body(body). Set("token", token) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } body := map[string]interface{}{ "dimension": map[string]interface{}{ "sheetId": runtime.Str("sheet-id"), "majorDimension": runtime.Str("dimension"), "startIndex": runtime.Int("start-index"), "endIndex": runtime.Int("end-index"), }, } if s := runtime.Str("inherit-style"); s != "" { body["inheritStyle"] = s } data, err := runtime.CallAPI("POST", fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/insert_dimension_range", validate.EncodePathSegment(token)), nil, body, ) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var SheetMergeCells = common.Shortcut{ Service: "sheets", Command: "+merge-cells", Description: "Merge cells in a spreadsheet", Risk: "write", Scopes: []string{"sheets:spreadsheet:write_only", "sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "range", Desc: "cell range (<sheetId>!A1:B2, or A1:B2 with --sheet-id)", Required: true}, {Name: "sheet-id", Desc: "sheet ID (for relative range)"}, {Name: "merge-type", Desc: "merge method", Required: true, Enum: []string{"MERGE_ALL", "MERGE_ROWS", "MERGE_COLUMNS"}}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } if err := validateSheetRangeInput(runtime.Str("sheet-id"), runtime.Str("range")); err != nil { return err } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } r := normalizeSheetRange(runtime.Str("sheet-id"), runtime.Str("range")) return common.NewDryRunAPI(). POST("/open-apis/sheets/v2/spreadsheets/:token/merge_cells"). Body(map[string]interface{}{ "range": r, "mergeType": runtime.Str("merge-type"), }). Set("token", token) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } r := normalizeSheetRange(runtime.Str("sheet-id"), runtime.Str("range")) data, err := runtime.CallAPI("POST", fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/merge_cells", validate.EncodePathSegment(token)), nil, map[string]interface{}{ "range": r, "mergeType": runtime.Str("merge-type"), }, ) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var SheetMoveDimension = common.Shortcut{ Service: "sheets", Command: "+move-dimension", Description: "Move rows or columns to a new position", Risk: "write", Scopes: []string{"sheets:spreadsheet:write_only", "sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "sheet-id", Desc: "worksheet ID", Required: true}, {Name: "dimension", Desc: "ROWS or COLUMNS", Required: true, Enum: []string{"ROWS", "COLUMNS"}}, {Name: "start-index", Type: "int", Desc: "source start position (0-indexed)", Required: true}, {Name: "end-index", Type: "int", Desc: "source end position (0-indexed, inclusive)", Required: true}, {Name: "destination-index", Type: "int", Desc: "target position to move to (0-indexed)", Required: true}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } if runtime.Int("start-index") < 0 { return common.FlagErrorf("--start-index must be >= 0") } if runtime.Int("end-index") < runtime.Int("start-index") { return common.FlagErrorf("--end-index must be >= --start-index") } if runtime.Int("destination-index") < 0 { return common.FlagErrorf("--destination-index must be >= 0") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } return common.NewDryRunAPI(). POST("/open-apis/sheets/v3/spreadsheets/:token/sheets/:sheet_id/move_dimension"). Body(map[string]interface{}{ "source": map[string]interface{}{ "major_dimension": runtime.Str("dimension"), "start_index": runtime.Int("start-index"), "end_index": runtime.Int("end-index"), }, "destination_index": runtime.Int("destination-index"), }). Set("token", token). Set("sheet_id", runtime.Str("sheet-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } data, err := runtime.CallAPI("POST", fmt.Sprintf("/open-apis/sheets/v3/spreadsheets/%s/sheets/%s/move_dimension", validate.EncodePathSegment(token), validate.EncodePathSegment(runtime.Str("sheet-id")), ), nil, map[string]interface{}{ "source": map[string]interface{}{ "major_dimension": runtime.Str("dimension"), "start_index": runtime.Int("start-index"), "end_index": runtime.Int("end-index"), }, "destination_index": runtime.Int("destination-index"), }, ) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var SheetRead = common.Shortcut{ Service: "sheets", Command: "+read", Description: "Read spreadsheet cell values", Risk: "read", Scopes: []string{"sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "range", Desc: "read range (<sheetId>!A1:D10, A1:D10 with --sheet-id, or a single cell like C2)"}, {Name: "sheet-id", Desc: "sheet ID"}, {Name: "value-render-option", Desc: "render option: ToString|FormattedValue|Formula|UnformattedValue"}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } if err := validateSheetRangeInput(runtime.Str("sheet-id"), runtime.Str("range")); err != nil { return err } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } readRange := runtime.Str("range") if readRange == "" && runtime.Str("sheet-id") != "" { readRange = runtime.Str("sheet-id") } readRange = normalizePointRange(runtime.Str("sheet-id"), readRange) return common.NewDryRunAPI(). GET("/open-apis/sheets/v2/spreadsheets/:token/values/:range"). Set("token", token).Set("range", readRange) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } readRange := runtime.Str("range") if readRange == "" && runtime.Str("sheet-id") != "" { readRange = runtime.Str("sheet-id") } if readRange == "" { var err error readRange, err = getFirstSheetID(runtime, token) if err != nil { return err } } readRange = normalizePointRange(runtime.Str("sheet-id"), readRange) params := map[string]interface{}{} renderOption := runtime.Str("value-render-option") if renderOption != "" { params["valueRenderOption"] = renderOption } data, err := runtime.CallAPI("GET", fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/values/%s", validate.EncodePathSegment(token), validate.EncodePathSegment(readRange)), params, nil) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var SheetReplace = common.Shortcut{ Service: "sheets", Command: "+replace", Description: "Find and replace cell values in a spreadsheet", Risk: "write", Scopes: []string{"sheets:spreadsheet:write_only", "sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "sheet-id", Desc: "sheet ID", Required: true}, {Name: "find", Desc: "search text or regex pattern", Required: true}, {Name: "replacement", Desc: "replacement text", Required: true}, {Name: "range", Desc: "search range (<sheetId>!A1:D10, or A1:D10 with --sheet-id)"}, {Name: "match-case", Type: "bool", Desc: "case-sensitive search"}, {Name: "match-entire-cell", Type: "bool", Desc: "match entire cell content"}, {Name: "search-by-regex", Type: "bool", Desc: "use regex search"}, {Name: "include-formulas", Type: "bool", Desc: "search in formulas"}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } if err := validateSheetRangeInput(runtime.Str("sheet-id"), runtime.Str("range")); err != nil { return err } if r := runtime.Str("range"); r != "" { if rangeSheetID, _, ok := splitSheetRange(r); ok && runtime.Str("sheet-id") != "" && rangeSheetID != runtime.Str("sheet-id") { return common.FlagErrorf("--range sheet ID %q does not match --sheet-id %q", rangeSheetID, runtime.Str("sheet-id")) } } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } sheetID := runtime.Str("sheet-id") findCondition := map[string]interface{}{ "range": sheetID, "match_case": runtime.Bool("match-case"), "match_entire_cell": runtime.Bool("match-entire-cell"), "search_by_regex": runtime.Bool("search-by-regex"), "include_formulas": runtime.Bool("include-formulas"), } if runtime.Str("range") != "" { findCondition["range"] = normalizeSheetRange(sheetID, runtime.Str("range")) } return common.NewDryRunAPI(). POST("/open-apis/sheets/v3/spreadsheets/:token/sheets/:sheet_id/replace"). Body(map[string]interface{}{ "find_condition": findCondition, "find": runtime.Str("find"), "replacement": runtime.Str("replacement"), }). Set("token", token).Set("sheet_id", sheetID) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } sheetID := runtime.Str("sheet-id") findCondition := map[string]interface{}{ "range": sheetID, "match_case": runtime.Bool("match-case"), "match_entire_cell": runtime.Bool("match-entire-cell"), "search_by_regex": runtime.Bool("search-by-regex"), "include_formulas": runtime.Bool("include-formulas"), } if runtime.Str("range") != "" { findCondition["range"] = normalizeSheetRange(sheetID, runtime.Str("range")) } data, err := runtime.CallAPI("POST", fmt.Sprintf("/open-apis/sheets/v3/spreadsheets/%s/sheets/%s/replace", validate.EncodePathSegment(token), validate.EncodePathSegment(sheetID), ), nil, map[string]interface{}{ "find_condition": findCondition, "find": runtime.Str("find"), "replacement": runtime.Str("replacement"), }, ) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var SheetSetStyle = common.Shortcut{ Service: "sheets", Command: "+set-style", Description: "Set cell style for a range", Risk: "write", Scopes: []string{"sheets:spreadsheet:write_only", "sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "range", Desc: "cell range (<sheetId>!A1:B2, or A1:B2 with --sheet-id)", Required: true}, {Name: "sheet-id", Desc: "sheet ID (for relative range)"}, {Name: "style", Desc: "style JSON object (e.g. {\"font\":{\"bold\":true},\"backColor\":\"#ff0000\"})", Required: true}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } var style interface{} if err := json.Unmarshal([]byte(runtime.Str("style")), &style); err != nil { return common.FlagErrorf("--style must be valid JSON: %v", err) } if _, ok := style.(map[string]interface{}); !ok { return common.FlagErrorf("--style must be a JSON object, got %T", style) } if err := validateSheetRangeInput(runtime.Str("sheet-id"), runtime.Str("range")); err != nil { return err } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } r := normalizeSheetRange(runtime.Str("sheet-id"), runtime.Str("range")) var style interface{} json.Unmarshal([]byte(runtime.Str("style")), &style) return common.NewDryRunAPI(). PUT("/open-apis/sheets/v2/spreadsheets/:token/style"). Body(map[string]interface{}{ "appendStyle": map[string]interface{}{ "range": r, "style": style, }, }). Set("token", token) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } r := normalizeSheetRange(runtime.Str("sheet-id"), runtime.Str("range")) var style interface{} if err := json.Unmarshal([]byte(runtime.Str("style")), &style); err != nil { return common.FlagErrorf("--style must be valid JSON: %v", err) } data, err := runtime.CallAPI("PUT", fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/style", validate.EncodePathSegment(token)), nil, map[string]interface{}{ "appendStyle": map[string]interface{}{ "range": r, "style": style, }, }, ) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var SheetUnmergeCells = common.Shortcut{ Service: "sheets", Command: "+unmerge-cells", Description: "Unmerge (split) cells in a spreadsheet", Risk: "write", Scopes: []string{"sheets:spreadsheet:write_only", "sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "range", Desc: "cell range (<sheetId>!A1:B2, or A1:B2 with --sheet-id)", Required: true}, {Name: "sheet-id", Desc: "sheet ID (for relative range)"}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } if err := validateSheetRangeInput(runtime.Str("sheet-id"), runtime.Str("range")); err != nil { return err } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } r := normalizeSheetRange(runtime.Str("sheet-id"), runtime.Str("range")) return common.NewDryRunAPI(). POST("/open-apis/sheets/v2/spreadsheets/:token/unmerge_cells"). Body(map[string]interface{}{ "range": r, }). Set("token", token) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } r := normalizeSheetRange(runtime.Str("sheet-id"), runtime.Str("range")) data, err := runtime.CallAPI("POST", fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/unmerge_cells", validate.EncodePathSegment(token)), nil, map[string]interface{}{ "range": r, }, ) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var SheetUpdateDimension = common.Shortcut{ Service: "sheets", Command: "+update-dimension", Description: "Update row or column properties (visibility, size)", Risk: "write", Scopes: []string{"sheets:spreadsheet:write_only", "sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "sheet-id", Desc: "worksheet ID", Required: true}, {Name: "dimension", Desc: "ROWS or COLUMNS", Required: true, Enum: []string{"ROWS", "COLUMNS"}}, {Name: "start-index", Type: "int", Desc: "start position (1-indexed, inclusive)", Required: true}, {Name: "end-index", Type: "int", Desc: "end position (1-indexed, inclusive)", Required: true}, {Name: "visible", Type: "bool", Desc: "true to show, false to hide"}, {Name: "fixed-size", Type: "int", Desc: "row height or column width in pixels"}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } if runtime.Int("start-index") < 1 { return common.FlagErrorf("--start-index must be >= 1") } if runtime.Int("end-index") < runtime.Int("start-index") { return common.FlagErrorf("--end-index must be >= --start-index") } if !runtime.Cmd.Flags().Changed("visible") && !runtime.Cmd.Flags().Changed("fixed-size") { return common.FlagErrorf("specify at least one of --visible or --fixed-size") } if runtime.Cmd.Flags().Changed("fixed-size") && runtime.Int("fixed-size") < 1 { return common.FlagErrorf("--fixed-size must be >= 1") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } props := map[string]interface{}{} if runtime.Cmd.Flags().Changed("visible") { props["visible"] = runtime.Bool("visible") } if runtime.Cmd.Flags().Changed("fixed-size") { props["fixedSize"] = runtime.Int("fixed-size") } return common.NewDryRunAPI(). PUT("/open-apis/sheets/v2/spreadsheets/:token/dimension_range"). Body(map[string]interface{}{ "dimension": map[string]interface{}{ "sheetId": runtime.Str("sheet-id"), "majorDimension": runtime.Str("dimension"), "startIndex": runtime.Int("start-index"), "endIndex": runtime.Int("end-index"), }, "dimensionProperties": props, }). Set("token", token) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } props := map[string]interface{}{} if runtime.Cmd.Flags().Changed("visible") { props["visible"] = runtime.Bool("visible") } if runtime.Cmd.Flags().Changed("fixed-size") { props["fixedSize"] = runtime.Int("fixed-size") } data, err := runtime.CallAPI("PUT", fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/dimension_range", validate.EncodePathSegment(token)), nil, map[string]interface{}{ "dimension": map[string]interface{}{ "sheetId": runtime.Str("sheet-id"), "majorDimension": runtime.Str("dimension"), "startIndex": runtime.Int("start-index"), "endIndex": runtime.Int("end-index"), }, "dimensionProperties": props, }, ) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var SheetWrite = common.Shortcut{ Service: "sheets", Command: "+write", Description: "Write to spreadsheet cells (overwrite mode)", Risk: "write", Scopes: []string{"sheets:spreadsheet:write_only", "sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "range", Desc: "write range (<sheetId>!A1:D10, A1:D10 with --sheet-id, or a single cell like C2)"}, {Name: "sheet-id", Desc: "sheet ID"}, {Name: "values", Desc: "2D array JSON", Required: true}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } var values interface{} if err := json.Unmarshal([]byte(runtime.Str("values")), &values); err != nil { return common.FlagErrorf("--values invalid JSON, must be a 2D array") } if err := validateSheetRangeInput(runtime.Str("sheet-id"), runtime.Str("range")); err != nil { return err } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } writeRange := runtime.Str("range") if writeRange == "" && runtime.Str("sheet-id") != "" { writeRange = runtime.Str("sheet-id") } var values interface{} json.Unmarshal([]byte(runtime.Str("values")), &values) writeRange = normalizeWriteRange(runtime.Str("sheet-id"), writeRange, values) return common.NewDryRunAPI(). PUT("/open-apis/sheets/v2/spreadsheets/:token/values"). Body(map[string]interface{}{"valueRange": map[string]interface{}{"range": writeRange, "values": values}}). Set("token", token) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } var values interface{} json.Unmarshal([]byte(runtime.Str("values")), &values) writeRange := runtime.Str("range") if writeRange == "" && runtime.Str("sheet-id") != "" { writeRange = runtime.Str("sheet-id") } if writeRange == "" { var err error writeRange, err = getFirstSheetID(runtime, token) if err != nil { return err } } writeRange = normalizeWriteRange(runtime.Str("sheet-id"), writeRange, values) data, err := runtime.CallAPI("PUT", fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/values", validate.EncodePathSegment(token)), nil, map[string]interface{}{ "valueRange": map[string]interface{}{ "range": writeRange, "values": values, }, }) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var SheetWriteImage = common.Shortcut{ Service: "sheets", Command: "+write-image", Description: "Write an image into a spreadsheet cell", Risk: "write", Scopes: []string{"sheets:spreadsheet:write_only", "sheets:spreadsheet:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "url", Desc: "spreadsheet URL"}, {Name: "spreadsheet-token", Desc: "spreadsheet token"}, {Name: "sheet-id", Desc: "sheet ID"}, {Name: "range", Desc: "target cell (e.g. A1 or <sheetId>!A1). Start and end cell must be the same", Required: true}, {Name: "image", Desc: "local image file path (supported formats: PNG, JPEG, JPG, GIF, BMP, JFIF, EXIF, TIFF, BPG, HEIC)", Required: true}, {Name: "name", Desc: "image file name with extension (defaults to the basename of --image)"}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } if token == "" { return common.FlagErrorf("specify --url or --spreadsheet-token") } if err := validateSheetRangeInput(runtime.Str("sheet-id"), runtime.Str("range")); err != nil { return err } if err := validateSingleCellRange(runtime.Str("range")); err != nil { return err } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } pointRange := normalizePointRange(runtime.Str("sheet-id"), runtime.Str("range")) imageName := runtime.Str("name") if imageName == "" { imageName = filepath.Base(runtime.Str("image")) } return common.NewDryRunAPI(). Desc("JSON upload with inline image bytes"). POST("/open-apis/sheets/v2/spreadsheets/:token/values_image"). Body(map[string]interface{}{ "range": pointRange, "image": fmt.Sprintf("<binary: %s>", runtime.Str("image")), "name": imageName, }). Set("token", token) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { token := runtime.Str("spreadsheet-token") if runtime.Str("url") != "" { token = extractSpreadsheetToken(runtime.Str("url")) } pointRange := normalizePointRange(runtime.Str("sheet-id"), runtime.Str("range")) imagePath := runtime.Str("image") safePath, err := validate.SafeInputPath(imagePath) if err != nil { return output.ErrValidation("unsafe image path: %s", err) } stat, err := vfs.Stat(safePath) if err != nil { return output.ErrValidation("image file not found: %s", imagePath) } if !stat.Mode().IsRegular() { return output.ErrValidation("image must be a regular file: %s", imagePath) } const maxImageSize int64 = 20 * 1024 * 1024 // 20 MB if stat.Size() > maxImageSize { return output.ErrValidation("image %.1fMB exceeds 20MB limit", float64(stat.Size())/1024/1024) } imageBytes, err := vfs.ReadFile(safePath) if err != nil { return output.ErrValidation("cannot read image file: %s", err) } imageName := runtime.Str("name") if imageName == "" { imageName = filepath.Base(imagePath) } fmt.Fprintf(runtime.IO().ErrOut, "Writing image: %s (%d bytes) → %s\n", imageName, stat.Size(), pointRange) data, err := runtime.CallAPI("POST", fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/values_image", validate.EncodePathSegment(token)), nil, map[string]interface{}{ "range": pointRange, "image": imageBytes, "name": imageName, }) if err != nil { return err } runtime.Out(data, nil) return nil }, }
Functions ¶
Types ¶
This section is empty.
Source Files
¶
- helpers.go
- sheet_add_dimension.go
- sheet_append.go
- sheet_batch_set_style.go
- sheet_create.go
- sheet_delete_dimension.go
- sheet_export.go
- sheet_find.go
- sheet_info.go
- sheet_insert_dimension.go
- sheet_merge_cells.go
- sheet_move_dimension.go
- sheet_read.go
- sheet_replace.go
- sheet_set_style.go
- sheet_unmerge_cells.go
- sheet_update_dimension.go
- sheet_write.go
- sheet_write_image.go
- shortcuts.go
Click to show internal directories.
Click to hide internal directories.