Compare commits

...
Author SHA1 Message Date
Maxime Beauchemin c778c15259 docs(ci): auto-regenerate openapi files used for docs 2025-05-06 16:02:41 -07:00
Beto DealmeidaandGitHub dc0d542054 chore: regenerate openapi.json (#33378) 2025-05-06 15:56:00 -07:00
0cd3a12daa chore(🦾): bump python markdown 3.7 -> 3.8 (#33279)
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Maxime Beauchemin <maximebeauchemin@gmail.com>
2025-05-06 09:06:59 -07:00
35b30480f0 fix: Exclude Filter Values (#33271)
Co-authored-by: Amaan Nawab <nelsondrew07@gmail.com>
2025-05-06 13:40:27 +02:00
6d1f17bd46 chore(🦾): bump python sshtunnel subpackage(s) (#33370)
Co-authored-by: GitHub Action <action@github.com>
2025-05-05 18:30:08 -07:00
ab899e71e7 chore(🦾): bump python cryptography 44.0.2 -> 44.0.3 (#33371)
Co-authored-by: GitHub Action <action@github.com>
2025-05-05 18:29:40 -07:00
6b9d8708d3 chore(🦾): bump python humanize 4.12.2 -> 4.12.3 (#33369)
Co-authored-by: GitHub Action <action@github.com>
2025-05-05 18:29:10 -07:00
bc1e8e07cf chore(🦾): bump python sqlglot 26.16.2 -> 26.16.4 (#33368)
Co-authored-by: GitHub Action <action@github.com>
2025-05-05 18:28:56 -07:00
82526865d2 chore(🦾): bump python h11 0.14.0 -> 0.16.0 (#33339)
Co-authored-by: GitHub Action <action@github.com>
2025-05-06 07:41:10 +08:00
Daniel Vaz GasparandGitHub 02c8c9c752 fix: bump FAB to 4.6.3 (#33363) 2025-05-06 00:02:25 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
6475188e6a chore(deps): bump swagger-ui-react from 5.20.2 to 5.21.0 in /docs (#33318)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-05 16:34:20 -06:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
6e485c9f70 chore(deps-dev): update ts-loader requirement from ^9.5.1 to ^9.5.2 in /superset-frontend/packages/superset-ui-demo (#33323)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-05 16:33:58 -06:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b49e5857c9 chore(deps): bump uuid from 11.0.2 to 11.1.0 in /superset-websocket (#33311)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-05 16:33:18 -06:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
13ced58261 chore(deps-dev): bump @eslint/js from 9.17.0 to 9.25.1 in /superset-websocket (#33312)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-05 16:32:59 -06:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ed36674a99 chore(deps): bump less from 4.2.2 to 4.3.0 in /docs (#33317)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-05 16:32:28 -06:00
Jonas DORELandGitHub 99aa3a6507 docs(docker-builds.mdx): clarify dockerize images (#33350) 2025-05-05 16:31:33 -06:00
Maxime BeaucheminandGitHub f045a73e2d fix: loading examples from raw.githubusercontent.com fails with 429 errors (#33354) 2025-05-05 13:07:23 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
7791674f24 chore(deps-dev): bump eslint-config-prettier from 10.1.1 to 10.1.2 in /docs (#33315)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-03 15:37:33 -06:00
Vitor AvilaandGitHub 9f0ae77341 fix: Edge case with metric not getting quoted in sort by when normalize_columns is enabled (#33337) 2025-05-02 18:20:57 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5a9e366c0a chore(deps-dev): bump typescript from 5.8.2 to 5.8.3 in /docs (#33320)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-02 11:00:13 -06:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c22c532a5c chore(deps-dev): bump eslint-plugin-react from 7.37.4 to 7.37.5 in /docs (#33314)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-02 10:59:53 -06:00
Michael S. MolinaandGitHub 6db3a4d9d2 fix: Temporal filter conversion in viz migrations (#33224) 2025-05-02 08:27:49 -03:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
17d7b72f3b chore(deps-dev): bump webpack from 5.98.0 to 5.99.7 in /docs (#33316)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-02 14:31:29 +07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
fee33dd0cf chore(deps): bump @rjsf/validator-ajv8 from 5.24.1 to 5.24.9 in /superset-frontend (#33321)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-02 14:29:44 +07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
65605b4a54 chore(deps-dev): bump @babel/plugin-transform-runtime from 7.25.9 to 7.27.1 in /superset-frontend (#33332)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-02 14:28:41 +07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e304f2d5ad chore(deps): bump react-intersection-observer from 9.15.1 to 9.16.0 in /superset-frontend (#33333)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-01 16:22:16 -07:00
Beto DealmeidaandGitHub 4e0c261c9d fix: show only filterable columns on filter dropdown (#33338) 2025-05-01 18:36:32 -04:00
Beto DealmeidaandGitHub 22de26cd77 fix: metric.currency should be JSON, not string (#33303) 2025-05-01 18:16:51 -04:00
Beto DealmeidaandGitHub 339ba96600 fix: improve function detection (#33306) 2025-05-01 13:45:03 -04:00
3c6091144b chore(deps): Upgrade pyarrow to 18.1.0 (#31476)
Co-authored-by: Ville Brofeldt <33317356+villebro@users.noreply.github.com>
2025-04-30 22:31:47 -06:00
45 changed files with 4941 additions and 2397 deletions
@@ -41,6 +41,8 @@ jobs:
node-version-file: './docs/.nvmrc'
- name: Setup Python
uses: ./.github/actions/setup-backend/
- name: Update openapi docs
run: superset update_api_docs
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
+1 -1
View File
@@ -44,7 +44,7 @@ Here are the build presets that are exposed through the `supersetbot docker` uti
- `py311`, e.g., Py311: Similar to lean but with a different Python version (in this example, 3.11).
- `ci`: For certain CI workloads.
- `websocket`: For Superset clusters supporting advanced features.
- `dockerize`: Used by Helm.
- `dockerize`: Used by Helm in initContainers to wait for database dependencies to be available.
## Key tags examples
+6 -6
View File
@@ -28,14 +28,14 @@
"@superset-ui/style": "^0.14.23",
"antd": "^5.24.5",
"docusaurus-plugin-less": "^2.0.2",
"less": "^4.2.2",
"less": "^4.3.0",
"less-loader": "^11.0.0",
"prism-react-renderer": "^2.4.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-github-btn": "^1.4.0",
"react-svg-pan-zoom": "^3.13.1",
"swagger-ui-react": "^5.20.2"
"swagger-ui-react": "^5.21.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.7.0",
@@ -44,12 +44,12 @@
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.0",
"eslint-config-prettier": "^10.1.1",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.0.0",
"eslint-plugin-react": "^7.37.5",
"prettier": "^2.0.0",
"typescript": "~5.8.2",
"webpack": "^5.98.0"
"typescript": "~5.8.3",
"webpack": "^5.99.7"
},
"browserslist": {
"production": [
+4222 -2067
View File
File diff suppressed because it is too large Load Diff
+43 -89
View File
@@ -3616,7 +3616,7 @@
dependencies:
"@types/istanbul-lib-report" "*"
"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
"@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
@@ -4062,16 +4062,11 @@ acorn-walk@^8.0.0:
dependencies:
acorn "^8.11.0"
acorn@^8.0.0, acorn@^8.0.4, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.8.2:
acorn@^8.0.0, acorn@^8.0.4, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.8.2, acorn@^8.9.0:
version "8.14.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb"
integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==
acorn@^8.9.0:
version "8.14.0"
resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz"
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
address@^1.0.1, address@^1.1.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e"
@@ -4658,7 +4653,7 @@ call-bind@^1.0.7, call-bind@^1.0.8:
get-intrinsic "^1.2.4"
set-function-length "^1.2.2"
call-bound@^1.0.2, call-bound@^1.0.3:
call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a"
integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==
@@ -6264,10 +6259,10 @@ escape-string-regexp@^5.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
eslint-config-prettier@^10.1.1:
version "10.1.1"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.1.tgz#cf0ff6e5c4e7e15f129f1f1ce2a5ecba92dec132"
integrity sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw==
eslint-config-prettier@^10.1.2:
version "10.1.2"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz#31a4b393c40c4180202c27e829af43323bf85276"
integrity sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA==
eslint-plugin-prettier@^4.0.0:
version "4.2.1"
@@ -6276,10 +6271,10 @@ eslint-plugin-prettier@^4.0.0:
dependencies:
prettier-linter-helpers "^1.0.0"
eslint-plugin-react@^7.0.0:
version "7.37.4"
resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz"
integrity sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==
eslint-plugin-react@^7.37.5:
version "7.37.5"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz#2975511472bdda1b272b34d779335c9b0e877065"
integrity sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==
dependencies:
array-includes "^3.1.8"
array.prototype.findlast "^1.2.5"
@@ -6291,7 +6286,7 @@ eslint-plugin-react@^7.0.0:
hasown "^2.0.2"
jsx-ast-utils "^2.4.1 || ^3.0.0"
minimatch "^3.1.2"
object.entries "^1.1.8"
object.entries "^1.1.9"
object.fromentries "^2.0.8"
object.values "^1.2.1"
prop-types "^15.8.1"
@@ -8233,10 +8228,10 @@ less-loader@^11.0.0:
resolved "https://registry.npmjs.org/less-loader/-/less-loader-11.1.4.tgz"
integrity sha512-6/GrYaB6QcW6Vj+/9ZPgKKs6G10YZai/l/eJ4SLwbzqNTBsAqt5hSLVF47TgsiBxV1P6eAU0GYRH3YRuQU9V3A==
less@^4.2.2:
version "4.2.2"
resolved "https://registry.npmjs.org/less/-/less-4.2.2.tgz"
integrity sha512-tkuLHQlvWUTeQ3doAqnHbNn8T6WX1KA8yvbKG9x4VtKtIjHsVKQZCH11zRgAfbDAXC2UNIg/K9BYAAcEzUIrNg==
less@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/less/-/less-4.3.0.tgz#ef0cfc260a9ca8079ed8d0e3512bda8a12c82f2a"
integrity sha512-X9RyH9fvemArzfdP8Pi3irr7lor2Ok4rOttDXBhlwDg+wKQsXOXgHWduAJE1EsF7JJx0w0bcO6BC6tCKKYnXKA==
dependencies:
copy-anything "^2.0.1"
parse-node-version "^1.0.1"
@@ -9436,14 +9431,15 @@ object.assign@^4.1.0, object.assign@^4.1.4, object.assign@^4.1.7:
has-symbols "^1.1.0"
object-keys "^1.1.1"
object.entries@^1.1.8:
version "1.1.8"
resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz"
integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==
object.entries@^1.1.9:
version "1.1.9"
resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.9.tgz#e4770a6a1444afb61bd39f984018b5bede25f8b3"
integrity sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==
dependencies:
call-bind "^1.0.7"
call-bind "^1.0.8"
call-bound "^1.0.4"
define-properties "^1.2.1"
es-object-atoms "^1.0.0"
es-object-atoms "^1.1.1"
object.fromentries@^2.0.8:
version "2.0.8"
@@ -11543,7 +11539,7 @@ resolve-pathname@^3.0.0:
resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
resolve@^1.1.6, resolve@^1.14.2:
resolve@^1.1.6, resolve@^1.12.0, resolve@^1.14.2:
version "1.22.10"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39"
integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==
@@ -11552,15 +11548,6 @@ resolve@^1.1.6, resolve@^1.14.2:
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
resolve@^1.12.0:
version "1.22.8"
resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz"
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
dependencies:
is-core-module "^2.13.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
resolve@^2.0.0-next.5:
version "2.0.0-next.5"
resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz"
@@ -11709,10 +11696,10 @@ schema-utils@^3.0.0:
ajv "^6.12.5"
ajv-keywords "^3.5.2"
schema-utils@^4.0.0, schema-utils@^4.0.1, schema-utils@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.0.tgz#3b669f04f71ff2dfb5aba7ce2d5a9d79b35622c0"
integrity sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==
schema-utils@^4.0.0, schema-utils@^4.0.1, schema-utils@^4.3.0, schema-utils@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.2.tgz#0c10878bf4a73fd2b1dfd14b9462b26788c806ae"
integrity sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==
dependencies:
"@types/json-schema" "^7.0.9"
ajv "^8.9.0"
@@ -12359,10 +12346,10 @@ swagger-client@^3.34.4:
ramda "^0.30.1"
ramda-adjunct "^5.1.0"
swagger-ui-react@^5.20.2:
version "5.20.2"
resolved "https://registry.yarnpkg.com/swagger-ui-react/-/swagger-ui-react-5.20.2.tgz#eb9f8de2e6c916661d8033d43119f6e105ab87da"
integrity sha512-6ifaFjT02yBv1kjEivIMWxQpI7r8O7D/oA8u1JiwhTkom0dOk85lTExao5Dj5ztS6dBg6i1zm+ILhH94fF9g8Q==
swagger-ui-react@^5.21.0:
version "5.21.0"
resolved "https://registry.yarnpkg.com/swagger-ui-react/-/swagger-ui-react-5.21.0.tgz#a9b22a24247c23eafb39b776bee5a4d5b56c57fe"
integrity sha512-lS5paITM1kkcBb/BTTSMHKelh8elHfcuUP4T3R3mO80tDR0AYJL2HR5UdQD6nV1LwdvekzRM8gKjJA6hVayi0A==
dependencies:
"@babel/runtime-corejs3" "^7.26.10"
"@scarf/scarf" "=1.4.0"
@@ -12540,16 +12527,11 @@ tslib@^1.8.1:
resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.3, tslib@^2.6.0:
tslib@^2.0.3, tslib@^2.3.0, tslib@^2.6.0:
version "2.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
tslib@^2.3.0:
version "2.6.2"
resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
tsutils@^3.21.0:
version "3.21.0"
resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz"
@@ -12651,10 +12633,10 @@ types-ramda@^0.30.0:
dependencies:
ts-toolbelt "^9.6.0"
typescript@~5.8.2:
version "5.8.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.2.tgz#8170b3702f74b79db2e5a96207c15e65807999e4"
integrity sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==
typescript@~5.8.3:
version "5.8.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e"
integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==
ufo@^1.5.4:
version "1.6.1"
@@ -13063,13 +13045,14 @@ webpack-sources@^3.2.3:
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
webpack@^5.88.1:
version "5.99.5"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.99.5.tgz#86e3b3a5a03377ea5da271c929934003f5ac5dd8"
integrity sha512-q+vHBa6H9qwBLUlHL4Y7L0L1/LlyBKZtS9FHNCQmtayxjI5RKC9yD8gpvLeqGv5lCQp1Re04yi0MF40pf30Pvg==
webpack@^5.88.1, webpack@^5.95.0, webpack@^5.99.7:
version "5.99.7"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.99.7.tgz#60201c1ca66da046b07d006c2f6e0cc5e8a7bdba"
integrity sha512-CNqKBRMQjwcmKR0idID5va1qlhrqVUKpovi+Ec79ksW8ux7iS1+A6VqzfZXgVYCFRKl7XL5ap3ZoMpwBJxcg0w==
dependencies:
"@types/eslint-scope" "^3.7.7"
"@types/estree" "^1.0.6"
"@types/json-schema" "^7.0.15"
"@webassemblyjs/ast" "^1.14.1"
"@webassemblyjs/wasm-edit" "^1.14.1"
"@webassemblyjs/wasm-parser" "^1.14.1"
@@ -13086,36 +13069,7 @@ webpack@^5.88.1:
loader-runner "^4.2.0"
mime-types "^2.1.27"
neo-async "^2.6.2"
schema-utils "^4.3.0"
tapable "^2.1.1"
terser-webpack-plugin "^5.3.11"
watchpack "^2.4.1"
webpack-sources "^3.2.3"
webpack@^5.95.0, webpack@^5.98.0:
version "5.98.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.98.0.tgz#44ae19a8f2ba97537978246072fb89d10d1fbd17"
integrity sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==
dependencies:
"@types/eslint-scope" "^3.7.7"
"@types/estree" "^1.0.6"
"@webassemblyjs/ast" "^1.14.1"
"@webassemblyjs/wasm-edit" "^1.14.1"
"@webassemblyjs/wasm-parser" "^1.14.1"
acorn "^8.14.0"
browserslist "^4.24.0"
chrome-trace-event "^1.0.2"
enhanced-resolve "^5.17.1"
es-module-lexer "^1.2.1"
eslint-scope "5.1.1"
events "^3.2.0"
glob-to-regexp "^0.4.1"
graceful-fs "^4.2.11"
json-parse-even-better-errors "^2.3.1"
loader-runner "^4.2.0"
mime-types "^2.1.27"
neo-async "^2.6.2"
schema-utils "^4.3.0"
schema-utils "^4.3.2"
tapable "^2.1.1"
terser-webpack-plugin "^5.3.11"
watchpack "^2.4.1"
+4 -2
View File
@@ -44,7 +44,7 @@ dependencies = [
"cryptography>=42.0.4, <45.0.0",
"deprecation>=2.1.0, <2.2.0",
"flask>=2.2.5, <3.0.0",
"flask-appbuilder>=4.6.1, <5.0.0",
"flask-appbuilder>=4.6.3, <5.0.0",
"flask-caching>=2.1.0, <3",
"flask-compress>=1.13, <2.0",
"flask-talisman>=1.0.0, <2.0",
@@ -81,7 +81,7 @@ dependencies = [
"python-dateutil",
"python-dotenv", # optional dependencies for Flask but required for Superset, see https://flask.palletsprojects.com/en/stable/installation/#optional-dependencies
"python-geohash",
"pyarrow>=14.0.1, <15",
"pyarrow>=18.1.0, <19",
"pyyaml>=6.0.0, <7.0.0",
"PyJWT>=2.4.0, <3.0",
"redis>=4.6.0, <5.0",
@@ -371,12 +371,14 @@ authorized_licenses = [
"apache software",
"apache software, bsd",
"bsd",
"bsd-3-clause",
"isc license (iscl)",
"isc license",
"mit",
"mozilla public license 2.0 (mpl 2.0)",
"osi approved",
"osi approved",
"psf-2.0",
"python software foundation",
"the unlicense (unlicense)",
"the unlicense",
+7 -8
View File
@@ -82,7 +82,7 @@ cron-descriptor==1.4.5
# via apache-superset (pyproject.toml)
croniter==6.0.0
# via apache-superset (pyproject.toml)
cryptography==44.0.2
cryptography==44.0.3
# via
# apache-superset (pyproject.toml)
# paramiko
@@ -118,7 +118,7 @@ flask==2.3.3
# flask-session
# flask-sqlalchemy
# flask-wtf
flask-appbuilder==4.6.1
flask-appbuilder==4.6.3
# via apache-superset (pyproject.toml)
flask-babel==2.0.0
# via flask-appbuilder
@@ -161,13 +161,13 @@ greenlet==3.1.1
# sqlalchemy
gunicorn==23.0.0
# via apache-superset (pyproject.toml)
h11==0.14.0
h11==0.16.0
# via wsproto
hashids==1.3.1
# via apache-superset (pyproject.toml)
holidays==0.25
# via apache-superset (pyproject.toml)
humanize==4.12.2
humanize==4.12.3
# via apache-superset (pyproject.toml)
idna==3.10
# via
@@ -202,7 +202,7 @@ mako==1.3.10
# via
# apache-superset (pyproject.toml)
# alembic
markdown==3.7
markdown==3.8
# via apache-superset (pyproject.toml)
markdown-it-py==3.0.0
# via rich
@@ -236,7 +236,6 @@ numpy==1.26.4
# bottleneck
# numexpr
# pandas
# pyarrow
odfpy==1.4.1
# via pandas
openpyxl==3.1.5
@@ -274,7 +273,7 @@ prison==0.2.1
# via flask-appbuilder
prompt-toolkit==3.0.51
# via click-repl
pyarrow==14.0.2
pyarrow==18.1.0
# via apache-superset (pyproject.toml)
pyasn1==0.6.1
# via
@@ -374,7 +373,7 @@ sqlalchemy-utils==0.38.3
# via
# apache-superset (pyproject.toml)
# flask-appbuilder
sqlglot==26.16.2
sqlglot==26.16.4
# via apache-superset (pyproject.toml)
sqlparse==0.5.3
# via apache-superset (pyproject.toml)
+7 -8
View File
@@ -138,7 +138,7 @@ croniter==6.0.0
# via
# -c requirements/base.txt
# apache-superset
cryptography==44.0.2
cryptography==44.0.3
# via
# -c requirements/base.txt
# apache-superset
@@ -202,7 +202,7 @@ flask==2.3.3
# flask-sqlalchemy
# flask-testing
# flask-wtf
flask-appbuilder==4.6.1
flask-appbuilder==4.6.3
# via
# -c requirements/base.txt
# apache-superset
@@ -330,7 +330,7 @@ gunicorn==23.0.0
# via
# -c requirements/base.txt
# apache-superset
h11==0.14.0
h11==0.16.0
# via
# -c requirements/base.txt
# wsproto
@@ -343,7 +343,7 @@ holidays==0.25
# -c requirements/base.txt
# apache-superset
# prophet
humanize==4.12.2
humanize==4.12.3
# via
# -c requirements/base.txt
# apache-superset
@@ -415,7 +415,7 @@ mako==1.3.10
# -c requirements/base.txt
# alembic
# apache-superset
markdown==3.7
markdown==3.8
# via
# -c requirements/base.txt
# apache-superset
@@ -473,7 +473,6 @@ numpy==1.26.4
# pandas
# pandas-gbq
# prophet
# pyarrow
oauthlib==3.2.2
# via requests-oauthlib
odfpy==1.4.1
@@ -587,7 +586,7 @@ psutil==6.1.0
# via apache-superset
psycopg2-binary==2.9.6
# via apache-superset
pyarrow==14.0.2
pyarrow==18.1.0
# via
# -c requirements/base.txt
# apache-superset
@@ -800,7 +799,7 @@ sqlalchemy-utils==0.38.3
# -c requirements/base.txt
# apache-superset
# flask-appbuilder
sqlglot==26.16.2
sqlglot==26.16.4
# via
# -c requirements/base.txt
# apache-superset
+76 -62
View File
@@ -23,7 +23,7 @@
"@reduxjs/toolkit": "^1.9.3",
"@rjsf/core": "^5.21.1",
"@rjsf/utils": "^5.24.3",
"@rjsf/validator-ajv8": "^5.22.3",
"@rjsf/validator-ajv8": "^5.24.9",
"@scarf/scarf": "^1.4.0",
"@superset-ui/chart-controls": "file:./packages/superset-ui-chart-controls",
"@superset-ui/core": "file:./packages/superset-ui-core",
@@ -115,7 +115,7 @@
"react-dom": "^17.0.2",
"react-draggable": "^4.4.6",
"react-hot-loader": "^4.13.1",
"react-intersection-observer": "^9.10.2",
"react-intersection-observer": "^9.16.0",
"react-js-cron": "^2.1.2",
"react-json-tree": "^0.17.0",
"react-lines-ellipsis": "^0.15.4",
@@ -162,7 +162,7 @@
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-modules-commonjs": "^7.26.3",
"@babel/plugin-transform-runtime": "^7.25.9",
"@babel/plugin-transform-runtime": "^7.27.1",
"@babel/preset-env": "^7.26.7",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
@@ -1146,14 +1146,14 @@
}
},
"node_modules/@babel/code-frame": {
"version": "7.26.2",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.25.9",
"@babel/helper-validator-identifier": "^7.27.1",
"js-tokens": "^4.0.0",
"picocolors": "^1.0.0"
"picocolors": "^1.1.1"
},
"engines": {
"node": ">=6.9.0"
@@ -1240,13 +1240,13 @@
}
},
"node_modules/@babel/generator": {
"version": "7.26.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz",
"integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==",
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz",
"integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.26.5",
"@babel/types": "^7.26.5",
"@babel/parser": "^7.27.1",
"@babel/types": "^7.27.1",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^3.0.2"
@@ -1387,13 +1387,13 @@
}
},
"node_modules/@babel/helper-module-imports": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
"integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
"license": "MIT",
"dependencies": {
"@babel/traverse": "^7.25.9",
"@babel/types": "^7.25.9"
"@babel/traverse": "^7.27.1",
"@babel/types": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
@@ -1431,9 +1431,9 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
"version": "7.26.5",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz",
"integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==",
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
"integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1491,18 +1491,18 @@
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
"integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
"integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -1572,12 +1572,12 @@
}
},
"node_modules/@babel/parser": {
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz",
"integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==",
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.1.tgz",
"integrity": "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.26.7"
"@babel/types": "^7.27.1"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -2849,16 +2849,16 @@
}
},
"node_modules/@babel/plugin-transform-runtime": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz",
"integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==",
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.27.1.tgz",
"integrity": "sha512-TqGF3desVsTcp3WrJGj4HfKokfCXCLcHpt4PJF0D8/iT6LPd9RS82Upw3KPeyr6B22Lfd3DO8MVrmp0oRkUDdw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.25.9",
"@babel/helper-plugin-utils": "^7.25.9",
"@babel/helper-module-imports": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1",
"babel-plugin-polyfill-corejs2": "^0.4.10",
"babel-plugin-polyfill-corejs3": "^0.10.6",
"babel-plugin-polyfill-corejs3": "^0.11.0",
"babel-plugin-polyfill-regenerator": "^0.6.1",
"semver": "^6.3.1"
},
@@ -2869,6 +2869,20 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-transform-runtime/node_modules/babel-plugin-polyfill-corejs3": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz",
"integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-define-polyfill-provider": "^0.6.3",
"core-js-compat": "^3.40.0"
},
"peerDependencies": {
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
}
},
"node_modules/@babel/plugin-transform-runtime/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@@ -3279,30 +3293,30 @@
}
},
"node_modules/@babel/template": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.1.tgz",
"integrity": "sha512-Fyo3ghWMqkHHpHQCoBs2VnYjR4iWFFjguTDEqA5WgZDOrFesVjMhMM2FSqTKSoUSDO1VQtavj8NFpdRBEvJTtg==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.25.9",
"@babel/parser": "^7.25.9",
"@babel/types": "^7.25.9"
"@babel/code-frame": "^7.27.1",
"@babel/parser": "^7.27.1",
"@babel/types": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz",
"integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==",
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz",
"integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.26.2",
"@babel/generator": "^7.26.5",
"@babel/parser": "^7.26.7",
"@babel/template": "^7.25.9",
"@babel/types": "^7.26.7",
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.27.1",
"@babel/parser": "^7.27.1",
"@babel/template": "^7.27.1",
"@babel/types": "^7.27.1",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
@@ -3311,13 +3325,13 @@
}
},
"node_modules/@babel/types": {
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz",
"integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==",
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz",
"integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
@@ -8498,9 +8512,9 @@
}
},
"node_modules/@rjsf/validator-ajv8": {
"version": "5.24.1",
"resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.24.1.tgz",
"integrity": "sha512-p6URehglU9yFUAoQXE1ryqZjLYSjc6qdbiUfCVvEFAzUuMECsIFomz2hH3CPlt10K72sAFdzwVvrKn1iWTnxDw==",
"version": "5.24.9",
"resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.24.9.tgz",
"integrity": "sha512-leHb39Qa612QhAfvw36qi/ubWa7LQ6hrPN4Ge93QBlWywRfV/M0Wmx9bPccCGgIL4Qnn1Wmt53EWV8kQT28xTA==",
"license": "Apache-2.0",
"dependencies": {
"ajv": "^8.12.0",
@@ -39476,9 +39490,9 @@
}
},
"node_modules/react-intersection-observer": {
"version": "9.15.1",
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.15.1.tgz",
"integrity": "sha512-vGrqYEVWXfH+AGu241uzfUpNK4HAdhCkSAyFdkMb9VWWXs6mxzBLpWCxEy9YcnDNY2g9eO6z7qUtTBdA9hc8pA==",
"version": "9.16.0",
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.16.0.tgz",
"integrity": "sha512-w9nJSEp+DrW9KmQmeWHQyfaP6b03v+TdXynaoA964Wxt7mdR3An11z4NNCQgL4gKSK7y1ver2Fq+JKH6CWEzUA==",
"license": "MIT",
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+3 -3
View File
@@ -90,7 +90,7 @@
"@reduxjs/toolkit": "^1.9.3",
"@rjsf/core": "^5.21.1",
"@rjsf/utils": "^5.24.3",
"@rjsf/validator-ajv8": "^5.22.3",
"@rjsf/validator-ajv8": "^5.24.9",
"@scarf/scarf": "^1.4.0",
"@superset-ui/chart-controls": "file:./packages/superset-ui-chart-controls",
"@superset-ui/core": "file:./packages/superset-ui-core",
@@ -182,7 +182,7 @@
"react-dom": "^17.0.2",
"react-draggable": "^4.4.6",
"react-hot-loader": "^4.13.1",
"react-intersection-observer": "^9.10.2",
"react-intersection-observer": "^9.16.0",
"react-js-cron": "^2.1.2",
"react-json-tree": "^0.17.0",
"react-lines-ellipsis": "^0.15.4",
@@ -229,7 +229,7 @@
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-modules-commonjs": "^7.26.3",
"@babel/plugin-transform-runtime": "^7.25.9",
"@babel/plugin-transform-runtime": "^7.27.1",
"@babel/preset-env": "^7.26.7",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
@@ -21,7 +21,6 @@ import { Dataset } from './types';
export const TestDataset: Dataset = {
column_formats: {},
currency_formats: {},
columns: [
{
advanced_data_type: undefined,
@@ -69,7 +69,6 @@ export interface Dataset {
columns: ColumnMeta[];
metrics: Metric[];
column_formats: Record<string, string>;
currency_formats: Record<string, Currency>;
verbose_map: Record<string, string>;
main_dttm_col: string;
// eg. ['["ds", true]', 'ds [asc]']
@@ -53,7 +53,6 @@ describe('columnChoices()', () => {
],
verbose_map: {},
column_formats: { fiz: 'NUMERIC', about: 'STRING', foo: 'DATE' },
currency_formats: {},
datasource_name: 'my_datasource',
description: 'this is my datasource',
}),
@@ -105,7 +104,6 @@ describe('columnChoices()', () => {
],
verbose_map: {},
column_formats: { fiz: 'NUMERIC', about: 'STRING', foo: 'DATE' },
currency_formats: {},
datasource_name: 'my_datasource',
description: 'this is my datasource',
}),
@@ -41,7 +41,6 @@ describe('defineSavedMetrics', () => {
columns: [],
verbose_map: {},
column_formats: {},
currency_formats: {},
datasource_name: 'my_datasource',
description: 'this is my datasource',
};
@@ -60,7 +60,7 @@
"@storybook/react-webpack5": "8.2.9",
"babel-loader": "^9.1.3",
"fork-ts-checker-webpack-plugin": "^9.0.2",
"ts-loader": "^9.5.1",
"ts-loader": "^9.5.2",
"typescript": "^5.7.2"
},
"peerDependencies": {
@@ -440,7 +440,7 @@ const Select = forwardRef(
const bulkSelectComponent = useMemo(
() => (
<StyledBulkActionsContainer size={0}>
<StyledBulkActionsContainer className="select-bulk-actions" size={0}>
<Button
type="link"
buttonSize="xsmall"
@@ -128,6 +128,14 @@ const VerticalFormItem = styled(StyledFormItem)<{
width: 140px;
`}
}
.select-bulk-actions {
${({ inverseSelection }) =>
inverseSelection &&
`
flex-direction: column;
`}
}
`;
const HorizontalFormItem = styled(StyledFormItem)<{
@@ -164,6 +172,10 @@ const HorizontalFormItem = styled(StyledFormItem)<{
width: 164px;
`}
}
.select-bulk-actions {
flex-direction: column;
}
`;
const HorizontalOverflowFormItem = VerticalFormItem;
@@ -99,6 +99,7 @@ export function ColumnSelect({
'columns.column_name',
'columns.is_dttm',
'columns.type_generic',
'columns.filterable',
],
})}`,
})
@@ -20,11 +20,38 @@ import { Filter, NativeFilterType } from '@superset-ui/core';
import { render, screen, userEvent } from 'spec/helpers/testing-library';
import { FormInstance } from 'src/components';
import getControlItemsMap, { ControlItemsProps } from './getControlItemsMap';
import { getControlItems, setNativeFilterFieldValues } from './utils';
import {
getControlItems,
setNativeFilterFieldValues,
doesColumnMatchFilterType,
} from './utils';
jest.mock('./utils', () => ({
getControlItems: jest.fn(),
setNativeFilterFieldValues: jest.fn(),
doesColumnMatchFilterType: jest.fn(),
}));
// Mock ColumnSelect to test filterValues logic
jest.mock('./ColumnSelect', () => ({
ColumnSelect: ({
filterValues,
}: {
filterValues: (column: any) => boolean;
}) => {
const columns = [
{ name: 'col1', filterable: true },
{ name: 'col2', filterable: false },
{ name: 'col3', filterable: true },
];
return (
<>
{columns.filter(filterValues).map(column => (
<div key={column.name}>{column.name}</div>
))}
</>
);
},
}));
const formMock: FormInstance = {
@@ -62,7 +89,7 @@ const filterMock: Filter = {
description: '',
};
const createProps: () => ControlItemsProps = () => ({
const createProps = (): ControlItemsProps => ({
expanded: false,
datasetId: 1,
disabled: false,
@@ -179,3 +206,44 @@ test('Clicking on checkbox when resetConfig:false', () => {
expect(props.forceUpdate).toHaveBeenCalled();
expect(setNativeFilterFieldValues).not.toHaveBeenCalled();
});
describe('ColumnSelect filterValues behavior', () => {
beforeEach(() => {
(getControlItems as jest.Mock).mockReturnValue([
{
name: 'groupby',
config: { label: 'Column', multiple: false, required: false },
},
]);
});
test('only renders filterable columns when doesColumnMatchFilterType returns true', () => {
(doesColumnMatchFilterType as jest.Mock).mockReturnValue(true);
const props = {
...createProps(),
formFilter: { filterType: 'filterType' },
};
// @ts-ignore: bypass incomplete formFilter type for test
const element = getControlItemsMap(props).mainControlItems.groupby
.element as React.ReactElement;
render(element);
expect(screen.getByText('col1')).toBeInTheDocument();
expect(screen.getByText('col3')).toBeInTheDocument();
expect(screen.queryByText('col2')).not.toBeInTheDocument();
});
test('renders no columns when doesColumnMatchFilterType returns false', () => {
(doesColumnMatchFilterType as jest.Mock).mockReturnValue(false);
const props = {
...createProps(),
formFilter: { filterType: 'filterType' },
};
// @ts-ignore: bypass incomplete formFilter type for test
const element = getControlItemsMap(props).mainControlItems.groupby
.element as React.ReactElement;
render(element);
expect(screen.queryByText('col1')).not.toBeInTheDocument();
expect(screen.queryByText('col3')).not.toBeInTheDocument();
expect(screen.queryByText('col2')).not.toBeInTheDocument();
});
});
@@ -131,7 +131,10 @@ export default function getControlItemsMap({
filterId={filterId}
datasetId={datasetId}
filterValues={column =>
doesColumnMatchFilterType(formFilter?.filterType || '', column)
doesColumnMatchFilterType(
formFilter?.filterType || '',
column,
) && !!column?.filterable
}
onChange={() => {
// We need reset default value when column changed
@@ -29,7 +29,6 @@ export const PLACEHOLDER_DATASOURCE: Datasource = {
column_types: [],
metrics: [],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '',
description: '',
+25
View File
@@ -27,10 +27,13 @@ import {
Filter,
FilterConfiguration,
Filters,
FilterState,
ExtraFormData,
} from '@superset-ui/core';
import { NATIVE_FILTER_PREFIX } from 'src/dashboard/components/nativeFilters/FiltersConfigModal/utils';
import { HYDRATE_DASHBOARD } from 'src/dashboard/actions/hydrate';
import { SaveFilterChangesType } from 'src/dashboard/components/nativeFilters/FiltersConfigModal/types';
import { isEqual } from 'lodash';
import {
AnyDataMaskAction,
CLEAR_DATA_MASK_STATE,
@@ -39,6 +42,11 @@ import {
} from './actions';
import { areObjectsEqual } from '../reduxUtils';
type FilterWithExtaFromData = Filter & {
extraFormData?: ExtraFormData;
filterState?: FilterState;
};
export function getInitialDataMask(
id?: string | number,
moreProps: DataMask = {},
@@ -106,10 +114,27 @@ function updateDataMaskForFilterChanges(
});
filterChanges.modified.forEach((filter: Filter) => {
const existingFilter = draftDataMask[filter.id] as FilterWithExtaFromData;
// Check if targets are equal
const areTargetsEqual = isEqual(existingFilter?.targets, filter?.targets);
// Preserve state only if filter exists, has enableEmptyFilter=true and targets match
const shouldPreserveState =
existingFilter &&
areTargetsEqual &&
(filter.controlValues?.enableEmptyFilter ||
filter.controlValues?.defaultToFirstItem);
mergedDataMask[filter.id] = {
...getInitialDataMask(filter.id),
...filter.defaultDataMask,
...filter,
// Preserve extraFormData and filterState if conditions match
...(shouldPreserveState && {
extraFormData: existingFilter.extraFormData,
filterState: existingFilter.filterState,
}),
};
});
@@ -41,7 +41,6 @@ const CURRENT_DATASOURCE = {
columns: [],
metrics: [],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '__timestamp',
// eg. ['["ds", true]', 'ds [asc]']
@@ -55,7 +54,6 @@ const NEW_DATASOURCE = {
columns: [],
metrics: [],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '__timestamp',
// eg. ['["ds", true]', 'ds [asc]']
@@ -56,7 +56,6 @@ describe('controlUtils', () => {
{ metric_name: 'second', uuid: '2' },
],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '',
datasource_name: '1__table',
@@ -35,7 +35,6 @@ const sampleDatasource: Dataset = {
],
metrics: [{ metric_name: 'saved_metric_2', uuid: '1' }],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '',
datasource_name: 'Sample Dataset',
@@ -138,7 +138,6 @@ export const exploreInitialData: ExplorePageInitialData = {
{ metric_name: 'second', uuid: '2' },
],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '',
datasource_name: '8__table',
@@ -157,7 +156,6 @@ export const fallbackExploreInitialData: ExplorePageInitialData = {
columns: [],
metrics: [],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '',
owners: [],
@@ -78,7 +78,6 @@ export type DatasetObject = {
always_filter_main_dttm: boolean;
type: DatasourceType;
column_formats: Record<string, string>;
currency_formats: Record<string, Currency>;
datasource_name: string | null;
verbose_map: Record<string, string>;
};
@@ -17,7 +17,7 @@
* under the License.
*/
/* eslint-disable no-param-reassign */
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
AppSection,
DataMask,
@@ -84,7 +84,10 @@ function reducer(draft: DataMask, action: DataMaskAction) {
}
}
const StyledSpace = styled(Space)<{ $inverseSelection: boolean }>`
const StyledSpace = styled(Space)<{
inverseSelection: boolean;
appSection: AppSection;
}>`
display: flex;
align-items: center;
width: 100%;
@@ -96,12 +99,17 @@ const StyledSpace = styled(Space)<{ $inverseSelection: boolean }>`
&.ant-space {
.ant-space-item {
width: ${({ $inverseSelection }) =>
!$inverseSelection ? '100%' : 'auto'};
width: ${({ inverseSelection, appSection }) =>
!inverseSelection || appSection === AppSection.FilterConfigModal
? '100%'
: 'auto'};
}
}
`;
// Keep track of orientation changes outside component with filter ID
const orientationMap = new Map<string, FilterBarOrientation>();
export default function PluginFilterSelect(props: PluginFilterSelectProps) {
const {
coltypeMap,
@@ -158,6 +166,30 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
: filterState?.excludeFilterValues,
);
const prevExcludeFilterValues = useRef(excludeFilterValues);
const hasOnlyOrientationChanged = useRef(false);
useEffect(() => {
// Get previous orientation for this specific filter
const previousOrientation = orientationMap.get(formData.nativeFilterId);
// Check if only orientation changed for this filter
if (
previousOrientation !== undefined &&
previousOrientation !== filterBarOrientation
) {
hasOnlyOrientationChanged.current = true;
} else {
hasOnlyOrientationChanged.current = false;
}
// Update orientation for this filter
if (filterBarOrientation) {
orientationMap.set(formData.nativeFilterId, filterBarOrientation);
}
}, [filterBarOrientation]);
const updateDataMask = useCallback(
(values: SelectValue) => {
const emptyFilter =
@@ -287,35 +319,44 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
[formData.sortAscending],
);
// Use effect for initialisation for filter plugin
// this should run only once when filter is configured & saved
// & shouldnt run when the component is remounted on change of
// orientation of filter bar
useEffect(() => {
if (defaultToFirstItem && filterState.value === undefined) {
// initialize to first value if set to default to first item
// Skip if only orientation changed
if (hasOnlyOrientationChanged.current) {
return;
}
// Case 1: Handle disabled state first
if (isDisabled) {
updateDataMask(null);
return;
}
// Case 2: Handle the default to first Value case
if (defaultToFirstItem) {
// Set to first item if defaultToFirstItem is true
const firstItem: SelectValue = data[0]
? (groupby.map(col => data[0][col]) as string[])
: null;
// firstItem[0] !== undefined for a case when groupby changed but new data still not fetched
// TODO: still need repopulate default value in config modal when column changed
if (firstItem?.[0] !== undefined) {
updateDataMask(firstItem);
}
} else if (isDisabled) {
// empty selection if filter is disabled
updateDataMask(null);
} else {
// reset data mask based on filter state
updateDataMask(filterState.value);
} else if (formData?.defaultValue) {
// Case 3 : Handle defalut value case
updateDataMask(formData.defaultValue);
}
}, [
col,
isDisabled,
defaultToFirstItem,
enableEmptyFilter,
inverseSelection,
excludeFilterValues,
updateDataMask,
defaultToFirstItem,
formData?.defaultValue,
data,
groupby,
JSON.stringify(filterState.value),
col,
inverseSelection,
]);
useEffect(() => {
@@ -323,23 +364,26 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
}, [JSON.stringify(dataMask)]);
useEffect(() => {
dispatchDataMask({
type: 'filterState',
extraFormData: getSelectExtraFormData(
col,
filterState.value,
!filterState.value?.length,
excludeFilterValues && inverseSelection,
),
filterState: {
...(filterState as {
value: SelectValue;
label?: string;
excludeFilterValues?: boolean;
}),
excludeFilterValues,
},
});
if (prevExcludeFilterValues.current !== excludeFilterValues) {
dispatchDataMask({
type: 'filterState',
extraFormData: getSelectExtraFormData(
col,
filterState.value,
!filterState.value?.length,
excludeFilterValues && inverseSelection,
),
filterState: {
...(filterState as {
value: SelectValue;
label?: string;
excludeFilterValues?: boolean;
}),
excludeFilterValues,
},
});
prevExcludeFilterValues.current = excludeFilterValues;
}
}, [excludeFilterValues]);
const handleExclusionToggle = (value: string) => {
@@ -352,8 +396,11 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
validateStatus={filterState.validateStatus}
extra={formItemExtra}
>
<StyledSpace $inverseSelection={inverseSelection}>
{inverseSelection && (
<StyledSpace
appSection={appSection}
inverseSelection={inverseSelection}
>
{appSection !== AppSection.FilterConfigModal && inverseSelection && (
<Select
className="exclude-select"
value={`${excludeFilterValues}`}
@@ -26,7 +26,6 @@ const TEST_DATASOURCE = {
columns: [],
metrics: [],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '__timestamp',
// eg. ['["ds", true]', 'ds [asc]']
+32 -14
View File
@@ -14,12 +14,12 @@
"ioredis": "^4.28.0",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"uuid": "^11.0.2",
"uuid": "^11.1.0",
"winston": "^3.17.0",
"ws": "^8.18.0"
},
"devDependencies": {
"@eslint/js": "^9.16.0",
"@eslint/js": "^9.25.1",
"@types/cookie": "^0.6.0",
"@types/eslint__js": "^8.42.3",
"@types/ioredis": "^4.27.8",
@@ -826,10 +826,11 @@
}
},
"node_modules/@eslint/js": {
"version": "9.17.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz",
"integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==",
"version": "9.25.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz",
"integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
@@ -3279,6 +3280,16 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/@eslint/js": {
"version": "9.17.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz",
"integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/eslint/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -6811,13 +6822,14 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/uuid": {
"version": "11.0.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz",
"integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==",
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/esm/bin/uuid"
}
@@ -7615,9 +7627,9 @@
}
},
"@eslint/js": {
"version": "9.17.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz",
"integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==",
"version": "9.25.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz",
"integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==",
"dev": true
},
"@eslint/object-schema": {
@@ -9388,6 +9400,12 @@
"optionator": "^0.9.3"
},
"dependencies": {
"@eslint/js": {
"version": "9.17.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz",
"integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==",
"dev": true
},
"escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -12007,9 +12025,9 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"uuid": {
"version": "11.0.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz",
"integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ=="
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="
},
"v8-compile-cache-lib": {
"version": "3.0.1",
+2 -2
View File
@@ -22,12 +22,12 @@
"ioredis": "^4.28.0",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"uuid": "^11.0.2",
"uuid": "^11.1.0",
"winston": "^3.17.0",
"ws": "^8.18.0"
},
"devDependencies": {
"@eslint/js": "^9.16.0",
"@eslint/js": "^9.25.1",
"@types/cookie": "^0.6.0",
"@types/eslint__js": "^8.42.3",
"@types/ioredis": "^4.27.8",
+1 -16
View File
@@ -313,10 +313,6 @@ class BaseDatasource(AuditMixinNullable, ImportExportMixin): # pylint: disable=
def column_formats(self) -> dict[str, str | None]:
return {m.metric_name: m.d3format for m in self.metrics if m.d3format}
@property
def currency_formats(self) -> dict[str, dict[str, str | None] | None]:
return {m.metric_name: m.currency_json for m in self.metrics if m.currency_json}
def add_missing_metrics(self, metrics: list[SqlMetric]) -> None:
existing_metrics = {m.metric_name for m in self.metrics}
for metric in metrics:
@@ -379,7 +375,6 @@ class BaseDatasource(AuditMixinNullable, ImportExportMixin): # pylint: disable=
"id": self.id,
"uid": self.uid,
"column_formats": self.column_formats,
"currency_formats": self.currency_formats,
"description": self.description,
"database": self.database.data, # pylint: disable=no-member
"default_endpoint": self.default_endpoint,
@@ -1046,7 +1041,7 @@ class SqlMetric(AuditMixinNullable, ImportExportMixin, CertificationMixin, Model
metric_type = Column(String(32))
description = Column(utils.MediumText())
d3format = Column(String(128))
currency = Column(String(128))
currency = Column(JSON, nullable=True)
warning_text = Column(Text)
table_id = Column(Integer, ForeignKey("tables.id", ondelete="CASCADE"))
expression = Column(utils.MediumText(), nullable=False)
@@ -1101,16 +1096,6 @@ class SqlMetric(AuditMixinNullable, ImportExportMixin, CertificationMixin, Model
def get_perm(self) -> str | None:
return self.perm
@property
def currency_json(self) -> dict[str, str | None] | None:
try:
return json.loads(self.currency or "{}") or None
except (TypeError, json.JSONDecodeError) as exc:
logger.error(
"Unable to load currency json: %r. Leaving empty.", exc, exc_info=True
)
return None
@property
def data(self) -> dict[str, Any]:
attrs = (
-1
View File
@@ -259,7 +259,6 @@ class DashboardDatasetSchema(Schema):
id = fields.Int()
uid = fields.Str()
column_formats = fields.Dict()
currency_formats = fields.Dict()
database = fields.Nested(DatabaseSchema)
default_endpoint = fields.String()
filter_select = fields.Bool()
-1
View File
@@ -221,7 +221,6 @@ class DatasetRestApi(BaseSupersetModelRestApi):
"datasource_name",
"name",
"column_formats",
"currency_formats",
"granularity_sqla",
"time_grain_sqla",
"order_by_choices",
+57 -8
View File
@@ -14,6 +14,34 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Helpers for loading Superset example datasets.
All Superset example data files (CSV, JSON, etc.) are fetched via the
jsDelivr CDN instead of raw.githubusercontent.com to avoid GitHub API
rate limits (60 anonymous requests/hour/IP).
jsDelivr is a multiCDN front for public GitHub repos and supports
arbitrary paths including nested folders. It doesnt use the GitHub REST API
and advertises unlimited bandwidth for open-source use.
Example URL::
https://cdn.jsdelivr.net/gh/apache-superset/examples-data@master/datasets/examples/slack/messages.csv
Environment knobs
-----------------
``SUPERSET_EXAMPLES_DATA_REF`` (default: ``master``)
Tag / branch / SHA to pin so builds remain reproducible.
``SUPERSET_EXAMPLES_BASE_URL``
Override the base completely if you want to host the files elsewhere
(internal mirror, S3 bucket, ASF downloads, ). **Include any query
string required by your hosting (e.g. ``?raw=true`` if you point back
to a GitHub *blob* URL).**
"""
from __future__ import annotations
import os
from typing import Any
@@ -22,27 +50,41 @@ from superset.connectors.sqla.models import SqlaTable
from superset.models.slice import Slice
from superset.utils import json
BASE_URL = "https://github.com/apache-superset/examples-data/blob/master/"
# ---------------------------------------------------------------------------
# Public sampledata mirror configuration
# ---------------------------------------------------------------------------
BASE_COMMIT: str = os.getenv("SUPERSET_EXAMPLES_DATA_REF", "master")
BASE_URL: str = os.getenv(
"SUPERSET_EXAMPLES_BASE_URL",
f"https://cdn.jsdelivr.net/gh/apache-superset/examples-data@{BASE_COMMIT}/",
)
misc_dash_slices: set[str] = set() # slices assembled in a 'Misc Chart' dashboard
# Slices assembled into a 'Misc Chart' dashboard
misc_dash_slices: set[str] = set()
# ---------------------------------------------------------------------------
# Utility functions
# ---------------------------------------------------------------------------
def get_table_connector_registry() -> Any:
"""Return the SqlaTable registry so we can mock it in unit tests."""
return SqlaTable
def get_examples_folder() -> str:
"""Return local path to the examples folder (when vendored)."""
return os.path.join(app.config["BASE_DIR"], "examples")
def update_slice_ids(pos: dict[Any, Any]) -> list[Slice]:
"""Update slice ids in position_json and return the slices found."""
"""Update slice ids in ``position_json`` and return the slices found."""
slice_components = [
component
for component in pos.values()
if isinstance(component, dict) and component.get("type") == "CHART"
]
slices = {}
slices: dict[str, Slice] = {}
for name in {component["meta"]["sliceName"] for component in slice_components}:
slc = db.session.query(Slice).filter_by(slice_name=name).first()
if slc:
@@ -56,17 +98,24 @@ def update_slice_ids(pos: dict[Any, Any]) -> list[Slice]:
def merge_slice(slc: Slice) -> None:
o = db.session.query(Slice).filter_by(slice_name=slc.slice_name).first()
if o:
db.session.delete(o)
"""Upsert a Slice by name."""
existing = db.session.query(Slice).filter_by(slice_name=slc.slice_name).first()
if existing:
db.session.delete(existing)
db.session.add(slc)
def get_slice_json(defaults: dict[Any, Any], **kwargs: Any) -> str:
"""Return JSON string for a chart definition, merging extra kwargs."""
defaults_copy = defaults.copy()
defaults_copy.update(kwargs)
return json.dumps(defaults_copy, indent=4, sort_keys=True)
def get_example_url(filepath: str) -> str:
return f"{BASE_URL}{filepath}?raw=true"
"""Return an absolute URL to *filepath* under the examplesdata repo.
All calls are routed through jsDelivr unless overridden. Supports nested
paths like ``datasets/examples/slack/messages.csv``.
"""
return f"{BASE_URL}{filepath}"
-1
View File
@@ -25,7 +25,6 @@ class DatasetSchema(Schema):
}
)
column_formats = fields.Dict(metadata={"description": "Column formats."})
currency_formats = fields.Dict(metadata={"description": "Currency formats."})
columns = fields.List(fields.Dict(), metadata={"description": "Columns metadata."})
database = fields.Dict(
metadata={"description": "Database associated with the dataset."}
+27 -7
View File
@@ -26,6 +26,7 @@ from superset import conf
from superset.constants import TimeGrain
from superset.migrations.shared.utils import paginated_update, try_load_json
from superset.utils import json
from superset.utils.date_parser import get_since_until
logger = logging.getLogger("alembic")
@@ -113,14 +114,33 @@ class MigrateViz:
}
if isinstance(granularity_sqla, dict):
temporal_filter["comparator"] = None
temporal_filter["expressionType"] = "SQL"
temporal_filter["subject"] = granularity_sqla["label"]
temporal_filter["sqlExpression"] = granularity_sqla["sqlExpression"]
since, until = get_since_until(time_range=time_range)
if not since and not until:
temporal_filter = {}
else:
temporal_filter["comparator"] = None
temporal_filter["expressionType"] = "SQL"
temporal_filter["subject"] = granularity_sqla["label"]
rv_data["adhoc_filters"] = (rv_data.get("adhoc_filters") or []) + [
temporal_filter
]
start_date = since.isoformat() if since else None
end_date = until.isoformat() if until else None
if start_date and end_date:
temporal_filter["sqlExpression"] = (
f"{granularity_sqla['sqlExpression']} >= '{start_date}' AND "
f"{granularity_sqla['sqlExpression']} < '{end_date}'"
)
elif start_date:
temporal_filter["sqlExpression"] = (
f"{granularity_sqla['sqlExpression']} >= '{start_date}'"
)
elif end_date:
temporal_filter["sqlExpression"] = (
f"{granularity_sqla['sqlExpression']} < '{end_date}'"
)
rv_data["adhoc_filters"] = rv_data.get("adhoc_filters") or []
if temporal_filter:
rv_data["adhoc_filters"].append(temporal_filter)
@classmethod
def upgrade_slice(cls, slc: Slice) -> None:
+124 -1
View File
@@ -22,7 +22,17 @@ from typing import Any, Callable, Optional, Union
from uuid import uuid4
from alembic import op
from sqlalchemy import Column, inspect
from sqlalchemy import (
Column,
inspect,
JSON,
MetaData,
select,
String,
Table,
text,
update,
)
from sqlalchemy.dialects.mysql.base import MySQLDialect
from sqlalchemy.dialects.postgresql.base import PGDialect
from sqlalchemy.dialects.sqlite.base import SQLiteDialect # noqa: E402
@@ -468,3 +478,116 @@ def create_fks_for_table(
remote_cols,
ondelete=ondelete,
)
def cast_text_column_to_json(
table: str,
column: str,
pk: str = "id",
nullable: bool = True,
suffix: str = "_tmp",
) -> None:
"""
Cast a text column to JSON.
SQLAlchemy now has a nice abstraction for JSON columns, even if the underlying
database doesn't support the type natively. We should always use it when storing
JSON payloads.
:param table: The name of the table.
:param column: The name of the column to be cast.
:param pk: The name of the primary key column.
:param nullable: Whether the new column should be nullable.
:param suffix: The suffix to be added to the temporary column name.
"""
conn = op.get_bind()
if isinstance(conn.dialect, PGDialect):
conn.execute(
text(
f"""
ALTER TABLE {table}
ALTER COLUMN {column} TYPE jsonb
USING {column}::jsonb
"""
)
)
return
tmp_column = column + suffix
op.add_column(
table,
Column(tmp_column, JSON(), nullable=nullable),
)
meta = MetaData()
t = Table(table, meta, autoload_with=conn)
stmt_select = select(t.c[pk], t.c[column]).where(t.c[column].is_not(None))
for row_pk, value in conn.execute(stmt_select):
stmt_update = update(t).where(t.c[pk] == row_pk).values({tmp_column: value})
conn.execute(stmt_update)
op.drop_column(table, column)
op.alter_column(table, tmp_column, existing_type=JSON(), new_column_name=column)
return
def cast_json_column_to_text(
table: str,
column: str,
pk: str = "id",
nullable: bool = True,
suffix: str = "_tmp",
length: int = 128,
) -> None:
"""
Cast a JSON column back to text.
:param table: The name of the table.
:param column: The name of the column to be cast.
:param pk: The name of the primary key column.
:param nullable: Whether the new column should be nullable.
:param suffix: The suffix to be added to the temporary column name.
:param length: The length of the text column.
"""
conn = op.get_bind()
if isinstance(conn.dialect, PGDialect):
conn.execute(
text(
f"""
ALTER TABLE {table}
ALTER COLUMN {column} TYPE text
USING {column}::text
"""
)
)
return
tmp_column = column + suffix
op.add_column(
table,
Column(tmp_column, String(length=length), nullable=nullable),
)
meta = MetaData()
t = Table(table, meta, autoload_with=conn)
stmt_select = select(t.c[pk], t.c[column]).where(t.c[column].is_not(None))
for row_pk, value in conn.execute(stmt_select):
stmt_update = (
update(t).where(t.c[pk] == row_pk).values({tmp_column: json.dumps(value)})
)
conn.execute(stmt_update)
op.drop_column(table, column)
op.alter_column(
table,
tmp_column,
existing_type=String(length=length),
new_column_name=column,
)
return
@@ -0,0 +1,46 @@
# 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.
"""metric currency should be JSON
Revision ID: f1edd4a4d4f2
Revises: 378cecfdba9f
Create Date: 2025-04-30 11:04:39.105229
"""
from superset.migrations.shared.utils import (
cast_json_column_to_text,
cast_text_column_to_json,
)
# revision identifiers, used by Alembic.
revision = "f1edd4a4d4f2"
down_revision = "378cecfdba9f"
def upgrade():
"""
Convert the currency column to JSON.
"""
cast_text_column_to_json("sql_metrics", "currency")
def downgrade():
"""
Convert the currency column back to text.
"""
cast_json_column_to_text("sql_metrics", "currency")
+4 -4
View File
@@ -1585,10 +1585,6 @@ class ExploreMixin: # pylint: disable=too-many-public-methods
# use the existing instance.
col = metrics_exprs_by_expr.get(str(col), col)
need_groupby = True
elif col in columns_by_name:
col = self.convert_tbl_column_to_sqla_col(
columns_by_name[col], template_processor=template_processor
)
elif col in metrics_exprs_by_label:
col = metrics_exprs_by_label[col]
need_groupby = True
@@ -1597,6 +1593,10 @@ class ExploreMixin: # pylint: disable=too-many-public-methods
template_processor=template_processor
)
need_groupby = True
elif col in columns_by_name:
col = self.convert_tbl_column_to_sqla_col(
columns_by_name[col], template_processor=template_processor
)
if isinstance(col, ColumnElement):
orderby_exprs.append(col)
+40
View File
@@ -32,6 +32,7 @@ from deprecation import deprecated
from sqlglot import exp
from sqlglot.dialects.dialect import Dialect, Dialects
from sqlglot.errors import ParseError
from sqlglot.expressions import Func
from sqlglot.optimizer.pushdown_predicates import pushdown_predicates
from sqlglot.optimizer.scope import Scope, ScopeType, traverse_scope
@@ -453,6 +454,23 @@ class SQLStatement(BaseSQLStatement[exp.Expression]):
return SQLStatement(sql, self.engine, optimized)
def check_functions_present(self, functions: set[str]) -> bool:
"""
Check if any of the given functions are present in the script.
:param functions: List of functions to check for
:return: True if any of the functions are present
"""
present = {
(
function.sql_name()
if function.sql_name() != "ANONYMOUS"
else function.name.upper()
)
for function in self._parsed.find_all(Func)
}
return any(function.upper() in present for function in functions)
class KQLSplitState(enum.Enum):
"""
@@ -619,6 +637,16 @@ class KustoKQLStatement(BaseSQLStatement[str]):
"""
return KustoKQLStatement(self._sql, self.engine, self._parsed)
def check_functions_present(self, functions: set[str]) -> bool:
"""
Check if any of the given functions are present in the script.
:param functions: List of functions to check for
:return: True if any of the functions are present
"""
logger.warning("Kusto KQL doesn't support checking for functions present.")
return True
class SQLScript:
"""
@@ -684,6 +712,18 @@ class SQLScript:
return script
def check_functions_present(self, functions: set[str]) -> bool:
"""
Check if any of the given functions are present in the script.
:param functions: List of functions to check for
:return: True if any of the functions are present
"""
return any(
statement.check_functions_present(functions)
for statement in self.statements
)
def extract_tables_from_statement(
statement: exp.Expression,
+1 -30
View File
@@ -31,7 +31,6 @@ from sqlalchemy import and_
from sqlparse import keywords
from sqlparse.lexer import Lexer
from sqlparse.sql import (
Function,
Identifier,
IdentifierList,
Parenthesis,
@@ -181,7 +180,7 @@ def check_sql_functions_exist(
:param function_list: The list of functions to search for
:param engine: The engine to use for parsing the SQL statement
"""
return ParsedQuery(sql, engine=engine).check_functions_exist(function_list)
return SQLScript(sql, engine=engine).check_functions_present(function_list)
def strip_comments_from_sql(statement: str, engine: str = "base") -> str:
@@ -229,34 +228,6 @@ class ParsedQuery:
self._tables = self._extract_tables_from_sql()
return self._tables
def _check_functions_exist_in_token(
self, token: Token, functions: set[str]
) -> bool:
if (
isinstance(token, Function)
and token.get_name() is not None
and token.get_name().lower() in functions
):
return True
if hasattr(token, "tokens"):
for inner_token in token.tokens:
if self._check_functions_exist_in_token(inner_token, functions):
return True
return False
def check_functions_exist(self, functions: set[str]) -> bool:
"""
Check if the SQL statement contains any of the specified functions.
:param functions: A set of functions to search for
:return: True if the statement contains any of the specified functions
"""
for statement in self._parsed:
for token in statement.tokens:
if self._check_functions_exist_in_token(token, functions):
return True
return False
def _extract_tables_from_sql(self) -> set[Table]:
"""
Extract all table references in a query.
+1 -4
View File
@@ -580,10 +580,7 @@ def test_get_samples_with_incorrect_cc(test_client, login_as_admin, virtual_data
)
rv = test_client.post(uri, json={})
assert rv.status_code == 422
assert "error" in rv.json
if virtual_dataset.database.db_engine_spec.engine_name == "PostgreSQL":
assert "INCORRECT SQL" in rv.json.get("error")
assert rv.json["errors"][0]["error_type"] == "INVALID_SQL_ERROR"
@with_feature_flags(ALLOW_ADHOC_SUBQUERY=True)
@@ -21,7 +21,7 @@ from tests.unit_tests.migrations.viz.utils import migrate_and_assert
SOURCE_FORM_DATA: dict[str, Any] = {
"granularity_sqla": "ds",
"time_range": "100 years ago : now",
"time_range": "1925-04-24 : 2025-04-24",
"viz_type": "pivot_table",
}
@@ -29,7 +29,7 @@ TARGET_FORM_DATA: dict[str, Any] = {
"form_data_bak": SOURCE_FORM_DATA,
"granularity_sqla": "ds",
"rowOrder": "value_z_to_a",
"time_range": "100 years ago : now",
"time_range": "1925-04-24 : 2025-04-24",
"viz_type": "pivot_table_v2",
}
@@ -40,7 +40,7 @@ def test_migration() -> None:
target["adhoc_filters"] = [
{
"clause": "WHERE",
"comparator": "100 years ago : now",
"comparator": "1925-04-24 : 2025-04-24",
"expressionType": "SIMPLE",
"operator": "TEMPORAL_RANGE",
"subject": "ds",
@@ -65,7 +65,9 @@ def test_custom_sql_time_column() -> None:
"comparator": None,
"expressionType": "SQL",
"operator": "TEMPORAL_RANGE",
"sqlExpression": "sum(ds)",
"sqlExpression": (
"sum(ds) >= '1925-04-24T00:00:00' AND sum(ds) < '2025-04-24T00:00:00'"
),
"subject": "ds",
}
]
+29
View File
@@ -1237,6 +1237,35 @@ def test_check_sql_functions_exist() -> None:
)
def test_check_sql_functions_exist_with_comments() -> None:
"""
Test sql functions are detected correctly with comments
"""
assert not (
check_sql_functions_exist(
"select a, b from version/**/", {"version"}, "postgresql"
)
)
assert check_sql_functions_exist("select version/**/()", {"version"}, "postgresql")
assert check_sql_functions_exist(
"select version from version/**/()", {"version"}, "postgresql"
)
assert check_sql_functions_exist(
"select 1, a.version from (select version from version/**/()) as a",
{"version"},
"postgresql",
)
assert check_sql_functions_exist(
"select 1, a.version from (select version/**/()) as a",
{"version"},
"postgresql",
)
def test_sanitize_clause_valid():
# regular clauses
assert sanitize_clause("col = 1") == "col = 1"