Compare commits

...

162 Commits

Author SHA1 Message Date
dependabot[bot]
139b21f593 chore(deps): bump github/codeql-action/init from 4.37.2 to 4.37.3
Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.37.2 to 4.37.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](e0647621c2...e4fba868fa)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.37.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-29 07:50:54 +00:00
Evan Rusackas
dac69f9bcd feat(pie): geometric recentering and scaling for partial arcs (#42151)
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-28 22:27:11 -07:00
Rehan Islam
d984fb08a4 feat(explore): add download control to standalone charts (#42238) 2026-07-28 22:06:45 -07:00
jesperct
8be430014e fix(dashboard): make Clear All clear filters that have default values (#42111) 2026-07-28 22:05:51 -07:00
aikawa-ohno
00c60bf2de fix(i18n): Update Japanese translations (#42444) 2026-07-28 17:04:47 -07:00
Evan Rusackas
1d4c3a2adb fix(core): don't blank a datetime column when its format coerces every value to NaT (#42405)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-28 15:18:53 -07:00
Elizabeth Thompson
4a4f0685fd fix(reports): prevent bad-date crontab from crashing the report scheduler for all tenants (#42486) 2026-07-28 15:02:36 -07:00
Kobi Hikri
22ad3f2c34 fix(ci): make the Dependency Review check able to fail again (#42537) 2026-07-28 11:52:12 -07:00
Joe Li
9cdb555255 fix(docker): isolate tagged images from host packages (#42249) 2026-07-28 11:51:48 -07:00
Grégoire Gailly
fc59d9c0cf fix(i18n): improve french translations (#42470)
Co-authored-by: Elsanbot <elsanbot@elsan.care>
2026-07-28 11:48:31 -07:00
dependabot[bot]
623348f190 chore(deps-dev): bump @typescript-eslint/eslint-plugin from 8.64.0 to 8.65.0 in /superset-frontend in the typescript-eslint group across 1 directory (#42512)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 10:29:58 -07:00
Amin Ghadersohi
5aaf9d337b fix(jinja): handle non-JSON body with JSON content-type in get_form_data (#42196) 2026-07-28 09:18:08 -07:00
JUST.in DO IT
181f5bc9c1 fix(mcp): capture created chart/dashboard id in generate_chart/generate_dashboard audit logs (#42367) 2026-07-28 09:16:58 -07:00
Đỗ Trọng Hải
e426fb7bc6 chore(ci): correct GH event type for opening new issue and PR in supersetbot orglabel job (#42424)
Signed-off-by: hainenber <dotronghai96@gmail.com>
2026-07-28 09:16:41 -07:00
Evan Rusackas
b5c8d4a5df fix(mixed-timeseries): stop duplicating first metric in multi-metric + group-by series names (#40146)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-28 09:15:26 -07:00
dependabot[bot]
236c9a52fd chore(deps-dev): bump @typescript-eslint/eslint-plugin from 8.63.0 to 8.65.0 in /superset-websocket (#42515)
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-07-28 08:06:12 -07:00
Mehmet Salih Yavuz
b983161eef feat(async): cancel running chart queries under GLOBAL_ASYNC_QUERIES (#42305) 2026-07-28 18:02:16 +03:00
Evan Rusackas
7b767f6c1d chore(ci): swap linkinator-action for lychee-action in docs link checker (#42501)
Co-authored-by: Superset Dev <dev@superset.apache.org>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 21:33:01 +07:00
dependabot[bot]
526cebac73 chore(deps-dev): bump prettier from 3.9.5 to 3.9.6 in /superset-websocket (#42514)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 21:30:23 +07:00
dependabot[bot]
39f9b7029b chore(deps): bump github/codeql-action/analyze from 4.37.1 to 4.37.2 (#42520)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 16:05:23 +03:00
dependabot[bot]
25a985893d chore(deps-dev): bump typescript-eslint from 8.64.0 to 8.65.0 in /superset-websocket (#42516)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 03:49:06 -07:00
dependabot[bot]
ad15c4ddd1 chore(deps): bump the storybook group in /docs with 2 updates (#42517)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 03:49:02 -07:00
dependabot[bot]
a301e7c511 chore(deps-dev): bump the typescript-eslint group in /docs with 3 updates (#42518)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 03:48:59 -07:00
dependabot[bot]
dd7bbe0adf chore(deps-dev): bump prettier from 3.9.5 to 3.9.6 in /docs (#42519)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 03:48:56 -07:00
dependabot[bot]
2e81eb580f chore(deps): bump baseline-browser-mapping from 2.10.43 to 2.10.44 in /docs (#42521)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 03:48:49 -07:00
dependabot[bot]
a970838b5c chore(deps): bump github/codeql-action/init from 4.37.1 to 4.37.2 (#42522)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 03:48:46 -07:00
dependabot[bot]
2c061f1e05 chore(deps-dev): bump the storybook group in /superset-frontend with 5 updates (#42523)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 03:48:42 -07:00
dependabot[bot]
be012c8faa chore(deps): bump markdown-to-jsx from 9.8.2 to 9.9.0 in /superset-frontend (#42524)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 03:48:39 -07:00
dependabot[bot]
e754b73af2 chore(deps-dev): bump baseline-browser-mapping from 2.10.43 to 2.10.44 in /superset-frontend (#42526)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 03:48:35 -07:00
dependabot[bot]
435e7825a6 chore(deps-dev): bump prettier from 3.9.5 to 3.9.6 in /superset-frontend (#42527)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 03:48:32 -07:00
Mehmet Salih Yavuz
ecc7f726a4 fix: guard potential null derefs and remove dead branches (#42358)
Co-authored-by: Enzo Martellucci <52219496+EnxDev@users.noreply.github.com>
2026-07-28 11:43:46 +03:00
PRATHAMESH HUKKERI
26b6f7bb5d fix(dashboard): preserve refresh_frequency when absent from save data (#42354)
Co-authored-by: Prathamesh Hukkeri <prathamesh04@users.noreply.github.com>
2026-07-27 23:13:11 -07:00
Phuc Hung Nguyen
a10c3f0b1d test(result_set): add regression test for empty result set column metadata (#35962)
Co-authored-by: Phuc Hung Nguyen <phucnguyen@geotab.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-07-27 23:08:59 -07:00
PRATHAMESH HUKKERI
3095d7b07f fix(metadata-bar): add max-width to text for proper ellipsis truncation (#42237)
Co-authored-by: Prathamesh Hukkeri <prathamesh04@users.noreply.github.com>
2026-07-27 20:38:29 -07:00
Gaurav Dubey
a8e2a340f1 fix(select): exclude null-valued options from "Select all" count (#42220)
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-07-27 20:24:44 -07:00
Pawan
c0117f78a9 feat(dashboard): respect GranularExportControls for download permission (#41799) 2026-07-27 20:00:03 -07:00
Mike Bridge
a73e2485de feat(versioning): version-history retention cleanup job (#41075)
Co-authored-by: Mike Bridge <michael.bridge@ext.preset.io>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 19:59:11 -07:00
Jean-Philippe Monette
79f3fed1f9 fix(country-map): carve the Great Lakes out of USA map states (#29503)
Co-authored-by: Evan Rusackas <evan@rusackas.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-07-27 19:58:32 -07:00
Amogh Atreya
c792752a58 fix(db_engine_specs): preserve DATE semantics when applying Postgres time grains (#42254) (#42329)
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
2026-07-27 17:31:28 -07:00
Elizabeth Thompson
eb914b8ae3 fix(datasets): log datetime format-detection DB query failures at WARNING (#42388)
Co-authored-by: Claude <noreply@anthropic.com>
2026-07-27 15:09:28 -07:00
Elizabeth Thompson
276b7e2d67 fix(importers): catch YAMLError instead of narrower ParserError in dataset v0 importer (#42442) 2026-07-27 15:04:52 -07:00
Elizabeth Thompson
3e7ae85cc2 fix(importers): catch YAMLError instead of narrower ParserError in load_yaml (#42426) 2026-07-27 15:03:37 -07:00
jobbywl
12d7179c21 fix(hive): select schema via USE prequery instead of rewriting the URI database (#36603)
Co-authored-by: Job <job@samax.nu>
Co-authored-by: Evan Rusackas <evan@preset.io>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 14:54:16 -07:00
Joe Li
1d752a0ced fix(embedded): load guest charts with missing or stale query_context (#42150)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 14:14:55 -07:00
Joe Li
a44afd8105 chore: drop legacy /superset prefix from remaining hardcoded URLs (#42156)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 14:13:35 -07:00
Nitish Agarwal
74924ae73a fix(db_engine_specs): use CAST(DATE({col}) AS DATETIME) in MySQL HOUR time grain (#38617) 2026-07-27 12:52:25 -07:00
dependabot[bot]
748e1e80f0 chore(deps): bump actions/setup-python from 6.3.0 to 7.0.0 (#42452)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 12:45:26 -07:00
dependabot[bot]
4300e9fb93 chore(deps): bump hot-shots from 17.0.0 to 17.0.1 in /superset-websocket (#42445)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 11:24:56 -07:00
dependabot[bot]
3540f3f957 chore(deps): bump the rjsf group in /superset-frontend with 3 updates (#42446)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 11:24:53 -07:00
dependabot[bot]
faf2cc0f04 chore(deps): bump @fontsource/ibm-plex-mono from 5.2.7 to 5.3.0 in /docs (#42447)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 11:24:49 -07:00
dependabot[bot]
45c2b801f7 chore(deps): bump @fontsource/ibm-plex-mono from 5.2.7 to 5.3.0 in /superset-frontend (#42458)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 11:24:45 -07:00
Đỗ Trọng Hải
03c97764d1 chore(ci): remove nyc usage for merging coverage results as Codecov natively supports the action (#42431)
Signed-off-by: hainenber <dotronghai96@gmail.com>
2026-07-27 10:28:26 -07:00
Evan Rusackas
95e5e25e1a feat(helm): mitigate init-db Job hang under Istio sidecar injection (closes #25798) (#41396)
Co-authored-by: Devin AI <devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 10:09:30 -07:00
dependabot[bot]
dece457792 chore(deps): bump @swc/core from 1.15.43 to 1.15.46 in /docs (#42448)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 07:52:52 -07:00
dependabot[bot]
3666b9a934 chore(deps): bump swagger-ui-react from 5.32.8 to 5.32.9 in /docs (#42449)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 07:51:55 -07:00
dependabot[bot]
c09ebc2087 chore(deps): bump @fontsource/inter from 5.2.8 to 5.3.0 in /docs (#42450)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 07:51:50 -07:00
dependabot[bot]
23e5cc24d5 chore(deps): bump @fontsource/fira-code from 5.2.7 to 5.3.0 in /docs (#42451)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 07:51:45 -07:00
dependabot[bot]
fe78726935 chore(deps-dev): bump stylelint from 17.14.0 to 17.14.1 in /superset-frontend (#42453)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 07:51:38 -07:00
dependabot[bot]
b3c95d138a chore(deps): bump chrono-node from 2.10.0 to 2.10.1 in /superset-frontend (#42454)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 07:51:32 -07:00
dependabot[bot]
5898463ced chore(deps-dev): bump @swc/core from 1.15.43 to 1.15.46 in /superset-frontend (#42455)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 07:51:26 -07:00
dependabot[bot]
0d2a82c2b2 chore(deps): bump react-arborist from 3.13.2 to 3.15.0 in /superset-frontend (#42456)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 07:51:21 -07:00
dependabot[bot]
67face3e3d chore(deps-dev): bump lightningcss from 1.32.0 to 1.33.0 in /superset-frontend (#42457)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 07:51:16 -07:00
dependabot[bot]
fdea53df9e chore(deps-dev): bump source-map from 0.7.6 to 0.8.0 in /superset-frontend (#42459)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 07:51:08 -07:00
dependabot[bot]
5dea601a09 chore(deps): bump @fontsource/inter from 5.2.8 to 5.3.0 in /superset-frontend (#42460)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 07:51:02 -07:00
dependabot[bot]
943bbb92e4 chore(deps): bump @fontsource/fira-code from 5.2.7 to 5.3.0 in /superset-frontend (#42461)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 07:50:57 -07:00
Enzo Martellucci
e338c77dd7 fix(dashboard-tabs): disable drag on input fields during tab reorder (#36889) 2026-07-27 16:06:41 +02:00
Sepuri Sai Krishna
6856d0fc67 fix(select): rank case-insensitive matches consistently in dropdown search (#42408) 2026-07-26 19:29:30 -07:00
Hans Yu
8f8331f656 chore: session enforce sqlalchemy 2.0 (#42365)
Co-authored-by: Evan Rusackas <evan@rusackas.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 19:28:42 -07:00
Evan Rusackas
e54eccd5fb chore(a11y): enable jsx-a11y/prefer-tag-over-role as error (#42078)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 17:38:55 -07:00
Evan Rusackas
710037d3d2 chore(importers): log field names instead of full config on validation failure (#42399)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 17:36:34 -07:00
Elizabeth Thompson
90040fc1f6 fix(mcp_service): downgrade client-disconnect transport noise to WARNING (SC-115264) (#42441)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
2026-07-26 15:02:15 -07:00
Evan Rusackas
ba0900d2b3 fix(semantic-layers): show a friendly warning when schema enrichment falls back (#42400)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 02:57:17 -07:00
Elizabeth Thompson
9a025267bd fix(reports): positive readiness check for non-tiled screenshots (#42253)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Mafi <matt.fitzgerald@gmail.com>
Co-authored-by: Matt Fitzgerald <matt.fitzgerald@preset.io>
2026-07-26 01:14:06 -04:00
dependabot[bot]
e3d3262e97 chore(deps-dev): bump grpcio from 1.81.1 to 1.83.0 (#42414)
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-07-25 18:47:24 -07:00
Evan Rusackas
6c2ccc44bd chore(table): drop dead th.ant-column-cell selector (#42407)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 18:31:07 -07:00
dependabot[bot]
e1ffa53d9b chore(deps): bump pyparsing from 3.2.3 to 3.3.2 (#42420)
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-07-25 21:53:51 +07:00
dependabot[bot]
4443357f33 chore(deps): bump flask-caching from 2.3.1 to 2.4.1 (#42413)
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-07-25 21:49:15 +07:00
dependabot[bot]
dd08c4f36c chore(deps-dev): update flightsql-dbapi requirement from <0.3,>=0.2.0 to >=0.2.2,<0.3 (#42416)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-25 21:36:04 +07:00
dependabot[bot]
4f3a1a5058 chore(deps-dev): update snowflake-sqlalchemy requirement from <2,>=1.10.2 to >=1.11.0,<2 (#42418)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-25 21:35:44 +07:00
dependabot[bot]
9d6fbdceff chore(deps-dev): update ibm-db-sa requirement from <=0.4.4,>0.3.8 to <=0.4.4,>=0.4.4 (#42419)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-25 20:15:32 +07:00
dependabot[bot]
8864b7a0a3 chore(deps-dev): update oracledb requirement from <5,>=2.0.0 to >=4.0.2,<5 (#42421)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-25 20:04:34 +07:00
dependabot[bot]
d4652460ee chore(deps-dev): bump google-cloud-bigquery from 3.42.1 to 3.42.2 (#42422)
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-07-25 20:04:07 +07:00
Gordon Cowie
8c37d627aa fix: allow clearing database/schema in DatasourceModal (#39918) 2026-07-24 16:17:25 -07:00
dependabot[bot]
5775bdbe5e chore(deps-dev): bump postcss from 8.5.15 to 8.5.23 in /superset-embedded-sdk (#42394)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 16:14:38 -07:00
Evan Rusackas
c65bedf2d7 fix(extensions): make LOCAL_EXTENSIONS hot reload reliable in Docker (#40084)
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-24 15:30:04 -07:00
dependabot[bot]
2a07f7967d chore(deps): bump brace-expansion, @cypress/code-coverage and eslint in /superset-frontend/cypress-base (#42275)
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: Evan <evan@preset.io>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: hainenber <dotronghai96@gmail.com>
2026-07-24 14:24:22 -07:00
Joe Li
b82ff27f9c fix(sqllab): render each query error once on async fallback (#42318)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Evan Rusackas <evan@rusackas.com>
2026-07-24 14:13:35 -07:00
dependabot[bot]
83ea9a1025 chore(deps): bump js-yaml from 5.2.1 to 5.2.2 in /docs (#42395)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 13:33:36 -07:00
dependabot[bot]
e9f61c1e66 chore(deps): bump postcss from 8.5.15 to 8.5.23 in /superset-frontend (#42396)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 13:33:22 -07:00
dependabot[bot]
857a194c66 chore(deps): bump postcss from 8.5.16 to 8.5.23 in /docs (#42397)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 13:33:09 -07:00
Joe Li
5ffdac1871 fix(mcp): align pooled screenshot override signature (#42384) 2026-07-24 13:32:31 -07:00
Joe Li
52552c8278 test(dashboard): migrate dashboard force-refresh control to Playwright (#41433)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 13:32:09 -07:00
Joe Li
134cae07ae fix(ui): update stale Ant Design v6 internal class selectors (#42146)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 13:31:45 -07:00
Evan Rusackas
f3fa1c7d4f fix(reports): write a single execution log row per report run (#29857) (#41966)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-24 13:30:31 -07:00
Evan Rusackas
99841fb504 fix(async): include datasets and rev claims in guest channel HMAC (#41487)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 13:29:58 -07:00
Evan Rusackas
3a53c4ebc3 chore(deps): raise SQLAlchemy lower bound to >=1.4.43 (#41123)
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 13:29:30 -07:00
Joe Li
f7d0ec3a2c chore: add @rusackas to migration code owners (#42398) 2026-07-24 13:18:33 -07:00
Bhargava Vadlamani
7eceebce1c feat(dbspec): Support apache datafusion dbspec (#42374) 2026-07-24 11:56:30 -07:00
Amin Ghadersohi
3bdf134aaa fix(logging): stop noisy LocalProxy-not-mapped warning for guest users (#42306)
Co-authored-by: Claude <noreply@anthropic.com>
2026-07-24 13:06:20 -04:00
ViktorGo86
65528a5b76 docs(map-tiles): add Yandex Maps Tiles API configuration (#42375) 2026-07-24 22:19:20 +07:00
Alejandro Solares
0792f98910 fix(security): bump pillow from 12.2.0 to 12.3.0 (#42362) 2026-07-24 21:49:27 +07:00
Alejandro Solares
67c3fea0ce fix(security): bump pyasn1 from 0.6.3 to 0.6.4 (#42363) 2026-07-24 21:46:21 +07:00
dependabot[bot]
d2ea5e9a1a chore(deps-dev): bump brace-expansion from 1.1.15 to 1.1.16 in /superset-embedded-sdk (#42369)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 21:23:48 +07:00
dependabot[bot]
0b9a9c5d03 chore(deps): bump body-parser from 1.20.5 to 1.20.6 in /docs (#42370)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 21:23:31 +07:00
dependabot[bot]
16de95f024 chore(deps): bump immer from 11.1.11 to 11.1.15 in /superset-frontend (#42378)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 21:23:14 +07:00
dependabot[bot]
5b5e5cf82d chore(deps): bump actions/checkout from 7.0.0 to 7.0.1 (#42376)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 21:23:00 +07:00
dependabot[bot]
542416ac6e chore(deps): bump @deck.gl/mapbox from 9.3.6 to 9.3.7 in /superset-frontend in the deckgl group (#42377)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 21:21:20 +07:00
SkinnyPigeon
4f017de532 docs(gunicorn): correct dead links in values.yaml (#42385) 2026-07-24 21:20:46 +07:00
suvankardas216
a1142b0f91 fix(dataset): disable duplicate button when name is empty (#42217)
Co-authored-by: AS-MAC-1123 <as-mac-1123@AS-MAC-1123.local>
Co-authored-by: Evan Rusackas <evan@rusackas.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 00:24:37 -07:00
Jean Massucatto
4ee500e47b fix(explore): render Jinja before validating legacy chart filters (#41996) 2026-07-23 22:05:14 -07:00
Ujjwal Jain
3ff5dbfe81 fix(native-filters): use FILTER_STATE_CACHE_CONFIG timeout for dynamic filter option queries (#38910) 2026-07-23 21:01:54 -07:00
dependabot[bot]
cfb04a6e79 chore(deps-dev): bump databricks-sql-connector from 4.2.6 to 4.3.0 (#42347)
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-07-23 20:57:58 -07:00
dependabot[bot]
49f4e84b48 chore(deps): bump flask-compress from 1.17 to 1.24 (#42346)
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-07-23 19:21:29 -07:00
dependabot[bot]
921f75d544 chore(deps): bump pillow from 12.2.0 to 12.3.0 (#42348)
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-07-23 19:21:17 -07:00
Ramachandran A G
bcf0361a91 feat(KustoKQL): Add support for NULL / IS NOT NULL operator (#37890)
Co-authored-by: ag-ramachandran <ramacg@microsoft.com>
Co-authored-by: Joe Li <joe@preset.io>
2026-07-23 18:28:37 -07:00
jenwitteng
5776aff50a fix(charts): handle async (202) chart-data responses in StatefulChart (#42157)
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-07-23 18:01:05 -07:00
Hans Yu
14c96761e5 chore: SQLAlchemy User cascade backref warnings are irrelevant (#42360) 2026-07-23 17:16:38 -07:00
Abdul Rehman
d41f0febae fix(api): add example to get_export_ids_schema so Swagger "Try it out" pre-fills a valid array (#42265) 2026-07-23 17:15:24 -07:00
jesperct
c43effa4a3 fix(explore): show the beginning date on time-series x-axis line charts (#42046) 2026-07-23 17:14:30 -07:00
Evan Rusackas
206fe7ab12 fix(embedded): stop rejecting guest chart data built from control-specific params keys (#42295)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-23 13:31:04 -07:00
dependabot[bot]
1049826bc1 chore(deps): bump sqlalchemy-continuum from 1.6.0 to 1.7.0 (#42351)
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-07-23 12:56:11 -07:00
dependabot[bot]
6317113ee8 chore(deps): bump pydantic from 2.11.7 to 2.13.4 (#42350)
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-07-23 12:56:03 -07:00
dependabot[bot]
fdb9c152f0 chore(deps): bump nh3 from 0.3.5 to 0.3.6 (#42349)
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-07-23 12:55:56 -07:00
Amin Ghadersohi
2317d9cf91 fix(mcp): truncate query-tool responses instead of hard-failing (#42244) 2026-07-23 14:38:49 -04:00
yousoph
df8bbb548a fix(native-filters): keep filter value input caret at inline start (#42323)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 11:26:56 -07:00
dependabot[bot]
6b02a423ef chore(deps-dev): bump the storybook group in /superset-frontend with 5 updates (#42355)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 00:39:08 +07:00
dependabot[bot]
f2848d951c chore(deps-dev): update taos-ws-py requirement from >=0.6.9 to >=0.7.0 (#42344)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-23 10:27:56 -07:00
JUST.in DO IT
cb319c9b81 fix(mcp): trust dataset is_dttm flag when applying time_grain to VARCHAR temporal columns (#42288) 2026-07-23 10:00:22 -07:00
dependabot[bot]
cc8edd2231 chore(deps): bump ag-grid from 36.0.0 to 36.0.1 in /superset-frontend (#42338)
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-07-23 22:21:37 +07:00
Yash Shrivastava
c5f6d33d21 docs: add pattern to the list of organisations using superset (#42341) 2026-07-23 22:11:17 +07:00
Evan Rusackas
2e72080a16 chore(deps): bump echarts to 6.1.0 with locale and containLabel guards (#42315) (#42321)
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
2026-07-23 21:48:11 +07:00
dependabot[bot]
6654318491 chore(deps): bump caniuse-lite from 1.0.30001805 to 1.0.30001806 in /docs (#42333)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-23 21:46:27 +07:00
dependabot[bot]
ab553c37ab chore(deps-dev): bump @formatjs/intl-durationformat from 0.10.17 to 0.10.18 in /superset-frontend (#42337)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-23 21:37:30 +07:00
dependabot[bot]
873da606dd chore(deps): bump github/codeql-action/init from 4.37.0 to 4.37.1 (#42334)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-23 21:36:39 +07:00
dependabot[bot]
1dd3103c24 chore(deps): bump github/codeql-action/analyze from 4.37.0 to 4.37.1 (#42331)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-23 21:36:16 +07:00
dependabot[bot]
2926d6f6a9 chore(deps): bump actions/labeler from 6.2.0 to 7.0.0 (#42332)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-23 21:34:54 +07:00
Evan Rusackas
67924ad35e fix(dashboard): offer Exit edit mode when there is nothing to discard (#42208)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-22 18:43:49 -07:00
David
ef459c21e8 fix(helm): add MCP HTTPRoute configuration (#42219) 2026-07-22 17:08:16 -07:00
Amitesh Gupta
3ed8889a01 fix: Added PostgreSQL 17.X to the supported database versions table in (#42280) 2026-07-22 16:24:13 -07:00
yousoph
17bd286ae9 fix(forecast): resolve time grain robustly for Prophet forecasting (#42145)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 16:08:57 -07:00
yousoph
32937f24d2 fix(heatmap): correct tooltip axis value lookup and percentage calculations and add tests (#41864)
Signed-off-by: yousoph <sophieyou12@gmail.com>
Co-authored-by: Kamil Gabryjelski <kamil.gabryjelski@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-22 15:34:10 -07:00
Elizabeth Thompson
e075133a83 fix(pandas_postprocessing): avoid FutureWarning for max/min in boxplot MINMAX (#42272) 2026-07-22 15:02:14 -07:00
Joe Li
8d034ef65c fix(plugin-chart-echarts): import the -obj locale build so time axes render (#42317)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 14:53:59 -07:00
Joe Li
641713f623 fix: Revert "chore(deps): bump echarts from 5.6.0 to 6.1.0 in /superset-frontend" (#42314)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 12:40:56 -07:00
Joe Li
40d9e7d048 fix(ag-grid-table): avoid ambiguous build query import (#42313) 2026-07-22 11:30:45 -07:00
Hans Yu
19159d58c8 refactor: engine enforce SQLAlchemy 2.0 (#42277) 2026-07-22 11:11:48 -07:00
Grégoire
894c0191d6 fix(superset_app_root): when used with oauth (#38033)
Signed-off-by: Grégoire Bellon-Gervais <gregoire.bellon-gervais@docaposte.fr>
Co-authored-by: Evan Rusackas <evan@preset.io>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 10:34:43 -07:00
Joe Li
49b4243129 test(dashboard): migrate dashboard load smoke test to Playwright (#41432)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 10:32:07 -07:00
dependabot[bot]
ac41f61d4a chore(deps): bump nanoid from 5.0.9 to 6.0.0 in /superset-frontend (#42230)
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>
Co-authored-by: Joe Li <joe@preset.io>
Co-authored-by: Evan <evan@preset.io>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 10:28:51 -07:00
dependabot[bot]
f50b7b7154 chore(deps): bump actions/setup-go from 6.5.0 to 7.0.0 (#42303)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 10:28:11 -07:00
Zack
be5744c431 fix(chart): updates counties of kenya map (#38019)
Co-authored-by: Zack Adams <zack@Zacks-Laptop.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Evan <evan@preset.io>
Co-authored-by: Evan Rusackas <evan@rusackas.com>
2026-07-22 10:17:29 -07:00
SkinnyPigeon
8c5d465a65 feat(datasets): add RLS filter indicator badge to dataset list and explore view (#38807)
Co-authored-by: Evan <evan@preset.io>
2026-07-22 10:07:52 -07:00
Evan Rusackas
c5935b6904 feat(table/pivot-table): correct non-additive totals/subtotals via DB rollup [SIP-216] (#41184)
Co-authored-by: Superset Dev <dev@superset.apache.org>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
2026-07-22 10:06:05 -07:00
Evan Rusackas
10ff470702 fix(ag-grid-table): respect row limit with server pagination (#41346)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 10:03:33 -07:00
Amin Ghadersohi
1c0f259e38 refactor(mcp): dedupe list-tool schemas and delete dead middleware (#41923) 2026-07-22 12:29:59 -04:00
Alexandru Soare
b8f3b768d4 fix(sqllab): Fall back to empty editor tab when linked query no longer exists (#42241) 2026-07-22 15:30:42 +03:00
dependabot[bot]
6e1954fd51 chore(deps): bump ws from 8.21.0 to 8.21.1 in /superset-websocket (#42301)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 02:21:25 -07:00
dependabot[bot]
248f6e1064 chore(deps): bump zizmorcore/zizmor-action from 0.5.7 to 0.6.0 (#42302)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 02:21:21 -07:00
dependabot[bot]
46c5e30533 chore(deps-dev): bump oxlint from 1.73.0 to 1.74.0 in /superset-frontend (#42304)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 02:21:17 -07:00
Mehmet Salih Yavuz
b71b58e821 fix(explore): render stopped state when a chart query is cancelled (#42270) 2026-07-22 11:14:08 +03:00
Damian Pendrak
1392fbc9b2 fix(sql): validate Custom SQL metric has an aggregate under GROUP BY (#42199) 2026-07-22 09:28:57 +02:00
Mafi
a086b2eae9 fix(clickhouse): retry adhoc column type probe with comment-safe SQL (#42298)
Co-authored-by: Matt Fitzgerald <matt.fitzgerald@preset.io>
2026-07-22 02:39:49 -04:00
646 changed files with 27000 additions and 12150 deletions

2
.github/CODEOWNERS vendored
View File

@@ -1,6 +1,6 @@
# Notify all committers of DB migration changes, per SIP-59
/superset/migrations/ @mistercrunch @michael-s-molina @betodealmeida @eschutho @sadpandajoe
/superset/migrations/ @mistercrunch @michael-s-molina @betodealmeida @eschutho @sadpandajoe @rusackas
# Notify some committers of changes in the components

View File

@@ -70,6 +70,10 @@ updates:
- "@storybook/*"
- "storybook"
- "eslint-plugin-storybook"
ag-grid:
patterns:
- "ag-grid-react"
- "ag-grid-community"
open-pull-requests-limit: 30
versioning-strategy: increase
cooldown:

View File

@@ -31,7 +31,7 @@ jobs:
checks: write
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: true
ref: master
@@ -40,7 +40,7 @@ jobs:
uses: ./.github/actions/setup-supersetbot/
- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.11"

View File

@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-26.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive

View File

@@ -25,7 +25,7 @@ jobs:
pull-requests: write
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check and notify

View File

@@ -26,7 +26,7 @@ jobs:
frontend: ${{ steps.check.outputs.frontend }}
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check for file changes
@@ -58,13 +58,13 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -75,6 +75,6 @@ jobs:
# queries: security-extended,security-and-quality
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
uses: github/codeql-action/analyze@e0647621c2984b5ed2f768cb892365bf2a616ad1 # v4.37.2
with:
category: "/language:${{matrix.language}}"

View File

@@ -27,12 +27,11 @@ jobs:
runs-on: ubuntu-26.04
steps:
- name: "Checkout Repository"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: "Dependency Review"
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
continue-on-error: true
with:
fail-on-severity: critical
# compatible/incompatible licenses addressed here: https://www.apache.org/legal/resolved.html
@@ -51,7 +50,7 @@ jobs:
runs-on: ubuntu-26.04
steps:
- name: "Checkout Repository"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

View File

@@ -30,7 +30,7 @@ jobs:
docker: ${{ steps.check.outputs.docker }}
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check for file changes
@@ -71,7 +71,7 @@ jobs:
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
@@ -184,7 +184,7 @@ jobs:
timeout-minutes: 30
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Free up disk space

View File

@@ -23,7 +23,7 @@ jobs:
run:
working-directory: superset-embedded-sdk
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# Note: registry-url is intentionally omitted. When set, actions/setup-node

View File

@@ -21,7 +21,7 @@ jobs:
run:
working-directory: superset-embedded-sdk
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0

View File

@@ -32,7 +32,7 @@ jobs:
runs-on: ubuntu-26.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive

View File

@@ -27,7 +27,7 @@ jobs:
security-events: write
steps:
- name: Checkout Repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
@@ -46,4 +46,4 @@ jobs:
run: bash .github/workflows/github-action-validator.sh
- name: Check for security issues on GHA workflows
uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7
uses: zizmorcore/zizmor-action@6599ee8b7a49aef6a770f63d261d214911a7ce02 # v0.6.0

View File

@@ -2,10 +2,10 @@ name: supersetbot orglabel based on author
on:
issues:
types: [created, edited]
types: [opened, edited]
pull_request:
types: [created, edited]
types: [opened, edited]
jobs:
superbot-orglabel:
@@ -16,7 +16,7 @@ jobs:
issues: write
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
@@ -30,4 +30,4 @@ jobs:
# Label the issue with the appropriate org using supersetbot
# - this requires for the author to be publicly associated with their org
# - and for the org to be listed in `supersetbot/src/metadata.js`
supersetbot orglabel --issue ${{ github.event.number }} --repo ${{ github.repository }} || true
supersetbot orglabel --issue ${{ github.event.number || github.event.issue.number }} --repo ${{ github.repository }} || true

View File

@@ -14,7 +14,7 @@ jobs:
pull-requests: write
runs-on: ubuntu-26.04
steps:
- uses: actions/labeler@b8dd2d9be0f68b860e7dae5dae7d772984eacd6d # v6.2.0
- uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0
with:
sync-labels: true

View File

@@ -12,7 +12,7 @@ jobs:
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive

View File

@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-26.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive

View File

@@ -27,7 +27,7 @@ jobs:
pull-requests: write
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive

View File

@@ -33,7 +33,7 @@ jobs:
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["current", "next"]') }}
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive
@@ -47,7 +47,7 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Setup Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
- name: Install helm-docs
run: go install github.com/norwoodj/helm-docs/cmd/helm-docs@v1.14.2

View File

@@ -33,7 +33,7 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# pulls all commits (needed for lerna / semantic release to correctly version)

View File

@@ -102,7 +102,7 @@ jobs:
fail-fast: false
steps:
- name: "Checkout release tag: ${{ needs.config.outputs.latest-release }}"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.config.outputs.latest-release }}
fetch-depth: 0

View File

@@ -153,7 +153,7 @@ jobs:
- name: Checkout PR code (only if build needed)
if: steps.auth.outputs.authorized == 'true' && steps.check.outputs.build_needed == 'true'
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ steps.check.outputs.target_sha }}
persist-credentials: false

View File

@@ -41,7 +41,7 @@ jobs:
- 16379:6379
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive

View File

@@ -60,7 +60,7 @@ jobs:
runs-on: ubuntu-26.04
steps:
- name: "Checkout ${{ github.event.workflow_run.head_sha || github.sha }}"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
persist-credentials: false

View File

@@ -21,47 +21,62 @@ permissions:
contents: read
jobs:
linkinator:
# See docs here: https://github.com/marketplace/actions/linkinator
lychee:
# See docs here: https://github.com/lycheeverse/lychee-action
# Only run on pull_request, not workflow_run
if: github.event_name == 'pull_request'
name: Link Checking
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# Do not bump this linkinator-action version without opening
# an ASF Infra ticket to allow the new version first!
- uses: JustinBeckwith/linkinator-action@af984b9f30f63e796ae2ea5be5e07cb587f1bbd9 # v2.3
# Restores the on-disk request cache (.lycheecache) lychee writes when
# `--cache` is set, so re-runs don't re-check every external link from
# scratch. Key never matches exactly (github.run_id is unique per run),
# so actions/cache always saves a fresh copy afterwards; restore-keys
# falls back to the most recent prior cache on lookup.
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: .lycheecache
key: lychee-cache-${{ github.run_id }}
restore-keys: lychee-cache-
# v2.8.0 and v2.9.0 are both on ASF Infra's action allowlist
# (apache/infrastructure-actions approved_patterns.yml) as of this
# writing. Do not bump past v2.9.0 without opening an Infra ticket
# to allow the new SHA first!
- uses: lycheeverse/lychee-action@e7477775783ea5526144ba13e8db5eec57747ce8 # v2.9.0
continue-on-error: true # This will make the job advisory (non-blocking, no red X)
with:
paths: "**/*.md, **/*.mdx"
linksToSkip: >-
^https://github.com/apache/(superset|incubator-superset)/(pull|issues)/\d+,
^https://github.com/apache/(superset|incubator-superset)/commit/[a-f0-9]+,
superset-frontend/.*CHANGELOG\.md,
http://localhost:8088/,
http://127.0.0.1:3000/,
http://localhost:9001/,
https://charts.bitnami.com/bitnami,
https://www.li.me/,
https://www.fanatics.com/,
https://tails.com/gb/,
https://www.techaudit.info/,
https://avetilearning.com/,
https://www.udemy.com/,
https://trustmedis.com/,
http://theiconic.com.au/,
https://dev.mysql.com/doc/refman/5.7/en/innodb-limits.html,
^https://img\.shields\.io/.*,
https://vkusvill.ru/,
https://www.linkedin.com/in/mark-thomas-b16751158/,
https://theiconic.com.au/,
https://wattbewerb.de/,
https://timbr.ai/,
https://opensource.org/license/apache-2-0,
https://www.plaidcloud.com/
fail: false
args: >-
--verbose --no-progress
--cache --max-cache-age 7d
--exclude '^https://github.com/apache/(superset|incubator-superset)/(pull|issues)/\d+'
--exclude '^https://github.com/apache/(superset|incubator-superset)/commit/[a-f0-9]+'
--exclude 'superset-frontend/.*CHANGELOG\.md'
--exclude 'http://localhost:8088/'
--exclude 'http://127.0.0.1:3000/'
--exclude 'http://localhost:9001/'
--exclude 'https://charts.bitnami.com/bitnami'
--exclude 'https://www.li.me/'
--exclude 'https://www.fanatics.com/'
--exclude 'https://tails.com/gb/'
--exclude 'https://www.techaudit.info/'
--exclude 'https://avetilearning.com/'
--exclude 'https://www.udemy.com/'
--exclude 'https://trustmedis.com/'
--exclude 'http://theiconic.com.au/'
--exclude 'https://dev.mysql.com/doc/refman/5.7/en/innodb-limits.html'
--exclude '^https://img\.shields\.io/.*'
--exclude 'https://vkusvill.ru/'
--exclude 'https://www.linkedin.com/in/mark-thomas-b16751158/'
--exclude 'https://theiconic.com.au/'
--exclude 'https://wattbewerb.de/'
--exclude 'https://timbr.ai/'
--exclude 'https://opensource.org/license/apache-2-0'
--exclude 'https://www.plaidcloud.com/'
'./**/*.md' './**/*.mdx'
build-on-pr:
# Build docs when PR changes docs/** (uses committed databases.json)
@@ -73,7 +88,7 @@ jobs:
working-directory: docs
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive
@@ -112,7 +127,7 @@ jobs:
working-directory: docs
steps:
- name: "Checkout PR head: ${{ github.event.workflow_run.head_sha }}"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.workflow_run.head_sha }}
persist-credentials: false

View File

@@ -38,7 +38,7 @@ jobs:
frontend: ${{ steps.check.outputs.frontend }}
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check for file changes
@@ -96,21 +96,21 @@ jobs:
# Conditional checkout based on context
- name: Checkout for push or pull_request event
if: github.event_name == 'push' || github.event_name == 'pull_request'
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
- name: Checkout using ref (workflow_dispatch)
if: github.event_name == 'workflow_dispatch' && github.event.inputs.ref != ''
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: ${{ github.event.inputs.ref }}
submodules: recursive
- name: Checkout using PR ID (workflow_dispatch)
if: github.event_name == 'workflow_dispatch' && github.event.inputs.pr_id != ''
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: refs/pull/${{ github.event.inputs.pr_id }}/merge
@@ -206,21 +206,21 @@ jobs:
# Conditional checkout based on context (same as Cypress workflow)
- name: Checkout for push or pull_request event
if: github.event_name == 'push' || github.event_name == 'pull_request'
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
- name: Checkout using ref (workflow_dispatch)
if: github.event_name == 'workflow_dispatch' && github.event.inputs.ref != ''
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: ${{ github.event.inputs.ref }}
submodules: recursive
- name: Checkout using PR ID (workflow_dispatch)
if: github.event_name == 'workflow_dispatch' && github.event.inputs.pr_id != ''
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: refs/pull/${{ github.event.inputs.pr_id }}/merge

View File

@@ -31,7 +31,7 @@ jobs:
working-directory: superset-extensions-cli
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive

View File

@@ -27,7 +27,7 @@ jobs:
should-run: ${{ steps.check.outputs.frontend }}
steps:
- name: Checkout Code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
@@ -110,7 +110,7 @@ jobs:
id-token: write
steps:
- name: Checkout Code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
@@ -122,25 +122,13 @@ jobs:
pattern: coverage-artifacts-*
path: coverage/
- name: Reorganize test result reports
run: |
find coverage/
for i in {1..8}; do
mv coverage/coverage-artifacts-${i}/coverage-final.json coverage/coverage-shard-${i}.json
done
shell: bash
- name: Merge Code Coverage
run: npx nyc merge coverage/ merged-output/coverage-summary.json
- name: Upload Code Coverage
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
flags: javascript
use_oidc: true
verbose: true
disable_search: true
files: merged-output/coverage-summary.json
directory: coverage
slug: apache/superset
lint-frontend:

View File

@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-26.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive

View File

@@ -29,7 +29,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.ref || github.ref_name }}
persist-credentials: true

View File

@@ -34,7 +34,7 @@ jobs:
frontend: ${{ steps.check.outputs.frontend }}
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check for file changes
@@ -83,21 +83,21 @@ jobs:
# Conditional checkout based on context (same as Cypress workflow)
- name: Checkout for push or pull_request event
if: github.event_name == 'push' || github.event_name == 'pull_request'
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
- name: Checkout using ref (workflow_dispatch)
if: github.event_name == 'workflow_dispatch' && github.event.inputs.ref != ''
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: ${{ github.event.inputs.ref }}
submodules: recursive
- name: Checkout using PR ID (workflow_dispatch)
if: github.event_name == 'workflow_dispatch' && github.event.inputs.pr_id != ''
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: refs/pull/${{ github.event.inputs.pr_id }}/merge

View File

@@ -29,7 +29,7 @@ jobs:
python: ${{ steps.check.outputs.python }}
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check for file changes
@@ -72,7 +72,7 @@ jobs:
- 16379:6379
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive
@@ -157,7 +157,7 @@ jobs:
- 16379:6379
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive
@@ -207,7 +207,7 @@ jobs:
- 16379:6379
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive

View File

@@ -25,7 +25,7 @@ jobs:
python: ${{ steps.check.outputs.python }}
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check for file changes
@@ -72,7 +72,7 @@ jobs:
- 16379:6379
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive
@@ -127,7 +127,7 @@ jobs:
- 16379:6379
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive

View File

@@ -30,7 +30,7 @@ jobs:
python: ${{ steps.check.outputs.python }}
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check for file changes
@@ -61,7 +61,7 @@ jobs:
SQLALCHEMY_WARN_20: "1"
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive

View File

@@ -25,7 +25,7 @@ jobs:
pull-requests: read
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive
@@ -61,7 +61,7 @@ jobs:
pull-requests: read
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: recursive

View File

@@ -25,7 +25,7 @@ jobs:
timeout-minutes: 20
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup Node.js

View File

@@ -38,7 +38,7 @@ jobs:
});
- name: "Checkout ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

View File

@@ -27,7 +27,7 @@ jobs:
# zizmor: ignore[artipacked] - required persisted credentials to push synced requirement changes back to remote
- name: Checkout source code
if: ${{ steps.dependabot-metadata.outputs.package-ecosystem == 'pip' }}
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha }}
persist-credentials: true

View File

@@ -60,7 +60,7 @@ jobs:
fail-fast: false
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
@@ -126,7 +126,7 @@ jobs:
pull-requests: write
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0

View File

@@ -32,7 +32,7 @@ jobs:
name: Generate Reports
steps:
- name: Checkout Repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

1
.gitignore vendored
View File

@@ -57,6 +57,7 @@ local_config.py
/superset_text.yml
superset.egg-info/
superset/bin/supersetc
superset/extensions/.reload_trigger
tmp
rat-results.txt
superset/app/

View File

@@ -144,7 +144,7 @@ repos:
git fetch --no-recurse-submodules origin "$TARGET_BRANCH" 2>/dev/null || true
fi
BASE=$(git merge-base origin/"$TARGET_BRANCH" HEAD 2>/dev/null) || BASE="HEAD"
files=$(git diff --name-only --diff-filter=ACM "$BASE"..HEAD 2>/dev/null | grep '^superset/.*\.py$' || true)
files=$(git diff --name-only --diff-filter=ACM "$BASE"..HEAD 2>/dev/null | grep '^superset/.*\.py$' | grep -v '^superset/migrations/' || true)
if [ -n "$files" ]; then
pylint --rcfile=.pylintrc --load-plugins=superset.extensions.pylint --reports=no $files
else

View File

@@ -168,6 +168,11 @@ categories:
url: https://www.now.vn/
contributors: ["@davidkohcw"]
- name: Pattern
url: https://www.pattern.com
contributors: ["@alephys26", "@hladush", "@sanketjadhavSF"]
logo: pattern_logo.png
- name: Qunar
url: https://www.qunar.com/
contributors: ["@flametest"]

View File

@@ -288,6 +288,22 @@ The pivot table chart's `First` and `Last` aggregations now return the first and
The `error` and `response` parameters of the `retryDelay` and `retryOn` callbacks in `FetchRetryOptions` (exported from `@superset-ui/core`) are now typed `Error | null` and `Response | null` to match the actual call-site signature provided by `fetch-retry`. Because these parameter types are contravariant, consumers who typed their callbacks with the non-nullable `(attempt: number, error: Error, response: Response) => number` will get a TypeScript compile error. Widen your callback signatures to accept `Error | null` / `Response | null`.
### Pivot Table totals are now computed by the database (per-metric "Aggregation function" control removed)
Pivot Table subtotals and grand totals are now computed by the database at each
rollup level instead of re-aggregating the already-aggregated cell values on the
client. This fixes long-standing incorrect totals for non-additive metrics
(ratios such as `SUM(a)/SUM(b)`, `COUNT_DISTINCT`, `AVG`, percentiles, etc.),
which previously summed the displayed cell values.
As a result the per-table **"Aggregation function"** control (which let you pick
how totals were aggregated client-side, e.g. Sum/Average/Count) has been
removed: totals now always reflect the metric's own definition evaluated at the
total's granularity. For additive metrics (`SUM`/`COUNT`/`MIN`/`MAX`) the result
is unchanged. Saved charts that set `aggregateFunction` will ignore it; no
migration is required. If you previously relied on a plain sum-of-cells total
for a non-additive metric, that specific behavior is no longer available.
### `thumbnail_url` removed from dashboard list API response
The `thumbnail_url` field has been removed from `GET /api/v1/dashboard/` list responses. External consumers relying on this field must now construct the thumbnail URL client-side using `id` and `changed_on_utc`:
@@ -342,6 +358,16 @@ A read-only companion to the version-history endpoints: each entity type gains a
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`).
### Version-history retention (pruning)
Entity version history (the `version_transaction` / `*_version` shadow tables that back version capture) is aged out by a nightly Celery beat task, `version_history.prune_old_versions` (`superset.tasks.version_history_retention`).
| Key | Default | Purpose |
|---|---|---|
| `SUPERSET_VERSION_HISTORY_RETENTION_DAYS` | `30` | Version rows whose owning `version_transaction.issued_at` is older than this many days are pruned. Each entity's live row (`end_transaction_id IS NULL`) is always preserved, as are the live rows of its children and associations; closed historical rows (including the baseline) age out. Set to `0` or a negative value to disable pruning. |
The task ships in the default `CeleryConfig.beat_schedule`; a deployment that overrides `CELERY_CONFIG` without inheriting the default will log a startup warning that the prune task is absent (so it never silently stops running). Retention only prunes whatever history exists — capture itself is gated separately by `ENABLE_VERSIONING_CAPTURE` (ships off).
### Webhook alerts/reports block private/internal hosts by default
Webhook alert/report dispatch (`WebhookNotification.send`) now validates the target URL's host against the same private/internal-IP block applied to dataset import URLs. If the resolved host is in a loopback, link-local, private (RFC-1918), shared-CGNAT, or multicast range, the webhook is rejected with `NotificationParamException`.
@@ -636,6 +662,10 @@ Added a new combined datasource list endpoint at `GET /api/v1/datasource/` to se
The minimum required version of `clickhouse-connect` has been raised to `>=0.13.0`. If you are using the ClickHouse connector, please upgrade your `clickhouse-connect` package. The `_mutate_label` workaround that appended hash suffixes to column aliases has also been removed, as it is no longer needed with modern versions of the driver.
### Kenya Country Map: Updated Administrative Divisions
The Kenya country map has been updated to reflect the 47 counties established under Kenya's 2010 constitution, replacing the outdated 8-province boundaries from the Natural Earth dataset. County keys now use ISO 3166-2:KE codes (`KE-01` through `KE-47`), replacing the former province codes (`KE-110`, `KE-200`, ..., `KE-800`). Dashboards that join on the old province codes will need to re-key their datasets to use the new county codes.
### MCP Tool Observability
MCP (Model Context Protocol) tools now include enhanced observability instrumentation for monitoring and debugging:

View File

@@ -28,8 +28,10 @@ x-superset-image: &superset-image apachesuperset.docker.scarf.sh/apache/superset
x-superset-volumes:
&superset-volumes # /app/pythonpath_docker will be appended to the PYTHONPATH in the final container
- ./docker:/app/docker
- ./superset-core:/app/superset-core
- superset_home:/app/superset_home
x-superset-environment: &superset-environment
# Tagged images must use their bundled packages rather than host bind mounts.
DEV_MODE: "false"
services:
redis:
@@ -58,6 +60,7 @@ services:
required: true
- path: docker/.env-local # optional override
required: false
environment: *superset-environment
image: *superset-image
container_name: superset_app
command: ["/app/docker/docker-bootstrap.sh", "app-gunicorn"]
@@ -79,6 +82,7 @@ services:
required: true
- path: docker/.env-local # optional override
required: false
environment: *superset-environment
depends_on:
db:
condition: service_started
@@ -98,6 +102,7 @@ services:
required: true
- path: docker/.env-local # optional override
required: false
environment: *superset-environment
restart: unless-stopped
depends_on:
superset-init:
@@ -120,6 +125,7 @@ services:
required: true
- path: docker/.env-local # optional override
required: false
environment: *superset-environment
restart: unless-stopped
depends_on:
superset-init:

View File

@@ -34,6 +34,7 @@ x-superset-volumes: &superset-volumes
- superset_home:/app/superset_home
- ./tests:/app/tests
- superset_data:/app/data
- ./local_extensions:/app/local_extensions
x-common-build: &common-build
context: .
target: ${SUPERSET_BUILD_TARGET:-dev} # can use `dev` (default) or `lean`

View File

@@ -98,7 +98,9 @@ case "${1}" in
echo " 🔒 Werkzeug debugger disabled (set SUPERSET_DEBUG_ENABLED=true to enable)"
fi
flask run -p $PORT --reload $DEBUGGER_FLAG --host=0.0.0.0 --exclude-patterns "*/node_modules/*:*/.venv/*:*/build/*:*/__pycache__/*:*/superset-frontend/*"
flask run -p $PORT --reload $DEBUGGER_FLAG --host=0.0.0.0 \
--extra-files "/app/superset/extensions/.reload_trigger" \
--exclude-patterns "*/node_modules/*:*/.venv/*:*/build/*:*/__pycache__/*:*/superset-frontend/*:*/superset/__init__.py"
;;
app-gunicorn)
echo "Starting web app..."

View File

@@ -149,10 +149,10 @@ database engine on a separate host or container.
Superset supports the following database engines/versions:
| Database Engine | Supported Versions |
| ----------------------------------------- | ---------------------------------------- |
| [PostgreSQL](https://www.postgresql.org/) | 10.X, 11.X, 12.X, 13.X, 14.X, 15.X, 16.X |
| [MySQL](https://www.mysql.com/) | 5.7, 8.X |
| Database Engine | Supported Versions |
| ----------------------------------------- | ---------------------------------------------- |
| [PostgreSQL](https://www.postgresql.org/) | 10.X, 11.X, 12.X, 13.X, 14.X, 15.X, 16.X, 17.X |
| [MySQL](https://www.mysql.com/) | 5.7, 8.X |
Use the following database drivers and connection strings:

View File

@@ -25,6 +25,33 @@ DECKGL_BASE_MAP = [
]
```
[Yandex Tiles API](https://yandex.ru/maps-api/docs/tiles-api/request.html) can be used as a custom tile provider. The URL must include the `apikey` from the [Yandex Developer Dashboard](https://developer.tech.yandex.ru/services), tile coordinates (`x`, `y`, and `z`), and the required map layer parameters.
```python
DECKGL_BASE_MAP = [
[
"tile://https://tiles.api-maps.yandex.ru/v1/tiles/"
"?apikey=YOUR_YANDEX_API_KEY"
"&lang=ru_RU"
"&x={x}&y={y}&z={z}"
"&l=map"
"&projection=web_mercator"
"&maptype=map",
"Yandex Maps - Web Mercator",
],
[
"tile://https://tiles.api-maps.yandex.ru/v1/tiles/"
"?apikey=YOUR_YANDEX_API_KEY"
"&lang=ru_RU"
"&x={x}&y={y}&z={z}"
"&l=map"
"&projection=wgs84_mercator"
"&maptype=map",
"Yandex Maps - WGS84 Mercator",
],
]
```
Default values are:
```python
DECKGL_BASE_MAP = [
@@ -56,6 +83,7 @@ CORS_OPTIONS: dict[Any, Any] = {
"https://tile.openstreetmap.org",
"https://tile.osm.ch",
"https://your_personal_url/{z}/{x}/{y}.png",
"https://tiles.api-maps.yandex.ru",
]
}
@@ -72,6 +100,7 @@ TALISMAN_CONFIG = {
"https://tile.openstreetmap.org",
"https://tile.osm.ch",
"https://your_personal_url/{z}/{x}/{y}.png",
"https://tiles.api-maps.yandex.ru",
],
...
}

View File

@@ -53,20 +53,20 @@
"@emotion/core": "^11.0.0",
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.14.1",
"@fontsource/fira-code": "^5.2.7",
"@fontsource/ibm-plex-mono": "^5.2.7",
"@fontsource/inter": "^5.2.8",
"@fontsource/fira-code": "^5.3.0",
"@fontsource/ibm-plex-mono": "^5.3.0",
"@fontsource/inter": "^5.3.0",
"@mdx-js/react": "^3.1.1",
"@saucelabs/theme-github-codeblock": "^0.3.0",
"@storybook/addon-docs": "^10.5.0",
"@storybook/addon-docs": "^10.5.3",
"@superset-ui/core": "^0.20.4",
"@swc/core": "^1.15.43",
"@swc/core": "^1.15.46",
"antd": "^6.5.1",
"baseline-browser-mapping": "^2.10.43",
"caniuse-lite": "^1.0.30001805",
"baseline-browser-mapping": "^2.10.44",
"caniuse-lite": "^1.0.30001806",
"docusaurus-plugin-openapi-docs": "^5.1.2",
"docusaurus-theme-openapi-docs": "^5.1.2",
"js-yaml": "^5.2.0",
"js-yaml": "^5.2.2",
"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.0",
"swagger-ui-react": "^5.32.8",
"storybook": "^10.5.3",
"swagger-ui-react": "^5.32.9",
"swc-loader": "^0.2.7",
"tinycolor2": "^1.4.2",
"unist-util-visit": "^5.1.0"
@@ -89,16 +89,16 @@
"@eslint/js": "^9.39.2",
"@types/js-yaml": "^4.0.9",
"@types/react": "^19.1.8",
"@typescript-eslint/eslint-plugin": "^8.64.0",
"@typescript-eslint/parser": "^8.64.0",
"@typescript-eslint/eslint-plugin": "^8.65.0",
"@typescript-eslint/parser": "^8.65.0",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.6",
"eslint-plugin-react": "^7.37.5",
"globals": "^17.7.0",
"prettier": "^3.9.5",
"prettier": "^3.9.6",
"typescript": "~6.0.3",
"typescript-eslint": "^8.64.0",
"typescript-eslint": "^8.65.0",
"webpack": "^5.108.2"
},
"browserslist": {

View File

@@ -1,16 +1,16 @@
{
"generated": "2026-04-17T17:08:35.132Z",
"statistics": {
"totalDatabases": 80,
"withDocumentation": 80,
"withConnectionString": 80,
"withDrivers": 28,
"totalDatabases": 81,
"withDocumentation": 81,
"withConnectionString": 81,
"withDrivers": 29,
"withAuthMethods": 5,
"supportsJoins": 74,
"supportsSubqueries": 77,
"supportsJoins": 75,
"supportsSubqueries": 78,
"supportsDynamicSchema": 29,
"supportsCatalog": 20,
"averageScore": 54,
"averageScore": 53,
"maxScore": 201,
"byCategory": {
"Cloud - AWS": [
@@ -20,6 +20,7 @@
],
"Query Engines": [
"Amazon Athena",
"Apache DataFusion",
"Apache Drill",
"Apache Hive",
"Apache Impala",
@@ -94,6 +95,7 @@
"Vertica"
],
"Apache Projects": [
"Apache DataFusion",
"Apache Doris",
"Apache Drill",
"Apache Druid",
@@ -108,6 +110,7 @@
"Databricks Interactive Cluster"
],
"Open Source": [
"Apache DataFusion",
"Apache Doris",
"Apache Drill",
"Apache Druid",
@@ -9698,6 +9701,97 @@
"supports_dynamic_schema": true,
"supports_catalog": true,
"supports_dynamic_catalog": true
},
"Apache DataFusion": {
"time_grains": {
"SECOND": true,
"FIVE_SECONDS": true,
"THIRTY_SECONDS": true,
"MINUTE": true,
"FIVE_MINUTES": true,
"TEN_MINUTES": true,
"FIFTEEN_MINUTES": true,
"THIRTY_MINUTES": true,
"HALF_HOUR": false,
"HOUR": true,
"SIX_HOURS": false,
"DAY": true,
"WEEK": true,
"WEEK_STARTING_SUNDAY": false,
"WEEK_STARTING_MONDAY": false,
"WEEK_ENDING_SATURDAY": false,
"WEEK_ENDING_SUNDAY": false,
"MONTH": true,
"QUARTER": true,
"QUARTER_YEAR": false,
"YEAR": true
},
"module": "superset.db_engine_specs.datafusion",
"limit_method": 1,
"limit_clause": true,
"joins": true,
"subqueries": true,
"alias_in_select": true,
"alias_in_orderby": true,
"time_groupby_inline": false,
"alias_to_source_column": false,
"order_by_not_in_select": true,
"expressions_in_orderby": false,
"cte_in_subquery": true,
"max_column_name": null,
"sql_comments": true,
"escaped_colons": true,
"masked_encrypted_extra": false,
"column_type_mapping": false,
"function_names": false,
"user_impersonation": false,
"file_upload": false,
"get_extra_table_metadata": false,
"dbapi_exception_mapping": false,
"custom_errors": false,
"dynamic_schema": false,
"catalog": false,
"dynamic_catalog": false,
"ssh_tunneling": true,
"query_cancelation": false,
"get_metrics": false,
"where_latest_partition": false,
"expand_data": false,
"query_cost_estimation": false,
"sql_validation": false,
"score": 24,
"max_score": 201,
"documentation": {
"description": "DataFusion is a highly performant query engine",
"logo": "datafusion.png",
"homepage_url": "https://datafusion.apache.org/",
"categories": [
"Query Engines",
"Open Source",
"Apache Projects"
],
"pypi_packages": [
"flightsql-dbapi"
],
"connection_string": "datafusion://host:port",
"drivers": [
{
"name": "Arrow Flight SQL (Recommended)",
"pypi_package": "flightsql-dbapi",
"connection_string": "datafusion://host:port",
"is_recommended": true
}
],
"category": "Apache Projects"
},
"engine": "datafusion",
"engine_name": "Apache DataFusion",
"engine_aliases": [],
"default_driver": "flightsql",
"supports_file_upload": false,
"supports_dynamic_schema": false,
"supports_catalog": false,
"supports_dynamic_catalog": false
}
}
}

BIN
docs/static/img/databases/datafusion.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
docs/static/img/logos/pattern_logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -2529,20 +2529,20 @@
resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-5.5.3.tgz#18e3af6b8eae7984072bbeb0c0858474d7c4cefe"
integrity sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==
"@fontsource/fira-code@^5.2.7":
version "5.2.7"
resolved "https://registry.yarnpkg.com/@fontsource/fira-code/-/fira-code-5.2.7.tgz#9ecbd909d53e7196a5d895b601747fe34491fc6a"
integrity sha512-tnB9NNund9TwIym8/7DMJe573nlPEQb+fKUV5GL8TBYXjIhDvL0D7mgmNVNQUPhXp+R7RylQeiBdkA4EbOHPGQ==
"@fontsource/fira-code@^5.3.0":
version "5.3.0"
resolved "https://registry.yarnpkg.com/@fontsource/fira-code/-/fira-code-5.3.0.tgz#487475cedfa1f7ba7650810ddce00b6c7b1b7a25"
integrity sha512-EJL968RJRkakubAj/coU8pSUaeTE5UNoRjtzAr6kGiSZ3jWuN8/AKWHwym/PFUaQL1q7IL/H+EXs4358YhrTBQ==
"@fontsource/ibm-plex-mono@^5.2.7":
version "5.2.7"
resolved "https://registry.yarnpkg.com/@fontsource/ibm-plex-mono/-/ibm-plex-mono-5.2.7.tgz#ef5b6f052115fdf6666208a5f8a0f13fcd7ba1fd"
integrity sha512-MKAb8qV+CaiMQn2B0dIi1OV3565NYzp3WN5b4oT6LTkk+F0jR6j0ZN+5BKJiIhffDC3rtBULsYZE65+0018z9w==
"@fontsource/ibm-plex-mono@^5.3.0":
version "5.3.0"
resolved "https://registry.yarnpkg.com/@fontsource/ibm-plex-mono/-/ibm-plex-mono-5.3.0.tgz#1879699d104602d5331e28e103f33c3b1b766b17"
integrity sha512-eTgnZjZEGk1QtD3ZstF+Vclo2HLAni8YMy34/DxllwZvyz1lR/1RF/xTiAquOBO7MvqBx8D2Ig2WCPMVfdZu7Q==
"@fontsource/inter@^5.2.8":
version "5.2.8"
resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-5.2.8.tgz#10c95d877d972c7de5bd4592309d42fb6a5e1a5b"
integrity sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg==
"@fontsource/inter@^5.3.0":
version "5.3.0"
resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-5.3.0.tgz#866278dc2357659f8be1755c8b67f29d3697b440"
integrity sha512-RofMylZmjlJEfELXeNHFWBRcSs75rGU/6bV2S2jfnvv/3rPXPGe0LgUJTklcHZ9lM4OZmAVFhcJPnACfb91A3g==
"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
version "9.3.0"
@@ -4005,23 +4005,23 @@
resolved "https://registry.yarnpkg.com/@standard-schema/utils/-/utils-0.3.0.tgz#3d5e608f16c2390c10528e98e59aef6bf73cae7b"
integrity sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==
"@storybook/addon-docs@^10.5.0":
version "10.5.2"
resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-10.5.2.tgz#e8023d1a68f637de9188be093f719895a8252d19"
integrity sha512-MoBANDsh5qEA14U+JaBoQcYsKbayJDDMopigFN0NdVAsZTdBfVIsL7cnjTFBL6ubB3ifb5M0tCXbScpml1KqiQ==
"@storybook/addon-docs@^10.5.3":
version "10.5.3"
resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-10.5.3.tgz#b5666cf20361d85c98cc836b2bfcdbbb72af3e97"
integrity sha512-MI1VDMSMQk78YxjIdt7WlrVOiA3TzTP00lRed1LeXh0fCvA9jxz9YXJI2+XigsLaxCSuOAEf/l35/GTLDMHD8A==
dependencies:
"@mdx-js/react" "^3.0.0"
"@storybook/csf-plugin" "10.5.2"
"@storybook/csf-plugin" "10.5.3"
"@storybook/icons" "^2.0.2"
"@storybook/react-dom-shim" "10.5.2"
"@storybook/react-dom-shim" "10.5.3"
react "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
react-dom "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
ts-dedent "^2.0.0"
"@storybook/csf-plugin@10.5.2":
version "10.5.2"
resolved "https://registry.yarnpkg.com/@storybook/csf-plugin/-/csf-plugin-10.5.2.tgz#a359dffe0b842dfffacb0e94ad98da0ccb13ba69"
integrity sha512-PK/wXiALFf88mt4HmDtiZJ6NRvhExSXEM9uFIN+OIHxGqg7Xbp6MB0SPdhsTbMY9720ahiu/DJx5iIzkidcA3w==
"@storybook/csf-plugin@10.5.3":
version "10.5.3"
resolved "https://registry.yarnpkg.com/@storybook/csf-plugin/-/csf-plugin-10.5.3.tgz#0818709c2761910f1e42710f6231f773eb97fb41"
integrity sha512-mkPq6zru8fN5+46uC1cZEbKW2ws1hh9KvF4g4/Gu8pNbKnvqULPhk0/Bf0ZCtlr7zI7DvcFhyCy3dbvN+2n4Gw==
dependencies:
unplugin "^2.3.5"
@@ -4035,10 +4035,10 @@
resolved "https://registry.yarnpkg.com/@storybook/icons/-/icons-2.1.0.tgz#edfc2450a39c5e780f28c6cbc49acd7bff59b41a"
integrity sha512-Fxh9vYpX9bQqFeHRiY8h2ApeRGDzRSMLwJwNZ/AIRqnyOKHxRKL+yFe+ctEkVJmuptRE9u1Hrn8ZZNHyfDKKNg==
"@storybook/react-dom-shim@10.5.2":
version "10.5.2"
resolved "https://registry.yarnpkg.com/@storybook/react-dom-shim/-/react-dom-shim-10.5.2.tgz#993077c517ee1a1c065c82bf0143ae08856a3c90"
integrity sha512-TbdYVLuD7gwj1CFsDJhCHUiwfVmzFWzalKEUGy9XgXyNpyOV1CYRsdmRdhaOHgmn2ljQZuTAxSnG7NlElghVaw==
"@storybook/react-dom-shim@10.5.3":
version "10.5.3"
resolved "https://registry.yarnpkg.com/@storybook/react-dom-shim/-/react-dom-shim-10.5.3.tgz#e176a6549aa02002e375cb66467a00c5403745f8"
integrity sha512-eUWBsRRax5R3MDJVFs/CrFDF1bYS58AMB9tX02lLRuiZe6xy1cKh3CRFS+2xH571l0fNaXQ+7j69TOJ0fk2tmA==
"@superset-ui/core@^0.20.4":
version "0.20.4"
@@ -4697,86 +4697,86 @@
dependencies:
apg-lite "^1.0.4"
"@swc/core-darwin-arm64@1.15.43":
version "1.15.43"
resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.43.tgz#386294f8427dde2df1a70dd0a5826d67af70e996"
integrity sha512-v1aVuvXdo/BHxJzco9V2xpHrvwWmhfS8t6gziY5wJxd+Z2h8AeJRnAwPD8itCDaGXVBwJ/CaKfxEzTkG0Va0OA==
"@swc/core-darwin-arm64@1.15.46":
version "1.15.46"
resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.46.tgz#393903c7eda790dbd89abd8fa0afdd9041543e5f"
integrity sha512-IsISIT22EfktVJrlvIpnAxG2u/A9aob9l99HMlx80x72WlFmFPk1V3UhkEzx86eJP8hw049KTFv/RISho2cq2Q==
"@swc/core-darwin-x64@1.15.43":
version "1.15.43"
resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.15.43.tgz#c4823529c424e2ae25b7eb786438474741521fcb"
integrity sha512-lp3d4Lamc8dt5huYdGLSR+9hLxmfr1jb0l+4XXG2zPqZwYWRN9R0U2qYoTrggiU2RWW0oV9VbWM3kBnqIc2kdQ==
"@swc/core-darwin-x64@1.15.46":
version "1.15.46"
resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.15.46.tgz#ddf16787e320636621180df480a3490fd9a868ca"
integrity sha512-4Tj4ppVIPCmUMpmGFiGtyEriwLyJ+yi/US4WfBrP/ok8COGddDZXLEzQETnKyK46mjvr1v0jevrS23zjoff7vA==
"@swc/core-linux-arm-gnueabihf@1.15.43":
version "1.15.43"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.43.tgz#c0a0ed17cffc5d4af192935667f12f05feeb39f9"
integrity sha512-JWTQQELtsG5GgphDrr/XqqmM2pDN3cZqbMS0Mrg+iTiXL3F74sn/S2IyYE/5u4h2KLkTf9qQ7dXyxsbx7YzkeA==
"@swc/core-linux-arm-gnueabihf@1.15.46":
version "1.15.46"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.46.tgz#7bee01b7311c43b913771ef9c7012931871de73b"
integrity sha512-i8tUGnNjyOgMmfmgFSg4aeJLQoFyfpIHK5FjpQAwpRyQIqEUB2w1e8zIDQzY1WhOxx8NoS1S5iUL813Un4Sf5A==
"@swc/core-linux-arm64-gnu@1.15.43":
version "1.15.43"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.43.tgz#1eb2d9c5eeee5bb9d00599b475ddc31dc2870d22"
integrity sha512-B4otJRdPWIsmiSBf0uG7Z/+vMWmkufjz5MmYxubwKuZazDW14Zd3symga1N62QR4RT+kEFeHEgsXfZGyn/w0hw==
"@swc/core-linux-arm64-gnu@1.15.46":
version "1.15.46"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.46.tgz#964596d757d18f04a02873d85a3660416c09c187"
integrity sha512-c0OnhqzdhfOvv6qhNCcByepB+sNYOGZyhtr2Qa6ZCHvAWTYhSRw4j/u92Stue9PbZ/6q74b9nHzi76+kVzqQHQ==
"@swc/core-linux-arm64-musl@1.15.43":
version "1.15.43"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.43.tgz#ea6b5c38088f3921a57922d3931b2d74fd23a9fd"
integrity sha512-6zB6OnpViBxYy4tgY3v2i6AZY9fwkcHZ032UOwtwUuW1d19sdT07qF0kZe6/3UR1tUaK6jjg2rmVcUIBCEYVjQ==
"@swc/core-linux-arm64-musl@1.15.46":
version "1.15.46"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.46.tgz#213d3ece772689a8166ed51064836346c6ce1c2a"
integrity sha512-imyRpNEcUzFQFV2LE4jL68ErvmKEuZCbvZru77iQREunJ+bR4i658cupTgtG1mLYM3F1Tzy3Sb9xYb02KghWTg==
"@swc/core-linux-ppc64-gnu@1.15.43":
version "1.15.43"
resolved "https://registry.yarnpkg.com/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.43.tgz#538fac30bbd5f1e678bb7bac9ccc62246a6f6d7a"
integrity sha512-coxE1ZWdB3uSDVNoEtYNrRi/1epvckZx9cTJ8ICUxTMTxGk+yvQ/Twacp3ruZSaMPGCriUjP86C37VhaT6nyRg==
"@swc/core-linux-ppc64-gnu@1.15.46":
version "1.15.46"
resolved "https://registry.yarnpkg.com/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.46.tgz#4d2ec554103c6bef60cc1e294f374ea5a5edaf78"
integrity sha512-ctEfcl/HcUeomK33cbySiHZm98GEDIxTm1EkpBsYCiHxElYBzvTXVeuQT2YwbUXn9XCrjiw4ipyUNk33k26qRg==
"@swc/core-linux-s390x-gnu@1.15.43":
version "1.15.43"
resolved "https://registry.yarnpkg.com/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.43.tgz#ee564b45f3f578b1fc82136c4dab163189316641"
integrity sha512-lXfLhs+LpBsD5inuYx+YDH5WsPPBQ95KPUiy8P5wq9ob9xKDZFqwNfU2QW6bGO8NqRO/H9JQomTSt5Yyh+FGfA==
"@swc/core-linux-s390x-gnu@1.15.46":
version "1.15.46"
resolved "https://registry.yarnpkg.com/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.46.tgz#097a19792ec22e2f51f6bfac02da1e0b3f5e5bb1"
integrity sha512-DxlMdnt84TtRVTv7WL/thWyz9+QU8QZNNoAP9rrk0P68LziuhfePp8MjQ44zIprpTHTsEwyziIuGUUN5iSC1bQ==
"@swc/core-linux-x64-gnu@1.15.43":
version "1.15.43"
resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.43.tgz#e6e3bfea76921c7f5e16d50a126615f2e04ce1c8"
integrity sha512-07XnKwTmKy8TGOZG3D9fRnLWGynxPjwQnZLVmBFbo6F+7vHYzBIOuwXEhemrChBWb6yDNZsVCcMWCPX6FDD2xg==
"@swc/core-linux-x64-gnu@1.15.46":
version "1.15.46"
resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.46.tgz#39c1ca215f9ca643a4aa3ca6250cc38ba5f5c673"
integrity sha512-SKxI7J6t90XPl8hRUqtJi9NfGdunN/E/vZMc7Bc0figeRdOPDBT+Tm8g7cx9xM0T0mewh2l+8dewa3Am27/P+A==
"@swc/core-linux-x64-musl@1.15.43":
version "1.15.43"
resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.43.tgz#539f6f2721c0cc32e5db5cf0d453c82045f6662d"
integrity sha512-TJc+bsSIaBh+hZvZ5GRtW/K1bw66TJ9vsUwvVIsZdiWxU5ObLwZvfcnZ3UpgVfMnFibRes9uriJrQNBHEEogRQ==
"@swc/core-linux-x64-musl@1.15.46":
version "1.15.46"
resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.46.tgz#323a720bc965fffeedacdc3167b46a291553b5e0"
integrity sha512-qj9T6B7bosI0VEsrWOVXZN1OXxS8Tp63ywyrLxNdOycnUtLdkgYcoBsN5y8ImnDDsnwrEWZOy1e+J4xSe7mA3Q==
"@swc/core-win32-arm64-msvc@1.15.43":
version "1.15.43"
resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.43.tgz#b7bb6b611d484ac19d0ee21469e7012d646c28b5"
integrity sha512-jfd7s2/bUQYkOHLs+LWQNKZdmDa8+sufKLllhpWAhVQ2GDCwsHe3vR/j+OSiItZNtkzFuaawa3+SAKz9y5gYfw==
"@swc/core-win32-arm64-msvc@1.15.46":
version "1.15.46"
resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.46.tgz#9c2cfd2a59be74671a018097b8914f8cfbcc698d"
integrity sha512-8p7l4c3LU+eA5g9Et1JPhNeMC1oQwXTGU+uah8DPIBX7YXzqswvaBtyKVmXefVGi/DJU1x3YJsc3mbAp9aWzSQ==
"@swc/core-win32-ia32-msvc@1.15.43":
version "1.15.43"
resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.43.tgz#e5b25722a7d27bb0c9a9bdee7863f29c8674364e"
integrity sha512-rLAE8JvucqEW1ZGohxPQrQWPBQeJG4+ypKbWfdlU/qmKScvCkxf9/Jxnzki1dkUQCQ7P5Enp13RlvqOlvx/32g==
"@swc/core-win32-ia32-msvc@1.15.46":
version "1.15.46"
resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.46.tgz#bd7bd009a47b0f9826212e7ed36385d32fe193d8"
integrity sha512-tUEnfr3Bn9u6FOjUb3PN9p+09qZC2j+wNDLKHzXXZn22rqGcUqR/ohCRSS+nG9B9+X+U+3FewNEHJkTmdIvMjQ==
"@swc/core-win32-x64-msvc@1.15.43":
version "1.15.43"
resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.43.tgz#d28842621201c345383d468d40c09648b6cd6e68"
integrity sha512-h8MLDHZcfIukwQWj03rIJZx1I0E81AYj2X7J/nGErG4nz+QAv6G1Z+peotvinL3lqpbo32tLYSMFo32/ySzxKg==
"@swc/core-win32-x64-msvc@1.15.46":
version "1.15.46"
resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.46.tgz#8371845a5bdb330cf05b009f602bb8c4636c6beb"
integrity sha512-Vux7UDzBJYQggSuPfcl2w9iu+IJpgpRCxHzgCaVkELnAXAE4XZMOTX9HNcaNiwfeIDqdu2rkr69RuDm6wY8neA==
"@swc/core@^1.15.40", "@swc/core@^1.15.43":
version "1.15.43"
resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.15.43.tgz#653e6573968fd5c74163b9885ea0a933012c9f22"
integrity sha512-1CuKjFkPxIgGdeHVuNbkxmBxkcbdc08u0aiI43pFq6yY1tTVKmXT9hFEooyyKs/sJ3xf1GPHyEwTtk9Xl8dvQw==
"@swc/core@^1.15.40", "@swc/core@^1.15.46":
version "1.15.46"
resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.15.46.tgz#8acc0f68ee55010fdc876adf2a8faf0b097c681b"
integrity sha512-Ri3em2mBpq3h2zSPliCYl63otDGqek8PPEfv2nWgRQEbZ/VBCNyypVTVQ6cEbTCXBhy+WE2T3fQb08moIyuYaw==
dependencies:
"@swc/counter" "^0.1.3"
"@swc/types" "^0.1.27"
optionalDependencies:
"@swc/core-darwin-arm64" "1.15.43"
"@swc/core-darwin-x64" "1.15.43"
"@swc/core-linux-arm-gnueabihf" "1.15.43"
"@swc/core-linux-arm64-gnu" "1.15.43"
"@swc/core-linux-arm64-musl" "1.15.43"
"@swc/core-linux-ppc64-gnu" "1.15.43"
"@swc/core-linux-s390x-gnu" "1.15.43"
"@swc/core-linux-x64-gnu" "1.15.43"
"@swc/core-linux-x64-musl" "1.15.43"
"@swc/core-win32-arm64-msvc" "1.15.43"
"@swc/core-win32-ia32-msvc" "1.15.43"
"@swc/core-win32-x64-msvc" "1.15.43"
"@swc/core-darwin-arm64" "1.15.46"
"@swc/core-darwin-x64" "1.15.46"
"@swc/core-linux-arm-gnueabihf" "1.15.46"
"@swc/core-linux-arm64-gnu" "1.15.46"
"@swc/core-linux-arm64-musl" "1.15.46"
"@swc/core-linux-ppc64-gnu" "1.15.46"
"@swc/core-linux-s390x-gnu" "1.15.46"
"@swc/core-linux-x64-gnu" "1.15.46"
"@swc/core-linux-x64-musl" "1.15.46"
"@swc/core-win32-arm64-msvc" "1.15.46"
"@swc/core-win32-ia32-msvc" "1.15.46"
"@swc/core-win32-x64-msvc" "1.15.46"
"@swc/counter@^0.1.3":
version "0.1.3"
@@ -5527,100 +5527,100 @@
dependencies:
"@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@8.64.0", "@typescript-eslint/eslint-plugin@^8.64.0":
version "8.64.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz#71a0c3d5f8a5e6c5dfdb4f0f04bd1bfb572d5e24"
integrity sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==
"@typescript-eslint/eslint-plugin@8.65.0", "@typescript-eslint/eslint-plugin@^8.65.0":
version "8.65.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz#0a58df6fea8c0bf6b396f518077099bc8b762bb5"
integrity sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==
dependencies:
"@eslint-community/regexpp" "^4.12.2"
"@typescript-eslint/scope-manager" "8.64.0"
"@typescript-eslint/type-utils" "8.64.0"
"@typescript-eslint/utils" "8.64.0"
"@typescript-eslint/visitor-keys" "8.64.0"
"@typescript-eslint/scope-manager" "8.65.0"
"@typescript-eslint/type-utils" "8.65.0"
"@typescript-eslint/utils" "8.65.0"
"@typescript-eslint/visitor-keys" "8.65.0"
ignore "^7.0.5"
natural-compare "^1.4.0"
ts-api-utils "^2.5.0"
"@typescript-eslint/parser@8.64.0", "@typescript-eslint/parser@^8.64.0":
version "8.64.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.64.0.tgz#c9864a1cc28a13ff29a7314fbdef0528bb122f72"
integrity sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==
"@typescript-eslint/parser@8.65.0", "@typescript-eslint/parser@^8.65.0":
version "8.65.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.65.0.tgz#5295c1058c0a1dd746ef28baaf9c0341dbdf03dc"
integrity sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==
dependencies:
"@typescript-eslint/scope-manager" "8.64.0"
"@typescript-eslint/types" "8.64.0"
"@typescript-eslint/typescript-estree" "8.64.0"
"@typescript-eslint/visitor-keys" "8.64.0"
"@typescript-eslint/scope-manager" "8.65.0"
"@typescript-eslint/types" "8.65.0"
"@typescript-eslint/typescript-estree" "8.65.0"
"@typescript-eslint/visitor-keys" "8.65.0"
debug "^4.4.3"
"@typescript-eslint/project-service@8.64.0":
version "8.64.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.64.0.tgz#14c4e29390d7325a7f8a1218c2788fd649b85da6"
integrity sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==
"@typescript-eslint/project-service@8.65.0":
version "8.65.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.65.0.tgz#65fbbc9a1591abffaeab5513200f848271cb0aa5"
integrity sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==
dependencies:
"@typescript-eslint/tsconfig-utils" "^8.64.0"
"@typescript-eslint/types" "^8.64.0"
"@typescript-eslint/tsconfig-utils" "^8.65.0"
"@typescript-eslint/types" "^8.65.0"
debug "^4.4.3"
"@typescript-eslint/scope-manager@8.64.0":
version "8.64.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz#d45f15304a94c85c39db317b717b158fb6259958"
integrity sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==
"@typescript-eslint/scope-manager@8.65.0":
version "8.65.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz#9547202ce7e608e7b6283df585703b980a0ea70d"
integrity sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==
dependencies:
"@typescript-eslint/types" "8.64.0"
"@typescript-eslint/visitor-keys" "8.64.0"
"@typescript-eslint/types" "8.65.0"
"@typescript-eslint/visitor-keys" "8.65.0"
"@typescript-eslint/tsconfig-utils@8.64.0", "@typescript-eslint/tsconfig-utils@^8.64.0":
version "8.64.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz#c62ac8ea9173c3cac8b38b8e66e30a046b548851"
integrity sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==
"@typescript-eslint/tsconfig-utils@8.65.0", "@typescript-eslint/tsconfig-utils@^8.65.0":
version "8.65.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz#36f168fcdbb1295f7446ff0379667f98c3cf1bf3"
integrity sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==
"@typescript-eslint/type-utils@8.64.0":
version "8.64.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz#106fa7d58cf9cf7758f3dd8e426ac8237eceacf3"
integrity sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==
"@typescript-eslint/type-utils@8.65.0":
version "8.65.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz#d316d7522d93cff4cd14f305e02f3df2d804f9c1"
integrity sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==
dependencies:
"@typescript-eslint/types" "8.64.0"
"@typescript-eslint/typescript-estree" "8.64.0"
"@typescript-eslint/utils" "8.64.0"
"@typescript-eslint/types" "8.65.0"
"@typescript-eslint/typescript-estree" "8.65.0"
"@typescript-eslint/utils" "8.65.0"
debug "^4.4.3"
ts-api-utils "^2.5.0"
"@typescript-eslint/types@8.64.0", "@typescript-eslint/types@^8.64.0":
version "8.64.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.64.0.tgz#b41f8ef5dd40616908658b991197a9d486cda60b"
integrity sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==
"@typescript-eslint/types@8.65.0", "@typescript-eslint/types@^8.65.0":
version "8.65.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.65.0.tgz#3e86738416a777c8b8925ab46745f48ecf904c9f"
integrity sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==
"@typescript-eslint/typescript-estree@8.64.0":
version "8.64.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz#b8d51255e2d726eb4bd80d397a4fb4170c02eecc"
integrity sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==
"@typescript-eslint/typescript-estree@8.65.0":
version "8.65.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz#f1f514808f6aa713e2d678ae8ff592a65e1632af"
integrity sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==
dependencies:
"@typescript-eslint/project-service" "8.64.0"
"@typescript-eslint/tsconfig-utils" "8.64.0"
"@typescript-eslint/types" "8.64.0"
"@typescript-eslint/visitor-keys" "8.64.0"
"@typescript-eslint/project-service" "8.65.0"
"@typescript-eslint/tsconfig-utils" "8.65.0"
"@typescript-eslint/types" "8.65.0"
"@typescript-eslint/visitor-keys" "8.65.0"
debug "^4.4.3"
minimatch "^10.2.2"
semver "^7.7.3"
tinyglobby "^0.2.15"
ts-api-utils "^2.5.0"
"@typescript-eslint/utils@8.64.0":
version "8.64.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.64.0.tgz#98bb2010cfb754b41985b9c93e6e8b3dcd7bd600"
integrity sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==
"@typescript-eslint/utils@8.65.0":
version "8.65.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.65.0.tgz#afedd974a0c8deeef553b509df5800bafd615a72"
integrity sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==
dependencies:
"@eslint-community/eslint-utils" "^4.9.1"
"@typescript-eslint/scope-manager" "8.64.0"
"@typescript-eslint/types" "8.64.0"
"@typescript-eslint/typescript-estree" "8.64.0"
"@typescript-eslint/scope-manager" "8.65.0"
"@typescript-eslint/types" "8.65.0"
"@typescript-eslint/typescript-estree" "8.65.0"
"@typescript-eslint/visitor-keys@8.64.0":
version "8.64.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz#7a08421d10e54960733352cd7c95fab1784e8473"
integrity sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==
"@typescript-eslint/visitor-keys@8.65.0":
version "8.65.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz#e3704c13cb4a1c22454c1abf28ff4737e15018c6"
integrity sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==
dependencies:
"@typescript-eslint/types" "8.64.0"
"@typescript-eslint/types" "8.65.0"
eslint-visitor-keys "^5.0.0"
"@ungap/structured-clone@^1.0.0":
@@ -6368,10 +6368,10 @@ base64-js@^1.3.1, base64-js@^1.5.1:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
baseline-browser-mapping@^2.10.38, baseline-browser-mapping@^2.10.43, baseline-browser-mapping@^2.9.19:
version "2.10.43"
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz#7b5d11590ce5acdbe4859443e3c940e81ce8c02d"
integrity sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==
baseline-browser-mapping@^2.10.38, baseline-browser-mapping@^2.10.44, baseline-browser-mapping@^2.9.19:
version "2.10.44"
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.44.tgz#2e074675e640b67cb3835c54c11aa2dd9119c0f8"
integrity sha512-T3ghW+sl/ZJ8w1v/yQx3qvJ9040DWoLBz8JT/CILbAKcFyG9b2MRe75v6W5uXjv6uH1lumK2Kv46y2zSkcej0Q==
batch@0.6.1:
version "0.6.1"
@@ -6394,9 +6394,9 @@ binary-extensions@^2.0.0:
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
body-parser@~1.20.5:
version "1.20.5"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.5.tgz#303c8c34423d1d6fa799bc764e93c1e4dc6ebf64"
integrity sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==
version "1.20.6"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.6.tgz#60c789c78e0992d906da0a29d71ae01d15c1ed76"
integrity sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==
dependencies:
bytes "~3.1.2"
content-type "~1.0.5"
@@ -6602,10 +6602,10 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001799, caniuse-lite@^1.0.30001805:
version "1.0.30001805"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001805.tgz#78d5d5968a69b7ff81af87a96d7ddc7ea6670b1e"
integrity sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001799, caniuse-lite@^1.0.30001806:
version "1.0.30001806"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz#1bc8e502b723fa393455dfbedd5ccec0c29bb74e"
integrity sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==
ccount@^2.0.0:
version "2.0.1"
@@ -7007,7 +7007,7 @@ copy-text-to-clipboard@^3.2.0:
resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.2.tgz#99bc79db3f2d355ec33a08d573aff6804491ddb9"
integrity sha512-T6SqyLd1iLuqPA90J5N4cTalrtovCySh58iiZDGJ6FGznbclKh4UI+FGacQSgFzwKG77W7XT5gwbVEbd9cIH1A==
copy-to-clipboard@^3.3.1:
copy-to-clipboard@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0"
integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==
@@ -10155,10 +10155,10 @@ js-yaml@=4.2.0:
dependencies:
argparse "^2.0.1"
js-yaml@^5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-5.2.1.tgz#bb3f2e87295ebfe6ef0a75e17c62834daeff9ce2"
integrity sha512-zfLtNfQqxVqq3uaTqSkh4x4hZw3KHobGUA0fJUj4wawW8bsQLTVqpHdXSIzidh7o+4lEW36tANuAGdaFx6Zgnw==
js-yaml@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-5.2.2.tgz#497bfe63f0b0db11c7bbc5ce8bc568e836c8b08c"
integrity sha512-dayzUzKkJ1MkuUtZglSebU43utNXH0OWQByK9rKOOuYIO8M5TV1y+n8ALMdG0rdzBnfNkOmZEqrURepb0ejqBw==
dependencies:
argparse "^2.0.1"
@@ -11681,10 +11681,10 @@ mz@^2.7.0:
object-assign "^4.0.1"
thenify-all "^1.0.0"
nanoid@^3.3.12:
version "3.3.15"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.15.tgz#36c490fad8c6e86c824c940dfdde999b69ed4316"
integrity sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==
nanoid@^3.3.16:
version "3.3.16"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.16.tgz#a04d8ec4b1f10009d2d533947aefe4293737816c"
integrity sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==
natural-compare@^1.4.0:
version "1.4.0"
@@ -12974,11 +12974,11 @@ postcss-zindex@^6.0.2:
integrity sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==
postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.33, postcss@^8.5.4:
version "8.5.16"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.16.tgz#1230ce0b5df354c24c0ea45f99ce5f6a88279d28"
integrity sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==
version "8.5.23"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.23.tgz#3493550116f478487298301d2c2e8dc5a56e6594"
integrity sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==
dependencies:
nanoid "^3.3.12"
nanoid "^3.3.16"
picocolors "^1.1.1"
source-map-js "^1.2.1"
@@ -13029,10 +13029,10 @@ prettier-linter-helpers@^1.0.1:
dependencies:
fast-diff "^1.1.2"
prettier@^3.9.5:
version "3.9.5"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.5.tgz#4fec97736e33b9d0b620b48914fe93b530e835ad"
integrity sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==
prettier@^3.9.6:
version "3.9.6"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.6.tgz#b3ea5146515d40fc53f18aa63f74dfab1e10dbf6"
integrity sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==
pretty-error@^4.0.0:
version "4.0.0"
@@ -13243,12 +13243,12 @@ rc@1.2.8:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
react-copy-to-clipboard@5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz#09aae5ec4c62750ccb2e6421a58725eabc41255c"
integrity sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==
react-copy-to-clipboard@5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.1.tgz#76adb8be03616e99692fcf3f762365ed3fb5ff16"
integrity sha512-s+HrzLyJBxrpGTYXF15dTgMjAJpEPZT/Yp6NytAtZMRngejxt6Pt5WrfFxLAcsqUDU6sY1Jz6tyHwIicE1U2Xg==
dependencies:
copy-to-clipboard "^3.3.1"
copy-to-clipboard "^3.3.3"
prop-types "^15.8.1"
react-debounce-input@=3.3.0:
@@ -14627,10 +14627,10 @@ stop-iteration-iterator@^1.1.0:
es-errors "^1.3.0"
internal-slot "^1.1.0"
storybook@^10.5.0:
version "10.5.2"
resolved "https://registry.yarnpkg.com/storybook/-/storybook-10.5.2.tgz#751aca53cbeea086886d84ceb46c8a29a5f802f3"
integrity sha512-zkYxVZoDMj8njzZc3EH5UyY7885wpi9a1mmWVwFiNHSo+i5r2Go84E2OI1cdOctRymLkNvgs1j5jqAKA0ftBqg==
storybook@^10.5.3:
version "10.5.3"
resolved "https://registry.yarnpkg.com/storybook/-/storybook-10.5.3.tgz#72ee7cc02e3b6353eeec9633c2367437cf2ac86e"
integrity sha512-c8Wumu5qz0N2fnzWBxcPzUsY+8BpKBKChNyl4BEh9qhMV6KW587gL8il8emRB+4Hay+zMjDHA7cIeTkl4FKYuw==
dependencies:
"@storybook/global" "^5.0.0"
"@storybook/icons" "^2.0.2"
@@ -14925,10 +14925,10 @@ swagger-client@^3.37.4:
ramda "^0.30.1"
ramda-adjunct "^5.1.0"
swagger-ui-react@^5.32.8:
version "5.32.8"
resolved "https://registry.yarnpkg.com/swagger-ui-react/-/swagger-ui-react-5.32.8.tgz#0608b45cf552f33fcc9b3fc5e07740c9a854861f"
integrity sha512-Cstx4Tq8fT5l2TBxHxts8pG+ks0qKSkuO1pwUwgrQQiZ241Mqs+KUODLVIonsYXL/gqX143rkcipUa4d0Rid7w==
swagger-ui-react@^5.32.9:
version "5.32.9"
resolved "https://registry.yarnpkg.com/swagger-ui-react/-/swagger-ui-react-5.32.9.tgz#d0e8521091b270e35f8d87771fba49eede425b38"
integrity sha512-zs6PG4G6XEWG2evKHSwx32nWXIlk48a9Hw9YS9hlt55O5W0FM1cpltMrM/AnU+mohdnV7oEx0ap2TaG3fbfLIA==
dependencies:
"@babel/runtime-corejs3" "^7.27.1"
"@scarf/scarf" "=1.4.0"
@@ -14946,7 +14946,7 @@ swagger-ui-react@^5.32.8:
prop-types "^15.8.1"
randexp "^0.5.3"
randombytes "^2.1.0"
react-copy-to-clipboard "5.1.0"
react-copy-to-clipboard "5.1.1"
react-debounce-input "=3.3.0"
react-immutable-proptypes "2.2.0"
react-immutable-pure-component "^2.2.0"
@@ -15309,15 +15309,15 @@ types-ramda@^0.30.1:
dependencies:
ts-toolbelt "^9.6.0"
typescript-eslint@^8.64.0:
version "8.64.0"
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.64.0.tgz#4984dae4de9dc8bf892acf5c394d0a2a5f08c3e1"
integrity sha512-0qg+pDNMnqYzqH9AnNK+39tejHvsShUOUUoRUgtnTGE7QuMZhiFDnozq8nHJVq+Wae6NMLKNWLg5WmkcC/ndyQ==
typescript-eslint@^8.65.0:
version "8.65.0"
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.65.0.tgz#754c953fd6a9a3d36fa54015bdba80a6eb2a4957"
integrity sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==
dependencies:
"@typescript-eslint/eslint-plugin" "8.64.0"
"@typescript-eslint/parser" "8.64.0"
"@typescript-eslint/typescript-estree" "8.64.0"
"@typescript-eslint/utils" "8.64.0"
"@typescript-eslint/eslint-plugin" "8.65.0"
"@typescript-eslint/parser" "8.65.0"
"@typescript-eslint/typescript-estree" "8.65.0"
"@typescript-eslint/utils" "8.65.0"
typescript@~6.0.3:
version "6.0.3"

View File

@@ -29,7 +29,7 @@ maintainers:
- name: craig-rueda
email: craig@craigrueda.com
url: https://github.com/craig-rueda
version: 0.22.1 # See [README](https://github.com/apache/superset/blob/master/helm/superset/README.md#versioning) for version details.
version: 0.22.4 # See [README](https://github.com/apache/superset/blob/master/helm/superset/README.md#versioning) for version details.
dependencies:
- name: postgresql
version: 16.7.27

View File

@@ -23,7 +23,7 @@ NOTE: This file is generated by helm-docs: https://github.com/norwoodj/helm-docs
# superset
![Version: 0.22.1](https://img.shields.io/badge/Version-0.22.1-informational?style=flat-square)
![Version: 0.22.4](https://img.shields.io/badge/Version-0.22.4-informational?style=flat-square)
Apache Superset is a modern, enterprise-ready business intelligence web application
@@ -131,7 +131,7 @@ Alternatively, perform a fresh install. This is a one-time migration; subsequent
| httproute.hostnames | list | `[]` | Hostnames that match against the HTTP Host header (templated) |
| httproute.labels | object | `{}` | Additional labels to add to the HTTPRoute |
| httproute.parentRefs | list | `[]` | Gateways this HTTPRoute attaches to |
| httproute.rules | list | `[{"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]` | Routing rules. Each rule is backed by the Superset service. Set `weight` per rule to leave room for traffic splitting (defaults to 1). When `supersetWebsockets.enabled` is true, an extra rule routing `supersetWebsockets.ingress.path` to the `-ws` service is appended automatically, mirroring the ingress behavior. |
| httproute.rules | list | `[{"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]` | Routing rules. Each rule is backed by the Superset service. Set `weight` per rule to leave room for traffic splitting (defaults to 1). When `supersetWebsockets.enabled` is true, an extra rule routing `supersetWebsockets.ingress.path` to the `-ws` service is appended automatically, mirroring the ingress behavior. When both `supersetMcp.enabled` and `supersetMcp.httproute.enabled` are true, an extra rule routing `supersetMcp.httproute.path` to the `-mcp` service is also appended. |
| image.pullPolicy | string | `"IfNotPresent"` | |
| image.repository | string | `"apachesuperset.docker.scarf.sh/apache/superset"` | |
| image.tag | string | `nil` | |
@@ -159,6 +159,10 @@ Alternatively, perform a fresh install. This is a one-time migration; subsequent
| init.extraInitContainers | list | `[]` | Extra init containers appended after init job initContainers |
| init.initContainers | list | a container waiting for postgres | List of initContainers |
| init.initscript | string | unused; kept for backwards-compatibility only | DEPRECATED: this field is no longer used by the chart. The init script is rendered entirely from the internal `superset.initScript` template (which runs `superset db upgrade`, `superset init`, admin creation, and examples). Any customization placed here is silently ignored. See UPGRADING.md. |
| init.istio | object | `{"disableSidecarInjection":false,"quitEndpoint":"http://localhost:15020/quitquitquit","terminateSidecarOnExit":false}` | Configuration for compatibility with the Istio service mesh. Without these mitigations, when the init-db Job runs in a namespace with automatic Istio sidecar injection enabled, the injected envoy-proxy container keeps running after the init container has exited, preventing the Job from ever reaching the Completed state. See https://github.com/apache/superset/issues/25798 |
| init.istio.disableSidecarInjection | bool | `false` | When true, adds the `sidecar.istio.io/inject: "false"` label to the init job pod template, opting it out of Istio automatic sidecar injection. This is the recommended fix when the cluster's mesh policy allows opting out per pod. |
| init.istio.quitEndpoint | string | `"http://localhost:15020/quitquitquit"` | Endpoint that the trap POSTs to in order to terminate the sidecar. The default matches the standard Istio pilot-agent admin port. |
| init.istio.terminateSidecarOnExit | bool | `false` | When true, the rendered `superset_init.sh` script (from the internal `superset.initScript` template) registers an `EXIT` trap that POSTs to the Istio pilot-agent's `/quitquitquit` endpoint after the init logic finishes, gracefully terminating an injected envoy-proxy sidecar so the Job can complete. Enable this when sidecar injection cannot be disabled per pod (e.g. when enforced by a cluster-wide Istio policy). Note: requires `curl` to be available in the init container image (it is included in the default `apache/superset` image) and only takes effect when `init.command` sources the rendered `superset_init.sh`. |
| init.jobAnnotations."helm.sh/hook" | string | `"post-install,post-upgrade"` | |
| init.jobAnnotations."helm.sh/hook-delete-policy" | string | `"before-hook-creation"` | |
| init.loadExamples | bool | `false` | |
@@ -266,6 +270,8 @@ Alternatively, perform a fresh install. This is a one-time migration; subsequent
| supersetMcp.extraContainers | list | `[]` | Launch additional containers into supersetMcp pods |
| supersetMcp.extraInitContainers | list | `[]` | Extra init containers appended after supersetMcp initContainers |
| supersetMcp.forceReload | bool | `false` | If true, forces deployment to reload on each upgrade |
| supersetMcp.httproute.enabled | bool | `false` | If true, the MCP server will be exposed via the HTTPRoute /mcp subpath |
| supersetMcp.httproute.path | string | `"/mcp"` | |
| supersetMcp.ingress.enabled | bool | `false` | If true, the MCP server will be exposed via the ingress /mcp subpath |
| supersetMcp.ingress.path | string | `"/mcp"` | |
| supersetMcp.ingress.pathType | string | `"Prefix"` | |

View File

@@ -685,6 +685,14 @@ TALISMAN_CONFIG = {
{{- define "superset.initScript" -}}
#!/bin/sh
set -eu
{{- if dig "istio" "terminateSidecarOnExit" false .Values.init }}
# Notify the Istio pilot-agent sidecar to exit when this script completes
# (whether successfully or via `set -e`), so that the Job can reach the
# Completed state instead of hanging on a still-running envoy-proxy.
# See https://github.com/apache/superset/issues/25798
ISTIO_QUIT_ENDPOINT={{ dig "istio" "quitEndpoint" "http://localhost:15020/quitquitquit" .Values.init | replace "'" "'\\''" | squote }}
trap 'rc=$?; curl -fsS -m 5 -X POST "$ISTIO_QUIT_ENDPOINT" >/dev/null 2>&1 || echo "WARNING: failed to notify Istio sidecar at $ISTIO_QUIT_ENDPOINT to quit; the Job may hang if sidecar injection is active" >&2; exit $rc' EXIT
{{- end }}
echo "Upgrading DB schema..."
superset db upgrade
echo "Initializing roles and permissions..."

View File

@@ -80,4 +80,16 @@ spec:
type: PathPrefix
value: {{ .Values.supersetWebsockets.ingress.path }}
{{- end }}
{{- if and .Values.supersetMcp.enabled .Values.supersetMcp.httproute.enabled }}
- backendRefs:
- group: ''
kind: Service
name: {{ $fullName }}-mcp
port: {{ .Values.supersetMcp.service.port }}
weight: 1
matches:
- path:
type: PathPrefix
value: {{ .Values.supersetMcp.httproute.path }}
{{- end }}
{{- end }}

View File

@@ -35,6 +35,7 @@ spec:
{{- if .Values.init.podAnnotations }}
annotations: {{- toYaml .Values.init.podAnnotations | nindent 8 }}
{{- end }}
{{- $istioDisableInject := dig "istio" "disableSidecarInjection" false .Values.init }}
labels:
{{- include "superset.componentSelectorLabels" (dict "component" "init" "root" .) | nindent 8 }}
job: {{ template "superset.fullname" . }}-init-db
@@ -44,6 +45,9 @@ spec:
{{- if .Values.init.podLabels }}
{{- toYaml .Values.init.podLabels | nindent 8 }}
{{- end }}
{{- if $istioDisableInject }}
sidecar.istio.io/inject: "false"
{{- end }}
spec:
{{- if .Values.init.additionalPodSpec }}
{{- tpl (toYaml .Values.init.additionalPodSpec) . | nindent 6 }}

View File

@@ -0,0 +1,92 @@
#
# 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.
#
suite: HTTPRoute
templates:
- httproute.yaml
tests:
- it: routes the general Superset path
set:
httproute.enabled: true
asserts:
- lengthEqual:
path: spec.rules
count: 1
- equal:
path: spec.rules[0].backendRefs[0].name
value: RELEASE-NAME-superset
- equal:
path: spec.rules[0].backendRefs[0].port
value: 8088
- equal:
path: spec.rules[0].matches[0].path.value
value: /
- it: routes the WebSocket path when WebSockets are enabled
set:
httproute.enabled: true
supersetWebsockets.enabled: true
asserts:
- lengthEqual:
path: spec.rules
count: 2
- equal:
path: spec.rules[1].backendRefs[0].name
value: RELEASE-NAME-superset-ws
- equal:
path: spec.rules[1].backendRefs[0].port
value: 8080
- equal:
path: spec.rules[1].matches[0].path.value
value: /ws
- it: does not route the WebSocket path when WebSockets are disabled
set:
httproute.enabled: true
supersetWebsockets.enabled: false
asserts:
- lengthEqual:
path: spec.rules
count: 1
- it: routes the MCP path when MCP HTTPRoute is enabled
set:
httproute.enabled: true
supersetMcp.enabled: true
supersetMcp.httproute.enabled: true
asserts:
- lengthEqual:
path: spec.rules
count: 2
- equal:
path: spec.rules[1].backendRefs[0].name
value: RELEASE-NAME-superset-mcp
- equal:
path: spec.rules[1].backendRefs[0].port
value: 5008
- equal:
path: spec.rules[1].matches[0].path.value
value: /mcp
- it: does not route MCP when MCP HTTPRoute is disabled
set:
httproute.enabled: true
supersetMcp.enabled: true
supersetMcp.httproute.enabled: false
asserts:
- lengthEqual:
path: spec.rules
count: 1

183
helm/superset/tests/test-istio.sh Executable file
View File

@@ -0,0 +1,183 @@
#!/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.
#
# Renders the chart with `helm template` for several `init.istio.*` value
# combinations and asserts that the relevant manifests contain (or omit)
# the expected fields. Intended to be run from the chart directory or via
# `bash helm/superset/tests/test-istio.sh` from the repo root.
#
# Covers the fix for:
# https://github.com/apache/superset/issues/25798
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CHART_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
pass=0
fail=0
assert_contains() {
local label="$1"
local needle="$2"
local haystack="$3"
if grep -qF -- "${needle}" <<<"${haystack}"; then
echo " PASS: ${label}"
pass=$((pass + 1))
else
echo " FAIL: ${label}"
echo " expected to contain: ${needle}"
fail=$((fail + 1))
fi
}
assert_not_contains() {
local label="$1"
local needle="$2"
local haystack="$3"
if ! grep -qF -- "${needle}" <<<"${haystack}"; then
echo " PASS: ${label}"
pass=$((pass + 1))
else
echo " FAIL: ${label}"
echo " expected NOT to contain: ${needle}"
fail=$((fail + 1))
fi
}
render() {
helm template release "${CHART_DIR}" "$@"
}
extract_init_job() {
awk '
/^# Source: superset\/templates\/init-job\.yaml/ { capture = 1 }
capture && /^---$/ { capture = 0 }
capture { print }
' <<<"$1"
}
extract_config_secret() {
awk '
/^# Source: superset\/templates\/secret-superset-config\.yaml/ { capture = 1 }
capture && /^---$/ { capture = 0 }
capture { print }
' <<<"$1"
}
echo "==> defaults: no istio mitigations applied"
out_default="$(render)"
init_job_default="$(extract_init_job "${out_default}")"
config_default="$(extract_config_secret "${out_default}")"
assert_not_contains "default does not set sidecar.istio.io/inject label" \
'sidecar.istio.io/inject' "${init_job_default}"
assert_not_contains "default initscript does not register quitquitquit trap" \
'quitquitquit' "${config_default}"
echo "==> init.istio.disableSidecarInjection=true"
out_disable="$(render --set init.istio.disableSidecarInjection=true)"
init_job_disable="$(extract_init_job "${out_disable}")"
config_disable="$(extract_config_secret "${out_disable}")"
assert_contains "init job sets sidecar.istio.io/inject: \"false\"" \
'sidecar.istio.io/inject: "false"' "${init_job_disable}"
assert_not_contains "disableSidecarInjection alone does not add quitquitquit trap" \
'quitquitquit' "${config_disable}"
echo "==> init.istio.terminateSidecarOnExit=true"
out_terminate="$(render --set init.istio.terminateSidecarOnExit=true)"
init_job_terminate="$(extract_init_job "${out_terminate}")"
config_terminate="$(extract_config_secret "${out_terminate}")"
assert_not_contains "terminateSidecarOnExit alone does not add inject label" \
'sidecar.istio.io/inject' "${init_job_terminate}"
assert_contains "initscript registers EXIT trap that calls quitquitquit" \
"trap 'rc=\$?; curl -fsS -m 5 -X POST" "${config_terminate}"
assert_contains "initscript trap targets the configured quit endpoint" \
'http://localhost:15020/quitquitquit' "${config_terminate}"
assert_contains "initscript trap binds to the EXIT signal" \
"' EXIT" "${config_terminate}"
echo "==> init.istio.quitEndpoint override"
out_endpoint="$(render --set init.istio.terminateSidecarOnExit=true \
--set init.istio.quitEndpoint=http://127.0.0.1:15020/quitquitquit)"
config_endpoint="$(extract_config_secret "${out_endpoint}")"
assert_contains "trap honours custom quitEndpoint" \
'http://127.0.0.1:15020/quitquitquit' "${config_endpoint}"
echo "==> both options combined"
out_both="$(render --set init.istio.disableSidecarInjection=true \
--set init.istio.terminateSidecarOnExit=true)"
init_job_both="$(extract_init_job "${out_both}")"
config_both="$(extract_config_secret "${out_both}")"
assert_contains "combined: inject label present" \
'sidecar.istio.io/inject: "false"' "${init_job_both}"
assert_contains "combined: trap present" \
'quitquitquit' "${config_both}"
echo "==> existing init.podLabels are preserved alongside istio label"
out_labels="$(render --set init.istio.disableSidecarInjection=true \
--set init.podLabels.team=data-platform)"
init_job_labels="$(extract_init_job "${out_labels}")"
assert_contains "user-supplied podLabel still rendered" \
'team: data-platform' "${init_job_labels}"
assert_contains "istio inject label rendered alongside" \
'sidecar.istio.io/inject: "false"' "${init_job_labels}"
echo "==> init.istio explicitly overridden to null"
out_null_istio="$(render --set init.istio=null)"
init_job_null_istio="$(extract_init_job "${out_null_istio}")"
config_null_istio="$(extract_config_secret "${out_null_istio}")"
assert_not_contains "null init.istio does not set inject label" \
'sidecar.istio.io/inject' "${init_job_null_istio}"
assert_not_contains "null init.istio does not register quitquitquit trap" \
'quitquitquit' "${config_null_istio}"
echo "==> EXIT trap propagates the script's exit code, not the notification's"
# The trap's own curl call is best-effort (failures are logged with
# `|| echo ... >&2`, not swallowed with `|| true`) and must not mask a
# failed migration. Extract the two rendered lines and actually run them,
# with curl pointed at a closed local port so the notification itself fails,
# to make sure the wrapped script's real exit code still comes through.
quit_endpoint_line="$(grep -F 'ISTIO_QUIT_ENDPOINT=' <<<"${config_terminate}" || true)"
trap_line="$(grep -F "trap 'rc=\$?; curl" <<<"${config_terminate}" || true)"
if [[ -z "${quit_endpoint_line}" || -z "${trap_line}" ]]; then
echo " FAIL: script exit code (42) survives a failing quitquitquit notification"
echo " could not locate the rendered ISTIO_QUIT_ENDPOINT/trap lines to exercise"
fail=$((fail + 1))
else
set +e
(
eval "${quit_endpoint_line}"
ISTIO_QUIT_ENDPOINT="http://127.0.0.1:1/quitquitquit"
eval "${trap_line}"
exit 42
)
trap_test_rc=$?
set -e
if [[ "${trap_test_rc}" -eq 42 ]]; then
echo " PASS: script exit code (42) survives a failing quitquitquit notification"
pass=$((pass + 1))
else
echo " FAIL: script exit code (42) survives a failing quitquitquit notification"
echo " got exit code: ${trap_test_rc}"
fail=$((fail + 1))
fi
fi
echo
echo "passed: ${pass}, failed: ${fail}"
if [[ ${fail} -gt 0 ]]; then
exit 1
fi

View File

@@ -98,13 +98,13 @@ envFromSecrets: []
# -- Extra environment variables that will be passed into pods
extraEnv: {}
# Different gunicorn settings, refer to the gunicorn documentation
# https://docs.gunicorn.org/en/stable/settings.html#
# https://gunicorn.org/reference/settings/
# These variables are used as Flags at the gunicorn startup
# https://github.com/apache/superset/blob/master/docker/run-server.sh#L22
# https://github.com/apache/superset/blob/master/docker/entrypoints/run-server.sh#L22
# Extend timeout to allow long running queries.
# GUNICORN_TIMEOUT: 300
# Increase the gunicorn worker amount, can improve performance drastically
# See: https://docs.gunicorn.org/en/stable/design.html#how-many-workers
# See: https://gunicorn.org/design/#how-many-workers
# SERVER_WORKER_AMOUNT: 4
# WORKER_MAX_REQUESTS: 0
# WORKER_MAX_REQUESTS_JITTER: 0
@@ -277,7 +277,9 @@ httproute:
# per rule to leave room for traffic splitting (defaults to 1). When
# `supersetWebsockets.enabled` is true, an extra rule routing
# `supersetWebsockets.ingress.path` to the `-ws` service is appended
# automatically, mirroring the ingress behavior.
# automatically, mirroring the ingress behavior. When both `supersetMcp.enabled` and
# `supersetMcp.httproute.enabled` are true, an extra rule routing
# `supersetMcp.httproute.path` to the `-mcp` service is also appended.
rules:
- matches:
- path:
@@ -987,7 +989,6 @@ supersetWebsockets:
# -- Set priorityClassName for supersetWebsockets pods
priorityClassName: ~
supersetMcp:
# -- Enables the Superset MCP Server. To expose it via the shared ingress at /mcp,
# also set supersetMcp.ingress.enabled=true.
@@ -1015,6 +1016,10 @@ supersetMcp:
enabled: false
path: /mcp
pathType: Prefix
httproute:
# -- If true, the MCP server will be exposed via the HTTPRoute /mcp subpath
enabled: false
path: /mcp
service:
type: ClusterIP
annotations: {}
@@ -1236,6 +1241,31 @@ init:
topologySpreadConstraints: []
# -- Set priorityClassName for init job pods
priorityClassName: ~
# -- Configuration for compatibility with the Istio service mesh.
# Without these mitigations, when the init-db Job runs in a namespace with
# automatic Istio sidecar injection enabled, the injected envoy-proxy
# container keeps running after the init container has exited, preventing
# the Job from ever reaching the Completed state.
# See https://github.com/apache/superset/issues/25798
istio:
# -- When true, adds the `sidecar.istio.io/inject: "false"` label to the
# init job pod template, opting it out of Istio automatic sidecar
# injection. This is the recommended fix when the cluster's mesh policy
# allows opting out per pod.
disableSidecarInjection: false
# -- When true, the rendered `superset_init.sh` script (from the internal
# `superset.initScript` template) registers an `EXIT` trap that POSTs to
# the Istio pilot-agent's `/quitquitquit` endpoint after the init logic
# finishes, gracefully terminating an injected envoy-proxy sidecar so
# the Job can complete. Enable this when sidecar injection cannot be
# disabled per pod (e.g. when enforced by a cluster-wide Istio policy).
# Note: requires `curl` to be available in the init container image
# (it is included in the default `apache/superset` image) and only
# takes effect when `init.command` sources the rendered `superset_init.sh`.
terminateSidecarOnExit: false
# -- Endpoint that the trap POSTs to in order to terminate the sidecar.
# The default matches the standard Istio pilot-agent admin port.
quitEndpoint: "http://localhost:15020/quitquitquit"
# -- Configuration values for the postgresql dependency.
# ref: https://github.com/bitnami/charts/tree/main/bitnami/postgresql

View File

@@ -54,7 +54,7 @@ dependencies = [
"deprecation>=2.1.0, <2.2.0",
"flask>=2.2.5, <4.0.0",
"flask-appbuilder>=5.2.2, <6.0.0",
"flask-caching>=2.1.0, <3",
"flask-caching>=2.4.1, <3",
"flask-compress>=1.13, <2.0",
"flask-talisman>=1.0.0, <2.0",
"flask-login>=0.6.0, < 1.0",
@@ -92,7 +92,7 @@ dependencies = [
"Pillow>=11.0.0, <13",
"polyline>=2.0.0, <3.0",
"pydantic>=2.8.0",
"pyparsing>=3.0.6, <4",
"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",
@@ -106,7 +106,7 @@ dependencies = [
"sshtunnel>=0.4.0, <0.5",
"simplejson>=4.1.1",
"slack_sdk>=3.43.0, <4",
"sqlalchemy>=1.4, <2",
"sqlalchemy>=1.4.43, <2", # 1.4.43 adds the python-oracledb (oracle+oracledb) dialect
"sqlalchemy-continuum>=1.6.0, <2.0.0",
"sqlalchemy-utils>=0.42.1, <0.43", # expanding lowerbound to work with pydoris
"sqlglot>=30.12.0, <31",
@@ -127,7 +127,7 @@ aurora-data-api = ["preset-sqlalchemy-aurora-data-api>=0.2.8,<0.3"]
bigquery = [
"pandas-gbq>=0.35.0",
"sqlalchemy-bigquery>=1.17.0",
"google-cloud-bigquery>=3.42.1",
"google-cloud-bigquery>=3.42.2",
]
clickhouse = ["clickhouse-connect>=1.4.2, <2.0"]
cockroachdb = ["cockroachdb>=0.3.5, <0.4"]
@@ -139,10 +139,11 @@ d1 = [
]
databend = ["databend-sqlalchemy>=0.5.5, <1.0"]
databricks = [
"databricks-sql-connector==4.2.6",
"databricks-sql-connector>=4.2.6, <4.4.0",
"databricks-sqlalchemy==1.0.5",
]
db2 = ["ibm-db-sa>0.3.8, <=0.4.4"]
datafusion = ["flightsql-dbapi>=0.2.2, <0.3"]
db2 = ["ibm-db-sa<=0.4.4, >=0.4.4"]
denodo = ["denodo-sqlalchemy>=2.0.5,<2.1.0"]
dremio = ["sqlalchemy-dremio>=1.2.1, <4"]
drill = ["sqlalchemy-drill>=1.1.10, <2"]
@@ -188,7 +189,7 @@ ocient = [
"shapely",
"geojson",
]
oracle = ["oracledb>=2.0.0, <5"]
oracle = ["oracledb>=4.0.2, <5"]
parseable = ["sqlalchemy-parseable>=0.1.6,<0.2.0"]
pinot = ["pinotdb>=5.0.0, <10.0.0"]
playwright = ["playwright>=1.61.0, <2"]
@@ -200,7 +201,7 @@ redshift = ["sqlalchemy-redshift>=0.8.1, <0.9"]
risingwave = ["sqlalchemy-risingwave"]
shillelagh = ["shillelagh[all]>=1.4.4, <2"]
singlestore = ["sqlalchemy-singlestoredb>=1.2.1, <2"]
snowflake = ["snowflake-sqlalchemy>=1.10.2, <2"]
snowflake = ["snowflake-sqlalchemy>=1.11.0, <2"]
sqlite = ["syntaqlite>=0.7.0,<0.8.0"]
spark = [
"pyhive[hive_pure_sasl]>=0.7",
@@ -209,7 +210,7 @@ spark = [
]
tdengine = [
"taospy>=2.8.9",
"taos-ws-py>=0.6.9"
"taos-ws-py>=0.7.0"
]
teradata = ["teradatasql>=20.0.0.62"]
thumbnails = [] # deprecated, will be removed in 7.0
@@ -226,7 +227,7 @@ development = [
"docker",
"flask-testing",
"freezegun",
"grpcio>=1.81.1",
"grpcio>=1.82.1",
"openapi-spec-validator",
"parameterized",
"pip",

View File

@@ -36,11 +36,9 @@ filterwarnings =
error:"TableColumn" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
error:"TaggedObject" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
error:The autoload parameter is deprecated:sqlalchemy.exc.RemovedIn20Warning
error:The current statement is being autocommitted using implicit autocommit:sqlalchemy.exc.RemovedIn20Warning
error:The connection.execute\(\) method:sqlalchemy.exc.RemovedIn20Warning
# error:The current statement is being autocommitted using implicit autocommit:sqlalchemy.exc.RemovedIn20Warning
error:The current statement is being autocommitted using implicit autocommit:sqlalchemy.exc.RemovedIn20Warning
error:The ``declarative_base\(\)`` function is now available:sqlalchemy.exc.RemovedIn20Warning
error:The Engine.execute\(\) method is considered legacy:sqlalchemy.exc.RemovedIn20Warning
error:The legacy calling style of select\(\) is deprecated:sqlalchemy.exc.RemovedIn20Warning
error:The "whens" argument to case:sqlalchemy.exc.RemovedIn20Warning
# error:"User" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning

View File

@@ -28,6 +28,8 @@ babel==2.17.0
# via flask-babel
backoff==2.2.1
# via apache-superset (pyproject.toml)
backports-zstd==1.6.0
# via flask-compress
bcrypt==4.3.0
# via paramiko
billiard==4.2.1
@@ -128,9 +130,9 @@ flask-appbuilder==5.2.2
# apache-superset-core
flask-babel==3.1.0
# via flask-appbuilder
flask-caching==2.3.1
flask-caching==2.4.1
# via apache-superset (pyproject.toml)
flask-compress==1.17
flask-compress==1.24
# via apache-superset (pyproject.toml)
flask-cors==6.0.5
# via apache-superset (pyproject.toml)
@@ -244,7 +246,7 @@ msgpack==1.2.1
# via apache-superset (pyproject.toml)
msgspec==0.19.0
# via flask-session
nh3==0.3.5
nh3==0.3.6
# via apache-superset (pyproject.toml)
numexpr==2.10.2
# via -r requirements/base.in
@@ -285,7 +287,7 @@ parsedatetime==2.6
# via apache-superset (pyproject.toml)
pgsanity==0.2.9
# via apache-superset (pyproject.toml)
pillow==12.2.0
pillow==12.3.0
# via apache-superset (pyproject.toml)
platformdirs==4.3.8
# via requests-cache
@@ -300,17 +302,17 @@ pyarrow==24.0.0
# -r requirements/base.in
# apache-superset (pyproject.toml)
# apache-superset-core
pyasn1==0.6.3
pyasn1==0.6.4
# via pyasn1-modules
pyasn1-modules==0.4.2
# via google-auth
pycparser==2.22
# via cffi
pydantic==2.11.7
pydantic==2.13.4
# via
# apache-superset (pyproject.toml)
# apache-superset-core
pydantic-core==2.33.2
pydantic-core==2.46.4
# via pydantic
pygeohash==3.2.2
# via apache-superset (pyproject.toml)
@@ -327,7 +329,7 @@ pyopenssl==26.3.0
# via
# -r requirements/base.in
# shillelagh
pyparsing==3.2.3
pyparsing==3.3.2
# via apache-superset (pyproject.toml)
pysocks==1.7.1
# via urllib3
@@ -408,7 +410,7 @@ sqlalchemy==1.4.54
# shillelagh
# sqlalchemy-continuum
# sqlalchemy-utils
sqlalchemy-continuum==1.6.0
sqlalchemy-continuum==1.7.0
# via apache-superset (pyproject.toml)
sqlalchemy-utils==0.42.1
# via
@@ -443,7 +445,7 @@ typing-extensions==4.16.0
# selenium
# shillelagh
# typing-inspection
typing-inspection==0.4.1
typing-inspection==0.4.2
# via pydantic
tzdata==2025.2
# via
@@ -496,5 +498,3 @@ xlsxwriter==3.2.9
# via
# apache-superset (pyproject.toml)
# pandas
zstandard==0.23.0
# via flask-compress

View File

@@ -64,6 +64,10 @@ backoff==2.2.1
# apache-superset
backports-tarfile==1.2.0
# via jaraco-context
backports-zstd==1.6.0
# via
# -c requirements/base-constraint.txt
# flask-compress
bcrypt==4.3.0
# via
# -c requirements/base-constraint.txt
@@ -272,11 +276,11 @@ flask-babel==3.1.0
# via
# -c requirements/base-constraint.txt
# flask-appbuilder
flask-caching==2.3.1
flask-caching==2.4.1
# via
# -c requirements/base-constraint.txt
# apache-superset
flask-compress==1.17
flask-compress==1.24
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -337,7 +341,7 @@ geopy==2.4.1
# apache-superset
gevent==26.4.0
# via apache-superset
google-api-core==2.23.0
google-api-core==2.33.0
# via
# google-cloud-bigquery
# google-cloud-core
@@ -358,7 +362,7 @@ google-auth-oauthlib==1.2.1
# via
# pandas-gbq
# pydata-google-auth
google-cloud-bigquery==3.42.1
google-cloud-bigquery==3.42.2
# via
# apache-superset
# pandas-gbq
@@ -382,7 +386,7 @@ greenlet==3.5.3
# sqlalchemy
griffelib==2.0.2
# via fastmcp-slim
grpcio==1.81.1
grpcio==1.83.0
# via
# apache-superset
# google-api-core
@@ -570,7 +574,7 @@ msgspec==0.19.0
# flask-session
mysqlclient==2.2.8
# via apache-superset
nh3==0.3.5
nh3==0.3.6
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -665,7 +669,7 @@ pgsanity==0.2.9
# via
# -c requirements/base-constraint.txt
# apache-superset
pillow==12.2.0
pillow==12.3.0
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -724,7 +728,7 @@ pyarrow==24.0.0
# apache-superset-core
# db-dtypes
# pandas-gbq
pyasn1==0.6.3
pyasn1==0.6.4
# via
# -c requirements/base-constraint.txt
# pyasn1-modules
@@ -738,7 +742,7 @@ pycparser==2.22
# via
# -c requirements/base-constraint.txt
# cffi
pydantic==2.11.7
pydantic==2.13.4
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -747,7 +751,7 @@ pydantic==2.11.7
# mcp
# openapi-pydantic
# pydantic-settings
pydantic-core==2.33.2
pydantic-core==2.46.4
# via
# -c requirements/base-constraint.txt
# pydantic
@@ -791,7 +795,7 @@ pyopenssl==26.3.0
# -c requirements/base-constraint.txt
# google-auth
# shillelagh
pyparsing==3.2.3
pyparsing==3.3.2
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -987,7 +991,7 @@ sqlalchemy==1.4.54
# sqlalchemy-utils
sqlalchemy-bigquery==1.17.0
# via apache-superset
sqlalchemy-continuum==1.6.0
sqlalchemy-continuum==1.7.0
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -1066,7 +1070,7 @@ typing-extensions==4.16.0
# shillelagh
# starlette
# typing-inspection
typing-inspection==0.4.1
typing-inspection==0.4.2
# via
# -c requirements/base-constraint.txt
# mcp
@@ -1168,7 +1172,4 @@ zope-event==5.0
zope-interface==5.4.0
# via gevent
zstandard==0.23.0
# via
# -c requirements/base-constraint.txt
# flask-compress
# trino
# via trino

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)
engine = create_engine(sqlalchemy_uri, future=True)
Base = automap_base() # noqa: N806
Base.prepare(engine, reflect=True)
seen = set()

View File

@@ -39,6 +39,7 @@ RETRYABLE_STATUS_CODES: frozenset[int] = frozenset({403, 429})
PATTERNS = {
"python": [
r"^\.github/workflows/.*python",
r"^docker-compose-image-tag\.yml$",
r"^tests/",
r"^superset/",
r"^scripts/",

View File

@@ -2662,9 +2662,9 @@
}
},
"node_modules/brace-expansion": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
"integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
"version": "1.1.16",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
"integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3937,9 +3937,9 @@
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.12",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
"version": "3.3.16",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
"dev": true,
"funding": [
{
@@ -4114,9 +4114,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.15",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
"version": "8.5.23",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz",
"integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==",
"dev": true,
"funding": [
{
@@ -4134,7 +4134,7 @@
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.12",
"nanoid": "^3.3.16",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},

View File

@@ -97,7 +97,7 @@ export const databasesPage = {
infoAlert: '.ant-alert',
serviceAccountInput: '[name="credentials_info"]',
connectionStep: {
modal: '.ant-modal-content',
modal: '.ant-modal-container',
modalBody: '.ant-modal-body',
stepTitle: '.css-7x6kk > h4',
helperBottom: '.helper-bottom',
@@ -261,7 +261,7 @@ export const datasetsList = {
},
},
deleteDatasetModal: {
modal: '.ant-modal-content',
modal: '.ant-modal-container',
deleteInput: dataTestLocator('delete-modal-input'),
deleteButton: dataTestLocator('modal-confirm-button'),
text: '.css-kxmt87',
@@ -474,7 +474,7 @@ export const exploreView = {
},
chartAreaItem: '.nv-legend-text',
viewQueryModal: {
container: '.ant-modal-content',
container: '.ant-modal-container',
closeButton: 'button.ant-modal-close',
},
embedCodeModal: {
@@ -482,7 +482,7 @@ export const exploreView = {
textfield: dataTestLocator('embed-code-textarea'),
},
saveModal: {
modal: '.ant-modal-content',
modal: '.ant-modal-container',
chartNameInput: dataTestLocator('new-chart-name'),
dashboardNameInput: '.ant-select-input',
addToDashboardInput: dataTestLocator(
@@ -578,7 +578,7 @@ export const exploreView = {
},
},
editDatasetModal: {
container: '.ant-modal-content',
container: '.ant-modal-container',
datasetTabsContainer: dataTestLocator('edit-dataset-tabs'),
saveButton: dataTestLocator('datasource-modal-save'),
metricsTab: {
@@ -617,12 +617,12 @@ export const dashboardView = {
closeButton: dataTestLocator('close-button'),
},
saveModal: {
modal: '.ant-modal-content',
modal: '.ant-modal-container',
dashboardNameInput: '.ant-input',
saveButton: dataTestLocator('modal-save-dashboard-button'),
},
dashboardProperties: {
modal: '.ant-modal-content',
modal: '.ant-modal-container',
dashboardTitleInput: dataTestLocator('dashboard-title-input'),
modalButton: '[type="button"]',
},

File diff suppressed because it is too large Load Diff

View File

@@ -22,7 +22,7 @@
"chai": "^4.5.0",
"cy-verify-downloads": "^0.2.5",
"cypress-fail-on-console-error": "^4.0.3",
"nanoid": "^5.0.9",
"nanoid": "^6.0.0",
"querystringify": "^2.2.0",
"react-dom": "^16.13.0",
"rison": "^0.1.1"

View File

@@ -215,6 +215,7 @@
"jsx-a11y/no-noninteractive-tabindex": "error",
"jsx-a11y/no-redundant-roles": "error",
"jsx-a11y/no-static-element-interactions": "error",
"jsx-a11y/prefer-tag-over-role": "error",
"jsx-a11y/role-has-required-aria-props": "error",
"jsx-a11y/role-supports-aria-props": "error",
"jsx-a11y/scope": "error",

File diff suppressed because it is too large Load Diff

View File

@@ -115,9 +115,9 @@
"@emotion/cache": "^11.4.0",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@fontsource/fira-code": "^5.2.7",
"@fontsource/ibm-plex-mono": "^5.2.7",
"@fontsource/inter": "^5.2.8",
"@fontsource/fira-code": "^5.3.0",
"@fontsource/ibm-plex-mono": "^5.3.0",
"@fontsource/inter": "^5.3.0",
"@googleapis/sheets": "^13.0.2",
"@great-expectations/jsonforms-antd-renderers": "^2.2.10",
"@jsonforms/core": "^3.7.0",
@@ -130,9 +130,9 @@
"@luma.gl/shadertools": "~9.2.5",
"@luma.gl/webgl": "~9.2.5",
"@reduxjs/toolkit": "^1.9.3",
"@rjsf/core": "^6.6.2",
"@rjsf/core": "^6.7.0",
"@rjsf/utils": "^6.6.2",
"@rjsf/validator-ajv8": "^6.6.2",
"@rjsf/validator-ajv8": "^6.7.0",
"@scarf/scarf": "^1.4.0",
"@superset-ui/chart-controls": "file:./packages/superset-ui-chart-controls",
"@superset-ui/core": "file:./packages/superset-ui-core",
@@ -166,10 +166,10 @@
"@visx/scale": "^4.0.0",
"@visx/tooltip": "^4.0.0",
"@visx/xychart": "^4.0.0",
"ag-grid-community": "36.0.0",
"ag-grid-react": "36.0.0",
"ag-grid-community": "36.0.1",
"ag-grid-react": "36.0.1",
"antd": "^6.5.1",
"chrono-node": "^2.10.0",
"chrono-node": "^2.10.1",
"classnames": "^2.2.5",
"content-disposition": "^2.0.1",
"d3-scale": "^4.0.2",
@@ -187,7 +187,7 @@
"geostyler-style": "11.0.2",
"geostyler-wfs-parser": "^3.0.1",
"google-auth-library": "^10.9.0",
"immer": "^11.1.11",
"immer": "^11.1.15",
"interweave": "^13.1.1",
"jquery": "^4.0.0",
"js-levenshtein": "^1.1.6",
@@ -196,17 +196,17 @@
"lodash": "^4.18.1",
"lodash-es": "^4.18.1",
"mapbox-gl": "^3.26.0",
"markdown-to-jsx": "^9.8.2",
"markdown-to-jsx": "^9.9.0",
"match-sorter": "^8.3.0",
"memoize-one": "^6.0.0",
"mousetrap": "^1.6.5",
"mustache": "^4.2.0",
"nanoid": "^5.1.16",
"nanoid": "^6.0.0",
"ol": "^10.9.0",
"query-string": "9.4.1",
"re-resizable": "^6.11.2",
"react": "^18.3.0",
"react-arborist": "^3.13.2",
"react-arborist": "^3.15.0",
"react-checkbox-tree": "^1.8.0",
"react-diff-viewer-continued": "^4.4.0",
"react-dnd": "^11.1.3",
@@ -263,16 +263,16 @@
"@babel/types": "^7.29.7",
"@emotion/babel-plugin": "^11.13.5",
"@emotion/jest": "^11.14.2",
"@formatjs/intl-durationformat": "^0.10.17",
"@formatjs/intl-durationformat": "^0.10.18",
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@playwright/test": "^1.61.1",
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
"@storybook/addon-docs": "10.5.0",
"@storybook/addon-links": "10.5.0",
"@storybook/react-webpack5": "10.5.0",
"@storybook/addon-docs": "10.5.3",
"@storybook/addon-links": "10.5.3",
"@storybook/react-webpack5": "10.5.3",
"@storybook/test-runner": "0.24.4",
"@svgr/webpack": "^8.1.0",
"@swc/core": "^1.15.43",
"@swc/core": "^1.15.46",
"@swc/plugin-emotion": "^14.15.0",
"@swc/plugin-transform-imports": "^12.5.0",
"@testing-library/dom": "^9.3.4",
@@ -300,13 +300,13 @@
"@types/rison": "0.1.0",
"@types/tinycolor2": "^1.4.3",
"@types/unzipper": "^0.10.11",
"@typescript-eslint/eslint-plugin": "^8.64.0",
"@typescript-eslint/eslint-plugin": "^8.65.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.10.43",
"baseline-browser-mapping": "^2.10.44",
"cheerio": "1.2.0",
"concurrently": "^10.0.3",
"copy-webpack-plugin": "^14.0.0",
@@ -325,7 +325,7 @@
"eslint-plugin-prettier": "^5.5.6",
"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.0",
"eslint-plugin-storybook": "10.5.3",
"eslint-plugin-testing-library": "^7.16.2",
"eslint-plugin-theme-colors": "file:eslint-rules/eslint-plugin-theme-colors",
"fetch-mock": "^12.6.0",
@@ -340,26 +340,26 @@
"js-yaml-loader": "^1.2.2",
"jsdom": "^29.1.1",
"lerna": "^9.0.4",
"lightningcss": "^1.32.0",
"lightningcss": "^1.33.0",
"mini-css-extract-plugin": "^2.10.2",
"minimizer-webpack-plugin": "^5.6.1",
"open-cli": "^9.0.0",
"oxlint": "^1.73.0",
"oxlint": "^1.74.0",
"po2json": "^0.4.5",
"postcss-styled-syntax": "^0.7.2",
"prettier": "3.9.5",
"prettier": "3.9.6",
"prettier-plugin-packagejson": "^3.0.2",
"process": "^0.11.10",
"react-dnd-test-backend": "^16.0.1",
"react-refresh": "^0.18.0",
"react-resizable": "^4.0.2",
"redux-mock-store": "^1.5.4",
"source-map": "^0.7.6",
"source-map": "^0.8.0",
"source-map-support": "^0.5.21",
"speed-measure-webpack-plugin": "^1.6.0",
"storybook": "10.5.0",
"storybook": "10.5.3",
"style-loader": "^4.0.0",
"stylelint": "^17.14.0",
"stylelint": "^17.14.1",
"swc-loader": "^0.2.7",
"ts-jest": "^29.4.11",
"tscw-config": "^1.1.2",

View File

@@ -108,7 +108,7 @@
"@emotion/styled": "^11.14.1",
"@fontsource/ibm-plex-mono": "^5.2.7",
"@fontsource/inter": "^5.2.6",
"nanoid": "^5.0.9",
"nanoid": "*",
"react": "^18.3.0",
"react-dom": "^18.3.0",
"react-loadable": "^5.5.0",

View File

@@ -16,9 +16,21 @@
* specific language governing permissions and limitationsxw
* under the License.
*/
import { PostProcessingProphet, getXAxisLabel } from '@superset-ui/core';
import {
PostProcessingProphet,
TimeGranularity,
getXAxisColumn,
getXAxisLabel,
isAdhocColumn,
} from '@superset-ui/core';
import { PostProcessingFactory } from './types';
// Fallback grain used only when no time grain can be resolved from the form
// data, query object, or x-axis column. Matches the `time_grain_sqla` control
// default in sharedControls so forecasting stays functional rather than failing
// with an opaque backend error.
const DEFAULT_TIME_GRAIN = TimeGranularity.DAY;
/* eslint-disable @typescript-eslint/no-unused-vars */
export const prophetOperator: PostProcessingFactory<PostProcessingProphet> = (
formData,
@@ -26,10 +38,25 @@ export const prophetOperator: PostProcessingFactory<PostProcessingProphet> = (
) => {
const xAxisLabel = getXAxisLabel(formData);
if (formData.forecastEnabled && xAxisLabel) {
// The effective time grain can live in several places depending on how the
// chart was configured. Prefer, in order:
// 1. the grain popover on an adhoc x-axis column (generic x-axis),
// 2. the grain resolved onto the query object's extras (picks up
// dashboard-applied grains and the panel control),
// 3. the `time_grain_sqla` panel control on the form data directly.
// Fall back to a daily grain so a saved/dashboard chart with the grain
// cleared to "None" still forecasts instead of raising a backend error.
const xAxisColumn = getXAxisColumn(formData);
const timeGrain =
(isAdhocColumn(xAxisColumn) &&
(xAxisColumn.timeGrain as TimeGranularity)) ||
queryObject.extras?.time_grain_sqla ||
formData.time_grain_sqla ||
DEFAULT_TIME_GRAIN;
return {
operation: 'prophet',
options: {
time_grain: formData.time_grain_sqla,
time_grain: timeGrain,
periods: parseInt(formData.forecastPeriods, 10),
confidence_interval: parseFloat(formData.forecastInterval),
yearly_seasonality: formData.forecastSeasonalityYearly,

View File

@@ -43,6 +43,11 @@ const queryObject: QueryObject = {
granularity: 'P1Y',
};
// A chart whose Time Grain control was cleared to "None": form_data has no
// `time_grain_sqla` key at all (SC-113749).
const formDataWithoutGrain: SqlaFormData = { ...formData };
delete formDataWithoutGrain.time_grain_sqla;
test('should skip prophetOperator', () => {
expect(prophetOperator(formData, queryObject)).toEqual(undefined);
});
@@ -137,3 +142,103 @@ test('should do prophetOperator over adhoc column', () => {
},
});
});
test('should fall back to a daily grain when no time grain is resolvable', () => {
// Regression for SC-113749: a saved/dashboard chart with the Time Grain
// control cleared to "None" has no `time_grain_sqla` in form_data. Prior to
// the fix this emitted `time_grain: undefined`, which `JSON.stringify` drops,
// causing the backend `prophet()` call to raise a raw `TypeError`.
expect(
prophetOperator(
{
...formDataWithoutGrain,
granularity_sqla: 'time_column',
forecastEnabled: true,
forecastPeriods: '3',
forecastInterval: '5',
forecastSeasonalityYearly: true,
forecastSeasonalityWeekly: false,
forecastSeasonalityDaily: false,
},
{ ...queryObject, extras: {} },
),
).toEqual({
operation: 'prophet',
options: {
time_grain: 'P1D',
periods: 3.0,
confidence_interval: 5.0,
yearly_seasonality: true,
weekly_seasonality: false,
daily_seasonality: false,
index: DTTM_ALIAS,
},
});
});
test('should resolve the time grain from the adhoc x-axis column', () => {
// With the generic x-axis, the grain lives on the column's popover
// (`timeGrain`) rather than the `time_grain_sqla` panel control.
expect(
prophetOperator(
{
...formDataWithoutGrain,
x_axis: {
label: 'ds',
expressionType: 'SQL',
sqlExpression: 'ds',
timeGrain: 'P1M',
},
forecastEnabled: true,
forecastPeriods: '3',
forecastInterval: '5',
forecastSeasonalityYearly: true,
forecastSeasonalityWeekly: false,
forecastSeasonalityDaily: false,
},
{ ...queryObject, extras: {} },
),
).toEqual({
operation: 'prophet',
options: {
time_grain: 'P1M',
periods: 3.0,
confidence_interval: 5.0,
yearly_seasonality: true,
weekly_seasonality: false,
daily_seasonality: false,
index: 'ds',
},
});
});
test('should resolve the time grain from the query object extras', () => {
// Dashboard-applied grains (e.g. via native filters) land in
// `queryObject.extras.time_grain_sqla` even when form_data has none.
expect(
prophetOperator(
{
...formDataWithoutGrain,
granularity_sqla: 'time_column',
forecastEnabled: true,
forecastPeriods: '3',
forecastInterval: '5',
forecastSeasonalityYearly: true,
forecastSeasonalityWeekly: false,
forecastSeasonalityDaily: false,
},
{ ...queryObject, extras: { time_grain_sqla: 'P1W' } },
),
).toEqual({
operation: 'prophet',
options: {
time_grain: 'P1W',
periods: 3.0,
confidence_interval: 5.0,
yearly_seasonality: true,
weekly_seasonality: false,
daily_seasonality: false,
index: DTTM_ALIAS,
},
});
});

View File

@@ -31,8 +31,8 @@
"@types/json-bigint": "^1.0.4",
"@visx/responsive": "^4.0.0",
"ace-builds": "^1.44.0",
"ag-grid-community": "36.0.0",
"ag-grid-react": "36.0.0",
"ag-grid-community": "36.0.1",
"ag-grid-react": "36.0.1",
"brace": "^0.11.1",
"classnames": "^2.5.1",
"core-js": "^3.49.0",
@@ -101,7 +101,7 @@
"@types/react-window": "^1.8.8",
"@types/tinycolor2": "*",
"antd": "^6.0.0",
"nanoid": "^5.0.9",
"nanoid": "*",
"react": "^18.3.0",
"react-dom": "^18.3.0",
"react-loadable": "^5.5.0",

View File

@@ -17,7 +17,7 @@
* under the License.
*/
import { render, waitFor, configure } from '@testing-library/react';
import { render, waitFor, configure, act } from '@testing-library/react';
import '@testing-library/jest-dom';
import StatefulChart from './StatefulChart';
import getChartControlPanelRegistry from '../registries/ChartControlPanelRegistrySingleton';
@@ -67,19 +67,19 @@ beforeEach(() => {
jest.clearAllMocks();
// Setup default registry mocks
(getChartMetadataRegistry as any).mockReturnValue({
jest.mocked(getChartMetadataRegistry).mockReturnValue({
get: jest.fn().mockReturnValue({
useLegacyApi: false,
}),
});
} as unknown as ReturnType<typeof getChartMetadataRegistry>);
(getChartBuildQueryRegistry as any).mockReturnValue({
jest.mocked(getChartBuildQueryRegistry).mockReturnValue({
get: jest.fn().mockResolvedValue(null),
});
} as unknown as ReturnType<typeof getChartBuildQueryRegistry>);
(getChartControlPanelRegistry as any).mockReturnValue({
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
get: jest.fn().mockReturnValue(null),
});
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
// Mock ChartClient constructor
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
@@ -113,9 +113,9 @@ test('should refetch data when non-renderTrigger control changes', async () => {
],
};
(getChartControlPanelRegistry as any).mockReturnValue({
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
get: jest.fn().mockReturnValue(controlPanelConfig),
});
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
const { rerender } = render(
<StatefulChart formData={mockFormData} chartType="test_chart" />,
@@ -165,9 +165,9 @@ test('should NOT refetch data when only renderTrigger controls change', async ()
],
};
(getChartControlPanelRegistry as any).mockReturnValue({
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
get: jest.fn().mockReturnValue(controlPanelConfig),
});
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
const { rerender, getByTestId } = render(
<StatefulChart formData={mockFormData} chartType="test_chart" />,
@@ -201,9 +201,9 @@ test('should NOT refetch data when only renderTrigger controls change', async ()
test('should refetch when control panel config is not available', async () => {
// No control panel config available
(getChartControlPanelRegistry as any).mockReturnValue({
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
get: jest.fn().mockReturnValue(null),
});
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
const { rerender } = render(
<StatefulChart formData={mockFormData} chartType="test_chart" />,
@@ -245,9 +245,9 @@ test('should refetch when viz_type changes', async () => {
],
};
(getChartControlPanelRegistry as any).mockReturnValue({
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
get: jest.fn().mockReturnValue(controlPanelConfig),
});
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
const { rerender } = render(
<StatefulChart formData={mockFormData} chartType="test_chart" />,
@@ -299,9 +299,9 @@ test('should handle mixed renderTrigger and non-renderTrigger changes', async ()
],
};
(getChartControlPanelRegistry as any).mockReturnValue({
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
get: jest.fn().mockReturnValue(controlPanelConfig),
});
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
const { rerender } = render(
<StatefulChart formData={mockFormData} chartType="test_chart" />,
@@ -352,9 +352,9 @@ test('should handle controls with complex structure', async () => {
],
};
(getChartControlPanelRegistry as any).mockReturnValue({
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
get: jest.fn().mockReturnValue(controlPanelConfig),
});
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
const { rerender, getByTestId } = render(
<StatefulChart formData={mockFormData} chartType="test_chart" />,
@@ -404,11 +404,11 @@ test('should not refetch when formData has not changed', async () => {
test('should handle errors gracefully when accessing registry', async () => {
// Mock registry to throw an error
(getChartControlPanelRegistry as any).mockReturnValue({
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
get: jest.fn().mockImplementation(() => {
throw new Error('Registry error');
}),
});
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
const { rerender } = render(
<StatefulChart formData={mockFormData} chartType="test_chart" />,
@@ -492,9 +492,9 @@ test('should NOT refetch data when string-based renderTrigger control (zoomable)
],
};
(getChartControlPanelRegistry as any).mockReturnValue({
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
get: jest.fn().mockReturnValue(controlPanelConfig),
});
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
const formDataWithZoom = {
...mockFormData,
@@ -542,9 +542,9 @@ test('should NOT refetch data when other string-based renderTrigger controls cha
],
};
(getChartControlPanelRegistry as any).mockReturnValue({
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
get: jest.fn().mockReturnValue(controlPanelConfig),
});
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
const { rerender, getByTestId } = render(
<StatefulChart formData={mockFormData} chartType="test_chart" />,
@@ -585,9 +585,9 @@ test('should refetch when string control is NOT in RENDER_TRIGGER_SHARED_CONTROL
],
};
(getChartControlPanelRegistry as any).mockReturnValue({
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
get: jest.fn().mockReturnValue(controlPanelConfig),
});
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
const { rerender } = render(
<StatefulChart formData={mockFormData} chartType="test_chart" />,
@@ -631,9 +631,9 @@ test('should handle mixed string and object controls correctly', async () => {
],
};
(getChartControlPanelRegistry as any).mockReturnValue({
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
get: jest.fn().mockReturnValue(controlPanelConfig),
});
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
const formDataWithControls = {
...mockFormData,
@@ -687,9 +687,9 @@ test('should refetch when mixing renderTrigger string control with non-renderTri
],
};
(getChartControlPanelRegistry as any).mockReturnValue({
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
get: jest.fn().mockReturnValue(controlPanelConfig),
});
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
const formDataWithZoom = {
...mockFormData,
@@ -720,6 +720,435 @@ test('should refetch when mixing renderTrigger string control with non-renderTri
});
});
test('resolves async (202) responses via the injected handleAsyncChartData hook', async () => {
const asyncJob = {
channel_id: 'c1',
job_id: 'j1',
status: 'running',
result_url: '/api/v1/chart/data/abc',
};
mockChartClient.client.post.mockResolvedValue({
response: { status: 202 } as Response,
json: asyncJob,
});
const handleAsyncChartData = jest
.fn()
.mockResolvedValue([{ data: 'async result' }]);
const { getByTestId } = render(
<StatefulChart
formData={mockFormData}
chartType="test_chart"
hooks={{ handleAsyncChartData }}
/>,
);
await waitFor(() => {
expect(handleAsyncChartData).toHaveBeenCalledTimes(1);
});
// Delegates the raw response + job metadata (and useLegacyApi + abort signal)
expect(handleAsyncChartData).toHaveBeenCalledWith(
{ status: 202 },
asyncJob,
false,
expect.any(AbortSignal),
);
// Chart renders once the async data resolves
await waitFor(() => {
expect(getByTestId('super-chart')).toBeInTheDocument();
});
});
test('errors on async (202) response when no async handler is provided', async () => {
mockChartClient.client.post.mockResolvedValue({
response: { status: 202 } as Response,
json: { job_id: 'j1', channel_id: 'c1', status: 'running' },
});
const onError = jest.fn();
const { findByText } = render(
<StatefulChart
formData={mockFormData}
chartType="test_chart"
onError={onError}
/>,
);
// Fails loudly instead of rendering the job metadata as empty data
expect(await findByText(/async handler/i)).toBeInTheDocument();
await waitFor(() => {
expect(onError).toHaveBeenCalledTimes(1);
});
});
test('renders synchronous (200) responses that include a response object', async () => {
mockChartClient.client.post.mockResolvedValue({
response: { status: 200 } as Response,
json: [{ result: [{ data: 'sync result' }] }],
});
const { getByTestId } = render(
<StatefulChart formData={mockFormData} chartType="test_chart" />,
);
await waitFor(() => {
expect(getByTestId('super-chart')).toBeInTheDocument();
});
// Synchronous path: no async handler needed, single request
expect(mockChartClient.client.post).toHaveBeenCalledTimes(1);
});
test('wraps the legacy async body as { result: [body] } for the async handler', async () => {
const legacyBody = { job_id: 'j1', channel_id: 'c1', status: 'running' };
mockChartClient.client.post.mockResolvedValue({
response: { status: 202 } as Response,
json: legacyBody,
});
// Force the legacy API path for this viz type
jest.mocked(getChartMetadataRegistry).mockReturnValue({
get: jest.fn().mockReturnValue({ useLegacyApi: true }),
} as unknown as ReturnType<typeof getChartMetadataRegistry>);
const handleAsyncChartData = jest
.fn()
.mockResolvedValue([{ data: 'legacy result' }]);
const { getByTestId } = render(
<StatefulChart
formData={mockFormData}
chartType="test_chart"
hooks={{ handleAsyncChartData }}
/>,
);
await waitFor(() => {
expect(handleAsyncChartData).toHaveBeenCalledTimes(1);
});
// Legacy body must be wrapped to match the V1 response signature
expect(handleAsyncChartData).toHaveBeenCalledWith(
{ status: 202 },
{ result: [legacyBody] },
true,
expect.any(AbortSignal),
);
await waitFor(() => {
expect(getByTestId('super-chart')).toBeInTheDocument();
});
});
test('does not apply a superseded async response over a newer one', async () => {
mockChartClient.client.post.mockResolvedValue({
response: { status: 202 } as Response,
json: { job_id: 'j', channel_id: 'c' },
});
let resolveFirst: (data: unknown) => void = () => {};
let resolveSecond: (data: unknown) => void = () => {};
const handleAsyncChartData = jest
.fn()
.mockImplementationOnce(
() =>
new Promise(resolve => {
resolveFirst = resolve;
}),
)
.mockImplementationOnce(
() =>
new Promise(resolve => {
resolveSecond = resolve;
}),
);
const onLoad = jest.fn();
const { rerender } = render(
<StatefulChart
formData={mockFormData}
chartType="test_chart"
hooks={{ handleAsyncChartData }}
onLoad={onLoad}
/>,
);
await waitFor(() => {
expect(handleAsyncChartData).toHaveBeenCalledTimes(1);
});
// A newer request supersedes the first (viz_type change forces a refetch)
const newFormData = { ...mockFormData, viz_type: 'different_chart' };
rerender(
<StatefulChart
formData={newFormData}
chartType="different_chart"
hooks={{ handleAsyncChartData }}
onLoad={onLoad}
/>,
);
await waitFor(() => {
expect(handleAsyncChartData).toHaveBeenCalledTimes(2);
});
// Resolve the newer request first, then the stale one
await act(async () => {
resolveSecond([{ data: 'B' }]);
});
await act(async () => {
resolveFirst([{ data: 'A' }]);
});
await waitFor(() => {
expect(onLoad).toHaveBeenCalledWith([{ data: 'B' }]);
});
// The stale (superseded) response must not overwrite the newer one
expect(onLoad).not.toHaveBeenCalledWith([{ data: 'A' }]);
expect(onLoad).toHaveBeenCalledTimes(1);
});
test('preserves the detailed message from an async (array) rejection', async () => {
mockChartClient.client.post.mockResolvedValue({
response: { status: 202 } as Response,
json: { job_id: 'j', channel_id: 'c' },
});
const handleAsyncChartData = jest
.fn()
.mockRejectedValue([{ error: 'Async query failed: table not found' }]);
const onError = jest.fn();
const { findByText } = render(
<StatefulChart
formData={mockFormData}
chartType="test_chart"
hooks={{ handleAsyncChartData }}
onError={onError}
/>,
);
// The detailed message survives instead of collapsing to the generic one
expect(await findByText(/table not found/i)).toBeInTheDocument();
await waitFor(() => {
expect(onError).toHaveBeenCalledTimes(1);
expect(onError.mock.calls[0][0].message).toContain('table not found');
});
});
test('refetches with the latest formData rather than the initial props', async () => {
mockChartClient.client.post.mockResolvedValue({
response: { status: 200 } as Response,
json: [{ result: [{ data: 'x' }] }],
});
const { rerender } = render(
<StatefulChart
formData={{ ...mockFormData, metrics: ['metric_v1'] }}
chartType="test_chart"
/>,
);
await waitFor(() => {
expect(mockChartClient.client.post).toHaveBeenCalledTimes(1);
});
// Change a data-affecting control -> triggers a refetch
rerender(
<StatefulChart
formData={{ ...mockFormData, metrics: ['metric_v2'] }}
chartType="test_chart"
/>,
);
await waitFor(() => {
expect(mockChartClient.client.post).toHaveBeenCalledTimes(2);
});
// The second request must carry the updated formData, not the initial props
const secondRequestConfig = mockChartClient.client.post.mock.calls[1][0];
expect(JSON.stringify(secondRequestConfig)).toContain('metric_v2');
expect(JSON.stringify(secondRequestConfig)).not.toContain('metric_v1');
});
test('does not revert a render-only change when a slow async request resolves', async () => {
mockChartClient.client.post.mockResolvedValue({
response: { status: 202 } as Response,
json: { job_id: 'j', channel_id: 'c' },
});
// color_scheme is a renderTrigger control -> its change does not refetch
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
get: jest.fn().mockReturnValue({
controlPanelSections: [
{
controlSetRows: [
[{ name: 'color_scheme', config: { renderTrigger: true } }],
],
},
],
}),
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
let resolveAsync: (data: unknown) => void = () => {};
const handleAsyncChartData = jest.fn(
() =>
new Promise(resolve => {
resolveAsync = resolve;
}),
);
const { rerender, getByTestId } = render(
<StatefulChart
formData={{ ...mockFormData, color_scheme: 'scheme_one' }}
chartType="test_chart"
hooks={{ handleAsyncChartData }}
/>,
);
await waitFor(() => {
expect(handleAsyncChartData).toHaveBeenCalledTimes(1);
});
// Render-only change while the async request is still pending (no refetch)
rerender(
<StatefulChart
formData={{ ...mockFormData, color_scheme: 'scheme_two' }}
chartType="test_chart"
hooks={{ handleAsyncChartData }}
/>,
);
expect(handleAsyncChartData).toHaveBeenCalledTimes(1);
// The stale request resolves; it must not revert color_scheme back
await act(async () => {
resolveAsync([{ data: 'd' }]);
});
await waitFor(() => {
expect(getByTestId('super-chart')).toHaveTextContent('scheme_two');
});
expect(getByTestId('super-chart')).not.toHaveTextContent('scheme_one');
});
test('passes an abort signal to the async handler and aborts it on unmount', async () => {
mockChartClient.client.post.mockResolvedValue({
response: { status: 202 } as Response,
json: { job_id: 'j', channel_id: 'c' },
});
// Typed with a rest param so mock.calls is indexable (the 4th arg is the signal)
const handleAsyncChartData = jest.fn(
(..._args: unknown[]) => new Promise<never>(() => {}), // never resolves
);
const { unmount } = render(
<StatefulChart
formData={mockFormData}
chartType="test_chart"
hooks={{ handleAsyncChartData }}
/>,
);
await waitFor(() => {
expect(handleAsyncChartData).toHaveBeenCalledTimes(1);
});
const signal = handleAsyncChartData.mock.calls[0][3] as AbortSignal;
expect(signal).toBeInstanceOf(AbortSignal);
expect(signal.aborted).toBe(false);
// Unmounting aborts the signal so a signal-aware handler can stop polling
unmount();
expect(signal.aborted).toBe(true);
});
test('suppresses stale error state from a superseded request', async () => {
mockChartClient.client.post.mockResolvedValue({
response: { status: 202 } as Response,
json: { job_id: 'j', channel_id: 'c' },
});
let rejectFirst: (err: unknown) => void = () => {};
const handleAsyncChartData = jest
.fn()
.mockImplementationOnce(
() =>
new Promise((_resolve, reject) => {
rejectFirst = reject;
}),
)
.mockImplementationOnce(() => new Promise(() => {})); // newer request stays pending
const onError = jest.fn();
const { rerender } = render(
<StatefulChart
formData={mockFormData}
chartType="test_chart"
hooks={{ handleAsyncChartData }}
onError={onError}
/>,
);
await waitFor(() => {
expect(handleAsyncChartData).toHaveBeenCalledTimes(1);
});
// Supersede the first request (aborts its controller)
rerender(
<StatefulChart
formData={{ ...mockFormData, viz_type: 'different_chart' }}
chartType="different_chart"
hooks={{ handleAsyncChartData }}
onError={onError}
/>,
);
await waitFor(() => {
expect(handleAsyncChartData).toHaveBeenCalledTimes(2);
});
// The stale request now fails; its error must not surface
await act(async () => {
rejectFirst(new Error('stale failure'));
});
expect(onError).not.toHaveBeenCalled();
});
test('does not publish stale data when switching from chartId to formData mode', async () => {
mockChartClient.loadFormData.mockResolvedValue({ ...mockFormData });
mockChartClient.client.post.mockResolvedValue({
response: { status: 202 } as Response,
json: { job_id: 'j', channel_id: 'c' },
});
let resolveFirst: (data: unknown) => void = () => {};
const handleAsyncChartData = jest
.fn()
.mockImplementationOnce(
() =>
new Promise(resolve => {
resolveFirst = resolve;
}),
)
.mockImplementationOnce(() => new Promise(() => {}));
const onLoad = jest.fn();
// Start in chartId mode
const { rerender } = render(
<StatefulChart
chartId={1}
chartType="test_chart"
hooks={{ handleAsyncChartData }}
onLoad={onLoad}
/>,
);
await waitFor(() => {
expect(handleAsyncChartData).toHaveBeenCalledTimes(1);
});
// Switch to direct-formData mode
rerender(
<StatefulChart
formData={{ ...mockFormData, metrics: ['m'] }}
chartType="test_chart"
hooks={{ handleAsyncChartData }}
onLoad={onLoad}
/>,
);
await waitFor(() => {
expect(handleAsyncChartData).toHaveBeenCalledTimes(2);
});
// The stale chartId-mode request resolves; its data must not be published
await act(async () => {
resolveFirst([{ data: 'stale' }]);
});
expect(onLoad).not.toHaveBeenCalledWith([{ data: 'stale' }]);
});
test('should display error message when HTTP request fails with Response object', async () => {
const errorBody = JSON.stringify({ message: 'Error: division by zero' });
const mockResponse = new Response(errorBody, {

View File

@@ -18,17 +18,19 @@
*/
import { useState, useEffect, useRef, useCallback } from 'react';
import { isEqual } from 'lodash';
import { ParentSize } from '@visx/responsive';
import { t } from '@apache-superset/core/translation';
import {
QueryFormData,
QueryData,
JsonObject,
SupersetClientInterface,
buildQueryContext,
RequestConfig,
getClientErrorObject,
ensureIsArray,
} from '../..';
import type { HandlerFunction } from '../types/Base';
import { Loading } from '../../components/Loading';
import ChartClient from '../clients/ChartClient';
import getChartBuildQueryRegistry from '../registries/ChartBuildQueryRegistrySingleton';
@@ -189,6 +191,12 @@ export default function StatefulChart(props: StatefulChartProps) {
const chartClientRef = useRef<ChartClient>();
const abortControllerRef = useRef<AbortController>();
// fetchData is memoized with an empty dep list, so it would otherwise close
// over the first render's props. Keep the latest props in a ref so refetches
// (triggered by updated filters/formData/overrides) use current values.
const propsRef = useRef(props);
propsRef.current = props;
// Initialize chart client
if (!chartClientRef.current) {
chartClientRef.current = new ChartClient({ client: props.client });
@@ -199,20 +207,48 @@ export default function StatefulChart(props: StatefulChartProps) {
chartId,
formData: propsFormData,
formDataOverrides,
onError,
onLoad,
chartType,
force,
timeout,
} = props;
hooks,
} = propsRef.current;
// Cancel any in-flight requests
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
// Create new abort controller
abortControllerRef.current = new AbortController();
// Create new abort controller (kept in a local so we can detect when this
// request has been superseded by a newer one, even across async awaits).
const controller = new AbortController();
abortControllerRef.current = controller;
// A request is superseded if it was aborted, or if the props changed in a
// data-affecting way since it began - including switching between chartId
// and direct-formData modes. Props are captured during render but the abort
// happens in a passive effect, so the abort signal alone can let a stale
// success or error slip through in the render->effect gap. This mirrors the
// effect's own refetch decision; render-only changes are intentionally not
// treated as superseding.
const isSuperseded = () => {
if (controller.signal.aborted) {
return true;
}
const latest = propsRef.current;
const vizTypeForCompare = latest.formData?.viz_type || latest.chartType;
return (
latest.chartId !== chartId ||
// Deep compare overrides: callers commonly pass a fresh object with the
// same contents each render, which should not count as superseding.
!isEqual(latest.formDataOverrides, formDataOverrides) ||
latest.force !== force ||
Boolean(propsFormData) !== Boolean(latest.formData) ||
(!!propsFormData &&
!!latest.formData &&
latest.formData !== propsFormData &&
shouldRefetchData(propsFormData, latest.formData, vizTypeForCompare))
);
};
setStatus('loading');
setError(undefined);
@@ -224,7 +260,7 @@ export default function StatefulChart(props: StatefulChartProps) {
// Load formData from chartId
finalFormData = await chartClientRef.current!.loadFormData(
{ sliceId: chartId },
{ signal: abortControllerRef.current.signal } as RequestConfig,
{ signal: controller.signal } as RequestConfig,
);
} else if (propsFormData) {
// Use provided formData
@@ -267,7 +303,7 @@ export default function StatefulChart(props: StatefulChartProps) {
const requestConfig: RequestConfig = {
endpoint,
signal: abortControllerRef.current.signal,
signal: controller.signal,
...(timeout && { timeout: timeout * 1000 }),
};
@@ -285,39 +321,136 @@ export default function StatefulChart(props: StatefulChartProps) {
};
}
const response = await chartClientRef.current!.client.post(requestConfig);
let responseData = Array.isArray(response.json)
? response.json
: [response.json];
const clientResponse =
await chartClientRef.current!.client.post(requestConfig);
// Handle the nested result structure from the new API
if (!useLegacyApi && responseData[0]?.result) {
responseData = responseData[0].result;
}
setStatus('loaded');
setData(responseData);
setFormData(finalFormData);
if (onLoad) {
onLoad(responseData);
}
} catch (err) {
// Ignore abort errors
if ((err as Error).name === 'AbortError') {
// A newer request may have started while the POST was in flight; discard
// this stale response so it can't overwrite the newer chart data.
if (isSuperseded()) {
return;
}
const parsedError = await getClientErrorObject(
err as Parameters<typeof getClientErrorObject>[0],
);
const errorMessage =
parsedError.error || parsedError.message || 'An error occurred';
const rawResponse = clientResponse.response as Response | undefined;
let responseData: QueryData[];
if (rawResponse?.status === 202) {
// With GLOBAL_ASYNC_QUERIES the query is dispatched to a Celery worker
// and the 202 body is job metadata (channel_id, job_id, result_url),
// not chart data. Delegate to the injected handler, which polls the
// async event channel and resolves the cached results. Without a
// handler we fail loudly rather than rendering the job metadata as if
// it were an (empty) result set.
if (!hooks?.handleAsyncChartData) {
throw new Error(
'Received an async chart data response (HTTP 202) but no async ' +
'handler was provided, so results cannot be retrieved. Wire up ' +
'the async handler or disable GLOBAL_ASYNC_QUERIES for this chart.',
);
}
// The async handler (handleChartDataResponse) expects the V1 chart data
// response signature. The legacy endpoint returns a flat body, so wrap
// it as { result: [body] } exactly like legacyChartDataRequest does for
// the standard chart path; the V1 body is already correctly shaped.
const asyncPayload = useLegacyApi
? ({ result: [clientResponse.json] } as JsonObject)
: (clientResponse.json as JsonObject);
responseData = ensureIsArray(
await hooks.handleAsyncChartData(
rawResponse,
asyncPayload,
useLegacyApi,
controller.signal,
),
);
// Async results can resolve well after a newer request began polling.
if (isSuperseded()) {
return;
}
} else {
const rows = (
Array.isArray(clientResponse.json)
? clientResponse.json
: [clientResponse.json]
) as JsonObject[];
// Handle the nested result structure from the new API
responseData = (
!useLegacyApi && rows[0]?.result ? rows[0].result : rows
) as QueryData[];
}
// Don't pair this request's data with newer props or fire a stale onLoad
// if it has been superseded (see isSuperseded).
if (isSuperseded()) {
return;
}
const latestProps = propsRef.current;
setStatus('loaded');
setData(responseData);
// Render the resolved data with the latest formData so a render-only
// change made while the request was in flight isn't reverted.
setFormData(
latestProps.formData
? {
...latestProps.formData,
...latestProps.formDataOverrides,
viz_type: finalFormData.viz_type,
}
: finalFormData,
);
// Read onLoad from the latest props (like setFormData above) so a stale
// callback captured at request start isn't invoked.
if (latestProps.onLoad) {
latestProps.onLoad(responseData);
}
} catch (err) {
// Ignore aborted requests, whether they threw AbortError or were
// superseded by a newer request (including the render->effect gap).
if ((err as Error)?.name === 'AbortError' || isSuperseded()) {
return;
}
// waitForAsyncData rejects with an array of already-parsed client-error
// objects; unwrap the first element so its detailed message survives.
const rawError = Array.isArray(err) ? err[0] : err;
let errorMessage: string | undefined;
if (
rawError &&
typeof rawError === 'object' &&
!(rawError instanceof Error) &&
!(rawError instanceof Response) &&
typeof (rawError as { error?: unknown }).error === 'string'
) {
// Already a parsed client-error object (e.g. from the async handler);
// getClientErrorObject would discard its `error` field, so read it here.
const parsed = rawError as { error?: string; message?: string };
errorMessage = parsed.error || parsed.message;
} else {
const parsedError = await getClientErrorObject(
rawError as Parameters<typeof getClientErrorObject>[0],
);
errorMessage = parsedError.error || parsedError.message;
}
const errorObj = new Error(errorMessage || 'An error occurred');
// The request may have been superseded while its error response was being
// parsed above (or in the render->effect gap before its abort ran); don't
// set stale error state or call onError in that case.
if (isSuperseded()) {
return;
}
const errorObj = new Error(errorMessage);
setStatus('error');
setError(errorObj);
// Read onError from the latest props so a stale callback captured at
// request start isn't invoked.
const { onError } = propsRef.current;
if (onError) {
onError(errorObj);
}
@@ -481,7 +614,7 @@ export default function StatefulChart(props: StatefulChartProps) {
enableNoResults={enableNoResults}
noResults={NoDataComponent && <NoDataComponent />}
onRenderSuccess={onRenderSuccess}
onRenderFailure={onRenderFailure as HandlerFunction | undefined}
onRenderFailure={onRenderFailure}
hooks={hooks}
/>
);

View File

@@ -66,6 +66,18 @@ type Hooks = {
setTooltip?: HandlerFunction;
/* handle legend scroll changes */
onLegendScroll?: HandlerFunction;
/**
* Resolve an async chart-data response (HTTP 202 from GLOBAL_ASYNC_QUERIES).
* Injected by the app so components in this package (e.g. Matrixify's
* StatefulChart) can await async results without importing app-level
* async-event middleware. Returns the resolved query results.
*/
handleAsyncChartData?: (
response: Response,
json: JsonObject,
useLegacyApi?: boolean,
signal?: AbortSignal,
) => Promise<QueryData[]> | QueryData[];
} & PlainObject;
/**

View File

@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { render, screen, userEvent, fireEvent } from '@superset-ui/core/spec';
import { render, screen, userEvent } from '@superset-ui/core/spec';
import { Icons } from '@superset-ui/core/components/Icons';
import { ActionButton } from '.';
@@ -45,18 +45,6 @@ test('calls onClick when clicked', async () => {
expect(onClick).toHaveBeenCalledTimes(1);
});
test('calls onClick when activated with the keyboard', () => {
const onClick = jest.fn();
render(<ActionButton {...defaultProps} onClick={onClick} />);
const button = screen.getByRole('button');
fireEvent.keyDown(button, { key: 'Enter' });
expect(onClick).toHaveBeenCalledTimes(1);
fireEvent.keyDown(button, { key: ' ' });
expect(onClick).toHaveBeenCalledTimes(2);
});
test('renders with tooltip when tooltip prop is provided', async () => {
const tooltipText = 'This is a tooltip';
render(<ActionButton {...defaultProps} tooltip={tooltipText} />);
@@ -115,6 +103,6 @@ test('has proper accessibility attributes', () => {
render(<ActionButton {...defaultProps} />);
const button = screen.getByRole('button');
expect(button).toHaveAttribute('tabIndex', '0');
expect(button).toHaveAttribute('role', 'button');
expect(button.tagName).toBe('BUTTON');
expect(button).toHaveAttribute('type', 'button');
});

View File

@@ -17,8 +17,8 @@
* under the License.
*/
import { handleKeyboardActivation } from '../../utils';
import type { ReactElement, ReactNode } from 'react';
import cx from 'classnames';
import { Tooltip, type TooltipPlacement } from '@superset-ui/core/components';
import { css, useTheme } from '@apache-superset/core/theme';
@@ -28,6 +28,9 @@ export interface ActionProps {
placement?: TooltipPlacement;
icon: ReactNode;
onClick: () => void;
className?: string;
disabled?: boolean;
dataTest?: string;
}
export const ActionButton = ({
@@ -36,30 +39,45 @@ export const ActionButton = ({
placement,
icon,
onClick,
className,
disabled = false,
dataTest,
}: ActionProps) => {
const theme = useTheme();
const actionButton = (
<span
role="button"
tabIndex={0}
<button
type="button"
aria-disabled={disabled}
aria-label={typeof tooltip === 'string' ? tooltip : label}
css={css`
appearance: none;
border: none;
background: none;
padding: 0;
margin: 0;
font: inherit;
line-height: 1;
display: inline-flex;
align-items: center;
cursor: pointer;
color: ${theme.colorIcon};
margin-right: ${theme.sizeUnit}px;
&:hover {
&:not(.disabled):hover {
path {
fill: ${theme.colorPrimary};
}
}
&.disabled {
color: ${theme.colorTextDisabled};
cursor: not-allowed;
}
`}
className="action-button"
data-test={label}
onClick={onClick}
onKeyDown={onClick ? handleKeyboardActivation(onClick) : undefined}
className={cx('action-button', className, { disabled })}
data-test={dataTest ?? label}
onClick={disabled ? undefined : onClick}
>
{icon}
</span>
</button>
);
const tooltipId = `${label.replaceAll(' ', '-').toLowerCase()}-tooltip`;

View File

@@ -21,7 +21,11 @@ import type { ButtonGroupProps } from './types';
export function ButtonGroup(props: ButtonGroupProps) {
const { className, children } = props;
return (
// role="group" is the correct ARIA pattern for a generic button toolbar;
// the suggested native tags (fieldset, etc.) carry unrelated form
// semantics and unwanted default browser styling.
<div
// eslint-disable-next-line jsx-a11y/prefer-tag-over-role
role="group"
className={className}
css={{

View File

@@ -134,6 +134,10 @@ const ImageContainer = ({
? imageMap[image as keyof typeof imageMap]
: image;
return (
// Groups Empty's SVG illustration + description into one accessible
// image; can't be a literal <img> since it's a component tree, not an
// image file reference.
// eslint-disable-next-line jsx-a11y/prefer-tag-over-role
<div role="img" aria-label="empty">
<Empty
description={false}

View File

@@ -25,8 +25,12 @@ import { Icons } from '@superset-ui/core/components/Icons';
import { Tooltip } from '../Tooltip';
import type { FaveStarProps } from './types';
const StyledLink = styled.a`
const StyledLink = styled.button`
${({ theme }) => css`
appearance: none;
border: none;
background: none;
font: inherit;
font-size: ${theme.fontSizeXL}px;
display: flex;
padding: 0 0 0 ${theme.sizeUnit * 2}px;
@@ -55,12 +59,10 @@ export const FaveStar = ({
const content = (
<StyledLink
href="#"
type="button"
onClick={onClick}
className="fave-unfave-icon"
data-test="fave-unfave-icon"
role="button"
tabIndex={0}
>
{isStarred ? (
<Icons.StarFilled

View File

@@ -102,6 +102,10 @@ export const LabeledErrorBoundInput = ({
</Tooltip>
)
}
// input[type=password] doesn't get an implicit "textbox" role
// (unlike other text inputs), so this explicit override is
// needed for it to be discoverable via role-based queries/AT.
// eslint-disable-next-line jsx-a11y/prefer-tag-over-role
role="textbox"
/>
) : renderAsTextArea ? (

View File

@@ -79,8 +79,12 @@ const IconButton: React.FC<IconButtonProps> = ({
};
return (
// antd's Card renders a fixed <div> (no polymorphic tag support) with
// its own rich internal layout (cover/title/tooltip); that doesn't map
// onto a native <button>, so role="button" is used instead.
<Card
hoverable
// eslint-disable-next-line jsx-a11y/prefer-tag-over-role
role="button"
tabIndex={0}
aria-label={buttonText}

View File

@@ -99,6 +99,10 @@ export function Loading({
$spinnerHeight="auto"
$opacity={opacity}
className={cls('loading', position, className)}
// role="status" is the standard WAI-ARIA live-region pattern for a
// loading spinner; <output> (the suggested tag) is for form
// calculation results, not a fit here.
// eslint-disable-next-line jsx-a11y/prefer-tag-over-role
role="status"
aria-live="polite"
aria-label={t('Loading')}

View File

@@ -89,6 +89,7 @@ const StyledItem = styled.div<{
& .metadata-text {
color: ${theme.colorTextSecondary};
min-width: ${TEXT_MIN_WIDTH}px;
max-width: ${TEXT_MAX_WIDTH}px;
overflow: hidden;
text-overflow: ${collapsed ? 'unset' : 'ellipsis'};
white-space: nowrap;

View File

@@ -84,7 +84,7 @@ export const StyledModal = styled(BaseModal)<StyledModalProps>`
`
}
.ant-modal-content {
.ant-modal-container {
background-color: ${theme.colorBgContainer};
display: flex;
flex-direction: column;
@@ -196,7 +196,7 @@ export const StyledModal = styled(BaseModal)<StyledModalProps>`
height: 100%;
}
.ant-modal-content {
.ant-modal-container {
height: 100%;
.ant-modal-body {

View File

@@ -127,10 +127,15 @@ export const ModalTrigger = forwardRef(
</Button>
)}
{!isButton && (
// Generic wrapper used by 19+ callers passing arbitrary
// triggerNode content that relies on block-level <div> layout;
// swapping to <button> risks a layout regression across callers
// with no shared CSS to guide a safe reset.
<div
data-test="span-modal-trigger"
onClick={open}
onKeyDown={handleKeyboardActivation(open)}
// eslint-disable-next-line jsx-a11y/prefer-tag-over-role
role="button"
tabIndex={0}
>

View File

@@ -103,7 +103,18 @@ const PopoverDropdown = (props: PopoverDropdownProps) => {
})),
}}
>
<div role="button" css={{ display: 'flex', alignItems: 'center' }}>
<button
type="button"
css={{
appearance: 'none',
border: 'none',
background: 'none',
padding: 0,
font: 'inherit',
display: 'flex',
alignItems: 'center',
}}
>
{selected && renderButton(selected)}
<Icons.DownOutlined
iconSize="s"
@@ -112,7 +123,7 @@ const PopoverDropdown = (props: PopoverDropdownProps) => {
marginLeft: theme.sizeUnit * 0.5,
}}
/>
</div>
</button>
</Dropdown>
);
};

View File

@@ -22,7 +22,7 @@ import PopoverSection from '.';
test('renders with default props', async () => {
render(
<PopoverSection title="Title">
<div role="form" />
<form aria-label="test-form" />
</PopoverSection>,
);
expect(await screen.findByRole('form')).toBeInTheDocument();
@@ -32,7 +32,7 @@ test('renders with default props', async () => {
test('renders tooltip icon', async () => {
render(
<PopoverSection title="Title" info="Tooltip">
<div role="form" />
<form />
</PopoverSection>,
);
expect((await screen.findAllByRole('img')).length).toBe(2);
@@ -41,7 +41,7 @@ test('renders tooltip icon', async () => {
test('renders a tooltip when hovered', async () => {
render(
<PopoverSection title="Title" info="Tooltip">
<div role="form" />
<form />
</PopoverSection>,
);
await userEvent.hover(screen.getAllByRole('img')[0]);
@@ -52,7 +52,7 @@ test('calls onSelect when clicked', async () => {
const onSelect = jest.fn();
render(
<PopoverSection title="Title" onSelect={onSelect}>
<div role="form" />
<form />
</PopoverSection>,
);
await userEvent.click(await screen.findByRole('img'));

View File

@@ -16,8 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { handleKeyboardActivation } from '../../utils';
import { ReactNode, SyntheticEvent } from 'react';
import { MouseEventHandler, ReactNode } from 'react';
import { css, useTheme } from '@apache-superset/core/theme';
import { Icons } from '@superset-ui/core/components/Icons';
import { Tooltip } from '../Tooltip';
@@ -25,10 +24,7 @@ import { Tooltip } from '../Tooltip';
export interface PopoverSectionProps {
title: string;
isSelected?: boolean;
// `SyntheticEvent` (rather than `MouseEventHandler`) so the same callback
// can be reused as the keyboard-activation handler via
// `handleKeyboardActivation`, which invokes it with a `KeyboardEvent`.
onSelect?: (event: SyntheticEvent) => void;
onSelect?: MouseEventHandler<HTMLButtonElement>;
info?: string;
children?: ReactNode;
}
@@ -48,12 +44,17 @@ export default function PopoverSection({
opacity: isSelected ? 1 : 0.6,
}}
>
<div
role="button"
tabIndex={0}
<button
type="button"
onClick={onSelect}
onKeyDown={onSelect ? handleKeyboardActivation(onSelect) : undefined}
css={css`
appearance: none;
border: none;
background: none;
padding: 0;
font: inherit;
text-align: left;
width: 100%;
display: flex;
align-items: center;
cursor: ${onSelect ? 'pointer' : 'default'};
@@ -68,8 +69,9 @@ export default function PopoverSection({
margin-right: ${theme.sizeUnit}px;
`}
>
{/* role is auto-computed by BaseIconComponent as "img" since
there's no onClick, so no explicit role needed here. */}
<Icons.InfoCircleOutlined
role="img"
iconSize="s"
iconColor={theme.colorIcon}
/>
@@ -77,10 +79,9 @@ export default function PopoverSection({
)}
<Icons.CheckOutlined
iconSize="s"
role="img"
iconColor={isSelected ? theme.colorPrimary : theme.colorIcon}
/>
</div>
</button>
<div
css={css`
margin-left: ${theme.sizeUnit}px;

View File

@@ -60,18 +60,26 @@ test('should render with success icon', () => {
expect(screen.getByLabelText('check-circle')).toBeInTheDocument();
});
// The stripes are painted on antd's inner progress track, so assert the
// computed style of that element — asserting the outer container would pass
// whether or not the gradient ever reached the track.
const getTrack = (container: HTMLElement) =>
container.querySelector('.ant-progress-track') as HTMLElement;
test('should render with stripes', () => {
const stripedProps = {
...mockedProps,
striped: true,
};
const { container } = render(<ProgressBar {...stripedProps} />);
expect(container).toHaveStyle(
`background-image: 'linear-gradient(
45deg,rgba(255, 255, 255, 0.15) 25%,
transparent 25%, transparent 50%,
rgba(255, 255, 255, 0.15) 50%,
rgba(255, 255, 255, 0.15) 75%,
transparent 75%, transparent) !important'`,
expect(getComputedStyle(getTrack(container)).backgroundImage).toContain(
'linear-gradient(45deg',
);
});
test('should render without stripes by default', () => {
const { container } = render(<ProgressBar {...mockedProps} />);
expect(getComputedStyle(getTrack(container)).backgroundImage).not.toContain(
'linear-gradient',
);
});

View File

@@ -29,10 +29,10 @@ const ProgressBar = styled(({ striped, ...props }: ProgressBarProps) => (
<AntdProgress data-test="progress-bar" {...props} />
))`
position: static;
.ant-progress-inner {
.ant-progress-rail {
position: static;
}
.ant-progress-bg {
.ant-progress-track {
position: static;
${({ striped }) =>
striped &&

View File

@@ -35,7 +35,8 @@ const RefreshLabel = ({
<Tooltip title={tooltipContent}>
<Icons.SyncOutlined
iconSize="l"
role="button"
// role is auto-computed by BaseIconComponent as "button" whenever
// onClick is present (and "img" otherwise), so no explicit role here.
tabIndex={disabled ? -1 : 0}
onClick={disabled ? undefined : onClick}
css={(theme: SupersetTheme) => ({

View File

@@ -0,0 +1,121 @@
/**
* 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 { render, screen, userEvent, waitFor } from '@superset-ui/core/spec';
import { RlsBadge, type RlsBadgeProps } from '.';
const mockFilters = [
{ id: 1, name: 'Finance filter', filter_type: 'Regular', group_key: 'dept' },
{ id: 2, name: 'Base restriction', filter_type: 'Base', group_key: null },
];
const asyncRender = (props: RlsBadgeProps) =>
waitFor(() => render(<RlsBadge {...props} />));
test('renders nothing when rlsFilters is empty', async () => {
const { container } = await asyncRender({ rlsFilters: [] });
expect(container.firstChild).toBeNull();
});
test('renders a lock icon when filters exist', async () => {
await asyncRender({ rlsFilters: mockFilters });
expect(
screen.getByRole('img', { name: /row-level security/i }),
).toBeInTheDocument();
});
test('renders a tooltip with filter details when hovered', async () => {
await asyncRender({ rlsFilters: mockFilters });
await userEvent.hover(screen.getByRole('img'));
const tooltip = await screen.findByRole('tooltip');
expect(tooltip).toHaveTextContent('Row-Level Security');
expect(tooltip).toHaveTextContent('Finance filter');
expect(tooltip).toHaveTextContent('Base restriction');
});
test('shows filter type in tooltip', async () => {
await asyncRender({ rlsFilters: mockFilters });
await userEvent.hover(screen.getByRole('img'));
const tooltip = await screen.findByRole('tooltip');
expect(tooltip).toHaveTextContent('Regular');
expect(tooltip).toHaveTextContent('Base');
});
test('shows group key in tooltip when present', async () => {
await asyncRender({ rlsFilters: mockFilters });
await userEvent.hover(screen.getByRole('img'));
const tooltip = await screen.findByRole('tooltip');
expect(tooltip).toHaveTextContent('[dept]');
});
test('shows inherited suffix and summary note for inherited filters', async () => {
const inheritedFilters = [
{
id: 10,
name: 'Inherited filter',
filter_type: 'Regular',
group_key: null,
inherited: true,
},
];
await asyncRender({ rlsFilters: inheritedFilters });
await userEvent.hover(screen.getByRole('img'));
const tooltip = await screen.findByRole('tooltip');
expect(tooltip).toHaveTextContent('from underlying table');
expect(tooltip).toHaveTextContent(
'Some filters are inherited from physical tables',
);
});
test('shows roles when present', async () => {
const filtersWithRoles = [
{
id: 20,
name: 'Role filter',
filter_type: 'Regular',
group_key: null,
roles: [
{ id: 1, name: 'Admin' },
{ id: 2, name: 'Analyst' },
],
},
];
await asyncRender({ rlsFilters: filtersWithRoles });
await userEvent.hover(screen.getByRole('img'));
const tooltip = await screen.findByRole('tooltip');
expect(tooltip).toHaveTextContent('Roles');
expect(tooltip).toHaveTextContent('Admin');
expect(tooltip).toHaveTextContent('Analyst');
});
test('shows clause when present', async () => {
const filtersWithClause = [
{
id: 30,
name: 'Clause filter',
filter_type: 'Base',
group_key: null,
clause: "dept = 'Finance'",
},
];
await asyncRender({ rlsFilters: filtersWithClause });
await userEvent.hover(screen.getByRole('img'));
const tooltip = await screen.findByRole('tooltip');
expect(tooltip).toHaveTextContent('Clause');
expect(tooltip).toHaveTextContent("dept = 'Finance'");
});

View File

@@ -0,0 +1,108 @@
/**
* 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 { t } from '@apache-superset/core/translation';
import { useTheme, css } from '@apache-superset/core/theme';
import { Icons } from '@superset-ui/core/components/Icons';
import { Tooltip } from '../Tooltip';
import type { IconType } from '@superset-ui/core/components/Icons/types';
export interface RlsFilterSummary {
id: number;
name: string;
filter_type?: string;
group_key?: string | null;
inherited?: boolean;
clause?: string;
roles?: Array<{ id: number; name: string }>;
}
export interface RlsBadgeProps {
rlsFilters: RlsFilterSummary[];
size?: IconType['iconSize'];
}
export function RlsBadge({ rlsFilters, size = 'l' }: RlsBadgeProps) {
const theme = useTheme();
if (!rlsFilters?.length) {
return null;
}
const hasInherited = rlsFilters.some(f => f.inherited);
const tooltipContent = (
<div>
<strong>
{t(
'Row-Level Security: %d filter(s) may restrict data based on your role.',
rlsFilters.length,
)}
</strong>
<ul
css={css`
padding-left: ${theme.sizeUnit * 4}px;
margin: ${theme.sizeUnit}px 0 0;
`}
>
{rlsFilters.map(filter => (
<li key={filter.id}>
<div>
{filter.name}
{filter.filter_type ? ` (${filter.filter_type})` : ''}
{filter.group_key ? ` [${filter.group_key}]` : ''}
{filter.inherited ? `${t('from underlying table')}` : ''}
</div>
{filter.roles && filter.roles.length > 0 && (
<div>
{t('Roles')}: {filter.roles.map(role => role.name).join(', ')}
</div>
)}
{filter.clause && (
<div>
{t('Clause')}: {filter.clause}
</div>
)}
</li>
))}
</ul>
{hasInherited && (
<div
css={css`
margin-top: ${theme.sizeUnit}px;
font-style: italic;
`}
>
{t(
'Some filters are inherited from physical tables referenced in this virtual dataset.',
)}
</div>
)}
</div>
);
return (
<Tooltip id="rls-badge-tooltip" title={tooltipContent}>
<Icons.LockOutlined
aria-label={t('Row-Level Security')}
iconColor={theme.colorWarning}
iconSize={size}
/>
</Tooltip>
);
}

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