Skip to content

Commit b609c22

Browse files
feat(sheets): add basic filter creation command (#902)
* feat(sheets): add basic filter creation command * fix(sheets): guard basic filter replacement * fix(sheets): satisfy dry-run safety contract * feat(sheets): add basic filter creation command --------- Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: Peter Steinberger <[email protected]>
1 parent 088198a commit b609c22

11 files changed

Lines changed: 381 additions & 11 deletions

File tree

.agents/skills/gog-sheets/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ gog --readonly --account [email protected] sheets get SHEET_ID 'Sheet1!A1:D20' --
4141
| `delete-dimension` | Delete rows or columns while preserving intersecting tables |
4242
| `delete-tab` | Delete a tab/sheet from a spreadsheet (use --force to skip confirmation) |
4343
| `export` | Export a Google Sheet (pdf\|xlsx\|csv) via Drive |
44+
| `filter` | Manage basic filters |
4445
| `find-replace` | Find and replace text across a spreadsheet |
4546
| `format` | Apply cell formatting to a range |
4647
| `freeze` | Freeze rows and columns on a sheet |

docs/commands.generated.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,8 @@ Generated from `gog schema --json`.
564564
- [`gog sheets (sheet) delete-dimension (delete-dim) --dimension=STRING <spreadsheetId> <rangeOrSheet> [flags]`](commands/gog-sheets-delete-dimension.md) - Delete rows or columns while preserving intersecting tables
565565
- [`gog sheets (sheet) delete-tab (delete-sheet) <spreadsheetId> <tabName>`](commands/gog-sheets-delete-tab.md) - Delete a tab/sheet from a spreadsheet (use --force to skip confirmation)
566566
- [`gog sheets (sheet) export (download,dl) <spreadsheetId> [flags]`](commands/gog-sheets-export.md) - Export a Google Sheet (pdf|xlsx|csv) via Drive
567+
- [`gog sheets (sheet) filter (filters,basic-filter,basic-filters) <command>`](commands/gog-sheets-filter.md) - Manage basic filters
568+
- [`gog sheets (sheet) filter (filters,basic-filter,basic-filters) set (create,add) <spreadsheetId> <range>`](commands/gog-sheets-filter-set.md) - Set a basic filter on a range; replacing an existing filter requires confirmation (or --force)
567569
- [`gog sheets (sheet) find-replace <spreadsheetId> <find> <replace> [flags]`](commands/gog-sheets-find-replace.md) - Find and replace text across a spreadsheet
568570
- [`gog sheets (sheet) format <spreadsheetId> <range> [flags]`](commands/gog-sheets-format.md) - Apply cell formatting to a range
569571
- [`gog sheets (sheet) freeze <spreadsheetId> [flags]`](commands/gog-sheets-freeze.md) - Freeze rows and columns on a sheet

docs/commands/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Every `gog` command has a generated docs page. The source of truth is the live CLI schema; run `make docs-commands` after changing command names, flags, help text, aliases, or arguments.
44

5-
Generated pages: 704.
5+
Generated pages: 706.
66

77
## Top-level Commands
88

@@ -617,6 +617,8 @@ Generated pages: 704.
617617
- [gog sheets delete-dimension](gog-sheets-delete-dimension.md) - Delete rows or columns while preserving intersecting tables
618618
- [gog sheets delete-tab](gog-sheets-delete-tab.md) - Delete a tab/sheet from a spreadsheet (use --force to skip confirmation)
619619
- [gog sheets export](gog-sheets-export.md) - Export a Google Sheet (pdf|xlsx|csv) via Drive
620+
- [gog sheets filter](gog-sheets-filter.md) - Manage basic filters
621+
- [gog sheets filter set](gog-sheets-filter-set.md) - Set a basic filter on a range; replacing an existing filter requires confirmation (or --force)
620622
- [gog sheets find-replace](gog-sheets-find-replace.md) - Find and replace text across a spreadsheet
621623
- [gog sheets format](gog-sheets-format.md) - Apply cell formatting to a range
622624
- [gog sheets freeze](gog-sheets-freeze.md) - Freeze rows and columns on a sheet
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# `gog sheets filter set`
2+
3+
> Generated from `gog schema --json`. Do not edit this page by hand; run `make docs-commands`.
4+
5+
Set a basic filter on a range; replacing an existing filter requires confirmation (or --force)
6+
7+
## Usage
8+
9+
```bash
10+
gog sheets (sheet) filter (filters,basic-filter,basic-filters) set (create,add) <spreadsheetId> <range>
11+
```
12+
13+
## Parent
14+
15+
- [gog sheets filter](gog-sheets-filter.md)
16+
17+
## Flags
18+
19+
| Flag | Type | Default | Help |
20+
| --- | --- | --- | --- |
21+
| `--access-token` | `string` | | Use provided access token directly (bypasses stored refresh tokens; token expires in ~1h) |
22+
| `-a`<br>`--account`<br>`--acct` | `string` | | Account email, alias, or auto for authenticated Google API commands |
23+
| `--client` | `string` | | OAuth client name (selects stored credentials + token bucket) |
24+
| `--color` | `string` | auto | Color output: auto\|always\|never |
25+
| `--disable-commands` | `string` | | Comma-separated list of disabled commands; dot paths allowed |
26+
| `-n`<br>`--dry-run`<br>`--dryrun`<br>`--noop`<br>`--preview` | `bool` | | Do not make changes; print intended actions and exit successfully |
27+
| `--enable-commands` | `string` | | Comma-separated list of enabled command prefixes; dot paths allowed (restricts CLI) |
28+
| `--enable-commands-exact` | `string` | | Comma-separated list of exact enabled commands; dot paths allowed and parent commands do not enable children |
29+
| `-y`<br>`--force`<br>`--assume-yes`<br>`--yes` | `bool` | | Skip confirmations for destructive commands |
30+
| `--gmail-no-send` | `bool` | false | Block Gmail send operations (agent safety) |
31+
| `-h`<br>`--help` | `kong.helpFlag` | | Show context-sensitive help. |
32+
| `--home` | `string` | | Override gogcli config/data/state/cache root (equivalent to GOG_HOME) |
33+
| `-j`<br>`--json`<br>`--machine` | `bool` | false | Output JSON to stdout (best for scripting) |
34+
| `--no-input`<br>`--non-interactive`<br>`--noninteractive` | `bool` | | Never prompt; fail instead (useful for CI) |
35+
| `-p`<br>`--plain`<br>`--tsv` | `bool` | false | Output stable, parseable text to stdout (TSV; no colors) |
36+
| `--readonly` | `bool` | false | Block mutating API requests at runtime; auth add also requests read-only OAuth scopes |
37+
| `--results-only` | `bool` | | In JSON mode, emit only the primary result (drops envelope fields like nextPageToken) |
38+
| `--select`<br>`--pick`<br>`--project` | `string` | | In JSON mode, select comma-separated fields (best-effort; supports dot paths). Desire path: use --fields for most commands. |
39+
| `-v`<br>`--verbose` | `bool` | | Enable verbose logging |
40+
| `--version` | `kong.VersionFlag` | | Print version and exit |
41+
| `--wrap-untrusted` | `bool` | false | In JSON/raw output, wrap fetched text fields in external untrusted-content markers |
42+
43+
## See Also
44+
45+
- [gog sheets filter](gog-sheets-filter.md)
46+
- [Command index](README.md)

docs/commands/gog-sheets-filter.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# `gog sheets filter`
2+
3+
> Generated from `gog schema --json`. Do not edit this page by hand; run `make docs-commands`.
4+
5+
Manage basic filters
6+
7+
## Usage
8+
9+
```bash
10+
gog sheets (sheet) filter (filters,basic-filter,basic-filters) <command>
11+
```
12+
13+
## Parent
14+
15+
- [gog sheets](gog-sheets.md)
16+
17+
## Subcommands
18+
19+
- [gog sheets filter set](gog-sheets-filter-set.md) - Set a basic filter on a range; replacing an existing filter requires confirmation (or --force)
20+
21+
## Flags
22+
23+
| Flag | Type | Default | Help |
24+
| --- | --- | --- | --- |
25+
| `--access-token` | `string` | | Use provided access token directly (bypasses stored refresh tokens; token expires in ~1h) |
26+
| `-a`<br>`--account`<br>`--acct` | `string` | | Account email, alias, or auto for authenticated Google API commands |
27+
| `--client` | `string` | | OAuth client name (selects stored credentials + token bucket) |
28+
| `--color` | `string` | auto | Color output: auto\|always\|never |
29+
| `--disable-commands` | `string` | | Comma-separated list of disabled commands; dot paths allowed |
30+
| `-n`<br>`--dry-run`<br>`--dryrun`<br>`--noop`<br>`--preview` | `bool` | | Do not make changes; print intended actions and exit successfully |
31+
| `--enable-commands` | `string` | | Comma-separated list of enabled command prefixes; dot paths allowed (restricts CLI) |
32+
| `--enable-commands-exact` | `string` | | Comma-separated list of exact enabled commands; dot paths allowed and parent commands do not enable children |
33+
| `-y`<br>`--force`<br>`--assume-yes`<br>`--yes` | `bool` | | Skip confirmations for destructive commands |
34+
| `--gmail-no-send` | `bool` | false | Block Gmail send operations (agent safety) |
35+
| `-h`<br>`--help` | `kong.helpFlag` | | Show context-sensitive help. |
36+
| `--home` | `string` | | Override gogcli config/data/state/cache root (equivalent to GOG_HOME) |
37+
| `-j`<br>`--json`<br>`--machine` | `bool` | false | Output JSON to stdout (best for scripting) |
38+
| `--no-input`<br>`--non-interactive`<br>`--noninteractive` | `bool` | | Never prompt; fail instead (useful for CI) |
39+
| `-p`<br>`--plain`<br>`--tsv` | `bool` | false | Output stable, parseable text to stdout (TSV; no colors) |
40+
| `--readonly` | `bool` | false | Block mutating API requests at runtime; auth add also requests read-only OAuth scopes |
41+
| `--results-only` | `bool` | | In JSON mode, emit only the primary result (drops envelope fields like nextPageToken) |
42+
| `--select`<br>`--pick`<br>`--project` | `string` | | In JSON mode, select comma-separated fields (best-effort; supports dot paths). Desire path: use --fields for most commands. |
43+
| `-v`<br>`--verbose` | `bool` | | Enable verbose logging |
44+
| `--version` | `kong.VersionFlag` | | Print version and exit |
45+
| `--wrap-untrusted` | `bool` | false | In JSON/raw output, wrap fetched text fields in external untrusted-content markers |
46+
47+
## See Also
48+
49+
- [gog sheets](gog-sheets.md)
50+
- [Command index](README.md)

docs/commands/gog-sheets.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ gog sheets (sheet) <command> [flags]
2929
- [gog sheets delete-dimension](gog-sheets-delete-dimension.md) - Delete rows or columns while preserving intersecting tables
3030
- [gog sheets delete-tab](gog-sheets-delete-tab.md) - Delete a tab/sheet from a spreadsheet (use --force to skip confirmation)
3131
- [gog sheets export](gog-sheets-export.md) - Export a Google Sheet (pdf|xlsx|csv) via Drive
32+
- [gog sheets filter](gog-sheets-filter.md) - Manage basic filters
3233
- [gog sheets find-replace](gog-sheets-find-replace.md) - Find and replace text across a spreadsheet
3334
- [gog sheets format](gog-sheets-format.md) - Apply cell formatting to a range
3435
- [gog sheets freeze](gog-sheets-freeze.md) - Freeze rows and columns on a sheet

internal/cmd/dryrun_e2e_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,11 @@ func TestDryRunE2E_CommandsSkipAuthAPIAndFileWrites(t *testing.T) {
649649
args: []string{"sheets", "validation", "clear", "sheet123", "Sheet1!A1:A10"},
650650
op: "sheets.validation.clear",
651651
},
652+
{
653+
name: "sheets filter set",
654+
args: []string{"sheets", "filter", "set", "sheet123", "Sheet1!A1:D10"},
655+
op: "sheets.filter.set",
656+
},
652657
{
653658
name: "sheets copy",
654659
args: []string{"sheets", "copy", "sheet123", "SmokeSheet"},

internal/cmd/sheets.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type SheetsCmd struct {
4242
Conditional SheetsConditionalCmd `cmd:"" name:"conditional-format" aliases:"cf,conditional-formats" help:"Manage conditional formatting rules"`
4343
Validation SheetsValidationCmd `cmd:"" name:"validation" aliases:"data-validation,validations" help:"Manage cell data validation rules"`
4444
Banding SheetsBandingCmd `cmd:"" name:"banding" aliases:"banded-ranges" help:"Manage alternating color banding"`
45+
Filter SheetsFilterCmd `cmd:"" name:"filter" aliases:"filters,basic-filter,basic-filters" help:"Manage basic filters"`
4546
Merge SheetsMergeCmd `cmd:"" name:"merge" help:"Merge cells in a range"`
4647
Unmerge SheetsUnmergeCmd `cmd:"" name:"unmerge" help:"Unmerge cells in a range"`
4748
CopyPaste SheetsCopyPasteCmd `cmd:"" name:"copy-paste" aliases:"fill,copy-range" help:"Copy a range's values/formulas/format to another range (tiles to fill down/across)"`

internal/cmd/sheets_filter.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
"google.golang.org/api/sheets/v4"
9+
)
10+
11+
type SheetsFilterCmd struct {
12+
Set SheetsFilterSetCmd `cmd:"" name:"set" aliases:"create,add" help:"Set a basic filter on a range; replacing an existing filter requires confirmation (or --force)"`
13+
}
14+
15+
type SheetsFilterSetCmd struct {
16+
SpreadsheetID string `arg:"" name:"spreadsheetId" help:"Spreadsheet ID"`
17+
Range string `arg:"" name:"range" help:"Range (A1 notation with sheet name or named range name)"`
18+
}
19+
20+
func (c *SheetsFilterSetCmd) Run(ctx context.Context, flags *RootFlags) error {
21+
spreadsheetID := normalizeGoogleID(strings.TrimSpace(c.SpreadsheetID))
22+
rangeSpec := cleanRange(c.Range)
23+
if spreadsheetID == "" {
24+
return usage("empty spreadsheetId")
25+
}
26+
if strings.TrimSpace(rangeSpec) == "" {
27+
return usage("empty range")
28+
}
29+
30+
dryRunPayload := map[string]any{
31+
"spreadsheet_id": spreadsheetID,
32+
"range": rangeSpec,
33+
}
34+
if dryRunErr := dryRunExit(ctx, flags, "sheets.filter.set", dryRunPayload); dryRunErr != nil {
35+
return dryRunErr
36+
}
37+
38+
return runSheetsMutation(ctx, flagsWithoutDryRun(flags), "sheets.filter.set", dryRunPayload, func(ctx context.Context, svc *sheets.Service) (map[string]any, string, error) {
39+
catalog, err := fetchSpreadsheetRangeCatalogWithBasicFilters(ctx, svc, spreadsheetID)
40+
if err != nil {
41+
return nil, "", err
42+
}
43+
gridRange, err := resolveGridRangeWithCatalog(rangeSpec, catalog, "filter")
44+
if err != nil {
45+
return nil, "", err
46+
}
47+
existingFilter := catalog.BasicFiltersBySheetID[gridRange.SheetId]
48+
if existingFilter != nil {
49+
sheetTitle := catalog.SheetTitlesByID[gridRange.SheetId]
50+
if err := confirmDestructiveChecked(ctx, flagsWithoutDryRun(flags), fmt.Sprintf("replace existing basic filter on sheet %q", sheetTitle)); err != nil {
51+
return nil, "", err
52+
}
53+
}
54+
55+
filter := &sheets.BasicFilter{Range: gridRange}
56+
req := &sheets.BatchUpdateSpreadsheetRequest{
57+
Requests: []*sheets.Request{{
58+
SetBasicFilter: &sheets.SetBasicFilterRequest{
59+
Filter: filter,
60+
},
61+
}},
62+
}
63+
if err := applySheetsBatchUpdate(ctx, svc, spreadsheetID, req); err != nil {
64+
return nil, "", err
65+
}
66+
return map[string]any{
67+
"spreadsheetId": spreadsheetID,
68+
"range": rangeSpec,
69+
"filter": filter,
70+
"replaced": existingFilter != nil,
71+
}, fmt.Sprintf("Set basic filter on %s", rangeSpec), nil
72+
})
73+
}

0 commit comments

Comments
 (0)