Skip to content

feat(dashboard): add widget add, edit, and delete commands#407

Merged
betegon merged 29 commits intomainfrom
feat/dashboard-widget-commands
Mar 20, 2026
Merged

feat(dashboard): add widget add, edit, and delete commands#407
betegon merged 29 commits intomainfrom
feat/dashboard-widget-commands

Conversation

@betegon
Copy link
Member

@betegon betegon commented Mar 12, 2026

Summary

  • Add sentry dashboard widget add — add a widget with inline --display/--query/--where/--group-by/--sort/--limit flags
  • Add sentry dashboard widget edit — edit an existing widget by --index or --title, merging only changed fields
  • Add sentry dashboard widget delete — remove a widget by --index or --title
  • Add widget formatters (formatWidgetAdded, formatWidgetDeleted, formatWidgetEdited)

All widget commands use the GET-modify-PUT pattern with server field stripping and auto-layout for new widgets.

Stacked on #406.

Test plan

  • bun run typecheck — no new type errors
  • bun run lint — passes
  • bun test test/types/dashboard.test.ts — 53 tests pass

🤖 Generated with Claude Code

@github-actions
Copy link
Contributor

github-actions bot commented Mar 12, 2026

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Telemetry

  • Include user email in Sentry telemetry context by BYK in #513
  • Track TTY vs non-TTY invocations via metric by betegon in #482

Other

  • (dashboard) Add widget add, edit, and delete commands by betegon in #407
  • (help) Fuzzy "Did you mean?" suggestions for command typos by BYK in #516
  • (upgrade) Add progress spinners for version check and download phases by BYK in #515
  • Dynamic cache-backed shell completions with fuzzy matching by BYK in #465

Bug Fixes 🐛

  • (completions) Populate project cache from listProjects by betegon in #517
  • (help) Hide ASCII banner when stdout is not a TTY by betegon in #501
  • (json) Flatten view command JSON output for --fields filtering by BYK in #495
  • (polling) Throw TimeoutError instead of bare Error on timeout by BYK in #503
  • (project) Fallback to org listing when bare slug matches an organization by betegon in #475
  • (setup) Auto-configure zsh fpath for shell completions by betegon in #509
  • Isolate multiregion 403 tests from env-var auth tokens by BYK in #514
  • Only mention token scopes in 403 errors for env-var tokens by BYK in #512
  • Suggest similar projects on project-search miss (CLI-A4) by BYK in #511
  • Preserve ApiError type in Seer handler + suggest trial start command (CLI-N, CLI-1D/BW/98) by BYK in #510
  • Add 403 scope guidance to issue list error handling (CLI-97) by BYK in #508
  • Propagate 403 from multi-region fan-out instead of returning empty list (CLI-89) by BYK in #507
  • Lowercase project slug in URL-parsed issue short IDs (CLI-C8 follow-up) by BYK in #506
  • Handle EIO stream errors gracefully in bin.ts by BYK in #505
  • Use fuzzyMatch for similar project suggestions and add tests (CLI-C0) by BYK in #504
  • Use resolved org in numeric issue ID 404 hint (CLI-BT) by BYK in #502
  • Include API endpoint in error messages for better diagnostics (CLI-BS) by BYK in #500
  • Enrich 403 on org listing with token scope guidance (CLI-89) by BYK in #498
  • Add 400 suggestions to org-all issue list path (CLI-BY) by BYK in #497
  • Lowercase project slug in issue arg parsing (CLI-C8) by BYK in #496
  • Enrich short ID 404 with org context and suggestions (CLI-A1) by BYK in #494
  • Suggest similar projects when project not found in org (CLI-C0) by BYK in #493
  • Event 404 hint should suggest different project, not repeat failing command by BYK in #492
  • Enrich event 404 errors with retention and format suggestions (CLI-6F) by BYK in #491
  • Add actionable suggestions for 400 Bad Request on issue list (CLI-BM, CLI-7B) by BYK in #489
  • Detect issue short IDs passed to issue list (CLI-C3) by BYK in #488
  • Add Glob.match() polyfill + improve auto-detect diagnostics (CLI-7T) by BYK in #487
  • Add org-slug pre-check to dispatchOrgScopedList (CLI-9A) by BYK in #485

Internal Changes 🔧

  • (issue) Skip getProject round-trip in project-search resolution by betegon in #473
  • (resolve) Carry project data through resolution to eliminate redundant getProject calls by BYK in #486
  • (telemetry) Convert is_tty metric to span tag by betegon in #499
  • HTTP latency optimizations — diagnostics, cache warming, concurrency limits by BYK in #490
  • Switch from @sentry/bun to @sentry/node-core/light (~170ms startup savings) by BYK in #474
  • Regenerate skill files by github-actions[bot] in b7b240ec

🤖 This preview updates automatically when you update the PR.

Dashboard API functions now live in src/lib/api/dashboards.ts following
the domain module pattern from #405.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@betegon betegon force-pushed the feat/dashboard-commands-core branch from 02c23d0 to f05f91a Compare March 13, 2026 09:39
@betegon betegon force-pushed the feat/dashboard-widget-commands branch from 17da821 to 64ddb86 Compare March 13, 2026 09:40
@github-actions
Copy link
Contributor

github-actions bot commented Mar 13, 2026

Codecov Results 📊

126 passed | Total: 126 | Pass Rate: 100% | Execution Time: 0ms

📊 Comparison with Base Branch

Metric Change
Total Tests
Passed Tests
Failed Tests
Skipped Tests

✨ No test changes detected

All tests are passing successfully.

✅ Patch coverage is 95.27%. Project has 1075 uncovered lines.
❌ Project coverage is 95.78%. Comparing base (base) to head (head).

Files with missing lines (4)
File Patch % Lines
dashboards.ts 10.00% ⚠️ 54 Missing
human.ts 96.42% ⚠️ 46 Missing
response-cache.ts 92.42% ⚠️ 25 Missing
dashboard.ts 99.73% ⚠️ 1 Missing
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
- Coverage    95.79%    95.78%    -0.01%
==========================================
  Files          180       184        +4
  Lines        24897     25502      +605
  Branches         0         0         —
==========================================
+ Hits         23850     24427      +577
- Misses        1047      1075       +28
- Partials         0         0         —

Generated by Codecov Action

betegon and others added 3 commits March 13, 2026 12:08
…end existing test suites

Add ~46 new tests covering dashboard commands (list, create, resolve),
URL builders (property-based), human formatters, and type utilities
(assignDefaultLayout, stripWidgetServerFields).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Convert list from buildListCommand to buildCommand with return-based output
- Extract formatDashboardListHuman for human-readable table rendering
- Add --fresh flag to both list and view commands
- Update list tests to include fresh flag

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@betegon betegon force-pushed the feat/dashboard-widget-commands branch from 2a0361d to 38e3eda Compare March 13, 2026 11:14
betegon and others added 3 commits March 13, 2026 12:22
…olve.ts

Align parseDashboardPositionalArgs with the established codebase pattern
used in event/view, trace/view, trace/logs, and dashboard/create.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add three widget management subcommands under `sentry dashboard widget`:
- `widget add` — add a widget with inline --display/--query/--where/--group-by flags
- `widget edit` — edit an existing widget by --index or --title, merging only changed fields
- `widget delete` — remove a widget by --index or --title

All widget commands use GET-modify-PUT pattern with server field stripping
and auto-layout for new widgets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…o shared module

- Move resolveWidgetIndex from edit.ts to resolve.ts (shared by edit + delete)
- Extract validateWidgetEnums to resolve.ts (shared by add + edit)
- Remove inline enum validation from add.ts and edit.ts
- Remove inline widget-index logic from delete.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@betegon betegon force-pushed the feat/dashboard-widget-commands branch from 1f3a700 to 0d179ef Compare March 13, 2026 11:22
@BYK BYK force-pushed the feat/dashboard-commands-core branch from f13bbd3 to 67b80d5 Compare March 17, 2026 12:21
Base automatically changed from feat/dashboard-commands-core to main March 17, 2026 14:41
betegon and others added 2 commits March 18, 2026 11:09
# Conflicts:
#	plugins/sentry-cli/skills/sentry-cli/SKILL.md
#	src/app.ts
#	src/commands/dashboard/create.ts
#	src/commands/dashboard/index.ts
#	src/commands/dashboard/list.ts
#	src/commands/dashboard/resolve.ts
#	src/commands/dashboard/view.ts
#	src/lib/api-client.ts
#	src/lib/api/dashboards.ts
#	src/lib/formatters/human.ts
#	src/types/dashboard.ts
#	test/commands/dashboard/create.test.ts
#	test/commands/dashboard/list.test.ts
#	test/types/dashboard.test.ts
@betegon betegon marked this pull request as ready for review March 18, 2026 10:24
betegon and others added 4 commits March 18, 2026 12:17
- Extract shared buildWidgetFromFlags() into resolve.ts (DRY create + add)
- Add return { hint } with dashboard URL to all dashboard commands
- Move enum validation before API calls in widget add (fail fast)
- Use proper DashboardDetail/DashboardWidget types in widget formatters
- Wrap getDashboard in withProgress for loading spinner in view command

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two bugs prevented dashboard widget commands from working correctly:

1. stripWidgetServerFields used a denylist that missed dozens of extra
   fields returned by the GET API via Zod .passthrough() (description,
   thresholds, interval, axisRange, datasetSource, etc.). Switch to an
   allowlist that only includes fields the PUT API accepts.

2. updateDashboard PUT succeeded but subsequent dashboard view returned
   stale cached GET data. Add invalidateCachedResponse() to response-cache
   and call it after every dashboard PUT with the correct URL format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Sentry events API silently resolves function aliases (spm→epm,
sps→eps, tpm→epm, tps→eps), but the dashboard widget UI only renders
canonical functions from AggregationKey. Widgets created with aliases
showed "None" in the visualize field.

- Add AGGREGATE_ALIASES map and resolve in parseAggregate()
- Remove aliases from SPAN/DISCOVER_AGGREGATE_FUNCTIONS lists
- Add validateAggregateNames() to reject unknown functions early
- Call validation in buildWidgetFromFlags() before API calls
- Update JSDoc to link canonical source files on GitHub

Source: https://github.com/getsentry/sentry/blob/master/static/app/utils/fields/index.ts
Aliases: https://github.com/getsentry/sentry/blob/master/src/sentry/search/events/constants.py

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Chart widgets (line, area, bar) with --group-by and --limit but no
--sort created broken widgets — the dashboard UI couldn't determine
which top N groups to display. Auto-default orderby to first aggregate
descending (e.g. -count()) when group-by is present and no explicit
sort is provided. This matches the Sentry UI's default behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Fix stale `fields` in mergeQueries — destructure to omit existing
   `fields` so prepareWidgetQueries recomputes from updated aggregates/
   columns (bugbot comment #1)

2. Preserve widgetType without silent default — only set widgetType if
   explicitly provided via --dataset or already on the existing widget,
   preventing parseWidgetInput from defaulting to "spans" (bugbot #4)

3. Resolve aliases in paren passthrough — `spm()` now correctly resolves
   to `epm()` instead of being rejected by validation (bugbot #3)

Comment #2 (drops interval/thresholds) is not a real issue — these fields
are already stripped by the allowlist in stripWidgetServerFields before
buildReplacement sees them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…-commands

# Conflicts:
#	plugins/sentry-cli/skills/sentry-cli/SKILL.md
The edit command's mergeQueries called parseAggregate but skipped
validateAggregateNames, allowing invalid aggregates like --query bogus
to pass CLI validation. Add validation in buildReplacement after
merging queries, matching the add/create path behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When --dataset is provided alongside --query, validate aggregates
against the new dataset, not the existing widget's widgetType.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…/--widget-title

Previously, passing --widget-query, --widget-dataset, etc. without
--widget-display or --widget-title silently created a dashboard with
no widgets. Now throws a ValidationError explaining both flags are
required.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…query

Changing --dataset (e.g. discover→spans) without --query skipped
aggregate validation, allowing dataset-incompatible aggregates like
failure_rate() to be sent with widgetType: "spans". Now validates
existing aggregates against the new dataset too.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
resolveWidgetIndex used strict === for title matching while
resolveDashboardId used case-insensitive .toLowerCase(). Now
both use case-insensitive matching for consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
betegon and others added 3 commits March 18, 2026 20:07
…JSON mode

- Add validateWidgetEnums() call in create command before buildInlineWidget
  for friendly error messages on invalid --widget-display/--widget-dataset
- Pass json: flags.json to withProgress in view command to suppress spinner
  ANSI escape codes when --json is used (matches list command pattern)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ands

Add 18 tests across 3 files covering:
- widget add: API args, JSON/human output, validation (title, display, aggregate), auto-default orderby
- widget edit: edit by index/title (case-insensitive), validation (index/title required, invalid aggregate, dataset change)
- widget delete: delete by index/title, validation (index/title required, out-of-range), human output

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ates

prepareDashboardForUpdate only returned title, widgets, and projects,
silently dropping dashboard-level environment and period filters during
widget add/edit/delete operations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@betegon betegon requested a review from BYK March 19, 2026 12:57
Copy link
Member

@BYK BYK left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did a very cursory look. Also seems to be missing the SKILL updates for some reason?

betegon and others added 2 commits March 20, 2026 17:09
…review comments

- Remove --widget-* flags from `dashboard create` (use `dashboard widget add` instead)
- Fix `prepareWidgetQueries` empty fields recomputation (cleanQuery sets [] which is truthy)
- Add max positional args validation in `widget add`
- Extract shared `WidgetQueryFlags` type in resolve.ts for add/edit commands
- Update dashboards.md with widget add/edit/delete subcommand docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

…y type

The body parameter type was missing environment and period fields that
prepareDashboardForUpdate includes, making the type signature inaccurate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@betegon betegon merged commit 383a98f into main Mar 20, 2026
22 checks passed
@betegon betegon deleted the feat/dashboard-widget-commands branch March 20, 2026 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants