Skip to content

feat(slack): Unfurl spans dashboard widget URLs in slack#113478

Merged
DominikB2014 merged 5 commits intomasterfrom
dominikbuszowiecki/dain-1479-unfurl-spans-dashboard-widget-urls-in-slack
Apr 21, 2026
Merged

feat(slack): Unfurl spans dashboard widget URLs in slack#113478
DominikB2014 merged 5 commits intomasterfrom
dominikbuszowiecki/dain-1479-unfurl-spans-dashboard-widget-urls-in-slack

Conversation

@DominikB2014
Copy link
Copy Markdown
Contributor

Add a Slack unfurl handler for dashboard widget URLs of the form
/organizations/{org}/dashboard/{dashboard_id}/widget/{widget_index}/
(plus the customer-domain equivalent). The handler looks up the widget
by its positional index within the dashboard (matching the frontend's
id-ordered array lookup), builds an events-timeseries request per
widget query, and renders a chartcuterie SLACK_TIMESERIES chart with
the widget title.

Only spans widgets are supported for this initial rollout to keep the
mapping from widget → timeseries request simple; other widget types
(errors, transactions, logs, tracemetrics, etc.) are skipped. Widgets
with multiple queries fan out to multiple timeseries calls and the
series are joined into a single chart, matching how the dashboard
renders them.

Behavior mirrors the existing explore unfurl (unfurl/explore.py):
same chart backend, same SlackDiscoverMessageBuilder shape, same
default statsPeriod, same topEvents/default-sort handling when a
groupBy is present. The dashboards handler adds a new
Referrer.DASHBOARDS_SLACK_UNFURL value and registers it in
SENTRY_BACKEND_REFERRERS so events-timeseries accepts the call.

Gated behind a new organizations:dashboards-widget-unfurl FlagPole
flag. Non-spans widgets, non-timeseries display types (table, big
number, text, …), and unknown widgets are all no-ops.

Refs DAIN-1479

Add a Slack unfurl handler for dashboard widget URLs of the form
/organizations/{org}/dashboard/{dashboard_id}/widget/{widget_index}/
(and the customer-domain equivalent). The handler looks up the widget,
builds an events-timeseries request per widget query, and renders a
chartcuterie SLACK_TIMESERIES chart with the widget title.

Only spans widgets are supported for now to keep the initial rollout
simple; other widget types are skipped. Widgets with multiple queries
fan out to multiple timeseries calls and the series are joined into a
single chart, matching how the dashboard renders them.

Gated behind the organizations:dashboards-widget-unfurl flag.

Refs DAIN-1479
Co-Authored-By: Claude <noreply@anthropic.com>
@linear-code
Copy link
Copy Markdown

linear-code Bot commented Apr 20, 2026

@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Apr 20, 2026
Copy link
Copy Markdown
Contributor

@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.

Reviewed by Cursor Bugbot for commit 96cee3e. Configure here.

Comment thread src/sentry/integrations/slack/unfurl/dashboards.py
…lue keys

The internal API client iterates params.items() and relies on
isinstance(value, list) to propagate multi-valued params via setlist.
A QueryDict's items() yields only the last value per key, so widgets
with multiple aggregates or groupBy columns were being silently
truncated to a single value on the way to events-timeseries.

Return a plain dict[str, str | list[str]] from _build_timeseries_params
so the client sees lists and forwards them correctly. Also tighten the
handler types: typed args via a DashboardsUnfurlArgs TypedDict, typed
combined_time_series via the existing TimeSeries TypedDict, and drop
the dead -1 sentinel path now that the regex guarantees valid input.

Co-Authored-By: Claude <noreply@anthropic.com>
mypy flagged the new tests for passing match_link's Optional return
values straight to UnfurlableUrl and link_handlers[]. Add an assertion
after each unpack to narrow link_type and args from Optional.

Co-Authored-By: Claude <noreply@anthropic.com>
@DominikB2014 DominikB2014 marked this pull request as ready for review April 21, 2026 15:28
@DominikB2014 DominikB2014 requested review from a team as code owners April 21, 2026 15:28
Comment thread src/sentry/integrations/slack/unfurl/dashboards.py
Also flip api_expose to False — the unfurl check runs only on the backend.

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread src/sentry/integrations/slack/unfurl/dashboards.py
Copy link
Copy Markdown
Contributor

@edwardgou-sentry edwardgou-sentry left a comment

Choose a reason for hiding this comment

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

I think the changes look fine to me.

Question though: would it make sense to have the dashboards unfurl integration reuse parts of existing integrations? ie for this pr we only have a code path for spans, should we use _unfurl_explore somehow or consolidate similar parts of the code since it's basically the same dataset and endpoint (/events-timeseries/)? It seems like the major difference is the source for the query parameters (url vs DashboardWidgetQuery), but everything else seems to mostly behave the same. This might also be cleaner once we start adding more support for other datasets within the dashboard unfurl integration too.

@DominikB2014
Copy link
Copy Markdown
Contributor Author

I think the changes look fine to me.

Question though: would it make sense to have the dashboards unfurl integration reuse parts of existing integrations? ie for this pr we only have a code path for spans, should we use _unfurl_explore somehow or consolidate similar parts of the code since it's basically the same dataset and endpoint (/events-timeseries/)? It seems like the major difference is the source for the query parameters (url vs DashboardWidgetQuery), but everything else seems to mostly behave the same. This might also be cleaner once we start adding more support for other datasets within the dashboard unfurl integration too.

@edwardgou-sentry 100% valid point. I was planning to wait until we add support for other datasets before consolidating (like you mentioned at the end), but yes, there's a lot of similarity and we can likely reuse more things.

@DominikB2014 DominikB2014 merged commit 2906d42 into master Apr 21, 2026
77 checks passed
@DominikB2014 DominikB2014 deleted the dominikbuszowiecki/dain-1479-unfurl-spans-dashboard-widget-urls-in-slack branch April 21, 2026 17:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants