feat(filter): expose view options (board/list, group by, sort) - #463
feat(filter): expose view options (board/list, group by, sort)#463alejandro-goffa wants to merge 1 commit into
Conversation
Filters could be created and updated from the CLI, but their presentation could not. A query like "(p1 | p2 | !no deadline)" is only readable as a board grouped by assignee, and that layout was unreachable without hand-rolling a Sync request. The SDK already types the view_options_set command and re-exports the enum constants, and the CLI already drives Sync commands the same way in lib/api/filters.ts, so this wires up the existing pieces. The Sync API requires these enums in UPPERCASE and rejects anything else with error_code 20, while the public API reference documents them in lowercase. Flag values are accepted in lowercase kebab-case and normalized (due-date -> DUE_DATE), so callers never meet that discrepancy. Allowed values come from the SDK constants rather than being restated. View options are stored per user, so this affects only the authenticated account even when the filter's underlying projects are shared. Refs Doist#462 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
scottlovegrove
left a comment
There was a problem hiding this comment.
Thanks for creating the PR, just a few small things to take care of
| .option('--query <query>', 'Filter query (required, e.g., "today | overdue")') | ||
| .option('--color <color>', 'Filter color') | ||
| .option('--favorite', 'Mark as favorite') | ||
| .option('--view-mode <mode>', 'Layout: list, board or calendar') |
There was a problem hiding this comment.
[P2] These new view-option flags need adding to SKILL_CONTENT in src/lib/skills/content.ts, then regenerate skills/todoist-cli/SKILL.md with npm run sync:skill. The installed agent skill is the command reference, and right now it documents filter create / filter update with no way to discover --view-mode, --group-by, --sort-by or --sort-order.
| .option('--query <query>', 'Filter query (required, e.g., "today | overdue")') | ||
| .option('--color <color>', 'Filter color') | ||
| .option('--favorite', 'Mark as favorite') | ||
| .option('--view-mode <mode>', 'Layout: list, board or calendar') |
There was a problem hiding this comment.
[P3] Worth centralising the registration of these four flags. The same definitions are duplicated for create and update, and the accepted values are separately derived from the SDK arrays in view-options.ts, so a future change to the options can easily leave one help block stale. Extracting a shared registration helper and deriving the choice descriptions from the same option metadata would keep them in step.
|
|
||
| export async function updateFilterCmd(nameOrId: string, options: UpdateOptions): Promise<void> { | ||
| const filter = await resolveFilterRef(nameOrId) | ||
| const viewOptions = parseViewOptionFlags(options) |
There was a problem hiding this comment.
[P2] Parse the view flags before resolving the filter. An invalid value currently triggers resolveFilterRef, which fetches the entire filter list, before failing locally — e.g. td filter update Work --group-by colour performs a Sync request that was never needed.
| } | ||
|
|
||
| await updateFilter(filter.id, args) | ||
| if (Object.keys(args).length > 0) { |
There was a problem hiding this comment.
[P2] When both filter fields and view flags are supplied this fires two independent Sync requests serially, adding a full network round trip to every combined update. Batching filter_update and view_options_set into a single api.sync call (or at least running them concurrently) would avoid that.
| }) | ||
| }) | ||
|
|
||
| describe('filter view options', () => { |
There was a problem hiding this comment.
[P2] Could we add dry-run coverage with a view flag, for both create and update, asserting setViewOptions is not called? The existing dry-run tests only exercise the filter fields, so a regression that persisted view settings during --dry-run would pass while quietly changing the user's layout.
Implements Option A from #462 — flags on the existing commands, since that was the smallest surface that covers the common cases. Happy to reshape into a dedicated subcommand (Option B) if you'd rather reach
TODAY/UPCOMINGtoo; the parsing and API layers are already separate from the filter commands, so moving them is cheap.What this adds
Four new flags on
filter createandfilter update:--view-mode,--group-by,--sort-by,--sort-order.Notes on the implementation
@doist/todoist-sdkalready typesview_options_setand re-exportsVIEW_MODES/GROUPED_BY_OPTIONS/SORTED_BY_OPTIONS/SORT_ORDERS. Allowed values are derived from those constants rather than restated, so the CLI can't drift from the SDK.error_code: 20, while the API reference documents lowercase. Flags accept lowercase kebab-case and normalise (due-date→DUE_DATE). Invalid values fail before any request with the allowed list echoed back.updatewith only view flags no longer tripsNO_CHANGES, and skips thefilter_updatecall when nothing about the filter itself changed — one command, one request.src/lib/api/view-options.tstakes aviewType/objectIdrather than being filter-specific, soproject/labelcan reuse it.Verification
npm run check,npm run type-check,npm run build— cleannpm test— 1784 passing (5 added)Exercised end-to-end against the live API: after
filter create ... --view-mode board --group-by assignee --sort-by priority --sort-order desc, aview_optionssync read returns{"view_type": "FILTER", "object_id": "...", "view_mode": "BOARD", "grouped_by": "ASSIGNEE", "sorted_by": "PRIORITY", "sort_order": "DESC"}Dry-run and validation output:
Closes #462