Compare commits

...
Author SHA1 Message Date
hainenber 74f57cb0f2 chore(ci): group related Dependabot PRs based on their common namespaces
Signed-off-by: hainenber <dotronghai96@gmail.com>
2026-08-29 13:30:06 +07:00
hainenber 5d399aeeca feat(ci): use native Dependabot semver label auto-assignment to indicate patch-level for auto-approval
Signed-off-by: hainenber <dotronghai96@gmail.com>
2026-08-29 13:29:40 +07:00
PRATHAMESH HUKKERIandPrathamesh Hukkeri 1c8d58a77b fix(i18n): make language pack endpoint public for embedded dashboards (#42438)
Co-authored-by: Prathamesh Hukkeri <prathamesh04@users.noreply.github.com>
2026-08-28 20:46:27 -07:00
Mallikarjuna Reddy Nimmakayala de33efae98 fix(table-chart): support where for adhoc columns with server-side pagination (#42706) 2026-08-28 20:37:20 -07:00
Sepuri Sai Krishna b30d569028 fix(chart): map the remaining time grains for Prophet forecasting (#43205) 2026-08-28 20:35:52 -07:00
6f69fc6dba feat(postprocessing): teach pivot() to compute percent-of-row/col/total (#42809) (#42976)
Co-authored-by: Evan Rusackas <evan@preset.io>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-28 20:34:15 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>hainenber
b2a8842551 chore(deps-dev): bump the swc group in /superset-frontend with 3 updates (#43447)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: hainenber <dotronghai96@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: hainenber <dotronghai96@gmail.com>
2026-08-28 20:30:12 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Evan RusackasSuperset Dev
c7cc22f4fe chore(deps): bump setuptools to 84.0.0 and pip to 26.2.1 (#43413)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Evan Rusackas <evan@rusackas.com>
Co-authored-by: Superset Dev <dev@superset.apache.org>
2026-08-28 18:34:09 -07:00
Francesco.CastaldiandFrancescoCastaldi 058eaf78ab fix(dashboard): remove @has_access_api from filter state REST API (#43564)
Co-authored-by: FrancescoCastaldi <francesco.castaldi@mapsgroup.it>
2026-08-28 17:06:06 -07:00
Elizabeth Thompson 30402b412c fix(explore): catch TemplateError when validating access for query-backed form_data (#43470) 2026-08-28 15:03:08 -07:00
Mike BridgeandClaude Fable 5 782a57fbfc chore(deps-dev): bump nwsapi from 2.2.23 to 2.2.24 to fix the SqlEditor jest flake (#43657)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 17:59:35 -04:00
ʈᵃᵢ 99a1f756cd fix(trino): coerce string-typed values results back to source type (#43654) 2026-08-28 14:25:42 -07:00
Mehmet Salih Yavuz 0eda633b45 fix(dataset): stop a stale edit modal from silently reverting a saved change (#43583) 2026-08-28 21:07:36 +03:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>hainenber
5c24f72d92 chore(deps): bump content-disposition from 2.0.1 to 3.0.0 in /superset-frontend (#43382)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: hainenber <dotronghai96@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: hainenber <dotronghai96@gmail.com>
2026-08-29 00:53:43 +07:00
Gabriel Torres Ruiz 60479fb958 feat(dashboard): add dashboard.slice.header.menu extension slot (#43624)
Signed-off-by: Gabriel Torres Ruiz <gabo2595@gmail.com>
2026-08-28 14:52:18 -03:00
Ville Brofeldt 4ced5ca35a chore(superset-core): drop unused __all__ lists (#43626) 2026-08-28 09:46:56 -07:00
rlei fc26991cd4 feat(plugin-chart-echarts): add gridline and axis tick controls (#43428) 2026-08-28 09:44:39 -07:00
shauryaandShaurya a5c68c8df9 fix(number-format): handle sub-byte values and unit rollover in memory formatter (#43549)
Co-authored-by: Shaurya <19599684+no-hup@users.noreply.github.com>
2026-08-28 09:44:19 -07:00
Đỗ Trọng Hải 53e76afd70 feat(ci): enforce min release age for npm dep installation (#43164)
Signed-off-by: hainenber <dotronghai96@gmail.com>
2026-08-28 23:42:41 +07:00
shauryaandShaurya d997d363e3 docs: update frontend Node/npm prerequisites to match engines (#43546)
Co-authored-by: Shaurya <19599684+no-hup@users.noreply.github.com>
2026-08-28 23:16:34 +07:00
Lalith Kothuru 9a6f6ee0c0 docs: fix docstring parameter names that do not match signatures (#43630) 2026-08-28 22:57:43 +07:00
Joe Li 94dd3d049c fix(ci): repair scheduled pre-commit drift (#43603) 2026-08-28 22:56:18 +07:00
b3f718da62 fix(explore): keep certification badges after saving or swapping a dataset (#43319)
Co-authored-by: rusackas <evan@rusackas.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-28 08:47:36 -07:00
Mehmet Salih YavuzandEnzo Martellucci abf338d611 feat(filters): search filter values server-side in Explore (#43518)
Co-authored-by: Enzo Martellucci <52219496+EnxDev@users.noreply.github.com>
2026-08-28 16:46:09 +03:00
Mehmet Salih Yavuz e518b21994 fix(dashboard): keep the chart menu usable in fullscreen on production builds (#43555) 2026-08-28 15:47:48 +03:00
Mehmet Salih Yavuz e18f27e1ce fix(native-filters): allow clearing an optional "select first value" filter (#43405) 2026-08-28 15:46:46 +03:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 9bf457dea6 chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 (#43643)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-28 15:26:18 +03:00
Enzo Martellucci cb7b790733 fix(dataset): surface real validation message in save-error dialog (#43459) 2026-08-28 13:35:09 +02:00
Alexandru Soare 8bec85158c feat(modals): add renderExtraFields prop to chart and dashboard properties modals (#43622) 2026-08-28 13:18:51 +03:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a30e4a4350 chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 (#43644)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-28 02:39:11 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 933dbbc2a2 chore(deps-dev): bump eslint-plugin-react-you-might-not-need-an-effect from 1.0.1 to 1.0.2 in /superset-frontend (#43645)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-28 02:39:06 -07:00
Enzo Martellucci a59b96c4f5 fix(explore): keep the row count label visible when the row limit is reached (#43296) 2026-08-28 10:51:27 +02:00
MafiandMatt Fitzgerald 12cd259c55 fix(dataset): preserve validation error messages (#43631)
Co-authored-by: Matt Fitzgerald <matt.fitzgerald@preset.io>
2026-08-28 01:01:17 -04:00
Chandan P 3ddc3b1d56 fix(cache): fall back to default timeout when cache_timeout is None (#43149) 2026-08-27 21:50:53 -07:00
Chandan P 98ec6018df fix(embedded): honor "can view query" permission for guest users (#43151) 2026-08-27 21:49:52 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Joe Li
2ebd415b8a chore(deps-dev): bump mcp from 1.29.1 to 2.0.0 (#43611)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Joe Li <joe@preset.io>
2026-08-27 21:49:50 -07:00
81b3e85522 fix(echarts): guard cross-filter when labelMap entry is missing (#42559)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: rusackas <evan@rusackas.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 21:49:21 -07:00
Viktor Högberg fd64efd72d fix(uploads): set catalog on datasets created by file upload (#43301) 2026-08-27 21:46:51 -07:00
bucketbase26andJoe Li e39bfb255b fix(explore): prevent duplicate Date Range tooltips (#43425)
Co-authored-by: Joe Li <joe@preset.io>
2026-08-27 21:02:57 -07:00
b7301ac88a fix: Dashboard export with charts from multiple databases (#37120)
Co-authored-by: rusackas <evan@rusackas.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 20:27:54 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> fa59b44cfe chore(deps): bump mapbox-gl from 3.28.1 to 3.29.0 in /superset-frontend (#43619)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-27 19:58:55 -07:00
8c5be889d9 fix(cockroachdb): replace abandoned cockroachdb package with sqlalchemy-cockroachdb (#43501)
Co-authored-by: Superset Dev <dev@superset.apache.org>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 19:48:13 -07:00
Evan RusackasandClaude b733b57e9e docs(mcp): document chart type plugin filtering config (#43597)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-27 19:47:07 -07:00
Joe Li 13e97ba913 fix(sql-lab): fix transparent background in ag-grid native menus (#43368) 2026-08-27 16:56:36 -07:00
Evan RusackasandClaude Sonnet 5 f9e43a37d8 docs(theming): document THEME_DEFAULT_MODE config setting (#43601)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 16:19:40 -07:00
Evan RusackasandClaude 5ff44c5ed6 docs(cli): document import_directory --username option (#43602)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-27 16:19:25 -07:00
Elizabeth ThompsonandClaude Opus 4.8 7b145a520c fix(tags): catch TemplateError when validating access for tagged SQL Lab queries (#43423)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 15:06:46 -07:00
Elizabeth Thompson 9f62aace7e fix(examples): replace deprecated granularity_sqla with granularity (#43432) 2026-08-27 15:06:42 -07:00
Elizabeth ThompsonandClaude Opus 4.8 20be48c085 fix(explore): catch TemplateError when validating access for permalinked query datasources (#43605)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 15:06:38 -07:00
Aurimas Navardauskas e1c7a51674 fix(mcp): flag failed tool calls with isError (#43374) 2026-08-27 14:59:19 -07:00
Gabriel Torres Ruiz ecbd6578b0 fix(db_engine_specs): always mask OAuth2 client secret in encrypted extra (#43491) 2026-08-27 18:42:51 -03:00
Amin Ghadersohi ea3206b076 fix(mcp): surface rejected filter columns in get_chart_data (#43598) 2026-08-27 17:36:50 -04:00
Amin Ghadersohi 478f612f25 fix(mcp): add open-world tool annotations (#43529) 2026-08-27 17:36:30 -04:00
Amin Ghadersohi a16bc0d94f fix(mcp): apply time grain overrides from extra_form_data (#43599) 2026-08-27 17:35:23 -04:00
Amin GhadersohiandClaude bcc6af6c5f fix(dao): don't mask transient OperationalError as a "not found" result (#43479)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-27 17:34:53 -04:00
Evan RusackasandClaude 94b59420b7 docs(embedding): document EMBEDDED_DISABLE_PERMALINK_ORIGIN_REWRITE (#43600)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-27 13:15:08 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Joe Li
6814a89230 chore(deps): bump gunicorn from 26.0.0 to 26.2.0 (#43609)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Joe Li <joe@preset.io>
2026-08-27 13:08:37 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ebbe2e8714 chore(deps): bump the storybook group in /docs with 2 updates (#43610)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-27 13:08:32 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
b8acf4b207 chore(deps-dev): bump syntaqlite from 0.7.1 to 0.9.0 (#43612)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-27 13:08:28 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Joe Li
ba5d5091b9 chore(deps): bump holidays from 0.102 to 0.103 (#43614)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Joe Li <joe@preset.io>
2026-08-27 13:08:24 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 8c88c0d3ac chore(deps-dev): update teradatasql requirement from >=20.0.0.65 to >=20.0.0.66 (#43615)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-27 13:08:18 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
e9e652d22d chore(deps-dev): bump trino from 0.338.0 to 0.339.0 (#43616)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-27 13:08:14 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
8f82d851cf chore(deps-dev): bump tiktoken from 0.13.0 to 0.14.0 (#43617)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-27 13:08:09 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 83934c038e chore(deps-dev): bump the storybook group in /superset-frontend with 5 updates (#43618)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-27 13:08:05 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 35fc36a97e chore(deps-dev): bump baseline-browser-mapping from 2.11.15 to 2.11.16 in /superset-frontend (#43621)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-27 13:08:00 -07:00
Evan RusackasandClaude Opus 4.8 88c4e689e3 fix(pivot-table): keep D3_FORMAT-styled numbers intact in pivoted Excel export (#42601)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 12:55:57 -07:00
Evan RusackasandClaude Sonnet 5 29f4a05eb7 docs(security): document the RLS indicator badge on dataset list/Explore (#43591)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 12:54:21 -07:00
Evan RusackasandClaude 323741b043 docs(alerts-reports): document report/alert content format options (#43594)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-27 12:53:31 -07:00
Evan RusackasandClaude 5c3c6362ae docs(cache): document NATIVE_FILTER_OPTIONS_CACHE_TIMEOUT (#43592)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-27 12:53:27 -07:00
Evan RusackasandClaude c1b6ff340b docs(mcp): document the update_dataset_metric tool (#43596)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-27 12:53:19 -07:00
Evan RusackasandClaude 05830ebc77 docs: document PNG/PDF chart export options (#43593)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-27 12:53:09 -07:00
Evan RusackasandClaude Sonnet 5 74dd690102 docs(security): document EXTRA_EDITORS_RESOLVER (#43595)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 12:52:56 -07:00
Joe Li 22027ec24c fix(explore): require a value for adhoc filters before save (#43317) 2026-08-27 12:17:46 -07:00
a3072c0bf6 fix(deckgl): give handlebars tooltips an opaque background (#43195)
Co-authored-by: bikashJMV <bikash@jmv.co.in>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 12:17:32 -07:00
6d8cbbfe8d fix(api): stop export downloads inheriting the one-year Cache-Control (#43185)
Co-authored-by: bikashJMV <bikash@jmv.co.in>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-08-27 12:17:02 -07:00
Sepuri Sai KrishnaandEvan Rusackas 926e0e6a92 fix(chart-data): validate select()'s exclude option instead of raising 500s (#42410)
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-08-27 12:14:33 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> d013bc7a21 chore(deps): bump baseline-browser-mapping from 2.11.15 to 2.11.16 in /docs (#43613)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-27 12:11:32 -07:00
87c48cc473 feat(echarts): make bar chart label position user-configurable (#38695)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Enzo Martellucci <52219496+EnxDev@users.noreply.github.com>
2026-08-27 12:10:38 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> c2266f860c chore(deps-dev): update clickhouse-connect requirement from <2.0,>=1.7.1 to >=1.7.2,<2.0 (#43608)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-27 12:06:41 -07:00
Viktor Högberg e8aea6b6df fix: remove redundant 'tooltip' tooltip in scoping modal (#43455) 2026-08-27 11:10:26 -07:00
Evan Rusackas bde1c483b0 chore(deps): group Dependabot security-update PRs per directory (#43604) 2026-08-27 11:08:21 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>hainenber
ff1d9f9e09 chore(deps): bump dawidd6/action-download-artifact from 21 to 24 (#43441)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: hainenber <dotronghai96@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: hainenber <dotronghai96@gmail.com>
2026-08-27 10:24:38 -07:00
Amogh AtreyaandEvan Rusackas 76151beade fix(export): truncate exported filenames to prevent Windows path extraction errors (#42531) (#42541)
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-08-27 08:48:58 -07:00
Amin GhadersohiandClaude 905a35d05f fix(mcp): resolve dashboard permalinks (#43482)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-27 11:35:36 -04:00
Mehmet Salih Yavuz fde0fbd315 fix(ci): unblock lint-frontend after the oxlint 1.79.0 bump (#43587) 2026-08-27 17:32:04 +03:00
Endi Monan 5879994e68 fix(charts): resolve export filenames from the chart, not the form data (#43280) 2026-08-27 08:31:32 -03:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> d1b7a4fdb4 chore(deps): bump dompurify from 3.4.13 to 3.4.14 in /superset-frontend (#43579)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-27 03:43:02 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 5ea7d05d98 chore(deps): bump immer from 11.1.17 to 11.1.18 in /superset-frontend (#43581)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-27 03:42:57 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 0ab64e305c chore(deps-dev): bump lerna from 10.0.0 to 10.0.1 in /superset-frontend (#43580)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-27 03:33:14 -07:00
Evan RusackasandClaude Opus 4.8 7d355a254e fix(Modal): stop the draggable modal from hijacking text selection (#43498)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 03:30:23 -07:00
Evan RusackasandClaude Opus 4.8 ca63bb532d fix(plugin-chart-table): preserve comparison arrow when a column-specific formatter entry is missing (#43494)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 03:30:17 -07:00
b71293acde fix(sqllab): stop a database with no extra from breaking SET_DATABASES (#43216)
Co-authored-by: bikashJMV <bikash@jmv.co.in>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Evan Rusackas <evan@rusackas.com>
2026-08-26 20:51:08 -07:00
0e172a6ff3 feat(waterfall): add show/hide toggles for X and Y axes (#42371)
Co-authored-by: Greg Neighbors <gregneighbors@Gregs-MacBook-Air-2.local>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Greg Neighbors <gregneighbors@Gregs-Air-2.lan>
2026-08-26 20:29:26 -07:00
rlei 1c66b71e23 feat(plugin-chart-echarts): allow decal patterns in custom ECharts options (#43427) 2026-08-26 18:01:52 -07:00
Alejandro Solares 6b0aa8714d chore(deps): raise Pillow/PyJWT floors to match resolved pins (#43519) 2026-08-26 15:55:14 -07:00
Vitor Avila 371e5e25e8 fix(trino): honor the verify arg for the user impersonation flow (#43534) 2026-08-26 19:44:30 -03:00
Luiz OtavioandClaude Opus 5 a140e74f5f fix(excel): handle duplicate column labels in xlsx export (#43561)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 16:39:03 -03:00
Amin Ghadersohi b8308a44a3 fix(pivot-table): exclude rollup totals from conditional formatting scale (#43481) 2026-08-26 15:08:07 -04:00
Amin Ghadersohi 13927f27e2 fix(mcp): raise SDK floor (#43530) 2026-08-26 15:04:59 -04:00
Amin Ghadersohi 434511cb37 fix(mcp): reject unsafe dashboard layout replacements (#43476) 2026-08-26 14:28:26 -04:00
JUST.in DO ITandClaude Sonnet 5 70c9203aa9 fix(sqllab): release DB connection before fetching query results from backend (#43371)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 10:37:42 -07:00
Alexandru Soare 455e6603c1 feat(reports): gate retry functionality behind ALERT_REPORTS_RETRY flag (#43553) 2026-08-26 09:18:11 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 0597f36bb6 chore(deps-dev): bump oxlint from 1.78.0 to 1.79.0 in /superset-frontend (#43543)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-26 08:20:39 -07:00
Rafael BenitezandClaude Opus 4.8 e7ca8b8a57 fix(chart): render ECharts charts at full resolution in download-as-image (#43456)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-26 06:22:23 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 686245a905 chore(deps-dev): bump oxfmt from 0.63.0 to 0.64.0 in /superset-websocket (#43537)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-26 02:17:00 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 5a4900abb1 chore(deps-dev): bump vitest from 4.1.10 to 4.1.11 in /superset-websocket (#43538)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-26 02:16:56 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 450e2edc2b chore(deps): bump @swc/core from 1.16.0 to 1.16.1 in /docs (#43539)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-26 02:16:53 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 2bc96a47a4 chore(deps): bump swagger-ui-react from 5.32.13 to 5.32.14 in /docs (#43540)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-26 02:16:49 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 622bc45c9e chore(deps-dev): bump @swc/core from 1.16.0 to 1.16.1 in /superset-frontend (#43541)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-26 02:16:45 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e97db01482 chore(deps-dev): bump oxfmt from 0.63.0 to 0.64.0 in /superset-frontend (#43542)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-26 02:16:40 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 6194c852f1 chore(deps-dev): bump oxfmt from 0.63.0 to 0.64.0 in /docs (#43544)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-26 02:16:36 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 19eca6523f chore(deps): bump uuid from 14.0.1 to 14.0.2 in /superset-frontend (#43545)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-26 02:16:31 -07:00
Amin GhadersohiandClaude 1fd763bd29 fix(mcp): honor and validate chart filters (#43478)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-25 20:54:09 -07:00
b4f7114a09 fix(semantic-layer): warn that deleting a layer cascade-deletes its dependent views (#42845)
Co-authored-by: Mike Bridge <michael.bridge@ext.preset.io>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 20:47:45 -07:00
Vitor Avila 2dfe8a5bd9 fix(OAuth2): Support creating OAuth2 connections via SQLAlchemy URI (#43489) 2026-08-26 00:07:19 -03:00
62d74be0af fix(metadb): apply SUPERSET_META_DB_LIMIT after join instead of per-table (#36304) (#42598)
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Joe Li <joe@preset.io>
2026-08-25 19:31:11 -07:00
Shivam Goel 473c318335 chore(superset-core): add __init__.py to semantic_layers (#43528) 2026-08-25 18:07:28 -07:00
Evan RusackasandSuperset Dev 61ab0cdb5d fix(ocient): update GIS test fixtures for pyocient's relocated geo types (#43496)
Co-authored-by: Superset Dev <dev@superset.apache.org>
2026-08-25 17:18:02 -07:00
e6b9205821 fix(country-map): give Alborz its own ISO code instead of reusing Tehran's (#42429)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Joe Li <joe@preset.io>
Co-authored-by: Đỗ Trọng Hải <41283691+hainenber@users.noreply.github.com>
2026-08-25 16:59:48 -07:00
0dcb2ca53a feat(maps): Add Italy regions and autonomous provinces country map (#42309)
Co-authored-by: lum4chi <francesco.lumachi@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Joe Li <joe@preset.io>
2026-08-25 15:45:31 -07:00
BexultanandBexultan Mustafin 649d6f1b41 fix(mcp): defer unknown numeric types to compile (#43131)
Co-authored-by: Bexultan Mustafin <bexultan.mustafin@ffins.kz>
2026-08-25 15:35:00 -07:00
BexultanandBexultan Mustafin 90dab7cf61 fix(mcp): accept common chart input variants (#43130)
Co-authored-by: Bexultan Mustafin <bexultan.mustafin@ffins.kz>
2026-08-25 15:22:52 -07:00
Sepuri Sai KrishnaandJoe Li 68386a53ee chore(database): remove dead extra validation exception classes (#42411)
Co-authored-by: Joe Li <joe@preset.io>
2026-08-25 15:14:16 -07:00
b89da3e9fc docs(versioning): fix post-flip doc and comment drift (#43493)
Co-authored-by: Mike Bridge <michael.bridge@ext.preset.io>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 13:00:25 -07:00
Mike BridgeandClaude Fable 5 88d2c2954e feat(deletion-retention): persist purge block reason codes on the audit log (#43485)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 12:46:55 -07:00
Amin Ghadersohi f903e02d91 chore(deps): restore permissive marshmallow lower bound (>=3.0, <5) (#43521) 2026-08-25 14:38:44 -04:00
Maxime Beaucheminandsadpandajoe fc4d7221ec fix(explore): skip re-fetch when navigating away from /explore (#39506)
Co-authored-by: sadpandajoe <jcli38@gmail.com>
2026-08-25 11:32:25 -07:00
Chen, Ting-AnandJoe Li 3585e8235a fix(i18n): review Traditional Chinese count labels (#43063)
Co-authored-by: Joe Li <joe@preset.io>
2026-08-25 10:28:12 -07:00
Evan RusackasandSuperset Dev 34ffa37aaa chore(ci): drop inert SQLALCHEMY_WARN_20 flag from unit-test CI (#43495)
Co-authored-by: Superset Dev <dev@superset.apache.org>
2026-08-25 10:19:38 -07:00
Russlan Ramdowar 02b3e43b66 fix(number-format): preserve custom smart formatter id (#43439) 2026-08-25 10:11:52 -07:00
DmitryandDmitry Kucher f9530f31ab fix(table): keep each metric's own aggregate in the summary row by default (#43421)
Co-authored-by: Dmitry Kucher <dima@virtuman.com>
2026-08-25 10:10:02 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>rusackasClaude Opus 4.8
fd3849cef1 chore(deps): bump antd from 6.6.0 to 6.6.1 in /superset-frontend (#43509)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: rusackas <evan@rusackas.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 05:56:18 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ef0ad01aa5 chore(deps-dev): bump the storybook group across 1 directory with 5 updates (#43507)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 05:52:46 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> d480152735 chore(deps): bump the storybook group in /docs with 2 updates (#43505)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 02:46:30 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> cbfa94c4dd chore(deps): bump antd from 6.6.0 to 6.6.1 in /docs (#43506)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 02:46:26 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> d2aeb29223 chore(deps-dev): bump baseline-browser-mapping from 2.11.14 to 2.11.15 in /superset-frontend (#43508)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 02:46:22 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 7bad69f523 chore(deps): bump dayjs from 1.11.22 to 1.11.23 in /superset-frontend (#43510)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 02:46:17 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 9dd8c42f3d chore(deps): bump dompurify from 3.4.12 to 3.4.13 in /superset-frontend (#43511)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 02:46:12 -07:00
6c2fef29cb feat(snowflake): Add support for OAuth 2.0 authentication (#36856)
Co-authored-by: Evan Rusackas <evan@preset.io>
Co-authored-by: Evan Rusackas <evan@rusackas.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Joe Li <joe@preset.io>
2026-08-24 21:22:14 -07:00
Evan RusackasandSuperset Dev bd7b739212 chore: remove obsolete pandas/SQLAlchemy version compat shim (#43497)
Co-authored-by: Superset Dev <dev@superset.apache.org>
2026-08-24 20:11:52 -07:00
f83fb7c0e0 fix(archived-list): use the semantic-layers-aware label for the dataset type (#43465)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
2026-08-24 20:38:29 -04:00
a9d54a0037 fix(soft-delete): card-view chart delete shows the archive dialog (#43469)
Co-authored-by: Mike Bridge <michael.bridge@ext.preset.io>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
2026-08-24 20:37:52 -04:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>rusackasClaude Opus 4.8
8b792ab660 chore(deps-dev): bump @swc/core from 1.15.47 to 1.16.0 in /superset-frontend (#43446)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: rusackas <evan@rusackas.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 16:52:00 -07:00
Durgaprasad M LandEvan Rusackas 107204a1fc fix(sqllab): allow SQL Lab query owners to create charts without all_datasource_access (#42479)
Co-authored-by: Evan Rusackas <evan@rusackas.com>
2026-08-24 16:51:57 -07:00
7e3d092ac4 fix(soft-delete): name the recovery location in the archive confirmation (#43401)
Co-authored-by: Mike Bridge <michael.bridge@ext.preset.io>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 18:56:20 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> dfd057b920 chore(deps-dev): update clickhouse-connect requirement from <2.0,>=1.6.0 to >=1.7.1,<2.0 (#43419)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 13:54:23 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Đỗ Trọng Hải
52571a5b8b chore(deps): bump the rjsf group in /superset-frontend with 3 updates (#43440)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Đỗ Trọng Hải <41283691+hainenber@users.noreply.github.com>
2026-08-24 13:54:19 -07:00
Joe Li ca3d7670b7 fix(ci): restore scheduled CI checks (#43266) 2026-08-24 12:31:50 -07:00
Joe Li 94855e9626 fix(frontend): ignore stale list responses (#43306) 2026-08-24 12:13:34 -07:00
Joe LiandClaude 7dbf71a379 fix(query-object): reject malformed ad-hoc metrics (#43353)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-24 12:12:25 -07:00
Joe LiandClaude Sonnet 5 24b95f9ca7 fix(gsheets): correctly format Date-column filter literals (#43355)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 12:12:04 -07:00
Joe Li c635d0754f fix(explore): honor column Label in filter search and pill (#43404) 2026-08-24 12:11:52 -07:00
Joe Li 31f06c0ee6 fix(dataset): correct Hours Offset filter bounds and grain-truncation order (#43173) 2026-08-24 11:47:16 -07:00
Joe LiandClaude Opus 4.8 27ec80c07b fix(explore): clear stale custom time-shift date error (#43146)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 11:46:48 -07:00
Joe Li a8a8b51afb fix(plugin-chart-table): guard row-indexed comparison-color lookups against undefined entries (#43139) 2026-08-24 11:45:25 -07:00
6ab21b381a fix(mysql): resolve wire-protocol column types and mutate rows from immutable results (#42821)
Co-authored-by: Jean Massucatto <massucattoj@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-24 11:45:15 -07:00
Đỗ Trọng Hải b0962ba5ed fix(plugin/chart/parallel-coordinate): prevent frontend crash with empty/undefined metrics when moving between pages quickly (#42392)
Signed-off-by: hainenber <dotronghai96@gmail.com>
2026-08-24 11:44:50 -07:00
Joe LiandClaude Opus 4.8 bf5f3a9e6b test(dashboard): migrate drill-to-detail E2E from Cypress to Playwright (#41437)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-24 11:44:35 -07:00
c980b3a361 fix(mcp): tighten auth and request validation edge cases (#43388)
Co-authored-by: Superset Dev <dev@superset.apache.org>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 10:58:59 -07:00
dc436c76f7 fix(datasets): keep a metric's warning text when editing from Explore (#43177)
Co-authored-by: bikashJMV <bikash@jmv.co.in>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Evan Rusackas <evan@rusackas.com>
2026-08-24 10:47:45 -07:00
6eced8e919 fix(roles): let the permissions dropdown size to its content (#43196)
Co-authored-by: bikashJMV <bikash@jmv.co.in>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Evan Rusackas <evan@preset.io>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Evan Rusackas <evan@rusackas.com>
2026-08-24 10:46:03 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1bde62f997 chore(deps-dev): bump gevent from 26.7.0 to 26.8.0 (#43411)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-24 10:21:06 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
f1f6347885 chore(deps-dev): bump prophet from 1.3.0 to 1.4.0 (#43412)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-24 10:21:02 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
8591f52ced chore(deps): bump pyarrow from 25.0.0 to 25.0.1 (#43414)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-24 10:20:59 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
ceb75b9350 chore(deps): bump sqlalchemy from 2.0.51 to 2.0.52 (#43415)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-24 10:20:55 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> db539288ac chore(deps-dev): update teradatasql requirement from >=20.0.0.64 to >=20.0.0.65 (#43416)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 10:20:51 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
a392e8b102 chore(deps): bump marshmallow from 4.3.0 to 4.3.1 (#43417)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-24 10:20:48 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
e450acf1c7 chore(deps): bump sqlglot from 30.16.0 to 30.17.0 (#43418)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-24 10:20:44 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 2c02965f2b chore(deps): bump @swc/core from 1.15.47 to 1.16.0 in /docs (#43443)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 10:20:38 -07:00
Evan Rusackas f3142e7b15 fix(tags): fix broken import in daos/tag.py (#43466) 2026-08-24 10:15:08 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 649c062825 chore(deps): bump baseline-browser-mapping from 2.11.13 to 2.11.15 in /docs (#43444)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 09:52:03 -07:00
3f10d8b1cc fix(versioning): suppress automatic chart normalization changes (#43350)
Co-authored-by: Mike Bridge <michael.bridge@ext.preset.io>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 09:09:20 -07:00
Mehmet Salih Yavuz 15b98aa6da fix(plugin-chart-echarts): apply contribution before rename with time comparison (#43384) 2026-08-24 19:08:08 +03:00
7f1b41444c fix: add missing access checks to semantic-layer and theme endpoints (#43389)
Co-authored-by: Superset Dev <dev@superset.apache.org>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 08:52:45 -07:00
8a32324184 fix: add missing ownership checks to tag, report-log, and dataset-schema endpoints (#43390)
Co-authored-by: Superset Dev <dev@superset.apache.org>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 08:44:22 -07:00
Joe Li 4623d9219a fix: update in the wild link on community page (#43403) 2026-08-24 22:28:19 +07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> dcac129b51 chore(deps): bump js-yaml from 5.2.3 to 5.3.0 in /docs (#43442)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 22:17:39 +07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 7196ce8a0c chore(deps): bump docker/setup-buildx-action from 4.2.0 to 4.3.0 (#43445)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 22:16:57 +07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e7756aeef9 chore(deps): bump immer from 11.1.16 to 11.1.17 in /superset-frontend (#43448)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 22:16:27 +07:00
DanielSwift1992 af859717a7 chore: remove two stale codecov ignore paths (#43438) 2026-08-24 22:15:57 +07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 8fa48d73f6 chore(deps-dev): bump concurrently from 10.0.4 to 10.0.5 in /superset-frontend (#43449)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 22:04:38 +07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 90a3d93002 chore(deps): bump dayjs from 1.11.21 to 1.11.22 in /superset-frontend (#43450)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 22:04:09 +07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 5ffb72151e chore(deps): bump react-error-boundary from 6.1.2 to 6.1.3 in /superset-frontend (#43451)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 22:03:04 +07:00
Enzo Martellucci c3ed8b312d fix(security): surface extra_editors in dashboard/chart lists (#43392) 2026-08-24 11:49:29 +02:00
Elizabeth Thompson 9f505eb0cb fix(examples): replace deprecated timeseries_limit_metric with series_limit_metric (#43422) 2026-08-22 15:02:20 -07:00
Joe LiandClaude Sonnet 5 f2610e9dca fix(sqllab): default PostgreSQL port to 5432 in the dynamic connection form (#43351)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 15:59:33 -07:00
abdullah revehaandAbdullah Sahin 09bd8c98de feat(chart): enable cross-filter on temporal x-axis (bar/label click) (#42792)
Co-authored-by: Abdullah Sahin <you@example.comclear>
2026-08-21 15:45:06 -07:00
Aurimas Navardauskas 5812c0eab5 fix(mcp): fail closed when MCP_AUTH_FACTORY raises (#43329) 2026-08-21 15:43:51 -07:00
Mike BridgeandClaude Fable 5 5f3e34908c fix(charts): surface blocking alerts/reports when archiving a chart (#43402)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 19:33:32 -03:00
Evan RusackasandSuperset Dev 9781254363 fix(export): escape formula-triggering values consistently and bound post-processing inputs (#43396)
Co-authored-by: Superset Dev <dev@superset.apache.org>
2026-08-21 12:37:33 -07:00
65893113fe fix(import): tighten ownership and validation checks across asset importers (#43393)
Co-authored-by: Superset Dev <dev@superset.apache.org>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 12:35:33 -07:00
04017f3956 fix(sqllab): re-validate access against rendered SQL and tighten cache/permalink scoping (#43394)
Co-authored-by: Superset Dev <dev@superset.apache.org>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 12:35:06 -07:00
2b6044b624 fix: tighten SSRF validation, executor resolution, and cache scoping across reports/thumbnails (#43395)
Co-authored-by: Superset Dev <dev@superset.apache.org>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 12:34:37 -07:00
Evan RusackasandSuperset Dev aee98b527d fix(charts): escape untrusted strings before rendering into chart tooltips and popups (#43397)
Co-authored-by: Superset Dev <dev@superset.apache.org>
2026-08-21 12:31:31 -07:00
edda9d1bbb fix(frontend): tighten SQL Lab autorun scoping and HTML-rendering defaults (#43398)
Co-authored-by: Superset Dev <dev@superset.apache.org>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 12:31:08 -07:00
Joe LiandClaude Sonnet 5 03eac279e5 fix(explore): exclude permalink_key from chart URL params (#43354)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 11:43:18 -07:00
yousophandClaude Opus 4.8 fd7095df9a fix(explore): align viz type gallery thumbnails and Featured tag (#43373)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-21 10:40:37 -07:00
d422f5b4b6 fix: last date label hidden on time series x-axis (#39899) (#42299)
Co-authored-by: Prathamesh Hukkeri <prathamesh04@users.noreply.github.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-08-21 09:59:06 -07:00
Alexandru Soare a05a099987 fix(embedded): block custom SQL injection in guest user chart payloads (#43111) 2026-08-21 17:05:40 +03:00
Evan RusackasandClaude Code 05842a6350 feat(metrics): add MEDIAN/STDDEV_SAMP/VAR_SAMP as system-wide aggregates (#42895)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-21 04:15:17 -07:00
Evan RusackasandClaude Opus 4.8 e45dd2d3f1 ci: pull CI service images from GHCR mirror (fork-safe) [depends on #40880] (#40882)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 04:11:37 -07:00
Mehmet Salih Yavuz 65910abf21 fix(sqllab): disable Save dataset until the query runs successfully (#43330) 2026-08-21 12:56:42 +03:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 6e22050b50 chore(deps): bump the storybook group in /docs with 2 updates (#43377)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-21 01:10:41 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 132340b652 chore(deps): bump astral-sh/setup-uv from 10.0.0 to 10.0.1 (#43378)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-21 01:10:37 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 14408eb3db chore(deps-dev): bump the storybook group in /superset-frontend with 5 updates (#43379)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-21 01:10:33 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 87743ef3f8 chore(deps): bump dompurify from 3.4.12 to 3.4.13 in /superset-frontend (#43380)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-21 01:10:29 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> fb53761ec8 chore(deps-dev): bump baseline-browser-mapping from 2.11.13 to 2.11.14 in /superset-frontend (#43381)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-21 01:10:25 -07:00
Amin Ghadersohi f7d505e1fd fix(listview): stop card clicks creating a duplicate history entry (#43310) 2026-08-20 23:23:46 -04:00
22396d504a fix(ux): use title case for button labels (#40048)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-08-20 20:14:33 -07:00
Gaston LaterzaandClaude Opus 5 27ea5de44a fix(i18n-es): correct case/number collapse in the semantic-layer labels (#43311)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 20:03:57 -07:00
Joe LiandClaude Sonnet 5 18fc2c6228 fix(sqllab): stop copying a permalink when opening a saved query (#43147)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 15:44:20 -07:00
Amin Ghadersohi 18a36d04c7 fix(mcp): preserve user-authored result values (#43202) 2026-08-20 18:29:39 -04:00
ʈᵃᵢ 01b1d58ac9 fix(plugin-chart-echarts): restore tooltips for metrics labelled like… (#43369) 2026-08-20 14:58:11 -07:00
Joe Li 7441ce90ae fix(charts): align grain-less time comparisons safely (#43315) 2026-08-20 14:55:01 -07:00
Amin Ghadersohi 42ba2a4433 fix(reports): humanize day-of-month + day-of-week crontabs as OR (#43307) 2026-08-20 16:46:08 -04:00
madhushreeagandmadhushree agarwal 271564cb0d feat(config): add EXTRA_PANDAS_POSTPROCESSING_OPS extension point (#43337)
Co-authored-by: madhushree agarwal <madhushree_agarwal@apple.com>
2026-08-20 13:15:41 -07:00
JUST.in DO ITandClaude Sonnet 5 bcfb4346f6 fix(mcp): honor use_cache and cache_timeout in get_chart_data (#43349)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 13:01:41 -07:00
Joe Li b5fe00b407 test(frontend): shrink flaky/misplaced recently-archived e2e coverage to Jest unit tests (#43264) 2026-08-20 11:48:02 -07:00
Parman MohammadalizadehandEvan Rusackas 148ffaff50 fix(plugin-chart-echarts): omit stacked value labels on zero-height segments (#42756)
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-08-20 11:04:19 -07:00
Ankit 83c6ea4e03 fix(explore): show the empty state when Samples returns no result payload (#43115) 2026-08-20 11:00:54 -07:00
Hans Yu 1ca5e61f54 chore: Unset future flag in engines and sessions. (#43273) 2026-08-20 10:58:37 -07:00
rlei 1ef12580a7 fix(chart): ignore chart actions for a chart no longer in state (#43228) 2026-08-20 10:57:21 -07:00
c0884c0f0c fix(explore): keep x-axis label when overriding Time Column with time comparison (#42875)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Joe Li <joe@preset.io>
2026-08-20 10:52:22 -07:00
Sepuri Sai KrishnaandClaude Opus 5 2ff79bd495 fix(github): point the issue templates at labels that exist (#43357)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 10:13:36 -07:00
e7dccd44a7 fix(reports): prevent blank/partial report PDFs from virtualized charts (#43348)
Co-authored-by: Matt Fitzgerald <matt.fitzgerald@preset.io>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
2026-08-20 09:10:30 -04:00
dependabot[bot] fdd3945dde chore(deps): bump github/codeql-action/analyze from 4.37.6 to 4.37.7 (#43361)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-20 13:58:31 +03:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 8b67fb4d30 chore(deps-dev): bump globals from 17.10.0 to 17.11.0 in /superset-websocket (#43360)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-20 00:41:49 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b288db91f9 chore(deps): bump github/codeql-action/init from 4.37.6 to 4.37.7 (#43362)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-20 00:41:43 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 80bba12d0f chore(deps-dev): bump globals from 17.10.0 to 17.11.0 in /docs (#43363)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-20 00:41:38 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 5d4358a149 chore(deps): bump google-auth-library from 11.0.1 to 11.0.2 in /superset-frontend (#43364)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-20 00:41:25 -07:00
f9cedf84e2 fix: drop post-processing options the operation no longer accepts (#42927)
Signed-off-by: Arya Ketan <aryaketan@sharechat.co>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-08-19 18:15:15 -07:00
Grégoire GaillyandEvan Rusackas c2d653b4b8 fix: set maxHeight of List components to height when in AutoSizer (#43056)
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-08-19 16:56:30 -07:00
Đỗ Trọng HảiandJoe Li 5a96c3f538 chore(ci): disable Git commit info capture in Playwright E2E tests to avoid timeout (#43213)
Signed-off-by: hainenber <dotronghai96@gmail.com>
Co-authored-by: Joe Li <joe@preset.io>
2026-08-19 16:54:44 -07:00
ʈᵃᵢ faf7c34c0a fix(explore): legacy boolean filters and limit available operators based on calculated column type (#43341) 2026-08-19 15:37:09 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b8fca2145d chore(deps): bump astral-sh/setup-uv from 9.0.0 to 10.0.0 (#43322)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-19 14:40:02 -07:00
Alejandro Solares c10054f521 fix(plugin-chart-chord): declare react as a peerDependency (#43304) 2026-08-19 17:35:38 -04:00
Amin GhadersohiandClaude 8c500ccee1 fix(users): show password validation errors (#43191)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-19 16:57:46 -04:00
Joe LiandClaude Sonnet 5 6d77efad29 fix(chart): stop contextmenu propagation in BigNumberViz (#43267)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 13:57:28 -07:00
Amin GhadersohiandClaude 8222db3340 fix(dataset): preserve legacy default dashboard URLs (#43190)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-19 14:44:54 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 01ce8358a6 chore(deps-dev): bump globals from 17.9.0 to 17.10.0 in /superset-websocket (#43321)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-19 11:36:48 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 53a8a0e140 chore(deps): bump the docusaurus-openapi group in /docs with 2 updates (#43323)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-19 11:36:44 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> eafbff9f8d chore(deps-dev): bump globals from 17.9.0 to 17.10.0 in /docs (#43324)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-19 11:36:37 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1339bcd9da chore(deps): bump swagger-ui-react from 5.32.12 to 5.32.13 in /docs (#43325)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-19 11:36:34 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 334e280489 chore(deps): bump dompurify from 3.4.12 to 3.4.13 in /superset-frontend (#43326)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-19 11:36:30 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> c07f3ebf2d chore(deps-dev): bump @swc/plugin-emotion from 14.15.0 to 14.19.0 in /superset-frontend (#43328)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-19 11:36:26 -07:00
Sumit KumarandClaude Opus 4.8 1569915096 feat(multi-value): array-typed column filters with two-tier operators (ClickHouse MVP) (#41279)
Signed-off-by: thedeceptio <thedeceptio@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 10:56:49 -07:00
BexultanandBexultan Mustafin fde0ba26d1 fix(mcp): validate virtual dataset metadata and surface errors (#43129)
Co-authored-by: Bexultan Mustafin <bexultan.mustafin@ffins.kz>
2026-08-19 10:50:37 -07:00
DanielSwift1992 097c99b19c fix: remove a labeler glob that matches no files (#43270) 2026-08-18 16:21:01 -07:00
David Dallakyan 5ce52e531d fix(clickhouse): add PT1S time grain (#43217) 2026-08-18 15:49:18 -07:00
34cd50cc48 test(chart): mock the event log endpoint in the drill-to-detail menu test (#43183)
Co-authored-by: bikashJMV <bikash@jmv.co.in>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Joe Li <joe@preset.io>
2026-08-18 13:51:54 -07:00
c0ab5f3385 fix(dashboard): preserve native filter keys for dataset-less filters on save (#42898)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Mehmet Salih Yavuz <salih.yavuz@proton.me>
2026-08-18 21:30:50 +03:00
ʈᵃᵢ 7d4f30574f feat(tooltip): add Truncate labels control to timeseries charts (#43272) 2026-08-18 11:05:07 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e4ea6e23d8 chore(deps): bump supercluster from 8.0.1 to 9.0.0 in /superset-frontend (#43290)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 10:35:17 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a13a5f1af4 chore(deps-dev): bump @typescript-eslint/eslint-plugin from 8.65.0 to 8.67.0 in /superset-websocket (#43284)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 10:34:39 -07:00
Endi Monan f994602096 fix(dashboard): align list OpenAPI schema (#43256) 2026-08-18 10:22:50 -07:00
Amin GhadersohiandClaude Fable 5 086b4af65d feat(mcp): per-resource token scopes with user-permission intersection (#42297)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 13:11:27 -04:00
fd063d17bf fix(security): harden account password-change and session-invalidation handling (#42934)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Joe Li <joe@preset.io>
2026-08-18 17:40:08 +01:00
60e1802c52 fix(dashboard): mute the Group By display control loading spinner (#42879)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-08-18 09:37:43 -07:00
Joe LiandClaude Opus 4.8 2d1daac11a fix(explore): samples endpoint now honors requested row limit (#43148)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 09:28:04 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 3c90bdc6f0 chore(deps-dev): bump oxfmt from 0.62.0 to 0.63.0 in /superset-frontend (#43293)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 06:15:08 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>rusackasClaude Opus 4.8
ebab31adc2 chore(deps): bump antd from 6.5.4 to 6.6.0 in /superset-frontend (#43294)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: rusackas <evan@rusackas.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 06:15:04 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 936f073b9a chore(deps-dev): bump @typescript-eslint/parser from 8.66.0 to 8.67.0 in /superset-websocket (#43281)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 03:00:46 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> d12320239a chore(deps-dev): bump typescript-eslint from 8.66.0 to 8.67.0 in /superset-websocket (#43282)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 03:00:42 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 8ef7be5788 chore(deps-dev): bump oxfmt from 0.62.0 to 0.63.0 in /superset-websocket (#43283)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 03:00:39 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 72458ab26f chore(deps-dev): bump @typescript-eslint/eslint-plugin from 8.66.0 to 8.67.0 in /superset-frontend in the typescript-eslint group (#43285)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 03:00:33 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 5105f13726 chore(deps-dev): bump the typescript-eslint group in /docs with 3 updates (#43286)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 03:00:29 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> bdafb6c330 chore(deps-dev): bump oxfmt from 0.62.0 to 0.63.0 in /docs (#43287)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 03:00:25 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b4d79462ec chore(deps-dev): bump oxlint from 1.77.0 to 1.78.0 in /superset-frontend (#43288)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 03:00:21 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 49374f1fe5 chore(deps): bump antd from 6.5.4 to 6.6.0 in /docs (#43289)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 03:00:17 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> f766de6d0d chore(deps): bump dompurify from 3.4.12 to 3.4.13 in /superset-frontend (#43291)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 02:59:59 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ed20e729d0 chore(deps): bump google-auth-library from 11.0.0 to 11.0.1 in /superset-frontend (#43292)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 02:59:55 -07:00
13eb47a1da fix(api): improved request handling and embedded dashboard scoping (#42930)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Superset Dev <dev@superset.apache.org>
2026-08-17 23:50:25 -07:00
Elizabeth ThompsonandClaude Opus 4.8 98136d547c fix(reports): fail closed on alert screenshot capture instead of delivering a blank (#43031)
Signed-off-by: Elizabeth Thompson <eschutho@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 00:03:15 -04:00
Elizabeth ThompsonandClaude Opus 4.8 e2070d79dc fix(reports): wait for ECharts paint before capturing report screenshots (#43077)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 00:02:36 -04:00
ʈᵃᵢ 2807f1b0e8 fix(dashboard): report the real error when saving a dashboard fails (#43140) 2026-08-17 17:51:28 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> c9c230142b chore(deps): bump baseline-browser-mapping from 2.11.12 to 2.11.13 in /docs (#43243)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 17:46:24 -07:00
Beto Dealmeida 2f8875aaef feat(semantic layers): optional metadata for metrics/dimensions (#43269) 2026-08-17 17:34:17 -07:00
Elizabeth Thompson 6e270df4a2 fix(sql-lab): catch TemplateError in StreamingSqlResultExportCommand.validate (#43226) 2026-08-17 15:02:11 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 97eafd6140 chore(deps-dev): bump @types/node from 26.1.2 to 26.2.0 in /superset-websocket (#43240)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 14:34:35 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 3ed97f9691 chore(deps): bump ws from 8.21.2 to 8.21.3 in /superset-websocket (#43241)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 14:34:31 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 5105899810 chore(deps-dev): bump eslint from 10.8.0 to 10.8.1 in /superset-websocket (#43242)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 14:34:25 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> d917071708 chore(deps): bump caniuse-lite from 1.0.30001807 to 1.0.30001809 in /docs (#43244)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 14:34:18 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> afde126d9a chore(deps-dev): bump @testing-library/jest-dom from 7.0.0 to 7.0.1 in /superset-frontend (#43246)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 14:34:15 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b3a9b9beb4 chore(deps-dev): bump @types/node from 26.1.2 to 26.2.0 in /superset-frontend (#43247)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 14:34:10 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 70ba9c9552 chore(deps-dev): bump eslint from 10.8.0 to 10.8.1 in /superset-frontend (#43248)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 14:34:06 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 98276cd1f3 chore(deps-dev): bump baseline-browser-mapping from 2.11.12 to 2.11.13 in /superset-frontend (#43249)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 14:34:03 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> cdeca0c179 chore(deps-dev): bump tsx from 4.23.10 to 4.23.12 in /superset-frontend (#43250)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 14:33:59 -07:00
Amin Ghadersohi aaf9eba161 fix(db_engine_specs): skip malformed third-party dialect entry points (#43110)
Signed-off-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
2026-08-17 14:10:06 -07:00
Mehmet Salih Yavuz 1991e3f0d2 fix(datasets): keep metric certified_by when certification details are typed next (#43255) 2026-08-17 19:23:31 +03:00
Mehmet Salih Yavuz cfd40bdd0d fix(dashboard): derive filter scope on read instead of serving a stale cache (#43252) 2026-08-17 19:23:19 +03:00
Endi Monan a8216e3787 fix(calendar): localize date labels (#43163) 2026-08-17 09:44:14 -03:00
anopsandEvan Rusackas d114eb638b fix(datasets): preserve metric warning_markdown when extra is absent (#42763)
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-08-16 00:22:24 -07:00
Abdrihman Hussein 70d06e3d77 docs(faq): convert flask fab freeze question to a proper heading (#43221) 2026-08-15 23:21:13 +07:00
Joe Li a2c004266b fix(ci): update vulnerable transitive nanoid (#43166) 2026-08-15 23:09:19 +07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Đỗ Trọng Hải
2773bc94eb chore(deps-dev): bump sqlalchemy-bigquery from 1.17.1 to 1.17.2 (#43209)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Đỗ Trọng Hải <41283691+hainenber@users.noreply.github.com>
2026-08-15 00:59:55 -07:00
94459ae885 ci: declare top-level permissions on the remaining workflows (#43197)
Co-authored-by: bikashJMV <bikash@jmv.co.in>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Đỗ Trọng Hải <41283691+hainenber@users.noreply.github.com>
2026-08-15 14:48:43 +07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
ad3103fdb0 chore(deps-dev): bump fastmcp from 3.4.5 to 3.4.7 (#43210)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-15 00:43:03 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
065342f8c9 chore(deps-dev): bump pandas-gbq from 0.35.0 to 0.35.1 (#43212)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-15 00:42:19 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2e8a8031f8 chore(deps): bump cachetools from 7.1.6 to 7.1.7 (#43208)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-15 14:33:36 +07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> cece082eed chore(deps-dev): update pyocient requirement from <4,>=1.0.15 to >=3.9.0,<4 (#43211)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-15 14:33:21 +07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
22dde07a8e chore(deps): bump mako from 1.3.12 to 1.4.1 (#43207)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-15 14:33:06 +07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Joe LiEvan Rusackas
93cc520482 chore(deps): bump @googleapis/sheets from 13.0.2 to 14.0.0 in /superset-frontend (#43047)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Joe Li <joe@preset.io>
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-08-15 14:23:13 +07:00
7679c5641a fix(lint-metrics): record oxlint plugin rules under their canonical id (#43198)
Co-authored-by: bikashJMV <bikash@jmv.co.in>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 14:21:38 +07:00
Evan RusackasandClaude Code f7c70568c3 fix(charts): fix Bubble chart crash and correct example's time_range to a year with real data (#43201)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-15 14:16:29 +07:00
Michael GerberandEvan Rusackas 69c4011a95 feat(embedded): Add setDataMask API method (#35754)
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-08-14 22:07:24 -07:00
4684851336 fix(import): isolate per-tag import in a SAVEPOINT to avoid poisoned session (#42912) (#42920)
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-08-14 21:56:43 -07:00
e7139a7ac2 fix(models): enforce one OAuth2 token per user+database (#42897)
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 21:50:56 -07:00
21ae918656 fix(mcp): validate chart queries before persistence (#43128)
Co-authored-by: Bexultan Mustafin <bexultan.mustafin@ffins.kz>
Co-authored-by: Joe Li <joe@preset.io>
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
2026-08-14 17:48:19 -07:00
Joe Li ca94026e97 fix(sqllab): async queries no longer stuck at Running forever (#42896) 2026-08-14 17:47:10 -07:00
Evan RusackasandClaude Opus 4.8 a3bc2d908c ci(frontend): track bundle size over time with benchmark-action (#42511)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 17:40:46 -07:00
738d12677a fix(i18n-es): correct stranded translations in API key and semantic layer catalogs (#43080)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Joe Li <joe@preset.io>
2026-08-14 17:40:23 -07:00
Elizabeth ThompsonandClaude Opus 4.8 2eedc609a8 fix(sql_lab): return 400 not 500 when raise_for_access hits malformed Jinja in results.py (#43145)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 15:02:47 -07:00
JUST.in DO IT f4587218dd fix(mcp): populate user_id in MCP audit logs (#42767) 2026-08-14 16:28:01 -04:00
Amin Ghadersohi 8967e6c2d3 fix(mcp): accept changed_on_delta_humanized as order_column in list tools (#42571) 2026-08-14 16:11:46 -04:00
Amin Ghadersohi 81e431cd50 fix(mcp): return all chart query results (#42824) 2026-08-14 16:10:26 -04:00
Amin Ghadersohi edfb009e1c fix(mcp): expose query context to Jinja macros (#42822) 2026-08-14 16:10:03 -04:00
Amin Ghadersohi e808fcbcad fix(mcp): bind all generated charts to dashboard time filters (#42490) 2026-08-14 16:07:50 -04:00
Evan RusackasandClaude Code 0a7ebe1dd1 fix(ci): stop superseded Docs Deployment runs from showing as cancelled (#42488)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-14 12:14:08 -07:00
Evan RusackasandClaude Opus 4.8 dd1afb029f fix(ci): stop py311/py312 docker builds from silently matching lean (#42509)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 12:07:23 -07:00
joeyandAmin Ghadersohi c068a8c09c fix(dashboard): preserve certification fields when Certification panel is closed (#42957)
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
2026-08-14 11:19:39 -07:00
Evan RusackasandClaude Code b62ec512d2 fix(explore): hide edit-properties menu item for non-owner/non-editor users (#38884) (#42737)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-14 11:15:10 -07:00
1e65d93a83 fix(sql): handle ORDER BY in embedded MSSQL queries (#43127)
Co-authored-by: Bexultan Mustafin <bexultan.mustafin@ffins.kz>
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
2026-08-14 14:07:10 -04:00
Gabriel Torres Ruiz 856599027a fix(mcp): harden the embedded-guest chart data-read path 2026-08-14 14:55:11 -03:00
Evan RusackasandClaude Code c395b9a238 fix(examples): migrate Life Expectancy VS Rural % example off the removed legacy bubble viz_type (#43152)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-14 10:27:14 -07:00
1085 changed files with 51450 additions and 9449 deletions
-2
View File
@@ -3,9 +3,7 @@ codecov:
after_n_builds: 4
ignore:
- "superset/migrations/versions/*.py"
- "superset-frontend/packages/superset-ui-demo/**/*"
- "**/*.stories.tsx"
- "**/*.stories.jsx"
coverage:
status:
project:
+1 -1
View File
@@ -1,6 +1,6 @@
name: Bug report
description: Report a bug to improve Superset's stability
labels: ["bug"]
labels: ["#bug"]
body:
- type: markdown
attributes:
+1 -1
View File
@@ -1,7 +1,7 @@
---
name: Cosmetic Issue
about: Describe a cosmetic issue with CSS, positioning, layout, labeling, or similar
labels: "cosmetic-issue"
labels: "#bug:cosmetic"
---
## Screenshot
+43 -8
View File
@@ -5,6 +5,10 @@ updates:
directory: "/"
schedule:
interval: "daily"
groups:
security:
applies-to: "security-updates"
patterns: ["*"]
cooldown:
default-days: 7
@@ -20,14 +24,6 @@ updates:
- dependency-name: "@types/react-dom"
update-types: ["version-update:semver-major"]
- dependency-name: "react-icons"
# JSDOM v30 doesn't play well with Jest v30
# Source: https://jestjs.io/blog#known-issues
# GH thread: https://github.com/jsdom/jsdom/issues/3492
- dependency-name: "jest-environment-jsdom"
# `@swc/plugin-transform-imports` doesn't work with current Webpack-SWC hybrid setup
# See https://github.com/apache/superset/pull/37384#issuecomment-3793991389
# TODO: remove the plugin once Lodash usage has been migrated to a more readily tree-shakeable alternative
- dependency-name: "@swc/plugin-transform-imports"
# deck.gl and luma.gl share strict peer constraints across the root and
# plugin workspaces, and root overrides pin their transitive versions.
# Upgrade both families together in a manually validated change.
@@ -61,6 +57,9 @@ updates:
- npm
- dependabot
groups:
security:
applies-to: "security-updates"
patterns: ["*"]
rjsf:
patterns:
- "@rjsf/*"
@@ -80,6 +79,23 @@ updates:
patterns:
- "ag-grid-react"
- "ag-grid-community"
swc:
patterns:
- "@swc/core"
- "@swc/plugin-emotion"
- "@swc/plugin-transform-imports"
jsonforms:
patterns:
- "@jsonforms/*"
visx:
patterns:
- "@visx/*"
emotion:
patterns:
- "@emotion/*"
fontsource:
patterns:
- "@fontsource/*"
open-pull-requests-limit: 30
versioning-strategy: increase
cooldown:
@@ -98,6 +114,10 @@ updates:
labels:
- pip
- dependabot
groups:
security:
applies-to: "security-updates"
patterns: ["*"]
cooldown:
default-days: 7
@@ -105,6 +125,10 @@ updates:
directory: ".github/actions"
schedule:
interval: "daily"
groups:
security:
applies-to: "security-updates"
patterns: ["*"]
open-pull-requests-limit: 10
versioning-strategy: increase
cooldown:
@@ -115,6 +139,9 @@ updates:
schedule:
interval: "daily"
groups:
security:
applies-to: "security-updates"
patterns: ["*"]
storybook:
patterns:
- "@storybook/*"
@@ -142,6 +169,10 @@ updates:
labels:
- npm
- dependabot
groups:
security:
applies-to: "security-updates"
patterns: ["*"]
versioning-strategy: increase
cooldown:
default-days: 7
@@ -153,6 +184,10 @@ updates:
labels:
- npm
- dependabot
groups:
security:
applies-to: "security-updates"
patterns: ["*"]
open-pull-requests-limit: 10
versioning-strategy: increase
cooldown:
+1 -1
View File
@@ -29,7 +29,7 @@
"dependencies:python":
- changed-files:
- any-glob-to-any-file:
- 'superset/requirements/**'
- 'requirements/**'
- 'superset/translations/requirements.txt'
- 'RELEASING/requirements.txt'
+4 -1
View File
@@ -21,6 +21,9 @@ on:
#schedule:
# - cron: '0 0 * * *' # Runs daily at midnight UTC
permissions:
contents: read
jobs:
bump-python-package:
runs-on: ubuntu-slim
@@ -45,7 +48,7 @@ jobs:
python-version: "3.11"
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.11"
enable-cache: true
@@ -16,6 +16,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
check_db_migration_conflict:
name: Check DB migration conflict
+5 -2
View File
@@ -14,6 +14,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
changes:
runs-on: ubuntu-26.04
@@ -64,7 +67,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -75,6 +78,6 @@ jobs:
# queries: security-extended,security-and-quality
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
with:
category: "/language:${{matrix.language}}"
+3 -11
View File
@@ -44,20 +44,12 @@ jobs:
permissions:
pull-requests: write # to post the approving review via `gh pr review`
steps:
- name: Fetch Dependabot metadata
id: metadata
# This exact SHA is on ASF Infra's action allowlist
# (apache/infrastructure-actions approved_patterns.yml) as of this
# writing. Do not bump without opening an Infra ticket to allow
# the new SHA first!
uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0
# Dependabot automatically attaches semver labels (major, minor and patch) to its generated PRs
- name: Approve patch-level bump
if: steps.metadata.outputs.update-type == 'version-update:semver-patch'
if: contains(github.event.pull_request.labels.*.name, 'patch')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_URL: ${{ github.event.pull_request.html_url }}
DEPENDENCY_NAMES: ${{ steps.metadata.outputs.dependency-names }}
run: |
gh pr review --approve "$PR_URL" \
--body "Auto-approved: patch-level bump only ($DEPENDENCY_NAMES)."
--body "Auto-approved: patch-level bump only."
+21
View File
@@ -66,6 +66,27 @@ jobs:
- name: "Set up liccheck"
run: |
# liccheck (as of 0.9.2) still does a bare `import pkg_resources`
# without declaring setuptools as a dependency, relying on it
# having historically been bundled. setuptools 81+ (installed
# above via requirements/base.txt) dropped the pkg_resources
# subpackage entirely, so liccheck's own import breaks outright.
#
# Reinstalling an older setuptools would restore pkg_resources but
# would also downgrade the *real* setuptools install, which then
# trips liccheck's own working_set.resolve() -- it cross-checks
# requirements/base.txt's declared `setuptools==84.0.0` against
# what's actually installed, and a downgrade makes those disagree.
#
# Instead, vendor just the pkg_resources/ package files from an
# old setuptools wheel into site-packages, leaving the real
# setuptools install (and its dist-info metadata) untouched. This
# gives liccheck an importable pkg_resources whose own working-set
# scan still correctly reports the real installed setuptools
# version, so no conflict is raised.
pip download "setuptools<81" --no-deps -d /tmp/old-setuptools
python -m zipfile -e /tmp/old-setuptools/setuptools-*.whl /tmp/old-setuptools-extracted/
cp -r /tmp/old-setuptools-extracted/pkg_resources "$(python -c 'import site; print(site.getsitepackages()[0])')/"
uv pip install --system liccheck
- name: "Run liccheck"
run: |
+56 -7
View File
@@ -51,6 +51,53 @@ jobs:
echo "matrix_config=${MATRIX_CONFIG}" >> $GITHUB_OUTPUT
echo $GITHUB_OUTPUT
# Runs unconditionally (no dependency on `changes`, and no build-preset
# matrix restriction) so a regression in the PY_VER override logic is
# always caught on PRs. Without this, the real docker-build job only runs
# when the change detector flags docker/python/frontend changes (a
# workflow-only edit like this one does not), and even then the PR build
# matrix never includes the "py311"/"py312" presets that logic protects -
# so a break here would otherwise first surface on a push to master.
pyver-override-check:
name: verify docker build PY_VER override
runs-on: ubuntu-26.04
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup supersetbot
uses: ./.github/actions/setup-supersetbot/
- name: Assert PY_VER override applies to every preset except py311/py312
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
# Asserts against the actual buildx command line `supersetbot docker
# --dry-run` would run, not just this repo's own extra-flags helper,
# so a regression in supersetbot itself (dropping the py311/py312
# PY_VER pin, or reordering args so our override no longer lands
# last) is caught here too, instead of only surfacing on master.
assert_effective_py_ver() {
local preset="$1" expected="$2" extra_flags command actual
extra_flags="$(scripts/docker-build-extra-flags.sh "$preset" dummy-tag)"
command="$(supersetbot docker --preset "$preset" --platform linux/amd64 --extra-flags "$extra_flags" --dry-run)"
# docker buildx keeps the LAST value of a repeated --build-arg key.
actual="$(grep -oE -- '--build-arg PY_VER=[^[:space:]]+' <<<"$command" | tail -1)"
if [ "$actual" != "--build-arg PY_VER=$expected" ]; then
echo "::error::preset '$preset' expected effective --build-arg PY_VER=$expected, got: ${actual:-<none>} (full command: $command)"
exit 1
fi
}
for preset in dev lean websocket dockerize; do
assert_effective_py_ver "$preset" "3.11.14-slim-trixie"
done
assert_effective_py_ver py311 "3.11-slim-bookworm"
assert_effective_py_ver py312 "3.12-slim-bookworm"
echo "PY_VER override logic verified against the assembled buildx command for all build presets"
docker-build:
name: docker-build
needs: [setup_matrix, changes]
@@ -124,19 +171,21 @@ jobs:
# the whole job. buildx reuses the buildkit layer cache from the
# failed attempt, so a retry mostly re-does just the failed push.
#
# supersetbot's "dev"/"lean" presets pin their own --build-arg
# PY_VER, which lands ahead of --extra-flags on the assembled
# buildx command line; docker/buildx keeps the last value for a
# repeated --build-arg key, so appending PY_VER here overrides
# supersetbot's pin and keeps the build on the Dockerfile's own
# supported Python version.
# See scripts/docker-build-extra-flags.sh for why "py311"/"py312"
# are excluded from the PY_VER override applied to every other
# preset; that logic is also exercised on every PR by the
# always-on pyver-override-check job below, since this job itself
# only runs when the change detector flags docker/python/frontend
# changes and the PR build matrix never includes py311/py312.
EXTRA_FLAGS="$(scripts/docker-build-extra-flags.sh "$BUILD_PRESET" "$IMAGE_TAG")"
for attempt in 1 2 3; do
if supersetbot docker \
$PUSH_OR_LOAD \
--preset "$BUILD_PRESET" \
--context "$EVENT" \
--context-ref "$RELEASE" $FORCE_LATEST \
--extra-flags "--build-arg PY_VER=3.11.14-slim-trixie --build-arg INCLUDE_CHROMIUM=false --tag $IMAGE_TAG" \
--extra-flags "$EXTRA_FLAGS" \
$PLATFORM_ARG; then
break
fi
@@ -33,6 +33,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
enforce-single-migration-head:
runs-on: ubuntu-26.04
@@ -0,0 +1,136 @@
name: Frontend bundle size (nightly baseline + analyzer)
# Refreshes the bundle-size baseline that superset-frontend.yml's `bundle-size`
# job compares PRs against, and publishes a browsable bundle-analyzer treemap
# report of the same build. Deliberately NOT triggered on every push to
# master: a day-old baseline/report is fine for catching relative
# regressions on PRs and for browsing what's actually in the bundle, and
# building the production bundle on every one of the many pushes master
# gets per day would burn CI time for no benefit a nightly refresh doesn't
# already cover.
on:
schedule:
- cron: "0 6 * * *"
workflow_dispatch: {}
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
env:
TAG: apache/superset:bundle-size-nightly-${{ github.run_id }}
permissions:
contents: read
jobs:
refresh-baseline:
runs-on: ubuntu-26.04
timeout-minutes: 30
env:
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_BUNDLE_ANALYZER_SITE_ID }}
steps:
- name: "Checkout master"
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: master
- name: Build Docker Image
run: |
docker buildx build \
-t $TAG \
--cache-from=type=registry,ref=apache/superset-cache:3.11-slim-trixie \
--target superset-node-ci \
.
# Same cache the PR-time bundle-size job restores/writes -- webpack's
# persistent filesystem cache turns a warm production build into ~20s
# instead of several minutes. See superset-frontend.yml for the
# matching restore step and why it's keyed this way.
- name: Restore webpack build cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: superset-frontend/.temp_cache
key: >-
webpack-prod-cache-${{ hashFiles('superset-frontend/package-lock.json',
'superset-frontend/babel.config.js', 'superset-frontend/tsconfig.json',
'superset-frontend/webpack.config.js') }}
# Only ever pull the last recorded data point off the cache, keyed by
# run ID -- `restore-keys` prefix-matches the most recently created
# entry. Absent on the very first run ever; benchmark-action starts a
# fresh history in that case.
- name: Restore bundle size history
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: bundle-size-history.json
key: bundle-size-history-${{ github.run_id }}
restore-keys: |
bundle-size-history-
# BUNDLE_ANALYZER rides along in the same build as BUNDLE_SIZE_STATS --
# they're independent env-gated additions in webpack.config.js (one
# sets `config.stats`, the other pushes plugins), so one production
# build produces both the numeric stats.json and the analyzer's
# report.html. Only report.html is mounted out, not
# BUNDLE_ANALYZER's sibling `statistics.html` sunburst -- that file is
# documented in webpack.config.js as routinely exceeding 100MB for
# this app (it's .gitignore'd for exactly that reason), too large to
# publish as a static site page.
- name: Build production bundle with stats and analyzer report
run: |
mkdir -p ${{ github.workspace }}/superset-frontend/bundle-stats
mkdir -p ${{ github.workspace }}/superset-frontend/.temp_cache
mkdir -p ${{ github.workspace }}/superset/static/assets
docker run \
-v ${{ github.workspace }}/superset-frontend/bundle-stats:/app/superset-frontend/bundle-stats \
-v ${{ github.workspace }}/superset-frontend/.temp_cache:/app/superset-frontend/.temp_cache \
-v ${{ github.workspace }}/superset/static/assets:/app/superset/static/assets \
--rm $TAG \
bash -c \
"npm i && BUNDLE_SIZE_STATS=true BUNDLE_ANALYZER=true npm run build -- --json=bundle-stats/stats.json"
- name: Summarize bundle size
run: |
node superset-frontend/scripts/bundle-size-summary.js \
superset-frontend/bundle-stats/stats.json > bundle-size-summary.json
rm -rf superset-frontend/bundle-stats
# No PR to comment on here, so comment-on-alert is off -- the job
# summary (summary-always) is the only surface for this run.
- name: Update bundle size baseline
uses: benchmark-action/github-action-benchmark@52576c92bccf6ac60c8223ec7eb2565637cae9ba # v1.22.1
with:
tool: customSmallerIsBetter
output-file-path: bundle-size-summary.json
external-data-json-path: bundle-size-history.json
github-token: ${{ secrets.GITHUB_TOKEN }}
fail-on-alert: false
summary-always: true
- name: Save bundle size history
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: bundle-size-history.json
key: bundle-size-history-${{ github.run_id }}
# Publishes the treemap to Netlify (the same host already used for
# superset-storybook.netlify.app and docs previews, reusing the
# existing NETLIFY_AUTH_TOKEN). Skipped until
# NETLIFY_BUNDLE_ANALYZER_SITE_ID exists -- create a new (free)
# Netlify site named superset-bundle-analyzer and add its site ID as
# that secret to turn this on; nothing else in this workflow depends
# on it.
- name: Publish bundle analyzer report to Netlify
if: ${{ env.NETLIFY_SITE_ID != '' }}
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
run: |
mkdir -p netlify-publish
cp superset/static/assets/report.html netlify-publish/index.html
# zizmor: ignore[adhoc-packages] - netlify-cli is a one-shot CI deploy
# tool, not an application dependency; a global/npx install has no
# lockfile context. Version pinned above the floor set by other
# ad-hoc installs in this repo (bump deliberately when upgrading).
npx --yes netlify-cli@27.0.1 deploy --prod --dir=netlify-publish
@@ -45,5 +45,8 @@ jobs:
- name: Run Script
run: bash .github/workflows/github-action-validator.sh
- name: Test docs-deploy freshness gate
run: bash .github/workflows/scripts/check-docs-deploy-freshness.test.sh
- name: Check for security issues on GHA workflows
uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2
+3
View File
@@ -7,6 +7,9 @@ on:
pull_request:
types: [opened, edited]
permissions:
contents: read
jobs:
superbot-orglabel:
runs-on: ubuntu-slim
+3
View File
@@ -7,6 +7,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
labeler:
permissions:
+3
View File
@@ -3,6 +3,9 @@ on:
release:
types: [published] # This makes it run only when a new released is published
permissions:
contents: read
jobs:
latest-release:
name: Add/update tag to new release
+1 -1
View File
@@ -93,7 +93,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
- name: Copy image to GHCR
env:
+3
View File
@@ -19,6 +19,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: false
permissions:
contents: read
jobs:
lint-check:
runs-on: ubuntu-slim
+49
View File
@@ -0,0 +1,49 @@
#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Shared freshness gate used by the Docs Deployment workflow
# (superset-docs-deploy.yml) both up front (check-freshness) and again right
# before the deploy step (recheck-freshness). Writes an output declaring
# whether BUILD_SHA is still master's current tip, so a superseded run can
# skip cleanly instead of racing (and clobbering, or being force-cancelled
# by) a fresher run.
#
# Required env vars:
# BUILD_SHA - the commit SHA this run is building
# REPO - "owner/repo" to query, e.g. github.repository
# OUTPUT_NAME - the GITHUB_OUTPUT key to write, e.g. "is-current"
# GITHUB_OUTPUT - path to append outputs to (set by the Actions runner)
# Optional env vars:
# EVENT_NAME - if "workflow_dispatch", bypasses the check and always
# reports current, since a manual dispatch is a deliberate,
# one-off action rather than something racing other triggers
# GH_TOKEN - passed through to `gh`, needed to call the GitHub API
set -euo pipefail
if [ "${EVENT_NAME:-}" = "workflow_dispatch" ]; then
echo "${OUTPUT_NAME}=true" >>"$GITHUB_OUTPUT"
exit 0
fi
latest_sha="$(gh api "repos/${REPO}/commits/master" --jq .sha)"
if [ "${latest_sha}" = "${BUILD_SHA}" ]; then
echo "${OUTPUT_NAME}=true" >>"$GITHUB_OUTPUT"
else
echo "${OUTPUT_NAME}=false" >>"$GITHUB_OUTPUT"
echo "::notice::master has moved on to ${latest_sha} since ${BUILD_SHA} was triggered — skipping this stale run."
fi
@@ -0,0 +1,100 @@
#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Exercises check-docs-deploy-freshness.sh against a stubbed `gh`, covering
# the dispatch-bypass, current-tip and stale-tip branches so the output
# contract (is-current / still-current) can't silently regress. Run
# directly, no extra tooling required:
# bash .github/workflows/scripts/check-docs-deploy-freshness.test.sh
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
script_under_test="${script_dir}/check-docs-deploy-freshness.sh"
failures=0
# Runs the script under test with a stubbed `gh` reporting $1 as master's
# latest sha, asserting that GITHUB_OUTPUT ends up containing exactly $4.
run_case() {
local case_name="$1"
local latest_sha="$2"
local build_sha="$3"
local event_name="$4"
local expected_line="$5"
local workdir
workdir="$(mktemp -d)"
trap 'rm -rf "${workdir}"' RETURN
# Fake `gh` that just echoes back the requested "latest" sha regardless of
# arguments, so the script under test never touches the network.
cat >"${workdir}/gh" <<EOF
#!/bin/bash
echo '${latest_sha}'
EOF
chmod +x "${workdir}/gh"
local output_file="${workdir}/github_output"
: >"${output_file}"
if PATH="${workdir}:${PATH}" \
GITHUB_OUTPUT="${output_file}" \
OUTPUT_NAME="is-current" \
REPO="apache/superset" \
BUILD_SHA="${build_sha}" \
EVENT_NAME="${event_name}" \
GH_TOKEN="fake-token" \
bash "${script_under_test}"; then
:
else
echo "FAIL (${case_name}): script exited non-zero"
failures=$((failures + 1))
return
fi
local actual
actual="$(cat "${output_file}")"
if [ "${actual}" = "${expected_line}" ]; then
echo "PASS (${case_name})"
else
echo "FAIL (${case_name}): expected '${expected_line}', got '${actual}'"
failures=$((failures + 1))
fi
}
# `gh` prints "should-not-be-called" for the dispatch case above the trick:
# it's never actually invoked since the bypass short-circuits before the
# `gh api` call, but the fake still needs a body.
run_case "workflow_dispatch bypasses the check" \
"unused" "abc123" "workflow_dispatch" \
"is-current=true"
run_case "build sha matches master's tip" \
"abc123" "abc123" "push" \
"is-current=true"
run_case "build sha is stale" \
"def456" "abc123" "push" \
"is-current=false"
if [ "${failures}" -gt 0 ]; then
echo "${failures} case(s) failed"
exit 1
fi
echo "All cases passed"
+3
View File
@@ -17,6 +17,9 @@ env:
GITHUB_ORG: ${{ github.repository_owner }}
GITHUB_REPO: ${{ github.event.repository.name }}
permissions:
contents: read
jobs:
cleanup-expired:
name: Clean up expired showtime environments
+3
View File
@@ -26,6 +26,9 @@ env:
GITHUB_REPO: ${{ github.event.repository.name }}
GITHUB_ACTOR: ${{ github.actor }}
permissions:
contents: read
jobs:
sync:
name: 🎪 Sync PR to desired state
+72 -13
View File
@@ -18,16 +18,6 @@ on:
workflow_dispatch: {}
# Serialize deploys: the action pushes to apache/superset-site without
# rebasing, so concurrent runs race on the final push and the loser fails
# with `! [rejected] asf-site -> asf-site (fetch first)`. Cancel any
# in-progress run as soon as a newer one starts — the destination repo
# isn't touched until the final push step, so canceling mid-build is safe,
# and the freshest content always wins.
concurrency:
group: docs-deploy-asf-site
cancel-in-progress: true
permissions:
contents: read
actions: read
@@ -48,17 +38,69 @@ jobs:
env:
SUPERSET_SITE_BUILD: ${{ (secrets.SUPERSET_SITE_BUILD != '' && secrets.SUPERSET_SITE_BUILD != '') || '' }}
# Master gets frequent, sometimes bursty pushes, and each one can trigger a
# deploy attempt. Rather than let every superseded attempt get force-killed
# by the build-deploy concurrency group below (which shows up as a
# `cancelled` — i.e. red/failing-looking — check on that commit), have each
# run check up front whether it's still building master's current tip and,
# if not, skip cleanly. Deliberately outside the docs-deploy-asf-site
# concurrency group so it runs immediately for every trigger without
# blocking or being blocked by anything.
check-freshness:
runs-on: ubuntu-26.04
outputs:
is-current: ${{ steps.check.outputs.is-current }}
steps:
# Sparse checkout: this job's only job is to be fast, so it fetches
# nothing but the freshness-check script itself.
- name: Checkout freshness-check script
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
sparse-checkout: |
.github/workflows/scripts
sparse-checkout-cone-mode: false
- name: "Check whether this is still master's current commit"
id: check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_SHA: ${{ github.event.workflow_run.head_sha || github.sha }}
EVENT_NAME: ${{ github.event_name }}
REPO: ${{ github.repository }}
OUTPUT_NAME: is-current
run: .github/workflows/scripts/check-docs-deploy-freshness.sh
build-deploy:
needs: config
needs: [config, check-freshness]
# Only the run for master's current tip proceeds; anything superseded
# already skipped at check-freshness above instead of landing here.
# For workflow_run triggers, only deploy when the triggering run originated
# from this repository (not a fork), ensuring the checked-out code and any
# local actions executed with deploy credentials are trusted.
if: >-
needs.config.outputs.has-secrets &&
needs.check-freshness.outputs.is-current == 'true' &&
(github.event_name != 'workflow_run' ||
github.event.workflow_run.head_repository.full_name == github.repository)
name: Build & Deploy
runs-on: ubuntu-26.04
# Serialize deploys: the action pushes to apache/superset-site without
# rebasing, so concurrent runs race on the final push and the loser fails
# with `! [rejected] asf-site -> asf-site (fetch first)`. Queue instead of
# canceling: a run that already passed check-freshness can still be
# sitting in the queue for a runner when a newer run starts and finishes
# first. cancel-in-progress would let that stale, queued run kill the
# newer run's in-progress deploy the moment it's finally scheduled, and
# then skip itself at the re-check below — losing the deploy entirely.
# Queuing means the stale run just waits its turn and then no-ops at the
# re-check, so the fresher content that already deployed is never
# clobbered or lost. The check-freshness gate above means it should be
# rare for more than one run to reach this point, so the queue stays
# short in practice.
concurrency:
group: docs-deploy-asf-site
cancel-in-progress: false
steps:
- uses: Kesin11/actions-timeline@57fc93f20c6da7fbc14063c6d24a2a5627c799ad # v3.2.0
with:
@@ -99,7 +141,7 @@ jobs:
yarn install --check-cache
- name: Download database diagnostics (if triggered by integration tests)
if: github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success'
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21
uses: dawidd6/action-download-artifact@d63b86af1b34672e53c440b1b83979861906bad7 # v24
continue-on-error: true
with:
workflow: superset-python-integrationtest.yml
@@ -108,7 +150,7 @@ jobs:
path: docs/src/data/
- name: Try to download latest diagnostics (for push/dispatch triggers)
if: github.event_name != 'workflow_run'
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21
uses: dawidd6/action-download-artifact@d63b86af1b34672e53c440b1b83979861906bad7 # v24
continue-on-error: true
with:
workflow: superset-python-integrationtest.yml
@@ -130,7 +172,24 @@ jobs:
working-directory: docs
run: |
yarn build
# The check-freshness job above narrows the window but doesn't close it: an
# older run can observe is-current=true, then sit through this build while a
# newer run's own freshness check also passes and it deploys and finishes
# first. If this (stale) run then wins entry into the concurrency group, it
# would overwrite the newer content that already deployed. Re-check right
# before the one step that actually mutates superset-site, so a stale run
# skips deploying instead of clobbering a fresher one that already ran.
- name: "Re-check freshness immediately before deploying"
id: recheck-freshness
if: github.event_name != 'workflow_dispatch'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_SHA: ${{ github.event.workflow_run.head_sha || github.sha }}
REPO: ${{ github.repository }}
OUTPUT_NAME: still-current
run: .github/workflows/scripts/check-docs-deploy-freshness.sh
- name: deploy docs
if: github.event_name == 'workflow_dispatch' || steps.recheck-freshness.outputs.still-current == 'true'
uses: ./.github/actions/github-action-push-to-another-repository
env:
API_TOKEN_GITHUB: ${{ secrets.SUPERSET_SITE_BUILD }}
+1 -1
View File
@@ -140,7 +140,7 @@ jobs:
run: |
yarn install --check-cache
- name: Download database diagnostics from integration tests
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21
uses: dawidd6/action-download-artifact@d63b86af1b34672e53c440b1b83979861906bad7 # v24
with:
workflow: superset-python-integrationtest.yml
run_id: ${{ github.event.workflow_run.id }}
+7 -4
View File
@@ -26,6 +26,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
changes:
runs-on: ubuntu-26.04
@@ -75,14 +78,14 @@ jobs:
USE_DASHBOARD: ${{ github.event.inputs.use_dashboard == 'true' || 'false' }}
services:
postgres:
image: postgres:17-alpine
image: ghcr.io/apache/superset/ci/postgres:17-alpine
env:
POSTGRES_USER: superset
POSTGRES_PASSWORD: superset
ports:
- 15432:5432
redis:
image: redis:7-alpine
image: ghcr.io/apache/superset/ci/redis:7-alpine
ports:
- 16379:6379
steps:
@@ -183,14 +186,14 @@ jobs:
GITHUB_TOKEN: ${{ github.token }}
services:
postgres:
image: postgres:17-alpine
image: ghcr.io/apache/superset/ci/postgres:17-alpine
env:
POSTGRES_USER: superset
POSTGRES_PASSWORD: superset
ports:
- 15432:5432
redis:
image: redis:7-alpine
image: ghcr.io/apache/superset/ci/redis:7-alpine
ports:
- 16379:6379
steps:
+97
View File
@@ -212,3 +212,100 @@ jobs:
- uses: Kesin11/actions-timeline@57fc93f20c6da7fbc14063c6d24a2a5627c799ad # v3.2.0
with:
expand-composite-actions: true
# Compares a PR's own bundle size against the last nightly-recorded
# baseline (see frontend-bundle-size-nightly.yml, which owns actually
# persisting new baselines). PR-only: a push to master doesn't need this
# check re-run against itself, and re-persisting the baseline on every
# push to master -- which happens many times a day -- would burn a full
# production build for no benefit nightly refresh doesn't already cover.
bundle-size:
needs: frontend-build
if: needs.frontend-build.outputs.should-run == 'true' && github.event_name == 'pull_request'
runs-on: ubuntu-26.04
timeout-minutes: 15
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout Code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
- name: Download Docker Image Artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: docker-image
- name: Load Docker Image
run: |
zstd -d < docker-image.tar.zst | docker load
# webpack's persistent filesystem cache (superset-frontend/webpack.config.js)
# turns a warm production build into ~20s instead of several minutes,
# but GH-hosted runners are fresh VMs with nothing carried over between
# jobs -- without restoring it explicitly, every single PR would pay
# the full cold-build cost. Keyed on the same files webpack's own
# `buildDependencies` invalidates on, so a stale cache is never used.
- name: Restore webpack build cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: superset-frontend/.temp_cache
key: >-
webpack-prod-cache-${{ hashFiles('superset-frontend/package-lock.json',
'superset-frontend/babel.config.js', 'superset-frontend/tsconfig.json',
'superset-frontend/webpack.config.js') }}
# Only ever pull the last recorded data point off the cache, keyed by
# run ID -- `restore-keys` prefix-matches the most recently created
# entry, which is always the latest nightly run. Absent before the
# first nightly run ever happens; benchmark-action starts a fresh
# history in that case.
- name: Restore bundle size history
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: bundle-size-history.json
key: bundle-size-history-${{ github.run_id }}
restore-keys: |
bundle-size-history-
- name: Build production bundle with stats
run: |
mkdir -p ${{ github.workspace }}/superset-frontend/bundle-stats
mkdir -p ${{ github.workspace }}/superset-frontend/.temp_cache
docker run \
-v ${{ github.workspace }}/superset-frontend/bundle-stats:/app/superset-frontend/bundle-stats \
-v ${{ github.workspace }}/superset-frontend/.temp_cache:/app/superset-frontend/.temp_cache \
--rm $TAG \
bash -c \
"npm i && BUNDLE_SIZE_STATS=true npm run build -- --json=bundle-stats/stats.json"
- name: Summarize bundle size
run: |
node superset-frontend/scripts/bundle-size-summary.js \
superset-frontend/bundle-stats/stats.json > bundle-size-summary.json
rm -rf superset-frontend/bundle-stats
# Comparison + alert only -- this job never persists. See
# frontend-bundle-size-nightly.yml for why.
#
# comment-on-alert is gated to same-repo PRs: on a fork PR,
# GITHUB_TOKEN is forced read-only regardless of the `permissions`
# block above, so once the alert threshold is crossed the action's
# `pulls.createReview` call 403s. That error isn't gated by
# fail-on-alert (which only governs the deliberate alert-threshold
# failure) -- it propagates and fails the job outright. Fork PRs
# still get the comparison via the job summary (summary-always).
- name: Compare bundle size against nightly baseline
uses: benchmark-action/github-action-benchmark@52576c92bccf6ac60c8223ec7eb2565637cae9ba # v1.22.1
with:
tool: customSmallerIsBetter
output-file-path: bundle-size-summary.json
external-data-json-path: bundle-size-history.json
github-token: ${{ secrets.GITHUB_TOKEN }}
comment-on-alert: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
alert-threshold: "110%"
fail-on-alert: false
summary-always: true
@@ -27,6 +27,9 @@ concurrency:
group: helm-release
cancel-in-progress: false
permissions:
contents: read
jobs:
release:
runs-on: ubuntu-26.04
@@ -22,6 +22,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
changes:
runs-on: ubuntu-26.04
@@ -53,9 +53,7 @@ jobs:
mysql+mysqldb://superset:superset@127.0.0.1:13306/superset?charset=utf8mb4&binary_prefix=true
services:
mysql:
image: mysql:8.0
# Authenticated pulls use our higher Docker Hub rate limit. Empty on
# fork PRs (secrets unavailable) -> runner falls back to anonymous.
image: ghcr.io/apache/superset/ci/mysql:8.0
env:
MYSQL_ROOT_PASSWORD: root
ports:
@@ -66,7 +64,7 @@ jobs:
--health-timeout=5s
--health-retries=5
redis:
image: redis:7-alpine
image: ghcr.io/apache/superset/ci/redis:7-alpine
options: --entrypoint redis-server
ports:
- 16379:6379
@@ -143,7 +141,7 @@ jobs:
SUPERSET__SQLALCHEMY_DATABASE_URI: postgresql+psycopg2://superset:superset@127.0.0.1:15432/superset
services:
postgres:
image: postgres:17-alpine
image: ghcr.io/apache/superset/ci/postgres:17-alpine
env:
POSTGRES_USER: superset
POSTGRES_PASSWORD: superset
@@ -152,7 +150,7 @@ jobs:
# GitHub action runner's default installations
- 15432:5432
redis:
image: redis:7-alpine
image: ghcr.io/apache/superset/ci/redis:7-alpine
ports:
- 16379:6379
steps:
@@ -202,7 +200,7 @@ jobs:
sqlite:///${{ github.workspace }}/.temp/examples.db?check_same_thread=true
services:
redis:
image: redis:7-alpine
image: ghcr.io/apache/superset/ci/redis:7-alpine
ports:
- 16379:6379
steps:
@@ -14,6 +14,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
changes:
runs-on: ubuntu-26.04
@@ -49,7 +52,7 @@ jobs:
SUPERSET__SQLALCHEMY_EXAMPLES_URI: presto://localhost:15433/memory/default
services:
postgres:
image: postgres:17-alpine
image: ghcr.io/apache/superset/ci/postgres:17-alpine
env:
POSTGRES_USER: superset
POSTGRES_PASSWORD: superset
@@ -58,7 +61,7 @@ jobs:
# GitHub action runner's default installations
- 15432:5432
presto:
image: starburstdata/presto:350-e.6
image: ghcr.io/apache/superset/ci/presto:350-e.6
env:
POSTGRES_USER: superset
POSTGRES_PASSWORD: superset
@@ -67,7 +70,7 @@ jobs:
# GitHub action runner's default installations
- 15433:8080
redis:
image: redis:7-alpine
image: ghcr.io/apache/superset/ci/redis:7-alpine
ports:
- 16379:6379
steps:
@@ -113,7 +116,7 @@ jobs:
UPLOAD_FOLDER: /tmp/.superset/uploads/
services:
postgres:
image: postgres:17-alpine
image: ghcr.io/apache/superset/ci/postgres:17-alpine
env:
POSTGRES_USER: superset
POSTGRES_PASSWORD: superset
@@ -122,7 +125,7 @@ jobs:
# GitHub action runner's default installations
- 15432:5432
redis:
image: redis:7-alpine
image: ghcr.io/apache/superset/ci/redis:7-alpine
ports:
- 16379:6379
steps:
@@ -53,12 +53,6 @@ jobs:
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["current", "next"]') }}
env:
PYTHONPATH: ${{ github.workspace }}
# Promotes the SQLAlchemy 2.0 deprecation warnings already locked in as
# errors via pytest.ini's `filterwarnings` to actually run in CI, so a
# regression on those fails the build instead of relying on a
# contributor remembering to set this locally. See the migration
# battleplan: https://github.com/apache/superset/discussions/40273
SQLALCHEMY_WARN_20: "1"
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -75,13 +69,18 @@ jobs:
SUPERSET_SECRET_KEY: not-a-secret
run: |
pytest --durations-min=0.5 --cov-report= --cov=superset ./tests/common ./tests/unit_tests --cache-clear --maxfail=50 --junit-xml=test-results/junit-unit.xml
# COVERAGE_FILE keeps these scoped gates off the default .coverage that
# the step above wrote. pytest-cov starts a fresh data file per run, so
# without it the last gate replaces the full-suite data and the report
# uploaded below contains only that gate's subtree -- every other file
# then reads as uncovered, and patch coverage on an unrelated PR is 0%.
- name: Python 100% coverage unit tests
env:
SUPERSET_TESTENV: true
SUPERSET_SECRET_KEY: not-a-secret
run: |
pytest --durations-min=0.5 --cov=superset/sql/ ./tests/unit_tests/sql/ --cache-clear --cov-fail-under=100 --junit-xml=test-results/junit-sql-coverage.xml
pytest --durations-min=0.5 --cov=superset/semantic_layers/ ./tests/unit_tests/semantic_layers/ --cache-clear --cov-fail-under=100 --junit-xml=test-results/junit-semantic-layers-coverage.xml
COVERAGE_FILE="${RUNNER_TEMP}/.coverage.sql" pytest --durations-min=0.5 --cov=superset/sql/ ./tests/unit_tests/sql/ --cache-clear --cov-fail-under=100 --junit-xml=test-results/junit-sql-coverage.xml
COVERAGE_FILE="${RUNNER_TEMP}/.coverage.semantic_layers" pytest --durations-min=0.5 --cov=superset/semantic_layers/ ./tests/unit_tests/semantic_layers/ --cache-clear --cov-fail-under=100 --junit-xml=test-results/junit-semantic-layers-coverage.xml
- name: Upload code coverage
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
+3
View File
@@ -13,6 +13,9 @@ on:
required: true
type: string
permissions:
contents: read
jobs:
supersetbot:
runs-on: ubuntu-26.04
+3
View File
@@ -5,6 +5,9 @@ on:
pull_request_target:
types: [opened]
permissions:
contents: read
jobs:
welcome:
runs-on: ubuntu-slim
+1
View File
@@ -10,6 +10,7 @@
.stylelintignore
.flake8
.nvmrc
.npmrc
.rat-excludes
.swcrc
.*log
+2
View File
@@ -31,6 +31,8 @@ under the License.
[![Open PRs](https://img.shields.io/github/issues-pr/apache/superset)](https://github.com/apache/superset/pulls)
[![Get on Slack](https://img.shields.io/badge/slack-join-orange.svg)](https://bit.ly/join-superset-slack)
[![Documentation](https://img.shields.io/badge/docs-apache.org-blue.svg)](https://superset.apache.org)
[![Storybook](https://img.shields.io/badge/storybook-live-ff4785.svg)](https://superset-storybook.netlify.app)
[![Bundle Analyzer](https://img.shields.io/badge/bundle%20analyzer-nightly-8dd6f9.svg)](https://superset-bundle-analyzer.netlify.app)
<picture width="500">
<source
+63 -12
View File
@@ -24,6 +24,32 @@ assists people when migrating to a new version.
## Next
- `SAMPLES_ROW_LIMIT` is now the default for `/datasource/samples` requests without a valid explicit `per_page`, rather than a hard per-request ceiling; explicit limits are honored up to the existing global row-limit ceiling, matching `/chart/data` SAMPLES requests.
- The `cockroachdb` extra (`pip install apache-superset[cockroachdb]`) now installs `sqlalchemy-cockroachdb` instead of the abandoned `cockroachdb` package, whose SQLAlchemy dialect could not be imported under SQLAlchemy 2.0. Existing environments with the old package installed should `pip uninstall cockroachdb && pip install sqlalchemy-cockroachdb` (or simply reinstall the extra) to restore CockroachDB connectivity.
### MCP tool results preserve stored string values
Structured MCP tool results no longer add `<UNTRUSTED-CONTENT>` wrappers or
rewrite delimiter-looking text inside string fields. Tool-result content remains
user-controlled data, but clients must convey that trust boundary outside domain
values instead of recognizing or removing marker strings.
Clients that handled the former delimiter convention should stop stripping marker
text: the same text can be legitimate stored content. Response models and content
types are unchanged, and no metadata-database migration is required. Automated
read-modify-write workflows should be paused or pinned away from older instances
until every serving instance is upgraded; a mixed-version response has no reliable
signal that tells a client whether its text is decorated. Redis-backed MCP response
caches use a new internal namespace after the upgrade, so upgraded instances do not
reuse older cached results.
Values that a client already wrote back with presentation wrappers cannot be
distinguished safely from intentional content. Operators should review possible
`<UNTRUSTED-CONTENT>` / `</UNTRUSTED-CONTENT>` wrappers and
`[ESCAPED-UNTRUSTED-CONTENT-OPEN]` /
`[ESCAPED-UNTRUSTED-CONTENT-CLOSE]` substitutions rather than applying an automatic
marker-removal migration.
### OAuth2 database callback metrics include their outcome
The unqualified `DatabaseRestApi.oauth2` StatsD counter has been replaced with
@@ -31,7 +57,10 @@ The unqualified `DatabaseRestApi.oauth2` StatsD counter has been replaced with
`DatabaseRestApi.oauth2.error`. Update monitoring rules and dashboards that consume
the old counter to use the outcome-specific replacements.
- [42930](https://github.com/apache/superset/pull/42930): Dataset import data-URI fetches no longer honor an HTTP(S) proxy when `DATASET_IMPORT_ALLOW_INTERNAL_DATA_URLS` is `False` (the default): the connection is now made directly to the destination so the peer-address check validates the real target instead of a proxy's. Deployments that require an egress proxy to reach legitimate external data URLs for dataset import should set `DATASET_IMPORT_ALLOW_INTERNAL_DATA_URLS = True` or otherwise ensure those URLs resolve without one.
- [42935](https://github.com/apache/superset/pull/42935): The MCP service now refuses to start (`MCPAuthConfigError`) when `MCP_JWT_ISSUER` trusts more than one issuer and no `MCP_USER_RESOLVER` is configured, instead of only logging a warning. This was already a documented misconfiguration (the default resolver isn't issuer-scoped, so distinct trusted issuers minting the same username/email would resolve to the same Superset user); deployments trusting multiple issuers must configure an `MCP_USER_RESOLVER` that derives its identity from the token's `iss` claim before upgrading. Single-issuer deployments are unaffected.
- [42429](https://github.com/apache/superset/pull/42429): The Country Map chart's Iran GeoJSON now gives Alborz province its own ISO 3166-2 code, `IR-32`, instead of `IR-30`. `ISO` is the join key used to color/filter provinces on this chart, so any existing dataset keyed on `IR-30` for Alborz will silently stop matching after upgrading; re-key that data to `IR-32`.
- [43388](https://github.com/apache/superset/pull/43388): The MCP service now refuses to start (`MCPAuthConfigError`) if `MCP_DEV_USERNAME` and `MCP_AUTH_ENABLED = True` are both set, and separately if `MCP_AUTH_ENABLED = True` but no usable JWT key material is configured (RSA key/JWKS, or an explicit `MCP_JWT_SECRET` for HMAC) — both previously started with authentication silently weaker than configured. Deployments combining a dev-mode username with JWT auth enabled, or enabling JWT auth without key material, must pick one before upgrading: unset `MCP_DEV_USERNAME` for a real auth deployment, or unset `MCP_AUTH_ENABLED` (or configure the key material) for a dev-mode one. Response caching (`MCP_CACHE_CONFIG["enabled"] = True`) now also excludes every tool with a side effect by default, not only a partial list, so a previously-cached mutating tool call is no longer served from cache; no config change is needed to pick this up.
- [42393](https://github.com/apache/superset/pull/42393): Exported dataset YAML now carries a `uuid` for each metric and column so that custom folder assignments (which reference metrics/columns by UUID) survive an import into another workspace. This affects any export bundle that contains datasets, not just a dataset export: chart, dashboard, database and full-asset exports all embed the same dataset YAML, so a dashboard exported from this release also fails to import into an older one even though no dataset was exported directly. As with `folders` and `currency_code_column`, the affected `datasets/` files fail schema validation (`Unknown field: uuid`) when imported into Superset releases that predate this change; regenerate or hand-edit exports for older targets in mixed-version fleets.
- [42300](https://github.com/apache/superset/pull/42300): Timeseries charts (line/area/bar) with a Y-axis bound in effect — either an explicit `yAxisBounds` or one derived from `truncateYAxis` — now clamp out-of-range data points to that bound instead of letting ECharts drop the point (and the line segments around it) entirely. Any existing chart with a configured Y-axis bound and data outside it will look different after upgrading: a gap becomes a point pinned to the boundary. The clamp also rewrites the value ECharts reads for that point's tooltip and data label, so the displayed value is the bound rather than the true observation.
- [42087](https://github.com/apache/superset/pull/42087): Stored calculated-column and metric expressions are validated when a query is built, under the same sub-query policy already applied to adhoc expressions. Previously only the dataset update path checked them on save, so expressions written by v1 import, by dataset duplication, or before that check existed were never validated. Since `ALLOW_ADHOC_SUBQUERY` defaults to `False` (see [19242](https://github.com/apache/superset/pull/19242)), a dataset whose stored expression contains a sub-query works before upgrading and afterwards fails at chart render with `Custom SQL fields cannot contain sub-queries.` There is no migration step, and the error does not name the offending dataset column, so audit stored expressions before upgrading: either rewrite them without the sub-query, or set `ALLOW_ADHOC_SUBQUERY = True` to keep the previous behaviour for both stored and adhoc expressions.
@@ -99,6 +128,23 @@ dialect; each package's constraint in `pyproject.toml` documents why.
No application-level configuration changes are required for deployments
that don't touch SQLAlchemy directly.
### New metric aggregates: MEDIAN, Sample Standard Deviation, Sample Variance
`MEDIAN`, `STDDEV_SAMP`, and `VAR_SAMP` are now available anywhere a metric
aggregate is chosen (every chart type, SQL Lab, MCP), not only in Pivot
Table's controls. Support is opt-in per database engine *spec class*,
verified against a live instance before being enabled: Postgres, MySQL
(`STDDEV_SAMP`/`VAR_SAMP` only, no `MEDIAN`), DuckDB, and Redshift (inherits
Postgres's support, not yet separately verified) ship enabled in this
release. Engine specs that subclass one of those (e.g. MariaDB, Aurora
MySQL/Postgres, TimescaleDB) inherit the same support, on the same
not-yet-independently-verified basis. Picking one of these aggregates on a
database that has not opted in returns a clear "not supported on this
database" error rather than a failed query. See
`docs/sip/median-stddev-variance-aggregates.md` for the full design
rationale, including why this is safe to add without reintroducing the
totals/subtotals correctness bug fixed by #41184 (SIP-216).
### Soft delete is on by default, and purging is live
`SOFT_DELETE` now ships **on** (`DEFAULT_FEATURE_FLAGS`), so deleting a
@@ -140,10 +186,12 @@ misrepresents the entity as unchanged.
- **Storage growth.** Capture writes shadow rows per save, so the metadata
database grows with edit volume. The `version_history.prune_old_versions`
beat task removes rows whose transaction is older than
`SUPERSET_VERSION_HISTORY_RETENTION_DAYS` (default 30). A deployment that
replaces `CELERY_CONFIG` rather than inheriting it must carry both the
`superset.tasks.version_history_retention` import and the beat entry; a
startup warning names whichever is absent.
`SUPERSET_VERSION_HISTORY_RETENTION_DAYS` (default 30).
- **Check a replaced `CELERY_CONFIG`.** Carry both the
`superset.tasks.version_history_retention` import and the
`version_history.prune_old_versions` beat entry; see
[Version-history retention (pruning)](#version-history-retention-pruning) for
the startup-warning behavior.
- **`PUT` responses change shape.** Entity updates now return populated
`old_version_uuid` / `new_version_uuid` fields and an `ETag` header, which
were null or absent while capture was off.
@@ -152,7 +200,10 @@ misrepresents the entity as unchanged.
kill-switch — not removed with the rollout toggles. Setting it to a falsy value
stops capture within a restart, without a revert-and-redeploy. Unlike the
soft-delete toggle, turning it off is a clean stop: existing version rows remain
readable and no entity state is altered.
readable and no entity state is altered. Restore is unavailable (404) while
capture is off. A full rollback also sets
`FEATURE_FLAGS = {"VERSION_HISTORY": False}` to hide the panel — capture off
with the panel left on shows an empty or stale history.
### Scheduled report execution now enforces one application deadline
@@ -614,9 +665,9 @@ ALTER TABLE tagged_object DROP CONSTRAINT <constraint_name>;
ALTER TABLE tagged_object DROP FOREIGN KEY <constraint_name>;
```
### Entity version-history infrastructure (gated off by default)
### Entity version-history infrastructure
Introduces the schema and SQLAlchemy-Continuum wiring that captures version history for charts, dashboards, and datasets, plus read-only `GET /api/v1/{chart,dashboard,dataset}/<uuid>/versions/` endpoints. This ships **inert**: a new config flag `ENABLE_VERSIONING_CAPTURE` defaults to `False`, so no save writes any version rows and the endpoints return empty. It is an operational kill-switch (a release toggle that becomes a permanent ops switch), not a feature flag — set it to `True` to enable capture once validated. The migration is additive; existing entity `PUT` responses gain `old_version_uuid` / `new_version_uuid` body fields and an `ETag` header (both null/absent when capture is off).
Introduces the schema and SQLAlchemy-Continuum wiring that captures version history for charts, dashboards, and datasets, plus read-only `GET /api/v1/{chart,dashboard,dataset}/<uuid>/versions/` endpoints. Capture is governed by the `ENABLE_VERSIONING_CAPTURE` config value — an operational kill-switch (a release toggle that became a permanent ops switch), not a feature flag; see "Version history is on by default" above for the shipped default. With capture off, no save writes version rows; the endpoints continue to serve already-captured rows read-only. The migration is additive; existing entity `PUT` responses gain `old_version_uuid` / `new_version_uuid` body fields and an `ETag` header (both null/absent when capture is off).
A few save- and import-path internals change **unconditionally** (independent of the flag), because the versioned mappers must behave correctly whether or not capture is enabled:
@@ -637,7 +688,7 @@ A read-only companion to the version-history endpoints: each entity type gains a
| `q` | string | — | Case-insensitive search over the full history, applied before pagination (so `count` reflects matches) |
| `page` / `page_size` | integer | `0` / `25` | Pagination (`page_size` clamped to 200) |
Authorization reuses the resource's `can_read` permission and per-object `raise_for_access`; related-entity rows are visibility-filtered to what the caller may see. The stream is empty unless version capture is on (`ENABLE_VERSIONING_CAPTURE`).
Authorization reuses the resource's `can_read` permission and per-object `raise_for_access`; related-entity rows are visibility-filtered to what the caller may see. The stream reflects captured history; with capture off it remains readable but stops accruing new entries.
### Version-history retention (pruning)
@@ -657,7 +708,7 @@ Purging is **live by default** (`SOFT_DELETE_PURGE_DRY_RUN=False`), so the reten
Deployments that replace the default `CELERY_CONFIG` must ensure workers register `superset.tasks.deletion_retention` and schedule the `deletion_retention.purge_soft_deleted` task themselves. The shipped Docker development config uses `imports` and includes both entries. While `SOFT_DELETE` is statically enabled, a missing beat entry logs a startup warning; when the override explicitly defines `imports`, a missing purge module is also reported.
Operators can immediately erase a specific entity for compliance (GDPR) via `superset deletion-retention force-purge --uuid <uuid>`; this applies legacy hard-delete semantics — a live chart referencing a force-purged dataset is left without a datasource until re-pointed (the chart is not modified), and it purges the named entity even when it was never soft-deleted. Every scheduled evaluation writes a provisional, content-free record to the new `purge_audit_log` table before the cascade starts. Meaningful retained outcomes survive the entity they name. Consecutive scheduled evaluations with the same blocked outcome suppress only the redundant current provisional record; completed outcomes, outcome transitions, and every force-purge attempt remain independent and immutable. The **scheduled** purge fails closed when its provisional record cannot be written, while **force-purge** proceeds even if the audit write fails — the operator is present and deletion outranks audit for a compliance erasure. Operators can monitor `deletion_retention.blocked_audit_suppressed` and `deletion_retention.blocked_audit_dedupe_fallback` to verify suppression and fail-safe fallback behavior without changing the existing blocked-workload gauge.
Operators can immediately erase a specific entity for compliance (GDPR) via `superset deletion-retention force-purge --uuid <uuid>`; this applies legacy hard-delete semantics — a live chart referencing a force-purged dataset is left without a datasource until re-pointed (the chart is not modified), and it purges the named entity even when it was never soft-deleted. Every scheduled evaluation writes a provisional, content-free record to the new `purge_audit_log` table before the cascade starts. Meaningful retained outcomes survive the entity they name. Blocked audit records carry a stable machine-readable `reason` code (`report_schedule`, `user_attribute`, or `cascade_integrity_failure` for an unexpected cascade failure caused by a database integrity constraint) so the audit table alone answers why an entity was not purged; records finalized before the column existed keep a NULL reason. Apply the migration before rolling out the new code: the audit model declares the column, so a worker on the new code with an un-migrated table fails its write-ahead write and the scheduled purge fails closed until the migration lands. During a rolling deploy, workers still on the old code write reason-less blocked rows and suppress on status alone; both effects are self-healing, since a NULL-reason record never matches a reason code and the next all-new-code run re-anchors the entity. Consecutive scheduled evaluations blocked with the same status **and reason** suppress only the redundant current provisional record — a reason change writes one new blocked record carrying the new code; completed outcomes, outcome transitions, and every force-purge attempt remain independent and immutable. Retained transition records are not automatically expired, so entities whose block reason changes repeatedly can accumulate multiple audit rows. The **scheduled** purge fails closed when its provisional record cannot be written, while **force-purge** proceeds even if the audit write fails — the operator is present and deletion outranks audit for a compliance erasure. Operators can monitor `deletion_retention.blocked_audit_suppressed` and `deletion_retention.blocked_audit_dedupe_fallback` to verify suppression and fail-safe fallback behavior without changing the existing blocked-workload gauge.
### Recently Archived view and permanent delete (purge) endpoints
@@ -853,7 +904,7 @@ The migration is transactional (all-or-nothing) and idempotent — it can be saf
### Soft delete and restore for datasets
**The soft-delete behavior in this section applies only when the `SOFT_DELETE` feature flag is enabled. The flag defaults to `False`** (`@lifecycle: development`), so on a default deployment `DELETE /api/v1/dataset/<id>` continues to **hard-delete permanently** — nothing is recoverable. Enable `SOFT_DELETE` to get the behavior described below.
**The soft-delete behavior in this section applies only when the `SOFT_DELETE` feature flag is enabled. The flag defaults to `True`** (`@lifecycle: testing`), so on a default deployment `DELETE /api/v1/dataset/<id>` uses the recoverable soft-delete behavior described below. Setting `SOFT_DELETE` to `False` restores legacy permanent hard-delete behavior for subsequent deletes.
**Flag-toggle caveat:** the soft-delete visibility filter is evaluated per query while the flag is on. If datasets are soft-deleted during a flag-on window and the flag is later turned **off**, those rows reappear as live datasets in all lists, lookups, and relationship loads (including charts that reference them). The `POST /<uuid>/restore` endpoint and the `dataset_deleted_state` list filter remain functional regardless of the flag, deliberately, so rows soft-deleted during a flag-on window stay discoverable and restorable after a rollback of the flag.
@@ -883,7 +934,7 @@ With the flag enabled: `DELETE /api/v1/dataset/<id>` no longer hard-deletes the
### Soft delete and restore for charts
**Everything in this section applies only when the `SOFT_DELETE` feature flag is enabled. The flag defaults to `False`** (`@lifecycle: development`), so on a default deployment `DELETE /api/v1/chart/<id>` continues to **hard-delete permanently** — nothing is recoverable. Enable `SOFT_DELETE` to get the behavior described below.
**Everything in this section applies only when the `SOFT_DELETE` feature flag is enabled. The flag defaults to `True`** (`@lifecycle: testing`), so on a default deployment `DELETE /api/v1/chart/<id>` uses the recoverable soft-delete behavior described below. Setting `SOFT_DELETE` to `False` restores legacy permanent hard-delete behavior for subsequent deletes.
**Flag-toggle caveat:** the soft-delete visibility filter is evaluated per query while the flag is on. If charts are soft-deleted during a flag-on window and the flag is later turned **off**, those rows reappear as live charts in all lists, lookups, and relationship loads (including dashboards that contained them). The `POST /<uuid>/restore` endpoint and the `chart_deleted_state` list filter remain functional regardless of the flag, deliberately, so rows soft-deleted during a flag-on window stay discoverable and restorable after a rollback of the flag.
@@ -907,7 +958,7 @@ With the flag enabled: `DELETE /api/v1/chart/<id>` no longer hard-deletes the ch
### Soft delete and restore for dashboards
**Everything in this section applies only when the `SOFT_DELETE` feature flag is enabled. The flag defaults to `False`** (`@lifecycle: development`), so on a default deployment `DELETE /api/v1/dashboard/<id>` continues to **hard-delete permanently** — nothing is recoverable. Enable `SOFT_DELETE` to get the behavior described below.
**Everything in this section applies only when the `SOFT_DELETE` feature flag is enabled. The flag defaults to `True`** (`@lifecycle: testing`), so on a default deployment `DELETE /api/v1/dashboard/<id>` uses the recoverable soft-delete behavior described below. Setting `SOFT_DELETE` to `False` restores legacy permanent hard-delete behavior for subsequent deletes.
**Flag-toggle caveat:** the soft-delete visibility filter is evaluated per query while the flag is on. If dashboards are soft-deleted during a flag-on window and the flag is later turned **off**, those rows reappear as live dashboards in all lists and lookups (including slug lookups — if a soft-deleted dashboard's slug was reused while the flag was on, both rows become visible with the same slug). The `POST /<uuid>/restore` endpoint and the `dashboard_deleted_state` list filter remain functional regardless of the flag, deliberately, so rows soft-deleted during a flag-on window stay discoverable and restorable after a rollback of the flag.
@@ -14,6 +14,20 @@ Users can configure automated alerts and reports to send dashboards or charts to
Alerts and reports are disabled by default. To turn them on, you'll need to change configuration settings and install a suitable headless browser in your environment.
## Content Format Options
When scheduling an alert or report, you can choose the format used to deliver the dashboard or chart:
- **PDF** a full-page screenshot rendered as a PDF attachment. Available for both dashboards and charts.
- **PNG** a screenshot delivered as an attachment. Emails embed the image inline in the message body; Slack and webhook recipients receive it as an uploaded file. Available for both dashboards and charts.
- **CSV** chart data attached as a `.csv` file. Available for charts only.
- **XLSX (Excel)** chart data attached as a `.xlsx` file. Available for charts only. If the chart's data spans multiple server-paginated files, email delivery detects the bundle and renames the attachment to `.zip`; Slack and webhook deliveries always name the file with an `.xlsx` extension even when the contents are a multi-file ZIP archive.
- **Text** chart data embedded directly in the email or Slack message body. Available only for charts using a text-based visualization type (e.g. Table, Pivot Table, Paired t-test).
Dashboard reports and alerts are limited to the PDF and PNG formats; the CSV, XLSX, and Text options are only available when scheduling a report or alert for an individual chart.
For alerts (not scheduled reports), PNG/PDF screenshots and chart CSV/XLSX data are only generated when the `ALERTS_ATTACH_REPORTS` [feature flag](/admin-docs/configuration/configuring-superset#feature-flags) is enabled (the default); with it disabled, an alert notification is still sent, but without the attachment.
## Requirements
### Commons
+21
View File
@@ -76,6 +76,27 @@ value defined in `DATA_CACHE_CONFIG`.
Note, that by setting the cache timeout to `-1`, caching for charting data can be disabled, either
per chart, dataset or database, or by default if set in `DATA_CACHE_CONFIG`.
Native filter option queries (the dropdown values for native filters) go through this same
chart-data cache, but their freshness needs often differ from regular chart queries, especially for
datasets whose visible values change frequently, including RLS-constrained datasets. Set
`NATIVE_FILTER_OPTIONS_CACHE_TIMEOUT` in `superset_config.py` to give these queries a dedicated
timeout, checked before the chart/dataset/database chain and the `DATA_CACHE_CONFIG` default above:
```python
NATIVE_FILTER_OPTIONS_CACHE_TIMEOUT = 60 # seconds
```
- `None` (default): native filter option queries fall through to the normal
chart/dataset/database/`DATA_CACHE_CONFIG` resolution chain.
- `-1`: disables caching for native filter option queries entirely.
- `0`: passed directly to the cache backend; behavior is backend-specific, so use `-1` if the intent
is to disable caching.
- A positive integer: cache native filter option queries for that many seconds.
This setting only applies to requests detected as native filter option queries. It takes precedence
over the per-chart/dataset/database timeouts, but not over an explicit per-request
`custom_cache_timeout` override (e.g. "Force refresh").
## Limiting Cached Result Size
Very large chart or SQL query results can flood the cache backend (Redis/Memcached), evicting many
@@ -97,6 +97,54 @@ for more information on how to configure it.
At the very least, you'll want to change `SECRET_KEY` and `SQLALCHEMY_DATABASE_URI`. Continue reading for more about each of these.
## Localizing D3 date and time labels
`BABEL_DEFAULT_LOCALE` controls Superset's application translations, while
`D3_TIME_FORMAT` provides localized date and time names to visualizations that
use the D3 formatter registry, including Calendar Heatmap. Configure both when
you want the application and chart labels to use the same locale.
`D3_TIME_FORMAT` accepts partial overrides. For example, Russian month names
can be configured in `superset_config.py` as follows:
```python
BABEL_DEFAULT_LOCALE = "ru"
D3_TIME_FORMAT = {
"months": [
"Январь",
"Февраль",
"Март",
"Апрель",
"Май",
"Июнь",
"Июль",
"Август",
"Сентябрь",
"Октябрь",
"Ноябрь",
"Декабрь",
],
"shortMonths": [
"Янв",
"Фев",
"Мар",
"Апр",
"Май",
"Июн",
"Июл",
"Авг",
"Сен",
"Окт",
"Ноя",
"Дек",
],
}
```
Restart Superset after changing `superset_config.py` so the frontend receives
the updated formatter configuration.
## Chart-data query timing
Set `CHART_DATA_INCLUDE_TIMING = True` to add an optional versioned timing object
@@ -83,6 +83,26 @@ The optional username flag **-u** sets the user used for the datasource import.
superset import_datasources -p <path / filename> -u 'admin'
```
## Importing a Directory of Assets
The `import_directory` command imports a directory of exported assets (databases, datasets,
dashboards, charts) in the same layout produced by the ZIP-based export. Saved queries and tags
included in a full export are not imported by this command:
```bash
superset import_directory <path / directory>
```
As with `import_datasources`, the optional username flag **-u** sets the user assigned as the
owner of the imported assets. The default is 'admin'. Example:
```bash
superset import_directory <path / directory> -u 'admin'
```
If the specified user does not exist, the command fails immediately with an error rather than
importing the assets without an owner.
## Dashboard Import Overwrite Behavior
When importing a dashboard ZIP with the **overwrite** option enabled, any existing charts that are part of the dashboard are **replaced** rather than duplicated. This applies to:
+75 -3
View File
@@ -505,6 +505,8 @@ All MCP settings go in `superset_config.py`. Defaults are defined in `superset/m
| `MCP_DEV_USERNAME` | -- | Superset username for development mode (no auth) |
| `MCP_RBAC_ENABLED` | `True` | Enforce Superset's role-based access control on MCP tool calls. When `True`, each tool checks that the authenticated user has the required FAB permission before executing. Disable only for testing or trusted-network deployments. |
| `MCP_DISABLED_TOOLS` | `set()` | Set of tool names to remove from the MCP server at startup. Disabled tools are never advertised to AI clients during tool discovery. Useful when a custom extension tool should replace a built-in Superset tool. See [Disabling built-in tools](#disabling-built-in-tools). |
| `MCP_DISABLED_CHART_PLUGINS` | `frozenset()` | Set of chart type plugin names (e.g. `"handlebars"`) to hide from `generate_chart`. Does not affect `get_chart_type_schema`. See [Disabling chart type plugins](#disabling-chart-type-plugins). |
| `MCP_CHART_PLUGIN_ENABLED_FUNC` | `None` | Callable `(chart_type: str) -> bool` evaluated per registry lookup for dynamic enable/disable decisions. Takes precedence over `MCP_DISABLED_CHART_PLUGINS` when set. See [Disabling chart type plugins](#disabling-chart-type-plugins). |
### Authentication
@@ -576,7 +578,7 @@ MCP_CACHE_CONFIG = {
| Key | Default | Description |
| -------------------- | --------- | ----------------------------------------------------------- |
| `enabled` | `False` | Enable response caching |
| `CACHE_KEY_PREFIX` | `None` | Optional prefix for cache keys (useful for shared Redis) |
| `CACHE_KEY_PREFIX` | `None` | Base prefix for shared Redis; Superset appends an internal response-contract namespace |
| `list_tools_ttl` | `300` | Cache TTL in seconds for `tools/list` |
| `list_resources_ttl` | `300` | Cache TTL for `resources/list` |
| `list_prompts_ttl` | `300` | Cache TTL for `prompts/list` |
@@ -718,6 +720,34 @@ Every MCP request passes through a middleware stack before reaching the tool fun
Additional middleware classes (`RateLimitMiddleware`, `FieldPermissionsMiddleware`, `PrivateToolMiddleware`) are implemented in `superset/mcp_service/middleware.py` but are not added to the default pipeline. They are available for operators who want to layer them in via a custom startup path.
### Tool Result Value Contract
Structured tool results preserve Superset domain values exactly. In particular,
string fields are not wrapped in trust delimiters, and text that resembles a
delimiter is returned as literal application data. This lets clients safely use a
read result as the basis for an update without persisting presentation markup.
All tool-result content should still be treated as user-controlled data with no
instruction authority. MCP clients should communicate that trust boundary through
their model instructions or presentation layer, outside the returned field values;
fixed or generated marker strings inside a value are ambiguous and must not be used
as a trust signal.
For compatibility, clients that supported the former
`<UNTRUSTED-CONTENT>` convention should stop recognizing or stripping those strings.
The response schemas and content types have not changed. Because marker-looking text
can be legitimate application data, a client cannot reliably distinguish a legacy
decorated response from a clean one. Pause automated read-modify-write workflows, or
route them only to upgraded instances, until every serving instance is upgraded.
Redis-backed MCP response caches include an internal response-contract namespace, so
an upgraded instance does not reuse responses cached by an older release. Older
instances can still return legacy values while they remain in service. After the
upgrade, review previously written values for wrapper text and both
`[ESCAPED-UNTRUSTED-CONTENT-OPEN]` and
`[ESCAPED-UNTRUSTED-CONTENT-CLOSE]`; do not remove these strings automatically,
because they may be intentional content.
### Error Sanitization
The `GlobalErrorHandlerMiddleware` automatically redacts sensitive information from all error messages before they reach the LLM client. The following are replaced with generic messages:
@@ -752,11 +782,20 @@ For a 3-pod Kubernetes deployment with the defaults above, expect up to 3 × (5
Enable response caching for read-heavy workloads (dashboards/datasets that don't change frequently). With the in-memory backend (default when `MCP_STORE_CONFIG` is disabled), caching is per-process. Use Redis-backed caching for consistent cache hits across multiple pods:
```python
MCP_CACHE_CONFIG = {"enabled": True, "call_tool_ttl": 3600}
MCP_CACHE_CONFIG = {
"enabled": True,
# Cache keys don't include the requesting principal and hits are served
# ahead of auth/RBAC, so a shared cache can return one caller's response
# to another. Required for caching to actually start -- only appropriate
# when every request is guaranteed to come from the same principal.
"dangerously_share_cache_across_principals": True,
"CACHE_KEY_PREFIX": "mcp_cache_",
"call_tool_ttl": 3600,
}
MCP_STORE_CONFIG = {"enabled": True, "CACHE_REDIS_URL": "redis://redis:6379/0"}
```
Mutating tools (`generate_chart`, `update_chart`, `execute_sql`, `generate_dashboard`) are always excluded from caching regardless of this setting.
Every tool with a side effect (create/update/delete/execute) is always excluded from caching regardless of this setting -- see the `excluded_tools` default in `superset/mcp_service/mcp_config.py` for the current list.
---
@@ -856,6 +895,39 @@ MCP_DISABLED_TOOLS = {"extensions.myorg.myextension.some_tool"}
Specifying a tool name that does not exist logs a warning at startup and is otherwise ignored — it will not prevent the server from starting.
:::
## Disabling chart type plugins
The `generate_chart` tool dispatches per chart type (`xy`, `table`, `pie`, `pivot_table`, `mixed_timeseries`, `handlebars`, `big_number`, `histogram`, `box_plot`, `waterfall`) to a registered chart type plugin. Two settings let operators enable or disable individual chart type plugins at runtime, without a code deploy.
### Static deny-list
Set `MCP_DISABLED_CHART_PLUGINS` in your `superset_config.py` to a set of chart type names:
```python
# superset_config.py
# Emergency kill switch: hide "handlebars" from all callers
MCP_DISABLED_CHART_PLUGINS = {"handlebars"}
```
Disabled chart types stay registered but are filtered out at lookup time: they're never listed in `generate_chart`'s supported chart types, and `generate_chart` calls for them are rejected. `get_chart_type_schema` consults its own static schema/example map rather than the registry filter, so a disabled chart type's schema remains queryable through that tool even though `generate_chart` will reject it.
### Dynamic predicate
For per-request control (A/B tests, gradual rollout, entitlement checks), set `MCP_CHART_PLUGIN_ENABLED_FUNC` to a callable. It's evaluated as `enabled_func(chart_type: str) -> bool` on every registry lookup, and it takes precedence over `MCP_DISABLED_CHART_PLUGINS` when set:
```python
# superset_config.py
from flask import g
def MCP_CHART_PLUGIN_ENABLED_FUNC(chart_type: str) -> bool:
flags = getattr(g, "feature_flags", {})
return flags.get(f"mcp_chart_{chart_type}", True)
```
The callable must be cheap and in-process (consult already-loaded feature flags or request-local context) -- do not perform network I/O per call. If it raises, the registry fails closed (the plugin is hidden) and logs a warning.
## Security Best Practices
- **Use TLS** for all production MCP endpoints -- place the server behind a reverse proxy with HTTPS
+21
View File
@@ -88,6 +88,27 @@ THEME_DARK = {
# - OS preference detection is automatically enabled
```
### Default Theme Mode
By default, Superset mimics the visitor's OS/browser preference (light or dark) for
sessions that don't have a saved user preference. Use `THEME_DEFAULT_MODE` to override
that starting point instance-wide for the standard application:
```python
# Default theme mode for sessions without a saved user preference.
# One of "default" (always light), "dark" (always dark), or "system" (mimic OS preference).
THEME_DEFAULT_MODE = "dark"
```
- `"system"` (the default) preserves the existing behavior of following the OS/browser
preference, provided a dark theme is configured (`THEME_DARK` is not `None`). If no dark
theme is available, Superset always starts in light mode regardless of this setting.
- `"default"` or `"dark"` forces that starting mode for first-time visitors; users can still switch themes manually afterward if both `THEME_DEFAULT` and `THEME_DARK` are available.
- A user's own saved preference, once they toggle the theme switcher, always takes precedence over `THEME_DEFAULT_MODE`.
- `THEME_DEFAULT_MODE` has no effect on embedded dashboards: the embed SDK sets the
starting mode via its own `themeMode` URL parameter, which takes precedence and falls
back to light mode when the parameter is absent.
### App Branding
The application name shown in the browser title bar and navigation can be
+71 -1
View File
@@ -243,6 +243,42 @@ Each subject in the response includes flat scalar ids (`user_id`, `role_id`, `gr
than a nested object, so callers can match directly on whichever id they already have — only the
id field matching the subject's `type` is populated; the others are `null`.
#### Extending Editorship with EXTRA_EDITORS_RESOLVER
Deployments that grant edit access to a dashboard or chart through a mechanism outside
Superset's own Subject-based `editors` list — for example, a folder-permission system or an
internal directory service — can plug that logic in with `EXTRA_EDITORS_RESOLVER`:
```python
def extra_editors_resolver(resource):
# `resource` is the Dashboard or Slice instance being checked.
# Return Subject instances, raw subject ids, or dicts with an `id` key.
return [...]
EXTRA_EDITORS_RESOLVER = extra_editors_resolver
```
The resolver's result is unioned with the resource's own `editors` for editorship checks: it
feeds `is_editor`, `raise_for_editorship`, save-as, and soft-delete restore. When
`EXTRA_EDITORS_RESOLVER` is configured, the usual lockout-prevention behavior — automatically
re-adding a non-admin who removes themselves from a resource's editors — is skipped, since the
deployment has its own way of keeping the resource editable. This skip is global to the setting,
not per-resource: it still applies on a resource where the resolver currently returns no
subjects, so a resolver that can't guarantee an alternate editor for every resource can let the
last editor remove themselves and leave it uneditable by non-admins.
Resolved subject ids are also surfaced as `extra_editors` in the chart and dashboard `GET`
responses, so API clients can distinguish resolver-granted editorship from the resource's own
`editors` list. This field is attached after serialization and isn't part of the OpenAPI response
schema, so generated API clients won't see it as a typed field.
Because the resolver is arbitrary per-deployment Python rather than a SQL-expressible condition,
editorship it grants cannot be reflected in list-view filtering (for example, the soft-deleted
archive is scoped to editors via a SQL query). It does still run once per row on chart and
dashboard list responses to populate `extra_editors`, so a slow or unavailable external resolver
affects ordinary list requests, not just direct per-object checks.
### Dashboard Access Control
Access to dashboards is managed via editors (subjects that have edit permissions to the dashboard).
@@ -400,7 +436,7 @@ Once enabled, each user manages their own keys from their profile page:
1. Open the user menu (top-right) and click **Info** to navigate to the User Info page
2. Expand the **API Keys** section
3. Click **+ API Key**
4. Enter a name and (optionally) an expiration date
4. Enter a name and optionally select resource scopes
5. Copy the generated token — it is shown only once
Only users with the `can_read` and `can_write` permissions on `ApiKey` (granted by default to Admins) can manage API keys.
@@ -415,6 +451,18 @@ Authorization: Bearer <your-api-key>
This works for all REST API endpoints and the MCP server. The request is executed with the permissions of the user who created the key.
#### API Key Scopes
The creation dialog can restrict an API key to MCP resource actions such as
`superset:dashboard:read` or `superset:chart:write`. A scope is an additional
restriction: it never grants a permission that the creating user does not
already have through Superset RBAC. Write scopes also cover update and delete
operations for that resource; `superset:sqllab:write` covers SQL execution.
Keys created without scopes retain legacy RBAC-only behavior. The scoped-key
restrictions described here are enforced by the MCP server; regular REST API
routes continue to apply their existing Superset RBAC checks.
#### Use Cases
- **CI/CD pipelines** — automated chart/dashboard exports and imports
@@ -571,6 +619,28 @@ SELECT * FROM (
queries run against tables that have associated datasets with RLS filters will then have
the appropriate predicates injected automatically.
#### RLS Indicator in the Dataset List and Explore
When a dataset has one or more RLS filters that apply to it, Superset shows a lock
icon badge next to the dataset name in the **Datasets** list and next to the dataset
selector in **Explore**. Hovering over the badge shows a tooltip listing each
applicable filter's name, filter type (Regular or Base), group key (if any), assigned
subjects (labeled "Roles" in the tooltip, but may include users and groups too), and
clause.
This badge also surfaces filters that are inherited from the physical tables
referenced by a virtual (SQL-based) dataset, as described above. Inherited filters
are marked "from underlying table" in the tooltip, and a summary note is shown
whenever any of the listed filters are inherited rather than assigned directly to
the dataset. Inherited-filter detection depends on Superset's SQL parser being able
to identify the referenced tables and match them to a physical dataset by name,
schema, and database, so it's best-effort: unparseable or unmatched references won't
surface a filter on the badge even if one would apply at query time.
The badge is a visibility aid only — it does not change which filters are applied to
a query. Use the RLS REST API described below if you need to confirm exactly which
filters affect a dataset.
#### Checking RLS Filters via the API
You can use the RLS REST API to audit which filters are configured and which datasets
+1
View File
@@ -86,6 +86,7 @@
"Israel",
"Italy",
"Italy (regions)",
"Italy (regions and autonomous provinces)",
"Ivory Coast",
"Japan",
"Jordan",
@@ -493,8 +493,8 @@ Frontend assets (TypeScript, JavaScript, CSS, and images) must be compiled in or
First, be sure you are using the following versions of Node.js and npm:
- `Node.js`: Version 22 (LTS)
- `npm`: Version 10
- `Node.js`: Version 24 (see `superset-frontend/.nvmrc` for the exact version)
- `npm`: Version 11
We recommend using [nvm](https://github.com/nvm-sh/nvm) to manage your node environment:
@@ -507,8 +507,8 @@ export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
cd superset-frontend
nvm install --lts
nvm use --lts
nvm install
nvm use
```
Or if you use the default macOS starting with Catalina shell `zsh`, try:
+49 -5
View File
@@ -46,13 +46,43 @@ import FAQSchema from '@site/src/components/FAQSchema';
answer:
'You need to register a free account at Mapbox.com, obtain an API key, and add it to your .env file at the key MAPBOX_API_KEY.',
},
{
question: 'How to limit the timed refresh on a dashboard?',
answer:
'To exclude specific slices from the timed refresh process, add the timed_refresh_immune_slices key to the dashboard JSON Metadata field with the slice IDs to exclude.',
},
{
question: "Why does 'flask fab' or Superset freeze, hang, or not respond when started (my home directory is NFS mounted)?",
answer:
'By default, Superset creates and uses an SQLite database at ~/.superset/superset.db. SQLite is known to not work well if used on NFS due to broken file locking. Override the path with the SUPERSET_HOME environment variable or change SQLALCHEMY_DATABASE_URI in superset_config.py.',
},
{
question: 'What if the table schema changed?',
answer:
'Go to Data -> Datasets, click the edit icon next to the dataset whose schema has changed, and hit Sync columns from source from the Columns tab. The new columns will get merged.',
},
{
question: 'What database engine can I use as a backend for Superset?',
answer:
'Superset is tested using MySQL, PostgreSQL, and SQLite backends for storing its internal metadata. While Superset supports many databases as data sources, only these are recommended for the metadata store in production.',
},
{
question: 'Does Superset work with my database?',
question: 'How can I configure OAuth authentication and authorization?',
answer:
'Take a look at the Flask-AppBuilder OAuth configuration example, which shows how to configure OAuth authentication and authorization in Superset.',
},
{
question: 'Is there a way to force the dashboard to use specific colors?',
answer:
'It is possible on a per-dashboard basis by providing a mapping of labels to colors in the JSON Metadata attribute using the label_colors key. You can use a full hex color, a named color, or the index in the current color palette.',
},
{
question: 'How do I expand all chart descriptions on a dashboard by default?',
answer:
'Add the expand_all_slices key to the dashboard JSON Metadata field with a value of true. Charts that have already been manually expanded or collapsed keep that per-chart override regardless of the setting.',
},
{
question: 'Does Superset work with [insert database engine here]?',
answer:
'Superset supports any database with a Python SQLAlchemy dialect and DBAPI driver. Check the Connecting to Databases documentation for the full list of supported databases.',
},
@@ -61,15 +91,30 @@ import FAQSchema from '@site/src/components/FAQSchema';
answer:
'Yes, Superset has a public REST API documented using Swagger. Enable FAB_API_SWAGGER_UI in superset_config.py to access interactive API documentation at /swagger/v1.',
},
{
question: 'How can I see usage statistics (e.g., monthly active users)?',
answer:
'This functionality is not included with Superset, but you can extract and analyze the logs table in Superset\u2019s metadata database to see what actions have occurred.',
},
{
question: 'What does Hours Offset in the Edit Dataset view do?',
answer:
'In the Edit Dataset view, the hours offset lets you configure the number of hours to be added or subtracted from the time column. This can be used, for example, to convert UTC time to local time.',
},
{
question: 'Does Superset collect any telemetry data?',
answer:
'Superset uses Scarf by default to collect basic telemetry data to help maintainers understand version usage. Users can opt out by setting the SCARF_ANALYTICS environment variable to false.',
},
{
question: 'Does Superset have a trash bin to recover deleted assets?',
question: 'Does Superset have an archive panel or trash bin from which a user can recover deleted assets?',
answer:
'No, there is no built-in way to recover deleted dashboards, charts, or datasets. It is recommended to take periodic backups of the metadata database and use export functionality for recovery.',
'No. Currently, there is no way to recover a deleted Superset dashboard, chart, dataset, or database from the UI. It is recommended to take periodic backups of the metadata database and use export functionality for recovery.',
},
{
question: 'I ran a security scan of the Superset container image and it showed dozens of "high" and "critical" vulnerabilities! Can you release a version of Superset without these?',
answer:
'These are dependency CVEs in software that Superset uses, mostly in the Linux kernel or Python. Superset addresses them by regularly updating dependencies and welcomes pull requests that fix dependency CVEs. The Superset security team focuses primarily on vulnerabilities in Superset itself.',
},
]}
/>
@@ -214,8 +259,7 @@ frontend falls back to a built-in default of `5000` milliseconds.
SUPERSET_DASHBOARD_MANUAL_REFRESH_STAGGER_MS = 0
```
**Why does flask fab or superset freeze/hang/not responding when started (my home directory is
NFS mounted)?**
## Why does `flask fab` or Superset freeze, hang, or not respond when started (my home directory is NFS mounted)?
By default, Superset creates and uses an SQLite database at `~/.superset/superset.db`. SQLite is
known to [not work well if used on NFS](https://www.sqlite.org/lockingv3.html) due to broken file
@@ -406,6 +406,18 @@ ECharts option overrides bypass Superset's validation layer. Invalid option keys
When the **Search Box** is visible in a Table chart, the **Download** action exports only the rows currently visible after the search filter is applied — not the full underlying dataset. This matches the visual output and is intentional. To export the full dataset regardless of search state, use the **Download as CSV** option from the chart's three-dot menu in the dashboard or from the Explore chart toolbar before applying a search filter.
### Exporting a Chart as an Image or PDF
Alongside the raw-data export options (CSV, JSON, Excel), a chart's three-dot menu — in a dashboard or from the Explore chart toolbar — offers a few ways to export a visual snapshot of the chart:
- **Export screenshot (jpeg)** — a single-click JPEG screenshot of the chart.
- **Export screenshot (png)** — opens a submenu with **Transparent background** and **Solid background** options. The solid option uses the current theme's background color. PNG produces a higher-quality image than the JPEG export.
- **Export as PDF** — downloads the chart as a PDF file.
The dropdown menu is briefly hidden while the screenshot or PDF is being captured so it doesn't appear in the exported file. In Explore, these image and PDF options are available from the **Export All Data** submenu, and also from the **Export current view** submenu when the chart type supports current-view export.
These menu items respect your permissions: the dashboard export menu only appears if you can download, and the image/PDF options are disabled if you lack image-export permission.
### Sharing a Specific Tab
When a dashboard has tabs, each tab gets its own shareable URL. Navigate to the tab you want to share and copy the URL from your browser's address bar — the tab anchor is encoded in the URL so that anyone opening the link lands directly on that tab.
+15
View File
@@ -88,6 +88,21 @@ embedDashboard({
If the callback returns `null` or is not provided, Superset uses its own permalink URL as a fallback.
### Permalink origin rewriting
This rewrite only applies to the non-embedded permalink path — it has no effect on embedded dashboards. When Superset is not embedded, it rewrites the origin of any permalink URL it generates to `window.location.origin` before showing it to the user, which keeps a proxied or subdirectory-deployed Superset from handing out a permalink that points at an internal hostname the user's browser can't reach.
When Superset **is** embedded, this rewrite is skipped entirely regardless of the flag below: a `resolvePermalinkUrl` callback's return value is used as-is, and if no callback is provided (or it fails), the backend-supplied URL is also returned as-is.
If your reverse proxy correctly forwards `X-Forwarded-Host` and you'd rather non-embedded permalinks carry the backend's literal origin, opt out of the rewrite with `EMBEDDED_DISABLE_PERMALINK_ORIGIN_REWRITE`:
```python
# superset_config.py
EMBEDDED_DISABLE_PERMALINK_ORIGIN_REWRITE = True
```
This defaults to `False` (rewrite enabled) and only affects non-embedded permalinks. Flipping the default would regress the common proxied/subdirectory deployment by exposing an unreachable internal host in copied permalinks.
---
## Feature Flags for Embedded Mode
@@ -11,6 +11,11 @@ When soft-delete is enabled, deleting a chart, dashboard, or dataset archives it
instead of removing it permanently. The **Recently Archived** view lets owners
and admins find archived objects and restore them.
A chart used by an alert or report cannot be archived while that dependency
exists. In the chart list view, the archive confirmation lists the alerts and
reports that use the chart; a blocked attempt names them and asks you to
detach or delete them first.
:::note
This view is gated by the `SOFT_DELETE` feature flag. When the flag is off the
@@ -21,7 +26,8 @@ page and its menu entry are hidden, and deletes are permanent as before.
## Finding archived objects
Open **Recently Archived** and pick a type — **Chart**, **Dashboard**, or
**Dataset** — from the Type selector. The view shows one type at a time; each
**Dataset** (shown as **Datasource** when semantic layers are enabled) — from
the Type selector. The view shows one type at a time; each
type is read from its own list endpoint, so the same row-level access rules that
govern the normal lists apply here.
@@ -248,11 +248,12 @@ Ask your admin for the MCP server URL and any authentication tokens you need.
### Datasets
| Tool | Description |
| ------------------------ | ------------------------------------------------ |
| `list_datasets` | List datasets with filtering and search |
| `get_dataset_info` | Get dataset metadata (columns, metrics, filters) |
| `create_virtual_dataset` | Create a virtual dataset from a SQL query |
| Tool | Description |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `list_datasets` | List datasets with filtering and search |
| `get_dataset_info` | Get dataset metadata (columns, metrics, schema details) |
| `create_virtual_dataset` | Create a virtual dataset from a SQL query |
| `update_dataset_metric` | Update a saved metric's expression, name, verbose_name, or format (affects every chart using it; requires dataset ownership) |
### Charts
+12 -12
View File
@@ -15,29 +15,29 @@ description of what changed — "Chart renamed to Q3 Revenue", "Added filter on
'Region'" — rather than a raw diff. You can search the history and filter it
down to changes on the entity itself or on the things it depends on.
## Enabling it
Two switches are involved, and both matter.
## Enabling and disabling it
| Setting | Type | Effect |
| --- | --- | --- |
| `VERSION_HISTORY` | Feature flag | Shows the version history UI |
| `ENABLE_VERSIONING_CAPTURE` | Config value | Records versions as entities are saved |
Both default to on. To turn the feature off:
```python
# superset_config.py
FEATURE_FLAGS = {"VERSION_HISTORY": True}
ENABLE_VERSIONING_CAPTURE = True
FEATURE_FLAGS = {"VERSION_HISTORY": False}
ENABLE_VERSIONING_CAPTURE = False
```
Both default to off. They are separate because capture is the expensive half:
an operator may want to start recording history before exposing the UI, so that
there is something to show when they do.
Restart Superset and its workers for the capture change to take effect. Existing
history remains readable while capture is off, but **Restore** is unavailable
(404).
Turning the UI on without capture gives a panel that reports "No history yet"
and never fills, so enable capture first — or at the same time. History only
accrues from the moment capture is switched on; earlier edits are not
reconstructed.
Disable them together: capture off with the UI left on gives a panel that
stops filling — an empty or stale history misrepresents the entity as
unchanged. History only accrues while capture is on; edits made while it was
off are not reconstructed.
## Viewing history
+15 -15
View File
@@ -58,15 +58,15 @@
"@fontsource/inter": "^5.3.0",
"@mdx-js/react": "^3.1.1",
"@saucelabs/theme-github-codeblock": "^0.3.0",
"@storybook/addon-docs": "^10.5.7",
"@storybook/addon-docs": "^10.5.10",
"@superset-ui/core": "^0.20.4",
"@swc/core": "^1.15.47",
"antd": "^6.5.4",
"baseline-browser-mapping": "^2.11.12",
"caniuse-lite": "^1.0.30001807",
"docusaurus-plugin-openapi-docs": "^5.1.3",
"docusaurus-theme-openapi-docs": "^5.1.3",
"js-yaml": "^5.2.3",
"@swc/core": "^1.16.1",
"antd": "^6.6.1",
"baseline-browser-mapping": "^2.11.16",
"caniuse-lite": "^1.0.30001809",
"docusaurus-plugin-openapi-docs": "^5.2.0",
"docusaurus-theme-openapi-docs": "^5.2.0",
"js-yaml": "^5.3.0",
"json-bigint": "^1.0.0",
"prism-react-renderer": "^2.4.1",
"react": "^18.3.1",
@@ -77,8 +77,8 @@
"react-table": "^7.8.0",
"remark-import-partial": "^0.0.2",
"reselect": "^5.2.0",
"storybook": "^10.5.7",
"swagger-ui-react": "^5.32.12",
"storybook": "^10.5.10",
"swagger-ui-react": "^5.32.14",
"swc-loader": "^0.2.7",
"tinycolor2": "^1.4.2",
"unist-util-visit": "^5.1.0"
@@ -89,14 +89,14 @@
"@eslint/js": "^9.39.2",
"@types/js-yaml": "^4.0.9",
"@types/react": "^19.1.8",
"@typescript-eslint/eslint-plugin": "^8.66.0",
"@typescript-eslint/parser": "^8.66.0",
"@typescript-eslint/eslint-plugin": "^8.67.0",
"@typescript-eslint/parser": "^8.67.0",
"eslint": "^9.39.2",
"eslint-plugin-react": "^7.37.5",
"globals": "^17.9.0",
"oxfmt": "^0.62.0",
"globals": "^17.11.0",
"oxfmt": "^0.64.0",
"typescript": "~6.0.3",
"typescript-eslint": "^8.66.0",
"typescript-eslint": "^8.67.0",
"webpack": "^5.109.2"
},
"browserslist": {
@@ -0,0 +1,236 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# SIP: System-wide MEDIAN, Sample Standard Deviation, and Sample Variance metric aggregates
## [DRAFT — proposal for discussion]
This document now has an accompanying implementation in this PR, for the
proposed mechanism plus a first, empirically-verified engine set (Postgres,
MySQL, DuckDB, Redshift by inheritance). It is intended to seed discussion on
whether this is the right shape and scope before it goes up for a formal SIP
vote, not to pre-empt that discussion, the code exists so reviewers have a
concrete design to react to rather than a description of one.
## Motivation
Before #41184 (SIP-216, the non-additive-totals fix), the Pivot Table chart
exposed an "Aggregation function" control with 18 choices, including
`Median`, `Sample Standard Deviation`, `Sample Variance`, `First`, `Last`,
`Count Unique Values`, and `List Unique Values`. #41184 deleted that control
wholesale, and deliberately so: it re-aggregated already-aggregated cell
values to compute totals/subtotals, which is exactly the class of bug
SIP-216 fixed (summing per-group averages, averaging per-group medians, etc.
produces silently wrong totals). #42761 subsequently restored the one piece
of that control's functionality that was cleanly separable from the
correctness bug, the "show as % of row/column/total" display option,
redesigned as a decoupled, post-hoc-only `showValuesAs` control.
A user has since noticed that several of the other pre-#41184 options never
came back. Checking today's metric aggregate list (`AVG, COUNT,
COUNT_DISTINCT, MAX, MIN, SUM`, see
`superset-frontend/packages/superset-ui-core/src/query/types/Metric.ts`),
most of these have a reasonable equivalent already: `Count Unique Values`
maps to `COUNT_DISTINCT`; `Count`/`Average`/`Max`/`Min` are already standard
aggregates; the two "fraction of" variants are already covered by
`showValuesAs`. But `Median`, `Sample Standard Deviation`, and `Sample
Variance` have no equivalent today anywhere in Superset, not just in Pivot
Table, in any chart type, since the aggregate list is shared across the
whole app.
This is a real, currently-live gap, not a hypothetical one:
`superset/mcp_service/chart/chart_utils.py`, `schemas.py`, and
`prompts/create_chart_guided.py` already treat `STDDEV`, `VAR`, `MEDIAN`,
and `PERCENTILE` as valid aggregate values in their own validation and
documentation, but those values are never recognized by
`superset/connectors/sqla/models.py`'s `sqla_aggregations` dict (the actual
mapping from aggregate name to SQL), so an AI agent using the MCP tool to
build a chart with `"aggregate": "STDDEV"` today creates a chart that
**errors at query time** with "Adhoc metric aggregate is invalid." This SIP
proposes closing that gap for real, at the source, rather than patching
around it in MCP.
## Proposed change
Add `MEDIAN`, `STDDEV_SAMP`, and `VAR_SAMP` as first-class, system-wide
metric aggregates, available anywhere a metric aggregate is chosen (every
chart type, SQL Lab metric picker, MCP), not as a Pivot-Table-specific
control.
**Why this is safe with respect to SIP-216, and needs no Pivot-Table-specific
code at all:** Pivot Table's non-additive-totals machinery
(`superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/utilities.ts`)
already classifies any metric aggregate not in `ADDITIVE_AGGREGATES = {SUM,
COUNT, MIN, MAX}` as non-additive, which routes totals/subtotals through the
correct DB-`GROUPING SETS`-rollup path rather than client-side
re-aggregation (`AVG` and `COUNT_DISTINCT` already go through this path
today). `MEDIAN`/`STDDEV_SAMP`/`VAR_SAMP` fall into that bucket
automatically, with zero changes needed to the additivity logic. So once
these are valid, buildable SQL aggregates, Pivot Table (and every other
chart) gets correct behavior for free. This is the version of "restore the
control" that does not reopen the bug that was just fixed.
**Where the actual change needs to land, and what this PR does:**
1. **Done.** `superset-frontend/packages/superset-ui-core/src/query/types/Metric.ts`,
extended the `Aggregate` type.
2. **Done.** `superset-frontend/src/explore/constants.ts`, added to `AGGREGATES`
(drives `AGGREGATES_OPTIONS`, the dropdown in `AdhocMetricEditPopover`).
3. **Done**, but not consolidated. `superset/connectors/sqla/models.py`
(`sqla_aggregations`) and `superset/models/helpers.py`
(`ExploreMixin.sqla_aggregations`) are both wired to consult the new
`BaseEngineSpec.get_extended_aggregation_func`, in addition to their
existing 6-aggregate dict, so neither's original, already-tested behavior
changed. They remain two separate dicts, consolidating them into one
source of truth is left as a follow-up (see Open questions).
4. **Done**, and it surfaced a second, smaller bug on top of the one this SIP
opened with: MCP's own aggregate names (`STDDEV`, `VAR`) never matched any
real Superset aggregate, before or after this PR, they were always going
to error regardless of what this SIP does. `superset/mcp_service/chart/*`
now accepts the old shorthand as an alias, normalized to the real,
unambiguous names (`STDDEV_SAMP`, `VAR_SAMP`) this PR ships, and the guided
prompt text points at the correct names going forward. `MEDIAN`/
`PERCENTILE` were already spelled correctly in MCP; `PERCENTILE` remains
unimplemented (it needs a parameter this schema has no field for) and is
unchanged by this PR, out of scope here.
**The part that needs real engineering care, this must not be a blind
`sa.func.MEDIAN` / `sa.func.STDDEV_SAMP` / `sa.func.VAR_SAMP`:**
`sqla_aggregations` today is a flat, engine-unaware dict (`sa.func.AVG`,
etc., SQLAlchemy emits whatever function name it is given, with zero
validation that the target dialect actually has it). Superset already has
precedent for exactly this class of per-engine capability difference:
`BaseEngineSpec.supports_grouping_sets` and `_time_grain_expressions`, both
introduced by #41184 itself. This SIP proposes the same shape, a new
per-engine-overridable mechanism (for example
`BaseEngineSpec.get_aggregate_sql(aggregate, column)` with a sensible
default, overridden per engine spec where the default does not hold),
rather than a single hardcoded dict.
Verified findings so far (via `sqlglot.transpile`, cross-checked against
known engine docs; **not** exhaustively tested against live databases, that
is necessary follow-up work this SIP alone cannot complete):
| Engine | `MEDIAN(x)` | `STDDEV_SAMP(x)` | `VAR_SAMP(x)` |
|---|---|---|---|
| Postgres | `PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY x)` | native | native |
| MySQL | no native equivalent, needs explicit "unsupported" handling, not a blind emit | native | MySQL's `VARIANCE()` is an alias for `VAR_POP` (population), not `VAR_SAMP` (sample); a naive dialect-name substitution would silently compute the wrong statistic and needs an explicit, verified expression instead |
| SQLite | only if the specific build was compiled with the (non-default) `SQLITE_ENABLE_PERCENTILE` extension (added in SQLite 3.43, 2023), cannot be assumed available | not available in core SQLite | not available in core SQLite |
| BigQuery / Snowflake / DuckDB / Redshift / Oracle / T-SQL / Databricks / Spark | native `MEDIAN(x)` | native | native on BigQuery/Snowflake/Databricks/Spark, where `VARIANCE` is correctly sample variance; T-SQL has no function named `VARIANCE` at all and needs `VAR(x)` instead |
| Trino / Presto / Hive | `PERCENTILE_CONT` / `approx_percentile` (dialect- and exactness-dependent) | native | `variance` is correctly sample variance per Trino/Presto docs |
This table is deliberately not exhaustive, Superset has roughly 75
`db_engine_specs` files. The proposed default (`BaseEngineSpec`) should be
the safe choice (mark unsupported, surface a clear user-facing error) rather
than an optimistic one, with individual engine specs opting in once
verified. Ship for the handful of engines above first, extend
opportunistically.
**`Count Unique Values`, `First`, `Last`, `List Unique Values`, explicitly
out of scope for this SIP:**
- `Count Unique Values` needs no work, it is already `COUNT_DISTINCT`.
- `First`/`Last` have no well-defined, unambiguous meaning as a plain
`GROUP BY` aggregate without an explicit ordering; most engines only
support this via window functions (`FIRST_VALUE`/`LAST_VALUE` `OVER
(ORDER BY ...)`) or do not support it as a simple aggregate at all
(Postgres has neither built in). Restoring this properly would mean
designing an "order by" sub-control on the metric, a real, separate
feature, not a one-line aggregate addition. Proposed as a follow-up SIP if
there is demand.
- `List Unique Values` maps to the `STRING_AGG`/`GROUP_CONCAT`/`LISTAGG`/
`ARRAY_AGG(DISTINCT ...)` family, real dialect differences, plus an open
UX question (unbounded cell content for high-cardinality columns).
Proposed as a follow-up SIP.
## New or changed public interfaces
- New `Aggregate` values (`MEDIAN`, `STDDEV_SAMP`, `VAR_SAMP`) selectable
anywhere the standard metric control appears, every chart type, not just
Pivot Table.
- New `BaseEngineSpec` extensibility point for per-engine aggregate SQL
generation (exact shape TBD in implementation, likely mirrors
`_time_grain_expressions`).
- No REST API surface changes beyond the existing metric aggregate field
accepting new values.
## Migration plan and compatibility
No new tables/columns needed for the aggregate addition itself.
Restoring prior chart settings, the way #42761 restored `show_values_as` for
charts that had it before #41184, is murkier here than it was for that PR
and needs its own design pass: the old `aggregate_function` was a single
Pivot-Table-level setting applied uniformly to every metric on the chart,
not a per-metric property. A chart that had `aggregate_function: Median`
before #41184, with a metric of `SUM(sales)`, was already silently wrong
under the old architecture (that is the bug that was fixed); mechanically
rewriting its metric to `MEDIAN(sales)` on upgrade would change what the
chart's leaf cells display, not just its totals, which may not match user
intent. This SIP proposes a best-effort, flagged-for-review migration
(surface affected charts to admins rather than silently rewriting them)
rather than a fully automatic one-to-one restoration.
## Rejected alternatives
- **Restoring the old `aggregateFunction` Pivot-Table control as-is.**
Rejected: this is the literal mechanism SIP-216 removed because it
reintroduces incorrect totals for non-additive metrics. Any fix has to go
through the metric's own aggregate, not a separate pivot-level override.
- **Routing all metric SQL generation through `sqlglot` expression-building
instead of SQLAlchemy's `sa.func`.** More architecturally thorough (would
give correct dialect syntax for free across more of the roughly 75 engine
specs), but a much larger, more invasive change to a hot path used by
every chart query. Noted as a possible future direction, not this SIP's
scope; this SIP proposes the smaller, `supports_grouping_sets`-shaped
extensibility point instead.
## Open questions
- **Resolved for this PR, worth confirming as the community's preferred
shape:** implemented as `BaseEngineSpec._extended_aggregations` (a
`{aggregate_name: sqla_column -> sqla_column}` dict) plus a
`get_extended_aggregation_func` accessor, set on the concrete or shared
base engine spec class per engine (e.g. on `PostgresBaseEngineSpec` so
Redshift inherits it, but *not* on `PrestoBaseEngineSpec` so Hive/Spark/
Databricks don't silently inherit unverified behavior, mirroring how
`supports_grouping_sets` is opted into per-concrete-engine there today).
Did not route through the `superset/sql/dialects/` sqlglot-based layer;
that layer is for SQL Lab parsing, wiring it into chart-metric query
building felt like a separate, larger change from this SIP's scope.
- **Still open, not addressed in this PR:** how aggressively should
`MEDIAN` degrade on engines without a native or exact equivalent?
Trino/Presto/Hive were left unimplemented (unsupported) specifically to
avoid silently answering this with an approximate function
(`approx_percentile`) that changes the semantics of what a user asked
for. If someone wants `MEDIAN` on those engines, this needs a real
decision: require explicit opt-in, show a UI warning, or keep it
disallowed.
- **Resolved for this PR:** left the two `sqla_aggregations` dicts
(`connectors/sqla/models.py` and `models/helpers.py`) unconsolidated,
both now separately wired to the same new `get_extended_aggregation_func`
hook. Consolidating them into one source of truth is real but unrelated
cleanup, not bundled here to keep the diff reviewable.
- **New, from implementation:** only Postgres, MySQL (partial), DuckDB, and
Redshift (by inheritance, unverified) ship enabled. BigQuery, Snowflake,
Trino, Presto, Hive, Spark, Databricks, Oracle, and T-SQL all have
documented (not live-verified) support per the table above but are not
yet wired up, each needs the same live-instance verification treatment
before being enabled, this PR intentionally didn't guess.
+1 -1
View File
@@ -67,7 +67,7 @@ const communityLinks = [
'Join our monthly virtual meetups and register for any upcoming events on Meetup',
},
{
url: 'https://github.com/apache/superset/blob/master/RESOURCES/INTHEWILD.md',
url: 'https://superset.apache.org/inTheWild/',
title: 'Organizations',
description:
'A list of some of the organizations using Superset in production.',
+18 -12
View File
@@ -93,12 +93,6 @@
"lifecycle": "development",
"description": "Enable semantic layers and show semantic views alongside datasets"
},
{
"name": "SOFT_DELETE",
"default": true,
"lifecycle": "development",
"description": "Temporary rollout / kill-switch gate for soft delete (off = legacy hard delete). An emergency stop, not a clean rollback: flipping ON->OFF resurrects already-soft-deleted rows. Retained through this release as the move-back lever; removed (along with its two gate points \u2014 BaseDAO.delete routing and the do_orm_execute visibility listener) once post-flip confidence is established."
},
{
"name": "TABLE_V2_TIME_COMPARISON_ENABLED",
"default": false,
@@ -110,12 +104,6 @@
"default": false,
"lifecycle": "development",
"description": "Enables the tagging system for organizing assets"
},
{
"name": "VERSION_HISTORY",
"default": true,
"lifecycle": "development",
"description": "Enables the version history panel on Explore and Dashboard pages. History only accrues while ``ENABLE_VERSIONING_CAPTURE`` is also on; with capture off the panel renders but stays empty, so the two ship with matching defaults and should be changed together."
}
],
"testing": [
@@ -132,6 +120,12 @@
"lifecycle": "testing",
"description": "Enables filter functionality in Alerts and Reports"
},
{
"name": "ALERT_REPORTS_RETRY",
"default": false,
"lifecycle": "testing",
"description": "Enables automatic retry functionality for failed report executions"
},
{
"name": "ALERT_REPORT_SLACK_V2",
"default": true,
@@ -233,6 +227,12 @@
"lifecycle": "testing",
"description": "Apply RLS rules to SQL Lab queries. Requires query parsing/manipulation. May break queries or allow RLS bypass. Use with care!"
},
{
"name": "SOFT_DELETE",
"default": true,
"lifecycle": "testing",
"description": "Temporary rollout / kill-switch gate for soft delete (off = legacy hard delete). An emergency stop, not a clean rollback: flipping ON->OFF resurrects already-soft-deleted rows. Retained through this release as the move-back lever; removed (along with its two gate points \u2014 BaseDAO.delete routing and the do_orm_execute visibility listener) once post-flip confidence is established."
},
{
"name": "SSH_TUNNELING",
"default": false,
@@ -245,6 +245,12 @@
"default": false,
"lifecycle": "testing",
"description": "Use analogous colors in charts"
},
{
"name": "VERSION_HISTORY",
"default": true,
"lifecycle": "testing",
"description": "Enables the version history panel on Explore and Dashboard pages. History only accrues while ``ENABLE_VERSIONING_CAPTURE`` is also on; with capture off the panel renders empty or stale history, so the two ship with matching defaults and should be changed together."
}
],
"stable": [
+44 -32
View File
@@ -3407,22 +3407,26 @@
"nullable": true,
"type": "string"
},
"description": {
"nullable": true,
"type": "string"
},
"editors": {
"items": {
"$ref": "#/components/schemas/DashboardRestApi.get_list.Subject"
},
"type": "array"
},
"id": {
"type": "integer"
},
"is_managed_externally": {
"type": "boolean"
},
"owners": {
"$ref": "#/components/schemas/DashboardRestApi.get_list.User2"
},
"published": {
"nullable": true,
"type": "boolean"
},
"roles": {
"$ref": "#/components/schemas/DashboardRestApi.get_list.Role"
},
"slug": {
"maxLength": 255,
"nullable": true,
@@ -3432,10 +3436,10 @@
"readOnly": true
},
"tags": {
"$ref": "#/components/schemas/DashboardRestApi.get_list.Tag"
},
"thumbnail_url": {
"readOnly": true
"items": {
"$ref": "#/components/schemas/DashboardRestApi.get_list.Tag"
},
"type": "array"
},
"url": {
"readOnly": true
@@ -3444,21 +3448,46 @@
"format": "uuid",
"nullable": true,
"type": "string"
},
"viewers": {
"items": {
"$ref": "#/components/schemas/DashboardRestApi.get_list.Subject1"
},
"type": "array"
}
},
"type": "object"
},
"DashboardRestApi.get_list.Role": {
"DashboardRestApi.get_list.Subject": {
"properties": {
"id": {
"type": "integer"
},
"name": {
"maxLength": 64,
"label": {
"maxLength": 255,
"type": "string"
},
"type": {
"type": "integer"
}
},
"required": ["name"],
"required": ["label", "type"],
"type": "object"
},
"DashboardRestApi.get_list.Subject1": {
"properties": {
"id": {
"type": "integer"
},
"label": {
"maxLength": 255,
"type": "string"
},
"type": {
"type": "integer"
}
},
"required": ["label", "type"],
"type": "object"
},
"DashboardRestApi.get_list.Tag": {
@@ -3511,23 +3540,6 @@
"required": ["first_name", "last_name"],
"type": "object"
},
"DashboardRestApi.get_list.User2": {
"properties": {
"first_name": {
"maxLength": 64,
"type": "string"
},
"id": {
"type": "integer"
},
"last_name": {
"maxLength": 64,
"type": "string"
}
},
"required": ["first_name", "last_name"],
"type": "object"
},
"DashboardRestApi.post": {
"properties": {
"certification_details": {
@@ -16506,7 +16518,7 @@
},
"result": {
"items": {
"type": "object"
"$ref": "#/components/schemas/DashboardRestApi.get_list"
},
"type": "array"
}
+507 -482
View File
File diff suppressed because it is too large Load Diff
+38 -23
View File
@@ -16,7 +16,7 @@
# under the License.
[build-system]
requires = ["setuptools>=40.9.0", "wheel"]
requires = ["setuptools>=84.0.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
@@ -42,7 +42,7 @@ dependencies = [
# ``google-auth`` 2.53+ dropped it, so Superset must declare it
# explicitly to keep fresh ``pip install apache-superset`` working
# without the ``base.txt`` lock file (#40962).
"cachetools>=7.1.6, <8",
"cachetools>=7.1.7, <8",
"celery>=5.6.3, <6.0.0",
"click>=8.4.2",
"click-option-group",
@@ -75,7 +75,7 @@ dependencies = [
"humanize",
"isodate",
"jsonpath-ng>=1.8.0, <2",
"Mako>=1.2.2",
"Mako>=1.4.1",
"markdown>=3.10.3",
# marshmallow 4 compatibility: see superset/marshmallow_compatibility.py for a
# Flask-AppBuilder workaround. Tracking issue:
@@ -94,16 +94,16 @@ dependencies = [
"parsedatetime",
"paramiko>=3.4.0, <4.0", # 4.0 removed DSSKey, still referenced by sshtunnel
"pgsanity",
"Pillow>=11.0.0, <13",
"Pillow>=12.3.0, <13", # raise floor to match resolved pin; closes SCA false-positive on 11.x-range CVEs already fixed in 12.3.0
"polyline>=2.0.4, <3.0",
"pydantic>=2.8.0",
"pyparsing>=3.3.2, <4",
"python-dateutil",
"python-dotenv", # optional dependencies for Flask but required for Superset, see https://flask.palletsprojects.com/en/stable/installation/#optional-dependencies
"pygeohash",
"pyarrow>=24.0.0, <26", # before upgrading pyarrow, check that all db dependencies support this, see e.g. https://github.com/apache/superset/pull/34693
"pyarrow>=25.0.1, <26", # before upgrading pyarrow, check that all db dependencies support this, see e.g. https://github.com/apache/superset/pull/34693
"pyyaml>=6.0.3, <7.0.0",
"PyJWT>=2.4.0, <3.0",
"PyJWT>=2.13.0, <3.0", # raise floor to match resolved pin; closes SCA false-positive on 2.4.x-range CVEs already fixed in 2.13.0
"redis>=5.0.0, <9.0",
"rison>=2.0.1, <3.0",
@@ -111,10 +111,10 @@ dependencies = [
"sshtunnel>=0.4.0, <0.5",
"simplejson>=4.1.1",
"slack_sdk>=3.43.0, <4",
"sqlalchemy>=2.0.0, <2.1",
"sqlalchemy>=2.0.52, <2.1",
"sqlalchemy-continuum>=1.6.0, <2.0.0",
"sqlalchemy-utils>=0.42.1, <0.43", # expanding lowerbound to work with pydoris
"sqlglot>=30.16.0, <31", # 30.16.0 adds Trino inline UDF IF/CASE routine statement parsing
"sqlglot>=30.17.0, <31", # 30.16.0 adds Trino inline UDF IF/CASE routine statement parsing
# newer pandas needs 0.9+
"tabulate>=0.10.0, <1.0",
"typing-extensions>=4.16.0, <5",
@@ -135,14 +135,24 @@ athena = ["pyathena[pandas]>=3.35.4, <4"]
# superset/db_engine_specs/aurora.py's known_incompatibilities metadata.
aurora-data-api = ["preset-sqlalchemy-aurora-data-api>=0.2.8,<0.3"]
bigquery = [
"pandas-gbq>=0.35.0",
"pandas-gbq>=0.35.1",
# 1.17.1 is likely the final release: googleapis/python-bigquery-sqlalchemy
# was archived 2026-05-16. Both 1.17.0 and 1.17.1 support SQLAlchemy 1.4/2.0.
"sqlalchemy-bigquery>=1.17.1",
"sqlalchemy-bigquery>=1.17.2",
"google-cloud-bigquery>=3.42.3",
]
clickhouse = ["clickhouse-connect>=1.6.0, <2.0"]
cockroachdb = ["cockroachdb>=0.3.5, <0.4"]
clickhouse = ["clickhouse-connect>=1.7.2, <2.0"]
# The `cockroachdb` PyPI package (last released 2021) is abandoned and its
# SQLAlchemy dialect cannot even import under SQLAlchemy 2.0 (it references
# sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2, removed in
# 2.0). sqlalchemy-cockroachdb is the actively maintained replacement,
# already linked from CockroachDbEngineSpec.metadata's docs_url, and
# registers the same `cockroachdb` SQLAlchemy dialect entry point.
# sqlalchemy-cockroachdb depends only on SQLAlchemy itself, not on a DBAPI
# driver, so psycopg2-binary is pinned alongside it (matching the `postgres`
# extra) to keep this extra self-contained -- CockroachDB speaks the
# PostgreSQL wire protocol, so psycopg2 is what actually opens connections.
cockroachdb = ["sqlalchemy-cockroachdb>=2.0.0, <3", "psycopg2-binary==2.9.12"]
crate = ["sqlalchemy-cratedb>=0.43.1, <1"]
# sqlalchemy-d1's only release (0.1.0, Nov 2025) pins sqlalchemy<2,>=1.4,
# explicitly excluding SQLAlchemy 2.0. See superset/db_engine_specs/d1.py's
@@ -185,11 +195,12 @@ excel = ["xlrd>=2.0.2, <2.1"]
# installing this extra is only required to actually run exports.
excel-export = ["boto3"]
fastmcp = [
"fastmcp>=3.4.5,<4.0",
"fastmcp>=3.4.7,<4.0",
"mcp>=1.29.1,<3.0",
# tiktoken backs the response-size-guard token estimator. Without
# it, the middleware falls back to a coarser character-based
# heuristic that under-counts JSON-heavy MCP responses.
"tiktoken>=0.13.0,<1.0",
"tiktoken>=0.14.0,<1.0",
]
# sqlalchemy-firebird >=2.0.0 unconditionally requires SQLAlchemy 2.0 on
# Python >=3.8 (which covers Superset's >=3.11 floor), with no dual-compat
@@ -197,7 +208,7 @@ fastmcp = [
# landed (discussion #40273).
firebird = ["sqlalchemy-firebird>=2.2.0"]
firebolt = ["firebolt-sqlalchemy>=1.1.2, <2"]
gevent = ["gevent>=26.7.0"]
gevent = ["gevent>=26.8.0"]
gsheets = ["shillelagh[gsheetsapi]>=1.4.5, <2"]
hana = ["hdbcli==2.29.25", "sqlalchemy_hana==3.0.3"]
hive = [
@@ -218,10 +229,14 @@ motherduck = ["apache-superset[duckdb]"]
mysql = ["mysqlclient>=2.2.8, <3"]
ocient = [
# Closed-source vendor package with no public changelog; permissive
# unpinned sqlalchemy>=1.4 declared, but SQLAlchemy 2.0 support is
# unverified. Lower confidence than the other bumps in this PR.
# unpinned sqlalchemy>=1.4 declared. Verified compatible with SQLAlchemy
# 2.0 against pyocient>=3.9.0 (discussion #40273): dialect construction,
# error extraction, and GIS-type sanitization all pass under 2.0.52. Note
# pyocient 3.9.0 relocated its geo-type classes from private top-level
# names (pyocient._STPoint) to public ones under pyocient.api
# (pyocient.api.STPoint), which is unrelated to the SQLAlchemy bump.
"sqlalchemy-ocient>=3.0.0, <4",
"pyocient>=1.0.15, <4",
"pyocient>=3.9.0, <4",
"shapely",
"geojson",
]
@@ -231,8 +246,8 @@ pinot = ["pinotdb>=5.0.0, <10.0.0"]
playwright = ["playwright>=1.62.0, <2"]
postgres = ["psycopg2-binary==2.9.12"]
presto = ["pyhive[presto]>=0.6.5"]
trino = ["trino>=0.338.0"]
prophet = ["prophet>=1.3.0, <2"]
trino = ["trino>=0.339.0"]
prophet = ["prophet>=1.4.0, <2"]
# sqlalchemy-redshift cuts hard from SQLAlchemy 1.4-only (0.8.x) to 2.0-only
# (>=1.0.0) with no dual-compat release. Bumped now that Superset's own
# SQLAlchemy 2.0 core bump has landed (discussion #40273).
@@ -245,7 +260,7 @@ risingwave = ["sqlalchemy-risingwave>=2.0.0"]
shillelagh = ["shillelagh[all]>=1.4.5, <2"]
singlestore = ["sqlalchemy-singlestoredb>=1.2.1, <2"]
snowflake = ["snowflake-sqlalchemy>=1.11.0, <2"]
sqlite = ["syntaqlite>=0.7.0,<0.8.0"]
sqlite = ["syntaqlite>=0.9.0,<0.10.0"]
spark = [
"pyhive[hive_pure_sasl]>=0.7",
"tableschema",
@@ -255,7 +270,7 @@ tdengine = [
"taospy>=2.8.10",
"taos-ws-py>=0.7.0"
]
teradata = ["teradatasql>=20.0.0.64"]
teradata = ["teradatasql>=20.0.0.66"]
thumbnails = [] # deprecated, will be removed in 7.0
vertica = ["sqlalchemy-vertica-python>= 0.6.3, < 0.7"]
netezza = ["nzalchemy>= 11.1.2, < 11.2"]
@@ -289,7 +304,7 @@ development = [
"ruff",
"sqloxide",
"statsd",
"syntaqlite>=0.7.0,<0.8.0",
"syntaqlite>=0.9.0,<0.10.0",
]
[project.urls]
+4 -4
View File
@@ -28,9 +28,9 @@ numexpr>=2.9.0
# Security: CVE-2026-34073 (MEDIUM) - Improper Certificate Validation
cryptography>=50.0.0,<51.0.0
# Security: Snyk - XSS vulnerability in Mako templates
mako>=1.3.11,<2.0.0
mako>=1.4.1,<2.0.0
# Security: CVE-2024-52338 (CRITICAL) - Deserialization of untrusted data in IPC/Parquet readers
pyarrow>=24.0.0,<26.0.0
pyarrow>=25.0.1,<26.0.0
# Security: CVE-2026-27459 - pyopenssl certificate validation
pyopenssl>=26.0.0,<27.0.0
# Security: CVE-2026-25645 (MEDIUM) - Insecure Temporary File
@@ -52,11 +52,11 @@ marshmallow-sqlalchemy>=1.5.0
# needed for python 3.12 support
openapi-schema-validator>=0.6.3
# Pin setuptools <81 until all dependencies migrate from pkg_resources to importlib.metadata
# Pin setuptools <85 until all dependencies migrate from pkg_resources to importlib.metadata
# pkg_resources is deprecated and will be removed in setuptools 81+ (around 2025-11-30)
# Known affected packages: Preset's 'clients' package
# See docs/docs/contributing/pkg-resources-migration.md for details
setuptools<81
setuptools<85
# google-auth 2.53+ dropped its transitive dependency on cachetools, which is
# imported directly by superset.db_engine_specs.aws_iam. We declare cachetools
+9 -10
View File
@@ -44,7 +44,7 @@ cachelib==0.13.0
# via
# flask-caching
# flask-session
cachetools==7.1.6
cachetools==7.1.7
# via apache-superset (pyproject.toml)
cattrs==25.1.1
# via requests-cache
@@ -168,11 +168,11 @@ greenlet==3.5.4
# apache-superset (pyproject.toml)
# shillelagh
# sqlalchemy
gunicorn==26.0.0
gunicorn==26.2.0
# via apache-superset (pyproject.toml)
hashids==1.3.1
# via apache-superset (pyproject.toml)
holidays==0.102
holidays==0.103
# via apache-superset (pyproject.toml)
humanize==4.12.3
# via apache-superset (pyproject.toml)
@@ -207,7 +207,7 @@ kombu==5.6.2
# via celery
limits==5.1.0
# via flask-limiter
mako==1.3.12
mako==1.4.1
# via
# -r requirements/base.in
# apache-superset (pyproject.toml)
@@ -222,7 +222,7 @@ markupsafe==3.0.2
# mako
# werkzeug
# wtforms
marshmallow==4.3.0
marshmallow==4.3.1
# via
# apache-superset (pyproject.toml)
# flask-appbuilder
@@ -263,7 +263,6 @@ packaging==25.0
# apache-superset (pyproject.toml)
# apispec
# deprecation
# gunicorn
# kombu
# limits
# shillelagh
@@ -287,7 +286,7 @@ prison==0.2.1
# via flask-appbuilder
prompt-toolkit==3.0.51
# via click-repl
pyarrow==25.0.0
pyarrow==25.0.1
# via
# -r requirements/base.in
# apache-superset (pyproject.toml)
@@ -367,7 +366,7 @@ rpds-py==0.25.0
# via
# jsonschema
# referencing
setuptools==80.9.0
setuptools==84.0.0
# via -r requirements/base.in
shillelagh==1.4.5
# via apache-superset (pyproject.toml)
@@ -381,7 +380,7 @@ six==1.17.0
# wtforms-json
slack-sdk==3.43.0
# via apache-superset (pyproject.toml)
sqlalchemy==2.0.51
sqlalchemy==2.0.52
# via
# apache-superset (pyproject.toml)
# alembic
@@ -399,7 +398,7 @@ sqlalchemy-utils==0.42.1
# apache-superset (pyproject.toml)
# apache-superset-core
# flask-appbuilder
sqlglot==30.16.0
sqlglot==30.17.0
# via
# apache-superset (pyproject.toml)
# apache-superset-core
+1 -1
View File
@@ -16,5 +16,5 @@
# specific language governing permissions and limitations
# under the License.
#
-e .[development,bigquery,druid,duckdb,fastmcp,gevent,gsheets,mysql,postgres,presto,prophet,trino,thumbnails]
-e .[development,bigquery,cockroachdb,druid,duckdb,fastmcp,gevent,gsheets,mysql,postgres,presto,prophet,trino,thumbnails]
-e ./superset-extensions-cli[test]
+26 -24
View File
@@ -99,7 +99,7 @@ cachelib==0.13.0
# -c requirements/base-constraint.txt
# flask-caching
# flask-session
cachetools==7.1.6
cachetools==7.1.7
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -239,9 +239,9 @@ et-xmlfile==2.0.0
# openpyxl
exceptiongroup==1.3.0
# via fastmcp-slim
fastmcp==3.4.5
fastmcp==3.4.7
# via apache-superset
fastmcp-slim==3.4.5
fastmcp-slim==3.4.7
# via fastmcp
filelock==3.20.3
# via
@@ -337,7 +337,7 @@ geopy==2.4.1
# via
# -c requirements/base-constraint.txt
# apache-superset
gevent==26.7.0
gevent==26.8.0
# via apache-superset
google-api-core==2.33.0
# via
@@ -391,7 +391,7 @@ grpcio==1.83.0
# grpcio-status
grpcio-status==1.60.1
# via google-api-core
gunicorn==26.0.0
gunicorn==26.2.0
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -403,7 +403,7 @@ hashids==1.3.1
# via
# -c requirements/base-constraint.txt
# apache-superset
holidays==0.102
holidays==0.103
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -434,8 +434,6 @@ importlib-metadata==8.7.0
# via
# keyring
# opentelemetry-api
importlib-resources==6.5.2
# via prophet
iniconfig==2.0.0
# via pytest
isodate==0.7.2
@@ -510,7 +508,7 @@ limits==5.1.0
# flask-limiter
lz4==4.4.5
# via trino
mako==1.3.12
mako==1.4.1
# via
# -c requirements/base-constraint.txt
# alembic
@@ -530,7 +528,7 @@ markupsafe==3.0.2
# mako
# werkzeug
# wtforms
marshmallow==4.3.0
marshmallow==4.3.1
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -549,8 +547,10 @@ matplotlib==3.9.0
# via prophet
mccabe==0.7.0
# via pylint
mcp==1.24.0
# via fastmcp-slim
mcp==1.29.1
# via
# apache-superset
# fastmcp-slim
mdurl==0.1.2
# via
# -c requirements/base-constraint.txt
@@ -624,7 +624,6 @@ packaging==25.0
# duckdb-engine
# fastmcp-slim
# google-cloud-bigquery
# gunicorn
# kombu
# limits
# matplotlib
@@ -640,7 +639,7 @@ pandas==2.3.3
# db-dtypes
# pandas-gbq
# prophet
pandas-gbq==0.35.0
pandas-gbq==0.35.1
# via apache-superset
parameterized==0.9.0
# via apache-superset
@@ -664,7 +663,7 @@ pillow==12.3.0
# -c requirements/base-constraint.txt
# apache-superset
# matplotlib
pip==25.1.1
pip==26.2.1
# via apache-superset
platformdirs==4.3.8
# via
@@ -693,7 +692,7 @@ prompt-toolkit==3.0.51
# via
# -c requirements/base-constraint.txt
# click-repl
prophet==1.3.0
prophet==1.4.0
# via apache-superset
proto-plus==1.25.0
# via google-api-core
@@ -711,7 +710,7 @@ psycopg2-binary==2.9.12
# via apache-superset
py-key-value-aio==0.4.4
# via fastmcp-slim
pyarrow==25.0.0
pyarrow==25.0.1
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -921,7 +920,7 @@ secretstorage==3.5.0
# via keyring
semver==3.0.4
# via apache-superset-extensions-cli
setuptools==80.9.0
setuptools==84.0.0
# via
# -c requirements/base-constraint.txt
# nodeenv
@@ -950,7 +949,7 @@ slack-sdk==3.43.0
# apache-superset
sniffio==1.3.1
# via anyio
sqlalchemy==2.0.51
sqlalchemy==2.0.52
# via
# -c requirements/base-constraint.txt
# alembic
@@ -962,9 +961,12 @@ sqlalchemy==2.0.51
# marshmallow-sqlalchemy
# shillelagh
# sqlalchemy-bigquery
# sqlalchemy-cockroachdb
# sqlalchemy-continuum
# sqlalchemy-utils
sqlalchemy-bigquery==1.17.1
sqlalchemy-bigquery==1.17.2
# via apache-superset
sqlalchemy-cockroachdb==2.0.4
# via apache-superset
sqlalchemy-continuum==1.7.0
# via
@@ -976,7 +978,7 @@ sqlalchemy-utils==0.42.1
# apache-superset
# apache-superset-core
# flask-appbuilder
sqlglot==30.16.0
sqlglot==30.17.0
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -995,13 +997,13 @@ starlette==1.3.1
# mcp
statsd==4.0.1
# via apache-superset
syntaqlite==0.7.1
syntaqlite==0.9.0
# via apache-superset
tabulate==0.10.0
# via
# -c requirements/base-constraint.txt
# apache-superset
tiktoken==0.13.0
tiktoken==0.14.0
# via apache-superset
tomli-w==1.2.0
# via apache-superset-extensions-cli
@@ -1011,7 +1013,7 @@ tqdm==4.67.1
# via
# cmdstanpy
# prophet
trino==0.338.0
trino==0.339.0
# via apache-superset
typing-extensions==4.16.0
# via
+1 -1
View File
@@ -93,7 +93,7 @@ def find_models(module: ModuleType) -> list[type[Model]]: # noqa: C901
# where the current model is out-of-sync with the existing table after a
# downgrade
sqlalchemy_uri = current_app.config["SQLALCHEMY_DATABASE_URI"]
engine = create_engine(sqlalchemy_uri, future=True)
engine = create_engine(sqlalchemy_uri)
Base = automap_base() # noqa: N806
Base.prepare(engine, reflect=True)
seen = set()
+1
View File
@@ -42,6 +42,7 @@ RETRYABLE_STATUS_CODES: frozenset[int] = frozenset({429})
PATTERNS = {
"python": [
r"^\.github/workflows/.*python",
r"^\.github/workflows/frontend-bundle-size-nightly\.yml$",
r"^\.github/workflows/scheduled-docker-image-refresh\.yml$",
r"^docker-compose-image-tag\.yml$",
r"^tests/",
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# Computes the `--extra-flags` value passed to `supersetbot docker` for a
# given build preset. Factored out of .github/workflows/docker.yml so the
# PY_VER override logic below can be exercised by an always-on CI check
# (docker.yml's docker-build job only runs when the change detector's
# docker/python/frontend outputs are true, and the PR build matrix never
# includes py311/py312 at all, so a regression here would otherwise go
# unnoticed until the fix actually runs on master) without duplicating -
# and risking drift from - the logic used by the real build step.
#
# supersetbot's "py311"/"py312" presets pin their own --build-arg PY_VER,
# which lands ahead of --extra-flags on the assembled buildx command line;
# docker/buildx keeps the last value for a repeated --build-arg key, so
# appending PY_VER here would override supersetbot's pin and silently make
# "py311"/"py312" build the exact same image as "lean". Every other preset
# gets the override so its build lands on the Dockerfile's own supported
# Python version.
#
# Usage: docker-build-extra-flags.sh <build_preset> <image_tag>
set -euo pipefail
BUILD_PRESET="${1:?usage: docker-build-extra-flags.sh <build_preset> <image_tag>}"
IMAGE_TAG="${2:?usage: docker-build-extra-flags.sh <build_preset> <image_tag>}"
EXTRA_FLAGS="--build-arg INCLUDE_CHROMIUM=false --tag $IMAGE_TAG"
if [ "$BUILD_PRESET" != "py311" ] && [ "$BUILD_PRESET" != "py312" ]; then
EXTRA_FLAGS="--build-arg PY_VER=3.11.14-slim-trixie $EXTRA_FLAGS"
fi
echo "$EXTRA_FLAGS"
@@ -390,19 +390,3 @@ def get_session() -> scoped_session:
:returns: The SQLAlchemy scoped session instance.
"""
raise NotImplementedError("Function will be replaced during initialization")
__all__ = [
"Dataset",
"Database",
"Chart",
"Dashboard",
"User",
"Role",
"Group",
"Tag",
"KeyValue",
"Subject",
"CoreModel",
"get_session",
]
@@ -183,10 +183,3 @@ def prompt(
"MCP prompt decorator not initialized. "
"This decorator should be replaced during Superset startup."
)
__all__ = [
"tool",
"prompt",
"ToolAnnotations",
]
@@ -55,9 +55,3 @@ class SavedQueryDAO(BaseDAO[SavedQuery]):
model_cls = None
base_filter = None
id_column_name = "id"
__all__ = [
"QueryDAO",
"SavedQueryDAO",
]
@@ -71,9 +71,3 @@ class SavedQuery(CoreModel):
database_id: int | None
description: str | None
user_id: int | None
__all__ = [
"Query",
"SavedQuery",
]
@@ -46,6 +46,3 @@ def get_sqlglot_dialect(database: "Database") -> Dialects:
:returns: The SQLGlot dialect enum corresponding to the database.
"""
raise NotImplementedError("Function will be replaced during initialization")
__all__ = ["get_sqlglot_dialect"]
@@ -165,13 +165,3 @@ class AsyncQueryHandle:
:returns: True if cancellation was successful
"""
raise NotImplementedError("Method will be replaced during initialization")
__all__ = [
"QueryStatus",
"QueryOptions",
"QueryResult",
"StatementResult",
"AsyncQueryHandle",
"CacheOptions",
]
@@ -27,6 +27,3 @@ class RestApi(BaseApi):
"""
allow_browser_login = True
__all__ = ["RestApi"]
@@ -98,6 +98,3 @@ def api(
"API decorator not initialized. "
"This decorator should be replaced during Superset startup."
)
__all__ = ["api"]
@@ -0,0 +1,18 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Semantic layer contracts for extension authors."""
@@ -164,6 +164,3 @@ class AbstractSemanticViewDAO(BaseDAO[SemanticViewModel]):
:return: SemanticViewModel instance or None
"""
...
__all__ = ["AbstractSemanticLayerDAO", "AbstractSemanticViewDAO"]
@@ -97,6 +97,3 @@ def semantic_layer(
"Semantic layer decorator not initialized. "
"This decorator should be replaced during Superset startup."
)
__all__ = ["semantic_layer"]
@@ -21,6 +21,7 @@ from abc import ABC, abstractmethod
from typing import Any, Generic, TypeVar
from pydantic import BaseModel
from superset_core.semantic_layers.view import SemanticView
ConfigT = TypeVar("ConfigT", bound=BaseModel)
@@ -80,6 +80,3 @@ class SemanticViewModel(CoreModel):
semantic_layer_uuid: UUID
created_on: datetime | None
changed_on: datetime | None
__all__ = ["SemanticLayerModel", "SemanticViewModel"]
@@ -18,8 +18,9 @@
from __future__ import annotations
import enum
from dataclasses import dataclass
from dataclasses import dataclass, field
from datetime import date, datetime, time, timedelta
from typing import Any
import isodate
import pyarrow as pa
@@ -90,6 +91,8 @@ class Dimension:
definition: str | None = None
description: str | None = None
grain: Grain | None = None
verbose_name: str | None = field(default=None, compare=False)
metadata: dict[str, Any] = field(default_factory=dict, compare=False)
class AggregationType(str, enum.Enum):
@@ -121,6 +124,9 @@ class Metric:
definition: str
description: str | None = None
aggregation: AggregationType | None = None
verbose_name: str | None = field(default=None, compare=False)
d3format: str | None = field(default=None, compare=False)
metadata: dict[str, Any] = field(default_factory=dict, compare=False)
@dataclass(frozen=True)
@@ -71,6 +71,3 @@ class TaskDAO(BaseDAO[Task]):
:returns: Task instance or None if not found or not active
"""
...
__all__ = ["TaskDAO"]
@@ -144,9 +144,3 @@ def get_context() -> TaskContext:
)
"""
raise NotImplementedError("Function will be replaced during initialization")
__all__ = [
"task",
"get_context",
]
@@ -161,9 +161,3 @@ class TaskSubscriber(CoreModel):
changed_on: datetime | None
created_by_fk: int | None
changed_by_fk: int | None
__all__ = [
"Task",
"TaskSubscriber",
]
@@ -226,12 +226,3 @@ class TaskContext(ABC):
cleanup_partial_work()
"""
...
__all__ = [
"TaskStatus",
"TaskScope",
"TaskProperties",
"TaskContext",
"TaskOptions",
]
@@ -0,0 +1,97 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import pyarrow as pa
from superset_core.semantic_layers.types import Dimension, Metric
def test_dimension_metadata_is_not_part_of_identity() -> None:
first = Dimension(
"sales.region",
"region",
pa.utf8(),
verbose_name="Region",
metadata={"display_name": "Region"},
)
second = Dimension(
"sales.region",
"region",
pa.utf8(),
verbose_name="Sales region",
metadata={"display_name": "Sales region"},
)
assert first == second
assert {first, second} == {first}
def test_metric_metadata_is_not_part_of_identity() -> None:
first = Metric(
"sales.total_revenue",
"total_revenue",
pa.float64(),
"SUM(revenue)",
verbose_name="Total revenue",
d3format="$,.2f",
metadata={"unit": {"kind": "currency", "code": "USD"}},
)
second = Metric(
"sales.total_revenue",
"total_revenue",
pa.float64(),
"SUM(revenue)",
verbose_name="Revenue",
d3format=",.0f",
metadata={"unit": {"kind": "currency", "code": "EUR"}},
)
assert first == second
assert {first, second} == {first}
def test_metric_accepts_superset_presentation_fields() -> None:
metric = Metric(
"sales.total_revenue",
"total_revenue",
pa.float64(),
"SUM(revenue)",
verbose_name="Total revenue",
d3format="$,.2f",
)
assert metric.verbose_name == "Total revenue"
assert metric.d3format == "$,.2f"
def test_dimension_accepts_superset_presentation_fields() -> None:
dimension = Dimension(
"sales.region",
"region",
pa.utf8(),
verbose_name="Region",
)
assert dimension.verbose_name == "Region"
def test_metadata_defaults_are_not_shared() -> None:
first = Metric("first", "first", pa.int64(), "COUNT(*)")
second = Metric("second", "second", pa.int64(), "COUNT(*)")
first.metadata["display_name"] = "First"
assert second.metadata == {}
+1
View File
@@ -0,0 +1 @@
../superset-frontend/.npmrc
+506
View File
@@ -19,6 +19,7 @@
"@babel/preset-typescript": "7.29.7",
"@types/node": "^25.4.0",
"babel-loader": "^9.1.3",
"jsdom": "^26.1.0",
"tscw-config": "^1.1.2",
"typescript": "^5.9.3",
"vitest": "^4.0.18",
@@ -26,6 +27,27 @@
"webpack-cli": "^5.1.4"
}
},
"node_modules/@asamuzakjp/css-color": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
"integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@csstools/css-calc": "^2.1.3",
"@csstools/css-color-parser": "^3.0.9",
"@csstools/css-parser-algorithms": "^3.0.4",
"@csstools/css-tokenizer": "^3.0.3",
"lru-cache": "^10.4.3"
}
},
"node_modules/@asamuzakjp/css-color/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true,
"license": "ISC"
},
"node_modules/@babel/cli": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.29.7.tgz",
@@ -1656,6 +1678,121 @@
"node": ">=6.9.0"
}
},
"node_modules/@csstools/color-helpers": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
"integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT-0",
"engines": {
"node": ">=18"
}
},
"node_modules/@csstools/css-calc": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
"integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT",
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@csstools/css-parser-algorithms": "^3.0.5",
"@csstools/css-tokenizer": "^3.0.4"
}
},
"node_modules/@csstools/css-color-parser": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
"integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT",
"dependencies": {
"@csstools/color-helpers": "^5.1.0",
"@csstools/css-calc": "^2.1.4"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@csstools/css-parser-algorithms": "^3.0.5",
"@csstools/css-tokenizer": "^3.0.4"
}
},
"node_modules/@csstools/css-parser-algorithms": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
"integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT",
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@csstools/css-tokenizer": "^3.0.4"
}
},
"node_modules/@csstools/css-tokenizer": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
"integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@discoveryjs/json-ext": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz",
@@ -2501,6 +2638,16 @@
"acorn": "^8.14.0"
}
},
"node_modules/agent-base": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/ajv": {
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
@@ -2868,6 +3015,34 @@
"node": ">= 8"
}
},
"node_modules/cssstyle": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
"integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@asamuzakjp/css-color": "^3.2.0",
"rrweb-cssom": "^0.8.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/data-urls": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
"integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
"dev": true,
"license": "MIT",
"dependencies": {
"whatwg-mimetype": "^4.0.0",
"whatwg-url": "^14.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -2886,6 +3061,13 @@
}
}
},
"node_modules/decimal.js": {
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
"dev": true,
"license": "MIT"
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
@@ -2915,6 +3097,19 @@
"node": ">=10.13.0"
}
},
"node_modules/entities": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/envinfo": {
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz",
@@ -3316,6 +3511,60 @@
"node": ">= 0.4"
}
},
"node_modules/html-encoding-sniffer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
"integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"whatwg-encoding": "^3.1.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/http-proxy-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.0",
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/import-local": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
@@ -3438,6 +3687,13 @@
"node": ">=0.10.0"
}
},
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
"dev": true,
"license": "MIT"
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -3498,6 +3754,46 @@
"dev": true,
"license": "MIT"
},
"node_modules/jsdom": {
"version": "26.1.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz",
"integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
"dev": true,
"license": "MIT",
"dependencies": {
"cssstyle": "^4.2.1",
"data-urls": "^5.0.0",
"decimal.js": "^10.5.0",
"html-encoding-sniffer": "^4.0.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
"is-potential-custom-element-name": "^1.0.1",
"nwsapi": "^2.2.16",
"parse5": "^7.2.1",
"rrweb-cssom": "^0.8.0",
"saxes": "^6.0.0",
"symbol-tree": "^3.2.4",
"tough-cookie": "^5.1.1",
"w3c-xmlserializer": "^5.0.0",
"webidl-conversions": "^7.0.0",
"whatwg-encoding": "^3.1.1",
"whatwg-mimetype": "^4.0.0",
"whatwg-url": "^14.1.1",
"ws": "^8.18.0",
"xml-name-validator": "^5.0.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"canvas": "^3.0.0"
},
"peerDependenciesMeta": {
"canvas": {
"optional": true
}
}
},
"node_modules/jsesc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
@@ -3977,6 +4273,13 @@
"node": ">=0.10.0"
}
},
"node_modules/nwsapi": {
"version": "2.2.24",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.24.tgz",
"integrity": "sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==",
"dev": true,
"license": "MIT"
},
"node_modules/obug": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
@@ -4033,6 +4336,19 @@
"node": ">=6"
}
},
"node_modules/parse5": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"entities": "^6.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4142,6 +4458,16 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -4314,6 +4640,33 @@
"@rolldown/binding-win32-x64-msvc": "1.1.3"
}
},
"node_modules/rrweb-cssom": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
"integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
"dev": true,
"license": "MIT"
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"license": "MIT"
},
"node_modules/saxes": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
"integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
"dev": true,
"license": "ISC",
"dependencies": {
"xmlchars": "^2.2.0"
},
"engines": {
"node": ">=v12.22.7"
}
},
"node_modules/schema-utils": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
@@ -4447,6 +4800,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/symbol-tree": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
"dev": true,
"license": "MIT"
},
"node_modules/tapable": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
@@ -4592,6 +4952,26 @@
"node": ">=14.0.0"
}
},
"node_modules/tldts": {
"version": "6.1.86",
"resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
"integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"tldts-core": "^6.1.86"
},
"bin": {
"tldts": "bin/cli.js"
}
},
"node_modules/tldts-core": {
"version": "6.1.86",
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
"integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
"dev": true,
"license": "MIT"
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -4605,6 +4985,32 @@
"node": ">=8.0"
}
},
"node_modules/tough-cookie": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
"integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"tldts": "^6.1.32"
},
"engines": {
"node": ">=16"
}
},
"node_modules/tr46": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
"dev": true,
"license": "MIT",
"dependencies": {
"punycode": "^2.3.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/tscw-config": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/tscw-config/-/tscw-config-1.1.2.tgz",
@@ -4931,6 +5337,19 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/w3c-xmlserializer": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
"integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
"dev": true,
"license": "MIT",
"dependencies": {
"xml-name-validator": "^5.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/watchpack": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz",
@@ -4944,6 +5363,16 @@
"node": ">=10.13.0"
}
},
"node_modules/webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
}
},
"node_modules/webpack": {
"version": "5.105.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz",
@@ -5070,6 +5499,44 @@
"node": ">=10.13.0"
}
},
"node_modules/whatwg-encoding": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
"deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
"dev": true,
"license": "MIT",
"dependencies": {
"iconv-lite": "0.6.3"
},
"engines": {
"node": ">=18"
}
},
"node_modules/whatwg-mimetype": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/whatwg-url": {
"version": "14.2.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
"dev": true,
"license": "MIT",
"dependencies": {
"tr46": "^5.1.0",
"webidl-conversions": "^7.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -5114,6 +5581,45 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
"node_modules/ws": {
"version": "8.21.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz",
"integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xml-name-validator": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
"integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18"
}
},
"node_modules/xmlchars": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"dev": true,
"license": "MIT"
},
"node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+1
View File
@@ -43,6 +43,7 @@
"@babel/preset-typescript": "7.29.7",
"@types/node": "^25.4.0",
"babel-loader": "^9.1.3",
"jsdom": "^26.1.0",
"tscw-config": "^1.1.2",
"typescript": "^5.9.3",
"vitest": "^4.0.18",
+213
View File
@@ -0,0 +1,213 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// @vitest-environment jsdom
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import { Switchboard } from "@superset-ui/switchboard";
import { embedDashboard } from "./index";
vi.mock("@superset-ui/switchboard");
function makeFakeJWT(claims: any) {
// not a valid jwt, but close enough for this code
const tokenifiedClaims = Buffer.from(JSON.stringify(claims)).toString(
"base64",
);
return `abc.${tokenifiedClaims}.xyz`;
}
describe("embedDashboard", () => {
let mountPoint: HTMLElement;
let mockSwitchboard: Switchboard;
beforeEach(() => {
mountPoint = document.createElement("div");
document.body.appendChild(mountPoint);
mockSwitchboard = {
emit: vi.fn(),
get: vi.fn(),
start: vi.fn(),
defineMethod: vi.fn(),
} as any;
// Constructor mocks must use `function`, since arrow functions cannot be
// invoked with `new`.
vi.mocked(Switchboard).mockImplementation(function () {
return mockSwitchboard;
} as any);
// Mock MessageChannel API
globalThis.MessageChannel = vi.fn(function (this: any) {
this.port1 = {};
this.port2 = {};
}) as any;
// Mock iframe load event and sandbox
const originalCreateElement = document.createElement.bind(document);
vi.spyOn(document, "createElement").mockImplementation(tagName => {
const element = originalCreateElement(tagName);
if (tagName === "iframe") {
// Mock sandbox DOMTokenList
(element as any).sandbox = {
add: vi.fn(),
};
// Mock contentWindow for postMessage
Object.defineProperty(element, "contentWindow", {
writable: true,
value: {
postMessage: vi.fn(),
},
});
setTimeout(() => {
element.dispatchEvent(new Event("load"));
}, 0);
}
return element;
});
});
afterEach(() => {
document.body.removeChild(mountPoint);
vi.restoreAllMocks();
});
test("setDataMask sends dataMask to iframe", async () => {
const fakeToken = makeFakeJWT({ exp: Date.now() / 1000 + 300 });
const mockFetchGuestToken = vi.fn().mockResolvedValue(fakeToken);
const testDataMask = {
"NATIVE_FILTER-1": {
filterState: {
value: ["value1", "value2"],
},
},
};
const dashboard = await embedDashboard({
id: "test-id",
supersetDomain: "https://superset.example.com",
mountPoint,
fetchGuestToken: mockFetchGuestToken,
});
dashboard.setDataMask(testDataMask);
expect(mockSwitchboard.get).toHaveBeenCalledWith("setDataMask", {
dataMask: testDataMask,
});
});
test("setDataMask sends empty dataMask", async () => {
const fakeToken = makeFakeJWT({ exp: Date.now() / 1000 + 300 });
const mockFetchGuestToken = vi.fn().mockResolvedValue(fakeToken);
const emptyDataMask = {};
const dashboard = await embedDashboard({
id: "test-id",
supersetDomain: "https://superset.example.com",
mountPoint,
fetchGuestToken: mockFetchGuestToken,
});
dashboard.setDataMask(emptyDataMask);
expect(mockSwitchboard.get).toHaveBeenCalledWith("setDataMask", {
dataMask: emptyDataMask,
});
});
test("setDataMask drops the change-trigger flags observeDataMask adds", async () => {
const fakeToken = makeFakeJWT({ exp: Date.now() / 1000 + 300 });
const mockFetchGuestToken = vi.fn().mockResolvedValue(fakeToken);
const observedMask = {
"NATIVE_FILTER-1": {
filterState: {
value: ["CA"],
},
},
crossFiltersChanged: false,
nativeFiltersChanged: true,
};
const dashboard = await embedDashboard({
id: "test-id",
supersetDomain: "https://superset.example.com",
mountPoint,
fetchGuestToken: mockFetchGuestToken,
});
dashboard.setDataMask(observedMask);
expect(mockSwitchboard.get).toHaveBeenCalledWith("setDataMask", {
dataMask: {
"NATIVE_FILTER-1": observedMask["NATIVE_FILTER-1"],
},
});
});
test("setDataMask sends complex dataMask with multiple filters", async () => {
const fakeToken = makeFakeJWT({ exp: Date.now() / 1000 + 300 });
const mockFetchGuestToken = vi.fn().mockResolvedValue(fakeToken);
const complexDataMask = {
"NATIVE_FILTER-1": {
filterState: {
value: ["CA", "NY"],
},
},
"NATIVE_FILTER-2": {
filterState: {
value: [2023, 2024],
},
},
};
const dashboard = await embedDashboard({
id: "test-id",
supersetDomain: "https://superset.example.com",
mountPoint,
fetchGuestToken: mockFetchGuestToken,
});
dashboard.setDataMask(complexDataMask);
expect(mockSwitchboard.get).toHaveBeenCalledWith("setDataMask", {
dataMask: complexDataMask,
});
});
test("setDataMask rejects when the embedded page does not support it", async () => {
const fakeToken = makeFakeJWT({ exp: Date.now() / 1000 + 300 });
const mockFetchGuestToken = vi.fn().mockResolvedValue(fakeToken);
vi.mocked(mockSwitchboard.get).mockRejectedValue(
new Error('Method "setDataMask" is not defined'),
);
const dashboard = await embedDashboard({
id: "test-id",
supersetDomain: "https://superset.example.com",
mountPoint,
fetchGuestToken: mockFetchGuestToken,
});
await expect(dashboard.setDataMask({})).rejects.toThrow(
'Method "setDataMask" is not defined',
);
});
});
+22
View File
@@ -115,6 +115,12 @@ export type EmbeddedDashboard = {
getActiveTabs: () => Promise<string[]>;
observeDataMask: (callbackFn: ObserveDataMaskCallbackFn) => void;
getDataMask: () => Promise<Record<string, any>>;
/**
* Applies a data mask to the dashboard.
* Rejects if the embedded Superset page does not support `setDataMask`,
* so a version mismatch surfaces instead of silently doing nothing.
*/
setDataMask: (dataMask: Record<string, any>) => Promise<void>;
getChartStates: () => Promise<Record<string, any>>;
getChartDataPayloads: (params?: {
chartId?: number;
@@ -355,6 +361,21 @@ export async function embedDashboard({
ourPort.get<string>("getDashboardPermalink", { anchor });
const getActiveTabs = () => ourPort.get<string[]>("getActiveTabs");
const getDataMask = () => ourPort.get<Record<string, any>>("getDataMask");
// `observeDataMask` hands the host a mask with the change-trigger booleans
// mixed in, so feeding that payload straight back into `setDataMask` is a
// natural thing for a host to do. Keep only the entries that look like a
// filter's mask, so those flags never reach the dashboard as filter ids.
// Sent with `get` rather than `emit` so the iframe acknowledges the call:
// an embedded page that predates `setDataMask` replies with an error instead
// of dropping the message silently.
const setDataMask = (dataMask: Record<string, any>) =>
ourPort.get<void>("setDataMask", {
dataMask: Object.fromEntries(
Object.entries(dataMask).filter(
([, mask]) => typeof mask === "object" && mask !== null,
),
),
});
const getChartStates = () =>
ourPort.get<Record<string, any>>("getChartStates");
const getChartDataPayloads = (params?: { chartId?: number }) =>
@@ -396,6 +417,7 @@ export async function embedDashboard({
getActiveTabs,
observeDataMask,
getDataMask,
setDataMask,
getChartStates,
getChartDataPayloads,
setThemeConfig,
+1
View File
@@ -0,0 +1 @@
min-release-age=3
+1 -1
View File
@@ -77,7 +77,7 @@ module.exports = {
// @ant-design/colors and @ant-design/fast-color are allowed through because
// @ant-design/icons >= 6.3 deep-imports the ESM build of @ant-design/colors
// from its CJS output, so babel-jest must transform those files.
'node_modules/(?!@ant-design/(colors|fast-color)|@formatjs/.*|d3-(array|interpolate|color|time|scale|time-format|format|selection)|internmap|@mapbox/tiny-sdf|remark-gfm|(?!@ngrx|(?!deck.gl)|d3-scale)|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|uuid|@rjsf/*.|@x0k/.*|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|parse5|dom-serializer|entities|htmlparser2|rehype-sanitize|hast-util-sanitize|unified|unist-.*|hast-.*|hastscript|refractor|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|character-reference-invalid|is-alphanumerical|is-alphabetical|is-decimal|is-hexadecimal|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|geostyler|geostyler-.*|(?!geostyler)lodash|react-error-boundary|react-json-tree|react-base16-styling|lodash-es|rbush|quickselect|react-diff-viewer-continued|storybook/*.|json-stringify-pretty-compact|@x0k/json-schema-merge)',
'node_modules/(?!@ant-design/(colors|fast-color)|@formatjs/.*|d3-(array|interpolate|color|time|scale|time-format|format|selection)|internmap|@mapbox/tiny-sdf|remark-gfm|(?!@ngrx|(?!deck.gl)|d3-scale)|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|uuid|@rjsf/*.|@x0k/.*|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|parse5|dom-serializer|entities|htmlparser2|rehype-sanitize|hast-util-sanitize|unified|unist-.*|hast-.*|hastscript|refractor|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|character-reference-invalid|is-alphanumerical|is-alphabetical|is-decimal|is-hexadecimal|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|geostyler|geostyler-.*|(?!geostyler)lodash|react-error-boundary|react-json-tree|react-base16-styling|lodash-es|rbush|quickselect|react-diff-viewer-continued|storybook/*.|json-stringify-pretty-compact|@x0k/json-schema-merge|content-disposition)',
],
preset: 'ts-jest',
transform: {
+1 -1
View File
@@ -113,7 +113,7 @@
// === Import plugin rules ===
"import/named": "error",
"import/export": "error",
"import/no-named-as-default": "error",
"import/no-named-as-default": "warn",
"import/no-named-as-default-member": "error",
"import/no-mutable-exports": "error",
"import/no-amd": "error",
+709 -1155
View File
File diff suppressed because it is too large Load Diff
+32 -31
View File
@@ -110,7 +110,7 @@
"@fontsource/fira-code": "^5.3.0",
"@fontsource/ibm-plex-mono": "^5.3.0",
"@fontsource/inter": "^5.3.0",
"@googleapis/sheets": "^13.0.2",
"@googleapis/sheets": "^14.0.0",
"@great-expectations/jsonforms-antd-renderers": "^2.2.10",
"@jsonforms/core": "^3.7.0",
"@jsonforms/react": "^3.7.0",
@@ -122,9 +122,9 @@
"@luma.gl/shadertools": "~9.2.5",
"@luma.gl/webgl": "~9.2.5",
"@reduxjs/toolkit": "^1.9.3",
"@rjsf/core": "^6.7.1",
"@rjsf/core": "^6.8.0",
"@rjsf/utils": "^6.6.2",
"@rjsf/validator-ajv8": "^6.7.1",
"@rjsf/validator-ajv8": "^6.8.0",
"@scarf/scarf": "^1.4.0",
"@superset-ui/chart-controls": "file:./packages/superset-ui-chart-controls",
"@superset-ui/core": "file:./packages/superset-ui-core",
@@ -158,12 +158,12 @@
"@visx/xychart": "^4.0.0",
"ag-grid-community": "36.1.0",
"ag-grid-react": "36.1.0",
"antd": "^6.5.4",
"antd": "^6.6.1",
"chrono-node": "^2.10.1",
"classnames": "^2.2.5",
"content-disposition": "^2.0.1",
"content-disposition": "^3.0.0",
"d3-scale": "^4.0.2",
"dayjs": "^1.11.21",
"dayjs": "^1.11.23",
"dom-to-image-more": "^3.10.2",
"dom-to-pdf": "^0.3.2",
"echarts": "^6.1.0",
@@ -176,8 +176,8 @@
"geostyler-openlayers-parser": "^5.7.1",
"geostyler-style": "11.0.2",
"geostyler-wfs-parser": "^3.0.1",
"google-auth-library": "^11.0.0",
"immer": "^11.1.16",
"google-auth-library": "^11.0.2",
"immer": "^11.1.18",
"interweave": "^13.1.1",
"jquery": "^4.0.0",
"js-levenshtein": "^1.1.6",
@@ -185,7 +185,7 @@
"json-stringify-pretty-compact": "^4.0.0",
"lodash": "^4.18.1",
"lodash-es": "^4.18.1",
"mapbox-gl": "^3.28.1",
"mapbox-gl": "^3.29.0",
"markdown-to-jsx": "^9.10.2",
"match-sorter": "^8.3.0",
"memoize-one": "^6.0.0",
@@ -230,7 +230,7 @@
"use-event-callback": "^0.1.0",
"use-immer": "^0.11.0",
"use-query-params": "^2.2.2",
"uuid": "^14.0.1",
"uuid": "^14.0.2",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
"yargs": "^18.1.0"
},
@@ -257,16 +257,16 @@
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@playwright/test": "^1.62.1",
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
"@storybook/addon-docs": "10.5.7",
"@storybook/addon-links": "10.5.7",
"@storybook/react-webpack5": "10.5.7",
"@storybook/addon-docs": "10.5.10",
"@storybook/addon-links": "10.5.10",
"@storybook/react-webpack5": "10.5.10",
"@storybook/test-runner": "0.24.4",
"@svgr/webpack": "^8.1.0",
"@swc/core": "^1.15.47",
"@swc/plugin-emotion": "^14.15.0",
"@swc/plugin-transform-imports": "^12.5.0",
"@swc/core": "^1.16.1",
"@swc/plugin-emotion": "^15.0.0",
"@swc/plugin-transform-imports": "^13.0.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^7.0.0",
"@testing-library/jest-dom": "^7.0.1",
"@testing-library/react": "^15.0.0",
"@testing-library/user-event": "^12.8.3",
"@types/content-disposition": "^0.5.9",
@@ -277,7 +277,7 @@
"@types/json-bigint": "^1.0.4",
"@types/lodash-es": "^4.17.12",
"@types/mousetrap": "^1.6.15",
"@types/node": "^26.1.2",
"@types/node": "^26.2.0",
"@types/react": "^18.3.0",
"@types/react-dom": "^18.3.0",
"@types/react-loadable": "^5.5.11",
@@ -289,19 +289,19 @@
"@types/rison": "0.1.0",
"@types/tinycolor2": "^1.4.3",
"@types/unzipper": "^0.10.11",
"@typescript-eslint/eslint-plugin": "^8.66.0",
"@typescript-eslint/eslint-plugin": "^8.67.0",
"@typescript-eslint/parser": "^8.63.0",
"babel-jest": "^30.4.1",
"babel-loader": "^10.1.1",
"babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
"baseline-browser-mapping": "^2.11.12",
"baseline-browser-mapping": "^2.11.16",
"cheerio": "1.2.0",
"concurrently": "^10.0.4",
"concurrently": "^10.0.5",
"copy-webpack-plugin": "^14.0.0",
"cross-env": "^10.1.0",
"css-loader": "^7.1.4",
"eslint": "^10.8.0",
"eslint": "^10.8.1",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-import-resolver-typescript": "^4.4.5",
"eslint-plugin-i18n-strings": "file:eslint-rules/eslint-plugin-i18n-strings",
@@ -311,8 +311,8 @@
"eslint-plugin-lodash": "^8.0.0",
"eslint-plugin-no-only-tests": "^3.4.0",
"eslint-plugin-react-prefer-function-component": "^5.0.0",
"eslint-plugin-react-you-might-not-need-an-effect": "^1.0.1",
"eslint-plugin-storybook": "10.5.7",
"eslint-plugin-react-you-might-not-need-an-effect": "^1.0.2",
"eslint-plugin-storybook": "10.5.10",
"eslint-plugin-testing-library": "^7.16.2",
"eslint-plugin-theme-colors": "file:eslint-rules/eslint-plugin-theme-colors",
"fetch-mock": "^12.6.0",
@@ -326,13 +326,13 @@
"jest-websocket-mock": "^2.5.0",
"js-yaml-loader": "^1.2.2",
"jsdom": "^30.0.1",
"lerna": "^10.0.0",
"lerna": "^10.0.1",
"lightningcss": "^1.33.0",
"mini-css-extract-plugin": "^2.10.2",
"minimizer-webpack-plugin": "^5.6.1",
"open-cli": "^9.0.0",
"oxfmt": "^0.62.0",
"oxlint": "^1.77.0",
"oxfmt": "^0.64.0",
"oxlint": "^1.79.0",
"po2json": "^0.4.5",
"postcss-styled-syntax": "^0.7.2",
"process": "^0.11.10",
@@ -343,13 +343,13 @@
"source-map": "^0.8.0",
"source-map-support": "^0.5.21",
"speed-measure-webpack-plugin": "^1.6.0",
"storybook": "10.5.7",
"storybook": "10.5.10",
"style-loader": "^4.0.0",
"stylelint": "^17.14.1",
"swc-loader": "^0.2.7",
"ts-jest": "^29.4.12",
"tscw-config": "^1.1.2",
"tsx": "^4.23.10",
"tsx": "^4.23.12",
"typescript": "5.4.5",
"unzipper": "^0.12.5",
"wait-on": "^9.1.0",
@@ -392,7 +392,7 @@
"@luma.gl/shadertools": "~9.2.5",
"@luma.gl/webgl": "~9.2.5",
"core-js": "^3.38.1",
"dompurify": "^3.4.11",
"dompurify": "^3.4.13",
"esbuild": "^0.28.1",
"eslint-plugin-import": {
"eslint": "$eslint"
@@ -415,7 +415,8 @@
"minimatch@>=10": {
"brace-expansion": ">=5.0.8"
},
"nwsapi": "^2.2.13",
"nanoid@>=3 <4": "3.3.18",
"nwsapi": "^2.2.24",
"puppeteer": "^22.4.1",
"tar": "^7.5.16",
"typescript-json-schema": "^0.68.0",
@@ -130,6 +130,7 @@ export enum GenericDataType {
String = 1,
Temporal = 2,
Boolean = 3,
MultiValue = 4,
}
/**
@@ -28,6 +28,7 @@ import {
FieldBinaryOutlined,
FieldStringOutlined,
NumberOutlined,
UnorderedListOutlined,
} from '@ant-design/icons';
import { Icons } from '@superset-ui/core/components';
@@ -72,6 +73,10 @@ export function ColumnTypeLabel({ type }: ColumnTypeLabelProps) {
typeIcon = <FieldBinaryOutlined aria-label={t('boolean type icon')} />;
} else if (type === GenericDataType.Temporal) {
typeIcon = <ClockCircleOutlined aria-label={t('temporal type icon')} />;
} else if (type === GenericDataType.MultiValue) {
typeIcon = (
<UnorderedListOutlined aria-label={t('multi-value type icon')} />
);
}
return <TypeIconWrapper>{typeIcon}</TypeIconWrapper>;
@@ -51,6 +51,20 @@ export interface MetricOptionProps {
shouldShowTooltip?: boolean;
}
/**
* `url` is an arbitrary caller-supplied string rendered as an href. Only
* http(s) and relative URLs become links; other schemes degrade to plain
* text.
*/
function isSafeHref(url: string): boolean {
try {
const { protocol } = new URL(url, window.location.origin);
return protocol === 'http:' || protocol === 'https:';
} catch {
return false;
}
}
export function MetricOption({
metric,
labelRef,
@@ -70,7 +84,7 @@ export function MetricOption({
`}
ref={labelRef}
>
{url ? (
{url && isSafeHref(url) ? (
<Typography.Link
href={url}
target={openInNewWindow ? '_blank' : ''}
@@ -122,6 +122,12 @@ export const timeComparisonControls: ({
}
return newState;
},
// Re-run this control's validation whenever `time_compare` changes so
// the "date required" error clears once a non-custom shift is picked.
// Without it the stale error survives in Redux (see the
// dependantControls path in exploreReducer's SET_FIELD_VALUE handler)
// and blocks further chart updates until a page refresh.
validationDependencies: ['time_compare'],
},
},
],
@@ -17,7 +17,7 @@
* under the License.
*/
import { QueryFormMetric } from '@superset-ui/core';
import { getTotalsMetrics } from './getTotalsMetrics';
import { getTotalsMetrics, toTotalsAggregate } from './getTotalsMetrics';
const simpleMetric = (aggregate: string): QueryFormMetric =>
({
@@ -76,4 +76,31 @@ describe('getTotalsMetrics', () => {
test('returns an empty array when given no metrics', () => {
expect(getTotalsMetrics([], 'AVG')).toEqual([]);
});
test("ORIGINAL keeps each metric's own aggregate", () => {
const metrics = [
simpleMetric('COUNT_DISTINCT'),
sqlMetric(),
savedMetric(),
];
const result = getTotalsMetrics(metrics, 'ORIGINAL');
expect(result).toBe(metrics);
expect(result[0]).toEqual(
expect.objectContaining({ aggregate: 'COUNT_DISTINCT' }),
);
});
});
describe('toTotalsAggregate', () => {
test.each(['SUM', 'AVG'] as const)('passes %s through', value => {
expect(toTotalsAggregate(value)).toBe(value);
});
test.each([undefined, null, '', 'MEDIAN', 'sum'])(
'falls back to ORIGINAL for %p',
value => {
expect(toTotalsAggregate(value)).toBe('ORIGINAL');
},
);
});

Some files were not shown because too many files have changed in this diff Show More