Compare commits

...

213 Commits

Author SHA1 Message Date
Beto Dealmeida
9042601f5c Line chart 2026-01-15 22:34:38 -05:00
Beto Dealmeida
1bebbcf067 Another chart 2026-01-15 19:23:54 -05:00
Beto Dealmeida
58ac91c059 feat: test Glyph chart 2026-01-15 18:52:49 -05:00
Alexandru Soare
81fdc2bd0e feat(dates): adding handling for first of (#37098) 2026-01-15 19:48:54 +02:00
Enzo Martellucci
23b91d22ef fix(charts): properly parse error responses in StatefulChart (#37130) 2026-01-15 15:22:45 +01:00
Mehmet Salih Yavuz
6eb4db6930 fix(controls): Only initialize categorical control on numeric x axis (#37115) 2026-01-15 11:03:36 +01:00
dependabot[bot]
2324b4c9e5 chore(deps-dev): bump @babel/preset-typescript from 7.26.0 to 7.28.5 in /superset-frontend (#36661)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-14 13:43:12 -08:00
dependabot[bot]
6ca0f7a925 chore(deps-dev): bump @babel/cli from 7.28.3 to 7.28.6 in /superset-frontend (#37124)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-14 13:09:11 -08:00
dependabot[bot]
64424f1625 chore(deps-dev): bump @types/node from 25.0.7 to 25.0.8 in /superset-websocket (#37116)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-14 13:08:53 -08:00
dependabot[bot]
debdfbc835 chore(deps-dev): bump @babel/plugin-transform-modules-commonjs from 7.27.1 to 7.28.6 in /superset-frontend (#37117)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-14 13:08:38 -08:00
Türker Ziya Ercin
4c01b5c324 docs: Add UserGuiding INTHEWILD.yaml (#37030) 2026-01-14 12:05:46 -08:00
Declan Zhao
137ebdee39 fix(prune_logs): improve performance by using id column only for ordering log records when max_rows_per_run is provided (#37138) 2026-01-14 16:05:56 -03:00
Elizabeth Thompson
a272253243 fix(table chart): time comparison totals returning null (#37111)
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-14 09:58:34 -08:00
Damian Pendrak
35ac4c74fd feat(dashboard): chart customizations modal and plugins (#36062) 2026-01-14 17:00:18 +01:00
dependabot[bot]
4f3403b134 chore(deps): bump antd from 6.1.2 to 6.2.0 in /docs (#37079)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 13:41:41 -08:00
dependabot[bot]
86bc493423 chore(deps-dev): bump @babel/types from 7.28.4 to 7.28.6 in /superset-frontend (#37087)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 13:41:32 -08:00
dependabot[bot]
91dba9dcbf chore(deps-dev): bump @babel/types from 7.28.4 to 7.28.5 in /superset-frontend (#36652)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 13:40:31 -08:00
dependabot[bot]
bdcc98743d chore(deps-dev): bump cheerio from 1.1.0 to 1.1.2 in /superset-frontend (#37002)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 13:40:21 -08:00
Mehmet Salih Yavuz
e053418c97 fix(Matrixify): Do not clear values when saving (#37090) 2026-01-13 23:22:51 +03:00
Reynold Morel
0404c99e39 fix(dashboard): revert cell hover and active colors to grayscale (#36991) 2026-01-13 22:22:37 +03:00
dependabot[bot]
fd3eea0557 chore(deps): bump lodash-es from 4.17.21 to 4.17.22 in /superset-frontend (#37048)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 10:59:16 -08:00
SakshiiRohida
0c490dc1ab docs(exploring-data): add overview to tutorial (#37028)
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-01-13 10:47:04 -08:00
Türker Ziya Ercin
1166df3579 docs(inthewild): Dark mode issues on InTheWild page (#37034) 2026-01-13 10:46:37 -08:00
dependabot[bot]
0b4fcce03b chore(deps-dev): bump cross-env from 10.0.0 to 10.1.0 in /superset-frontend (#36657)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 10:45:14 -08:00
dependabot[bot]
393259bb9e chore(deps-dev): bump typescript-eslint from 8.52.0 to 8.53.0 in /superset-websocket (#37082)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 10:25:40 -08:00
dependabot[bot]
560da50df8 chore(deps): bump @babel/runtime from 7.28.4 to 7.28.6 in /superset-frontend (#37080)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 10:25:18 -08:00
dependabot[bot]
5d5012aa9f chore(deps-dev): bump @types/lodash from 4.17.21 to 4.17.23 in /superset-frontend (#37077)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 10:24:38 -08:00
dependabot[bot]
6c75365427 chore(deps-dev): bump typescript-eslint from 8.52.0 to 8.53.0 in /docs (#37076)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 10:24:07 -08:00
dependabot[bot]
3a3cbc2900 chore(deps-dev): bump @types/node from 25.0.3 to 25.0.7 in /superset-websocket (#37081)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 22:47:22 +07:00
dependabot[bot]
23a47e2f5a chore(deps-dev): bump webpack-dev-server from 5.2.2 to 5.2.3 in /superset-frontend (#37088)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 22:46:37 +07:00
Alexandru Soare
fc67569cd4 fix(datasets): ui bug fixes (#37058) 2026-01-13 16:21:46 +01:00
dependabot[bot]
e17bfae6bd chore(deps-dev): bump webpack from 5.103.0 to 5.104.1 in /superset-frontend (#37083)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 21:27:39 +07:00
dependabot[bot]
936e37bd02 chore(deps-dev): bump @babel/node from 7.28.0 to 7.28.6 in /superset-frontend (#37089)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 21:24:03 +07:00
Jonathan Alberth Quispe Fuentes
ad3812edd7 fix(table): keep d3-format semantics when applying currency formatting (#37039) 2026-01-13 14:59:31 +03:00
Enzo Martellucci
5f58241795 fix(explore): dispatch NumberControl value on blur to allow field clearing (#37007) 2026-01-13 10:37:15 +01:00
ʈᵃᵢ
481bfa0f68 feat(starrocks): add catalog support for StarRocks database connections (#37026) 2026-01-12 13:02:22 -08:00
Pere Gomila
462fffc23c fix(docs): Update references from LLMS.md to AGENTS.md (#37060) 2026-01-12 12:25:39 -08:00
JUST.in DO IT
7503ee4e09 feat(sqllab): primary/secondary action extensions (#36644) 2026-01-12 12:06:15 -08:00
dependabot[bot]
fa3d4a75ca chore(deps): bump actions/download-artifact from 6 to 7 (#36699)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-12 11:00:47 -08:00
dependabot[bot]
adb575be2f chore(deps-dev): bump typescript-eslint from 8.50.1 to 8.52.0 in /docs (#36913)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-12 11:00:31 -08:00
dependabot[bot]
d56bc5826f chore(deps-dev): bump @applitools/eyes-storybook from 3.60.0 to 3.63.4 in /superset-frontend (#37003)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-12 10:59:37 -08:00
dependabot[bot]
72c69e2ca6 chore(deps): bump fs-extra from 11.3.0 to 11.3.3 in /superset-frontend (#37001)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-12 10:59:09 -08:00
Joe Li
22cfc4536b fix(export): URL prefix handling for subdirectory deployments (#36771)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 10:58:59 -08:00
Igor Shmulyan
fac5d2bcb6 feat(db): add dynamic schema support for athena (#36003) 2026-01-12 10:58:12 -08:00
dependabot[bot]
4fe2085596 chore(deps): bump caniuse-lite from 1.0.30001763 to 1.0.30001764 in /docs (#37049)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-12 10:52:13 -08:00
dependabot[bot]
005b2af985 chore(deps-dev): bump @types/lodash from 4.17.21 to 4.17.23 in /superset-websocket (#37045)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-12 10:51:01 -08:00
Damian Pendrak
2a38ce001e fix(deckgl): remove visibility condition in deckgl stroke color (#37029) 2026-01-12 10:44:19 -08:00
dependabot[bot]
f4772a9383 chore(deps-dev): bump webpack-bundle-analyzer from 5.1.0 to 5.1.1 in /superset-frontend (#37006)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-12 10:35:55 -08:00
Luis Sánchez
dcdcf88969 chore(chart): rollback legend top alignment to the right (#36994) 2026-01-12 10:30:34 -08:00
ankitajhanwar2001
d8f7ae83ee fix(sqlglot): use Athena dialect for awsathena parsing (#36747) 2026-01-12 10:06:46 -08:00
Amin Ghadersohi
911d72c957 fix(models): prevent SQLAlchemy and_() deprecation warning (#37020)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 17:53:04 +01:00
Ville Brofeldt
169d27c9e9 fix(extensions): gracefully handle dist directory rebuilding (#37025) 2026-01-12 06:53:04 -08:00
Ville Brofeldt
62c7b48b5c fix(extensions-cli): fix dev mode error (#37024) 2026-01-12 06:52:09 -08:00
amaannawab923
4f444ae1d2 feat(ag-grid): Server Side Filtering for Column Level Filters (#35683) 2026-01-12 19:25:07 +05:30
Jean Massucatto
459b4cb23d perf(date_parser): bound regex quantifiers for deterministic parsing performance (#36983) 2026-01-12 12:52:19 +03:00
Mehmet Salih Yavuz
d914b35cc0 fix(translations): post merge fix for new translations addded (#37041) 2026-01-12 12:31:09 +03:00
Mehmet Salih Yavuz
4f5789abfe fix(reports): Use authenticated user as recipient for chart/dashboard reports (#36981) 2026-01-12 12:29:54 +03:00
Mehmet Salih Yavuz
e9b6791ffb refactor: move translations and logging to new core (#36929) 2026-01-12 08:58:59 +03:00
Mehmet Salih Yavuz
0294c30c9e fix(Tabs): prevent infinite rerenders with nested tabs (#37018) 2026-01-10 18:38:09 +03:00
stockarea
1cface15e6 docs: Update INTHEWILD.yaml (#36888)
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-01-09 15:12:25 -08:00
Amin Ghadersohi
ecefba5bf7 fix(mcp): resolve startup failures from circular DAO imports (#37023) 2026-01-09 15:07:30 -08:00
Michael S. Molina
53dddf4db2 feat(embedded-sdk): Add resolvePermalinkUrl callback for custom permalink URLs (#36924) 2026-01-09 17:02:38 -03:00
Luiz Otavio
1e8d648f47 feat: Chart query last run timestamp (#36934) 2026-01-09 17:02:18 -03:00
Beto Dealmeida
14c0cad0ba chore: remove deprecated function (#37021) 2026-01-09 14:52:53 -05:00
Amin Ghadersohi
f895250cf9 fix(mypy): add overrides for superset-core local dev consistency (#36907)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 10:27:16 -08:00
Enzo Martellucci
ea90d1f141 refactor(db-engine-specs): use standard OAuth 2.0 params in base class (#37010) 2026-01-09 17:44:32 +01:00
Daniel Vaz Gaspar
f48322c17d chore: bump flask-cors to 6.0.2 (#36640) 2026-01-09 16:13:22 +00:00
dependabot[bot]
413dfc98ff chore(deps): bump @storybook/addon-docs from 8.6.14 to 8.6.15 in /docs (#36824)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-08 21:45:58 -08:00
dependabot[bot]
57f8f50292 chore(deps-dev): bump @typescript-eslint/eslint-plugin from 8.50.1 to 8.51.0 in /docs (#36898)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-08 21:45:34 -08:00
dependabot[bot]
07aa7622f7 chore(deps): bump react-resizable from 3.0.5 to 3.1.3 in /superset-frontend (#36911)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-08 21:44:21 -08:00
dependabot[bot]
ffbf81e952 chore(deps): bump ws from 8.18.3 to 8.19.0 in /superset-websocket (#36919)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-08 21:43:09 -08:00
dependabot[bot]
734d64081f chore(deps): bump caniuse-lite from 1.0.30001762 to 1.0.30001763 in /docs (#36970)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-08 21:42:57 -08:00
Amin Ghadersohi
85cf46dc1c fix(mcp): remove unused ctx parameter from health_check tool (#36992) 2026-01-08 21:38:22 -08:00
dependabot[bot]
4fc8157d6f chore(deps-dev): bump source-map from 0.7.4 to 0.7.6 in /superset-frontend (#36653)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-08 11:47:50 -08:00
dependabot[bot]
af185ad8c9 chore(deps-dev): bump jsdom from 27.0.0 to 27.4.0 in /superset-frontend (#36922)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-08 11:43:54 -08:00
dependabot[bot]
dd7f0c4224 chore(deps-dev): bump typescript-eslint from 8.50.1 to 8.52.0 in /superset-websocket (#36916)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-08 11:42:14 -08:00
dependabot[bot]
77ebc5ac10 chore(deps-dev): bump @typescript-eslint/parser from 8.51.0 to 8.52.0 in /docs (#36918)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-08 11:38:57 -08:00
dependabot[bot]
e9b91f6ec9 chore(deps): bump ioredis from 5.8.2 to 5.9.0 in /superset-websocket (#36920)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-08 11:38:13 -08:00
Amit Sharma
3ac76e116d chore: Update INTHEWILD.yaml (#36733)
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-01-08 11:36:33 -08:00
dependabot[bot]
4249b8ee6a chore(deps-dev): bump eslint-plugin-react-you-might-not-need-an-effect from 0.5.1 to 0.8.3 in /superset-frontend (#36972)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-08 11:35:37 -08:00
Amin Ghadersohi
047360641a feat(mcp): change save_chart default to False for preview-first workflow (#36935)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 11:30:01 -08:00
Tu Shaokun
337eb3baf5 fix(dashboard): prevent PublishedLabel overlap in non-English languages (#36877) 2026-01-08 11:22:55 -08:00
Connor Shea
70b2e520b2 chore(frontend): Update $schema path in oxlint.json (#36859) 2026-01-08 11:21:42 -08:00
hlyda0601
16e046b5d9 docs(INTHEWILD): add HOLLYLAND猛玛 (#36968) 2026-01-08 13:47:28 -05:00
Daniel Vaz Gaspar
a6d85dccf8 fix: streaming export losing g context (#36950)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 16:11:03 +00:00
Amin Ghadersohi
84279acd2f feat(mcp): add unified get_schema tool for schema discovery (#36458)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 15:26:17 -08:00
Jeroen Habets
03caa7b337 docs(INTHEWILD): add Cirrus Assessment (#36871)
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-01-07 13:53:09 -08:00
Pat Buxton
6f67b05375 chore: Update INTHEWILD.yaml for PlaidCloud (#36741) 2026-01-07 11:44:34 -08:00
Amin Ghadersohi
64ee48f147 fix(mcp): use chart.query_context for get_chart_data like the API does (#36937)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 16:32:45 -03:00
Evan Rusackas
f9be2b816a feat(docs): add resources admonition with external links (#36761)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 11:11:53 -08:00
Levis Mbote
dfdf8e75d8 fix: handle undefined template variables safely in query rendering. (#35009) 2026-01-07 19:44:03 +01:00
Michael S. Molina
0c1edd4568 chore: Bump @apache-superset/core (0.0.1-rc9) (#36953) 2026-01-07 15:16:22 -03:00
Michael S. Molina
7a5441bc7a refactor: Remove unimplemented APIs from @apache-superset/core (#36952) 2026-01-07 14:17:06 -03:00
Gabriel Torres Ruiz
5edaed2e5b fix(alerts): wrong alert trigger with custom query (#35871) 2026-01-07 18:06:18 +01:00
Evan Rusackas
861e5cd013 fix(security): enforce datasource access control in get_samples() (#36550)
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-07 08:54:11 -08:00
amaannawab923
d7d94ba640 fix(ag-grid): Ag Grid Date Filter timezone correction (#36270) 2026-01-07 08:27:20 -03:00
Đỗ Trọng Hải
9968393e4c feat(ci): add container security scan for image built for commits merged to mainline (#36604) 2026-01-07 07:04:46 +07:00
Evan Rusackas
9aff89c1b4 chore(frontend): migrate SqlLab and explore JS/JSX files to TypeScript (#36760)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 10:52:58 -08:00
Evan Rusackas
aaa174f820 docs: add Netlify configuration for PR deploy previews (#36908)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 10:52:12 -08:00
Evan Rusackas
1949d1bb96 feat(dev): add make ports and make open commands (#36906)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 10:52:00 -08:00
Evan Rusackas
f9fde87e85 feat(native filters): add filter type icons in config modal (#36905)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 10:51:48 -08:00
Felipe López
cedc35e39f fix(SQLLab): remove error icon displayed when writing Jinja SQL even when the script is correct (#36422) 2026-01-06 16:58:40 +03:00
JUST.in DO IT
12a266fd2f fix(trino): update query progress using cursor stats (#36872) 2026-01-05 13:19:20 -08:00
Evan Rusackas
5909e90081 feat(security): add built-in Public role for anonymous dashboard access (#36548)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 10:27:10 -08:00
Bouke van der Bijl
dcc556a9a7 chore(CHANGELOG): add link to 6.0.0 CHANGELOG (#36832) 2026-01-05 12:59:42 -05:00
dependabot[bot]
61986100bd chore(deps): bump hot-shots from 12.0.0 to 12.1.0 in /superset-websocket (#36899)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-05 09:22:09 -08:00
Mehmet Salih Yavuz
c76ddcbbec fix(deck.gl): Fix Scatterplot chart error when using fixed point size (#36890)
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-05 15:55:43 +03:00
Enzo Martellucci
740ddc03e2 fix(plugin-chart-table): remove column misalignment when no scrollbars are present (#36891) 2026-01-05 13:52:37 +01:00
Đỗ Trọng Hải
2080633e57 other: resolve frontend dep vulns (#36820)
Signed-off-by: hainenber <dotronghai96@gmail.com>
2026-01-02 23:28:44 -08:00
dependabot[bot]
ac27c0aa3c chore(deps-dev): bump @typescript-eslint/parser from 8.50.1 to 8.51.0 in /superset-websocket (#36861)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 23:21:59 -08:00
dependabot[bot]
53fa65fe67 chore(deps): bump caniuse-lite from 1.0.30001761 to 1.0.30001762 in /docs (#36865)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 23:19:04 -08:00
dependabot[bot]
fdef8fa50a chore(deps-dev): bump @typescript-eslint/parser from 8.50.1 to 8.51.0 in /docs (#36866)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 23:18:50 -08:00
İbrahim Ercan
1334040fd6 chore(docs): Include VLMedia logo to intothewild (#36868)
Co-authored-by: Ibrahim Ercan <ibrahim.ercan@vlmedia.com.tr>
2026-01-02 23:18:31 -08:00
nmdo
52af489d8f docs: Add Rogow to INTHEWILD.yaml (#36879) 2026-01-02 23:14:43 -08:00
innovark
d07a452e9b fix(RightMenu): fix inconsistent icon alignment in RightMenu items (#36532) 2026-01-02 23:13:08 -08:00
dependabot[bot]
aed95453b3 chore(deps): bump qs from 6.14.0 to 6.14.1 in /superset-websocket/utils/client-ws-app (#36883)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 23:10:25 -08:00
dependabot[bot]
4451e8db05 chore(deps): bump qs from 6.14.0 to 6.14.1 in /docs (#36884)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 23:10:08 -08:00
dependabot[bot]
dd2eb6293d chore(deps-dev): bump globals from 16.5.0 to 17.0.0 in /superset-websocket (#36885)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 23:09:39 -08:00
dependabot[bot]
1b1be96274 chore(deps-dev): bump globals from 16.5.0 to 17.0.0 in /docs (#36886)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 23:09:22 -08:00
innovark
e5489bd30f fix(SavedQueries): unify query card actions styling across all home page cards (#36671) 2026-01-02 14:41:47 +01:00
Levis Mbote
12aa425049 fix(logout): clicking logout displays an error notification "invalid username or password" (#36490) 2026-01-02 12:59:27 +01:00
Luis Sánchez
c31224c891 fix(TableChart): render cell bars for columns with NULL values (#36819) 2025-12-31 17:51:35 +03:00
Mehmet Salih Yavuz
85e830de46 fix: Clear database form errors (#36854) 2025-12-31 15:25:23 +01:00
Geidō
d4ba44fce2 fix: Query history view button in SqlLab (#36540) 2025-12-31 15:18:59 +01:00
Enzo Martellucci
7cd76e4647 fix: SqlLab error when collapsing the left panel preview (#36858)
Co-authored-by: Diego Pucci <diegopucci.me@gmail.com>
2025-12-31 14:11:59 +01:00
Enzo Martellucci
e112d863bf fix(dashboard): resolve tab reorder state sync issues (#36855) 2025-12-31 13:50:58 +01:00
Enzo Martellucci
fe5d5fdae6 fix(chart-creation): use exact match when loading dataset from URL parameter (#36831) 2025-12-31 13:50:03 +01:00
cr0fters
02411ffde0 docs: Add EnquiryLabs to INTHEWILD.yaml (#36843) 2025-12-27 20:44:36 -08:00
dependabot[bot]
1697cf733b chore(deps): bump antd from 6.1.1 to 6.1.2 in /docs (#36823)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-27 20:42:16 -08:00
Mehmet Salih Yavuz
28c802fb6c fix(TableCollection): only apply highlight class when defined (#36809) 2025-12-24 16:26:01 +01:00
dependabot[bot]
362b5e3b89 chore(deps-dev): bump js-yaml from 3.14.1 to 3.14.2 in /superset-frontend (#36711)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 15:20:58 -08:00
dependabot[bot]
bf5070471d chore(deps): bump storybook from 8.6.14 to 8.6.15 in /docs (#36727)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 15:20:39 -08:00
dependabot[bot]
100789200a chore(deps): bump hot-shots from 11.4.0 to 12.0.0 in /superset-websocket (#36786)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 15:20:22 -08:00
dependabot[bot]
f95f125c4c chore(deps-dev): bump html-webpack-plugin from 5.6.4 to 5.6.5 in /superset-frontend (#36787)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 15:20:05 -08:00
dependabot[bot]
fd67d3190a chore(deps-dev): bump typescript-eslint from 8.50.0 to 8.50.1 in /docs (#36801)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 15:19:30 -08:00
dependabot[bot]
bd8d4ddbee chore(deps-dev): bump typescript-eslint from 8.50.0 to 8.50.1 in /superset-websocket (#36802)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 15:19:13 -08:00
Beto Dealmeida
ecb4e483df fix: apply EXCLUDE_USERS_FROM_LISTS to /api/v1/security/users/ (#36742)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-23 15:18:34 -08:00
Sebastian Jingoi
f8cb935105 feat(reports): add webhook option for notifications (#36127)
Co-authored-by: Hriday Algh <hridayalgh@gmail.com>
Co-authored-by: Hriday Algh <53922405+AlwaysIngame@users.noreply.github.com>
Co-authored-by: codeant-ai-for-open-source[bot] <244253245+codeant-ai-for-open-source[bot]@users.noreply.github.com>
2025-12-23 10:51:04 -08:00
Michael S. Molina
ba8d6eb9ac docs: Adds a new "Dependencies" page to the Developer Portal (#36817) 2025-12-23 10:41:10 -08:00
Michael S. Molina
c399fd2801 fix: Stale editor content in @apache-superset/core (#36813) 2025-12-23 10:40:26 -08:00
Michael S. Molina
9e04c3471d docs: Add SQL Lab Result Stats to community extensions registry (#36796) 2025-12-23 10:39:47 -08:00
Michael S. Molina
8f8fe19e3e docs: Add SQL Snippets to community extensions registry (#36797) 2025-12-23 14:52:09 -03:00
Michael S. Molina
ff3dab9b3b docs: Add Query Estimator to community extensions registry (#36814) 2025-12-23 14:25:45 -03:00
Levis Mbote
ff24e2f27d fix: fix error with dashboard filters when global async queries is enabled and user navigates quickly (#36639) 2025-12-23 15:53:01 +01:00
Alexandru Soare
54eb6317ef fix(margin): Fixing margin issues (#36479) 2025-12-22 15:57:43 +01:00
Enzo Martellucci
32c98d02d3 fix: UI cut off (#36531)
Co-authored-by: Diego Pucci <diegopucci.me@gmail.com>
2025-12-22 15:52:04 +01:00
Michael S. Molina
6b25d0663e refactor: Migrates the MCP execute_sql tool to use the SQL execution API (#36739)
Co-authored-by: codeant-ai-for-open-source[bot] <244253245+codeant-ai-for-open-source[bot]@users.noreply.github.com>
2025-12-22 09:48:28 -03:00
dependabot[bot]
c0bcf28947 chore(deps-dev): bump @playwright/test from 1.56.0 to 1.57.0 in /superset-frontend (#36571)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-20 20:56:15 -08:00
dependabot[bot]
e0ea807031 chore(deps-dev): bump baseline-browser-mapping from 2.9.8 to 2.9.9 in /superset-frontend (#36725)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-20 20:55:59 -08:00
dependabot[bot]
8d070f5cb6 chore(deps-dev): bump @types/node from 25.0.2 to 25.0.3 in /superset-frontend (#36726)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-20 20:55:41 -08:00
dependabot[bot]
5cd8e1e736 chore(deps): bump caniuse-lite from 1.0.30001760 to 1.0.30001761 in /docs (#36757)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-20 20:55:21 -08:00
dependabot[bot]
0ced20457b chore(deps-dev): bump webpack from 5.104.0 to 5.104.1 in /docs (#36758)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-20 20:55:01 -08:00
Amin Ghadersohi
e3e6b0e18b fix(mcp): use SQLScript for all SQL parsing in execute_sql (#36599) 2025-12-20 20:52:56 -08:00
Joe Li
c026ae2ce7 fix(dashboard): prevent table chart infinite reload loop (#36686)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-19 13:45:36 -08:00
Michael S. Molina
ae491aee00 docs: Add SQL Lab Query Comparison to community extensions registry (#36769) 2025-12-19 16:55:16 -03:00
Evan Rusackas
3258082819 docs: add SQL Lab Export to Parquet to community extensions registry (#36755)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-19 09:16:43 -08:00
Michael S. Molina
d36ddbbb33 feat(extensions-cli): Add .gitignore generation to init command (#36768) 2025-12-19 13:33:37 -03:00
Michael S. Molina
5920cb57ea refactor(extensions): add Pydantic validation for extension configuration (#36767) 2025-12-19 13:33:10 -03:00
Joe Li
fb6f3fbb4d chore: update files for release 6.0.0 (#36745) 2025-12-18 21:19:12 -08:00
Evan Rusackas
91539f77aa feat(docker): support running multiple Superset instances simultaneously (#36751)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 17:04:58 -08:00
Evan Rusackas
b8f31124d0 chore(frontend): migrate 13 JS/JSX files to TypeScript (#36720)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 13:54:49 -08:00
Evan Rusackas
da8e077a44 chore(frontend): migrate utility JS files to TypeScript (#36721)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 13:54:40 -08:00
Evan Rusackas
32435bc3e9 feat(docs): enhance Matomo analytics tracking (#36743)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 13:52:08 -08:00
Evan Rusackas
2cf0d7936e chore(pre-commit): exclude logos from end-of-file-fixer (#36744)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 13:51:50 -08:00
Evan Rusackas
0830a57fa6 feat(docs): add llms.txt for LLM-friendly documentation index (#36730)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 13:48:28 -08:00
Brandon Sovran
0f56e3b9ae fix: Implement SIP-40 error styles for GAQ (#36596) 2025-12-18 12:22:17 -08:00
Evan Rusackas
ee45b26ad7 fix(tests): optimize DatasourceEditorCurrency tests for CI reliability (#36723)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 09:37:53 -08:00
Kamil Gabryjelski
f3407d7a56 chore: Close playwright browser gracefully (#36537) 2025-12-18 17:30:22 +01:00
Joe Li
f51f7f3307 fix(tests): resolve flakey selectOption helper race condition (#36719)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-17 21:27:17 -08:00
Evan Rusackas
2f4f64dfe8 chore(frontend): migrate easy JS/JSX files to TypeScript (#36713)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 17:12:02 -08:00
Evan Rusackas
ae584c8886 chore: remove INTHEWILD.md after migration to YAML (#36718)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 12:59:19 -08:00
Đỗ Trọng Hải
b1e004e122 build(dev-deps): remove stub type definition packages (#36706)
Signed-off-by: hainenber <dotronghai96@gmail.com>
2025-12-17 12:57:44 -08:00
Vitor Avila
737a5162e4 fix: Use is_active for guest users (#36716) 2025-12-17 17:23:22 -03:00
Evan Rusackas
b800412eda fix(docs): add retry logic and concurrency handling for badge downloads (#36715)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 12:19:39 -08:00
Michael S. Molina
24a4f8510d docs: Add SQL Lab Export to Google Sheets to community extensions registry (#36714) 2025-12-17 16:53:10 -03:00
Yousuf Ansari
33a425bbbc fix(echarts): use scroll legend for horizontal layouts to prevent overlap (#36306) 2025-12-17 11:16:35 -08:00
Catherine Qu
5ce4c52cfa feat(docs): In the Wild page with YAML data and AntD components (#36386)
Co-authored-by: Catherine Qu <catherine.qu@mail.utoronto.ca>
Co-authored-by: Evan Rusackas <evan@rusackas.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 09:42:29 -08:00
Luis Sánchez
c9ec173647 fix(SearchFilter): prevent unintended autocomplete on search input (#36209) 2025-12-17 20:41:07 +03:00
Michael S. Molina
71f9dcff5a chore: Bump core packages (0.0.1rc3, 0.0.1-rc6) (#36707) 2025-12-17 09:12:26 -08:00
dependabot[bot]
479b7a3fba chore(deps-dev): bump @pmmmwh/react-refresh-webpack-plugin from 0.5.17 to 0.6.2 in /superset-frontend (#36691)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 09:01:02 -08:00
dependabot[bot]
594ea972ca chore(deps-dev): bump @types/node from 25.0.2 to 25.0.3 in /superset-websocket (#36692)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 08:46:34 -08:00
dependabot[bot]
d77f7b6d20 chore(deps): bump nanoid from 5.0.9 to 5.1.6 in /superset-frontend (#36586)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 08:46:17 -08:00
dependabot[bot]
f4ded02e0d chore(deps-dev): bump typescript-eslint from 8.49.0 to 8.50.0 in /docs (#36650)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 08:46:01 -08:00
dependabot[bot]
f97fa08477 chore(deps-dev): bump baseline-browser-mapping from 2.9.7 to 2.9.8 in /superset-frontend (#36690)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 08:45:47 -08:00
dependabot[bot]
789be78166 chore(deps-dev): bump webpack from 5.103.0 to 5.104.0 in /docs (#36695)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 21:42:48 +07:00
dependabot[bot]
ea3d247017 chore(deps-dev): bump webpack-bundle-analyzer from 4.10.2 to 5.1.0 in /superset-frontend (#36610)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 16:06:20 -08:00
dependabot[bot]
6456f4c516 chore(deps): bump googleapis from 168.0.0 to 169.0.0 in /superset-frontend (#36646)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 15:48:07 -08:00
dependabot[bot]
e9bbf06938 chore(deps): bump re-resizable from 6.10.3 to 6.11.2 in /superset-frontend (#36647)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 15:47:53 -08:00
dependabot[bot]
ebee35ea5a chore(deps-dev): bump typescript-eslint from 8.49.0 to 8.50.0 in /superset-websocket (#36649)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 15:47:13 -08:00
SBIN2010
d0fb77cbc8 fix: removed dashboard from main page in "All" tab, refreshes dashboard list (#35945) 2025-12-16 15:45:58 -08:00
Evan Rusackas
46659c2bd1 fix(tests): resolve flaky ExploreChartHeader export menu tests (#36642)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 15:44:30 -08:00
dependabot[bot]
8407e9cf3b chore(deps): bump antd from 6.1.0 to 6.1.1 in /docs (#36655)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 15:43:53 -08:00
dependabot[bot]
5eeba2e734 chore(deps-dev): bump @typescript-eslint/parser from 8.49.0 to 8.50.0 in /docs (#36656)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 15:43:33 -08:00
dependabot[bot]
4ca8c000d1 chore(deps): update classnames requirement from ^2.2.5 to ^2.5.1 in /superset-frontend/packages/superset-ui-core (#36660)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 15:43:15 -08:00
dependabot[bot]
7108658de0 chore(deps-dev): bump @babel/runtime-corejs3 from 7.28.2 to 7.28.4 in /superset-frontend (#36664)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 15:42:58 -08:00
dependabot[bot]
42311f602e chore(deps-dev): bump npm from 11.5.2 to 11.7.0 in /superset-frontend (#36668)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 15:42:39 -08:00
Evan Rusackas
6b948ee894 docs(badges): Restore project badges on README - and re-implement the Docusaurus ones (#36495)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 14:45:51 -08:00
Joshua Daniel
5e0ee40762 feat(chart): support icons and text in the deck.gl Geojson visualization (#36201)
Co-authored-by: Joshua Daniel <jdaniel@gflenv.com>
2025-12-16 14:28:04 -08:00
Evan Rusackas
6aaf2266a9 chore(deps-dev): add baseline-browser-mapping (#36645)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 14:20:37 -08:00
Vitor Avila
d14f502126 fix: store form_data as dict during viz type migration (#36680) 2025-12-16 18:32:29 -03:00
Joe Li
d0361cb881 test(playwright): convert and create new dataset list playwright tests (#36196)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-16 11:07:11 -08:00
Vitor Avila
821b259805 fix: Support datetime_format during import (#36679) 2025-12-16 15:15:04 -03:00
Joe Li
2329d49f9e fix(DatasourceEditor): add mount guards and fix async race conditions (#35810)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-16 09:54:32 -08:00
Michael S. Molina
28e3ba749e feat: SQL execution API for Superset (#36529)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-16 14:39:29 -03:00
Evan Rusackas
cd2c889c9a feat(frontend): upgrade Storybook and add extension component documentation (#36498)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 09:31:35 -08:00
Evan Rusackas
52c711b0bc fix(dashboard): import with overwrite flag replaces charts instead of merging (#36551)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-16 09:09:56 -08:00
Evan Rusackas
6f8052b828 docs: add contribution guidelines from wiki to Developer Portal (#36523)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 16:54:29 -08:00
dependabot[bot]
5f431ee1ec chore(deps-dev): bump @types/node from 24.8.1 to 25.0.2 in /superset-frontend (#36620)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 16:49:16 -08:00
Maxime Beauchemin
de7a72a37b feat(ci): use TTL labels for showtime cleanup (#36643)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-15 15:07:20 -08:00
Evan Rusackas
a1a57d50a4 fix(tests): resolve flaky "should edit correctly" test in chart list (#36641)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 14:45:01 -08:00
Evan Rusackas
5844c05281 docs: clarify Jinja from_dttm/to_dttm availability in SQL Lab (#36544)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 13:00:31 -08:00
Evan Rusackas
c7a4d4f2cc fix(sql): handle backtick-quoted identifiers with base dialect (#36545)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-15 13:00:13 -08:00
Evan Rusackas
d6d8e71b71 chore(deps): Remove redundant polished direct dependency (#36431)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-15 12:59:59 -08:00
1297 changed files with 60985 additions and 24464 deletions

41
.envrc.example Normal file
View File

@@ -0,0 +1,41 @@
#
# 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.
#
# Auto-configure Docker Compose for multi-instance support
# Requires direnv: https://direnv.net/
#
# Install: brew install direnv (or apt install direnv)
# Setup: Add 'eval "$(direnv hook bash)"' to ~/.bashrc (or ~/.zshrc)
# Allow: Run 'direnv allow' in this directory once
# Generate unique project name from directory
export COMPOSE_PROJECT_NAME=$(basename "$PWD" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g')
# Find available ports sequentially to avoid collisions
_is_free() { ! lsof -i ":$1" &>/dev/null 2>&1; }
_p=80; while ! _is_free $_p; do ((_p++)); done; export NGINX_PORT=$_p
_p=8088; while ! _is_free $_p; do ((_p++)); done; export SUPERSET_PORT=$_p
_p=9000; while ! _is_free $_p; do ((_p++)); done; export NODE_PORT=$_p
_p=8080; while ! _is_free $_p || [ $_p -eq $NGINX_PORT ]; do ((_p++)); done; export WEBSOCKET_PORT=$_p
_p=8081; while ! _is_free $_p || [ $_p -eq $WEBSOCKET_PORT ]; do ((_p++)); done; export CYPRESS_PORT=$_p
_p=5432; while ! _is_free $_p; do ((_p++)); done; export DATABASE_PORT=$_p
_p=6379; while ! _is_free $_p; do ((_p++)); done; export REDIS_PORT=$_p
unset _p _is_free
echo "🐳 Superset configured: http://localhost:$SUPERSET_PORT (dev: localhost:$NODE_PORT)"

View File

@@ -41,8 +41,8 @@ body:
label: Superset version
options:
- master / latest-dev
- "6.0.0"
- "5.0.0"
- "4.1.3"
validations:
required: true
- type: dropdown

View File

@@ -117,6 +117,19 @@ testdata() {
say "::endgroup::"
}
playwright_testdata() {
cd "$GITHUB_WORKSPACE"
say "::group::Load all examples for Playwright tests"
# must specify PYTHONPATH to make `tests.superset_test_config` importable
export PYTHONPATH="$GITHUB_WORKSPACE"
pip install -e .
superset db upgrade
superset load_test_users
superset load_examples
superset init
say "::endgroup::"
}
celery-worker() {
cd "$GITHUB_WORKSPACE"
say "::group::Start Celery worker"

View File

@@ -101,6 +101,23 @@ jobs:
docker images $IMAGE_TAG
docker history $IMAGE_TAG
# Scan for vulnerabilities in built container image after pushes to mainline branch.
- name: Run Trivy container image vulnerabity scan
if: github.event_name == 'push' && github.ref == 'refs/heads/master' && (steps.check.outputs.python || steps.check.outputs.frontend || steps.check.outputs.docker) && matrix.build_preset == 'lean'
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # v0.33.1
with:
image-ref: ${{ env.IMAGE_TAG }}
format: 'sarif'
output: 'trivy-results.sarif'
vuln-type: 'os'
severity: 'CRITICAL,HIGH'
ignore-unfixed: true
- name: Upload Trivy scan results to GitHub Security tab
if: github.event_name == 'push' && github.ref == 'refs/heads/master' && (steps.check.outputs.python || steps.check.outputs.frontend || steps.check.outputs.docker) && matrix.build_preset == 'lean'
uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8
with:
sarif_file: 'trivy-results.sarif'
- name: docker-compose sanity check
if: (steps.check.outputs.python || steps.check.outputs.frontend || steps.check.outputs.docker) && matrix.build_preset == 'dev'
shell: bash

View File

@@ -7,12 +7,6 @@ on:
# Manual trigger for testing
workflow_dispatch:
inputs:
max_age_hours:
description: 'Maximum age in hours before cleanup'
required: false
default: '48'
type: string
# Common environment variables
env:
@@ -38,13 +32,5 @@ jobs:
- name: Cleanup expired environments
run: |
MAX_AGE="${{ github.event.inputs.max_age_hours || '48' }}"
# Validate max_age is numeric
if [[ ! "$MAX_AGE" =~ ^[0-9]+$ ]]; then
echo "❌ Invalid max_age_hours format: $MAX_AGE (must be numeric)"
exit 1
fi
echo "Cleaning up environments older than ${MAX_AGE}h"
python -m showtime cleanup --older-than "${MAX_AGE}h"
echo "Cleaning up environments respecting TTL labels"
python -m showtime cleanup --respect-ttl

View File

@@ -223,7 +223,7 @@ jobs:
if: steps.check.outputs.python || steps.check.outputs.frontend
uses: ./.github/actions/cached-dependencies
with:
run: testdata
run: playwright_testdata
- name: Setup Node.js
if: steps.check.outputs.python || steps.check.outputs.frontend
uses: actions/setup-node@v6

View File

@@ -167,3 +167,21 @@ jobs:
run: |
docker run --rm $TAG bash -c \
"npm run plugins:build-storybook"
test-storybook:
needs: frontend-build
if: needs.frontend-build.outputs.should-run == 'true'
runs-on: ubuntu-24.04
steps:
- name: Download Docker Image Artifact
uses: actions/download-artifact@v7
with:
name: docker-image
- name: Load Docker Image
run: docker load < docker-image.tar.gz
- name: Build Storybook and Run Tests
run: |
docker run --rm $TAG bash -c \
"npm run build-storybook && npx playwright install-deps && npx playwright install chromium && npm run test-storybook:ci"

View File

@@ -97,7 +97,7 @@ jobs:
if: steps.check.outputs.python || steps.check.outputs.frontend
uses: ./.github/actions/cached-dependencies
with:
run: testdata
run: playwright_testdata
- name: Setup Node.js
if: steps.check.outputs.python || steps.check.outputs.frontend
uses: actions/setup-node@v6

View File

@@ -54,7 +54,7 @@ repos:
exclude: ^helm/superset/templates/
- id: debug-statements
- id: end-of-file-fixer
exclude: .*/lerna\.json$
exclude: .*/lerna\.json$|^docs/static/img/logos/
- id: trailing-whitespace
exclude: ^.*\.(snap)
args: ["--markdown-linebreak-ext=md"]

View File

@@ -76,6 +76,9 @@ snowflake.svg
ydb.svg
loading.svg
# docs third-party logos, i.e. docs/static/img/logos/*
logos/*
# docs-related
erd.puml
erd.svg
@@ -83,6 +86,7 @@ intro_header.txt
# for LLMs
llm-context.md
llms.txt
AGENTS.md
LLMS.md
CLAUDE.md

View File

@@ -49,3 +49,4 @@ under the License.
- [4.1.3](./CHANGELOG/4.1.3.md)
- [4.1.4](./CHANGELOG/4.1.4.md)
- [5.0.0](./CHANGELOG/5.0.0.md)
- [6.0.0](./CHANGELOG/6.0.0.md)

1062
CHANGELOG/6.0.0.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -160,7 +160,7 @@ ENV SUPERSET_HOME="/app/superset_home" \
COPY --chmod=755 docker/entrypoints /app/docker/entrypoints
WORKDIR /app
# Set up necessary directories and user
# Set up necessary directories
RUN mkdir -p \
${PYTHONPATH} \
superset/static \

View File

@@ -18,7 +18,7 @@
# Python version installed; we need 3.10-3.11
PYTHON=`command -v python3.11 || command -v python3.10`
.PHONY: install superset venv pre-commit
.PHONY: install superset venv pre-commit up down logs ps nuke ports open
install: superset pre-commit
@@ -112,3 +112,28 @@ report-celery-beat:
admin-user:
superset fab create-admin
# Docker Compose with auto-assigned ports (for running multiple instances)
up:
./scripts/docker-compose-up.sh
up-detached:
./scripts/docker-compose-up.sh -d
down:
./scripts/docker-compose-up.sh down
logs:
./scripts/docker-compose-up.sh logs -f
ps:
./scripts/docker-compose-up.sh ps
nuke:
./scripts/docker-compose-up.sh nuke
ports:
./scripts/docker-compose-up.sh ports
open:
./scripts/docker-compose-up.sh open

View File

@@ -17,6 +17,21 @@ specific language governing permissions and limitations
under the License.
-->
# Superset
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/license/apache-2-0)
[![Latest Release on Github](https://img.shields.io/github/v/release/apache/superset?sort=semver)](https://github.com/apache/superset/releases/latest)
[![Build Status](https://github.com/apache/superset/actions/workflows/superset-python-unittest.yml/badge.svg)](https://github.com/apache/superset/actions)
[![PyPI version](https://badge.fury.io/py/apache_superset.svg)](https://badge.fury.io/py/apache_superset)
[![PyPI](https://img.shields.io/pypi/pyversions/apache_superset.svg?maxAge=2592000)](https://pypi.python.org/pypi/apache_superset)
[![GitHub Stars](https://img.shields.io/github/stars/apache/superset?style=social)](https://github.com/apache/superset/stargazers)
[![Contributors](https://img.shields.io/github/contributors/apache/superset)](https://github.com/apache/superset/graphs/contributors)
[![Last Commit](https://img.shields.io/github/last-commit/apache/superset)](https://github.com/apache/superset/commits/master)
[![Open Issues](https://img.shields.io/github/issues/apache/superset)](https://github.com/apache/superset/issues)
[![Open PRs](https://img.shields.io/github/issues-pr/apache/superset)](https://github.com/apache/superset/pulls)
[![Get on Slack](https://img.shields.io/badge/slack-join-orange.svg)](http://bit.ly/join-superset-slack)
[![Documentation](https://img.shields.io/badge/docs-apache.org-blue.svg)](https://superset.apache.org)
<picture width="500">
<source
width="600"
@@ -40,7 +55,7 @@ A modern, enterprise-ready business intelligence web application.
[**Get Involved**](#get-involved) |
[**Contributor Guide**](#contributor-guide) |
[**Resources**](#resources) |
[**Organizations Using Superset**](https://github.com/apache/superset/blob/master/RESOURCES/INTHEWILD.md)
[**Organizations Using Superset**](https://superset.apache.org/inTheWild)
## Why Superset?
@@ -156,7 +171,7 @@ how to set up a development environment.
## Resources
- [Superset "In the Wild"](https://github.com/apache/superset/blob/master/RESOURCES/INTHEWILD.md) - open a PR to add your org to the list!
- [Superset "In the Wild"](https://superset.apache.org/inTheWild) - see who's using Superset, and [add your organization](https://github.com/apache/superset/edit/master/RESOURCES/INTHEWILD.yaml) to the list!
- [Feature Flags](https://github.com/apache/superset/blob/master/RESOURCES/FEATURE_FLAGS.md) - the status of Superset's Feature Flags.
- [Standard Roles](https://github.com/apache/superset/blob/master/RESOURCES/STANDARD_ROLES.md) - How RBAC permissions map to roles.
- [Superset Wiki](https://github.com/apache/superset/wiki) - Tons of additional community resources: best practices, community content and other information.

View File

@@ -1,226 +0,0 @@
<!--
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.
-->
## Superset Users in the Wild
Here's a list of organizations, broken down into broad industry categories, that have taken the time to send a PR to let
the world know they are using Apache Superset. If you are a user and want to be recognized,
all you have to do is file a simple PR [like this one](https://github.com/apache/superset/pull/10122) — [just click here](https://github.com/apache/superset/edit/master/RESOURCES/INTHEWILD.md) to do so. If you think
the categorization is inaccurate, please file a PR with your correction as well.
Join our growing community!
### Sharing Economy
- [Airbnb](https://github.com/airbnb)
- [Faasos](https://faasos.com/) [@shashanksingh]
- [Free2Move](https://www.free2move.com/) [@PaoloTerzi]
- [Hostnfly](https://www.hostnfly.com/) [@alexisrosuel]
- [Lime](https://www.li.me/) [@cxmcc]
- [Lyft](https://www.lyft.com/)
- [Ontruck](https://www.ontruck.com/)
### Financial Services
- [Aktia Bank plc](https://www.aktia.com)
- [American Express](https://www.americanexpress.com) [@TheLastSultan]
- [bumper](https://www.bumper.co/) [@vasu-ram, @JamiePercival]
- [Cape Crypto](https://capecrypto.com)
- [Capital Service S.A.](https://capitalservice.pl) [@pkonarzewski]
- [Clark.de](https://clark.de/)
- [Europace](https://europace.de)
- [KarrotPay](https://www.daangnpay.com/)
- [Remita](https://remita.net) [@mujibishola]
- [Taveo](https://www.taveo.com) [@codek]
- [Unit](https://www.unit.co/about-us) [@amitmiran137]
- [Wise](https://wise.com) [@koszti]
- [Xendit](https://xendit.co/) [@LieAlbertTriAdrian]
- [Cover Genius](https://covergenius.com/)
### Gaming
- [Popoko VM Games Studio](https://popoko.live)
### E-Commerce
- [AiHello](https://www.aihello.com) [@ganeshkrishnan1]
- [Bazaar Technologies](https://www.bazaartech.com) [@umair-abro]
- [Dragonpass](https://www.dragonpass.com.cn/) [@zhxjdwh]
- [Dropit Shopping](https://www.dropit.shop/) [@dropit-dev]
- [Fanatics](https://www.fanatics.com/) [@coderfender]
- [Fordeal](https://www.fordeal.com) [@Renkai]
- [Fynd](https://www.fynd.com/) [@darpanjain07]
- [GFG - Global Fashion Group](https://global-fashion-group.com) [@ksaagariconic]
- [GoTo/Gojek](https://www.gojek.io/) [@gwthm-in]
- [HuiShouBao](https://www.huishoubao.com/) [@Yukinoshita-Yukino]
- [Now](https://www.now.vn/) [@davidkohcw]
- [Qunar](https://www.qunar.com/) [@flametest]
- [Rakuten Viki](https://www.viki.com)
- [Shopee](https://shopee.sg) [@xiaohanyu]
- [Shopkick](https://www.shopkick.com) [@LAlbertalli]
- [ShopUp](https://www.shopup.org/) [@gwthm-in]
- [Tails.com](https://tails.com/gb/) [@alanmcruickshank]
- [THE ICONIC](https://theiconic.com.au/) [@ksaagariconic]
- [Utair](https://www.utair.ru) [@utair-digital]
- [VkusVill](https://vkusvill.ru/) [@ETselikov]
- [Zalando](https://www.zalando.com) [@dmigo]
- [Zalora](https://www.zalora.com) [@ksaagariconic]
- [Zepto](https://www.zeptonow.com/) [@gwthm-in]
### Enterprise Technology
- [A3Data](https://a3data.com.br) [@neylsoncrepalde]
- [Analytics Aura](https://analyticsaura.com/) [@Analytics-Aura]
- [Apollo GraphQL](https://www.apollographql.com/) [@evans]
- [Astronomer](https://www.astronomer.io) [@ryw]
- [Avesta Technologies](https://avestatechnologies.com/) [@TheRum]
- [Caizin](https://caizin.com/) [@tejaskatariya]
- [Canonical](https://canonical.com)
- [Careem](https://www.careem.com/) [@samraHanif0340]
- [Cloudsmith](https://cloudsmith.io) [@alancarson]
- [Cyberhaven](https://www.cyberhaven.com/) [@toliver-ch]
- [Deepomatic](https://deepomatic.com/) [@Zanoellia]
- [Dial Once](https://www.dial-once.com/)
- [Dremio](https://dremio.com) [@narendrans]
- [EFinance](https://www.efinance.com.eg) [@habeeb556]
- [Elestio](https://elest.io/) [@kaiwalyakoparkar]
- [ELMO Cloud HR & Payroll](https://elmosoftware.com.au/)
- [Endress+Hauser](https://www.endress.com/) [@rumbin]
- [FBK - ICT center](https://ict.fbk.eu)
- [Formbricks](https://formbricks.com)
- [Gavagai](https://gavagai.io) [@gavagai-corp]
- [GfK Data Lab](https://www.gfk.com/home) [@mherr]
- [HPE](https://www.hpe.com/in/en/home.html) [@anmol-hpe]
- [Hydrolix](https://www.hydrolix.io/)
- [Intercom](https://www.intercom.com/) [@kate-gallo]
- [jampp](https://jampp.com/)
- [Konfío](https://konfio.mx) [@uis-rodriguez]
- [Mainstrat](https://mainstrat.com/)
- [mishmash io](https://mishmash.io/) [@mishmash-io]
- [Myra Labs](https://www.myralabs.com/) [@viksit]
- [Nielsen](https://www.nielsen.com/) [@amitNielsen]
- [Ona](https://ona.io) [@pld]
- [Orange](https://www.orange.com) [@icsu]
- [Oslandia](https://oslandia.com)
- [Oxylabs](https://oxylabs.io/) [@rytis-ulys]
- [Peak AI](https://www.peak.ai/) [@azhar22k]
- [PeopleDoc](https://www.people-doc.com) [@rodo]
- [PlaidCloud](https://www.plaidcloud.com)
- [Preset, Inc.](https://preset.io)
- [PubNub](https://pubnub.com) [@jzucker2]
- [ReadyTech](https://www.readytech.io)
- [Reward Gateway](https://www.rewardgateway.com)
- [RIADVICE](https://riadvice.tn) [@riadvice]
- [ScopeAI](https://www.getscopeai.com) [@iloveluce]
- [shipmnts](https://shipmnts.com)
- [Showmax](https://showmax.com) [@bobek]
- [SingleStore](https://www.singlestore.com/)
- [TechAudit](https://www.techaudit.info) [@ETselikov]
- [Tenable](https://www.tenable.com) [@dflionis]
- [Tentacle](https://www.linkedin.com/company/tentacle-cmi/) [@jdclarke5]
- [timbr.ai](https://timbr.ai/) [@semantiDan]
- [Tobii](https://www.tobii.com/) [@dwa]
- [Tooploox](https://www.tooploox.com/) [@jakubczaplicki]
- [Unvired](https://unvired.com) [@srinisubramanian]
- [Virtuoso QA](https://www.virtuosoqa.com)
- [Whale](https://whale.im)
- [Windsor.ai](https://www.windsor.ai/) [@octaviancorlade]
- [WinWin Network马上赢](https://brandct.cn/) [@wenbinye]
- [Zeta](https://www.zeta.tech/) [@shaikidris]
### Media & Entertainment
- [6play](https://www.6play.fr) [@CoryChaplin]
- [bilibili](https://www.bilibili.com) [@Moinheart]
- [BurdaForward](https://www.burda-forward.de/en/)
- [Douban](https://www.douban.com/) [@luchuan]
- [Kuaishou](https://www.kuaishou.com/) [@zhaoyu89730105]
- [Netflix](https://www.netflix.com/)
- [Prensa Iberica](https://www.prensaiberica.es/) [@zamar-roura]
- [TME QQMUSIC/WESING](https://www.tencentmusic.com/) [@shenyuanli,@marklaw]
- [Xite](https://xite.com/) [@shashankkoppar]
- [Zaihang](https://www.zaih.com/)
### Education
- [Aveti Learning](https://avetilearning.com/) [@TheShubhendra]
- [Brilliant.org](https://brilliant.org/)
- [Open edX](https://openedx.org/)
- [Platzi.com](https://platzi.com/)
- [Sunbird](https://www.sunbird.org/) [@eksteporg]
- [The GRAPH Network](https://thegraphnetwork.org/) [@fccoelho]
- [Udemy](https://www.udemy.com/) [@sungjuly]
- [VIPKID](https://www.vipkid.com.cn/) [@illpanda]
- [WikiMedia Foundation](https://wikimediafoundation.org) [@vg]
### Energy
- [Airboxlab](https://foobot.io) [@antoine-galataud]
- [DouroECI](https://www.douroeci.com/) [@nunohelibeires]
- [Safaricom](https://www.safaricom.co.ke/) [@mmutiso]
- [Scoot](https://scoot.co/) [@haaspt]
- [Wattbewerb](https://wattbewerb.de/) [@wattbewerb]
### Healthcare
- [Amino](https://amino.com) [@shkr]
- [Bluesquare](https://www.bluesquarehub.com/) [@madewulf]
- [Care](https://www.getcare.io/) [@alandao2021]
- [Living Goods](https://www.livinggoods.org) [@chelule]
- [Maieutical Labs](https://maieuticallabs.it) [@xrmx]
- [Medic](https://medic.org) [@1yuv]
- [REDCap Cloud](https://www.redcapcloud.com/)
- [TrustMedis](https://trustmedis.com/) [@famasya]
- [WeSure](https://www.wesure.cn/)
- [2070Health](https://2070health.com/)
### HR / Staffing
- [Swile](https://www.swile.co/) [@PaoloTerzi]
- [Symmetrics](https://www.symmetrics.fyi)
- [bluquist](https://bluquist.com/)
### Government
- [City of Ann Arbor, MI](https://www.a2gov.org/) [@sfirke]
- [RIS3 Strategy of CZ, MIT CR](https://www.ris3.cz/) [@RIS3CZ]
- [NRLM - Sarathi, India](https://pib.gov.in/PressReleasePage.aspx?PRID=1999586)
### Travel
- [Agoda](https://www.agoda.com/) [@lostseaway, @maiake, @obombayo]
- [HomeToGo](https://hometogo.com/) [@pedromartinsteenstrup]
- [Skyscanner](https://www.skyscanner.net/) [@cleslie, @stanhoucke]
### Others
- [10Web](https://10web.io/)
- [AI inside](https://inside.ai/en/)
- [Automattic](https://automattic.com/) [@Khrol, @Usiel]
- [Dropbox](https://www.dropbox.com/) [@bkyryliuk]
- [Flowbird](https://flowbird.com) [@EmmanuelCbd]
- [GEOTAB](https://www.geotab.com) [@JZ6]
- [Grassroot](https://www.grassrootinstitute.org/)
- [Increff](https://www.increff.com/) [@ishansinghania]
- [komoot](https://www.komoot.com/) [@christophlingg]
- [Let's Roam](https://www.letsroam.com/)
- [Machrent SA](https://www.machrent.com/)
- [Onebeat](https://1beat.com/) [@GuyAttia]
- [X](https://x.com/)
- [VLMedia](https://www.vlmedia.com.tr/) [@ibotheperfect]
- [Yahoo!](https://yahoo.com/)

678
RESOURCES/INTHEWILD.yaml Normal file
View File

@@ -0,0 +1,678 @@
# 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.
# Apache Superset Users in the Wild
#
# To add your organization:
# 1. Find the appropriate category (or add a new one)
# 2. Add an entry with your organization details
# 3. Optionally add a logo file to docs/static/img/logos/
#
# Required fields:
# - name: Your organization name
# - url: Link to your organization's website
#
# Optional fields:
# - logo: Filename of logo in docs/static/img/logos/ (e.g., "mycompany.svg")
# - contributors: List of GitHub usernames who contributed (e.g., ["@username"])
categories:
Sharing Economy:
- name: Airbnb
url: https://github.com/airbnb
- name: Faasos
url: https://faasos.com/
contributors: ["@shashanksingh"]
- name: Free2Move
url: https://www.free2move.com/
contributors: ["@PaoloTerzi"]
- name: Hostnfly
url: https://www.hostnfly.com/
contributors: ["@alexisrosuel"]
- name: Lime
url: https://www.li.me/
contributors: ["@cxmcc"]
- name: Lyft
url: https://www.lyft.com/
- name: Ontruck
url: https://www.ontruck.com/
Financial Services:
- name: Aktia Bank plc
url: https://www.aktia.com
- name: American Express
url: https://www.americanexpress.com
contributors: ["@TheLastSultan"]
- name: bumper
url: https://www.bumper.co/
contributors: ["@vasu-ram", "@JamiePercival"]
- name: Cape Crypto
url: https://capecrypto.com
- name: Capital Service S.A.
url: https://capitalservice.pl
contributors: ["@pkonarzewski"]
- name: Clark.de
url: https://clark.de/
- name: EnquiryLabs
url: https://www.enquirylabs.co.uk
- name: Europace
url: https://europace.de
- name: KarrotPay
url: https://www.daangnpay.com/
- name: Remita
url: https://remita.net
contributors: ["@mujibishola"]
- name: Taveo
url: https://www.taveo.com
contributors: ["@codek"]
- name: Unit
url: https://www.unit.co/about-us
contributors: ["@amitmiran137"]
- name: Wise
url: https://wise.com
contributors: ["@koszti"]
- name: Xendit
url: https://xendit.co/
contributors: ["@LieAlbertTriAdrian"]
- name: Cover Genius
url: https://covergenius.com/
Gaming:
- name: Popoko VM Games Studio
url: https://popoko.live
E-Commerce:
- name: AiHello
url: https://www.aihello.com
contributors: ["@ganeshkrishnan1"]
- name: Bazaar Technologies
url: https://www.bazaartech.com
contributors: ["@umair-abro"]
- name: Blinkit
url: https://www.blinkit.com/
contributors: ["@amsharm2"]
- name: Dragonpass
url: https://www.dragonpass.com.cn/
contributors: ["@zhxjdwh"]
- name: Dropit Shopping
url: https://www.dropit.shop/
contributors: ["@dropit-dev"]
- name: Fanatics
url: https://www.fanatics.com/
contributors: ["@coderfender"]
- name: Fordeal
url: https://www.fordeal.com
contributors: ["@Renkai"]
- name: Fynd
url: https://www.fynd.com/
contributors: ["@darpanjain07"]
- name: GFG - Global Fashion Group
url: https://global-fashion-group.com
contributors: ["@ksaagariconic"]
- name: GoTo/Gojek
url: https://www.gojek.io/
contributors: ["@gwthm-in"]
- name: HuiShouBao
url: https://www.huishoubao.com/
contributors: ["@Yukinoshita-Yukino"]
- name: Now
url: https://www.now.vn/
contributors: ["@davidkohcw"]
- name: Qunar
url: https://www.qunar.com/
contributors: ["@flametest"]
- name: Rakuten Viki
url: https://www.viki.com
- name: Shopee
url: https://shopee.sg
contributors: ["@xiaohanyu"]
- name: Shopkick
url: https://www.shopkick.com
contributors: ["@LAlbertalli"]
- name: ShopUp
url: https://www.shopup.org/
contributors: ["@gwthm-in"]
- name: Tails.com
url: https://tails.com/gb/
contributors: ["@alanmcruickshank"]
- name: THE ICONIC
url: https://theiconic.com.au/
contributors: ["@ksaagariconic"]
- name: Utair
url: https://www.utair.ru
contributors: ["@utair-digital"]
- name: VkusVill
url: https://vkusvill.ru/
contributors: ["@ETselikov"]
- name: Zalando
url: https://www.zalando.com
contributors: ["@dmigo"]
- name: Zalora
url: https://www.zalora.com
contributors: ["@ksaagariconic"]
- name: Zepto
url: https://www.zeptonow.com/
contributors: ["@gwthm-in"]
Enterprise Technology:
- name: A3Data
url: https://a3data.com.br
contributors: ["@neylsoncrepalde"]
- name: Analytics Aura
url: https://analyticsaura.com/
contributors: ["@Analytics-Aura"]
- name: Apollo GraphQL
url: https://www.apollographql.com/
contributors: ["@evans"]
- name: Astronomer
url: https://www.astronomer.io
contributors: ["@ryw"]
- name: Avesta Technologies
url: https://avestatechnologies.com/
contributors: ["@TheRum"]
- name: Caizin
url: https://caizin.com/
contributors: ["@tejaskatariya"]
- name: Canonical
url: https://canonical.com
- name: Careem
url: https://www.careem.com/
contributors: ["@samraHanif0340"]
- name: Cloudsmith
url: https://cloudsmith.io
contributors: ["@alancarson"]
- name: Cyberhaven
url: https://www.cyberhaven.com/
contributors: ["@toliver-ch"]
- name: Deepomatic
url: https://deepomatic.com/
contributors: ["@Zanoellia"]
- name: Dial Once
url: https://www.dial-once.com/
- name: Dremio
url: https://dremio.com
contributors: ["@narendrans"]
- name: EFinance
url: https://www.efinance.com.eg
contributors: ["@habeeb556"]
- name: Elestio
url: https://elest.io/
contributors: ["@kaiwalyakoparkar"]
- name: ELMO Cloud HR & Payroll
url: https://elmosoftware.com.au/
- name: Endress+Hauser
url: https://www.endress.com/
contributors: ["@rumbin"]
- name: FBK - ICT center
url: https://ict.fbk.eu
- name: Formbricks
url: https://formbricks.com
- name: Gavagai
url: https://gavagai.io
contributors: ["@gavagai-corp"]
- name: GfK Data Lab
url: https://www.gfk.com/home
contributors: ["@mherr"]
- name: HPE
url: https://www.hpe.com/in/en/home.html
contributors: ["@anmol-hpe"]
- name: Hydrolix
url: https://www.hydrolix.io/
- name: Intercom
url: https://www.intercom.com/
contributors: ["@kate-gallo"]
- name: jampp
url: https://jampp.com/
- name: Konfío
url: https://konfio.mx
contributors: ["@uis-rodriguez"]
- name: Mainstrat
url: https://mainstrat.com/
- name: mishmash io
url: https://mishmash.io/
contributors: ["@mishmash-io"]
- name: Myra Labs
url: https://www.myralabs.com/
contributors: ["@viksit"]
- name: Nielsen
url: https://www.nielsen.com/
contributors: ["@amitNielsen"]
- name: Ona
url: https://ona.io
contributors: ["@pld"]
- name: Orange
url: https://www.orange.com
contributors: ["@icsu"]
- name: Oslandia
url: https://oslandia.com
- name: Oxylabs
url: https://oxylabs.io/
contributors: ["@rytis-ulys"]
- name: Peak AI
url: https://www.peak.ai/
contributors: ["@azhar22k"]
- name: PeopleDoc
url: https://www.people-doc.com
contributors: ["@rodo"]
- name: PlaidCloud
url: https://plaidcloud.com
logo: plaidcloud.svg
contributors: ["@rad-pat"]
- name: Preset, Inc.
url: https://preset.io
logo: preset.svg
contributors: ["@mistercrunch", "@betodealmeida", "@dpgaspar", "@rusackas", "@sadpandajoe", "@Vitor-Avila", "@kgabryje", "@geido", "@eschutho", "@Antonio-RiveroMartnez", "@yousoph"]
- name: PubNub
url: https://pubnub.com
contributors: ["@jzucker2"]
- name: ReadyTech
url: https://www.readytech.io
- name: Reward Gateway
url: https://www.rewardgateway.com
- name: RIADVICE
url: https://riadvice.tn
contributors: ["@riadvice"]
- name: ScopeAI
url: https://www.getscopeai.com
contributors: ["@iloveluce"]
- name: shipmnts
url: https://shipmnts.com
- name: Showmax
url: https://showmax.com
contributors: ["@bobek"]
- name: SingleStore
url: https://www.singlestore.com/
- name: TechAudit
url: https://www.techaudit.info
contributors: ["@ETselikov"]
- name: Tenable
url: https://www.tenable.com
contributors: ["@dflionis"]
- name: Tentacle
url: https://www.linkedin.com/company/tentacle-cmi/
contributors: ["@jdclarke5"]
- name: timbr.ai
url: https://timbr.ai/
contributors: ["@semantiDan"]
- name: Tobii
url: https://www.tobii.com/
contributors: ["@dwa"]
- name: Tooploox
url: https://www.tooploox.com/
contributors: ["@jakubczaplicki"]
- name: Unvired
url: https://unvired.com
contributors: ["@srinisubramanian"]
- name: UserGuiding
url: https://userguiding.com/
logo: userguiding.svg
contributors: ["@tzercin"]
- name: Virtuoso QA
url: https://www.virtuosoqa.com
- name: Whale
url: https://whale.im
- name: Windsor.ai
url: https://www.windsor.ai/
contributors: ["@octaviancorlade"]
- name: WinWin Network马上赢
url: https://brandct.cn/
contributors: ["@wenbinye"]
- name: Zeta
url: https://www.zeta.tech/
contributors: ["@shaikidris"]
Media & Entertainment:
- name: 6play
url: https://www.6play.fr
contributors: ["@CoryChaplin"]
- name: bilibili
url: https://www.bilibili.com
contributors: ["@Moinheart"]
- name: BurdaForward
url: https://www.burda-forward.de/en/
- name: Douban
url: https://www.douban.com/
contributors: ["@luchuan"]
- name: Kuaishou
url: https://www.kuaishou.com/
contributors: ["@zhaoyu89730105"]
- name: Netflix
url: https://www.netflix.com/
- name: Prensa Iberica
url: https://www.prensaiberica.es/
contributors: ["@zamar-roura"]
- name: TME QQMUSIC/WESING
url: https://www.tencentmusic.com/
contributors: ["@shenyuanli", "@marklaw"]
- name: Xite
url: https://xite.com/
contributors: ["@shashankkoppar"]
- name: Zaihang
url: https://www.zaih.com/
Education:
- name: Aveti Learning
url: https://avetilearning.com/
contributors: ["@TheShubhendra"]
- name: Brilliant.org
url: https://brilliant.org/
- name: Cirrus Assessment
url: https://cirrusassessment.com/
logo: cirrus.svg
contributors: ["@jeroenhabets", "@ddmm-white", "@paulrocost"]
- name: Open edX
url: https://openedx.org/
- name: Platzi.com
url: https://platzi.com/
- name: Sunbird
url: https://www.sunbird.org/
contributors: ["@eksteporg"]
- name: The GRAPH Network
url: https://thegraphnetwork.org/
contributors: ["@fccoelho"]
- name: Udemy
url: https://www.udemy.com/
contributors: ["@sungjuly"]
- name: VIPKID
url: https://www.vipkid.com.cn/
contributors: ["@illpanda"]
- name: WikiMedia Foundation
url: https://wikimediafoundation.org
contributors: ["@vg"]
Energy:
- name: Airboxlab
url: https://foobot.io
contributors: ["@antoine-galataud"]
- name: DouroECI
url: https://www.douroeci.com/
contributors: ["@nunohelibeires"]
- name: Safaricom
url: https://www.safaricom.co.ke/
contributors: ["@mmutiso"]
- name: Scoot
url: https://scoot.co/
contributors: ["@haaspt"]
- name: Wattbewerb
url: https://wattbewerb.de/
contributors: ["@wattbewerb"]
- name: Rogow
url: https://rogow.com.br/
contributors: ["@nilmonto"]
Healthcare:
- name: Amino
url: https://amino.com
contributors: ["@shkr"]
- name: Bluesquare
url: https://www.bluesquarehub.com/
contributors: ["@madewulf"]
- name: Care
url: https://www.getcare.io/
contributors: ["@alandao2021"]
- name: Living Goods
url: https://www.livinggoods.org
contributors: ["@chelule"]
- name: Maieutical Labs
url: https://maieuticallabs.it
contributors: ["@xrmx"]
- name: Medic
url: https://medic.org
contributors: ["@1yuv"]
- name: REDCap Cloud
url: https://www.redcapcloud.com/
- name: TrustMedis
url: https://trustmedis.com/
contributors: ["@famasya"]
- name: WeSure
url: https://www.wesure.cn/
- name: 2070Health
url: https://2070health.com/
HR / Staffing:
- name: Swile
url: https://www.swile.co/
contributors: ["@PaoloTerzi"]
- name: Symmetrics
url: https://www.symmetrics.fyi
- name: bluquist
url: https://bluquist.com/
Government:
- name: City of Ann Arbor, MI
url: https://www.a2gov.org/
contributors: ["@sfirke"]
- name: RIS3 Strategy of CZ, MIT CR
url: https://www.ris3.cz/
contributors: ["@RIS3CZ"]
- name: NRLM - Sarathi, India
url: https://pib.gov.in/PressReleasePage.aspx?PRID=1999586
Mobile Software:
- name: VLMedia
url: https://www.vlmedia.com.tr
logo: vlmedia.svg
contributors: ["@iercan"]
Travel:
- name: Agoda
url: https://www.agoda.com/
contributors: ["@lostseaway", "@maiake", "@obombayo"]
- name: HomeToGo
url: https://hometogo.com/
contributors: ["@pedromartinsteenstrup"]
- name: Skyscanner
url: https://www.skyscanner.net/
contributors: ["@cleslie", "@stanhoucke"]
Logistics:
- name: Stockarea
url: https://stockarea.io
Others:
- name: 10Web
url: https://10web.io/
- name: AI inside
url: https://inside.ai/en/
- name: Automattic
url: https://automattic.com/
contributors: ["@Khrol", "@Usiel"]
- name: Dropbox
url: https://www.dropbox.com/
contributors: ["@bkyryliuk"]
- name: Flowbird
url: https://flowbird.com
contributors: ["@EmmanuelCbd"]
- name: GEOTAB
url: https://www.geotab.com
contributors: ["@JZ6"]
- name: Grassroot
url: https://www.grassrootinstitute.org/
- name: HOLLYLAND猛玛
url: https://www.hollyland.com
logo: hollyland猛玛.svg
contributors: ["@hlyda0601"]
- name: Increff
url: https://www.increff.com/
contributors: ["@ishansinghania"]
- name: komoot
url: https://www.komoot.com/
contributors: ["@christophlingg"]
- name: Let's Roam
url: https://www.letsroam.com/
- name: Machrent SA
url: https://www.machrent.com/
- name: Onebeat
url: https://1beat.com/
contributors: ["@GuyAttia"]
- name: X
url: https://x.com/
- name: Yahoo!
url: https://yahoo.com/

View File

@@ -17,192 +17,193 @@ specific language governing permissions and limitations
under the License.
-->
| |Admin|Alpha|Gamma|SQL_LAB|
|--------------------------------------------------|---|---|---|---|
| Permission/role description |Admins have all possible rights, including granting or revoking rights from other users and altering other peoples slices and dashboards.|Alpha users have access to all data sources, but they cannot grant or revoke access from other users. They are also limited to altering the objects that they own. Alpha users can add and alter data sources.|Gamma users have limited access. They can only consume data coming from data sources they have been given access to through another complementary role. They only have access to view the slices and dashboards made from data sources that they have access to. Currently Gamma users are not able to alter or add data sources. We assume that they are mostly content consumers, though they can create slices and dashboards.|The sql_lab role grants access to SQL Lab. Note that while Admin users have access to all databases by default, both Alpha and Gamma users need to be given access on a per database basis.||
| can read on SavedQuery |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can write on SavedQuery |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can read on CssTemplate |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can write on CssTemplate |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can read on ReportSchedule |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can write on ReportSchedule |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can read on Chart |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can write on Chart |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can read on Annotation |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can write on Annotation |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can read on Dataset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can write on Dataset |:heavy_check_mark:|:heavy_check_mark:|O|O|
| can read on Log |:heavy_check_mark:|O|O|O|
| can write on Log |:heavy_check_mark:|O|O|O|
| can read on Dashboard |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can write on Dashboard |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can read on Database |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can write on Database |:heavy_check_mark:|O|O|O|
| can read on Query |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can this form get on ResetPasswordView |:heavy_check_mark:|O|O|O|
| can this form post on ResetPasswordView |:heavy_check_mark:|O|O|O|
| can this form get on ResetMyPasswordView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can this form post on ResetMyPasswordView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can this form get on UserInfoEditView |:heavy_check_mark:|O|O|O|
| can this form post on UserInfoEditView |:heavy_check_mark:|O|O|O|
| can show on UserDBModelView |:heavy_check_mark:|O|O|O|
| can edit on UserDBModelView |:heavy_check_mark:|O|O|O|
| can delete on UserDBModelView |:heavy_check_mark:|O|O|O|
| can add on UserDBModelView |:heavy_check_mark:|O|O|O|
| can list on UserDBModelView |:heavy_check_mark:|O|O|O|
| can userinfo on UserDBModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| resetmypassword on UserDBModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| resetpasswords on UserDBModelView |:heavy_check_mark:|O|O|O|
| userinfoedit on UserDBModelView |:heavy_check_mark:|O|O|O|
| can show on RoleModelView |:heavy_check_mark:|O|O|O|
| can edit on RoleModelView |:heavy_check_mark:|O|O|O|
| can delete on RoleModelView |:heavy_check_mark:|O|O|O|
| can add on RoleModelView |:heavy_check_mark:|O|O|O|
| can list on RoleModelView |:heavy_check_mark:|O|O|O|
| copyrole on RoleModelView |:heavy_check_mark:|O|O|O|
| can get on OpenApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can show on SwaggerView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can get on MenuApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can list on AsyncEventsRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can invalidate on CacheRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can csv upload on Database |:heavy_check_mark:|O|O|O|
| can excel upload on Database |:heavy_check_mark:|O|O|O|
| can query form data on Api |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can query on Api |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can time range on Api |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can external metadata on Datasource |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can save on Datasource |:heavy_check_mark:|:heavy_check_mark:|O|O|
| can get on Datasource |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can my queries on SqlLab |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can log on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can import dashboards on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can schemas on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can sqllab history on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can publish on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can csv on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can slice on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can sync druid source on Superset |:heavy_check_mark:|O|O|O|
| can explore on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can approve on Superset |:heavy_check_mark:|O|O|O|
| can explore json on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can fetch datasource metadata on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can csrf token on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can sqllab on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can select star on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can warm up cache on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can sqllab table viz on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can available domains on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can request access on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can dashboard on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can post on TableSchemaView |:heavy_check_mark:|O|O|:heavy_check_mark:|
| can expanded on TableSchemaView |:heavy_check_mark:|O|O|:heavy_check_mark:|
| can delete on TableSchemaView |:heavy_check_mark:|O|O|:heavy_check_mark:|
| can get on TabStateView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can post on TabStateView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can delete query on TabStateView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can migrate query on TabStateView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can activate on TabStateView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can delete on TabStateView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can put on TabStateView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can read on SecurityRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| menu access on Security |:heavy_check_mark:|O|O|O|
| menu access on List Users |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on List Roles |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Action Log |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Manage |:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Annotation Layers |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on CSS Templates |:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Import Dashboards |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Data |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Databases |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Datasets |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Charts |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Dashboards |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on SQL Lab |:heavy_check_mark:|O|O|:heavy_check_mark:|
| menu access on SQL Editor |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| menu access on Saved Queries |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| menu access on Query Search |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| all datasource access on all_datasource_access |:heavy_check_mark:|:heavy_check_mark:|O|O|
| all database access on all_database_access |:heavy_check_mark:|:heavy_check_mark:|O|O|
| all query access on all_query_access |:heavy_check_mark:|O|O|O|
| can write on DynamicPlugin |:heavy_check_mark:|O|O|O|
| can edit on DynamicPlugin |:heavy_check_mark:|O|O|O|
| can list on DynamicPlugin |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can show on DynamicPlugin |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can download on DynamicPlugin |:heavy_check_mark:|O|O|O|
| can add on DynamicPlugin |:heavy_check_mark:|O|O|O|
| can delete on DynamicPlugin |:heavy_check_mark:|O|O|O|
| can external metadata by name on Datasource |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can get value on KV |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can store on KV |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can tagged objects on TagView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can suggestions on TagView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can get on TagView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can post on TagView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can delete on TagView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can edit on DashboardEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can list on DashboardEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can show on DashboardEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can add on DashboardEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can delete on DashboardEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| muldelete on DashboardEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|O|O|
| can edit on SliceEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can list on SliceEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can show on SliceEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can add on SliceEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can delete on SliceEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| muldelete on SliceEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|O|O|
| can edit on AlertModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can list on AlertModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can show on AlertModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can add on AlertModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can delete on AlertModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can list on AlertLogModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can show on AlertLogModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can list on AlertObservationModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can show on AlertObservationModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Row Level Security |:heavy_check_mark:|O|O|O|
| menu access on Access requests |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Home |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Plugins |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Dashboard Email Schedules |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Chart Emails |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Alerts |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Alerts & Report |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| menu access on Scan New Datasources |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can share dashboard on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can share chart on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can this form get on ColumnarToDatabaseView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can this form post on ColumnarToDatabaseView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can export on Chart |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can write on DashboardFilterStateRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can read on DashboardFilterStateRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can write on DashboardPermalinkRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can read on DashboardPermalinkRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can delete embedded on Dashboard |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can set embedded on Dashboard |:heavy_check_mark:|O|O|O|
| can export on Dashboard |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can get embedded on Dashboard |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can export on Database |:heavy_check_mark:|O|O|O|
| can export on Dataset |:heavy_check_mark:|:heavy_check_mark:|O|O|
| can write on ExploreFormDataRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can read on ExploreFormDataRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can write on ExplorePermalinkRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can read on ExplorePermalinkRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can export on ImportExportRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can import on ImportExportRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can export on SavedQuery |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
| can dashboard permalink on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can grant guest token on SecurityRestApi |:heavy_check_mark:|O|O|O|
| can read on AdvancedDataType |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can read on EmbeddedDashboard |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can duplicate on Dataset |:heavy_check_mark:|:heavy_check_mark:|O|O|
| can read on Explore |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can samples on Datasource |:heavy_check_mark:|:heavy_check_mark:|O|O|
| can read on AvailableDomains |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can get or create dataset on Dataset |:heavy_check_mark:|:heavy_check_mark:|O|O|
| can get column values on Datasource |:heavy_check_mark:|:heavy_check_mark:|O|O|
| can export csv on SQLLab |:heavy_check_mark:|O|O|:heavy_check_mark:|
| can get results on SQLLab |:heavy_check_mark:|O|O|:heavy_check_mark:|
| can execute sql query on SQLLab |:heavy_check_mark:|O|O|:heavy_check_mark:|
| can recent activity on Log |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| |Admin|Alpha|Gamma|Public|SQL_LAB|
|--------------------------------------------------|---|---|---|---|---|
| Permission/role description |Admins have all possible rights, including granting or revoking rights from other users and altering other people's slices and dashboards.|Alpha users have access to all data sources, but they cannot grant or revoke access from other users. They are also limited to altering the objects that they own. Alpha users can add and alter data sources.|Gamma users have limited access. They can only consume data coming from data sources they have been given access to through another complementary role. They only have access to view the slices and dashboards made from data sources that they have access to. Currently Gamma users are not able to alter or add data sources. We assume that they are mostly content consumers, though they can create slices and dashboards.|Public is the most restrictive built-in role, designed for anonymous/unauthenticated users viewing public dashboards. It provides minimal read-only access for dashboard viewing with interactive filters. Use `PUBLIC_ROLE_LIKE = "Public"` to apply these permissions to anonymous users.|The sql_lab role grants access to SQL Lab. Note that while Admin users have access to all databases by default, both Alpha and Gamma users need to be given access on a per database basis.||
| can read on SavedQuery |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can write on SavedQuery |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can read on CssTemplate |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can write on CssTemplate |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can read on ReportSchedule |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can write on ReportSchedule |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can read on Chart |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can write on Chart |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can read on Annotation |:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|O|
| can write on Annotation |:heavy_check_mark:|:heavy_check_mark:|O|O|O|
| can read on AnnotationLayerRestApi |:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|O|
| can read on Dataset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can write on Dataset |:heavy_check_mark:|:heavy_check_mark:|O|O|O|
| can read on Log |:heavy_check_mark:|O|O|O|O|
| can write on Log |:heavy_check_mark:|O|O|O|O|
| can read on Dashboard |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can write on Dashboard |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can read on Database |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can write on Database |:heavy_check_mark:|O|O|O|O|
| can read on Query |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can this form get on ResetPasswordView |:heavy_check_mark:|O|O|O|O|
| can this form post on ResetPasswordView |:heavy_check_mark:|O|O|O|O|
| can this form get on ResetMyPasswordView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can this form post on ResetMyPasswordView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can this form get on UserInfoEditView |:heavy_check_mark:|O|O|O|O|
| can this form post on UserInfoEditView |:heavy_check_mark:|O|O|O|O|
| can show on UserDBModelView |:heavy_check_mark:|O|O|O|O|
| can edit on UserDBModelView |:heavy_check_mark:|O|O|O|O|
| can delete on UserDBModelView |:heavy_check_mark:|O|O|O|O|
| can add on UserDBModelView |:heavy_check_mark:|O|O|O|O|
| can list on UserDBModelView |:heavy_check_mark:|O|O|O|O|
| can userinfo on UserDBModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| resetmypassword on UserDBModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| resetpasswords on UserDBModelView |:heavy_check_mark:|O|O|O|O|
| userinfoedit on UserDBModelView |:heavy_check_mark:|O|O|O|O|
| can show on RoleModelView |:heavy_check_mark:|O|O|O|O|
| can edit on RoleModelView |:heavy_check_mark:|O|O|O|O|
| can delete on RoleModelView |:heavy_check_mark:|O|O|O|O|
| can add on RoleModelView |:heavy_check_mark:|O|O|O|O|
| can list on RoleModelView |:heavy_check_mark:|O|O|O|O|
| copyrole on RoleModelView |:heavy_check_mark:|O|O|O|O|
| can get on OpenApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can show on SwaggerView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can get on MenuApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can list on AsyncEventsRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can invalidate on CacheRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can csv upload on Database |:heavy_check_mark:|O|O|O|O|
| can excel upload on Database |:heavy_check_mark:|O|O|O|O|
| can query form data on Api |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can query on Api |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can time range on Api |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can external metadata on Datasource |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can save on Datasource |:heavy_check_mark:|:heavy_check_mark:|O|O|O|
| can get on Datasource |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can my queries on SqlLab |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can log on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can import dashboards on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can schemas on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can sqllab history on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can publish on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can csv on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can slice on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can sync druid source on Superset |:heavy_check_mark:|O|O|O|O|
| can explore on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can approve on Superset |:heavy_check_mark:|O|O|O|O|
| can explore json on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can fetch datasource metadata on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can csrf token on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can sqllab on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can select star on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can warm up cache on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can sqllab table viz on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can available domains on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can request access on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can dashboard on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can post on TableSchemaView |:heavy_check_mark:|O|O|O|:heavy_check_mark:|
| can expanded on TableSchemaView |:heavy_check_mark:|O|O|O|:heavy_check_mark:|
| can delete on TableSchemaView |:heavy_check_mark:|O|O|O|:heavy_check_mark:|
| can get on TabStateView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can post on TabStateView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can delete query on TabStateView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can migrate query on TabStateView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can activate on TabStateView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can delete on TabStateView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can put on TabStateView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can read on SecurityRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| menu access on Security |:heavy_check_mark:|O|O|O|O|
| menu access on List Users |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on List Roles |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Action Log |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Manage |:heavy_check_mark:|:heavy_check_mark:|O|O|O|
| menu access on Annotation Layers |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on CSS Templates |:heavy_check_mark:|:heavy_check_mark:|O|O|O|
| menu access on Import Dashboards |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Data |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Databases |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Datasets |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Charts |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Dashboards |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on SQL Lab |:heavy_check_mark:|O|O|O|:heavy_check_mark:|
| menu access on SQL Editor |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| menu access on Saved Queries |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| menu access on Query Search |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| all datasource access on all_datasource_access |:heavy_check_mark:|:heavy_check_mark:|O|O|O|
| all database access on all_database_access |:heavy_check_mark:|:heavy_check_mark:|O|O|O|
| all query access on all_query_access |:heavy_check_mark:|O|O|O|O|
| can write on DynamicPlugin |:heavy_check_mark:|O|O|O|O|
| can edit on DynamicPlugin |:heavy_check_mark:|O|O|O|O|
| can list on DynamicPlugin |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can show on DynamicPlugin |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can download on DynamicPlugin |:heavy_check_mark:|O|O|O|O|
| can add on DynamicPlugin |:heavy_check_mark:|O|O|O|O|
| can delete on DynamicPlugin |:heavy_check_mark:|O|O|O|O|
| can external metadata by name on Datasource |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can get value on KV |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can store on KV |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can tagged objects on TagView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can suggestions on TagView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can get on TagView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can post on TagView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can delete on TagView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can edit on DashboardEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can list on DashboardEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can show on DashboardEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can add on DashboardEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can delete on DashboardEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| muldelete on DashboardEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|O|O|O|
| can edit on SliceEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can list on SliceEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can show on SliceEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can add on SliceEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can delete on SliceEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| muldelete on SliceEmailScheduleView |:heavy_check_mark:|:heavy_check_mark:|O|O|O|
| can edit on AlertModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can list on AlertModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can show on AlertModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can add on AlertModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can delete on AlertModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can list on AlertLogModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can show on AlertLogModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can list on AlertObservationModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can show on AlertObservationModelView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Row Level Security |:heavy_check_mark:|O|O|O|O|
| menu access on Access requests |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Home |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Plugins |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Dashboard Email Schedules |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Chart Emails |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Alerts |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Alerts & Report |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| menu access on Scan New Datasources |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can share dashboard on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can share chart on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can this form get on ColumnarToDatabaseView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can this form post on ColumnarToDatabaseView |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can export on Chart |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can write on DashboardFilterStateRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can read on DashboardFilterStateRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can write on DashboardPermalinkRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can read on DashboardPermalinkRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can delete embedded on Dashboard |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can set embedded on Dashboard |:heavy_check_mark:|O|O|O|O|
| can export on Dashboard |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can get embedded on Dashboard |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can export on Database |:heavy_check_mark:|O|O|O|O|
| can export on Dataset |:heavy_check_mark:|:heavy_check_mark:|O|O|O|
| can write on ExploreFormDataRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can read on ExploreFormDataRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can write on ExplorePermalinkRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can read on ExplorePermalinkRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can export on ImportExportRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can import on ImportExportRestApi |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can export on SavedQuery |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|:heavy_check_mark:|
| can dashboard permalink on Superset |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can grant guest token on SecurityRestApi |:heavy_check_mark:|O|O|O|O|
| can read on AdvancedDataType |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can read on EmbeddedDashboard |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|
| can duplicate on Dataset |:heavy_check_mark:|:heavy_check_mark:|O|O|O|
| can read on Explore |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can samples on Datasource |:heavy_check_mark:|:heavy_check_mark:|O|O|O|
| can read on AvailableDomains |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|
| can get or create dataset on Dataset |:heavy_check_mark:|:heavy_check_mark:|O|O|O|
| can get column values on Datasource |:heavy_check_mark:|:heavy_check_mark:|O|O|O|
| can export csv on SQLLab |:heavy_check_mark:|O|O|O|:heavy_check_mark:|
| can get results on SQLLab |:heavy_check_mark:|O|O|O|:heavy_check_mark:|
| can execute sql query on SQLLab |:heavy_check_mark:|O|O|O|:heavy_check_mark:|
| can recent activity on Log |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|O|O|

View File

@@ -125,8 +125,30 @@ See `superset/mcp_service/PRODUCTION.md` for deployment guides.
---
- [35621](https://github.com/apache/superset/pull/35621): The default hash algorithm has changed from MD5 to SHA-256 for improved security and FedRAMP compliance. This affects cache keys for thumbnails, dashboard digests, chart digests, and filter option names. Existing cached data will be invalidated upon upgrade. To opt out of this change and maintain backward compatibility, set `HASH_ALGORITHM = "md5"` in your `superset_config.py`.
- [33055](https://github.com/apache/superset/pull/33055): Upgrades Flask-AppBuilder to 5.0.0. The AUTH_OID authentication type has been deprecated and is no longer available as an option in Flask-AppBuilder. OpenID (OID) is considered a deprecated authentication protocol - if you are using AUTH_OID, you will need to migrate to an alternative authentication method such as OAuth, LDAP, or database authentication before upgrading.
- [35062](https://github.com/apache/superset/pull/35062): Changed the function signature of `setupExtensions` to `setupCodeOverrides` with options as arguments.
### Breaking Changes
- [36317](https://github.com/apache/superset/pull/36317): The `CUSTOM_FONT_URLS` configuration option has been removed. Use the new per-theme `fontUrls` token in `THEME_DEFAULT` or database-managed themes instead.
- **Before:**
```python
CUSTOM_FONT_URLS = [
"https://fonts.example.com/myfont.css",
]
```
- **After:**
```python
THEME_DEFAULT = {
"token": {
"fontUrls": [
"https://fonts.example.com/myfont.css",
],
# ... other tokens
}
}
```
## 6.0.0
- [33055](https://github.com/apache/superset/pull/33055): Upgrades Flask-AppBuilder to 5.0.0. The AUTH_OID authentication type has been deprecated and is no longer available as an option in Flask-AppBuilder. OpenID (OID) is considered a deprecated authentication protocol - if you are using AUTH_OID, you will need to migrate to an alternative authentication method such as OAuth, LDAP, or database authentication before upgrading.
- [34871](https://github.com/apache/superset/pull/34871): Fixed Jest test hanging issue from Ant Design v5 upgrade. MessageChannel is now mocked in test environment to prevent rc-overflow from causing Jest to hang. Test environment only - no production impact.
- [34782](https://github.com/apache/superset/pull/34782): Dataset exports now include the dataset ID in their file name (similar to charts and dashboards). If managing assets as code, make sure to rename existing dataset YAMLs to include the ID (and avoid duplicated files).
- [34536](https://github.com/apache/superset/pull/34536): The `ENVIRONMENT_TAG_CONFIG` color values have changed to support only Ant Design semantic colors. Update your `superset_config.py`:
@@ -143,35 +165,10 @@ Note: Pillow is now a required dependency (previously optional) to support image
- [33116](https://github.com/apache/superset/pull/33116) In Echarts Series charts (e.g. Line, Area, Bar, etc.) charts, the `x_axis_sort_series` and `x_axis_sort_series_ascending` form data items have been renamed with `x_axis_sort` and `x_axis_sort_asc`.
There's a migration added that can potentially affect a significant number of existing charts.
- [32317](https://github.com/apache/superset/pull/32317) The horizontal filter bar feature is now out of testing/beta development and its feature flag `HORIZONTAL_FILTER_BAR` has been removed.
- [31590](https://github.com/apache/superset/pull/31590) Marks the beginning of intricate work around supporting dynamic Theming, and breaks support for [THEME_OVERRIDES](https://github.com/apache/superset/blob/732de4ac7fae88e29b7f123b6cbb2d7cd411b0e4/superset/config.py#L671) in favor of a new theming system based on AntD V5. Likely this will be in disrepair until settling over the 5.x lifecycle.
- [31590](https://github.com/apache/superset/pull/31590) Marks the begining of intricate work around supporting dynamic Theming, and breaks support for [THEME_OVERRIDES](https://github.com/apache/superset/blob/732de4ac7fae88e29b7f123b6cbb2d7cd411b0e4/superset/config.py#L671) in favor of a new theming system based on AntD V5. Likely this will be in disrepair until settling over the 5.x lifecycle.
- [32432](https://github.com/apache/superset/pull/31260) Moves the List Roles FAB view to the frontend and requires `FAB_ADD_SECURITY_API` to be enabled in the configuration and `superset init` to be executed.
- [34319](https://github.com/apache/superset/pull/34319) Drill to Detail and Drill By is now supported in Embedded mode, and also with the `DASHBOARD_RBAC` FF. If you don't want to expose these features in Embedded / `DASHBOARD_RBAC`, make sure the roles used for Embedded / `DASHBOARD_RBAC`don't have the required permissions to perform D2D actions.
### Breaking Changes
#### CUSTOM_FONT_URLS removed
The `CUSTOM_FONT_URLS` configuration option has been removed. Use the new per-theme `fontUrls` token in `THEME_DEFAULT` or database-managed themes instead.
**Before (5.x):**
```python
CUSTOM_FONT_URLS = [
"https://fonts.example.com/myfont.css",
]
```
**After (6.0):**
```python
THEME_DEFAULT = {
"token": {
"fontUrls": [
"https://fonts.example.com/myfont.css",
],
# ... other tokens
}
}
```
## 5.0.0
- [31976](https://github.com/apache/superset/pull/31976) Removed the `DISABLE_LEGACY_DATASOURCE_EDITOR` feature flag. The previous value of the feature flag was `True` and now the feature is permanently removed.

View File

@@ -112,6 +112,8 @@ services:
superset-init-light:
condition: service_completed_successfully
volumes: *superset-volumes
ports:
- "${SUPERSET_PORT:-8088}:8088"
environment:
DATABASE_HOST: db-light
DATABASE_DB: superset_light
@@ -157,7 +159,7 @@ services:
environment:
# set this to false if you have perf issues running the npm i; npm run dev in-docker
# if you do so, you have to run this manually on the host, which should perform better!
BUILD_SUPERSET_FRONTEND_IN_DOCKER: true
BUILD_SUPERSET_FRONTEND_IN_DOCKER: false
NPM_RUN_PRUNE: false
SCARF_ANALYTICS: "${SCARF_ANALYTICS:-}"
# configuring the dev-server to use the host.docker.internal to connect to the backend

View File

@@ -54,10 +54,9 @@ services:
- path: docker/.env-local # optional override
required: false
image: nginx:latest
container_name: superset_nginx
restart: unless-stopped
ports:
- "80:80"
- "${NGINX_PORT:-80}:80"
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
@@ -66,10 +65,9 @@ services:
redis:
image: redis:7
container_name: superset_cache
restart: unless-stopped
ports:
- "127.0.0.1:6379:6379"
- "127.0.0.1:${REDIS_PORT:-6379}:6379"
volumes:
- redis:/data
@@ -80,10 +78,9 @@ services:
- path: docker/.env-local # optional override
required: false
image: postgres:16
container_name: superset_db
restart: unless-stopped
ports:
- "127.0.0.1:5432:5432"
- "127.0.0.1:${DATABASE_PORT:-5432}:5432"
volumes:
- db_home:/var/lib/postgresql/data
- ./docker/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
@@ -96,13 +93,12 @@ services:
required: false
build:
<<: *common-build
container_name: superset_app
command: ["/app/docker/docker-bootstrap.sh", "app"]
restart: unless-stopped
ports:
- 8088:8088
- ${SUPERSET_PORT:-8088}:8088
# When in cypress-mode ->
- 8081:8081
- ${CYPRESS_PORT:-8081}:8081
extra_hosts:
- "host.docker.internal:host-gateway"
user: *superset-user
@@ -114,10 +110,9 @@ services:
SUPERSET__SQLALCHEMY_EXAMPLES_URI: "duckdb:////app/data/examples.duckdb"
superset-websocket:
container_name: superset_websocket
build: ./superset-websocket
ports:
- 8080:8080
- ${WEBSOCKET_PORT:-8080}:8080
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
@@ -149,7 +144,6 @@ services:
superset-init:
build:
<<: *common-build
container_name: superset_init
command: ["/app/docker/docker-init.sh"]
env_file:
- path: docker/.env # default
@@ -186,9 +180,10 @@ services:
SCARF_ANALYTICS: "${SCARF_ANALYTICS:-}"
# configuring the dev-server to use the host.docker.internal to connect to the backend
superset: "http://superset:8088"
# Bind to all interfaces so Docker port mapping works
WEBPACK_DEVSERVER_HOST: "0.0.0.0"
ports:
- "127.0.0.1:9000:9000" # exposing the dynamic webpack dev server
container_name: superset_node
- "127.0.0.1:${NODE_PORT:-9000}:9000" # exposing the dynamic webpack dev server
command: ["/app/docker/docker-frontend.sh"]
env_file:
- path: docker/.env # default
@@ -200,7 +195,6 @@ services:
superset-worker:
build:
<<: *common-build
container_name: superset_worker
command: ["/app/docker/docker-bootstrap.sh", "worker"]
env_file:
- path: docker/.env # default
@@ -226,7 +220,6 @@ services:
superset-worker-beat:
build:
<<: *common-build
container_name: superset_worker_beat
command: ["/app/docker/docker-bootstrap.sh", "beat"]
env_file:
- path: docker/.env # default
@@ -244,7 +237,6 @@ services:
superset-tests-worker:
build:
<<: *common-build
container_name: superset_tests_worker
command: ["/app/docker/docker-bootstrap.sh", "worker"]
env_file:
- path: docker/.env # default

View File

@@ -21,6 +21,15 @@ PYTHONUNBUFFERED=1
COMPOSE_PROJECT_NAME=superset
DEV_MODE=true
# Port configuration (override in .env-local for multiple instances)
# NGINX_PORT=80
# SUPERSET_PORT=8088
# NODE_PORT=9000
# WEBSOCKET_PORT=8080
# CYPRESS_PORT=8081
# DATABASE_PORT=5432
# REDIS_PORT=6379
# database configurations (do not modify)
DATABASE_DB=superset
DATABASE_HOST=db

39
docker/.env-local.example Normal file
View File

@@ -0,0 +1,39 @@
#
# 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.
#
# -----------------------------------------------------------------------
# Example .env-local file for running multiple Superset instances
# Copy this file to .env-local and customize for your setup
# -----------------------------------------------------------------------
# Unique project name prevents container/volume conflicts between clones
# Each clone should have a different name (e.g., superset-pr123, superset-feature-x)
COMPOSE_PROJECT_NAME=superset-dev2
# Port offsets for running multiple instances simultaneously
# Instance 1 (default): 80, 8088, 9000, 8080, 8081, 5432, 6379
# Instance 2 example: 81, 8089, 9001, 8082, 8083, 5433, 6380
NGINX_PORT=81
SUPERSET_PORT=8089
NODE_PORT=9001
WEBSOCKET_PORT=8082
CYPRESS_PORT=8083
DATABASE_PORT=5433
REDIS_PORT=6380
# For verbose logging during development:
# SUPERSET_LOG_LEVEL=debug

View File

@@ -77,6 +77,34 @@ To run the container, simply run: `docker compose up`
After waiting several minutes for Superset initialization to finish, you can open a browser and view [`http://localhost:8088`](http://localhost:8088)
to start your journey.
### Running Multiple Instances
If you need to run multiple Superset instances simultaneously (e.g., different branches or clones), use the make targets which automatically find available ports:
```bash
make up
```
This automatically:
- Generates a unique project name from your directory
- Finds available ports (incrementing from defaults if in use)
- Displays the assigned URLs before starting
Available commands (run from repo root):
| Command | Description |
|---------|-------------|
| `make up` | Start services (foreground) |
| `make up-detached` | Start services (background) |
| `make down` | Stop all services |
| `make ps` | Show running containers |
| `make logs` | Follow container logs |
| `make nuke` | Stop, remove volumes & local images |
From a subdirectory, use: `make -C $(git rev-parse --show-toplevel) up`
**Important**: Always use these commands instead of plain `docker compose down`, which won't know the correct project name.
## Developing
While running, the container server will reload on modification of the Superset Python and JavaScript source code.

3
docs/.gitignore vendored
View File

@@ -23,3 +23,6 @@ docs/.zshrc
# Gets copied from the root of the project at build time (yarn start / yarn build)
docs/intro.md
# Generated badge images (downloaded at build time by remark-localize-badges plugin)
static/badges/

View File

@@ -139,6 +139,41 @@ docker volume rm superset_db_home
docker-compose up
```
### Running multiple instances
If you need to run multiple Superset clones simultaneously (e.g., testing different branches),
use `make up` instead of `docker compose up`:
```bash
make up
```
This automatically:
- Generates a unique project name from your directory name
- Finds available ports (incrementing from 8088, 9000, etc. if already in use)
- Displays the assigned URLs before starting
Each clone gets isolated containers and volumes, so you can run them side-by-side without conflicts.
Available commands (run from repo root):
| Command | Description |
|---------|-------------|
| `make up` | Start services (foreground) |
| `make up-detached` | Start services (background) |
| `make down` | Stop all services |
| `make ps` | Show running containers |
| `make logs` | Follow container logs |
| `make ports` | Show assigned URLs and ports |
| `make open` | Open browser to dev server |
| `make nuke` | Stop, remove volumes & local images |
From a subdirectory, use: `make -C $(git rev-parse --show-toplevel) up`
:::warning
Always use these commands instead of plain `docker compose down`, which won't know the correct project name for your instance.
:::
## GitHub Codespaces (Cloud Development)
GitHub Codespaces provides a complete, pre-configured development environment in the cloud. This is ideal for:

View File

@@ -138,19 +138,6 @@ The diagram shows:
3. **The host application** implements the APIs and manages extensions
4. **Extensions** integrate seamlessly with the host through well-defined interfaces
### Extension Dependencies
Extensions can depend on any combination of packages based on their needs. For example:
**Frontend-only extension** (e.g., a custom chart type):
- Depends on `@apache-superset/core` for UI components and React APIs
**Full-stack extension** (e.g., a custom SQL editor with new API endpoints):
- Depends on `@apache-superset/core` for frontend components
- Depends on `apache-superset-core` for backend APIs and models
This modular approach allows extension authors to choose exactly what they need while promoting consistency and reusability.
## Dynamic Module Loading
One of the most sophisticated aspects of the extension architecture is how frontend code is dynamically loaded at runtime using Webpack's Module Federation.
@@ -215,7 +202,6 @@ import {
authentication,
core,
commands,
environment,
extensions,
sqlLab,
} from 'src/extensions';
@@ -226,7 +212,6 @@ export default function setupExtensionsAPI() {
authentication,
core,
commands,
environment,
extensions,
sqlLab,
};
@@ -248,6 +233,7 @@ This architecture provides several key benefits:
Now that you understand the architecture, explore:
- **[Dependencies](./dependencies)** - Managing dependencies and understanding API stability
- **[Quick Start](./quick-start)** - Build your first extension
- **[Contribution Types](./contribution-types)** - What kinds of extensions you can build
- **[Development](./development)** - Project structure, APIs, and development workflow

View File

@@ -0,0 +1,131 @@
---
title: Alert
sidebar_label: Alert
---
<!--
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 { StoryWithControls } from '../../../src/components/StorybookWrapper';
import { Alert } from '@apache-superset/core/ui';
# Alert
Alert component for displaying important messages to users. Wraps Ant Design Alert with sensible defaults and improved accessibility.
## Live Example
<StoryWithControls
component={Alert}
props={{
closable: true,
type: 'info',
message: 'This is a sample alert message.',
description: 'Sample description for additional context.',
showIcon: true
}}
controls={[
{
name: 'type',
label: 'Type',
type: 'select',
options: [
'info',
'error',
'warning',
'success'
]
},
{
name: 'closable',
label: 'Closable',
type: 'boolean'
},
{
name: 'showIcon',
label: 'Show Icon',
type: 'boolean'
},
{
name: 'message',
label: 'Message',
type: 'text'
},
{
name: 'description',
label: 'Description',
type: 'text'
}
]}
/>
## Try It
Edit the code below to experiment with the component:
```tsx live
function Demo() {
return (
<Alert
closable
type="info"
message="This is a sample alert message."
description="Sample description for additional context."
showIcon
/>
);
}
```
## Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `closable` | `boolean` | `true` | Whether the Alert can be closed with a close button. |
| `type` | `string` | `"info"` | Type of the alert (e.g., info, error, warning, success). |
| `message` | `string` | `"This is a sample alert message."` | Message |
| `description` | `string` | `"Sample description for additional context."` | Description |
| `showIcon` | `boolean` | `true` | Whether to display an icon in the Alert. |
## Usage in Extensions
This component is available in the `@apache-superset/core/ui` package, which is automatically available to Superset extensions.
```tsx
import { Alert } from '@apache-superset/core/ui';
function MyExtension() {
return (
<Alert
closable
type="info"
message="This is a sample alert message."
/>
);
}
```
## Source Links
- [Story file](https://github.com/apache/superset/blob/master/superset-frontend/packages/superset-core/src/ui/components/Alert/Alert.stories.tsx)
- [Component source](https://github.com/apache/superset/blob/master/superset-frontend/packages/superset-core/src/ui/components/Alert/index.tsx)
---
*This page was auto-generated from the component's Storybook story.*

View File

@@ -0,0 +1,93 @@
---
title: Extension Components
sidebar_label: Overview
sidebar_position: 1
---
<!--
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.
-->
# Extension Components
These UI components are available to Superset extension developers through the `@apache-superset/core/ui` package. They provide a consistent look and feel with the rest of Superset and are designed to be used in extension panels, views, and other UI elements.
## Available Components
- [Alert](./alert)
## Usage
All components are exported from the `@apache-superset/core/ui` package:
```tsx
import { Alert } from '@apache-superset/core/ui';
export function MyExtensionPanel() {
return (
<Alert type="info">
Welcome to my extension!
</Alert>
);
}
```
## Adding New Components
Components in `@apache-superset/core/ui` are automatically documented here. To add a new extension component:
1. Add the component to `superset-frontend/packages/superset-core/src/ui/components/`
2. Export it from `superset-frontend/packages/superset-core/src/ui/components/index.ts`
3. Create a Storybook story with an `Interactive` export:
```tsx
export default {
title: 'Extension Components/MyComponent',
component: MyComponent,
parameters: {
docs: {
description: {
component: 'Description of the component...',
},
},
},
};
export const InteractiveMyComponent = (args) => <MyComponent {...args} />;
InteractiveMyComponent.args = {
variant: 'primary',
disabled: false,
};
InteractiveMyComponent.argTypes = {
variant: {
control: { type: 'select' },
options: ['primary', 'secondary'],
},
disabled: {
control: { type: 'boolean' },
},
};
```
4. Run `yarn start` in `docs/` - the page generates automatically!
## Interactive Documentation
For interactive examples with controls, visit the [Storybook](/storybook/?path=/docs/extension-components--docs).

View File

@@ -1,6 +1,6 @@
---
title: Contribution Types
sidebar_position: 4
sidebar_position: 5
---
<!--

View File

@@ -0,0 +1,166 @@
---
title: Dependencies
sidebar_position: 4
---
<!--
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.
-->
# Dependencies
This guide explains how to manage dependencies in your Superset extensions, including the difference between public APIs and internal code, and best practices for maintaining stable extensions.
## Core Packages vs Internal Code
Extensions run in the same context as Superset during runtime. This means extension developers can technically import any module from the Superset codebase, not just the public APIs. Understanding the distinction between public and internal code is critical for building maintainable extensions.
### Public APIs (Stable)
The core packages follow [semantic versioning](https://semver.org/) and provide stable, documented APIs:
| Package | Language | Description |
|---------|----------|-------------|
| `@apache-superset/core` | JavaScript/TypeScript | Frontend APIs, UI components, hooks, and utilities |
| `apache-superset-core` | Python | Backend APIs, models, DAOs, and utilities |
**Benefits of using core packages:**
- **Semantic versioning**: Breaking changes are communicated through version numbers
- **Documentation**: APIs are documented with clear usage examples
- **Stability commitment**: We strive to maintain backward compatibility
- **Type safety**: Full TypeScript and Python type definitions
### Internal Code (Unstable)
Any code that is not exported through the core packages is considered internal. This includes:
- Direct imports from `superset-frontend/src/` modules
- Direct imports from `superset/` Python modules (outside of `superset_core`)
- Undocumented functions, classes, or utilities
:::warning Use at Your Own Risk
Internal code can change at any time without notice. If you depend on internal modules, your extension may break when Superset is upgraded. There is no guarantee of backward compatibility for internal code.
:::
**Example of internal vs public imports:**
```typescript
// ✅ Public API - stable
import { Button, sqlLab } from '@apache-superset/core';
// ❌ Internal code - may break without notice
import { someInternalFunction } from 'src/explore/components/SomeComponent';
```
```python
# ✅ Public API - stable
from superset_core.api.models import Database
from superset_core.api.daos import DatabaseDAO
# ❌ Internal code - may break without notice
from superset.views.core import SomeInternalClass
```
## API Evolution
The core packages are still evolving. While we follow semantic versioning, the APIs may change as we add new extension points and refine existing ones based on community feedback.
**What this means for extension developers:**
- Check the release notes when upgrading Superset
- Test your extensions against new Superset versions before deploying
- Participate in discussions about API changes to influence the direction
- In some cases, using internal dependencies may be acceptable while the public API is being developed for your use case
### When Internal Dependencies May Be Acceptable
While public APIs are always preferred, there are situations where using internal code may be reasonable:
1. **Missing functionality**: The public API doesn't yet expose what you need
2. **Prototype/experimental extensions**: You're exploring capabilities before committing to a stable implementation
3. **Bridge period**: You need functionality that's planned for the public API but not yet released
In these cases, document your internal dependencies clearly and plan to migrate to public APIs when they become available.
## Core Library Dependencies
An important architectural principle of the Superset extension system is that **we do not provide abstractions on top of core dependencies** like React (frontend) or SQLAlchemy (backend).
### Why We Don't Abstract Core Libraries
Abstracting libraries like React or SQLAlchemy would:
- Create maintenance overhead keeping abstractions in sync with upstream
- Limit access to the full power of these libraries
- Add unnecessary abstraction layers
- Fragment the ecosystem with Superset-specific variants
### Depending on Core Libraries Directly
Extension developers should depend on and use core libraries directly:
**Frontend (examples):**
- [React](https://react.dev/) - UI framework
- [Ant Design](https://ant.design/) - UI component library (prefer Superset components from `@apache-superset/core/ui` when available to preserve visual consistency)
- [Emotion](https://emotion.sh/) - CSS-in-JS styling
- ...
**Backend (examples):**
- [SQLAlchemy](https://www.sqlalchemy.org/) - Database toolkit
- [Flask](https://flask.palletsprojects.com/) - Web framework
- [Flask-AppBuilder](https://flask-appbuilder.readthedocs.io/) - Application framework
- ...
:::info Version Compatibility
When Superset upgrades its core dependencies (e.g., a new major version of Ant Design or SQLAlchemy), extension developers should upgrade their extensions accordingly. This ensures compatibility and access to the latest features and security fixes.
:::
## API Versioning and Changelog
Once the extensions API reaches **v1**, we will maintain a dedicated `CHANGELOG.md` file to track all changes to the public APIs. This will include:
- New APIs and features
- Deprecation notices
- Breaking changes with migration guides
- Bug fixes affecting API behavior
Until then, monitor the Superset release notes and test your extensions with each new release.
## Best Practices
### Do
- **Prefer public APIs**: Always check if functionality exists in `@apache-superset/core` or `apache-superset-core` before using internal code
- **Pin versions**: Specify compatible Superset versions in your extension metadata
- **Test upgrades**: Verify your extension works with new Superset releases before deploying
- **Report missing APIs**: If you need functionality not in the public API, open a GitHub issue to request it
- **Use core libraries directly**: Leverage Ant Design, SQLAlchemy, and other core libraries directly
### Don't
- **Assume stability of internal code**: Internal modules can change or be removed in any release
- **Depend on implementation details**: Even if something works, it may not be supported
- **Skip upgrade testing**: Always test your extension against new Superset versions
- **Expect abstractions**: Use core dependencies directly rather than expecting Superset-specific abstractions
## Next Steps
- **[Architecture](./architecture)** - Understand the extension system design
- **[Development](./development)** - Learn about APIs and development workflow
- **[Quick Start](./quick-start)** - Build your first extension

View File

@@ -1,6 +1,6 @@
---
title: Deployment
sidebar_position: 6
sidebar_position: 7
---
<!--

View File

@@ -1,6 +1,6 @@
---
title: Development
sidebar_position: 5
sidebar_position: 6
---
<!--
@@ -130,10 +130,6 @@ export const getDatabases: () => Database[];
export const getTabs: () => Tab[];
export const onDidChangeEditorContent: Event<string>;
export const onDidClosePanel: Event<Panel>;
export const onDidChangeActivePanel: Event<Panel>;
export const onDidChangeTabTitle: Event<string>;
@@ -237,3 +233,73 @@ superset-extensions dev
✅ Manifest updated
👀 Watching for changes in: /dataset_references/frontend, /dataset_references/backend
```
## Contributing Extension-Compatible Components
Components in `@apache-superset/core` are automatically documented in the Developer Portal. Simply add a component to the package and it will appear in the extension documentation.
### Requirements
1. **Location**: The component must be in `superset-frontend/packages/superset-core/src/ui/components/`
2. **Exported**: The component must be exported from the package's `index.ts`
3. **Story**: The component must have a Storybook story
### Creating a Story for Your Component
Create a story file with an `Interactive` export that defines args and argTypes:
```typescript
// MyComponent.stories.tsx
import { MyComponent } from '.';
export default {
title: 'Extension Components/MyComponent',
component: MyComponent,
parameters: {
docs: {
description: {
component: 'A brief description of what this component does.',
},
},
},
};
// Define an interactive story with args
export const InteractiveMyComponent = (args) => <MyComponent {...args} />;
InteractiveMyComponent.args = {
variant: 'primary',
disabled: false,
};
InteractiveMyComponent.argTypes = {
variant: {
control: { type: 'select' },
options: ['primary', 'secondary', 'danger'],
},
disabled: {
control: { type: 'boolean' },
},
};
```
### How Documentation is Generated
When the docs site is built (`yarn start` or `yarn build` in the `docs/` directory):
1. The `generate-extension-components` script scans all stories in `superset-core`
2. For each story, it generates an MDX page with:
- Component description
- **Live interactive example** with controls extracted from `argTypes`
- **Editable code playground** for experimentation
- Props table from story `args`
- Usage code snippet
- Links to source files
3. Pages appear automatically in **Developer Portal → Extensions → Components**
### Best Practices
- **Use descriptive titles**: The title path determines the component's location in docs (e.g., `Extension Components/Alert`)
- **Define argTypes**: These become interactive controls in the documentation
- **Provide default args**: These populate the initial state of the live example
- **Write clear descriptions**: Help extension developers understand when to use each component

View File

@@ -1,7 +1,7 @@
---
title: MCP Integration
hide_title: true
sidebar_position: 7
sidebar_position: 8
version: 1
---

View File

@@ -45,6 +45,7 @@ Extensions can provide:
- **[Quick Start](./quick-start)** - Build your first extension with a complete walkthrough
- **[Architecture](./architecture)** - Design principles and system overview
- **[Dependencies](./dependencies)** - Managing dependencies and understanding API stability
- **[Contribution Types](./contribution-types)** - Available extension points
- **[Development](./development)** - Project structure, APIs, and development workflow
- **[Deployment](./deployment)** - Packaging and deploying extensions

View File

@@ -1,6 +1,6 @@
---
title: Community Extensions
sidebar_position: 9
sidebar_position: 10
---
<!--
@@ -28,10 +28,17 @@ This page serves as a registry of community-created Superset extensions. These e
## Extensions
| Name | Description | Author | Preview |
| --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Extensions API Explorer](https://github.com/michael-s-molina/superset-extensions/tree/main/api_explorer) | A SQL Lab panel that demonstrates the Extensions API by providing an interactive explorer for testing commands like getTabs, getCurrentTab, and getDatabases. Useful for extension developers to understand and experiment with the available APIs. | Michael S. Molina | <a href="/img/extensions/api_explorer.png" target="_blank"><img src="/img/extensions/api_explorer.png" alt="Extensions API Explorer" width="120" /></a> |
| [SQL Query Flow Visualizer](https://github.com/msyavuz/superset-sql-visualizer) | A SQL Lab panel that transforms SQL queries into interactive flow diagrams, helping developers and analysts understand query execution paths and data relationships.| Mehmet Salih Yavuz | <a href="/img/extensions/sql_flow_visualizer.png" target="_blank"><img src="/img/extensions/sql_flow_visualizer.png" alt="SQL Flow Visualizer" width="120" /></a> |
| Name | Description | Author | Preview |
| ------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Extensions API Explorer](https://github.com/michael-s-molina/superset-extensions/tree/main/api_explorer) | A SQL Lab panel that demonstrates the Extensions API by providing an interactive explorer for testing commands like getTabs, getCurrentTab, and getDatabases. Useful for extension developers to understand and experiment with the available APIs. | Michael S. Molina | <a href="/img/extensions/api-explorer.png" target="_blank"><img src="/img/extensions/api-explorer.png" alt="Extensions API Explorer" width="120" /></a> |
| [SQL Query Flow Visualizer](https://github.com/msyavuz/superset-sql-visualizer) | A SQL Lab panel that transforms SQL queries into interactive flow diagrams, helping developers and analysts understand query execution paths and data relationships. | Mehmet Salih Yavuz | <a href="/img/extensions/sql-flow-visualizer.png" target="_blank"><img src="/img/extensions/sql-flow-visualizer.png" alt="SQL Flow Visualizer" width="120" /></a> |
| [SQL Lab Export to Google Sheets](https://github.com/michael-s-molina/superset-extensions/tree/main/sqllab_gsheets) | A Superset extension that allows users to export SQL Lab query results directly to Google Sheets. | Michael S. Molina | <a href="/img/extensions/gsheets-export.png" target="_blank"><img src="/img/extensions/gsheets-export.png" alt="SQL Lab Export to Google Sheets" width="120" /></a> |
| [SQL Lab Export to Parquet](https://github.com/rusackas/superset-extensions/tree/main/sqllab_parquet) | Export SQL Lab query results directly to Apache Parquet format with Snappy compression. | Evan Rusackas | <a href="/img/extensions/parquet-export.png" target="_blank"><img src="/img/extensions/parquet-export.png" alt="SQL Lab Export to Parquet" width="120" /></a> |
| [SQL Lab Query Comparison](https://github.com/michael-s-molina/superset-extensions/tree/main/query_comparison) | A SQL Lab extension that enables side-by-side comparison of query results across different tabs, with GitHub-style diff visualization showing added/removed rows and columns. | Michael S. Molina | <a href="/img/extensions/query-comparison.png" target="_blank"><img src="/img/extensions/query-comparison.png" alt="Query Comparison" width="120" /></a> |
| [SQL Lab Result Stats](https://github.com/michael-s-molina/superset-extensions/tree/main/result_stats) | A SQL Lab extension that automatically computes statistics for query results, providing type-aware analysis including numeric metrics (min, max, mean, median, std dev), string analysis (length, empty counts), and date range information. | Michael S. Molina | <a href="/img/extensions/result-stats.png" target="_blank"><img src="/img/extensions/result-stats.png" alt="Result Stats" width="120" /></a> |
| [SQL Snippets](https://github.com/michael-s-molina/superset-extensions/tree/main/sql_snippets) | A SQL Lab extension that provides reusable SQL code snippets, enabling quick insertion of commonly used code blocks such as license headers, author information, and frequently used SQL patterns. | Michael S. Molina | <a href="/img/extensions/sql-snippets.png" target="_blank"><img src="/img/extensions/sql-snippets.png" alt="SQL Snippets" width="120" /></a> |
| [SQL Lab Query Estimator](https://github.com/michael-s-molina/superset-extensions/tree/main/query_estimator) | A SQL Lab panel that analyzes query execution plans to estimate resource impact, detect performance issues like Cartesian products and high-cost operations, and visualize the query plan tree. | Michael S. Molina | <a href="/img/extensions/query-estimator.png" target="_blank"><img src="/img/extensions/query-estimator.png" alt="Query Estimator" width="120" /></a> |
## How to Add Your Extension
To add your extension to this registry, submit a pull request to the [Apache Superset repository](https://github.com/apache/superset) with the following changes:

View File

@@ -1,6 +1,6 @@
---
title: Security
sidebar_position: 8
sidebar_position: 9
---
<!--

View File

@@ -1,6 +1,6 @@
---
title: Backend Style Guidelines
sidebar_position: 3
title: Overview
sidebar_position: 1
---
<!--
@@ -26,22 +26,22 @@ under the License.
This is a list of statements that describe how we do backend development in Superset. While they might not be 100% true for all files in the repo, they represent the gold standard we strive towards for backend quality and style.
* We use a monolithic Python/Flask/Flask-AppBuilder backend, with small single-responsibility satellite services where necessary.
* Files are generally organized by feature or object type. Within each domain, we can have api controllers, models, schemas, commands, and data access objects (dao).
* See: [Proposal for Improving Superset's Python Code Organization](https://github.com/apache/superset/issues/9077)
* API controllers use Marshmallow Schemas to serialize/deserialize data.
* Authentication and authorization are controlled by the [security manager](https://github.com/apache/superset/blob/master/superset/security/manager).
* We use Pytest for unit and integration tests. These live in the `tests` directory.
* We add tests for every new piece of functionality added to the backend.
* We use pytest fixtures to share setup between tests.
* We use sqlalchemy to access both Superset's application database, and users' analytics databases.
* We make changes backwards compatible whenever possible.
* If a change cannot be made backwards compatible, it goes into a major release.
* See: [Proposal For Semantic Versioning](https://github.com/apache/superset/issues/12566)
* We use Swagger for API documentation, with docs written inline on the API endpoint code.
* We prefer thin ORM models, putting shared functionality in other utilities.
* Several linters/checkers are used to maintain consistent code style and type safety: pylint, pypy, black, isort.
* `__init__.py` files are kept empty to avoid implicit dependencies.
- We use a monolithic Python/Flask/Flask-AppBuilder backend, with small single-responsibility satellite services where necessary.
- Files are generally organized by feature or object type. Within each domain, we can have api controllers, models, schemas, commands, and data access objects (DAO).
- See: [Proposal for Improving Superset's Python Code Organization](https://github.com/apache/superset/issues/9077)
- API controllers use Marshmallow Schemas to serialize/deserialize data.
- Authentication and authorization are controlled by the [security manager](https://github.com/apache/superset/blob/master/superset/security/manager).
- We use Pytest for unit and integration tests. These live in the `tests` directory.
- We add tests for every new piece of functionality added to the backend.
- We use pytest fixtures to share setup between tests.
- We use SQLAlchemy to access both Superset's application database, and users' analytics databases.
- We make changes backwards compatible whenever possible.
- If a change cannot be made backwards compatible, it goes into a major release.
- See: [Proposal For Semantic Versioning](https://github.com/apache/superset/issues/12566)
- We use Swagger for API documentation, with docs written inline on the API endpoint code.
- We prefer thin ORM models, putting shared functionality in other utilities.
- Several linters/checkers are used to maintain consistent code style and type safety: pylint, mypy, black, isort.
- `__init__.py` files are kept empty to avoid implicit dependencies.
## Code Organization

View File

@@ -1,6 +1,6 @@
---
title: DAO Style Guidelines and Best Practices
sidebar_position: 1
sidebar_position: 2
---
<!--
@@ -26,19 +26,29 @@ under the License.
A Data Access Object (DAO) is a pattern that provides an abstract interface to the SQLAlchemy Object Relational Mapper (ORM). The DAOs are critical as they form the building block of the application which are wrapped by the associated commands and RESTful API endpoints.
Currently there are numerous inconsistencies and violation of the DRY principal within the codebase as it relates to DAOs and ORMs—unnecessary commits, non-ACID transactions, etc.—which makes the code unnecessarily complex and convoluted. Addressing the underlying issues with the DAOs _should_ help simplify the downstream operations and improve the developer experience.
There are numerous inconsistencies and violations of the DRY principal within the codebase as it relates to DAOs and ORMs—unnecessary commits, non-ACID transactions, etc.—which makes the code unnecessarily complex and convoluted. Addressing the underlying issues with the DAOs _should_ help simplify the downstream operations and improve the developer experience.
To ensure consistency the following rules should be adhered to:
1. All database operations (including testing) should be defined within a DAO, i.e., there should not be any explicit `db.session.add`, `db.session.merge`, etc. calls outside of a DAO.
## Core Rules
2. A DAO should use `create`, `update`, `delete`, `upsert` terms—typical database operations which ensure consistency with commands—rather than action based terms like `save`, `saveas`, `override`, etc.
1. **All database operations (including testing) should be defined within a DAO**, i.e., there should not be any explicit `db.session.add`, `db.session.merge`, etc. calls outside of a DAO.
3. Sessions should be managed via a [context manager](https://docs.sqlalchemy.org/en/20/orm/session_transaction.html#begin-once) which auto-commits on success and rolls back on failure, i.e., there should be no explicit `db.session.commit` or `db.session.rollback` calls within the DAO.
2. **A DAO should use `create`, `update`, `delete`, `upsert` terms**—typical database operations which ensure consistency with commands—rather than action based terms like `save`, `saveas`, `override`, etc.
4. There should be a single atomic transaction representing the entirety of the operation, i.e., when creating a dataset with associated columns and metrics either all the changes succeed when the transaction is committed, or all the changes are undone when the transaction is rolled back. SQLAlchemy supports [nested transactions](https://docs.sqlalchemy.org/en/20/orm/session_transaction.html#nested-transaction) via the `begin_nested` method which can be nested—inline with how DAOs are invoked.
3. **Sessions should be managed via a [context manager](https://docs.sqlalchemy.org/en/20/orm/session_transaction.html#begin-once)** which auto-commits on success and rolls back on failure, i.e., there should be no explicit `db.session.commit` or `db.session.rollback` calls within the DAO.
5. The database layer should adopt a "shift left" mentality i.e., uniqueness/foreign key constraints, relationships, cascades, etc. should all be defined in the database layer rather than being enforced in the application layer.
4. **There should be a single atomic transaction representing the entirety of the operation**, i.e., when creating a dataset with associated columns and metrics either all the changes succeed when the transaction is committed, or all the changes are undone when the transaction is rolled back. SQLAlchemy supports [nested transactions](https://docs.sqlalchemy.org/en/20/orm/session_transaction.html#nested-transaction) via the `begin_nested` method which can be nested—inline with how DAOs are invoked.
5. **The database layer should adopt a "shift left" mentality** i.e., uniqueness/foreign key constraints, relationships, cascades, etc. should all be defined in the database layer rather than being enforced in the application layer.
6. **Exception-based validation**: Ask for forgiveness rather than permission. Try to perform the operation and rely on database constraints to verify that the model is acceptable, rather than pre-validating conditions.
7. **Bulk operations**: Provide bulk `create`, `update`, and `delete` methods where applicable for performance optimization.
8. **Sparse updates**: Updates should only modify explicitly defined attributes.
9. **Test transactions**: Tests should leverage nested transactions which should be rolled back on teardown, rather than deleting objects.
## DAO Implementation Examples

View File

@@ -30,21 +30,23 @@ This is an area to host resources and documentation supporting the evolution and
### Sentence case
Use sentence-case capitalization for everything in the UI (except these **).
Use sentence-case capitalization for everything in the UI (except these exceptions below).
Sentence case is predominantly lowercase. Capitalize only the initial character of the first word, and other words that require capitalization, like:
* **Proper nouns.** Objects in the product _are not_ considered proper nouns e.g. dashboards, charts, saved queries etc. Proprietary feature names eg. SQL Lab, Preset Manager _are_ considered proper nouns
* **Acronyms** (e.g. CSS, HTML)
* When referring to **UI labels that are themselves capitalized** from sentence case (e.g. page titles - Dashboards page, Charts page, Saved queries page, etc.)
* User input that is reflected in the UI. E.g. a user-named a dashboard tab
- **Proper nouns.** Objects in the product _are not_ considered proper nouns e.g. dashboards, charts, saved queries etc. Proprietary feature names eg. SQL Lab, Preset Manager _are_ considered proper nouns
- **Acronyms** (e.g. CSS, HTML)
- When referring to **UI labels that are themselves capitalized** from sentence case (e.g. page titles - Dashboards page, Charts page, Saved queries page, etc.)
- User input that is reflected in the UI. E.g. a user-named a dashboard tab
**Sentence case vs. Title case:** Title case: "A Dog Takes a Walk in Paris" Sentence case: "A dog takes a walk in Paris"
**Sentence case vs. Title case:**
- Title case: "A Dog Takes a Walk in Paris"
- Sentence case: "A dog takes a walk in Paris"
**Why sentence case?**
* It's generally accepted as the quickest to read
* It's the easiest form to distinguish between common and proper nouns
- It's generally accepted as the quickest to read
- It's the easiest form to distinguish between common and proper nouns
### How to refer to UI elements
@@ -52,21 +54,38 @@ When writing about a UI element, use the same capitalization as used in the UI.
For example, if an input field is labeled "Name" then you refer to this as the "Name input field". Similarly, if a button has the label "Save" in it, then it is correct to refer to the "Save button".
Where a product page is titled "Settings", you refer to this in writing as follows: "Edit your personal information on the Settings page".
Where a product page is titled "Settings", you refer to this in writing as follows:
"Edit your personal information on the Settings page".
Often a product page will have the same title as the objects it contains. In this case, refer to the page as it appears in the UI, and the objects as common nouns:
* Upload a dashboard on the Dashboards page
* Go to Dashboards
* View dashboard
* View all dashboards
* Upload CSS templates on the CSS templates page
* Queries that you save will appear on the Saved queries page
* Create custom queries in SQL Lab then create dashboards
- Upload a dashboard on the Dashboards page
- Go to Dashboards
- View dashboard
- View all dashboards
- Upload CSS templates on the CSS templates page
- Queries that you save will appear on the Saved queries page
- Create custom queries in SQL Lab then create dashboards
### **Exceptions to sentence case:**
### Exceptions to sentence case
1. Acronyms and abbreviations. Examples: URL, CSV, XML
1. **Acronyms and abbreviations.**
Examples: URL, CSV, XML, CSS, SQL, SSH, URI, NaN, CRON, CC, BCC
2. **Proper nouns and brand names.**
Examples: Apache, Superset, AntD JavaScript, GeoJSON, Slack, Google Sheets, SQLAlchemy
3. **Technical terms derived from proper nouns.**
Examples: Jinja, Gaussian, European (as in European time zone)
4. **Key names.** Capitalize button labels and UI elements as they appear in the product UI.
Examples: Shift (as in the keyboard button), Enter key
5. **Named queries or specific labeled items.**
Examples: Query A, Query B
6. **Database names.** Always capitalize names of database engines and connectors.
Examples: Presto, Trino, Drill, Hive, Google Sheets
## Button Design Guidelines
@@ -98,6 +117,32 @@ Primary buttons have a fourth style: dropdown.
| Tertiary | For less prominent actions; can be used in isolation or paired with a primary button |
| Destructive | For actions that could have destructive effects on the user's data |
### Format
#### Anatomy
Button text is centered using the Label style. Icons appear left of text when combined. If no text label exists, an icon must indicate the button's function.
#### Button size
- Default dimensions: 160px width × 32px height
- Text: 11px, Inter Medium, all caps
- Corners: 4px border radius
- Minimum padding: 8px around text
- Width can decrease if space is limited, but maintain minimum padding
#### Button groups
- Group related buttons to establish visual hierarchy
- Avoid overwhelming users with too many actions
- Limit calls to action; use tertiary/ghost buttons for layouts with 3+ actions
- Maintain consistent styles within groups when possible
- Space buttons 8px apart vertically or horizontally
#### Content guidelines
Button labels should be clear and predictable. Use the "\{verb\} + \{noun\}" format, except for common actions like "Done," "Close," "Cancel," "Add," or "Delete." This formula provides necessary context and aids translation, though compact UIs or localization needs may warrant exceptions.
## Error Message Design Guidelines
### Definition
@@ -128,10 +173,10 @@ In all cases, encountering errors increases user friction and frustration while
Select one pattern per error (e.g. do not implement an inline and banner pattern for the same error).
When the error... | Use...
---------------- | ------
Is directly related to a UI control | Inline error
Is not directly related to a UI control | Banner error
| When the error... | Use... |
|------------------|--------|
| Is directly related to a UI control | Inline error |
| Is not directly related to a UI control | Banner error |
#### Inline
@@ -146,3 +191,45 @@ Use the `LabeledErrorBoundInput` component for this error pattern.
##### Implementation details
- Where and when relevant, scroll the screen to the UI control with the error
- When multiple inline errors are present, scroll to the topmost error
#### Banner
Banner errors are used when the source of the error is not directly related to a UI control (text input, selector, etc.) such as a technical failure or a loading problem.
##### Anatomy
Use the `ErrorAlert` component for this error pattern.
1. **Headline** (optional): 1-2 word summary of the error
2. **Message**: What went wrong and what users should do next
3. **Expand option** (optional): "See more"/"See less"
4. **Details** (optional): Additional helpful context
5. **Modal** (optional): For spatial constraints using `ToastType.DANGER`
##### Implementation details
- Place the banner near the content area most relevant to the error
- For chart errors in Explore, use the chart area
- For modal errors, use the modal footer
- For app-wide errors, use the top of the screen
### Content guidelines
Effective error messages communicate:
1. What went wrong
2. What users should do next
Error messages should be:
- Clear and accurate, leaving no room for misinterpretation
- Short and concise
- Understandable to non-technical users
- Non-blaming and avoiding negative language
**Example:**
❌ "Cannot delete a datasource that has slices attached to it."
✅ "Please delete all charts using this dataset before deleting the dataset."

View File

@@ -1,6 +1,6 @@
---
title: Frontend Style Guidelines
sidebar_position: 2
title: Overview
sidebar_position: 1
---
<!--
@@ -26,19 +26,25 @@ under the License.
This is a list of statements that describe how we do frontend development in Superset. While they might not be 100% true for all files in the repo, they represent the gold standard we strive towards for frontend quality and style.
* We develop using TypeScript.
* We use React for building components, and Redux to manage app/global state.
* See: [Component Style Guidelines and Best Practices](./frontend/component-style-guidelines)
* We prefer functional components to class components and use hooks for local component state.
* We use [Ant Design](https://ant.design/) components from our component library whenever possible, only building our own custom components when it's required.
* We use [@emotion](https://emotion.sh/docs/introduction) to provide styling for our components, co-locating styling within component files.
* See: [Emotion Styling Guidelines and Best Practices](./frontend/emotion-styling-guidelines)
* We use Jest for unit tests, React Testing Library for component tests, and Cypress for end-to-end tests.
* See: [Testing Guidelines and Best Practices](./frontend/testing-guidelines)
* We add tests for every new component or file added to the frontend.
* We organize our repo so similar files live near each other, and tests are co-located with the files they test.
* We prefer small, easily testable files and components.
* We use ESLint and Prettier to automatically fix lint errors and format the code.
* We do not debate code formatting style in PRs, instead relying on automated tooling to enforce it.
* If there's not a linting rule, we don't have a rule!
* We use [React Storybook](https://storybook.js.org/) and [Applitools](https://applitools.com/) to help preview/test and stabilize our components
- We develop using TypeScript.
- See: [SIP-36](https://github.com/apache/superset/issues/9101)
- We use React for building components, and Redux to manage app/global state.
- See: [Component Style Guidelines and Best Practices](./frontend/component-style-guidelines)
- We prefer functional components to class components and use hooks for local component state.
- We use [Ant Design](https://ant.design/) components from our component library whenever possible, only building our own custom components when it's required.
- See: [SIP-48](https://github.com/apache/superset/issues/11283)
- We use [@emotion](https://emotion.sh/docs/introduction) to provide styling for our components, co-locating styling within component files.
- See: [SIP-37](https://github.com/apache/superset/issues/9145)
- See: [Emotion Styling Guidelines and Best Practices](./frontend/emotion-styling-guidelines)
- We use Jest for unit tests, React Testing Library for component tests, and Cypress for end-to-end tests.
- See: [SIP-56](https://github.com/apache/superset/issues/11830)
- See: [Testing Guidelines and Best Practices](../testing/testing-guidelines)
- We add tests for every new component or file added to the frontend.
- We organize our repo so similar files live near each other, and tests are co-located with the files they test.
- See: [SIP-61](https://github.com/apache/superset/issues/12098)
- We prefer small, easily testable files and components.
- We use ESLint and Prettier to automatically fix lint errors and format the code.
- We do not debate code formatting style in PRs, instead relying on automated tooling to enforce it.
- If there's not a linting rule, we don't have a rule!
- We use [React Storybook](https://storybook.js.org/) and [Applitools](https://applitools.com/) to help preview/test and stabilize our components
- A public Storybook with components from the `master` branch is available [here](https://apache-superset.github.io/superset-ui/?path=/story/*)

View File

@@ -1,6 +1,6 @@
---
title: Component Style Guidelines and Best Practices
sidebar_position: 1
sidebar_position: 2
---
<!--
@@ -35,7 +35,7 @@ This guide is intended primarily for reusable components. Whenever possible, all
- All components should be made to be reusable whenever possible
- All components should follow the structure and best practices as detailed below
## Directory and component structure
### Directory and component structure
```
superset-frontend/src/components
@@ -51,208 +51,142 @@ superset-frontend/src/components
**Component directory name:** Use `PascalCase` for the component directory name
**Storybook:** Components should come with a storybook file whenever applicable, with the following naming convention `{ComponentName}.stories.tsx`. More details about Storybook below
**Storybook:** Components should come with a storybook file whenever applicable, with the following naming convention `\{ComponentName\}.stories.tsx`. More details about Storybook below
**Unit and end-to-end tests:** All components should come with unit tests using Jest and React Testing Library. The file name should follow this naming convention `{ComponentName}.test.tsx.` Read the [Testing Guidelines and Best Practices](./testing-guidelines) for more details about tests
**Unit and end-to-end tests:** All components should come with unit tests using Jest and React Testing Library. The file name should follow this naming convention `\{ComponentName\}.test.tsx`. Read the [Testing Guidelines and Best Practices](../../testing/testing-guidelines) for more details
## Component Development Best Practices
**Reference naming:** Use `PascalCase` for React components and `camelCase` for component instances
### Use TypeScript
All new components should be written in TypeScript. This helps catch errors early and provides better development experience with IDE support.
```tsx
interface ComponentProps {
title: string;
isVisible?: boolean;
onClose?: () => void;
}
export const MyComponent: React.FC<ComponentProps> = ({
title,
isVisible = true,
onClose
}) => {
// Component implementation
};
**BAD:**
```jsx
import mainNav from './MainNav';
```
### Prefer Functional Components
**GOOD:**
```jsx
import MainNav from './MainNav';
```
Use functional components with hooks instead of class components:
**BAD:**
```jsx
const NavItem = <MainNav />;
```
**GOOD:**
```jsx
const navItem = <MainNav />;
```
**Component naming:** Use the file name as the component name
**BAD:**
```jsx
import MainNav from './MainNav/index';
```
**GOOD:**
```jsx
import MainNav from './MainNav';
```
**Props naming:** Do not use DOM related props for different purposes
**BAD:**
```jsx
<MainNav style="big" />
```
**GOOD:**
```jsx
<MainNav variant="big" />
```
**Importing dependencies:** Only import what you need
**BAD:**
```jsx
import * as React from "react";
```
**GOOD:**
```jsx
import React, { useState } from "react";
```
**Default VS named exports:** As recommended by [TypeScript](https://www.typescriptlang.org/docs/handbook/modules.html), "If a module's primary purpose is to house one specific export, then you should consider exporting it as a default export. This makes both importing and actually using the import a little easier". If you're exporting multiple objects, use named exports instead.
_As a default export_
```jsx
import MainNav from './MainNav';
```
_As a named export_
```jsx
import { MainNav, SecondaryNav } from './Navbars';
```
**ARIA roles:** Always make sure you are writing accessible components by using the official [ARIA roles](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques)
## Use TypeScript
All components should be written in TypeScript and their extensions should be `.ts` or `.tsx`
### type vs interface
Validate all props with the correct types. This replaces the need for a run-time validation as provided by the prop-types library.
```tsx
// ✅ Good - Functional component with hooks
export const MyComponent: React.FC<Props> = ({ data }) => {
const [loading, setLoading] = useState(false);
type HeadingProps = {
param: string;
}
useEffect(() => {
// Effect logic
}, []);
return <div>{/* Component JSX */}</div>;
};
// ❌ Avoid - Class component
class MyComponent extends React.Component {
// Class implementation
export default function Heading({ children }: HeadingProps) {
return <h2>{children}</h2>
}
```
### Follow Ant Design Patterns
Use `type` for your component props and state. Use `interface` when you want to enable _declaration merging_.
Extend Ant Design components rather than building from scratch:
### Define default values for non-required props
In order to improve the readability of your code and reduce assumptions, always add default values for non required props, when applicable, for example:
```tsx
import { Button } from 'antd';
import styled from '@emotion/styled';
const StyledButton = styled(Button)`
// Custom styling using emotion
`;
const applyDiscount = (price: number, discount = 0.05) => price * (1 - discount);
```
### Reusability and Props Design
## Functional components and Hooks
Design components with reusability in mind:
We prefer functional components and the usage of hooks over class components.
### useState
Always explicitly declare the type unless the type can easily be assumed by the declaration.
```tsx
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'tertiary';
size?: 'small' | 'medium' | 'large';
loading?: boolean;
disabled?: boolean;
children: React.ReactNode;
onClick?: () => void;
}
export const CustomButton: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'medium',
...props
}) => {
// Implementation
};
const [customer, setCustomer] = useState<ICustomer | null>(null);
```
## Testing Components
### useReducer
Every component should include comprehensive tests:
Always prefer `useReducer` over `useState` when your state has complex logics.
```tsx
// MyComponent.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { MyComponent } from './MyComponent';
### useMemo and useCallback
test('renders component with title', () => {
render(<MyComponent title="Test Title" />);
expect(screen.getByText('Test Title')).toBeInTheDocument();
});
Always memoize when your components take functions or complex objects as props to avoid unnecessary rerenders.
test('calls onClose when close button is clicked', () => {
const mockOnClose = jest.fn();
render(<MyComponent title="Test" onClose={mockOnClose} />);
### Custom hooks
fireEvent.click(screen.getByRole('button', { name: /close/i }));
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
```
All custom hooks should be located in the directory `/src/hooks`. Before creating a new custom hook, make sure that is not available in the existing custom hooks.
## Storybook Stories
## Storybook
Create stories for visual testing and documentation:
Each component should come with its dedicated storybook file.
```tsx
// MyComponent.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { MyComponent } from './MyComponent';
**One component per story:** Each storybook file should only contain one component unless substantially different variants are required
const meta: Meta<typeof MyComponent> = {
title: 'Components/MyComponent',
component: MyComponent,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
};
**Component variants:** If the component behavior is substantially different when certain props are used, it is best to separate the story into different types. See the `superset-frontend/src/components/Select/Select.stories.tsx` as an example.
export default meta;
type Story = StoryObj<typeof meta>;
**Isolated state:** The storybook should show how the component works in an isolated state and with as few dependencies as possible
export const Default: Story = {
args: {
title: 'Default Component',
isVisible: true,
},
};
export const Hidden: Story = {
args: {
title: 'Hidden Component',
isVisible: false,
},
};
```
## Performance Considerations
### Use React.memo for Expensive Components
```tsx
import React, { memo } from 'react';
export const ExpensiveComponent = memo<Props>(({ data }) => {
// Expensive rendering logic
return <div>{/* Component content */}</div>;
});
```
### Optimize Re-renders
Use `useCallback` and `useMemo` appropriately:
```tsx
export const OptimizedComponent: React.FC<Props> = ({ items, onSelect }) => {
const expensiveValue = useMemo(() => {
return items.reduce((acc, item) => acc + item.value, 0);
}, [items]);
const handleSelect = useCallback((id: string) => {
onSelect(id);
}, [onSelect]);
return <div>{/* Component content */}</div>;
};
```
## Accessibility
Ensure components are accessible:
```tsx
export const AccessibleButton: React.FC<Props> = ({ children, onClick }) => {
return (
<button
type="button"
aria-label="Descriptive label"
onClick={onClick}
>
{children}
</button>
);
};
```
## Error Boundaries
For components that might fail, consider error boundaries:
```tsx
export const SafeComponent: React.FC<Props> = ({ children }) => {
return (
<ErrorBoundary fallback={<div>Something went wrong</div>}>
{children}
</ErrorBoundary>
);
};
```
**Use args:** It should be possible to test the component with every variant of the available props. We recommend using [args](https://storybook.js.org/docs/react/writing-stories/args)

View File

@@ -1,6 +1,6 @@
---
title: Emotion Styling Guidelines and Best Practices
sidebar_position: 2
sidebar_position: 3
---
<!--
@@ -33,314 +33,245 @@ under the License.
- **DO** use `css` when you want to amend/merge sets of styles compositionally
- **DO** use `css` when you're making a small, or single-use set of styles for a component
- **DO** move your style definitions from direct usage in the `css` prop to an external variable when they get long
- **DO** prefer tagged template literals (`css={css\`...\`}`) over style objects wherever possible for maximum style portability/consistency
- **DO** use `useTheme` to get theme variables
- **DO** prefer tagged template literals (`css={css`...`}`) over style objects wherever possible for maximum style portability/consistency (note: typescript support may be diminished, but IDE plugins like [this](https://marketplace.visualstudio.com/items?itemName=jpoissonnier.vscode-styled-components) make life easy)
- **DO** use `useTheme` to get theme variables. `withTheme` should be only used for wrapping legacy Class-based components.
### DON'T do these things:
- **DON'T** use `styled` for small, single-use style tweaks that would be easier to read/review if they were inline
- **DON'T** export incomplete Ant Design components
- **DON'T** export incomplete AntD components (make sure all their compound components are exported)
## Emotion Tips and Strategies
The first thing to consider when adding styles to an element is how much you think a style might be reusable in other areas of Superset. Always err on the side of reusability here. Nobody wants to chase styling inconsistencies, or try to debug little endless overrides scattered around the codebase. The more we can consolidate, the less will have to be figured out by those who follow. Reduce, reuse, recycle.
## When to use `css` or `styled`
### Use `css` for:
In short, either works for just about any use case! And you'll see them used somewhat interchangeably in the existing codebase. But we need a way to weigh it when we encounter the choice, so here's one way to think about it:
1. **Small, single-use styles**
```tsx
import { css } from '@emotion/react';
A good use of `styled` syntax if you want to re-use a styled component. In other words, if you wanted to export flavors of a component for use, like so:
const MyComponent = () => (
<div
css={css`
margin: 8px;
padding: 16px;
`}
>
Content
</div>
);
```
2. **Composing styles**
```tsx
const baseStyles = css`
padding: 16px;
border-radius: 4px;
```jsx
const StatusThing = styled.div`
padding: 10px;
border-radius: 10px;
`;
const primaryStyles = css`
${baseStyles}
background-color: blue;
color: white;
export const InfoThing = styled(StatusThing)`
background: blue;
&::before {
content: "";
}
`;
const secondaryStyles = css`
${baseStyles}
background-color: gray;
color: black;
export const WarningThing = styled(StatusThing)`
background: orange;
&::before {
content: "⚠️";
}
`;
```
3. **Conditional styling**
```tsx
const MyComponent = ({ isActive }: { isActive: boolean }) => (
<div
css={[
baseStyles,
isActive && activeStyles,
]}
>
Content
</div>
);
```
### Use `styled` for:
1. **Reusable components**
```tsx
import styled from '@emotion/styled';
const StyledButton = styled.button`
padding: 12px 24px;
border: none;
border-radius: 4px;
background-color: ${({ theme }) => theme.colors.primary};
color: white;
&:hover {
background-color: ${({ theme }) => theme.colors.primaryDark};
export const TerribleThing = styled(StatusThing)`
background: red;
&::before {
content: "🔥";
}
`;
```
2. **Components with complex nested selectors**
```tsx
const StyledCard = styled.div`
padding: 16px;
border: 1px solid ${({ theme }) => theme.colors.border};
You can also use `styled` when you're building a bigger component, and just want to have some custom bits for internal use in your JSX. For example:
.card-header {
font-weight: bold;
margin-bottom: 8px;
}
.card-content {
color: ${({ theme }) => theme.colors.text};
p {
margin-bottom: 12px;
}
}
```jsx
const SeparatorOnlyUsedInThisComponent = styled.hr`
height: 12px;
border: 0;
box-shadow: inset 0 12px 12px -12px rgba(0, 0, 0, 0.5);
`;
function SuperComplicatedComponent(props) {
return (
<>
Daily standup for {user.name}!
<SeparatorOnlyUsedInThisComponent />
<h2>Yesterday:</h2>
// spit out a list of accomplishments
<SeparatorOnlyUsedInThisComponent />
<h2>Today:</h2>
// spit out a list of plans
<SeparatorOnlyUsedInThisComponent />
<h2>Tomorrow:</h2>
// spit out a list of goals
</>
);
}
```
3. **Extending Ant Design components**
```tsx
import { Button } from 'antd';
import styled from '@emotion/styled';
The `css` prop, in reality, shares all the same styling capabilities as `styled` but it does have some particular use cases that jump out as sensible. For example, if you just want to style one element in your component, you could add the styles inline like so:
const CustomButton = styled(Button)`
border-radius: 8px;
font-weight: 600;
&.ant-btn-primary {
background: linear-gradient(45deg, #1890ff, #722ed1);
}
`;
```jsx
function SomeFanciness(props) {
return (
<>
Here's an awesome report card for {user.name}!
<div
css={css`
box-shadow: 5px 5px 10px #ccc;
border-radius: 10px;
`}
>
<h2>Yesterday:</h2>
// ...some stuff
<h2>Today:</h2>
// ...some stuff
<h2>Tomorrow:</h2>
// ...some stuff
</div>
</>
);
}
```
## Using Theme Variables
You can also define the styles as a variable, external to your JSX. This is handy if the styles get long and you just want it out of the way. This is also handy if you want to apply the same styles to disparate element types, kind of like you might use a CSS class on varied elements. Here's a trumped up example:
Always use theme variables for consistent styling:
```tsx
import { useTheme } from '@emotion/react';
const MyComponent = () => {
const theme = useTheme();
```jsx
function FakeGlobalNav(props) {
const menuItemStyles = css`
display: block;
border-bottom: 1px solid cadetblue;
font-family: "Comic Sans", cursive;
`;
return (
<div
css={css`
background-color: ${theme.colors.grayscale.light5};
color: ${theme.colors.grayscale.dark2};
padding: ${theme.gridUnit * 4}px;
border-radius: ${theme.borderRadius}px;
`}
>
Content
<Nav>
<a css={menuItemStyles} href="#">One link</a>
<Link css={menuItemStyles} to={url}>Another link</Link>
<div css={menuItemStyles} onClick={() => alert('clicked')}>Another link</div>
</Nav>
);
}
```
## CSS tips and tricks
### `css` lets you write actual CSS
By default the `css` prop uses the object syntax with JS style definitions, like so:
```jsx
<div css={{
borderRadius: 10,
marginTop: 10,
backgroundColor: '#00FF00'
}}>Howdy</div>
```
But you can use the `css` interpolator as well to get away from icky JS styling syntax. Doesn't this look cleaner?
```jsx
<div css={css`
border-radius: 10px;
margin-top: 10px;
background-color: #00FF00;
`}>Howdy</div>
```
You might say "whatever… I can read and write JS syntax just fine." Well, that's great. But… let's say you're migrating in some of our legacy LESS styles… now it's copy/paste! Or if you want to migrate to or from `styled` syntax… also copy/paste!
### You can combine `css` definitions with array syntax
You can use multiple groupings of styles with the `css` interpolator, and combine/override them in array syntax, like so:
```jsx
function AnotherSillyExample(props) {
const shadowedCard = css`
box-shadow: 2px 2px 4px #999;
padding: 4px;
`;
const infoCard = css`
background-color: #33f;
border-radius: 4px;
`;
const overrideInfoCard = css`
background-color: #f33;
`;
return (
<div className="App">
Combining two classes:
<div css={[shadowedCard, infoCard]}>Hello</div>
Combining again, but now with overrides:
<div css={[shadowedCard, infoCard, overrideInfoCard]}>Hello</div>
</div>
);
};
```
## Common Patterns
### Responsive Design
```tsx
const ResponsiveContainer = styled.div`
display: flex;
flex-direction: column;
${({ theme }) => theme.breakpoints.up('md')} {
flex-direction: row;
}
`;
```
### Animation
```tsx
const FadeInComponent = styled.div`
opacity: 0;
transition: opacity 0.3s ease-in-out;
&.visible {
opacity: 1;
}
`;
```
### Conditional Props
```tsx
interface StyledDivProps {
isHighlighted?: boolean;
size?: 'small' | 'medium' | 'large';
}
```
const StyledDiv = styled.div<StyledDivProps>`
padding: 16px;
background-color: ${({ isHighlighted, theme }) =>
isHighlighted ? theme.colors.primary : theme.colors.grayscale.light5};
### Style variations with props
${({ size }) => {
switch (size) {
case 'small':
return css`font-size: 12px;`;
case 'large':
return css`font-size: 18px;`;
default:
return css`font-size: 14px;`;
}
}}
You can give any component a custom prop, and reference that prop in your component styles, effectively using the prop to turn on a "flavor" of that component.
For example, let's make a styled component that acts as a card. Of course, this could be done with any AntD component, or any component at all. But we'll do this with a humble `div` to illustrate the point:
```jsx
const SuperCard = styled.div`
${({ theme, cutout }) => `
padding: ${theme.gridUnit * 2}px;
border-radius: ${theme.borderRadius}px;
box-shadow: 10px 5px 10px #ccc ${cutout && 'inset'};
border: 1px solid ${cutout ? 'transparent' : theme.colors.secondary.light3};
`}
`;
```
## Best Practices
Then just use the component as `<SuperCard>Some content</SuperCard>` or with the (potentially dynamic) prop: `<SuperCard cutout>Some content</SuperCard>`
### 1. Use Semantic CSS Properties
```tsx
// ✅ Good - semantic property names
const Header = styled.h1`
font-size: ${({ theme }) => theme.typography.sizes.xl};
margin-bottom: ${({ theme }) => theme.gridUnit * 4}px;
`;
## Styled component tips
// ❌ Avoid - magic numbers
const Header = styled.h1`
font-size: 24px;
margin-bottom: 16px;
`;
```
### No need to use `theme` the hard way
### 2. Group Related Styles
```tsx
// ✅ Good - grouped styles
const Card = styled.div`
/* Layout */
display: flex;
flex-direction: column;
padding: ${({ theme }) => theme.gridUnit * 4}px;
It's very tempting (and commonly done) to use the `theme` prop inline in the template literal like so:
/* Appearance */
background-color: ${({ theme }) => theme.colors.grayscale.light5};
border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
```jsx
const SomeStyledThing = styled.div`
padding: ${({ theme }) => theme.gridUnit * 2}px;
border-radius: ${({ theme }) => theme.borderRadius}px;
/* Typography */
font-family: ${({ theme }) => theme.typography.families.sansSerif};
color: ${({ theme }) => theme.colors.grayscale.dark2};
border: 1px solid ${({ theme }) => theme.colors.secondary.light3};
`;
```
### 3. Extract Complex Styles
```tsx
// ✅ Good - extract complex styles to variables
const complexGradient = css`
background: linear-gradient(
135deg,
${({ theme }) => theme.colors.primary} 0%,
${({ theme }) => theme.colors.secondary} 100%
);
`;
Instead, you can make things a little easier to read/type by writing it like so:
const GradientButton = styled.button`
${complexGradient}
padding: 12px 24px;
border: none;
color: white;
```jsx
const SomeStyledThing = styled.div`
${({ theme }) => `
padding: ${theme.gridUnit * 2}px;
border-radius: ${theme.borderRadius}px;
border: 1px solid ${theme.colors.secondary.light3};
`}
`;
```
### 4. Avoid Deep Nesting
```tsx
// ✅ Good - shallow nesting
const Navigation = styled.nav`
.nav-item {
padding: 8px 16px;
}
## Extend an AntD component with custom styling
.nav-link {
color: ${({ theme }) => theme.colors.text};
text-decoration: none;
}
As mentioned, you want to keep your styling as close to the root of your component system as possible, to minimize repetitive styling/overrides, and err on the side of reusability. In some cases, that means you'll want to globally tweak one of our core components to match our design system. In Superset, that's Ant Design (AntD).
AntD uses a cool trick called compound components. For example, the `Menu` component also lets you use `Menu.Item`, `Menu.SubMenu`, `Menu.ItemGroup`, and `Menu.Divider`.
### The `Object.assign` trick
Let's say you want to override an AntD component called `Foo`, and have `Foo.Bar` display some custom CSS for the `Bar` compound component. You can do it effectively like so:
```jsx
import {
Foo as AntdFoo,
} from 'antd';
export const StyledBar = styled(AntdFoo.Bar)`
border-radius: ${({ theme }) => theme.borderRadius}px;
`;
// ❌ Avoid - deep nesting
const Navigation = styled.nav`
ul {
li {
a {
span {
color: blue; /* Too nested */
}
}
}
}
`;
```
## Performance Tips
### 1. Avoid Inline Functions in CSS
```tsx
// ✅ Good - external function
const getBackgroundColor = (isActive: boolean, theme: any) =>
isActive ? theme.colors.primary : theme.colors.secondary;
const Button = styled.button<{ isActive: boolean }>`
background-color: ${({ isActive, theme }) => getBackgroundColor(isActive, theme)};
`;
// ❌ Avoid - inline function (creates new function on each render)
const Button = styled.button<{ isActive: boolean }>`
background-color: ${({ isActive, theme }) =>
isActive ? theme.colors.primary : theme.colors.secondary};
`;
```
### 2. Use CSS Objects for Dynamic Styles
```tsx
// For highly dynamic styles, consider CSS objects
const dynamicStyles = (props: Props) => ({
backgroundColor: props.color,
fontSize: `${props.size}px`,
// ... other dynamic properties
export const Foo = Object.assign(AntdFoo, {
Bar: StyledBar,
});
const DynamicComponent = (props: Props) => (
<div css={dynamicStyles(props)}>
Content
</div>
);
```
You can then import this customized `Foo` and use `Foo.Bar` as expected. You should probably save your creation in `src/components` for maximum reusability, and add a Storybook entry so future engineers can view your creation, and designers can better understand how it fits the Superset Design System.

View File

@@ -1,297 +0,0 @@
---
title: Testing Guidelines and Best Practices
sidebar_position: 3
---
<!--
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.
-->
# Testing Guidelines and Best Practices
We feel that tests are an important part of a feature and not an additional or optional effort. That's why we colocate test files with functionality and sometimes write tests upfront to help validate requirements and shape the API of our components. Every new component or file added should have an associated test file with the `.test` extension.
We use Jest, React Testing Library (RTL), and Cypress to write our unit, integration, and end-to-end tests. For each type, we have a set of best practices/tips described below:
## Jest
### Write simple, standalone tests
The importance of simplicity is often overlooked in test cases. Clear, dumb code should always be preferred over complex ones. The test cases should be pretty much standalone and should not involve any external logic if not absolutely necessary. That's because you want the corpus of the tests to be easy to read and understandable at first sight.
### Avoid nesting when you're testing
Avoid the use of `describe` blocks in favor of inlined tests. If your tests start to grow and you feel the need to group tests, prefer to break them into multiple test files. Check this awesome [article](https://kentcdodds.com/blog/avoid-nesting-when-youre-testing) written by [Kent C. Dodds](https://kentcdodds.com/) about this topic.
### No warnings!
Your tests shouldn't trigger warnings. This is really common when testing async functionality. It's really difficult to read test results when we have a bunch of warnings.
## React Testing Library (RTL)
### Keep accessibility in mind when writing your tests
One of the most important points of RTL is accessibility and this is also a very important point for us. We should try our best to follow the RTL [Priority](https://testing-library.com/docs/queries/about/#priority) when querying for elements in our tests. `getByTestId` is not viewable by the user and should only be used when the element isn't accessible in any other way.
### Don't use `act` unnecessarily
`render` and `fireEvent` are already wrapped in `act`, so wrapping them in `act` again is a common mistake. Some solutions to the warnings related to async testing can be found in the RTL docs.
## Example Test Structure
```tsx
// MyComponent.test.tsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MyComponent } from './MyComponent';
// ✅ Good - Simple, standalone test
test('renders loading state initially', () => {
render(<MyComponent />);
expect(screen.getByText('Loading...')).toBeInTheDocument();
});
// ✅ Good - Tests user interaction
test('calls onSubmit when form is submitted', async () => {
const user = userEvent.setup();
const mockOnSubmit = jest.fn();
render(<MyComponent onSubmit={mockOnSubmit} />);
await user.type(screen.getByLabelText('Username'), 'testuser');
await user.click(screen.getByRole('button', { name: 'Submit' }));
expect(mockOnSubmit).toHaveBeenCalledWith({ username: 'testuser' });
});
// ✅ Good - Tests async behavior
test('displays error message when API call fails', async () => {
const mockFetch = jest.fn().mockRejectedValue(new Error('API Error'));
global.fetch = mockFetch;
render(<MyComponent />);
await waitFor(() => {
expect(screen.getByText('Error: API Error')).toBeInTheDocument();
});
});
```
## Testing Best Practices
### Use appropriate queries in priority order
1. **Accessible to everyone**: `getByRole`, `getByLabelText`, `getByPlaceholderText`, `getByText`
2. **Semantic queries**: `getByAltText`, `getByTitle`
3. **Test IDs**: `getByTestId` (last resort)
```tsx
// ✅ Good - using accessible queries
test('user can submit form', () => {
render(<LoginForm />);
const usernameInput = screen.getByLabelText('Username');
const passwordInput = screen.getByLabelText('Password');
const submitButton = screen.getByRole('button', { name: 'Log in' });
// Test implementation
});
// ❌ Avoid - using test IDs when better options exist
test('user can submit form', () => {
render(<LoginForm />);
const usernameInput = screen.getByTestId('username-input');
const passwordInput = screen.getByTestId('password-input');
const submitButton = screen.getByTestId('submit-button');
// Test implementation
});
```
### Test behavior, not implementation details
```tsx
// ✅ Good - tests what the user experiences
test('shows success message after successful form submission', async () => {
render(<ContactForm />);
await userEvent.type(screen.getByLabelText('Email'), 'test@example.com');
await userEvent.click(screen.getByRole('button', { name: 'Submit' }));
await waitFor(() => {
expect(screen.getByText('Message sent successfully!')).toBeInTheDocument();
});
});
// ❌ Avoid - testing implementation details
test('calls setState when form is submitted', () => {
const component = shallow(<ContactForm />);
const instance = component.instance();
const spy = jest.spyOn(instance, 'setState');
instance.handleSubmit();
expect(spy).toHaveBeenCalled();
});
```
### Mock external dependencies appropriately
```tsx
// Mock API calls
jest.mock('../api/userService', () => ({
getUser: jest.fn(),
createUser: jest.fn(),
}));
// Mock components that aren't relevant to the test
jest.mock('../Chart/Chart', () => {
return function MockChart() {
return <div data-testid="mock-chart">Chart Component</div>;
};
});
```
## Async Testing Patterns
### Testing async operations
```tsx
test('loads and displays user data', async () => {
const mockUser = { id: 1, name: 'John Doe' };
const mockGetUser = jest.fn().mockResolvedValue(mockUser);
render(<UserProfile getUserData={mockGetUser} />);
// Wait for the async operation to complete
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
expect(mockGetUser).toHaveBeenCalledTimes(1);
});
```
### Testing loading states
```tsx
test('shows loading spinner while fetching data', async () => {
const mockGetUser = jest.fn().mockImplementation(
() => new Promise(resolve => setTimeout(resolve, 100))
);
render(<UserProfile getUserData={mockGetUser} />);
expect(screen.getByText('Loading...')).toBeInTheDocument();
await waitFor(() => {
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
});
});
```
## Component-Specific Testing
### Testing form components
```tsx
test('validates required fields', async () => {
render(<RegistrationForm />);
const submitButton = screen.getByRole('button', { name: 'Register' });
await userEvent.click(submitButton);
expect(screen.getByText('Username is required')).toBeInTheDocument();
expect(screen.getByText('Email is required')).toBeInTheDocument();
});
```
### Testing modals and overlays
```tsx
test('opens and closes modal', async () => {
render(<ModalContainer />);
const openButton = screen.getByRole('button', { name: 'Open Modal' });
await userEvent.click(openButton);
expect(screen.getByRole('dialog')).toBeInTheDocument();
const closeButton = screen.getByRole('button', { name: 'Close' });
await userEvent.click(closeButton);
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});
```
### Testing with context providers
```tsx
const renderWithTheme = (component: React.ReactElement) => {
return render(
<ThemeProvider theme={mockTheme}>
{component}
</ThemeProvider>
);
};
test('applies theme colors correctly', () => {
renderWithTheme(<ThemedButton />);
const button = screen.getByRole('button');
expect(button).toHaveStyle({
backgroundColor: mockTheme.colors.primary,
});
});
```
## Performance Testing
### Testing with large datasets
```tsx
test('handles large lists efficiently', () => {
const largeDataset = Array.from({ length: 10000 }, (_, i) => ({
id: i,
name: `Item ${i}`,
}));
const { container } = render(<VirtualizedList items={largeDataset} />);
// Should only render visible items
const renderedItems = container.querySelectorAll('[data-testid="list-item"]');
expect(renderedItems.length).toBeLessThan(100);
});
```
## Testing Accessibility
```tsx
test('is accessible to screen readers', () => {
render(<AccessibleForm />);
const form = screen.getByRole('form');
const inputs = screen.getAllByRole('textbox');
inputs.forEach(input => {
expect(input).toHaveAttribute('aria-label');
});
expect(form).toHaveAttribute('aria-describedby');
});
```

View File

@@ -36,6 +36,7 @@ module.exports = {
'extensions/overview',
'extensions/quick-start',
'extensions/architecture',
'extensions/dependencies',
'extensions/contribution-types',
{
type: 'category',

View File

@@ -0,0 +1,129 @@
---
title: Testing Guidelines and Best Practices
sidebar_position: 1
---
<!--
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.
-->
# Testing Guidelines and Best Practices
We feel that tests are an important part of a feature and not an additional or optional effort. That's why we colocate test files with functionality and sometimes write tests upfront to help validate requirements and shape the API of our components. Every new component or file added should have an associated test file with the `.test` extension.
We use Jest, React Testing Library (RTL), and Cypress to write our unit, integration, and end-to-end tests. For each type, we have a set of best practices/tips described below:
## Jest
### Write simple, standalone tests
The importance of simplicity is often overlooked in test cases. Clear, dumb code should always be preferred over complex ones. The test cases should be pretty much standalone and should not involve any external logic if not absolutely necessary. That's because you want the corpus of the tests to be easy to read and understandable at first sight.
### Avoid nesting when you're testing
Avoid the use of `describe` blocks in favor of inlined tests. If your tests start to grow and you feel the need to group tests, prefer to break them into multiple test files. Check this awesome [article](https://kentcdodds.com/blog/avoid-nesting-when-youre-testing) written by [Kent C. Dodds](https://kentcdodds.com/) about this topic.
### No warnings!
Your tests shouldn't trigger warnings. This is really common when testing async functionality. It's really difficult to read test results when we have a bunch of warnings.
### Example
You can find an example of a test [here](https://github.com/apache/superset/blob/e6c5bf4/superset-frontend/src/common/hooks/useChangeEffect/useChangeEffect.test.ts).
## React Testing Library (RTL)
### Keep accessibility in mind when writing your tests
One of the most important points of RTL is accessibility and this is also a very important point for us. We should try our best to follow the RTL [Priority](https://testing-library.com/docs/queries/about/#priority) when querying for elements in our tests. `getByTestId` is not viewable by the user and should only be used when the element isn't accessible in any other way.
### Don't use `act` unnecessarily
`render` and `fireEvent` are already wrapped in `act`, so wrapping them in `act` again is a common mistake. Some solutions to the warnings related to `act` might be found [here](https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning).
### Be specific when using *ByRole
By using the `name` option we can point to the items by their accessible name. For example:
```jsx
screen.getByRole('button', { name: /hello world/i })
```
Using the `name` property also avoids breaking the tests in the future if other components with the same role are added.
### userEvent vs fireEvent
Prefer the [user-event](https://github.com/testing-library/user-event) library, which provides a more advanced simulation of browser interactions than the built-in [fireEvent](https://testing-library.com/docs/dom-testing-library/api-events/#fireevent) method.
### Usage of waitFor
- [Prefer to use `find`](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#using-waitfor-to-wait-for-elements-that-can-be-queried-with-find) over `waitFor` when you're querying for elements. Even though both achieve the same objective, the `find` version is simpler and you'll get better error messages.
- Prefer to use only [one assertion at a time](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#having-multiple-assertions-in-a-single-waitfor-callback). If you put multiple assertions inside a `waitFor` we could end up waiting for the whole block timeout before seeing a test failure even if the failure occurred at the first assertion. By putting a single assertion in there, we can improve on test execution time.
- Do not perform [side-effects](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#performing-side-effects-in-waitfor) inside `waitFor`. The callback can be called a non-deterministic number of times and frequency (it's called both on an interval as well as when there are DOM mutations). So this means that your side-effect could run multiple times.
### Example
You can find an example of a test [here](https://github.com/apache/superset/blob/master/superset-frontend/src/dashboard/components/PublishedStatus/PublishedStatus.test.tsx).
## Cypress
### Prefer to use Cypress for e2e testing and RTL for integration testing
Here it's important to make the distinction between e2e and integration testing. This [article](https://www.onpathtesting.com/blog/end-to-end-vs-integration-testing) gives an excellent definition:
> End-to-end testing verifies that your software works correctly from the beginning to the end of a particular user flow. It replicates expected user behavior and various usage scenarios to ensure that your software works as whole. End-to-end testing uses a production equivalent environment and data to simulate real-world situations and may also involve the integrations your software has with external applications.
> A typical software project consists of multiple software units, usually coded by different developers. Integration testing combines those software units logically and tests them as a group.
> Essentially, integration testing verifies whether or not the individual modules or services that make up your application work well together. The purpose of this level of testing is to expose defects in the interaction between these software modules when they are integrated.
Do not use Cypress when RTL can do it better and faster. Many of the Cypress tests that we have right now, fall into the integration testing category and can be ported to RTL which is much faster and gives more immediate feedback. Cypress should be used mainly for end-to-end testing, replicating the user experience, with positive and negative flows.
### Isolated and standalone tests
Tests should never rely on other tests to pass. This might be hard when a single user is used for testing as data will be stored in the database. At every new test, we should reset the database.
### Cleaning state
Cleaning the state of the application, such as resetting the DB, or in general, any state that might affect consequent tests should always be done in the `beforeEach` hook and never in the `afterEach` one as the `beforeEach` is guaranteed to run, while the test might never reach the point to run the `afterEach` hook. One example would be if you refresh Cypress in the middle of the test. At this point, you will have built up a partial state in the database, and your clean-up function will never get called. You can read more about it [here](https://docs.cypress.io/guides/references/best-practices#Using-after-or-afterEach-hooks).
### Unnecessary use of `cy.wait`
- Unnecessary when using `cy.request()` as it will resolve when a response is received from the server
- Unnecessary when using `cy.visit()` as it resolves only when the page fires the load event
- Unnecessary when using `cy.get()`. When the selector should wait for a request to happen, aliases would come in handy:
```js
cy.intercept('GET', '/users', [{ name: 'Maggy' }, { name: 'Joan' }]).as('getUsers')
cy.get('#fetch').click()
cy.wait('@getUsers') // <--- wait explicitly for this route to finish
cy.get('table tr').should('have.length', 2)
```
### Accessibility and Resilience
The same accessibility principles in the RTL section apply here. Use accessible selectors when querying for elements. Those principles also help to isolate selectors from eventual CSS and JS changes and improve the resilience of your tests.
## References
- [Avoid Nesting when you're Testing](https://kentcdodds.com/blog/avoid-nesting-when-youre-testing)
- [RTL - Queries](https://testing-library.com/docs/queries/about/#priority)
- [Fix the "not wrapped in act(...)" warning](https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning)
- [Common mistakes with React Testing Library](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library)
- [ARIA Roles](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles)
- [Cypress - Best Practices](https://docs.cypress.io/guides/references/best-practices)
- [End-to-end vs integration tests: what's the difference?](https://www.onpathtesting.com/blog/end-to-end-vs-integration-testing)

View File

@@ -16,6 +16,11 @@ Superset's public **REST API** follows the
documented here. The docs below are generated using
[Swagger React UI](https://www.npmjs.com/package/swagger-ui-react).
:::resources
- [Blog: The Superset REST API](https://preset.io/blog/2020-10-01-superset-api/)
- [Blog: Accessing APIs with Superset](https://preset.io/blog/accessing-apis-with-superset/)
:::
<Alert
type="info"
message={

View File

@@ -398,3 +398,8 @@ the user can add the metadata required for scheduling the query.
This information can then be retrieved from the endpoint `/api/v1/saved_query/` and used to
schedule the queries that have `schedule_info` in their JSON metadata. For schedulers other than
Airflow, additional fields can be easily added to the configuration file above.
:::resources
- [Tutorial: Automated Alerts and Reporting via Slack/Email in Superset](https://dev.to/ngtduc693/apache-superset-topic-5-automated-alerts-and-reporting-via-slackemail-in-superset-2gbe)
- [Blog: Integrating Slack alerts and Apache Superset for better data observability](https://medium.com/affinityanswers-tech/integrating-slack-alerts-and-apache-superset-for-better-data-observability-fd2f9a12c350)
:::

View File

@@ -102,3 +102,7 @@ You can run flower using:
```bash
celery --app=superset.tasks.celery_app:app flower
```
:::resources
- [Blog: How to Set Up Global Async Queries (GAQ) in Apache Superset](https://medium.com/@ngigilevis/how-to-set-up-global-async-queries-gaq-in-apache-superset-a-complete-guide-9d2f4a047559)
:::

View File

@@ -152,3 +152,8 @@ Then on configuration:
```
WEBDRIVER_AUTH_FUNC = auth_driver
```
:::resources
- [Blog: The Data Engineer's Guide to Lightning-Fast Superset Dashboards](https://preset.io/blog/the-data-engineers-guide-to-lightning-fast-apache-superset-dashboards/)
- [Blog: Accelerating Dashboards with Materialized Views](https://preset.io/blog/accelerating-apache-superset-dashboards-with-materialized-views/)
:::

View File

@@ -442,3 +442,7 @@ FEATURE_FLAGS = {
```
A current list of feature flags can be found in [RESOURCES/FEATURE_FLAGS.md](https://github.com/apache/superset/blob/master/RESOURCES/FEATURE_FLAGS.md).
:::resources
- [Blog: Feature Flags in Apache Superset](https://preset.io/blog/feature-flags-in-apache-superset-and-preset/)
:::

View File

@@ -99,8 +99,9 @@ exists, please file an issue on the
[Superset GitHub repo](https://github.com/apache/superset/issues), so we can work on documenting and
supporting it.
If you'd like to build a database connector for Superset integration,
read the [following tutorial](https://preset.io/blog/building-database-connector/).
:::resources
- [Tutorial: Building a Database Connector for Superset](https://preset.io/blog/building-database-connector/)
:::
### Installing Drivers in Docker Images
@@ -219,6 +220,10 @@ Here's what the connection string looks like:
doris://<User>:<Password>@<Host>:<Port>/<Catalog>.<Database>
```
:::resources
- [Apache Doris Docs: Superset Integration](https://doris.apache.org/docs/ecosystem/bi/apache-superset/)
:::
#### AWS Athena
##### PyAthenaJDBC
@@ -360,6 +365,13 @@ uses the default user without a password (and doesn't encrypt the connection):
clickhousedb://localhost/default
```
:::resources
- [ClickHouse Docs: Superset Integration](https://clickhouse.com/docs/integrations/superset)
- [Altinity: Connect ClickHouse to Superset](https://docs.altinity.com/integrations/clickhouse-and-superset/connect-clickhouse-to-superset/)
- [Instaclustr: Connecting to ClickHouse from Superset](https://www.instaclustr.com/support/documentation/clickhouse/using-a-clickhouse-cluster/connecting-to-clickhouse-from-apache-superset/)
- [Blog: ClickHouse and Apache Superset](https://preset.io/blog/2021-5-26-clickhouse-superset/)
:::
#### Cloudflare D1
To use Cloudflare D1 with superset, install the [superset-engine-d1](https://github.com/sqlalchemy-cf-d1/superset-engine-d1) library.
@@ -432,6 +444,11 @@ Docker Compose.
echo "sqlalchemy-cratedb" >> ./docker/requirements-local.txt
```
:::resources
- [CrateDB Docs: Apache Superset Integration](https://cratedb.com/docs/guide/integrate/apache-superset/index.html)
- [Blog: Visualizing Time-Series Data with CrateDB and Superset](https://preset.io/blog/timeseries-cratedb-superset/)
:::
[CrateDB Cloud]: https://cratedb.com/product/cloud
[CrateDB Self-Managed]: https://cratedb.com/product/self-managed
[sqlalchemy-cratedb]: https://pypi.org/project/sqlalchemy-cratedb/
@@ -564,8 +581,10 @@ The expected connection string for Arrow Flight (Dremio 4.9.1+. Default port is
dremio+flight://{username}:{password}@{host}:{port}/dremio
```
This [blog post by Dremio](https://www.dremio.com/tutorials/dremio-apache-superset/) has some
additional helpful instructions on connecting Superset to Dremio.
:::resources
- [Dremio Docs: Superset Integration](https://docs.dremio.com/current/client-applications/superset/)
- [Blog: Connecting Dremio to Apache Superset](https://www.dremio.com/tutorials/dremio-apache-superset/)
:::
#### Apache Drill
@@ -608,6 +627,10 @@ We recommend reading the
the [GitHub README](https://github.com/JohnOmernik/sqlalchemy-drill#usage-with-odbc) to learn how to
work with Drill through ODBC.
:::resources
- [Tutorial: Query MongoDB in Superset with Apache Drill](https://medium.com/@thoren.lederer/query-data-from-mongodb-in-apache-superset-with-the-help-of-apache-drill-full-tutorial-b34c33eac6c1)
:::
import useBaseUrl from "@docusaurus/useBaseUrl";
#### Apache Druid
@@ -674,6 +697,10 @@ much like you would create an aggregation manually, but specify `postagg` as a `
then have to provide a valid json post-aggregation definition (as specified in the Druid docs) in
the JSON field.
:::resources
- [Blog: Real-Time Business Insights with Apache Druid and Superset](https://www.deep.bi/blog/real-time-business-insights-with-apache-druid-and-apache-superset)
:::
#### Elasticsearch
The recommended connector library for Elasticsearch is
@@ -744,6 +771,10 @@ To disable SSL verification, add the following to the **SQLALCHEMY URI** field:
elasticsearch+https://{user}:{password}@{host}:9200/?verify_certs=False
```
:::resources
- [Blog: Superset Announces Elasticsearch Support](https://preset.io/blog/2019-12-16-elasticsearch-in-superset/)
:::
#### Exasol
The recommended connector library for Exasol is
@@ -792,6 +823,10 @@ or
firebolt://{client_id}:{client_secret}@{database}/{engine_name}?account_name={name}
```
:::resources
- [Firebolt Docs: Connecting to Apache Superset](https://docs.firebolt.io/guides/integrations/connecting-to-apache-superset)
:::
#### Google BigQuery
The recommended connector library for BigQuery is
@@ -878,15 +913,21 @@ To be able to upload CSV or Excel files to BigQuery in Superset, you'll need to
Currently, the Google BigQuery Python SDK is not compatible with `gevent`, due to some dynamic monkeypatching on python core library by `gevent`.
So, when you deploy Superset with `gunicorn` server, you have to use worker type except `gevent`.
:::resources
- [Tutorial: Build A StackOverflow Dashboard — Connecting Superset to BigQuery](https://preset.io/blog/2020-08-04-google-bigquery/)
:::
#### Google Sheets
Google Sheets has a very limited
[SQL API](https://developers.google.com/chart/interactive/docs/querylanguage). The recommended
connector library for Google Sheets is [shillelagh](https://github.com/betodealmeida/shillelagh).
There are a few steps involved in connecting Superset to Google Sheets. This
[tutorial](https://preset.io/blog/2020-06-01-connect-superset-google-sheets/) has the most up to date
instructions on setting up this connection.
There are a few steps involved in connecting Superset to Google Sheets.
:::resources
- [Tutorial: Connect Superset to Google Sheets](https://preset.io/blog/2020-06-01-connect-superset-google-sheets/)
:::
#### Hana
@@ -908,6 +949,10 @@ The expected connection string is formatted as follows:
hive://hive@{hostname}:{port}/{database}
```
:::resources
- [Cloudera: Connect Apache Hive to Superset](https://docs-archive.cloudera.com/HDPDocuments/HDP3/HDP-3.0.0/integrating-hive/content/hive_connect_hive_to_apache_superset.html)
:::
#### Hologres
Hologres is a real-time interactive analytics service developed by Alibaba Cloud. It is fully compatible with PostgreSQL 11 and integrates seamlessly with the big data ecosystem.
@@ -1059,6 +1104,10 @@ The connection string is formatted as follows:
oracle://<username>:<password>@<hostname>:<port>
```
:::resources
- [Oracle Developers: Steps to use Apache Superset and Oracle Database](https://medium.com/oracledevs/steps-to-use-apache-superset-and-oracle-database-ae0858b4f134)
:::
#### Parseable
[Parseable](https://www.parseable.io) is a distributed log analytics database that provides SQL-like query interface for log data. The recommended connector library is [sqlalchemy-parseable](https://github.com/parseablehq/sqlalchemy-parseable).
@@ -1077,7 +1126,10 @@ parseable://admin:admin@demo.parseable.com:443/ingress-nginx
Note: The stream_name in the URI represents the Parseable logstream you want to query. You can use both HTTP (port 80) and HTTPS (port 443) connections.
>>>>>>>
:::resources
- [Blog: Parseable with Apache Superset](https://www.parseable.com/blog/parseable-with-apache-superset)
:::
#### Apache Pinot
The recommended connector library for Apache Pinot is [pinotdb](https://pypi.org/project/pinotdb/).
@@ -1101,6 +1153,11 @@ Add below argument while creating database connection in Advanced -> Other -> EN
{"connect_args":{"use_multistage_engine":"true"}}
```
:::resources
- [Apache Pinot Docs: Superset Integration](https://docs.pinot.apache.org/integrations/superset)
- [StarTree: Data Visualization with Apache Superset and Pinot](https://startree.ai/resources/data-visualization-with-apache-superset-and-pinot)
:::
#### Postgres
Note that, if you're using docker compose, the Postgres connector library [psycopg2](https://www.psycopg.org/docs/)
@@ -1137,6 +1194,28 @@ More information about PostgreSQL connection options can be found in the
and the
[PostgreSQL docs](https://www.postgresql.org/docs/9.1/libpq-connect.html#LIBPQ-PQCONNECTDBPARAMS).
:::resources
- [Blog: Data Visualization in PostgreSQL With Apache Superset](https://www.tigerdata.com/blog/data-visualization-in-postgresql-with-apache-superset)
:::
#### QuestDB
[QuestDB](https://questdb.io/) is a high-performance, open-source time-series database with SQL support.
The recommended connector library is the PostgreSQL driver [psycopg2](https://www.psycopg.org/docs/),
as QuestDB supports the PostgreSQL wire protocol.
The connection string is formatted as follows:
```
postgresql+psycopg2://{username}:{password}@{hostname}:{port}/{database}
```
The default port for QuestDB's PostgreSQL interface is `8812`.
:::resources
- [QuestDB Docs: Apache Superset Integration](https://questdb.com/docs/third-party-tools/superset/)
:::
#### Presto
The [pyhive](https://pypi.org/project/PyHive/) library is the recommended way to connect to Presto through SQLAlchemy.
@@ -1179,6 +1258,10 @@ SSL Secure extra add json config to extra connection information.
}
```
:::resources
- [Tutorial: Presto SQL + S3 Data + Superset Data Lake](https://hackernoon.com/presto-sql-s3-data-superset-data-lake)
:::
#### RisingWave
The recommended connector library for RisingWave is
@@ -1252,6 +1335,10 @@ If your private key is stored on server, you can replace "privatekey_body" with
}
```
:::resources
- [Snowflake Builders Blog: Building Real-Time Operational Dashboards with Apache Superset and Snowflake](https://medium.com/snowflake/building-real-time-operational-dashboards-with-apache-superset-and-snowflake-23f625e07d7c)
:::
#### Apache Solr
The [sqlalchemy-solr](https://pypi.org/project/sqlalchemy-solr/) library provides a
@@ -1273,6 +1360,10 @@ The expected connection string is formatted as follows:
hive://hive@{hostname}:{port}/{database}
```
:::resources
- [Tutorial: How to Connect Apache Superset with Apache SparkSQL](https://medium.com/free-or-open-source-software/how-to-connect-apache-superset-with-apache-sparksql-50efe48ac0e4)
:::
#### Arc
There are two ways to connect Superset to Arc:
@@ -1356,8 +1447,8 @@ Here's what the connection string looks like:
starrocks://<User>:<Password>@<Host>:<Port>/<Catalog>.<Database>
```
:::note
StarRocks maintains their Superset docuementation [here](https://docs.starrocks.io/docs/integrations/BI_integrations/Superset/).
:::resources
- [StarRocks Docs: Superset Integration](https://docs.starrocks.io/docs/integrations/BI_integrations/Superset/)
:::
#### TDengine
@@ -1441,6 +1532,11 @@ postgresql://{username}:{password}@{host}:{port}/{database name}?sslmode=require
[Learn more about TimescaleDB!](https://docs.timescale.com/)
:::resources
- [Timescale DevRel: Visualize time series data with TimescaleDB and Apache Superset](https://attilatoth.dev/speaking/timescaledb-superset/)
- [Tutorial: PostgreSQL with TimescaleDB — Visualizing Real-Time Data with Superset](https://www.slingacademy.com/article/postgresql-with-timescaledb-visualizing-real-time-data-with-superset/)
:::
#### Trino
Supported trino version 352 and higher
@@ -1566,9 +1662,11 @@ or factory function (which returns an `Authentication` instance) to `auth_method
All fields in `auth_params` are passed directly to your class/function.
**Reference**:
- [Trino-Superset-Podcast](https://trino.io/episodes/12.html)
:::resources
- [Starburst Docs: Superset Integration](https://docs.starburst.io/clients/superset.html)
- [Podcast: Trino and Superset](https://trino.io/episodes/12.html)
- [Blog: Trino and Apache Superset](https://preset.io/blog/2021-6-22-trino-superset/)
:::
#### Vertica
@@ -1680,6 +1778,10 @@ The connection string looks like:
postgresql://{username}:{password}@{host}:{port}/{database}
```
:::resources
- [Blog: Introduction to YugabyteDB and Apache Superset](https://preset.io/blog/introduction-yugabytedb-apache-superset/)
:::
## Connecting through the UI
Here is the documentation on how to leverage the new DB Connection UI. This will provide admins the ability to enhance the UX for users who want to connect to new databases.

View File

@@ -51,8 +51,20 @@ Restart Superset for this configuration change to take effect.
#### Making a Dashboard Public
1. Add the `'DASHBOARD_RBAC': True` [Feature Flag](https://github.com/apache/superset/blob/master/RESOURCES/FEATURE_FLAGS.md) to `superset_config.py`
2. Add the `Public` role to your dashboard as described [here](https://superset.apache.org/docs/using-superset/creating-your-first-dashboard/#manage-access-to-dashboards)
There are two approaches to making dashboards publicly accessible:
**Option 1: Dataset-based access (simpler)**
1. Set `PUBLIC_ROLE_LIKE = "Public"` in `superset_config.py`
2. Grant the Public role access to the relevant datasets (Menu → Security → List Roles → Public)
3. All published dashboards using those datasets become visible to anonymous users
**Option 2: Dashboard-level access (selective control)**
1. Set `PUBLIC_ROLE_LIKE = "Public"` in `superset_config.py`
2. Add the `'DASHBOARD_RBAC': True` [Feature Flag](https://github.com/apache/superset/blob/master/RESOURCES/FEATURE_FLAGS.md)
3. Edit each dashboard's properties and add the "Public" role
4. Only dashboards with the Public role explicitly assigned are visible to anonymous users
See the [Public role documentation](/docs/security/security#public) for more details.
#### Embedding a Public Dashboard

View File

@@ -26,8 +26,8 @@ made available in the Jinja context:
- `columns`: columns which to group by in the query
- `filter`: filters applied in the query
- `from_dttm`: start `datetime` value from the selected time range (`None` if undefined) (deprecated beginning in version 5.0, use `get_time_filter` instead)
- `to_dttm`: end `datetime` value from the selected time range (`None` if undefined). (deprecated beginning in version 5.0, use `get_time_filter` instead)
- `from_dttm`: start `datetime` value from the selected time range (`None` if undefined). **Note:** Only available in virtual datasets when a time range filter is applied in Explore/Chart views—not available in standalone SQL Lab queries. (deprecated beginning in version 5.0, use `get_time_filter` instead)
- `to_dttm`: end `datetime` value from the selected time range (`None` if undefined). **Note:** Only available in virtual datasets when a time range filter is applied in Explore/Chart views—not available in standalone SQL Lab queries. (deprecated beginning in version 5.0, use `get_time_filter` instead)
- `groupby`: columns which to group by in the query (deprecated)
- `metrics`: aggregate expressions in the query
- `row_limit`: row limit of the query
@@ -67,6 +67,54 @@ the time filter is not set. For many database engines, this could be replaced wi
Note that the Jinja parameters are called within _double_ brackets in the query and with
_single_ brackets in the logic blocks.
### Understanding Context Availability
Some Jinja variables like `from_dttm`, `to_dttm`, and `filter` are **only available when a chart or dashboard provides them**. They are populated from:
- Time range filters applied in Explore/Chart views
- Dashboard native filters
- Filter components
**These variables are NOT available in standalone SQL Lab queries** because there's no filter context. If you try to use `{{ from_dttm }}` directly in SQL Lab, you'll get an "undefined parameter" error.
#### Testing Time-Filtered Queries in SQL Lab
To test queries that use time variables in SQL Lab, you have several options:
**Option 1: Use Jinja defaults (recommended)**
```sql
SELECT *
FROM tbl
WHERE dttm_col > '{{ from_dttm | default("2024-01-01", true) }}'
AND dttm_col < '{{ to_dttm | default("2024-12-31", true) }}'
```
**Option 2: Use SQL Lab Parameters**
Set parameters in the SQL Lab UI (Parameters menu):
```json
{
"from_dttm": "2024-01-01",
"to_dttm": "2024-12-31"
}
```
**Option 3: Use `{% set %}` for testing**
```sql
{% set from_dttm = "2024-01-01" %}
{% set to_dttm = "2024-12-31" %}
SELECT *
FROM tbl
WHERE dttm_col > '{{ from_dttm }}' AND dttm_col < '{{ to_dttm }}'
```
:::tip
When you save a SQL Lab query as a virtual dataset and use it in a chart with time filters,
the actual filter values will override any defaults or test values you set.
:::
To add custom functionality to the Jinja context, you need to overload the default Jinja
context in your environment by defining the `JINJA_CONTEXT_ADDONS` in your superset configuration
(`superset_config.py`). Objects referenced in this dictionary are made available for users to use
@@ -542,3 +590,7 @@ Loads a string as a `datetime` object. This is useful when performing date opera
do something else
{% endif %}
```
:::resources
- [Blog: Intro to Jinja Templating in Apache Superset](https://preset.io/blog/intro-jinja-templating-apache-superset/)
:::

View File

@@ -395,3 +395,11 @@ For programmatic theme management, Superset provides REST endpoints:
- `POST /api/v1/theme/import/` - Import themes from YAML
These endpoints require appropriate permissions and are subject to RBAC controls.
:::resources
- [Video: Live Demo — Theming Apache Superset](https://www.youtube.com/watch?v=XsZAsO9tC3o)
- [CSS and Theming](https://docs.preset.io/docs/css-and-theming) - Additional theming techniques and CSS customization
- [Blog: Customizing Apache Superset Dashboards with CSS](https://preset.io/blog/customizing-superset-dashboards-with-css/)
- [Blog: Customizing Dashboards with CSS — Tips and Tricks](https://preset.io/blog/customizing-apache-superset-dashboards-with-css-additional-tips-and-tricks/)
- [Blog: Customizing Chart Colors](https://preset.io/blog/customizing-chart-colors-with-superset-and-preset/)
:::

View File

@@ -136,3 +136,7 @@ Security team members should also follow these general expectations:
- Actively participate in assessing, discussing, fixing, and releasing security issues in Superset.
- Avoid discussing security fixes in public forums. Pull request (PR) descriptions should not contain any information about security issues. The corresponding JIRA ticket should contain a link to the PR.
- Security team members who contribute to a fix may be listed as remediation developers in the CVE report, along with their job affiliation (if they choose to include it).
:::resources
- [Blog: Comprehensive Tutorial for Contributing Code to Apache Superset](https://preset.io/blog/tutorial-contributing-code-to-apache-superset/)
:::

View File

@@ -282,13 +282,13 @@ curl -f http://localhost:8088/health && echo "✅ Superset ready"
### LLM Session Best Practices
- Always validate environment setup first using the health checks above
- Use focused validation commands: `pre-commit run` (not `--all-files`)
- **Read [LLMS.md](https://github.com/apache/superset/blob/master/LLMS.md) first** - Contains comprehensive development guidelines, coding standards, and critical refactor information
- **Read [AGENTS.md](https://github.com/apache/superset/blob/master/AGENTS.md) first** - Contains comprehensive development guidelines, coding standards, and critical refactor information
- **Check platform-specific files** when available:
- `CLAUDE.md` - For Claude/Anthropic tools
- `CURSOR.md` - For Cursor editor
- `GEMINI.md` - For Google Gemini tools
- `GPT.md` - For OpenAI/ChatGPT tools
- Follow the TypeScript migration guidelines and avoid deprecated patterns listed in LLMS.md
- Follow the TypeScript migration guidelines and avoid deprecated patterns listed in AGENTS.md
### Key Development Commands
```bash
@@ -306,7 +306,7 @@ pytest tests/unit_tests/specific_test.py # Run single test file
pytest tests/unit_tests/ # Run all tests in directory
```
For detailed development context, environment setup, and coding guidelines, see [LLMS.md](https://github.com/apache/superset/blob/master/LLMS.md).
For detailed development context, environment setup, and coding guidelines, see [AGENTS.md](https://github.com/apache/superset/blob/master/AGENTS.md).
## Alternatives to `docker compose`
@@ -934,6 +934,20 @@ npm run storybook
When contributing new React components to Superset, please try to add a Story alongside the component's `jsx/tsx` file.
#### Testing Stories
Superset uses [@storybook/test-runner](https://storybook.js.org/docs/writing-tests/test-runner) to validate that all stories compile and render without errors. This helps catch broken stories before they're merged.
```bash
# Run against a running Storybook server (start with `npm run storybook` first)
npm run test-storybook
# Build static Storybook and test (CI-friendly, no server needed)
npm run test-storybook:ci
```
The `test-storybook` job runs automatically in CI on every pull request, ensuring stories remain functional.
## Tips
### Adding a new datasource
@@ -958,7 +972,12 @@ When contributing new React components to Superset, please try to add a Story al
The topic of authoring new plugins, whether you'd like to contribute
it back or not has been well documented in the
[the documentation](https://superset.apache.org/docs/contributing/creating-viz-plugins), and in [this blog post](https://preset.io/blog/building-custom-viz-plugins-in-superset-v2).
[documentation](https://superset.apache.org/docs/contributing/creating-viz-plugins).
:::resources
- [Blog: Building Custom Viz Plugins in Superset v2](https://preset.io/blog/building-custom-viz-plugins-in-superset-v2)
- [Blog: Enhancing Superset Visualization Plugins](https://preset.io/blog/enhancing-superset-visualization-plugins-part-1/)
:::
To contribute a plugin to Superset, your plugin must meet the following criteria:

View File

@@ -444,3 +444,8 @@ To load the examples, add the following to the `my_values.yaml` file:
init:
loadExamples: true
```
:::resources
- [Tutorial: Mastering Data Visualization — Installing Superset on Kubernetes with Helm Chart](https://mahira-technology.medium.com/mastering-data-visualization-installing-superset-on-kubernetes-cluster-using-helm-chart-e4ec99199e1e)
- [Tutorial: Installing Apache Superset in Kubernetes](https://aws.plainenglish.io/installing-apache-superset-in-kubernetes-1aec192ac495)
:::

View File

@@ -80,3 +80,9 @@ From this point on, you can head on to:
- [Installing on Kubernetes](/docs/installation/kubernetes/)
Or just explore our [Documentation](https://superset.apache.org/docs/intro)!
:::resources
- [Video: Superset in 2 Minutes](https://www.youtube.com/watch?v=AqousXQ7YHw)
- [Video: Superset 101](https://www.youtube.com/watch?v=mAIH3hUoxEE)
- [Tutorial: Creating Your First Dashboard](/docs/using-superset/creating-your-first-dashboard)
:::

View File

@@ -172,3 +172,8 @@ Rotating the `SUPERSET_SECRET_KEY` is a critical security procedure. It is manda
**Procedure for Rotating the Key**
The procedure for safely rotating the SECRET_KEY must be followed precisely to avoid locking yourself out of your instance. The official Apache Superset documentation maintains the correct, up-to-date procedure. Please follow the official guide here:
https://superset.apache.org/docs/configuration/configuring-superset/#rotating-to-a-newer-secret_key
:::resources
- [Blog: Running Apache Superset on the Open Internet](https://preset.io/blog/running-apache-superset-on-the-open-internet-a-report-from-the-fireline/)
- [Blog: How Security Vulnerabilities are Reported & Handled in Apache Superset](https://preset.io/blog/how-security-vulnerabilities-are-reported-and-handled-in-apache-superset/)
:::

View File

@@ -46,12 +46,62 @@ to all databases by default, both **Alpha** and **Gamma** users need to be given
### Public
To allow logged-out users to access some Superset features, you can use the `PUBLIC_ROLE_LIKE` config setting and assign it to another role whose permissions you want passed to this role.
The **Public** role is the most restrictive built-in role, designed specifically for anonymous/unauthenticated
users who need to view dashboards. It provides minimal read-only access for:
For example, by setting `PUBLIC_ROLE_LIKE = "Gamma"` in your `superset_config.py` file, you grant
public role the same set of permissions as for the **Gamma** role. This is useful if one
wants to enable anonymous users to view dashboards. Explicit grant on specific datasets is
still required, meaning that you need to edit the **Public** role and add the public data sources to the role manually.
- Viewing dashboards and charts
- Using interactive dashboard filters
- Accessing dashboard and chart permalinks
- Reading embedded dashboards
- Viewing annotations on charts
The Public role explicitly excludes:
- Any write permissions on dashboards, charts, or datasets
- SQL Lab access
- Share functionality
- User profile or admin features
- Menu access to most Superset features
Anonymous users are automatically assigned the Public role when `AUTH_ROLE_PUBLIC` is configured
(a Flask-AppBuilder setting). The `PUBLIC_ROLE_LIKE` setting is **optional** and controls what
permissions are synced to the Public role when you run `superset init`:
```python
# Optional: Sync sensible default permissions to the Public role
PUBLIC_ROLE_LIKE = "Public"
# Alternative: Copy permissions from Gamma for broader access
# PUBLIC_ROLE_LIKE = "Gamma"
```
If you prefer to manually configure the Public role's permissions (or use `DASHBOARD_RBAC` to
grant access at the dashboard level), you do not need to set `PUBLIC_ROLE_LIKE`.
**Important notes:**
- **Data access is still required:** The Public role only grants UI/API permissions. You must
also grant access to specific datasets necessary to view a dashboard. As with other roles,
this can be done in two ways:
- **Without `DASHBOARD_RBAC`:** Dashboards only appear in the list and are accessible if
the user has permission to at least one of their datasets. Grant dataset access by editing
the Public role in the Superset UI (Menu → Security → List Roles → Public) and adding the
relevant data sources. All published dashboards using those datasets become visible.
- **With `DASHBOARD_RBAC` enabled:** Anonymous users will only see dashboards where the
"Public" role has been explicitly added in the dashboard's properties. Dataset permissions
are not required—DASHBOARD_RBAC handles the cascading permissions check. This provides
fine-grained control over which dashboards are publicly visible.
- **Role synchronization:** Built-in role permissions (Admin, Alpha, Gamma, sql_lab, and Public
when `PUBLIC_ROLE_LIKE = "Public"`) are synchronized when you run `superset init`. Any manual
permission edits to these roles may be overwritten during upgrades. To customize the Public
role permissions, you can either:
- Edit the Public role directly and avoid setting `PUBLIC_ROLE_LIKE` (permissions won't be
overwritten by `superset init`)
- Copy the Public role via "Copy Role" in the Superset web UI, save it under a different name
(e.g., "Public_Custom"), customize the permissions, then update **both** configs:
`PUBLIC_ROLE_LIKE = "Public_Custom"` and `AUTH_ROLE_PUBLIC = "Public_Custom"`
### Managing Data Source Access for Gamma Roles
@@ -64,6 +114,46 @@ tables in the **Permissions** dropdown. To select the data sources you want to a
You can then confirm with users assigned to the **Gamma** role that they see the
objects (dashboards and slices) associated with the tables you just extended them.
### Dashboard Access Control
Access to dashboards is managed via owners (users that have edit permissions to the dashboard).
Non-owner user access can be managed in two ways. Note that dashboards must be published to be
visible to other users.
#### Dataset-Based Access (Default)
By default, users can view published dashboards if they have access to at least one dataset
used in that dashboard. Grant dataset access by adding the relevant data source permissions
to a role (Menu → Security → List Roles).
This is the simplest approach but provides all-or-nothing access based on dataset permissions—
if a user has access to a dataset, they can see all published dashboards using that dataset.
#### Dashboard-Level Access (DASHBOARD_RBAC)
For fine-grained control over which dashboards specific roles can access, enable the
`DASHBOARD_RBAC` feature flag:
```python
FEATURE_FLAGS = {
"DASHBOARD_RBAC": True,
}
```
With this enabled, you can assign specific roles to each dashboard in its properties. Users
will only see dashboards where their role is explicitly added.
**Important considerations:**
- Dashboard access **bypasses** dataset-level checks—granting a role access to a dashboard
implicitly grants read access to all charts and datasets in that dashboard
- Dashboards without any assigned roles fall back to dataset-based access
- The dashboard must still be published to be visible
This feature is particularly useful for:
- Making specific dashboards public while keeping others private
- Granting access to dashboards without exposing the underlying datasets for other uses
- Creating dashboard-specific access patterns that don't align with dataset ownership
### SQL Execution Security Considerations
Apache Superset includes features designed to provide safeguards when interacting with connected databases, such as the `DISALLOWED_SQL_FUNCTIONS` configuration setting. This aims to prevent the execution of potentially harmful database functions or system variables directly from Superset interfaces like SQL Lab.

View File

@@ -114,6 +114,12 @@ Aggregate functions aren't allowed in calculated columns.
<img src={useBaseUrl("/img/tutorial/tutorial_calculated_column.png" )} />
:::resources
- [Using Metrics and Calculated Columns](https://docs.preset.io/docs/using-metrics-and-calculated-columns) - In-depth guide to the semantic layer
- [Blog: Understanding the Superset Semantic Layer](https://preset.io/blog/understanding-superset-semantic-layer/)
- [Blog: Unlocking the Power of Virtual Datasets](https://preset.io/blog/unlocking-the-power-of-virtual-datasets-in-apache-superset/)
:::
### Creating charts in Explore view
Superset has 2 main interfaces for exploring data:
@@ -183,14 +189,12 @@ slices and dashboards of your own.
### Manage access to Dashboards
Access to dashboards is managed via owners (users that have edit permissions to the dashboard).
Access to dashboards is managed via owners and permissions. Non-owner access can be controlled
through dataset permissions or dashboard-level roles (using the `DASHBOARD_RBAC` feature flag).
Non-owner users access can be managed in two different ways. The dashboard needs to be published to be visible to other users.
1. Dataset permissions - if you add to the relevant role permissions to datasets it automatically grants implicit access to all dashboards that uses those permitted datasets.
2. Dashboard roles - if you enable [**DASHBOARD_RBAC** feature flag](/docs/configuration/configuring-superset#feature-flags) then you will be able to manage which roles can access the dashboard
- Granting a role access to a dashboard will bypass dataset level checks. Having dashboard access implicitly grants read access to all the featured charts in the dashboard, and thereby also all the associated datasets.
- If no roles are specified for a dashboard, regular **Dataset permissions** will apply.
For detailed information on configuring dashboard access, see the
[Dashboard Access Control](/docs/security/security#dashboard-access-control) section in the
Security documentation.
<img src={useBaseUrl("/img/tutorial/tutorial_dashboard_access.png" )} />
@@ -229,3 +233,8 @@ The following URL parameters can be used to modify how the dashboard is rendered
For example, when running the local development build, the following will disable the
Top Nav and remove the Filter Bar:
`http://localhost:8088/superset/dashboard/my-dashboard/?standalone=1&show_filters=0`
:::resources
- [Dashboard Customization](https://docs.preset.io/docs/dashboard-customization) - Advanced dashboard styling and layout options
- [Blog: BI Dashboard Best Practices](https://preset.io/blog/bi-dashboard-best-practices/)
:::

View File

@@ -9,9 +9,11 @@ import useBaseUrl from "@docusaurus/useBaseUrl";
## Exploring Data in Superset
In this tutorial, we will introduce key concepts in Apache Superset through the exploration of a
real dataset which contains the flights made by employees of a UK-based organization in 2011. The
following information about each flight is given:
Apache Superset enables users to explore data interactively through SQL queries, visual query builders, and rich visualizations, making it easier to understand datasets before building charts and dashboards.
In this tutorial, we will introduce key concepts in Apache Superset through the exploration of a real dataset which contains the flights made by employees of a UK-based organization in 2011.
The following information about each flight is given:
- The travelers department. For the purposes of this tutorial the departments have been renamed
Orange, Yellow and Purple.
@@ -326,3 +328,12 @@ various options in this section, refer to the
Lastly, save your chart as Tutorial Resample and add it to the Tutorial Dashboard. Go to the
tutorial dashboard to see the four charts side by side and compare the different outputs.
:::resources
- [Chart Walkthroughs](https://docs.preset.io/docs/chart-walkthroughs) - Detailed guides for most chart types
- [Blog: Why Apache ECharts is the Future of Apache Superset](https://preset.io/blog/2021-4-1-why-echarts/)
- [Blog: ECharts Time-Series Visualizations in Superset](https://preset.io/blog/echarts-time-series-visualizations-in-superset/)
- [Blog: Finding New Insights with Drill By](https://preset.io/blog/drill-by/)
- [Blog: From Drill Down to Drill By](https://preset.io/blog/drill-down-and-drill-by/)
- [Blog: Cross-Filtering in Apache Superset](https://preset.io/blog/cross-filtering-in-Superset-and-Preset/)
:::

View File

@@ -21,6 +21,7 @@ import type { Config } from '@docusaurus/types';
import type { Options, ThemeConfig } from '@docusaurus/preset-classic';
import { themes } from 'prism-react-renderer';
import remarkImportPartial from 'remark-import-partial';
import remarkLocalizeBadges from './plugins/remark-localize-badges.mjs';
import * as fs from 'fs';
import * as path from 'path';
@@ -44,7 +45,11 @@ if (!versionsConfig.components.disabled) {
sidebarPath: require.resolve('./sidebarComponents.js'),
editUrl:
'https://github.com/apache/superset/edit/master/docs/components',
remarkPlugins: [remarkImportPartial],
remarkPlugins: [remarkImportPartial, remarkLocalizeBadges],
admonitions: {
keywords: ['note', 'tip', 'info', 'warning', 'danger', 'resources'],
extendDefaults: true,
},
docItemComponent: '@theme/DocItem',
includeCurrentVersion: versionsConfig.components.includeCurrentVersion,
lastVersion: versionsConfig.components.lastVersion,
@@ -68,7 +73,11 @@ if (!versionsConfig.developer_portal.disabled) {
sidebarPath: require.resolve('./sidebarTutorials.js'),
editUrl:
'https://github.com/apache/superset/edit/master/docs/developer_portal',
remarkPlugins: [remarkImportPartial],
remarkPlugins: [remarkImportPartial, remarkLocalizeBadges],
admonitions: {
keywords: ['note', 'tip', 'info', 'warning', 'danger', 'resources'],
extendDefaults: true,
},
docItemComponent: '@theme/DocItem',
includeCurrentVersion: versionsConfig.developer_portal.includeCurrentVersion,
lastVersion: versionsConfig.developer_portal.lastVersion,
@@ -164,7 +173,11 @@ const config: Config = {
favicon: '/img/favicon.ico',
organizationName: 'apache',
projectName: 'superset',
themes: ['@saucelabs/theme-github-codeblock', '@docusaurus/theme-mermaid'],
themes: [
'@saucelabs/theme-github-codeblock',
'@docusaurus/theme-mermaid',
'@docusaurus/theme-live-codeblock',
],
plugins: [
require.resolve('./src/webpack.extend.ts'),
[
@@ -337,6 +350,11 @@ const config: Config = {
}
return `https://github.com/apache/superset/edit/master/docs/${versionDocsDirPath}/${docPath}`;
},
remarkPlugins: [remarkImportPartial, remarkLocalizeBadges],
admonitions: {
keywords: ['note', 'tip', 'info', 'warning', 'danger', 'resources'],
extendDefaults: true,
},
includeCurrentVersion: versionsConfig.docs.includeCurrentVersion,
lastVersion: versionsConfig.docs.lastVersion, // Make 'next' the default
onlyIncludeVersions: versionsConfig.docs.onlyIncludeVersions,
@@ -423,6 +441,14 @@ const config: Config = {
label: 'Stack Overflow',
href: 'https://stackoverflow.com/questions/tagged/apache-superset',
},
{
label: 'Community Calendar',
href: '/community#superset-community-calendar',
},
{
label: 'In the Wild',
href: '/inTheWild',
},
],
},
...dynamicNavbarItems,
@@ -474,6 +500,9 @@ const config: Config = {
hideable: true,
},
},
liveCodeBlock: {
playgroundPosition: 'bottom',
},
} satisfies ThemeConfig,
scripts: [
// {

56
docs/netlify.toml Normal file
View File

@@ -0,0 +1,56 @@
# 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.
# Netlify configuration for Superset documentation
# This enables automatic deploy previews for PRs that modify docs
[build]
# Base directory is the docs folder
base = "docs"
# Build command for Docusaurus
command = "yarn install && yarn build"
# Output directory (relative to base)
publish = "build"
# Skip builds when no docs changes (exit 0 = skip, exit 1 = build)
# Checks for changes in docs/ and README.md (which gets pulled into docs)
ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF -- . ../README.md"
[build.environment]
# Node version matching docs/.nvmrc
NODE_VERSION = "20"
# Yarn version
YARN_VERSION = "1.22.22"
# Deploy preview settings
[context.deploy-preview]
command = "yarn install && yarn build"
# Branch deploy settings (for feature branches)
[context.branch-deploy]
command = "yarn install && yarn build"
# Redirect /docs to the main docs page
[[redirects]]
from = "/docs"
to = "/docs/intro"
status = 301
# Handle SPA routing for Docusaurus
[[redirects]]
from = "/*"
to = "/index.html"
status = 200

View File

@@ -6,16 +6,17 @@
"scripts": {
"docusaurus": "docusaurus",
"_init": "cat src/intro_header.txt ../README.md > docs/intro.md",
"start": "yarn run _init && NODE_ENV=development docusaurus start",
"start": "yarn run _init && yarn run generate:extension-components && NODE_ENV=development docusaurus start",
"stop": "pkill -f 'docusaurus start' || pkill -f 'docusaurus serve' || echo 'No docusaurus server running'",
"build": "yarn run _init && DEBUG=docusaurus:* docusaurus build",
"build": "yarn run _init && yarn run generate:extension-components && DEBUG=docusaurus:* docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "yarn run _init && docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc",
"typecheck": "yarn run generate:extension-components && tsc",
"generate:extension-components": "node scripts/generate-extension-components.mjs",
"eslint": "eslint .",
"version:add": "node scripts/manage-versions.mjs add",
"version:remove": "node scripts/manage-versions.mjs remove",
@@ -31,13 +32,14 @@
"@docusaurus/core": "3.9.2",
"@docusaurus/plugin-client-redirects": "3.9.2",
"@docusaurus/preset-classic": "3.9.2",
"@docusaurus/theme-live-codeblock": "^3.9.2",
"@docusaurus/theme-mermaid": "^3.9.2",
"@emotion/core": "^11.0.0",
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.14.1",
"@mdx-js/react": "^3.1.1",
"@saucelabs/theme-github-codeblock": "^0.3.0",
"@storybook/addon-docs": "^8.6.11",
"@storybook/addon-docs": "^8.6.15",
"@storybook/blocks": "^8.6.11",
"@storybook/channels": "^8.6.11",
"@storybook/client-logger": "^8.6.11",
@@ -49,9 +51,11 @@
"@storybook/preview-api": "^8.6.11",
"@storybook/theming": "^8.6.11",
"@superset-ui/core": "^0.20.4",
"antd": "^6.1.0",
"caniuse-lite": "^1.0.30001760",
"antd": "^6.2.0",
"caniuse-lite": "^1.0.30001764",
"docusaurus-plugin-less": "^2.0.2",
"js-yaml": "^4.1.1",
"js-yaml-loader": "^1.2.2",
"json-bigint": "^1.0.0",
"less": "^4.5.1",
"less-loader": "^12.3.0",
@@ -62,27 +66,29 @@
"react-svg-pan-zoom": "^3.13.1",
"remark-import-partial": "^0.0.2",
"reselect": "^5.1.1",
"storybook": "^8.6.11",
"storybook": "^8.6.15",
"swagger-ui-react": "^5.31.0",
"tinycolor2": "^1.4.2",
"ts-loader": "^9.5.4"
"ts-loader": "^9.5.4",
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.9.1",
"@docusaurus/tsconfig": "^3.9.2",
"@eslint/js": "^9.39.2",
"@types/js-yaml": "^4.0.9",
"@types/react": "^19.1.8",
"@typescript-eslint/eslint-plugin": "^8.37.0",
"@typescript-eslint/parser": "^8.49.0",
"@typescript-eslint/eslint-plugin": "^8.52.0",
"@typescript-eslint/parser": "^8.52.0",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.3",
"eslint-plugin-react": "^7.37.5",
"globals": "^16.5.0",
"globals": "^17.0.0",
"prettier": "^3.7.4",
"typescript": "~5.9.3",
"typescript-eslint": "^8.49.0",
"webpack": "^5.103.0"
"typescript-eslint": "^8.53.0",
"webpack": "^5.104.1"
},
"browserslist": {
"production": [

View File

@@ -0,0 +1,286 @@
/**
* 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.
*/
/**
* Remark plugin to localize badge images from shields.io and similar services.
*
* This plugin downloads badge SVGs at build time and serves them locally,
* avoiding external dependencies and caching issues with dynamic badges.
*
* Inspired by Apache Commons' fixshields.py approach.
*/
import { visit } from 'unist-util-visit';
import * as fs from 'fs';
import * as path from 'path';
import * as crypto from 'crypto';
// Badge domains to localize (always localize all URLs from these domains)
const BADGE_DOMAINS = [
'img.shields.io',
'badge.fury.io',
'codecov.io',
'badgen.net',
'nodei.co',
];
// Patterns for badge URLs on other domains (e.g., GitHub Actions badges)
const BADGE_PATH_PATTERNS = [
/github\.com\/.*\/actions\/workflows\/.*\/badge\.svg/,
/github\.com\/.*\/badge\.svg/,
];
// Cache for downloaded badges (persists across files in a single build)
const badgeCache = new Map();
// Track in-flight downloads to prevent duplicate concurrent requests
const inFlightDownloads = new Map();
// Track if we've already ensured the badges directory exists
let badgesDirCreated = false;
// Retry configuration
const MAX_RETRIES = 3;
const RETRY_DELAY_MS = 1000;
/**
* Sleep for a given number of milliseconds
*/
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Generate a stable filename for a badge URL
*/
function getBadgeFilename(url) {
const hash = crypto.createHash('md5').update(url).digest('hex').slice(0, 12);
// Extract a readable name from the URL
const urlPath = new URL(url).pathname;
const readablePart = urlPath
.replace(/^\//, '')
.replace(/[^a-zA-Z0-9-]/g, '_')
.slice(0, 40);
return `${readablePart}_${hash}.svg`;
}
/**
* Check if a URL is a badge we should localize
*/
function isBadgeUrl(url) {
if (!url) return false;
try {
const parsed = new URL(url);
// Check if it's from a known badge domain
if (BADGE_DOMAINS.some(domain => parsed.hostname.includes(domain))) {
return true;
}
// Check if it matches a badge path pattern
return BADGE_PATH_PATTERNS.some(pattern => pattern.test(url));
} catch {
return false;
}
}
/**
* Fetch a badge with retry logic
*/
async function fetchWithRetry(url, retries = MAX_RETRIES) {
let lastError;
for (let attempt = 1; attempt <= retries; attempt++) {
try {
const response = await fetch(url, {
headers: {
// Some services need a user agent
'User-Agent': 'Mozilla/5.0 (compatible; DocusaurusBuild/1.0)',
Accept: 'image/svg+xml,image/*,*/*',
},
// Follow redirects
redirect: 'follow',
// Add timeout to prevent hanging
signal: AbortSignal.timeout(30000),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response;
} catch (error) {
lastError = error;
if (attempt < retries) {
const delay = RETRY_DELAY_MS * attempt; // Exponential backoff
console.log(
`[remark-localize-badges] Retry ${attempt}/${retries} for ${url} after ${delay}ms...`,
);
await sleep(delay);
}
}
}
throw lastError;
}
/**
* Download a badge and return the local path
*/
async function downloadBadge(url, staticDir) {
// Check memory cache first
if (badgeCache.has(url)) {
return badgeCache.get(url);
}
const badgesDir = path.join(staticDir, 'badges');
// Ensure badges directory exists
if (!badgesDirCreated) {
fs.mkdirSync(badgesDir, { recursive: true });
badgesDirCreated = true;
}
const filename = getBadgeFilename(url);
const localPath = path.join(badgesDir, filename);
const webPath = `/badges/${filename}`;
// Check if already downloaded in a previous build or by another concurrent request
if (fs.existsSync(localPath)) {
badgeCache.set(url, webPath);
return webPath;
}
// Check if there's already an in-flight download for this URL
// This prevents duplicate concurrent downloads of the same badge
if (inFlightDownloads.has(url)) {
return inFlightDownloads.get(url);
}
// Create the download promise and store it
const downloadPromise = (async () => {
// Double-check file existence after acquiring the "lock"
if (fs.existsSync(localPath)) {
badgeCache.set(url, webPath);
return webPath;
}
console.log(`[remark-localize-badges] Downloading: ${url}`);
try {
const response = await fetchWithRetry(url);
const contentType = response.headers.get('content-type') || '';
const content = await response.text();
// Validate it's actually an SVG or image
if (
!contentType.includes('svg') &&
!contentType.includes('image') &&
!content.trim().startsWith('<svg') &&
!content.trim().startsWith('<?xml')
) {
throw new Error(
`Invalid content type: ${contentType}. Expected SVG image.`,
);
}
// Write the badge to disk
fs.writeFileSync(localPath, content, 'utf8');
console.log(`[remark-localize-badges] Saved: ${filename}`);
badgeCache.set(url, webPath);
return webPath;
} catch (error) {
// Fail the build on badge download failure
throw new Error(
`[remark-localize-badges] Failed to download badge: ${url}\n` +
`Error: ${error.message}\n` +
`Build cannot continue with broken badges. Please fix the badge URL or remove it.`,
);
} finally {
// Clean up the in-flight tracker
inFlightDownloads.delete(url);
}
})();
inFlightDownloads.set(url, downloadPromise);
return downloadPromise;
}
/**
* The remark plugin factory
*/
export default function remarkLocalizeBadges(options = {}) {
// __dirname equivalent for ES modules - use import.meta.url
const currentDir = path.dirname(new URL(import.meta.url).pathname);
const docsRoot = path.resolve(currentDir, '..');
const staticDir = options.staticDir || path.join(docsRoot, 'static');
return async function transformer(tree) {
const promises = [];
// Find all image nodes
visit(tree, 'image', node => {
if (isBadgeUrl(node.url)) {
promises.push(
downloadBadge(node.url, staticDir).then(localPath => {
node.url = localPath;
}),
);
}
});
// Also handle HTML img tags in raw HTML or JSX
visit(tree, ['html', 'jsx'], node => {
if (!node.value) return;
// Find img src attributes pointing to badge URLs
const imgRegex = /<img[^>]+src=["']([^"']+)["'][^>]*>/gi;
let match;
while ((match = imgRegex.exec(node.value)) !== null) {
const url = match[1];
if (isBadgeUrl(url)) {
promises.push(
downloadBadge(url, staticDir).then(localPath => {
node.value = node.value.replace(url, localPath);
}),
);
}
}
});
// Also handle markdown link images: [![alt](img-url)](link-url)
visit(tree, 'link', node => {
if (node.children) {
node.children.forEach(child => {
if (child.type === 'image' && isBadgeUrl(child.url)) {
promises.push(
downloadBadge(child.url, staticDir).then(localPath => {
child.url = localPath;
}),
);
}
});
}
});
// Wait for all downloads to complete
await Promise.all(promises);
};
}

View File

@@ -0,0 +1,676 @@
/**
* 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.
*/
/**
* This script scans for Storybook stories in superset-core/src and generates
* MDX documentation pages for the developer portal. All components in
* superset-core are considered extension-compatible by virtue of their location.
*
* Usage: node scripts/generate-extension-components.mjs
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const ROOT_DIR = path.resolve(__dirname, '../..');
const DOCS_DIR = path.resolve(__dirname, '..');
const OUTPUT_DIR = path.join(
DOCS_DIR,
'developer_portal/extensions/components'
);
const TYPES_OUTPUT_DIR = path.join(DOCS_DIR, 'src/types/apache-superset-core');
const TYPES_OUTPUT_PATH = path.join(TYPES_OUTPUT_DIR, 'index.d.ts');
const SUPERSET_CORE_DIR = path.join(
ROOT_DIR,
'superset-frontend/packages/superset-core'
);
/**
* Find all story files in the superset-core package
*/
async function findStoryFiles() {
const files = [];
// Use fs to recursively find files since glob might not be available
function walkDir(dir) {
if (!fs.existsSync(dir)) return;
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
walkDir(fullPath);
} else if (entry.name.endsWith('.stories.tsx')) {
files.push(fullPath);
}
}
}
walkDir(path.join(SUPERSET_CORE_DIR, 'src'));
return files;
}
/**
* Parse a story file and extract metadata
*
* All stories in superset-core are considered extension-compatible
* by virtue of their location - no tag needed.
*/
function parseStoryFile(filePath) {
const content = fs.readFileSync(filePath, 'utf-8');
// Extract component name from title
const titleMatch = content.match(/title:\s*['"]([^'"]+)['"]/);
const title = titleMatch ? titleMatch[1] : null;
// Extract component name (last part of title path)
const componentName = title ? title.split('/').pop() : null;
// Extract description from parameters
// Handle concatenated strings like: 'part1 ' + 'part2'
let description = '';
// First try to find the description block
const descBlockMatch = content.match(
/description:\s*{\s*component:\s*([\s\S]*?)\s*},?\s*}/
);
if (descBlockMatch) {
const descBlock = descBlockMatch[1];
// Extract all string literals and concatenate them
const stringParts = [];
const stringMatches = descBlock.matchAll(/['"]([^'"]*)['"]/g);
for (const match of stringMatches) {
stringParts.push(match[1]);
}
description = stringParts.join('').trim();
}
// Extract package info
const packageMatch = content.match(/package:\s*['"]([^'"]+)['"]/);
const packageName = packageMatch ? packageMatch[1] : '@apache-superset/core/ui';
// Extract import path - handle double-quoted strings containing single quotes
// Match: importPath: "import { Alert } from '@apache-superset/core';"
const importMatchDouble = content.match(/importPath:\s*"([^"]+)"/);
const importMatchSingle = content.match(/importPath:\s*'([^']+)'/);
let importPath = `import { ${componentName} } from '${packageName}';`;
if (importMatchDouble) {
importPath = importMatchDouble[1];
} else if (importMatchSingle) {
importPath = importMatchSingle[1];
}
// Get the directory containing the story to find the component
const storyDir = path.dirname(filePath);
const componentFile = path.join(storyDir, 'index.tsx');
const hasComponentFile = fs.existsSync(componentFile);
// Try to extract props interface from component file (for future use)
if (hasComponentFile) {
// Read component file - props extraction reserved for future enhancement
// const componentContent = fs.readFileSync(componentFile, 'utf-8');
}
// Extract story exports (named exports that aren't the default)
const storyExports = [];
const exportMatches = content.matchAll(
/export\s+(?:const|function)\s+(\w+)/g
);
for (const match of exportMatches) {
if (match[1] !== 'default') {
storyExports.push(match[1]);
}
}
return {
filePath,
title,
componentName,
description,
packageName,
importPath,
storyExports,
hasComponentFile,
relativePath: path.relative(ROOT_DIR, filePath),
};
}
/**
* Extract argTypes/args from story content for generating controls
*/
function extractArgsAndControls(content, componentName, storyContent) {
// Look for InteractiveX.args pattern - handle multi-line objects
const argsMatch = content.match(
new RegExp(`Interactive${componentName}\\.args\\s*=\\s*\\{([\\s\\S]*?)\\};`, 's')
);
// Look for argTypes
const argTypesMatch = content.match(
new RegExp(`Interactive${componentName}\\.argTypes\\s*=\\s*\\{([\\s\\S]*?)\\};`, 's')
);
const args = {};
const controls = [];
const propDescriptions = {};
if (argsMatch) {
// Parse args - handle strings, booleans, numbers
// Note: Using simple regex without escape handling for security (avoids ReDoS)
// This is sufficient for Storybook args which rarely contain escaped quotes
const argsContent = argsMatch[1];
const argLines = argsContent.matchAll(/(\w+):\s*(['"]([^'"]*)['"']|true|false|\d+)/g);
for (const match of argLines) {
const key = match[1];
let value = match[2];
// Convert string booleans
if (value === 'true') value = true;
else if (value === 'false') value = false;
else if (!isNaN(Number(value))) value = Number(value);
else if (match[3] !== undefined) value = match[3]; // Use captured string content
args[key] = value;
}
}
if (argTypesMatch) {
const argTypesContent = argTypesMatch[1];
// Match each top-level property in argTypes
// Pattern: propertyName: { ... }, (with balanced braces)
const propPattern = /(\w+):\s*\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}/g;
let propMatch;
while ((propMatch = propPattern.exec(argTypesContent)) !== null) {
const name = propMatch[1];
const propContent = propMatch[2];
// Extract description if present
const descMatch = propContent.match(/description:\s*['"]([^'"]+)['"]/);
if (descMatch) {
propDescriptions[name] = descMatch[1];
}
// Skip if it's an action (not a control)
if (propContent.includes('action:')) continue;
// Extract label for display
const label = name.charAt(0).toUpperCase() + name.slice(1).replace(/([A-Z])/g, ' $1');
// Check for select control
if (propContent.includes("type: 'select'") || propContent.includes('type: "select"')) {
// Look for options - could be inline array or variable reference
const inlineOptionsMatch = propContent.match(/options:\s*\[([^\]]+)\]/);
const varOptionsMatch = propContent.match(/options:\s*(\w+)/);
let options = [];
if (inlineOptionsMatch) {
options = [...inlineOptionsMatch[1].matchAll(/['"]([^'"]+)['"]/g)].map(m => m[1]);
} else if (varOptionsMatch && storyContent) {
// Look up the variable
const varName = varOptionsMatch[1];
const varDefMatch = storyContent.match(
new RegExp(`const\\s+${varName}[^=]*=\\s*\\[([^\\]]+)\\]`)
);
if (varDefMatch) {
options = [...varDefMatch[1].matchAll(/['"]([^'"]+)['"]/g)].map(m => m[1]);
}
}
if (options.length > 0) {
controls.push({ name, label, type: 'select', options });
}
}
// Check for boolean control
else if (propContent.includes("type: 'boolean'") || propContent.includes('type: "boolean"')) {
controls.push({ name, label, type: 'boolean' });
}
// Check for text/string control (default for props in args without explicit control)
else if (args[name] !== undefined && typeof args[name] === 'string') {
controls.push({ name, label, type: 'text' });
}
}
}
// Add text controls for string args that don't have explicit argTypes
for (const [key, value] of Object.entries(args)) {
if (typeof value === 'string' && !controls.find(c => c.name === key)) {
const label = key.charAt(0).toUpperCase() + key.slice(1).replace(/([A-Z])/g, ' $1');
controls.push({ name: key, label, type: 'text' });
}
}
return { args, controls, propDescriptions };
}
/**
* Generate MDX content for a component
*/
function generateMDX(component, storyContent) {
const { componentName, description, importPath, packageName, relativePath } =
component;
// Extract args, controls, and descriptions from the story
const { args, controls, propDescriptions } = extractArgsAndControls(storyContent, componentName, storyContent);
// Generate the controls array for StoryWithControls
const controlsJson = JSON.stringify(controls, null, 2)
.replace(/"(\w+)":/g, '$1:') // Remove quotes from keys
.replace(/"/g, "'"); // Use single quotes for strings
// Generate default props
const propsJson = JSON.stringify(args, null, 2)
.replace(/"(\w+)":/g, '$1:')
.replace(/"/g, "'");
// Generate a realistic live code example from the actual args
const liveExampleProps = Object.entries(args)
.map(([key, value]) => {
if (typeof value === 'string') return `${key}="${value}"`;
if (typeof value === 'boolean') return value ? key : null;
return `${key}={${JSON.stringify(value)}}`;
})
.filter(Boolean)
.join('\n ');
// Generate props table with descriptions from argTypes
const propsTable = Object.entries(args).map(([key, value]) => {
const type = typeof value === 'boolean' ? 'boolean' : typeof value === 'string' ? 'string' : 'any';
const desc = propDescriptions[key] || key.charAt(0).toUpperCase() + key.slice(1).replace(/([A-Z])/g, ' $1');
return `| \`${key}\` | \`${type}\` | \`${JSON.stringify(value)}\` | ${desc} |`;
}).join('\n');
// Generate usage example props (simplified for readability)
const usageExampleProps = Object.entries(args)
.slice(0, 3) // Show first 3 props for brevity
.map(([key, value]) => {
if (typeof value === 'string') return `${key}="${value}"`;
if (typeof value === 'boolean') return value ? key : `${key}={false}`;
return `${key}={${JSON.stringify(value)}}`;
})
.join('\n ');
return `---
title: ${componentName}
sidebar_label: ${componentName}
---
<!--
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 { StoryWithControls } from '../../../src/components/StorybookWrapper';
import { ${componentName} } from '@apache-superset/core/ui';
# ${componentName}
${description || `The ${componentName} component from the Superset extension API.`}
## Live Example
<StoryWithControls
component={${componentName}}
props={${propsJson}}
controls={${controlsJson}}
/>
## Try It
Edit the code below to experiment with the component:
\`\`\`tsx live
function Demo() {
return (
<${componentName}
${liveExampleProps}
/>
);
}
\`\`\`
## Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
${propsTable}
## Usage in Extensions
This component is available in the \`${packageName}\` package, which is automatically available to Superset extensions.
\`\`\`tsx
${importPath}
function MyExtension() {
return (
<${componentName}
${usageExampleProps}
/>
);
}
\`\`\`
## Source Links
- [Story file](https://github.com/apache/superset/blob/master/${relativePath})
- [Component source](https://github.com/apache/superset/blob/master/${relativePath.replace(/\/[^/]+\.stories\.tsx$/, '/index.tsx')})
---
*This page was auto-generated from the component's Storybook story.*
`;
}
/**
* Generate index page for extension components
*/
function generateIndexMDX(components) {
const componentList = components
.map(c => `- [${c.componentName}](./${c.componentName.toLowerCase()})`)
.join('\n');
return `---
title: Extension Components
sidebar_label: Overview
sidebar_position: 1
---
<!--
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.
-->
# Extension Components
These UI components are available to Superset extension developers through the \`@apache-superset/core/ui\` package. They provide a consistent look and feel with the rest of Superset and are designed to be used in extension panels, views, and other UI elements.
## Available Components
${componentList}
## Usage
All components are exported from the \`@apache-superset/core/ui\` package:
\`\`\`tsx
import { Alert } from '@apache-superset/core/ui';
export function MyExtensionPanel() {
return (
<Alert type="info">
Welcome to my extension!
</Alert>
);
}
\`\`\`
## Adding New Components
Components in \`@apache-superset/core/ui\` are automatically documented here. To add a new extension component:
1. Add the component to \`superset-frontend/packages/superset-core/src/ui/components/\`
2. Export it from \`superset-frontend/packages/superset-core/src/ui/components/index.ts\`
3. Create a Storybook story with an \`Interactive\` export:
\`\`\`tsx
export default {
title: 'Extension Components/MyComponent',
component: MyComponent,
parameters: {
docs: {
description: {
component: 'Description of the component...',
},
},
},
};
export const InteractiveMyComponent = (args) => <MyComponent {...args} />;
InteractiveMyComponent.args = {
variant: 'primary',
disabled: false,
};
InteractiveMyComponent.argTypes = {
variant: {
control: { type: 'select' },
options: ['primary', 'secondary'],
},
disabled: {
control: { type: 'boolean' },
},
};
\`\`\`
4. Run \`yarn start\` in \`docs/\` - the page generates automatically!
## Interactive Documentation
For interactive examples with controls, visit the [Storybook](/storybook/?path=/docs/extension-components--docs).
`;
}
/**
* Extract type exports from a component file
*/
function extractComponentTypes(componentPath) {
if (!fs.existsSync(componentPath)) {
return null;
}
const content = fs.readFileSync(componentPath, 'utf-8');
const types = [];
// Find all "export type X = ..." declarations
const typeMatches = content.matchAll(/export\s+type\s+(\w+)\s*=\s*([^;]+);/g);
for (const match of typeMatches) {
types.push({
name: match[1],
definition: match[2].trim(),
});
}
// Find all "export const X = ..." declarations (components)
const constMatches = content.matchAll(/export\s+const\s+(\w+)\s*[=:]/g);
const components = [];
for (const match of constMatches) {
components.push(match[1]);
}
return { types, components };
}
/**
* Generate the type declarations file content
*/
function generateTypeDeclarations(componentInfos) {
const imports = new Set();
const typeDeclarations = [];
const componentDeclarations = [];
for (const info of componentInfos) {
const componentDir = path.dirname(info.filePath);
const componentFile = path.join(componentDir, 'index.tsx');
const extracted = extractComponentTypes(componentFile);
if (!extracted) continue;
// Check if types reference antd or react
for (const type of extracted.types) {
if (type.definition.includes('AntdAlertProps') || type.definition.includes('AlertProps')) {
imports.add("import type { AlertProps as AntdAlertProps } from 'antd/es/alert';");
}
if (type.definition.includes('PropsWithChildren') || type.definition.includes('FC')) {
imports.add("import type { PropsWithChildren, FC } from 'react';");
}
// Add the type declaration
typeDeclarations.push(` export type ${type.name} = ${type.definition};`);
}
// Add component declarations
for (const comp of extracted.components) {
const propsType = `${comp}Props`;
const hasPropsType = extracted.types.some(t => t.name === propsType);
if (hasPropsType) {
componentDeclarations.push(` export const ${comp}: FC<${propsType}>;`);
} else {
componentDeclarations.push(` export const ${comp}: FC<Record<string, unknown>>;`);
}
}
}
// Remove 'export' prefix for direct exports (not in declare module)
const cleanedTypes = typeDeclarations.map(t => t.replace(/^ {2}export /, 'export '));
const cleanedComponents = componentDeclarations.map(c => c.replace(/^ {2}export /, 'export '));
return `/**
* 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.
*/
/**
* Type declarations for @apache-superset/core/ui
*
* AUTO-GENERATED by scripts/generate-extension-components.mjs
* Do not edit manually - regenerate by running: yarn generate:extension-components
*/
${Array.from(imports).join('\n')}
${cleanedTypes.join('\n')}
${cleanedComponents.join('\n')}
`;
}
/**
* Main function
*/
async function main() {
console.log('Scanning for extension-compatible stories...\n');
// Find all story files
const storyFiles = await findStoryFiles();
console.log(`Found ${storyFiles.length} story files in superset-core\n`);
// Parse each story file
const components = [];
for (const file of storyFiles) {
const parsed = parseStoryFile(file);
if (parsed) {
components.push(parsed);
console.log(`${parsed.componentName} (${parsed.relativePath})`);
}
}
if (components.length === 0) {
console.log(
'\nNo extension-compatible components found. Make sure stories have:'
);
console.log(" tags: ['extension-compatible']");
return;
}
console.log(`\nFound ${components.length} extension-compatible components\n`);
// Ensure output directory exists
if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
console.log(`Created directory: ${OUTPUT_DIR}\n`);
}
// Generate MDX files
for (const component of components) {
// Read the story content for extracting args/controls
const storyContent = fs.readFileSync(component.filePath, 'utf-8');
const mdxContent = generateMDX(component, storyContent);
const outputPath = path.join(
OUTPUT_DIR,
`${component.componentName.toLowerCase()}.mdx`
);
fs.writeFileSync(outputPath, mdxContent);
console.log(` Generated: ${path.relative(DOCS_DIR, outputPath)}`);
}
// Generate index page
const indexContent = generateIndexMDX(components);
const indexPath = path.join(OUTPUT_DIR, 'index.mdx');
fs.writeFileSync(indexPath, indexContent);
console.log(` Generated: ${path.relative(DOCS_DIR, indexPath)}`);
// Generate type declarations
if (!fs.existsSync(TYPES_OUTPUT_DIR)) {
fs.mkdirSync(TYPES_OUTPUT_DIR, { recursive: true });
}
const typesContent = generateTypeDeclarations(components);
fs.writeFileSync(TYPES_OUTPUT_PATH, typesContent);
console.log(` Generated: ${path.relative(DOCS_DIR, TYPES_OUTPUT_PATH)}`);
console.log('\nDone! Extension component documentation generated.');
console.log(
`\nGenerated ${components.length + 2} files (${components.length + 1} MDX + 1 type declaration)`
);
}
main().catch(console.error);

View File

@@ -42,32 +42,24 @@ const sidebars = {
'contributing/howtos',
'contributing/release-process',
'contributing/resources',
'guidelines/design-guidelines',
{
type: 'category',
label: 'Contribution Guidelines',
label: 'Frontend Style Guidelines',
collapsed: true,
items: [
'guidelines/design-guidelines',
{
type: 'category',
label: 'Frontend Style Guidelines',
collapsed: true,
items: [
'guidelines/frontend-style-guidelines',
'guidelines/frontend/component-style-guidelines',
'guidelines/frontend/emotion-styling-guidelines',
'guidelines/frontend/testing-guidelines',
],
},
{
type: 'category',
label: 'Backend Style Guidelines',
collapsed: true,
items: [
'guidelines/backend-style-guidelines',
'guidelines/backend/dao-style-guidelines',
],
},
'guidelines/frontend-style-guidelines',
'guidelines/frontend/component-style-guidelines',
'guidelines/frontend/emotion-styling-guidelines',
],
},
{
type: 'category',
label: 'Backend Style Guidelines',
collapsed: true,
items: [
'guidelines/backend-style-guidelines',
'guidelines/backend/dao-style-guidelines',
],
},
],
@@ -80,7 +72,19 @@ const sidebars = {
'extensions/overview',
'extensions/quick-start',
'extensions/architecture',
'extensions/dependencies',
'extensions/contribution-types',
{
type: 'category',
label: 'Components',
collapsed: true,
items: [
{
type: 'autogenerated',
dirName: 'extensions/components',
},
],
},
{
type: 'category',
label: 'Extension Points',
@@ -102,6 +106,7 @@ const sidebars = {
collapsed: true,
items: [
'testing/overview',
'testing/testing-guidelines',
'testing/frontend-testing',
'testing/backend-testing',
'testing/e2e-testing',

View File

@@ -0,0 +1,165 @@
/**
* 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 Layout from '@theme/Layout';
import { Avatar, Card, Col, Collapse, Row, Typography } from 'antd';
import BlurredSection from '../components/BlurredSection';
import SectionHeader from '../components/SectionHeader';
import DataSet from '../../../RESOURCES/INTHEWILD.yaml';
const { Text, Link } = Typography;
interface Organization {
name: string;
url: string;
logo?: string;
contributors?: string[];
}
interface DataSetType {
categories: Record<string, Organization[]>;
}
const typedDataSet = DataSet as DataSetType;
const ContributorAvatars = ({ contributors }: { contributors?: string[] }) => {
if (!contributors?.length) return null;
return (
<Avatar.Group size="small" max={{ count: 3 }}>
{contributors.map((handle) => {
const username = handle.replace('@', '');
return (
<a
key={username}
href={`https://github.com/${username}`}
target="_blank"
rel="noreferrer"
onClick={(e) => e.stopPropagation()}
>
<Avatar
src={`https://github.com/${username}.png?size=40`}
alt={username}
style={{ cursor: 'pointer' }}
>
{username.charAt(0).toUpperCase()}
</Avatar>
</a>
);
})}
</Avatar.Group>
);
};
export default function InTheWild() {
return (
<Layout title="In the Wild" description="Organizations using Apache Superset">
<main>
<BlurredSection>
<SectionHeader
level="h2"
title="In the Wild"
subtitle="See who's using Superset and join our growing community"
/>
<div style={{ textAlign: 'center', marginTop: 10 }}>
<Link
href="https://github.com/apache/superset/edit/master/RESOURCES/INTHEWILD.yaml"
target="_blank"
>
Add your name/org!
</Link>
</div>
</BlurredSection>
<div style={{ maxWidth: 850, margin: '70px auto 60px', padding: '0 20px' }}>
<Collapse
bordered={false}
defaultActiveKey={Object.keys(typedDataSet.categories)}
style={{
background: 'var(--ifm-background-color)',
border: '1px solid var(--ifm-border-color)',
borderRadius: 10,
}}
items={Object.entries(typedDataSet.categories).map(([category, items]) => {
const logoItems = items.filter(({ logo }) => logo?.trim());
const textItems = items.filter(({ logo }) => !logo?.trim());
return {
key: category,
label: (
<Text strong style={{ fontSize: 16, lineHeight: '22px', color: 'var(--ifm-font-base-color)' }}>
{category} ({items.length})
</Text>
),
children: (
<>
{logoItems.length > 0 && (
<Row gutter={[16, 16]} style={{ marginBottom: textItems.length > 0 ? 24 : 0 }}>
{logoItems.map(({ name, url, logo, contributors }) => (
<Col xs={24} sm={12} md={8} key={name}>
<a href={url} target="_blank" rel="noreferrer">
<Card
hoverable
style={{ height: 150, position: 'relative' }}
styles={{ body: { padding: 16, height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' } }}
>
<img
src={`/img/logos/${logo}`}
alt={name}
style={{ maxHeight: 80, maxWidth: '100%', objectFit: 'contain' }}
/>
{contributors?.length && (
<div style={{ position: 'absolute', bottom: 8, right: 8 }}>
<ContributorAvatars contributors={contributors} />
</div>
)}
</Card>
</a>
</Col>
))}
</Row>
)}
{textItems.length > 0 && (
<Row gutter={[8, 8]}>
{textItems.map(({ name, url, contributors }) => (
<Col xs={24} sm={12} md={8} key={name}>
<a href={url} target="_blank" rel="noreferrer">
<Card
size="small"
hoverable
styles={{ body: { padding: '8px 12px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 } }}
>
<Text ellipsis style={{ flex: 1 }}>{name}</Text>
<ContributorAvatars contributors={contributors} />
</Card>
</a>
</Col>
))}
</Row>
)}
</>
),
};
})}
/>
</div>
</main>
</Layout>
);
}

View File

@@ -19,15 +19,43 @@
import { useRef, useState, useEffect, JSX } from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
import { Carousel } from 'antd';
import { Card, Carousel, Flex } from 'antd';
import styled from '@emotion/styled';
import GitHubButton from 'react-github-btn';
import { mq } from '../utils';
import { Databases } from '../resources/data';
import SectionHeader from '../components/SectionHeader';
import BlurredSection from '../components/BlurredSection';
import DataSet from '../../../RESOURCES/INTHEWILD.yaml';
import '../styles/main.less';
interface Organization {
name: string;
url: string;
logo?: string;
}
interface DataSetType {
categories: Record<string, Organization[]>;
}
const typedDataSet = DataSet as DataSetType;
// Extract all organizations with logos for the carousel
const companiesWithLogos = Object.values(typedDataSet.categories)
.flat()
.filter((org) => org.logo?.trim());
// Fisher-Yates shuffle for fair randomization
function shuffleArray<T>(array: T[]): T[] {
const shuffled = [...array];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled;
}
const features = [
{
image: 'powerful-yet-easy.jpg',
@@ -452,6 +480,7 @@ export default function Home(): JSX.Element {
const slider = useRef(null);
const [slideIndex, setSlideIndex] = useState(0);
const [shuffledCompanies, setShuffledCompanies] = useState(companiesWithLogos);
const onChange = (current, next) => {
setSlideIndex(next);
@@ -479,6 +508,11 @@ export default function Home(): JSX.Element {
}
};
// Shuffle companies on mount for fair rotation
useEffect(() => {
setShuffledCompanies(shuffleArray(companiesWithLogos));
}, []);
// Set up dark <-> light navbar change
useEffect(() => {
changeToDark();
@@ -747,6 +781,74 @@ export default function Home(): JSX.Element {
</span>
</StyledIntegrations>
</BlurredSection>
{/* Only show carousel when we have enough logos (>10) for a good display */}
{companiesWithLogos.length > 10 && (
<BlurredSection>
<div style={{ padding: '0 20px' }}>
<SectionHeader
level="h2"
title="Trusted by teams everywhere"
subtitle="Join thousands of companies using Superset to explore and visualize their data"
/>
<div style={{ maxWidth: 1160, margin: '25px auto 0' }}>
<Carousel
autoplay
autoplaySpeed={2000}
slidesToShow={6}
slidesToScroll={1}
dots={false}
responsive={[
{ breakpoint: 1024, settings: { slidesToShow: 4 } },
{ breakpoint: 768, settings: { slidesToShow: 3 } },
{ breakpoint: 480, settings: { slidesToShow: 2 } },
]}
>
{shuffledCompanies.map(({ name, url, logo }) => (
<div key={name}>
<a
href={url}
target="_blank"
rel="noreferrer"
aria-label={`Visit ${name}`}
>
<Card
style={{ margin: '0 8px' }}
styles={{
body: {
height: 80,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: 16,
},
}}
>
<img
src={`/img/logos/${logo}`}
alt={name}
title={name}
style={{ maxHeight: 48, maxWidth: '100%', objectFit: 'contain' }}
/>
</Card>
</a>
</div>
))}
</Carousel>
</div>
<Flex justify="center" style={{ marginTop: 30, fontSize: 17 }}>
<Link to="/inTheWild">See all companies</Link>
<span style={{ margin: '0 8px' }}>·</span>
<a
href="https://github.com/apache/superset/edit/master/RESOURCES/INTHEWILD.yaml"
target="_blank"
rel="noreferrer"
>
Add yours to the list!
</a>
</Flex>
</div>
</BlurredSection>
)}
</StyledMain>
</Layout>
);

View File

@@ -134,3 +134,51 @@ ul.dropdown__menu svg {
--ifm-font-base-color: #bbb5ac;
--ifm-border-color: #797063;
}
/* Custom "resources" admonition for additional resources/links */
.alert--resources {
--ifm-alert-background-color: #f8f9fa;
--ifm-alert-border-color: #6c757d;
--ifm-alert-foreground-color: #495057;
background-color: var(--ifm-alert-background-color);
border-left: 5px solid var(--ifm-alert-border-color);
}
.alert--resources .admonition-heading h5 {
color: var(--ifm-alert-foreground-color);
}
.alert--resources .admonition-icon svg {
fill: var(--ifm-alert-foreground-color);
stroke: var(--ifm-alert-foreground-color);
}
/* Resources admonition - dark mode */
[data-theme='dark'] .alert--resources {
--ifm-alert-background-color: #2d3748;
--ifm-alert-border-color: #718096;
--ifm-alert-foreground-color: #e2e8f0;
}
/* Style links within resources admonition */
.alert--resources a {
color: var(--ifm-link-color);
text-decoration: none;
}
.alert--resources a:hover {
text-decoration: underline;
}
[data-theme='dark'] .alert--resources a {
color: var(--ifm-color-primary-light);
}
/* Fix Ant Design Collapse arrow visibility in dark mode */
[data-theme='dark'] .ant-collapse-expand-icon {
color: var(--ifm-font-base-color);
}
[data-theme='dark'] .ant-collapse-header {
color: var(--ifm-font-base-color);
}

View File

@@ -0,0 +1,63 @@
/**
* 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 React from 'react';
import DefaultAdmonitionTypes from '@theme-original/Admonition/Types';
function ResourcesIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
{/* Bookmark/link icon */}
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" />
</svg>
);
}
function ResourcesAdmonition(props) {
return (
<div className="admonition admonition-resources alert alert--resources">
<div className="admonition-heading">
<h5>
<span className="admonition-icon">
<ResourcesIcon />
</span>
{props.title || 'Additional Resources'}
</h5>
</div>
<div className="admonition-content">{props.children}</div>
</div>
);
}
const AdmonitionTypes = {
...DefaultAdmonitionTypes,
resources: ResourcesAdmonition,
};
export default AdmonitionTypes;

View File

@@ -0,0 +1,53 @@
/**
* 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 React from 'react';
import { Button, Card, Input, Space, Tag, Tooltip } from 'antd';
// Import extension components from @apache-superset/core/ui
// This matches the established pattern used throughout the Superset codebase
// Resolved via webpack alias to superset-frontend/packages/superset-core/src/ui/components
import { Alert } from '@apache-superset/core/ui';
/**
* ReactLiveScope provides the scope for live code blocks.
* Any component added here will be available in ```tsx live blocks.
*
* To add more components:
* 1. Import the component from @apache-superset/core above
* 2. Add it to the scope object below
*/
const ReactLiveScope = {
// React core
React,
...React,
// Extension components from @apache-superset/core
Alert,
// Common Ant Design components (for demos)
Button,
Card,
Input,
Space,
Tag,
Tooltip,
};
export default ReactLiveScope;

View File

@@ -19,6 +19,17 @@
import { useEffect } from 'react';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
// File extensions to track as downloads
const DOWNLOAD_EXTENSIONS = [
'pdf', 'zip', 'tar', 'gz', 'tgz', 'bz2',
'exe', 'dmg', 'pkg', 'deb', 'rpm',
'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
'csv', 'json', 'yaml', 'yml',
];
// Scroll depth milestones to track
const SCROLL_MILESTONES = [25, 50, 75, 100];
export default function Root({ children }) {
const { siteConfig } = useDocusaurusContext();
const { customFields } = siteConfig;
@@ -27,10 +38,9 @@ export default function Root({ children }) {
const { matomoUrl, matomoSiteId } = customFields;
if (typeof window !== 'undefined') {
// Making testing easier, logging debug junk if we're in development
const devMode = window.location.hostname === 'localhost' ? true : false;
const devMode = ['localhost', '127.0.0.1', '::1', '0.0.0.0'].includes(window.location.hostname);
// Initialize the _paq array first
// Initialize the _paq array
window._paq = window._paq || [];
// Configure the tracker before loading matomo.js
@@ -39,7 +49,8 @@ export default function Root({ children }) {
window._paq.push(['setTrackerUrl', `${matomoUrl}/matomo.php`]);
window._paq.push(['setSiteId', matomoSiteId]);
// Initial page view is handled by handleRouteChange
// Track downloads with custom extensions
window._paq.push(['setDownloadExtensions', DOWNLOAD_EXTENSIONS.join('|')]);
// Now load the matomo.js script
const script = document.createElement('script');
@@ -47,19 +58,168 @@ export default function Root({ children }) {
script.src = `${matomoUrl}/matomo.js`;
document.head.appendChild(script);
// Helper to track events
const trackEvent = (category, action, name, value) => {
if (devMode) {
console.log('Matomo trackEvent:', { category, action, name, value });
}
window._paq.push(['trackEvent', category, action, name, value]);
};
// Helper to track site search
const trackSiteSearch = (keyword, category, resultsCount) => {
if (devMode) {
console.log('Matomo trackSiteSearch:', { keyword, category, resultsCount });
}
window._paq.push(['trackSiteSearch', keyword, category, resultsCount]);
};
// Track external link clicks using domain as category (vendor-agnostic)
const handleLinkClick = (event) => {
const link = event.target.closest('a');
if (!link) return;
const href = link.getAttribute('href');
if (!href) return;
try {
const url = new URL(href, window.location.origin);
// Skip internal links
if (url.hostname === window.location.hostname) return;
// Use hostname as category for vendor-agnostic tracking
trackEvent('Outbound Link', url.hostname, href);
} catch {
// Invalid URL, skip tracking
}
};
// Track Algolia search queries
const setupAlgoliaTracking = () => {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
const searchInput = node.querySelector?.('.DocSearch-Input') ||
(node.classList?.contains('DocSearch-Input') ? node : null);
if (searchInput) {
let debounceTimer;
searchInput.addEventListener('input', (e) => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
const query = e.target.value.trim();
if (query.length >= 3) {
const results = document.querySelectorAll('.DocSearch-Hit');
trackSiteSearch(query, 'Documentation', results.length);
}
}, 1000);
});
}
}
});
});
});
observer.observe(document.body, { childList: true, subtree: true });
return observer;
};
// Track video plays
const handleVideoPlay = (event) => {
if (event.target.tagName === 'VIDEO') {
const videoSrc = event.target.currentSrc || event.target.src || 'unknown';
trackEvent('Video', 'Play', videoSrc);
}
};
// Track CTA button clicks
const handleCTAClick = (event) => {
const button = event.target.closest('.get-started-button, .default-button-theme');
if (button) {
const buttonText = button.textContent?.trim() || 'Unknown';
const href = button.getAttribute('href') || '';
trackEvent('CTA', 'Click', `${buttonText} - ${href}`);
}
};
// Track scroll depth
let scrollMilestonesReached = new Set();
const handleScroll = () => {
const scrollTop = window.scrollY;
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
if (docHeight <= 0) return;
const scrollPercent = Math.round((scrollTop / docHeight) * 100);
SCROLL_MILESTONES.forEach(milestone => {
if (scrollPercent >= milestone && !scrollMilestonesReached.has(milestone)) {
scrollMilestonesReached.add(milestone);
trackEvent('Scroll Depth', `${milestone}%`, window.location.pathname);
}
});
};
// Reset scroll tracking on route change
const resetScrollTracking = () => {
scrollMilestonesReached = new Set();
};
// Track 404 pages
const track404 = () => {
const is404 = document.querySelector('.theme-doc-404') ||
document.title.toLowerCase().includes('not found') ||
document.querySelector('h1')?.textContent?.toLowerCase().includes('not found');
if (is404) {
trackEvent('Error', '404', window.location.pathname);
if (devMode) {
console.log('Matomo: 404 page detected', window.location.pathname);
}
}
};
// Track copy-to-clipboard events on code blocks
const handleCopy = (event) => {
const codeBlock = event.target.closest('pre, code, .prism-code');
if (codeBlock) {
const codeText = window.getSelection()?.toString() || '';
const codeSnippet = codeText.substring(0, 100) + (codeText.length > 100 ? '...' : '');
trackEvent('Code', 'Copy', `${window.location.pathname}: ${codeSnippet}`);
}
};
// Track color mode preference (as event, no admin config needed)
const trackColorMode = () => {
const colorMode = document.documentElement.getAttribute('data-theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
trackEvent('User Preference', 'Color Mode', colorMode);
};
// Track docs version from URL (as event, no admin config needed)
const trackDocsVersion = () => {
const pathMatch = window.location.pathname.match(/\/docs\/([\d.]+)\//);
const version = pathMatch ? pathMatch[1] : 'latest';
trackEvent('User Preference', 'Docs Version', version);
};
// Handle route changes for SPA
const handleRouteChange = () => {
if (devMode) {
console.log('Route changed to:', window.location.pathname);
}
// Short timeout to ensure the page has fully rendered
// Reset scroll tracking for new page
resetScrollTracking();
setTimeout(() => {
// Get the current page title from the document
const currentTitle = document.title;
const currentPath = window.location.pathname;
// For testing: impersonate real domain - ONLY FOR DEVELOPMENT
// Set custom dimensions before tracking page view
trackColorMode();
trackDocsVersion();
if (devMode) {
console.log('Tracking page view:', currentPath, currentTitle);
window._paq.push(['setDomains', ['superset.apache.org']]);
@@ -74,10 +234,13 @@ export default function Root({ children }) {
window._paq.push(['setReferrerUrl', window.location.href]);
window._paq.push(['setDocumentTitle', currentTitle]);
window._paq.push(['trackPageView']);
}, 100); // Increased delay to ensure page has fully rendered
// Check for 404 after page renders
setTimeout(track404, 500);
}, 100);
};
// Try all possible Docusaurus events - they've changed between versions
// Set up Docusaurus route listeners
const possibleEvents = [
'docusaurus.routeDidUpdate',
'docusaurusRouteDidUpdate',
@@ -85,21 +248,22 @@ export default function Root({ children }) {
];
if (devMode) {
console.log('Setting up Docusaurus route listeners');
console.log('Setting up Matomo tracking with enhanced features');
}
possibleEvents.forEach(eventName => {
document.addEventListener(eventName, () => {
// Store handler references for proper cleanup
const routeHandlers = possibleEvents.map(eventName => {
const handler = () => {
if (devMode) {
console.log(`Docusaurus route update detected via ${eventName}`);
}
handleRouteChange();
});
};
document.addEventListener(eventName, handler);
return { eventName, handler };
});
// Also set up manual history tracking as fallback
if (devMode) {
console.log('Setting up manual history tracking as fallback');
}
// Manual history tracking as fallback
const originalPushState = window.history.pushState;
window.history.pushState = function () {
originalPushState.apply(this, arguments);
@@ -108,19 +272,53 @@ export default function Root({ children }) {
window.addEventListener('popstate', handleRouteChange);
// Set up event listeners
document.addEventListener('click', handleLinkClick);
document.addEventListener('click', handleCTAClick);
document.addEventListener('play', handleVideoPlay, true);
document.addEventListener('copy', handleCopy);
window.addEventListener('scroll', handleScroll, { passive: true });
// Watch for color mode changes
const colorModeObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'data-theme') {
trackEvent('User Preference', 'Color Mode Change',
document.documentElement.getAttribute('data-theme'));
}
});
});
colorModeObserver.observe(document.documentElement, { attributes: true });
// Set up Algolia tracking
const algoliaObserver = setupAlgoliaTracking();
// Initial page tracking
handleRouteChange();
// Cleanup
return () => {
// Cleanup listeners
possibleEvents.forEach(eventName => {
document.removeEventListener(eventName, handleRouteChange);
routeHandlers.forEach(({ eventName, handler }) => {
document.removeEventListener(eventName, handler);
});
if (originalPushState) {
window.history.pushState = originalPushState;
window.removeEventListener('popstate', handleRouteChange);
}
document.removeEventListener('click', handleLinkClick);
document.removeEventListener('click', handleCTAClick);
document.removeEventListener('play', handleVideoPlay, true);
document.removeEventListener('copy', handleCopy);
window.removeEventListener('scroll', handleScroll);
if (algoliaObserver) {
algoliaObserver.disconnect();
}
if (colorModeObserver) {
colorModeObserver.disconnect();
}
};
}
}, []);

View File

@@ -0,0 +1,31 @@
/**
* 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.
*/
/**
* Type declarations for @apache-superset/core/ui
*
* AUTO-GENERATED by scripts/generate-extension-components.mjs
* Do not edit manually - regenerate by running: yarn generate:extension-components
*/
import type { AlertProps as AntdAlertProps } from 'antd/es/alert';
import type { PropsWithChildren, FC } from 'react';
export type AlertProps = PropsWithChildren<Omit<AntdAlertProps, 'children'>>;
export const Alert: FC<AlertProps>;

28
docs/src/types/yaml.d.ts vendored Normal file
View File

@@ -0,0 +1,28 @@
/**
* 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.
*/
declare module '*.yaml' {
const content: unknown;
export default content;
}
declare module '*.yml' {
const content: unknown;
export default content;
}

View File

@@ -25,6 +25,13 @@ export default function webpackExtendPlugin(): Plugin<void> {
name: 'custom-webpack-plugin',
configureWebpack(config) {
const isDev = process.env.NODE_ENV === 'development';
// Add YAML loader rule directly to existing rules
config.module?.rules?.push({
test: /\.ya?ml$/,
use: 'js-yaml-loader',
});
return {
devtool: isDev ? 'eval-source-map' : config.devtool,
...(isDev && {
@@ -51,6 +58,15 @@ export default function webpackExtendPlugin(): Plugin<void> {
__dirname,
'../../superset-frontend/packages/superset-ui-core/src/components',
),
// Extension API package - allows docs to import from @apache-superset/core/ui
// This matches the established pattern used throughout the Superset codebase
// Point directly to components to avoid importing theme (which has font dependencies)
// Note: TypeScript types come from docs/src/types/apache-superset-core (see tsconfig.json)
// This split is intentional: webpack resolves actual source, tsconfig provides simplified types
'@apache-superset/core/ui': path.resolve(
__dirname,
'../../superset-frontend/packages/superset-core/src/ui/components',
),
// Add proper Storybook aliases
'@storybook/blocks': path.resolve(
__dirname,

View File

Before

Width:  |  Height:  |  Size: 476 KiB

After

Width:  |  Height:  |  Size: 476 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

View File

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

1
docs/static/img/logos/cirrus.svg vendored Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 447.83 91.8"><defs><style>.cls-1{fill:#464361;}.cls-2{fill:#e76a25;}</style></defs><rect class="cls-1" x="101.34" y="1.8" width="19.98" height="88.02"></rect><path class="cls-1" d="M196.19,2l-2.34,19.44a26.92,26.92,0,0,0-10.8-2.16c-9.71,0-20.51,8.1-20.51,23.58v47h-20V1.8h20V14.4C166.5,5.94,176.4,0,185,0A28.16,28.16,0,0,1,196.19,2Z"></path><path class="cls-1" d="M263.87,2l-2.34,19.44a26.92,26.92,0,0,0-10.8-2.16c-9.72,0-20.52,8.1-20.52,23.58v47h-20V1.8h20V14.4c4-8.46,13.86-14.4,22.5-14.4A28.16,28.16,0,0,1,263.87,2Z"></path><path class="cls-1" d="M359.81,1.8v88H339.65V76c-5.58,9.9-14.76,15.66-26.46,15.66-22.67,0-36.53-15.12-36.53-39.78V1.8h20V49.5c0,13.86,8.46,22.86,21.42,22.86s21.6-9,21.6-22.86V1.8Z"></path><path class="cls-1" d="M375.29,79l7.92-15.12c6.84,6.48,19.8,11,29.7,11,8.82,0,14.58-4.14,14.58-9.72,0-16.2-49-4.86-49-37.44,0-16.38,14-27.72,34.2-27.72,12.24,0,25,3.42,32.94,10.62L437.75,26.1c-7-6.12-19.62-9.36-27-9.36s-12.24,3.6-12.24,9.36c0,14.94,49.32,5.76,49.32,38.34,0,15.84-14.22,27.36-34.38,27.36C399.23,91.8,383.93,86.76,375.29,79Z"></path><path class="cls-1" d="M45.76,19.26h.14c9.54,0,17.46,4.68,21.06,11.7l16.92-9.54C77.4,8.46,63,0,45.9,0h-.14Z"></path><path class="cls-1" d="M68.76,59.4c-4,8.1-12.42,13-22.68,13C31,72.36,19.8,60.84,19.8,45.9v-.09H0v.09C0,72,20,91.62,46.08,91.62c18.36,0,33.84-9.54,40.5-24.12Z"></path><path class="cls-2" d="M32.36,19.08,27,25.41l-5,6-.54.65-.68-.57L18,29.11l-5.82-4.88L5.94,19l-.63-.52.53-.65,2.75-3.27,1.55-1.86c.18-.2.55-.65.55-.65s.47.36.68.54L14.18,15l6.23,5.22,1-1.17c1.72-2.05,3.44-4.09,5.15-6.14l5.73-6.82L37.36,0H9.65A9.65,9.65,0,0,0,0,9.65V37.36H27.81l9.55,8V13.12C35.69,15.11,34,17.09,32.36,19.08Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 75 KiB

1973
docs/static/img/logos/plaidcloud.svg vendored Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 79 KiB

19
docs/static/img/logos/preset.svg vendored Normal file
View File

@@ -0,0 +1,19 @@
<!--
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.
-->
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 494.72 148.76"><defs><style>.cls-1{fill:#2fc096;}</style></defs><title>preset-logo</title><path class="cls-1" d="M91,51.77H43.11L0,145H27.81L59.3,77.14H91a12.69,12.69,0,0,0,0-25.37ZM91,0H61.28L49.51,25.37H91a39.08,39.08,0,0,1,0,78.16H74.8L63,128.84a12.1,12.1,0,0,0,1.21.06H91A64.45,64.45,0,0,0,91,0Z"/><path d="M205,38.46c-14.41,0-24.47,10.31-24.47,25.07V146a2.66,2.66,0,0,0,2.75,2.75h5.62a2.66,2.66,0,0,0,2.75-2.75V114.25a20.32,20.32,0,0,0,14.72,5.65c13.17,0,23.1-10.78,23.1-25.08V63.53C229.5,48.77,219.44,38.46,205,38.46ZM191.68,63.53c0-8.66,5.24-14.26,13.35-14.26s13.35,5.6,13.35,14.26V94.82c0,8.66-5.24,14.26-13.35,14.26s-13.35-5.6-13.35-14.26Z"/><path d="M272.15,39.52h-6.68c-14.53,0-25.08,10.35-25.08,24.62v51.49a2.66,2.66,0,0,0,2.75,2.75h5.62a2.67,2.67,0,0,0,2.76-2.75V64.14c0-8.51,5.34-13.8,14-13.8h6.68a2.67,2.67,0,0,0,2.76-2.76V42.27A2.66,2.66,0,0,0,272.15,39.52Z"/><path d="M304.2,86.64c14.17,0,24.47-9.85,24.47-23.41,0-14.5-10.07-24.63-24.47-24.63-14.64,0-24.47,10.14-24.47,25.24V94.67c0,15,9.83,25.08,24.47,25.08,14.06,0,23.84-10.06,24.32-25.08a2.66,2.66,0,0,0-2.76-2.75h-5.62a2.57,2.57,0,0,0-2.75,2.71c-.28,8.69-5.46,14.3-13.19,14.3-8.11,0-13.35-5.6-13.35-14.26v-8Zm-13.35-22.8c0-9,5-14.41,13.35-14.41,8.1,0,13.34,5.41,13.34,13.8,0,7.64-5.24,12.59-13.34,12.59H290.85Z"/><path d="M363.68,73.69c-8.11-3.42-15.12-6.37-15.12-13.5,0-6.53,5-10.92,12.44-10.92S373.13,53.94,373.58,62a2.59,2.59,0,0,0,2.76,2.75H382A2.66,2.66,0,0,0,384.7,62c-.67-14.94-9.47-23.5-24.16-23.5-13.17,0-23.11,9.34-23.11,21.73,0,14.52,11.48,19.4,21.6,23.7,8.38,3.56,15.62,6.64,15.62,14.27,0,6.94-4.54,10.92-12.44,10.92-9.28,0-14-4.68-14.41-14.26a2.58,2.58,0,0,0-2.75-2.75h-5.62a2.66,2.66,0,0,0-2.75,2.75v.05c.65,15.44,10.43,25,25.53,25,13.43,0,23.56-9.35,23.56-21.74C385.77,83,374,78.05,363.68,73.69Z"/><path d="M418.55,86.64c14.18,0,24.47-9.85,24.47-23.41C443,48.73,433,38.6,418.55,38.6c-14.64,0-24.47,10.14-24.47,25.24V94.67c0,15,9.83,25.08,24.47,25.08,14.07,0,23.84-10.06,24.32-25.08a2.66,2.66,0,0,0-2.75-2.75H434.5a2.57,2.57,0,0,0-2.75,2.71c-.29,8.69-5.47,14.3-13.2,14.3-8.11,0-13.35-5.6-13.35-14.26v-8ZM405.2,63.84c0-9,5-14.41,13.35-14.41,8.11,0,13.35,5.41,13.35,13.8,0,7.64-5.24,12.59-13.35,12.59H405.2Z"/><path d="M478.14,108.05h-2c-7.65,0-11.53-5.36-11.53-15.93V50.83h11.68a2.67,2.67,0,0,0,2.75-2.76V42.91a2.66,2.66,0,0,0-2.75-2.75H464.64V21.65a2.67,2.67,0,0,0-2.75-2.76h-5.62a2.67,2.67,0,0,0-2.75,2.76V92.12c0,16.75,8.46,26.75,22.65,26.75h2a2.65,2.65,0,0,0,2.75-2.75V110.8A2.58,2.58,0,0,0,478.14,108.05Z"/><path d="M484,27h-1.91V25.85h5.18V27h-1.91v5.17H484Z"/><path d="M493.41,29.77c0-1.07,0-2.27,0-3h0c-.3,1.28-.93,3.37-1.53,5.34h-1.16c-.46-1.72-1.11-4.1-1.38-5.36h0c.06.74.08,2,.08,3.11v2.25h-1.24V25.85h2c.49,1.64,1,3.7,1.23,4.63h0c.15-.82.84-3,1.37-4.63h2v6.28h-1.31Z"/></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

17
docs/static/img/logos/userguiding.svg vendored Normal file
View File

@@ -0,0 +1,17 @@
<svg width="756" height="177" viewBox="0 0 756 177" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M148.784 108V61.1465H158.176V107.688C158.176 117.404 163.965 123.985 175.237 123.985C186.505 123.985 192.297 117.562 192.297 107.688V61.1465H201.532V108C201.532 123.357 190.576 133.076 175.079 133.076C159.74 133.076 148.784 123.357 148.784 108Z" fill="#1E2533"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M250.445 117.981C250.445 127.318 242.198 133.076 231.42 133.076C221.27 133.076 214.138 128.252 211.123 121.404L218.734 117.047C220.321 121.871 225.077 124.83 231.42 124.83C236.811 124.83 241.566 122.961 241.566 117.981C241.566 107.246 213.027 113.314 213.027 95.2628C213.027 86.3925 220.797 80.3276 231.1 80.3276C239.503 80.3276 246.162 84.2174 249.493 90.7526L242.042 94.7958C240.138 90.2822 235.539 88.4174 231.1 88.4174C226.504 88.4174 222.065 90.5958 222.065 95.1094C221.745 105.845 250.445 99.4662 250.445 117.981Z" fill="#1E2533"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M265.094 102.967H298.726C297.163 92.9558 290.123 88.4182 282.456 88.4182C273.073 88.4182 266.503 94.2068 265.094 102.967ZM283.395 124.868C290.28 124.868 295.442 121.584 297.944 117.516L305.453 121.742C301.073 128.621 293.406 133.162 283.241 133.162C266.972 133.162 256.176 121.742 256.176 106.724C256.176 91.86 266.814 80.2861 282.614 80.2861C297.789 80.2861 307.643 92.8024 307.643 106.878C307.643 108.129 307.485 109.538 307.331 110.789H265.093C266.814 119.706 274.011 124.868 283.395 124.868Z" fill="#1E2533"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M342.515 80.3276V89.6151C334.843 89.3012 326.529 93.2362 326.529 105.2V132.117H317.579V81.4312H326.529V89.9323C329.727 82.69 335.963 80.3276 342.515 80.3276Z" fill="#1E2533"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M423.076 101.147C423.076 119.056 409.755 133.076 388.445 133.076C366.661 133.076 351.146 116.719 351.146 96.6298C351.146 76.5404 366.661 60.187 388.129 60.187C401.292 60.187 412.89 67.0415 418.689 77.1647L410.538 81.8357C406.466 74.3635 398.003 69.2218 387.975 69.2218C371.677 69.2218 360.396 81.2113 360.396 96.6298C360.396 111.895 371.519 123.884 388.445 123.884C403.019 123.884 411.795 115.788 413.518 105.354H387.659V96.9403H422.918V101.147H423.076Z" fill="#1E2533"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M477.743 81.4438V131.675H468.886V124.498C465.091 130.424 459.554 133.076 452.436 133.076C440.575 133.076 432.667 125.274 432.667 112.328V81.4438H441.524V111.706C441.524 119.973 446.268 124.498 454.176 124.498C462.402 124.498 468.886 119.819 468.886 107.964V81.2866H477.743V81.4438Z" fill="#1E2533"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M488.719 131.751H497.479V81.3779H488.719V131.751ZM487.311 66.3627C487.311 63.076 489.971 60.4165 493.254 60.4165C496.541 60.4165 499.043 63.076 499.043 66.3627C499.043 69.6461 496.383 72.148 493.254 72.148C489.813 72.3056 487.311 69.6461 487.311 66.3627Z" fill="#1E2533"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M554.418 106.566C554.418 96.2431 546.597 88.5798 536.429 88.5798C526.418 88.5798 518.593 96.4007 518.593 106.566C518.593 116.892 526.418 124.556 536.429 124.556C546.752 124.556 554.418 116.892 554.418 106.566ZM563.336 61.2026V131.752H554.573V123.147C550.508 129.251 544.092 133.161 535.49 133.161C521.411 133.161 509.991 121.587 509.991 106.723C509.991 91.8628 521.411 80.2856 535.49 80.2856C544.092 80.2856 550.508 84.0384 554.573 90.2965V61.5145H563.336V61.2026Z" fill="#1E2533"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M576.634 131.751H585.394V81.3779H576.634V131.751ZM575.068 66.3627C575.068 63.076 577.728 60.4165 581.015 60.4165C584.298 60.4165 586.803 63.076 586.803 66.3627C586.803 69.6461 584.144 72.148 581.015 72.148C577.728 72.3056 575.068 69.6461 575.068 66.3627Z" fill="#1E2533"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M643.661 100.982V131.963H634.804V101.606C634.804 93.3136 630.06 88.7747 622.149 88.7747C613.926 88.7747 607.439 93.4713 607.439 105.364V132.117H598.585V81.7366H607.439V88.9324C611.237 82.9879 616.771 80.3276 623.888 80.3276C635.909 80.3276 643.661 88.1507 643.661 100.982Z" fill="#1E2533"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M697.397 106.255C697.397 96.2439 689.576 88.4229 679.408 88.4229C669.239 88.4229 661.418 96.2439 661.418 106.255C661.418 116.423 669.239 124.244 679.408 124.244C689.576 124.244 697.397 116.423 697.397 106.255ZM706 81.3816V129.562C706 145.204 693.487 153.182 680.347 153.182C669.085 153.182 660.637 148.806 656.411 140.827L664.078 136.447C666.425 141.136 670.648 144.892 680.501 144.892C690.982 144.892 697.398 138.949 697.398 129.404V122.21C693.329 128.311 686.917 132.221 678.469 132.221C664.232 132.221 652.816 120.644 652.816 105.941C652.816 91.3926 664.232 79.8154 678.469 79.8154C686.917 79.8154 693.329 83.5716 697.398 89.8298V81.224H706V81.3816Z" fill="#1E2533"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M83.0878 75.5321C89.1786 75.5321 94.1171 70.5962 94.1171 64.5046C94.1171 58.4129 89.1786 53.4736 83.0878 53.4736C76.9971 53.4736 72.0586 58.4129 72.0586 64.5046C72.0586 70.5962 76.9971 75.5321 83.0878 75.5321Z" fill="#5478F0"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M114.152 111.977L51.1011 132.339L50 153.216L115.216 132.155L114.152 111.977Z" fill="#5478F0"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M112.236 76.4912L53.0173 95.7661L51.918 116.772L113.298 96.7966L112.236 76.4912Z" fill="#5478F0"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M110.421 46.7603H55.7544V34.7049L83.0877 22.7837L110.421 34.7049V46.7603Z" fill="#5478F0"/>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

48
docs/static/img/logos/vlmedia.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

64
docs/static/llms.txt vendored Normal file
View File

@@ -0,0 +1,64 @@
# Apache Superset
> Apache Superset is a modern, enterprise-ready business intelligence web application. It provides a no-code interface for building charts, a powerful SQL editor, support for nearly any SQL database, and beautiful visualizations. Superset is open source under the Apache 2.0 license.
Superset is designed for data exploration and visualization at scale. It features a lightweight semantic layer, configurable caching, extensible security with RBAC, a REST API for programmatic access, and a cloud-native architecture.
## Getting Started
- [Introduction](https://superset.apache.org/docs/intro): Overview of Superset features, supported databases, and resources
- [Quickstart](https://superset.apache.org/docs/quickstart): Get Superset running quickly with Docker Compose
## Installation
- [Architecture](https://superset.apache.org/docs/installation/architecture): Production deployment architecture and components
- [Docker Compose](https://superset.apache.org/docs/installation/docker-compose): Install Superset using Docker Compose
- [Docker Builds](https://superset.apache.org/docs/installation/docker-builds): Building and customizing Docker images
- [Kubernetes](https://superset.apache.org/docs/installation/kubernetes): Deploy Superset on Kubernetes with Helm
- [PyPI](https://superset.apache.org/docs/installation/pypi): Install from PyPI using pip
- [Upgrading Superset](https://superset.apache.org/docs/installation/upgrading-superset): Upgrade between Superset versions
## Configuration
- [Configuring Superset](https://superset.apache.org/docs/configuration/configuring-superset): Main configuration options and superset_config.py
- [Databases](https://superset.apache.org/docs/configuration/databases): Connect to databases with SQLAlchemy connection strings
- [Caching](https://superset.apache.org/docs/configuration/cache): Configure caching with Redis or other backends
- [Async Queries & Celery](https://superset.apache.org/docs/configuration/async-queries-celery): Set up async query execution with Celery
- [Alerts & Reports](https://superset.apache.org/docs/configuration/alerts-reports): Configure email/Slack alerts and scheduled reports
- [SQL Templating](https://superset.apache.org/docs/configuration/sql-templating): Use Jinja templates in SQL queries
- [Theming](https://superset.apache.org/docs/configuration/theming): Customize Superset's appearance
- [Networking Settings](https://superset.apache.org/docs/configuration/networking-settings): CORS, proxy, and network configuration
- [Timezones](https://superset.apache.org/docs/configuration/timezones): Timezone handling configuration
- [Event Logging](https://superset.apache.org/docs/configuration/event-logging): Configure event and analytics logging
- [Importing/Exporting Datasources](https://superset.apache.org/docs/configuration/importing-exporting-datasources): Import and export datasource definitions
## Using Superset
- [Creating Your First Dashboard](https://superset.apache.org/docs/using-superset/creating-your-first-dashboard): Step-by-step tutorial for building dashboards
- [Exploring Data](https://superset.apache.org/docs/using-superset/exploring-data): Guide to data exploration features
- [Issue Codes](https://superset.apache.org/docs/using-superset/issue-codes): Reference for Superset error codes
## Security
- [Security Overview](https://superset.apache.org/docs/security/security): RBAC, row-level security, and authentication options
- [Securing Superset](https://superset.apache.org/docs/security/securing_superset): Production security hardening guide
- [CVEs](https://superset.apache.org/docs/security/cves): Security vulnerabilities and advisories
## Contributing
- [Contributing Guide](https://superset.apache.org/docs/contributing/contributing): How to contribute to Apache Superset
- [Development Environment](https://superset.apache.org/docs/contributing/development): Set up a local development environment
- [Guidelines](https://superset.apache.org/docs/contributing/guidelines): Code style, testing, and PR guidelines
- [How-Tos](https://superset.apache.org/docs/contributing/howtos): Common development tasks and recipes
- [Resources](https://superset.apache.org/docs/contributing/resources): Additional contributor resources
## Reference
- [FAQ](https://superset.apache.org/docs/faq): Frequently asked questions
- [REST API](https://superset.apache.org/docs/api): Superset REST API documentation
## Optional
- [Country Map Tools](https://superset.apache.org/docs/configuration/country-map-tools): Custom country map visualizations
- [Map Tiles](https://superset.apache.org/docs/configuration/map-tiles): Configure map tile providers for deck.gl charts
- [Miscellaneous](https://superset.apache.org/docs/contributing/misc): Additional contributor information

View File

@@ -6,19 +6,23 @@
"skipLibCheck": true,
"noImplicitAny": false,
"strict": false,
"types": ["@docusaurus/module-type-aliases"]
},
"jsx": "react-jsx",
"moduleResolution": "node",
"baseUrl": "./",
"paths": {
"@superset-ui/core": ["../superset-frontend/packages/superset-ui-core/src"],
"@superset-ui/core/*": ["../superset-frontend/packages/superset-ui-core/src/*"],
"*": ["src/*", "node_modules/*"]
"jsx": "react-jsx",
"moduleResolution": "node",
"types": ["@docusaurus/module-type-aliases"],
"paths": {
"@superset-ui/core": ["../superset-frontend/packages/superset-ui-core/src"],
"@superset-ui/core/*": ["../superset-frontend/packages/superset-ui-core/src/*"],
// Types for @apache-superset/core/ui are auto-generated by scripts/generate-extension-components.mjs
// Runtime resolution uses webpack alias pointing to actual source (see src/webpack.extend.ts)
// Using /ui path matches the established pattern used throughout the Superset codebase
"@apache-superset/core/ui": ["./src/types/apache-superset-core"],
"*": ["src/*", "node_modules/*"]
}
},
"include": [
"src/**/*.ts",
"src/**/*.tsx"
"src/**/*.tsx",
"src/**/*.d.ts"
],
"exclude": [
"node_modules",

View File

@@ -17,8 +17,8 @@ made available in the Jinja context:
- `columns`: columns which to group by in the query
- `filter`: filters applied in the query
- `from_dttm`: start `datetime` value from the selected time range (`None` if undefined) (deprecated beginning in version 5.0, use `get_time_filter` instead)
- `to_dttm`: end `datetime` value from the selected time range (`None` if undefined). (deprecated beginning in version 5.0, use `get_time_filter` instead)
- `from_dttm`: start `datetime` value from the selected time range (`None` if undefined). **Note:** Only available in virtual datasets when a time range filter is applied in Explore/Chart views—not available in standalone SQL Lab queries. (deprecated beginning in version 5.0, use `get_time_filter` instead)
- `to_dttm`: end `datetime` value from the selected time range (`None` if undefined). **Note:** Only available in virtual datasets when a time range filter is applied in Explore/Chart views—not available in standalone SQL Lab queries. (deprecated beginning in version 5.0, use `get_time_filter` instead)
- `groupby`: columns which to group by in the query (deprecated)
- `metrics`: aggregate expressions in the query
- `row_limit`: row limit of the query
@@ -58,6 +58,54 @@ the time filter is not set. For many database engines, this could be replaced wi
Note that the Jinja parameters are called within _double_ brackets in the query and with
_single_ brackets in the logic blocks.
### Understanding Context Availability
Some Jinja variables like `from_dttm`, `to_dttm`, and `filter` are **only available when a chart or dashboard provides them**. They are populated from:
- Time range filters applied in Explore/Chart views
- Dashboard native filters
- Filter components
**These variables are NOT available in standalone SQL Lab queries** because there's no filter context. If you try to use `{{ from_dttm }}` directly in SQL Lab, you'll get an "undefined parameter" error.
#### Testing Time-Filtered Queries in SQL Lab
To test queries that use time variables in SQL Lab, you have several options:
**Option 1: Use Jinja defaults (recommended)**
```sql
SELECT *
FROM tbl
WHERE dttm_col > '{{ from_dttm | default("2024-01-01", true) }}'
AND dttm_col < '{{ to_dttm | default("2024-12-31", true) }}'
```
**Option 2: Use SQL Lab Parameters**
Set parameters in the SQL Lab UI (Parameters menu):
```json
{
"from_dttm": "2024-01-01",
"to_dttm": "2024-12-31"
}
```
**Option 3: Use `{% set %}` for testing**
```sql
{% set from_dttm = "2024-01-01" %}
{% set to_dttm = "2024-12-31" %}
SELECT *
FROM tbl
WHERE dttm_col > '{{ from_dttm }}' AND dttm_col < '{{ to_dttm }}'
```
:::tip
When you save a SQL Lab query as a virtual dataset and use it in a chart with time filters,
the actual filter values will override any defaults or test values you set.
:::
To add custom functionality to the Jinja context, you need to overload the default Jinja
context in your environment by defining the `JINJA_CONTEXT_ADDONS` in your superset configuration
(`superset_config.py`). Objects referenced in this dictionary are made available for users to use

View File

@@ -914,6 +914,20 @@ npm run storybook
When contributing new React components to Superset, please try to add a Story alongside the component's `jsx/tsx` file.
#### Testing Stories
Superset uses [@storybook/test-runner](https://storybook.js.org/docs/writing-tests/test-runner) to validate that all stories compile and render without errors. This helps catch broken stories before they're merged.
```bash
# Run against a running Storybook server (start with `npm run storybook` first)
npm run test-storybook
# Build static Storybook and test (CI-friendly, no server needed)
npm run test-storybook:ci
```
The `test-storybook` job runs automatically in CI on every pull request, ensuring stories remain functional.
## Tips
### Adding a new datasource

File diff suppressed because it is too large Load Diff

View File

@@ -42,7 +42,7 @@ dependencies = [
"click>=8.0.3",
"click-option-group",
"colorama",
"flask-cors>=4.0.2, <7.0",
"flask-cors>=6.0.0, <7.0",
"croniter>=0.3.28",
"cron-descriptor",
"cryptography>=42.0.4, <45.0.0",
@@ -266,6 +266,26 @@ module = "cryptography.*"
ignore_errors = true
follow_imports = "skip"
# superset-core is installed as editable locally but CI may not expose types
# This ensures consistent behavior between local dev and CI
[[tool.mypy.overrides]]
module = "superset_core.*"
follow_imports = "skip"
# Disable warn_unused_ignores for modules with dynamic type assignments
# These type: ignore comments are needed in CI where superset-core types aren't visible
[[tool.mypy.overrides]]
module = [
"superset.core.api.core_api_injection",
"superset.security.manager",
"superset.daos.base",
"superset.connectors.sqla.models",
"superset.tags.filters",
"superset.commands.security.update",
"superset.commands.security.create",
]
warn_unused_ignores = false
[tool.ruff]
# Exclude a variety of commonly ignored directories.
exclude = [
@@ -334,6 +354,7 @@ select = [
ignore = [
"S101",
"PT004", # Fixtures that don't return values - underscore prefix conflicts with pytest usage
"PT006",
"T201",
"N999",

View File

@@ -126,7 +126,7 @@ flask-caching==2.3.1
# via apache-superset (pyproject.toml)
flask-compress==1.17
# via apache-superset (pyproject.toml)
flask-cors==4.0.2
flask-cors==6.0.2
# via apache-superset (pyproject.toml)
flask-jwt-extended==4.7.1
# via flask-appbuilder

View File

@@ -271,7 +271,7 @@ flask-compress==1.17
# via
# -c requirements/base-constraint.txt
# apache-superset
flask-cors==4.0.2
flask-cors==6.0.2
# via
# -c requirements/base-constraint.txt
# apache-superset

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