Compare commits

..

5 Commits

Author SHA1 Message Date
Beto Dealmeida
503933756e fix: show only filterable columns on filter dropdown 2025-05-01 17:20:02 -04:00
Beto Dealmeida
60261a5dc6 Fix docstring 2025-05-01 12:33:44 -04:00
Beto Dealmeida
456512c508 Fix test 2025-05-01 11:56:13 -04:00
Beto Dealmeida
c1d9b06649 Remove old method 2025-05-01 09:49:50 -04:00
Beto Dealmeida
7a64a82cd9 fix: improve function detection 2025-04-30 16:58:11 -04:00
40 changed files with 2358 additions and 4795 deletions

View File

@@ -41,8 +41,6 @@ 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'

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 in initContainers to wait for database dependencies to be available.
- `dockerize`: Used by Helm.
## Key tags examples

View File

@@ -28,14 +28,14 @@
"@superset-ui/style": "^0.14.23",
"antd": "^5.24.5",
"docusaurus-plugin-less": "^2.0.2",
"less": "^4.3.0",
"less": "^4.2.2",
"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.21.0"
"swagger-ui-react": "^5.20.2"
},
"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.2",
"eslint-config-prettier": "^10.1.1",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react": "^7.0.0",
"prettier": "^2.0.0",
"typescript": "~5.8.3",
"webpack": "^5.99.7"
"typescript": "~5.8.2",
"webpack": "^5.98.0"
},
"browserslist": {
"production": [

File diff suppressed because it is too large Load Diff

View File

@@ -3616,7 +3616,7 @@
dependencies:
"@types/istanbul-lib-report" "*"
"@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":
"@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":
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,11 +4062,16 @@ 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.9.0:
acorn@^8.0.0, acorn@^8.0.4, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.8.2:
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"
@@ -4653,7 +4658,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.4:
call-bound@^1.0.2, call-bound@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a"
integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==
@@ -6259,10 +6264,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.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-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-plugin-prettier@^4.0.0:
version "4.2.1"
@@ -6271,10 +6276,10 @@ eslint-plugin-prettier@^4.0.0:
dependencies:
prettier-linter-helpers "^1.0.0"
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==
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==
dependencies:
array-includes "^3.1.8"
array.prototype.findlast "^1.2.5"
@@ -6286,7 +6291,7 @@ eslint-plugin-react@^7.37.5:
hasown "^2.0.2"
jsx-ast-utils "^2.4.1 || ^3.0.0"
minimatch "^3.1.2"
object.entries "^1.1.9"
object.entries "^1.1.8"
object.fromentries "^2.0.8"
object.values "^1.2.1"
prop-types "^15.8.1"
@@ -8228,10 +8233,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.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/less/-/less-4.3.0.tgz#ef0cfc260a9ca8079ed8d0e3512bda8a12c82f2a"
integrity sha512-X9RyH9fvemArzfdP8Pi3irr7lor2Ok4rOttDXBhlwDg+wKQsXOXgHWduAJE1EsF7JJx0w0bcO6BC6tCKKYnXKA==
less@^4.2.2:
version "4.2.2"
resolved "https://registry.npmjs.org/less/-/less-4.2.2.tgz"
integrity sha512-tkuLHQlvWUTeQ3doAqnHbNn8T6WX1KA8yvbKG9x4VtKtIjHsVKQZCH11zRgAfbDAXC2UNIg/K9BYAAcEzUIrNg==
dependencies:
copy-anything "^2.0.1"
parse-node-version "^1.0.1"
@@ -9431,15 +9436,14 @@ 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.9:
version "1.1.9"
resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.9.tgz#e4770a6a1444afb61bd39f984018b5bede25f8b3"
integrity sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==
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==
dependencies:
call-bind "^1.0.8"
call-bound "^1.0.4"
call-bind "^1.0.7"
define-properties "^1.2.1"
es-object-atoms "^1.1.1"
es-object-atoms "^1.0.0"
object.fromentries@^2.0.8:
version "2.0.8"
@@ -11539,7 +11543,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.12.0, resolve@^1.14.2:
resolve@^1.1.6, 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==
@@ -11548,6 +11552,15 @@ resolve@^1.1.6, resolve@^1.12.0, 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"
@@ -11696,10 +11709,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, 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==
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==
dependencies:
"@types/json-schema" "^7.0.9"
ajv "^8.9.0"
@@ -12346,10 +12359,10 @@ swagger-client@^3.34.4:
ramda "^0.30.1"
ramda-adjunct "^5.1.0"
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==
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==
dependencies:
"@babel/runtime-corejs3" "^7.26.10"
"@scarf/scarf" "=1.4.0"
@@ -12527,11 +12540,16 @@ 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.3.0, tslib@^2.6.0:
tslib@^2.0.3, 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"
@@ -12633,10 +12651,10 @@ types-ramda@^0.30.0:
dependencies:
ts-toolbelt "^9.6.0"
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==
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==
ufo@^1.5.4:
version "1.6.1"
@@ -13045,14 +13063,13 @@ 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, 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==
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==
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"
@@ -13069,7 +13086,36 @@ webpack@^5.88.1, webpack@^5.95.0, webpack@^5.99.7:
loader-runner "^4.2.0"
mime-types "^2.1.27"
neo-async "^2.6.2"
schema-utils "^4.3.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"
tapable "^2.1.1"
terser-webpack-plugin "^5.3.11"
watchpack "^2.4.1"

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.3, <5.0.0",
"flask-appbuilder>=4.6.1, <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>=18.1.0, <19",
"pyarrow>=14.0.1, <15",
"pyyaml>=6.0.0, <7.0.0",
"PyJWT>=2.4.0, <3.0",
"redis>=4.6.0, <5.0",
@@ -371,14 +371,12 @@ 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",

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.3
cryptography==44.0.2
# via
# apache-superset (pyproject.toml)
# paramiko
@@ -118,7 +118,7 @@ flask==2.3.3
# flask-session
# flask-sqlalchemy
# flask-wtf
flask-appbuilder==4.6.3
flask-appbuilder==4.6.1
# 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.16.0
h11==0.14.0
# via wsproto
hashids==1.3.1
# via apache-superset (pyproject.toml)
holidays==0.25
# via apache-superset (pyproject.toml)
humanize==4.12.3
humanize==4.12.2
# 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.8
markdown==3.7
# via apache-superset (pyproject.toml)
markdown-it-py==3.0.0
# via rich
@@ -236,6 +236,7 @@ numpy==1.26.4
# bottleneck
# numexpr
# pandas
# pyarrow
odfpy==1.4.1
# via pandas
openpyxl==3.1.5
@@ -273,7 +274,7 @@ prison==0.2.1
# via flask-appbuilder
prompt-toolkit==3.0.51
# via click-repl
pyarrow==18.1.0
pyarrow==14.0.2
# via apache-superset (pyproject.toml)
pyasn1==0.6.1
# via
@@ -373,7 +374,7 @@ sqlalchemy-utils==0.38.3
# via
# apache-superset (pyproject.toml)
# flask-appbuilder
sqlglot==26.16.4
sqlglot==26.16.2
# via apache-superset (pyproject.toml)
sqlparse==0.5.3
# via apache-superset (pyproject.toml)

View File

@@ -138,7 +138,7 @@ croniter==6.0.0
# via
# -c requirements/base.txt
# apache-superset
cryptography==44.0.3
cryptography==44.0.2
# 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.3
flask-appbuilder==4.6.1
# via
# -c requirements/base.txt
# apache-superset
@@ -330,7 +330,7 @@ gunicorn==23.0.0
# via
# -c requirements/base.txt
# apache-superset
h11==0.16.0
h11==0.14.0
# via
# -c requirements/base.txt
# wsproto
@@ -343,7 +343,7 @@ holidays==0.25
# -c requirements/base.txt
# apache-superset
# prophet
humanize==4.12.3
humanize==4.12.2
# via
# -c requirements/base.txt
# apache-superset
@@ -415,7 +415,7 @@ mako==1.3.10
# -c requirements/base.txt
# alembic
# apache-superset
markdown==3.8
markdown==3.7
# via
# -c requirements/base.txt
# apache-superset
@@ -473,6 +473,7 @@ numpy==1.26.4
# pandas
# pandas-gbq
# prophet
# pyarrow
oauthlib==3.2.2
# via requests-oauthlib
odfpy==1.4.1
@@ -586,7 +587,7 @@ psutil==6.1.0
# via apache-superset
psycopg2-binary==2.9.6
# via apache-superset
pyarrow==18.1.0
pyarrow==14.0.2
# via
# -c requirements/base.txt
# apache-superset
@@ -799,7 +800,7 @@ sqlalchemy-utils==0.38.3
# -c requirements/base.txt
# apache-superset
# flask-appbuilder
sqlglot==26.16.4
sqlglot==26.16.2
# via
# -c requirements/base.txt
# apache-superset

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.24.9",
"@rjsf/validator-ajv8": "^5.22.3",
"@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.16.0",
"react-intersection-observer": "^9.10.2",
"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.27.1",
"@babel/plugin-transform-runtime": "^7.25.9",
"@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.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
"version": "7.26.2",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.27.1",
"@babel/helper-validator-identifier": "^7.25.9",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
"picocolors": "^1.0.0"
},
"engines": {
"node": ">=6.9.0"
@@ -1240,13 +1240,13 @@
}
},
"node_modules/@babel/generator": {
"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==",
"version": "7.26.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz",
"integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.27.1",
"@babel/types": "^7.27.1",
"@babel/parser": "^7.26.5",
"@babel/types": "^7.26.5",
"@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.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
"integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
"license": "MIT",
"dependencies": {
"@babel/traverse": "^7.27.1",
"@babel/types": "^7.27.1"
"@babel/traverse": "^7.25.9",
"@babel/types": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
@@ -1431,9 +1431,9 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
"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==",
"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==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1491,18 +1491,18 @@
}
},
"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.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==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"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==",
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
"integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -1572,12 +1572,12 @@
}
},
"node_modules/@babel/parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.1.tgz",
"integrity": "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ==",
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz",
"integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.27.1"
"@babel/types": "^7.26.7"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -2849,16 +2849,16 @@
}
},
"node_modules/@babel/plugin-transform-runtime": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.27.1.tgz",
"integrity": "sha512-TqGF3desVsTcp3WrJGj4HfKokfCXCLcHpt4PJF0D8/iT6LPd9RS82Upw3KPeyr6B22Lfd3DO8MVrmp0oRkUDdw==",
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz",
"integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-module-imports": "^7.25.9",
"@babel/helper-plugin-utils": "^7.25.9",
"babel-plugin-polyfill-corejs2": "^0.4.10",
"babel-plugin-polyfill-corejs3": "^0.11.0",
"babel-plugin-polyfill-corejs3": "^0.10.6",
"babel-plugin-polyfill-regenerator": "^0.6.1",
"semver": "^6.3.1"
},
@@ -2869,20 +2869,6 @@
"@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",
@@ -3293,30 +3279,30 @@
}
},
"node_modules/@babel/template": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.1.tgz",
"integrity": "sha512-Fyo3ghWMqkHHpHQCoBs2VnYjR4iWFFjguTDEqA5WgZDOrFesVjMhMM2FSqTKSoUSDO1VQtavj8NFpdRBEvJTtg==",
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/parser": "^7.27.1",
"@babel/types": "^7.27.1"
"@babel/code-frame": "^7.25.9",
"@babel/parser": "^7.25.9",
"@babel/types": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz",
"integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==",
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz",
"integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==",
"license": "MIT",
"dependencies": {
"@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",
"@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",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
@@ -3325,13 +3311,13 @@
}
},
"node_modules/@babel/types": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz",
"integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==",
"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==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
@@ -8512,9 +8498,9 @@
}
},
"node_modules/@rjsf/validator-ajv8": {
"version": "5.24.9",
"resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.24.9.tgz",
"integrity": "sha512-leHb39Qa612QhAfvw36qi/ubWa7LQ6hrPN4Ge93QBlWywRfV/M0Wmx9bPccCGgIL4Qnn1Wmt53EWV8kQT28xTA==",
"version": "5.24.1",
"resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.24.1.tgz",
"integrity": "sha512-p6URehglU9yFUAoQXE1ryqZjLYSjc6qdbiUfCVvEFAzUuMECsIFomz2hH3CPlt10K72sAFdzwVvrKn1iWTnxDw==",
"license": "Apache-2.0",
"dependencies": {
"ajv": "^8.12.0",
@@ -39490,9 +39476,9 @@
}
},
"node_modules/react-intersection-observer": {
"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==",
"version": "9.15.1",
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.15.1.tgz",
"integrity": "sha512-vGrqYEVWXfH+AGu241uzfUpNK4HAdhCkSAyFdkMb9VWWXs6mxzBLpWCxEy9YcnDNY2g9eO6z7qUtTBdA9hc8pA==",
"license": "MIT",
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",

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.24.9",
"@rjsf/validator-ajv8": "^5.22.3",
"@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.16.0",
"react-intersection-observer": "^9.10.2",
"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.27.1",
"@babel/plugin-transform-runtime": "^7.25.9",
"@babel/preset-env": "^7.26.7",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",

View File

@@ -21,6 +21,7 @@ import { Dataset } from './types';
export const TestDataset: Dataset = {
column_formats: {},
currency_formats: {},
columns: [
{
advanced_data_type: undefined,

View File

@@ -69,6 +69,7 @@ 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]']

View File

@@ -53,6 +53,7 @@ describe('columnChoices()', () => {
],
verbose_map: {},
column_formats: { fiz: 'NUMERIC', about: 'STRING', foo: 'DATE' },
currency_formats: {},
datasource_name: 'my_datasource',
description: 'this is my datasource',
}),
@@ -104,6 +105,7 @@ describe('columnChoices()', () => {
],
verbose_map: {},
column_formats: { fiz: 'NUMERIC', about: 'STRING', foo: 'DATE' },
currency_formats: {},
datasource_name: 'my_datasource',
description: 'this is my datasource',
}),

View File

@@ -41,6 +41,7 @@ describe('defineSavedMetrics', () => {
columns: [],
verbose_map: {},
column_formats: {},
currency_formats: {},
datasource_name: 'my_datasource',
description: 'this is my datasource',
};

View File

@@ -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.2",
"ts-loader": "^9.5.1",
"typescript": "^5.7.2"
},
"peerDependencies": {

View File

@@ -440,7 +440,7 @@ const Select = forwardRef(
const bulkSelectComponent = useMemo(
() => (
<StyledBulkActionsContainer className="select-bulk-actions" size={0}>
<StyledBulkActionsContainer size={0}>
<Button
type="link"
buttonSize="xsmall"

View File

@@ -128,14 +128,6 @@ const VerticalFormItem = styled(StyledFormItem)<{
width: 140px;
`}
}
.select-bulk-actions {
${({ inverseSelection }) =>
inverseSelection &&
`
flex-direction: column;
`}
}
`;
const HorizontalFormItem = styled(StyledFormItem)<{
@@ -172,10 +164,6 @@ const HorizontalFormItem = styled(StyledFormItem)<{
width: 164px;
`}
}
.select-bulk-actions {
flex-direction: column;
}
`;
const HorizontalOverflowFormItem = VerticalFormItem;

View File

@@ -223,7 +223,6 @@ describe('ColumnSelect filterValues behavior', () => {
...createProps(),
formFilter: { filterType: 'filterType' },
};
// @ts-ignore: bypass incomplete formFilter type for test
const element = getControlItemsMap(props).mainControlItems.groupby
.element as React.ReactElement;
render(element);
@@ -238,7 +237,6 @@ describe('ColumnSelect filterValues behavior', () => {
...createProps(),
formFilter: { filterType: 'filterType' },
};
// @ts-ignore: bypass incomplete formFilter type for test
const element = getControlItemsMap(props).mainControlItems.groupby
.element as React.ReactElement;
render(element);

View File

@@ -134,7 +134,7 @@ export default function getControlItemsMap({
doesColumnMatchFilterType(
formFilter?.filterType || '',
column,
) && !!column?.filterable
) && column.filterable
}
onChange={() => {
// We need reset default value when column changed

View File

@@ -29,6 +29,7 @@ export const PLACEHOLDER_DATASOURCE: Datasource = {
column_types: [],
metrics: [],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '',
description: '',

View File

@@ -27,13 +27,10 @@ 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,
@@ -42,11 +39,6 @@ import {
} from './actions';
import { areObjectsEqual } from '../reduxUtils';
type FilterWithExtaFromData = Filter & {
extraFormData?: ExtraFormData;
filterState?: FilterState;
};
export function getInitialDataMask(
id?: string | number,
moreProps: DataMask = {},
@@ -114,27 +106,10 @@ 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,
}),
};
});

View File

@@ -41,6 +41,7 @@ const CURRENT_DATASOURCE = {
columns: [],
metrics: [],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '__timestamp',
// eg. ['["ds", true]', 'ds [asc]']
@@ -54,6 +55,7 @@ const NEW_DATASOURCE = {
columns: [],
metrics: [],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '__timestamp',
// eg. ['["ds", true]', 'ds [asc]']

View File

@@ -56,6 +56,7 @@ describe('controlUtils', () => {
{ metric_name: 'second', uuid: '2' },
],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '',
datasource_name: '1__table',

View File

@@ -35,6 +35,7 @@ const sampleDatasource: Dataset = {
],
metrics: [{ metric_name: 'saved_metric_2', uuid: '1' }],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '',
datasource_name: 'Sample Dataset',

View File

@@ -138,6 +138,7 @@ export const exploreInitialData: ExplorePageInitialData = {
{ metric_name: 'second', uuid: '2' },
],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '',
datasource_name: '8__table',
@@ -156,6 +157,7 @@ export const fallbackExploreInitialData: ExplorePageInitialData = {
columns: [],
metrics: [],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '',
owners: [],

View File

@@ -78,6 +78,7 @@ 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>;
};

View File

@@ -17,7 +17,7 @@
* under the License.
*/
/* eslint-disable no-param-reassign */
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import {
AppSection,
DataMask,
@@ -84,10 +84,7 @@ function reducer(draft: DataMask, action: DataMaskAction) {
}
}
const StyledSpace = styled(Space)<{
inverseSelection: boolean;
appSection: AppSection;
}>`
const StyledSpace = styled(Space)<{ $inverseSelection: boolean }>`
display: flex;
align-items: center;
width: 100%;
@@ -99,17 +96,12 @@ const StyledSpace = styled(Space)<{
&.ant-space {
.ant-space-item {
width: ${({ inverseSelection, appSection }) =>
!inverseSelection || appSection === AppSection.FilterConfigModal
? '100%'
: 'auto'};
width: ${({ $inverseSelection }) =>
!$inverseSelection ? '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,
@@ -166,30 +158,6 @@ 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 =
@@ -319,44 +287,35 @@ 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(() => {
// 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
if (defaultToFirstItem && filterState.value === undefined) {
// initialize to first value if set to default to first item
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 (formData?.defaultValue) {
// Case 3 : Handle defalut value case
updateDataMask(formData.defaultValue);
} else if (isDisabled) {
// empty selection if filter is disabled
updateDataMask(null);
} else {
// reset data mask based on filter state
updateDataMask(filterState.value);
}
}, [
col,
isDisabled,
enableEmptyFilter,
defaultToFirstItem,
formData?.defaultValue,
enableEmptyFilter,
inverseSelection,
excludeFilterValues,
updateDataMask,
data,
groupby,
col,
inverseSelection,
JSON.stringify(filterState.value),
]);
useEffect(() => {
@@ -364,26 +323,23 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
}, [JSON.stringify(dataMask)]);
useEffect(() => {
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;
}
dispatchDataMask({
type: 'filterState',
extraFormData: getSelectExtraFormData(
col,
filterState.value,
!filterState.value?.length,
excludeFilterValues && inverseSelection,
),
filterState: {
...(filterState as {
value: SelectValue;
label?: string;
excludeFilterValues?: boolean;
}),
excludeFilterValues,
},
});
}, [excludeFilterValues]);
const handleExclusionToggle = (value: string) => {
@@ -396,11 +352,8 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
validateStatus={filterState.validateStatus}
extra={formItemExtra}
>
<StyledSpace
appSection={appSection}
inverseSelection={inverseSelection}
>
{appSection !== AppSection.FilterConfigModal && inverseSelection && (
<StyledSpace $inverseSelection={inverseSelection}>
{inverseSelection && (
<Select
className="exclude-select"
value={`${excludeFilterValues}`}

View File

@@ -26,6 +26,7 @@ const TEST_DATASOURCE = {
columns: [],
metrics: [],
column_formats: {},
currency_formats: {},
verbose_map: {},
main_dttm_col: '__timestamp',
// eg. ['["ds", true]', 'ds [asc]']

View File

@@ -14,12 +14,12 @@
"ioredis": "^4.28.0",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"uuid": "^11.1.0",
"uuid": "^11.0.2",
"winston": "^3.17.0",
"ws": "^8.18.0"
},
"devDependencies": {
"@eslint/js": "^9.25.1",
"@eslint/js": "^9.16.0",
"@types/cookie": "^0.6.0",
"@types/eslint__js": "^8.42.3",
"@types/ioredis": "^4.27.8",
@@ -826,11 +826,10 @@
}
},
"node_modules/@eslint/js": {
"version": "9.25.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz",
"integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==",
"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"
}
@@ -3280,16 +3279,6 @@
"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",
@@ -6822,14 +6811,13 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/uuid": {
"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==",
"version": "11.0.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz",
"integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/esm/bin/uuid"
}
@@ -7627,9 +7615,9 @@
}
},
"@eslint/js": {
"version": "9.25.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz",
"integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==",
"version": "9.17.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz",
"integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==",
"dev": true
},
"@eslint/object-schema": {
@@ -9400,12 +9388,6 @@
"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",
@@ -12025,9 +12007,9 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"uuid": {
"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=="
"version": "11.0.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz",
"integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ=="
},
"v8-compile-cache-lib": {
"version": "3.0.1",

View File

@@ -22,12 +22,12 @@
"ioredis": "^4.28.0",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"uuid": "^11.1.0",
"uuid": "^11.0.2",
"winston": "^3.17.0",
"ws": "^8.18.0"
},
"devDependencies": {
"@eslint/js": "^9.25.1",
"@eslint/js": "^9.16.0",
"@types/cookie": "^0.6.0",
"@types/eslint__js": "^8.42.3",
"@types/ioredis": "^4.27.8",

View File

@@ -313,6 +313,10 @@ 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:
@@ -375,6 +379,7 @@ 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,
@@ -1041,7 +1046,7 @@ class SqlMetric(AuditMixinNullable, ImportExportMixin, CertificationMixin, Model
metric_type = Column(String(32))
description = Column(utils.MediumText())
d3format = Column(String(128))
currency = Column(JSON, nullable=True)
currency = Column(String(128))
warning_text = Column(Text)
table_id = Column(Integer, ForeignKey("tables.id", ondelete="CASCADE"))
expression = Column(utils.MediumText(), nullable=False)
@@ -1096,6 +1101,16 @@ 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 = (

View File

@@ -259,6 +259,7 @@ 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()

View File

@@ -221,6 +221,7 @@ class DatasetRestApi(BaseSupersetModelRestApi):
"datasource_name",
"name",
"column_formats",
"currency_formats",
"granularity_sqla",
"time_grain_sqla",
"order_by_choices",

View File

@@ -14,34 +14,6 @@
# 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
@@ -50,41 +22,27 @@ from superset.connectors.sqla.models import SqlaTable
from superset.models.slice import Slice
from superset.utils import json
# ---------------------------------------------------------------------------
# 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}/",
)
BASE_URL = "https://github.com/apache-superset/examples-data/blob/master/"
# Slices assembled into a 'Misc Chart' dashboard
misc_dash_slices: set[str] = set()
# ---------------------------------------------------------------------------
# Utility functions
# ---------------------------------------------------------------------------
misc_dash_slices: set[str] = set() # slices assembled in a 'Misc Chart' dashboard
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: dict[str, Slice] = {}
slices = {}
for name in {component["meta"]["sliceName"] for component in slice_components}:
slc = db.session.query(Slice).filter_by(slice_name=name).first()
if slc:
@@ -98,24 +56,17 @@ def update_slice_ids(pos: dict[Any, Any]) -> list[Slice]:
def merge_slice(slc: Slice) -> None:
"""Upsert a Slice by name."""
existing = db.session.query(Slice).filter_by(slice_name=slc.slice_name).first()
if existing:
db.session.delete(existing)
o = db.session.query(Slice).filter_by(slice_name=slc.slice_name).first()
if o:
db.session.delete(o)
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 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}"
return f"{BASE_URL}{filepath}?raw=true"

View File

@@ -25,6 +25,7 @@ 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."}

View File

@@ -26,7 +26,6 @@ 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")
@@ -114,33 +113,14 @@ class MigrateViz:
}
if isinstance(granularity_sqla, dict):
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"]
temporal_filter["comparator"] = None
temporal_filter["expressionType"] = "SQL"
temporal_filter["subject"] = granularity_sqla["label"]
temporal_filter["sqlExpression"] = granularity_sqla["sqlExpression"]
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)
rv_data["adhoc_filters"] = (rv_data.get("adhoc_filters") or []) + [
temporal_filter
]
@classmethod
def upgrade_slice(cls, slc: Slice) -> None:

View File

@@ -22,17 +22,7 @@ from typing import Any, Callable, Optional, Union
from uuid import uuid4
from alembic import op
from sqlalchemy import (
Column,
inspect,
JSON,
MetaData,
select,
String,
Table,
text,
update,
)
from sqlalchemy import Column, inspect
from sqlalchemy.dialects.mysql.base import MySQLDialect
from sqlalchemy.dialects.postgresql.base import PGDialect
from sqlalchemy.dialects.sqlite.base import SQLiteDialect # noqa: E402
@@ -478,116 +468,3 @@ 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

View File

@@ -1,46 +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.
"""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")

View File

@@ -1585,6 +1585,10 @@ 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
@@ -1593,10 +1597,6 @@ 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)

View File

@@ -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": "1925-04-24 : 2025-04-24",
"time_range": "100 years ago : now",
"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": "1925-04-24 : 2025-04-24",
"time_range": "100 years ago : now",
"viz_type": "pivot_table_v2",
}
@@ -40,7 +40,7 @@ def test_migration() -> None:
target["adhoc_filters"] = [
{
"clause": "WHERE",
"comparator": "1925-04-24 : 2025-04-24",
"comparator": "100 years ago : now",
"expressionType": "SIMPLE",
"operator": "TEMPORAL_RANGE",
"subject": "ds",
@@ -65,9 +65,7 @@ def test_custom_sql_time_column() -> None:
"comparator": None,
"expressionType": "SQL",
"operator": "TEMPORAL_RANGE",
"sqlExpression": (
"sum(ds) >= '1925-04-24T00:00:00' AND sum(ds) < '2025-04-24T00:00:00'"
),
"sqlExpression": "sum(ds)",
"subject": "ds",
}
]