From 8ef572a4121ffd2fec6f64d0a053218dbf98308a Mon Sep 17 00:00:00 2001 From: simcha90 <56388545+simcha90@users.noreply.github.com> Date: Thu, 15 Apr 2021 17:43:29 +0300 Subject: [PATCH] refactor(native-filters): update dataMask and ExtraFormData schema (#13983) * refactor: updates usage of `ownFilters` to `ownState` * refactor: update dataMask (final) * lint: fix lint * refactor: revert feat * fix: fix missed chart configuration * add filter set migration * apply new changes * fix migration revision * update migration * fix jest mock * js lint * fix test types * update tests and types * remove append_form_data from tests * fix findExistingFilterSet tests * add migration test Co-authored-by: Ville Brofeldt --- superset-frontend/package-lock.json | 582 +++++++++--------- superset-frontend/package.json | 54 +- .../spec/fixtures/mockNativeFilters.ts | 73 +-- .../dashboard/fixtures/mockNativeFilters.ts | 15 +- .../util/getFormDataWithExtraFilters_spec.ts | 13 +- .../spec/javascripts/filters/utils_spec.ts | 144 ++--- superset-frontend/src/chart/Chart.jsx | 3 + superset-frontend/src/chart/ChartRenderer.jsx | 13 +- superset-frontend/src/chart/chartAction.js | 29 +- .../src/dashboard/actions/hydrate.js | 46 +- .../components/FiltersBadge/selectors.ts | 6 +- .../components/SliceHeader/index.tsx | 3 +- .../components/SliceHeaderControls/index.jsx | 2 +- .../components/gridComponents/Chart.jsx | 9 +- .../CascadeFilters/CascadePopover/index.tsx | 11 +- .../FilterControls/FilterControls.tsx | 6 +- .../FilterBar/FilterControls/FilterValue.tsx | 3 + .../FilterBar/FilterControls/state.ts | 4 +- .../FilterBar/FilterSets/EditSection.test.tsx | 2 +- .../FilterBar/FilterSets/EditSection.tsx | 6 +- .../FilterBar/FilterSets/FilterSetUnit.tsx | 6 +- .../FilterBar/FilterSets/FilterSets.test.tsx | 2 +- .../FilterSets/FiltersHeader.test.tsx | 2 +- .../FilterBar/FilterSets/FiltersHeader.tsx | 6 +- .../FilterBar/FilterSets/index.tsx | 71 +-- .../utils/findExistingFilterSet.test.ts | 38 +- .../FilterBar/FilterSets/utils/index.ts | 31 +- .../FilterBar/Header/Header.test.tsx | 4 +- .../nativeFilters/FilterBar/Header/index.tsx | 16 +- .../nativeFilters/FilterBar/index.tsx | 16 +- .../nativeFilters/FilterBar/state.ts | 14 +- .../FiltersConfigForm/FiltersConfigForm.tsx | 4 +- .../components/nativeFilters/types.ts | 4 +- .../components/nativeFilters/utils.ts | 66 +- .../src/dashboard/containers/Chart.jsx | 4 +- .../src/dashboard/containers/Dashboard.jsx | 5 +- .../src/dashboard/reducers/types.ts | 2 +- .../util/activeAllDashboardFilters.ts | 5 +- .../charts/getFormDataWithExtraFilters.ts | 12 +- .../dashboard/util/charts/getOwnDataCharts.ts | 9 +- superset-frontend/src/dataMask/actions.ts | 34 +- superset-frontend/src/dataMask/reducer.ts | 40 +- superset-frontend/src/dataMask/types.ts | 23 +- .../DataTablesPane/DataTablesPane.test.tsx | 6 +- .../components/DataTablesPane/index.tsx | 5 +- .../explore/components/ExploreChartHeader.jsx | 2 + .../explore/components/ExploreChartPanel.jsx | 6 +- .../components/ExploreViewContainer.jsx | 10 +- .../components/controls/VizTypeControl.jsx | 2 +- .../src/explore/exploreUtils/index.js | 4 + .../components/Range/RangeFilterPlugin.tsx | 28 +- .../src/filters/components/Range/index.ts | 2 +- .../components/Range/transformProps.ts | 11 +- .../src/filters/components/Range/types.ts | 4 +- .../components/Select/SelectFilterPlugin.tsx | 26 +- .../filters/components/Select/buildQuery.ts | 2 +- .../src/filters/components/Select/index.ts | 2 +- .../components/Select/transformProps.ts | 2 + .../src/filters/components/Select/types.ts | 6 +- .../components/Time/TimeFilterPlugin.tsx | 32 +- .../src/filters/components/Time/index.ts | 2 +- .../filters/components/Time/transformProps.ts | 11 +- .../src/filters/components/Time/types.ts | 10 +- .../TimeColumn/TimeColumnFilterPlugin.tsx | 38 +- .../filters/components/TimeColumn/index.ts | 2 +- .../components/TimeColumn/transformProps.ts | 11 +- .../filters/components/TimeColumn/types.ts | 10 +- .../TimeGrain/TimeGrainFilterPlugin.tsx | 26 +- .../src/filters/components/TimeGrain/index.ts | 2 +- .../components/TimeGrain/transformProps.ts | 10 +- .../src/filters/components/TimeGrain/types.ts | 10 +- superset-frontend/src/filters/utils.ts | 51 +- superset/constants.py | 30 + ...ff221_migrate_filter_sets_to_new_format.py | 233 +++++++ superset/utils/core.py | 52 +- tests/migrations/__init__.py | 16 + tests/migrations/fc3a3a8ff221_tests.py | 364 +++++++++++ tests/utils_tests.py | 28 +- 78 files changed, 1536 insertions(+), 958 deletions(-) create mode 100644 superset/migrations/versions/fc3a3a8ff221_migrate_filter_sets_to_new_format.py create mode 100644 tests/migrations/__init__.py create mode 100644 tests/migrations/fc3a3a8ff221_tests.py diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index d1b1d0db86e..b7c4780b296 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -13,34 +13,34 @@ "@babel/runtime-corejs3": "^7.12.5", "@data-ui/sparkline": "^0.0.84", "@emotion/core": "^10.0.35", - "@superset-ui/chart-controls": "^0.17.30", - "@superset-ui/core": "^0.17.30", - "@superset-ui/legacy-plugin-chart-calendar": "^0.17.30", - "@superset-ui/legacy-plugin-chart-chord": "^0.17.30", - "@superset-ui/legacy-plugin-chart-country-map": "^0.17.31", - "@superset-ui/legacy-plugin-chart-event-flow": "^0.17.30", - "@superset-ui/legacy-plugin-chart-force-directed": "^0.17.30", - "@superset-ui/legacy-plugin-chart-heatmap": "^0.17.30", - "@superset-ui/legacy-plugin-chart-histogram": "^0.17.30", - "@superset-ui/legacy-plugin-chart-horizon": "^0.17.30", - "@superset-ui/legacy-plugin-chart-map-box": "^0.17.30", - "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.30", - "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.30", - "@superset-ui/legacy-plugin-chart-partition": "^0.17.30", - "@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.30", - "@superset-ui/legacy-plugin-chart-rose": "^0.17.30", - "@superset-ui/legacy-plugin-chart-sankey": "^0.17.30", - "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.30", - "@superset-ui/legacy-plugin-chart-sunburst": "^0.17.30", - "@superset-ui/legacy-plugin-chart-treemap": "^0.17.30", - "@superset-ui/legacy-plugin-chart-world-map": "^0.17.30", - "@superset-ui/legacy-preset-chart-big-number": "^0.17.30", + "@superset-ui/chart-controls": "^0.17.32", + "@superset-ui/core": "^0.17.32", + "@superset-ui/legacy-plugin-chart-calendar": "^0.17.32", + "@superset-ui/legacy-plugin-chart-chord": "^0.17.32", + "@superset-ui/legacy-plugin-chart-country-map": "^0.17.32", + "@superset-ui/legacy-plugin-chart-event-flow": "^0.17.32", + "@superset-ui/legacy-plugin-chart-force-directed": "^0.17.32", + "@superset-ui/legacy-plugin-chart-heatmap": "^0.17.32", + "@superset-ui/legacy-plugin-chart-histogram": "^0.17.32", + "@superset-ui/legacy-plugin-chart-horizon": "^0.17.32", + "@superset-ui/legacy-plugin-chart-map-box": "^0.17.32", + "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.32", + "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.32", + "@superset-ui/legacy-plugin-chart-partition": "^0.17.32", + "@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.32", + "@superset-ui/legacy-plugin-chart-rose": "^0.17.32", + "@superset-ui/legacy-plugin-chart-sankey": "^0.17.32", + "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.32", + "@superset-ui/legacy-plugin-chart-sunburst": "^0.17.32", + "@superset-ui/legacy-plugin-chart-treemap": "^0.17.32", + "@superset-ui/legacy-plugin-chart-world-map": "^0.17.32", + "@superset-ui/legacy-preset-chart-big-number": "^0.17.32", "@superset-ui/legacy-preset-chart-deckgl": "^0.4.6", - "@superset-ui/legacy-preset-chart-nvd3": "^0.17.30", - "@superset-ui/plugin-chart-echarts": "^0.17.30", - "@superset-ui/plugin-chart-table": "^0.17.30", - "@superset-ui/plugin-chart-word-cloud": "^0.17.30", - "@superset-ui/preset-chart-xy": "^0.17.30", + "@superset-ui/legacy-preset-chart-nvd3": "^0.17.32", + "@superset-ui/plugin-chart-echarts": "^0.17.32", + "@superset-ui/plugin-chart-table": "^0.17.32", + "@superset-ui/plugin-chart-word-cloud": "^0.17.32", + "@superset-ui/preset-chart-xy": "^0.17.32", "@vx/responsive": "^0.0.195", "abortcontroller-polyfill": "^1.1.9", "antd": "^4.9.4", @@ -15925,11 +15925,11 @@ } }, "node_modules/@superset-ui/chart-controls": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.17.30.tgz", - "integrity": "sha512-knwGXvDBPyKu4QN7Zdi8l/deT8hawnCJmtcYsQxJt7+55CrmDJf3mauOpPt7xezvEwFknmSblcgfm1Q3gnIMyw==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.17.32.tgz", + "integrity": "sha512-seHKefTTiT6IHDv/OaS83J0ZVw4Apq/c9kpK97SW/GLrZWQUNRc9gB9b7EBiBUoxeKszw6nQMzW/VJiZTrolaw==", "dependencies": { - "@superset-ui/core": "0.17.30", + "@superset-ui/core": "0.17.32", "lodash": "^4.17.15", "prop-types": "^15.7.2" }, @@ -15943,9 +15943,9 @@ } }, "node_modules/@superset-ui/core": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.17.30.tgz", - "integrity": "sha512-wYCHKmJF/kOiKbDZ6XlDCPTh3dHQ9nTMmulOpWBTtsLbcCMYhz5mdxNeIbSxrf7B+ZOWET/FgwT9mjwcLRZWcg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.17.32.tgz", + "integrity": "sha512-uaSzlZmolqV3XVDVYYy7sVOB76gMkUrKnc+Wng8BDejzK7Cbr5VicbnNNDNvQuh9llfIop2ptNYAtBcdeqPnig==", "dependencies": { "@babel/runtime": "^7.1.2", "@emotion/core": "^10.0.28", @@ -16023,12 +16023,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-calendar": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.17.30.tgz", - "integrity": "sha512-bvFMdA+ZXuTqFlaGdUWA614Pz6fxwNrI33iWyxKMTdmnxx6sxzwUTaACb0dIEtxpLASAVoiMp/aV94vjfHm0GA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.17.32.tgz", + "integrity": "sha512-rFRqw0A+YjGiRwd8c4RFY/SIyC5jY44jdPnJrCi3vOovLRFY+YQ101AW+beA+o7gLBjyLYqZOc8DZK9eEcJIMA==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-array": "^2.0.3", "d3-selection": "^1.4.0", "d3-tip": "^0.9.1", @@ -16047,24 +16047,24 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-chord": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.17.30.tgz", - "integrity": "sha512-vz5TFGOIVylZ+BlUD6iFlNmQnIsdT3ttpCgVNe2sJQ6dXpYXM8HF/Fm0rQGDarDK0kyVN/6H6HpRlQ6iE3PJNQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.17.32.tgz", + "integrity": "sha512-Rc1gdRCu985vJQrJDBQOKl6uWbbqq3Hbt9ZXuLJPkjbu+McTIrsFF9ihxsrLjYlgFtSJWZB3AX/CtkLTXinMKw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.6.2", "react": "^16.13.1" } }, "node_modules/@superset-ui/legacy-plugin-chart-country-map": { - "version": "0.17.31", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.17.31.tgz", - "integrity": "sha512-Mga075bvVIz0HIUJ4UpHr/RTQOsbQH9Ra59rZ1SfCuKf1ZoobYRrm3KrUv5VqdKjHS4vEKNXiJwcdQveWHgnNA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.17.32.tgz", + "integrity": "sha512-0Vv2xAdGnwL76XuKeAbDeh7fXJbbY1XwY6Jrke1gvlOgpx6d/i0PeQrwHduUPWvCo1cDwPv8sIP7HYPh5XwBCw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-array": "^2.0.3", "prop-types": "^15.6.2" @@ -16079,13 +16079,13 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-event-flow": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.17.30.tgz", - "integrity": "sha512-RHUPYeROon4gY90I1WAuEnx71qxiLsMgvEcdKoiORDOsxYn4nrVKlhrQ1xa+9fhZHcBg6a4CUwfwX27BUDxhrQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.17.32.tgz", + "integrity": "sha512-s4DrFW4nGZEMCRgs9Ln9OmoTjDC2BlZ3p+XEjp3+QlE+AfcesH0ZxcbgImo03hIBLemby7MkSW3aORA+1+drMA==", "dependencies": { "@data-ui/event-flow": "^0.0.84", - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "prop-types": "^15.6.2" }, "peerDependencies": { @@ -16093,12 +16093,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-force-directed": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.17.30.tgz", - "integrity": "sha512-n/W9IbiBrDXPq2Ggi/bBNRJZYEpKijZCR4kfJpl8XzBEwnKY/aBHYXO4zi6LuRR6yvwe7PchguAjm30kP4XoYg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.17.32.tgz", + "integrity": "sha512-M6iZM7P80b3iib1oCblLeqO424ZHOeCInTSS8/vMc/BRPjX0savYM4IBQnVJibkTA6CGEsgAqbDYlPQiqF3TwQ==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.7.2" }, @@ -16107,12 +16107,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-heatmap": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.17.30.tgz", - "integrity": "sha512-cLOL9KkoCAdQUsURkcbWCI+olqrI0NJaSIOVMB1utSTyz4uHHQuMDmaMMm2Gs4IIO0sPOpZ3U0uBBtF4H6r3dg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.17.32.tgz", + "integrity": "sha512-kdKD6FEytz5ByPjdmXpjCqqZ9H70HPlCautoK7RGyXVLqqzw4qiz2/8x1tk2aEaqWN3eFWnBKOoHOer7vjw30w==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-svg-legend": "^1.x", "d3-tip": "^0.9.1", @@ -16120,14 +16120,14 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-histogram": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.17.30.tgz", - "integrity": "sha512-WZiHWWNGw/UaDKPSBbp06FXPrUC86qyDLcb0Qfknc9LNKYAfXkncYP+kZVu8vjJ5jMBsMcitONF7LVXdZMt8YQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.17.32.tgz", + "integrity": "sha512-f/vj/5I3/3ne4RITQEDiJxmuojFoj56XVd/waG+2gz7rWmKoMlQlbW3Oj1AMwpiKMWZYfUQ//twgDRz/skbSPg==", "dependencies": { "@data-ui/histogram": "^0.0.84", "@data-ui/theme": "^0.0.84", - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@vx/legend": "^0.0.198", "@vx/responsive": "^0.0.199", "@vx/scale": "^0.0.197", @@ -16199,12 +16199,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-horizon": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.17.30.tgz", - "integrity": "sha512-KS51qdvTBglTqPVs2zXE41f3qqX3EghYtqazrLuNmNAosMrg4vHEFGlxZj8uk/uN4xa8QD4RUA6E8zg7fWZA9w==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.17.32.tgz", + "integrity": "sha512-lfD+a+WPlGxIzqWiAjLVW4wqTUheZ4jF8SneZetP0KT2TT2fdkmoxu3qem87nX+YpyAOJYYVh3zuDx7vqaoV5g==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-array": "^2.0.3", "d3-scale": "^3.0.1", "prop-types": "^15.6.2" @@ -16234,12 +16234,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-map-box": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.17.30.tgz", - "integrity": "sha512-e2svCKH/95biqDS/c1yABtFmiDw0s/leSo7X6hxqSVDD7W5m0MSejA5+zpfOAQZTimnehwnr80zyUoWuga24zA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.17.32.tgz", + "integrity": "sha512-QhJbNMFUpYPwv6bPTCq5KkYecybkpSju9RhtbSKaqGyA6dNezhPF3WIWyR5oSW5Wm6E7EoTKRi2UDPJsBOO09g==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "immutable": "^3.8.2", "mapbox-gl": "^0.53.0", "prop-types": "^15.6.2", @@ -16260,12 +16260,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-paired-t-test": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.17.30.tgz", - "integrity": "sha512-KCi9mhvij0reG17YByti40PiWBkxzZzxh+77q7FUxEDL7IRFBpx35wQVrFu0GQbxLYe6F7tZAI66aiZXzBq7fA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.17.32.tgz", + "integrity": "sha512-O2dwG4/VuS3oHCYE3cpX52sqduDRLTMZIelmI4L0FcDO5Z1hpfxhFDFIfcqpk6KzxJnuwoCJ88Sf42zSS83HCw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "distributions": "^1.0.0", "prop-types": "^15.6.2", "reactable": "^1.1.0" @@ -16275,12 +16275,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-parallel-coordinates": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.17.30.tgz", - "integrity": "sha512-ae9iIYYCSrGYCMbMKZ51wolUl40Iq2LXtNrZAPAYEPtgrzbZuDtit6Q5+fiq/HsMHZhj2wM3NKfHl+t/1dO9fA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.17.32.tgz", + "integrity": "sha512-grHQYcOTzC+FQ3NGs87sPl9abCaradsucE0AUiG9ZM3/WX7M5SrTJ5fQgPmBejUVMzXGeJVcW8CvUYkL4wTsfg==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.7.2" }, @@ -16289,12 +16289,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-partition": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.17.30.tgz", - "integrity": "sha512-SterH2xDmHvYtYxyR9qIpZ08JbDtUWWXdw9F1lJvuaxEBbG1plHC9ChaxA6cQZxEUN7RtnBBcZbCJ5iaObIbBA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.17.32.tgz", + "integrity": "sha512-mn8gzhggUpeNGMBLOK0tEfalYZdrDUdHxKCrAkIMheS/L+/pZhb7gs2DPY7R746SvsT9ZRr5Q3IBv+Nv5XSDig==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-hierarchy": "^1.1.8", "prop-types": "^15.6.2" @@ -16304,24 +16304,24 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-pivot-table": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.17.30.tgz", - "integrity": "sha512-prMaGetBzEA0G+hu19N8ILArqlWOC1H5NIEDgLDdPHUGTNiNGpLq3l3pr2H2GFWCyrZHPKPSlZdVeczHFPfpuA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.17.32.tgz", + "integrity": "sha512-fQ6+Qg3Mj9g3aLvNWn+C9SW1x7kQglotoc1cl0/L5SfVswsW4eqgZSWkLlvYGU3JlL5r+durIqD97gt5cmiB7g==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "datatables.net-bs": "^1.10.15", "prop-types": "^15.6.2" } }, "node_modules/@superset-ui/legacy-plugin-chart-rose": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.17.30.tgz", - "integrity": "sha512-jaf9rqIWNVYfYAuiZBLzv6NFNA8jU3VtkANL/J/IStL9Z7ell3azVBhrvMdvRXb5Q3mvKNs5XS+VVEs+fHuqKA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.17.32.tgz", + "integrity": "sha512-HjzYOZP++kWVVclNYOtq2To6EAn6j+XZjllgd9EGn7TuF+e1DBHjHTOVyTWyJCMSMR1451kpjQouzZLzTIwQvw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "nvd3": "1.8.6", "prop-types": "^15.6.2" @@ -16331,12 +16331,12 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-sankey": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.17.30.tgz", - "integrity": "sha512-sb8SY/slUH1y7pILxaxuclYi6Bnury/6JF9PvBTBuY4XarOC44vuo8u6ko1QFBWpDP0fG79KgxRT+B8y+cPLZQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.17.32.tgz", + "integrity": "sha512-xaX/b1aRgh5XoyjFIKOJkj/8WaI9oAP0tbBVLFaxzGhZzQvWwxOluTA1QqeR6ndKeLgbX8LZxc5xXd3kLLsBLA==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-sankey": "^0.4.2", "prop-types": "^15.6.2" @@ -16346,47 +16346,47 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-sankey-loop": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.17.30.tgz", - "integrity": "sha512-1z1aNcyNLE3C/R3aCkDPubwn2Kp3I63FVOmAwvVHt1vtRqvd0X6V3B2IGiXD8mQUpcMXGDKXhdHWEZzI5pLTYg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.17.32.tgz", + "integrity": "sha512-7R+1+MJ5Sel8z8dklMmU45/RktOkpmfLcE9zu7S7/vG3aI9ORrp5HFSl6PfzmQ71Q/uHww2m+18LAGMkFsxxTA==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-sankey-diagram": "^0.7.3", "d3-selection": "^1.4.0", "prop-types": "^15.6.2" } }, "node_modules/@superset-ui/legacy-plugin-chart-sunburst": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.17.30.tgz", - "integrity": "sha512-VxDNSYwipIWp6s0CguKKtp7yAx9CfKo4QQky3209gMfENmFCyf0ZABPZW65OtXiEaUJ5gbnEo9o+hKM1BjQh8w==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.17.32.tgz", + "integrity": "sha512-zqg3uB/1n+x++PTeiKo2w6qCmTR2sLqWkTR8Towlg1wLNOJHSNbkxDa9CDx6TfKciUCGlJc/FtS4G9lLpwN5Yg==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.6.2" } }, "node_modules/@superset-ui/legacy-plugin-chart-treemap": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.17.30.tgz", - "integrity": "sha512-nVBRsOcBbU6NpCPyE0nz1BKyM4kQXhw3J7iBNLMG1apxkFpLmggd+op/eI2bK4hR7FdszOmkkgtULangI/cDlA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.17.32.tgz", + "integrity": "sha512-4g3plLNN/QNi2Zs9MkOTSdZtucaCFE5a/nKsuGKa0ZBc8Nm8xAS0u32fA/sBgdoV25uJTLedGfJMzgvqZ43epw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-hierarchy": "^1.1.8", "d3-selection": "^1.4.0", "prop-types": "^15.6.2" } }, "node_modules/@superset-ui/legacy-plugin-chart-world-map": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.17.30.tgz", - "integrity": "sha512-iHKn8BCt4beqqJvZKZ6DLOQSoEvNCUlOCcdH+zRT3u8dGg7pdVXyuaLM0Nm/pKp1zKCeRj2gmckVyAHjsnp98g==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.17.32.tgz", + "integrity": "sha512-0/fLJ9kxLD8/R5eNCDLCCDuzwsX6m4r7N6Iqdliqk5HM9mBOJAhB/gpeE1jNoFvCwl4ntxnuToP6QN6xrjw0Qg==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-array": "^2.4.0", "d3-color": "^1.4.1", @@ -16411,13 +16411,13 @@ "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" }, "node_modules/@superset-ui/legacy-preset-chart-big-number": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.17.30.tgz", - "integrity": "sha512-uuEwL8DRzDTJQteEoYS736dgqsNsnJi6MYCSTGgwXQ1DkT42nz0ciRDvGiDmGYFVILqEXfbfegJ7vZTrHlgNKA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.17.32.tgz", + "integrity": "sha512-mSKwdG88WhySuqpmrCHa/R+cDfwqph/Vee4RsOrGzBFS/fPrlCotUbcLaoZnKJtso4cIIynSJRn29CB8bvd82A==", "dependencies": { "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@types/d3-color": "^1.2.2", "@types/shortid": "^0.0.29", "d3-color": "^1.2.3", @@ -16453,13 +16453,13 @@ } }, "node_modules/@superset-ui/legacy-preset-chart-nvd3": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.17.30.tgz", - "integrity": "sha512-9BoMNg+JH5cXt6N/6hYdjc5do3jLeFcLIlKULtZQo3ndyfOrY7mL38fLIeYAG1VRXA5vQUOmIDLgr70s6T9eSA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.17.32.tgz", + "integrity": "sha512-P6HeR8/ik2edSSm+Sxe9TTaCcomb0fAtp7RKOB8lXIGU6oksdvxFcxCB9mbVlnn2ChH9nlUOw5JKo9v3r50w3w==", "dependencies": { "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-tip": "^0.9.1", "dompurify": "^2.0.6", @@ -16476,12 +16476,12 @@ } }, "node_modules/@superset-ui/plugin-chart-echarts": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.17.30.tgz", - "integrity": "sha512-Fley3EYDUnbShs1sj0D2Y046mRVe733TcGaNMC1mAJPd0+251hmqQ5+LlG8mq+4oMiCw62hMt8PZ/COBZnB9IQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.17.32.tgz", + "integrity": "sha512-/ifeCsqh1/iSBMv41JWl4HaNUlLmKCVIQyDMTsdJr4MsvYQG1HHtDUpa3eHnMS7RlrLVZLVmlr59LS6S4Dcoww==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@types/mathjs": "^6.0.7", "d3-array": "^1.2.0", "echarts": "^5.0.2", @@ -16492,13 +16492,13 @@ } }, "node_modules/@superset-ui/plugin-chart-table": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.17.30.tgz", - "integrity": "sha512-uRsTfPdC+ApumaJ+Kut566/Wv6AI1YbO8b68w9XkFEx4xrvQ6fNGwzeNetYaNOulXksg09C51MqrG3bvo+yePw==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.17.32.tgz", + "integrity": "sha512-q3REacsT0oDTnM2ZaFy82Gc+ys62fhEBgZl/dkdxK/3To5jZ29++yU+ctYxdYuf0zFqGZltYRh9Ges03MwaS+g==", "dependencies": { "@emotion/core": "^10.0.28", - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@types/d3-array": "^2.9.0", "@types/react-table": "^7.0.29", "d3-array": "^2.4.0", @@ -16524,12 +16524,12 @@ } }, "node_modules/@superset-ui/plugin-chart-word-cloud": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.17.30.tgz", - "integrity": "sha512-h+QAK1e2wqSF+Ldpyz1cGsIGxfMN1gJmn1PPUDGX0cEt5/QTO/xiWuxrRGEc9KzOOhk8gHKgKQn9iADk+Wh/Dg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.17.32.tgz", + "integrity": "sha512-G+ghQ5PW3rJMnbxnoo4yfG8HXJ5N23iFXfH/x4AqWgL0RyTJry/dsyvCpKMQNzvsB2M0dQbY5sy4/uG5JIo7Zg==", "dependencies": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@types/d3-cloud": "^1.2.1", "@types/d3-scale": "^2.0.2", "d3-cloud": "^1.2.5", @@ -16563,14 +16563,14 @@ } }, "node_modules/@superset-ui/preset-chart-xy": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.17.30.tgz", - "integrity": "sha512-/HIP5ue0IAlolvuRqs727/0dKKLSN4MypuGELoRuiFZVeSYWnrCHh5nOqYDtVa79efPTx5c6shn4ocjphi2Nag==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.17.32.tgz", + "integrity": "sha512-tViT1tIBxzhpAbOZI465mF46M5UQ42qA89mGOdzqJLTVe5aggev3oNN8vP35rVsHPz9M96vN3fe8/G1aEqGi/g==", "dependencies": { "@data-ui/theme": "^0.0.84", "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@vx/axis": "^0.0.198", "@vx/legend": "^0.0.198", "@vx/scale": "^0.0.197", @@ -71891,19 +71891,19 @@ } }, "@superset-ui/chart-controls": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.17.30.tgz", - "integrity": "sha512-knwGXvDBPyKu4QN7Zdi8l/deT8hawnCJmtcYsQxJt7+55CrmDJf3mauOpPt7xezvEwFknmSblcgfm1Q3gnIMyw==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.17.32.tgz", + "integrity": "sha512-seHKefTTiT6IHDv/OaS83J0ZVw4Apq/c9kpK97SW/GLrZWQUNRc9gB9b7EBiBUoxeKszw6nQMzW/VJiZTrolaw==", "requires": { - "@superset-ui/core": "0.17.30", + "@superset-ui/core": "0.17.32", "lodash": "^4.17.15", "prop-types": "^15.7.2" } }, "@superset-ui/core": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.17.30.tgz", - "integrity": "sha512-wYCHKmJF/kOiKbDZ6XlDCPTh3dHQ9nTMmulOpWBTtsLbcCMYhz5mdxNeIbSxrf7B+ZOWET/FgwT9mjwcLRZWcg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.17.32.tgz", + "integrity": "sha512-uaSzlZmolqV3XVDVYYy7sVOB76gMkUrKnc+Wng8BDejzK7Cbr5VicbnNNDNvQuh9llfIop2ptNYAtBcdeqPnig==", "requires": { "@babel/runtime": "^7.1.2", "@emotion/core": "^10.0.28", @@ -71972,12 +71972,12 @@ } }, "@superset-ui/legacy-plugin-chart-calendar": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.17.30.tgz", - "integrity": "sha512-bvFMdA+ZXuTqFlaGdUWA614Pz6fxwNrI33iWyxKMTdmnxx6sxzwUTaACb0dIEtxpLASAVoiMp/aV94vjfHm0GA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.17.32.tgz", + "integrity": "sha512-rFRqw0A+YjGiRwd8c4RFY/SIyC5jY44jdPnJrCi3vOovLRFY+YQ101AW+beA+o7gLBjyLYqZOc8DZK9eEcJIMA==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-array": "^2.0.3", "d3-selection": "^1.4.0", "d3-tip": "^0.9.1", @@ -71995,24 +71995,24 @@ } }, "@superset-ui/legacy-plugin-chart-chord": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.17.30.tgz", - "integrity": "sha512-vz5TFGOIVylZ+BlUD6iFlNmQnIsdT3ttpCgVNe2sJQ6dXpYXM8HF/Fm0rQGDarDK0kyVN/6H6HpRlQ6iE3PJNQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.17.32.tgz", + "integrity": "sha512-Rc1gdRCu985vJQrJDBQOKl6uWbbqq3Hbt9ZXuLJPkjbu+McTIrsFF9ihxsrLjYlgFtSJWZB3AX/CtkLTXinMKw==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.6.2", "react": "^16.13.1" } }, "@superset-ui/legacy-plugin-chart-country-map": { - "version": "0.17.31", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.17.31.tgz", - "integrity": "sha512-Mga075bvVIz0HIUJ4UpHr/RTQOsbQH9Ra59rZ1SfCuKf1ZoobYRrm3KrUv5VqdKjHS4vEKNXiJwcdQveWHgnNA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.17.32.tgz", + "integrity": "sha512-0Vv2xAdGnwL76XuKeAbDeh7fXJbbY1XwY6Jrke1gvlOgpx6d/i0PeQrwHduUPWvCo1cDwPv8sIP7HYPh5XwBCw==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-array": "^2.0.3", "prop-types": "^15.6.2" @@ -72029,34 +72029,34 @@ } }, "@superset-ui/legacy-plugin-chart-event-flow": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.17.30.tgz", - "integrity": "sha512-RHUPYeROon4gY90I1WAuEnx71qxiLsMgvEcdKoiORDOsxYn4nrVKlhrQ1xa+9fhZHcBg6a4CUwfwX27BUDxhrQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.17.32.tgz", + "integrity": "sha512-s4DrFW4nGZEMCRgs9Ln9OmoTjDC2BlZ3p+XEjp3+QlE+AfcesH0ZxcbgImo03hIBLemby7MkSW3aORA+1+drMA==", "requires": { "@data-ui/event-flow": "^0.0.84", - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-force-directed": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.17.30.tgz", - "integrity": "sha512-n/W9IbiBrDXPq2Ggi/bBNRJZYEpKijZCR4kfJpl8XzBEwnKY/aBHYXO4zi6LuRR6yvwe7PchguAjm30kP4XoYg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.17.32.tgz", + "integrity": "sha512-M6iZM7P80b3iib1oCblLeqO424ZHOeCInTSS8/vMc/BRPjX0savYM4IBQnVJibkTA6CGEsgAqbDYlPQiqF3TwQ==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.7.2" } }, "@superset-ui/legacy-plugin-chart-heatmap": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.17.30.tgz", - "integrity": "sha512-cLOL9KkoCAdQUsURkcbWCI+olqrI0NJaSIOVMB1utSTyz4uHHQuMDmaMMm2Gs4IIO0sPOpZ3U0uBBtF4H6r3dg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.17.32.tgz", + "integrity": "sha512-kdKD6FEytz5ByPjdmXpjCqqZ9H70HPlCautoK7RGyXVLqqzw4qiz2/8x1tk2aEaqWN3eFWnBKOoHOer7vjw30w==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-svg-legend": "^1.x", "d3-tip": "^0.9.1", @@ -72064,14 +72064,14 @@ } }, "@superset-ui/legacy-plugin-chart-histogram": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.17.30.tgz", - "integrity": "sha512-WZiHWWNGw/UaDKPSBbp06FXPrUC86qyDLcb0Qfknc9LNKYAfXkncYP+kZVu8vjJ5jMBsMcitONF7LVXdZMt8YQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.17.32.tgz", + "integrity": "sha512-f/vj/5I3/3ne4RITQEDiJxmuojFoj56XVd/waG+2gz7rWmKoMlQlbW3Oj1AMwpiKMWZYfUQ//twgDRz/skbSPg==", "requires": { "@data-ui/histogram": "^0.0.84", "@data-ui/theme": "^0.0.84", - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@vx/legend": "^0.0.198", "@vx/responsive": "^0.0.199", "@vx/scale": "^0.0.197", @@ -72139,12 +72139,12 @@ } }, "@superset-ui/legacy-plugin-chart-horizon": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.17.30.tgz", - "integrity": "sha512-KS51qdvTBglTqPVs2zXE41f3qqX3EghYtqazrLuNmNAosMrg4vHEFGlxZj8uk/uN4xa8QD4RUA6E8zg7fWZA9w==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.17.32.tgz", + "integrity": "sha512-lfD+a+WPlGxIzqWiAjLVW4wqTUheZ4jF8SneZetP0KT2TT2fdkmoxu3qem87nX+YpyAOJYYVh3zuDx7vqaoV5g==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-array": "^2.0.3", "d3-scale": "^3.0.1", "prop-types": "^15.6.2" @@ -72173,12 +72173,12 @@ } }, "@superset-ui/legacy-plugin-chart-map-box": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.17.30.tgz", - "integrity": "sha512-e2svCKH/95biqDS/c1yABtFmiDw0s/leSo7X6hxqSVDD7W5m0MSejA5+zpfOAQZTimnehwnr80zyUoWuga24zA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.17.32.tgz", + "integrity": "sha512-QhJbNMFUpYPwv6bPTCq5KkYecybkpSju9RhtbSKaqGyA6dNezhPF3WIWyR5oSW5Wm6E7EoTKRi2UDPJsBOO09g==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "immutable": "^3.8.2", "mapbox-gl": "^0.53.0", "prop-types": "^15.6.2", @@ -72195,118 +72195,118 @@ } }, "@superset-ui/legacy-plugin-chart-paired-t-test": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.17.30.tgz", - "integrity": "sha512-KCi9mhvij0reG17YByti40PiWBkxzZzxh+77q7FUxEDL7IRFBpx35wQVrFu0GQbxLYe6F7tZAI66aiZXzBq7fA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.17.32.tgz", + "integrity": "sha512-O2dwG4/VuS3oHCYE3cpX52sqduDRLTMZIelmI4L0FcDO5Z1hpfxhFDFIfcqpk6KzxJnuwoCJ88Sf42zSS83HCw==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "distributions": "^1.0.0", "prop-types": "^15.6.2", "reactable": "^1.1.0" } }, "@superset-ui/legacy-plugin-chart-parallel-coordinates": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.17.30.tgz", - "integrity": "sha512-ae9iIYYCSrGYCMbMKZ51wolUl40Iq2LXtNrZAPAYEPtgrzbZuDtit6Q5+fiq/HsMHZhj2wM3NKfHl+t/1dO9fA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.17.32.tgz", + "integrity": "sha512-grHQYcOTzC+FQ3NGs87sPl9abCaradsucE0AUiG9ZM3/WX7M5SrTJ5fQgPmBejUVMzXGeJVcW8CvUYkL4wTsfg==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.7.2" } }, "@superset-ui/legacy-plugin-chart-partition": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.17.30.tgz", - "integrity": "sha512-SterH2xDmHvYtYxyR9qIpZ08JbDtUWWXdw9F1lJvuaxEBbG1plHC9ChaxA6cQZxEUN7RtnBBcZbCJ5iaObIbBA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.17.32.tgz", + "integrity": "sha512-mn8gzhggUpeNGMBLOK0tEfalYZdrDUdHxKCrAkIMheS/L+/pZhb7gs2DPY7R746SvsT9ZRr5Q3IBv+Nv5XSDig==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-hierarchy": "^1.1.8", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-pivot-table": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.17.30.tgz", - "integrity": "sha512-prMaGetBzEA0G+hu19N8ILArqlWOC1H5NIEDgLDdPHUGTNiNGpLq3l3pr2H2GFWCyrZHPKPSlZdVeczHFPfpuA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.17.32.tgz", + "integrity": "sha512-fQ6+Qg3Mj9g3aLvNWn+C9SW1x7kQglotoc1cl0/L5SfVswsW4eqgZSWkLlvYGU3JlL5r+durIqD97gt5cmiB7g==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "datatables.net-bs": "^1.10.15", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-rose": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.17.30.tgz", - "integrity": "sha512-jaf9rqIWNVYfYAuiZBLzv6NFNA8jU3VtkANL/J/IStL9Z7ell3azVBhrvMdvRXb5Q3mvKNs5XS+VVEs+fHuqKA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.17.32.tgz", + "integrity": "sha512-HjzYOZP++kWVVclNYOtq2To6EAn6j+XZjllgd9EGn7TuF+e1DBHjHTOVyTWyJCMSMR1451kpjQouzZLzTIwQvw==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "nvd3": "1.8.6", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-sankey": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.17.30.tgz", - "integrity": "sha512-sb8SY/slUH1y7pILxaxuclYi6Bnury/6JF9PvBTBuY4XarOC44vuo8u6ko1QFBWpDP0fG79KgxRT+B8y+cPLZQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.17.32.tgz", + "integrity": "sha512-xaX/b1aRgh5XoyjFIKOJkj/8WaI9oAP0tbBVLFaxzGhZzQvWwxOluTA1QqeR6ndKeLgbX8LZxc5xXd3kLLsBLA==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-sankey": "^0.4.2", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-sankey-loop": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.17.30.tgz", - "integrity": "sha512-1z1aNcyNLE3C/R3aCkDPubwn2Kp3I63FVOmAwvVHt1vtRqvd0X6V3B2IGiXD8mQUpcMXGDKXhdHWEZzI5pLTYg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.17.32.tgz", + "integrity": "sha512-7R+1+MJ5Sel8z8dklMmU45/RktOkpmfLcE9zu7S7/vG3aI9ORrp5HFSl6PfzmQ71Q/uHww2m+18LAGMkFsxxTA==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-sankey-diagram": "^0.7.3", "d3-selection": "^1.4.0", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-sunburst": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.17.30.tgz", - "integrity": "sha512-VxDNSYwipIWp6s0CguKKtp7yAx9CfKo4QQky3209gMfENmFCyf0ZABPZW65OtXiEaUJ5gbnEo9o+hKM1BjQh8w==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.17.32.tgz", + "integrity": "sha512-zqg3uB/1n+x++PTeiKo2w6qCmTR2sLqWkTR8Towlg1wLNOJHSNbkxDa9CDx6TfKciUCGlJc/FtS4G9lLpwN5Yg==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-treemap": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.17.30.tgz", - "integrity": "sha512-nVBRsOcBbU6NpCPyE0nz1BKyM4kQXhw3J7iBNLMG1apxkFpLmggd+op/eI2bK4hR7FdszOmkkgtULangI/cDlA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.17.32.tgz", + "integrity": "sha512-4g3plLNN/QNi2Zs9MkOTSdZtucaCFE5a/nKsuGKa0ZBc8Nm8xAS0u32fA/sBgdoV25uJTLedGfJMzgvqZ43epw==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-hierarchy": "^1.1.8", "d3-selection": "^1.4.0", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-world-map": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.17.30.tgz", - "integrity": "sha512-iHKn8BCt4beqqJvZKZ6DLOQSoEvNCUlOCcdH+zRT3u8dGg7pdVXyuaLM0Nm/pKp1zKCeRj2gmckVyAHjsnp98g==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.17.32.tgz", + "integrity": "sha512-0/fLJ9kxLD8/R5eNCDLCCDuzwsX6m4r7N6Iqdliqk5HM9mBOJAhB/gpeE1jNoFvCwl4ntxnuToP6QN6xrjw0Qg==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-array": "^2.4.0", "d3-color": "^1.4.1", @@ -72330,13 +72330,13 @@ } }, "@superset-ui/legacy-preset-chart-big-number": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.17.30.tgz", - "integrity": "sha512-uuEwL8DRzDTJQteEoYS736dgqsNsnJi6MYCSTGgwXQ1DkT42nz0ciRDvGiDmGYFVILqEXfbfegJ7vZTrHlgNKA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.17.32.tgz", + "integrity": "sha512-mSKwdG88WhySuqpmrCHa/R+cDfwqph/Vee4RsOrGzBFS/fPrlCotUbcLaoZnKJtso4cIIynSJRn29CB8bvd82A==", "requires": { "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@types/d3-color": "^1.2.2", "@types/shortid": "^0.0.29", "d3-color": "^1.2.3", @@ -72369,13 +72369,13 @@ } }, "@superset-ui/legacy-preset-chart-nvd3": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.17.30.tgz", - "integrity": "sha512-9BoMNg+JH5cXt6N/6hYdjc5do3jLeFcLIlKULtZQo3ndyfOrY7mL38fLIeYAG1VRXA5vQUOmIDLgr70s6T9eSA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.17.32.tgz", + "integrity": "sha512-P6HeR8/ik2edSSm+Sxe9TTaCcomb0fAtp7RKOB8lXIGU6oksdvxFcxCB9mbVlnn2ChH9nlUOw5JKo9v3r50w3w==", "requires": { "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-tip": "^0.9.1", "dompurify": "^2.0.6", @@ -72389,12 +72389,12 @@ } }, "@superset-ui/plugin-chart-echarts": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.17.30.tgz", - "integrity": "sha512-Fley3EYDUnbShs1sj0D2Y046mRVe733TcGaNMC1mAJPd0+251hmqQ5+LlG8mq+4oMiCw62hMt8PZ/COBZnB9IQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.17.32.tgz", + "integrity": "sha512-/ifeCsqh1/iSBMv41JWl4HaNUlLmKCVIQyDMTsdJr4MsvYQG1HHtDUpa3eHnMS7RlrLVZLVmlr59LS6S4Dcoww==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@types/mathjs": "^6.0.7", "d3-array": "^1.2.0", "echarts": "^5.0.2", @@ -72402,13 +72402,13 @@ } }, "@superset-ui/plugin-chart-table": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.17.30.tgz", - "integrity": "sha512-uRsTfPdC+ApumaJ+Kut566/Wv6AI1YbO8b68w9XkFEx4xrvQ6fNGwzeNetYaNOulXksg09C51MqrG3bvo+yePw==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.17.32.tgz", + "integrity": "sha512-q3REacsT0oDTnM2ZaFy82Gc+ys62fhEBgZl/dkdxK/3To5jZ29++yU+ctYxdYuf0zFqGZltYRh9Ges03MwaS+g==", "requires": { "@emotion/core": "^10.0.28", - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@types/d3-array": "^2.9.0", "@types/react-table": "^7.0.29", "d3-array": "^2.4.0", @@ -72430,12 +72430,12 @@ } }, "@superset-ui/plugin-chart-word-cloud": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.17.30.tgz", - "integrity": "sha512-h+QAK1e2wqSF+Ldpyz1cGsIGxfMN1gJmn1PPUDGX0cEt5/QTO/xiWuxrRGEc9KzOOhk8gHKgKQn9iADk+Wh/Dg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.17.32.tgz", + "integrity": "sha512-G+ghQ5PW3rJMnbxnoo4yfG8HXJ5N23iFXfH/x4AqWgL0RyTJry/dsyvCpKMQNzvsB2M0dQbY5sy4/uG5JIo7Zg==", "requires": { - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@types/d3-cloud": "^1.2.1", "@types/d3-scale": "^2.0.2", "d3-cloud": "^1.2.5", @@ -72467,14 +72467,14 @@ } }, "@superset-ui/preset-chart-xy": { - "version": "0.17.30", - "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.17.30.tgz", - "integrity": "sha512-/HIP5ue0IAlolvuRqs727/0dKKLSN4MypuGELoRuiFZVeSYWnrCHh5nOqYDtVa79efPTx5c6shn4ocjphi2Nag==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.17.32.tgz", + "integrity": "sha512-tViT1tIBxzhpAbOZI465mF46M5UQ42qA89mGOdzqJLTVe5aggev3oNN8vP35rVsHPz9M96vN3fe8/G1aEqGi/g==", "requires": { "@data-ui/theme": "^0.0.84", "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.30", - "@superset-ui/core": "0.17.30", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@vx/axis": "^0.0.198", "@vx/legend": "^0.0.198", "@vx/scale": "^0.0.197", diff --git a/superset-frontend/package.json b/superset-frontend/package.json index d8d60dfe590..e3613e9636a 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -65,34 +65,34 @@ "@babel/runtime-corejs3": "^7.12.5", "@data-ui/sparkline": "^0.0.84", "@emotion/core": "^10.0.35", - "@superset-ui/chart-controls": "^0.17.30", - "@superset-ui/core": "^0.17.30", - "@superset-ui/legacy-plugin-chart-calendar": "^0.17.30", - "@superset-ui/legacy-plugin-chart-chord": "^0.17.30", - "@superset-ui/legacy-plugin-chart-country-map": "^0.17.31", - "@superset-ui/legacy-plugin-chart-event-flow": "^0.17.30", - "@superset-ui/legacy-plugin-chart-force-directed": "^0.17.30", - "@superset-ui/legacy-plugin-chart-heatmap": "^0.17.30", - "@superset-ui/legacy-plugin-chart-histogram": "^0.17.30", - "@superset-ui/legacy-plugin-chart-horizon": "^0.17.30", - "@superset-ui/legacy-plugin-chart-map-box": "^0.17.30", - "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.30", - "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.30", - "@superset-ui/legacy-plugin-chart-partition": "^0.17.30", - "@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.30", - "@superset-ui/legacy-plugin-chart-rose": "^0.17.30", - "@superset-ui/legacy-plugin-chart-sankey": "^0.17.30", - "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.30", - "@superset-ui/legacy-plugin-chart-sunburst": "^0.17.30", - "@superset-ui/legacy-plugin-chart-treemap": "^0.17.30", - "@superset-ui/legacy-plugin-chart-world-map": "^0.17.30", - "@superset-ui/legacy-preset-chart-big-number": "^0.17.30", + "@superset-ui/chart-controls": "^0.17.32", + "@superset-ui/core": "^0.17.32", + "@superset-ui/legacy-plugin-chart-calendar": "^0.17.32", + "@superset-ui/legacy-plugin-chart-chord": "^0.17.32", + "@superset-ui/legacy-plugin-chart-country-map": "^0.17.32", + "@superset-ui/legacy-plugin-chart-event-flow": "^0.17.32", + "@superset-ui/legacy-plugin-chart-force-directed": "^0.17.32", + "@superset-ui/legacy-plugin-chart-heatmap": "^0.17.32", + "@superset-ui/legacy-plugin-chart-histogram": "^0.17.32", + "@superset-ui/legacy-plugin-chart-horizon": "^0.17.32", + "@superset-ui/legacy-plugin-chart-map-box": "^0.17.32", + "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.32", + "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.32", + "@superset-ui/legacy-plugin-chart-partition": "^0.17.32", + "@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.32", + "@superset-ui/legacy-plugin-chart-rose": "^0.17.32", + "@superset-ui/legacy-plugin-chart-sankey": "^0.17.32", + "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.32", + "@superset-ui/legacy-plugin-chart-sunburst": "^0.17.32", + "@superset-ui/legacy-plugin-chart-treemap": "^0.17.32", + "@superset-ui/legacy-plugin-chart-world-map": "^0.17.32", + "@superset-ui/legacy-preset-chart-big-number": "^0.17.32", "@superset-ui/legacy-preset-chart-deckgl": "^0.4.6", - "@superset-ui/legacy-preset-chart-nvd3": "^0.17.30", - "@superset-ui/plugin-chart-echarts": "^0.17.30", - "@superset-ui/plugin-chart-table": "^0.17.30", - "@superset-ui/plugin-chart-word-cloud": "^0.17.30", - "@superset-ui/preset-chart-xy": "^0.17.30", + "@superset-ui/legacy-preset-chart-nvd3": "^0.17.32", + "@superset-ui/plugin-chart-echarts": "^0.17.32", + "@superset-ui/plugin-chart-table": "^0.17.32", + "@superset-ui/plugin-chart-word-cloud": "^0.17.32", + "@superset-ui/preset-chart-xy": "^0.17.32", "@vx/responsive": "^0.0.195", "abortcontroller-polyfill": "^1.1.9", "antd": "^4.9.4", diff --git a/superset-frontend/spec/fixtures/mockNativeFilters.ts b/superset-frontend/spec/fixtures/mockNativeFilters.ts index 6d475e3b095..d293103556a 100644 --- a/superset-frontend/spec/fixtures/mockNativeFilters.ts +++ b/superset-frontend/spec/fixtures/mockNativeFilters.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import { ExtraFormData } from '@superset-ui/core'; import { NativeFiltersState } from 'src/dashboard/reducers/types'; import { DataMaskStateWithId } from '../../src/dataMask/types'; @@ -76,44 +77,38 @@ export const nativeFilters: NativeFiltersState = { }; export const dataMaskWith2Filters: DataMaskStateWithId = { - crossFilters: {}, - ownFilters: {}, - nativeFilters: { - 'NATIVE_FILTER-e7Q8zKixx': { - id: 'NATIVE_FILTER-e7Q8zKixx', - extraFormData: { - append_form_data: { - filters: [ - { - col: 'region', - op: 'IN', - val: ['East Asia & Pacific'], - }, - ], + 'NATIVE_FILTER-e7Q8zKixx': { + id: 'NATIVE_FILTER-e7Q8zKixx', + ownState: {}, + extraFormData: { + filters: [ + { + col: 'region', + op: 'IN', + val: ['East Asia & Pacific'], }, - }, - currentState: { - value: ['East Asia & Pacific'], - }, + ], }, - 'NATIVE_FILTER-x9QPw0so1': { - id: 'NATIVE_FILTER-x9QPw0so1', - extraFormData: {}, - currentState: {}, + filterState: { + value: ['East Asia & Pacific'], }, }, + 'NATIVE_FILTER-x9QPw0so1': { + id: 'NATIVE_FILTER-x9QPw0so1', + ownState: {}, + extraFormData: {}, + filterState: {}, + }, }; -export const extraFormData = { - append_form_data: { - filters: [ - { - col: 'ethnic_minority', - op: 'IN', - val: 'No, not an ethnic minority', - }, - ], - }, +export const extraFormData: ExtraFormData = { + filters: [ + { + col: 'ethnic_minority', + op: 'IN', + val: ['No, not an ethnic minority'], + }, + ], }; export const NATIVE_FILTER_ID = 'NATIVE_FILTER-p4LImrSgA'; @@ -136,14 +131,12 @@ export const singleNativeFiltersState = { }, }; -export const dataMaskWith1Filter = { - nativeFilters: { - [NATIVE_FILTER_ID]: { - id: NATIVE_FILTER_ID, - extraFormData, - currentState: { - value: ['No, not an ethnic minority'], - }, +export const dataMaskWith1Filter: DataMaskStateWithId = { + [NATIVE_FILTER_ID]: { + id: NATIVE_FILTER_ID, + extraFormData, + filterState: { + value: ['No, not an ethnic minority'], }, }, }; diff --git a/superset-frontend/spec/javascripts/dashboard/fixtures/mockNativeFilters.ts b/superset-frontend/spec/javascripts/dashboard/fixtures/mockNativeFilters.ts index e598560083c..6e6e4b8d390 100644 --- a/superset-frontend/spec/javascripts/dashboard/fixtures/mockNativeFilters.ts +++ b/superset-frontend/spec/javascripts/dashboard/fixtures/mockNativeFilters.ts @@ -16,18 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -import { DataMaskStateWithId, DataMaskType } from 'src/dataMask/types'; +import { DataMaskStateWithId } from 'src/dataMask/types'; import { NativeFiltersState } from 'src/dashboard/reducers/types'; export const mockDataMaskInfo: DataMaskStateWithId = { - [DataMaskType.CrossFilters]: {}, - [DataMaskType.OwnFilters]: {}, - [DataMaskType.NativeFilters]: { - DefaultsID: { - id: 'DefaultId', - currentState: { - value: [], - }, + DefaultsID: { + id: 'DefaultId', + ownState: {}, + filterState: { + value: [], }, }, }; diff --git a/superset-frontend/spec/javascripts/dashboard/util/getFormDataWithExtraFilters_spec.ts b/superset-frontend/spec/javascripts/dashboard/util/getFormDataWithExtraFilters_spec.ts index e30ef3f128f..38c29e0f5d0 100644 --- a/superset-frontend/spec/javascripts/dashboard/util/getFormDataWithExtraFilters_spec.ts +++ b/superset-frontend/spec/javascripts/dashboard/util/getFormDataWithExtraFilters_spec.ts @@ -64,14 +64,11 @@ describe('getFormDataWithExtraFilters', () => { }, }, dataMask: { - crossFilters: {}, - ownFilters: {}, - nativeFilters: { - [filterId]: { - id: filterId, - extraFormData: {}, - currentState: {}, - }, + [filterId]: { + id: filterId, + extraFormData: {}, + filterState: {}, + ownState: {}, }, }, layout: (dashboardLayout.present as unknown) as { diff --git a/superset-frontend/spec/javascripts/filters/utils_spec.ts b/superset-frontend/spec/javascripts/filters/utils_spec.ts index dcc02b70531..d997e6495cb 100644 --- a/superset-frontend/spec/javascripts/filters/utils_spec.ts +++ b/superset-frontend/spec/javascripts/filters/utils_spec.ts @@ -35,64 +35,56 @@ describe('Filter utils', () => { describe('getRangeExtraFormData', () => { it('getRangeExtraFormData - col: "testCol", lower: 1, upper: 2', () => { expect(getRangeExtraFormData('testCol', 1, 2)).toEqual({ - append_form_data: { - filters: [ - { - col: 'testCol', - op: '>=', - val: 1, - }, - { - col: 'testCol', - op: '<=', - val: 2, - }, - ], - }, + filters: [ + { + col: 'testCol', + op: '>=', + val: 1, + }, + { + col: 'testCol', + op: '<=', + val: 2, + }, + ], }); }); it('getRangeExtraFormData - col: "testCol", lower: 0, upper: 0', () => { expect(getRangeExtraFormData('testCol', 0, 0)).toEqual({ - append_form_data: { - filters: [ - { - col: 'testCol', - op: '>=', - val: 0, - }, - { - col: 'testCol', - op: '<=', - val: 0, - }, - ], - }, + filters: [ + { + col: 'testCol', + op: '>=', + val: 0, + }, + { + col: 'testCol', + op: '<=', + val: 0, + }, + ], }); }); it('getRangeExtraFormData - col: "testCol", lower: null, upper: 2', () => { expect(getRangeExtraFormData('testCol', null, 2)).toEqual({ - append_form_data: { - filters: [ - { - col: 'testCol', - op: '<=', - val: 2, - }, - ], - }, + filters: [ + { + col: 'testCol', + op: '<=', + val: 2, + }, + ], }); }); it('getRangeExtraFormData - col: "testCol", lower: 1, upper: undefined', () => { expect(getRangeExtraFormData('testCol', 1, undefined)).toEqual({ - append_form_data: { - filters: [ - { - col: 'testCol', - op: '>=', - val: 1, - }, - ], - }, + filters: [ + { + col: 'testCol', + op: '>=', + val: 1, + }, + ], }); }); }); @@ -101,68 +93,56 @@ describe('Filter utils', () => { expect( getSelectExtraFormData('testCol', ['value'], false, false), ).toEqual({ - append_form_data: { - filters: [ - { - col: 'testCol', - op: 'IN', - val: ['value'], - }, - ], - }, + filters: [ + { + col: 'testCol', + op: 'IN', + val: ['value'], + }, + ], }); }); it('getSelectExtraFormData - col: "testCol", value: ["value"], emptyFilter: true, inverseSelection: false', () => { expect(getSelectExtraFormData('testCol', ['value'], true, false)).toEqual( { - append_form_data: { - adhoc_filters: [ - { - clause: 'WHERE', - expressionType: 'SQL', - sqlExpression: '1 = 0', - }, - ], - }, + adhoc_filters: [ + { + clause: 'WHERE', + expressionType: 'SQL', + sqlExpression: '1 = 0', + }, + ], }, ); }); it('getSelectExtraFormData - col: "testCol", value: ["value"], emptyFilter: false, inverseSelection: true', () => { expect(getSelectExtraFormData('testCol', ['value'], false, true)).toEqual( { - append_form_data: { - filters: [ - { - col: 'testCol', - op: 'NOT IN', - val: ['value'], - }, - ], - }, + filters: [ + { + col: 'testCol', + op: 'NOT IN', + val: ['value'], + }, + ], }, ); }); it('getSelectExtraFormData - col: "testCol", value: [], emptyFilter: false, inverseSelection: false', () => { expect(getSelectExtraFormData('testCol', [], false, false)).toEqual({ - append_form_data: { - filters: [], - }, + filters: [], }); }); it('getSelectExtraFormData - col: "testCol", value: undefined, emptyFilter: false, inverseSelection: false', () => { expect( getSelectExtraFormData('testCol', undefined, false, false), ).toEqual({ - append_form_data: { - filters: [], - }, + filters: [], }); }); it('getSelectExtraFormData - col: "testCol", value: null, emptyFilter: false, inverseSelection: false', () => { expect(getSelectExtraFormData('testCol', null, false, false)).toEqual({ - append_form_data: { - filters: [], - }, + filters: [], }); }); }); diff --git a/superset-frontend/src/chart/Chart.jsx b/superset-frontend/src/chart/Chart.jsx index 3032b33a306..7ec31b6fb9a 100644 --- a/superset-frontend/src/chart/Chart.jsx +++ b/superset-frontend/src/chart/Chart.jsx @@ -62,6 +62,7 @@ const propTypes = { onQuery: PropTypes.func, onFilterMenuOpen: PropTypes.func, onFilterMenuClose: PropTypes.func, + ownState: PropTypes.object, }; const BLANK = {}; @@ -127,6 +128,7 @@ class Chart extends React.PureComponent { this.props.timeout, this.props.chartId, this.props.dashboardId, + this.props.ownState, ); } else { // Create chart with POST request @@ -136,6 +138,7 @@ class Chart extends React.PureComponent { this.props.timeout, this.props.chartId, this.props.dashboardId, + this.props.ownState, ); } } diff --git a/superset-frontend/src/chart/ChartRenderer.jsx b/superset-frontend/src/chart/ChartRenderer.jsx index bb985786134..f40e46e83cb 100644 --- a/superset-frontend/src/chart/ChartRenderer.jsx +++ b/superset-frontend/src/chart/ChartRenderer.jsx @@ -45,7 +45,7 @@ const propTypes = { setDataMask: PropTypes.func, onFilterMenuOpen: PropTypes.func, onFilterMenuClose: PropTypes.func, - ownCurrentState: PropTypes.object, + ownState: PropTypes.object, }; const BLANK = {}; @@ -94,7 +94,8 @@ class ChartRenderer extends React.Component { return ( this.hasQueryResponseChange || nextProps.annotationData !== this.props.annotationData || - nextProps.ownCurrentState !== this.props.ownCurrentState || + nextProps.ownState !== this.props.ownState || + nextProps.filterState !== this.props.filterState || nextProps.height !== this.props.height || nextProps.width !== this.props.width || nextProps.triggerRender || @@ -184,7 +185,8 @@ class ChartRenderer extends React.Component { annotationData, datasource, initialValues, - ownCurrentState, + ownState, + filterState, formData, queriesResponse, } = this.props; @@ -224,9 +226,10 @@ class ChartRenderer extends React.Component { datasource={datasource} initialValues={initialValues} formData={formData} - ownCurrentState={ownCurrentState} + ownState={ownState} + filterState={filterState} hooks={this.hooks} - behaviors={[Behavior.CROSS_FILTER]} + behaviors={[Behavior.INTERACTIVE_CHART]} queriesData={queriesResponse} onRenderSuccess={this.handleRenderSuccess} onRenderFailure={this.handleRenderFailure} diff --git a/superset-frontend/src/chart/chartAction.js b/superset-frontend/src/chart/chartAction.js index 906a41c4f36..5e5fc4624cf 100644 --- a/superset-frontend/src/chart/chartAction.js +++ b/superset-frontend/src/chart/chartAction.js @@ -161,6 +161,7 @@ const v1ChartDataRequest = async ( force, requestParams, setDataMask, + ownState, ) => { const payload = buildV1ChartDataPayload({ formData, @@ -168,6 +169,7 @@ const v1ChartDataRequest = async ( resultFormat, force, setDataMask, + ownState, }); // The dashboard id is added to query params for tracking purposes @@ -205,6 +207,7 @@ export async function getChartDataRequest({ force = false, method = 'POST', requestParams = {}, + ownState = {}, }) { let querySettings = { ...requestParams, @@ -235,6 +238,7 @@ export async function getChartDataRequest({ force, querySettings, setDataMask, + ownState, ); } @@ -351,6 +355,7 @@ export function exploreJSON( key, method, dashboardId, + ownState, ) { return async dispatch => { const logStart = Logger.getTimestamp(); @@ -373,6 +378,7 @@ export function exploreJSON( force, method, requestParams, + ownState, }); dispatch(chartUpdateStarted(controller, formData, key)); @@ -470,6 +476,7 @@ export function getSavedChart( timeout = 60, key, dashboardId, + ownState, ) { /* * Perform a GET request to `/explore_json`. @@ -481,7 +488,15 @@ export function getSavedChart( * GET /explore_json?{"chart_id":1,"extra_filters":"..."} * */ - return exploreJSON(formData, force, timeout, key, 'GET', dashboardId); + return exploreJSON( + formData, + force, + timeout, + key, + 'GET', + dashboardId, + ownState, + ); } export const POST_CHART_FORM_DATA = 'POST_CHART_FORM_DATA'; @@ -491,6 +506,7 @@ export function postChartFormData( timeout = 60, key, dashboardId, + ownState, ) { /* * Perform a POST request to `/explore_json`. @@ -498,7 +514,15 @@ export function postChartFormData( * This will post the form data to the endpoint, returning a new chart. * */ - return exploreJSON(formData, force, timeout, key, 'POST', dashboardId); + return exploreJSON( + formData, + force, + timeout, + key, + 'POST', + dashboardId, + ownState, + ); } export function redirectSQLLab(formData) { @@ -537,6 +561,7 @@ export function refreshChart(chartKey, force, dashboardId) { timeout, chart.id, dashboardId, + getState().dataMask[chart.id]?.ownState, ), ); }; diff --git a/superset-frontend/src/dashboard/actions/hydrate.js b/superset-frontend/src/dashboard/actions/hydrate.js index 7b87d5c5104..d304eda535d 100644 --- a/superset-frontend/src/dashboard/actions/hydrate.js +++ b/superset-frontend/src/dashboard/actions/hydrate.js @@ -19,7 +19,11 @@ /* eslint-disable camelcase */ import { isString, keyBy } from 'lodash'; import shortid from 'shortid'; -import { CategoricalColorNamespace } from '@superset-ui/core'; +import { + Behavior, + CategoricalColorNamespace, + getChartMetadataRegistry, +} from '@superset-ui/core'; import querystring from 'query-string'; import { chart } from 'src/chart/chartReducer'; @@ -37,6 +41,7 @@ import { DASHBOARD_HEADER_ID, GRID_DEFAULT_CHART_WIDTH, GRID_COLUMN_COUNT, + DASHBOARD_ROOT_ID, } from 'src/dashboard/util/constants'; import { DASHBOARD_HEADER_TYPE, @@ -49,6 +54,7 @@ import getFilterConfigsFromFormdata from 'src/dashboard/util/getFilterConfigsFro import getLocationHash from 'src/dashboard/util/getLocationHash'; import newComponentFactory from 'src/dashboard/util/newComponentFactory'; import { TIME_RANGE } from 'src/visualizations/FilterBox/FilterBox'; +import { FeatureFlag, isFeatureEnabled } from '../../featureFlags'; const reservedQueryParams = new Set(['standalone', 'edit']); @@ -204,10 +210,7 @@ export const hydrateDashboard = (dashboardData, chartData, datasourcesData) => ( } // build DashboardFilters for interactive filter features - if ( - slice.form_data.viz_type === 'filter_box' || - slice.form_data.viz_type === 'filter_select' - ) { + if (slice.form_data.viz_type === 'filter_box') { const configs = getFilterConfigsFromFormdata(slice.form_data); let { columns } = configs; const { labels } = configs; @@ -299,6 +302,38 @@ export const hydrateDashboard = (dashboardData, chartData, datasourcesData) => ( filterSetsConfig: metadata?.filter_sets_configuration || [], }); + if (isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS)) { + // If user just added cross filter to dashboard it's not saving it scope on server, + // so we tweak it until user will update scope and will save it in server + Object.values(dashboardLayout.present).forEach(layoutItem => { + const chartId = layoutItem.meta?.chartId; + const behaviors = + ( + getChartMetadataRegistry().get( + chartQueries[chartId]?.formData?.viz_type, + ) ?? {} + )?.behaviors ?? []; + + if (!metadata.chart_configuration) { + metadata.chart_configuration = {}; + } + if ( + behaviors.includes(Behavior.INTERACTIVE_CHART) && + !metadata.chart_configuration[chartId] + ) { + metadata.chart_configuration[chartId] = { + id: chartId, + crossFilters: { + scope: { + rootPath: [DASHBOARD_ROOT_ID], + excluded: [chartId], // By default it doesn't affects itself + }, + }, + }; + } + }); + } + const { roles } = getState().user; return dispatch({ @@ -310,6 +345,7 @@ export const hydrateDashboard = (dashboardData, chartData, datasourcesData) => ( // read-only data dashboardInfo: { ...dashboardData, + metadata, userId: String(user.userId), // legacy, please use state.user instead dash_edit_perm: findPermission('can_write', 'Dashboard', roles), dash_save_perm: findPermission('can_save_dash', 'Superset', roles), diff --git a/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts b/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts index 2e5e7ac4b34..8b6bed2e538 100644 --- a/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts +++ b/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts @@ -229,8 +229,7 @@ export const selectNativeIndicatorsForChart = ( layoutItem => dashboardLayout[layoutItem]?.meta?.chartId === chartId, ); const column = nativeFilter.targets[0]?.column?.name; - const dataMaskNativeFilters = dataMask.nativeFilters?.[nativeFilter.id]; - let value = dataMaskNativeFilters?.currentState?.value ?? null; + let value = dataMask[nativeFilter.id]?.filterState?.value ?? null; if (!Array.isArray(value) && value !== null) { value = [value]; } @@ -254,8 +253,7 @@ export const selectNativeIndicatorsForChart = ( layoutItem => dashboardLayout[layoutItem]?.meta?.chartId === chartId, ); - const dataMaskCrossFilters = dataMask.crossFilters?.[chartConfig.id]; - let value = dataMaskCrossFilters?.currentState?.value ?? null; + let value = dataMask[chartConfig.id]?.filterState?.value ?? null; if (!Array.isArray(value) && value !== null) { value = [value]; } diff --git a/superset-frontend/src/dashboard/components/SliceHeader/index.tsx b/superset-frontend/src/dashboard/components/SliceHeader/index.tsx index 72bd0897f40..f2570eb6ee5 100644 --- a/superset-frontend/src/dashboard/components/SliceHeader/index.tsx +++ b/superset-frontend/src/dashboard/components/SliceHeader/index.tsx @@ -98,8 +98,7 @@ const SliceHeader: FC = ({ }) => { // TODO: change to indicator field after it will be implemented const crossFilterValue = useSelector( - state => - state.dataMask?.crossFilters?.[slice?.slice_id]?.currentState?.value, + state => state.dataMask[slice?.slice_id]?.filterState?.value, ); return ( diff --git a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx index 650a590ac15..2c66bca1938 100644 --- a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx +++ b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx @@ -195,7 +195,7 @@ class SliceHeaderControls extends React.PureComponent { const isCrossFilter = Object.entries(crossFilterItems) // @ts-ignore .filter(([, { value }]) => - value.behaviors?.includes(Behavior.CROSS_FILTER), + value.behaviors?.includes(Behavior.INTERACTIVE_CHART), ) .find(([key]) => key === slice.viz_type); diff --git a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx index bfe775eb8e8..bc1f78f7df1 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx @@ -70,7 +70,8 @@ const propTypes = { sliceCanEdit: PropTypes.bool.isRequired, addSuccessToast: PropTypes.func.isRequired, addDangerToast: PropTypes.func.isRequired, - ownCurrentState: PropTypes.object, + ownState: PropTypes.object, + filterState: PropTypes.object, }; const defaultProps = { @@ -262,7 +263,8 @@ export default class Chart extends React.Component { sliceCanEdit, addSuccessToast, addDangerToast, - ownCurrentState, + ownState, + filterState, handleToggleFullSize, isFullSize, } = this.props; @@ -371,7 +373,8 @@ export default class Chart extends React.Component { dashboardId={dashboardId} initialValues={initialValues} formData={formData} - ownCurrentState={ownCurrentState} + ownState={ownState} + filterState={filterState} queriesResponse={chart.queriesResponse} timeout={timeout} triggerQuery={chart.triggerQuery} diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadePopover/index.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadePopover/index.tsx index 76be1409fb2..60396caea1b 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadePopover/index.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadePopover/index.tsx @@ -22,8 +22,8 @@ import Popover from 'src/components/Popover'; import Icon from 'src/components/Icon'; import { Pill } from 'src/dashboard/components/FiltersBadge/Styles'; import { useSelector } from 'react-redux'; -import { getInitialMask } from 'src/dataMask/reducer'; -import { MaskWithId } from 'src/dataMask/types'; +import { getInitialDataMask } from 'src/dataMask/reducer'; +import { DataMaskWithId } from 'src/dataMask/types'; import FilterControl from 'src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControl'; import CascadeFilterControl from 'src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadeFilterControl'; import { CascadeFilter } from 'src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/types'; @@ -82,9 +82,8 @@ const CascadePopover: React.FC = ({ directPathToChild, }) => { const [currentPathToChild, setCurrentPathToChild] = useState(); - const dataMask = useSelector( - state => - state.dataMask.nativeFilters[filter.id] ?? getInitialMask(filter.id), + const dataMask = useSelector( + state => state.dataMask[filter.id] ?? getInitialDataMask(filter.id), ); useEffect(() => { @@ -98,7 +97,7 @@ const CascadePopover: React.FC = ({ const getActiveChildren = useCallback( (filter: CascadeFilter): CascadeFilter[] | null => { const children = filter.cascadeChildren || []; - const currentValue = dataMask.currentState?.value; + const currentValue = dataMask.filterState?.value; const activeChildren = children.flatMap( childFilter => getActiveChildren(childFilter) || [], diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx index 424e5872175..7b630f8ff6f 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx @@ -17,7 +17,7 @@ * under the License. */ import React, { FC, useMemo, useState } from 'react'; -import { DataMaskUnit } from 'src/dataMask/types'; +import { DataMaskState } from 'src/dataMask/types'; import { DataMask, styled } from '@superset-ui/core'; import CascadePopover from '../CascadeFilters/CascadePopover'; import { buildCascadeFiltersTree } from './utils'; @@ -33,7 +33,7 @@ const Wrapper = styled.div` type FilterControlsProps = { directPathToChild?: string[]; - dataMaskSelected: DataMaskUnit; + dataMaskSelected: DataMaskState; onFilterSelectionChange: (filter: Filter, dataMask: DataMask) => void; }; @@ -49,7 +49,7 @@ const FilterControls: FC = ({ const cascadeFilters = useMemo(() => { const filtersWithValue = filterValues.map(filter => ({ ...filter, - currentValue: dataMaskSelected[filter.id]?.currentState?.value, + dataMask: dataMaskSelected[filter.id], })); return buildCascadeFiltersTree(filtersWithValue); }, [filterValues, dataMaskSelected]); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx index 06241e944ef..f8b97cfc342 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx @@ -78,6 +78,7 @@ const FilterValue: React.FC = ({ formData: newFormData, force: false, requestParams: { dashboardId: 0 }, + ownState: filter.dataMask?.ownState, }) .then(response => { if (isFeatureEnabled(FeatureFlag.GLOBAL_ASYNC_QUERIES)) { @@ -150,6 +151,8 @@ const FilterValue: React.FC = ({ queriesData={hasDataSource ? state : [{ data: [{}] }]} chartType={filterType} behaviors={[Behavior.NATIVE_FILTER]} + filterState={filter.dataMask?.filterState} + ownState={filter.dataMask?.ownState} hooks={{ setDataMask }} /> )} diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/state.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/state.ts index 0a60c2c0223..802a12d1953 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/state.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/state.ts @@ -29,9 +29,9 @@ export function useCascadingFilters(id: string) { const filter = filters[id]; const cascadeParentIds: string[] = filter?.cascadeParentIds ?? []; let cascadedFilters = {}; - const nativeFilters = useDataMask(); + const nativeFiltersDataMask = useDataMask(); cascadeParentIds.forEach(parentId => { - const parentState = nativeFilters[parentId] || {}; + const parentState = nativeFiltersDataMask[parentId] || {}; const { extraFormData: parentExtra = {} } = parentState; cascadedFilters = mergeExtraFormData(cascadedFilters, parentExtra); }); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.test.tsx index d98bbe77438..812b0af270f 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.test.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.test.tsx @@ -27,7 +27,7 @@ const createProps = () => ({ filterSetId: 'set-id', dataMaskSelected: { DefaultsID: { - currentState: { + filterState: { value: 'value', }, }, diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.tsx index 5ab5a03337b..c77ea56c8fb 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.tsx @@ -22,7 +22,7 @@ import { Typography, Tooltip } from 'src/common/components'; import { useDispatch } from 'react-redux'; import Button from 'src/components/Button'; import { setFilterSetsConfiguration } from 'src/dashboard/actions/nativeFilters'; -import { DataMaskUnit } from 'src/dataMask/types'; +import { DataMaskState } from 'src/dataMask/types'; import { WarningOutlined } from '@ant-design/icons'; import { ActionButtons } from './Footer'; import { useDataMask, useFilters, useFilterSets } from '../state'; @@ -60,7 +60,7 @@ const ActionButton = styled.div<{ disabled?: boolean }>` export type EditSectionProps = { filterSetId: string; - dataMaskSelected: DataMaskUnit; + dataMaskSelected: DataMaskState; onCancel: HandlerFunction; disabled: boolean; }; @@ -94,7 +94,7 @@ const EditSection: FC = ({ ...filterSet, name: filterSetName, nativeFilters: filters, - dataMask: { nativeFilters: { ...dataMaskApplied } }, + dataMask: { ...dataMaskApplied }, }; return filterSetId === filterSet.id ? newFilterSet : filterSet; }), diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSetUnit.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSetUnit.tsx index 6181205ab80..2f628933e45 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSetUnit.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSetUnit.tsx @@ -19,7 +19,7 @@ import { Typography, Dropdown, Menu } from 'src/common/components'; import React, { FC } from 'react'; import { FilterSet } from 'src/dashboard/reducers/types'; -import { DataMaskUnit } from 'src/dataMask/types'; +import { DataMaskState } from 'src/dataMask/types'; import { CheckOutlined, EllipsisOutlined } from '@ant-design/icons'; import { HandlerFunction, styled, supersetTheme, t } from '@superset-ui/core'; import { Tooltip } from 'src/common/components/Tooltip'; @@ -45,7 +45,7 @@ export type FilterSetUnitProps = { isApplied?: boolean; filterSet?: FilterSet; filterSetName?: string; - dataMaskSelected?: DataMaskUnit; + dataMaskSelected?: DataMaskState; setFilterSetName?: (name: string) => void; onDelete?: HandlerFunction; onEdit?: HandlerFunction; @@ -114,7 +114,7 @@ const FilterSetUnit: FC = ({ ); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSets.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSets.test.tsx index 104fb7aebdc..ef8f8592742 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSets.test.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSets.test.tsx @@ -27,7 +27,7 @@ const createProps = () => ({ isFilterSetChanged: false, dataMaskSelected: { DefaultsID: { - currentState: { + filterState: { value: 'value', }, }, diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.test.tsx index 7e40e5625a6..1879b7922b4 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.test.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.test.tsx @@ -25,7 +25,7 @@ import FiltersHeader, { FiltersHeaderProps } from './FiltersHeader'; const mockedProps = { dataMask: { DefaultsID: { - currentState: { + filterState: { value: 'value', }, }, diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.tsx index 04cd86b1de0..3b4b91a868c 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.tsx @@ -19,7 +19,7 @@ import React, { FC } from 'react'; import { styled, t } from '@superset-ui/core'; import { Collapse, Typography, Tooltip } from 'src/common/components'; -import { DataMaskUnit } from 'src/dataMask/types'; +import { DataMaskState } from 'src/dataMask/types'; import { CaretDownOutlined } from '@ant-design/icons'; import { areObjectsEqual } from 'src/reduxUtils'; import { FilterSet } from 'src/dashboard/reducers/types'; @@ -55,7 +55,7 @@ const StyledCollapse = styled(Collapse)` `; export type FiltersHeaderProps = { - dataMask?: DataMaskUnit; + dataMask?: DataMaskState; filterSet?: FilterSet; }; @@ -100,7 +100,7 @@ const FiltersHeader: FC = ({ dataMask, filterSet }) => { {name}:  - {getFilterValueForDisplay(dataMask?.[id]?.currentState?.value) || ( + {getFilterValueForDisplay(dataMask?.[id]?.filterState?.value) || ( {t('None')} )} diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/index.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/index.tsx index d7dda1fa9b7..6513bfaf471 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/index.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/index.tsx @@ -18,9 +18,9 @@ */ import React, { useEffect, useState, MouseEvent } from 'react'; -import { HandlerFunction, styled, t } from '@superset-ui/core'; +import { DataMask, HandlerFunction, styled, t } from '@superset-ui/core'; import { useDispatch } from 'react-redux'; -import { DataMaskState, DataMaskUnit, MaskWithId } from 'src/dataMask/types'; +import { DataMaskState, DataMaskWithId } from 'src/dataMask/types'; import { setFilterSetsConfiguration } from 'src/dashboard/actions/nativeFilters'; import { Filters, FilterSet, FilterSets } from 'src/dashboard/reducers/types'; import { areObjectsEqual } from 'src/reduxUtils'; @@ -64,11 +64,11 @@ const FilterSetUnitWrapper = styled.div<{ export type FilterSetsProps = { disabled: boolean; isFilterSetChanged: boolean; - dataMaskSelected: DataMaskUnit; + dataMaskSelected: DataMaskState; onEditFilterSet: (id: string) => void; onFilterSelectionChange: ( filter: Pick & Partial, - dataMask: Partial, + dataMask: Partial, ) => void; }; @@ -135,42 +135,41 @@ const FilterSets: React.FC = ({ const filterSet = filterSets[id]; - Object.values(filterSet?.dataMask?.nativeFilters ?? []).forEach( - dataMask => { - const { extraFormData, currentState, id } = dataMask as MaskWithId; - if (isFilterMissingOrContainsInvalidMetadata(id, filterSet)) { - return; - } - onFilterSelectionChange( - { id }, - { nativeFilters: { extraFormData, currentState } }, - ); - }, - ); + Object.values(filterSet?.dataMask ?? []).forEach(dataMask => { + const { extraFormData, filterState, id } = dataMask as DataMaskWithId; + if (isFilterMissingOrContainsInvalidMetadata(id, filterSet)) { + return; + } + onFilterSelectionChange({ id }, { extraFormData, filterState }); + }); }; const handleRebuild = (id: string) => { const filterSet = filterSets[id]; // We need remove invalid filters from filter set - const newFilters = Object.values(filterSet?.dataMask?.nativeFilters ?? []) + const newFilters = Object.values(filterSet?.dataMask ?? {}) .filter(dataMask => { - const { id } = dataMask as MaskWithId; + const { id } = dataMask as DataMaskWithId; return !isFilterMissingOrContainsInvalidMetadata(id, filterSet); }) - .reduce((prev, next) => ({ ...prev, [next.id]: filters[next.id] }), {}); + .reduce( + (prev, next: DataMaskWithId) => ({ + ...prev, + [next.id]: filters[next.id], + }), + {}, + ); const updatedFilterSet: FilterSet = { ...filterSet, nativeFilters: newFilters as Filters, - dataMask: { - nativeFilters: Object.keys(newFilters).reduce( - (prev, nextFilterId) => ({ - ...prev, - [nextFilterId]: filterSet.dataMask?.nativeFilters?.[nextFilterId], - }), - {}, - ), - }, + dataMask: Object.keys(newFilters).reduce( + (prev, nextFilterId) => ({ + ...prev, + [nextFilterId]: filterSet.dataMask?.nativeFilters?.[nextFilterId], + }), + {}, + ), }; dispatch( setFilterSetsConfiguration( @@ -210,15 +209,13 @@ const FilterSets: React.FC = ({ name: filterSetName.trim(), id: generateFiltersSetId(), nativeFilters: filters, - dataMask: { - nativeFilters: Object.keys(filters).reduce( - (prev, nextFilterId) => ({ - ...prev, - [nextFilterId]: dataMaskApplied[nextFilterId], - }), - {}, - ), - }, + dataMask: Object.keys(filters).reduce( + (prev, nextFilterId) => ({ + ...prev, + [nextFilterId]: dataMaskApplied[nextFilterId], + }), + {}, + ), }; dispatch( setFilterSetsConfiguration([newFilterSet].concat(filterSetFilterValues)), diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/utils/findExistingFilterSet.test.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/utils/findExistingFilterSet.test.ts index bd87363ced9..ac0401bf6bb 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/utils/findExistingFilterSet.test.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/utils/findExistingFilterSet.test.ts @@ -20,22 +20,20 @@ import { FilterSet } from 'src/dashboard/reducers/types'; import { findExistingFilterSet } from '.'; const createDataMaskSelected = () => ({ - filterId: { currentState: { value: 'value-1' } }, - filterId2: { currentState: { value: 'value-2' } }, + filterId: { filterState: { value: 'value-1' } }, + filterId2: { filterState: { value: 'value-2' } }, }); test('Should find correct filter', () => { const dataMaskSelected = createDataMaskSelected(); - const filterSetFilterValues = [ + const filterSetFilterValues: FilterSet[] = [ { id: 'id-01', name: 'name-01', nativeFilters: {}, dataMask: { - nativeFilters: { - filterId: { currentState: { value: 'value-1' } }, - filterId2: { currentState: { value: 'value-2' } }, - }, + filterId: { id: 'filterId', filterState: { value: 'value-1' } }, + filterId2: { id: 'filterId2', filterState: { value: 'value-2' } }, } as any, }, ]; @@ -45,10 +43,8 @@ test('Should find correct filter', () => { }); expect(response).toEqual({ dataMask: { - nativeFilters: { - filterId: { currentState: { value: 'value-1' } }, - filterId2: { currentState: { value: 'value-2' } }, - }, + filterId: { id: 'filterId', filterState: { value: 'value-1' } }, + filterId2: { id: 'filterId2', filterState: { value: 'value-2' } }, }, id: 'id-01', name: 'name-01', @@ -64,9 +60,7 @@ test('Should return undefined when nativeFilters has less values', () => { name: 'name-01', nativeFilters: {}, dataMask: { - nativeFilters: { - filterId: { currentState: { value: 'value-1' } }, - }, + filterId: { id: 'filterId', filterState: { value: 'value-1' } }, } as any, }, ]; @@ -79,17 +73,15 @@ test('Should return undefined when nativeFilters has less values', () => { test('Should return undefined when nativeFilters has different values', () => { const dataMaskSelected = createDataMaskSelected(); - const filterSetFilterValues = [ + const filterSetFilterValues: FilterSet[] = [ { id: 'id-01', name: 'name-01', nativeFilters: {}, dataMask: { - nativeFilters: { - filterId: { currentState: { value: 'value-1' } }, - filterId2: { currentState: { value: 'value-1' } }, - }, - } as any, + filterId: { id: 'filterId', filterState: { value: 'value-1' } }, + filterId2: { id: 'filterId2', filterState: { value: 'value-1' } }, + }, }, ]; const response = findExistingFilterSet({ @@ -116,14 +108,14 @@ test('Should return undefined when dataMask:{}', () => { expect(response).toBeUndefined(); }); -test('Should return undefined when dataMask.nativeFilters is undefined}', () => { +test('Should return undefined when dataMask is empty}', () => { const dataMaskSelected = createDataMaskSelected(); - const filterSetFilterValues = [ + const filterSetFilterValues: FilterSet[] = [ { id: 'id-01', name: 'name-01', nativeFilters: {}, - dataMask: { nativeFilters: undefined }, + dataMask: {}, }, ]; const response = findExistingFilterSet({ diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/utils/index.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/utils/index.ts index 9c2e9411bdc..3da7a8d5aa7 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/utils/index.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/utils/index.ts @@ -20,7 +20,7 @@ import shortid from 'shortid'; import { t } from '@superset-ui/core'; import { areObjectsEqual } from 'src/reduxUtils'; -import { DataMaskUnit } from 'src/dataMask/types'; +import { DataMaskState } from 'src/dataMask/types'; import { FilterSet } from 'src/dashboard/reducers/types'; export const generateFiltersSetId = () => `FILTERS_SET-${shortid.generate()}`; @@ -50,23 +50,18 @@ export const findExistingFilterSet = ({ dataMaskSelected, }: { filterSetFilterValues: FilterSet[]; - dataMaskSelected: DataMaskUnit; + dataMaskSelected: DataMaskState; }) => - filterSetFilterValues.find(({ dataMask: dataMaskFromFilterSet }) => { - if (dataMaskFromFilterSet?.nativeFilters) { - const dataMaskSelectedEntries = Object.entries(dataMaskSelected); - return dataMaskSelectedEntries.every( - ([id, filterFromSelectedFilters]) => { - const isEqual = areObjectsEqual( - filterFromSelectedFilters.currentState, - dataMaskFromFilterSet?.nativeFilters?.[id]?.currentState, - ); - const hasSamePropsNumber = - dataMaskSelectedEntries.length === - Object.keys(dataMaskFromFilterSet?.nativeFilters ?? {}).length; - return isEqual && hasSamePropsNumber; - }, + filterSetFilterValues.find(({ dataMask: dataMaskFromFilterSet = {} }) => { + const dataMaskSelectedEntries = Object.entries(dataMaskSelected); + return dataMaskSelectedEntries.every(([id, filterFromSelectedFilters]) => { + const isEqual = areObjectsEqual( + filterFromSelectedFilters.filterState, + dataMaskFromFilterSet?.[id]?.filterState, ); - } - return false; + const hasSamePropsNumber = + dataMaskSelectedEntries.length === + Object.keys(dataMaskFromFilterSet ?? {}).length; + return isEqual && hasSamePropsNumber; + }); }); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Header/Header.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Header/Header.test.tsx index 926d48700cb..4b6418e21dd 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Header/Header.test.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Header/Header.test.tsx @@ -27,7 +27,7 @@ const createProps = () => ({ setDataMaskSelected: jest.fn(), dataMaskSelected: { DefaultsID: { - currentState: { + filterState: { value: null, }, }, @@ -35,7 +35,7 @@ const createProps = () => ({ dataMaskApplied: { DefaultsID: { id: 'DefaultsID', - currentState: { + filterState: { value: null, }, }, diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Header/index.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Header/index.tsx index 849e1015dd7..28423ea35f5 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Header/index.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Header/index.tsx @@ -22,8 +22,8 @@ import React, { FC } from 'react'; import Icon from 'src/components/Icon'; import Button from 'src/components/Button'; import { useSelector } from 'react-redux'; -import { getInitialMask } from 'src/dataMask/reducer'; -import { DataMaskUnit, DataMaskUnitWithId } from 'src/dataMask/types'; +import { getInitialDataMask } from 'src/dataMask/reducer'; +import { DataMaskState, DataMaskStateWithId } from 'src/dataMask/types'; import FilterConfigurationLink from 'src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink'; import { useFilters } from 'src/dashboard/components/nativeFilters/FilterBar/state'; import { Filter } from 'src/dashboard/components/nativeFilters/types'; @@ -58,9 +58,9 @@ const ActionButtons = styled.div` type HeaderProps = { toggleFiltersBar: (arg0: boolean) => void; onApply: () => void; - setDataMaskSelected: (arg0: (draft: DataMaskUnit) => void) => void; - dataMaskSelected: DataMaskUnit; - dataMaskApplied: DataMaskUnitWithId; + setDataMaskSelected: (arg0: (draft: DataMaskState) => void) => void; + dataMaskSelected: DataMaskState; + dataMaskApplied: DataMaskStateWithId; isApplyDisabled: boolean; }; @@ -81,15 +81,15 @@ const Header: FC = ({ const handleClearAll = () => { filterValues.forEach(filter => { setDataMaskSelected(draft => { - draft[filter.id] = getInitialMask(filter.id); + draft[filter.id] = getInitialDataMask(filter.id); }); }); }; const isClearAllDisabled = Object.values(dataMaskApplied).every( filter => - dataMaskSelected[filter.id]?.currentState?.value === null || - (!dataMaskSelected[filter.id] && filter.currentState?.value === null), + dataMaskSelected[filter.id]?.filterState?.value === null || + (!dataMaskSelected[filter.id] && filter.filterState?.value === null), ); return ( diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx index 2106570742c..87b296b5987 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx @@ -26,7 +26,7 @@ import Icon from 'src/components/Icon'; import { Tabs } from 'src/common/components'; import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; import { updateDataMask } from 'src/dataMask/actions'; -import { DataMaskState, DataMaskUnit } from 'src/dataMask/types'; +import { DataMaskState } from 'src/dataMask/types'; import { useImmer } from 'use-immer'; import { areObjectsEqual } from 'src/reduxUtils'; import { Filter } from 'src/dashboard/components/nativeFilters/types'; @@ -150,11 +150,11 @@ const FilterBar: React.FC = ({ directPathToChild, }) => { const [editFilterSetId, setEditFilterSetId] = useState(null); - const [dataMaskSelected, setDataMaskSelected] = useImmer({}); + const [dataMaskSelected, setDataMaskSelected] = useImmer({}); const [ lastAppliedFilterData, setLastAppliedFilterData, - ] = useImmer({}); + ] = useImmer({}); const dispatch = useDispatch(); const filterSets = useFilterSets(); const filterSetFilterValues = Object.values(filterSets); @@ -181,9 +181,7 @@ const FilterBar: React.FC = ({ dispatch(updateDataMask(filter.id, dataMask)); } - if (dataMask.nativeFilters) { - draft[filter.id] = dataMask.nativeFilters; - } + draft[filter.id] = dataMask; }); }; @@ -191,11 +189,7 @@ const FilterBar: React.FC = ({ const filterIds = Object.keys(dataMaskSelected); filterIds.forEach(filterId => { if (dataMaskSelected[filterId]) { - dispatch( - updateDataMask(filterId, { - nativeFilters: dataMaskSelected[filterId], - }), - ); + dispatch(updateDataMask(filterId, dataMaskSelected[filterId])); } }); setLastAppliedFilterData(() => dataMaskSelected); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts index 6f3d10263a0..c25b5b837dd 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts @@ -22,7 +22,7 @@ import { Filters, FilterSets as FilterSetsType, } from 'src/dashboard/reducers/types'; -import { DataMaskUnit, DataMaskUnitWithId } from 'src/dataMask/types'; +import { DataMaskState, DataMaskStateWithId } from 'src/dataMask/types'; import { useEffect, useState } from 'react'; import { areObjectsEqual } from 'src/reduxUtils'; import { Filter } from '../types'; @@ -36,10 +36,10 @@ export const useFilters = () => useSelector(state => state.nativeFilters.filters); export const useDataMask = () => - useSelector(state => state.dataMask.nativeFilters); + useSelector(state => state.dataMask); export const useFiltersInitialisation = ( - dataMaskSelected: DataMaskUnit, + dataMaskSelected: DataMaskState, handleApply: () => void, ) => { const [isInitialized, setIsInitialized] = useState(false); @@ -52,7 +52,7 @@ export const useFiltersInitialisation = ( const areFiltersInitialized = filterValues.every(filterValue => areObjectsEqual( filterValue?.defaultValue, - dataMaskSelected[filterValue?.id]?.currentState?.value, + dataMaskSelected[filterValue?.id]?.filterState?.value, ), ); if (areFiltersInitialized) { @@ -67,9 +67,9 @@ export const useFiltersInitialisation = ( }; export const useFilterUpdates = ( - dataMaskSelected: DataMaskUnit, - setDataMaskSelected: (arg0: (arg0: DataMaskUnit) => void) => void, - setLastAppliedFilterData: (arg0: (arg0: DataMaskUnit) => void) => void, + dataMaskSelected: DataMaskState, + setDataMaskSelected: (arg0: (arg0: DataMaskState) => void) => void, + setLastAppliedFilterData: (arg0: (arg0: DataMaskState) => void) => void, ) => { const filters = useFilters(); const dataMaskApplied = useDataMask(); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx index e59b2b6fb71..97b4288b938 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx @@ -261,9 +261,9 @@ export const FiltersConfigForm: React.FC = ({ > {(hasFilledDataset || !hasDataset) && ( { + setDataMask={({ filterState }) => { setNativeFilterFieldValues(form, filterId, { - defaultValue: nativeFilters?.currentState?.value, + defaultValue: filterState?.value, }); forceUpdate(); }} diff --git a/superset-frontend/src/dashboard/components/nativeFilters/types.ts b/superset-frontend/src/dashboard/components/nativeFilters/types.ts index f1045234417..8e44636441d 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/types.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/types.ts @@ -17,6 +17,8 @@ * under the License. */ +import { DataMask } from '@superset-ui/core'; + export interface Column { name: string; displayName?: string; @@ -40,7 +42,7 @@ export interface Target { export interface Filter { cascadeParentIds: string[]; defaultValue: any; - currentValue?: any; + dataMask?: DataMask; isInstant: boolean; id: string; // randomly generated at filter creation name: string; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/utils.ts b/superset-frontend/src/dashboard/components/nativeFilters/utils.ts index 48193ba53ae..dad5f1586e1 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/utils.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/utils.ts @@ -20,8 +20,9 @@ import { ExtraFormData, QueryFormData, getChartMetadataRegistry, - QueryObject, Behavior, + EXTRA_FORM_DATA_APPEND_KEYS, + EXTRA_FORM_DATA_OVERRIDE_KEYS, } from '@superset-ui/core'; import { Charts } from 'src/dashboard/types'; import { RefObject } from 'react'; @@ -32,7 +33,6 @@ export const getFormData = ({ datasetId, cascadingFilters = {}, groupby, - currentValue, inputRef, defaultValue, controlValues, @@ -60,7 +60,6 @@ export const getFormData = ({ metrics: ['count'], row_limit: 10000, showSearch: true, - currentValue, defaultValue, time_range: 'No filter', time_range_endpoints: ['inclusive', 'exclusive'], @@ -72,46 +71,35 @@ export const getFormData = ({ export function mergeExtraFormData( originalExtra: ExtraFormData = {}, - newExtra: ExtraFormData, + newExtra: ExtraFormData = {}, ): ExtraFormData { - const { - override_form_data: originalOverride = {}, - append_form_data: originalAppend = {}, - } = originalExtra; - const { - override_form_data: newOverride = {}, - append_form_data: newAppend = {}, - custom_form_data: newCustom = {}, - } = newExtra; - - const appendKeys = new Set([ - ...Object.keys(originalAppend), - ...Object.keys(newAppend), - ]); - const appendFormData: Partial = {}; - appendKeys.forEach(key => { - appendFormData[key] = [ - // @ts-ignore - ...(originalAppend?.[key] || []), - // @ts-ignore - ...(newAppend?.[key] || []), + const mergedExtra: ExtraFormData = {}; + EXTRA_FORM_DATA_APPEND_KEYS.forEach((key: string) => { + const mergedValues = [ + ...(originalExtra[key] || []), + ...(newExtra[key] || []), ]; + if (mergedValues.length) { + mergedExtra[key] = mergedValues; + } }); - - return { - custom_form_data: newCustom, - override_form_data: { - ...originalOverride, - ...newOverride, - }, - append_form_data: appendFormData, - }; + EXTRA_FORM_DATA_OVERRIDE_KEYS.forEach((key: string) => { + const originalValue = originalExtra[key]; + if (originalValue !== undefined) { + mergedExtra[key] = originalValue; + } + const newValue = newExtra[key]; + if (newValue !== undefined) { + mergedExtra[key] = newValue; + } + }); + return mergedExtra; } export function isCrossFilter(vizType: string) { // @ts-ignore need export from superset-ui `ItemWithValue` return getChartMetadataRegistry().items[vizType]?.value.behaviors?.includes( - Behavior.CROSS_FILTER, + Behavior.INTERACTIVE_CHART, ); } @@ -122,10 +110,10 @@ export function getExtraFormData( ): ExtraFormData { let extraFormData: ExtraFormData = {}; filterIdsAppliedOnChart.forEach(key => { - const singleDataMask = - dataMask.nativeFilters[key] ?? dataMask.crossFilters[key] ?? {}; - const { extraFormData: newExtra = {} } = singleDataMask; - extraFormData = mergeExtraFormData(extraFormData, newExtra); + extraFormData = mergeExtraFormData( + extraFormData, + dataMask[key]?.extraFormData ?? {}, + ); }); return extraFormData; } diff --git a/superset-frontend/src/dashboard/containers/Chart.jsx b/superset-frontend/src/dashboard/containers/Chart.jsx index 31c0e3255c7..6ef9e4e71b8 100644 --- a/superset-frontend/src/dashboard/containers/Chart.jsx +++ b/superset-frontend/src/dashboard/containers/Chart.jsx @@ -87,8 +87,8 @@ function mapStateToProps( supersetCanShare: !!dashboardInfo.superset_can_share, supersetCanCSV: !!dashboardInfo.superset_can_csv, sliceCanEdit: !!dashboardInfo.slice_can_edit, - ownCurrentState: dataMask.ownFilters?.[id]?.currentState, - crossFilterCurrentState: dataMask.crossFilters?.[id]?.currentState, + ownState: dataMask[id]?.ownState, + filterState: dataMask[id]?.filterState, }; } diff --git a/superset-frontend/src/dashboard/containers/Dashboard.jsx b/superset-frontend/src/dashboard/containers/Dashboard.jsx index 53acf6ae950..98c58d27c2d 100644 --- a/superset-frontend/src/dashboard/containers/Dashboard.jsx +++ b/superset-frontend/src/dashboard/containers/Dashboard.jsx @@ -66,7 +66,10 @@ function mapStateToProps(state) { layout: dashboardLayout.present, }), }, - ownDataCharts: dataMask.ownFilters ?? {}, + ownDataCharts: Object.values(dataMask).reduce( + (prev, next) => ({ ...prev, [next.id]: next.ownState }), + {}, + ), slices: sliceEntities.slices, layout: dashboardLayout.present, impressionId, diff --git a/superset-frontend/src/dashboard/reducers/types.ts b/superset-frontend/src/dashboard/reducers/types.ts index 7e7e88f16ee..770d561a329 100644 --- a/superset-frontend/src/dashboard/reducers/types.ts +++ b/superset-frontend/src/dashboard/reducers/types.ts @@ -85,7 +85,7 @@ export type FilterSet = { id: string; name: string; nativeFilters: Filters; - dataMask: Partial; + dataMask: DataMaskStateWithId; }; export type FilterSets = { diff --git a/superset-frontend/src/dashboard/util/activeAllDashboardFilters.ts b/superset-frontend/src/dashboard/util/activeAllDashboardFilters.ts index 3d5c3ea4b10..54fce7f9100 100644 --- a/superset-frontend/src/dashboard/util/activeAllDashboardFilters.ts +++ b/superset-frontend/src/dashboard/util/activeAllDashboardFilters.ts @@ -85,10 +85,7 @@ export const getAllActiveFilters = ({ const activeFilters = {}; // Combine native filters with cross filters, because they have similar logic - Object.values({ - ...dataMask.nativeFilters, - ...dataMask.crossFilters, - }).forEach(({ id: filterId, extraFormData }) => { + Object.values(dataMask).forEach(({ id: filterId, extraFormData }) => { const scope = nativeFilters?.[filterId]?.scope ?? chartConfiguration?.[filterId]?.crossFilters?.scope ?? { rootPath: [DASHBOARD_ROOT_ID], diff --git a/superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts b/superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts index 2b85d396d97..48cfd8ffcdd 100644 --- a/superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts +++ b/superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts @@ -23,10 +23,7 @@ import { JsonObject, } from '@superset-ui/core'; import { ChartQueryPayload, Charts, LayoutItem } from 'src/dashboard/types'; -import { - getExtraFormData, - mergeExtraFormData, -} from 'src/dashboard/components/nativeFilters/utils'; +import { getExtraFormData } from 'src/dashboard/components/nativeFilters/utils'; import { DataMaskStateWithId } from 'src/dataMask/types'; import getEffectiveExtraFilters from './getEffectiveExtraFilters'; import { ChartConfiguration, NativeFiltersState } from '../../reducers/types'; @@ -102,13 +99,6 @@ export default function getFormDataWithExtraFilters({ }; } - const { extraFormData: newExtra = {} } = - dataMask?.ownFilters?.[chart.id] ?? {}; - extraData.extra_form_data = mergeExtraFormData( - extraData?.extra_form_data, - newExtra, - ); - const formData = { ...chart.formData, ...(colorScheme && { color_scheme: colorScheme }), diff --git a/superset-frontend/src/dashboard/util/charts/getOwnDataCharts.ts b/superset-frontend/src/dashboard/util/charts/getOwnDataCharts.ts index 096f18af4f3..813917af59f 100644 --- a/superset-frontend/src/dashboard/util/charts/getOwnDataCharts.ts +++ b/superset-frontend/src/dashboard/util/charts/getOwnDataCharts.ts @@ -31,17 +31,12 @@ export const getAffectedOwnDataCharts = ( const chartIds = Object.keys(ownDataCharts); const appliedChartIds = Object.keys(appliedOwnDataCharts); const affectedIds: string[] = arrayDiff(chartIds, appliedChartIds).filter( - id => - ownDataCharts[id]?.extraFormData || - appliedOwnDataCharts[id]?.extraFormData, + id => ownDataCharts[id] || appliedOwnDataCharts[id], ); const checkForUpdateIds = new Set([...chartIds, ...appliedChartIds]); checkForUpdateIds.forEach(chartId => { if ( - !areObjectsEqual( - ownDataCharts[chartId]?.extraFormData, - appliedOwnDataCharts[chartId]?.extraFormData, - ) + !areObjectsEqual(ownDataCharts[chartId], appliedOwnDataCharts[chartId]) ) { affectedIds.push(chartId); } diff --git a/superset-frontend/src/dataMask/actions.ts b/superset-frontend/src/dataMask/actions.ts index 331be07a540..9d557da6b57 100644 --- a/superset-frontend/src/dataMask/actions.ts +++ b/superset-frontend/src/dataMask/actions.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { DataMaskType, MaskWithId } from './types'; +import { DataMask } from '@superset-ui/core'; import { FilterConfiguration } from '../dashboard/components/nativeFilters/types'; import { FeatureFlag, isFeatureEnabled } from '../featureFlags'; @@ -24,20 +24,20 @@ export const UPDATE_DATA_MASK = 'UPDATE_DATA_MASK'; export interface UpdateDataMask { type: typeof UPDATE_DATA_MASK; filterId: string; - [DataMaskType.NativeFilters]?: Omit; - [DataMaskType.CrossFilters]?: Omit; - [DataMaskType.OwnFilters]?: Omit; + dataMask: DataMask; } export const SET_DATA_MASK_FOR_FILTER_CONFIG_COMPLETE = 'SET_DATA_MASK_FOR_FILTER_CONFIG_COMPLETE'; + export interface SetDataMaskForFilterConfigComplete { type: typeof SET_DATA_MASK_FOR_FILTER_CONFIG_COMPLETE; filterConfig: FilterConfiguration; - unitName: DataMaskType; } + export const SET_DATA_MASK_FOR_FILTER_CONFIG_FAIL = 'SET_DATA_MASK_FOR_FILTER_CONFIG_FAIL'; + export interface SetDataMaskForFilterConfigFail { type: typeof SET_DATA_MASK_FOR_FILTER_CONFIG_FAIL; filterConfig: FilterConfiguration; @@ -45,28 +45,16 @@ export interface SetDataMaskForFilterConfigFail { export function updateDataMask( filterId: string, - dataMask: { - nativeFilters?: Omit; - crossFilters?: Omit; - ownFilters?: Omit; - }, + dataMask: DataMask, ): UpdateDataMask { - const { nativeFilters, crossFilters, ownFilters } = dataMask; - const filteredDataMask: { - nativeFilters?: Omit; - crossFilters?: Omit; - ownFilters?: Omit; - } = { ownFilters }; - if (isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS) && nativeFilters) { - filteredDataMask.nativeFilters = nativeFilters; - } - if (isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS) && crossFilters) { - filteredDataMask.crossFilters = crossFilters; - } + // Only apply data mask if one of the relevant features is enabled + const isFeatureFlagActive = + isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS) || + isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS); return { type: UPDATE_DATA_MASK, filterId, - ...filteredDataMask, + dataMask: isFeatureFlagActive ? dataMask : {}, }; } diff --git a/superset-frontend/src/dataMask/reducer.ts b/superset-frontend/src/dataMask/reducer.ts index 387f81179b7..7179fdda9bb 100644 --- a/superset-frontend/src/dataMask/reducer.ts +++ b/superset-frontend/src/dataMask/reducer.ts @@ -20,61 +20,45 @@ /* eslint-disable no-param-reassign */ // <- When we work with Immer, we need reassign, so disabling lint import produce from 'immer'; -import { MaskWithId, DataMaskType, DataMaskStateWithId, Mask } from './types'; +import { DataMaskStateWithId, DataMaskWithId } from './types'; import { AnyDataMaskAction, SET_DATA_MASK_FOR_FILTER_CONFIG_COMPLETE, UPDATE_DATA_MASK, - UpdateDataMask, } from './actions'; -export function getInitialMask(id: string): MaskWithId { +export function getInitialDataMask(id: string): DataMaskWithId { return { id, extraFormData: {}, - currentState: {}, + filterState: {}, + ownState: {}, }; } -const setUnitDataMask = ( - unitName: DataMaskType, - action: UpdateDataMask, - dataMaskState: DataMaskStateWithId, -) => { - if (action[unitName]) { - dataMaskState[unitName][action.filterId] = { - ...(action[unitName] as Mask), - id: action.filterId, - }; - } -}; - const dataMaskReducer = produce( (draft: DataMaskStateWithId, action: AnyDataMaskAction) => { const oldData = { ...draft }; switch (action.type) { case UPDATE_DATA_MASK: - Object.values(DataMaskType).forEach(unitName => - setUnitDataMask(unitName, action, draft), - ); + draft[action.filterId] = { + ...draft[action.filterId], + ...action.dataMask, + id: action.filterId, + }; break; case SET_DATA_MASK_FOR_FILTER_CONFIG_COMPLETE: - draft[action.unitName] = {}; (action.filterConfig ?? []).forEach(filter => { - draft[action.unitName][filter.id] = - oldData[action.unitName][filter.id] ?? getInitialMask(filter.id); + draft[filter.id] = + oldData[filter.id] ?? getInitialDataMask(filter.id); }); break; default: } }, - { - [DataMaskType.NativeFilters]: {}, - [DataMaskType.CrossFilters]: {}, - [DataMaskType.OwnFilters]: {}, - }, + {}, ); export default dataMaskReducer; diff --git a/superset-frontend/src/dataMask/types.ts b/superset-frontend/src/dataMask/types.ts index e73077aa79c..f9e4ceff4b6 100644 --- a/superset-frontend/src/dataMask/types.ts +++ b/superset-frontend/src/dataMask/types.ts @@ -16,29 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import { ExtraFormData, DataMaskCurrentState } from '@superset-ui/core'; +import { DataMask } from '@superset-ui/core'; export enum DataMaskType { NativeFilters = 'nativeFilters', CrossFilters = 'crossFilters', - OwnFilters = 'ownFilters', } -export type Mask = { - extraFormData?: ExtraFormData; - currentState: DataMaskCurrentState; -}; -export type DataMaskUnit = { [filterId: string]: Mask }; -export type DataMaskState = { - [DataMaskType.NativeFilters]: Mask; - [DataMaskType.CrossFilters]: Mask; - [DataMaskType.OwnFilters]: Mask; -}; +export type DataMaskState = { [id: string]: DataMask }; -export type MaskWithId = Mask & { id: string }; -export type DataMaskUnitWithId = { [filterId: string]: MaskWithId }; -export type DataMaskStateWithId = { - [DataMaskType.NativeFilters]: DataMaskUnitWithId; - [DataMaskType.CrossFilters]: DataMaskUnitWithId; - [DataMaskType.OwnFilters]: DataMaskUnitWithId; -}; +export type DataMaskWithId = DataMask & { id: string }; +export type DataMaskStateWithId = { [filterId: string]: DataMaskWithId }; diff --git a/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.test.tsx b/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.test.tsx index 9f80efa6440..1199deecccc 100644 --- a/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.test.tsx +++ b/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.test.tsx @@ -53,11 +53,7 @@ const createProps = () => ({ show_perc: true, sort_x_axis: 'alpha_asc', sort_y_axis: 'alpha_asc', - extra_form_data: { - custom_form_data: {}, - override_form_data: {}, - append_form_data: {}, - }, + extra_form_data: {}, }, tableSectionHeight: 156.9, chartStatus: 'rendered', diff --git a/superset-frontend/src/explore/components/DataTablesPane/index.tsx b/superset-frontend/src/explore/components/DataTablesPane/index.tsx index f0747290246..4b462083704 100644 --- a/superset-frontend/src/explore/components/DataTablesPane/index.tsx +++ b/superset-frontend/src/explore/components/DataTablesPane/index.tsx @@ -17,7 +17,7 @@ * under the License. */ import React, { useCallback, useEffect, useState } from 'react'; -import { styled, t } from '@superset-ui/core'; +import { JsonObject, styled, t } from '@superset-ui/core'; import Collapse from 'src/common/components/Collapse'; import Tabs from 'src/common/components/Tabs'; import Loading from 'src/components/Loading'; @@ -105,10 +105,12 @@ export const DataTablesPane = ({ tableSectionHeight, onCollapseChange, chartStatus, + ownState, }: { queryFormData: Record; tableSectionHeight: number; chartStatus: string; + ownState?: JsonObject; onCollapseChange: (openPanelName: string) => void; }) => { const [data, setData] = useState<{ @@ -142,6 +144,7 @@ export const DataTablesPane = ({ formData: queryFormData, resultFormat: 'json', resultType, + ownState, }) .then(response => { // Only displaying the first query is currently supported diff --git a/superset-frontend/src/explore/components/ExploreChartHeader.jsx b/superset-frontend/src/explore/components/ExploreChartHeader.jsx index 7fb57fa1dee..8b93aff3b5a 100644 --- a/superset-frontend/src/explore/components/ExploreChartHeader.jsx +++ b/superset-frontend/src/explore/components/ExploreChartHeader.jsx @@ -49,6 +49,7 @@ const propTypes = { sliceName: PropTypes.string, table_name: PropTypes.string, form_data: PropTypes.object, + ownState: PropTypes.object, timeout: PropTypes.number, chart: chartPropShape, }; @@ -106,6 +107,7 @@ export class ExploreChartHeader extends React.PureComponent { true, this.props.timeout, this.props.chart.id, + this.props.ownState, ); } diff --git a/superset-frontend/src/explore/components/ExploreChartPanel.jsx b/superset-frontend/src/explore/components/ExploreChartPanel.jsx index 779f515ab1d..67c41083773 100644 --- a/superset-frontend/src/explore/components/ExploreChartPanel.jsx +++ b/superset-frontend/src/explore/components/ExploreChartPanel.jsx @@ -47,7 +47,7 @@ const propTypes = { table_name: PropTypes.string, vizType: PropTypes.string.isRequired, form_data: PropTypes.object, - ownCurrentState: PropTypes.object, + ownState: PropTypes.object, standalone: PropTypes.number, timeout: PropTypes.number, refreshOverlayVisible: PropTypes.bool, @@ -190,7 +190,7 @@ const ExploreChartPanel = props => { { const header = ( { > {panelBody} { if (previousControls) { @@ -356,11 +356,11 @@ function ExploreViewContainer(props) { }, [previousControls, props.controls]); useEffect(() => { - if (props.ownCurrentState !== undefined) { + if (props.ownState !== undefined) { onQuery(); reRenderChart(); } - }, [props.ownCurrentState]); + }, [props.ownState]); if (chartIsStale) { props.actions.logEvent(LOG_ACTIONS_CHANGE_EXPLORE_CONTROLS); @@ -557,7 +557,7 @@ function mapStateToProps(state) { form_data.extra_form_data = mergeExtraFormData( { ...form_data.extra_form_data }, { - ...dataMask?.ownFilters?.[form_data.slice_id]?.extraFormData, + ...dataMask[form_data.slice_id]?.ownState, }, ); const chartKey = Object.keys(charts)[0]; @@ -589,7 +589,7 @@ function mapStateToProps(state) { forcedHeight: explore.forced_height, chart, timeout: explore.common.conf.SUPERSET_WEBSERVER_TIMEOUT, - ownCurrentState: dataMask?.ownFilters?.[form_data.slice_id]?.currentState, + ownState: dataMask[form_data.slice_id]?.ownState, impressionId, }; } diff --git a/superset-frontend/src/explore/components/controls/VizTypeControl.jsx b/superset-frontend/src/explore/components/controls/VizTypeControl.jsx index c6d267b0dbb..84aee278f6d 100644 --- a/superset-frontend/src/explore/components/controls/VizTypeControl.jsx +++ b/superset-frontend/src/explore/components/controls/VizTypeControl.jsx @@ -110,7 +110,7 @@ function VizSupportValidation({ vizType }) { const nativeFilterGate = behaviors => !behaviors.includes(Behavior.NATIVE_FILTER) || (isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS) && - behaviors.includes(Behavior.CROSS_FILTER)); + behaviors.includes(Behavior.INTERACTIVE_CHART)); const VizTypeControl = props => { const [showModal, setShowModal] = useState(false); diff --git a/superset-frontend/src/explore/exploreUtils/index.js b/superset-frontend/src/explore/exploreUtils/index.js index a09f6b7cca7..12a70d36240 100644 --- a/superset-frontend/src/explore/exploreUtils/index.js +++ b/superset-frontend/src/explore/exploreUtils/index.js @@ -209,6 +209,7 @@ export const buildV1ChartDataPayload = ({ resultFormat, resultType, setDataMask, + ownState, }) => { const buildQuery = getChartBuildQueryRegistry().get(formData.viz_type) ?? @@ -226,6 +227,7 @@ export const buildV1ChartDataPayload = ({ result_type: resultType, }, { + ownState, hooks: { setDataMask, }, @@ -266,6 +268,7 @@ export const exportChart = ({ resultFormat = 'json', resultType = 'full', force = false, + ownState = {}, }) => { let url; let payload; @@ -284,6 +287,7 @@ export const exportChart = ({ force, resultFormat, resultType, + ownState, }); } postForm(url, payload); diff --git a/superset-frontend/src/filters/components/Range/RangeFilterPlugin.tsx b/superset-frontend/src/filters/components/Range/RangeFilterPlugin.tsx index e628509f64e..78cd6980655 100644 --- a/superset-frontend/src/filters/components/Range/RangeFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Range/RangeFilterPlugin.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { t, DataMask, Behavior } from '@superset-ui/core'; +import { t } from '@superset-ui/core'; import React, { useEffect, useState } from 'react'; import { Slider } from 'src/common/components'; import { PluginFilterRangeProps } from './types'; @@ -31,12 +31,12 @@ export default function RangeFilterPlugin(props: PluginFilterRangeProps) { width, setDataMask, inputRef, - behaviors, + filterState, } = props; const [row] = data; // @ts-ignore const { min, max }: { min: number; max: number } = row; - const { groupby, currentValue, defaultValue } = formData; + const { groupby, defaultValue } = formData; const [col = ''] = groupby || []; const [value, setValue] = useState<[number, number]>( defaultValue ?? [min, max], @@ -45,23 +45,13 @@ export default function RangeFilterPlugin(props: PluginFilterRangeProps) { const handleAfterChange = (value: [number, number]) => { const [lower, upper] = value; setValue(value); - const dataMask = { + + setDataMask({ extraFormData: getRangeExtraFormData(col, lower, upper), - currentState: { + filterState: { value, }, - }; - - const dataMaskObject: DataMask = {}; - if (behaviors.includes(Behavior.NATIVE_FILTER)) { - dataMaskObject.nativeFilters = dataMask; - } - - if (behaviors.includes(Behavior.CROSS_FILTER)) { - dataMaskObject.crossFilters = dataMask; - } - - setDataMask(dataMaskObject); + }); }; const handleChange = (value: [number, number]) => { @@ -69,8 +59,8 @@ export default function RangeFilterPlugin(props: PluginFilterRangeProps) { }; useEffect(() => { - handleAfterChange(currentValue ?? [min, max]); - }, [JSON.stringify(currentValue)]); + handleAfterChange(filterState.value ?? [min, max]); + }, [JSON.stringify(filterState.value)]); useEffect(() => { handleAfterChange(defaultValue ?? [min, max]); diff --git a/superset-frontend/src/filters/components/Range/index.ts b/superset-frontend/src/filters/components/Range/index.ts index 59a92f0111e..5762810daaf 100644 --- a/superset-frontend/src/filters/components/Range/index.ts +++ b/superset-frontend/src/filters/components/Range/index.ts @@ -27,7 +27,7 @@ export default class RangeFilterPlugin extends ChartPlugin { const metadata = new ChartMetadata({ name: t('Range filter'), description: t('Range filter plugin using AntD'), - behaviors: [Behavior.CROSS_FILTER, Behavior.NATIVE_FILTER], + behaviors: [Behavior.INTERACTIVE_CHART, Behavior.NATIVE_FILTER], thumbnail, }); diff --git a/superset-frontend/src/filters/components/Range/transformProps.ts b/superset-frontend/src/filters/components/Range/transformProps.ts index 56b8bc04941..afaa5d14fd7 100644 --- a/superset-frontend/src/filters/components/Range/transformProps.ts +++ b/superset-frontend/src/filters/components/Range/transformProps.ts @@ -19,7 +19,15 @@ import { ChartProps } from '@superset-ui/core'; export default function transformProps(chartProps: ChartProps) { - const { formData, height, hooks, queriesData, width, behaviors } = chartProps; + const { + formData, + height, + hooks, + queriesData, + width, + behaviors, + filterState, + } = chartProps; const { setDataMask } = hooks; const { data } = queriesData[0]; @@ -29,6 +37,7 @@ export default function transformProps(chartProps: ChartProps) { behaviors, height, setDataMask, + filterState, width, }; } diff --git a/superset-frontend/src/filters/components/Range/types.ts b/superset-frontend/src/filters/components/Range/types.ts index a3b9d56b9c7..38e445a9dd8 100644 --- a/superset-frontend/src/filters/components/Range/types.ts +++ b/superset-frontend/src/filters/components/Range/types.ts @@ -17,10 +17,11 @@ * under the License. */ import { + Behavior, DataRecord, + FilterState, QueryFormData, SetDataMaskHook, - Behavior, } from '@superset-ui/core'; import { RefObject } from 'react'; import { PluginFilterStylesProps } from '../types'; @@ -38,6 +39,7 @@ export type PluginFilterRangeProps = PluginFilterStylesProps & { data: DataRecord[]; formData: PluginFilterRangeQueryFormData; setDataMask: SetDataMaskHook; + filterState: FilterState; behaviors: Behavior[]; inputRef: RefObject; }; diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx index da0bdf1c618..6f777efb252 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx @@ -18,8 +18,6 @@ */ import { AppSection, - Behavior, - DataMask, ensureIsArray, GenericDataType, smartDateDetailedFormatter, @@ -41,8 +39,8 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { formData, height, width, - behaviors, setDataMask, + filterState, appSection, } = props; const { @@ -50,7 +48,6 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { enableEmptyFilter, multiSelect, showSearch, - currentValue, inverseSelection, inputRef, defaultToFirstItem, @@ -100,38 +97,27 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { const emptyFilter = enableEmptyFilter && !inverseSelection && selectValue?.length === 0; - const dataMask = { + setDataMask({ extraFormData: getSelectExtraFormData( col, selectValue, emptyFilter, inverseSelection, ), - currentState: { + filterState: { // We need to save in state `FIRST_VALUE` as some const and not as REAL value, // because when FiltersBar check if all filters initialized it compares `defaultValue` with this value // and because REAL value can be unpredictable for users that have different data for same dashboard we use `FIRST_VALUE` value: stateValue, }, - }; - - const dataMaskObject: DataMask = {}; - if (behaviors.includes(Behavior.NATIVE_FILTER)) { - dataMaskObject.nativeFilters = dataMask; - } - - if (behaviors.includes(Behavior.CROSS_FILTER)) { - dataMaskObject.crossFilters = dataMask; - } - - setDataMask(dataMaskObject); + }); }; useEffect(() => { // For currentValue we need set always `FIRST_VALUE` only if we in config modal for `defaultToFirstItem` mode - handleChange(forceFirstValue ? FIRST_VALUE : currentValue ?? []); + handleChange(forceFirstValue ? FIRST_VALUE : filterState.value ?? []); }, [ - JSON.stringify(currentValue), + JSON.stringify(filterState.value), defaultToFirstItem, multiSelect, enableEmptyFilter, diff --git a/superset-frontend/src/filters/components/Select/buildQuery.ts b/superset-frontend/src/filters/components/Select/buildQuery.ts index ad3cbb6658a..15256266be2 100644 --- a/superset-frontend/src/filters/components/Select/buildQuery.ts +++ b/superset-frontend/src/filters/components/Select/buildQuery.ts @@ -22,7 +22,7 @@ import { DEFAULT_FORM_DATA, PluginFilterSelectQueryFormData } from './types'; export default function buildQuery(formData: PluginFilterSelectQueryFormData) { const { sortAscending } = { ...DEFAULT_FORM_DATA, ...formData }; return buildQueryContext(formData, baseQueryObject => { - const { columns, filters = [] } = baseQueryObject; + const { columns = [], filters = [] } = baseQueryObject; return [ { ...baseQueryObject, diff --git a/superset-frontend/src/filters/components/Select/index.ts b/superset-frontend/src/filters/components/Select/index.ts index 73b9ffa992f..96513cc21f8 100644 --- a/superset-frontend/src/filters/components/Select/index.ts +++ b/superset-frontend/src/filters/components/Select/index.ts @@ -27,7 +27,7 @@ export default class FilterSelectPlugin extends ChartPlugin { const metadata = new ChartMetadata({ name: t('Select filter'), description: t('Select filter plugin using AntD'), - behaviors: [Behavior.CROSS_FILTER, Behavior.NATIVE_FILTER], + behaviors: [Behavior.INTERACTIVE_CHART, Behavior.NATIVE_FILTER], thumbnail, }); diff --git a/superset-frontend/src/filters/components/Select/transformProps.ts b/superset-frontend/src/filters/components/Select/transformProps.ts index 4bf27d008b4..4c57e86ac2f 100644 --- a/superset-frontend/src/filters/components/Select/transformProps.ts +++ b/superset-frontend/src/filters/components/Select/transformProps.ts @@ -30,6 +30,7 @@ export default function transformProps( width, behaviors, appSection, + filterState, } = chartProps; const newFormData = { ...DEFAULT_FORM_DATA, ...formData }; const { setDataMask = () => {} } = hooks; @@ -41,6 +42,7 @@ export default function transformProps( ); return { + filterState, coltypeMap, appSection, width, diff --git a/superset-frontend/src/filters/components/Select/types.ts b/superset-frontend/src/filters/components/Select/types.ts index 8542e988fa5..306a7368081 100644 --- a/superset-frontend/src/filters/components/Select/types.ts +++ b/superset-frontend/src/filters/components/Select/types.ts @@ -18,9 +18,10 @@ */ import { AppSection, - ChartProps, Behavior, + ChartProps, DataRecord, + FilterState, GenericDataType, QueryFormData, SetDataMaskHook, @@ -34,7 +35,6 @@ export type SelectValue = (number | string)[] | null; interface PluginFilterSelectCustomizeProps { defaultValue?: SelectValue | typeof FIRST_VALUE; - currentValue?: SelectValue | typeof FIRST_VALUE; enableEmptyFilter: boolean; inverseSelection: boolean; multiSelect: boolean; @@ -58,11 +58,11 @@ export type PluginFilterSelectProps = PluginFilterStylesProps & { behaviors: Behavior[]; appSection: AppSection; formData: PluginFilterSelectQueryFormData; + filterState: FilterState; }; export const DEFAULT_FORM_DATA: PluginFilterSelectCustomizeProps = { defaultValue: null, - currentValue: null, enableEmptyFilter: false, inverseSelection: false, defaultToFirstItem: false, diff --git a/superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx b/superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx index 469d3e75354..949cf25e6be 100644 --- a/superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { styled, DataMask, Behavior } from '@superset-ui/core'; +import { styled } from '@superset-ui/core'; import React, { useState, useEffect } from 'react'; import DateFilterControl from 'src/explore/components/controls/DateFilterControl'; import { PluginFilterTimeProps } from './types'; @@ -29,37 +29,25 @@ const TimeFilterStyles = styled(Styles)` `; export default function TimeFilterPlugin(props: PluginFilterTimeProps) { - const { formData, setDataMask, width, behaviors } = props; - const { defaultValue, currentValue } = formData; + const { formData, setDataMask, width, filterState } = props; + const { defaultValue } = formData; const [value, setValue] = useState(defaultValue ?? DEFAULT_VALUE); const handleTimeRangeChange = (timeRange: string): void => { setValue(timeRange); - const dataMask = { + + setDataMask({ extraFormData: { - override_form_data: { - time_range: timeRange, - }, + time_range: timeRange, }, - currentState: { value: timeRange }, - }; - - const dataMaskObject: DataMask = {}; - if (behaviors.includes(Behavior.NATIVE_FILTER)) { - dataMaskObject.nativeFilters = dataMask; - } - - if (behaviors.includes(Behavior.CROSS_FILTER)) { - dataMaskObject.crossFilters = dataMask; - } - - setDataMask(dataMaskObject); + filterState: { value: timeRange }, + }); }; useEffect(() => { - handleTimeRangeChange(currentValue ?? DEFAULT_VALUE); - }, [currentValue]); + handleTimeRangeChange(filterState.value ?? DEFAULT_VALUE); + }, [filterState.value]); useEffect(() => { handleTimeRangeChange(defaultValue ?? DEFAULT_VALUE); diff --git a/superset-frontend/src/filters/components/Time/index.ts b/superset-frontend/src/filters/components/Time/index.ts index 4099e400e21..4b72113ac8d 100644 --- a/superset-frontend/src/filters/components/Time/index.ts +++ b/superset-frontend/src/filters/components/Time/index.ts @@ -26,7 +26,7 @@ export default class TimeFilterPlugin extends ChartPlugin { const metadata = new ChartMetadata({ name: t('Time filter'), description: t('Custom time filter plugin'), - behaviors: [Behavior.CROSS_FILTER, Behavior.NATIVE_FILTER], + behaviors: [Behavior.INTERACTIVE_CHART, Behavior.NATIVE_FILTER], thumbnail, datasourceCount: 0, }); diff --git a/superset-frontend/src/filters/components/Time/transformProps.ts b/superset-frontend/src/filters/components/Time/transformProps.ts index 1cdd7e6d20f..93a1dfb51b8 100644 --- a/superset-frontend/src/filters/components/Time/transformProps.ts +++ b/superset-frontend/src/filters/components/Time/transformProps.ts @@ -20,12 +20,21 @@ import { ChartProps } from '@superset-ui/core'; import { DEFAULT_FORM_DATA } from './types'; export default function transformProps(chartProps: ChartProps) { - const { formData, height, hooks, queriesData, width, behaviors } = chartProps; + const { + formData, + height, + hooks, + queriesData, + width, + behaviors, + filterState, + } = chartProps; const { setDataMask = () => {} } = hooks; const { data } = queriesData[0]; return { data, + filterState, formData: { ...DEFAULT_FORM_DATA, ...formData, diff --git a/superset-frontend/src/filters/components/Time/types.ts b/superset-frontend/src/filters/components/Time/types.ts index bb22498d808..98617a3a0ba 100644 --- a/superset-frontend/src/filters/components/Time/types.ts +++ b/superset-frontend/src/filters/components/Time/types.ts @@ -17,16 +17,16 @@ * under the License. */ import { - QueryFormData, - DataRecord, - SetDataMaskHook, Behavior, + DataRecord, + FilterState, + QueryFormData, + SetDataMaskHook, } from '@superset-ui/core'; import { PluginFilterStylesProps } from '../types'; interface PluginFilterTimeCustomizeProps { defaultValue?: string | null; - currentValue?: string | null; } export type PluginFilterSelectQueryFormData = QueryFormData & @@ -38,9 +38,9 @@ export type PluginFilterTimeProps = PluginFilterStylesProps & { data: DataRecord[]; setDataMask: SetDataMaskHook; formData: PluginFilterSelectQueryFormData; + filterState: FilterState; }; export const DEFAULT_FORM_DATA: PluginFilterTimeCustomizeProps = { defaultValue: null, - currentValue: null, }; diff --git a/superset-frontend/src/filters/components/TimeColumn/TimeColumnFilterPlugin.tsx b/superset-frontend/src/filters/components/TimeColumn/TimeColumnFilterPlugin.tsx index 2f63c26976a..2b5164ce6ec 100644 --- a/superset-frontend/src/filters/components/TimeColumn/TimeColumnFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/TimeColumn/TimeColumnFilterPlugin.tsx @@ -17,9 +17,8 @@ * under the License. */ import { - Behavior, - DataMask, ensureIsArray, + ExtraFormData, GenericDataType, t, tn, @@ -34,41 +33,30 @@ const { Option } = Select; export default function PluginFilterTimeColumn( props: PluginFilterTimeColumnProps, ) { - const { behaviors, data, formData, height, width, setDataMask } = props; - const { defaultValue, currentValue, inputRef } = formData; + const { data, formData, height, width, setDataMask, filterState } = props; + const { defaultValue, inputRef } = formData; const [value, setValue] = useState(defaultValue ?? []); const handleChange = (value?: string[] | string | null) => { const resultValue: string[] = ensureIsArray(value); setValue(resultValue); + const extraFormData: ExtraFormData = {}; + if (resultValue.length) { + extraFormData.granularity_sqla = resultValue[0]; + } - const dataMask = { - extraFormData: { - override_form_data: { - granularity_sqla: resultValue.length ? resultValue[0] : null, - }, - }, - currentState: { + setDataMask({ + extraFormData, + filterState: { value: resultValue.length ? resultValue : null, }, - }; - - const dataMaskObject: DataMask = {}; - if (behaviors.includes(Behavior.NATIVE_FILTER)) { - dataMaskObject.nativeFilters = dataMask; - } - - if (behaviors.includes(Behavior.CROSS_FILTER)) { - dataMaskObject.crossFilters = dataMask; - } - - setDataMask(dataMaskObject); + }); }; useEffect(() => { - handleChange(currentValue ?? null); - }, [JSON.stringify(currentValue)]); + handleChange(filterState.value ?? null); + }, [JSON.stringify(filterState.value)]); useEffect(() => { handleChange(defaultValue ?? null); diff --git a/superset-frontend/src/filters/components/TimeColumn/index.ts b/superset-frontend/src/filters/components/TimeColumn/index.ts index ac09b6580e0..492f48f05e7 100644 --- a/superset-frontend/src/filters/components/TimeColumn/index.ts +++ b/superset-frontend/src/filters/components/TimeColumn/index.ts @@ -27,7 +27,7 @@ export default class FilterTimeColumnPlugin extends ChartPlugin { const metadata = new ChartMetadata({ name: t('Time column'), description: t('Time column filter plugin'), - behaviors: [Behavior.CROSS_FILTER, Behavior.NATIVE_FILTER], + behaviors: [Behavior.INTERACTIVE_CHART, Behavior.NATIVE_FILTER], thumbnail, }); diff --git a/superset-frontend/src/filters/components/TimeColumn/transformProps.ts b/superset-frontend/src/filters/components/TimeColumn/transformProps.ts index e6ae61f9464..ee075bbe4a2 100644 --- a/superset-frontend/src/filters/components/TimeColumn/transformProps.ts +++ b/superset-frontend/src/filters/components/TimeColumn/transformProps.ts @@ -20,12 +20,21 @@ import { ChartProps } from '@superset-ui/core'; import { DEFAULT_FORM_DATA } from './types'; export default function transformProps(chartProps: ChartProps) { - const { behaviors, formData, height, hooks, queriesData, width } = chartProps; + const { + behaviors, + formData, + height, + hooks, + queriesData, + width, + filterState, + } = chartProps; const { setDataMask = () => {} } = hooks; const { data } = queriesData[0]; return { + filterState, behaviors, width, height, diff --git a/superset-frontend/src/filters/components/TimeColumn/types.ts b/superset-frontend/src/filters/components/TimeColumn/types.ts index 954d686167f..39ef136c977 100644 --- a/superset-frontend/src/filters/components/TimeColumn/types.ts +++ b/superset-frontend/src/filters/components/TimeColumn/types.ts @@ -17,17 +17,17 @@ * under the License. */ import { - QueryFormData, - DataRecord, - SetDataMaskHook, Behavior, + DataRecord, + FilterState, + QueryFormData, + SetDataMaskHook, } from '@superset-ui/core'; import { RefObject } from 'react'; import { PluginFilterStylesProps } from '../types'; interface PluginFilterTimeColumnCustomizeProps { defaultValue?: string[] | null; - currentValue?: string[] | null; inputRef?: RefObject; } @@ -39,10 +39,10 @@ export type PluginFilterTimeColumnProps = PluginFilterStylesProps & { behaviors: Behavior[]; data: DataRecord[]; setDataMask: SetDataMaskHook; + filterState: FilterState; formData: PluginFilterTimeColumnQueryFormData; }; export const DEFAULT_FORM_DATA: PluginFilterTimeColumnCustomizeProps = { defaultValue: null, - currentValue: null, }; diff --git a/superset-frontend/src/filters/components/TimeGrain/TimeGrainFilterPlugin.tsx b/superset-frontend/src/filters/components/TimeGrain/TimeGrainFilterPlugin.tsx index ce890712d4a..e2dd8e9041c 100644 --- a/superset-frontend/src/filters/components/TimeGrain/TimeGrainFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/TimeGrain/TimeGrainFilterPlugin.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { ensureIsArray, QueryObjectExtras, t, tn } from '@superset-ui/core'; +import { ensureIsArray, ExtraFormData, t, tn } from '@superset-ui/core'; import React, { useEffect, useState } from 'react'; import { Select } from 'src/common/components'; import { Styles, StyledSelect } from '../common'; @@ -27,8 +27,8 @@ const { Option } = Select; export default function PluginFilterTimegrain( props: PluginFilterTimeGrainProps, ) { - const { data, formData, height, width, setDataMask } = props; - const { defaultValue, currentValue, inputRef } = formData; + const { data, formData, height, width, setDataMask, filterState } = props; + const { defaultValue, inputRef } = formData; const [value, setValue] = useState(defaultValue ?? []); @@ -36,28 +36,22 @@ export default function PluginFilterTimegrain( const resultValue: string[] = ensureIsArray(values); const [timeGrain] = resultValue; - const extras: QueryObjectExtras = {}; + const extraFormData: ExtraFormData = {}; if (timeGrain) { - extras.time_grain_sqla = timeGrain; + extraFormData.time_grain_sqla = timeGrain; } setValue(resultValue); setDataMask({ - nativeFilters: { - extraFormData: { - override_form_data: { - extras, - }, - }, - currentState: { - value: resultValue.length ? resultValue : null, - }, + extraFormData, + filterState: { + value: resultValue.length ? resultValue : null, }, }); }; useEffect(() => { - handleChange(currentValue ?? []); - }, [JSON.stringify(currentValue)]); + handleChange(filterState.value ?? []); + }, [JSON.stringify(filterState.value)]); useEffect(() => { handleChange(defaultValue ?? []); diff --git a/superset-frontend/src/filters/components/TimeGrain/index.ts b/superset-frontend/src/filters/components/TimeGrain/index.ts index 9839c7dacce..8ce943ffbc9 100644 --- a/superset-frontend/src/filters/components/TimeGrain/index.ts +++ b/superset-frontend/src/filters/components/TimeGrain/index.ts @@ -27,7 +27,7 @@ export default class FilterTimeGrainPlugin extends ChartPlugin { const metadata = new ChartMetadata({ name: t('Time grain'), description: t('Time grain filter plugin'), - behaviors: [Behavior.CROSS_FILTER, Behavior.NATIVE_FILTER], + behaviors: [Behavior.INTERACTIVE_CHART, Behavior.NATIVE_FILTER], thumbnail, }); diff --git a/superset-frontend/src/filters/components/TimeGrain/transformProps.ts b/superset-frontend/src/filters/components/TimeGrain/transformProps.ts index b7aef808f6e..6a4a9d79f5b 100644 --- a/superset-frontend/src/filters/components/TimeGrain/transformProps.ts +++ b/superset-frontend/src/filters/components/TimeGrain/transformProps.ts @@ -20,12 +20,20 @@ import { ChartProps } from '@superset-ui/core'; import { DEFAULT_FORM_DATA } from './types'; export default function transformProps(chartProps: ChartProps) { - const { formData, height, hooks, queriesData, width } = chartProps; + const { + formData, + height, + hooks, + queriesData, + width, + filterState, + } = chartProps; const { setDataMask = () => {} } = hooks; const { data } = queriesData[0]; return { + filterState, width, height, data, diff --git a/superset-frontend/src/filters/components/TimeGrain/types.ts b/superset-frontend/src/filters/components/TimeGrain/types.ts index a1f3f39698a..35a887c80f6 100644 --- a/superset-frontend/src/filters/components/TimeGrain/types.ts +++ b/superset-frontend/src/filters/components/TimeGrain/types.ts @@ -16,13 +16,17 @@ * specific language governing permissions and limitations * under the License. */ -import { QueryFormData, DataRecord, SetDataMaskHook } from '@superset-ui/core'; +import { + FilterState, + QueryFormData, + DataRecord, + SetDataMaskHook, +} from '@superset-ui/core'; import { RefObject } from 'react'; import { PluginFilterStylesProps } from '../types'; interface PluginFilterTimeGrainCustomizeProps { defaultValue?: string[] | null; - currentValue?: string[] | null; inputRef?: RefObject; } @@ -33,10 +37,10 @@ export type PluginFilterTimeGrainQueryFormData = QueryFormData & export type PluginFilterTimeGrainProps = PluginFilterStylesProps & { data: DataRecord[]; setDataMask: SetDataMaskHook; + filterState: FilterState; formData: PluginFilterTimeGrainQueryFormData; }; export const DEFAULT_FORM_DATA: PluginFilterTimeGrainCustomizeProps = { defaultValue: null, - currentValue: null, }; diff --git a/superset-frontend/src/filters/utils.ts b/superset-frontend/src/filters/utils.ts index 186cd8aec81..6ca88dd058d 100644 --- a/superset-frontend/src/filters/utils.ts +++ b/superset-frontend/src/filters/utils.ts @@ -22,6 +22,7 @@ import { NumberFormatter, QueryObjectFilterClause, TimeFormatter, + ExtraFormData, } from '@superset-ui/core'; import { FALSE_STRING, NULL_STRING, TRUE_STRING } from 'src/utils/common'; @@ -30,30 +31,30 @@ export const getSelectExtraFormData = ( value?: null | (string | number)[], emptyFilter = false, inverseSelection = false, -) => ({ - append_form_data: emptyFilter - ? { - adhoc_filters: [ - { - expressionType: 'SQL', - clause: 'WHERE', - sqlExpression: '1 = 0', - }, - ], - } - : { - filters: - value === undefined || value === null || value.length === 0 - ? [] - : [ - { - col, - op: inverseSelection ? ('NOT IN' as const) : ('IN' as const), - val: value, - }, - ], +): ExtraFormData => { + const extra: ExtraFormData = {}; + if (emptyFilter) { + extra.adhoc_filters = [ + { + expressionType: 'SQL', + clause: 'WHERE', + sqlExpression: '1 = 0', }, -}); + ]; + } else { + extra.filters = + value === undefined || value === null || value.length === 0 + ? [] + : [ + { + col, + op: inverseSelection ? ('NOT IN' as const) : ('IN' as const), + val: value, + }, + ]; + } + return extra; +}; export const getRangeExtraFormData = ( col: string, @@ -69,9 +70,7 @@ export const getRangeExtraFormData = ( } return { - append_form_data: { - filters, - }, + filters, }; }; diff --git a/superset/constants.py b/superset/constants.py index 7e8a379c6cf..0c9016fb553 100644 --- a/superset/constants.py +++ b/superset/constants.py @@ -117,3 +117,33 @@ MODEL_API_RW_METHOD_PERMISSION_MAP = { "get_charts": "read", "get_datasets": "read", } + +EXTRA_FORM_DATA_APPEND_KEYS = { + "adhoc_filters", + "filters", + "interactive_groupby", + "interactive_highlight", + "interactive_drilldown", + "custom_form_data", +} + +EXTRA_FORM_DATA_OVERRIDE_REGULAR_MAPPINGS = { + "granularity": "granularity", + "granularity_sqla": "granularity", + "time_column": "time_column", + "time_grain": "time_grain", + "time_range": "time_range", +} + +EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS = { + "druid_time_origin", + "relative_start", + "relative_end", + "time_grain_sqla", + "time_range_endpoints", +} + +EXTRA_FORM_DATA_OVERRIDE_KEYS = ( + set(EXTRA_FORM_DATA_OVERRIDE_REGULAR_MAPPINGS.values()) + | EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS +) diff --git a/superset/migrations/versions/fc3a3a8ff221_migrate_filter_sets_to_new_format.py b/superset/migrations/versions/fc3a3a8ff221_migrate_filter_sets_to_new_format.py new file mode 100644 index 00000000000..79b032894f0 --- /dev/null +++ b/superset/migrations/versions/fc3a3a8ff221_migrate_filter_sets_to_new_format.py @@ -0,0 +1,233 @@ +# 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. +"""migrate filter sets to new format + +Revision ID: fc3a3a8ff221 +Revises: 085f06488938 +Create Date: 2021-04-12 12:38:03.913514 + +""" + +# revision identifiers, used by Alembic. +revision = "fc3a3a8ff221" +down_revision = "085f06488938" + +import json +from typing import Any, Dict, Iterable + +from alembic import op +from sqlalchemy import Column, Integer, Text +from sqlalchemy.ext.declarative import declarative_base + +from superset import db + +Base = declarative_base() + + +class Dashboard(Base): + """Declarative class to do query in upgrade""" + + __tablename__ = "dashboards" + id = Column(Integer, primary_key=True) + json_metadata = Column(Text) + + +# these are copied over from `superset/constants.py` to make sure they stay unchanged +EXTRA_FORM_DATA_APPEND_KEYS = { + "adhoc_filters", + "filters", + "interactive_groupby", + "interactive_highlight", + "interactive_drilldown", + "custom_form_data", +} + +EXTRA_FORM_DATA_OVERRIDE_REGULAR_KEYS = { + "granularity", + "granularity_sqla", + "time_column", + "time_grain", + "time_range", +} + +EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS = { + "druid_time_origin", + "relative_start", + "relative_end", + "time_grain_sqla", + "time_range_endpoints", +} + +EXTRA_FORM_DATA_OVERRIDE_KEYS = ( + EXTRA_FORM_DATA_OVERRIDE_REGULAR_KEYS | EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS +) + + +def upgrade_select_filters(native_filters: Iterable[Dict[str, Any]]) -> None: + """ + Add `defaultToFirstItem` to `controlValues` of `select_filter` components + """ + for native_filter in native_filters: + filter_type = native_filter.get("filterType") + if filter_type == "filter_select": + control_values = native_filter.get("controlValues", {}) + value = control_values.get("defaultToFirstItem", False) + control_values["defaultToFirstItem"] = value + + +def upgrade_filter_set(filter_set: Dict[str, Any]) -> int: + changed_filters = 0 + upgrade_select_filters(filter_set.get("nativeFilters", {}).values()) + data_mask = filter_set.get("dataMask", {}) + native_filters = data_mask.pop("nativeFilters", {}) + for filter_id, filter_obj in native_filters.items(): + changed_filters += 1 + # move filter up one level + data_mask[filter_id] = filter_obj + + # rename currentState to filterState + current_state = filter_obj.pop("currentState", {}) + filter_obj["filterState"] = current_state + + # create new extraFormData field + old_extra_form_data = filter_obj.pop("extraFormData", {}) + extra_form_data = {} + filter_obj["extraFormData"] = extra_form_data + + # upgrade append filters + appends = old_extra_form_data.pop("append_form_data", {}) + extra_form_data.update(appends) + + # upgrade override filters + overrides = old_extra_form_data.pop("override_form_data", {}) + for override_key, override_value in overrides.items(): + # nested extras are also moved up to main object + if override_key == "extras": + for extra_key, extra_value in override_value.items(): + extra_form_data[extra_key] = extra_value + else: + extra_form_data[override_key] = override_value + return changed_filters + + +def downgrade_filter_set(filter_set: Dict[str, Any]) -> int: + changed_filters = 0 + old_data_mask = filter_set.pop("dataMask", {}) + native_filters = {} + data_mask = {"nativeFilters": native_filters} + filter_set["dataMask"] = data_mask + for filter_id, filter_obj in old_data_mask.items(): + changed_filters += 1 + # move filter object down one level + native_filters[filter_id] = filter_obj + + # downgrade filter state + filter_state = filter_obj.pop("filterState", {}) + filter_obj["currentState"] = filter_state + + old_extra_form_data = filter_obj.pop("extraFormData", {}) + extra_form_data = {} + filter_obj["extraFormData"] = extra_form_data + + # downgrade append keys + append_form_data = {} + extra_form_data["append_form_data"] = append_form_data + for key in EXTRA_FORM_DATA_APPEND_KEYS: + value = old_extra_form_data.pop(key, None) + if value is not None: + append_form_data[key] = value + if not append_form_data: + del extra_form_data["append_form_data"] + + # downgrade override keys + override_form_data = {} + extra_form_data["override_form_data"] = override_form_data + for key in EXTRA_FORM_DATA_OVERRIDE_KEYS: + value = old_extra_form_data.pop(key, None) + if key in EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS: + extras = override_form_data.get("extras", {}) + extras[key] = value + elif value is not None: + override_form_data[key] = value + if not override_form_data: + del extra_form_data["override_form_data"] + + return changed_filters + + +def upgrade(): + bind = op.get_bind() + session = db.Session(bind=bind) + + dashboards = ( + session.query(Dashboard) + .filter(Dashboard.json_metadata.like('%"filter_sets_configuration"%')) + .all() + ) + changed_filter_sets, changed_filters = 0, 0 + for dashboard in dashboards: + try: + json_metadata = json.loads(dashboard.json_metadata) + + # upgrade native select filter metadata + native_filters = json_metadata.get("native_filter_configuration") + if native_filters: + upgrade_select_filters(native_filters) + + # upgrade filter sets + filter_sets = json_metadata["filter_sets_configuration"] + for filter_set in filter_sets: + changed_filter_sets += 1 + changed_filters += upgrade_filter_set(filter_set) + + dashboard.json_metadata = json.dumps(json_metadata, sort_keys=True) + + except Exception as e: + print(f"Parsing json_metadata for dashboard {dashboard.id} failed.") + raise e + + session.commit() + session.close() + print(f"Updated {changed_filter_sets} filter sets with {changed_filters} filters.") + + +def downgrade(): + bind = op.get_bind() + session = db.Session(bind=bind) + + dashboards = ( + session.query(Dashboard) + .filter(Dashboard.json_metadata.like('%"filter_sets_configuration"%')) + .all() + ) + changed_filter_sets, changed_filters = 0, 0 + for dashboard in dashboards: + try: + json_metadata = json.loads(dashboard.json_metadata) + filter_sets = json_metadata.get("filter_sets_configuration", {}) + json_metadata["filter_sets_configuration"] = filter_sets + for filter_set in filter_sets: + changed_filter_sets += 1 + changed_filters += downgrade_filter_set(filter_set) + dashboard.json_metadata = json.dumps(json_metadata, sort_keys=True) + except Exception as e: + print(f"Parsing json_metadata for dashboard {dashboard.id} failed.") + raise e + + session.commit() + session.close() + print(f"Updated {changed_filter_sets} filter sets with {changed_filters} filters.") diff --git a/superset/utils/core.py b/superset/utils/core.py index 882423ed51a..0c60d05f264 100644 --- a/superset/utils/core.py +++ b/superset/utils/core.py @@ -86,6 +86,11 @@ from sqlalchemy.types import TEXT, TypeDecorator, TypeEngine from typing_extensions import TypedDict import _thread # pylint: disable=C0411 +from superset.constants import ( + EXTRA_FORM_DATA_APPEND_KEYS, + EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS, + EXTRA_FORM_DATA_OVERRIDE_REGULAR_MAPPINGS, +) from superset.errors import ErrorLevel, SupersetErrorType from superset.exceptions import ( CertificateException, @@ -1061,39 +1066,36 @@ def merge_extra_form_data(form_data: Dict[str, Any]) -> None: Merge extra form data (appends and overrides) into the main payload and add applied time extras to the payload. """ - time_extras = { - "granularity": "__granularity", - "granularity_sqla": "__granularity", - "time_range": "__time_range", - } - allowed_extra_overrides: Dict[str, Optional[str]] = { - "time_grain_sqla": "__time_grain", - "druid_time_origin": "__time_origin", - "time_range_endpoints": None, - } - - applied_time_extras = form_data.get("applied_time_extras", {}) - form_data["applied_time_extras"] = applied_time_extras + filter_keys = ["filters", "adhoc_filters"] extra_form_data = form_data.pop("extra_form_data", {}) - append_form_data = extra_form_data.pop("append_form_data", {}) - append_filters = append_form_data.get("filters", None) - override_form_data = extra_form_data.pop("override_form_data", {}) - for key, value in override_form_data.items(): - form_data[key] = value - # mark as temporal overrides as applied time extras - time_extra = time_extras.get(key) - if time_extra: - applied_time_extras[time_extra] = value + append_filters = extra_form_data.get("filters", None) + + # merge append extras + for key in [key for key in EXTRA_FORM_DATA_APPEND_KEYS if key not in filter_keys]: + extra_value = getattr(extra_form_data, key, {}) + form_value = getattr(form_data, key, {}) + form_value.update(extra_value) + if form_value: + form_data["key"] = extra_value + + # map regular extras that apply to form data properties + for src_key, target_key in EXTRA_FORM_DATA_OVERRIDE_REGULAR_MAPPINGS.items(): + value = extra_form_data.get(src_key) + if value is not None: + form_data[target_key] = value + + # map extras that apply to form data extra properties extras = form_data.get("extras", {}) - for key, value in allowed_extra_overrides.items(): + for key in EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS: + value = extra_form_data.get(key) extra = extras.get(key) if value and extra: - applied_time_extras[value] = extra + extras[key] = value form_data.update(extras) adhoc_filters = form_data.get("adhoc_filters", []) form_data["adhoc_filters"] = adhoc_filters - append_adhoc_filters = append_form_data.get("adhoc_filters", []) + append_adhoc_filters = extra_form_data.get("adhoc_filters", []) adhoc_filters.extend({"isExtra": True, **fltr} for fltr in append_adhoc_filters) if append_filters: adhoc_filters.extend( diff --git a/tests/migrations/__init__.py b/tests/migrations/__init__.py new file mode 100644 index 00000000000..13a83393a91 --- /dev/null +++ b/tests/migrations/__init__.py @@ -0,0 +1,16 @@ +# 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. diff --git a/tests/migrations/fc3a3a8ff221_tests.py b/tests/migrations/fc3a3a8ff221_tests.py new file mode 100644 index 00000000000..5a5e0cc71e6 --- /dev/null +++ b/tests/migrations/fc3a3a8ff221_tests.py @@ -0,0 +1,364 @@ +# 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 copy import deepcopy + +from superset.migrations.versions.fc3a3a8ff221_migrate_filter_sets_to_new_format import ( + downgrade_filter_set, + upgrade_filter_set, + upgrade_select_filters, +) + +native_filters_v1 = [ + { + "cascadeParentIds": [], + "controlValues": { + "enableEmptyFilter": False, + "inverseSelection": False, + "multiSelect": True, + "sortAscending": True, + }, + "defaultValue": None, + "filterType": "filter_select", + "id": "NATIVE_FILTER-CZpnK0rM-", + "isInstant": True, + "name": "Region", + "scope": {"excluded": [], "rootPath": ["ROOT_ID"]}, + "targets": [{"column": {"name": "region"}, "datasetId": 2}], + }, + { + "cascadeParentIds": [], + "defaultValue": "No filter", + "filterType": "filter_time", + "id": "NATIVE_FILTER-gCMse9C7e", + "isInstant": True, + "name": "Time Range", + "scope": {"excluded": [], "rootPath": ["ROOT_ID"]}, + "targets": [{}], + }, + { + "cascadeParentIds": ["NATIVE_FILTER-CZpnK0rM-"], + "controlValues": { + "defaultToFirstItem": False, + "enableEmptyFilter": False, + "inverseSelection": False, + "multiSelect": True, + "sortAscending": True, + }, + "defaultValue": None, + "filterType": "filter_select", + "id": "NATIVE_FILTER-oQRgQ25Au", + "isInstant": True, + "name": "Country", + "scope": {"excluded": [], "rootPath": ["ROOT_ID"]}, + "targets": [{"column": {"name": "country_name"}, "datasetId": 2}], + }, +] +native_filters_v2 = [ + { + "cascadeParentIds": [], + "controlValues": { + "defaultToFirstItem": False, + "enableEmptyFilter": False, + "inverseSelection": False, + "multiSelect": True, + "sortAscending": True, + }, + "defaultValue": None, + "filterType": "filter_select", + "id": "NATIVE_FILTER-CZpnK0rM-", + "isInstant": True, + "name": "Region", + "scope": {"excluded": [], "rootPath": ["ROOT_ID"]}, + "targets": [{"column": {"name": "region"}, "datasetId": 2}], + }, + { + "cascadeParentIds": [], + "defaultValue": "No filter", + "filterType": "filter_time", + "id": "NATIVE_FILTER-gCMse9C7e", + "isInstant": True, + "name": "Time Range", + "scope": {"excluded": [], "rootPath": ["ROOT_ID"]}, + "targets": [{}], + }, + { + "cascadeParentIds": ["NATIVE_FILTER-CZpnK0rM-"], + "controlValues": { + "defaultToFirstItem": False, + "enableEmptyFilter": False, + "inverseSelection": False, + "multiSelect": True, + "sortAscending": True, + }, + "defaultValue": None, + "filterType": "filter_select", + "id": "NATIVE_FILTER-oQRgQ25Au", + "isInstant": True, + "name": "Country", + "scope": {"excluded": [], "rootPath": ["ROOT_ID"]}, + "targets": [{"column": {"name": "country_name"}, "datasetId": 2}], + }, +] + +filter_sets_v1 = { + "name": "New filter set", + "id": "FILTERS_SET-tt_Ovwy95", + "nativeFilters": { + "NATIVE_FILTER-tx05Ze2Hm": { + "id": "NATIVE_FILTER-tx05Ze2Hm", + "name": "Time range", + "filterType": "filter_time", + "targets": [{}], + "defaultValue": "No filter", + "cascadeParentIds": [], + "scope": {"excluded": [], "rootPath": ["ROOT_ID"]}, + "isInstant": False, + }, + "NATIVE_FILTER-JeZ9HYoTP": { + "cascadeParentIds": [], + "controlValues": { + "enableEmptyFilter": False, + "inverseSelection": False, + "multiSelect": True, + "sortAscending": True, + }, + "defaultValue": None, + "filterType": "filter_select", + "id": "NATIVE_FILTER-JeZ9HYoTP", + "isInstant": False, + "name": "Platform", + "scope": {"excluded": [], "rootPath": ["ROOT_ID"]}, + "targets": [{"column": {"name": "platform"}, "datasetId": 33}], + }, + "NATIVE_FILTER-B2PFYVIUw": { + "cascadeParentIds": [], + "controlValues": { + "enableEmptyFilter": False, + "inverseSelection": False, + "multiSelect": True, + "sortAscending": True, + }, + "defaultValue": None, + "filterType": "filter_select", + "id": "NATIVE_FILTER-B2PFYVIUw", + "isInstant": False, + "name": "Genre", + "scope": {"excluded": [], "rootPath": ["ROOT_ID"]}, + "targets": [{"column": {"name": "genre"}, "datasetId": 33}], + }, + "NATIVE_FILTER-VDLd4Wq-v": { + "cascadeParentIds": [], + "controlValues": { + "enableEmptyFilter": False, + "inverseSelection": False, + "multiSelect": True, + "sortAscending": True, + }, + "defaultValue": None, + "filterType": "filter_select", + "id": "NATIVE_FILTER-VDLd4Wq-v", + "isInstant": False, + "name": "Publisher", + "scope": {"excluded": [], "rootPath": ["ROOT_ID"]}, + "targets": [{"column": {"name": "publisher"}, "datasetId": 33}], + }, + }, + "dataMask": { + "nativeFilters": { + "NATIVE_FILTER-tx05Ze2Hm": { + "extraFormData": {"override_form_data": {"time_range": "No filter"}}, + "currentState": {"value": "No filter"}, + "id": "NATIVE_FILTER-tx05Ze2Hm", + }, + "NATIVE_FILTER-B2PFYVIUw": { + "extraFormData": { + "append_form_data": { + "filters": [ + { + "col": "genre", + "op": "IN", + "val": ["Adventure", "Fighting", "Misc"], + } + ] + } + }, + "currentState": {"value": ["Adventure", "Fighting", "Misc"]}, + "id": "NATIVE_FILTER-B2PFYVIUw", + }, + "NATIVE_FILTER-VDLd4Wq-v": { + "extraFormData": {"append_form_data": {"filters": []}}, + "currentState": {"value": None}, + "id": "NATIVE_FILTER-VDLd4Wq-v", + }, + "NATIVE_FILTER-JeZ9HYoTP": { + "extraFormData": { + "append_form_data": { + "filters": [ + { + "col": "platform", + "op": "IN", + "val": ["GB", "GBA", "PSV", "DS", "3DS"], + } + ] + } + }, + "currentState": {"value": ["GB", "GBA", "PSV", "DS", "3DS"]}, + "id": "NATIVE_FILTER-JeZ9HYoTP", + }, + } + }, +} + +filter_sets_v2 = { + "name": "New filter set", + "id": "FILTERS_SET-tt_Ovwy95", + "nativeFilters": { + "NATIVE_FILTER-tx05Ze2Hm": { + "id": "NATIVE_FILTER-tx05Ze2Hm", + "name": "Time range", + "filterType": "filter_time", + "targets": [{}], + "defaultValue": "No filter", + "cascadeParentIds": [], + "scope": {"excluded": [], "rootPath": ["ROOT_ID"]}, + "isInstant": False, + }, + "NATIVE_FILTER-JeZ9HYoTP": { + "cascadeParentIds": [], + "controlValues": { + "enableEmptyFilter": False, + "inverseSelection": False, + "multiSelect": True, + "sortAscending": True, + "defaultToFirstItem": False, + }, + "defaultValue": None, + "filterType": "filter_select", + "id": "NATIVE_FILTER-JeZ9HYoTP", + "isInstant": False, + "name": "Platform", + "scope": {"excluded": [], "rootPath": ["ROOT_ID"]}, + "targets": [{"column": {"name": "platform"}, "datasetId": 33}], + }, + "NATIVE_FILTER-B2PFYVIUw": { + "cascadeParentIds": [], + "controlValues": { + "enableEmptyFilter": False, + "inverseSelection": False, + "multiSelect": True, + "sortAscending": True, + "defaultToFirstItem": False, + }, + "defaultValue": None, + "filterType": "filter_select", + "id": "NATIVE_FILTER-B2PFYVIUw", + "isInstant": False, + "name": "Genre", + "scope": {"excluded": [], "rootPath": ["ROOT_ID"]}, + "targets": [{"column": {"name": "genre"}, "datasetId": 33}], + }, + "NATIVE_FILTER-VDLd4Wq-v": { + "cascadeParentIds": [], + "controlValues": { + "enableEmptyFilter": False, + "inverseSelection": False, + "multiSelect": True, + "sortAscending": True, + "defaultToFirstItem": False, + }, + "defaultValue": None, + "filterType": "filter_select", + "id": "NATIVE_FILTER-VDLd4Wq-v", + "isInstant": False, + "name": "Publisher", + "scope": {"excluded": [], "rootPath": ["ROOT_ID"]}, + "targets": [{"column": {"name": "publisher"}, "datasetId": 33}], + }, + }, + "dataMask": { + "NATIVE_FILTER-tx05Ze2Hm": { + "id": "NATIVE_FILTER-tx05Ze2Hm", + "filterState": {"value": "No filter"}, + "extraFormData": {"time_range": "No filter"}, + }, + "NATIVE_FILTER-B2PFYVIUw": { + "id": "NATIVE_FILTER-B2PFYVIUw", + "filterState": {"value": ["Adventure", "Fighting", "Misc"]}, + "extraFormData": { + "filters": [ + { + "col": "genre", + "op": "IN", + "val": ["Adventure", "Fighting", "Misc"], + } + ] + }, + }, + "NATIVE_FILTER-VDLd4Wq-v": { + "id": "NATIVE_FILTER-VDLd4Wq-v", + "filterState": {"value": None}, + "extraFormData": {"filters": []}, + }, + "NATIVE_FILTER-JeZ9HYoTP": { + "id": "NATIVE_FILTER-JeZ9HYoTP", + "filterState": {"value": ["GB", "GBA", "PSV", "DS", "3DS"]}, + "extraFormData": { + "filters": [ + { + "col": "platform", + "op": "IN", + "val": ["GB", "GBA", "PSV", "DS", "3DS"], + } + ] + }, + }, + }, +} + + +def test_upgrade_select_filters(): + """ + ensure that controlValue.defaultToFirstItem is added if it's missing + """ + converted_filters = deepcopy(native_filters_v1) + upgrade_select_filters(converted_filters) + assert converted_filters == native_filters_v2 + + +def test_upgrade_filter_sets(): + """ + ensure that filter set upgrade operation produces a object that is compatible + with a currently functioning set + """ + converted_filter_set = deepcopy(filter_sets_v1) + upgrade_filter_set(converted_filter_set) + assert converted_filter_set == filter_sets_v2 + + +def test_downgrade_filter_set(): + """ + ensure that the filter set downgrade operation produces an almost identical dict + as the original value + """ + converted_v1_set = deepcopy(filter_sets_v1) + # upgrade the native filter metadata in the comparison fixture, + # as removing the defaultToFirstItem is not necessary + upgrade_select_filters(converted_v1_set["nativeFilters"].values()) + + converted_filter_set = deepcopy(filter_sets_v2) + downgrade_filter_set(converted_filter_set) + assert converted_filter_set == converted_v1_set diff --git a/tests/utils_tests.py b/tests/utils_tests.py index ee373c25cbe..1041091fd01 100644 --- a/tests/utils_tests.py +++ b/tests/utils_tests.py @@ -911,33 +911,25 @@ class TestUtils(SupersetTestCase): } merge_extra_form_data(form_data) self.assertEqual( - form_data, - { - "time_range": "Last 10 days", - "applied_time_extras": {}, - "adhoc_filters": [], - }, + form_data, {"time_range": "Last 10 days", "adhoc_filters": [],}, ) def test_merge_extra_filters_with_extras(self): form_data = { "time_range": "Last 10 days", "extra_form_data": { - "append_form_data": { - "filters": [{"col": "foo", "op": "IN", "val": ["bar"]}], - "adhoc_filters": [ - { - "expressionType": "SQL", - "clause": "WHERE", - "sqlExpression": "1 = 0", - } - ], - }, - "override_form_data": {"time_range": "Last 100 years",}, + "filters": [{"col": "foo", "op": "IN", "val": ["bar"]}], + "adhoc_filters": [ + { + "expressionType": "SQL", + "clause": "WHERE", + "sqlExpression": "1 = 0", + } + ], + "time_range": "Last 100 years", }, } merge_extra_form_data(form_data) - assert form_data["applied_time_extras"] == {"__time_range": "Last 100 years"} adhoc_filters = form_data["adhoc_filters"] assert adhoc_filters[0] == { "clause": "WHERE",