Report Schedule Dashboard Integration#
A ReportSchedule can target either a chart or a dashboard. When targeting a dashboard, the optional extra.dashboard field carries a DashboardPermalinkState snapshot — native filter values, active tabs, URL parameters, and an anchor — that the execution engine uses to render the dashboard in a specific state before screenshotting it.
This behavior is gated by the ALERT_REPORT_TABS feature flag . Without the flag, the report always renders the full dashboard at its default state.
The extra.dashboard Field#
ReportSchedule.extra_json stores a ReportScheduleExtra TypedDict with a single key dashboard of type DashboardPermalinkState:
| Field | Type | Purpose |
|---|---|---|
dataMask | Optional[dict] | Native filter state (applied data masks per filter ID) |
activeTabs | Optional[list[str]] | Active dashboard tab IDs |
anchor | Optional[str] | Single tab/component ID, or a JSON-encoded list of tab IDs for multi-tab screenshots |
urlParams | Optional[list[tuple]] | URL parameters to include |
The extra field is declared as a plain fields.Dict in both ReportSchedulePostSchema and ReportSchedulePutSchema — structure enforcement happens in the command layer, not the schema layer.
Permalink State Creation at Execution#
When a dashboard report executes, BaseReportState._get_url() decides whether to use a stateful permalink or a plain dashboard URL:
- If
extra.dashboardis set andALERT_REPORT_TABSis enabled, it calls_get_tab_url(dashboard_state). - Otherwise, it falls back to a plain
Superset.dashboardURL using the dashboard's UUID .
_get_tab_url() invokes CreateDashboardPermalinkCommand with the dashboard's UUID and the full DashboardPermalinkState, stores the state in the key-value store, and returns the /superset/dashboard/p/<key>/ permalink URL . The webdriver then loads this URL and the frontend restores all filter and tab state before taking the screenshot.
Multi-tab screenshots: When anchor is a JSON-encoded list of tab IDs, _get_tabs_urls() generates a separate permalink per tab — one screenshot per tab. Each per-tab permalink is constructed with dataMask, activeTabs, and urlParams all set to None, only the anchor varies .
get_dashboard_urls() is the entry point for _get_screenshots() when the report targets a dashboard .
extra.dashboard set + ALERT_REPORT_TABS enabled
└─ anchor is JSON list? → _get_tabs_urls() → one permalink per tab
└─ otherwise → _get_tab_url() → single permalink
extra.dashboard absent OR flag disabled
└─ plain dashboard URL (no permalink created)
Validation on Create and Update#
Tab/anchor validation — CreateReportScheduleCommand._validate_report_extra() checks that every ID in activeTabs and anchor exists as a key in the dashboard's position_json. A JSON-encoded anchor list is parsed and each entry validated individually; a plain string anchor is checked directly against position_data . Invalid IDs produce a 422 ValidationError on the extra field.
Native filter validation — PR #38715 added _validate_native_filters() to the base command, enforcing that each object in extra.dashboard.nativeFilters:
- Contains all required keys:
nativeFilterId,filterType,columnName,filterValues - Has
filterValuesas a list - References a
nativeFilterIdthat actually exists on the target dashboard
Previously, arbitrary native filter data was accepted at creation time and only failed at execution . The same PR moved _validate_report_extra() to BaseReportScheduleCommand so these checks run on both create and update.
Automatic Report Deactivation on Dashboard Changes#
UpdateDashboardCommand proactively deactivates reports when the dashboard elements they reference are removed.
Tab deletion — process_tab_diff() compares the new position_json against the current tab configuration, finds reports referencing deleted tab IDs via ReportScheduleDAO.find_by_extra_metadata(tab), sets each report to active=False, and sends a deactivation email to all report owners .
Native filter deletion — Added in PR #38715, process_native_filter_diff() diffs json_metadata.native_filter_configuration before and after the update to detect removed filter IDs. Affected reports are found via ReportScheduleDAO.find_by_native_filter_id() (which searches extra_json for the filter ID with SQL LIKE and autoescape=True), then deactivated and owners notified via _send_deactivated_report_email() — a shared helper reused for both tab and filter deactivation flows .
Key Files#
| File | Role |
|---|---|
superset/commands/report/execute.py | _get_url(), _get_tab_url(), _get_tabs_urls(), get_dashboard_urls() — permalink creation at execution time |
superset/commands/report/create.py | _validate_report_extra() — tab/anchor validation on report creation |
superset/commands/report/base.py | BaseReportScheduleCommand — shared validation including native filter checks (added in PR #38715) |
superset/reports/types.py | ReportScheduleExtra TypedDict |
superset/dashboards/permalink/types.py | DashboardPermalinkState, DashboardPermalinkValue TypedDicts |
superset/commands/dashboard/permalink/create.py | CreateDashboardPermalinkCommand — called by the execution engine to persist dashboard state |
superset/commands/dashboard/update.py | process_tab_diff(), process_native_filter_diff() — report deactivation on dashboard changes |
superset/reports/schemas.py | ReportSchedulePostSchema / ReportSchedulePutSchema — extra field declared as untyped Dict |