mirror of
https://github.com/apache/superset.git
synced 2026-06-26 01:49:21 +00:00
Compare commits
451 Commits
fix/dropdo
...
adopt/data
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2561312bb3 | ||
|
|
2798a4f608 | ||
|
|
52aeff0472 | ||
|
|
f5d4e38406 | ||
|
|
d84b5dbee6 | ||
|
|
47b7e010da | ||
|
|
d1fa2b41e8 | ||
|
|
e07b387f25 | ||
|
|
d1cc18c008 | ||
|
|
dafe036658 | ||
|
|
fe2424ec14 | ||
|
|
b4f43bd7e0 | ||
|
|
2b25345ed9 | ||
|
|
e0f3f93cd4 | ||
|
|
0667ba6097 | ||
|
|
81f7e42f4e | ||
|
|
0fd244b5c6 | ||
|
|
1f16d10cbf | ||
|
|
4f4663418f | ||
|
|
4519a5c52d | ||
|
|
da9fbadaf6 | ||
|
|
f40abbbefd | ||
|
|
6166af3c3c | ||
|
|
076d8c1508 | ||
|
|
518cadd907 | ||
|
|
b955c90de4 | ||
|
|
7363774869 | ||
|
|
6f12d17313 | ||
|
|
09c7ba14df | ||
|
|
3ec4bd23c4 | ||
|
|
f6ce105450 | ||
|
|
7bb4e82a82 | ||
|
|
2d78a8733c | ||
|
|
3261d10270 | ||
|
|
a57b5f6078 | ||
|
|
d1b523b97f | ||
|
|
91188a0302 | ||
|
|
ac234d0fb2 | ||
|
|
8eb753eab2 | ||
|
|
779fa13679 | ||
|
|
caf81e71d2 | ||
|
|
1b8c6d109d | ||
|
|
eb60e5477b | ||
|
|
7b9bcdd951 | ||
|
|
d9d395bde1 | ||
|
|
584d41759b | ||
|
|
8f22b71898 | ||
|
|
1ea3584dcb | ||
|
|
6bc77fecc2 | ||
|
|
420a74b01e | ||
|
|
7ba59c2d79 | ||
|
|
b77c525d4b | ||
|
|
41ce9ca7d3 | ||
|
|
c2fb94cedf | ||
|
|
1d0866556f | ||
|
|
b4dfeef2fd | ||
|
|
0ec6cae45d | ||
|
|
d6ede99861 | ||
|
|
9b6d3ce775 | ||
|
|
c1f4062af6 | ||
|
|
3bc3f47d67 | ||
|
|
acb996a324 | ||
|
|
c1d08bf27c | ||
|
|
d3d5297025 | ||
|
|
b1470bd5a5 | ||
|
|
18fea37e84 | ||
|
|
1b71c105b7 | ||
|
|
b061b5d317 | ||
|
|
386893f9f2 | ||
|
|
c1787a67aa | ||
|
|
dee5859599 | ||
|
|
1d3daf2ac8 | ||
|
|
9d56b1721d | ||
|
|
67182e255c | ||
|
|
e2c6dc3e1a | ||
|
|
c539ae98ba | ||
|
|
ca3c420412 | ||
|
|
5e8a0c0244 | ||
|
|
90fa31f305 | ||
|
|
5731d0874a | ||
|
|
66f5ab2d2f | ||
|
|
36b0ed023b | ||
|
|
3ff90bd532 | ||
|
|
5d06438a07 | ||
|
|
eb0d4dd601 | ||
|
|
92109f0f99 | ||
|
|
9431381c3e | ||
|
|
b94f90e39e | ||
|
|
714c5cd075 | ||
|
|
c65c0951cf | ||
|
|
ae5c08b993 | ||
|
|
b9c61a079d | ||
|
|
2599bea0c2 | ||
|
|
6c70f3d275 | ||
|
|
da893462b8 | ||
|
|
18853c6ecf | ||
|
|
8768e5be0f | ||
|
|
133473d0f4 | ||
|
|
5916ec4876 | ||
|
|
36781fbf47 | ||
|
|
215b207ae4 | ||
|
|
3b46a5f121 | ||
|
|
416fa266d9 | ||
|
|
f70a2eac89 | ||
|
|
c49391ab08 | ||
|
|
0fbace5b5d | ||
|
|
c55c85f824 | ||
|
|
e34b7c2daf | ||
|
|
eac5bd23bd | ||
|
|
27a65257ee | ||
|
|
932bb2f154 | ||
|
|
4b87e03e7c | ||
|
|
2a7fadbd08 | ||
|
|
403e11e2ef | ||
|
|
d76b896c9c | ||
|
|
36632c20eb | ||
|
|
cb1694575c | ||
|
|
449bd69802 | ||
|
|
7340d06a05 | ||
|
|
b8aeecfc44 | ||
|
|
7128760d32 | ||
|
|
6118a01bc1 | ||
|
|
b7451cd16d | ||
|
|
4312d67775 | ||
|
|
55190b1da0 | ||
|
|
dc8f0d7b24 | ||
|
|
8b430caef4 | ||
|
|
f5dd28714d | ||
|
|
6d15876b13 | ||
|
|
3dd570cd9b | ||
|
|
e6fffe95c2 | ||
|
|
27d9bcb7bc | ||
|
|
f33c209f7a | ||
|
|
ba339fd9c1 | ||
|
|
defacc3237 | ||
|
|
b612f573d7 | ||
|
|
99ffaf3694 | ||
|
|
14d4432843 | ||
|
|
686beb9117 | ||
|
|
3aa1218f9b | ||
|
|
0d92d0dbb7 | ||
|
|
2d5df6625b | ||
|
|
d0a34d9372 | ||
|
|
b2e5f80db2 | ||
|
|
f1504611fd | ||
|
|
382a094795 | ||
|
|
334b13c3d9 | ||
|
|
9e130e5927 | ||
|
|
fe017d0b20 | ||
|
|
97659678f9 | ||
|
|
141f045104 | ||
|
|
919bd35028 | ||
|
|
be225e5c20 | ||
|
|
81b7f31096 | ||
|
|
045674ab3c | ||
|
|
061f61977f | ||
|
|
ffa98d03df | ||
|
|
bdf494d8b5 | ||
|
|
d32170b020 | ||
|
|
1467006427 | ||
|
|
e18cd1f50c | ||
|
|
9d3efb0aab | ||
|
|
cc9c20fcb6 | ||
|
|
f545d70647 | ||
|
|
e1be76e5fa | ||
|
|
55eb5699d5 | ||
|
|
4d5c171e9e | ||
|
|
a85796418a | ||
|
|
655395cb4e | ||
|
|
28f9b3786c | ||
|
|
25ad827ff3 | ||
|
|
afbbe44de2 | ||
|
|
79cfe4d9bc | ||
|
|
3eae8cd614 | ||
|
|
0c9ece65bb | ||
|
|
7040388ad1 | ||
|
|
a5ece52207 | ||
|
|
a7c0f4b83d | ||
|
|
0f05239260 | ||
|
|
60a7804193 | ||
|
|
4053f53c29 | ||
|
|
7837054dbc | ||
|
|
69c8f37c67 | ||
|
|
76e2418f1e | ||
|
|
b4e3452bfd | ||
|
|
188c84f1cd | ||
|
|
74ae5a45f9 | ||
|
|
fc61918364 | ||
|
|
3e811087de | ||
|
|
c218dc418b | ||
|
|
c98ed92303 | ||
|
|
84c32ec132 | ||
|
|
8636875b39 | ||
|
|
dde6974ac2 | ||
|
|
e36eb6f47c | ||
|
|
f6e12278dc | ||
|
|
43d5b6319b | ||
|
|
ae0b1f0308 | ||
|
|
4acb777a40 | ||
|
|
7e98410743 | ||
|
|
883b7a286d | ||
|
|
d9d8b2bcc0 | ||
|
|
9da54eff84 | ||
|
|
fb2b9fa8ff | ||
|
|
31797005db | ||
|
|
ca2d340db3 | ||
|
|
ef82da8458 | ||
|
|
fee1cf9f08 | ||
|
|
2d2a8f3ab0 | ||
|
|
a19093e65a | ||
|
|
b72a0a53c0 | ||
|
|
512b6f43c1 | ||
|
|
b18fab7fc1 | ||
|
|
b06c6b7464 | ||
|
|
bede4b2121 | ||
|
|
5e812c8757 | ||
|
|
de390f22a4 | ||
|
|
464c67d586 | ||
|
|
7f7f87e823 | ||
|
|
7c2f5142ce | ||
|
|
874ac3dc01 | ||
|
|
f56e34d6e6 | ||
|
|
742a21f6f7 | ||
|
|
a7c49ac9f2 | ||
|
|
99d927eac7 | ||
|
|
994594e4a8 | ||
|
|
e92599fb50 | ||
|
|
eebe1a1a5b | ||
|
|
664e777a84 | ||
|
|
750518cf6f | ||
|
|
59d1b5f300 | ||
|
|
a27ec1923e | ||
|
|
3e2174b50f | ||
|
|
5b66443d48 | ||
|
|
2ea7585490 | ||
|
|
eeac76146c | ||
|
|
6a1091d576 | ||
|
|
8e82b6b2c3 | ||
|
|
b0c5f99007 | ||
|
|
f1ae683923 | ||
|
|
d51d98891e | ||
|
|
1f95a6c486 | ||
|
|
e93cbd6c38 | ||
|
|
dca8af770c | ||
|
|
81c1181519 | ||
|
|
387c62919e | ||
|
|
77d7483f27 | ||
|
|
1a8d08152d | ||
|
|
257dafeec5 | ||
|
|
6d08e79259 | ||
|
|
01ed81785e | ||
|
|
7b4efacbc2 | ||
|
|
7cb4990403 | ||
|
|
c90b2571d7 | ||
|
|
1a4941eee5 | ||
|
|
d839cca995 | ||
|
|
0ec7e7df99 | ||
|
|
9d8287e1bd | ||
|
|
0c696cea7e | ||
|
|
fe625a917e | ||
|
|
a69f9eb00d | ||
|
|
1311d040ba | ||
|
|
6e2db42d98 | ||
|
|
28aedc82c3 | ||
|
|
f56524bb71 | ||
|
|
4ae9980e4c | ||
|
|
c1b5d05f83 | ||
|
|
e16bb29faf | ||
|
|
09b4bc51a3 | ||
|
|
379435b7eb | ||
|
|
4b96b91b53 | ||
|
|
073599bd0c | ||
|
|
9e2c4533c8 | ||
|
|
1ae115981f | ||
|
|
b078ae4b51 | ||
|
|
48b755470f | ||
|
|
dad7dae4f6 | ||
|
|
11e35eca3b | ||
|
|
8093197c97 | ||
|
|
919c6eddc1 | ||
|
|
886bb200d0 | ||
|
|
b5ca00d06b | ||
|
|
5719f8e349 | ||
|
|
9d72a39e10 | ||
|
|
66733a5d72 | ||
|
|
a435002293 | ||
|
|
2d8447af42 | ||
|
|
bf5daf0a1e | ||
|
|
b656b1d477 | ||
|
|
5a97e01d6e | ||
|
|
38cc70de2f | ||
|
|
a1bc3c67ed | ||
|
|
e5b6642b18 | ||
|
|
dd3a61156b | ||
|
|
820e3d18d3 | ||
|
|
2dd8fe362f | ||
|
|
9d2f625e55 | ||
|
|
d66895c566 | ||
|
|
2734bde504 | ||
|
|
f88f1fad04 | ||
|
|
9ec9c8658b | ||
|
|
9f81565625 | ||
|
|
c934498696 | ||
|
|
99bdacd2e1 | ||
|
|
48feb432de | ||
|
|
669ddb2913 | ||
|
|
e7ecf5479a | ||
|
|
c0fbe243e8 | ||
|
|
df0668eda5 | ||
|
|
6b682b1aa1 | ||
|
|
13145e8ad8 | ||
|
|
02f32469b0 | ||
|
|
d120b1c250 | ||
|
|
9d167dfada | ||
|
|
3c6239363b | ||
|
|
5fb13f102a | ||
|
|
aa3d2b9e81 | ||
|
|
e5986b32ff | ||
|
|
a41869ec7d | ||
|
|
6487cf03c5 | ||
|
|
ca0596bca2 | ||
|
|
e4f82109e4 | ||
|
|
8d315dd2b6 | ||
|
|
b05fe4857e | ||
|
|
4a5c0d9042 | ||
|
|
daff4fd87e | ||
|
|
a8e26c254f | ||
|
|
e7337eacfc | ||
|
|
9e7e1ecdbc | ||
|
|
8ed7ebb5b7 | ||
|
|
2f008afca9 | ||
|
|
814b72c6f9 | ||
|
|
663b47aa75 | ||
|
|
9938ee273f | ||
|
|
74845eaf0b | ||
|
|
b0d7880ac0 | ||
|
|
058be4b904 | ||
|
|
42d0c4436e | ||
|
|
378473a6fe | ||
|
|
32ae0afcac | ||
|
|
db7e1c67d8 | ||
|
|
6c5ad1e912 | ||
|
|
2b18dc0a5c | ||
|
|
cc2845168d | ||
|
|
97073340cc | ||
|
|
046b1b61b3 | ||
|
|
da9756ef14 | ||
|
|
f79a88c685 | ||
|
|
b1d965932d | ||
|
|
7d046340dc | ||
|
|
aa872cd0a1 | ||
|
|
b2c5a1ecb3 | ||
|
|
6cd9bdee0b | ||
|
|
a8a1d9c17d | ||
|
|
97058d2cf0 | ||
|
|
ef57409209 | ||
|
|
5f06e66cf1 | ||
|
|
11af932099 | ||
|
|
c9c05d8d0a | ||
|
|
0f59705806 | ||
|
|
320965612d | ||
|
|
c3df60c12b | ||
|
|
4f69949c10 | ||
|
|
3380496e9f | ||
|
|
248ccadecd | ||
|
|
cc5a3ddd05 | ||
|
|
f27424d72e | ||
|
|
5a0e3f15ca | ||
|
|
3d1253c992 | ||
|
|
2b58411391 | ||
|
|
08b8bdecbd | ||
|
|
0a1e51f542 | ||
|
|
1bfdb19e88 | ||
|
|
c0e78f39d7 | ||
|
|
d51753dfdc | ||
|
|
543ad04ca0 | ||
|
|
00e3682aaf | ||
|
|
004101a752 | ||
|
|
568f34d6d8 | ||
|
|
a0cf798409 | ||
|
|
88ea96d417 | ||
|
|
c88438ad35 | ||
|
|
76f334f252 | ||
|
|
ab0fa5c3c8 | ||
|
|
9b4aaaa080 | ||
|
|
eeaa213475 | ||
|
|
2d1b17d1ca | ||
|
|
ff4783f1e4 | ||
|
|
f9ba11961a | ||
|
|
8117488fd8 | ||
|
|
336384bc67 | ||
|
|
065578e48a | ||
|
|
3949089438 | ||
|
|
efa88b9b7f | ||
|
|
f51736437d | ||
|
|
6311e2c315 | ||
|
|
7a3b8f49c7 | ||
|
|
17fb7a7c75 | ||
|
|
bf9ad4d2ba | ||
|
|
6681ab571d | ||
|
|
58d29e0779 | ||
|
|
0133ebc9f2 | ||
|
|
b64dd4af4a | ||
|
|
95d46073cb | ||
|
|
7b1e1e5668 | ||
|
|
62084f4015 | ||
|
|
f70cd8b5b8 | ||
|
|
a32b7b1523 | ||
|
|
9105adc67b | ||
|
|
443fd7bcee | ||
|
|
3259a4a781 | ||
|
|
56c856e802 | ||
|
|
2f71771b56 | ||
|
|
d7ddf2023d | ||
|
|
c58408d76c | ||
|
|
1188cfef1d | ||
|
|
fb0e7fecaf | ||
|
|
3afbb48188 | ||
|
|
837f41986d | ||
|
|
8eda626466 | ||
|
|
fe9818226d | ||
|
|
1e8438a478 | ||
|
|
8fdabc44f5 | ||
|
|
e9e9245112 | ||
|
|
580be2cf32 | ||
|
|
911bb9dcda | ||
|
|
507cf93687 | ||
|
|
ba6e9cc90f | ||
|
|
228ac0d568 | ||
|
|
c6ecaf9642 | ||
|
|
534d2191ff | ||
|
|
709fd52b0b | ||
|
|
c5d795c1f1 | ||
|
|
983f2818b0 | ||
|
|
b4eda37fbf | ||
|
|
a5fe47ee71 | ||
|
|
dc423b22b3 | ||
|
|
7c7ab88a60 | ||
|
|
21189ae130 | ||
|
|
06f95f5362 | ||
|
|
5da63d716b | ||
|
|
9bb700ff0d | ||
|
|
c0a12f4cfb | ||
|
|
138e405cb6 | ||
|
|
849f297e9d | ||
|
|
9da4536354 | ||
|
|
2463eb65b1 | ||
|
|
d3f07a7ba5 | ||
|
|
6348aa1917 | ||
|
|
ef7379c47e | ||
|
|
84aaaaa6b0 |
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -41,8 +41,8 @@ body:
|
||||
label: Superset version
|
||||
options:
|
||||
- master / latest-dev
|
||||
- "6.1.0"
|
||||
- "6.0.0"
|
||||
- "5.0.0"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
||||
2
.github/actions/setup-backend/action.yml
vendored
2
.github/actions/setup-backend/action.yml
vendored
@@ -42,7 +42,7 @@ runs:
|
||||
fi
|
||||
echo "python-version=$RESOLVED_VERSION" >> "$GITHUB_OUTPUT"
|
||||
- name: Set up Python ${{ steps.set-python-version.outputs.python-version }}
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||
with:
|
||||
python-version: ${{ steps.set-python-version.outputs.python-version }}
|
||||
cache: ${{ inputs.cache }}
|
||||
|
||||
32
.github/dependabot.yml
vendored
32
.github/dependabot.yml
vendored
@@ -3,10 +3,6 @@ enable-beta-ecosystems: true
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
ignore:
|
||||
# Ignore temporarily as release schedule is too mentally taxing for dep-handling maintainers
|
||||
# Additionally, very few PRs are reviewed by this action.
|
||||
- dependency-name: anthropics/claude-code-action
|
||||
schedule:
|
||||
interval: "daily"
|
||||
cooldown:
|
||||
@@ -14,12 +10,6 @@ updates:
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
ignore:
|
||||
# TODO: remove below entries until React >= 18.0.0
|
||||
- dependency-name: "storybook"
|
||||
update-types: ["version-update:semver-major", "version-update:semver-minor"]
|
||||
- dependency-name: "@storybook*"
|
||||
update-types: ["version-update:semver-major", "version-update:semver-minor"]
|
||||
- dependency-name: "eslint-plugin-storybook"
|
||||
- dependency-name: "react-error-boundary"
|
||||
- dependency-name: "@rjsf/*"
|
||||
# remark-gfm v4+ requires react-markdown v9+, which needs React 18
|
||||
@@ -42,14 +32,6 @@ updates:
|
||||
# and confirm the issue https://github.com/apache/superset/issues/39600 is fixed
|
||||
- dependency-name: "react-checkbox-tree"
|
||||
update-types: ["version-update:semver-major"]
|
||||
groups:
|
||||
storybook:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "@storybook*"
|
||||
- "storybook"
|
||||
update-types:
|
||||
- "patch"
|
||||
directory: "/superset-frontend/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
@@ -90,21 +72,7 @@ updates:
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/docs/"
|
||||
ignore:
|
||||
# TODO: remove below entries until React >= 18.0.0 in superset-frontend
|
||||
- dependency-name: "storybook"
|
||||
update-types: ["version-update:semver-major", "version-update:semver-minor"]
|
||||
- dependency-name: "@storybook*"
|
||||
update-types: ["version-update:semver-major", "version-update:semver-minor"]
|
||||
- dependency-name: "eslint-plugin-storybook"
|
||||
- dependency-name: "react-error-boundary"
|
||||
groups:
|
||||
storybook:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "@storybook*"
|
||||
- "storybook"
|
||||
update-types:
|
||||
- "patch"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
4
.github/workflows/bashlib.sh
vendored
4
.github/workflows/bashlib.sh
vendored
@@ -114,7 +114,7 @@ testdata() {
|
||||
say "::group::Load test data"
|
||||
# must specify PYTHONPATH to make `tests.superset_test_config` importable
|
||||
export PYTHONPATH="$GITHUB_WORKSPACE"
|
||||
pip install -e .
|
||||
uv pip install --system -e .
|
||||
superset db upgrade
|
||||
superset load_test_users
|
||||
superset load_examples --load-test-data
|
||||
@@ -127,7 +127,7 @@ playwright_testdata() {
|
||||
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 .
|
||||
uv pip install --system -e .
|
||||
superset db upgrade
|
||||
superset load_test_users
|
||||
superset load_examples
|
||||
|
||||
3
.github/workflows/bump-python-package.yml
vendored
3
.github/workflows/bump-python-package.yml
vendored
@@ -30,9 +30,8 @@ jobs:
|
||||
pull-requests: write
|
||||
checks: write
|
||||
steps:
|
||||
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: master
|
||||
|
||||
2
.github/workflows/check-python-deps.yml
vendored
2
.github/workflows/check-python-deps.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check and notify
|
||||
|
||||
88
.github/workflows/claude.yml
vendored
88
.github/workflows/claude.yml
vendored
@@ -1,88 +0,0 @@
|
||||
name: Claude PR Assistant
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-permissions:
|
||||
if: |
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude'))
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
allowed: ${{ steps.check.outputs.allowed }}
|
||||
steps:
|
||||
- name: Check if user is allowed
|
||||
id: check
|
||||
env:
|
||||
COMMENTER: ${{ github.event.comment.user.login }}
|
||||
run: |
|
||||
# List of allowed users
|
||||
ALLOWED_USERS="mistercrunch,rusackas"
|
||||
|
||||
echo "Checking permissions for user: $COMMENTER"
|
||||
|
||||
# Check if user is in allowed list
|
||||
if [[ ",$ALLOWED_USERS," == *",$COMMENTER,"* ]]; then
|
||||
echo "allowed=true" >> $GITHUB_OUTPUT
|
||||
echo "✅ User $COMMENTER is allowed to use Claude"
|
||||
else
|
||||
echo "allowed=false" >> $GITHUB_OUTPUT
|
||||
echo "❌ User $COMMENTER is not allowed to use Claude"
|
||||
fi
|
||||
|
||||
deny-access:
|
||||
needs: check-permissions
|
||||
if: needs.check-permissions.outputs.allowed == 'false'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Comment access denied
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
COMMENTER_LOGIN: ${{ github.event.comment.user.login || github.event.review.user.login || github.event.issue.user.login }}
|
||||
with:
|
||||
script: |
|
||||
const commenter = process.env.COMMENTER_LOGIN;
|
||||
const message = `👋 Hi @${commenter}!
|
||||
|
||||
Thanks for trying to use Claude Code, but currently only certain team members have access to this feature.
|
||||
|
||||
If you believe you should have access, please contact a project maintainer.`;
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: message
|
||||
});
|
||||
|
||||
claude-code-action:
|
||||
needs: check-permissions
|
||||
if: needs.check-permissions.outputs.allowed == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run Claude PR Action
|
||||
uses: anthropics/claude-code-action@5fb899572b81d2bb648d4d187173a2f423a9677c # beta
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
timeout_minutes: "60"
|
||||
9
.github/workflows/codeql-analysis.yml
vendored
9
.github/workflows/codeql-analysis.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
frontend: ${{ steps.check.outputs.frontend }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
@@ -47,6 +47,7 @@ jobs:
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
pull-requests: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
@@ -57,13 +58,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4
|
||||
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -74,6 +75,6 @@ jobs:
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4
|
||||
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
6
.github/workflows/dependency-review.yml
vendored
6
.github/workflows/dependency-review.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: "Checkout Repository"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: "Dependency Review"
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
# the latest version. It's MIT: https://github.com/nbubna/store/blob/master/LICENSE-MIT
|
||||
# pkg:npm/node-forge@1.3.1
|
||||
# selecting BSD-3-Clause licensing terms for node-forge to ensure compatibility with Apache
|
||||
allow-dependencies-licenses: pkg:npm/store2@2.14.2, pkg:npm/node-forge@1.3.1, pkg:npm/rgbcolor, pkg:npm/jszip@3.10.1
|
||||
allow-dependencies-licenses: pkg:npm/rgbcolor, pkg:npm/jszip@3.10.1
|
||||
|
||||
python-dependency-liccheck:
|
||||
# NOTE: Configuration for liccheck lives in our pyproject.yml.
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: "Checkout Repository"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
69
.github/workflows/docker.yml
vendored
69
.github/workflows/docker.yml
vendored
@@ -18,7 +18,6 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
changes:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
@@ -31,7 +30,7 @@ jobs:
|
||||
docker: ${{ steps.check.outputs.docker }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
@@ -71,12 +70,29 @@ jobs:
|
||||
IMAGE_TAG: apache/superset:GHA-${{ matrix.build_preset }}-${{ github.run_id }}
|
||||
|
||||
steps:
|
||||
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Free up disk space
|
||||
shell: bash
|
||||
run: |
|
||||
# Reclaim large preinstalled toolchains we don't use. The image
|
||||
# build, and especially the docker-compose sanity check (which
|
||||
# rebuilds from scratch whenever the registry cache image
|
||||
# apache/superset-cache is unavailable), can otherwise exhaust the
|
||||
# runner's root disk and fail with "no space left on device".
|
||||
echo "Disk before cleanup:"; df -h /
|
||||
sudo rm -rf \
|
||||
/usr/share/dotnet \
|
||||
/usr/local/lib/android \
|
||||
/opt/ghc \
|
||||
/usr/local/.ghcup \
|
||||
/opt/hostedtoolcache/CodeQL \
|
||||
/usr/local/share/boost || true
|
||||
echo "Disk after cleanup:"; df -h /
|
||||
|
||||
- name: Setup Docker Environment
|
||||
uses: ./.github/actions/setup-docker
|
||||
with:
|
||||
@@ -103,13 +119,27 @@ jobs:
|
||||
PUSH_OR_LOAD="--load"
|
||||
fi
|
||||
|
||||
supersetbot docker \
|
||||
$PUSH_OR_LOAD \
|
||||
--preset "$BUILD_PRESET" \
|
||||
--context "$EVENT" \
|
||||
--context-ref "$RELEASE" $FORCE_LATEST \
|
||||
--extra-flags "--build-arg INCLUDE_CHROMIUM=false --tag $IMAGE_TAG" \
|
||||
$PLATFORM_ARG
|
||||
# Retry to absorb transient Docker Hub registry errors (base-image
|
||||
# pull timeouts, 504/401 on push, ECONNRESET) that otherwise fail
|
||||
# the whole job. buildx reuses the buildkit layer cache from the
|
||||
# failed attempt, so a retry mostly re-does just the failed push.
|
||||
for attempt in 1 2 3; do
|
||||
if supersetbot docker \
|
||||
$PUSH_OR_LOAD \
|
||||
--preset "$BUILD_PRESET" \
|
||||
--context "$EVENT" \
|
||||
--context-ref "$RELEASE" $FORCE_LATEST \
|
||||
--extra-flags "--build-arg INCLUDE_CHROMIUM=false --tag $IMAGE_TAG" \
|
||||
$PLATFORM_ARG; then
|
||||
break
|
||||
fi
|
||||
if [ "$attempt" -eq 3 ]; then
|
||||
echo "::error::supersetbot docker build failed after 3 attempts"
|
||||
exit 1
|
||||
fi
|
||||
echo "::warning::Build attempt ${attempt} failed; retrying in 30s..."
|
||||
sleep 30
|
||||
done
|
||||
|
||||
# in the context of push (using multi-platform build), we need to pull the image locally
|
||||
- name: Docker pull
|
||||
@@ -147,9 +177,24 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Free up disk space
|
||||
shell: bash
|
||||
run: |
|
||||
# The sanity check rebuilds the image from scratch whenever the
|
||||
# registry cache image apache/superset-cache is unavailable, which
|
||||
# can exhaust the runner's root disk ("no space left on device").
|
||||
echo "Disk before cleanup:"; df -h /
|
||||
sudo rm -rf \
|
||||
/usr/share/dotnet \
|
||||
/usr/local/lib/android \
|
||||
/opt/ghc \
|
||||
/usr/local/.ghcup \
|
||||
/opt/hostedtoolcache/CodeQL \
|
||||
/usr/local/share/boost || true
|
||||
echo "Disk after cleanup:"; df -h /
|
||||
- name: Setup Docker Environment
|
||||
uses: ./.github/actions/setup-docker
|
||||
with:
|
||||
|
||||
36
.github/workflows/embedded-sdk-release.yml
vendored
36
.github/workflows/embedded-sdk-release.yml
vendored
@@ -10,37 +10,29 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
config:
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
has-secrets: ${{ steps.check.outputs.has-secrets }}
|
||||
steps:
|
||||
- name: "Check for secrets"
|
||||
id: check
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "${NPM_TOKEN}" ]; then
|
||||
echo "has-secrets=1" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
env:
|
||||
NPM_TOKEN: ${{ (secrets.NPM_TOKEN != '') || '' }}
|
||||
build:
|
||||
needs: config
|
||||
if: needs.config.outputs.has-secrets
|
||||
# Publishing uses npm trusted publishing (OIDC), so there is no NPM_TOKEN to
|
||||
# gate on. Restrict to the canonical repo: forks cannot mint a valid OIDC
|
||||
# token for this package and must not publish.
|
||||
if: github.repository == 'apache/superset'
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # required for npm trusted publishing (OIDC)
|
||||
defaults:
|
||||
run:
|
||||
working-directory: superset-embedded-sdk
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
# Note: registry-url is intentionally omitted. When set, actions/setup-node
|
||||
# writes an .npmrc with `_authToken=${NODE_AUTH_TOKEN}` and a placeholder
|
||||
# token, which makes npm attempt token auth and skip the OIDC
|
||||
# trusted-publishing exchange. With no .npmrc auth line, npm authenticates
|
||||
# via OIDC against the default registry (registry.npmjs.org).
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version-file: './superset-embedded-sdk/.nvmrc'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: "./superset-embedded-sdk/.nvmrc"
|
||||
- run: npm ci
|
||||
- run: npm run ci:release
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
6
.github/workflows/embedded-sdk-test.yml
vendored
6
.github/workflows/embedded-sdk-test.yml
vendored
@@ -21,13 +21,13 @@ jobs:
|
||||
run:
|
||||
working-directory: superset-embedded-sdk
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version-file: './superset-embedded-sdk/.nvmrc'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: "./superset-embedded-sdk/.nvmrc"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
- run: npm run build
|
||||
|
||||
4
.github/workflows/generate-FOSSA-report.yml
vendored
4
.github/workflows/generate-FOSSA-report.yml
vendored
@@ -32,12 +32,12 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
|
||||
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "11"
|
||||
|
||||
@@ -18,7 +18,6 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
validate-all-ghas:
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
@@ -28,14 +27,14 @@ jobs:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: "20"
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install -g @action-validator/core @action-validator/cli --save-dev
|
||||
|
||||
3
.github/workflows/issue_creation.yml
vendored
3
.github/workflows/issue_creation.yml
vendored
@@ -15,9 +15,8 @@ jobs:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
steps:
|
||||
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
46
.github/workflows/latest-release-tag.yml
vendored
46
.github/workflows/latest-release-tag.yml
vendored
@@ -11,29 +11,29 @@ jobs:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
|
||||
- name: Check for latest tag
|
||||
id: latest-tag
|
||||
env:
|
||||
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
source ./scripts/tag_latest_release.sh "$RELEASE_TAG_NAME" --dry-run
|
||||
- name: Check for latest tag
|
||||
id: latest-tag
|
||||
env:
|
||||
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
source ./scripts/tag_latest_release.sh "$RELEASE_TAG_NAME" --dry-run
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config user.name "$GITHUB_ACTOR"
|
||||
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config user.name "$GITHUB_ACTOR"
|
||||
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
|
||||
|
||||
- name: Run latest-tag
|
||||
uses: ./.github/actions/latest-tag
|
||||
if: steps.latest-tag.outputs.SKIP_TAG != 'true'
|
||||
with:
|
||||
description: Superset latest release
|
||||
tag-name: latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
- name: Run latest-tag
|
||||
uses: ./.github/actions/latest-tag
|
||||
if: steps.latest-tag.outputs.SKIP_TAG != 'true'
|
||||
with:
|
||||
description: Superset latest release
|
||||
tag-name: latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
8
.github/workflows/license-check.yml
vendored
8
.github/workflows/license-check.yml
vendored
@@ -18,14 +18,14 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
|
||||
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '11'
|
||||
distribution: "temurin"
|
||||
java-version: "11"
|
||||
- name: Run license check
|
||||
run: ./scripts/check_license.sh
|
||||
|
||||
5
.github/workflows/pr-lint.yml
vendored
5
.github/workflows/pr-lint.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -31,6 +31,5 @@ jobs:
|
||||
on-failed-regex-fail-action: true
|
||||
on-failed-regex-request-changes: false
|
||||
on-failed-regex-create-review: false
|
||||
on-failed-regex-comment:
|
||||
"Please format your PR title to match: `%regex%`!"
|
||||
on-failed-regex-comment: "Please format your PR title to match: `%regex%`!"
|
||||
repo-token: "${{ github.token }}"
|
||||
|
||||
10
.github/workflows/pre-commit.yml
vendored
10
.github/workflows/pre-commit.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["current", "previous", "next"]') }}
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -48,9 +48,9 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'superset-frontend/package-lock.json'
|
||||
node-version-file: "superset-frontend/.nvmrc"
|
||||
cache: "npm"
|
||||
cache-dependency-path: "superset-frontend/package-lock.json"
|
||||
|
||||
- name: Install Frontend Dependencies
|
||||
run: |
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
id: changed_files
|
||||
uses: ./.github/actions/file-changes-action
|
||||
with:
|
||||
output: ' '
|
||||
output: " "
|
||||
|
||||
- name: pre-commit
|
||||
env:
|
||||
|
||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
# pulls all commits (needed for lerna / semantic release to correctly version)
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
if: env.HAS_TAGS
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version-file: './superset-frontend/.nvmrc'
|
||||
node-version-file: "./superset-frontend/.nvmrc"
|
||||
|
||||
- name: Cache npm
|
||||
if: env.HAS_TAGS
|
||||
|
||||
6
.github/workflows/showtime-trigger.yml
vendored
6
.github/workflows/showtime-trigger.yml
vendored
@@ -10,11 +10,11 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number to sync'
|
||||
description: "PR number to sync"
|
||||
required: true
|
||||
type: number
|
||||
sha:
|
||||
description: 'Specific SHA to deploy (optional, defaults to latest)'
|
||||
description: "Specific SHA to deploy (optional, defaults to latest)"
|
||||
required: false
|
||||
type: string
|
||||
|
||||
@@ -152,7 +152,7 @@ jobs:
|
||||
|
||||
- name: Checkout PR code (only if build needed)
|
||||
if: steps.auth.outputs.authorized == 'true' && steps.check.outputs.build_needed == 'true'
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ steps.check.outputs.target_sha }}
|
||||
persist-credentials: false
|
||||
|
||||
4
.github/workflows/superset-app-cli.yml
vendored
4
.github/workflows/superset-app-cli.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
- 16379:6379
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
- name: superset init
|
||||
if: steps.check.outputs.python
|
||||
run: |
|
||||
pip install -e .
|
||||
uv pip install --system -e .
|
||||
superset db upgrade
|
||||
superset load_test_users
|
||||
- name: superset load_examples
|
||||
|
||||
10
.github/workflows/superset-docs-deploy.yml
vendored
10
.github/workflows/superset-docs-deploy.yml
vendored
@@ -60,7 +60,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: "Checkout ${{ github.event.workflow_run.head_sha || github.sha }}"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
|
||||
persist-credentials: false
|
||||
@@ -68,13 +68,13 @@ jobs:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version-file: './docs/.nvmrc'
|
||||
node-version-file: "./docs/.nvmrc"
|
||||
- name: Setup Python
|
||||
uses: ./.github/actions/setup-backend/
|
||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
|
||||
- uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '21'
|
||||
distribution: "zulu"
|
||||
java-version: "21"
|
||||
- name: Install Graphviz
|
||||
run: sudo apt-get install -y graphviz
|
||||
- name: Compute Entity Relationship diagram (ERD)
|
||||
|
||||
14
.github/workflows/superset-docs-verify.yml
vendored
14
.github/workflows/superset-docs-verify.yml
vendored
@@ -28,12 +28,12 @@ jobs:
|
||||
name: Link Checking
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
# Do not bump this linkinator-action version without opening
|
||||
# an ASF Infra ticket to allow the new version first!
|
||||
- uses: JustinBeckwith/linkinator-action@af984b9f30f63e796ae2ea5be5e07cb587f1bbd9 # v2.3
|
||||
- uses: JustinBeckwith/linkinator-action@af984b9f30f63e796ae2ea5be5e07cb587f1bbd9 # v2.3
|
||||
continue-on-error: true # This will make the job advisory (non-blocking, no red X)
|
||||
with:
|
||||
paths: "**/*.md, **/*.mdx"
|
||||
@@ -73,14 +73,14 @@ jobs:
|
||||
working-directory: docs
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version-file: './docs/.nvmrc'
|
||||
node-version-file: "./docs/.nvmrc"
|
||||
- name: yarn install
|
||||
run: |
|
||||
yarn install --check-cache
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
working-directory: docs
|
||||
steps:
|
||||
- name: "Checkout PR head: ${{ github.event.workflow_run.head_sha }}"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ github.event.workflow_run.head_sha }}
|
||||
persist-credentials: false
|
||||
@@ -120,7 +120,7 @@ jobs:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version-file: './docs/.nvmrc'
|
||||
node-version-file: "./docs/.nvmrc"
|
||||
- name: yarn install
|
||||
run: |
|
||||
yarn install --check-cache
|
||||
@@ -131,7 +131,7 @@ jobs:
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
name: database-diagnostics
|
||||
path: docs/src/data/
|
||||
if_no_artifact_found: 'warning'
|
||||
if_no_artifact_found: "warning"
|
||||
- name: Use fresh diagnostics
|
||||
run: |
|
||||
if [ -f "src/data/databases-diagnostics.json" ]; then
|
||||
|
||||
40
.github/workflows/superset-e2e.yml
vendored
40
.github/workflows/superset-e2e.yml
vendored
@@ -10,17 +10,17 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
use_dashboard:
|
||||
description: 'Use Cypress Dashboard (true/false) [paid service - trigger manually when needed]. You MUST provide a branch and/or PR number below for this to work.'
|
||||
description: "Use Cypress Dashboard (true/false) [paid service - trigger manually when needed]. You MUST provide a branch and/or PR number below for this to work."
|
||||
required: false
|
||||
default: 'false'
|
||||
default: "false"
|
||||
ref:
|
||||
description: 'The branch or tag to checkout'
|
||||
description: "The branch or tag to checkout"
|
||||
required: false
|
||||
default: ''
|
||||
default: ""
|
||||
pr_id:
|
||||
description: 'The pull request ID to checkout'
|
||||
description: "The pull request ID to checkout"
|
||||
required: false
|
||||
default: ''
|
||||
default: ""
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
frontend: ${{ steps.check.outputs.frontend }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
cypress-matrix:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.python == 'true' || needs.changes.outputs.frontend == 'true'
|
||||
if: (needs.changes.outputs.python == 'true' || needs.changes.outputs.frontend == 'true') && github.event.pull_request.draft == false
|
||||
# Somehow one test flakes on 24.04 for unknown reasons, this is the only GHA left on 22.04
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
@@ -97,21 +97,21 @@ jobs:
|
||||
# Conditional checkout based on context
|
||||
- name: Checkout for push or pull_request event
|
||||
if: github.event_name == 'push' || github.event_name == 'pull_request'
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Checkout using ref (workflow_dispatch)
|
||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.ref != ''
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event.inputs.ref }}
|
||||
submodules: recursive
|
||||
- name: Checkout using PR ID (workflow_dispatch)
|
||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.pr_id != ''
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: refs/pull/${{ github.event.inputs.pr_id }}/merge
|
||||
@@ -130,9 +130,9 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version-file: './superset-frontend/.nvmrc'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'superset-frontend/package-lock.json'
|
||||
node-version-file: "./superset-frontend/.nvmrc"
|
||||
cache: "npm"
|
||||
cache-dependency-path: "superset-frontend/package-lock.json"
|
||||
- name: Install npm dependencies
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
@@ -207,21 +207,21 @@ jobs:
|
||||
# Conditional checkout based on context (same as Cypress workflow)
|
||||
- name: Checkout for push or pull_request event
|
||||
if: github.event_name == 'push' || github.event_name == 'pull_request'
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Checkout using ref (workflow_dispatch)
|
||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.ref != ''
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event.inputs.ref }}
|
||||
submodules: recursive
|
||||
- name: Checkout using PR ID (workflow_dispatch)
|
||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.pr_id != ''
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: refs/pull/${{ github.event.inputs.pr_id }}/merge
|
||||
@@ -240,9 +240,9 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version-file: './superset-frontend/.nvmrc'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'superset-frontend/package-lock.json'
|
||||
node-version-file: "./superset-frontend/.nvmrc"
|
||||
cache: "npm"
|
||||
cache-dependency-path: "superset-frontend/package-lock.json"
|
||||
- name: Install npm dependencies
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
working-directory: superset-extensions-cli
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
if: steps.check.outputs.superset-extensions-cli
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
with:
|
||||
file: ./coverage.xml
|
||||
flags: superset-extensions-cli
|
||||
|
||||
6
.github/workflows/superset-frontend.yml
vendored
6
.github/workflows/superset-frontend.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
should-run: ${{ steps.check.outputs.frontend }}
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
run: npx nyc merge coverage/ merged-output/coverage-summary.json
|
||||
|
||||
- name: Upload Code Coverage
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
with:
|
||||
flags: javascript
|
||||
use_oidc: true
|
||||
|
||||
4
.github/workflows/superset-helm-lint.yml
vendored
4
.github/workflows/superset-helm-lint.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
- name: Setup Python
|
||||
uses: ./.github/actions/setup-backend/
|
||||
with:
|
||||
install-superset: 'false'
|
||||
install-superset: "false"
|
||||
|
||||
- name: Set up chart-testing
|
||||
uses: ./.github/actions/chart-testing-action
|
||||
|
||||
2
.github/workflows/superset-helm-release.yml
vendored
2
.github/workflows/superset-helm-release.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref_name }}
|
||||
persist-credentials: true
|
||||
|
||||
22
.github/workflows/superset-playwright.yml
vendored
22
.github/workflows/superset-playwright.yml
vendored
@@ -10,13 +10,13 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'The branch or tag to checkout'
|
||||
description: "The branch or tag to checkout"
|
||||
required: false
|
||||
default: ''
|
||||
default: ""
|
||||
pr_id:
|
||||
description: 'The pull request ID to checkout'
|
||||
description: "The pull request ID to checkout"
|
||||
required: false
|
||||
default: ''
|
||||
default: ""
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
frontend: ${{ steps.check.outputs.frontend }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
@@ -83,21 +83,21 @@ jobs:
|
||||
# Conditional checkout based on context (same as Cypress workflow)
|
||||
- name: Checkout for push or pull_request event
|
||||
if: github.event_name == 'push' || github.event_name == 'pull_request'
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Checkout using ref (workflow_dispatch)
|
||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.ref != ''
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event.inputs.ref }}
|
||||
submodules: recursive
|
||||
- name: Checkout using PR ID (workflow_dispatch)
|
||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.pr_id != ''
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: refs/pull/${{ github.event.inputs.pr_id }}/merge
|
||||
@@ -116,9 +116,9 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version-file: './superset-frontend/.nvmrc'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'superset-frontend/package-lock.json'
|
||||
node-version-file: "./superset-frontend/.nvmrc"
|
||||
cache: "npm"
|
||||
cache-dependency-path: "superset-frontend/package-lock.json"
|
||||
- name: Install npm dependencies
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
# Python integration tests
|
||||
name: Python-Integration
|
||||
|
||||
# Least-privilege default for GITHUB_TOKEN. Jobs that need more (e.g. OIDC for
|
||||
# codecov uploads) opt in via their own job-level `permissions:` block.
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -24,7 +29,7 @@ jobs:
|
||||
python: ${{ steps.check.outputs.python }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
@@ -67,7 +72,7 @@ jobs:
|
||||
- 16379:6379
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -85,7 +90,7 @@ jobs:
|
||||
run: |
|
||||
./scripts/python_tests.sh
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
with:
|
||||
flags: python,mysql
|
||||
verbose: true
|
||||
@@ -152,7 +157,7 @@ jobs:
|
||||
- 16379:6379
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -173,7 +178,7 @@ jobs:
|
||||
run: |
|
||||
./scripts/python_tests.sh
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
with:
|
||||
flags: python,postgres
|
||||
verbose: true
|
||||
@@ -202,7 +207,7 @@ jobs:
|
||||
- 16379:6379
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -222,7 +227,7 @@ jobs:
|
||||
run: |
|
||||
./scripts/python_tests.sh
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
with:
|
||||
flags: python,sqlite
|
||||
verbose: true
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
python: ${{ steps.check.outputs.python }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
- 16379:6379
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
run: |
|
||||
./scripts/python_tests.sh -m 'chart_data_flow or sql_json_flow'
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
with:
|
||||
flags: python,presto
|
||||
verbose: true
|
||||
@@ -127,7 +127,7 @@ jobs:
|
||||
- 16379:6379
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -149,10 +149,10 @@ jobs:
|
||||
run: celery-worker
|
||||
- name: Python unit tests (PostgreSQL)
|
||||
run: |
|
||||
pip install -e .[hive]
|
||||
uv pip install --system -e .[hive]
|
||||
./scripts/python_tests.sh -m 'chart_data_flow or sql_json_flow'
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
with:
|
||||
flags: python,hive
|
||||
verbose: true
|
||||
|
||||
11
.github/workflows/superset-python-unittest.yml
vendored
11
.github/workflows/superset-python-unittest.yml
vendored
@@ -1,6 +1,11 @@
|
||||
# Python unit tests
|
||||
name: Python-Unit
|
||||
|
||||
# Least-privilege default for GITHUB_TOKEN. Jobs that need more (e.g. OIDC for
|
||||
# codecov uploads) opt in via their own job-level `permissions:` block.
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -25,7 +30,7 @@ jobs:
|
||||
python: ${{ steps.check.outputs.python }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
@@ -50,7 +55,7 @@ jobs:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -72,7 +77,7 @@ jobs:
|
||||
pytest --durations-min=0.5 --cov=superset/sql/ ./tests/unit_tests/sql/ --cache-clear --cov-fail-under=100
|
||||
pytest --durations-min=0.5 --cov=superset/semantic_layers/ ./tests/unit_tests/semantic_layers/ --cache-clear --cov-fail-under=100
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
with:
|
||||
flags: python,unit
|
||||
verbose: true
|
||||
|
||||
10
.github/workflows/superset-translations.yml
vendored
10
.github/workflows/superset-translations.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
pull-requests: read
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -40,9 +40,9 @@ jobs:
|
||||
if: steps.check.outputs.frontend
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version-file: './superset-frontend/.nvmrc'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'superset-frontend/package-lock.json'
|
||||
node-version-file: "./superset-frontend/.nvmrc"
|
||||
cache: "npm"
|
||||
cache-dependency-path: "superset-frontend/package-lock.json"
|
||||
- name: Install dependencies
|
||||
if: steps.check.outputs.frontend
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
pull-requests: read
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
|
||||
2
.github/workflows/superset-websocket.yml
vendored
2
.github/workflows/superset-websocket.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install dependencies
|
||||
|
||||
4
.github/workflows/supersetbot.yml
vendored
4
.github/workflows/supersetbot.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
comment_body:
|
||||
description: 'Comment Body'
|
||||
description: "Comment Body"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
});
|
||||
|
||||
- name: "Checkout ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
60
.github/workflows/sync-requirements-for-python-dep-upgrade-pr.yml
vendored
Normal file
60
.github/workflows/sync-requirements-for-python-dep-upgrade-pr.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
name: Sync requirements for Python dependency PRs
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
sync-python-dep-requirements:
|
||||
# This action is limited for (1) PRs authored by Dependabot and (2) upstream repo due to write back to remote
|
||||
if: github.repository == 'apache/superset' && github.event.pull_request.user.login == 'dependabot[bot]' && github.event.pull_request.head.repo.fork == false
|
||||
runs-on: ubuntu-26.04
|
||||
steps:
|
||||
- name: Fetch Dependabot metadata
|
||||
id: dependabot-metadata
|
||||
shell: bash
|
||||
env:
|
||||
BRANCH_NAME: ${{ github.head_ref }}
|
||||
run: |
|
||||
# Get current branch name, extract the package ecosystem and return as GHA step output
|
||||
packageEcosystem=$(echo "$BRANCH_NAME" | cut -d'/' -f2)
|
||||
echo "package-ecosystem=$packageEcosystem" >> $GITHUB_OUTPUT
|
||||
|
||||
# zizmor: ignore[artipacked] - required persisted credentials to push synced requirement changes back to remote
|
||||
- name: Checkout source code
|
||||
if: ${{ steps.dependabot-metadata.outputs.package-ecosystem == 'pip' }}
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
persist-credentials: true
|
||||
|
||||
# Authenticate the Docker daemon so the python:slim pull in
|
||||
# uv-pip-compile.sh uses our (much higher) authenticated rate limit
|
||||
# instead of the shared-runner anonymous one.
|
||||
- name: Login to Docker Hub
|
||||
if: ${{ steps.dependabot-metadata.outputs.package-ecosystem == 'pip' }}
|
||||
continue-on-error: true
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USER }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Sync requirements in containerized environment
|
||||
if: ${{ steps.dependabot-metadata.outputs.package-ecosystem == 'pip' }}
|
||||
run: ./scripts/uv-pip-compile.sh
|
||||
|
||||
- name: Push changes to remote PRs
|
||||
if: ${{ steps.dependabot-metadata.outputs.package-ecosystem == 'pip' }}
|
||||
run: |
|
||||
git config user.name 'github-actions[bot]'
|
||||
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
|
||||
git add requirements
|
||||
git diff --cached --quiet && exit 0
|
||||
git commit --signoff --message "build(deps): sync pinned requirements for Dependabot pip PRs"
|
||||
git push origin "HEAD:refs/heads/${GITHUB_EVENT_PULL_REQUEST_HEAD_REF}"
|
||||
env:
|
||||
GITHUB_EVENT_PULL_REQUEST_HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
||||
15
.github/workflows/tag-release.yml
vendored
15
.github/workflows/tag-release.yml
vendored
@@ -16,11 +16,11 @@ on:
|
||||
force-latest:
|
||||
required: true
|
||||
type: choice
|
||||
default: 'false'
|
||||
default: "false"
|
||||
description: Whether to force a latest tag on the release
|
||||
options:
|
||||
- 'true'
|
||||
- 'false'
|
||||
- "true"
|
||||
- "false"
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -49,12 +49,12 @@ jobs:
|
||||
contents: write
|
||||
strategy:
|
||||
matrix:
|
||||
build_preset: ["dev", "lean", "py310", "websocket", "dockerize", "py311", "py312"]
|
||||
build_preset:
|
||||
["dev", "lean", "py310", "websocket", "dockerize", "py311", "py312"]
|
||||
fail-fast: false
|
||||
steps:
|
||||
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -119,9 +119,8 @@ jobs:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
4
.github/workflows/tech-debt.yml
vendored
4
.github/workflows/tech-debt.yml
vendored
@@ -32,14 +32,14 @@ jobs:
|
||||
name: Generate Reports
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version-file: './superset-frontend/.nvmrc'
|
||||
node-version-file: "./superset-frontend/.nvmrc"
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
|
||||
@@ -50,3 +50,4 @@ under the License.
|
||||
- [4.1.4](./CHANGELOG/4.1.4.md)
|
||||
- [5.0.0](./CHANGELOG/5.0.0.md)
|
||||
- [6.0.0](./CHANGELOG/6.0.0.md)
|
||||
- [6.1.0](./CHANGELOG/6.1.0.md)
|
||||
|
||||
1563
CHANGELOG/6.1.0.md
Normal file
1563
CHANGELOG/6.1.0.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@ ARG BUILD_TRANSLATIONS="false"
|
||||
######################################################################
|
||||
# superset-node-ci used as a base for building frontend assets and CI
|
||||
######################################################################
|
||||
FROM --platform=${BUILDPLATFORM} node:22-trixie-slim AS superset-node-ci
|
||||
FROM --platform=${BUILDPLATFORM} node:24-trixie-slim AS superset-node-ci
|
||||
ARG BUILD_TRANSLATIONS
|
||||
ENV BUILD_TRANSLATIONS=${BUILD_TRANSLATIONS}
|
||||
ARG DEV_MODE="false" # Skip frontend build in dev mode
|
||||
@@ -55,6 +55,13 @@ WORKDIR /app/superset-frontend
|
||||
RUN mkdir -p /app/superset/static/assets \
|
||||
/app/superset/translations
|
||||
|
||||
# Harden `npm ci` against transient npm-registry network blips (e.g. ECONNRESET),
|
||||
# which otherwise fail the entire multi-platform image build with no retry.
|
||||
ENV npm_config_fetch_retries=5 \
|
||||
npm_config_fetch_retry_mintimeout=20000 \
|
||||
npm_config_fetch_retry_maxtimeout=120000 \
|
||||
npm_config_fetch_timeout=600000
|
||||
|
||||
# Mount package files and install dependencies if not in dev mode
|
||||
# NOTE: we mount packages and plugins as they are referenced in package.json as workspaces
|
||||
# ideally we'd COPY only their package.json. Here npm ci will be cached as long
|
||||
|
||||
17
Makefile
17
Makefile
@@ -23,11 +23,14 @@ PYTHON=`command -v python3.11 || command -v python3.10`
|
||||
install: superset pre-commit
|
||||
|
||||
superset:
|
||||
# Bootstrap uv (the project's installer) into the active environment
|
||||
pip install uv
|
||||
|
||||
# Install external dependencies
|
||||
pip install -r requirements/development.txt
|
||||
uv pip install -r requirements/development.txt
|
||||
|
||||
# Install Superset in editable (development) mode
|
||||
pip install -e .
|
||||
uv pip install -e .
|
||||
|
||||
# Create an admin user in your metadata database
|
||||
superset fab create-admin \
|
||||
@@ -52,11 +55,14 @@ superset:
|
||||
update: update-py update-js
|
||||
|
||||
update-py:
|
||||
# Bootstrap uv (the project's installer) into the active environment
|
||||
pip install uv
|
||||
|
||||
# Install external dependencies
|
||||
pip install -r requirements/development.txt
|
||||
uv pip install -r requirements/development.txt
|
||||
|
||||
# Install Superset in editable (development) mode
|
||||
pip install -e .
|
||||
uv pip install -e .
|
||||
|
||||
# Initialize the database
|
||||
superset db upgrade
|
||||
@@ -79,7 +85,8 @@ activate:
|
||||
|
||||
pre-commit:
|
||||
# setup pre commit dependencies
|
||||
pip3 install -r requirements/development.txt
|
||||
pip install uv
|
||||
uv pip install -r requirements/development.txt
|
||||
pre-commit install
|
||||
|
||||
format: py-format js-format
|
||||
|
||||
@@ -83,6 +83,9 @@ categories:
|
||||
- name: Clark.de
|
||||
url: https://clark.de/
|
||||
|
||||
- name: Cover Genius
|
||||
url: https://covergenius.com/
|
||||
|
||||
- name: EnquiryLabs
|
||||
url: https://www.enquirylabs.co.uk
|
||||
|
||||
@@ -92,6 +95,10 @@ categories:
|
||||
- name: KarrotPay
|
||||
url: https://www.daangnpay.com/
|
||||
|
||||
- name: NICE Actimize
|
||||
url: https://www.niceactimize.com/
|
||||
contributors: ["@stevensuting"]
|
||||
|
||||
- name: Remita
|
||||
url: https://remita.net
|
||||
contributors: ["@mujibishola"]
|
||||
@@ -112,9 +119,6 @@ categories:
|
||||
url: https://xendit.co/
|
||||
contributors: ["@LieAlbertTriAdrian"]
|
||||
|
||||
- name: Cover Genius
|
||||
url: https://covergenius.com/
|
||||
|
||||
Gaming:
|
||||
- name: Popoko VM Games Studio
|
||||
url: https://popoko.live
|
||||
@@ -296,7 +300,6 @@ categories:
|
||||
logo: hifadih.png
|
||||
contributors: ["@saintLaurent00"]
|
||||
|
||||
# Logo approved by @anmol-hpe on behalf of HPE
|
||||
- name: HPE
|
||||
url: https://www.hpe.com/in/en/home.html
|
||||
logo: hpe.png
|
||||
@@ -396,6 +399,10 @@ categories:
|
||||
url: https://www.techaudit.info
|
||||
contributors: ["@ETselikov"]
|
||||
|
||||
- name: Tech Solution
|
||||
url: https://www.tech-solution.com.ar/
|
||||
contributors: ["@danteGiuliano", "@LeandroVallejos", "@McJaben", "@xJeree", "@zeo-return-null"]
|
||||
|
||||
- name: Tenable
|
||||
url: https://www.tenable.com
|
||||
contributors: ["@dflionis"]
|
||||
@@ -425,6 +432,10 @@ categories:
|
||||
logo: userguiding.svg
|
||||
contributors: ["@tzercin"]
|
||||
|
||||
- name: Value Ad
|
||||
url: https://bestpair.info/
|
||||
contributors: ["@stevensuting"]
|
||||
|
||||
- name: Virtuoso QA
|
||||
url: https://www.virtuosoqa.com
|
||||
|
||||
@@ -509,10 +520,6 @@ categories:
|
||||
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"]
|
||||
@@ -521,7 +528,24 @@ categories:
|
||||
url: https://www.vipkid.com.cn/
|
||||
contributors: ["@illpanda"]
|
||||
|
||||
- name: WikiMedia Foundation
|
||||
Social Organization:
|
||||
- name: Living Goods
|
||||
url: https://www.livinggoods.org
|
||||
contributors: ["@chelule"]
|
||||
|
||||
- name: One Acre Fund
|
||||
url: https://oneacrefund.org/
|
||||
contributors: ["@stevensuting"]
|
||||
|
||||
- name: Quest Alliance
|
||||
url: https://www.questalliance.net/
|
||||
contributors: ["@stevensuting"]
|
||||
|
||||
- name: The GRAPH Network
|
||||
url: https://thegraphnetwork.org/
|
||||
contributors: ["@fccoelho"]
|
||||
|
||||
- name: Wikimedia Foundation
|
||||
url: https://wikimediafoundation.org
|
||||
contributors: ["@vg"]
|
||||
|
||||
@@ -534,6 +558,10 @@ categories:
|
||||
url: https://www.douroeci.com/
|
||||
contributors: ["@nunohelibeires"]
|
||||
|
||||
- name: Rogow
|
||||
url: https://rogow.com.br/
|
||||
contributors: ["@nilmonto"]
|
||||
|
||||
- name: Safaricom
|
||||
url: https://www.safaricom.co.ke/
|
||||
contributors: ["@mmutiso"]
|
||||
@@ -546,11 +574,10 @@ categories:
|
||||
url: https://wattbewerb.de/
|
||||
contributors: ["@wattbewerb"]
|
||||
|
||||
- name: Rogow
|
||||
url: https://rogow.com.br/
|
||||
contributors: ["@nilmonto"]
|
||||
|
||||
Healthcare:
|
||||
- name: 2070Health
|
||||
url: https://2070health.com/
|
||||
|
||||
- name: Amino
|
||||
url: https://amino.com
|
||||
contributors: ["@shkr"]
|
||||
@@ -563,10 +590,6 @@ categories:
|
||||
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"]
|
||||
@@ -585,10 +608,10 @@ categories:
|
||||
- name: WeSure
|
||||
url: https://www.wesure.cn/
|
||||
|
||||
- name: 2070Health
|
||||
url: https://2070health.com/
|
||||
|
||||
HR / Staffing:
|
||||
- name: bluquist
|
||||
url: https://bluquist.com/
|
||||
|
||||
- name: Swile
|
||||
url: https://www.swile.co/
|
||||
contributors: ["@PaoloTerzi"]
|
||||
@@ -596,21 +619,18 @@ categories:
|
||||
- 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: NRLM - Sarathi, India
|
||||
url: https://pib.gov.in/PressReleasePage.aspx?PRID=1999586
|
||||
|
||||
- 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
|
||||
|
||||
146
UPDATING.md
146
UPDATING.md
@@ -24,6 +24,75 @@ assists people when migrating to a new version.
|
||||
|
||||
## Next
|
||||
|
||||
### Guest-token RLS rules reject unknown fields
|
||||
|
||||
The `rls` rules passed to `POST /api/v1/security/guest_token/` are now validated strictly: a rule may only contain `dataset` and `clause`. Previously unknown fields were silently dropped, so a mistyped or legacy scope key (most commonly `datasource` instead of `dataset`) produced a rule with no `dataset`, which is treated as a *global* rule applied to every dataset the embedded resource can reach. Such a request now returns HTTP 400 identifying the offending field instead of issuing a token with an unintended global rule. Integrators that were sending extra fields in RLS rules must remove them; valid dataset-scoped (`{"dataset": 41, "clause": "..."}`) and global (`{"clause": "..."}`) rules are unaffected.
|
||||
|
||||
### MCP service requires `MCP_JWT_AUDIENCE` when JWT auth is enabled
|
||||
|
||||
When the MCP service has JWT auth enabled (`MCP_AUTH_ENABLED = True`), an audience must be configured via `MCP_JWT_AUDIENCE` so issued tokens are bound to this service. The service now fails to start with a clear configuration error when the audience is unset, instead of starting with audience validation skipped. Deployments that enable MCP JWT auth must set `MCP_JWT_AUDIENCE` to the audience value their identity provider issues for the MCP service. API-key-only MCP deployments (JWT auth disabled) are unaffected.
|
||||
|
||||
### Pivot table First/Last aggregations follow data order
|
||||
|
||||
The pivot table chart's `First` and `Last` aggregations now return the first and last value in data (query result) order, instead of effectively returning the minimum and maximum. Existing pivot tables that use these aggregations for totals/subtotals may show different values after upgrading. For deterministic results, ensure the underlying query has a stable sort order.
|
||||
|
||||
### `thumbnail_url` removed from dashboard list API response
|
||||
|
||||
The `thumbnail_url` field has been removed from `GET /api/v1/dashboard/` list responses. External consumers relying on this field must now construct the thumbnail URL client-side using `id` and `changed_on_utc`:
|
||||
|
||||
```
|
||||
/api/v1/dashboard/{id}/thumbnail/{changed_on_utc}/
|
||||
```
|
||||
|
||||
The thumbnail endpoint redirects to the current digest URL regardless of whether the supplied digest is exact. If the image is not yet cached, that digest URL may return `202` and trigger async generation. Using `changed_on_utc` as the digest is sufficient for cache-busting purposes.
|
||||
|
||||
### Webhook alerts/reports block private/internal hosts by default
|
||||
|
||||
Webhook alert/report dispatch (`WebhookNotification.send`) now validates the target URL's host against the same private/internal-IP block applied to dataset import URLs. If the resolved host is in a loopback, link-local, private (RFC-1918), shared-CGNAT, or multicast range, the webhook is rejected with `NotificationParamException`.
|
||||
|
||||
Deployments that intentionally point webhooks at internal targets (chatops bridges, internal automation servers, on-premises Mattermost/Rocket.Chat, etc.) can opt out by setting `ALERT_REPORTS_WEBHOOK_ALLOW_INTERNAL_HOSTS = True` in `superset_config.py`. This mirrors the existing `DATASET_IMPORT_ALLOW_INTERNAL_DATA_URLS` opt-out for dataset imports.
|
||||
|
||||
### Impala cancel_query blocks private/internal hosts by default
|
||||
|
||||
The Impala engine spec's `cancel_query` issues an HTTP request from the Superset backend to the host configured on the Impala database connection. That host is now validated before the request: if it resolves to a private/internal IP range, the cancel call is refused and a warning is logged. Operators whose Impala cluster runs on an internal network can opt out by setting `IMPALA_CANCEL_QUERY_ALLOW_INTERNAL_HOSTS = True` in `superset_config.py`. This mirrors the dataset-import and webhook opt-out flags.
|
||||
### Map chart renderer and OpenStreetMap migration behavior
|
||||
|
||||
The MapLibre migration for deck.gl charts preserves saved non-Mapbox styles on
|
||||
the MapLibre-compatible path. Saved styles such as OpenStreetMap, `tile://`
|
||||
tile templates, generic HTTPS style URLs, and charts without a saved style are
|
||||
not reclassified as Mapbox during migration and do not require
|
||||
`MAPBOX_API_KEY` only because of the migration.
|
||||
|
||||
Saved true Mapbox styles whose value starts with `mapbox://` remain
|
||||
Mapbox-backed. If a Superset deployment does not configure `MAPBOX_API_KEY`,
|
||||
those saved Mapbox charts keep the existing missing-key message instead of
|
||||
silently falling back to MapLibre or another provider. In Explore, deck.gl and
|
||||
point-cluster renderer controls preserve saved Mapbox state, but the Mapbox
|
||||
choice is not available as a new working renderer without a configured key.
|
||||
|
||||
The MapLibre style choices include `Streets (OSM)`, backed by
|
||||
`https://tile.openstreetmap.org/{z}/{x}/{y}.png`. This OpenStreetMap tile
|
||||
service requires visible `© OpenStreetMap contributors` attribution and should
|
||||
be used through normal browser map tile requests and caching; it is not intended
|
||||
for bulk prefetch or offline tile downloads.
|
||||
|
||||
### Password complexity policy enabled by default
|
||||
|
||||
Superset now ships a default password-complexity policy, enforced (via Flask-AppBuilder) across self-registration, the user create/edit/reset forms, and the User REST API. The policy requires a minimum password length of 8 characters and rejects a built-in blocklist of common/guessable passwords.
|
||||
|
||||
This is enabled by default (`FAB_PASSWORD_COMPLEXITY_ENABLED = True`), so new or reset passwords that are too short or appear in the blocklist will be rejected where they were previously accepted. Existing stored passwords are unaffected until they are next changed.
|
||||
|
||||
Operators can tune or disable the policy via config:
|
||||
|
||||
- `AUTH_PASSWORD_MIN_LENGTH` — minimum length (default `8`).
|
||||
- `AUTH_PASSWORD_COMMON_BLOCKLIST` — extra passwords to reject, in addition to the built-in list.
|
||||
- `FAB_PASSWORD_COMPLEXITY_VALIDATOR` — replace with your own callable for custom rules.
|
||||
- `FAB_PASSWORD_COMPLEXITY_ENABLED = False` — disable enforcement entirely.
|
||||
|
||||
### Data uploads bounded by UPLOAD_MAX_FILE_SIZE_BYTES
|
||||
|
||||
Single data-file uploads (CSV, Excel, columnar) are now bounded by the `UPLOAD_MAX_FILE_SIZE_BYTES` config option, which defaults to `100 * 1024 * 1024` (100 MB). Files larger than this are rejected with a `413` before their contents are buffered into memory. Set `UPLOAD_MAX_FILE_SIZE_BYTES = None` to disable the check and restore unbounded uploads.
|
||||
|
||||
### Duration formatter precision
|
||||
|
||||
The `DURATION` number formatter now uses `Intl.DurationFormat` for locale-aware output. By default, sub-second fields are omitted, so values that previously displayed fractional seconds with `pretty-ms`, such as `10500` milliseconds rendering as `10.5s`, now render as `10s`.
|
||||
@@ -44,6 +113,50 @@ The embedded dashboard page now validates the origin of incoming `postMessage` e
|
||||
|
||||
Enforcement only applies when the Allowed Domains list is non-empty. If the list is empty (the default), any origin is accepted, so there is no behavior change for embeds that did not configure Allowed Domains.
|
||||
|
||||
### Default guest/async JWT secrets are rejected at startup
|
||||
|
||||
Superset already refuses to start in production (non-debug, non-testing) when `SECRET_KEY` is left at its built-in default, and when `GUEST_TOKEN_JWT_SECRET` is left at its default while `EMBEDDED_SUPERSET` is enabled. This behavior is extended to `GLOBAL_ASYNC_QUERIES_JWT_SECRET`: if the `GLOBAL_ASYNC_QUERIES` feature flag is enabled and the secret is still the publicly known default (`test-secret-change-me`), Superset logs a clear error and refuses to start.
|
||||
|
||||
As with the existing `SECRET_KEY` check, this only fails in production. In debug mode, testing mode, or under the test runner, a warning is logged instead of exiting, so local development is unaffected.
|
||||
|
||||
To resolve the error, set a strong random value in `superset_config.py`:
|
||||
|
||||
```python
|
||||
GLOBAL_ASYNC_QUERIES_JWT_SECRET = "<output of: openssl rand -base64 42>"
|
||||
```
|
||||
|
||||
The check is only active when the relevant feature is enabled, so deployments that do not use global async queries (or embedding) are not affected.
|
||||
|
||||
### Guest token revocation (opt-in)
|
||||
|
||||
Embedded guest tokens can be coarsely revoked at runtime via a new opt-in mechanism. A new config flag `GUEST_TOKEN_REVOCATION_ENABLED` (default `False`) gates the feature. When enabled, every minted guest token carries a revocation version, and tokens whose version is below the current expected version (stored in the metadata database) are rejected at validation time.
|
||||
|
||||
Bump the expected version with the new CLI command to invalidate all outstanding guest tokens:
|
||||
|
||||
```bash
|
||||
superset revoke-guest-tokens
|
||||
```
|
||||
|
||||
This change is backward compatible. The feature is off by default, and even when enabled nothing is revoked until an admin explicitly bumps the version: the expected version starts at `0`, and tokens minted before this change (which carry no version claim) are treated as version `0`. No database migration is required.
|
||||
|
||||
### Sessions are terminated when an account is disabled
|
||||
|
||||
Disabling a user account (setting `active` to `False`, via the admin UI, REST API, or CLI) now terminates that user's outstanding sessions on their next request, instead of relying on a passive check. This works for both client-side cookie sessions and server-side session stores via a per-user invalidation epoch (`user_attribute.sessions_invalidated_at`, added by a migration). The mechanism is inert for users that were never disabled (NULL epoch), so there is no behavior change for active users. Re-enabling an account and logging in again starts a fresh, valid session. The migration backfills the epoch for accounts that are already disabled at upgrade time, so re-enabling such an account does not revive a session that predates this feature.
|
||||
|
||||
### Opt-in SSH tunnel server host key verification
|
||||
|
||||
SSH tunnels can now optionally pin the expected SSH server host key as a defense-in-depth measure against man-in-the-middle attacks. paramiko's transport performs no known-hosts checking by default, so previously the SSH server's identity was not verified. This feature is opt-in and off by default; existing tunnels are unaffected.
|
||||
|
||||
- A new nullable `server_host_key` column on the `ssh_tunnels` table stores the expected host key in authorized-key form (e.g. `ssh-ed25519 AAAA...`). It is a public key and is stored in plaintext. It can be set via the SSH tunnel POST/PUT payloads (`ssh_tunnel.server_host_key`).
|
||||
- When a tunnel has `server_host_key` set, Superset connects to the SSH server, reads the host key it presents, and rejects the tunnel if it does not match.
|
||||
- A new config flag `SSH_TUNNEL_STRICT_HOST_KEY_CHECKING` (default `False`) controls fail-closed behavior. When `True`, every tunnel must declare a `server_host_key`; a tunnel without one is rejected.
|
||||
|
||||
Runbook to adopt:
|
||||
|
||||
1. Capture the SSH server's host key, e.g. `ssh-keyscan -t ed25519 ssh.example.com` (verify it out-of-band).
|
||||
2. Set that value on the tunnel's `server_host_key` (via the database/SSH tunnel API or UI payload).
|
||||
3. Optionally set `SSH_TUNNEL_STRICT_HOST_KEY_CHECKING = True` in `superset_config.py` to require host-key verification on all tunnels.
|
||||
|
||||
### Dataset import validates catalog against the target connection
|
||||
|
||||
Importing a dataset now validates the `catalog` field against the target database connection. When the connection has multi-catalog disabled (`allow_multi_catalog` off) and the dataset's catalog is not the connection's default catalog, the import fails instead of silently persisting the non-default catalog. This matches the validation already enforced on the dataset update path and prevents imported datasets from querying an unintended database.
|
||||
@@ -63,6 +176,36 @@ Both default to empty (no behavior change). They apply to both the `LOCAL_EXTENS
|
||||
|
||||
The Dynamic Group By chart customization now orders its display values according to the "Sort display control values" toggle: ascending (A–Z), descending (Z–A), or the dataset's source order when the toggle is unset. Previously the dropdown always sorted alphabetically. Existing dashboards where the toggle was never set will show options in source order instead of A–Z; open the customization and enable the toggle to restore alphabetical ordering.
|
||||
|
||||
### Selectable encryption engine for app-encrypted fields (AES-GCM)
|
||||
|
||||
App-encrypted fields (database passwords, SSH tunnel credentials, OAuth tokens, etc.) can now use authenticated **AES-GCM** encryption instead of the historical unauthenticated **AES-CBC**. A new config selects the engine for the default adapter:
|
||||
|
||||
```python
|
||||
# "aes" (AES-CBC, historical default) | "aes-gcm" (authenticated, recommended for new installs)
|
||||
SQLALCHEMY_ENCRYPTED_FIELD_ENGINE = "aes"
|
||||
```
|
||||
|
||||
**No action required / no behavior change:** the default remains `"aes"`, so existing installs are unaffected.
|
||||
|
||||
**Opting in on an existing install:** flipping the engine on a populated database without re-encrypting first will make stored secrets undecryptable, because the two ciphertext formats are not compatible. A migrator is provided. Recommended runbook:
|
||||
|
||||
1. Take a metadata-DB backup.
|
||||
2. Re-encrypt existing secrets into the new engine (the `SECRET_KEY` is unchanged):
|
||||
```bash
|
||||
superset re-encrypt-secrets --engine aes-gcm
|
||||
```
|
||||
3. Set `SQLALCHEMY_ENCRYPTED_FIELD_ENGINE = "aes-gcm"` in your config.
|
||||
4. Restart Superset.
|
||||
5. Re-run the migrator once more after the restart:
|
||||
```bash
|
||||
superset re-encrypt-secrets --engine aes-gcm
|
||||
```
|
||||
A live instance keeps writing *new* secrets as AES-CBC during the window between step 2 and the restart in step 4; this second pass sweeps those up (it is idempotent, so already-migrated values are skipped).
|
||||
|
||||
Schedule the cutover in a quiet window. Runtime reads use only the single configured engine, so in a multi-worker deployment there is an unavoidable brief decrypt-outage between the migration commit and the last worker restarting with the new config — each migrator run is transactional, but the fleet-wide cutover is not zero-downtime.
|
||||
|
||||
The migration is transactional (all-or-nothing) and idempotent — it can be safely re-run or resumed. Note that AES-GCM, unlike AES-CBC, does not support querying directly over encrypted columns; audit any code that filters on an encrypted column before switching. See the SIP at `docs/sip/authenticated-encryption-at-rest.md` for details.
|
||||
|
||||
### Granular Export Controls
|
||||
|
||||
A new feature flag `GRANULAR_EXPORT_CONTROLS` introduces three fine-grained permissions that replace the legacy `can_csv` permission:
|
||||
@@ -92,6 +235,9 @@ Added a new combined datasource list endpoint at `GET /api/v1/datasource/` to se
|
||||
- The endpoint is available to users with at least one of `can_read` on `Dataset` or `SemanticView`.
|
||||
- Semantic views are included only when the `SEMANTIC_LAYERS` feature flag is enabled.
|
||||
- The endpoint enforces strict `order_column` validation and returns `400` for invalid sort columns.
|
||||
|
||||
## 6.1.0
|
||||
|
||||
### ClickHouse minimum driver version bump
|
||||
|
||||
The minimum required version of `clickhouse-connect` has been raised to `>=0.13.0`. If you are using the ClickHouse connector, please upgrade your `clickhouse-connect` package. The `_mutate_label` workaround that appended hash suffixes to column aliases has also been removed, as it is no longer needed with modern versions of the driver.
|
||||
|
||||
@@ -72,20 +72,23 @@ services:
|
||||
- -c
|
||||
- |
|
||||
url="http://host.docker.internal:9000/static/assets/manifest.json"
|
||||
max_attempts=150 # ~5 minutes at 2s intervals
|
||||
echo "Waiting for webpack dev server at $url..."
|
||||
max_attempts=300 # ~10 minutes at 2s intervals; first build can be slow
|
||||
echo "Waiting for webpack dev server at $$url..."
|
||||
attempt=0
|
||||
until curl -sf --max-time 5 -o /dev/null "$url"; do
|
||||
attempt=$((attempt + 1))
|
||||
if [ "$attempt" -ge "$max_attempts" ]; then
|
||||
echo "ERROR: webpack dev server did not serve $url after $max_attempts attempts (~5 minutes)." >&2
|
||||
until curl -sf --max-time 5 -H "Host: localhost" -o /dev/null "$$url"; do
|
||||
attempt=$$((attempt + 1))
|
||||
if [ "$$attempt" -ge "$$max_attempts" ]; then
|
||||
echo "ERROR: webpack dev server did not serve $$url after $$max_attempts attempts." >&2
|
||||
echo "Is the dev server running? With BUILD_SUPERSET_FRONTEND_IN_DOCKER=false you must start it on the host (e.g. 'npm run dev' in superset-frontend)." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ $$((attempt % 15)) -eq 0 ]; then
|
||||
echo "Still waiting for webpack dev server... ($$attempt/$$max_attempts)"
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
echo "Webpack dev server is ready; starting nginx."
|
||||
exec nginx -g 'daemon off;'
|
||||
exec /docker-entrypoint.sh nginx -g 'daemon off;'
|
||||
|
||||
redis:
|
||||
image: redis:7
|
||||
|
||||
@@ -71,27 +71,29 @@ case "${1}" in
|
||||
worker)
|
||||
echo "Starting Celery worker..."
|
||||
# setting up only 2 workers by default to contain memory usage in dev environments
|
||||
celery --app=superset.tasks.celery_app:app worker -O fair -l INFO --concurrency=${CELERYD_CONCURRENCY:-2}
|
||||
celery --app=superset.tasks.celery_app:app worker -O fair -l INFO --concurrency=${CELERYD_CONCURRENCY:-2} ${WORKER_LOG_FILE:+--logfile=$WORKER_LOG_FILE}
|
||||
;;
|
||||
beat)
|
||||
echo "Starting Celery beat..."
|
||||
rm -f /tmp/celerybeat.pid
|
||||
celery --app=superset.tasks.celery_app:app beat --pidfile /tmp/celerybeat.pid -l INFO -s "${SUPERSET_HOME}"/celerybeat-schedule
|
||||
celery --app=superset.tasks.celery_app:app beat --pidfile /tmp/celerybeat.pid -l INFO -s "${SUPERSET_HOME}"/celerybeat-schedule ${BEAT_LOG_FILE:+--logfile=$BEAT_LOG_FILE}
|
||||
;;
|
||||
app)
|
||||
echo "Starting web app (using development server)..."
|
||||
|
||||
# Environment-based debugger control for security
|
||||
# Only enable Werkzeug interactive debugger when explicitly requested
|
||||
# Modern Werkzeug (3.0+) includes PIN protection, but defense-in-depth approach
|
||||
# Override FLASK_DEBUG so the effective state matches SUPERSET_DEBUG_ENABLED even
|
||||
# when FLASK_DEBUG=true is inherited from docker/.env or .flaskenv
|
||||
# Default to Flask debug mode in this dev compose entrypoint so the Talisman
|
||||
# dev CSP (which permits 'unsafe-eval' required by React Refresh / HMR) is
|
||||
# served. Operators can still set FLASK_DEBUG=false in docker/.env-local
|
||||
# to exercise the production-like CSP and error handling.
|
||||
: "${FLASK_DEBUG:=1}"
|
||||
export FLASK_DEBUG
|
||||
|
||||
# Werkzeug's interactive debugger (/console) is a separate, security-sensitive
|
||||
# feature and must be opted into explicitly via SUPERSET_DEBUG_ENABLED=true.
|
||||
if [[ "${SUPERSET_DEBUG_ENABLED:-}" == "true" ]]; then
|
||||
export FLASK_DEBUG=1
|
||||
DEBUGGER_FLAG="--debugger"
|
||||
echo " ⚠️ Werkzeug debugger enabled (requires PIN for /console access)"
|
||||
else
|
||||
export FLASK_DEBUG=0
|
||||
DEBUGGER_FLAG="--no-debugger"
|
||||
echo " 🔒 Werkzeug debugger disabled (set SUPERSET_DEBUG_ENABLED=true to enable)"
|
||||
fi
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#
|
||||
HYPHEN_SYMBOL='-'
|
||||
|
||||
gunicorn \
|
||||
exec gunicorn \
|
||||
--bind "${SUPERSET_BIND_ADDRESS:-0.0.0.0}:${SUPERSET_PORT:-8088}" \
|
||||
--access-logfile "${ACCESS_LOG_FILE:-$HYPHEN_SYMBOL}" \
|
||||
--error-logfile "${ERROR_LOG_FILE:-$HYPHEN_SYMBOL}" \
|
||||
|
||||
@@ -1 +1 @@
|
||||
v22.22.0
|
||||
v24.16.0
|
||||
|
||||
@@ -455,6 +455,51 @@ def FLASK_APP_MUTATOR(app: Flask) -> None:
|
||||
app.before_request_funcs.setdefault(None, []).append(make_session_permanent)
|
||||
```
|
||||
|
||||
## Customizing the landing page (index view)
|
||||
|
||||
The page served at `/` is rendered by an index view. By default Superset registers
|
||||
`SupersetIndexView`, which redirects to `/superset/welcome/` and also adds the
|
||||
`/lang/<locale>` locale handler. You can replace it with your own view, for example
|
||||
to send users straight to a specific dashboard or to a chart list.
|
||||
|
||||
Set `FAB_INDEX_VIEW` to the **importable dotted path** of your view class. Flask-AppBuilder
|
||||
resolves this during app initialization and uses it in place of the default:
|
||||
|
||||
```python
|
||||
# my_overrides.py — must be importable on the PYTHONPATH
|
||||
from flask import redirect
|
||||
from superset.initialization import SupersetIndexView
|
||||
from superset.superset_typing import FlaskResponse
|
||||
from flask_appbuilder import expose
|
||||
|
||||
|
||||
class MyIndexView(SupersetIndexView):
|
||||
@expose("/")
|
||||
def index(self) -> FlaskResponse:
|
||||
return redirect("/chart/list/")
|
||||
```
|
||||
|
||||
```python
|
||||
# superset_config.py
|
||||
FAB_INDEX_VIEW = "my_overrides.MyIndexView"
|
||||
```
|
||||
|
||||
A few things that commonly trip people up:
|
||||
|
||||
- **Subclass `SupersetIndexView`, not Flask-AppBuilder's bare `IndexView`.** Subclassing
|
||||
keeps Superset's `/lang/<locale>` locale handling; replacing it with a bare `IndexView`
|
||||
silently drops that behavior.
|
||||
- **The class must be importable as a real module.** `FAB_INDEX_VIEW` is resolved by
|
||||
importing the dotted path, which is independent of how `superset_config.py` itself is
|
||||
loaded. Superset only copies **uppercase** names out of `superset_config.py` into its
|
||||
runtime config, so a `FAB_INDEX_VIEW = "superset_config.MyIndexView"` reference only works
|
||||
if `superset_config` is itself importable by that name on the `PYTHONPATH`. If you load
|
||||
config via `SUPERSET_CONFIG_PATH` (an arbitrary file path), put the view in a separate
|
||||
importable module instead and reference that module.
|
||||
- **Don't set `appbuilder.indexview` from `FLASK_APP_MUTATOR`.** The mutator runs after
|
||||
routes are already registered, so the assignment has no effect on the `/` route. Use
|
||||
`FAB_INDEX_VIEW` instead.
|
||||
|
||||
## Feature Flags
|
||||
|
||||
To support a diverse set of users, Superset has some features that are not enabled by default. For
|
||||
|
||||
@@ -22,31 +22,24 @@ level dependencies.
|
||||
|
||||
**Debian and Ubuntu**
|
||||
|
||||
Ubuntu **24.04** uses python 3.12 per default, which currently is not supported by Superset. You need to add a second python installation of 3.11 and install the required additional dependencies.
|
||||
```bash
|
||||
sudo add-apt-repository ppa:deadsnakes/ppa
|
||||
sudo apt update
|
||||
sudo apt install python3.11 python3.11-dev python3.11-venv build-essential libssl-dev libffi-dev libsasl2-dev libldap2-dev default-libmysqlclient-dev
|
||||
```
|
||||
|
||||
In Ubuntu **20.04 and 22.04** the following command will ensure that the required dependencies are installed:
|
||||
The following command will ensure that the required dependencies are installed (tested on Ubuntu 20.04, 22.04, and 24.04):
|
||||
|
||||
```bash
|
||||
sudo apt-get install build-essential libssl-dev libffi-dev python3-dev python3-pip libsasl2-dev libldap2-dev default-libmysqlclient-dev
|
||||
sudo apt-get install build-essential libssl-dev libffi-dev python3-dev python3-pip python3-venv libsasl2-dev libldap2-dev libpq-dev default-libmysqlclient-dev pkg-config
|
||||
```
|
||||
|
||||
In Ubuntu **before 20.04** the following command will ensure that the required dependencies are installed:
|
||||
|
||||
```bash
|
||||
sudo apt-get install build-essential libssl-dev libffi-dev python-dev python-pip libsasl2-dev libldap2-dev default-libmysqlclient-dev
|
||||
```
|
||||
Refer to the
|
||||
[pyproject.toml](https://github.com/apache/superset/blob/master/pyproject.toml) file for the list of
|
||||
Python versions officially supported by Superset, and install a matching `python3` interpreter for
|
||||
your distribution. The `libpq-dev` package is only needed if you intend to connect to (or use) a
|
||||
PostgreSQL database; you can omit it otherwise.
|
||||
|
||||
**Fedora and RHEL-derivative Linux distributions**
|
||||
|
||||
Install the following packages using the `yum` package manager:
|
||||
|
||||
```bash
|
||||
sudo yum install gcc gcc-c++ libffi-devel python-devel python-pip python-wheel openssl-devel cyrus-sasl-devel openldap-devel
|
||||
sudo yum install gcc gcc-c++ libffi-devel python3-devel python3-pip python3-wheel openssl-devel cyrus-sasl-devel openldap-devel
|
||||
```
|
||||
|
||||
In more recent versions of CentOS and Fedora, you may need to install a slightly different set of packages using `dnf`:
|
||||
|
||||
@@ -161,6 +161,7 @@ Here's the documentation section how how to set up Talisman: https://superset.ap
|
||||
|
||||
- [ ] Regularly update to the latest major or minor versions of Superset. Those versions receive up-to-date security patches.
|
||||
- [ ] Rotate the `SUPERSET_SECRET_KEY` periodically (e.g., quarterly) and after any potential security incident.
|
||||
- [ ] Rotate the other security-critical secrets (guest-token and async-query JWT secrets, SMTP and database credentials) on the cadence in Appendix C, and after any potential security incident.
|
||||
- [ ] Conduct quarterly access reviews for all users.
|
||||
- [ ] Assuming logging and monitoring is in place, review security monitoring alerts weekly.
|
||||
|
||||
@@ -173,6 +174,24 @@ Rotating the `SUPERSET_SECRET_KEY` is a critical security procedure. It is manda
|
||||
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/admin-docs/configuration/configuring-superset/#rotating-to-a-newer-secret_key
|
||||
|
||||
### **Appendix C: Secrets Register and Rotation Schedule**
|
||||
|
||||
`SUPERSET_SECRET_KEY` is not the only security-critical secret in a Superset deployment. Maintain an inventory of all such secrets, store each in a secrets manager (not in `superset_config.py` or version control), assign an owner, and rotate them on a defined cadence as well as after any suspected compromise.
|
||||
|
||||
| Secret | Purpose | Risk if leaked | Suggested rotation |
|
||||
|---|---|---|---|
|
||||
| `SUPERSET_SECRET_KEY` | Signs session cookies; key material for encrypting stored DB credentials (Fernet/AES) | Forged sessions (auth bypass / privilege escalation); decryption of exfiltrated metadata-DB secrets | Quarterly + post-incident |
|
||||
| `GUEST_TOKEN_JWT_SECRET` | Signs embedded-dashboard guest tokens | Forged guest tokens → unauthorized dashboard/data access | Quarterly + post-incident |
|
||||
| `GLOBAL_ASYNC_QUERIES_JWT_SECRET` | Signs the async-query channel JWT | Forged async-query tokens | Quarterly + post-incident |
|
||||
| SMTP password | Outbound email for alerts & reports | Email relay abuse / spoofing | Per organizational policy + post-incident |
|
||||
| Database connection passwords | Access to analytical databases and the metadata DB | Direct database access | Per organizational policy + post-incident |
|
||||
|
||||
Notes:
|
||||
|
||||
- Rotating `GUEST_TOKEN_JWT_SECRET` or `GLOBAL_ASYNC_QUERIES_JWT_SECRET` invalidates outstanding tokens of that type; schedule rotations accordingly.
|
||||
- After a suspected compromise, rotate **all** of the above, not only `SUPERSET_SECRET_KEY`.
|
||||
- Keep the register under change control so new secrets introduced by future features are added to the rotation schedule.
|
||||
|
||||
:::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/)
|
||||
|
||||
@@ -28,14 +28,19 @@
|
||||
# Skip builds when no docs changes (exit 0 = skip, non-zero = build).
|
||||
# Checks for changes in docs/ and README.md (which gets pulled into docs).
|
||||
#
|
||||
# $CACHED_COMMIT_REF is the last *deployed* commit. On a PR's first build it
|
||||
# is empty, so the original `git diff` errored and Netlify fell back to
|
||||
# building -- which is why every PR built a docs preview once even with no
|
||||
# docs changes. When it is empty we instead diff the whole branch against its
|
||||
# merge-base with master, so non-docs PRs are skipped from the very first
|
||||
# build. Subsequent builds (and the master production build) keep the cheaper
|
||||
# incremental $CACHED_COMMIT_REF diff. Any failure exits non-zero -> build.
|
||||
ignore = 'if [ -n "$CACHED_COMMIT_REF" ]; then git diff --quiet "$CACHED_COMMIT_REF" "$COMMIT_REF" -- . ../README.md; else git fetch origin master --depth=100 >/dev/null 2>&1; git diff --quiet "$(git merge-base origin/master "$COMMIT_REF" 2>/dev/null || echo origin/master)" "$COMMIT_REF" -- . ../README.md; fi'
|
||||
# $CACHED_COMMIT_REF is the last *deployed* commit; it is set on incremental
|
||||
# builds (notably the master production deploy) and empty on a context's
|
||||
# first build (every deploy preview). The production path diffs against it
|
||||
# and skips correctly.
|
||||
#
|
||||
# Deploy previews need different handling: Netlify checks out a *merge*
|
||||
# commit, so $COMMIT_REF (the PR head SHA) is frequently not resolvable in
|
||||
# the clone, and on a shallow clone `git merge-base` can fail too -- so the
|
||||
# previous logic fell through to a build on every PR, even non-docs ones.
|
||||
# Instead, always diff the checked-out HEAD against its merge-base with
|
||||
# master, deepening the shallow clone until that merge-base resolves. If it
|
||||
# genuinely can't be determined, exit non-zero to build (fail safe).
|
||||
ignore = 'if [ -n "$CACHED_COMMIT_REF" ]; then git diff --quiet "$CACHED_COMMIT_REF" HEAD -- . ../README.md; else git fetch --no-tags origin master >/dev/null 2>&1 || true; i=0; while [ "$i" -lt 10 ] && ! git merge-base origin/master HEAD >/dev/null 2>&1; do git fetch --deepen=200 origin master >/dev/null 2>&1 || break; i=$((i+1)); done; BASE="$(git merge-base origin/master HEAD 2>/dev/null || true)"; if [ -z "$BASE" ]; then exit 1; fi; git diff --quiet "$BASE" HEAD -- . ../README.md; fi'
|
||||
|
||||
[build.environment]
|
||||
# Node version matching docs/.nvmrc
|
||||
|
||||
@@ -70,13 +70,13 @@
|
||||
"@storybook/preview-api": "^8.6.18",
|
||||
"@storybook/theming": "^8.6.15",
|
||||
"@superset-ui/core": "^0.20.4",
|
||||
"@swc/core": "^1.15.40",
|
||||
"antd": "^6.4.3",
|
||||
"baseline-browser-mapping": "^2.10.32",
|
||||
"caniuse-lite": "^1.0.30001793",
|
||||
"@swc/core": "^1.15.41",
|
||||
"antd": "^6.4.4",
|
||||
"baseline-browser-mapping": "^2.10.38",
|
||||
"caniuse-lite": "^1.0.30001799",
|
||||
"docusaurus-plugin-openapi-docs": "^5.0.2",
|
||||
"docusaurus-theme-openapi-docs": "^5.0.2",
|
||||
"js-yaml": "^4.1.1",
|
||||
"js-yaml": "^4.2.0",
|
||||
"js-yaml-loader": "^1.2.2",
|
||||
"json-bigint": "^1.0.0",
|
||||
"prism-react-renderer": "^2.4.1",
|
||||
@@ -101,15 +101,15 @@
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/react": "^19.1.8",
|
||||
"@typescript-eslint/eslint-plugin": "^8.59.3",
|
||||
"@typescript-eslint/parser": "^8.60.0",
|
||||
"@typescript-eslint/parser": "^8.61.0",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.5.6",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"globals": "^17.6.0",
|
||||
"prettier": "^3.8.3",
|
||||
"prettier": "^3.8.4",
|
||||
"typescript": "~6.0.3",
|
||||
"typescript-eslint": "^8.60.0",
|
||||
"typescript-eslint": "^8.61.1",
|
||||
"webpack": "^5.107.2"
|
||||
},
|
||||
"browserslist": {
|
||||
|
||||
136
docs/sip/authenticated-encryption-at-rest.md
Normal file
136
docs/sip/authenticated-encryption-at-rest.md
Normal file
@@ -0,0 +1,136 @@
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
|
||||
# SIP: Authenticated encryption (AES-GCM) for app-encrypted fields
|
||||
|
||||
## [DRAFT — proposal for discussion]
|
||||
|
||||
This document is a draft proposal accompanying the code in this PR. It is
|
||||
intended to seed the formal SIP discussion. The code here ships the
|
||||
backward-compatible engine selection **and** the re-encryption migrator
|
||||
(Phases 1–2 below); both are opt-in and change nothing for existing installs by
|
||||
default. Flipping the default for fresh installs (Phase 3) remains future work.
|
||||
|
||||
## Motivation
|
||||
|
||||
Superset app-encrypts a number of sensitive fields before persisting them to
|
||||
the metadata database, including:
|
||||
|
||||
- database connection passwords and `encrypted_extra` (`superset/models/core.py`),
|
||||
- SSH tunnel credentials — password, private key, private-key password
|
||||
(`superset/databases/ssh_tunnel/models.py`),
|
||||
- OAuth2 tokens and other secrets stored via `EncryptedType`.
|
||||
|
||||
These fields are encrypted with `sqlalchemy_utils.EncryptedType`, which
|
||||
**defaults to `AesEngine` (AES-CBC)**. AES-CBC provides confidentiality but is
|
||||
**unauthenticated**: it has no integrity tag. An attacker with write access to
|
||||
the ciphertext (e.g. direct metadata-DB access, a backup, or a compromised
|
||||
replica) can perform **bit-flipping / chosen-ciphertext manipulation** to
|
||||
silently alter the decrypted plaintext of a secret without detection.
|
||||
|
||||
`AesGcmEngine` (AES-GCM) is authenticated encryption: tampering causes
|
||||
decryption to fail loudly rather than yielding attacker-influenced plaintext.
|
||||
Using authenticated encryption for secrets at rest is an ASVS L1 expectation
|
||||
(11.3.2 / cryptography best practice).
|
||||
|
||||
`config.py` already documents that operators *can* switch to GCM by writing a
|
||||
custom `AbstractEncryptedFieldAdapter`, but:
|
||||
|
||||
1. it is opt-in, undocumented as a security recommendation, and easy to miss;
|
||||
2. there is **no migration path** — flipping the engine on a populated database
|
||||
makes every existing secret undecryptable, because GCM ciphertext is not
|
||||
format-compatible with CBC.
|
||||
|
||||
## Proposed change
|
||||
|
||||
A three-part change, delivered incrementally so existing deployments are never
|
||||
broken:
|
||||
|
||||
### Phase 1 — engine selection (this PR)
|
||||
|
||||
- Add a `SQLALCHEMY_ENCRYPTED_FIELD_ENGINE` config (`"aes"` | `"aes-gcm"`),
|
||||
**defaulting to `"aes"`** (no behavior change for existing installs).
|
||||
- Teach the default `SQLAlchemyUtilsAdapter` to honor it (an explicit `engine`
|
||||
kwarg still wins, so the migrator can pin an engine).
|
||||
- This lets **new** deployments choose AES-GCM from day one with a one-line
|
||||
config, instead of writing a custom adapter.
|
||||
|
||||
### Phase 2 — CBC→GCM re-encryption migrator (this PR)
|
||||
|
||||
The existing `SecretsMigrator` (previously only used for `SECRET_KEY` rotation)
|
||||
gains an **engine migration** mode that:
|
||||
|
||||
1. discovers every `EncryptedType` column (via `discover_encrypted_fields()`),
|
||||
2. decrypts each value with the **source** engine (AES-CBC) under the current
|
||||
`SECRET_KEY`,
|
||||
3. re-encrypts with the **target** engine (AES-GCM),
|
||||
4. runs transactionally per the existing all-or-nothing semantics, and is
|
||||
idempotent per column (already-migrated values are skipped), so a run can be
|
||||
safely repeated or resumed.
|
||||
|
||||
Exposed via a new `--engine` option on the existing CLI command:
|
||||
`superset re-encrypt-secrets --engine aes-gcm`, runnable by operators with a DB
|
||||
backup in hand. The `SECRET_KEY` is unchanged; an engine change and a key
|
||||
rotation can also be combined (pass `--previous_secret_key` as well).
|
||||
|
||||
### Phase 3 — flip the default for new installs
|
||||
|
||||
Once the migrator and docs are in place, change the default to `"aes-gcm"` for
|
||||
**fresh** installs only (e.g. keyed off an empty metadata DB / documented in
|
||||
`UPDATING.md`), keeping existing installs on `"aes"` until they run Phase 2.
|
||||
|
||||
## New or changed public interfaces
|
||||
|
||||
- New config: `SQLALCHEMY_ENCRYPTED_FIELD_ENGINE: Literal["aes", "aes-gcm"]`.
|
||||
- New (Phase 2) CLI: `superset re-encrypt-secrets --engine <name>`.
|
||||
- No schema changes; ciphertext format changes per migrated column.
|
||||
|
||||
## Migration plan and compatibility
|
||||
|
||||
- **Backward compatible by default.** Phase 1 changes nothing unless the
|
||||
operator opts in.
|
||||
- Switching an existing deployment to `"aes-gcm"` **without** running the Phase
|
||||
2 migrator will make existing secrets undecryptable — this is called out in
|
||||
the config comment and must be in `UPDATING.md`.
|
||||
- Recommended operator runbook: take a metadata-DB backup → run
|
||||
`re-encrypt-secrets --engine aes-gcm` → set
|
||||
`SQLALCHEMY_ENCRYPTED_FIELD_ENGINE = "aes-gcm"` → restart → re-run
|
||||
`re-encrypt-secrets --engine aes-gcm` once more to sweep up any secrets a live
|
||||
instance wrote as AES-CBC during the cutover window. The canonical, more
|
||||
detailed version of this runbook lives in `UPDATING.md`; this is a summary.
|
||||
- `AesEngine` allows queryability over encrypted fields; AES-GCM does not.
|
||||
Any code that filters/queries on an encrypted column directly must be audited
|
||||
before Phase 3 (none is expected, but it must be verified).
|
||||
|
||||
## Rejected alternatives
|
||||
|
||||
- **Flip the default immediately.** Rejected: bricks every existing
|
||||
deployment's secrets with no migration path.
|
||||
- **Document-only (custom adapter).** Status quo; high friction and no
|
||||
migration tooling — most operators will never do it.
|
||||
|
||||
## Open questions
|
||||
|
||||
- GCM→CBC rollback (for operators who need queryability) already works via the
|
||||
same command (`re-encrypt-secrets --engine aes`), since the migrator is
|
||||
engine-symmetric. Should rollback be documented as a supported path or
|
||||
discouraged?
|
||||
- The migrator already supports a concurrent `SECRET_KEY` rotation + engine
|
||||
change in a single pass (pass `--previous_secret_key` alongside `--engine`).
|
||||
Is that combination worth calling out in the operator docs, or kept advanced?
|
||||
@@ -7235,10 +7235,10 @@
|
||||
"pypi_packages": [
|
||||
"oracledb"
|
||||
],
|
||||
"connection_string": "oracle://{username}:{password}@{hostname}:{port}",
|
||||
"connection_string": "oracle+oracledb://{username}:{password}@{hostname}:{port}",
|
||||
"default_port": 1521,
|
||||
"notes": "Previously used cx_Oracle, now uses oracledb.",
|
||||
"docs_url": "https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html",
|
||||
"docs_url": "https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html",
|
||||
"category": "Other Databases"
|
||||
},
|
||||
"engine": "oracle",
|
||||
|
||||
@@ -291,6 +291,12 @@ a > span > svg {
|
||||
.footer__social-links img {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
/* The brand SVGs ship in their native colors (e.g. Slack's dark aubergine,
|
||||
X's near-black), which disappear on the dark footer. Render them all as
|
||||
uniform white silhouettes. The icons are single-path glyphs whose
|
||||
counters (the LinkedIn "in", Slack gaps, Reddit face) are transparent
|
||||
cut-outs, so they stay legible against the footer background. */
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
|
||||
.footer__ci-services {
|
||||
|
||||
@@ -519,6 +519,104 @@ For a connection to a SQL endpoint you need to use the HTTP path from the endpoi
|
||||
{"connect_args": {"http_path": "/sql/1.0/endpoints/****", "driver_path": "/path/to/odbc/driver"}}
|
||||
```
|
||||
|
||||
##### OAuth2 Authentication
|
||||
|
||||
Superset supports OAuth2 authentication for Databricks, allowing users to authenticate with their personal Databricks accounts instead of using shared access tokens. This provides better security and audit capabilities.
|
||||
|
||||
###### Prerequisites
|
||||
|
||||
1. Create an OAuth2 application in your Databricks account:
|
||||
- Go to your Databricks account console
|
||||
- Navigate to **Settings** → **Developer** → **OAuth apps**
|
||||
- Create a new OAuth app with the redirect URI: `http://your-superset-host:port/api/v1/database/oauth2/`
|
||||
|
||||
2. Configure OAuth2 in your `superset_config.py`:
|
||||
|
||||
```python
|
||||
from datetime import timedelta
|
||||
|
||||
# OAuth2 configuration for Databricks
|
||||
# OAuth2 endpoints are automatically detected based on your Databricks cloud provider
|
||||
DATABASE_OAUTH2_CLIENTS = {
|
||||
"Databricks (legacy)": {
|
||||
"id": "your-databricks-client-id",
|
||||
"secret": "your-databricks-client-secret",
|
||||
"scope": "sql",
|
||||
# The authorization endpoint is auto-detected from the hostname; the
|
||||
# token endpoint must be set explicitly (no DB context at exchange):
|
||||
# AWS: "authorization_request_uri": "https://accounts.cloud.databricks.com/oidc/accounts/{account_id}/v1/authorize",
|
||||
# Azure: "authorization_request_uri": "https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/authorize",
|
||||
# GCP: "authorization_request_uri": "https://accounts.gcp.databricks.com/oidc/accounts/{account_id}/v1/authorize",
|
||||
# "token_request_uri": "https://<provider-token-endpoint>",
|
||||
},
|
||||
"Databricks": {
|
||||
"id": "your-databricks-client-id",
|
||||
"secret": "your-databricks-client-secret",
|
||||
"scope": "sql",
|
||||
# Authorization endpoint auto-detected from hostname; set
|
||||
# "token_request_uri" explicitly for the token exchange.
|
||||
},
|
||||
}
|
||||
|
||||
# OAuth2 redirect URI (adjust hostname/port for your setup)
|
||||
DATABASE_OAUTH2_REDIRECT_URI = "http://your-superset-host:port/api/v1/database/oauth2/"
|
||||
|
||||
# Optional: OAuth2 timeout
|
||||
DATABASE_OAUTH2_TIMEOUT = timedelta(seconds=30)
|
||||
```
|
||||
|
||||
Replace the following placeholders:
|
||||
- `your-databricks-client-id`: Your Databricks OAuth2 application client ID
|
||||
- `your-databricks-client-secret`: Your Databricks OAuth2 application client secret
|
||||
- `your-superset-host:port`: Your Superset instance hostname and port
|
||||
|
||||
**Multi-Cloud Provider Support**
|
||||
|
||||
Superset automatically detects your Databricks cloud provider and uses the appropriate OAuth2 endpoints:
|
||||
|
||||
- **AWS**: Detected from hostnames containing `cloud.databricks.com`
|
||||
- **Azure**: Detected from hostnames containing `azure` or `azuredatabricks`
|
||||
- **GCP**: Detected from hostnames containing `gcp` or `googleusercontent`
|
||||
|
||||
You can also explicitly specify the cloud provider, along with the account
|
||||
identifier used to build the OAuth2 endpoints, in your database configuration
|
||||
under **Advanced** → **Other** → **ENGINE PARAMETERS**:
|
||||
|
||||
```json
|
||||
{
|
||||
"cloud_provider": "azure",
|
||||
"tenant_id": "your-azure-tenant-id"
|
||||
}
|
||||
```
|
||||
|
||||
For AWS and GCP, supply `account_id` instead:
|
||||
|
||||
```json
|
||||
{
|
||||
"cloud_provider": "aws",
|
||||
"account_id": "your-databricks-account-id"
|
||||
}
|
||||
```
|
||||
|
||||
Valid cloud provider values are: `aws`, `azure`, `gcp`. The **authorization**
|
||||
endpoint is auto-detected: Superset substitutes this identifier into the
|
||||
provider's authorization template. The **token** endpoint is not auto-resolved
|
||||
(token exchange has no database context to detect the provider), so for the
|
||||
auto-detected flow you must still supply a fully-resolved `token_request_uri`
|
||||
in `DATABASE_OAUTH2_CLIENTS`. If you supply fully-resolved
|
||||
`authorization_request_uri` and `token_request_uri` values, those take
|
||||
precedence and no `account_id`/`tenant_id` is required.
|
||||
|
||||
###### Usage
|
||||
|
||||
Once configured, users can:
|
||||
|
||||
1. Connect to Databricks databases normally using access tokens
|
||||
2. When querying data, Superset will automatically redirect users to authenticate with Databricks if needed
|
||||
3. User-specific OAuth2 tokens will be used for database connections, providing better security and audit trails
|
||||
|
||||
This feature works with both "Databricks (legacy)" and "Databricks" engine types and automatically supports all major cloud providers (AWS, Azure, GCP).
|
||||
|
||||
#### Denodo
|
||||
|
||||
The recommended connector library for Denodo is
|
||||
@@ -1808,6 +1906,10 @@ If you enable DML in the meta database users will be able to run DML queries on
|
||||
|
||||
Second, you might want to change the value of `SUPERSET_META_DB_LIMIT`. The default value is 1000, and defines how many are read from each database before any aggregations and joins are executed. You can also set this value `None` if you only have small tables.
|
||||
|
||||
:::warning
|
||||
`SUPERSET_META_DB_LIMIT` is applied to **each** underlying table *before* the in-memory join runs, not to the final result. If any table involved in a join has more rows than the limit, the meta database will read only the first `SUPERSET_META_DB_LIMIT` rows of that table, which means matching rows can be silently dropped and the join can return **incomplete or even empty** results with no error. If you join tables larger than the limit, raise `SUPERSET_META_DB_LIMIT` to comfortably exceed your largest joined table, or set it to `None` when working only with small tables, to get correct results.
|
||||
:::
|
||||
|
||||
Additionally, you might want to restrict the databases to with the meta database has access to. This can be done in the database configuration, under "Advanced" -> "Other" -> "ENGINE PARAMETERS" and adding:
|
||||
|
||||
```json
|
||||
|
||||
730
docs/yarn.lock
730
docs/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
apiVersion: v2
|
||||
appVersion: "5.0.0"
|
||||
appVersion: "6.1.0"
|
||||
description: Apache Superset is a modern, enterprise-ready business intelligence web application
|
||||
name: superset
|
||||
icon: https://artifacthub.io/image/68c1d717-0e97-491f-b046-754e46f46922@2x
|
||||
@@ -29,7 +29,7 @@ maintainers:
|
||||
- name: craig-rueda
|
||||
email: craig@craigrueda.com
|
||||
url: https://github.com/craig-rueda
|
||||
version: 0.15.5 # See [README](https://github.com/apache/superset/blob/master/helm/superset/README.md#versioning) for version details.
|
||||
version: 0.17.3 # See [README](https://github.com/apache/superset/blob/master/helm/superset/README.md#versioning) for version details.
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
version: 16.7.27
|
||||
|
||||
@@ -23,7 +23,7 @@ NOTE: This file is generated by helm-docs: https://github.com/norwoodj/helm-docs
|
||||
|
||||
# superset
|
||||
|
||||

|
||||

|
||||
|
||||
Apache Superset is a modern, enterprise-ready business intelligence web application
|
||||
|
||||
@@ -111,9 +111,6 @@ On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverri
|
||||
| init.resources | object | `{}` | |
|
||||
| init.tolerations | list | `[]` | |
|
||||
| init.topologySpreadConstraints | list | `[]` | TopologySpreadConstrains to be added to init job |
|
||||
| initImage.pullPolicy | string | `"IfNotPresent"` | |
|
||||
| initImage.repository | string | `"apache/superset"` | |
|
||||
| initImage.tag | string | `"dockerize"` | |
|
||||
| nameOverride | string | `nil` | Provide a name to override the name of the chart |
|
||||
| nodeSelector | object | `{}` | |
|
||||
| postgresql | object | see `values.yaml` | Configuration values for the postgresql dependency. ref: https://github.com/bitnami/charts/tree/main/bitnami/postgresql |
|
||||
@@ -219,6 +216,7 @@ On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverri
|
||||
| supersetNode.extraContainers | list | `[]` | Launch additional containers into supersetNode pod |
|
||||
| supersetNode.forceReload | bool | `false` | If true, forces deployment to reload on each upgrade |
|
||||
| supersetNode.initContainers | list | a container waiting for postgres | Init containers |
|
||||
| supersetNode.lifecycle | object | `{}` | Container lifecycle hooks, e.g. a preStop sleep so the Service/Ingress stops routing to the pod before gunicorn receives SIGTERM |
|
||||
| supersetNode.livenessProbe.failureThreshold | int | `3` | |
|
||||
| supersetNode.livenessProbe.httpGet.path | string | `"/health"` | |
|
||||
| supersetNode.livenessProbe.httpGet.port | string | `"http"` | |
|
||||
@@ -251,6 +249,7 @@ On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverri
|
||||
| supersetNode.startupProbe.successThreshold | int | `1` | |
|
||||
| supersetNode.startupProbe.timeoutSeconds | int | `1` | |
|
||||
| supersetNode.strategy | object | `{}` | |
|
||||
| supersetNode.terminationGracePeriodSeconds | string | `nil` | Pod termination grace period (seconds). Set greater than GUNICORN_TIMEOUT so in-flight requests can drain before SIGKILL |
|
||||
| supersetNode.topologySpreadConstraints | list | `[]` | TopologySpreadConstrains to be added to supersetNode deployments |
|
||||
| supersetWebsockets.affinity | object | `{}` | Affinity to be added to supersetWebsockets deployment |
|
||||
| supersetWebsockets.command | list | `[]` | |
|
||||
@@ -314,6 +313,7 @@ On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverri
|
||||
| supersetWorker.extraContainers | list | `[]` | Launch additional containers into supersetWorker pod |
|
||||
| supersetWorker.forceReload | bool | `false` | If true, forces deployment to reload on each upgrade |
|
||||
| supersetWorker.initContainers | list | a container waiting for postgres and redis | Init container |
|
||||
| supersetWorker.lifecycle | object | `{}` | Container lifecycle hooks for the worker pod |
|
||||
| supersetWorker.livenessProbe.exec.command | list | a `celery inspect ping` command | Liveness probe command |
|
||||
| supersetWorker.livenessProbe.failureThreshold | int | `3` | |
|
||||
| supersetWorker.livenessProbe.initialDelaySeconds | int | `120` | |
|
||||
@@ -334,6 +334,7 @@ On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverri
|
||||
| supersetWorker.resources | object | `{}` | Resource settings for the supersetWorker pods - these settings overwrite might existing values from the global resources object defined above. |
|
||||
| supersetWorker.startupProbe | object | `{}` | No startup/readiness probes by default since we don't really care about its startup time (it doesn't serve traffic) |
|
||||
| supersetWorker.strategy | object | `{}` | |
|
||||
| supersetWorker.terminationGracePeriodSeconds | string | `nil` | Pod termination grace period (seconds) for the worker pod so in-flight tasks can drain before SIGKILL |
|
||||
| supersetWorker.topologySpreadConstraints | list | `[]` | TopologySpreadConstrains to be added to supersetWorker deployments |
|
||||
| tolerations | list | `[]` | |
|
||||
| topologySpreadConstraints | list | `[]` | TopologySpreadConstrains to be added to all deployments |
|
||||
|
||||
@@ -108,8 +108,6 @@ else:
|
||||
{{ fail (printf "Unsupported database type: %s. Please use 'postgresql' or 'mysql'." .Values.supersetNode.connections.db_type) }}
|
||||
{{- end }}
|
||||
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||
|
||||
class CeleryConfig:
|
||||
imports = ("superset.sql_lab", )
|
||||
broker_url = CELERY_REDIS_URL
|
||||
|
||||
@@ -126,7 +126,7 @@ spec:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetCeleryBeat.extraContainers }}
|
||||
{{- toYaml .Values.supersetCeleryBeat.extraContainers | nindent 8 }}
|
||||
{{- tpl (toYaml .Values.supersetCeleryBeat.extraContainers) . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
|
||||
@@ -121,7 +121,7 @@ spec:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetCeleryFlower.extraContainers }}
|
||||
{{- toYaml .Values.supersetCeleryFlower.extraContainers | nindent 8 }}
|
||||
{{- tpl (toYaml .Values.supersetCeleryFlower.extraContainers) . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
|
||||
@@ -134,6 +134,9 @@ spec:
|
||||
{{- if .Values.supersetWorker.livenessProbe }}
|
||||
livenessProbe: {{- .Values.supersetWorker.livenessProbe | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetWorker.lifecycle }}
|
||||
lifecycle: {{- .Values.supersetWorker.lifecycle | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- if .Values.supersetWorker.resources }}
|
||||
{{- toYaml .Values.supersetWorker.resources | nindent 12 }}
|
||||
@@ -141,7 +144,7 @@ spec:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetWorker.extraContainers }}
|
||||
{{- toYaml .Values.supersetWorker.extraContainers | nindent 8 }}
|
||||
{{- tpl (toYaml .Values.supersetWorker.extraContainers) . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
@@ -170,6 +173,9 @@ spec:
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetWorker.terminationGracePeriodSeconds }}
|
||||
terminationGracePeriodSeconds: {{ .Values.supersetWorker.terminationGracePeriodSeconds }}
|
||||
{{- end }}
|
||||
{{- if .Values.imagePullSecrets }}
|
||||
imagePullSecrets: {{- toYaml .Values.imagePullSecrets | nindent 8 }}
|
||||
{{- end }}
|
||||
|
||||
@@ -120,7 +120,7 @@ spec:
|
||||
livenessProbe: {{- .Values.supersetWebsockets.livenessProbe | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetWebsockets.extraContainers }}
|
||||
{{- toYaml .Values.supersetWebsockets.extraContainers | nindent 8 }}
|
||||
{{- tpl (toYaml .Values.supersetWebsockets.extraContainers) . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
|
||||
@@ -144,6 +144,9 @@ spec:
|
||||
{{- if .Values.supersetNode.livenessProbe }}
|
||||
livenessProbe: {{- .Values.supersetNode.livenessProbe | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetNode.lifecycle }}
|
||||
lifecycle: {{- .Values.supersetNode.lifecycle | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- if .Values.supersetNode.resources }}
|
||||
{{- toYaml .Values.supersetNode.resources | nindent 12 }}
|
||||
@@ -151,7 +154,7 @@ spec:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetNode.extraContainers }}
|
||||
{{- toYaml .Values.supersetNode.extraContainers | nindent 8 }}
|
||||
{{- tpl (toYaml .Values.supersetNode.extraContainers) . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
@@ -180,6 +183,9 @@ spec:
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetNode.terminationGracePeriodSeconds }}
|
||||
terminationGracePeriodSeconds: {{ .Values.supersetNode.terminationGracePeriodSeconds }}
|
||||
{{- end }}
|
||||
{{- if .Values.imagePullSecrets }}
|
||||
imagePullSecrets: {{- toYaml .Values.imagePullSecrets | nindent 8 }}
|
||||
{{- end }}
|
||||
|
||||
@@ -62,6 +62,9 @@ spec:
|
||||
{{- if .Values.init.initContainers }}
|
||||
initContainers: {{- tpl (toYaml .Values.init.initContainers) . | nindent 6 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hostAliases }}
|
||||
hostAliases: {{- toYaml . | nindent 6 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: {{ template "superset.name" . }}-init-db
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
@@ -101,7 +104,7 @@ spec:
|
||||
command: {{ tpl (toJson .Values.init.command) . }}
|
||||
resources: {{- toYaml .Values.init.resources | nindent 10 }}
|
||||
{{- if .Values.init.extraContainers }}
|
||||
{{- toYaml .Values.init.extraContainers | nindent 6 }}
|
||||
{{- tpl (toYaml .Values.init.extraContainers) . | nindent 6 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
|
||||
@@ -194,11 +194,6 @@ image:
|
||||
|
||||
imagePullSecrets: []
|
||||
|
||||
initImage:
|
||||
repository: apache/superset
|
||||
tag: dockerize
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8088
|
||||
@@ -274,7 +269,7 @@ supersetNode:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- ". {{ .Values.configMountPath }}/superset_bootstrap.sh; /usr/bin/run-server.sh"
|
||||
- ". {{ .Values.configMountPath }}/superset_bootstrap.sh; exec /usr/bin/run-server.sh"
|
||||
connections:
|
||||
# -- Change in case of bringing your own redis and then also set redis.enabled:false
|
||||
redis_host: "{{ .Release.Name }}-redis-headless"
|
||||
@@ -303,15 +298,29 @@ supersetNode:
|
||||
# @default -- a container waiting for postgres
|
||||
initContainers:
|
||||
- name: wait-for-postgres
|
||||
image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}"
|
||||
imagePullPolicy: "{{ .Values.initImage.pullPolicy }}"
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: "{{ tpl .Values.envFromSecret . }}"
|
||||
command:
|
||||
- /bin/sh
|
||||
- /bin/bash
|
||||
- -c
|
||||
- dockerize -wait "tcp://$DB_HOST:$DB_PORT" -timeout 120s
|
||||
- |
|
||||
# opening a /dev/tcp fd performs a TCP connect without sending any
|
||||
# payload (avoids postgres "incomplete startup packet" log noise);
|
||||
# no external `dockerize`, `nc`, or busybox needed. SECONDS-based
|
||||
# deadline mirrors the prior `dockerize -timeout 120s` behaviour.
|
||||
SECONDS=0
|
||||
until (exec 3<>/dev/tcp/"$DB_HOST"/"$DB_PORT") 2>/dev/null; do
|
||||
if [ "$SECONDS" -ge 120 ]; then
|
||||
echo "timeout waiting for postgres at $DB_HOST:$DB_PORT after 120s" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "waiting for postgres at $DB_HOST:$DB_PORT (elapsed ${SECONDS}s)"
|
||||
sleep 2
|
||||
done
|
||||
echo "postgres at $DB_HOST:$DB_PORT is up"
|
||||
resources:
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
@@ -360,6 +369,12 @@ supersetNode:
|
||||
failureThreshold: 3
|
||||
periodSeconds: 15
|
||||
successThreshold: 1
|
||||
# -- Container lifecycle hooks, e.g. a preStop sleep so the Service/Ingress
|
||||
# stops routing to the pod before gunicorn receives SIGTERM
|
||||
lifecycle: {}
|
||||
# -- Pod termination grace period (seconds). Set greater than GUNICORN_TIMEOUT so
|
||||
# in-flight requests can drain before SIGKILL
|
||||
terminationGracePeriodSeconds: ~
|
||||
# -- Resource settings for the supersetNode pods - these settings overwrite might existing values from the global resources object defined above.
|
||||
resources: {}
|
||||
# limits:
|
||||
@@ -400,22 +415,38 @@ supersetWorker:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- ". {{ .Values.configMountPath }}/superset_bootstrap.sh; celery --app=superset.tasks.celery_app:app worker"
|
||||
- ". {{ .Values.configMountPath }}/superset_bootstrap.sh; exec celery --app=superset.tasks.celery_app:app worker"
|
||||
# -- If true, forces deployment to reload on each upgrade
|
||||
forceReload: false
|
||||
# -- Init container
|
||||
# @default -- a container waiting for postgres and redis
|
||||
initContainers:
|
||||
- name: wait-for-postgres-redis
|
||||
image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}"
|
||||
imagePullPolicy: "{{ .Values.initImage.pullPolicy }}"
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: "{{ tpl .Values.envFromSecret . }}"
|
||||
command:
|
||||
- /bin/sh
|
||||
- /bin/bash
|
||||
- -c
|
||||
- dockerize -wait "tcp://$DB_HOST:$DB_PORT" -wait "tcp://$REDIS_HOST:$REDIS_PORT" -timeout 120s
|
||||
- |
|
||||
# See supersetNode.initContainers for the rationale.
|
||||
SECONDS=0
|
||||
wait_for() {
|
||||
local host=$1 port=$2 name=$3
|
||||
until (exec 3<>/dev/tcp/"$host"/"$port") 2>/dev/null; do
|
||||
if [ "$SECONDS" -ge 120 ]; then
|
||||
echo "timeout waiting for $name at $host:$port after 120s" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "waiting for $name at $host:$port (elapsed ${SECONDS}s)"
|
||||
sleep 2
|
||||
done
|
||||
echo "$name at $host:$port is up"
|
||||
}
|
||||
wait_for "$DB_HOST" "$DB_PORT" postgres
|
||||
wait_for "$REDIS_HOST" "$REDIS_PORT" redis
|
||||
resources:
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
@@ -464,6 +495,10 @@ supersetWorker:
|
||||
failureThreshold: 3
|
||||
periodSeconds: 60
|
||||
successThreshold: 1
|
||||
# -- Container lifecycle hooks for the worker pod
|
||||
lifecycle: {}
|
||||
# -- Pod termination grace period (seconds) for the worker pod so in-flight tasks can drain before SIGKILL
|
||||
terminationGracePeriodSeconds: ~
|
||||
# -- No startup/readiness probes by default since we don't really care about its startup time (it doesn't serve traffic)
|
||||
startupProbe: {}
|
||||
# -- No startup/readiness probes by default since we don't really care about its startup time (it doesn't serve traffic)
|
||||
@@ -488,22 +523,38 @@ supersetCeleryBeat:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- ". {{ .Values.configMountPath }}/superset_bootstrap.sh; celery --app=superset.tasks.celery_app:app beat --pidfile /tmp/celerybeat.pid --schedule /tmp/celerybeat-schedule"
|
||||
- ". {{ .Values.configMountPath }}/superset_bootstrap.sh; exec celery --app=superset.tasks.celery_app:app beat --pidfile /tmp/celerybeat.pid --schedule /tmp/celerybeat-schedule"
|
||||
# -- If true, forces deployment to reload on each upgrade
|
||||
forceReload: false
|
||||
# -- List of init containers
|
||||
# @default -- a container waiting for postgres
|
||||
initContainers:
|
||||
- name: wait-for-postgres-redis
|
||||
image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}"
|
||||
imagePullPolicy: "{{ .Values.initImage.pullPolicy }}"
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: "{{ tpl .Values.envFromSecret . }}"
|
||||
command:
|
||||
- /bin/sh
|
||||
- /bin/bash
|
||||
- -c
|
||||
- dockerize -wait "tcp://$DB_HOST:$DB_PORT" -wait "tcp://$REDIS_HOST:$REDIS_PORT" -timeout 120s
|
||||
- |
|
||||
# See supersetNode.initContainers for the rationale.
|
||||
SECONDS=0
|
||||
wait_for() {
|
||||
local host=$1 port=$2 name=$3
|
||||
until (exec 3<>/dev/tcp/"$host"/"$port") 2>/dev/null; do
|
||||
if [ "$SECONDS" -ge 120 ]; then
|
||||
echo "timeout waiting for $name at $host:$port after 120s" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "waiting for $name at $host:$port (elapsed ${SECONDS}s)"
|
||||
sleep 2
|
||||
done
|
||||
echo "$name at $host:$port is up"
|
||||
}
|
||||
wait_for "$DB_HOST" "$DB_PORT" postgres
|
||||
wait_for "$REDIS_HOST" "$REDIS_PORT" redis
|
||||
resources:
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
@@ -594,15 +645,31 @@ supersetCeleryFlower:
|
||||
# @default -- a container waiting for postgres and redis
|
||||
initContainers:
|
||||
- name: wait-for-postgres-redis
|
||||
image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}"
|
||||
imagePullPolicy: "{{ .Values.initImage.pullPolicy }}"
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: "{{ tpl .Values.envFromSecret . }}"
|
||||
command:
|
||||
- /bin/sh
|
||||
- /bin/bash
|
||||
- -c
|
||||
- dockerize -wait "tcp://$DB_HOST:$DB_PORT" -wait "tcp://$REDIS_HOST:$REDIS_PORT" -timeout 120s
|
||||
- |
|
||||
# See supersetNode.initContainers for the rationale.
|
||||
SECONDS=0
|
||||
wait_for() {
|
||||
local host=$1 port=$2 name=$3
|
||||
until (exec 3<>/dev/tcp/"$host"/"$port") 2>/dev/null; do
|
||||
if [ "$SECONDS" -ge 120 ]; then
|
||||
echo "timeout waiting for $name at $host:$port after 120s" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "waiting for $name at $host:$port (elapsed ${SECONDS}s)"
|
||||
sleep 2
|
||||
done
|
||||
echo "$name at $host:$port is up"
|
||||
}
|
||||
wait_for "$DB_HOST" "$DB_PORT" postgres
|
||||
wait_for "$REDIS_HOST" "$REDIS_PORT" redis
|
||||
resources:
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
@@ -764,15 +831,26 @@ init:
|
||||
# @default -- a container waiting for postgres
|
||||
initContainers:
|
||||
- name: wait-for-postgres
|
||||
image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}"
|
||||
imagePullPolicy: "{{ .Values.initImage.pullPolicy }}"
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: "{{ tpl .Values.envFromSecret . }}"
|
||||
command:
|
||||
- /bin/sh
|
||||
- /bin/bash
|
||||
- -c
|
||||
- dockerize -wait "tcp://$DB_HOST:$DB_PORT" -timeout 120s
|
||||
- |
|
||||
# See supersetNode.initContainers for the rationale.
|
||||
SECONDS=0
|
||||
until (exec 3<>/dev/tcp/"$DB_HOST"/"$DB_PORT") 2>/dev/null; do
|
||||
if [ "$SECONDS" -ge 120 ]; then
|
||||
echo "timeout waiting for postgres at $DB_HOST:$DB_PORT after 120s" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "waiting for postgres at $DB_HOST:$DB_PORT (elapsed ${SECONDS}s)"
|
||||
sleep 2
|
||||
done
|
||||
echo "postgres at $DB_HOST:$DB_PORT is up"
|
||||
resources:
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
|
||||
@@ -38,14 +38,20 @@ dependencies = [
|
||||
# no bounds for apache-superset-core until we have a stable version
|
||||
"apache-superset-core",
|
||||
"backoff>=1.8.0",
|
||||
# cachetools is used directly by ``superset.db_engine_specs.aws_iam`` (TTLCache).
|
||||
# It used to be installed transitively via ``google-auth`` (<2.53), but
|
||||
# ``google-auth`` 2.53+ dropped it, so Superset must declare it
|
||||
# explicitly to keep fresh ``pip install apache-superset`` working
|
||||
# without the ``base.txt`` lock file (#40962).
|
||||
"cachetools>=6.2.1, <7",
|
||||
"celery>=5.3.6, <6.0.0",
|
||||
"click>=8.4.0",
|
||||
"click-option-group",
|
||||
"colorama",
|
||||
"flask-cors>=6.0.0, <7.0",
|
||||
"croniter>=0.3.28",
|
||||
"flask-cors>=6.0.5, <7.0",
|
||||
"croniter>=6.2.2",
|
||||
"cron-descriptor",
|
||||
"cryptography>=42.0.4, <47.0.0",
|
||||
"cryptography>=48.0.0, <49.0.0",
|
||||
"deprecation>=2.1.0, <2.2.0",
|
||||
"flask>=2.2.5, <4.0.0",
|
||||
"flask-appbuilder>=5.2.1, <6.0.0",
|
||||
@@ -53,24 +59,26 @@ dependencies = [
|
||||
"flask-compress>=1.13, <2.0",
|
||||
"flask-talisman>=1.0.0, <2.0",
|
||||
"flask-login>=0.6.0, < 1.0",
|
||||
"flask-migrate>=3.1.0, <5.0",
|
||||
"flask-migrate>=4.1.0, <5.0",
|
||||
"flask-session>=0.4.0, <1.0",
|
||||
"flask-wtf>=1.1.0, <2.0",
|
||||
"flask-wtf>=1.3.0, <2.0",
|
||||
"geopy",
|
||||
"greenlet>=3.0.3, <=3.5.0",
|
||||
"greenlet<=3.5.1, >=3.5.1",
|
||||
"gunicorn>=25.3.0, <26; sys_platform != 'win32'",
|
||||
"hashids>=1.3.1, <2",
|
||||
# holidays>=0.45 required for security fix
|
||||
"holidays>=0.45, <1",
|
||||
"humanize",
|
||||
"isodate",
|
||||
"jsonpath-ng>=1.6.1, <2",
|
||||
"jsonpath-ng>=1.8.0, <2",
|
||||
"Mako>=1.2.2",
|
||||
"markdown>=3.10.2",
|
||||
# marshmallow>=4 has issues: https://github.com/apache/superset/issues/33162
|
||||
"marshmallow>=3.0, <4",
|
||||
# marshmallow 4 compatibility: see superset/marshmallow_compatibility.py for a
|
||||
# Flask-AppBuilder workaround. Tracking issue:
|
||||
# https://github.com/apache/superset/issues/33162
|
||||
"marshmallow>=3.0, <5",
|
||||
"marshmallow-union>=0.1",
|
||||
"msgpack>=1.0.0, <1.2",
|
||||
"msgpack>=1.2.0, <1.3",
|
||||
"nh3>=0.3.5, <0.4",
|
||||
"numpy>1.23.5, <2.3",
|
||||
"packaging",
|
||||
@@ -80,7 +88,7 @@ dependencies = [
|
||||
"bottleneck", # recommended performance dependency for pandas, see https://pandas.pydata.org/docs/getting_started/install.html#performance-dependencies-recommended
|
||||
# --------------------------
|
||||
"parsedatetime",
|
||||
"paramiko>=3.4.0",
|
||||
"paramiko>=3.4.0, <4.0", # 4.0 removed DSSKey, still referenced by sshtunnel
|
||||
"pgsanity",
|
||||
"Pillow>=11.0.0, <13",
|
||||
"polyline>=2.0.0, <3.0",
|
||||
@@ -89,15 +97,15 @@ dependencies = [
|
||||
"python-dateutil",
|
||||
"python-dotenv", # optional dependencies for Flask but required for Superset, see https://flask.palletsprojects.com/en/stable/installation/#optional-dependencies
|
||||
"pygeohash",
|
||||
"pyarrow>=16.1.0, <21", # before upgrading pyarrow, check that all db dependencies support this, see e.g. https://github.com/apache/superset/pull/34693
|
||||
"pyyaml>=6.0.0, <7.0.0",
|
||||
"pyarrow>=24.0.0, <25", # before upgrading pyarrow, check that all db dependencies support this, see e.g. https://github.com/apache/superset/pull/34693
|
||||
"pyyaml>=6.0.3, <7.0.0",
|
||||
"PyJWT>=2.4.0, <3.0",
|
||||
"redis>=5.0.0, <6.0",
|
||||
"rison>=2.0.0, <3.0",
|
||||
"selenium>=4.14.0, <5.0",
|
||||
"selenium>=4.44.0, <5.0",
|
||||
"shillelagh[gsheetsapi]>=1.4.4, <2.0",
|
||||
"sshtunnel>=0.4.0, <0.5",
|
||||
"simplejson>=3.15.0",
|
||||
"simplejson>=4.1.1",
|
||||
"slack_sdk>=3.19.0, <4",
|
||||
"sqlalchemy>=1.4, <2",
|
||||
"sqlalchemy-utils>=0.38.0, <0.43", # expanding lowerbound to work with pydoris
|
||||
@@ -107,9 +115,9 @@ dependencies = [
|
||||
"typing-extensions>=4, <5",
|
||||
"waitress; sys_platform == 'win32'",
|
||||
"watchdog>=6.0.0",
|
||||
"wtforms>=2.3.3, <4",
|
||||
"wtforms>=3.2.2, <4",
|
||||
"wtforms-json",
|
||||
"xlsxwriter>=3.0.7, <3.3",
|
||||
"xlsxwriter>=3.2.9, <3.3",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
@@ -118,10 +126,10 @@ athena = ["pyathena[pandas]>=2, <4"]
|
||||
aurora-data-api = ["preset-sqlalchemy-aurora-data-api>=0.2.8,<0.3"]
|
||||
bigquery = [
|
||||
"pandas-gbq>=0.19.1",
|
||||
"sqlalchemy-bigquery>=1.15.0",
|
||||
"sqlalchemy-bigquery>=1.17.0",
|
||||
"google-cloud-bigquery>=3.10.0",
|
||||
]
|
||||
clickhouse = ["clickhouse-connect>=0.13.0, <2.0"]
|
||||
clickhouse = ["clickhouse-connect>=1.1.1, <2.0"]
|
||||
cockroachdb = ["cockroachdb>=0.3.5, <0.4"]
|
||||
crate = ["sqlalchemy-cratedb>=0.41.0, <1"]
|
||||
d1 = [
|
||||
@@ -141,49 +149,49 @@ drill = ["sqlalchemy-drill>=1.1.10, <2"]
|
||||
druid = ["pydruid>=0.6.5,<0.7"]
|
||||
duckdb = ["duckdb>=1.5.2,<2", "duckdb-engine>=0.17.0"]
|
||||
dynamodb = ["pydynamodb>=0.4.2"]
|
||||
solr = ["sqlalchemy-solr >= 0.2.0"]
|
||||
solr = ["sqlalchemy-solr >= 0.2.4.3"]
|
||||
elasticsearch = ["elasticsearch-dbapi>=0.2.13, <0.3.0"]
|
||||
exasol = ["sqlalchemy-exasol >= 2.4.0, < 8.0"]
|
||||
excel = ["xlrd>=1.2.0, <1.3"]
|
||||
exasol = ["sqlalchemy-exasol>=2.4.0, <8.0"]
|
||||
excel = ["xlrd>=2.0.2, <2.1"]
|
||||
fastmcp = [
|
||||
"fastmcp>=3.2.4,<4.0",
|
||||
# tiktoken backs the response-size-guard token estimator. Without
|
||||
# it, the middleware falls back to a coarser character-based
|
||||
# heuristic that under-counts JSON-heavy MCP responses.
|
||||
"tiktoken>=0.7.0,<1.0",
|
||||
"tiktoken>=0.13.0,<1.0",
|
||||
]
|
||||
firebird = ["sqlalchemy-firebird>=0.7.0, <2.2"]
|
||||
firebolt = ["firebolt-sqlalchemy>=1.0.0, <2"]
|
||||
gevent = ["gevent>=26.4.0"]
|
||||
gsheets = ["shillelagh[gsheetsapi]>=1.4.4, <2"]
|
||||
hana = ["hdbcli==2.28.20", "sqlalchemy_hana==0.4.0"]
|
||||
hana = ["hdbcli==2.28.21", "sqlalchemy_hana==0.4.0"]
|
||||
hive = [
|
||||
"pyhive[hive]>=0.6.5;python_version<'3.11'",
|
||||
"pyhive[hive_pure_sasl]>=0.7.0",
|
||||
"tableschema",
|
||||
"thrift>=0.14.1, <1.0.0",
|
||||
"thrift>=0.23.0, <1.0.0",
|
||||
"thrift_sasl>=0.4.3, < 1.0.0",
|
||||
]
|
||||
impala = ["impyla>0.16.2, <0.23"]
|
||||
kusto = ["sqlalchemy-kusto>=3.0.0, <4"]
|
||||
kusto = ["sqlalchemy-kusto>=3.1.2, <4"]
|
||||
kylin = ["kylinpy>=2.8.1, <2.9"]
|
||||
mssql = ["pymssql>=2.2.8, <3"]
|
||||
mssql = ["pymssql>=2.3.13, <3"]
|
||||
# motherduck is an alias for duckdb - MotherDuck works via the duckdb driver
|
||||
motherduck = ["apache-superset[duckdb]"]
|
||||
mysql = ["mysqlclient>=2.1.0, <3"]
|
||||
ocient = [
|
||||
"sqlalchemy-ocient>=1.0.0",
|
||||
"pyocient>=1.0.15, <2",
|
||||
"pyocient>=1.0.15, <4",
|
||||
"shapely",
|
||||
"geojson",
|
||||
]
|
||||
oracle = ["cx-Oracle>8.0.0, <8.4"]
|
||||
oracle = ["oracledb>=2.0.0, <5"]
|
||||
parseable = ["sqlalchemy-parseable>=0.1.3,<0.2.0"]
|
||||
pinot = ["pinotdb>=5.0.0, <10.0.0"]
|
||||
playwright = ["playwright>=1.37.0, <2"]
|
||||
playwright = ["playwright>=1.60.0, <2"]
|
||||
postgres = ["psycopg2-binary==2.9.12"]
|
||||
presto = ["pyhive[presto]>=0.6.5"]
|
||||
trino = ["trino>=0.328.0"]
|
||||
trino = ["trino>=0.337.0"]
|
||||
prophet = ["prophet>=1.1.6, <2"]
|
||||
redshift = ["sqlalchemy-redshift>=0.8.1, <0.9"]
|
||||
risingwave = ["sqlalchemy-risingwave"]
|
||||
@@ -195,20 +203,20 @@ spark = [
|
||||
"pyhive[hive]>=0.6.5;python_version<'3.11'",
|
||||
"pyhive[hive_pure_sasl]>=0.7",
|
||||
"tableschema",
|
||||
"thrift>=0.14.1, <1",
|
||||
"thrift>=0.23.0, <1",
|
||||
]
|
||||
tdengine = [
|
||||
"taospy>=2.7.21",
|
||||
"taos-ws-py>=0.3.8"
|
||||
"taos-ws-py>=0.6.9"
|
||||
]
|
||||
teradata = ["teradatasql>=16.20.0.23"]
|
||||
thumbnails = [] # deprecated, will be removed in 7.0
|
||||
vertica = ["sqlalchemy-vertica-python>= 0.5.9, < 0.7"]
|
||||
vertica = ["sqlalchemy-vertica-python>= 0.6.3, < 0.7"]
|
||||
netezza = ["nzalchemy>=11.0.2"]
|
||||
starrocks = ["starrocks>=1.0.0"]
|
||||
starrocks = ["starrocks>=1.3.3, <2"]
|
||||
doris = ["pydoris>=1.0.0, <2.0.0"]
|
||||
oceanbase = ["oceanbase_py>=0.0.1"]
|
||||
ydb = ["ydb-sqlalchemy>=0.1.2", "ydb-sqlglot-plugin>=0.2.5"]
|
||||
oceanbase = ["oceanbase_py>=0.0.1.2"]
|
||||
ydb = ["ydb-sqlalchemy>=0.1.22", "ydb-sqlglot-plugin>=0.2.5"]
|
||||
development = [
|
||||
# no bounds for apache-superset-extensions-cli until a stable version
|
||||
"apache-superset-extensions-cli",
|
||||
@@ -216,13 +224,13 @@ development = [
|
||||
"docker",
|
||||
"flask-testing",
|
||||
"freezegun",
|
||||
"grpcio>=1.55.3",
|
||||
"grpcio>=1.81.1",
|
||||
"openapi-spec-validator",
|
||||
"parameterized",
|
||||
"pip",
|
||||
"polib", # used by scripts/translations/ and their unit tests
|
||||
"pre-commit",
|
||||
"progress>=1.5,<2",
|
||||
"progress>=1.6.1,<2",
|
||||
"psutil",
|
||||
"pyfakefs",
|
||||
"pyinstrument>=5.1.2,<6",
|
||||
@@ -231,7 +239,7 @@ development = [
|
||||
"pytest-asyncio",
|
||||
"pytest-cov",
|
||||
"pytest-mock",
|
||||
"python-ldap>=3.4.4",
|
||||
"python-ldap>=3.4.7",
|
||||
"ruff",
|
||||
"sqloxide",
|
||||
"statsd",
|
||||
@@ -367,7 +375,6 @@ select = [
|
||||
|
||||
ignore = [
|
||||
"S101",
|
||||
"PT004", # Fixtures that don't return values - underscore prefix conflicts with pytest usage
|
||||
"PT006",
|
||||
"T201",
|
||||
"N999",
|
||||
@@ -447,6 +454,7 @@ requirement_txt_file = "requirements/base.txt"
|
||||
authorized_licenses = [
|
||||
"academic free license (afl)",
|
||||
"any-osi",
|
||||
"apache-2.0",
|
||||
"apache license 2.0",
|
||||
"apache software",
|
||||
"apache software, bsd",
|
||||
|
||||
27
pytest.ini
27
pytest.ini
@@ -18,5 +18,30 @@
|
||||
testpaths =
|
||||
tests
|
||||
python_files = *_test.py test_*.py *_tests.py *viz/utils.py
|
||||
addopts = -p no:warnings
|
||||
# `-p no:warnings` temporarily disabled in favor of more finely tuned `filterwarnings`.
|
||||
#addopts = -p no:warnings
|
||||
asyncio_mode = auto
|
||||
|
||||
# `ignore` is effectively equivalent to `-p no:warnings`.
|
||||
# Always print RemovedIn20Warning when SQLALCHEMY_WARN_20=1.
|
||||
# Additionally, raise errors for refactored RemovedIn20Warning cases to prevent regression.
|
||||
filterwarnings =
|
||||
ignore
|
||||
always::sqlalchemy.exc.RemovedIn20Warning
|
||||
error:Passing a string to Connection.execute\(\) is deprecated:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"Query" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"SavedQuery" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"SqlaTable" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"SqlMetric" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"TableColumn" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"TaggedObject" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The ``as_declarative\(\)`` function is now available:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The autoload parameter is deprecated:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The connection.execute\(\) method:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The current statement is being autocommitted using implicit autocommit:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The `database` package is deprecated:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The ``declarative_base\(\)`` function is now available:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The Engine.execute\(\) method is considered legacy:sqlalchemy.exc.RemovedIn20Warning
|
||||
error:The legacy calling style of select\(\) is deprecated:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The "whens" argument to case:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"User" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
|
||||
@@ -26,11 +26,11 @@ filelock>=3.20.3,<4.0.0
|
||||
brotli>=1.2.0,<2.0.0
|
||||
numexpr>=2.9.0
|
||||
# Security: CVE-2026-34073 (MEDIUM) - Improper Certificate Validation
|
||||
cryptography>=46.0.7,<47.0.0
|
||||
cryptography>=48.0.0,<49.0.0
|
||||
# Security: Snyk - XSS vulnerability in Mako templates
|
||||
mako>=1.3.11,<2.0.0
|
||||
# Security: CVE-2024-52338 (CRITICAL) - Deserialization of untrusted data in IPC/Parquet readers
|
||||
pyarrow>=20.0.0,<21.0.0
|
||||
pyarrow>=24.0.0,<25.0.0
|
||||
# Security: CVE-2026-27459 - pyopenssl certificate validation
|
||||
pyopenssl>=26.0.0,<27.0.0
|
||||
# Security: CVE-2026-25645 (MEDIUM) - Insecure Temporary File
|
||||
@@ -44,11 +44,10 @@ async_timeout>=4.0.0,<5.0.0
|
||||
# a bit of attention to bump.
|
||||
apispec>=6.0.0,<6.7.0
|
||||
|
||||
# 1.4.1 appears to use much more memory, where the python test suite runs out of memory
|
||||
# causing CI to fail. 1.4.0 is the last version that works.
|
||||
# https://marshmallow-sqlalchemy.readthedocs.io/en/latest/changelog.html#id3
|
||||
# Opened this issue https://github.com/marshmallow-code/marshmallow-sqlalchemy/issues/665
|
||||
marshmallow-sqlalchemy>=1.3.0,<1.4.1
|
||||
# 1.4.1 introduced a memory regression that exhausts memory in the test suite
|
||||
# (https://github.com/marshmallow-code/marshmallow-sqlalchemy/issues/665). 1.4.2
|
||||
# claimed a fix but did not address the root cause; only 1.5.0 actually fixes it.
|
||||
marshmallow-sqlalchemy>=1.5.0
|
||||
|
||||
# needed for python 3.12 support
|
||||
openapi-schema-validator>=0.6.3
|
||||
@@ -58,3 +57,9 @@ openapi-schema-validator>=0.6.3
|
||||
# Known affected packages: Preset's 'clients' package
|
||||
# See docs/docs/contributing/pkg-resources-migration.md for details
|
||||
setuptools<81
|
||||
|
||||
# google-auth 2.53+ dropped its transitive dependency on cachetools, which is
|
||||
# imported directly by superset.db_engine_specs.aws_iam. We declare cachetools
|
||||
# explicitly in pyproject.toml and pin google-auth to the post-drop range so
|
||||
# the install path is internally consistent (#40962).
|
||||
google-auth>=2.53.0,<3.0.0
|
||||
|
||||
@@ -45,12 +45,12 @@ cachelib==0.13.0
|
||||
# flask-caching
|
||||
# flask-session
|
||||
cachetools==6.2.1
|
||||
# via google-auth
|
||||
# via apache-superset (pyproject.toml)
|
||||
cattrs==25.1.1
|
||||
# via requests-cache
|
||||
celery==5.5.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
certifi==2025.6.15
|
||||
certifi==2026.5.20
|
||||
# via
|
||||
# requests
|
||||
# selenium
|
||||
@@ -84,12 +84,13 @@ colorama==0.4.6
|
||||
# flask-appbuilder
|
||||
cron-descriptor==1.4.5
|
||||
# via apache-superset (pyproject.toml)
|
||||
croniter==6.0.0
|
||||
croniter==6.2.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
cryptography==46.0.7
|
||||
cryptography==48.0.1
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# apache-superset (pyproject.toml)
|
||||
# google-auth
|
||||
# paramiko
|
||||
# pyopenssl
|
||||
defusedxml==0.7.1
|
||||
@@ -131,7 +132,7 @@ flask-caching==2.3.1
|
||||
# via apache-superset (pyproject.toml)
|
||||
flask-compress==1.17
|
||||
# via apache-superset (pyproject.toml)
|
||||
flask-cors==6.0.2
|
||||
flask-cors==6.0.5
|
||||
# via apache-superset (pyproject.toml)
|
||||
flask-jwt-extended==4.7.1
|
||||
# via flask-appbuilder
|
||||
@@ -141,7 +142,7 @@ flask-login==0.6.3
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# flask-appbuilder
|
||||
flask-migrate==3.1.0
|
||||
flask-migrate==4.1.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
flask-session==0.8.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
@@ -151,7 +152,7 @@ flask-sqlalchemy==2.5.1
|
||||
# flask-migrate
|
||||
flask-talisman==1.1.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
flask-wtf==1.2.2
|
||||
flask-wtf==1.3.0
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# flask-appbuilder
|
||||
@@ -159,9 +160,11 @@ geographiclib==2.0
|
||||
# via geopy
|
||||
geopy==2.4.1
|
||||
# via apache-superset (pyproject.toml)
|
||||
google-auth==2.43.0
|
||||
# via shillelagh
|
||||
greenlet==3.5.0
|
||||
google-auth==2.53.0
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# shillelagh
|
||||
greenlet==3.5.1
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# shillelagh
|
||||
@@ -194,7 +197,7 @@ jinja2==3.1.6
|
||||
# via
|
||||
# flask
|
||||
# flask-babel
|
||||
jsonpath-ng==1.7.0
|
||||
jsonpath-ng==1.8.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
jsonschema==4.23.0
|
||||
# via
|
||||
@@ -223,13 +226,13 @@ markupsafe==3.0.2
|
||||
# mako
|
||||
# werkzeug
|
||||
# wtforms
|
||||
marshmallow==3.26.2
|
||||
marshmallow==4.3.0
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# flask-appbuilder
|
||||
# marshmallow-sqlalchemy
|
||||
# marshmallow-union
|
||||
marshmallow-sqlalchemy==1.4.0
|
||||
marshmallow-sqlalchemy==1.5.0
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# flask-appbuilder
|
||||
@@ -237,7 +240,7 @@ marshmallow-union==0.1.15
|
||||
# via apache-superset (pyproject.toml)
|
||||
mdurl==0.1.2
|
||||
# via markdown-it-py
|
||||
msgpack==1.0.8
|
||||
msgpack==1.2.1
|
||||
# via apache-superset (pyproject.toml)
|
||||
msgspec==0.19.0
|
||||
# via flask-session
|
||||
@@ -270,7 +273,6 @@ packaging==25.0
|
||||
# deprecation
|
||||
# gunicorn
|
||||
# limits
|
||||
# marshmallow
|
||||
# shillelagh
|
||||
pandas==2.1.4
|
||||
# via apache-superset (pyproject.toml)
|
||||
@@ -286,23 +288,19 @@ pillow==12.2.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
platformdirs==4.3.8
|
||||
# via requests-cache
|
||||
ply==3.11
|
||||
# via jsonpath-ng
|
||||
polyline==2.0.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
prison==0.2.1
|
||||
# via flask-appbuilder
|
||||
prompt-toolkit==3.0.51
|
||||
# via click-repl
|
||||
pyarrow==20.0.0
|
||||
pyarrow==24.0.0
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# apache-superset (pyproject.toml)
|
||||
# apache-superset-core
|
||||
pyasn1==0.6.3
|
||||
# via
|
||||
# pyasn1-modules
|
||||
# rsa
|
||||
# via pyasn1-modules
|
||||
pyasn1-modules==0.4.2
|
||||
# via google-auth
|
||||
pycparser==2.22
|
||||
@@ -317,7 +315,7 @@ pygeohash==3.2.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
pygments==2.20.0
|
||||
# via rich
|
||||
pyjwt==2.12.0
|
||||
pyjwt==2.13.0
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# flask-appbuilder
|
||||
@@ -325,7 +323,7 @@ pyjwt==2.12.0
|
||||
# redis
|
||||
pynacl==1.6.2
|
||||
# via paramiko
|
||||
pyopenssl==26.0.0
|
||||
pyopenssl==26.2.0
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# shillelagh
|
||||
@@ -346,12 +344,11 @@ python-dotenv==1.2.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
pytz==2025.2
|
||||
# via
|
||||
# croniter
|
||||
# flask-babel
|
||||
# pandas
|
||||
pyxlsb==1.0.10
|
||||
# via pandas
|
||||
pyyaml==6.0.2
|
||||
pyyaml==6.0.3
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# apispec
|
||||
@@ -378,15 +375,13 @@ rpds-py==0.25.0
|
||||
# via
|
||||
# jsonschema
|
||||
# referencing
|
||||
rsa==4.9.1
|
||||
# via google-auth
|
||||
selenium==4.32.0
|
||||
selenium==4.44.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
setuptools==80.9.0
|
||||
# via -r requirements/base.in
|
||||
shillelagh==1.4.4
|
||||
# via apache-superset (pyproject.toml)
|
||||
simplejson==3.20.1
|
||||
simplejson==4.1.1
|
||||
# via apache-superset (pyproject.toml)
|
||||
six==1.17.0
|
||||
# via
|
||||
@@ -423,7 +418,7 @@ sshtunnel==0.4.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
tabulate==0.10.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
trio==0.30.0
|
||||
trio==0.33.0
|
||||
# via
|
||||
# selenium
|
||||
# trio-websocket
|
||||
@@ -480,7 +475,7 @@ wrapt==1.17.2
|
||||
# via deprecated
|
||||
wsproto==1.2.0
|
||||
# via trio-websocket
|
||||
wtforms==3.2.1
|
||||
wtforms==3.2.2
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# flask-appbuilder
|
||||
@@ -490,7 +485,7 @@ wtforms-json==0.3.5
|
||||
# via apache-superset (pyproject.toml)
|
||||
xlrd==2.0.1
|
||||
# via pandas
|
||||
xlsxwriter==3.0.9
|
||||
xlsxwriter==3.2.9
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# pandas
|
||||
|
||||
@@ -100,7 +100,7 @@ cachelib==0.13.0
|
||||
cachetools==6.2.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# google-auth
|
||||
# apache-superset
|
||||
# py-key-value-aio
|
||||
caio==0.9.25
|
||||
# via aiofile
|
||||
@@ -112,7 +112,7 @@ celery==5.5.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
certifi==2025.6.15
|
||||
certifi==2026.5.20
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# httpcore
|
||||
@@ -174,15 +174,16 @@ cron-descriptor==1.4.5
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
croniter==6.0.0
|
||||
croniter==6.2.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
cryptography==46.0.7
|
||||
cryptography==48.0.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
# authlib
|
||||
# google-auth
|
||||
# paramiko
|
||||
# pyjwt
|
||||
# pyopenssl
|
||||
@@ -276,7 +277,7 @@ flask-compress==1.17
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
flask-cors==6.0.2
|
||||
flask-cors==6.0.5
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -293,7 +294,7 @@ flask-login==0.6.3
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
# flask-appbuilder
|
||||
flask-migrate==3.1.0
|
||||
flask-migrate==4.1.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -312,7 +313,7 @@ flask-talisman==1.1.0
|
||||
# apache-superset
|
||||
flask-testing==0.8.1
|
||||
# via apache-superset
|
||||
flask-wtf==1.2.2
|
||||
flask-wtf==1.3.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -340,7 +341,7 @@ google-api-core==2.23.0
|
||||
# google-cloud-core
|
||||
# pandas-gbq
|
||||
# sqlalchemy-bigquery
|
||||
google-auth==2.43.0
|
||||
google-auth==2.53.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# google-api-core
|
||||
@@ -373,7 +374,7 @@ googleapis-common-protos==1.66.0
|
||||
# via
|
||||
# google-api-core
|
||||
# grpcio-status
|
||||
greenlet==3.5.0
|
||||
greenlet==3.5.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -382,7 +383,7 @@ greenlet==3.5.0
|
||||
# sqlalchemy
|
||||
griffelib==2.0.2
|
||||
# via fastmcp
|
||||
grpcio==1.71.0
|
||||
grpcio==1.81.1
|
||||
# via
|
||||
# apache-superset
|
||||
# google-api-core
|
||||
@@ -471,7 +472,7 @@ jmespath==1.1.0
|
||||
# via
|
||||
# boto3
|
||||
# botocore
|
||||
jsonpath-ng==1.7.0
|
||||
jsonpath-ng==1.8.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -507,6 +508,8 @@ limits==5.1.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask-limiter
|
||||
lz4==4.4.5
|
||||
# via trino
|
||||
mako==1.3.12
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
@@ -527,14 +530,14 @@ markupsafe==3.0.2
|
||||
# mako
|
||||
# werkzeug
|
||||
# wtforms
|
||||
marshmallow==3.26.2
|
||||
marshmallow==4.3.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
# flask-appbuilder
|
||||
# marshmallow-sqlalchemy
|
||||
# marshmallow-union
|
||||
marshmallow-sqlalchemy==1.4.0
|
||||
marshmallow-sqlalchemy==1.5.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask-appbuilder
|
||||
@@ -556,7 +559,7 @@ more-itertools==10.8.0
|
||||
# via
|
||||
# jaraco-classes
|
||||
# jaraco-functools
|
||||
msgpack==1.0.8
|
||||
msgpack==1.2.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -608,6 +611,8 @@ ordered-set==4.1.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask-limiter
|
||||
orjson==3.11.9
|
||||
# via trino
|
||||
outcome==1.3.0.post0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
@@ -626,7 +631,6 @@ packaging==25.0
|
||||
# google-cloud-bigquery
|
||||
# gunicorn
|
||||
# limits
|
||||
# marshmallow
|
||||
# matplotlib
|
||||
# pytest
|
||||
# shillelagh
|
||||
@@ -674,10 +678,6 @@ platformdirs==4.3.8
|
||||
# virtualenv
|
||||
pluggy==1.5.0
|
||||
# via pytest
|
||||
ply==3.11
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# jsonpath-ng
|
||||
polib==1.2.0
|
||||
# via apache-superset
|
||||
polyline==2.0.2
|
||||
@@ -690,7 +690,7 @@ prison==0.2.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask-appbuilder
|
||||
progress==1.6
|
||||
progress==1.6.1
|
||||
# via apache-superset
|
||||
prompt-toolkit==3.0.51
|
||||
# via
|
||||
@@ -715,7 +715,7 @@ psycopg2-binary==2.9.12
|
||||
# via apache-superset
|
||||
py-key-value-aio==0.4.4
|
||||
# via fastmcp
|
||||
pyarrow==20.0.0
|
||||
pyarrow==24.0.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -727,7 +727,6 @@ pyasn1==0.6.3
|
||||
# -c requirements/base-constraint.txt
|
||||
# pyasn1-modules
|
||||
# python-ldap
|
||||
# rsa
|
||||
pyasn1-modules==0.4.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
@@ -770,7 +769,7 @@ pyhive==0.7.0
|
||||
# via apache-superset
|
||||
pyinstrument==5.1.2
|
||||
# via apache-superset
|
||||
pyjwt==2.12.0
|
||||
pyjwt==2.13.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -784,7 +783,7 @@ pynacl==1.6.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# paramiko
|
||||
pyopenssl==26.0.0
|
||||
pyopenssl==26.2.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# shillelagh
|
||||
@@ -838,14 +837,13 @@ python-dotenv==1.2.2
|
||||
# apache-superset
|
||||
# fastmcp
|
||||
# pydantic-settings
|
||||
python-ldap==3.4.5
|
||||
python-ldap==3.4.7
|
||||
# via apache-superset
|
||||
python-multipart==0.0.29
|
||||
# via mcp
|
||||
pytz==2025.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# croniter
|
||||
# flask-babel
|
||||
# pandas
|
||||
# trino
|
||||
@@ -853,7 +851,7 @@ pyxlsb==1.0.10
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# pandas
|
||||
pyyaml==6.0.2
|
||||
pyyaml==6.0.3
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -915,17 +913,13 @@ rpds-py==0.25.0
|
||||
# -c requirements/base-constraint.txt
|
||||
# jsonschema
|
||||
# referencing
|
||||
rsa==4.9.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# google-auth
|
||||
ruff==0.9.7
|
||||
# via apache-superset
|
||||
s3transfer==0.16.0
|
||||
# via boto3
|
||||
secretstorage==3.5.0
|
||||
# via keyring
|
||||
selenium==4.32.0
|
||||
selenium==4.44.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -943,7 +937,7 @@ shillelagh==1.4.4
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
simplejson==3.20.1
|
||||
simplejson==4.1.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -980,7 +974,7 @@ sqlalchemy==1.4.54
|
||||
# shillelagh
|
||||
# sqlalchemy-bigquery
|
||||
# sqlalchemy-utils
|
||||
sqlalchemy-bigquery==1.15.0
|
||||
sqlalchemy-bigquery==1.17.0
|
||||
# via apache-superset
|
||||
sqlalchemy-utils==0.42.0
|
||||
# via
|
||||
@@ -1011,7 +1005,7 @@ tabulate==0.10.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
tiktoken==0.12.0
|
||||
tiktoken==0.13.0
|
||||
# via apache-superset
|
||||
tomli-w==1.2.0
|
||||
# via apache-superset-extensions-cli
|
||||
@@ -1021,9 +1015,9 @@ tqdm==4.67.1
|
||||
# via
|
||||
# cmdstanpy
|
||||
# prophet
|
||||
trino==0.330.0
|
||||
trino==0.337.0
|
||||
# via apache-superset
|
||||
trio==0.30.0
|
||||
trio==0.33.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# selenium
|
||||
@@ -1041,6 +1035,7 @@ typing-extensions==4.15.0
|
||||
# apache-superset-core
|
||||
# cattrs
|
||||
# exceptiongroup
|
||||
# grpcio
|
||||
# limits
|
||||
# mcp
|
||||
# opentelemetry-api
|
||||
@@ -1125,7 +1120,7 @@ wsproto==1.2.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# trio-websocket
|
||||
wtforms==3.2.1
|
||||
wtforms==3.2.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -1140,7 +1135,7 @@ xlrd==2.0.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# pandas
|
||||
xlsxwriter==3.0.9
|
||||
xlsxwriter==3.2.9
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -1155,3 +1150,4 @@ zstandard==0.23.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask-compress
|
||||
# trino
|
||||
|
||||
@@ -30,7 +30,7 @@ from flask import current_app
|
||||
from flask_appbuilder import Model
|
||||
from flask_migrate import downgrade, upgrade
|
||||
from progress.bar import ChargingBar
|
||||
from sqlalchemy import create_engine, inspect
|
||||
from sqlalchemy import create_engine, inspect, text
|
||||
from sqlalchemy.ext.automap import automap_base
|
||||
|
||||
from superset import db
|
||||
@@ -154,7 +154,7 @@ def main( # noqa: C901
|
||||
|
||||
print(f"Migration goes from {down_revision} to {revision}")
|
||||
current_revision = db.engine.execute(
|
||||
"SELECT version_num FROM alembic_version"
|
||||
text("SELECT version_num FROM alembic_version")
|
||||
).scalar()
|
||||
print(f"Current version of the DB is {current_revision}")
|
||||
|
||||
|
||||
@@ -55,10 +55,21 @@ msgcat --sort-by-msgid --no-wrap --no-location superset/translations/messages.po
|
||||
cat $LICENSE_TMP superset/translations/messages.pot > messages.pot.tmp \
|
||||
&& mv messages.pot.tmp superset/translations/messages.pot
|
||||
|
||||
# --no-fuzzy-matching: when a *new* source string is added, Babel's fuzzy
|
||||
# matcher otherwise guesses a "close" existing translation and marks it
|
||||
# `#, fuzzy` in every language catalog. Those guesses are (a) usually wrong
|
||||
# (e.g. a new "valuename" string mapped onto an unrelated "table name"
|
||||
# translation) and (b) counted by check_translation_regression.py as a
|
||||
# regression, so every PR that merely adds a translatable string failed the
|
||||
# babel-extract check. Disabling fuzzy matching means new strings land as
|
||||
# cleanly untranslated (empty msgstr) instead — accurate, and no spurious
|
||||
# regression. Renames likewise drop the stale translation rather than
|
||||
# stranding a wrong guess; the string is re-translated by the community.
|
||||
pybabel update \
|
||||
-i superset/translations/messages.pot \
|
||||
-d superset/translations \
|
||||
--ignore-obsolete
|
||||
--ignore-obsolete \
|
||||
--no-fuzzy-matching
|
||||
|
||||
# Chop off last blankline from po/pot files, see https://github.com/python-babel/babel/issues/799
|
||||
for file in $( find superset/translations/** );
|
||||
|
||||
@@ -106,6 +106,7 @@ LANGUAGE_NAMES: dict[str, str] = {
|
||||
"ru": "Russian",
|
||||
"sk": "Slovak",
|
||||
"sl": "Slovenian",
|
||||
"sr": "Serbian",
|
||||
"tr": "Turkish",
|
||||
"uk": "Ukrainian",
|
||||
"zh": "Chinese (Simplified)",
|
||||
|
||||
@@ -20,20 +20,21 @@ Check that source-code changes don't cause translation regressions.
|
||||
|
||||
What counts as a regression
|
||||
---------------------------
|
||||
A regression is an *existing translation that a source change invalidated* —
|
||||
i.e. a string was renamed/reworded so its committed translation no longer
|
||||
applies. ``babel_update.sh`` (``pybabel update --ignore-obsolete``) surfaces
|
||||
exactly these as **newly fuzzy** entries: the old translation is fuzzy-matched
|
||||
onto the new ``msgid`` and flagged ``#, fuzzy``.
|
||||
A regression is an *existing translation that a source change invalidated*.
|
||||
The check keys on the **increase in fuzzy entries** rather than a drop in the
|
||||
translated count, because a count drop happens identically for a benign
|
||||
*deletion* and a real *rename*, so it cannot distinguish the two — whereas a
|
||||
``#, fuzzy`` marker unambiguously flags a stranded translation.
|
||||
|
||||
Crucially, *deleting* a translatable string is **not** a regression. With
|
||||
``--ignore-obsolete`` a removed string is dropped from the catalogs entirely;
|
||||
no fuzzy entry is created. So a PR that intentionally removes a string (e.g. a
|
||||
security fix that stops rendering a value) legitimately lowers the translated
|
||||
count without introducing any fuzzies, and must not be flagged. We therefore
|
||||
key the check on the **increase in fuzzy entries**, not on a drop in the
|
||||
translated count (a drop happens identically for a benign deletion and a real
|
||||
rename, so it cannot distinguish the two).
|
||||
Note ``babel_update.sh`` runs ``pybabel update`` with ``--no-fuzzy-matching``,
|
||||
so *adding* (or renaming) a source string does **not** auto-generate a fuzzy
|
||||
guess against an unrelated existing translation — new strings land as cleanly
|
||||
untranslated (empty ``msgstr``). This deliberately avoids the prior behaviour
|
||||
where *every* PR that merely added a translatable string tripped this check on
|
||||
spurious fuzzies. As a result the check now guards against ``#, fuzzy`` entries
|
||||
that arrive another way — e.g. a committed ``.po`` edit — rather than ones the
|
||||
update step synthesises. *Deleting* a string is still not a regression: with
|
||||
``--ignore-obsolete`` it is simply dropped and no fuzzy is created.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
@@ -1 +1 @@
|
||||
v22.22.0
|
||||
v24.16.0
|
||||
|
||||
@@ -29,8 +29,8 @@ Embedding is done by inserting an iframe, containing a Superset page, into the h
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* Activate the feature flag `EMBEDDED_SUPERSET`
|
||||
* Set a strong password in configuration variable `GUEST_TOKEN_JWT_SECRET` (see configuration file config.py). Be aware that its default value must be changed in production.
|
||||
- Activate the feature flag `EMBEDDED_SUPERSET`
|
||||
- Set a strong password in configuration variable `GUEST_TOKEN_JWT_SECRET` (see configuration file config.py). Be aware that its default value must be changed in production.
|
||||
|
||||
## Embedding a Dashboard
|
||||
|
||||
@@ -41,32 +41,37 @@ npm install --save @superset-ui/embedded-sdk
|
||||
```
|
||||
|
||||
```js
|
||||
import { embedDashboard } from "@superset-ui/embedded-sdk";
|
||||
import { embedDashboard } from '@superset-ui/embedded-sdk';
|
||||
|
||||
embedDashboard({
|
||||
id: "abc123", // given by the Superset embedding UI
|
||||
supersetDomain: "https://superset.example.com",
|
||||
mountPoint: document.getElementById("my-superset-container"), // any html element that can contain an iframe
|
||||
id: 'abc123', // given by the Superset embedding UI
|
||||
supersetDomain: 'https://superset.example.com',
|
||||
mountPoint: document.getElementById('my-superset-container'), // any html element that can contain an iframe
|
||||
fetchGuestToken: () => fetchGuestTokenFromBackend(),
|
||||
dashboardUiConfig: { // dashboard UI config: hideTitle, hideTab, hideChartControls, filters.visible, filters.expanded (optional), urlParams (optional)
|
||||
hideTitle: true,
|
||||
filters: {
|
||||
expanded: true,
|
||||
},
|
||||
urlParams: {
|
||||
foo: 'value1',
|
||||
bar: 'value2',
|
||||
// ...
|
||||
}
|
||||
dashboardUiConfig: {
|
||||
// dashboard UI config: hideTitle, hideTab, hideChartControls, filters.visible, filters.expanded (optional), urlParams (optional)
|
||||
hideTitle: true,
|
||||
filters: {
|
||||
expanded: true,
|
||||
},
|
||||
urlParams: {
|
||||
foo: 'value1',
|
||||
bar: 'value2',
|
||||
// themeMode: 'dark', // set the initial theme: 'dark' | 'system' | 'default' (default: 'default')
|
||||
// ...
|
||||
},
|
||||
},
|
||||
// optional additional iframe sandbox attributes
|
||||
iframeSandboxExtras: ['allow-top-navigation', 'allow-popups-to-escape-sandbox'],
|
||||
iframeSandboxExtras: [
|
||||
'allow-top-navigation',
|
||||
'allow-popups-to-escape-sandbox',
|
||||
],
|
||||
// optional Permissions Policy features
|
||||
iframeAllowExtras: ['clipboard-write', 'fullscreen'],
|
||||
// optional config to enforce a particular referrerPolicy
|
||||
referrerPolicy: "same-origin",
|
||||
referrerPolicy: 'same-origin',
|
||||
// optional callback to customize permalink URLs
|
||||
resolvePermalinkUrl: ({ key }) => `https://my-app.com/analytics/share/${key}`
|
||||
resolvePermalinkUrl: ({ key }) => `https://my-app.com/analytics/share/${key}`,
|
||||
});
|
||||
```
|
||||
|
||||
@@ -97,7 +102,7 @@ Guest tokens can have Row Level Security rules which filter data for the user ca
|
||||
|
||||
The agent making the `POST` request must be authenticated with the `can_grant_guest_token` permission.
|
||||
|
||||
Within your app, using the Guest Token will then allow authentication to your Superset instance via creating an Anonymous user object. This guest anonymous user will default to the public role as per this setting `GUEST_ROLE_NAME = "Public"`.
|
||||
Within your app, using the Guest Token will then allow authentication to your Superset instance via creating an Anonymous user object. This guest anonymous user will default to the public role as per this setting `GUEST_ROLE_NAME = "Public"`.
|
||||
|
||||
The user parameters in the example below are optional and are provided as a means of passing user attributes that may be accessed in jinja templates inside your charts.
|
||||
|
||||
@@ -110,13 +115,13 @@ Example `POST /security/guest_token` payload:
|
||||
"first_name": "Stan",
|
||||
"last_name": "Lee"
|
||||
},
|
||||
"resources": [{
|
||||
"type": "dashboard",
|
||||
"id": "abc123"
|
||||
}],
|
||||
"rls": [
|
||||
{ "clause": "publisher = 'Nintendo'" }
|
||||
]
|
||||
"resources": [
|
||||
{
|
||||
"type": "dashboard",
|
||||
"id": "abc123"
|
||||
}
|
||||
],
|
||||
"rls": [{ "clause": "publisher = 'Nintendo'" }]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -152,15 +157,43 @@ In this example, the configuration file includes the following setting:
|
||||
GUEST_TOKEN_JWT_AUDIENCE="superset"
|
||||
```
|
||||
|
||||
### Setting the Initial Theme Mode
|
||||
|
||||
Use the `themeMode` URL parameter to control the embedded dashboard's initial colour scheme:
|
||||
|
||||
```js
|
||||
embedDashboard({
|
||||
id: 'abc123',
|
||||
supersetDomain: 'https://superset.example.com',
|
||||
mountPoint: document.getElementById('my-superset-container'),
|
||||
fetchGuestToken: () => fetchGuestTokenFromBackend(),
|
||||
dashboardUiConfig: {
|
||||
urlParams: {
|
||||
themeMode: 'dark', // 'dark' | 'system' | 'default' (default: 'default')
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
The supported values are:
|
||||
|
||||
| Value | Behaviour |
|
||||
| --------- | --------------------------------------------------------- |
|
||||
| `default` | Light theme (Superset default) |
|
||||
| `dark` | Dark theme |
|
||||
| `system` | Follows the user's OS preference (`prefers-color-scheme`) |
|
||||
|
||||
The theme can also be changed at runtime via `embeddedDashboard.setThemeMode(mode)`.
|
||||
|
||||
### Sandbox iframe
|
||||
|
||||
The Embedded SDK creates an iframe with [sandbox](https://developer.mozilla.org/es/docs/Web/HTML/Element/iframe#sandbox) mode by default
|
||||
which applies certain restrictions to the iframe's content.
|
||||
To pass additional sandbox attributes you can use `iframeSandboxExtras`:
|
||||
|
||||
```js
|
||||
// optional additional iframe sandbox attributes
|
||||
iframeSandboxExtras: ['allow-top-navigation', 'allow-popups-to-escape-sandbox']
|
||||
// optional additional iframe sandbox attributes
|
||||
iframeSandboxExtras: ['allow-top-navigation', 'allow-popups-to-escape-sandbox'];
|
||||
```
|
||||
|
||||
### Permissions Policy
|
||||
@@ -168,11 +201,12 @@ To pass additional sandbox attributes you can use `iframeSandboxExtras`:
|
||||
To enable specific browser features within the embedded iframe, use `iframeAllowExtras` to set the iframe's [Permissions Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Permissions_Policy) (the `allow` attribute):
|
||||
|
||||
```js
|
||||
// optional Permissions Policy features
|
||||
iframeAllowExtras: ['clipboard-write', 'fullscreen']
|
||||
// optional Permissions Policy features
|
||||
iframeAllowExtras: ['clipboard-write', 'fullscreen'];
|
||||
```
|
||||
|
||||
Common permissions you might need:
|
||||
|
||||
- `clipboard-write` - Required for "Copy permalink to clipboard" functionality
|
||||
- `fullscreen` - Required for fullscreen chart viewing
|
||||
- `camera`, `microphone` - If your dashboards include media capture features
|
||||
@@ -191,16 +225,16 @@ When users click share buttons inside an embedded dashboard, Superset generates
|
||||
|
||||
```js
|
||||
embedDashboard({
|
||||
id: "abc123",
|
||||
supersetDomain: "https://superset.example.com",
|
||||
mountPoint: document.getElementById("my-superset-container"),
|
||||
id: 'abc123',
|
||||
supersetDomain: 'https://superset.example.com',
|
||||
mountPoint: document.getElementById('my-superset-container'),
|
||||
fetchGuestToken: () => fetchGuestTokenFromBackend(),
|
||||
|
||||
// Customize permalink URLs
|
||||
resolvePermalinkUrl: ({ key }) => {
|
||||
// key: the permalink key (e.g., "xyz789")
|
||||
return `https://my-app.com/analytics/share/${key}`;
|
||||
}
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
@@ -211,15 +245,15 @@ To restore the dashboard state from a permalink in your app:
|
||||
const permalinkKey = routeParams.key;
|
||||
|
||||
embedDashboard({
|
||||
id: "abc123",
|
||||
supersetDomain: "https://superset.example.com",
|
||||
mountPoint: document.getElementById("my-superset-container"),
|
||||
id: 'abc123',
|
||||
supersetDomain: 'https://superset.example.com',
|
||||
mountPoint: document.getElementById('my-superset-container'),
|
||||
fetchGuestToken: () => fetchGuestTokenFromBackend(),
|
||||
resolvePermalinkUrl: ({ key }) => `https://my-app.com/analytics/share/${key}`,
|
||||
dashboardUiConfig: {
|
||||
urlParams: {
|
||||
permalink_key: permalinkKey, // Restores filters, tabs, chart states, and scrolls to anchor
|
||||
}
|
||||
}
|
||||
permalink_key: permalinkKey, // Restores filters, tabs, chart states, and scrolls to anchor
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
288
superset-embedded-sdk/package-lock.json
generated
288
superset-embedded-sdk/package-lock.json
generated
@@ -27,19 +27,6 @@
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
||||
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/cli": {
|
||||
"version": "7.25.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.25.6.tgz",
|
||||
@@ -71,12 +58,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
|
||||
"integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
|
||||
"integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.28.5",
|
||||
"@babel/helper-validator-identifier": "^7.29.7",
|
||||
"js-tokens": "^4.0.0",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
@@ -85,32 +72,30 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz",
|
||||
"integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
|
||||
"integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/core": {
|
||||
"version": "7.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz",
|
||||
"integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==",
|
||||
"version": "7.29.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.6.tgz",
|
||||
"integrity": "sha512-QdxmAo/ikZqqRGA8s43ww8lcql6naWRvEz0FFrl6MIlc7Gi6TroXnSdWa5U/kq6fzcpqpHesicQxFZIieZbyIA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.24.7",
|
||||
"@babel/generator": "^7.25.0",
|
||||
"@babel/helper-compilation-targets": "^7.25.2",
|
||||
"@babel/helper-module-transforms": "^7.25.2",
|
||||
"@babel/helpers": "^7.25.0",
|
||||
"@babel/parser": "^7.25.0",
|
||||
"@babel/template": "^7.25.0",
|
||||
"@babel/traverse": "^7.25.2",
|
||||
"@babel/types": "^7.25.2",
|
||||
"@babel/code-frame": "^7.29.0",
|
||||
"@babel/generator": "^7.29.6",
|
||||
"@babel/helper-compilation-targets": "^7.28.6",
|
||||
"@babel/helper-module-transforms": "^7.28.6",
|
||||
"@babel/helpers": "^7.29.2",
|
||||
"@babel/parser": "^7.29.3",
|
||||
"@babel/template": "^7.28.6",
|
||||
"@babel/traverse": "^7.29.0",
|
||||
"@babel/types": "^7.29.0",
|
||||
"@jridgewell/remapping": "^2.3.5",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"debug": "^4.1.0",
|
||||
"gensync": "^1.0.0-beta.2",
|
||||
@@ -126,13 +111,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
|
||||
"integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
|
||||
"integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.29.0",
|
||||
"@babel/types": "^7.29.0",
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@babel/types": "^7.29.7",
|
||||
"@jridgewell/gen-mapping": "^0.3.12",
|
||||
"@jridgewell/trace-mapping": "^0.3.28",
|
||||
"jsesc": "^3.0.2"
|
||||
@@ -169,15 +154,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-compilation-targets": {
|
||||
"version": "7.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz",
|
||||
"integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
|
||||
"integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.25.2",
|
||||
"@babel/helper-validator-option": "^7.24.8",
|
||||
"browserslist": "^4.23.1",
|
||||
"@babel/compat-data": "^7.29.7",
|
||||
"@babel/helper-validator-option": "^7.29.7",
|
||||
"browserslist": "^4.24.0",
|
||||
"lru-cache": "^5.1.1",
|
||||
"semver": "^6.3.1"
|
||||
},
|
||||
@@ -382,29 +366,28 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
|
||||
"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
|
||||
"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-option": {
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz",
|
||||
"integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
|
||||
"integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -425,26 +408,25 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.25.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz",
|
||||
"integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
|
||||
"integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.25.0",
|
||||
"@babel/types": "^7.25.6"
|
||||
"@babel/template": "^7.29.7",
|
||||
"@babel/types": "^7.29.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.29.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz",
|
||||
"integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
|
||||
"integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.29.0"
|
||||
"@babel/types": "^7.29.7"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -1843,14 +1825,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
|
||||
"integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
|
||||
"integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.28.6",
|
||||
"@babel/parser": "^7.28.6",
|
||||
"@babel/types": "^7.28.6"
|
||||
"@babel/code-frame": "^7.29.7",
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@babel/types": "^7.29.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -1875,13 +1857,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
|
||||
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
|
||||
"integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.28.5"
|
||||
"@babel/helper-string-parser": "^7.29.7",
|
||||
"@babel/helper-validator-identifier": "^7.29.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -2649,6 +2631,16 @@
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/remapping": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
|
||||
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
@@ -7983,16 +7975,6 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
||||
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
}
|
||||
},
|
||||
"@babel/cli": {
|
||||
"version": "7.25.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.25.6.tgz",
|
||||
@@ -8011,38 +7993,38 @@
|
||||
}
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
|
||||
"integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
|
||||
"integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.28.5",
|
||||
"@babel/helper-validator-identifier": "^7.29.7",
|
||||
"js-tokens": "^4.0.0",
|
||||
"picocolors": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"@babel/compat-data": {
|
||||
"version": "7.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz",
|
||||
"integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
|
||||
"integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/core": {
|
||||
"version": "7.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz",
|
||||
"integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==",
|
||||
"version": "7.29.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.6.tgz",
|
||||
"integrity": "sha512-QdxmAo/ikZqqRGA8s43ww8lcql6naWRvEz0FFrl6MIlc7Gi6TroXnSdWa5U/kq6fzcpqpHesicQxFZIieZbyIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.24.7",
|
||||
"@babel/generator": "^7.25.0",
|
||||
"@babel/helper-compilation-targets": "^7.25.2",
|
||||
"@babel/helper-module-transforms": "^7.25.2",
|
||||
"@babel/helpers": "^7.25.0",
|
||||
"@babel/parser": "^7.25.0",
|
||||
"@babel/template": "^7.25.0",
|
||||
"@babel/traverse": "^7.25.2",
|
||||
"@babel/types": "^7.25.2",
|
||||
"@babel/code-frame": "^7.29.0",
|
||||
"@babel/generator": "^7.29.6",
|
||||
"@babel/helper-compilation-targets": "^7.28.6",
|
||||
"@babel/helper-module-transforms": "^7.28.6",
|
||||
"@babel/helpers": "^7.29.2",
|
||||
"@babel/parser": "^7.29.3",
|
||||
"@babel/template": "^7.28.6",
|
||||
"@babel/traverse": "^7.29.0",
|
||||
"@babel/types": "^7.29.0",
|
||||
"@jridgewell/remapping": "^2.3.5",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"debug": "^4.1.0",
|
||||
"gensync": "^1.0.0-beta.2",
|
||||
@@ -8051,13 +8033,13 @@
|
||||
}
|
||||
},
|
||||
"@babel/generator": {
|
||||
"version": "7.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
|
||||
"integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
|
||||
"integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/parser": "^7.29.0",
|
||||
"@babel/types": "^7.29.0",
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@babel/types": "^7.29.7",
|
||||
"@jridgewell/gen-mapping": "^0.3.12",
|
||||
"@jridgewell/trace-mapping": "^0.3.28",
|
||||
"jsesc": "^3.0.2"
|
||||
@@ -8083,14 +8065,14 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-compilation-targets": {
|
||||
"version": "7.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz",
|
||||
"integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
|
||||
"integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/compat-data": "^7.25.2",
|
||||
"@babel/helper-validator-option": "^7.24.8",
|
||||
"browserslist": "^4.23.1",
|
||||
"@babel/compat-data": "^7.29.7",
|
||||
"@babel/helper-validator-option": "^7.29.7",
|
||||
"browserslist": "^4.24.0",
|
||||
"lru-cache": "^5.1.1",
|
||||
"semver": "^6.3.1"
|
||||
}
|
||||
@@ -8229,21 +8211,21 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-string-parser": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
|
||||
"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-validator-identifier": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
|
||||
"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-validator-option": {
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz",
|
||||
"integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
|
||||
"integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-wrap-function": {
|
||||
@@ -8258,22 +8240,22 @@
|
||||
}
|
||||
},
|
||||
"@babel/helpers": {
|
||||
"version": "7.25.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz",
|
||||
"integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
|
||||
"integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/template": "^7.25.0",
|
||||
"@babel/types": "^7.25.6"
|
||||
"@babel/template": "^7.29.7",
|
||||
"@babel/types": "^7.29.7"
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.29.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz",
|
||||
"integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
|
||||
"integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.29.0"
|
||||
"@babel/types": "^7.29.7"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
|
||||
@@ -9157,14 +9139,14 @@
|
||||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
|
||||
"integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
|
||||
"integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.28.6",
|
||||
"@babel/parser": "^7.28.6",
|
||||
"@babel/types": "^7.28.6"
|
||||
"@babel/code-frame": "^7.29.7",
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@babel/types": "^7.29.7"
|
||||
}
|
||||
},
|
||||
"@babel/traverse": {
|
||||
@@ -9183,13 +9165,13 @@
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
|
||||
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
|
||||
"integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.28.5"
|
||||
"@babel/helper-string-parser": "^7.29.7",
|
||||
"@babel/helper-validator-identifier": "^7.29.7"
|
||||
}
|
||||
},
|
||||
"@bcoe/v8-coverage": {
|
||||
@@ -9771,6 +9753,16 @@
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
}
|
||||
},
|
||||
"@jridgewell/remapping": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
|
||||
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
}
|
||||
},
|
||||
"@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@superset-ui/embedded-sdk",
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.0",
|
||||
"description": "SDK for embedding resources from Superset into your own application",
|
||||
"access": "public",
|
||||
"keywords": [
|
||||
|
||||
@@ -47,7 +47,11 @@ function logError(...args) {
|
||||
execSync('npm publish --access public', { stdio: 'pipe' });
|
||||
log(`published ${version} to npm`);
|
||||
} catch (err) {
|
||||
console.error(String(err.stdout));
|
||||
// npm writes failure details to stderr (auth/permission/registry
|
||||
// errors in particular), so surface both streams to avoid masking
|
||||
// the real cause in CI logs.
|
||||
if (err.stdout) console.error(String(err.stdout));
|
||||
if (err.stderr) console.error(String(err.stderr));
|
||||
logError('Encountered an error, details should be above');
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
getGuestTokenRefreshTiming,
|
||||
MIN_REFRESH_WAIT_MS,
|
||||
DEFAULT_TOKEN_EXP_MS,
|
||||
DEFAULT_TOKEN_REFRESH_RETRY_MS,
|
||||
} from "./guestTokenRefresh";
|
||||
|
||||
describe("guest token refresh", () => {
|
||||
@@ -93,4 +94,11 @@ describe("guest token refresh", () => {
|
||||
expect(timing).toBeGreaterThan(MIN_REFRESH_WAIT_MS);
|
||||
expect(timing).toBe(DEFAULT_TOKEN_EXP_MS - REFRESH_TIMING_BUFFER_MS);
|
||||
});
|
||||
|
||||
it("exposes a positive retry delay for failed token refreshes", () => {
|
||||
// The refresh loop reschedules itself after this delay when a fetch
|
||||
// fails or times out, so it must be a sane positive value.
|
||||
expect(DEFAULT_TOKEN_REFRESH_RETRY_MS).toBe(10000);
|
||||
expect(DEFAULT_TOKEN_REFRESH_RETRY_MS).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ import { jwtDecode } from "jwt-decode";
|
||||
export const REFRESH_TIMING_BUFFER_MS = 5000 // refresh guest token early to avoid failed superset requests
|
||||
export const MIN_REFRESH_WAIT_MS = 10000 // avoid blasting requests as fast as the cpu can handle
|
||||
export const DEFAULT_TOKEN_EXP_MS = 300000 // (5 min) used only when parsing guest token exp fails
|
||||
export const DEFAULT_TOKEN_REFRESH_RETRY_MS = 10000 // wait before retrying a failed/timed-out token refresh
|
||||
|
||||
// when do we refresh the guest token?
|
||||
export function getGuestTokenRefreshTiming(currentGuestToken: string) {
|
||||
|
||||
@@ -24,7 +24,11 @@ import {
|
||||
|
||||
// We can swap this out for the actual switchboard package once it gets published
|
||||
import { Switchboard } from '@superset-ui/switchboard';
|
||||
import { getGuestTokenRefreshTiming } from './guestTokenRefresh';
|
||||
import {
|
||||
getGuestTokenRefreshTiming,
|
||||
DEFAULT_TOKEN_REFRESH_RETRY_MS,
|
||||
} from './guestTokenRefresh';
|
||||
import { withTimeout } from './withTimeout';
|
||||
|
||||
/**
|
||||
* The function to fetch a guest token from your Host App's backend server.
|
||||
@@ -49,6 +53,9 @@ export type UiConfigType = {
|
||||
showRowLimitWarning?: boolean;
|
||||
};
|
||||
|
||||
/** Default per-call timeout (ms) applied to the host `fetchGuestToken` callback. */
|
||||
const DEFAULT_GUEST_TOKEN_FETCH_TIMEOUT_MS = 30_000;
|
||||
|
||||
export type EmbedDashboardParams = {
|
||||
/** The id provided by the embed configuration UI in Superset */
|
||||
id: string;
|
||||
@@ -73,6 +80,10 @@ export type EmbedDashboardParams = {
|
||||
/** Callback to resolve permalink URLs. If provided, this will be called when generating permalinks
|
||||
* to allow the host app to customize the URL. If not provided, Superset's default URL is used. */
|
||||
resolvePermalinkUrl?: ResolvePermalinkUrlFn;
|
||||
/** Timeout, in milliseconds, applied to each `fetchGuestToken` call so a host
|
||||
* callback that never resolves cannot hang the embed/refresh cycle. Defaults
|
||||
* to 30000ms. Set to 0 to disable the timeout. */
|
||||
guestTokenFetchTimeoutMs?: number;
|
||||
};
|
||||
|
||||
export type Size = {
|
||||
@@ -127,6 +138,7 @@ export async function embedDashboard({
|
||||
iframeAllowExtras = [],
|
||||
referrerPolicy,
|
||||
resolvePermalinkUrl,
|
||||
guestTokenFetchTimeoutMs = DEFAULT_GUEST_TOKEN_FETCH_TIMEOUT_MS,
|
||||
}: EmbedDashboardParams): Promise<EmbeddedDashboard> {
|
||||
function log(...info: unknown[]) {
|
||||
if (debug) {
|
||||
@@ -134,6 +146,16 @@ export async function embedDashboard({
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap the host-provided fetchGuestToken so a callback that never settles
|
||||
// cannot hang the initial embed or a later refresh cycle.
|
||||
function fetchGuestTokenWithTimeout(): Promise<string> {
|
||||
return withTimeout(
|
||||
fetchGuestToken(),
|
||||
guestTokenFetchTimeoutMs,
|
||||
'fetchGuestToken',
|
||||
);
|
||||
}
|
||||
|
||||
log('embedding');
|
||||
|
||||
if (supersetDomain.endsWith('/')) {
|
||||
@@ -247,21 +269,57 @@ export async function embedDashboard({
|
||||
});
|
||||
}
|
||||
|
||||
const [guestToken, ourPort]: [string, Switchboard] = await Promise.all([
|
||||
fetchGuestToken(),
|
||||
mountIframe(),
|
||||
]);
|
||||
let guestToken: string;
|
||||
let ourPort: Switchboard;
|
||||
try {
|
||||
[guestToken, ourPort] = await Promise.all([
|
||||
fetchGuestTokenWithTimeout(),
|
||||
mountIframe(),
|
||||
]);
|
||||
} catch (err) {
|
||||
// If the initial token fetch (or timeout) rejects after the iframe has
|
||||
// already been mounted, tear down the partially initialized iframe so the
|
||||
// host isn't left with an orphaned embedded dashboard before rethrowing.
|
||||
//@ts-ignore
|
||||
mountPoint.replaceChildren();
|
||||
throw err;
|
||||
}
|
||||
|
||||
ourPort.emit('guestToken', { guestToken });
|
||||
log('sent guest token');
|
||||
|
||||
// Track the pending refresh timer so it can be cancelled on unmount, and
|
||||
// stop the cycle once unmounted so it cannot leak across mount/unmount cycles.
|
||||
let refreshTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
let unmounted = false;
|
||||
|
||||
async function refreshGuestToken() {
|
||||
const newGuestToken = await fetchGuestToken();
|
||||
ourPort.emit('guestToken', { guestToken: newGuestToken });
|
||||
setTimeout(refreshGuestToken, getGuestTokenRefreshTiming(newGuestToken));
|
||||
if (unmounted) return;
|
||||
try {
|
||||
const newGuestToken = await fetchGuestTokenWithTimeout();
|
||||
if (unmounted) return;
|
||||
ourPort.emit('guestToken', { guestToken: newGuestToken });
|
||||
refreshTimer = setTimeout(
|
||||
refreshGuestToken,
|
||||
getGuestTokenRefreshTiming(newGuestToken),
|
||||
);
|
||||
} catch (err) {
|
||||
// A transient fetch failure or timeout must not permanently stop the
|
||||
// refresh cycle. Log it and retry so the session can recover once the
|
||||
// host callback succeeds again.
|
||||
log('failed to refresh guest token, will retry:', err);
|
||||
if (unmounted) return;
|
||||
refreshTimer = setTimeout(
|
||||
refreshGuestToken,
|
||||
DEFAULT_TOKEN_REFRESH_RETRY_MS,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(refreshGuestToken, getGuestTokenRefreshTiming(guestToken));
|
||||
refreshTimer = setTimeout(
|
||||
refreshGuestToken,
|
||||
getGuestTokenRefreshTiming(guestToken),
|
||||
);
|
||||
|
||||
// Register the resolvePermalinkUrl method for the iframe to call
|
||||
// Returns null if no callback provided or on error, allowing iframe to use default URL
|
||||
@@ -283,6 +341,11 @@ export async function embedDashboard({
|
||||
|
||||
function unmount() {
|
||||
log('unmounting');
|
||||
unmounted = true;
|
||||
if (refreshTimer !== undefined) {
|
||||
clearTimeout(refreshTimer);
|
||||
refreshTimer = undefined;
|
||||
}
|
||||
//@ts-ignore
|
||||
mountPoint.replaceChildren();
|
||||
}
|
||||
|
||||
@@ -16,14 +16,24 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useMemo } from 'react';
|
||||
import { RootState } from 'src/dashboard/types';
|
||||
import getChartIdsFromLayout from '../getChartIdsFromLayout';
|
||||
|
||||
export const useAllChartIds = () => {
|
||||
const layout = useSelector(
|
||||
(state: RootState) => state.dashboardLayout.present,
|
||||
import { withTimeout } from "./withTimeout";
|
||||
|
||||
test("resolves with the value when the promise settles in time", async () => {
|
||||
await expect(withTimeout(Promise.resolve("ok"), 1000, "fetch")).resolves.toBe(
|
||||
"ok"
|
||||
);
|
||||
return useMemo(() => getChartIdsFromLayout(layout), [layout]);
|
||||
};
|
||||
});
|
||||
|
||||
test("rejects when the promise does not settle within the timeout", async () => {
|
||||
const never = new Promise<string>(() => {});
|
||||
await expect(withTimeout(never, 10, "fetch")).rejects.toThrow(
|
||||
/fetch did not resolve within 10ms/
|
||||
);
|
||||
});
|
||||
|
||||
test("passes the promise through unchanged when the timeout is disabled", async () => {
|
||||
await expect(withTimeout(Promise.resolve("ok"), 0, "fetch")).resolves.toBe(
|
||||
"ok"
|
||||
);
|
||||
});
|
||||
@@ -17,23 +17,27 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { isDefined } from '@superset-ui/core';
|
||||
|
||||
export const useMemoCompare = <T>(
|
||||
next: T,
|
||||
compare: (prev: T | undefined, next: T) => boolean,
|
||||
) => {
|
||||
const previousRef = useRef<T>();
|
||||
const previous = previousRef.current;
|
||||
const isEqual = compare(previous, next);
|
||||
useEffect(() => {
|
||||
if (!isEqual) {
|
||||
previousRef.current = next;
|
||||
}
|
||||
});
|
||||
if (!isDefined(previous)) {
|
||||
return next;
|
||||
/**
|
||||
* Rejects if `promise` does not settle within `ms` milliseconds. A non-positive
|
||||
* `ms` disables the timeout and returns the promise unchanged. The timer is
|
||||
* always cleared so it cannot keep the event loop alive.
|
||||
*/
|
||||
export function withTimeout<T>(
|
||||
promise: Promise<T>,
|
||||
ms: number,
|
||||
label: string,
|
||||
): Promise<T> {
|
||||
if (!ms || ms <= 0) {
|
||||
return promise;
|
||||
}
|
||||
return isEqual ? previous : next;
|
||||
};
|
||||
let timer: ReturnType<typeof setTimeout>;
|
||||
const timeout = new Promise<never>((_resolve, reject) => {
|
||||
timer = setTimeout(
|
||||
() => reject(new Error(`${label} did not resolve within ${ms}ms`)),
|
||||
ms,
|
||||
);
|
||||
});
|
||||
return Promise.race([promise, timeout]).finally(() =>
|
||||
clearTimeout(timer),
|
||||
) as Promise<T>;
|
||||
}
|
||||
@@ -226,7 +226,7 @@ def copy_frontend_dist(cwd: Path) -> str:
|
||||
def copy_backend_files(cwd: Path) -> None:
|
||||
"""Copy backend files based on pyproject.toml build configuration (validation already passed)."""
|
||||
dist_dir = cwd / "dist"
|
||||
backend_dir = cwd / "backend"
|
||||
backend_dir = (cwd / "backend").resolve()
|
||||
|
||||
# Read build config from pyproject.toml
|
||||
pyproject = read_toml(backend_dir / "pyproject.toml")
|
||||
@@ -239,11 +239,31 @@ def copy_backend_files(cwd: Path) -> None:
|
||||
|
||||
# Process include patterns
|
||||
for pattern in include_patterns:
|
||||
# Include patterns are only meant to select files within the backend
|
||||
# directory. Reject absolute patterns or ones that walk outside it via
|
||||
# parent ("..") components before handing them to glob().
|
||||
pattern_parts = Path(pattern).parts
|
||||
if Path(pattern).is_absolute() or ".." in pattern_parts:
|
||||
raise click.ClickException(
|
||||
f"Invalid include pattern {pattern!r}: patterns must be "
|
||||
"relative to the backend directory and may not contain '..'."
|
||||
)
|
||||
for f in backend_dir.glob(pattern):
|
||||
if not f.is_file():
|
||||
continue
|
||||
|
||||
# Check exclude patterns
|
||||
# Defense in depth: confirm the matched file resolves to a location
|
||||
# inside the backend directory before copying it into the bundle.
|
||||
resolved = f.resolve()
|
||||
if not resolved.is_relative_to(backend_dir):
|
||||
raise click.ClickException(
|
||||
f"Refusing to copy {f}: resolved path is outside the "
|
||||
f"backend directory {backend_dir}."
|
||||
)
|
||||
|
||||
# Use the matched path (not the resolved target) for the bundle
|
||||
# layout and exclude evaluation so symlinked files are staged at
|
||||
# their configured path rather than their symlink target.
|
||||
relative_path = f.relative_to(backend_dir)
|
||||
should_exclude = any(
|
||||
relative_path.match(excl_pattern) for excl_pattern in exclude_patterns
|
||||
|
||||
@@ -20,6 +20,7 @@ from __future__ import annotations
|
||||
import json
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import click
|
||||
import pytest
|
||||
from superset_extensions_cli.cli import (
|
||||
app,
|
||||
@@ -625,6 +626,155 @@ exclude = []
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_copy_backend_files_supports_legitimate_nested_patterns(isolated_filesystem):
|
||||
"""Test copy_backend_files copies deeply nested files via recursive globs."""
|
||||
backend_dir = isolated_filesystem / "backend"
|
||||
nested = backend_dir / "src" / "test_org" / "test_ext" / "deep" / "deeper"
|
||||
nested.mkdir(parents=True)
|
||||
(nested / "module.py").write_text("# nested module")
|
||||
|
||||
pyproject_content = """[project]
|
||||
name = "test_org-test_ext"
|
||||
version = "1.0.0"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[tool.apache_superset_extensions.build]
|
||||
include = [
|
||||
"src/test_org/test_ext/**/*.py",
|
||||
]
|
||||
exclude = []
|
||||
"""
|
||||
(backend_dir / "pyproject.toml").write_text(pyproject_content)
|
||||
|
||||
extension_data = {
|
||||
"publisher": "test-org",
|
||||
"name": "test-ext",
|
||||
"displayName": "Test Extension",
|
||||
"version": "1.0.0",
|
||||
"permissions": [],
|
||||
}
|
||||
(isolated_filesystem / "extension.json").write_text(json.dumps(extension_data))
|
||||
|
||||
clean_dist(isolated_filesystem)
|
||||
copy_backend_files(isolated_filesystem)
|
||||
|
||||
dist_dir = isolated_filesystem / "dist"
|
||||
assert_file_exists(
|
||||
dist_dir
|
||||
/ "backend"
|
||||
/ "src"
|
||||
/ "test_org"
|
||||
/ "test_ext"
|
||||
/ "deep"
|
||||
/ "deeper"
|
||||
/ "module.py"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.parametrize(
|
||||
"bad_pattern",
|
||||
[
|
||||
"../../.ssh/*",
|
||||
"../config",
|
||||
"src/../../secret.txt",
|
||||
"/etc/passwd",
|
||||
],
|
||||
)
|
||||
def test_copy_backend_files_rejects_patterns_escaping_backend_dir(
|
||||
isolated_filesystem, bad_pattern
|
||||
):
|
||||
"""Test copy_backend_files refuses include patterns that escape backend_dir."""
|
||||
# Create a sensitive file outside the backend directory.
|
||||
(isolated_filesystem / "secret.txt").write_text("SECRET")
|
||||
(isolated_filesystem / "config").write_text("SECRET")
|
||||
|
||||
backend_dir = isolated_filesystem / "backend"
|
||||
backend_src = backend_dir / "src" / "test_org" / "test_ext"
|
||||
backend_src.mkdir(parents=True)
|
||||
(backend_src / "__init__.py").write_text("# init")
|
||||
|
||||
pyproject_content = f"""[project]
|
||||
name = "test_org-test_ext"
|
||||
version = "1.0.0"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[tool.apache_superset_extensions.build]
|
||||
include = [
|
||||
"{bad_pattern}",
|
||||
]
|
||||
exclude = []
|
||||
"""
|
||||
(backend_dir / "pyproject.toml").write_text(pyproject_content)
|
||||
|
||||
extension_data = {
|
||||
"publisher": "test-org",
|
||||
"name": "test-ext",
|
||||
"displayName": "Test Extension",
|
||||
"version": "1.0.0",
|
||||
"permissions": [],
|
||||
}
|
||||
(isolated_filesystem / "extension.json").write_text(json.dumps(extension_data))
|
||||
|
||||
clean_dist(isolated_filesystem)
|
||||
|
||||
with pytest.raises(click.ClickException):
|
||||
copy_backend_files(isolated_filesystem)
|
||||
|
||||
# Nothing outside the backend directory should have been staged into dist,
|
||||
# including paths reachable via ".." from inside dist/backend.
|
||||
dist_dir = isolated_filesystem / "dist"
|
||||
assert not (dist_dir / "secret.txt").exists()
|
||||
assert not (dist_dir / "config").exists()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_copy_backend_files_stages_symlink_at_matched_path(isolated_filesystem):
|
||||
"""Symlinked files inside backend are staged at the matched path, not the target."""
|
||||
backend_dir = isolated_filesystem / "backend"
|
||||
target_dir = backend_dir / "src" / "common"
|
||||
target_dir.mkdir(parents=True)
|
||||
(target_dir / "module.py").write_text("# shared module")
|
||||
|
||||
link_dir = backend_dir / "src" / "test_org" / "test_ext" / "common"
|
||||
link_dir.mkdir(parents=True)
|
||||
link = link_dir / "module.py"
|
||||
link.symlink_to(target_dir / "module.py")
|
||||
|
||||
pyproject_content = """[project]
|
||||
name = "test_org-test_ext"
|
||||
version = "1.0.0"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[tool.apache_superset_extensions.build]
|
||||
include = [
|
||||
"src/test_org/test_ext/**/*.py",
|
||||
]
|
||||
exclude = []
|
||||
"""
|
||||
(backend_dir / "pyproject.toml").write_text(pyproject_content)
|
||||
|
||||
extension_data = {
|
||||
"publisher": "test-org",
|
||||
"name": "test-ext",
|
||||
"displayName": "Test Extension",
|
||||
"version": "1.0.0",
|
||||
"permissions": [],
|
||||
}
|
||||
(isolated_filesystem / "extension.json").write_text(json.dumps(extension_data))
|
||||
|
||||
clean_dist(isolated_filesystem)
|
||||
copy_backend_files(isolated_filesystem)
|
||||
|
||||
dist_dir = isolated_filesystem / "dist"
|
||||
# Staged at the configured (symlink) path, not the resolved target path.
|
||||
assert_file_exists(
|
||||
dist_dir / "backend" / "src" / "test_org" / "test_ext" / "common" / "module.py"
|
||||
)
|
||||
assert not (dist_dir / "backend" / "src" / "common" / "module.py").exists()
|
||||
|
||||
|
||||
# Removed obsolete tests:
|
||||
# - test_copy_backend_files_handles_no_backend_config: This scenario can't happen since copy_backend_files is only called when backend exists
|
||||
# - test_copy_backend_files_exits_when_extension_json_missing: Validation catches this before copy_backend_files is called
|
||||
|
||||
@@ -1,34 +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.
|
||||
#
|
||||
**/*{.,-}min.js
|
||||
**/*.sh
|
||||
coverage/**
|
||||
dist/*
|
||||
src/assets/images/*
|
||||
node_modules/*
|
||||
node_modules*/*
|
||||
vendor/*
|
||||
docs/*
|
||||
src/dashboard/deprecated/*
|
||||
src/temp/*
|
||||
**/node_modules
|
||||
*.d.ts
|
||||
coverage/
|
||||
esm/
|
||||
lib/
|
||||
tmp/
|
||||
storybook-static/
|
||||
@@ -1,523 +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.
|
||||
*/
|
||||
|
||||
// Register TypeScript require hook so ESLint can load .ts plugin files
|
||||
require('tsx/cjs');
|
||||
|
||||
const packageConfig = require('./package.json');
|
||||
|
||||
const importCoreModules = [];
|
||||
Object.entries(packageConfig.dependencies).forEach(([pkg]) => {
|
||||
if (/@superset-ui/.test(pkg)) {
|
||||
importCoreModules.push(pkg);
|
||||
}
|
||||
});
|
||||
|
||||
// ignore files in production mode
|
||||
let ignorePatterns = [];
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
ignorePatterns = [
|
||||
'*.test.{js,ts,jsx,tsx}',
|
||||
'plugins/**/test/**/*',
|
||||
'packages/**/test/**/*',
|
||||
'packages/generator-superset/**/*',
|
||||
];
|
||||
}
|
||||
|
||||
const restrictedImportsRules = {
|
||||
'no-design-icons': {
|
||||
name: '@ant-design/icons',
|
||||
message:
|
||||
'Avoid importing icons directly from @ant-design/icons. Use the src/components/Icons component instead.',
|
||||
},
|
||||
'no-moment': {
|
||||
name: 'moment',
|
||||
message:
|
||||
'Please use the dayjs library instead of moment.js. See https://day.js.org',
|
||||
},
|
||||
'no-lodash-memoize': {
|
||||
name: 'lodash/memoize',
|
||||
message: 'Lodash Memoize is unsafe! Please use memoize-one instead',
|
||||
},
|
||||
'no-testing-library-react': {
|
||||
name: '@superset-ui/core/spec',
|
||||
message: 'Please use spec/helpers/testing-library instead',
|
||||
},
|
||||
'no-testing-library-react-dom-utils': {
|
||||
name: '@testing-library/react-dom-utils',
|
||||
message: 'Please use spec/helpers/testing-library instead',
|
||||
},
|
||||
'no-antd': {
|
||||
name: 'antd',
|
||||
message: 'Please import Ant components from the index of src/components',
|
||||
},
|
||||
'no-superset-theme': {
|
||||
name: '@superset-ui/core',
|
||||
importNames: ['supersetTheme'],
|
||||
message:
|
||||
'Please use the theme directly from the ThemeProvider rather than importing supersetTheme.',
|
||||
},
|
||||
'no-query-string': {
|
||||
name: 'query-string',
|
||||
message: 'Please use the URLSearchParams API instead of query-string.',
|
||||
},
|
||||
'no-jest-mock-console': {
|
||||
name: 'jest-mock-console',
|
||||
message: 'Please use native Jest spies, i.e. jest.spyOn(console, "warn")',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:import/recommended',
|
||||
'plugin:react-prefer-function-component/recommended',
|
||||
'plugin:storybook/recommended',
|
||||
'prettier',
|
||||
],
|
||||
parser: '@babel/eslint-parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
requireConfigFile: false,
|
||||
babelOptions: {
|
||||
presets: ['@babel/preset-react', '@babel/preset-env'],
|
||||
},
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es2020: true,
|
||||
},
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
node: {
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
|
||||
moduleDirectory: ['node_modules', '.'],
|
||||
},
|
||||
typescript: {
|
||||
alwaysTryTypes: true,
|
||||
project: [
|
||||
'./tsconfig.json',
|
||||
'./packages/superset-ui-core/tsconfig.json',
|
||||
'./packages/superset-ui-chart-controls/',
|
||||
'./plugins/*/tsconfig.json',
|
||||
],
|
||||
},
|
||||
},
|
||||
'import/core-modules': importCoreModules,
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
'import',
|
||||
'lodash',
|
||||
'theme-colors',
|
||||
'icons',
|
||||
'i18n-strings',
|
||||
'react-prefer-function-component',
|
||||
'react-you-might-not-need-an-effect',
|
||||
'prettier',
|
||||
],
|
||||
rules: {
|
||||
// === Essential Superset customizations ===
|
||||
|
||||
// Prettier integration
|
||||
'prettier/prettier': 'error',
|
||||
|
||||
// Custom Superset rules
|
||||
'theme-colors/no-literal-colors': 'error',
|
||||
'icons/no-fa-icons-usage': 'error',
|
||||
'i18n-strings/no-template-vars': 'error',
|
||||
|
||||
// Core ESLint overrides for Superset
|
||||
'no-console': 'warn',
|
||||
'no-unused-vars': 'off', // TypeScript handles this
|
||||
camelcase: [
|
||||
'error',
|
||||
{
|
||||
allow: ['^UNSAFE_', '__REDUX_DEVTOOLS_EXTENSION_COMPOSE__'],
|
||||
properties: 'never',
|
||||
},
|
||||
],
|
||||
'prefer-destructuring': ['error', { object: true, array: false }],
|
||||
'no-prototype-builtins': 0,
|
||||
curly: 'off',
|
||||
|
||||
// Import plugin overrides
|
||||
'import/extensions': [
|
||||
'error',
|
||||
'ignorePackages',
|
||||
{
|
||||
js: 'never',
|
||||
jsx: 'never',
|
||||
ts: 'never',
|
||||
tsx: 'never',
|
||||
},
|
||||
],
|
||||
'import/no-cycle': 0,
|
||||
'import/prefer-default-export': 0,
|
||||
'import/no-named-as-default-member': 0,
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
devDependencies: [
|
||||
'test/**',
|
||||
'tests/**',
|
||||
'spec/**',
|
||||
'**/__tests__/**',
|
||||
'**/__mocks__/**',
|
||||
'*.test.{js,jsx,ts,tsx}',
|
||||
'*.spec.{js,jsx,ts,tsx}',
|
||||
'**/*.test.{js,jsx,ts,tsx}',
|
||||
'**/*.spec.{js,jsx,ts,tsx}',
|
||||
'**/jest.config.js',
|
||||
'**/jest.setup.js',
|
||||
'**/webpack.config.js',
|
||||
'**/webpack.config.*.js',
|
||||
'**/.eslintrc*.js',
|
||||
],
|
||||
optionalDependencies: false,
|
||||
},
|
||||
],
|
||||
|
||||
// React plugin overrides
|
||||
'react-prefer-function-component/react-prefer-function-component': 1,
|
||||
|
||||
// React effect best practices
|
||||
'react-you-might-not-need-an-effect/no-empty-effect': 'error',
|
||||
'react-you-might-not-need-an-effect/no-pass-live-state-to-parent': 'error',
|
||||
'react-you-might-not-need-an-effect/no-initialize-state': 'error',
|
||||
|
||||
// Lodash
|
||||
'lodash/import-scope': [2, 'member'],
|
||||
|
||||
// React effect best practices
|
||||
'react-you-might-not-need-an-effect/no-reset-all-state-on-prop-change':
|
||||
'error',
|
||||
'react-you-might-not-need-an-effect/no-chain-state-updates': 'error',
|
||||
'react-you-might-not-need-an-effect/no-event-handler': 'error',
|
||||
'react-you-might-not-need-an-effect/no-derived-state': 'error',
|
||||
|
||||
// Storybook
|
||||
'storybook/prefer-pascal-case': 'error',
|
||||
|
||||
// File progress
|
||||
'file-progress/activate': 1,
|
||||
|
||||
// React effect rules
|
||||
'react-you-might-not-need-an-effect/no-adjust-state-on-prop-change':
|
||||
'error',
|
||||
'react-you-might-not-need-an-effect/no-pass-data-to-parent': 'error',
|
||||
|
||||
// Restricted imports
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: Object.values(restrictedImportsRules).filter(Boolean),
|
||||
patterns: ['antd/*'],
|
||||
},
|
||||
],
|
||||
|
||||
// Temporarily disabled for migration
|
||||
'no-unsafe-optional-chaining': 0,
|
||||
'no-import-assign': 0,
|
||||
'import/no-relative-packages': 0,
|
||||
'no-promise-executor-return': 0,
|
||||
'import/no-import-module-exports': 0,
|
||||
|
||||
// Restrict certain syntax patterns
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
selector:
|
||||
"ImportDeclaration[source.value='react'] :matches(ImportDefaultSpecifier, ImportNamespaceSpecifier)",
|
||||
message:
|
||||
'Default React import is not required due to automatic JSX runtime in React 16.4',
|
||||
},
|
||||
{
|
||||
selector: 'ImportNamespaceSpecifier[parent.source.value!=/^(\\.|src)/]',
|
||||
message: 'Wildcard imports are not allowed',
|
||||
},
|
||||
],
|
||||
},
|
||||
overrides: [
|
||||
// Ban JavaScript files in src/ - all new code must be TypeScript
|
||||
{
|
||||
files: ['src/**/*.js', 'src/**/*.jsx'],
|
||||
rules: {
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
selector: 'Program',
|
||||
message:
|
||||
'JavaScript files are not allowed in src/. Please use TypeScript (.ts/.tsx) instead.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// Ban JavaScript files in plugins/ - all plugin source code must be TypeScript
|
||||
{
|
||||
files: ['plugins/**/src/**/*.js', 'plugins/**/src/**/*.jsx'],
|
||||
rules: {
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
selector: 'Program',
|
||||
message:
|
||||
'JavaScript files are not allowed in plugins/. Please use TypeScript (.ts/.tsx) instead.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// Ban JavaScript files in packages/ - with exceptions for config files and generators
|
||||
{
|
||||
files: ['packages/**/src/**/*.js', 'packages/**/src/**/*.jsx'],
|
||||
excludedFiles: [
|
||||
'packages/generator-superset/**/*', // Yeoman generator templates run via Node
|
||||
'packages/**/__mocks__/**/*', // Test mocks
|
||||
],
|
||||
rules: {
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
selector: 'Program',
|
||||
message:
|
||||
'JavaScript files are not allowed in packages/. Please use TypeScript (.ts/.tsx) instead.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
extends: ['plugin:@typescript-eslint/recommended', 'prettier'],
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
rules: {
|
||||
// TypeScript-specific rule overrides
|
||||
'@typescript-eslint/ban-ts-ignore': 0,
|
||||
'@typescript-eslint/ban-ts-comment': 0,
|
||||
'@typescript-eslint/ban-types': 0,
|
||||
'@typescript-eslint/naming-convention': [
|
||||
'error',
|
||||
{
|
||||
selector: 'enum',
|
||||
format: ['PascalCase'],
|
||||
},
|
||||
{
|
||||
selector: 'enumMember',
|
||||
format: ['PascalCase'],
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-empty-function': 0,
|
||||
'@typescript-eslint/no-explicit-any': 0,
|
||||
'@typescript-eslint/no-use-before-define': 'error',
|
||||
'@typescript-eslint/no-non-null-assertion': 0,
|
||||
'@typescript-eslint/explicit-function-return-type': 0,
|
||||
'@typescript-eslint/explicit-module-boundary-types': 0,
|
||||
'@typescript-eslint/no-unused-vars': 'warn',
|
||||
'@typescript-eslint/prefer-optional-chain': 'error',
|
||||
|
||||
// Disable base rules that conflict with TS versions
|
||||
'no-unused-vars': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'no-shadow': 'off',
|
||||
|
||||
// Import overrides for TypeScript
|
||||
'import/extensions': [
|
||||
'error',
|
||||
'ignorePackages',
|
||||
{
|
||||
js: 'never',
|
||||
jsx: 'never',
|
||||
ts: 'never',
|
||||
tsx: 'never',
|
||||
},
|
||||
],
|
||||
},
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
typescript: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/**'],
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{ devDependencies: true },
|
||||
],
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: [
|
||||
restrictedImportsRules['no-moment'],
|
||||
restrictedImportsRules['no-lodash-memoize'],
|
||||
restrictedImportsRules['no-superset-theme'],
|
||||
],
|
||||
patterns: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['plugins/**'],
|
||||
rules: {
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: [
|
||||
restrictedImportsRules['no-moment'],
|
||||
restrictedImportsRules['no-lodash-memoize'],
|
||||
],
|
||||
patterns: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['src/components/**', 'src/theme/**'],
|
||||
rules: {
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: Object.values(restrictedImportsRules).filter(
|
||||
r => r.name !== 'antd',
|
||||
),
|
||||
patterns: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'*.test.ts',
|
||||
'*.test.tsx',
|
||||
'*.test.js',
|
||||
'*.test.jsx',
|
||||
'*.stories.tsx',
|
||||
'*.stories.jsx',
|
||||
'fixtures.*',
|
||||
'**/test/**/*',
|
||||
'**/tests/**/*',
|
||||
'spec/**/*',
|
||||
'**/fixtures/**/*',
|
||||
'**/__mocks__/**/*',
|
||||
'**/spec/**/*',
|
||||
],
|
||||
excludedFiles: 'cypress-base/cypress/**/*',
|
||||
plugins: ['jest-dom', 'no-only-tests', 'testing-library'],
|
||||
extends: ['plugin:jest-dom/recommended', 'plugin:testing-library/react'],
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{ devDependencies: true },
|
||||
],
|
||||
'prefer-promise-reject-errors': 0,
|
||||
'max-classes-per-file': 0,
|
||||
|
||||
// Temporary for migration
|
||||
'testing-library/await-async-queries': 0,
|
||||
'testing-library/await-async-utils': 0,
|
||||
'testing-library/no-await-sync-events': 0,
|
||||
'testing-library/no-render-in-lifecycle': 0,
|
||||
'testing-library/no-unnecessary-act': 0,
|
||||
'testing-library/no-wait-for-multiple-assertions': 0,
|
||||
'testing-library/prefer-screen-queries': 0,
|
||||
'testing-library/await-async-events': 0,
|
||||
'testing-library/no-node-access': 0,
|
||||
'testing-library/no-wait-for-side-effects': 0,
|
||||
'testing-library/prefer-presence-queries': 0,
|
||||
'testing-library/render-result-naming-convention': 0,
|
||||
'testing-library/no-container': 0,
|
||||
'testing-library/prefer-find-by': 0,
|
||||
'testing-library/no-manual-cleanup': 0,
|
||||
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
selector:
|
||||
"ImportDeclaration[source.value='react'] :matches(ImportDefaultSpecifier, ImportNamespaceSpecifier)",
|
||||
message:
|
||||
'Default React import is not required due to automatic JSX runtime in React 16.4',
|
||||
},
|
||||
],
|
||||
'no-restricted-imports': 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'*.test.ts',
|
||||
'*.test.tsx',
|
||||
'*.test.js',
|
||||
'*.test.jsx',
|
||||
'*.stories.tsx',
|
||||
'*.stories.jsx',
|
||||
'fixtures.*',
|
||||
'**/test/**/*',
|
||||
'**/tests/**/*',
|
||||
'spec/**/*',
|
||||
'**/fixtures/**/*',
|
||||
'**/__mocks__/**/*',
|
||||
'**/spec/**/*',
|
||||
'cypress-base/cypress/**/*',
|
||||
'Stories.tsx',
|
||||
'packages/superset-ui-core/src/theme/index.tsx',
|
||||
],
|
||||
rules: {
|
||||
'theme-colors/no-literal-colors': 0,
|
||||
'icons/no-fa-icons-usage': 0,
|
||||
'i18n-strings/no-template-vars': 0,
|
||||
'no-restricted-imports': 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'packages/**/*.stories.*',
|
||||
'packages/**/*.overview.*',
|
||||
'packages/**/fixtures.*',
|
||||
],
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['playwright/**/*.ts', 'playwright/**/*.js'],
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{ devDependencies: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
ignorePatterns,
|
||||
};
|
||||
@@ -1,124 +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.
|
||||
*/
|
||||
|
||||
// Register TypeScript require hook so ESLint can load .ts plugin files
|
||||
require('tsx/cjs');
|
||||
|
||||
/**
|
||||
* MINIMAL ESLint config - ONLY for rules OXC doesn't support
|
||||
* This config is designed to be run alongside OXC linter
|
||||
*
|
||||
* Only covers:
|
||||
* - Custom Superset plugins (theme-colors, icons, i18n)
|
||||
* - Prettier formatting
|
||||
* - File progress indicator
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
// Don't report on eslint-disable comments for rules we don't have
|
||||
reportUnusedDisableDirectives: false,
|
||||
// Simple parser - no TypeScript needed since OXC handles that
|
||||
parser: '@babel/eslint-parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
requireConfigFile: false,
|
||||
babelOptions: {
|
||||
presets: ['@babel/preset-react', '@babel/preset-env'],
|
||||
},
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es2020: true,
|
||||
},
|
||||
plugins: [
|
||||
// ONLY custom Superset plugins that OXC doesn't support
|
||||
'theme-colors',
|
||||
'icons',
|
||||
'i18n-strings',
|
||||
'file-progress',
|
||||
'prettier',
|
||||
],
|
||||
rules: {
|
||||
// === ONLY rules that OXC cannot handle ===
|
||||
|
||||
// Prettier integration (formatting)
|
||||
'prettier/prettier': 'error',
|
||||
|
||||
// Custom Superset plugins
|
||||
'theme-colors/no-literal-colors': 'error',
|
||||
'icons/no-fa-icons-usage': 'error',
|
||||
'i18n-strings/no-template-vars': 'error',
|
||||
'file-progress/activate': 1,
|
||||
|
||||
// Explicitly turn off all other rules to avoid conflicts
|
||||
// when the config gets merged with other configs
|
||||
'import/no-unresolved': 'off',
|
||||
'import/extensions': 'off',
|
||||
'@typescript-eslint/naming-convention': 'off',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
// Disable custom rules in test/story files
|
||||
files: [
|
||||
'**/*.test.*',
|
||||
'**/*.spec.*',
|
||||
'**/*.stories.*',
|
||||
'**/test/**',
|
||||
'**/tests/**',
|
||||
'**/spec/**',
|
||||
'**/__tests__/**',
|
||||
'**/__mocks__/**',
|
||||
'cypress-base/**',
|
||||
'packages/superset-ui-core/src/theme/index.tsx',
|
||||
],
|
||||
rules: {
|
||||
'theme-colors/no-literal-colors': 0,
|
||||
'icons/no-fa-icons-usage': 0,
|
||||
'i18n-strings/no-template-vars': 0,
|
||||
'file-progress/activate': 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
// Only check src/ files where theme/icon rules matter
|
||||
ignorePatterns: [
|
||||
'node_modules',
|
||||
'dist',
|
||||
'build',
|
||||
'.next',
|
||||
'coverage',
|
||||
'*.min.js',
|
||||
'vendor',
|
||||
// Skip packages/plugins since they have different theming rules
|
||||
'packages/**',
|
||||
'plugins/**',
|
||||
// Skip generated/external files
|
||||
'*.generated.*',
|
||||
'*.config.js',
|
||||
'webpack.*',
|
||||
// Temporary analysis files
|
||||
'*.js', // Skip all standalone JS files in root
|
||||
'*.json',
|
||||
],
|
||||
};
|
||||
@@ -1 +1 @@
|
||||
v22.22.0
|
||||
v24.16.0
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { dirname, join } from 'path';
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
@@ -17,8 +16,16 @@ import { dirname, join } from 'path';
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
// This file has been automatically migrated to valid ESM format by Storybook.
|
||||
import path from 'node:path';
|
||||
import { createRequire } from 'node:module';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
// Superset's webpack.config.js
|
||||
const customConfig = require('../webpack.config.js');
|
||||
import customConfig from '../webpack.config.js';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Filter out plugins that shouldn't be included in Storybook's static build
|
||||
// ReactRefreshWebpackPlugin adds Fast Refresh code that requires a dev server runtime,
|
||||
@@ -76,7 +83,7 @@ const disableDevModeInRules = rules =>
|
||||
};
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
stories: [
|
||||
'../src/**/*.stories.tsx',
|
||||
'../packages/superset-ui-core/src/**/*.stories.tsx',
|
||||
@@ -84,11 +91,8 @@ module.exports = {
|
||||
],
|
||||
|
||||
addons: [
|
||||
getAbsolutePath('@storybook/addon-essentials'),
|
||||
getAbsolutePath('@storybook/addon-links'),
|
||||
'@mihkeleidast/storybook-addon-source',
|
||||
getAbsolutePath('@storybook/addon-controls'),
|
||||
getAbsolutePath('@storybook/addon-mdx-gfm'),
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-docs"
|
||||
],
|
||||
|
||||
staticDirs: ['../src/assets/images'],
|
||||
@@ -105,11 +109,13 @@ module.exports = {
|
||||
alias: {
|
||||
...config.resolve?.alias,
|
||||
...customConfig.resolve?.alias,
|
||||
// Fix for Storybook 8.6.x with React 17 - resolve ESM module paths
|
||||
'react-dom/test-utils': require.resolve('react-dom/test-utils'),
|
||||
// Shared storybook utilities
|
||||
'@storybook-shared': join(__dirname, 'shared'),
|
||||
'@storybook-shared': path.join(__dirname, 'shared'),
|
||||
},
|
||||
fallback: {
|
||||
tty: false,
|
||||
vm: require.resolve('vm-browserify')
|
||||
}
|
||||
},
|
||||
plugins: [...config.plugins, ...filteredPlugins],
|
||||
}),
|
||||
@@ -119,15 +125,11 @@ module.exports = {
|
||||
},
|
||||
|
||||
framework: {
|
||||
name: getAbsolutePath('@storybook/react-webpack5'),
|
||||
name: getAbsolutePath("@storybook/react-webpack5"),
|
||||
options: {},
|
||||
},
|
||||
|
||||
docs: {
|
||||
autodocs: false,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
function getAbsolutePath(value) {
|
||||
return dirname(require.resolve(join(value, 'package.json')));
|
||||
return path.dirname(require.resolve(path.join(value, 'package.json')));
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { withJsx } from '@mihkeleidast/storybook-addon-source';
|
||||
import { themeObject, css, exampleThemes } from '@apache-superset/core/theme';
|
||||
import { combineReducers, createStore, applyMiddleware, compose } from 'redux';
|
||||
import thunk from 'redux-thunk';
|
||||
@@ -114,9 +113,12 @@ const providerDecorator = Story => (
|
||||
</Provider>
|
||||
);
|
||||
|
||||
export const decorators = [withJsx, themeDecorator, providerDecorator];
|
||||
export const decorators = [themeDecorator, providerDecorator];
|
||||
|
||||
export const parameters = {
|
||||
docs: {
|
||||
codePanel: true,
|
||||
},
|
||||
paddings: {
|
||||
values: [
|
||||
{ name: 'None', value: '0px' },
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
import { useState, ReactNode, SyntheticEvent } from 'react';
|
||||
import { styled } from '@apache-superset/core/theme';
|
||||
import type { Decorator } from '@storybook/react';
|
||||
import type { Decorator } from '@storybook/react-webpack5';
|
||||
import { ResizeCallbackData } from 'react-resizable';
|
||||
import ResizablePanel, { Size } from './ResizablePanel';
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ module.exports = {
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'@babel/plugin-transform-export-namespace-from',
|
||||
['@babel/plugin-transform-class-properties', { loose: true }],
|
||||
'@babel/plugin-transform-class-static-block',
|
||||
['@babel/plugin-transform-optional-chaining', { loose: true }],
|
||||
['@babel/plugin-transform-private-methods', { loose: true }],
|
||||
['@babel/plugin-transform-nullish-coalescing-operator', { loose: true }],
|
||||
@@ -106,7 +107,13 @@ module.exports = {
|
||||
[
|
||||
'babel-plugin-jsx-remove-data-test-id',
|
||||
{
|
||||
attributes: 'data-test',
|
||||
// The plugin matches attribute names exactly (no prefix match),
|
||||
// so each data-test* attribute must be listed explicitly.
|
||||
attributes: [
|
||||
'data-test',
|
||||
'data-test-drag-source-id',
|
||||
'data-test-drop-target-id',
|
||||
],
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1,67 +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.
|
||||
*/
|
||||
import { SAMPLE_DASHBOARD_1 } from 'cypress/utils/urls';
|
||||
import { interceptFav, interceptUnfav } from './utils';
|
||||
|
||||
describe('Dashboard actions', () => {
|
||||
beforeEach(() => {
|
||||
cy.createSampleDashboards([0]);
|
||||
cy.visit(SAMPLE_DASHBOARD_1);
|
||||
});
|
||||
it('should allow to favorite/unfavorite dashboard', () => {
|
||||
interceptFav();
|
||||
interceptUnfav();
|
||||
|
||||
// Find and click StarOutlined (adds to favorites)
|
||||
cy.getBySel('dashboard-header-container')
|
||||
.find("[aria-label='unstarred']")
|
||||
.as('starIconOutlined')
|
||||
.should('exist')
|
||||
.click();
|
||||
|
||||
cy.wait('@select');
|
||||
|
||||
// After clicking, StarFilled should appear
|
||||
cy.getBySel('dashboard-header-container')
|
||||
.find("[aria-label='starred']")
|
||||
.as('starIconFilled')
|
||||
.should('exist');
|
||||
|
||||
// Verify the color of the filled star (gold)
|
||||
cy.get('@starIconFilled')
|
||||
.should('have.css', 'color')
|
||||
.and('eq', 'rgb(252, 199, 0)');
|
||||
|
||||
// Click on StarFilled (removes from favorites)
|
||||
cy.get('@starIconFilled').click();
|
||||
|
||||
cy.wait('@unselect');
|
||||
|
||||
// After clicking, StarOutlined should reappear
|
||||
cy.getBySel('dashboard-header-container')
|
||||
.find("[aria-label='unstarred']")
|
||||
.as('starIconOutlinedAfter')
|
||||
.should('exist');
|
||||
|
||||
// Verify the color of the outlined star (gray)
|
||||
cy.get('@starIconOutlinedAfter')
|
||||
.should('have.css', 'color')
|
||||
.and('eq', 'rgba(0, 0, 0, 0.45)');
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user