From be4bc3dec57cfef3c5b32359461c3b47e8b91ffc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:19:54 -0700 Subject: [PATCH 01/14] chore(deps-dev): bump ts-jest from 29.2.5 to 29.3.1 in /superset-websocket (#32924) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- superset-websocket/package-lock.json | 53 ++++++++++++++++++++-------- superset-websocket/package.json | 2 +- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/superset-websocket/package-lock.json b/superset-websocket/package-lock.json index 2c42942d8d5..3614e6eb7aa 100644 --- a/superset-websocket/package-lock.json +++ b/superset-websocket/package-lock.json @@ -37,7 +37,7 @@ "globals": "^16.0.0", "jest": "^29.7.0", "prettier": "^3.4.2", - "ts-jest": "^29.2.5", + "ts-jest": "^29.3.1", "ts-node": "^10.9.2", "tscw-config": "^1.1.2", "typescript": "^5.6.2", @@ -6052,9 +6052,10 @@ } }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -6336,10 +6337,11 @@ } }, "node_modules/ts-jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", - "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.1.tgz", + "integrity": "sha512-FT2PIRtZABwl6+ZCry8IY7JZ3xMuppsEV9qFVHOVe8jDzggwUZ9TsM4chyJxL9yi6LvkqcZYU3LmapEE454zBQ==", "dev": true, + "license": "MIT", "dependencies": { "bs-logger": "^0.2.6", "ejs": "^3.1.10", @@ -6348,7 +6350,8 @@ "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.6.3", + "semver": "^7.7.1", + "type-fest": "^4.38.0", "yargs-parser": "^21.1.1" }, "bin": { @@ -6486,6 +6489,19 @@ "node": ">=4" } }, + "node_modules/type-fest": { + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.38.0.tgz", + "integrity": "sha512-2dBz5D5ycHIoliLYLi0Q2V7KRaDlH0uWIvmk7TYlAg5slqwiPv1ezJdZm1QEM0xgk29oYWMCbIG7E6gHpvChlg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", @@ -11361,9 +11377,9 @@ "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==" }, "semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==" + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==" }, "shebang-command": { "version": "2.0.0", @@ -11575,9 +11591,9 @@ "requires": {} }, "ts-jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", - "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.1.tgz", + "integrity": "sha512-FT2PIRtZABwl6+ZCry8IY7JZ3xMuppsEV9qFVHOVe8jDzggwUZ9TsM4chyJxL9yi6LvkqcZYU3LmapEE454zBQ==", "dev": true, "requires": { "bs-logger": "^0.2.6", @@ -11587,7 +11603,8 @@ "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.6.3", + "semver": "^7.7.1", + "type-fest": "^4.38.0", "yargs-parser": "^21.1.1" } }, @@ -11652,6 +11669,12 @@ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, + "type-fest": { + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.38.0.tgz", + "integrity": "sha512-2dBz5D5ycHIoliLYLi0Q2V7KRaDlH0uWIvmk7TYlAg5slqwiPv1ezJdZm1QEM0xgk29oYWMCbIG7E6gHpvChlg==", + "dev": true + }, "typescript": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", diff --git a/superset-websocket/package.json b/superset-websocket/package.json index 152b8058b2a..6d517280c3b 100644 --- a/superset-websocket/package.json +++ b/superset-websocket/package.json @@ -45,7 +45,7 @@ "globals": "^16.0.0", "jest": "^29.7.0", "prettier": "^3.4.2", - "ts-jest": "^29.2.5", + "ts-jest": "^29.3.1", "ts-node": "^10.9.2", "tscw-config": "^1.1.2", "typescript": "^5.6.2", From a8a6254ea2ae935ddabe6cfe70225d4e6139eafb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:21:01 -0700 Subject: [PATCH 02/14] chore(deps-dev): bump @typescript-eslint/parser from 8.19.0 to 8.29.0 in /superset-websocket (#32925) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- superset-websocket/package-lock.json | 275 +++++++++++++++++++++++++-- superset-websocket/package.json | 2 +- 2 files changed, 260 insertions(+), 17 deletions(-) diff --git a/superset-websocket/package-lock.json b/superset-websocket/package-lock.json index 3614e6eb7aa..52740791336 100644 --- a/superset-websocket/package-lock.json +++ b/superset-websocket/package-lock.json @@ -30,7 +30,7 @@ "@types/uuid": "^10.0.0", "@types/ws": "^8.5.12", "@typescript-eslint/eslint-plugin": "^8.26.0", - "@typescript-eslint/parser": "^8.6.0", + "@typescript-eslint/parser": "^8.29.0", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-lodash": "^8.0.0", @@ -2047,15 +2047,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", - "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.0.tgz", + "integrity": "sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.29.0", + "@typescript-eslint/types": "8.29.0", + "@typescript-eslint/typescript-estree": "8.29.0", + "@typescript-eslint/visitor-keys": "8.29.0", "debug": "^4.3.4" }, "engines": { @@ -2067,7 +2068,136 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.0.tgz", + "integrity": "sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.29.0", + "@typescript-eslint/visitor-keys": "8.29.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.0.tgz", + "integrity": "sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.0.tgz", + "integrity": "sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.29.0", + "@typescript-eslint/visitor-keys": "8.29.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.0.tgz", + "integrity": "sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.29.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" } }, "node_modules/@typescript-eslint/scope-manager": { @@ -6567,6 +6697,31 @@ "typescript": ">=4.8.4 <5.8.0" } }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", + "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.19.0", + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/typescript-estree": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.0.tgz", @@ -8429,16 +8584,91 @@ } }, "@typescript-eslint/parser": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", - "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.0.tgz", + "integrity": "sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.29.0", + "@typescript-eslint/types": "8.29.0", + "@typescript-eslint/typescript-estree": "8.29.0", + "@typescript-eslint/visitor-keys": "8.29.0", "debug": "^4.3.4" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.0.tgz", + "integrity": "sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.29.0", + "@typescript-eslint/visitor-keys": "8.29.0" + } + }, + "@typescript-eslint/types": { + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.0.tgz", + "integrity": "sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.0.tgz", + "integrity": "sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.29.0", + "@typescript-eslint/visitor-keys": "8.29.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.0.tgz", + "integrity": "sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.29.0", + "eslint-visitor-keys": "^4.2.0" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "requires": {} + } } }, "@typescript-eslint/scope-manager": { @@ -11709,6 +11939,19 @@ "ts-api-utils": "^1.3.0" } }, + "@typescript-eslint/parser": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", + "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "8.19.0", + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/typescript-estree": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0", + "debug": "^4.3.4" + } + }, "@typescript-eslint/type-utils": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.0.tgz", diff --git a/superset-websocket/package.json b/superset-websocket/package.json index 6d517280c3b..651172e97e5 100644 --- a/superset-websocket/package.json +++ b/superset-websocket/package.json @@ -38,7 +38,7 @@ "@types/uuid": "^10.0.0", "@types/ws": "^8.5.12", "@typescript-eslint/eslint-plugin": "^8.26.0", - "@typescript-eslint/parser": "^8.6.0", + "@typescript-eslint/parser": "^8.29.0", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-lodash": "^8.0.0", From 8b0bda3bad9dd2a26b414f028293fa9f29933502 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:22:12 -0700 Subject: [PATCH 03/14] chore(deps): update @types/react-redux requirement from ^7.1.10 to ^7.1.34 in /superset-frontend/plugins/plugin-chart-echarts (#32927) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- superset-frontend/plugins/plugin-chart-echarts/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset-frontend/plugins/plugin-chart-echarts/package.json b/superset-frontend/plugins/plugin-chart-echarts/package.json index 01763f3078a..388fc8f7222 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/package.json +++ b/superset-frontend/plugins/plugin-chart-echarts/package.json @@ -24,7 +24,7 @@ "lib" ], "dependencies": { - "@types/react-redux": "^7.1.10", + "@types/react-redux": "^7.1.34", "d3-array": "^1.2.0", "dayjs": "^1.11.13", "lodash": "^4.17.21" From 26743dfcee0fa0d5b1f629c24f917e7d7707c40a Mon Sep 17 00:00:00 2001 From: notHuman9504 <127070922+notHuman9504@users.noreply.github.com> Date: Tue, 1 Apr 2025 22:53:48 +0530 Subject: [PATCH 04/14] fix: Clicking in the body of a Markdown component does not put it into edit mode (#32384) --- .../dashboard/components/menu/WithPopoverMenu.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx b/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx index 0845e2e964b..7081445c9d0 100644 --- a/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx +++ b/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx @@ -115,10 +115,12 @@ export default class WithPopoverMenu extends PureComponent< onChangeFocus: null, menuItems: [], isFocused: false, - shouldFocus: (event: any, container: ShouldFocusContainer) => - container?.contains(event.target) || - event.target.id === 'menu-item' || - event.target.parentNode?.id === 'menu-item', + shouldFocus: (event: any, container: ShouldFocusContainer) => { + if (container?.contains(event.target)) return true; + if (event.target.id === 'menu-item') return true; + if (event.target.parentNode?.id === 'menu-item') return true; + return false; + }, style: null, }; @@ -156,6 +158,9 @@ export default class WithPopoverMenu extends PureComponent< if (!this.props.editMode) { return; } + + event.stopPropagation(); + const { onChangeFocus, shouldFocus: shouldFocusFunc, From e1c1de1b94f03caec5904511774aa781ad5776ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:24:13 -0700 Subject: [PATCH 05/14] chore(deps-dev): bump css-minimizer-webpack-plugin from 7.0.0 to 7.0.2 in /superset-frontend (#32937) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- superset-frontend/package-lock.json | 12 ++++++------ superset-frontend/package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 89f7f031637..92cb465c06e 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -234,7 +234,7 @@ "copy-webpack-plugin": "^12.0.2", "cross-env": "^7.0.3", "css-loader": "^7.1.2", - "css-minimizer-webpack-plugin": "^7.0.0", + "css-minimizer-webpack-plugin": "^7.0.2", "enzyme": "^3.11.0", "enzyme-matchers": "^7.1.2", "eslint": "^8.56.0", @@ -18988,16 +18988,16 @@ } }, "node_modules/css-minimizer-webpack-plugin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-7.0.0.tgz", - "integrity": "sha512-niy66jxsQHqO+EYbhPuIhqRQ1mNcNVUHrMnkzzir9kFOERJUaQDDRhh7dKDz33kBpkWMF9M8Vx0QlDbc5AHOsw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-7.0.2.tgz", + "integrity": "sha512-nBRWZtI77PBZQgcXMNqiIXVshiQOVLGSf2qX/WZfG8IQfMbeHUMXaBWQmiiSTmPJUflQxHjZjzAmuyO7tpL2Jg==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", - "cssnano": "^7.0.1", + "cssnano": "^7.0.4", "jest-worker": "^29.7.0", - "postcss": "^8.4.38", + "postcss": "^8.4.40", "schema-utils": "^4.2.0", "serialize-javascript": "^6.0.2" }, diff --git a/superset-frontend/package.json b/superset-frontend/package.json index 5a1ce072d9b..e879c53bb4e 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -301,7 +301,7 @@ "copy-webpack-plugin": "^12.0.2", "cross-env": "^7.0.3", "css-loader": "^7.1.2", - "css-minimizer-webpack-plugin": "^7.0.0", + "css-minimizer-webpack-plugin": "^7.0.2", "enzyme": "^3.11.0", "enzyme-matchers": "^7.1.2", "eslint": "^8.56.0", From 2bc33beec428fe2426522f5b50e2058f95b60a61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:24:51 -0700 Subject: [PATCH 06/14] chore(deps-dev): bump @babel/compat-data from 7.26.5 to 7.26.8 in /superset-frontend (#32939) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- superset-frontend/package-lock.json | 8 ++++---- superset-frontend/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 92cb465c06e..ae3db3472f9 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -152,7 +152,7 @@ "devDependencies": { "@applitools/eyes-storybook": "^3.50.9", "@babel/cli": "^7.22.6", - "@babel/compat-data": "^7.22.6", + "@babel/compat-data": "^7.26.8", "@babel/core": "^7.26.0", "@babel/eslint-parser": "^7.25.9", "@babel/node": "^7.22.6", @@ -1160,9 +1160,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", - "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "dev": true, "license": "MIT", "engines": { diff --git a/superset-frontend/package.json b/superset-frontend/package.json index e879c53bb4e..e46b828fc15 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -219,7 +219,7 @@ "devDependencies": { "@applitools/eyes-storybook": "^3.50.9", "@babel/cli": "^7.22.6", - "@babel/compat-data": "^7.22.6", + "@babel/compat-data": "^7.26.8", "@babel/core": "^7.26.0", "@babel/eslint-parser": "^7.25.9", "@babel/node": "^7.22.6", From 00933a27af5e9cbea4d27691dff3933d43ecd3a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:26:55 -0700 Subject: [PATCH 07/14] chore(deps): bump swagger-ui-react from 5.20.0 to 5.20.2 in /docs (#32950) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package.json | 2 +- docs/yarn.lock | 380 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 295 insertions(+), 87 deletions(-) diff --git a/docs/package.json b/docs/package.json index 7bb75fb34a0..1141cec4984 100644 --- a/docs/package.json +++ b/docs/package.json @@ -34,7 +34,7 @@ "react-dom": "^18.3.1", "react-github-btn": "^1.4.0", "react-svg-pan-zoom": "^3.13.1", - "swagger-ui-react": "^5.20.0" + "swagger-ui-react": "^5.20.2" }, "devDependencies": { "@docusaurus/module-type-aliases": "^3.7.0", diff --git a/docs/yarn.lock b/docs/yarn.lock index 2d84567ad48..9c40f4a5135 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -1366,10 +1366,10 @@ resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime-corejs3@^7.20.7", "@babel/runtime-corejs3@^7.22.15", "@babel/runtime-corejs3@^7.22.6", "@babel/runtime-corejs3@^7.26.7": - version "7.26.10" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.26.10.tgz#5a3185ca2813f8de8ae68622572086edf5cf51f2" - integrity sha512-uITFQYO68pMEYR46AHgQoyBg7KPPJDAbGn4jUTIRgCFJIp88MIBUianVOplhZDEec07bp9zIyr4Kp0FCyQzmWg== +"@babel/runtime-corejs3@^7.20.7", "@babel/runtime-corejs3@^7.22.15", "@babel/runtime-corejs3@^7.22.6", "@babel/runtime-corejs3@^7.26.10": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.27.0.tgz#c766df350ec7a2caf3ed64e3659b100954589413" + integrity sha512-UWjX6t+v+0ckwZ50Y5ShZLnlk95pP5MyW/pon9tiYzl3+18pkTHTFNTKr7rQbfRXPkowt2QAn30o1b6oswszew== dependencies: core-js-pure "^3.30.2" regenerator-runtime "^0.14.0" @@ -2491,7 +2491,34 @@ ramda-adjunct "^5.0.0" unraw "^3.0.0" -"@swagger-api/apidom-core@>=1.0.0-beta.12 <1.0.0-rc.0", "@swagger-api/apidom-core@^1.0.0-beta.12", "@swagger-api/apidom-core@^1.0.0-beta.3": +"@swagger-api/apidom-ast@^1.0.0-beta.30": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ast/-/apidom-ast-1.0.0-beta.30.tgz#88c25259a4a0b8fd9fa2106634089a927fd1d2d9" + integrity sha512-5Wj3zdt0dxS9ERVk4qSuqDIsMQ8dP2vop8b494OpJ/O2W261yCV39Z+vN+PqeJ2NiKDRMlJ+QoQ1uVfKwEo8Kg== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-error" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + unraw "^3.0.0" + +"@swagger-api/apidom-core@>=1.0.0-beta.13 <1.0.0-rc.0", "@swagger-api/apidom-core@^1.0.0-beta.30": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-core/-/apidom-core-1.0.0-beta.30.tgz#e0f52d343c109304878e5bf119a9fc297f5a09dc" + integrity sha512-pDnUhXIKKUvmeezQfwKLL05rkOH1L7ueiy5ja5ob9y2w4r+HXDID7qHtDGeRxKZoIt4E3Sd1K37OjcE9fNcknQ== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-ast" "^1.0.0-beta.30" + "@swagger-api/apidom-error" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + minim "~0.23.8" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + short-unique-id "^5.0.2" + ts-mixer "^6.0.3" + +"@swagger-api/apidom-core@^1.0.0-beta.12", "@swagger-api/apidom-core@^1.0.0-beta.3": version "1.0.0-beta.12" resolved "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-1.0.0-beta.12.tgz" integrity sha512-CAr6aSk9l9ZJUneHpmwk4Br0NZhFLy2QRHoPmr2pWMlAn+0YC4eRYtwOEB8PVsCmP83D4MiXU5zi6cOZyV/cVw== @@ -2506,14 +2533,33 @@ short-unique-id "^5.0.2" ts-mixer "^6.0.3" -"@swagger-api/apidom-error@>=1.0.0-beta.12 <1.0.0-rc.0", "@swagger-api/apidom-error@^1.0.0-beta.12", "@swagger-api/apidom-error@^1.0.0-beta.3", "@swagger-api/apidom-error@^1.0.0-beta.3 <1.0.0-rc.0": +"@swagger-api/apidom-error@>=1.0.0-beta.13 <1.0.0-rc.0", "@swagger-api/apidom-error@^1.0.0-beta.30": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-error/-/apidom-error-1.0.0-beta.30.tgz#97fdc0eedc9cb28f8f76b29933b7e0b8f784740e" + integrity sha512-hVDx0kUF1DTyaEXwmsF3wpJClEfnH0pxjEubqtvHpjjeTMgZzmKc5azbYtvgBX3uUpGHyQZyG/O9g94/wIhhMA== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + +"@swagger-api/apidom-error@^1.0.0-beta.12", "@swagger-api/apidom-error@^1.0.0-beta.3", "@swagger-api/apidom-error@^1.0.0-beta.3 <1.0.0-rc.0": version "1.0.0-beta.12" resolved "https://registry.npmjs.org/@swagger-api/apidom-error/-/apidom-error-1.0.0-beta.12.tgz" integrity sha512-p74a/8GgitGIYvjD5WmROEHv2bGCnDKug3QpJvC5+g36ErZQp428+fK5hhfKQuCo0rjD2fZvs27S17Zh8y0zFw== dependencies: "@babel/runtime-corejs3" "^7.20.7" -"@swagger-api/apidom-json-pointer@>=1.0.0-beta.12 <1.0.0-rc.0", "@swagger-api/apidom-json-pointer@^1.0.0-beta.12", "@swagger-api/apidom-json-pointer@^1.0.0-beta.3 <1.0.0-rc.0": +"@swagger-api/apidom-json-pointer@>=1.0.0-beta.13 <1.0.0-rc.0", "@swagger-api/apidom-json-pointer@^1.0.0-beta.30": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-1.0.0-beta.30.tgz#7dc9d17190924bd848661961b85569e854cffdee" + integrity sha512-G+BDNXU/ARJCbJiFq1A6dh6pNDDp1J0jPfKeIHjsD8aZoRdpJC0F3F7onm8TjQm2cnvAi4B7vPOKzjWrYN1VWw== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-core" "^1.0.0-beta.30" + "@swagger-api/apidom-error" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + +"@swagger-api/apidom-json-pointer@^1.0.0-beta.12", "@swagger-api/apidom-json-pointer@^1.0.0-beta.3 <1.0.0-rc.0": version "1.0.0-beta.12" resolved "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-1.0.0-beta.12.tgz" integrity sha512-JuCqMVfDSWJ7JcdPjYgGjNlqjmKQwxuQh7uKKBLTpNccmXYT+x7WemPuzcWjVVHDd5plw8yQ0YvaU0HlqjS1mA== @@ -2539,6 +2585,19 @@ ramda-adjunct "^5.0.0" ts-mixer "^6.0.3" +"@swagger-api/apidom-ns-arazzo-1@^1.0.0-beta.3 <1.0.0-rc.0", "@swagger-api/apidom-ns-arazzo-1@^1.0.0-beta.30": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-arazzo-1/-/apidom-ns-arazzo-1-1.0.0-beta.30.tgz#3f2f8a9ed5d2c8c10157981231afd6032e20b6b9" + integrity sha512-HpszcpuDlSOXWruHzasR64L8640VHVDuy8xXJrhx1iBu+gDHriOM8gbh8jQgWST91H0smtPeTG9WV1/h6frhRw== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-core" "^1.0.0-beta.30" + "@swagger-api/apidom-ns-json-schema-2020-12" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + ts-mixer "^6.0.3" + "@swagger-api/apidom-ns-asyncapi-2@^1.0.0-beta.3", "@swagger-api/apidom-ns-asyncapi-2@^1.0.0-beta.3 <1.0.0-rc.0": version "1.0.0-beta.3" resolved "https://registry.npmjs.org/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-1.0.0-beta.3.tgz" @@ -2566,6 +2625,20 @@ ramda-adjunct "^5.0.0" ts-mixer "^6.0.4" +"@swagger-api/apidom-ns-json-schema-2019-09@^1.0.0-beta.30": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-json-schema-2019-09/-/apidom-ns-json-schema-2019-09-1.0.0-beta.30.tgz#2467cc1d780f7a20442b8892d77dd9a25147031c" + integrity sha512-HZL76SJaUDmL1GuFcev23UX1vVuxSHIED3vvKso+k3KWNfVWZJrr7GX1ELJx84fWW8g3b5S5+nyz5q1ApT084A== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-core" "^1.0.0-beta.30" + "@swagger-api/apidom-error" "^1.0.0-beta.30" + "@swagger-api/apidom-ns-json-schema-draft-7" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + ts-mixer "^6.0.4" + "@swagger-api/apidom-ns-json-schema-2020-12@^1.0.0-beta.12": version "1.0.0-beta.12" resolved "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-2020-12/-/apidom-ns-json-schema-2020-12-1.0.0-beta.12.tgz" @@ -2580,6 +2653,20 @@ ramda-adjunct "^5.0.0" ts-mixer "^6.0.4" +"@swagger-api/apidom-ns-json-schema-2020-12@^1.0.0-beta.30": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-json-schema-2020-12/-/apidom-ns-json-schema-2020-12-1.0.0-beta.30.tgz#7247885d81a7639b695687d1912cb1c66ea5897c" + integrity sha512-D2adAcu/ISoBe0zRbcX0HyaDvWoMhmaL8iPR4pvjLY7soB2tCR4uLEzAkqPa2zaOKBRA2ziF74aNKrKbM5sX8w== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-core" "^1.0.0-beta.30" + "@swagger-api/apidom-error" "^1.0.0-beta.30" + "@swagger-api/apidom-ns-json-schema-2019-09" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + ts-mixer "^6.0.4" + "@swagger-api/apidom-ns-json-schema-draft-4@^1.0.0-beta.12", "@swagger-api/apidom-ns-json-schema-draft-4@^1.0.0-beta.3": version "1.0.0-beta.12" resolved "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-1.0.0-beta.12.tgz" @@ -2593,6 +2680,19 @@ ramda-adjunct "^5.0.0" ts-mixer "^6.0.4" +"@swagger-api/apidom-ns-json-schema-draft-4@^1.0.0-beta.30": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-1.0.0-beta.30.tgz#c87a9d2c92236098e78948594fdf3b24d1f77d07" + integrity sha512-u5YMIw/g74Z59wPBFS2A2LaheC+EEqRcbpUQOApTvb6zjW+xWxbCuKV1ypzIaVDDPIry8e3mpwjjXLj1mvad5w== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-ast" "^1.0.0-beta.30" + "@swagger-api/apidom-core" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + ts-mixer "^6.0.4" + "@swagger-api/apidom-ns-json-schema-draft-6@^1.0.0-beta.12": version "1.0.0-beta.12" resolved "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-1.0.0-beta.12.tgz" @@ -2607,6 +2707,20 @@ ramda-adjunct "^5.0.0" ts-mixer "^6.0.4" +"@swagger-api/apidom-ns-json-schema-draft-6@^1.0.0-beta.30": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-1.0.0-beta.30.tgz#980bce110261b9554bdea19bf3ac01d0e1d43c07" + integrity sha512-/Mp11+tBKTN6XnpOiQo/cKnqmvfJhdCniHCK6Bg8wpCI3dMi+nSSpIYgWEPVQfNsLtf/PaYegrtYY56W4UzNRw== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-core" "^1.0.0-beta.30" + "@swagger-api/apidom-error" "^1.0.0-beta.30" + "@swagger-api/apidom-ns-json-schema-draft-4" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + ts-mixer "^6.0.4" + "@swagger-api/apidom-ns-json-schema-draft-7@^1.0.0-beta.12", "@swagger-api/apidom-ns-json-schema-draft-7@^1.0.0-beta.3": version "1.0.0-beta.12" resolved "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-1.0.0-beta.12.tgz" @@ -2621,6 +2735,20 @@ ramda-adjunct "^5.0.0" ts-mixer "^6.0.4" +"@swagger-api/apidom-ns-json-schema-draft-7@^1.0.0-beta.30": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-1.0.0-beta.30.tgz#a1a665ff33f71679085f60ea444b46c257b2650e" + integrity sha512-6sZ0LLYnEz9KXtt9xTRSc0EORBl5Fj3LUbfabUjqLQZGldsJWU+3TTQ4XtzFFHlan7z2WYyALKP7iP+b60XbPg== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-core" "^1.0.0-beta.30" + "@swagger-api/apidom-error" "^1.0.0-beta.30" + "@swagger-api/apidom-ns-json-schema-draft-6" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + ts-mixer "^6.0.4" + "@swagger-api/apidom-ns-openapi-2@^1.0.0-beta.3", "@swagger-api/apidom-ns-openapi-2@^1.0.0-beta.3 <1.0.0-rc.0": version "1.0.0-beta.3" resolved "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-2/-/apidom-ns-openapi-2-1.0.0-beta.3.tgz" @@ -2649,7 +2777,37 @@ ramda-adjunct "^5.0.0" ts-mixer "^6.0.3" -"@swagger-api/apidom-ns-openapi-3-1@>=1.0.0-beta.12 <1.0.0-rc.0", "@swagger-api/apidom-ns-openapi-3-1@^1.0.0-beta.3", "@swagger-api/apidom-ns-openapi-3-1@^1.0.0-beta.3 <1.0.0-rc.0": +"@swagger-api/apidom-ns-openapi-3-0@^1.0.0-beta.30": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-1.0.0-beta.30.tgz#611709a24279435a9cf8f98552b6fba3991da031" + integrity sha512-7bz6kCgjStTKGGI4wBP2ho574lyfjH5EDPPuXhkwmAG2mOn9MZezlQhsbdo3B+vbi/58mqQb2XCoB4aeP1F+GQ== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-core" "^1.0.0-beta.30" + "@swagger-api/apidom-error" "^1.0.0-beta.30" + "@swagger-api/apidom-ns-json-schema-draft-4" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + ts-mixer "^6.0.3" + +"@swagger-api/apidom-ns-openapi-3-1@>=1.0.0-beta.13 <1.0.0-rc.0": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-1.0.0-beta.30.tgz#a43fe94c57ec576c8b4a5ebeb7c2a35683ebf84b" + integrity sha512-pq2jxSp0I6xnGzyAiEXWYMuurp8H7TlOQ6Ijr/XX54gNmaIK+yQ3HXc7S6FZx+B2kQx03Tb8Y8O7L7J7YnmFiA== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-ast" "^1.0.0-beta.30" + "@swagger-api/apidom-core" "^1.0.0-beta.30" + "@swagger-api/apidom-json-pointer" "^1.0.0-beta.30" + "@swagger-api/apidom-ns-json-schema-2020-12" "^1.0.0-beta.30" + "@swagger-api/apidom-ns-openapi-3-0" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + ts-mixer "^6.0.3" + +"@swagger-api/apidom-ns-openapi-3-1@^1.0.0-beta.3", "@swagger-api/apidom-ns-openapi-3-1@^1.0.0-beta.3 <1.0.0-rc.0": version "1.0.0-beta.12" resolved "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-1.0.0-beta.12.tgz" integrity sha512-IayaLSawWo5rAyM2nRY6faTfK8cJQ+mGGR94NOmsjcUQw9IljY9uX7PXj3izOdFlXFYjgR1P+mIhuuXyDuw4qg== @@ -2665,19 +2823,6 @@ ramda-adjunct "^5.0.0" ts-mixer "^6.0.3" -"@swagger-api/apidom-ns-workflows-1@^1.0.0-beta.3", "@swagger-api/apidom-ns-workflows-1@^1.0.0-beta.3 <1.0.0-rc.0": - version "1.0.0-beta.3" - resolved "https://registry.npmjs.org/@swagger-api/apidom-ns-workflows-1/-/apidom-ns-workflows-1-1.0.0-beta.3.tgz" - integrity sha512-+7i8CZAC+TypSYuxTtwXH2qIyQC1ATn8r+1pW4NWCs4F2Yr4K2gGG4ZmOE6ckNa+Q53yyx+Spt7xhLfZDJZp/w== - dependencies: - "@babel/runtime-corejs3" "^7.20.7" - "@swagger-api/apidom-core" "^1.0.0-beta.3" - "@swagger-api/apidom-ns-openapi-3-1" "^1.0.0-beta.3" - "@types/ramda" "~0.30.0" - ramda "~0.30.0" - ramda-adjunct "^5.0.0" - ts-mixer "^6.0.3" - "@swagger-api/apidom-parser-adapter-api-design-systems-json@^1.0.0-beta.3 <1.0.0-rc.0": version "1.0.0-beta.3" resolved "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-1.0.0-beta.3.tgz" @@ -2704,6 +2849,32 @@ ramda "~0.30.0" ramda-adjunct "^5.0.0" +"@swagger-api/apidom-parser-adapter-arazzo-json-1@^1.0.0-beta.3 <1.0.0-rc.0": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-arazzo-json-1/-/apidom-parser-adapter-arazzo-json-1-1.0.0-beta.30.tgz#3250aa29e0b423bfdf59fd355c8927a6588ee5e2" + integrity sha512-SZajkrTJ7c1I9CI3gnsdHZCQFSIyQ2H/lkWDjA/drZkRcfbR1CTbR2q0BGGlV5Y+nFHBxjRNpPbYbZrqh0WV4w== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-core" "^1.0.0-beta.30" + "@swagger-api/apidom-ns-arazzo-1" "^1.0.0-beta.30" + "@swagger-api/apidom-parser-adapter-json" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + +"@swagger-api/apidom-parser-adapter-arazzo-yaml-1@^1.0.0-beta.3 <1.0.0-rc.0": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-arazzo-yaml-1/-/apidom-parser-adapter-arazzo-yaml-1-1.0.0-beta.30.tgz#54ab0622a73d8ecc3190a3f1d07c4b994903536a" + integrity sha512-T+N1ix+V5IpOWMFcamQRI50830JayD1gifnRm+mVeWJKMzp+xm08bnO8NiR9LQ2SKJZ6FWYM38oG2tAt0Lwxcg== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-core" "^1.0.0-beta.30" + "@swagger-api/apidom-ns-arazzo-1" "^1.0.0-beta.30" + "@swagger-api/apidom-parser-adapter-yaml-1-2" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + "@swagger-api/apidom-parser-adapter-asyncapi-json-2@^1.0.0-beta.3 <1.0.0-rc.0": version "1.0.0-beta.3" resolved "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-1.0.0-beta.3.tgz" @@ -2746,6 +2917,22 @@ tree-sitter-json "=0.24.8" web-tree-sitter "=0.24.3" +"@swagger-api/apidom-parser-adapter-json@^1.0.0-beta.30": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-1.0.0-beta.30.tgz#2febfadd7e3e4ca755fcf954d44a01ee9e66e2ab" + integrity sha512-cciT19OOXafwBnXe9KFVwUGEVu4Zrvb4k12TYNlNqzVg1xA9pBc3Ywq5EgHIhiiQOLY3fILr0fr6B36N6irN2Q== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-ast" "^1.0.0-beta.30" + "@swagger-api/apidom-core" "^1.0.0-beta.30" + "@swagger-api/apidom-error" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + tree-sitter "=0.22.1" + tree-sitter-json "=0.24.8" + web-tree-sitter "=0.24.5" + "@swagger-api/apidom-parser-adapter-openapi-json-2@^1.0.0-beta.3 <1.0.0-rc.0": version "1.0.0-beta.3" resolved "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-2/-/apidom-parser-adapter-openapi-json-2-1.0.0-beta.3.tgz" @@ -2824,32 +3011,6 @@ ramda "~0.30.0" ramda-adjunct "^5.0.0" -"@swagger-api/apidom-parser-adapter-workflows-json-1@^1.0.0-beta.3 <1.0.0-rc.0": - version "1.0.0-beta.3" - resolved "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-workflows-json-1/-/apidom-parser-adapter-workflows-json-1-1.0.0-beta.3.tgz" - integrity sha512-OsKz09YcfQfTbiNZueTLHBrn7umnMjtuN0ZzuNiBs5txaLS196grpzyTiG+4UJ1zIWvjvZmLZEbQqbKZ9qTw8A== - dependencies: - "@babel/runtime-corejs3" "^7.20.7" - "@swagger-api/apidom-core" "^1.0.0-beta.3" - "@swagger-api/apidom-ns-workflows-1" "^1.0.0-beta.3" - "@swagger-api/apidom-parser-adapter-json" "^1.0.0-beta.3" - "@types/ramda" "~0.30.0" - ramda "~0.30.0" - ramda-adjunct "^5.0.0" - -"@swagger-api/apidom-parser-adapter-workflows-yaml-1@^1.0.0-beta.3 <1.0.0-rc.0": - version "1.0.0-beta.3" - resolved "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-workflows-yaml-1/-/apidom-parser-adapter-workflows-yaml-1-1.0.0-beta.3.tgz" - integrity sha512-IifK3T6UtqBkIoHOQe6QRGpFU9LFqmJ5T1JzbWnVX+gazoVE+N9ZkFWQfb9pKCaCfAwPVp+vai6bQ2eUsGh4CA== - dependencies: - "@babel/runtime-corejs3" "^7.20.7" - "@swagger-api/apidom-core" "^1.0.0-beta.3" - "@swagger-api/apidom-ns-workflows-1" "^1.0.0-beta.3" - "@swagger-api/apidom-parser-adapter-yaml-1-2" "^1.0.0-beta.3" - "@types/ramda" "~0.30.0" - ramda "~0.30.0" - ramda-adjunct "^5.0.0" - "@swagger-api/apidom-parser-adapter-yaml-1-2@^1.0.0-beta.3", "@swagger-api/apidom-parser-adapter-yaml-1-2@^1.0.0-beta.3 <1.0.0-rc.0": version "1.0.0-beta.3" resolved "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-1.0.0-beta.3.tgz" @@ -2866,15 +3027,31 @@ tree-sitter "=0.21.1" web-tree-sitter "=0.24.3" -"@swagger-api/apidom-reference@>=1.0.0-beta.12 <1.0.0-rc.0": - version "1.0.0-beta.12" - resolved "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-1.0.0-beta.12.tgz" - integrity sha512-4A5dvra9NCsl9Dp3x3UyNV3tyTl1LJwvNowaLfMuY5r8jtQLzkcCW+CLPyP2Y64qeT30sklZp7/M3VVd6jKPOg== +"@swagger-api/apidom-parser-adapter-yaml-1-2@^1.0.0-beta.30": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-1.0.0-beta.30.tgz#b48d0f8093d976f97dbc86a51fa86f84dfec8f5a" + integrity sha512-NRmQehyw4gbDzeBAl0zjyPqj4e/jNYgqnRLcOsxTKpWODud8RHBqEvju/M6iET6ru0o+A9265efFzqR9hiE0LA== dependencies: - "@babel/runtime-corejs3" "^7.20.7" - "@swagger-api/apidom-core" "^1.0.0-beta.12" + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-ast" "^1.0.0-beta.30" + "@swagger-api/apidom-core" "^1.0.0-beta.30" + "@swagger-api/apidom-error" "^1.0.0-beta.30" + "@tree-sitter-grammars/tree-sitter-yaml" "=0.7.0" "@types/ramda" "~0.30.0" - axios "^1.7.4" + ramda "~0.30.0" + ramda-adjunct "^5.0.0" + tree-sitter "=0.22.1" + web-tree-sitter "=0.24.5" + +"@swagger-api/apidom-reference@>=1.0.0-beta.13 <1.0.0-rc.0": + version "1.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-reference/-/apidom-reference-1.0.0-beta.30.tgz#231f80df4060a88391ecd7cece6c71cc9ae60544" + integrity sha512-l1MpLMlmaX+y2hra5EadfR37sAMzmEz1wZomVcnw7vJEFlLQo3WwOdFvpQemPCZ9IJHUs+5zhZ++w7z60uKpSw== + dependencies: + "@babel/runtime-corejs3" "^7.26.10" + "@swagger-api/apidom-core" "^1.0.0-beta.30" + "@types/ramda" "~0.30.0" + axios "^1.8.2" minimatch "^7.4.3" process "^0.11.10" ramda "~0.30.0" @@ -2882,13 +3059,15 @@ optionalDependencies: "@swagger-api/apidom-error" "^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-json-pointer" "^1.0.0-beta.3 <1.0.0-rc.0" + "@swagger-api/apidom-ns-arazzo-1" "^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-ns-asyncapi-2" "^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-ns-openapi-2" "^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-ns-openapi-3-0" "^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-ns-openapi-3-1" "^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-ns-workflows-1" "^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-parser-adapter-api-design-systems-json" "^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-parser-adapter-api-design-systems-yaml" "^1.0.0-beta.3 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-arazzo-json-1" "^1.0.0-beta.3 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-arazzo-yaml-1" "^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-parser-adapter-asyncapi-json-2" "^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2" "^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-parser-adapter-json" "^1.0.0-beta.3 <1.0.0-rc.0" @@ -2898,8 +3077,6 @@ "@swagger-api/apidom-parser-adapter-openapi-yaml-2" "^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0" "^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1" "^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-workflows-json-1" "^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-workflows-yaml-1" "^1.0.0-beta.3 <1.0.0-rc.0" "@swagger-api/apidom-parser-adapter-yaml-1-2" "^1.0.0-beta.3 <1.0.0-rc.0" "@swaggerexpert/cookie@^2.0.2": @@ -2924,6 +3101,14 @@ node-addon-api "^8.0.0" node-gyp-build "^4.8.0" +"@tree-sitter-grammars/tree-sitter-yaml@=0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@tree-sitter-grammars/tree-sitter-yaml/-/tree-sitter-yaml-0.7.0.tgz#83995463cdeed8bb9ad2cdcbeb4d4aed9472411f" + integrity sha512-GOMIK3IaDvECD0eZEhAsLl03RMtM1E8StxuGMn6PpMKFg7jyQ+jSzxJZ4Jmc/tYitah9/AECt8o4tlRQ5yEZQg== + dependencies: + node-addon-api "^8.3.0" + node-gyp-build "^4.8.4" + "@trysound/sax@0.2.0": version "0.2.0" resolved "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz" @@ -3885,10 +4070,10 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" -axios@^1.7.4: - version "1.8.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.2.tgz#fabe06e241dfe83071d4edfbcaa7b1c3a40f7979" - integrity sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg== +axios@^1.8.2: + version "1.8.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.4.tgz#78990bb4bc63d2cae072952d374835950a82f447" + integrity sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw== dependencies: follow-redirects "^1.15.6" form-data "^4.0.0" @@ -8256,6 +8441,11 @@ node-addon-api@^8.0.0, node-addon-api@^8.2.2: resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.0.tgz" integrity sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg== +node-addon-api@^8.2.1, node-addon-api@^8.3.0: + version "8.3.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-8.3.1.tgz#53bc8a4f8dbde3de787b9828059da94ba9fd4eed" + integrity sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA== + node-domexception@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz" @@ -8284,7 +8474,7 @@ node-forge@^1: resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== -node-gyp-build@^4.8.0, node-gyp-build@^4.8.2: +node-gyp-build@^4.8.0, node-gyp-build@^4.8.2, node-gyp-build@^4.8.4: version "4.8.4" resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz" integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ== @@ -8424,16 +8614,16 @@ open@^8.0.9, open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" -openapi-path-templating@^2.0.1: - version "2.1.0" - resolved "https://registry.npmjs.org/openapi-path-templating/-/openapi-path-templating-2.1.0.tgz" - integrity sha512-fLs5eJmLyU8wPRz+JSH5uLE7TE4Ohg6VHOtj0C0AlD3GTCCcw2LgKW6MSN1A8ZBKHEg2O4/d02knmVU1nvGAKQ== +openapi-path-templating@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/openapi-path-templating/-/openapi-path-templating-2.2.1.tgz#57026767530667096d33d7362382a93d75d497f6" + integrity sha512-eN14VrDvl/YyGxxrkGOHkVkWEoPyhyeydOUrbvjoz8K5eIGgELASwN1eqFOJ2CTQMGCy2EntOK1KdtJ8ZMekcg== dependencies: apg-lite "^1.0.4" -openapi-server-url-templating@^1.2.0: +openapi-server-url-templating@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/openapi-server-url-templating/-/openapi-server-url-templating-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/openapi-server-url-templating/-/openapi-server-url-templating-1.3.0.tgz#80bc6ea5209a3c4fe9d359673ba51635676e2503" integrity sha512-DPlCms3KKEbjVQb0spV6Awfn6UWNheuG/+folQPzh/wUaKwuqvj8zt5gagD7qoyxtE03cIiKPgLFS3Q8Bz00uQ== dependencies: apg-lite "^1.0.4" @@ -9180,6 +9370,11 @@ ramda-adjunct@^5.0.0: resolved "https://registry.npmjs.org/ramda-adjunct/-/ramda-adjunct-5.0.0.tgz" integrity sha512-iEehjqp/ZGjYZybZByDaDu27c+79SE7rKDcySLdmjAwKWkz6jNhvGgZwzUGaMsij8Llp9+1N1Gy0drpAq8ZSyA== +ramda-adjunct@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ramda-adjunct/-/ramda-adjunct-5.1.0.tgz#c1281100922b03e74b1535cb9c966628697c5cc1" + integrity sha512-8qCpl2vZBXEJyNbi4zqcgdfHtcdsWjOGbiNSEnEBrM6Y0OKOT8UxJbIVGm1TIcjaSu2MxaWcgtsNlKlCk7o7qg== + ramda@^0.30.1, ramda@~0.30.0: version "0.30.1" resolved "https://registry.npmjs.org/ramda/-/ramda-0.30.1.tgz" @@ -10866,18 +11061,18 @@ svgo@^3.0.2, svgo@^3.2.0: csso "^5.0.5" picocolors "^1.0.0" -swagger-client@^3.34.1: - version "3.34.1" - resolved "https://registry.yarnpkg.com/swagger-client/-/swagger-client-3.34.1.tgz#e61e8120fe80addc3563e0ec5147a9ca71b6e7f4" - integrity sha512-aqk315C959936kijVpR28Q07eugElW9vp77a57hdFlQDF8Kuln7SeB1MwXnTCOQEM6/pIWYN00QlvIEwHqQkqw== +swagger-client@^3.34.4: + version "3.34.4" + resolved "https://registry.yarnpkg.com/swagger-client/-/swagger-client-3.34.4.tgz#034cb241af39b988ca10c39e7add2596c480075d" + integrity sha512-Qvtu8DtARAx5GwefA0eV1WRLa4Q9bhczrtNAsiBMOx3HkxAOczy1APQhrcblJdLys0xEGQ4xYizYFXfIL9BhpA== dependencies: "@babel/runtime-corejs3" "^7.22.15" "@scarf/scarf" "=1.4.0" - "@swagger-api/apidom-core" ">=1.0.0-beta.12 <1.0.0-rc.0" - "@swagger-api/apidom-error" ">=1.0.0-beta.12 <1.0.0-rc.0" - "@swagger-api/apidom-json-pointer" ">=1.0.0-beta.12 <1.0.0-rc.0" - "@swagger-api/apidom-ns-openapi-3-1" ">=1.0.0-beta.12 <1.0.0-rc.0" - "@swagger-api/apidom-reference" ">=1.0.0-beta.12 <1.0.0-rc.0" + "@swagger-api/apidom-core" ">=1.0.0-beta.13 <1.0.0-rc.0" + "@swagger-api/apidom-error" ">=1.0.0-beta.13 <1.0.0-rc.0" + "@swagger-api/apidom-json-pointer" ">=1.0.0-beta.13 <1.0.0-rc.0" + "@swagger-api/apidom-ns-openapi-3-1" ">=1.0.0-beta.13 <1.0.0-rc.0" + "@swagger-api/apidom-reference" ">=1.0.0-beta.13 <1.0.0-rc.0" "@swaggerexpert/cookie" "^2.0.2" deepmerge "~4.3.0" fast-json-patch "^3.0.0-1" @@ -10885,17 +11080,17 @@ swagger-client@^3.34.1: neotraverse "=0.6.18" node-abort-controller "^3.1.1" node-fetch-commonjs "^3.3.2" - openapi-path-templating "^2.0.1" - openapi-server-url-templating "^1.2.0" + openapi-path-templating "^2.2.1" + openapi-server-url-templating "^1.3.0" ramda "^0.30.1" - ramda-adjunct "^5.0.0" + ramda-adjunct "^5.1.0" -swagger-ui-react@^5.20.0: - version "5.20.0" - resolved "https://registry.yarnpkg.com/swagger-ui-react/-/swagger-ui-react-5.20.0.tgz#46b7d65feefad6490e80a4c9f102e44c407c902a" - integrity sha512-txC3j+aPi6KOV7OxJvCdmeosg1oPmPdtOH2Ny3kogRCSwj9y7FF0IHv5KWhjmOaUvQa+9+XiYiw+NGQvi88pSg== +swagger-ui-react@^5.20.2: + version "5.20.2" + resolved "https://registry.yarnpkg.com/swagger-ui-react/-/swagger-ui-react-5.20.2.tgz#eb9f8de2e6c916661d8033d43119f6e105ab87da" + integrity sha512-6ifaFjT02yBv1kjEivIMWxQpI7r8O7D/oA8u1JiwhTkom0dOk85lTExao5Dj5ztS6dBg6i1zm+ILhH94fF9g8Q== dependencies: - "@babel/runtime-corejs3" "^7.26.7" + "@babel/runtime-corejs3" "^7.26.10" "@scarf/scarf" "=1.4.0" base64-js "^1.5.1" classnames "^2.5.1" @@ -10923,7 +11118,7 @@ swagger-ui-react@^5.20.0: reselect "^5.1.1" serialize-error "^8.1.0" sha.js "^2.4.11" - swagger-client "^3.34.1" + swagger-client "^3.34.4" url-parse "^1.5.10" xml "=1.0.1" xml-but-prettier "^1.0.1" @@ -11043,6 +11238,14 @@ tree-sitter@=0.21.1: node-addon-api "^8.0.0" node-gyp-build "^4.8.0" +tree-sitter@=0.22.1: + version "0.22.1" + resolved "https://registry.yarnpkg.com/tree-sitter/-/tree-sitter-0.22.1.tgz#5a5296fc0898b21443657e071b050c95c0d7afbd" + integrity sha512-gRO+jk2ljxZlIn20QRskIvpLCMtzuLl5T0BY6L9uvPYD17uUrxlxWkvYCiVqED2q2q7CVtY52Uex4WcYo2FEXw== + dependencies: + node-addon-api "^8.2.1" + node-gyp-build "^4.8.2" + trim-lines@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz" @@ -11452,6 +11655,11 @@ web-tree-sitter@=0.24.3: resolved "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.24.3.tgz" integrity sha512-uR9YNewr1S2EzPKE+y39nAwaTyobBaZRG/IsfkB/OT4v0lXtNj5WjtHKgn2h7eOYUWIZh5rK9Px7tI6S9CRKdA== +web-tree-sitter@=0.24.5: + version "0.24.5" + resolved "https://registry.yarnpkg.com/web-tree-sitter/-/web-tree-sitter-0.24.5.tgz#16cea449da63012f23ca7b83bd32817dd0520400" + integrity sha512-+J/2VSHN8J47gQUAvF8KDadrfz6uFYVjxoxbKWDoXVsH2u7yLdarCnIURnrMA6uSRkgX3SdmqM5BOoQjPdSh5w== + webpack-bundle-analyzer@^4.9.0: version "4.10.2" resolved "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz" From 8eeed4954765cb863e303c2f8b87eeb4988f3787 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:27:27 -0700 Subject: [PATCH 08/14] chore(deps): bump antd from 5.24.2 to 5.24.5 in /docs (#32951) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package.json | 2 +- docs/yarn.lock | 62 +++++++++++++++++++++++------------------------ 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/docs/package.json b/docs/package.json index 1141cec4984..c122d2e2626 100644 --- a/docs/package.json +++ b/docs/package.json @@ -25,7 +25,7 @@ "@emotion/styled": "^10.0.27", "@saucelabs/theme-github-codeblock": "^0.3.0", "@superset-ui/style": "^0.14.23", - "antd": "^5.24.2", + "antd": "^5.24.5", "docusaurus-plugin-less": "^2.0.2", "less": "^4.2.2", "less-loader": "^11.0.0", diff --git a/docs/yarn.lock b/docs/yarn.lock index 9c40f4a5135..f4b0cecc22b 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -3853,10 +3853,10 @@ ansi-styles@^6.1.0: resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== -antd@^5.24.2: - version "5.24.2" - resolved "https://registry.yarnpkg.com/antd/-/antd-5.24.2.tgz#df28583b68279821096234920584dadea6f9b283" - integrity sha512-7Z9HsE3ZIK3sE/WuUqii3w7Gl1IJuRL21sDUTtkN95JS5KhRYP8ISv7m/HxsJ3Mn/yxgojBCgLPJ212+Dn+aPw== +antd@^5.24.5: + version "5.24.5" + resolved "https://registry.yarnpkg.com/antd/-/antd-5.24.5.tgz#b0976a113163888d1477f9e666c3c23352b098e9" + integrity sha512-1lAv/G+9ewQanyoAo3JumQmIlVxwo5QwWGb6QCHYc40Cq0NxC/EzITcjsgq1PSaTUpLkKq8A2l7Fjtu47vqQBg== dependencies: "@ant-design/colors" "^7.2.0" "@ant-design/cssinjs" "^1.23.0" @@ -3873,22 +3873,22 @@ antd@^5.24.2: classnames "^2.5.1" copy-to-clipboard "^3.3.3" dayjs "^1.11.11" - rc-cascader "~3.33.0" + rc-cascader "~3.33.1" rc-checkbox "~3.5.0" rc-collapse "~3.9.0" rc-dialog "~9.6.0" rc-drawer "~7.2.0" rc-dropdown "~4.2.1" rc-field-form "~2.7.0" - rc-image "~7.11.0" - rc-input "~1.7.2" + rc-image "~7.11.1" + rc-input "~1.7.3" rc-input-number "~9.4.0" rc-mentions "~2.19.1" rc-menu "~9.16.1" rc-motion "^2.9.5" rc-notification "~5.6.3" rc-pagination "~5.1.0" - rc-picker "~4.11.2" + rc-picker "~4.11.3" rc-progress "~4.0.0" rc-rate "~2.13.1" rc-resize-observer "^1.4.3" @@ -3897,11 +3897,11 @@ antd@^5.24.2: rc-slider "~11.1.8" rc-steps "~6.0.1" rc-switch "~4.1.0" - rc-table "~7.50.3" + rc-table "~7.50.4" rc-tabs "~15.5.1" rc-textarea "~1.9.0" rc-tooltip "~6.4.0" - rc-tree "~5.13.0" + rc-tree "~5.13.1" rc-tree-select "~5.27.0" rc-upload "~4.8.1" rc-util "^5.44.4" @@ -9415,10 +9415,10 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -rc-cascader@~3.33.0: - version "3.33.0" - resolved "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.33.0.tgz" - integrity sha512-JvZrMbKBXIbEDmpIORxqvedY/bck6hGbs3hxdWT8eS9wSQ1P7//lGxbyKjOSyQiVBbgzNWriSe6HoMcZO/+0rQ== +rc-cascader@~3.33.1: + version "3.33.1" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.33.1.tgz#19e01462ef5ef51b723c1f562c7b9cde4691e7ee" + integrity sha512-Kyl4EJ7ZfCBuidmZVieegcbFw0RcU5bHHSbtEdmuLYd0fYHCAiYKZ6zon7fWAVyC6rWWOOib0XKdTSf7ElC9rg== dependencies: "@babel/runtime" "^7.25.7" classnames "^2.3.1" @@ -9486,10 +9486,10 @@ rc-field-form@~2.7.0: "@rc-component/async-validator" "^5.0.3" rc-util "^5.32.2" -rc-image@~7.11.0: - version "7.11.0" - resolved "https://registry.npmjs.org/rc-image/-/rc-image-7.11.0.tgz" - integrity sha512-aZkTEZXqeqfPZtnSdNUnKQA0N/3MbgR7nUnZ+/4MfSFWPFHZau4p5r5ShaI0KPEMnNjv4kijSCFq/9wtJpwykw== +rc-image@~7.11.1: + version "7.11.1" + resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.11.1.tgz#3ab290708dc053d3681de94186522e4e594f6772" + integrity sha512-XuoWx4KUXg7hNy5mRTy1i8c8p3K8boWg6UajbHpDXS5AlRVucNfTi5YxTtPBTBzegxAZpvuLfh3emXFt6ybUdA== dependencies: "@babel/runtime" "^7.11.2" "@rc-component/portal" "^1.0.2" @@ -9509,10 +9509,10 @@ rc-input-number@~9.4.0: rc-input "~1.7.1" rc-util "^5.40.1" -rc-input@~1.7.1, rc-input@~1.7.2: - version "1.7.2" - resolved "https://registry.npmjs.org/rc-input/-/rc-input-1.7.2.tgz" - integrity sha512-g3nYONnl4edWj2FfVoxsU3Ec4XTE+Hb39Kfh2MFxMZjp/0gGyPUgy/v7ZhS27ZxUFNkuIDYXm9PJsLyJbtg86A== +rc-input@~1.7.1, rc-input@~1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.7.3.tgz#cb334a17b93ce985bceb243b4c111a5ed641e0e3" + integrity sha512-A5w4egJq8+4JzlQ55FfQjDnPvOaAbzwC3VLOAdOytyek3TboSOP9qxN+Gifup+shVXfvecBLBbWBpWxmk02SWQ== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" @@ -9581,7 +9581,7 @@ rc-pagination@~5.1.0: classnames "^2.3.2" rc-util "^5.38.0" -rc-picker@~4.11.2: +rc-picker@~4.11.3: version "4.11.3" resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.11.3.tgz#7e7e3ad83aa461c284b8391c697492d1c34d2cb8" integrity sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg== @@ -9671,10 +9671,10 @@ rc-switch@~4.1.0: classnames "^2.2.1" rc-util "^5.30.0" -rc-table@~7.50.3: - version "7.50.3" - resolved "https://registry.npmjs.org/rc-table/-/rc-table-7.50.3.tgz" - integrity sha512-Z4/zNCzjv7f/XzPRecb+vJU0DJKdsYt4YRkDzNl4G05m7JmxrKGYC2KqN1Ew6jw2zJq7cxVv3z39qyZOHMuf7A== +rc-table@~7.50.4: + version "7.50.4" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.50.4.tgz#687b5bf76d1a94168f75481cbc83be9442010432" + integrity sha512-Y+YuncnQqoS5e7yHvfvlv8BmCvwDYDX/2VixTBEhkMDk9itS9aBINp4nhzXFKiBP/frG4w0pS9d9Rgisl0T1Bw== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/context" "^1.4.0" @@ -9728,10 +9728,10 @@ rc-tree-select@~5.27.0: rc-tree "~5.13.0" rc-util "^5.43.0" -rc-tree@~5.13.0: - version "5.13.0" - resolved "https://registry.npmjs.org/rc-tree/-/rc-tree-5.13.0.tgz" - integrity sha512-2+lFvoVRnvHQ1trlpXMOWtF8BUgF+3TiipG72uOfhpL5CUdXCk931kvDdUkTL/IZVtNEDQKwEEmJbAYJSA5NnA== +rc-tree@~5.13.0, rc-tree@~5.13.1: + version "5.13.1" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.13.1.tgz#f36a33a94a1282f4b09685216c01487089748910" + integrity sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" From f5d362746823bb17a21292809be0718194067343 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:28:50 -0700 Subject: [PATCH 09/14] chore(deps-dev): bump eslint-config-prettier from 10.0.2 to 10.1.1 in /docs (#32952) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package.json | 2 +- docs/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/package.json b/docs/package.json index c122d2e2626..b100ce41f61 100644 --- a/docs/package.json +++ b/docs/package.json @@ -43,7 +43,7 @@ "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "eslint": "^8.0.0", - "eslint-config-prettier": "^10.0.2", + "eslint-config-prettier": "^10.1.1", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-react": "^7.0.0", "prettier": "^2.0.0", diff --git a/docs/yarn.lock b/docs/yarn.lock index f4b0cecc22b..faa7ccc96a6 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -5534,10 +5534,10 @@ escape-string-regexp@^5.0.0: resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz" integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== -eslint-config-prettier@^10.0.2: - version "10.0.2" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.0.2.tgz#47444de8aa104ce82c2f91ad2a5e96b62c01e20d" - integrity sha512-1105/17ZIMjmCOJOPNfVdbXafLCLj3hPmkmB7dLgt7XsQ/zkxSuDerE/xgO3RxoHysR1N1whmquY0lSn2O0VLg== +eslint-config-prettier@^10.1.1: + version "10.1.1" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.1.tgz#cf0ff6e5c4e7e15f129f1f1ce2a5ecba92dec132" + integrity sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw== eslint-plugin-prettier@^4.0.0: version "4.2.1" From a36e636a58b24fa312c6619aeaf61aad4513a8f8 Mon Sep 17 00:00:00 2001 From: "JUST.in DO IT" Date: Tue, 1 Apr 2025 15:05:36 -0700 Subject: [PATCH 10/14] fix(pivot-table): Revert "fix(Pivot Table): Fix column width to respect currency config (#31414)" (#32968) --- .../plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx index aef8b8b3212..a17bac64aa7 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx +++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx @@ -51,7 +51,6 @@ const Styles = styled.div` width: ${ typeof width === 'string' ? parseInt(width, 10) : width - margin * 2 }px; - white-space: nowrap; `} `; From c83eda9551ebb14f0fb4d2eb2c5276f00bddc1f7 Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Tue, 1 Apr 2025 17:13:09 -0700 Subject: [PATCH 11/14] feat: add latest partition support for BigQuery (#30760) --- superset/db_engine_specs/base.py | 1 + superset/db_engine_specs/bigquery.py | 160 ++++++++++++------ .../db_engine_specs/bigquery_tests.py | 133 +++++---------- 3 files changed, 150 insertions(+), 144 deletions(-) diff --git a/superset/db_engine_specs/base.py b/superset/db_engine_specs/base.py index 79e0eb3bfd7..eaecb74020f 100644 --- a/superset/db_engine_specs/base.py +++ b/superset/db_engine_specs/base.py @@ -1630,6 +1630,7 @@ class BaseEngineSpec: # pylint: disable=too-many-public-methods :return: SqlAlchemy query with additional where clause referencing the latest partition """ + # TODO: Fix circular import caused by importing Database, TableColumn return None @classmethod diff --git a/superset/db_engine_specs/bigquery.py b/superset/db_engine_specs/bigquery.py index c6c57b1624a..cf5cfaad511 100644 --- a/superset/db_engine_specs/bigquery.py +++ b/superset/db_engine_specs/bigquery.py @@ -27,15 +27,15 @@ from typing import Any, TYPE_CHECKING, TypedDict import pandas as pd from apispec import APISpec from apispec.ext.marshmallow import MarshmallowPlugin -from deprecation import deprecated from flask_babel import gettext as __ from marshmallow import fields, Schema from marshmallow.exceptions import ValidationError -from sqlalchemy import column, types +from sqlalchemy import column, func, types from sqlalchemy.engine.base import Engine from sqlalchemy.engine.reflection import Inspector from sqlalchemy.engine.url import URL -from sqlalchemy.sql import sqltypes +from sqlalchemy.sql import column as sql_column, select, sqltypes +from sqlalchemy.sql.expression import table as sql_table from superset.constants import TimeGrain from superset.databases.schemas import encrypted_field_properties, EncryptedString @@ -50,6 +50,11 @@ from superset.superset_typing import ResultSetColumnType from superset.utils import core as utils, json from superset.utils.hashing import md5_sha_from_str +if TYPE_CHECKING: + from sqlalchemy.sql.expression import Select + +logger = logging.getLogger(__name__) + try: import google.auth from google.cloud import bigquery @@ -289,42 +294,80 @@ class BigQueryEngineSpec(BaseEngineSpec): # pylint: disable=too-many-public-met return "_" + md5_sha_from_str(label) @classmethod - @deprecated(deprecated_in="3.0") - def normalize_indexes(cls, indexes: list[dict[str, Any]]) -> list[dict[str, Any]]: - """ - Normalizes indexes for more consistency across db engines - - :param indexes: Raw indexes as returned by SQLAlchemy - :return: cleaner, more aligned index definition - """ - normalized_idxs = [] - # Fixing a bug/behavior observed in pybigquery==0.4.15 where - # the index's `column_names` == [None] - # Here we're returning only non-None indexes - for ix in indexes: - column_names = ix.get("column_names") or [] - ix["column_names"] = [col for col in column_names if col is not None] - if ix["column_names"]: - normalized_idxs.append(ix) - return normalized_idxs - - @classmethod - def get_indexes( + def where_latest_partition( cls, database: Database, - inspector: Inspector, table: Table, - ) -> list[dict[str, Any]]: - """ - Get the indexes associated with the specified schema/table. + query: Select, + columns: list[ResultSetColumnType] | None = None, + ) -> Select | None: + if partition_column := cls.get_time_partition_column(database, table): + max_partition_id = cls.get_max_partition_id(database, table) + query = query.where( + column(partition_column) == func.PARSE_DATE("%Y%m%d", max_partition_id) + ) - :param database: The database to inspect - :param inspector: The SQLAlchemy inspector - :param table: The table instance to inspect - :returns: The indexes - """ + return query - return cls.normalize_indexes(inspector.get_indexes(table.table, table.schema)) + @classmethod + def get_max_partition_id( + cls, + database: Database, + table: Table, + ) -> Select | None: + # Compose schema from catalog and schema + schema_parts = [] + if table.catalog: + schema_parts.append(table.catalog) + if table.schema: + schema_parts.append(table.schema) + schema_parts.append("INFORMATION_SCHEMA") + schema = ".".join(schema_parts) + # Define a virtual table reference to INFORMATION_SCHEMA.PARTITIONS + partitions_table = sql_table( + "PARTITIONS", + sql_column("partition_id"), + sql_column("table_name"), + schema=schema, + ) + + # Build the query + query = select( + func.max(partitions_table.c.partition_id).label("max_partition_id") + ).where(partitions_table.c.table_name == table.table) + + # Compile to BigQuery SQL + compiled_query = query.compile( + dialect=database.get_dialect(), + compile_kwargs={"literal_binds": True}, + ) + + # Run the query and handle result + with database.get_raw_connection( + catalog=table.catalog, + schema=table.schema, + ) as conn: + cursor = conn.cursor() + cursor.execute(str(compiled_query)) + if row := cursor.fetchone(): + return row[0] + return None + + @classmethod + def get_time_partition_column( + cls, + database: Database, + table: Table, + ) -> str | None: + with cls.get_engine( + database, catalog=table.catalog, schema=table.schema + ) as engine: + client = cls._get_client(engine, database) + bq_table = client.get_table(f"{table.schema}.{table.table}") + + if bq_table.time_partitioning: + return bq_table.time_partitioning.field + return None @classmethod def get_extra_table_metadata( @@ -332,23 +375,38 @@ class BigQueryEngineSpec(BaseEngineSpec): # pylint: disable=too-many-public-met database: Database, table: Table, ) -> dict[str, Any]: - indexes = database.get_indexes(table) - if not indexes: - return {} - partitions_columns = [ - index.get("column_names", []) - for index in indexes - if index.get("name") == "partition" - ] - cluster_columns = [ - index.get("column_names", []) - for index in indexes - if index.get("name") == "clustering" - ] - return { - "partitions": {"cols": partitions_columns}, - "clustering": {"cols": cluster_columns}, - } + payload = {} + partition_column = cls.get_time_partition_column(database, table) + with cls.get_engine( + database, catalog=table.catalog, schema=table.schema + ) as engine: + if partition_column: + max_partition_id = cls.get_max_partition_id(database, table) + sql = cls.select_star( + database, + table, + engine, + indent=False, + show_cols=False, + latest_partition=True, + ) + payload.update( + { + "partitions": { + "cols": [partition_column], + "latest": {partition_column: max_partition_id}, + "partitionQuery": sql, + }, + "indexes": [ + { + "name": "partitioned", + "cols": [partition_column], + "type": "partitioned", + } + ], + } + ) + return payload @classmethod def epoch_to_dttm(cls) -> str: diff --git a/tests/integration_tests/db_engine_specs/bigquery_tests.py b/tests/integration_tests/db_engine_specs/bigquery_tests.py index 45edb5a0d7c..636fc3523ae 100644 --- a/tests/integration_tests/db_engine_specs/bigquery_tests.py +++ b/tests/integration_tests/db_engine_specs/bigquery_tests.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. import unittest.mock as mock +from contextlib import contextmanager import pytest from pandas import DataFrame @@ -32,6 +33,15 @@ from tests.integration_tests.fixtures.birth_names_dashboard import ( ) +@contextmanager +def mock_engine_with_credentials(*args, **kwargs): + engine_mock = mock.Mock() + engine_mock.dialect.credentials_info = { + "key": "value" + } # Add the credentials_info attribute + yield engine_mock + + class TestBigQueryDbEngineSpec(TestDbEngineSpec): def test_bigquery_sqla_column_label(self): """ @@ -111,108 +121,45 @@ class TestBigQueryDbEngineSpec(TestDbEngineSpec): result = BigQueryEngineSpec.fetch_data(None, 0) assert result == [1, 2] - def test_get_extra_table_metadata(self): + @mock.patch.object( + BigQueryEngineSpec, "get_engine", side_effect=mock_engine_with_credentials + ) + @mock.patch.object(BigQueryEngineSpec, "get_time_partition_column") + @mock.patch.object(BigQueryEngineSpec, "get_max_partition_id") + @mock.patch.object(BigQueryEngineSpec, "quote_table", return_value="`table_name`") + def test_get_extra_table_metadata( + self, + mock_quote_table, + mock_get_max_partition_id, + mock_get_time_partition_column, + mock_get_engine, + ): """ DB Eng Specs (bigquery): Test extra table metadata """ database = mock.Mock() + sql = "SELECT * FROM `table_name`" + database.compile_sqla_query.return_value = sql + tbl = Table("some_table", "some_schema") + # Test no indexes - database.get_indexes = mock.MagicMock(return_value=None) - result = BigQueryEngineSpec.get_extra_table_metadata( - database, - Table("some_table", "some_schema"), - ) + mock_get_time_partition_column.return_value = None + mock_get_max_partition_id.return_value = None + result = BigQueryEngineSpec.get_extra_table_metadata(database, tbl) assert result == {} - index_metadata = [ - { - "name": "clustering", - "column_names": ["c_col1", "c_col2", "c_col3"], + mock_get_time_partition_column.return_value = "ds" + mock_get_max_partition_id.return_value = "19690101" + result = BigQueryEngineSpec.get_extra_table_metadata(database, tbl) + print(result) + assert result == { + "indexes": [{"cols": ["ds"], "name": "partitioned", "type": "partitioned"}], + "partitions": { + "cols": ["ds"], + "latest": {"ds": "19690101"}, + "partitionQuery": sql, }, - { - "name": "partition", - "column_names": ["p_col1", "p_col2", "p_col3"], - }, - ] - expected_result = { - "partitions": {"cols": [["p_col1", "p_col2", "p_col3"]]}, - "clustering": {"cols": [["c_col1", "c_col2", "c_col3"]]}, } - database.get_indexes = mock.MagicMock(return_value=index_metadata) - result = BigQueryEngineSpec.get_extra_table_metadata( - database, - Table("some_table", "some_schema"), - ) - assert result == expected_result - - def test_get_indexes(self): - database = mock.Mock() - inspector = mock.Mock() - schema = "foo" - table_name = "bar" - - inspector.get_indexes = mock.Mock( - return_value=[ - { - "name": "partition", - "column_names": [None], - "unique": False, - } - ] - ) - - assert ( - BigQueryEngineSpec.get_indexes( - database, - inspector, - Table(table_name, schema), - ) - == [] - ) - - inspector.get_indexes = mock.Mock( - return_value=[ - { - "name": "partition", - "column_names": ["dttm"], - "unique": False, - } - ] - ) - - assert BigQueryEngineSpec.get_indexes( - database, - inspector, - Table(table_name, schema), - ) == [ - { - "name": "partition", - "column_names": ["dttm"], - "unique": False, - } - ] - - inspector.get_indexes = mock.Mock( - return_value=[ - { - "name": "partition", - "column_names": ["dttm", None], - "unique": False, - } - ] - ) - - assert BigQueryEngineSpec.get_indexes( - database, - inspector, - Table(table_name, schema), - ) == [ - { - "name": "partition", - "column_names": ["dttm"], - "unique": False, - } - ] @mock.patch("superset.db_engine_specs.bigquery.BigQueryEngineSpec.get_engine") @mock.patch("superset.db_engine_specs.bigquery.pandas_gbq") From 4f0020d0df9634b06a2c70699c3ed5b88cffee7c Mon Sep 17 00:00:00 2001 From: Enzo Martellucci <52219496+EnxDev@users.noreply.github.com> Date: Wed, 2 Apr 2025 13:06:17 +0200 Subject: [PATCH 12/14] feat(List Roles): Migrate FAB view to React (#32432) Co-authored-by: Diego Pucci --- UPDATING.md | 1 + .../e2e/dashboard/drilltodetail.test.ts | 2 +- .../components/ListView/Filters/Select.tsx | 3 + .../src/components/ListView/Filters/index.tsx | 2 + .../src/components/ListView/types.ts | 1 + .../src/features/home/SubMenu.tsx | 2 + .../src/features/roles/RoleFormItems.tsx | 70 +++ .../features/roles/RoleListAddModal.test.tsx | 92 ++++ .../src/features/roles/RoleListAddModal.tsx | 71 +++ .../roles/RoleListDuplicateModal.test.tsx | 100 ++++ .../features/roles/RoleListDuplicateModal.tsx | 69 +++ .../features/roles/RoleListEditModal.test.tsx | 153 ++++++ .../src/features/roles/RoleListEditModal.tsx | 153 ++++++ superset-frontend/src/features/roles/types.ts | 66 +++ superset-frontend/src/features/roles/utils.ts | 47 ++ .../src/pages/RolesList/RolesList.test.tsx | 206 +++++++ .../src/pages/RolesList/index.tsx | 507 ++++++++++++++++++ superset-frontend/src/views/routes.tsx | 16 + superset/config.py | 1 + superset/initialization/__init__.py | 13 +- superset/security/api.py | 165 +++++- superset/security/manager.py | 45 +- superset/views/roles.py | 34 ++ tests/integration_tests/core_tests.py | 2 +- tests/integration_tests/security/api_tests.py | 17 + tests/unit_tests/security/api_test.py | 5 + 26 files changed, 1831 insertions(+), 12 deletions(-) create mode 100644 superset-frontend/src/features/roles/RoleFormItems.tsx create mode 100644 superset-frontend/src/features/roles/RoleListAddModal.test.tsx create mode 100644 superset-frontend/src/features/roles/RoleListAddModal.tsx create mode 100644 superset-frontend/src/features/roles/RoleListDuplicateModal.test.tsx create mode 100644 superset-frontend/src/features/roles/RoleListDuplicateModal.tsx create mode 100644 superset-frontend/src/features/roles/RoleListEditModal.test.tsx create mode 100644 superset-frontend/src/features/roles/RoleListEditModal.tsx create mode 100644 superset-frontend/src/features/roles/types.ts create mode 100644 superset-frontend/src/features/roles/utils.ts create mode 100644 superset-frontend/src/pages/RolesList/RolesList.test.tsx create mode 100644 superset-frontend/src/pages/RolesList/index.tsx create mode 100644 superset/views/roles.py diff --git a/UPDATING.md b/UPDATING.md index 7fd97a4dab0..077f43cdd89 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -46,6 +46,7 @@ assists people when migrating to a new version. - [30284](https://github.com/apache/superset/pull/30284) Deprecated GLOBAL_ASYNC_QUERIES_REDIS_CONFIG in favor of the new GLOBAL_ASYNC_QUERIES_CACHE_BACKEND configuration. To leverage Redis Sentinel, set CACHE_TYPE to RedisSentinelCache, or use RedisCache for standalone Redis - [31961](https://github.com/apache/superset/pull/31961) Upgraded React from version 16.13.1 to 17.0.2. If you are using custom frontend extensions or plugins, you may need to update them to be compatible with React 17. - [31260](https://github.com/apache/superset/pull/31260) Docker images now use `uv pip install` instead of `pip install` to manage the python envrionment. Most docker-based deployments will be affected, whether you derive one of the published images, or have custom bootstrap script that install python libraries (drivers) +- [32432](https://github.com/apache/superset/pull/31260) Moves the List Roles FAB view to the frontend and requires `FAB_ADD_SECURITY_API` to be enabled in the configuration and `superset init` to be executed. ### Potential Downtime diff --git a/superset-frontend/cypress-base/cypress/e2e/dashboard/drilltodetail.test.ts b/superset-frontend/cypress-base/cypress/e2e/dashboard/drilltodetail.test.ts index f6fd4b6a5c2..eda6e56c452 100644 --- a/superset-frontend/cypress-base/cypress/e2e/dashboard/drilltodetail.test.ts +++ b/superset-frontend/cypress-base/cypress/e2e/dashboard/drilltodetail.test.ts @@ -434,7 +434,7 @@ describe('Drill to detail modal', () => { SUPPORTED_TIER2_CHARTS.forEach(waitForChartLoad); }); - describe.only('Modal actions', () => { + describe('Modal actions', () => { it('clears filters', () => { interceptSamples(); diff --git a/superset-frontend/src/components/ListView/Filters/Select.tsx b/superset-frontend/src/components/ListView/Filters/Select.tsx index edbc5ad452b..02cd8989759 100644 --- a/superset-frontend/src/components/ListView/Filters/Select.tsx +++ b/superset-frontend/src/components/ListView/Filters/Select.tsx @@ -37,6 +37,7 @@ interface SelectFilterProps extends BaseFilter { onSelect: (selected: SelectOption | undefined, isClear?: boolean) => void; paginate?: boolean; selects: Filter['selects']; + loading?: boolean; } function SelectFilter( @@ -47,6 +48,7 @@ function SelectFilter( initialValue, onSelect, selects = [], + loading = false, }: SelectFilterProps, ref: RefObject, ) { @@ -115,6 +117,7 @@ function SelectFilter( placeholder={t('Select or type a value')} showSearch value={selectedOption} + loading={loading} /> )} diff --git a/superset-frontend/src/components/ListView/Filters/index.tsx b/superset-frontend/src/components/ListView/Filters/index.tsx index 5733151180e..8f64b67d051 100644 --- a/superset-frontend/src/components/ListView/Filters/index.tsx +++ b/superset-frontend/src/components/ListView/Filters/index.tsx @@ -75,6 +75,7 @@ function UIFilters( selects, toolTipDescription, onFilterUpdate, + loading, }, index, ) => { @@ -103,6 +104,7 @@ function UIFilters( }} paginate={paginate} selects={selects} + loading={loading ?? false} /> ); } diff --git a/superset-frontend/src/components/ListView/types.ts b/superset-frontend/src/components/ListView/types.ts index 90de4eab18e..5b498070acf 100644 --- a/superset-frontend/src/components/ListView/types.ts +++ b/superset-frontend/src/components/ListView/types.ts @@ -59,6 +59,7 @@ export interface Filter { pageSize: number, ) => Promise<{ data: SelectOption[]; totalCount: number }>; paginate?: boolean; + loading?: boolean; } export type Filters = Filter[]; diff --git a/superset-frontend/src/features/home/SubMenu.tsx b/superset-frontend/src/features/home/SubMenu.tsx index 7945e76da9b..dd03fba9d13 100644 --- a/superset-frontend/src/features/home/SubMenu.tsx +++ b/superset-frontend/src/features/home/SubMenu.tsx @@ -134,6 +134,7 @@ export interface ButtonProps { | 'warning' | 'success' | 'tertiary'; + loading?: boolean; } export interface SubMenuProps { @@ -282,6 +283,7 @@ const SubMenuComponent: FunctionComponent = props => { buttonStyle={btn.buttonStyle} onClick={btn.onClick} data-test={btn['data-test']} + loading={btn.loading ?? false} > {btn.name} diff --git a/superset-frontend/src/features/roles/RoleFormItems.tsx b/superset-frontend/src/features/roles/RoleFormItems.tsx new file mode 100644 index 00000000000..56c4ad76f4b --- /dev/null +++ b/superset-frontend/src/features/roles/RoleFormItems.tsx @@ -0,0 +1,70 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { FormItem } from 'src/components/Form'; +import Select from 'src/components/Select/Select'; +import { Input } from 'src/components/Input'; +import { t } from '@superset-ui/core'; +import { FC } from 'react'; +import { FormattedPermission, UserObject } from './types'; + +interface PermissionsFieldProps { + permissions: FormattedPermission[]; +} + +interface UsersFieldProps { + users: UserObject[]; +} + +export const RoleNameField = () => ( + + + +); + +export const PermissionsField: FC = ({ + permissions, +}) => ( + + ({ label: user.username, value: user.id }))} + data-test="users-select" + /> + +); diff --git a/superset-frontend/src/features/roles/RoleListAddModal.test.tsx b/superset-frontend/src/features/roles/RoleListAddModal.test.tsx new file mode 100644 index 00000000000..69d7eb66365 --- /dev/null +++ b/superset-frontend/src/features/roles/RoleListAddModal.test.tsx @@ -0,0 +1,92 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + render, + screen, + fireEvent, + waitFor, +} from 'spec/helpers/testing-library'; +import RoleListAddModal from './RoleListAddModal'; +import { createRole } from './utils'; + +const mockToasts = { + addDangerToast: jest.fn(), + addSuccessToast: jest.fn(), +}; + +jest.mock('./utils'); +const mockCreateRole = jest.mocked(createRole); + +jest.mock('src/components/MessageToasts/withToasts', () => ({ + useToasts: () => mockToasts, +})); + +describe('RoleListAddModal', () => { + const mockProps = { + show: true, + onHide: jest.fn(), + onSave: jest.fn(), + permissions: [ + { id: 1, label: 'Permission 1', value: 'Permission_1' }, + { id: 2, label: 'Permission 2', value: 'Permission_2' }, + ], + }; + + it('renders modal with form fields', () => { + render(); + expect(screen.getByText('Add Role')).toBeInTheDocument(); + expect(screen.getByText('Role Name')).toBeInTheDocument(); + expect(screen.getByText('Permissions')).toBeInTheDocument(); + }); + + it('calls onHide when cancel button is clicked', () => { + render(); + fireEvent.click(screen.getByTestId('modal-cancel-button')); + expect(mockProps.onHide).toHaveBeenCalled(); + }); + + it('disables save button when role name is empty', () => { + render(); + expect(screen.getByTestId('form-modal-save-button')).toBeDisabled(); + }); + + it('enables save button when role name is entered', () => { + render(); + fireEvent.change(screen.getByTestId('role-name-input'), { + target: { value: 'New Role' }, + }); + expect(screen.getByTestId('form-modal-save-button')).toBeEnabled(); + }); + + it('calls createRole when save button is clicked', async () => { + render(); + + fireEvent.change(screen.getByTestId('role-name-input'), { + target: { value: 'New Role' }, + }); + + const saveButton = screen.getByRole('button', { name: 'Save' }); + fireEvent.click(saveButton); + + await waitFor(() => { + expect(mockCreateRole).toHaveBeenCalledWith('New Role'); + }); + }); +}); diff --git a/superset-frontend/src/features/roles/RoleListAddModal.tsx b/superset-frontend/src/features/roles/RoleListAddModal.tsx new file mode 100644 index 00000000000..c80def6724a --- /dev/null +++ b/superset-frontend/src/features/roles/RoleListAddModal.tsx @@ -0,0 +1,71 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { t } from '@superset-ui/core'; +import { useToasts } from 'src/components/MessageToasts/withToasts'; +import FormModal from 'src/components/Modal/FormModal'; +import { createRole, updateRolePermissions } from './utils'; +import { PermissionsField, RoleNameField } from './RoleFormItems'; +import { BaseModalProps, FormattedPermission, RoleForm } from './types'; + +export interface RoleListAddModalProps extends BaseModalProps { + permissions: FormattedPermission[]; +} + +function RoleListAddModal({ + show, + onHide, + onSave, + permissions, +}: RoleListAddModalProps) { + const { addDangerToast, addSuccessToast } = useToasts(); + + const handleFormSubmit = async (values: RoleForm) => { + try { + const { json: roleResponse } = await createRole(values.roleName); + + if (values.rolePermissions?.length > 0) { + await updateRolePermissions(roleResponse.id, values.rolePermissions); + } + + addSuccessToast(t('Role was successfully created!')); + } catch (err) { + addDangerToast(t('Error while adding role!')); + throw err; + } + }; + + return ( + + <> + + + + + ); +} + +export default RoleListAddModal; diff --git a/superset-frontend/src/features/roles/RoleListDuplicateModal.test.tsx b/superset-frontend/src/features/roles/RoleListDuplicateModal.test.tsx new file mode 100644 index 00000000000..21c56b9b309 --- /dev/null +++ b/superset-frontend/src/features/roles/RoleListDuplicateModal.test.tsx @@ -0,0 +1,100 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + render, + screen, + fireEvent, + waitFor, +} from 'spec/helpers/testing-library'; +import RoleListDuplicateModal from './RoleListDuplicateModal'; +import { createRole, updateRolePermissions } from './utils'; + +const mockToasts = { + addDangerToast: jest.fn(), + addSuccessToast: jest.fn(), +}; + +jest.mock('./utils'); +const mockCreateRole = jest.mocked(createRole); +const mockUpdateRolePermissions = jest.mocked(updateRolePermissions); + +jest.mock('src/components/MessageToasts/withToasts', () => ({ + useToasts: () => mockToasts, +})); + +describe('RoleListDuplicateModal', () => { + const mockRole = { + id: 1, + name: 'Admin', + permission_ids: [10, 20], + user_ids: [1], + }; + + const mockProps = { + role: mockRole, + show: true, + onHide: jest.fn(), + onSave: jest.fn(), + }; + + it('renders modal with form fields', () => { + render(); + expect( + screen.getByText(`Duplicate role ${mockRole.name}`), + ).toBeInTheDocument(); + expect(screen.getByText('Role Name')).toBeInTheDocument(); + }); + + it('calls onHide when cancel button is clicked', () => { + render(); + fireEvent.click(screen.getByTestId('modal-cancel-button')); + expect(mockProps.onHide).toHaveBeenCalled(); + }); + + it('disables save button when role name is empty', () => { + render(); + expect(screen.getByTestId('form-modal-save-button')).toBeDisabled(); + }); + + it('enables save button when role name is entered', () => { + render(); + fireEvent.change(screen.getByTestId('role-name-input'), { + target: { value: 'New Role' }, + }); + expect(screen.getByTestId('form-modal-save-button')).toBeEnabled(); + }); + + it('calls createRole when save button is clicked', async () => { + mockCreateRole.mockResolvedValue({ json: { id: 2 } } as any); + + render(); + + fireEvent.change(screen.getByTestId('role-name-input'), { + target: { value: 'New Role' }, + }); + + fireEvent.click(screen.getByTestId('form-modal-save-button')); + + await waitFor(() => { + expect(mockCreateRole).toHaveBeenCalledWith('New Role'); + expect(mockUpdateRolePermissions).toHaveBeenCalledWith(2, [10, 20]); + }); + }); +}); diff --git a/superset-frontend/src/features/roles/RoleListDuplicateModal.tsx b/superset-frontend/src/features/roles/RoleListDuplicateModal.tsx new file mode 100644 index 00000000000..f7d6b7a19d0 --- /dev/null +++ b/superset-frontend/src/features/roles/RoleListDuplicateModal.tsx @@ -0,0 +1,69 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { t } from '@superset-ui/core'; +import { RoleObject } from 'src/pages/RolesList'; +import { useToasts } from 'src/components/MessageToasts/withToasts'; +import FormModal from 'src/components/Modal/FormModal'; +import { RoleNameField } from './RoleFormItems'; +import { BaseModalProps, RoleForm } from './types'; +import { createRole, updateRolePermissions } from './utils'; + +export interface RoleListDuplicateModalProps extends BaseModalProps { + role: RoleObject; +} + +function RoleListDuplicateModal({ + role, + show, + onHide, + onSave, +}: RoleListDuplicateModalProps) { + const { name, permission_ids } = role; + const { addDangerToast, addSuccessToast } = useToasts(); + + const handleFormSubmit = async (values: RoleForm) => { + try { + const { json: roleResponse } = await createRole(values.roleName); + + if (permission_ids.length > 0) { + await updateRolePermissions(roleResponse.id, permission_ids); + } + addSuccessToast(t('Role was successfully duplicated!')); + } catch (err) { + addDangerToast(t('Error while duplicating role!')); + throw err; + } + }; + + return ( + + + + ); +} +export default RoleListDuplicateModal; diff --git a/superset-frontend/src/features/roles/RoleListEditModal.test.tsx b/superset-frontend/src/features/roles/RoleListEditModal.test.tsx new file mode 100644 index 00000000000..8e8c17dc0a0 --- /dev/null +++ b/superset-frontend/src/features/roles/RoleListEditModal.test.tsx @@ -0,0 +1,153 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + render, + screen, + fireEvent, + waitFor, +} from 'spec/helpers/testing-library'; +import RoleListEditModal from './RoleListEditModal'; +import { + updateRoleName, + updateRolePermissions, + updateRoleUsers, +} from './utils'; + +const mockToasts = { + addDangerToast: jest.fn(), + addSuccessToast: jest.fn(), +}; + +jest.mock('./utils'); +const mockUpdateRoleName = jest.mocked(updateRoleName); +const mockUpdateRolePermissions = jest.mocked(updateRolePermissions); +const mockUpdateRoleUsers = jest.mocked(updateRoleUsers); + +jest.mock('src/components/MessageToasts/withToasts', () => ({ + useToasts: () => mockToasts, +})); + +describe('RoleListEditModal', () => { + const mockRole = { + id: 1, + name: 'Admin', + permission_ids: [10, 20], + user_ids: [5, 7], + }; + + const mockPermissions = [ + { id: 10, label: 'Permission A', value: 'perm_a' }, + { id: 20, label: 'Permission B', value: 'perm_b' }, + ]; + + const mockUsers = [ + { + id: 5, + firstName: 'John', + lastName: 'Doe', + username: 'johndoe', + email: 'john@example.com', + isActive: true, + roles: [ + { + id: 1, + name: 'Admin', + }, + ], + }, + ]; + + const mockProps = { + role: mockRole, + show: true, + onHide: jest.fn(), + onSave: jest.fn(), + permissions: mockPermissions, + users: mockUsers, + }; + + it('renders modal with correct title and fields', () => { + render(); + expect(screen.getAllByText('Edit Role')[0]).toBeInTheDocument(); + expect(screen.getByText('Role Name')).toBeInTheDocument(); + expect(screen.getByText('Permissions')).toBeInTheDocument(); + expect(screen.getAllByText('Users')[0]).toBeInTheDocument(); + }); + + it('calls onHide when cancel button is clicked', () => { + render(); + fireEvent.click(screen.getByTestId('modal-cancel-button')); + expect(mockProps.onHide).toHaveBeenCalled(); + }); + + it('disables save button when role name is empty', () => { + render(); + fireEvent.change(screen.getByTestId('role-name-input'), { + target: { value: '' }, + }); + expect(screen.getByTestId('form-modal-save-button')).toBeDisabled(); + }); + + it('enables save button when role name is entered', () => { + render(); + fireEvent.change(screen.getByTestId('role-name-input'), { + target: { value: 'Updated Role' }, + }); + expect(screen.getByTestId('form-modal-save-button')).toBeEnabled(); + }); + + it('calls update functions when save button is clicked', async () => { + render(); + + fireEvent.change(screen.getByTestId('role-name-input'), { + target: { value: 'Updated Role' }, + }); + + fireEvent.click(screen.getByTestId('form-modal-save-button')); + + await waitFor(() => { + expect(mockUpdateRoleName).toHaveBeenCalledWith( + mockRole.id, + 'Updated Role', + ); + expect(mockUpdateRolePermissions).toHaveBeenCalledWith( + mockRole.id, + mockRole.permission_ids, + ); + expect(mockUpdateRoleUsers).toHaveBeenCalledWith( + mockRole.id, + mockRole.user_ids, + ); + expect(mockProps.onSave).toHaveBeenCalled(); + }); + }); + + it('switches tabs correctly', () => { + render(); + + const usersTab = screen.getByRole('tab', { name: 'Users' }); + fireEvent.click(usersTab); + + expect(screen.getByText('First Name')).toBeInTheDocument(); + expect(screen.getByText('Last Name')).toBeInTheDocument(); + expect(screen.getByText('User Name')).toBeInTheDocument(); + expect(screen.getByText('Email')).toBeInTheDocument(); + }); +}); diff --git a/superset-frontend/src/features/roles/RoleListEditModal.tsx b/superset-frontend/src/features/roles/RoleListEditModal.tsx new file mode 100644 index 00000000000..e22f950d5e2 --- /dev/null +++ b/superset-frontend/src/features/roles/RoleListEditModal.tsx @@ -0,0 +1,153 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { useState } from 'react'; +import { t } from '@superset-ui/core'; +import Tabs from 'src/components/Tabs'; +import { RoleObject } from 'src/pages/RolesList'; +import TableView, { EmptyWrapperType } from 'src/components/TableView'; +import { + BaseModalProps, + FormattedPermission, + RoleForm, + UserObject, +} from 'src/features/roles/types'; +import { CellProps } from 'react-table'; +import { useToasts } from 'src/components/MessageToasts/withToasts'; +import FormModal from 'src/components/Modal/FormModal'; +import { PermissionsField, RoleNameField, UsersField } from './RoleFormItems'; +import { + updateRoleName, + updateRolePermissions, + updateRoleUsers, +} from './utils'; + +export interface RoleListEditModalProps extends BaseModalProps { + role: RoleObject; + permissions: FormattedPermission[]; + users: UserObject[]; +} + +const roleTabs = { + edit: { + key: 'edit', + name: t('Edit Role'), + }, + users: { + key: 'users', + name: t('Users'), + }, +}; + +const userColumns = [ + { + accessor: 'first_name', + Header: 'First Name', + }, + { + accessor: 'last_name', + Header: 'Last Name', + }, + { + accessor: 'username', + Header: 'User Name', + }, + { + accessor: 'email', + Header: 'Email', + }, + { + accessor: 'active', + Header: 'Is Active?', + Cell: ({ cell }: CellProps<{ active: boolean }>) => + cell.value ? 'Yes' : 'No', + }, +]; + +function RoleListEditModal({ + show, + onHide, + role, + onSave, + permissions, + users, +}: RoleListEditModalProps) { + const { id, name, permission_ids, user_ids } = role; + const [activeTabKey, setActiveTabKey] = useState(roleTabs.edit.key); + const { addDangerToast, addSuccessToast } = useToasts(); + const filteredUsers = users.filter(user => + user?.roles.some(role => role.id === id), + ); + + const handleFormSubmit = async (values: RoleForm) => { + try { + await updateRoleName(id, values.roleName); + await updateRolePermissions(id, values.rolePermissions); + await updateRoleUsers(id, values.roleUsers); + addSuccessToast(t('Role successfully updated!')); + } catch (err) { + addDangerToast(t('Error while updating role!')); + throw err; + } + }; + + const initialValues = { + roleName: name, + rolePermissions: permission_ids, + roleUsers: user_ids, + }; + + return ( + + setActiveTabKey(activeKey)} + > + + <> + + + + + + + + + + + ); +} + +export default RoleListEditModal; diff --git a/superset-frontend/src/features/roles/types.ts b/superset-frontend/src/features/roles/types.ts new file mode 100644 index 00000000000..d97a8dad611 --- /dev/null +++ b/superset-frontend/src/features/roles/types.ts @@ -0,0 +1,66 @@ +/** + * 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. + */ +export type PermissionView = { + name: string; +}; + +export type PermissionResource = { + id: number; + permission: PermissionView; + view_menu: PermissionView; +}; + +export type FormattedPermission = { + label: string; + value: string; + id: number; +}; + +export type RolePermissions = { + id: number; + permission_name: string; + view_menu_name: string; +}; + +export type UserObject = { + id: number; + firstName: string; + lastName: string; + username: string; + email: string; + isActive: boolean; + roles: Array; +}; + +export type RoleInfo = { + id: number; + name: string; +}; + +export type RoleForm = { + roleName: string; + rolePermissions: number[]; + roleUsers: number[]; +}; + +export interface BaseModalProps { + show: boolean; + onHide: () => void; + onSave: () => void; +} diff --git a/superset-frontend/src/features/roles/utils.ts b/superset-frontend/src/features/roles/utils.ts new file mode 100644 index 00000000000..fcee8e64633 --- /dev/null +++ b/superset-frontend/src/features/roles/utils.ts @@ -0,0 +1,47 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SupersetClient } from '@superset-ui/core'; + +export const createRole = async (name: string) => + SupersetClient.post({ + endpoint: '/api/v1/security/roles/', + jsonPayload: { name }, + }); + +export const updateRolePermissions = async ( + roleId: number, + permissionIds: number[], +) => + SupersetClient.post({ + endpoint: `/api/v1/security/roles/${roleId}/permissions`, + jsonPayload: { permission_view_menu_ids: permissionIds }, + }); + +export const updateRoleUsers = async (roleId: number, userIds: number[]) => + SupersetClient.put({ + endpoint: `/api/v1/security/roles/${roleId}/users`, + jsonPayload: { user_ids: userIds }, + }); + +export const updateRoleName = async (roleId: number, name: string) => + SupersetClient.put({ + endpoint: `/api/v1/security/roles/${roleId}`, + jsonPayload: { name }, + }); diff --git a/superset-frontend/src/pages/RolesList/RolesList.test.tsx b/superset-frontend/src/pages/RolesList/RolesList.test.tsx new file mode 100644 index 00000000000..c4c4ee25541 --- /dev/null +++ b/superset-frontend/src/pages/RolesList/RolesList.test.tsx @@ -0,0 +1,206 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import fetchMock from 'fetch-mock'; +import configureStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; +import { + render, + screen, + fireEvent, + waitFor, + act, + within, +} from 'spec/helpers/testing-library'; +import { MemoryRouter } from 'react-router-dom'; +import { QueryParamProvider } from 'use-query-params'; +import RolesList from './index'; + +const mockStore = configureStore([thunk]); +const store = mockStore({}); + +const rolesEndpoint = 'glob:*/security/roles/search/?*'; +const roleEndpoint = 'glob:*/api/v1/security/roles/*'; +const permissionsEndpoint = 'glob:*/api/v1/security/permissions-resources/?*'; +const usersEndpoint = 'glob:*/api/v1/security/users/?*'; + +const mockRoles = [...new Array(3)].map((_, i) => ({ + id: i, + name: `role ${i}`, + user_ids: [i, i + 1], + permission_ids: [i, i + 1, i + 2], +})); + +const mockPermissions = [...new Array(10)].map((_, i) => ({ + id: i, + permission: { name: `permission_${i}` }, + view_menu: { name: `view_menu_${i}` }, +})); + +const mockUsers = [...new Array(5)].map((_, i) => ({ + id: i, + username: `user_${i}`, + first_name: `User`, + last_name: `${i}`, + roles: [ + { + id: 1, + }, + ], +})); + +const mockUser = { + userId: 1, + firstName: 'Admin', + lastName: 'User', + roles: [ + { + id: 1, + name: 'Admin', + }, + ], +}; + +jest.mock('src/dashboard/util/permissionUtils', () => ({ + ...jest.requireActual('src/dashboard/util/permissionUtils'), + isUserAdmin: jest.fn(() => true), +})); + +fetchMock.get(rolesEndpoint, { + ids: [2, 0, 1], + result: mockRoles, + count: 3, +}); + +fetchMock.get(permissionsEndpoint, { + count: mockPermissions.length, + result: mockPermissions, +}); + +fetchMock.get(usersEndpoint, { + count: mockUsers.length, + result: mockUsers, +}); + +fetchMock.delete(roleEndpoint, {}); +fetchMock.put(roleEndpoint, {}); + +describe('RolesList', () => { + async function renderAndWait() { + const mounted = act(async () => { + const mockedProps = {}; + render( + + + {}} + addSuccessToast={() => {}} + {...mockedProps} + /> + + , + { useRedux: true, store }, + ); + }); + return mounted; + } + beforeEach(() => { + fetchMock.resetHistory(); + }); + + it('renders', async () => { + await renderAndWait(); + expect(await screen.findByText('List Roles')).toBeInTheDocument(); + }); + + it('fetches roles on load', async () => { + await renderAndWait(); + await waitFor(() => { + const calls = fetchMock.calls(rolesEndpoint); + expect(calls.length).toBeGreaterThan(0); + }); + }); + + it('fetches permissions and users on load', async () => { + await renderAndWait(); + await waitFor(() => { + const permissionCalls = fetchMock.calls(permissionsEndpoint); + const userCalls = fetchMock.calls(usersEndpoint); + expect(permissionCalls.length).toBeGreaterThan(0); + expect(userCalls.length).toBeGreaterThan(0); + }); + }); + + it('renders filters options', async () => { + await renderAndWait(); + + const typeFilter = screen.queryAllByTestId('filters-select'); + expect(typeFilter).toHaveLength(3); + }); + + it('renders correct list columns', async () => { + await renderAndWait(); + + const table = screen.getByRole('table'); + expect(table).toBeInTheDocument(); + + const nameColumn = await within(table).findByText('Name'); + const actionsColumn = await within(table).findByText('Actions'); + + expect(nameColumn).toBeInTheDocument(); + expect(actionsColumn).toBeInTheDocument(); + }); + + it('opens add modal when Add Role button is clicked', async () => { + await renderAndWait(); + + const addButton = screen.getByTestId('add-role-button'); + fireEvent.click(addButton); + + expect(screen.queryByTestId('Add Role-modal')).toBeInTheDocument(); + }); + + it('open duplicate modal when duplicate button is clicked', async () => { + await renderAndWait(); + + const table = screen.getByRole('table'); + expect(table).toBeInTheDocument(); + const duplicateAction = within(table).queryAllByTestId( + 'role-list-duplicate-action', + )[0]; + expect(duplicateAction).toBeInTheDocument(); + fireEvent.click(duplicateAction); + expect( + screen.queryByTestId('Duplicate role role 0-modal'), + ).toBeInTheDocument(); + }); + + it('open edit modal when edit button is clicked', async () => { + await renderAndWait(); + + const table = screen.getByRole('table'); + expect(table).toBeInTheDocument(); + const editAction = within(table).queryAllByTestId( + 'role-list-edit-action', + )[0]; + expect(editAction).toBeInTheDocument(); + fireEvent.click(editAction); + expect(screen.queryByTestId('Edit Role-modal')).toBeInTheDocument(); + }); +}); diff --git a/superset-frontend/src/pages/RolesList/index.tsx b/superset-frontend/src/pages/RolesList/index.tsx new file mode 100644 index 00000000000..be97427bd20 --- /dev/null +++ b/superset-frontend/src/pages/RolesList/index.tsx @@ -0,0 +1,507 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { css, t, SupersetClient, useTheme } from '@superset-ui/core'; +import { useListViewResource } from 'src/views/CRUD/hooks'; +import RoleListAddModal from 'src/features/roles/RoleListAddModal'; +import RoleListEditModal from 'src/features/roles/RoleListEditModal'; +import RoleListDuplicateModal from 'src/features/roles/RoleListDuplicateModal'; +import withToasts from 'src/components/MessageToasts/withToasts'; +import SubMenu, { SubMenuProps } from 'src/features/home/SubMenu'; +import ActionsBar, { ActionProps } from 'src/components/ListView/ActionsBar'; +import ListView, { + ListViewProps, + Filters, + FilterOperator, +} from 'src/components/ListView'; +import DeleteModal from 'src/components/DeleteModal'; +import ConfirmStatusChange from 'src/components/ConfirmStatusChange'; +import { + FormattedPermission, + PermissionResource, + UserObject, +} from 'src/features/roles/types'; +import { isUserAdmin } from 'src/dashboard/util/permissionUtils'; +import { Icons } from 'src/components/Icons'; + +const PAGE_SIZE = 25; + +interface RolesListProps { + addDangerToast: (msg: string) => void; + addSuccessToast: (msg: string) => void; + user: { + userId: string | number; + firstName: string; + lastName: string; + roles: object; + }; +} + +export type RoleObject = { + id: number; + name: string; + permission_ids: number[]; + users?: Array; + user_ids: number[]; +}; + +enum ModalType { + ADD = 'add', + EDIT = 'edit', + DUPLICATE = 'duplicate', +} + +function RolesList({ addDangerToast, addSuccessToast, user }: RolesListProps) { + const theme = useTheme(); + const { + state: { + loading, + resourceCount: rolesCount, + resourceCollection: roles, + bulkSelectEnabled, + }, + fetchData, + refreshData, + toggleBulkSelect, + } = useListViewResource( + 'security/roles/search', + t('Role'), + addDangerToast, + false, + ); + const [modalState, setModalState] = useState({ + edit: false, + add: false, + duplicate: false, + }); + const openModal = (type: ModalType) => + setModalState(prev => ({ ...prev, [type]: true })); + const closeModal = (type: ModalType) => + setModalState(prev => ({ ...prev, [type]: false })); + + const [currentRole, setCurrentRole] = useState(null); + const [roleCurrentlyDeleting, setRoleCurrentlyDeleting] = + useState(null); + const [permissions, setPermissions] = useState([]); + const [users, setUsers] = useState([]); + const [loadingState, setLoadingState] = useState({ + permissions: true, + users: true, + }); + + const isAdmin = useMemo(() => isUserAdmin(user), [user]); + + const fetchPermissions = useCallback(async () => { + try { + const pageSize = 100; + + const fetchPage = async (pageIndex: number) => { + const response = await SupersetClient.get({ + endpoint: `api/v1/security/permissions-resources/?q={"page_size":${pageSize}, "page":${pageIndex}}`, + }); + + return { + count: response.json.count, + results: response.json.result.map( + ({ permission, view_menu, id }: PermissionResource) => ({ + label: `${permission.name.replace(/_/g, ' ')} ${view_menu.name.replace(/_/g, ' ')}`, + value: `${permission.name}__${view_menu.name}`, + id, + }), + ), + }; + }; + + const initialResponse = await fetchPage(0); + const totalPermissions = initialResponse.count; + const firstPageResults = initialResponse.results; + + if (firstPageResults.length >= totalPermissions) { + setPermissions(firstPageResults); + return; + } + + const totalPages = Math.ceil(totalPermissions / pageSize); + + const permissionRequests = Array.from( + { length: totalPages - 1 }, + (_, i) => fetchPage(i + 1), + ); + const remainingResults = await Promise.all(permissionRequests); + + setPermissions([ + ...firstPageResults, + ...remainingResults.flatMap(res => res.results), + ]); + } catch (err) { + addDangerToast(t('Error while fetching permissions')); + } finally { + setLoadingState(prev => ({ ...prev, permissions: false })); + } + }, []); + + const fetchUsers = useCallback(async () => { + try { + const pageSize = 100; + + const fetchPage = async (pageIndex: number) => { + const response = await SupersetClient.get({ + endpoint: `api/v1/security/users/?q={"page_size":${pageSize},"page":${pageIndex}}`, + }); + return response.json; + }; + + const initialResponse = await fetchPage(0); + const totalUsers = initialResponse.count; + const firstPageResults = initialResponse.result; + + if (pageSize >= totalUsers) { + setUsers(firstPageResults); + return; + } + + const totalPages = Math.ceil(totalUsers / pageSize); + + const userRequests = Array.from({ length: totalPages - 1 }, (_, i) => + fetchPage(i + 1), + ); + const remainingResults = await Promise.all(userRequests); + + setUsers([ + ...firstPageResults, + ...remainingResults.flatMap(res => res.result), + ]); + } catch (err) { + addDangerToast(t('Error while fetching users')); + } finally { + setLoadingState(prev => ({ ...prev, users: false })); + } + }, []); + + useEffect(() => { + fetchPermissions(); + }, [fetchPermissions]); + + useEffect(() => { + fetchUsers(); + }, [fetchUsers]); + + const handleRoleDelete = async ({ id, name }: RoleObject) => { + try { + await SupersetClient.delete({ + endpoint: `/api/v1/security/roles/${id}`, + }); + + refreshData(); + setRoleCurrentlyDeleting(null); + addSuccessToast(t('Deleted role: %s', name)); + } catch (error) { + addDangerToast(t('There was an issue deleting %s', name)); + } + }; + + const handleBulkRolesDelete = async (rolesToDelete: RoleObject[]) => { + const deletedRoleNames: string[] = []; + + await Promise.all( + rolesToDelete.map(async role => { + try { + await SupersetClient.delete({ + endpoint: `api/v1/security/roles/${role.id}`, + }); + + deletedRoleNames.push(role.name); + } catch (error) { + addDangerToast(t('Error deleting %s', role.name)); + } + }), + ); + + if (deletedRoleNames.length > 0) { + addSuccessToast(t('Deleted roles: %s', deletedRoleNames.join(', '))); + } + + refreshData(); + }; + + const initialSort = [{ id: 'name', desc: true }]; + const columns = useMemo( + () => [ + { + accessor: 'name', + Header: t('Name'), + Cell: ({ + row: { + original: { name }, + }, + }: any) => {name}, + }, + { + accessor: 'user_ids', + Header: t('Users'), + hidden: true, + Cell: ({ row: { original } }: any) => original.user_ids.join(', '), + }, + { + accessor: 'permission_ids', + Header: t('Permissions'), + hidden: true, + Cell: ({ row: { original } }: any) => + original.permission_ids.join(', '), + }, + { + Cell: ({ row: { original } }: any) => { + const handleEdit = () => { + setCurrentRole(original); + openModal(ModalType.EDIT); + }; + const handleDelete = () => setRoleCurrentlyDeleting(original); + const handleDuplicate = () => { + setCurrentRole(original); + openModal(ModalType.DUPLICATE); + }; + + const actions = isAdmin + ? [ + { + label: 'role-list-edit-action', + tooltip: t('Edit role'), + placement: 'bottom', + icon: 'EditOutlined', + onClick: handleEdit, + }, + { + label: 'role-list-duplicate-action', + tooltip: t('Duplicate role'), + placement: 'bottom', + icon: 'CopyOutlined', + onClick: handleDuplicate, + }, + { + label: 'role-list-delete-action', + tooltip: t('Delete role'), + placement: 'bottom', + icon: 'DeleteOutlined', + onClick: handleDelete, + }, + ] + : []; + + return ; + }, + Header: t('Actions'), + id: 'actions', + disableSortBy: true, + hidden: !isAdmin, + size: 'xl', + }, + ], + [isAdmin], + ); + + const subMenuButtons: SubMenuProps['buttons'] = []; + + if (isAdmin) { + subMenuButtons.push( + { + name: ( + <> + + {t('Role')} + + ), + buttonStyle: 'primary', + onClick: () => { + openModal(ModalType.ADD); + }, + loading: loadingState.permissions, + 'data-test': 'add-role-button', + }, + { + name: t('Bulk select'), + onClick: toggleBulkSelect, + buttonStyle: 'secondary', + }, + ); + } + + const filters: Filters = useMemo( + () => [ + { + Header: t('Name'), + key: 'name', + id: 'name', + input: 'search', + operator: FilterOperator.Contains, + }, + { + Header: t('Users'), + key: 'user_ids', + id: 'user_ids', + input: 'select', + operator: FilterOperator.RelationOneMany, + unfilteredLabel: t('All'), + selects: users?.map(user => ({ + label: user.username, + value: user.id, + })), + loading: loadingState.users, + }, + { + Header: t('Permissions'), + key: 'permission_ids', + id: 'permission_ids', + input: 'select', + operator: FilterOperator.RelationOneMany, + unfilteredLabel: t('All'), + selects: permissions?.map(permission => ({ + label: permission.label, + value: permission.id, + })), + loading: loadingState.permissions, + }, + ], + [permissions, users, loadingState.users, loadingState.permissions], + ); + + const emptyState = { + title: t('No roles yet'), + image: 'filter-results.svg', + ...(isAdmin && { + buttonAction: () => { + openModal(ModalType.ADD); + }, + buttonText: ( + <> + + {t('Role')} + + ), + }), + }; + + return ( + <> + + closeModal(ModalType.ADD)} + show={modalState.add} + onSave={() => { + refreshData(); + closeModal(ModalType.ADD); + }} + permissions={permissions} + /> + {modalState.edit && currentRole && ( + closeModal(ModalType.EDIT)} + onSave={() => { + refreshData(); + closeModal(ModalType.EDIT); + fetchUsers(); + }} + permissions={permissions} + users={users} + /> + )} + {modalState.duplicate && currentRole && ( + closeModal(ModalType.DUPLICATE)} + onSave={() => { + refreshData(); + closeModal(ModalType.DUPLICATE); + }} + /> + )} + {roleCurrentlyDeleting && ( + { + if (roleCurrentlyDeleting) { + handleRoleDelete(roleCurrentlyDeleting); + } + }} + onHide={() => setRoleCurrentlyDeleting(null)} + open + title={t('Delete Role?')} + /> + )} + + {confirmDelete => { + const bulkActions: ListViewProps['bulkActions'] = isAdmin + ? [ + { + key: 'delete', + name: t('Delete'), + onSelect: confirmDelete, + type: 'danger', + }, + ] + : []; + + return ( + + className="role-list-view" + columns={columns} + count={rolesCount} + data={roles} + fetchData={fetchData} + filters={filters} + initialSort={initialSort} + loading={loading} + pageSize={PAGE_SIZE} + bulkActions={bulkActions} + bulkSelectEnabled={bulkSelectEnabled} + disableBulkSelect={toggleBulkSelect} + addDangerToast={addDangerToast} + addSuccessToast={addSuccessToast} + emptyState={emptyState} + refreshData={refreshData} + /> + ); + }} + + + ); +} + +export default withToasts(RolesList); diff --git a/superset-frontend/src/views/routes.tsx b/superset-frontend/src/views/routes.tsx index 30ce65f0695..a9ade6b75ee 100644 --- a/superset-frontend/src/views/routes.tsx +++ b/superset-frontend/src/views/routes.tsx @@ -18,6 +18,8 @@ */ import { FeatureFlag, isFeatureEnabled } from '@superset-ui/core'; import { lazy, ComponentType, ComponentProps } from 'react'; +import { isUserAdmin } from 'src/dashboard/util/permissionUtils'; +import getBootstrapData from 'src/utils/getBootstrapData'; // not lazy loaded since this is the home page. import Home from 'src/pages/Home'; @@ -123,6 +125,10 @@ const RowLevelSecurityList = lazy( ), ); +const RolesList = lazy( + () => import(/* webpackChunkName: "RolesList" */ 'src/pages/RolesList'), +); + type Routes = { path: string; Component: ComponentType; @@ -238,6 +244,16 @@ if (isFeatureEnabled(FeatureFlag.TaggingSystem)) { }); } +const user = getBootstrapData()?.user; +const isAdmin = isUserAdmin(user); + +if (isAdmin) { + routes.push({ + path: '/roles/', + Component: RolesList, + }); +} + const frontEndRoutes: Record = routes .map(r => r.path) .reduce( diff --git a/superset/config.py b/superset/config.py index d9c35e04fab..78d6567a6a9 100644 --- a/superset/config.py +++ b/superset/config.py @@ -1253,6 +1253,7 @@ ENABLE_CHUNK_ENCODING = False SILENCE_FAB = True FAB_ADD_SECURITY_VIEWS = True +FAB_ADD_SECURITY_API = True FAB_ADD_SECURITY_PERMISSION_VIEW = False FAB_ADD_SECURITY_VIEW_MENU_VIEW = False FAB_ADD_SECURITY_PERMISSION_VIEWS_VIEW = False diff --git a/superset/initialization/__init__.py b/superset/initialization/__init__.py index 1a09d8b24c7..cd3510abbf4 100644 --- a/superset/initialization/__init__.py +++ b/superset/initialization/__init__.py @@ -153,7 +153,7 @@ class SupersetAppInitializer: # pylint: disable=too-many-public-methods from superset.reports.api import ReportScheduleRestApi from superset.reports.logs.api import ReportExecutionLogRestApi from superset.row_level_security.api import RLSRestApi - from superset.security.api import SecurityRestApi + from superset.security.api import RoleRestAPI, SecurityRestApi from superset.sqllab.api import SqlLabRestApi from superset.sqllab.permalink.api import SqlLabPermalinkRestApi from superset.tags.api import TagRestApi @@ -175,6 +175,7 @@ class SupersetAppInitializer: # pylint: disable=too-many-public-methods from superset.views.explore import ExplorePermalinkView, ExploreView from superset.views.log.api import LogRestApi from superset.views.log.views import LogModelView + from superset.views.roles import RolesListView from superset.views.sql_lab.views import ( SavedQueryView, TableSchemaView, @@ -265,6 +266,15 @@ class SupersetAppInitializer: # pylint: disable=too-many-public-methods category_icon="", ) + appbuilder.add_view( + RolesListView, + "List Roles", + label=__("List Roles"), + category="Security", + category_label=__("Security"), + icon="fa-lock", + ) + appbuilder.add_view( DynamicPluginsView, "Plugins", @@ -305,6 +315,7 @@ class SupersetAppInitializer: # pylint: disable=too-many-public-methods appbuilder.add_view_no_menu(TaggedObjectsModelView) appbuilder.add_view_no_menu(TagView) appbuilder.add_view_no_menu(ReportView) + appbuilder.add_view_no_menu(RoleRestAPI) # # Add links diff --git a/superset/security/api.py b/superset/security/api.py index 02bf6b7101e..756f1d7bbd3 100644 --- a/superset/security/api.py +++ b/superset/security/api.py @@ -19,16 +19,21 @@ from typing import Any from flask import current_app, request, Response from flask_appbuilder import expose -from flask_appbuilder.api import safe +from flask_appbuilder.api import rison, safe +from flask_appbuilder.api.schemas import get_list_schema from flask_appbuilder.security.decorators import permission_name, protect +from flask_appbuilder.security.sqla.models import Role from flask_wtf.csrf import generate_csrf from marshmallow import EXCLUDE, fields, post_load, Schema, ValidationError +from sqlalchemy import asc, desc +from sqlalchemy.orm import joinedload from superset.commands.dashboard.embedded.exceptions import ( EmbeddedDashboardNotFoundError, ) +from superset.commands.exceptions import ForbiddenError from superset.exceptions import SupersetGenericErrorException -from superset.extensions import event_logger +from superset.extensions import db, event_logger from superset.security.guest_token import GuestTokenResourceType from superset.views.base_api import BaseSupersetApi, statsd_metrics @@ -76,6 +81,19 @@ class GuestTokenCreateSchema(PermissiveSchema): rls = fields.List(fields.Nested(RlsRuleSchema), required=True) +class RoleResponseSchema(PermissiveSchema): + id = fields.Integer() + name = fields.String() + user_ids = fields.List(fields.Integer()) + permission_ids = fields.List(fields.Integer()) + + +class RolesResponseSchema(PermissiveSchema): + count = fields.Integer() + ids = fields.List(fields.Integer()) + result = fields.List(fields.Nested(RoleResponseSchema)) + + guest_token_create_schema = GuestTokenCreateSchema() @@ -172,3 +190,146 @@ class SecurityRestApi(BaseSupersetApi): return self.response_400(message=error.message) except ValidationError as error: return self.response_400(message=error.messages) + + +class RoleRestAPI(BaseSupersetApi): + """ + APIs for listing roles with usersIds and permissionsIds and possibility to update + users of roles + """ + + resource_name = "security/roles" + allow_browser_login = True + openapi_spec_tag = "Security Roles" + openapi_spec_component_schemas = ( + RoleResponseSchema, + RolesResponseSchema, + ) + + @expose("/search/", methods=["GET"]) + @event_logger.log_this + @protect() + @safe + @rison(get_list_schema) + @statsd_metrics + @permission_name("list_roles") + def get_list(self, **kwargs: Any) -> Response: + """ + List roles, including associated user IDs and permission IDs. + + --- + get: + summary: List roles + description: Fetch a paginated list of roles with user and permission IDs. + parameters: + - in: query + name: q + schema: + type: object + properties: + order_column: + type: string + enum: ["id", "name"] + default: "id" + order_direction: + type: string + enum: ["asc", "desc"] + default: "asc" + page: + type: integer + default: 0 + page_size: + type: integer + default: 10 + filters: + type: array + items: + type: object + properties: + col: + type: string + enum: ["user_ids", "permission_ids", "name"] + value: + type: string + responses: + 200: + description: Successfully retrieved roles + content: + application/json: + schema: RolesResponseSchema + 400: + description: Bad request (invalid input) + content: + application/json: + schema: + type: object + properties: + error: + type: string + 403: + description: Forbidden + content: + application/json: + schema: + type: object + properties: + error: + type: string + """ + try: + args = kwargs.get("rison", {}) + order_column = args.get("order_column", "id") + order_direction = args.get("order_direction", "asc") + + valid_columns = ["id", "name"] + if order_column not in valid_columns: + return self.response_400( + message=f"Invalid order column: {order_column}" + ) + + order_by = getattr(Role, order_column) + order_by = asc(order_by) if order_direction == "asc" else desc(order_by) + + page = args.get("page", 0) + page_size = args.get("page_size", 10) + + query = db.session.query(Role).options( + joinedload(Role.permissions), joinedload(Role.user) + ) + + filters = args.get("filters", []) + filter_dict = {f["col"]: f["value"] for f in filters if "col" in f} + + if "user_ids" in filter_dict: + query = query.filter(Role.user.any(id=filter_dict["user_ids"])) + + if "permission_ids" in filter_dict: + query = query.filter( + Role.permissions.any(id=filter_dict["permission_ids"]) + ) + + if "name" in filter_dict: + query = query.filter(Role.name.ilike(f"%{filter_dict['name']}%")) + + roles = ( + query.order_by(order_by).offset(page * page_size).limit(page_size).all() + ) + + return self.response( + 200, + result=[ + { + "id": role.id, + "name": role.name, + "user_ids": [user.id for user in role.user], + "permission_ids": [perm.id for perm in role.permissions], + } + for role in roles + ], + count=query.count(), + ids=[role.id for role in roles], + ) + except ForbiddenError as e: + return self.response_403(message=str(e)) + except Exception as e: + return self.response_500(message=str(e)) diff --git a/superset/security/manager.py b/superset/security/manager.py index d11e0f3e41c..009e7f314cb 100644 --- a/superset/security/manager.py +++ b/superset/security/manager.py @@ -25,6 +25,7 @@ from typing import Any, Callable, cast, NamedTuple, Optional, TYPE_CHECKING from flask import current_app, Flask, g, Request from flask_appbuilder import Model +from flask_appbuilder.security.sqla.apis import RoleApi from flask_appbuilder.security.sqla.manager import SecurityManager from flask_appbuilder.security.sqla.models import ( assoc_group_role, @@ -40,7 +41,6 @@ from flask_appbuilder.security.sqla.models import ( from flask_appbuilder.security.views import ( PermissionModelView, PermissionViewModelView, - RoleModelView, UserModelView, ViewMenuModelView, ) @@ -126,8 +126,19 @@ class SupersetRoleListWidget(ListWidget): # pylint: disable=too-few-public-meth super().__init__(**kwargs) +class SupersetRoleApi(RoleApi): + """ + Overriding the RoleApi to be able to delete roles with permissions + """ + + def pre_delete(self, item: Model) -> None: + """ + Overriding this method to be able to delete items when they have constraints + """ + item.permissions = [] + + UserModelView.list_widget = SupersetSecurityListWidget -RoleModelView.list_widget = SupersetRoleListWidget PermissionViewModelView.list_widget = SupersetSecurityListWidget PermissionModelView.list_widget = SupersetSecurityListWidget @@ -138,15 +149,10 @@ UserModelView.include_route_methods = RouteMethod.CRUD_SET | { RouteMethod.ACTION_POST, "userinfo", } -RoleModelView.include_route_methods = RouteMethod.CRUD_SET PermissionViewModelView.include_route_methods = {RouteMethod.LIST} PermissionModelView.include_route_methods = {RouteMethod.LIST} ViewMenuModelView.include_route_methods = {RouteMethod.LIST} -RoleModelView.list_columns = ["name"] -RoleModelView.edit_columns = ["name", "permissions", "user"] -RoleModelView.related_views = [] - def freeze_value(value: Any) -> str: """ @@ -218,6 +224,8 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods userstatschartview = None READ_ONLY_MODEL_VIEWS = {"Database", "DynamicPlugin"} + role_api = SupersetRoleApi + USER_MODEL_VIEWS = { "RegisterUserModelView", "UserDBModelView", @@ -251,6 +259,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods "User Registrations", "User's Statistics", # Guarding all AB_ADD_SECURITY_API = True REST APIs + "RoleRestAPI", "Role", "Permission", "PermissionViewMenu", @@ -279,6 +288,8 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods } ADMIN_ONLY_PERMISSIONS = { + "update_roles_users", + "list_roles", "can_update_role", "all_query_access", "can_grant_guest_token", @@ -2749,3 +2760,23 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods return current_app.config["AUTH_ROLE_ADMIN"] in [ role.name for role in self.get_user_roles() ] + + # temporal change to remove the roles view from the security menu, + # after migrating all views to frontend, we will set FAB_ADD_SECURITY_VIEWS = False + def register_views(self) -> None: + super().register_views() + + for view in list(self.appbuilder.baseviews): + if ( + isinstance(view, self.rolemodelview.__class__) + and getattr(view, "route_base", None) == "/roles" + ): + self.appbuilder.baseviews.remove(view) + + security_menu = next( + (m for m in self.appbuilder.menu.get_list() if m.name == "Security"), None + ) + if security_menu: + for item in list(security_menu.childs): + if item.name == "List Roles": + security_menu.childs.remove(item) diff --git a/superset/views/roles.py b/superset/views/roles.py new file mode 100644 index 00000000000..29f293f51e5 --- /dev/null +++ b/superset/views/roles.py @@ -0,0 +1,34 @@ +# 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 flask_appbuilder import permission_name +from flask_appbuilder.api import expose +from flask_appbuilder.security.decorators import has_access + +from superset.superset_typing import FlaskResponse + +from .base import BaseSupersetView + + +class RolesListView(BaseSupersetView): + route_base = "/" + class_permission_name = "security" + + @expose("/roles/") + @has_access + @permission_name("read") + def list(self) -> FlaskResponse: + return super().render_app_template() diff --git a/tests/integration_tests/core_tests.py b/tests/integration_tests/core_tests.py index 6b28ac13bfd..8fe424b1bdf 100644 --- a/tests/integration_tests/core_tests.py +++ b/tests/integration_tests/core_tests.py @@ -161,7 +161,7 @@ class TestCore(SupersetTestCase): role = security_manager.find_role(role_name) view_menus = [p.view_menu.name for p in role.permissions] assert_func("ResetPasswordView", view_menus) - assert_func("RoleModelView", view_menus) + assert_func("RoleRestAPI", view_menus) assert_func("Security", view_menus) assert_func("SQL Lab", view_menus) diff --git a/tests/integration_tests/security/api_tests.py b/tests/integration_tests/security/api_tests.py index 3bb85c4cad4..5667c64ca11 100644 --- a/tests/integration_tests/security/api_tests.py +++ b/tests/integration_tests/security/api_tests.py @@ -219,6 +219,7 @@ class TestSecurityGuestTokenApiTokenValidator(SupersetTestCase): class TestSecurityRolesApi(SupersetTestCase): uri = "api/v1/security/roles/" # noqa: F541 + show_uri = "api/v1/security/roles/search/" @with_config({"FAB_ADD_SECURITY_API": True}) def test_get_security_roles_admin(self): @@ -276,3 +277,19 @@ class TestSecurityRolesApi(SupersetTestCase): content_type="application/json", ) self.assert403(response) + + def test_show_roles_admin(self): + """ + Security API: Admin should be able to show roles with permissions and users + """ + self.login(ADMIN_USERNAME) + response = self.client.get(self.show_uri) + self.assert200(response) + + def test_show_roles_gamma(self): + """ + Security API: Gamma should not be able to show roles + """ + self.login(GAMMA_USERNAME) + response = self.client.get(self.show_uri) + self.assert403(response) diff --git a/tests/unit_tests/security/api_test.py b/tests/unit_tests/security/api_test.py index 73227166c21..faeec96f558 100644 --- a/tests/unit_tests/security/api_test.py +++ b/tests/unit_tests/security/api_test.py @@ -32,4 +32,9 @@ def test_csrf_not_exempt(app_context: None) -> None: "MenuApi", "SecurityApi", "OpenApi", + "PermissionViewMenuApi", + "SupersetRoleApi", + "UserApi", + "PermissionApi", + "ViewMenuApi", } From f5d64176f6c6e85081ba91cae6f6174ded84fd7e Mon Sep 17 00:00:00 2001 From: SBIN2010 <132096459+SBIN2010@users.noreply.github.com> Date: Wed, 2 Apr 2025 19:04:36 +0300 Subject: [PATCH 13/14] fix: fixed Add Metrics to Tree Chart (#29158) (#30679) --- .../plugins/plugin-chart-echarts/src/Tree/controlPanel.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Tree/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Tree/controlPanel.tsx index 34d65a27d31..4d8b0fa978e 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Tree/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Tree/controlPanel.tsx @@ -89,9 +89,9 @@ const controlPanel: ControlPanelConfig = { { name: 'metric', config: { - ...optionalEntity, - type: 'DndMetricSelect', - label: t('Metric'), + ...sharedControls.metric, + clearable: true, + validators: [], description: t('Metric for node values'), }, }, From bc0bf946800a9f31b3929985e8785c2dc4ada028 Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Wed, 2 Apr 2025 09:09:08 -0700 Subject: [PATCH 14/14] chore: bump marshmallow-sqlalchemy to 1.4.0 (#32922) --- requirements/base.in | 7 +++---- requirements/base.txt | 3 +-- requirements/development.txt | 3 +-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/requirements/base.in b/requirements/base.in index 56085768b8f..f23e6ad1cad 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -28,9 +28,8 @@ async_timeout>=4.0.0,<5.0.0 # a bit of attention to bump. apispec>=6.0.0,<6.7.0 -# 1.4.0 appears to use much more memory, where the python test suite runs out of memory -# causing CI to fail. 1.3.0 is the last version that works. -# This is probably related to the changes around PickleType +# 1.4.1 appears to use much more memory, where the python test suite runs out of memory +# causing CI to fail. 1.4.0 is the last version that works. # https://marshmallow-sqlalchemy.readthedocs.io/en/latest/changelog.html#id3 # Opened this issue https://github.com/marshmallow-code/marshmallow-sqlalchemy/issues/665 -marshmallow-sqlalchemy>=1.3.0,<1.4.0 +marshmallow-sqlalchemy>=1.3.0,<1.4.1 diff --git a/requirements/base.txt b/requirements/base.txt index 8fc8fa2966b..7056592a421 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -158,7 +158,6 @@ greenlet==3.1.1 # via # apache-superset (pyproject.toml) # shillelagh - # sqlalchemy gunicorn==23.0.0 # via apache-superset (pyproject.toml) h11==0.14.0 @@ -216,7 +215,7 @@ marshmallow==3.26.1 # via # flask-appbuilder # marshmallow-sqlalchemy -marshmallow-sqlalchemy==1.3.0 +marshmallow-sqlalchemy==1.4.0 # via # -r requirements/base.in # flask-appbuilder diff --git a/requirements/development.txt b/requirements/development.txt index 04daa92de1c..845deb272a5 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -318,7 +318,6 @@ greenlet==3.1.1 # apache-superset # gevent # shillelagh - # sqlalchemy grpcio==1.71.0 # via # apache-superset @@ -435,7 +434,7 @@ marshmallow==3.26.1 # -c requirements/base.txt # flask-appbuilder # marshmallow-sqlalchemy -marshmallow-sqlalchemy==1.3.0 +marshmallow-sqlalchemy==1.4.0 # via # -c requirements/base.txt # flask-appbuilder