mirror of
https://github.com/apache/superset.git
synced 2026-07-17 20:25:35 +00:00
Compare commits
2 Commits
claude/ant
...
chore/remo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd544b8bcb | ||
|
|
79e09ec1b3 |
13
UPDATING.md
13
UPDATING.md
@@ -24,6 +24,19 @@ assists people when migrating to a new version.
|
||||
|
||||
## Next
|
||||
|
||||
### Removed deck.gl JavaScript tooltip/data-mutator controls and ENABLE_JAVASCRIPT_CONTROLS
|
||||
|
||||
The `ENABLE_JAVASCRIPT_CONTROLS` feature flag and the deck.gl chart controls it gated
|
||||
(`js_tooltip`, `js_onclick_href`, `js_data_mutator`, and the GeoJSON layer's label/icon
|
||||
JavaScript-mode generators) have been removed. These controls let users write arbitrary
|
||||
JavaScript, sandboxed via Node's `vm` module, to customize deck.gl tooltips, click
|
||||
behavior, and data transforms; the flag defaulted off and the feature saw negligible use.
|
||||
|
||||
Any saved charts with these fields set will simply ignore them going forward and fall back
|
||||
to deck.gl's built-in field-based tooltips (`tooltip_contents`/`tooltip_template`) and
|
||||
native click/cross-filter behavior. No migration is required; the fields are dropped
|
||||
silently on next save.
|
||||
|
||||
### Owners, dashboard roles, and RLS roles replaced by Subjects
|
||||
|
||||
Superset now uses subject-based access assignments for dashboards, charts, datasets,
|
||||
|
||||
6
docs/static/feature-flags.json
vendored
6
docs/static/feature-flags.json
vendored
@@ -405,12 +405,6 @@
|
||||
"default": true,
|
||||
"lifecycle": "deprecated",
|
||||
"description": "Enable drill-to-detail functionality in charts"
|
||||
},
|
||||
{
|
||||
"name": "ENABLE_JAVASCRIPT_CONTROLS",
|
||||
"default": false,
|
||||
"lifecycle": "deprecated",
|
||||
"description": "Allow JavaScript in chart controls. WARNING: XSS security vulnerability!"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ export default {
|
||||
},
|
||||
fallback: {
|
||||
tty: false,
|
||||
vm: require.resolve('vm-browserify')
|
||||
vm: false,
|
||||
}
|
||||
},
|
||||
plugins: [...config.plugins, ...filteredPlugins],
|
||||
|
||||
255
superset-frontend/package-lock.json
generated
255
superset-frontend/package-lock.json
generated
@@ -282,7 +282,6 @@
|
||||
"tsx": "^4.23.0",
|
||||
"typescript": "5.4.5",
|
||||
"unzipper": "^0.12.5",
|
||||
"vm-browserify": "^1.1.2",
|
||||
"wait-on": "^9.0.10",
|
||||
"webpack": "^5.108.4",
|
||||
"webpack-bundle-analyzer": "^5.3.0",
|
||||
@@ -8569,9 +8568,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -8589,9 +8585,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -8609,9 +8602,6 @@
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -8629,9 +8619,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -8649,9 +8636,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -8669,9 +8653,6 @@
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -8689,9 +8670,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -8709,9 +8687,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -12646,13 +12621,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/underscore": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.13.0.tgz",
|
||||
"integrity": "sha512-L6LBgy1f0EFQZ+7uSA57+n2g/s4Qs5r06Vwrwn0/nuK1de+adz00NWaztRQ30aEqw5qOaWbPI8u2cGQ52lj6VA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/unist": {
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
|
||||
@@ -15534,6 +15502,7 @@
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
|
||||
"integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
@@ -16512,6 +16481,7 @@
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
@@ -16526,6 +16496,7 @@
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
@@ -16805,6 +16776,7 @@
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/concat-stream": {
|
||||
@@ -21394,6 +21366,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
@@ -22487,6 +22460,7 @@
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
@@ -23954,6 +23928,7 @@
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
@@ -24370,6 +24345,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -26888,6 +26864,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/jsdom/node_modules/@noble/hashes": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz",
|
||||
"integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 20.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/jsdom/node_modules/css-tree": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
|
||||
@@ -30242,6 +30233,7 @@
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.4.tgz",
|
||||
"integrity": "sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
@@ -30370,7 +30362,6 @@
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
|
||||
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
@@ -31870,6 +31861,7 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
@@ -32794,6 +32786,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -36603,6 +36596,7 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -38561,6 +38555,7 @@
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
@@ -38598,6 +38593,7 @@
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string.prototype.trim": {
|
||||
@@ -38674,6 +38670,7 @@
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
@@ -40583,38 +40580,115 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-json-schema": {
|
||||
"version": "0.67.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.67.1.tgz",
|
||||
"integrity": "sha512-vKTZB/RoYTIBdVP7E7vrgHMCssBuhja91wQy498QIVhvfRimaOgjc98uwAXmZ7mbLUytJmOSbF11wPz+ByQeXg==",
|
||||
"version": "0.68.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.68.0.tgz",
|
||||
"integrity": "sha512-1X60xXxkZpLAr125Jt5MT9VJTTxT0xfafbjdcbtml26nvpUr2p/P4V34JEXa+ObrHc71W9ksH5pemstY6y+SEw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@types/node": "^18.11.9",
|
||||
"glob": "^7.1.7",
|
||||
"@types/json-schema": "^7.0.15",
|
||||
"@types/node": "^24.10.2",
|
||||
"glob": "^13.0.6",
|
||||
"path-equal": "^1.2.5",
|
||||
"safe-stable-stringify": "^2.2.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "~5.5.0",
|
||||
"vm2": "^3.10.0",
|
||||
"yargs": "^17.1.1"
|
||||
"safe-stable-stringify": "^2.5.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "~5.9.3",
|
||||
"yargs": "^18.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"typescript-json-schema": "bin/typescript-json-schema"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-json-schema/node_modules/@types/node": {
|
||||
"version": "18.19.130",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz",
|
||||
"integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==",
|
||||
"version": "24.13.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",
|
||||
"integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
"undici-types": "~7.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-json-schema/node_modules/balanced-match": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-json-schema/node_modules/brace-expansion": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
||||
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-json-schema/node_modules/glob": {
|
||||
"version": "13.0.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
||||
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"minimatch": "^10.2.2",
|
||||
"minipass": "^7.1.3",
|
||||
"path-scurry": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-json-schema/node_modules/lru-cache": {
|
||||
"version": "11.5.2",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz",
|
||||
"integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-json-schema/node_modules/minimatch": {
|
||||
"version": "10.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
|
||||
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-json-schema/node_modules/path-scurry": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
|
||||
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"lru-cache": "^11.0.0",
|
||||
"minipass": "^7.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-json-schema/node_modules/typescript": {
|
||||
"version": "5.5.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@@ -40625,29 +40699,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-json-schema/node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"version": "7.18.2",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
||||
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/typescript-json-schema/node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/typewise": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/typewise/-/typewise-1.0.3.tgz",
|
||||
@@ -40724,6 +40780,7 @@
|
||||
"version": "1.13.8",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz",
|
||||
"integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/undici": {
|
||||
@@ -41460,41 +41517,6 @@
|
||||
"integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vm-browserify": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
|
||||
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vm2": {
|
||||
"version": "3.11.5",
|
||||
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.11.5.tgz",
|
||||
"integrity": "sha512-RSrkBiwrj6FRU+QdqNs6KG0XdlvJCjpQ4GXiqmMbrhmwfu5k/XIMpAer0L8f6iuf0uJ3a4T1xJN126Q8yf0VIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.15.0",
|
||||
"acorn-walk": "^8.3.4"
|
||||
},
|
||||
"bin": {
|
||||
"vm2": "bin/vm2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vm2/node_modules/acorn": {
|
||||
"version": "8.16.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/w3c-xmlserializer": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
|
||||
@@ -42410,6 +42432,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-url/node_modules/@noble/hashes": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz",
|
||||
"integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 20.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-url/node_modules/webidl-conversions": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz",
|
||||
@@ -42603,6 +42640,7 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/write-file-atomic": {
|
||||
@@ -42860,6 +42898,7 @@
|
||||
"version": "21.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -44349,14 +44388,12 @@
|
||||
"prop-types": "^15.8.1",
|
||||
"react-map-gl": "^8.1.1",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"underscore": "^1.13.7",
|
||||
"urijs": "^1.19.11",
|
||||
"xss": "^1.0.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mapbox__geojson-extent": "^1.0.3",
|
||||
"@types/ngeohash": "^0.6.8",
|
||||
"@types/underscore": "^1.13.0",
|
||||
"@types/urijs": "^1.19.26"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -367,7 +367,6 @@
|
||||
"tsx": "^4.23.0",
|
||||
"typescript": "5.4.5",
|
||||
"unzipper": "^0.12.5",
|
||||
"vm-browserify": "^1.1.2",
|
||||
"wait-on": "^9.0.10",
|
||||
"webpack": "^5.108.4",
|
||||
"webpack-bundle-analyzer": "^5.3.0",
|
||||
@@ -425,6 +424,7 @@
|
||||
"jest-util": "^30.4.0",
|
||||
"jest-circus": "^30.4.0",
|
||||
"jest-environment-node": "^30.4.0",
|
||||
"typescript-json-schema": "^0.68.0",
|
||||
"@babel/eslint-parser": {
|
||||
"eslint": "$eslint"
|
||||
},
|
||||
|
||||
@@ -47,8 +47,6 @@ export enum FeatureFlag {
|
||||
EnableAdvancedDataTypes = 'ENABLE_ADVANCED_DATA_TYPES',
|
||||
EnableExtensions = 'ENABLE_EXTENSIONS',
|
||||
EnableViewers = 'ENABLE_VIEWERS',
|
||||
/** @deprecated */
|
||||
EnableJavascriptControls = 'ENABLE_JAVASCRIPT_CONTROLS',
|
||||
EnableTemplateProcessing = 'ENABLE_TEMPLATE_PROCESSING',
|
||||
EscapeMarkdownHtml = 'ESCAPE_MARKDOWN_HTML',
|
||||
EstimateQueryCost = 'ESTIMATE_QUERY_COST',
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
"prop-types": "^15.8.1",
|
||||
"react-map-gl": "^8.1.1",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"underscore": "^1.13.7",
|
||||
"urijs": "^1.19.11",
|
||||
"xss": "^1.0.15",
|
||||
"lodash-es": "^4.18.1"
|
||||
@@ -59,7 +58,6 @@
|
||||
"devDependencies": {
|
||||
"@types/mapbox__geojson-extent": "^1.0.3",
|
||||
"@types/ngeohash": "^0.6.8",
|
||||
"@types/underscore": "^1.13.0",
|
||||
"@types/urijs": "^1.19.26"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -40,7 +40,6 @@ import type { Layer } from '@deck.gl/core';
|
||||
import Legend from './components/Legend';
|
||||
import { hexToRGB } from './utils/colors';
|
||||
import { getMapboxApiKey } from './utils/mapbox';
|
||||
import sandboxedEval from './utils/sandbox';
|
||||
import fitViewport, { Viewport } from './utils/fitViewport';
|
||||
import {
|
||||
DeckGLContainerHandle,
|
||||
@@ -173,12 +172,6 @@ const CategoricalDeckGLContainer = (props: CategoricalDeckGLContainerProps) => {
|
||||
// Add colors from categories or fixed color
|
||||
features = addColor(features, fd, selectedColorScheme);
|
||||
|
||||
// Apply user defined data mutator if defined
|
||||
if (fd.js_data_mutator) {
|
||||
const jsFnMutator = sandboxedEval(fd.js_data_mutator);
|
||||
features = jsFnMutator(features);
|
||||
}
|
||||
|
||||
// Show only categories selected in the legend
|
||||
if (fd.dimension) {
|
||||
features = features.filter(d => categories[d.cat_color]?.enabled);
|
||||
|
||||
@@ -32,9 +32,6 @@ import {
|
||||
filterNulls,
|
||||
autozoom,
|
||||
jsColumns,
|
||||
jsDataMutator,
|
||||
jsTooltip,
|
||||
jsOnclickHref,
|
||||
legendFormat,
|
||||
legendPosition,
|
||||
viewport,
|
||||
@@ -168,12 +165,7 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
{
|
||||
label: t('Advanced'),
|
||||
controlSetRows: [
|
||||
[jsColumns],
|
||||
[jsDataMutator],
|
||||
[jsTooltip],
|
||||
[jsOnclickHref],
|
||||
],
|
||||
controlSetRows: [[jsColumns]],
|
||||
},
|
||||
],
|
||||
controlOverrides: {
|
||||
|
||||
@@ -127,9 +127,6 @@ export const ArcChartViz = ({
|
||||
legend_position: 'tr',
|
||||
legend_format: null,
|
||||
js_columns: [],
|
||||
js_data_mutator: '',
|
||||
js_tooltip: '',
|
||||
js_onclick_href: '',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -21,7 +21,6 @@ import { PolygonLayer } from '@deck.gl/layers';
|
||||
import { Position } from '@deck.gl/core';
|
||||
import { t } from '@apache-superset/core/translation';
|
||||
import { commonLayerProps } from '../common';
|
||||
import sandboxedEval from '../../utils/sandbox';
|
||||
import { GetLayerType, createDeckGLComponent } from '../../factory';
|
||||
import { ColorType } from '../../types';
|
||||
import TooltipRow from '../../TooltipRow';
|
||||
@@ -69,13 +68,8 @@ export const getLayer: GetLayerType<ContourLayer> = function ({
|
||||
emitCrossFilters,
|
||||
}) {
|
||||
const fd = formData;
|
||||
const {
|
||||
aggregation = 'SUM',
|
||||
js_data_mutator: jsFnMutator,
|
||||
contours: rawContours,
|
||||
cellSize = '200',
|
||||
} = fd;
|
||||
let data = payload.data.features;
|
||||
const { aggregation = 'SUM', contours: rawContours, cellSize = '200' } = fd;
|
||||
const data = payload.data.features;
|
||||
|
||||
// Store original data for tooltip access
|
||||
const originalDataMap = new Map();
|
||||
@@ -113,12 +107,6 @@ export const getLayer: GetLayerType<ContourLayer> = function ({
|
||||
},
|
||||
);
|
||||
|
||||
if (jsFnMutator) {
|
||||
// Applying user defined data mutator if defined
|
||||
const jsFnMutatorFunction = sandboxedEval(fd.js_data_mutator);
|
||||
data = jsFnMutatorFunction(data);
|
||||
}
|
||||
|
||||
// Create wrapper for tooltip content that adds nearby points
|
||||
const tooltipContentGenerator = (o: any) => {
|
||||
// Find nearby points based on hover coordinate
|
||||
|
||||
@@ -26,9 +26,6 @@ import {
|
||||
autozoom,
|
||||
filterNulls,
|
||||
jsColumns,
|
||||
jsDataMutator,
|
||||
jsOnclickHref,
|
||||
jsTooltip,
|
||||
mapboxStyle,
|
||||
maplibreStyle,
|
||||
mapProvider,
|
||||
@@ -116,12 +113,7 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
{
|
||||
label: t('Advanced'),
|
||||
controlSetRows: [
|
||||
[jsColumns],
|
||||
[jsDataMutator],
|
||||
[jsTooltip],
|
||||
[jsOnclickHref],
|
||||
],
|
||||
controlSetRows: [[jsColumns]],
|
||||
},
|
||||
],
|
||||
controlOverrides: {
|
||||
|
||||
@@ -27,9 +27,7 @@ import { render } from '@testing-library/react';
|
||||
import { SqlaFormData } from '@superset-ui/core';
|
||||
import { supersetTheme, ThemeProvider } from '@apache-superset/core/theme';
|
||||
import DeckGLGeoJson, {
|
||||
computeGeoJsonTextOptionsFromJsOutput,
|
||||
computeGeoJsonTextOptionsFromFormData,
|
||||
computeGeoJsonIconOptionsFromJsOutput,
|
||||
computeGeoJsonIconOptionsFromFormData,
|
||||
getPoints,
|
||||
getLayer,
|
||||
@@ -61,26 +59,6 @@ jest.mock('react-map-gl/maplibre', () => ({
|
||||
useControl: () => null,
|
||||
}));
|
||||
|
||||
test('computeGeoJsonTextOptionsFromJsOutput returns an empty object for non-object input', () => {
|
||||
expect(computeGeoJsonTextOptionsFromJsOutput(null)).toEqual({});
|
||||
expect(computeGeoJsonTextOptionsFromJsOutput(42)).toEqual({});
|
||||
expect(computeGeoJsonTextOptionsFromJsOutput([1, 2, 3])).toEqual({});
|
||||
expect(computeGeoJsonTextOptionsFromJsOutput('string')).toEqual({});
|
||||
});
|
||||
|
||||
test('computeGeoJsonTextOptionsFromJsOutput extracts valid text options from the input object', () => {
|
||||
const input = {
|
||||
getText: 'name',
|
||||
getTextColor: [1, 2, 3, 255],
|
||||
invalidOption: true,
|
||||
};
|
||||
const expectedOutput = {
|
||||
getText: 'name',
|
||||
getTextColor: [1, 2, 3, 255],
|
||||
};
|
||||
expect(computeGeoJsonTextOptionsFromJsOutput(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
test('computeGeoJsonTextOptionsFromFormData computes text options based on form data', () => {
|
||||
const formData: SqlaFormData = {
|
||||
label_property_name: 'name',
|
||||
@@ -105,28 +83,6 @@ test('computeGeoJsonTextOptionsFromFormData computes text options based on form
|
||||
expect(actualOutput.getText(sampleFeature)).toBe('Test');
|
||||
});
|
||||
|
||||
test('computeGeoJsonIconOptionsFromJsOutput returns an empty object for non-object input', () => {
|
||||
expect(computeGeoJsonIconOptionsFromJsOutput(null)).toEqual({});
|
||||
expect(computeGeoJsonIconOptionsFromJsOutput(42)).toEqual({});
|
||||
expect(computeGeoJsonIconOptionsFromJsOutput([1, 2, 3])).toEqual({});
|
||||
expect(computeGeoJsonIconOptionsFromJsOutput('string')).toEqual({});
|
||||
});
|
||||
|
||||
test('computeGeoJsonIconOptionsFromJsOutput extracts valid icon options from the input object', () => {
|
||||
const input = {
|
||||
getIcon: 'icon_name',
|
||||
getIconColor: [1, 2, 3, 255],
|
||||
invalidOption: false,
|
||||
};
|
||||
|
||||
const expectedOutput = {
|
||||
getIcon: 'icon_name',
|
||||
getIconColor: [1, 2, 3, 255],
|
||||
};
|
||||
|
||||
expect(computeGeoJsonIconOptionsFromJsOutput(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
test('computeGeoJsonIconOptionsFromFormData computes icon options based on form data', () => {
|
||||
const formData: SqlaFormData = {
|
||||
icon_url: 'https://example.com/icon.png',
|
||||
|
||||
@@ -38,7 +38,6 @@ import {
|
||||
DeckGLContainerStyledWrapper,
|
||||
} from '../../DeckGLContainer';
|
||||
import { hexToRGB } from '../../utils/colors';
|
||||
import sandboxedEval from '../../utils/sandbox';
|
||||
import { commonLayerProps } from '../common';
|
||||
import TooltipRow from '../../TooltipRow';
|
||||
import fitViewport, { Viewport } from '../../utils/fitViewport';
|
||||
@@ -146,52 +145,6 @@ const getFillColor = (feature: JsonObject, filterStateValue: unknown[]) => {
|
||||
};
|
||||
const getLineColor = (feature: JsonObject) => feature?.properties?.strokeColor;
|
||||
|
||||
const isObject = (value: unknown): value is Record<string, unknown> =>
|
||||
typeof value === 'object' && value !== null && !Array.isArray(value);
|
||||
|
||||
export const computeGeoJsonTextOptionsFromJsOutput = (
|
||||
output: unknown,
|
||||
): Partial<GeoJsonLayerProps> => {
|
||||
if (!isObject(output)) return {};
|
||||
|
||||
// Properties sourced from:
|
||||
// https://deck.gl/docs/api-reference/layers/geojson-layer#pointtype-options-2
|
||||
const options: (keyof GeoJsonLayerProps)[] = [
|
||||
'getText',
|
||||
'getTextColor',
|
||||
'getTextAngle',
|
||||
'getTextSize',
|
||||
'getTextAnchor',
|
||||
'getTextAlignmentBaseline',
|
||||
'getTextPixelOffset',
|
||||
'getTextBackgroundColor',
|
||||
'getTextBorderColor',
|
||||
'getTextBorderWidth',
|
||||
'textSizeUnits',
|
||||
'textSizeScale',
|
||||
'textSizeMinPixels',
|
||||
'textSizeMaxPixels',
|
||||
'textCharacterSet',
|
||||
'textFontFamily',
|
||||
'textFontWeight',
|
||||
'textLineHeight',
|
||||
'textMaxWidth',
|
||||
'textWordBreak',
|
||||
'textBackground',
|
||||
'textBackgroundPadding',
|
||||
'textOutlineColor',
|
||||
'textOutlineWidth',
|
||||
'textBillboard',
|
||||
'textFontSettings',
|
||||
];
|
||||
|
||||
const allEntries = Object.entries(output);
|
||||
const validEntries = allEntries.filter(([k]) =>
|
||||
options.includes(k as keyof GeoJsonLayerProps),
|
||||
);
|
||||
return Object.fromEntries(validEntries);
|
||||
};
|
||||
|
||||
export const computeGeoJsonTextOptionsFromFormData = (
|
||||
fd: SqlaFormData,
|
||||
): Partial<GeoJsonLayerProps> => {
|
||||
@@ -205,36 +158,6 @@ export const computeGeoJsonTextOptionsFromFormData = (
|
||||
};
|
||||
};
|
||||
|
||||
export const computeGeoJsonIconOptionsFromJsOutput = (
|
||||
output: unknown,
|
||||
): Partial<GeoJsonLayerProps> => {
|
||||
if (!isObject(output)) return {};
|
||||
|
||||
// Properties sourced from:
|
||||
// https://deck.gl/docs/api-reference/layers/geojson-layer#pointtype-options-1
|
||||
const options: (keyof GeoJsonLayerProps)[] = [
|
||||
'getIcon',
|
||||
'getIconSize',
|
||||
'getIconColor',
|
||||
'getIconAngle',
|
||||
'getIconPixelOffset',
|
||||
'iconSizeUnits',
|
||||
'iconSizeScale',
|
||||
'iconSizeMinPixels',
|
||||
'iconSizeMaxPixels',
|
||||
'iconAtlas',
|
||||
'iconMapping',
|
||||
'iconBillboard',
|
||||
'iconAlphaCutoff',
|
||||
];
|
||||
|
||||
const allEntries = Object.entries(output);
|
||||
const validEntries = allEntries.filter(([k]) =>
|
||||
options.includes(k as keyof GeoJsonLayerProps),
|
||||
);
|
||||
return Object.fromEntries(validEntries);
|
||||
};
|
||||
|
||||
export const computeGeoJsonIconOptionsFromFormData = (
|
||||
fd: SqlaFormData,
|
||||
): Partial<GeoJsonLayerProps> => ({
|
||||
@@ -288,13 +211,6 @@ export const getLayer: GetLayerType<GeoJsonLayer> = function ({
|
||||
features = [];
|
||||
recurseGeoJson(payload.data, propOverrides);
|
||||
|
||||
let processedFeatures = features;
|
||||
if (fd.js_data_mutator) {
|
||||
// Applying user defined data mutator if defined
|
||||
const jsFnMutator = sandboxedEval(fd.js_data_mutator);
|
||||
processedFeatures = jsFnMutator(features) as ProcessedFeature[];
|
||||
}
|
||||
|
||||
let pointType = 'circle';
|
||||
if (fd.enable_labels) {
|
||||
pointType = `${pointType}+text`;
|
||||
@@ -303,33 +219,17 @@ export const getLayer: GetLayerType<GeoJsonLayer> = function ({
|
||||
pointType = `${pointType}+icon`;
|
||||
}
|
||||
|
||||
let labelOpts: Partial<GeoJsonLayerProps> = {};
|
||||
if (fd.enable_labels) {
|
||||
if (fd.enable_label_javascript_mode) {
|
||||
const generator = sandboxedEval(fd.label_javascript_config_generator);
|
||||
if (typeof generator === 'function') {
|
||||
labelOpts = computeGeoJsonTextOptionsFromJsOutput(generator());
|
||||
}
|
||||
} else {
|
||||
labelOpts = computeGeoJsonTextOptionsFromFormData(fd);
|
||||
}
|
||||
}
|
||||
const labelOpts: Partial<GeoJsonLayerProps> = fd.enable_labels
|
||||
? computeGeoJsonTextOptionsFromFormData(fd)
|
||||
: {};
|
||||
|
||||
let iconOpts: Partial<GeoJsonLayerProps> = {};
|
||||
if (fd.enable_icons) {
|
||||
if (fd.enable_icon_javascript_mode) {
|
||||
const generator = sandboxedEval(fd.icon_javascript_config_generator);
|
||||
if (typeof generator === 'function') {
|
||||
iconOpts = computeGeoJsonIconOptionsFromJsOutput(generator());
|
||||
}
|
||||
} else {
|
||||
iconOpts = computeGeoJsonIconOptionsFromFormData(fd);
|
||||
}
|
||||
}
|
||||
const iconOpts: Partial<GeoJsonLayerProps> = fd.enable_icons
|
||||
? computeGeoJsonIconOptionsFromFormData(fd)
|
||||
: {};
|
||||
|
||||
return new GeoJsonLayer({
|
||||
id: `geojson-layer-${fd.slice_id}` as const,
|
||||
data: processedFeatures,
|
||||
data: features,
|
||||
extruded: fd.extruded,
|
||||
filled: fd.filled,
|
||||
stroked: fd.stroked,
|
||||
|
||||
@@ -20,8 +20,6 @@ import { ControlPanelConfig } from '@superset-ui/chart-controls';
|
||||
import { t } from '@apache-superset/core/translation';
|
||||
import {
|
||||
legacyValidateInteger,
|
||||
isFeatureEnabled,
|
||||
FeatureFlag,
|
||||
validateNumber,
|
||||
validateInteger,
|
||||
} from '@superset-ui/core';
|
||||
@@ -29,9 +27,6 @@ import { formatSelectOptions } from '../../utilities/utils';
|
||||
import {
|
||||
filterNulls,
|
||||
jsColumns,
|
||||
jsDataMutator,
|
||||
jsTooltip,
|
||||
jsOnclickHref,
|
||||
fillColorPicker,
|
||||
strokeColorPicker,
|
||||
filled,
|
||||
@@ -45,29 +40,11 @@ import {
|
||||
lineWidth,
|
||||
tooltipContents,
|
||||
tooltipTemplate,
|
||||
jsFunctionControl,
|
||||
crossFilterColumn,
|
||||
} from '../../utilities/Shared_DeckGL';
|
||||
import { dndGeojsonColumn } from '../../utilities/sharedDndControls';
|
||||
import { BLACK_COLOR } from '../../utilities/controls';
|
||||
|
||||
const defaultLabelConfigGenerator = `() => ({
|
||||
// Check the documentation at:
|
||||
// https://deck.gl/docs/api-reference/layers/geojson-layer#pointtype-options-2
|
||||
getText: f => f.properties.name,
|
||||
getTextColor: [0, 0, 0, 255],
|
||||
getTextSize: 24,
|
||||
textSizeUnits: 'pixels',
|
||||
})`;
|
||||
|
||||
const defaultIconConfigGenerator = `() => ({
|
||||
// Check the documentation at:
|
||||
// https://deck.gl/docs/api-reference/layers/geojson-layer#pointtype-options-1
|
||||
getIcon: () => ({ url: '', height: 128, width: 128 }),
|
||||
getIconSize: 32,
|
||||
iconSizeUnits: 'pixels',
|
||||
})`;
|
||||
|
||||
const config: ControlPanelConfig = {
|
||||
controlPanelSections: [
|
||||
{
|
||||
@@ -110,24 +87,6 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'enable_label_javascript_mode',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Enable label JavaScript mode'),
|
||||
description: t(
|
||||
'Enables custom label configuration via JavaScript',
|
||||
),
|
||||
visibility: ({ form_data }) =>
|
||||
!!form_data.enable_labels &&
|
||||
isFeatureEnabled(FeatureFlag.EnableJavascriptControls),
|
||||
default: false,
|
||||
renderTrigger: true,
|
||||
resetOnHide: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'label_property_name',
|
||||
@@ -135,10 +94,7 @@ const config: ControlPanelConfig = {
|
||||
type: 'TextControl',
|
||||
label: t('Label property name'),
|
||||
description: t('The feature property to use for point labels'),
|
||||
visibility: ({ form_data }) =>
|
||||
!!form_data.enable_labels &&
|
||||
(!form_data.enable_label_javascript_mode ||
|
||||
!isFeatureEnabled(FeatureFlag.EnableJavascriptControls)),
|
||||
visibility: ({ form_data }) => !!form_data.enable_labels,
|
||||
default: 'name',
|
||||
renderTrigger: true,
|
||||
resetOnHide: false,
|
||||
@@ -152,10 +108,7 @@ const config: ControlPanelConfig = {
|
||||
type: 'ColorPickerControl',
|
||||
label: t('Label color'),
|
||||
description: t('The color of the point labels'),
|
||||
visibility: ({ form_data }) =>
|
||||
!!form_data.enable_labels &&
|
||||
(!form_data.enable_label_javascript_mode ||
|
||||
!isFeatureEnabled(FeatureFlag.EnableJavascriptControls)),
|
||||
visibility: ({ form_data }) => !!form_data.enable_labels,
|
||||
default: BLACK_COLOR,
|
||||
renderTrigger: true,
|
||||
resetOnHide: false,
|
||||
@@ -170,10 +123,7 @@ const config: ControlPanelConfig = {
|
||||
freeForm: true,
|
||||
label: t('Label size'),
|
||||
description: t('The font size of the point labels'),
|
||||
visibility: ({ form_data }) =>
|
||||
!!form_data.enable_labels &&
|
||||
(!form_data.enable_label_javascript_mode ||
|
||||
!isFeatureEnabled(FeatureFlag.EnableJavascriptControls)),
|
||||
visibility: ({ form_data }) => !!form_data.enable_labels,
|
||||
validators: [legacyValidateInteger],
|
||||
choices: formatSelectOptions([8, 16, 24, 32, 64, 128]),
|
||||
default: 24,
|
||||
@@ -189,10 +139,7 @@ const config: ControlPanelConfig = {
|
||||
type: 'SelectControl',
|
||||
label: t('Label size unit'),
|
||||
description: t('The unit for label size'),
|
||||
visibility: ({ form_data }) =>
|
||||
!!form_data.enable_labels &&
|
||||
(!form_data.enable_label_javascript_mode ||
|
||||
!isFeatureEnabled(FeatureFlag.EnableJavascriptControls)),
|
||||
visibility: ({ form_data }) => !!form_data.enable_labels,
|
||||
choices: [
|
||||
['meters', t('Meters')],
|
||||
['pixels', t('Pixels')],
|
||||
@@ -203,27 +150,6 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'label_javascript_config_generator',
|
||||
config: {
|
||||
...jsFunctionControl(
|
||||
t('Label JavaScript config generator'),
|
||||
t(
|
||||
'A JavaScript function that generates a label configuration object',
|
||||
),
|
||||
undefined,
|
||||
undefined,
|
||||
defaultLabelConfigGenerator,
|
||||
),
|
||||
visibility: ({ form_data }) =>
|
||||
!!form_data.enable_labels &&
|
||||
!!form_data.enable_label_javascript_mode &&
|
||||
isFeatureEnabled(FeatureFlag.EnableJavascriptControls),
|
||||
resetOnHide: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'enable_icons',
|
||||
@@ -236,24 +162,6 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'enable_icon_javascript_mode',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Enable icon JavaScript mode'),
|
||||
description: t(
|
||||
'Enables custom icon configuration via JavaScript',
|
||||
),
|
||||
visibility: ({ form_data }) =>
|
||||
!!form_data.enable_icons &&
|
||||
isFeatureEnabled(FeatureFlag.EnableJavascriptControls),
|
||||
default: false,
|
||||
renderTrigger: true,
|
||||
resetOnHide: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'icon_url',
|
||||
@@ -265,10 +173,7 @@ const config: ControlPanelConfig = {
|
||||
'Note that the image URL must conform to the content ' +
|
||||
'security policy (CSP) in order to load correctly.',
|
||||
),
|
||||
visibility: ({ form_data }) =>
|
||||
!!form_data.enable_icons &&
|
||||
(!form_data.enable_icon_javascript_mode ||
|
||||
!isFeatureEnabled(FeatureFlag.EnableJavascriptControls)),
|
||||
visibility: ({ form_data }) => !!form_data.enable_icons,
|
||||
default: '',
|
||||
renderTrigger: true,
|
||||
resetOnHide: false,
|
||||
@@ -283,10 +188,7 @@ const config: ControlPanelConfig = {
|
||||
freeForm: true,
|
||||
label: t('Icon size'),
|
||||
description: t('The size of the point icons'),
|
||||
visibility: ({ form_data }) =>
|
||||
!!form_data.enable_icons &&
|
||||
(!form_data.enable_icon_javascript_mode ||
|
||||
!isFeatureEnabled(FeatureFlag.EnableJavascriptControls)),
|
||||
visibility: ({ form_data }) => !!form_data.enable_icons,
|
||||
validators: [legacyValidateInteger],
|
||||
choices: formatSelectOptions([16, 24, 32, 64, 128]),
|
||||
default: 32,
|
||||
@@ -302,10 +204,7 @@ const config: ControlPanelConfig = {
|
||||
type: 'SelectControl',
|
||||
label: t('Icon size unit'),
|
||||
description: t('The unit for icon size'),
|
||||
visibility: ({ form_data }) =>
|
||||
!!form_data.enable_icons &&
|
||||
(!form_data.enable_icon_javascript_mode ||
|
||||
!isFeatureEnabled(FeatureFlag.EnableJavascriptControls)),
|
||||
visibility: ({ form_data }) => !!form_data.enable_icons,
|
||||
choices: [
|
||||
['meters', t('Meters')],
|
||||
['pixels', t('Pixels')],
|
||||
@@ -316,27 +215,6 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'icon_javascript_config_generator',
|
||||
config: {
|
||||
...jsFunctionControl(
|
||||
t('Icon JavaScript config generator'),
|
||||
t(
|
||||
'A JavaScript function that generates an icon configuration object',
|
||||
),
|
||||
undefined,
|
||||
undefined,
|
||||
defaultIconConfigGenerator,
|
||||
),
|
||||
visibility: ({ form_data }) =>
|
||||
!!form_data.enable_icons &&
|
||||
!!form_data.enable_icon_javascript_mode &&
|
||||
isFeatureEnabled(FeatureFlag.EnableJavascriptControls),
|
||||
resetOnHide: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
[lineWidth],
|
||||
[
|
||||
{
|
||||
@@ -411,13 +289,7 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
{
|
||||
label: t('Advanced'),
|
||||
controlSetRows: [
|
||||
[crossFilterColumn],
|
||||
[jsColumns],
|
||||
[jsDataMutator],
|
||||
[jsTooltip],
|
||||
[jsOnclickHref],
|
||||
],
|
||||
controlSetRows: [[crossFilterColumn], [jsColumns]],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -29,7 +29,6 @@ import {
|
||||
getColorForBreakpoints,
|
||||
getColorRange,
|
||||
} from '../common';
|
||||
import sandboxedEval from '../../utils/sandbox';
|
||||
import { createDeckGLComponent, GetLayerType } from '../../factory';
|
||||
import TooltipRow from '../../TooltipRow';
|
||||
import { COLOR_SCHEME_TYPES } from '../../utilities/utils';
|
||||
@@ -65,12 +64,7 @@ export const getLayer: GetLayerType<GridLayer> = function ({
|
||||
const fd = formData;
|
||||
const appliedScheme = fd.color_scheme;
|
||||
const colorScale = CategoricalColorNamespace.getScale(appliedScheme);
|
||||
let data = payload.data.features;
|
||||
|
||||
if (fd.js_data_mutator) {
|
||||
const jsFnMutator = sandboxedEval(fd.js_data_mutator);
|
||||
data = jsFnMutator(data);
|
||||
}
|
||||
const data = payload.data.features;
|
||||
|
||||
const colorBreakpoints = fd.color_breakpoints;
|
||||
|
||||
@@ -132,13 +126,7 @@ export const getHighlightLayer: GetLayerType<GridLayer> = function ({
|
||||
filterState,
|
||||
}) {
|
||||
const fd = formData;
|
||||
let data = payload.data.features;
|
||||
|
||||
if (fd.js_data_mutator) {
|
||||
// Applying user defined data mutator if defined
|
||||
const jsFnMutator = sandboxedEval(fd.js_data_mutator);
|
||||
data = jsFnMutator(data);
|
||||
}
|
||||
const data = payload.data.features;
|
||||
|
||||
const aggFunc = getAggFunc(fd.js_agg_function, p => p.weight);
|
||||
|
||||
|
||||
@@ -26,9 +26,6 @@ import {
|
||||
filterNulls,
|
||||
autozoom,
|
||||
jsColumns,
|
||||
jsDataMutator,
|
||||
jsTooltip,
|
||||
jsOnclickHref,
|
||||
extruded,
|
||||
gridSize,
|
||||
viewport,
|
||||
@@ -77,12 +74,7 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
{
|
||||
label: t('Advanced'),
|
||||
controlSetRows: [
|
||||
[jsColumns],
|
||||
[jsDataMutator],
|
||||
[jsTooltip],
|
||||
[jsOnclickHref],
|
||||
],
|
||||
controlSetRows: [[jsColumns]],
|
||||
},
|
||||
],
|
||||
controlOverrides: {
|
||||
|
||||
@@ -93,9 +93,6 @@ export const GridChartViz = ({
|
||||
grid_size: gridSize,
|
||||
extruded,
|
||||
js_columns: [],
|
||||
js_data_mutator: '',
|
||||
js_tooltip: '',
|
||||
js_onclick_href: '',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
} from '@superset-ui/core';
|
||||
import { isPointInBonds } from '../../utilities/utils';
|
||||
import { commonLayerProps, getColorRange } from '../common';
|
||||
import sandboxedEval from '../../utils/sandbox';
|
||||
import { GetLayerType, createDeckGLComponent } from '../../factory';
|
||||
import TooltipRow from '../../TooltipRow';
|
||||
import { createTooltipContent } from '../../utilities/tooltipUtils';
|
||||
@@ -110,15 +109,9 @@ export const getLayer: GetLayerType<HeatmapLayer> = ({
|
||||
intensity = 1,
|
||||
radius_pixels: radiusPixels = 30,
|
||||
aggregation = 'SUM',
|
||||
js_data_mutator: jsFnMutator,
|
||||
linear_color_scheme: colorScheme,
|
||||
} = fd;
|
||||
let data = payload.data.features;
|
||||
|
||||
if (jsFnMutator) {
|
||||
const jsFnMutatorFunction = sandboxedEval(fd.js_data_mutator);
|
||||
data = jsFnMutatorFunction(data);
|
||||
}
|
||||
const data = payload.data.features;
|
||||
|
||||
const colorScale = getSequentialSchemeRegistry()
|
||||
?.get(colorScheme)
|
||||
@@ -173,15 +166,8 @@ export const getHighlightLayer: GetLayerType<HeatmapLayer> = ({
|
||||
intensity = 1,
|
||||
radius_pixels: radiusPixels = 30,
|
||||
aggregation = 'SUM',
|
||||
js_data_mutator: jsFnMutator,
|
||||
} = fd;
|
||||
let data = payload.data.features;
|
||||
|
||||
if (jsFnMutator) {
|
||||
// Applying user defined data mutator if defined
|
||||
const jsFnMutatorFunction = sandboxedEval(fd.js_data_mutator);
|
||||
data = jsFnMutatorFunction(data);
|
||||
}
|
||||
const data = payload.data.features;
|
||||
|
||||
const dataInside = data.filter((d: JsonObject) =>
|
||||
isPointInBonds(d.position, filterState?.value),
|
||||
|
||||
@@ -33,9 +33,6 @@ import {
|
||||
deckGLLinearColorSchemeSelect,
|
||||
filterNulls,
|
||||
jsColumns,
|
||||
jsDataMutator,
|
||||
jsOnclickHref,
|
||||
jsTooltip,
|
||||
mapboxStyle,
|
||||
maplibreStyle,
|
||||
mapProvider,
|
||||
@@ -150,12 +147,7 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
{
|
||||
label: t('Advanced'),
|
||||
controlSetRows: [
|
||||
[jsColumns],
|
||||
[jsDataMutator],
|
||||
[jsTooltip],
|
||||
[jsOnclickHref],
|
||||
],
|
||||
controlSetRows: [[jsColumns]],
|
||||
},
|
||||
],
|
||||
controlOverrides: {
|
||||
|
||||
@@ -30,7 +30,6 @@ import {
|
||||
getColorForBreakpoints,
|
||||
getColorRange,
|
||||
} from '../common';
|
||||
import sandboxedEval from '../../utils/sandbox';
|
||||
import { GetLayerType, createDeckGLComponent } from '../../factory';
|
||||
import {
|
||||
createTooltipContent,
|
||||
@@ -65,13 +64,7 @@ export const getLayer: GetLayerType<HexagonLayer> = function ({
|
||||
const fd = formData;
|
||||
const appliedScheme = fd.color_scheme;
|
||||
const colorScale = CategoricalColorNamespace.getScale(appliedScheme);
|
||||
let data = payload.data.features;
|
||||
|
||||
if (fd.js_data_mutator) {
|
||||
// Applying user defined data mutator if defined
|
||||
const jsFnMutator = sandboxedEval(fd.js_data_mutator);
|
||||
data = jsFnMutator(data);
|
||||
}
|
||||
const data = payload.data.features;
|
||||
|
||||
const colorSchemeType = fd.color_scheme_type;
|
||||
const colorRange = getColorRange({
|
||||
@@ -133,13 +126,7 @@ export const getHighlightLayer: GetLayerType<HexagonLayer> = function ({
|
||||
filterState,
|
||||
}) {
|
||||
const fd = formData;
|
||||
let data = payload.data.features;
|
||||
|
||||
if (fd.js_data_mutator) {
|
||||
// Applying user defined data mutator if defined
|
||||
const jsFnMutator = sandboxedEval(fd.js_data_mutator);
|
||||
data = jsFnMutator(data);
|
||||
}
|
||||
const data = payload.data.features;
|
||||
|
||||
const aggFunc = getAggFunc(fd.js_agg_function, p => p.weight);
|
||||
|
||||
|
||||
@@ -28,9 +28,6 @@ import {
|
||||
generateDeckGLColorSchemeControls,
|
||||
gridSize,
|
||||
jsColumns,
|
||||
jsDataMutator,
|
||||
jsOnclickHref,
|
||||
jsTooltip,
|
||||
mapboxStyle,
|
||||
maplibreStyle,
|
||||
mapProvider,
|
||||
@@ -103,12 +100,7 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
{
|
||||
label: t('Advanced'),
|
||||
controlSetRows: [
|
||||
[jsColumns],
|
||||
[jsDataMutator],
|
||||
[jsTooltip],
|
||||
[jsOnclickHref],
|
||||
],
|
||||
controlSetRows: [[jsColumns]],
|
||||
},
|
||||
],
|
||||
formDataOverrides: formData => ({
|
||||
|
||||
@@ -94,9 +94,6 @@ export const HexChartViz = ({
|
||||
extruded,
|
||||
js_agg_function: 'sum',
|
||||
js_columns: [],
|
||||
js_data_mutator: '',
|
||||
js_tooltip: '',
|
||||
js_onclick_href: '',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
import { PathLayer } from '@deck.gl/layers';
|
||||
import { JsonObject, QueryFormData } from '@superset-ui/core';
|
||||
import { commonLayerProps } from '../common';
|
||||
import sandboxedEval from '../../utils/sandbox';
|
||||
import { GetLayerType, createCategoricalDeckGLComponent } from '../../factory';
|
||||
import { Point } from '../../types';
|
||||
import {
|
||||
@@ -115,11 +114,6 @@ export const getLayer: GetLayerType<PathLayer> = function ({
|
||||
});
|
||||
}
|
||||
|
||||
if (fd.js_data_mutator) {
|
||||
const jsFnMutator = sandboxedEval(fd.js_data_mutator);
|
||||
data = jsFnMutator(data);
|
||||
}
|
||||
|
||||
return new PathLayer({
|
||||
id: `path-layer-${fd.slice_id}` as const,
|
||||
getColor: (d: any) => d.color || [0, 0, 0, 255],
|
||||
@@ -163,7 +157,7 @@ export const getHighlightLayer: GetLayerType<PathLayer> = function ({
|
||||
const maxWidth = Number(fd.max_width) || 20;
|
||||
const multiplier = Number(fd.line_width_multiplier) || 1;
|
||||
const fixedColor = HIGHLIGHT_COLOR_ARRAY;
|
||||
let data = payload.data.features.map((feature: JsonObject) => {
|
||||
const data = payload.data.features.map((feature: JsonObject) => {
|
||||
const baseWidth = Number.isFinite(feature.width) ? feature.width : 1;
|
||||
let width = baseWidth * multiplier;
|
||||
|
||||
@@ -177,11 +171,6 @@ export const getHighlightLayer: GetLayerType<PathLayer> = function ({
|
||||
};
|
||||
});
|
||||
|
||||
if (fd.js_data_mutator) {
|
||||
const jsFnMutator = sandboxedEval(fd.js_data_mutator);
|
||||
data = jsFnMutator(data);
|
||||
}
|
||||
|
||||
const filteredData = data.filter(
|
||||
(d: JsonObject) =>
|
||||
JSON.stringify(d.path).replaceAll(' ', '') === filterState?.value?.[0],
|
||||
|
||||
@@ -22,9 +22,6 @@ import {
|
||||
filterNulls,
|
||||
autozoom,
|
||||
jsColumns,
|
||||
jsDataMutator,
|
||||
jsTooltip,
|
||||
jsOnclickHref,
|
||||
viewport,
|
||||
lineType,
|
||||
reverseLongLat,
|
||||
@@ -155,12 +152,7 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
{
|
||||
label: t('Advanced'),
|
||||
controlSetRows: [
|
||||
[jsColumns],
|
||||
[jsDataMutator],
|
||||
[jsTooltip],
|
||||
[jsOnclickHref],
|
||||
],
|
||||
controlSetRows: [[jsColumns]],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -98,9 +98,6 @@ export const PathChartViz = ({
|
||||
reverse_long_lat: false,
|
||||
autozoom,
|
||||
js_columns: ['color'],
|
||||
js_data_mutator: '',
|
||||
js_tooltip: '',
|
||||
js_onclick_href: '',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -39,12 +39,6 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export interface DeckPathTransformPropsFormData extends DeckPathFormData {
|
||||
js_data_mutator?: string;
|
||||
js_tooltip?: string;
|
||||
js_onclick_href?: string;
|
||||
}
|
||||
|
||||
interface PathFeature {
|
||||
path: [number, number][];
|
||||
metric?: number;
|
||||
@@ -174,7 +168,7 @@ export default function transformProps(chartProps: ChartProps) {
|
||||
reverse_long_lat = false,
|
||||
js_columns,
|
||||
breakpoint_metric,
|
||||
} = formData as DeckPathTransformPropsFormData;
|
||||
} = formData as DeckPathFormData;
|
||||
|
||||
// Check so legacy values still work
|
||||
const fixedWidthValue =
|
||||
|
||||
@@ -47,7 +47,6 @@ import {
|
||||
} from '../../utils';
|
||||
|
||||
import { commonLayerProps, getColorForBreakpoints } from '../common';
|
||||
import sandboxedEval from '../../utils/sandbox';
|
||||
import getPointsFromPolygon from '../../utils/getPointsFromPolygon';
|
||||
import fitViewport, { Viewport } from '../../utils/fitViewport';
|
||||
import {
|
||||
@@ -121,13 +120,7 @@ export const getLayer: GetLayerType<PolygonLayer> = function ({
|
||||
const sc: { r: number; g: number; b: number; a: number } =
|
||||
fd.stroke_color_picker;
|
||||
const defaultBreakpointColor = fd.default_breakpoint_color;
|
||||
let data = [...payload.data.features];
|
||||
|
||||
if (fd.js_data_mutator) {
|
||||
// Applying user defined data mutator if defined
|
||||
const jsFnMutator = sandboxedEval(fd.js_data_mutator);
|
||||
data = jsFnMutator(data);
|
||||
}
|
||||
const data = [...payload.data.features];
|
||||
|
||||
const colorSchemeType = fd.color_scheme_type;
|
||||
|
||||
|
||||
@@ -27,9 +27,6 @@ import {
|
||||
filterNulls,
|
||||
autozoom,
|
||||
jsColumns,
|
||||
jsDataMutator,
|
||||
jsTooltip,
|
||||
jsOnclickHref,
|
||||
crossFilterColumn,
|
||||
legendFormat,
|
||||
legendPosition,
|
||||
@@ -203,13 +200,7 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
{
|
||||
label: t('Advanced'),
|
||||
controlSetRows: [
|
||||
[crossFilterColumn],
|
||||
[jsColumns],
|
||||
[jsDataMutator],
|
||||
[jsTooltip],
|
||||
[jsOnclickHref],
|
||||
],
|
||||
controlSetRows: [[crossFilterColumn], [jsColumns]],
|
||||
},
|
||||
],
|
||||
controlOverrides: {
|
||||
|
||||
@@ -138,9 +138,6 @@ export const PolygonChartViz = ({
|
||||
legend_position: 'tr',
|
||||
legend_format: null,
|
||||
js_columns: ['population', 'area'],
|
||||
js_data_mutator: '',
|
||||
js_tooltip: '',
|
||||
js_onclick_href: '',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -24,9 +24,6 @@ import {
|
||||
filterNulls,
|
||||
autozoom,
|
||||
jsColumns,
|
||||
jsDataMutator,
|
||||
jsTooltip,
|
||||
jsOnclickHref,
|
||||
legendFormat,
|
||||
legendPosition,
|
||||
viewport,
|
||||
@@ -146,12 +143,7 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
{
|
||||
label: t('Advanced'),
|
||||
controlSetRows: [
|
||||
[jsColumns],
|
||||
[jsDataMutator],
|
||||
[jsTooltip],
|
||||
[jsOnclickHref],
|
||||
],
|
||||
controlSetRows: [[jsColumns]],
|
||||
},
|
||||
],
|
||||
controlOverrides: {
|
||||
|
||||
@@ -107,9 +107,6 @@ export const ScatterChartViz = ({
|
||||
color_scheme: 'bnbColors',
|
||||
label_colors: {},
|
||||
js_columns: [],
|
||||
js_data_mutator: '',
|
||||
js_tooltip: '',
|
||||
js_onclick_href: '',
|
||||
granularity: null,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -31,7 +31,6 @@ import {
|
||||
ColorSchemeType,
|
||||
isPointInBonds,
|
||||
} from '../../utilities/utils';
|
||||
import sandboxedEval from '../../utils/sandbox';
|
||||
import { commonLayerProps, getColorRange } from '../common';
|
||||
import TooltipRow from '../../TooltipRow';
|
||||
import { GetLayerType, createDeckGLComponent } from '../../factory';
|
||||
@@ -117,12 +116,7 @@ export const getLayer: GetLayerType<ScreenGridLayer> = function ({
|
||||
const fd = formData;
|
||||
const appliedScheme = fd.color_scheme;
|
||||
const colorScale = CategoricalColorNamespace.getScale(appliedScheme);
|
||||
let data = payload.data.features;
|
||||
|
||||
if (fd.js_data_mutator) {
|
||||
const jsFnMutator = sandboxedEval(fd.js_data_mutator);
|
||||
data = jsFnMutator(data);
|
||||
}
|
||||
const data = payload.data.features;
|
||||
|
||||
const colorSchemeType = fd.color_scheme_type as ColorSchemeType & 'default';
|
||||
const colorRange = getColorRange({
|
||||
@@ -225,14 +219,8 @@ const getHighlightLayer: GetLayerType<ScreenGridLayer> = function ({
|
||||
filterState,
|
||||
payload,
|
||||
}) {
|
||||
const fd = formData;
|
||||
let data = payload.data.features;
|
||||
const data = payload.data.features;
|
||||
|
||||
if (fd.js_data_mutator) {
|
||||
// Applying user defined data mutator if defined
|
||||
const jsFnMutator = sandboxedEval(fd.js_data_mutator);
|
||||
data = jsFnMutator(data);
|
||||
}
|
||||
const dataInside = data.filter((d: JsonObject) =>
|
||||
isPointInBonds(d.position, filterState?.value),
|
||||
);
|
||||
|
||||
@@ -27,9 +27,6 @@ import {
|
||||
filterNulls,
|
||||
autozoom,
|
||||
jsColumns,
|
||||
jsDataMutator,
|
||||
jsTooltip,
|
||||
jsOnclickHref,
|
||||
gridSize,
|
||||
viewport,
|
||||
spatial,
|
||||
@@ -96,12 +93,7 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
{
|
||||
label: t('Advanced'),
|
||||
controlSetRows: [
|
||||
[jsColumns],
|
||||
[jsDataMutator],
|
||||
[jsTooltip],
|
||||
[jsOnclickHref],
|
||||
],
|
||||
controlSetRows: [[jsColumns]],
|
||||
},
|
||||
],
|
||||
controlOverrides: {
|
||||
|
||||
@@ -85,9 +85,6 @@ export const ScreengridChartViz = ({
|
||||
grid_size: gridSize,
|
||||
color_picker: { a: 1, b: 0, g: 255, r: 14 },
|
||||
js_columns: [],
|
||||
js_data_mutator: '',
|
||||
js_tooltip: '',
|
||||
js_onclick_href: '',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -103,11 +103,8 @@ describe('commonLayerProps', () => {
|
||||
) as any;
|
||||
const mockOnSelect = jest.fn();
|
||||
|
||||
test('returns correct props when js_tooltip is provided', () => {
|
||||
const formData = {
|
||||
...partialformData,
|
||||
js_tooltip: 'tooltip => tooltip.content',
|
||||
} as QueryFormData;
|
||||
test('returns correct props with a tooltip content generator', () => {
|
||||
const formData = { ...partialformData } as QueryFormData;
|
||||
const props = commonLayerProps({
|
||||
formData,
|
||||
setTooltip: mockSetTooltip,
|
||||
@@ -118,7 +115,7 @@ describe('commonLayerProps', () => {
|
||||
});
|
||||
|
||||
test('calls onHover and sets tooltip', () => {
|
||||
const formData = { ...partialformData, js_tooltip: null } as QueryFormData;
|
||||
const formData = { ...partialformData } as QueryFormData;
|
||||
const props = commonLayerProps({
|
||||
formData,
|
||||
setTooltip: mockSetTooltip,
|
||||
|
||||
@@ -40,7 +40,6 @@ import {
|
||||
import { Layer, PickingInfo, Color } from '@deck.gl/core';
|
||||
import { ScaleLinear } from 'd3-scale';
|
||||
import { ColorBreakpointType } from '../types';
|
||||
import sandboxedEval from '../utils/sandbox';
|
||||
import { TooltipProps } from '../components/Tooltip';
|
||||
import { getCrossFilterDataMask } from '../utils/crossFiltersDataMask';
|
||||
import { COLOR_SCHEME_TYPES, ColorSchemeType } from '../utilities/utils';
|
||||
@@ -68,11 +67,7 @@ export function commonLayerProps({
|
||||
}) {
|
||||
const fd = formData;
|
||||
let onHover;
|
||||
let tooltipContentGenerator = setTooltipContent;
|
||||
if (fd.js_tooltip) {
|
||||
tooltipContentGenerator = sandboxedEval(fd.js_tooltip);
|
||||
}
|
||||
if (tooltipContentGenerator) {
|
||||
if (setTooltipContent) {
|
||||
let currentTooltipContent: ReactNode = null;
|
||||
|
||||
const isCustomTooltip = (content: ReactNode): boolean =>
|
||||
@@ -81,7 +76,7 @@ export function commonLayerProps({
|
||||
|
||||
onHover = (o: JsonObject) => {
|
||||
if (o.picked) {
|
||||
currentTooltipContent = tooltipContentGenerator(o);
|
||||
currentTooltipContent = setTooltipContent(o);
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -102,13 +97,7 @@ export function commonLayerProps({
|
||||
}
|
||||
|
||||
let onClick;
|
||||
if (fd.js_onclick_href) {
|
||||
onClick = (o: any) => {
|
||||
const href = sandboxedEval(fd.js_onclick_href)(o);
|
||||
window.open(href);
|
||||
return true;
|
||||
};
|
||||
} else if (fd.table_filter && onSelect !== undefined) {
|
||||
if (fd.table_filter && onSelect !== undefined) {
|
||||
onClick = (o: any) => {
|
||||
onSelect(o.object[fd.line_column]);
|
||||
return true;
|
||||
|
||||
@@ -53,7 +53,6 @@ export interface SpatialFormData extends QueryFormData {
|
||||
spatial: SpatialConfiguration;
|
||||
size?: string;
|
||||
grid_size?: number;
|
||||
js_data_mutator?: string;
|
||||
js_agg_function?: string;
|
||||
js_columns?: string[];
|
||||
color_scheme?: string;
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
|
||||
import { t } from '@apache-superset/core/translation';
|
||||
import {
|
||||
FeatureFlag,
|
||||
isFeatureEnabled,
|
||||
validateNonEmpty,
|
||||
getCategoricalSchemeRegistry,
|
||||
getSequentialSchemeRegistry,
|
||||
@@ -151,49 +149,6 @@ const DEFAULT_VIEWPORT = {
|
||||
pitch: 0,
|
||||
};
|
||||
|
||||
const sandboxUrl =
|
||||
'https://github.com/apache/superset/' +
|
||||
'blob/master/superset-frontend/plugins/preset-chart-deckgl/src/utils/sandbox.ts';
|
||||
const jsFunctionInfo = (
|
||||
<div>
|
||||
{t(
|
||||
'For more information about objects are in context in the scope of this function, refer to the',
|
||||
)}
|
||||
<a href={sandboxUrl}>{t(" source code of Superset's sandboxed parser")}.</a>
|
||||
.
|
||||
</div>
|
||||
);
|
||||
|
||||
export function jsFunctionControl(
|
||||
label: string,
|
||||
description: string,
|
||||
extraDescr = null,
|
||||
height = 100,
|
||||
defaultText = '',
|
||||
) {
|
||||
return {
|
||||
type: 'TextAreaControl',
|
||||
language: 'javascript',
|
||||
label,
|
||||
description,
|
||||
height,
|
||||
default: defaultText,
|
||||
aboveEditorSection: (
|
||||
<div>
|
||||
<p>{description}</p>
|
||||
<p>{jsFunctionInfo}</p>
|
||||
{extraDescr}
|
||||
</div>
|
||||
),
|
||||
warning: !isFeatureEnabled(FeatureFlag.EnableJavascriptControls)
|
||||
? t(
|
||||
'This functionality is disabled in your environment for security reasons.',
|
||||
)
|
||||
: null,
|
||||
readOnly: !isFeatureEnabled(FeatureFlag.EnableJavascriptControls),
|
||||
};
|
||||
}
|
||||
|
||||
export const filterNulls = {
|
||||
name: 'filter_nulls',
|
||||
config: {
|
||||
@@ -255,36 +210,6 @@ export const crossFilterColumn: CustomControlItem = {
|
||||
},
|
||||
};
|
||||
|
||||
export const jsDataMutator = {
|
||||
name: 'js_data_mutator',
|
||||
config: jsFunctionControl(
|
||||
t('JavaScript data interceptor'),
|
||||
t(
|
||||
'Define a javascript function that receives the data array used in the visualization ' +
|
||||
'and is expected to return a modified version of that array. This can be used ' +
|
||||
'to alter properties of the data, filter, or enrich the array.',
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
export const jsTooltip = {
|
||||
name: 'js_tooltip',
|
||||
config: jsFunctionControl(
|
||||
t('JavaScript tooltip generator'),
|
||||
t(
|
||||
'Define a function that receives the input and outputs the content for a tooltip',
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
export const jsOnclickHref = {
|
||||
name: 'js_onclick_href',
|
||||
config: jsFunctionControl(
|
||||
t('JavaScript onClick href'),
|
||||
t('Define a function that returns a URL to navigate to when user clicks'),
|
||||
),
|
||||
};
|
||||
|
||||
export const legendFormat = {
|
||||
name: 'legend_format',
|
||||
config: {
|
||||
|
||||
@@ -1,65 +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.
|
||||
*/
|
||||
// A safe alternative to JS's eval
|
||||
import vm, { Context, RunningScriptOptions } from 'vm';
|
||||
import _ from 'underscore';
|
||||
/* eslint-disable-next-line no-restricted-syntax */
|
||||
import * as d3array from 'd3-array';
|
||||
/* eslint-disable-next-line no-restricted-syntax */
|
||||
import * as colors from './colors';
|
||||
|
||||
// Objects exposed here should be treated like a public API
|
||||
// if `underscore` had backwards incompatible changes in a future release, we'd
|
||||
// have to be careful about bumping the library as those changes could break user charts
|
||||
const GLOBAL_CONTEXT = {
|
||||
console,
|
||||
_,
|
||||
colors,
|
||||
d3array,
|
||||
};
|
||||
|
||||
type GlobalContext = {
|
||||
console: Console;
|
||||
_: _.UnderscoreStatic;
|
||||
colors: typeof colors;
|
||||
d3array: typeof d3array;
|
||||
};
|
||||
|
||||
// Copied/modified from https://github.com/hacksparrow/safe-eval/blob/master/index.js
|
||||
export default function sandboxedEval(
|
||||
code: string,
|
||||
context?: Context,
|
||||
opts?: RunningScriptOptions | string,
|
||||
) {
|
||||
const sandbox: Context = {};
|
||||
const resultKey = `SAFE_EVAL_${Math.floor(Math.random() * 1000000)}`;
|
||||
sandbox[resultKey] = {};
|
||||
const codeToEval = `${resultKey}=${code}`;
|
||||
const sandboxContext: GlobalContext = { ...GLOBAL_CONTEXT, ...context };
|
||||
Object.keys(sandboxContext).forEach(key => {
|
||||
sandbox[key] = sandboxContext[key as keyof GlobalContext];
|
||||
});
|
||||
try {
|
||||
vm.runInNewContext(codeToEval, sandbox, opts);
|
||||
|
||||
return sandbox[resultKey];
|
||||
} catch (error) {
|
||||
return () => error;
|
||||
}
|
||||
}
|
||||
@@ -472,7 +472,7 @@ const config = {
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx', '.yml'],
|
||||
fallback: {
|
||||
fs: false,
|
||||
vm: require.resolve('vm-browserify'),
|
||||
vm: false,
|
||||
path: false,
|
||||
stream: require.resolve('stream-browserify'),
|
||||
...(isDevMode ? { buffer: require.resolve('buffer/') } : {}), // Fix legacy-plugin-chart-paired-t-test broken Story
|
||||
|
||||
@@ -936,9 +936,6 @@ DEFAULT_FEATURE_FLAGS: dict[str, bool] = {
|
||||
# Enable drill-to-detail functionality in charts
|
||||
# @lifecycle: deprecated
|
||||
"DRILL_TO_DETAIL": True,
|
||||
# Allow JavaScript in chart controls. WARNING: XSS security vulnerability!
|
||||
# @lifecycle: deprecated
|
||||
"ENABLE_JAVASCRIPT_CONTROLS": False,
|
||||
}
|
||||
|
||||
# ------------------------------
|
||||
|
||||
@@ -28,10 +28,6 @@ params:
|
||||
datasource: 12__table
|
||||
js_columns:
|
||||
- color
|
||||
js_data_mutator: "data => data.map(d => ({\n ...d,\n color: colors.hexToRGB(d.extraProps.color)\n\
|
||||
}));"
|
||||
js_onclick_href: ''
|
||||
js_tooltip: ''
|
||||
line_column: path_json
|
||||
line_type: json
|
||||
line_width: 150
|
||||
|
||||
@@ -30,9 +30,6 @@ params:
|
||||
filled: true
|
||||
granularity_sqla: null
|
||||
js_columns: []
|
||||
js_data_mutator: ''
|
||||
js_onclick_href: ''
|
||||
js_tooltip: ''
|
||||
legend_format: .1s
|
||||
legend_position: tr
|
||||
line_column: contour
|
||||
|
||||
@@ -80,7 +80,6 @@ FRONTEND_CONF_KEYS = (
|
||||
"SUPERSET_DASHBOARD_PERIODICAL_REFRESH_LIMIT",
|
||||
"SUPERSET_DASHBOARD_PERIODICAL_REFRESH_WARNING_MESSAGE",
|
||||
"SUPERSET_DASHBOARD_MANUAL_REFRESH_STAGGER_MS",
|
||||
"ENABLE_JAVASCRIPT_CONTROLS",
|
||||
"DEFAULT_SQLLAB_LIMIT",
|
||||
"DEFAULT_VIZ_TYPE",
|
||||
"SQL_MAX_ROW",
|
||||
|
||||
@@ -46,7 +46,7 @@ from superset.exceptions import (
|
||||
SupersetException,
|
||||
SupersetSecurityException,
|
||||
)
|
||||
from superset.extensions import cache_manager, feature_flag_manager, security_manager
|
||||
from superset.extensions import cache_manager, security_manager
|
||||
from superset.legacy import update_time_range
|
||||
from superset.models.core import Database
|
||||
from superset.models.dashboard import Dashboard
|
||||
@@ -65,23 +65,6 @@ from superset.viz import BaseViz
|
||||
logger = logging.getLogger(__name__)
|
||||
stats_logger = app.config["STATS_LOGGER"]
|
||||
|
||||
# Form-data keys whose values are executed as JavaScript at render time by the
|
||||
# deck.gl charts (via the frontend ``sandboxedEval`` helper). These are stripped
|
||||
# from incoming form_data unless the ``ENABLE_JAVASCRIPT_CONTROLS`` feature flag
|
||||
# is enabled. Keep this list in sync with every ``sandboxedEval(fd.<key>)`` call
|
||||
# site in the deck.gl plugins.
|
||||
JS_CONTROL_FORM_DATA_KEYS: list[str] = [
|
||||
"js_tooltip",
|
||||
"js_onclick_href",
|
||||
"js_data_mutator",
|
||||
"label_javascript_config_generator",
|
||||
"icon_javascript_config_generator",
|
||||
]
|
||||
|
||||
REJECTED_FORM_DATA_KEYS: list[str] = []
|
||||
if not feature_flag_manager.is_feature_enabled("ENABLE_JAVASCRIPT_CONTROLS"):
|
||||
REJECTED_FORM_DATA_KEYS = list(JS_CONTROL_FORM_DATA_KEYS)
|
||||
|
||||
|
||||
def redirect_to_login(next_target: str | None = None) -> FlaskResponse:
|
||||
"""Return a redirect response to the login view, preserving target URL.
|
||||
@@ -396,8 +379,6 @@ def get_form_data(
|
||||
json_data = form_data["queries"][0] if "queries" in form_data else {}
|
||||
form_data.update(json_data)
|
||||
|
||||
form_data = {k: v for k, v in form_data.items() if k not in REJECTED_FORM_DATA_KEYS}
|
||||
|
||||
# When a slice_id is present, load from DB and override
|
||||
# the form_data from the DB with the other form_data provided
|
||||
slice_id = form_data.get("slice_id") or slice_id
|
||||
|
||||
@@ -235,9 +235,6 @@ class TestImportChartsCommand(SupersetTestCase):
|
||||
"color_picker": {"a": 1, "b": 135, "g": 122, "r": 0},
|
||||
"datasource": dataset.uid if dataset else None,
|
||||
"js_columns": ["color"],
|
||||
"js_data_mutator": "data => data.map(d => ({\\n ...d,\\n color: colors.hexToRGB(d.extraProps.color)\\n}));", # noqa: E501
|
||||
"js_onclick_href": "",
|
||||
"js_tooltip": "",
|
||||
"line_column": "path_json",
|
||||
"line_type": "json",
|
||||
"line_width": 150,
|
||||
|
||||
@@ -11,9 +11,6 @@
|
||||
"js_columns": [
|
||||
"color"
|
||||
],
|
||||
"js_datapoint_mutator": "d => {\n return {\n ...d,\n color: colors.hexToRGB(d.extraProps.color),\n }\n}",
|
||||
"js_onclick_href": "",
|
||||
"js_tooltip": "",
|
||||
"mapbox_style": "mapbox://styles/mapbox/light-v9",
|
||||
"reverse_long_lat": false,
|
||||
"row_limit": 5000,
|
||||
|
||||
@@ -11,9 +11,6 @@
|
||||
"js_columns": [
|
||||
"color"
|
||||
],
|
||||
"js_datapoint_mutator": "d => {\n return {\n ...d,\n color: colors.hexToRGB(d.extraProps.color),\n }\n}",
|
||||
"js_onclick_href": "",
|
||||
"js_tooltip": "",
|
||||
"line_column": "path_json",
|
||||
"line_type": "json",
|
||||
"line_width": 150,
|
||||
|
||||
@@ -588,9 +588,6 @@ chart_config: dict[str, Any] = {
|
||||
"color_picker": {"a": 1, "b": 135, "g": 122, "r": 0},
|
||||
"datasource": "12__table",
|
||||
"js_columns": ["color"],
|
||||
"js_data_mutator": r"data => data.map(d => ({\n ...d,\n color: colors.hexToRGB(d.extraProps.color)\n}));", # noqa: E501
|
||||
"js_onclick_href": "",
|
||||
"js_tooltip": "",
|
||||
"line_column": "path_json",
|
||||
"line_type": "json",
|
||||
"line_width": 150,
|
||||
|
||||
@@ -1,48 +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.
|
||||
"""Tests for superset.views.utils module"""
|
||||
|
||||
from superset.views.utils import (
|
||||
get_form_data,
|
||||
JS_CONTROL_FORM_DATA_KEYS,
|
||||
REJECTED_FORM_DATA_KEYS,
|
||||
)
|
||||
|
||||
|
||||
def test_rejected_form_data_keys_cover_all_js_control_keys() -> None:
|
||||
"""
|
||||
With ENABLE_JAVASCRIPT_CONTROLS disabled (the default), every form_data key
|
||||
that is later executed as JavaScript by the deck.gl charts must be rejected.
|
||||
|
||||
This guards against a new ``sandboxedEval(fd.<key>)`` call site being added
|
||||
without also adding its key to the strip list.
|
||||
"""
|
||||
# The test app keeps ENABLE_JAVASCRIPT_CONTROLS at its default (off).
|
||||
assert set(JS_CONTROL_FORM_DATA_KEYS) <= set(REJECTED_FORM_DATA_KEYS)
|
||||
|
||||
|
||||
def test_get_form_data_strips_js_control_keys() -> None:
|
||||
"""get_form_data drops all JS-executed keys when the flag is disabled."""
|
||||
initial_form_data = dict.fromkeys(JS_CONTROL_FORM_DATA_KEYS, "data => data")
|
||||
initial_form_data["viz_type"] = "deck_geojson"
|
||||
|
||||
form_data, _ = get_form_data(initial_form_data=initial_form_data)
|
||||
|
||||
for key in JS_CONTROL_FORM_DATA_KEYS:
|
||||
assert key not in form_data
|
||||
# Non-JS keys are preserved.
|
||||
assert form_data["viz_type"] == "deck_geojson"
|
||||
Reference in New Issue
Block a user