mirror of
https://github.com/apache/superset.git
synced 2026-06-11 18:49:15 +00:00
Compare commits
17 Commits
fix-alert-
...
fix/chart-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d544bff071 | ||
|
|
f79a88c685 | ||
|
|
b1d965932d | ||
|
|
7d046340dc | ||
|
|
aa872cd0a1 | ||
|
|
b2c5a1ecb3 | ||
|
|
6cd9bdee0b | ||
|
|
a8a1d9c17d | ||
|
|
97058d2cf0 | ||
|
|
ef57409209 | ||
|
|
5f06e66cf1 | ||
|
|
11af932099 | ||
|
|
c9c05d8d0a | ||
|
|
0f59705806 | ||
|
|
320965612d | ||
|
|
c3df60c12b | ||
|
|
4f69949c10 |
@@ -64,7 +64,7 @@ dependencies = [
|
||||
"holidays>=0.45, <1",
|
||||
"humanize",
|
||||
"isodate",
|
||||
"jsonpath-ng>=1.6.1, <2",
|
||||
"jsonpath-ng>=1.8.0, <2",
|
||||
"Mako>=1.2.2",
|
||||
"markdown>=3.10.2",
|
||||
# marshmallow>=4 has issues: https://github.com/apache/superset/issues/33162
|
||||
@@ -94,7 +94,7 @@ dependencies = [
|
||||
"PyJWT>=2.4.0, <3.0",
|
||||
"redis>=5.0.0, <6.0",
|
||||
"rison>=2.0.0, <3.0",
|
||||
"selenium>=4.14.0, <5.0",
|
||||
"selenium>=4.44.0, <5.0",
|
||||
"shillelagh[gsheetsapi]>=1.4.4, <2.0",
|
||||
"sshtunnel>=0.4.0, <0.5",
|
||||
"simplejson>=3.15.0",
|
||||
@@ -107,7 +107,7 @@ dependencies = [
|
||||
"typing-extensions>=4, <5",
|
||||
"waitress; sys_platform == 'win32'",
|
||||
"watchdog>=6.0.0",
|
||||
"wtforms>=2.3.3, <4",
|
||||
"wtforms>=3.2.2, <4",
|
||||
"wtforms-json",
|
||||
"xlsxwriter>=3.2.9, <3.3",
|
||||
]
|
||||
@@ -121,7 +121,7 @@ bigquery = [
|
||||
"sqlalchemy-bigquery>=1.15.0",
|
||||
"google-cloud-bigquery>=3.10.0",
|
||||
]
|
||||
clickhouse = ["clickhouse-connect>=0.13.0, <2.0"]
|
||||
clickhouse = ["clickhouse-connect>=1.1.1, <2.0"]
|
||||
cockroachdb = ["cockroachdb>=0.3.5, <0.4"]
|
||||
crate = ["sqlalchemy-cratedb>=0.41.0, <1"]
|
||||
d1 = [
|
||||
@@ -161,7 +161,7 @@ hive = [
|
||||
"pyhive[hive]>=0.6.5;python_version<'3.11'",
|
||||
"pyhive[hive_pure_sasl]>=0.7.0",
|
||||
"tableschema",
|
||||
"thrift>=0.14.1, <1.0.0",
|
||||
"thrift>=0.23.0, <1.0.0",
|
||||
"thrift_sasl>=0.4.3, < 1.0.0",
|
||||
]
|
||||
impala = ["impyla>0.16.2, <0.23"]
|
||||
@@ -195,7 +195,7 @@ spark = [
|
||||
"pyhive[hive]>=0.6.5;python_version<'3.11'",
|
||||
"pyhive[hive_pure_sasl]>=0.7",
|
||||
"tableschema",
|
||||
"thrift>=0.14.1, <1",
|
||||
"thrift>=0.23.0, <1",
|
||||
]
|
||||
tdengine = [
|
||||
"taospy>=2.7.21",
|
||||
|
||||
@@ -50,7 +50,7 @@ cattrs==25.1.1
|
||||
# via requests-cache
|
||||
celery==5.5.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
certifi==2025.6.15
|
||||
certifi==2026.5.20
|
||||
# via
|
||||
# requests
|
||||
# selenium
|
||||
@@ -194,7 +194,7 @@ jinja2==3.1.6
|
||||
# via
|
||||
# flask
|
||||
# flask-babel
|
||||
jsonpath-ng==1.7.0
|
||||
jsonpath-ng==1.8.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
jsonschema==4.23.0
|
||||
# via
|
||||
@@ -286,8 +286,6 @@ pillow==12.2.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
platformdirs==4.3.8
|
||||
# via requests-cache
|
||||
ply==3.11
|
||||
# via jsonpath-ng
|
||||
polyline==2.0.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
prison==0.2.1
|
||||
@@ -380,7 +378,7 @@ rpds-py==0.25.0
|
||||
# referencing
|
||||
rsa==4.9.1
|
||||
# via google-auth
|
||||
selenium==4.32.0
|
||||
selenium==4.44.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
setuptools==80.9.0
|
||||
# via -r requirements/base.in
|
||||
@@ -423,7 +421,7 @@ sshtunnel==0.4.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
tabulate==0.10.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
trio==0.30.0
|
||||
trio==0.33.0
|
||||
# via
|
||||
# selenium
|
||||
# trio-websocket
|
||||
@@ -480,7 +478,7 @@ wrapt==1.17.2
|
||||
# via deprecated
|
||||
wsproto==1.2.0
|
||||
# via trio-websocket
|
||||
wtforms==3.2.1
|
||||
wtforms==3.2.2
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# flask-appbuilder
|
||||
|
||||
@@ -112,7 +112,7 @@ celery==5.5.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
certifi==2025.6.15
|
||||
certifi==2026.5.20
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# httpcore
|
||||
@@ -471,7 +471,7 @@ jmespath==1.1.0
|
||||
# via
|
||||
# boto3
|
||||
# botocore
|
||||
jsonpath-ng==1.7.0
|
||||
jsonpath-ng==1.8.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -674,10 +674,6 @@ platformdirs==4.3.8
|
||||
# virtualenv
|
||||
pluggy==1.5.0
|
||||
# via pytest
|
||||
ply==3.11
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# jsonpath-ng
|
||||
polib==1.2.0
|
||||
# via apache-superset
|
||||
polyline==2.0.2
|
||||
@@ -925,7 +921,7 @@ s3transfer==0.16.0
|
||||
# via boto3
|
||||
secretstorage==3.5.0
|
||||
# via keyring
|
||||
selenium==4.32.0
|
||||
selenium==4.44.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -1023,7 +1019,7 @@ tqdm==4.67.1
|
||||
# prophet
|
||||
trino==0.330.0
|
||||
# via apache-superset
|
||||
trio==0.30.0
|
||||
trio==0.33.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# selenium
|
||||
@@ -1125,7 +1121,7 @@ wsproto==1.2.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# trio-websocket
|
||||
wtforms==3.2.1
|
||||
wtforms==3.2.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
|
||||
@@ -48,6 +48,7 @@ module.exports = {
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'@babel/plugin-transform-export-namespace-from',
|
||||
['@babel/plugin-transform-class-properties', { loose: true }],
|
||||
'@babel/plugin-transform-class-static-block',
|
||||
['@babel/plugin-transform-optional-chaining', { loose: true }],
|
||||
['@babel/plugin-transform-private-methods', { loose: true }],
|
||||
['@babel/plugin-transform-nullish-coalescing-operator', { loose: true }],
|
||||
|
||||
234
superset-frontend/package-lock.json
generated
234
superset-frontend/package-lock.json
generated
@@ -95,7 +95,7 @@
|
||||
"echarts": "^5.6.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fs-extra": "^11.3.5",
|
||||
"fuse.js": "^7.3.0",
|
||||
"fuse.js": "^7.4.1",
|
||||
"geolib": "^3.3.14",
|
||||
"geostyler": "^18.6.0",
|
||||
"geostyler-data": "^1.1.0",
|
||||
@@ -178,13 +178,13 @@
|
||||
"@babel/types": "^7.29.7",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/jest": "^11.14.2",
|
||||
"@formatjs/intl-durationformat": "^0.10.3",
|
||||
"@formatjs/intl-durationformat": "^0.10.13",
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@playwright/test": "^1.60.0",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
|
||||
"@storybook/addon-docs": "10.4.1",
|
||||
"@storybook/addon-links": "10.4.1",
|
||||
"@storybook/react-webpack5": "10.4.1",
|
||||
"@storybook/addon-docs": "10.4.2",
|
||||
"@storybook/addon-links": "10.4.2",
|
||||
"@storybook/react-webpack5": "10.4.2",
|
||||
"@storybook/test-runner": "0.24.4",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@swc/core": "^1.15.40",
|
||||
@@ -242,7 +242,7 @@
|
||||
"eslint-plugin-prettier": "^5.5.6",
|
||||
"eslint-plugin-react-prefer-function-component": "^5.0.0",
|
||||
"eslint-plugin-react-you-might-not-need-an-effect": "^0.10.4",
|
||||
"eslint-plugin-storybook": "10.4.1",
|
||||
"eslint-plugin-storybook": "10.4.2",
|
||||
"eslint-plugin-testing-library": "^7.16.2",
|
||||
"eslint-plugin-theme-colors": "file:eslint-rules/eslint-plugin-theme-colors",
|
||||
"fetch-mock": "^12.6.0",
|
||||
@@ -272,7 +272,7 @@
|
||||
"source-map": "^0.7.6",
|
||||
"source-map-support": "^0.5.21",
|
||||
"speed-measure-webpack-plugin": "^1.6.0",
|
||||
"storybook": "10.4.1",
|
||||
"storybook": "10.4.2",
|
||||
"style-loader": "^4.0.0",
|
||||
"swc-loader": "^0.2.7",
|
||||
"terser-webpack-plugin": "^5.6.1",
|
||||
@@ -3939,50 +3939,38 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/bigdecimal": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/bigdecimal/-/bigdecimal-0.2.0.tgz",
|
||||
"integrity": "sha512-GeaxHZbUoYvHL9tC5eltHLs+1zU70aPw0s7LwqgktIzF5oMhNY4o4deEtusJMsq7WFJF3Ye2zQEzdG8beVk73w==",
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/bigdecimal/-/bigdecimal-0.2.5.tgz",
|
||||
"integrity": "sha512-2XTKNrZRaCUyXK2976wfutqxMBuPO/S/zbJnQdysLI2Zy5mWPVNVEkE6tsTcSVWSE7DgO88t8DtBy+uf3I8bxg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@formatjs/ecma402-abstract": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-3.2.0.tgz",
|
||||
"integrity": "sha512-dHnqHgBo6GXYGRsepaE1wmsC2etaivOWd5VaJstZd+HI2zR3DCUjbDVZRtoPGkkXZmyHvBwrdEUuqfvzhF/DtQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/bigdecimal": "0.2.0",
|
||||
"@formatjs/fast-memoize": "3.1.1",
|
||||
"@formatjs/intl-localematcher": "0.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/fast-memoize": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.1.tgz",
|
||||
"integrity": "sha512-CbNbf+tlJn1baRnPkNePnBqTLxGliG6DDgNa/UtV66abwIjwsliPMOt0172tzxABYzSuxZBZfcp//qI8AvBWPg==",
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.5.tgz",
|
||||
"integrity": "sha512-KLi3fan6WnCHmigd9pmEEN8Hid0v4wiFBW576M/d07KMWYecf1CvyMI3n34vCmHT4AoVqG2n702kiHbXjzZX2A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@formatjs/intl-durationformat": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-durationformat/-/intl-durationformat-0.10.3.tgz",
|
||||
"integrity": "sha512-xRS3GaOlsQLwz0n56SvaddwEnl2NLPKBvYg2M32ak/27dodmVxFJz3j7Nqj7EwKyHTu3f/e+BeoKPrIDUSXTuQ==",
|
||||
"version": "0.10.13",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-durationformat/-/intl-durationformat-0.10.13.tgz",
|
||||
"integrity": "sha512-A1dBcOh1YrcRf/AbmZHFVXgIYkpAaFgyGaYavO/KutbqEXY3HI63o2E1ctmxmllfg3qn3TZGtZux42EFwHNTbg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "3.2.0",
|
||||
"@formatjs/intl-localematcher": "0.8.2"
|
||||
"@formatjs/bigdecimal": "0.2.5",
|
||||
"@formatjs/intl-localematcher": "0.8.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl-localematcher": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.2.tgz",
|
||||
"integrity": "sha512-q05KMYGJLyqFNFtIb8NhWLF5X3aK/k0wYt7dnRFuy6aLQL+vUwQ1cg5cO4qawEiINybeCPXAWlprY2mSBjSXAQ==",
|
||||
"version": "0.8.9",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.9.tgz",
|
||||
"integrity": "sha512-GmB0F/gYh4Hdl4rLWjgDsgT+x4pB54fkJeRh8kAZ4XFzKeCK8dGs+SBJWXO42QZtOUni+IDWKNuCw6wiL4lTvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/fast-memoize": "3.1.1"
|
||||
"@formatjs/fast-memoize": "3.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@gar/promise-retry": {
|
||||
@@ -9696,16 +9684,16 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@storybook/addon-docs": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.4.1.tgz",
|
||||
"integrity": "sha512-IYqUdjoZe4VO2LFZlKL/gwy7DsQSWCq6hX+zc1MBmZo04yycDASk1tte57n9pdlW3ajw9yYMF/+lVBi+xQjyvw==",
|
||||
"version": "10.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.4.2.tgz",
|
||||
"integrity": "sha512-CtW1O4xSKZPNtpWgpfp4yB/x4pj/of+3MvlEDfErSlr3Hp3QmEa2pCLaecR08H5LJqJFlt1PtG0UrIynTvgW9w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"@storybook/csf-plugin": "10.4.1",
|
||||
"@storybook/csf-plugin": "10.4.2",
|
||||
"@storybook/icons": "^2.0.2",
|
||||
"@storybook/react-dom-shim": "10.4.1",
|
||||
"@storybook/react-dom-shim": "10.4.2",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"ts-dedent": "^2.0.0"
|
||||
@@ -9716,7 +9704,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"storybook": "^10.4.1"
|
||||
"storybook": "^10.4.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
@@ -9724,45 +9712,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/addon-docs/node_modules/@storybook/csf-plugin": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.4.1.tgz",
|
||||
"integrity": "sha512-WdPepGBxDGOUDjYd8KxMtcf+us/2PAcnBczl77XtrnxxHNs0jWesxKkiJ9yiuGrge4BPhDeAj6rxjbBoaHxLBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"unplugin": "^2.3.5"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"esbuild": "*",
|
||||
"rollup": "*",
|
||||
"storybook": "^10.4.1",
|
||||
"vite": "*",
|
||||
"webpack": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"esbuild": {
|
||||
"optional": true
|
||||
},
|
||||
"rollup": {
|
||||
"optional": true
|
||||
},
|
||||
"vite": {
|
||||
"optional": true
|
||||
},
|
||||
"webpack": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/addon-links": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-10.4.1.tgz",
|
||||
"integrity": "sha512-h/5D23GwMuHA55sB7XDyhByF9psF7UFmaQOn72pjNAarew5eOpue5A+jXk3AKEYokHbvgQaoz+FrvWo9GEfSKQ==",
|
||||
"version": "10.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-10.4.2.tgz",
|
||||
"integrity": "sha512-cU8h4/m+oAr8UUwF4teZG2N1ilV+vU+98Ii/Ma+IIx9M/V7i5544UxfAz84dV5Rx2Oho6x8XH3gIvmevSyPi/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -9775,7 +9728,7 @@
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"storybook": "^10.4.1"
|
||||
"storybook": "^10.4.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
@@ -9787,13 +9740,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/builder-webpack5": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-10.4.1.tgz",
|
||||
"integrity": "sha512-3Ah4jUjg8nEms/5JV6odtQj9+pQ1DT/04s/V6dZKThGdl85YTrYUZV5OTgbNxYbmQn/TwpWWjQlcW8ulpo2WBw==",
|
||||
"version": "10.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-10.4.2.tgz",
|
||||
"integrity": "sha512-nhmV0+nThCgy1y5742SS7c4vJrd5/1KfCXCNfsJ1v4Rkq7NIQnUhEIBwkSaY63lqH7FRHlFxIjwGS63veiCJuw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/core-webpack": "10.4.1",
|
||||
"@storybook/core-webpack": "10.4.2",
|
||||
"case-sensitive-paths-webpack-plugin": "^2.4.0",
|
||||
"cjs-module-lexer": "^1.2.3",
|
||||
"css-loader": "^7.1.2",
|
||||
@@ -9814,7 +9767,7 @@
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"storybook": "^10.4.1"
|
||||
"storybook": "^10.4.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
@@ -9823,9 +9776,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/core-webpack": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-10.4.1.tgz",
|
||||
"integrity": "sha512-Wert/4ou5WRl8WYWWS8bBW7Lxa/ASMEuQ3EVuG3SITAtPNvKDKqTFBjZLx9eJSefkX6fJ3yG85FFUOPsv6GemQ==",
|
||||
"version": "10.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-10.4.2.tgz",
|
||||
"integrity": "sha512-qnYKMruU8lvI4yaq2PA9Gmxjrc7EZ3DRBI/cVKwEgOIREoxzr1F1IE7t7+325k9Phylue7E5rD3A7yjxeEKUyw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -9836,7 +9789,42 @@
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"storybook": "^10.4.1"
|
||||
"storybook": "^10.4.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/csf-plugin": {
|
||||
"version": "10.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.4.2.tgz",
|
||||
"integrity": "sha512-GqX/2DeF3/jKs5D7gpDiuT9gd0c/f2TKcnQ5av4/s3YqeN+0nhm7btkCrDfgF16uzE1Zj3OrkxvB3AOkfxWgDg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"unplugin": "^2.3.5"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"esbuild": "*",
|
||||
"rollup": "*",
|
||||
"storybook": "^10.4.2",
|
||||
"vite": "*",
|
||||
"webpack": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"esbuild": {
|
||||
"optional": true
|
||||
},
|
||||
"rollup": {
|
||||
"optional": true
|
||||
},
|
||||
"vite": {
|
||||
"optional": true
|
||||
},
|
||||
"webpack": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/global": {
|
||||
@@ -9858,13 +9846,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/preset-react-webpack": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-10.4.1.tgz",
|
||||
"integrity": "sha512-uAR/C/oDZYhReaYpD4Rd5S4VWcXP2XO8+BwXwanKt4UHbYfOw7AQgBTeZ/6Wns/0xIXhOoA1rxO5TA2wDLUjLA==",
|
||||
"version": "10.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-10.4.2.tgz",
|
||||
"integrity": "sha512-21ld380f0/jTTitkfhTKgP3FBnVAgMu1P1ymrRyiFYJVSJBA5YejndFFBo0ugq9iGGsHXrVdOphC/OJKbTSWRQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/core-webpack": "10.4.1",
|
||||
"@storybook/core-webpack": "10.4.2",
|
||||
"@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.0c3f3b7.0",
|
||||
"@types/semver": "^7.7.1",
|
||||
"magic-string": "^0.30.5",
|
||||
@@ -9881,7 +9869,7 @@
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"storybook": "^10.4.1"
|
||||
"storybook": "^10.4.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
@@ -9890,14 +9878,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/react": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/react/-/react-10.4.1.tgz",
|
||||
"integrity": "sha512-WuYz4NaUk4gmFAMliSpCbV8w6jP5OY9juBfw1huwzu2S/k5FhnVXwmrUaL0fmf3Bq/7NgkzmBBbZr6I6LuHayQ==",
|
||||
"version": "10.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/react/-/react-10.4.2.tgz",
|
||||
"integrity": "sha512-NfEH3CrdCAgUV4Z7SPN3Iw6nofcueqtRj8iHuo77GNjz0qSfuVi9iS7a8o7x7QFSeIBZwS0Jv3CgmhN8qvoLjg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/global": "^5.0.0",
|
||||
"@storybook/react-dom-shim": "10.4.1",
|
||||
"@storybook/react-dom-shim": "10.4.2",
|
||||
"react-docgen": "^8.0.2",
|
||||
"react-docgen-typescript": "^2.2.2"
|
||||
},
|
||||
@@ -9910,7 +9898,7 @@
|
||||
"@types/react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"storybook": "^10.4.1",
|
||||
"storybook": "^10.4.2",
|
||||
"typescript": ">= 4.9.x"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
@@ -9990,9 +9978,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/react-dom-shim": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.4.1.tgz",
|
||||
"integrity": "sha512-6QFqfDNH4DMrt7yHKRfpqRopsVUc/Az+sXIdJ39IetYnHUxL3nW4NVaPc6uy/8Qi8urzUyEXL/nn7cpSIP2aPQ==",
|
||||
"version": "10.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.4.2.tgz",
|
||||
"integrity": "sha512-Eng3Yt2NCjPX94QcfyLeUFhrMj0hec2yU9J/qafBVbfj9XrFI8o+0ZwYJ7uXb9ECbvPN4y06dgt/2W/LiR417w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -10004,7 +9992,7 @@
|
||||
"@types/react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"storybook": "^10.4.1"
|
||||
"storybook": "^10.4.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
@@ -10016,15 +10004,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/react-webpack5": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/react-webpack5/-/react-webpack5-10.4.1.tgz",
|
||||
"integrity": "sha512-2jF231DrEk70I8+wVakCnKtpweGFNfxdaov883Rve0TFvhxZs42Y9PpKzSf4rusvSrWc9jdWuJ2k7ERbS50MLg==",
|
||||
"version": "10.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/react-webpack5/-/react-webpack5-10.4.2.tgz",
|
||||
"integrity": "sha512-x7xwGLxU0w6/qi29/cHhua8qiCvfE05ku4pPLTXF8TsP/zfGsY8tbdlKO2+YKp+iBG8vafVc//ZXOAty1oypDA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/builder-webpack5": "10.4.1",
|
||||
"@storybook/preset-react-webpack": "10.4.1",
|
||||
"@storybook/react": "10.4.1"
|
||||
"@storybook/builder-webpack5": "10.4.2",
|
||||
"@storybook/preset-react-webpack": "10.4.2",
|
||||
"@storybook/react": "10.4.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -10033,7 +10021,7 @@
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"storybook": "^10.4.1",
|
||||
"storybook": "^10.4.2",
|
||||
"typescript": ">= 4.9.x"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
@@ -19421,9 +19409,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-storybook": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-10.4.1.tgz",
|
||||
"integrity": "sha512-sLEvd/7lg/LtXwMjj3iFxZtoeAC/8l1Qhuw3Noa8iF8i0UIgAejUs7k6DNSqHkwrPR8caWT4+3fxdMXs1iGLTg==",
|
||||
"version": "10.4.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-10.4.2.tgz",
|
||||
"integrity": "sha512-l3/vzLRmb8VSi3X1Bo6/Pa+64naw1jFsZE5jPPA4izvVdNhH1rF4rGuOC3kDTU926qKVBQtKua8D24XWQtvcGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -19431,7 +19419,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=8",
|
||||
"storybook": "^10.4.1"
|
||||
"storybook": "^10.4.2"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-testing-library": {
|
||||
@@ -20933,9 +20921,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fuse.js": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.4.0.tgz",
|
||||
"integrity": "sha512-3UqmoSFwzX1sNB1YSk+Co0EdH29XCW2p9g48OAiy93cjKqzuABsqw2VIgSN3CmsT/wo6pIJ3F0Jxeiiby8rhIQ==",
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.4.1.tgz",
|
||||
"integrity": "sha512-AY7lKAXK71hi3WgUvDy6oZL67UEHOOtvCAwVdOXHyJd6ZzftBy7QqxuXt4HxmmAhYjmp/YCuOELZtIvAdlZ+fw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
@@ -39048,9 +39036,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/storybook": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/storybook/-/storybook-10.4.1.tgz",
|
||||
"integrity": "sha512-V1Zd2e+gBFufqAQVZ1JR8KLqALsEZ3JYSBnWwQbKa6zCfWWanR6AFMyuOkLt2gZOgGp3h2Riuz88pGNVTQSG0A==",
|
||||
"version": "10.4.2",
|
||||
"resolved": "https://registry.npmjs.org/storybook/-/storybook-10.4.2.tgz",
|
||||
"integrity": "sha512-5Ax5vbHxFgMBGGhQDm75Rrumm/HZC4ICFhMcJaM0UlqnC/4FKj/IaZtImZFupknyiiyUEcWHPQFA2kX3/VSv1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -45375,7 +45363,7 @@
|
||||
"@deck.gl/extensions": "~9.2.9",
|
||||
"@deck.gl/geo-layers": "~9.2.5",
|
||||
"@deck.gl/layers": "~9.2.5",
|
||||
"@deck.gl/mapbox": "~9.3.2",
|
||||
"@deck.gl/mapbox": "^9.3.3",
|
||||
"@deck.gl/mesh-layers": "~9.2.5",
|
||||
"@luma.gl/constants": "~9.2.5",
|
||||
"@luma.gl/core": "~9.2.5",
|
||||
@@ -45423,9 +45411,9 @@
|
||||
}
|
||||
},
|
||||
"plugins/preset-chart-deckgl/node_modules/@deck.gl/mapbox": {
|
||||
"version": "9.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/mapbox/-/mapbox-9.3.2.tgz",
|
||||
"integrity": "sha512-+T9pJwsOXwjUxyGN6oiBMfIs28VtDIG1V1Rqz4qqn4TjjNEFFw+xO0olJIg8FO5IAqw2OtePdsrMj0tX8tHdGQ==",
|
||||
"version": "9.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/mapbox/-/mapbox-9.3.3.tgz",
|
||||
"integrity": "sha512-aUPqrwF6wkx+EtvKA3SaiK+UROMnZSmgEJWZ1qSKFSiH//kPuo5imbtXyan8sGhOet7NjnfEwJqFA3EBk7zDLA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@math.gl/web-mercator": "^4.1.0"
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
"echarts": "^5.6.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fs-extra": "^11.3.5",
|
||||
"fuse.js": "^7.3.0",
|
||||
"fuse.js": "^7.4.1",
|
||||
"geolib": "^3.3.14",
|
||||
"geostyler": "^18.6.0",
|
||||
"geostyler-data": "^1.1.0",
|
||||
@@ -261,13 +261,13 @@
|
||||
"@babel/types": "^7.29.7",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/jest": "^11.14.2",
|
||||
"@formatjs/intl-durationformat": "^0.10.3",
|
||||
"@formatjs/intl-durationformat": "^0.10.13",
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@playwright/test": "^1.60.0",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
|
||||
"@storybook/addon-docs": "10.4.1",
|
||||
"@storybook/addon-links": "10.4.1",
|
||||
"@storybook/react-webpack5": "10.4.1",
|
||||
"@storybook/addon-docs": "10.4.2",
|
||||
"@storybook/addon-links": "10.4.2",
|
||||
"@storybook/react-webpack5": "10.4.2",
|
||||
"@storybook/test-runner": "0.24.4",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@swc/core": "^1.15.40",
|
||||
@@ -325,7 +325,7 @@
|
||||
"eslint-plugin-prettier": "^5.5.6",
|
||||
"eslint-plugin-react-prefer-function-component": "^5.0.0",
|
||||
"eslint-plugin-react-you-might-not-need-an-effect": "^0.10.4",
|
||||
"eslint-plugin-storybook": "10.4.1",
|
||||
"eslint-plugin-storybook": "10.4.2",
|
||||
"eslint-plugin-testing-library": "^7.16.2",
|
||||
"eslint-plugin-theme-colors": "file:eslint-rules/eslint-plugin-theme-colors",
|
||||
"fetch-mock": "^12.6.0",
|
||||
@@ -355,7 +355,7 @@
|
||||
"source-map": "^0.7.6",
|
||||
"source-map-support": "^0.5.21",
|
||||
"speed-measure-webpack-plugin": "^1.6.0",
|
||||
"storybook": "10.4.1",
|
||||
"storybook": "10.4.2",
|
||||
"style-loader": "^4.0.0",
|
||||
"swc-loader": "^0.2.7",
|
||||
"terser-webpack-plugin": "^5.6.1",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"d3-time": "^3.1.0",
|
||||
"d3-time-format": "^4.1.0",
|
||||
"dayjs": "^1.11.21",
|
||||
"dompurify": "^3.4.7",
|
||||
"dompurify": "^3.4.8",
|
||||
"fetch-retry": "^6.0.0",
|
||||
"handlebars": "^4.7.9",
|
||||
"jed": "^1.1.1",
|
||||
|
||||
@@ -35,7 +35,7 @@ test('format milliseconds in human readable format with default options', () =>
|
||||
});
|
||||
test('format seconds in human readable format with default options', () => {
|
||||
const formatter = createDurationFormatter({ multiplier: 1000 });
|
||||
expect(formatter(-0.5)).toBe('-0s');
|
||||
expect(formatter(-0.5)).toBe('0s');
|
||||
expect(formatter(0.5)).toBe('0s');
|
||||
expect(formatter(1)).toBe('1s');
|
||||
expect(formatter(30)).toBe('30s');
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"fast-safe-stringify": "^2.1.1",
|
||||
"lodash": "^4.18.1",
|
||||
"nvd3-fork": "^2.0.5",
|
||||
"dompurify": "^3.4.7",
|
||||
"dompurify": "^3.4.8",
|
||||
"prop-types": "^15.8.1",
|
||||
"urijs": "^1.19.11"
|
||||
},
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"@deck.gl/extensions": "~9.2.9",
|
||||
"@deck.gl/geo-layers": "~9.2.5",
|
||||
"@deck.gl/layers": "~9.2.5",
|
||||
"@deck.gl/mapbox": "~9.3.2",
|
||||
"@deck.gl/mapbox": "~9.3.3",
|
||||
"@deck.gl/mesh-layers": "~9.2.5",
|
||||
"@luma.gl/constants": "~9.2.5",
|
||||
"@luma.gl/core": "~9.2.5",
|
||||
|
||||
@@ -168,31 +168,6 @@ test('non-text chart shows screenshot width and message content', () => {
|
||||
expect(screen.getByText('Screenshot width')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('screenshot width input preserves a typed zero instead of dropping it', () => {
|
||||
const lineChartProps = {
|
||||
...defaultProps,
|
||||
dashboardId: undefined,
|
||||
chart: { id: 1, sliceFormData: { viz_type: VizType.Line } },
|
||||
chartName: 'My Line Chart',
|
||||
creationMethod: 'charts' as const,
|
||||
};
|
||||
render(<ReportModal {...lineChartProps} />, { useRedux: true });
|
||||
|
||||
const widthInput = screen.getByPlaceholderText(
|
||||
'Input custom width in pixels',
|
||||
);
|
||||
|
||||
// The old `|| null` / `|| ''` logic silently coerced a typed 0 to null, so the
|
||||
// invalid width was swallowed instead of being submitted and surfaced by the
|
||||
// server's min-width validation. The field must preserve the literal value.
|
||||
userEvent.type(widthInput, '0');
|
||||
expect(widthInput).toHaveDisplayValue('0');
|
||||
|
||||
// Clearing the field still yields an empty value (parsed NaN → null).
|
||||
userEvent.clear(widthInput);
|
||||
expect(widthInput).toHaveDisplayValue('');
|
||||
});
|
||||
|
||||
test('dashboard report hides message content section', () => {
|
||||
const dashboardProps = {
|
||||
...defaultProps,
|
||||
|
||||
@@ -296,12 +296,11 @@ function ReportModal({
|
||||
<Input
|
||||
type="number"
|
||||
name="custom_width"
|
||||
value={currentReport?.custom_width ?? ''}
|
||||
value={currentReport?.custom_width || ''}
|
||||
placeholder={t('Input custom width in pixels')}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
const parsedWidth = parseInt(event.target.value, 10);
|
||||
setCurrentReport({
|
||||
custom_width: Number.isNaN(parsedWidth) ? null : parsedWidth,
|
||||
custom_width: parseInt(event.target.value, 10) || null,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
2
superset-websocket/package-lock.json
generated
2
superset-websocket/package-lock.json
generated
@@ -25,7 +25,7 @@
|
||||
"@types/lodash": "^4.17.24",
|
||||
"@types/node": "^25.9.1",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.60.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.60.1",
|
||||
"@typescript-eslint/parser": "^8.60.1",
|
||||
"eslint": "^10.4.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"@types/lodash": "^4.17.24",
|
||||
"@types/node": "^25.9.1",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.60.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.60.1",
|
||||
"@typescript-eslint/parser": "^8.60.1",
|
||||
"eslint": "^10.4.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
|
||||
118
superset-websocket/utils/client-ws-app/package-lock.json
generated
118
superset-websocket/utils/client-ws-app/package-lock.json
generated
@@ -13,7 +13,7 @@
|
||||
"express": "~5.2.1",
|
||||
"http-errors": "~2.0.1",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"morgan": "~1.10.1",
|
||||
"morgan": "~1.11.0",
|
||||
"pug": "~3.0.4"
|
||||
}
|
||||
},
|
||||
@@ -127,17 +127,6 @@
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-equal-constant-time": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||
@@ -455,17 +444,6 @@
|
||||
"node": ">=6.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
||||
@@ -483,18 +461,6 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler/node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
@@ -867,18 +833,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/morgan": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz",
|
||||
"integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==",
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.11.0.tgz",
|
||||
"integrity": "sha512-zSkVu3t18r39pw4ixfBKvfZi3y2UOqr7d4WYwcj3m8nXpEQK4rPO6GLzs/CExoRgmX3y9EjmmcXqv6jq0SK46g==",
|
||||
"dependencies": {
|
||||
"basic-auth": "~2.0.1",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~2.0.0",
|
||||
"on-finished": "~2.3.0",
|
||||
"on-finished": "~2.4.1",
|
||||
"on-headers": "~1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/morgan/node_modules/debug": {
|
||||
@@ -928,9 +898,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
@@ -1227,18 +1197,6 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
|
||||
@@ -1509,16 +1467,6 @@
|
||||
"qs": "^6.14.0",
|
||||
"raw-body": "^3.0.1",
|
||||
"type-is": "^2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"requires": {
|
||||
"ee-first": "1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"buffer-equal-constant-time": {
|
||||
@@ -1740,14 +1688,6 @@
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
|
||||
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="
|
||||
},
|
||||
"on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"requires": {
|
||||
"ee-first": "1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1762,16 +1702,6 @@
|
||||
"on-finished": "^2.4.1",
|
||||
"parseurl": "^1.3.3",
|
||||
"statuses": "^2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"requires": {
|
||||
"ee-first": "1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"forwarded": {
|
||||
@@ -2040,14 +1970,14 @@
|
||||
}
|
||||
},
|
||||
"morgan": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz",
|
||||
"integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==",
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.11.0.tgz",
|
||||
"integrity": "sha512-zSkVu3t18r39pw4ixfBKvfZi3y2UOqr7d4WYwcj3m8nXpEQK4rPO6GLzs/CExoRgmX3y9EjmmcXqv6jq0SK46g==",
|
||||
"requires": {
|
||||
"basic-auth": "~2.0.1",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~2.0.0",
|
||||
"on-finished": "~2.3.0",
|
||||
"on-finished": "~2.4.1",
|
||||
"on-headers": "~1.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -2087,9 +2017,9 @@
|
||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="
|
||||
},
|
||||
"on-finished": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"requires": {
|
||||
"ee-first": "1.1.1"
|
||||
}
|
||||
@@ -2337,16 +2267,6 @@
|
||||
"on-finished": "^2.4.1",
|
||||
"range-parser": "^1.2.1",
|
||||
"statuses": "^2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"requires": {
|
||||
"ee-first": "1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve-static": {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"express": "~5.2.1",
|
||||
"http-errors": "~2.0.1",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"morgan": "~1.10.1",
|
||||
"morgan": "~1.11.0",
|
||||
"pug": "~3.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,19 @@ class DatasourceTypeUpdateRequiredValidationError(ValidationError):
|
||||
)
|
||||
|
||||
|
||||
class ChartQueryContextDatasourceMismatchValidationError(ValidationError):
|
||||
"""
|
||||
Raised when a query-context-only update carries a datasource that does not
|
||||
match the chart's own datasource.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__(
|
||||
_("The query context datasource does not match the chart datasource"),
|
||||
field_name="query_context",
|
||||
)
|
||||
|
||||
|
||||
class ChartNotFoundError(CommandException):
|
||||
message = "Chart not found."
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ from superset.commands.chart.exceptions import (
|
||||
ChartForbiddenError,
|
||||
ChartInvalidError,
|
||||
ChartNotFoundError,
|
||||
ChartQueryContextDatasourceMismatchValidationError,
|
||||
ChartUpdateFailedError,
|
||||
DashboardsForbiddenError,
|
||||
DashboardsNotFoundValidationError,
|
||||
@@ -41,6 +42,7 @@ from superset.exceptions import SupersetSecurityException
|
||||
from superset.models.dashboard import Dashboard
|
||||
from superset.models.slice import Slice
|
||||
from superset.tags.models import ObjectType
|
||||
from superset.utils import json
|
||||
from superset.utils.decorators import on_error, transaction
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -101,6 +103,51 @@ class UpdateChartCommand(UpdateMixin, BaseCommand):
|
||||
if not security_manager.is_owner(dash):
|
||||
raise DashboardsForbiddenError()
|
||||
|
||||
def _validate_query_context_datasource(
|
||||
self, exceptions: list[ValidationError]
|
||||
) -> None:
|
||||
"""
|
||||
Ensure a query-context-only update keeps the chart's own datasource.
|
||||
|
||||
The submitted query context is only verified when it carries a parseable
|
||||
``datasource`` object; a payload that references a different datasource than
|
||||
the chart's persisted one is rejected. Payloads without a datasource fall
|
||||
back to the chart's datasource at execution time and need no check.
|
||||
"""
|
||||
if not self._model:
|
||||
return
|
||||
|
||||
raw_query_context = self._properties.get("query_context")
|
||||
if not raw_query_context:
|
||||
return
|
||||
|
||||
try:
|
||||
query_context = json.loads(raw_query_context)
|
||||
except (TypeError, ValueError):
|
||||
# An unparseable payload cannot be verified or replayed; leave it for
|
||||
# downstream handling rather than guessing at its intent.
|
||||
return
|
||||
|
||||
datasource = (
|
||||
query_context.get("datasource") if isinstance(query_context, dict) else None
|
||||
)
|
||||
if not isinstance(datasource, dict):
|
||||
return
|
||||
|
||||
try:
|
||||
ids_match = int(datasource["id"]) == self._model.datasource_id
|
||||
except (KeyError, TypeError, ValueError):
|
||||
ids_match = False
|
||||
|
||||
datasource_type = datasource.get("type")
|
||||
types_match = (
|
||||
datasource_type is None
|
||||
or str(datasource_type) == self._model.datasource_type
|
||||
)
|
||||
|
||||
if not ids_match or not types_match:
|
||||
exceptions.append(ChartQueryContextDatasourceMismatchValidationError())
|
||||
|
||||
def validate(self) -> None: # noqa: C901
|
||||
exceptions: list[ValidationError] = []
|
||||
dashboard_ids = self._properties.get("dashboards")
|
||||
@@ -134,6 +181,12 @@ class UpdateChartCommand(UpdateMixin, BaseCommand):
|
||||
raise ChartForbiddenError() from ex
|
||||
except ValidationError as ex:
|
||||
exceptions.append(ex)
|
||||
else:
|
||||
# The query-context-only path skips the ownership check so report and
|
||||
# alert workers can refresh a chart's cached payload. Keep that payload
|
||||
# bound to the chart's own datasource so it cannot be repointed at an
|
||||
# unrelated one.
|
||||
self._validate_query_context_datasource(exceptions)
|
||||
|
||||
# validate tags
|
||||
try:
|
||||
|
||||
@@ -25,14 +25,6 @@ from superset.reports.models import ReportSchedule
|
||||
from superset.views.base import BaseFilter
|
||||
|
||||
|
||||
def _escape_like(value: str) -> str:
|
||||
"""
|
||||
Escape LIKE/ILIKE wildcard characters so user-supplied search text is matched
|
||||
literally instead of being interpreted as wildcards (e.g. ``%`` and ``_``).
|
||||
"""
|
||||
return value.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_")
|
||||
|
||||
|
||||
class ReportScheduleFilter(BaseFilter): # pylint: disable=too-few-public-methods
|
||||
def apply(self, query: Query, value: Any) -> Query:
|
||||
if security_manager.can_access_all_datasources():
|
||||
@@ -55,11 +47,11 @@ class ReportScheduleAllTextFilter(BaseFilter): # pylint: disable=too-few-public
|
||||
def apply(self, query: Query, value: Any) -> Query:
|
||||
if not value:
|
||||
return query
|
||||
ilike_value = f"%{_escape_like(value)}%"
|
||||
ilike_value = f"%{value}%"
|
||||
return query.filter(
|
||||
or_(
|
||||
ReportSchedule.name.ilike(ilike_value, escape="\\"),
|
||||
ReportSchedule.description.ilike(ilike_value, escape="\\"),
|
||||
ReportSchedule.sql.ilike(ilike_value, escape="\\"),
|
||||
ReportSchedule.name.ilike(ilike_value),
|
||||
ReportSchedule.description.ilike(ilike_value),
|
||||
ReportSchedule.sql.ilike(ilike_value),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -26,6 +26,7 @@ import pandas as pd
|
||||
import simplejson
|
||||
from flask_babel.speaklater import LazyString
|
||||
from jsonpath_ng import parse
|
||||
from jsonpath_ng.jsonpath import Child, Fields, Root
|
||||
from simplejson import JSONDecodeError
|
||||
|
||||
from superset.constants import PASSWORD_MASK
|
||||
@@ -304,6 +305,30 @@ def reveal_sensitive(
|
||||
return revealed_payload
|
||||
|
||||
|
||||
def _render_jsonpath(node: Any) -> str:
|
||||
"""
|
||||
Render a JSONPath node as a stable dotted string (e.g. ``foo.bar``).
|
||||
|
||||
``str()`` of a jsonpath-ng path is not stable across releases: as of
|
||||
jsonpath-ng 1.8.0 a ``Child`` node renders with surrounding parentheses
|
||||
(e.g. ``(foo.bar)``). This helper produces the historic dotted notation so
|
||||
that the strings returned by :func:`get_masked_fields` remain consistent and
|
||||
can be round-tripped back through ``parse``.
|
||||
|
||||
Falls back to ``str()`` for node kinds that aren't plain field access (e.g.
|
||||
array indices, slices), preserving their existing representation.
|
||||
"""
|
||||
if isinstance(node, Child):
|
||||
left = _render_jsonpath(node.left)
|
||||
right = _render_jsonpath(node.right)
|
||||
return f"{left}.{right}" if left else right
|
||||
if isinstance(node, Fields):
|
||||
return ".".join(node.fields)
|
||||
if isinstance(node, Root):
|
||||
return ""
|
||||
return str(node)
|
||||
|
||||
|
||||
def get_masked_fields(
|
||||
payload: dict[str, Any],
|
||||
sensitive_fields: set[str],
|
||||
@@ -321,8 +346,10 @@ def get_masked_fields(
|
||||
for match in jsonpath_expr.find(payload):
|
||||
if match.value == PASSWORD_MASK:
|
||||
# Using `match.full_path` instead of json_path to account
|
||||
# for wildcards
|
||||
masked.append(f"$.{match.full_path}")
|
||||
# for wildcards. Render the path explicitly so the output is
|
||||
# stable across jsonpath-ng versions (newer releases wrap
|
||||
# `Child` paths in parentheses when stringified).
|
||||
masked.append(f"$.{_render_jsonpath(match.full_path)}")
|
||||
return masked
|
||||
|
||||
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
import pytest
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from superset.commands.chart.exceptions import ChartForbiddenError
|
||||
from superset.commands.chart.exceptions import ChartForbiddenError, ChartInvalidError
|
||||
from superset.commands.chart.update import UpdateChartCommand
|
||||
from superset.errors import ErrorLevel, SupersetError, SupersetErrorType
|
||||
from superset.exceptions import SupersetSecurityException
|
||||
from superset.utils import json
|
||||
|
||||
|
||||
def _ownership_exc() -> SupersetSecurityException:
|
||||
@@ -91,3 +92,73 @@ def test_update_chart_owner_can_perform_regular_update(
|
||||
|
||||
find_by_id.assert_called_once_with(1)
|
||||
raise_for_ownership.assert_called_once()
|
||||
|
||||
|
||||
def _query_context_payload(datasource: object) -> dict[str, object]:
|
||||
return {
|
||||
"query_context": json.dumps({"datasource": datasource, "queries": []}),
|
||||
"query_context_generation": True,
|
||||
}
|
||||
|
||||
|
||||
def test_update_chart_query_context_matching_datasource_is_allowed(
|
||||
mocker: MockerFixture,
|
||||
) -> None:
|
||||
"""A query context that targets the chart's own datasource is accepted."""
|
||||
find_by_id = mocker.patch("superset.commands.chart.update.ChartDAO.find_by_id")
|
||||
find_by_id.return_value = mocker.MagicMock(
|
||||
id=1, tags=[], dashboards=[], datasource_id=42, datasource_type="table"
|
||||
)
|
||||
mocker.patch("superset.commands.chart.update.security_manager.raise_for_ownership")
|
||||
|
||||
UpdateChartCommand(
|
||||
1, _query_context_payload({"id": 42, "type": "table"})
|
||||
).validate()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"datasource",
|
||||
[
|
||||
{"id": 99, "type": "table"}, # different id
|
||||
{"id": 42, "type": "query"}, # different type
|
||||
{"id": "99", "type": "table"}, # different id as string
|
||||
],
|
||||
)
|
||||
def test_update_chart_query_context_mismatched_datasource_is_rejected(
|
||||
mocker: MockerFixture,
|
||||
datasource: dict[str, object],
|
||||
) -> None:
|
||||
"""A query context pointing at a different datasource is rejected with a 4xx."""
|
||||
find_by_id = mocker.patch("superset.commands.chart.update.ChartDAO.find_by_id")
|
||||
find_by_id.return_value = mocker.MagicMock(
|
||||
id=1, tags=[], dashboards=[], datasource_id=42, datasource_type="table"
|
||||
)
|
||||
mocker.patch("superset.commands.chart.update.security_manager.raise_for_ownership")
|
||||
|
||||
with pytest.raises(ChartInvalidError):
|
||||
UpdateChartCommand(1, _query_context_payload(datasource)).validate()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"query_context",
|
||||
[
|
||||
"{}", # no datasource key
|
||||
'{"datasource": null}', # null datasource
|
||||
"not-json", # unparseable payload
|
||||
],
|
||||
)
|
||||
def test_update_chart_query_context_without_datasource_is_allowed(
|
||||
mocker: MockerFixture,
|
||||
query_context: str,
|
||||
) -> None:
|
||||
"""Payloads with no verifiable datasource fall back to the chart's own."""
|
||||
find_by_id = mocker.patch("superset.commands.chart.update.ChartDAO.find_by_id")
|
||||
find_by_id.return_value = mocker.MagicMock(
|
||||
id=1, tags=[], dashboards=[], datasource_id=42, datasource_type="table"
|
||||
)
|
||||
mocker.patch("superset.commands.chart.update.security_manager.raise_for_ownership")
|
||||
|
||||
UpdateChartCommand(
|
||||
1,
|
||||
{"query_context": query_context, "query_context_generation": True},
|
||||
).validate()
|
||||
|
||||
@@ -447,43 +447,6 @@ def test_ownership_check_raises_forbidden(mocker: MockerFixture) -> None:
|
||||
cmd.validate()
|
||||
|
||||
|
||||
# --- Dashboard extra (activeTabs) validation on update ---
|
||||
|
||||
|
||||
def test_update_rejects_invalid_active_tab_ids(mocker: MockerFixture) -> None:
|
||||
"""On PUT, activeTabs must be validated against the model's dashboard layout.
|
||||
|
||||
The dashboard is not in the payload, so validation must fall back to the
|
||||
existing model's dashboard; tab ids absent from position_json are rejected.
|
||||
"""
|
||||
model = _make_model(mocker, model_type=ReportScheduleType.REPORT, database_id=None)
|
||||
model.dashboard.position_json = '{"TAB-valid": {}}'
|
||||
_setup_mocks(mocker, model)
|
||||
|
||||
cmd = UpdateReportScheduleCommand(
|
||||
model_id=1,
|
||||
data={"extra": {"dashboard": {"activeTabs": ["TAB-missing"]}}},
|
||||
)
|
||||
with pytest.raises(ReportScheduleInvalidError) as exc_info:
|
||||
cmd.validate()
|
||||
messages = _get_validation_messages(exc_info)
|
||||
assert "extra" in messages
|
||||
assert "invalid tab ids" in messages["extra"].lower()
|
||||
|
||||
|
||||
def test_update_accepts_valid_active_tab_ids(mocker: MockerFixture) -> None:
|
||||
"""A tab id present in the model dashboard's position_json passes validation."""
|
||||
model = _make_model(mocker, model_type=ReportScheduleType.REPORT, database_id=None)
|
||||
model.dashboard.position_json = '{"TAB-valid": {}}'
|
||||
_setup_mocks(mocker, model)
|
||||
|
||||
cmd = UpdateReportScheduleCommand(
|
||||
model_id=1,
|
||||
data={"extra": {"dashboard": {"activeTabs": ["TAB-valid"]}}},
|
||||
)
|
||||
cmd.validate() # should not raise
|
||||
|
||||
|
||||
# --- Database not found for alert ---
|
||||
|
||||
|
||||
|
||||
@@ -89,42 +89,3 @@ def test_find_last_error_notification_returns_log_when_only_errors(
|
||||
|
||||
result = ReportScheduleDAO.find_last_error_notification(schedule)
|
||||
assert result is error_log
|
||||
|
||||
|
||||
@patch("superset.daos.report.ReportSchedule")
|
||||
@patch("superset.daos.report.db")
|
||||
def test_find_by_extra_metadata_escapes_like_wildcards(
|
||||
mock_db: MagicMock, mock_report_schedule: MagicMock
|
||||
) -> None:
|
||||
"""LIKE wildcards in the slug must be escaped so they match literally."""
|
||||
from superset.daos.report import ReportScheduleDAO
|
||||
|
||||
expected: list[MagicMock] = [MagicMock()]
|
||||
mock_db.session.query.return_value.filter.return_value.all.return_value = expected
|
||||
|
||||
result = ReportScheduleDAO.find_by_extra_metadata("100%_off")
|
||||
|
||||
assert result is expected
|
||||
# autoescape=True is what neutralises the LIKE wildcards in the slug
|
||||
mock_report_schedule.extra_json.contains.assert_called_once_with(
|
||||
"100%_off", autoescape=True
|
||||
)
|
||||
|
||||
|
||||
@patch("superset.daos.report.ReportSchedule")
|
||||
@patch("superset.daos.report.db")
|
||||
def test_find_by_native_filter_id_escapes_like_wildcards(
|
||||
mock_db: MagicMock, mock_report_schedule: MagicMock
|
||||
) -> None:
|
||||
"""LIKE wildcards in the filter id must be escaped so they match literally."""
|
||||
from superset.daos.report import ReportScheduleDAO
|
||||
|
||||
expected: list[MagicMock] = [MagicMock()]
|
||||
mock_db.session.query.return_value.filter.return_value.all.return_value = expected
|
||||
|
||||
result = ReportScheduleDAO.find_by_native_filter_id("NATIVE_FILTER-%_x")
|
||||
|
||||
assert result is expected
|
||||
mock_report_schedule.extra_json.contains.assert_called_once_with(
|
||||
"NATIVE_FILTER-%_x", autoescape=True
|
||||
)
|
||||
|
||||
@@ -62,26 +62,3 @@ def test_report_schedule_all_text_filter_applies_ilike() -> None:
|
||||
f = ReportScheduleAllTextFilter("name", MagicMock())
|
||||
f.apply(query, "test")
|
||||
query.filter.assert_called_once()
|
||||
|
||||
|
||||
@patch("superset.reports.filters.or_")
|
||||
@patch("superset.reports.filters.ReportSchedule")
|
||||
def test_report_schedule_all_text_filter_escapes_wildcards(
|
||||
mock_report_schedule: MagicMock, mock_or: MagicMock
|
||||
) -> None:
|
||||
"""User-supplied wildcards must be escaped so they match literally."""
|
||||
from superset.reports.filters import ReportScheduleAllTextFilter
|
||||
|
||||
query = MagicMock()
|
||||
f = ReportScheduleAllTextFilter("name", MagicMock())
|
||||
# raw input contains every LIKE special character plus a backslash
|
||||
f.apply(query, "50%_off\\promo")
|
||||
|
||||
# %, _ and \ are all escaped, and the literal is wrapped for a "contains" match
|
||||
expected = "%50\\%\\_off\\\\promo%"
|
||||
for column in (
|
||||
mock_report_schedule.name,
|
||||
mock_report_schedule.description,
|
||||
mock_report_schedule.sql,
|
||||
):
|
||||
column.ilike.assert_called_once_with(expected, escape="\\")
|
||||
|
||||
81
tests/unit_tests/utils/test_split.py
Normal file
81
tests/unit_tests/utils/test_split.py
Normal file
@@ -0,0 +1,81 @@
|
||||
# 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.
|
||||
|
||||
from superset.utils.core import split
|
||||
|
||||
|
||||
def test_split_empty_string():
|
||||
assert list(split("")) == [""]
|
||||
|
||||
|
||||
def test_split_leading_delimiter():
|
||||
assert list(split(" a")) == [
|
||||
"",
|
||||
"a",
|
||||
]
|
||||
|
||||
|
||||
def test_split_trailing_delimiter():
|
||||
assert list(split("a ")) == [
|
||||
"a",
|
||||
"",
|
||||
]
|
||||
|
||||
|
||||
def test_split_only_delimiter():
|
||||
assert list(split(" ")) == [
|
||||
"",
|
||||
"",
|
||||
]
|
||||
|
||||
|
||||
def test_split_nested_parentheses():
|
||||
assert list(
|
||||
split(
|
||||
"a,(b,(c,d))",
|
||||
delimiter=",",
|
||||
)
|
||||
) == [
|
||||
"a",
|
||||
"(b,(c,d))",
|
||||
]
|
||||
|
||||
|
||||
def test_branch_separator_found():
|
||||
assert list(split("a b")) == [
|
||||
"a",
|
||||
"b",
|
||||
]
|
||||
|
||||
|
||||
def test_branch_separator_not_found():
|
||||
assert list(split("ab")) == [
|
||||
"ab",
|
||||
]
|
||||
|
||||
|
||||
def test_branch_parentheses():
|
||||
assert list(split("(a b)")) == [
|
||||
"(a b)",
|
||||
]
|
||||
|
||||
|
||||
def test_branch_escaped_quote():
|
||||
assert list(split(r'"a\"b c" d')) == [
|
||||
r'"a\"b c"',
|
||||
"d",
|
||||
]
|
||||
Reference in New Issue
Block a user