You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by el...@apache.org on 2021/10/27 21:06:44 UTC

[superset] branch 1.4 updated (739d841 -> e80c8ea)

This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a change to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git.


    from 739d841  fix(build): make npm linking work pt. 2 (#16958)
     new 083ff12  chore:upgrade superset-ui dependencies (#16965)
     new a6a3ced  fix: Revert "fix: RBAC hide right menu (#16902)" (#16968)
     new 6ed84f2  feat: upgrade docker image to py38 and add support for py39 (#16889)
     new b2b3cd7  fix: handle mixed time-series error (#16928)
     new 012f6ac  fix: When click on "View all" from favorite tab, get error (#16988)
     new 8755911  chore: Translates the favorite filter param (#16990)
     new ddb35a3  fix(BigQuery): explicitly quote columns in select_star (#16822)
     new 4d33f7b  fix: check if owners are actually being updated in `PUT /datasets/<id>` (#16941)
     new 19d2fef  fix: rolling and cum operator on multiple series (#16945)
     new 3d77daa  fix(dashboard): Race condition when setting activeTabs with nested tabs (#17007)
     new f56a3d8  bug fix (#17019)
     new bd5d787  fix: Loading indicator of table and schema selectors (#17040)
     new 43e27b1  fix: clear modal state after adding dataset (#17044)
     new ed98027  add logging on successful data uploads (#17065)
     new c03771d  fix: Owners selection in dataset edit UX (#17063)
     new 81da0fb  fix(dashboard): race condition between hydrating dashboard and set active tabs (#17084)
     new 5bfa6e9  fix: Bump FAB to 3.3.4 (#17113)
     new 35dda57  fix: escape bind-like strings in virtual table query (#17111)
     new 4e3fa1a  feat: Custom filters control (#17006)
     new d90bfa6  fix(filter-indicator): show filters handled by jinja as applied (#17140)
     new 23313fd  fix(Explore): Undefined owners (#17167)
     new cf4e129  use typing_extension instead (#17174)
     new f3bde45  fix: Filtering db names while creating dataset is not working (#17023)
     new 3f869ad  fix: error alerts js crash (#17015)
     new 43019a1  fix: error alerts again (#17026)
     new e80c8ea  fix: error alert levels again (#17027)

The 26 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .github/workflows/superset-e2e.yml                 |   2 +-
 .github/workflows/superset-helm-lint.yml           |   2 +-
 .../workflows/superset-python-integrationtest.yml  |   6 +-
 .github/workflows/superset-python-misc.yml         |   6 +-
 .github/workflows/superset-python-unittest.yml     |   2 +-
 .github/workflows/superset-translations.yml        |   2 +-
 Dockerfile                                         |   6 +-
 Makefile                                           |   6 +-
 RELEASING/Dockerfile.from_local_tarball            |   2 +-
 RELEASING/Dockerfile.from_svn_tarball              |   2 +-
 RELEASING/Dockerfile.make_docs                     |   2 +-
 RELEASING/Dockerfile.make_tarball                  |   2 +-
 .../pages/docs/installation/installing_scratch.mdx |   2 +-
 requirements/base.txt                              |   4 +-
 requirements/development.in                        |   2 +-
 requirements/development.txt                       |   4 +-
 setup.py                                           |   5 +-
 superset-frontend/package-lock.json                | 996 +++++++++++----------
 superset-frontend/package.json                     |  56 +-
 superset-frontend/spec/fixtures/mockDatasource.js  |   1 +
 .../dashboard/components/FiltersBadge_spec.tsx     |   1 -
 .../dashboard/components/menu/HoverMenu_spec.tsx   |   9 +-
 .../src/SqlLab/components/ResultSet/index.tsx      |  22 +-
 superset-frontend/src/chart/chartAction.js         |   7 +-
 .../DatabaseSelector/DatabaseSelector.test.tsx     |   8 +-
 .../src/components/DatabaseSelector/index.tsx      |  59 +-
 .../components/ErrorMessage/BasicErrorAlert.tsx    |  11 +-
 .../src/components/ErrorMessage/ErrorAlert.tsx     |  27 +-
 .../src/components/Menu/Menu.test.tsx              |  52 --
 .../src/components/Menu/MenuRight.tsx              | 269 +++---
 superset-frontend/src/components/Select/Select.tsx |  22 +-
 .../TableSelector/TableSelector.test.tsx           |   7 +-
 .../src/components/TableSelector/index.tsx         |  61 +-
 .../src/dashboard/actions/dashboardState.js        |   4 +-
 .../dashboard/components/gridComponents/Tabs.jsx   |  10 +-
 .../src/dashboard/containers/DashboardPage.tsx     |  13 +-
 .../src/dashboard/reducers/dashboardState.js       |   5 +-
 .../dashboard/reducers/dashboardState.test.ts}     |  32 +-
 .../src/datasource/DatasourceEditor.jsx            |  60 +-
 .../src/datasource/DatasourceModal.tsx             |   9 +-
 .../DatasourceControl/DatasourceControl.test.tsx   |   1 +
 .../FilterControl/AdhocFilterControl/index.jsx     |   6 +
 .../FilterControl/AdhocFilterEditPopover/index.jsx | 122 ++-
 .../index.tsx                                      |  46 +-
 .../FilterControl/AdhocFilterOption/index.jsx      |   6 +
 .../AdhocFilterPopoverTrigger/index.tsx            |   5 +
 superset-frontend/src/reduxUtils.ts                |   7 +-
 .../CRUD/data/database/DatabaseModal/index.tsx     |   4 +
 .../views/CRUD/data/dataset/AddDatasetModal.tsx    |  47 +-
 .../src/views/CRUD/data/dataset/DatasetList.tsx    |   3 +-
 .../src/views/CRUD/welcome/ChartTable.tsx          |   4 +-
 .../src/views/CRUD/welcome/DashboardTable.tsx      |   4 +-
 superset-frontend/src/views/menu.tsx               |   7 +-
 superset/common/query_actions.py                   |   7 +-
 superset/common/query_context.py                   |   1 +
 superset/common/utils.py                           |   9 +-
 superset/config.py                                 |   1 +
 superset/connectors/base/models.py                 |  12 +
 superset/connectors/sqla/models.py                 |  17 +-
 superset/db_engine_specs/bigquery.py               | 120 ++-
 superset/db_engine_specs/gsheets.py                |   9 +-
 superset/jinja_context.py                          |   8 +
 superset/models/helpers.py                         |   2 +
 superset/result_set.py                             |   4 +-
 superset/utils/core.py                             |  29 +
 superset/utils/memoized.py                         |   4 +-
 superset/utils/pandas_postprocessing.py            |  60 +-
 superset/views/core.py                             |   3 +
 superset/views/database/views.py                   |  22 +-
 superset/viz.py                                    |  25 +-
 tests/integration_tests/csv_upload_tests.py        |  67 +-
 tests/integration_tests/fixtures/dataframes.py     |  16 +
 tests/integration_tests/jinja_context_tests.py     |   5 +
 .../pandas_postprocessing_tests.py                 | 118 +++
 tests/unit_tests/db_engine_specs/test_bigquery.py  | 190 ++++
 75 files changed, 1766 insertions(+), 1023 deletions(-)
 copy superset-frontend/{spec/javascripts/dashboard/util/getChartAndLabelComponentIdFromPath_spec.js => src/dashboard/reducers/dashboardState.test.ts} (57%)
 create mode 100644 tests/unit_tests/db_engine_specs/test_bigquery.py

[superset] 25/26: fix: error alerts again (#17026)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 43019a1b9d9f2b0f4ca5e2b180cda8529bb45d33
Author: Erik Ritter <er...@airbnb.com>
AuthorDate: Thu Oct 7 15:30:56 2021 -0700

    fix: error alerts again (#17026)
---
 superset-frontend/src/components/ErrorMessage/BasicErrorAlert.tsx | 2 +-
 superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx      | 8 ++++++++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/superset-frontend/src/components/ErrorMessage/BasicErrorAlert.tsx b/superset-frontend/src/components/ErrorMessage/BasicErrorAlert.tsx
index b32196b..2817c50 100644
--- a/superset-frontend/src/components/ErrorMessage/BasicErrorAlert.tsx
+++ b/superset-frontend/src/components/ErrorMessage/BasicErrorAlert.tsx
@@ -57,7 +57,7 @@ export default function BasicErrorAlert({
   return (
     <StyledContainer level={level} role="alert">
       {!level || level === 'error' ? (
-        <Icons.ErrorSolid iconColor={supersetTheme.colors[level].base} />
+        <Icons.ErrorSolid iconColor={supersetTheme.colors.error.base} />
       ) : (
         <Icons.WarningSolid iconColor={supersetTheme.colors[level].base} />
       )}
diff --git a/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx b/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
index 7620682..0d37e8a 100644
--- a/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
+++ b/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
@@ -109,7 +109,11 @@ export default function ErrorAlert({
           {!level || level === 'error' ? (
             <Icons.ErrorSolid
               className="icon"
+<<<<<<< HEAD
               iconColor={supersetTheme.colors[level].base}
+=======
+              iconColor={theme.colors.error.base}
+>>>>>>> ea700384b... fix: error alerts again (#17026)
             />
           ) : (
             <Icons.WarningSolid
@@ -172,7 +176,11 @@ export default function ErrorAlert({
               {!level || level === 'error' ? (
                 <Icons.ErrorSolid
                   className="icon"
+<<<<<<< HEAD
                   iconColor={supersetTheme.colors[level].base}
+=======
+                  iconColor={theme.colors.error.base}
+>>>>>>> ea700384b... fix: error alerts again (#17026)
                 />
               ) : (
                 <Icons.WarningSolid

[superset] 05/26: fix: When click on "View all" from favorite tab, get error (#16988)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 012f6ac206330e355168f0be078f2b9cad2e1c72
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Wed Oct 6 08:18:56 2021 -0300

    fix: When click on "View all" from favorite tab, get error (#16988)
    
    (cherry picked from commit c57719128f5208d83ad15268b3d2bb4d00b62954)
---
 superset-frontend/src/views/CRUD/welcome/ChartTable.tsx     | 2 +-
 superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
index b7d81b3..60a9181 100644
--- a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
@@ -218,7 +218,7 @@ function ChartTable({
             onClick: () => {
               const target =
                 chartFilter === 'Favorite'
-                  ? '/chart/list/?filters=(favorite:!t)'
+                  ? '/chart/list/?filters=(favorite:(label:Yes,value:!t))'
                   : '/chart/list/';
               history.push(target);
             },
diff --git a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
index 4f8c1ff..0dd594d 100644
--- a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
@@ -220,7 +220,7 @@ function DashboardTable({
             onClick: () => {
               const target =
                 dashboardFilter === 'Favorite'
-                  ? '/dashboard/list/?filters=(favorite:!t)'
+                  ? '/dashboard/list/?filters=(favorite:(label:Yes,value:!t))'
                   : '/dashboard/list/';
               history.push(target);
             },

[superset] 22/26: use typing_extension instead (#17174)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit cf4e129f5d5e4cfde9e3f48a257cd974e96a5e38
Author: Hugh A. Miles II <hu...@gmail.com>
AuthorDate: Wed Oct 20 15:55:36 2021 -0400

    use typing_extension instead (#17174)
    
    (cherry picked from commit aa0f4d6c4fd1d705691a4e33d757528b0924f689)
---
 superset/config.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/superset/config.py b/superset/config.py
index 7f50458..54545df 100644
--- a/superset/config.py
+++ b/superset/config.py
@@ -38,6 +38,7 @@ from dateutil import tz
 from flask import Blueprint
 from flask_appbuilder.security.manager import AUTH_DB
 from pandas.io.parsers import STR_NA_VALUES
+from typing_extensions import Literal
 from werkzeug.local import LocalProxy
 
 from superset.jinja_context import BaseTemplateProcessor

[superset] 06/26: chore: Translates the favorite filter param (#16990)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 8755911765d39574d39d63dbf9c951896b055750
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Wed Oct 6 11:04:14 2021 -0300

    chore: Translates the favorite filter param (#16990)
    
    (cherry picked from commit 191033cb4496876ce594f2b758c1c3f14dfada68)
---
 superset-frontend/src/views/CRUD/welcome/ChartTable.tsx     | 4 +++-
 superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx | 4 +++-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
index 60a9181..c673d2a 100644
--- a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
@@ -218,7 +218,9 @@ function ChartTable({
             onClick: () => {
               const target =
                 chartFilter === 'Favorite'
-                  ? '/chart/list/?filters=(favorite:(label:Yes,value:!t))'
+                  ? `/chart/list/?filters=(favorite:(label:${t(
+                      'Yes',
+                    )},value:!t))`
                   : '/chart/list/';
               history.push(target);
             },
diff --git a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
index 0dd594d..f4a3d9d 100644
--- a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
@@ -220,7 +220,9 @@ function DashboardTable({
             onClick: () => {
               const target =
                 dashboardFilter === 'Favorite'
-                  ? '/dashboard/list/?filters=(favorite:(label:Yes,value:!t))'
+                  ? `/dashboard/list/?filters=(favorite:(label:${t(
+                      'Yes',
+                    )},value:!t))`
                   : '/dashboard/list/';
               history.push(target);
             },

[superset] 21/26: fix(Explore): Undefined owners (#17167)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 23313fdb1f350e42f3039c87a307ac36b61f67df
Author: Geido <60...@users.noreply.github.com>
AuthorDate: Wed Oct 20 18:45:58 2021 +0300

    fix(Explore): Undefined owners (#17167)
    
    * Reconcile owners data
    
    * Fix on save
    
    (cherry picked from commit f580f6bcba15a20c12ead5b3e6eef643cc0aa1d6)
---
 superset-frontend/src/datasource/DatasourceEditor.jsx | 4 ++--
 superset-frontend/src/datasource/DatasourceModal.tsx  | 7 +++++--
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/superset-frontend/src/datasource/DatasourceEditor.jsx b/superset-frontend/src/datasource/DatasourceEditor.jsx
index e725cdc..e80e402 100644
--- a/superset-frontend/src/datasource/DatasourceEditor.jsx
+++ b/superset-frontend/src/datasource/DatasourceEditor.jsx
@@ -409,8 +409,8 @@ class DatasourceEditor extends React.PureComponent {
       datasource: {
         ...props.datasource,
         owners: props.datasource.owners.map(owner => ({
-          value: owner.id,
-          label: `${owner.first_name} ${owner.last_name}`,
+          value: owner.value || owner.id,
+          label: owner.label || `${owner.first_name} ${owner.last_name}`,
         })),
         metrics: props.datasource.metrics?.map(metric => {
           const {
diff --git a/superset-frontend/src/datasource/DatasourceModal.tsx b/superset-frontend/src/datasource/DatasourceModal.tsx
index 5cd6f3a..89b7b5d 100644
--- a/superset-frontend/src/datasource/DatasourceModal.tsx
+++ b/superset-frontend/src/datasource/DatasourceModal.tsx
@@ -119,14 +119,17 @@ const DatasourceModal: FunctionComponent<DatasourceModalProps> = ({
           ),
           type: currentDatasource.type || currentDatasource.datasource_type,
           owners: currentDatasource.owners.map(
-            (o: { label: string; value: number }) => o.value,
+            (o: Record<string, number>) => o.value || o.id,
           ),
         },
       },
     })
       .then(({ json }) => {
         addSuccessToast(t('The dataset has been saved'));
-        onDatasourceSave(json);
+        onDatasourceSave({
+          ...json,
+          owners: currentDatasource.owners,
+        });
         onHide();
       })
       .catch(response => {

[superset] 16/26: fix(dashboard): race condition between hydrating dashboard and set active tabs (#17084)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 81da0fb4666aed26e5415336a7bb9c825a3de051
Author: Kamil Gabryjelski <ka...@gmail.com>
AuthorDate: Wed Oct 13 20:18:44 2021 +0200

    fix(dashboard): race condition between hydrating dashboard and set active tabs (#17084)
    
    * fix(dashboard): race condition between hydrating dashboard and set active tabs
    
    * Fix linting
    
    * Change variable name
    
    (cherry picked from commit 3ad7483dc19b75afe40df80eab10523c9a13485f)
---
 .../src/dashboard/containers/DashboardPage.tsx     | 13 ++++----
 .../src/dashboard/reducers/dashboardState.js       | 12 +++----
 .../src/dashboard/reducers/dashboardState.test.ts  | 38 ++++++++++++++++++++++
 superset-frontend/src/reduxUtils.ts                |  7 +++-
 4 files changed, 54 insertions(+), 16 deletions(-)

diff --git a/superset-frontend/src/dashboard/containers/DashboardPage.tsx b/superset-frontend/src/dashboard/containers/DashboardPage.tsx
index 01f4886..4cb1e5b 100644
--- a/superset-frontend/src/dashboard/containers/DashboardPage.tsx
+++ b/superset-frontend/src/dashboard/containers/DashboardPage.tsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { useEffect, FC } from 'react';
+import React, { useEffect, useRef, FC } from 'react';
 import { t } from '@superset-ui/core';
 import { useDispatch } from 'react-redux';
 import { useParams } from 'react-router-dom';
@@ -57,17 +57,16 @@ const DashboardPage: FC = () => {
   const { result: datasets, error: datasetsApiError } = useDashboardDatasets(
     idOrSlug,
   );
+  const isDashboardHydrated = useRef(false);
 
   const error = dashboardApiError || chartsApiError;
   const readyToRender = Boolean(dashboard && charts);
   const { dashboard_title, css } = dashboard || {};
 
-  useEffect(() => {
-    if (readyToRender) {
-      dispatch(hydrateDashboard(dashboard, charts));
-    }
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [readyToRender]);
+  if (readyToRender && !isDashboardHydrated.current) {
+    isDashboardHydrated.current = true;
+    dispatch(hydrateDashboard(dashboard, charts));
+  }
 
   useEffect(() => {
     if (dashboard_title) {
diff --git a/superset-frontend/src/dashboard/reducers/dashboardState.js b/superset-frontend/src/dashboard/reducers/dashboardState.js
index 4b0a401..0f1d784 100644
--- a/superset-frontend/src/dashboard/reducers/dashboardState.js
+++ b/superset-frontend/src/dashboard/reducers/dashboardState.js
@@ -150,16 +150,12 @@ export default function dashboardStateReducer(state = {}, action) {
       };
     },
     [SET_ACTIVE_TABS]() {
-      const prevActiveTabs = state.activeTabs ?? [];
-      const newActiveTabs = action.prevTabId
-        ? [
-            ...prevActiveTabs.filter(tabId => tabId !== action.prevTabId),
-            action.tabId,
-          ]
-        : [...prevActiveTabs, action.tabId];
+      const newActiveTabs = new Set(state.activeTabs);
+      newActiveTabs.delete(action.prevTabId);
+      newActiveTabs.add(action.tabId);
       return {
         ...state,
-        activeTabs: newActiveTabs,
+        activeTabs: Array.from(newActiveTabs),
       };
     },
     [SET_FOCUSED_FILTER_FIELD]() {
diff --git a/superset-frontend/src/dashboard/reducers/dashboardState.test.ts b/superset-frontend/src/dashboard/reducers/dashboardState.test.ts
new file mode 100644
index 0000000..274b267
--- /dev/null
+++ b/superset-frontend/src/dashboard/reducers/dashboardState.test.ts
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import dashboardStateReducer from './dashboardState';
+import { setActiveTabs } from '../actions/dashboardState';
+
+describe('DashboardState reducer', () => {
+  it('SET_ACTIVE_TABS', () => {
+    expect(
+      dashboardStateReducer({ activeTabs: [] }, setActiveTabs('tab1')),
+    ).toEqual({ activeTabs: ['tab1'] });
+    expect(
+      dashboardStateReducer({ activeTabs: ['tab1'] }, setActiveTabs('tab1')),
+    ).toEqual({ activeTabs: ['tab1'] });
+    expect(
+      dashboardStateReducer(
+        { activeTabs: ['tab1'] },
+        setActiveTabs('tab2', 'tab1'),
+      ),
+    ).toEqual({ activeTabs: ['tab2'] });
+  });
+});
diff --git a/superset-frontend/src/reduxUtils.ts b/superset-frontend/src/reduxUtils.ts
index 53d84c6..605e769 100644
--- a/superset-frontend/src/reduxUtils.ts
+++ b/superset-frontend/src/reduxUtils.ts
@@ -141,7 +141,12 @@ export function initEnhancer(
   const composeEnhancers =
     process.env.WEBPACK_MODE === 'development'
       ? /* eslint-disable-next-line no-underscore-dangle, dot-notation */
-        window['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__'] || compose
+        window['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__']
+        ? /* eslint-disable-next-line no-underscore-dangle, dot-notation */
+          window['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__']({
+            trace: true,
+          })
+        : compose
       : compose;
 
   return persist

[superset] 24/26: fix: error alerts js crash (#17015)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 3f869aded203d531b429612e551fe44385dd7200
Author: Erik Ritter <er...@airbnb.com>
AuthorDate: Thu Oct 7 13:09:54 2021 -0700

    fix: error alerts js crash (#17015)
---
 superset-frontend/src/components/ErrorMessage/BasicErrorAlert.tsx | 2 +-
 superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx      | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/superset-frontend/src/components/ErrorMessage/BasicErrorAlert.tsx b/superset-frontend/src/components/ErrorMessage/BasicErrorAlert.tsx
index 0d5d993..b32196b 100644
--- a/superset-frontend/src/components/ErrorMessage/BasicErrorAlert.tsx
+++ b/superset-frontend/src/components/ErrorMessage/BasicErrorAlert.tsx
@@ -56,7 +56,7 @@ export default function BasicErrorAlert({
 }: BasicErrorAlertProps) {
   return (
     <StyledContainer level={level} role="alert">
-      {level === 'error' ? (
+      {!level || level === 'error' ? (
         <Icons.ErrorSolid iconColor={supersetTheme.colors[level].base} />
       ) : (
         <Icons.WarningSolid iconColor={supersetTheme.colors[level].base} />
diff --git a/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx b/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
index 3298fcf..7620682 100644
--- a/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
+++ b/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
@@ -106,7 +106,7 @@ export default function ErrorAlert({
     <ErrorAlertDiv level={level} role="alert">
       <div className="top-row">
         <LeftSideContent>
-          {level === 'error' ? (
+          {!level || level === 'error' ? (
             <Icons.ErrorSolid
               className="icon"
               iconColor={supersetTheme.colors[level].base}
@@ -169,7 +169,7 @@ export default function ErrorAlert({
           onHide={() => setIsModalOpen(false)}
           title={
             <div className="header">
-              {level === 'error' ? (
+              {!level || level === 'error' ? (
                 <Icons.ErrorSolid
                   className="icon"
                   iconColor={supersetTheme.colors[level].base}

[superset] 01/26: chore:upgrade superset-ui dependencies (#16965)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 083ff128642e8bc8dd8d5e26c7757dc49a928dcd
Author: Grace Guo <gr...@airbnb.com>
AuthorDate: Mon Oct 4 15:31:36 2021 -0700

    chore:upgrade superset-ui dependencies (#16965)
    
    (cherry picked from commit 85e3cec521e40b9b2d087c7340900af014815f01)
---
 superset-frontend/package-lock.json | 996 ++++++++++++++++++++----------------
 superset-frontend/package.json      |  56 +-
 2 files changed, 571 insertions(+), 481 deletions(-)

diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json
index a43c3bd..a06c9c0 100644
--- a/superset-frontend/package-lock.json
+++ b/superset-frontend/package-lock.json
@@ -16,35 +16,35 @@
         "@emotion/cache": "^11.4.0",
         "@emotion/react": "^11.4.1",
         "@emotion/styled": "^11.3.0",
-        "@superset-ui/chart-controls": "^0.18.8",
-        "@superset-ui/core": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-calendar": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-chord": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-country-map": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-event-flow": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-force-directed": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-heatmap": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-histogram": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-horizon": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-map-box": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-partition": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-pivot-table": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-rose": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-sankey": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-sunburst": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-treemap": "^0.18.8",
-        "@superset-ui/legacy-plugin-chart-world-map": "^0.18.8",
-        "@superset-ui/legacy-preset-chart-big-number": "^0.18.8",
+        "@superset-ui/chart-controls": "^0.18.9",
+        "@superset-ui/core": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-calendar": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-chord": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-country-map": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-event-flow": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-force-directed": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-heatmap": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-histogram": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-horizon": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-map-box": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-partition": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-pivot-table": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-rose": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-sankey": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-sunburst": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-treemap": "^0.18.9",
+        "@superset-ui/legacy-plugin-chart-world-map": "^0.18.9",
+        "@superset-ui/legacy-preset-chart-big-number": "^0.18.9",
         "@superset-ui/legacy-preset-chart-deckgl": "^0.4.12",
-        "@superset-ui/legacy-preset-chart-nvd3": "^0.18.8",
-        "@superset-ui/plugin-chart-echarts": "^0.18.8",
-        "@superset-ui/plugin-chart-pivot-table": "^0.18.8",
-        "@superset-ui/plugin-chart-table": "^0.18.8",
-        "@superset-ui/plugin-chart-word-cloud": "^0.18.8",
-        "@superset-ui/preset-chart-xy": "^0.18.8",
+        "@superset-ui/legacy-preset-chart-nvd3": "^0.18.9",
+        "@superset-ui/plugin-chart-echarts": "^0.18.9",
+        "@superset-ui/plugin-chart-pivot-table": "^0.18.9",
+        "@superset-ui/plugin-chart-table": "^0.18.9",
+        "@superset-ui/plugin-chart-word-cloud": "^0.18.9",
+        "@superset-ui/preset-chart-xy": "^0.18.9",
         "@vx/responsive": "^0.0.195",
         "abortcontroller-polyfill": "^1.1.9",
         "antd": "^4.9.4",
@@ -3067,18 +3067,21 @@
       }
     },
     "node_modules/@babel/runtime-corejs2": {
-      "version": "7.8.7",
-      "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.8.7.tgz",
-      "integrity": "sha512-R8zbPiv25S0pGfMqAr55dRRxWB8vUeo3wicI4g9PFVBKmsy/9wmQUV1AaYW/kxRHUhx42TTh6F0+QO+4pwfYWg==",
+      "version": "7.15.4",
+      "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.15.4.tgz",
+      "integrity": "sha512-TmuTI+n5HsMesW6Ah2WjvBwix9fBMXwbMxQV3c0ETLAzlmwN4OeRVbYMYwp9P4LEOlAxwGKdd9e8pMiLMAg/Mg==",
       "dependencies": {
         "core-js": "^2.6.5",
         "regenerator-runtime": "^0.13.4"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/runtime-corejs2/node_modules/core-js": {
-      "version": "2.6.11",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
-      "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==",
+      "version": "2.6.12",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
+      "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
       "deprecated": "core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.",
       "hasInstallScript": true
     },
@@ -3237,14 +3240,6 @@
         "react": "^15.0.0-0 || ^16.0.0-0"
       }
     },
-    "node_modules/@data-ui/event-flow/node_modules/immutable": {
-      "version": "3.8.2",
-      "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
-      "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/@data-ui/forms": {
       "version": "0.0.84",
       "resolved": "https://registry.npmjs.org/@data-ui/forms/-/forms-0.0.84.tgz",
@@ -4561,27 +4556,32 @@
       "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
     },
     "node_modules/@encodable/color": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/@encodable/color/-/color-1.1.1.tgz",
-      "integrity": "sha512-3QlHqsaD+D4W4T6E4Wq4mp7MBpt5yCkCmgTh6AfsoUfJeAEogA92d8r3Y67Zuppcs/eepHW0ip8zfehS2jZNkQ==",
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@encodable/color/-/color-1.1.2.tgz",
+      "integrity": "sha512-Ag0C3vfYbl8qQcWpP6VYbM6qTIlonOhTa4zwRtPs9K6GbGFZM+XB/CHQeuklKD+HN2JLVtGH6YkfYKGbcstStw==",
       "dependencies": {
         "@encodable/registry": "^1.0.3",
         "@types/d3-interpolate": "^1.3.1",
         "@types/d3-scale": "^3.1.0",
-        "@types/d3-scale-chromatic": "^1.2.0",
+        "@types/d3-scale-chromatic": "^2.0.0",
         "d3-interpolate": "^2.0.1",
         "d3-scale": "^3.2.1",
         "d3-scale-chromatic": "^2.0.0"
       }
     },
     "node_modules/@encodable/color/node_modules/@types/d3-scale": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.2.2.tgz",
-      "integrity": "sha512-qpQe8G02tzUwt9sdWX1h8A/W0Q1+N48wMnYXVOkrzeLUkCfvzJYV9Ee3aORCS4dN4ONRLFmMvaXdziQ29XGLjQ==",
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.3.2.tgz",
+      "integrity": "sha512-gGqr7x1ost9px3FvIfUMi5XA/F/yAf4UkUDtdQhpH92XCT0Oa7zkkRzY61gPVJq+DxpHn/btouw5ohWkbBsCzQ==",
       "dependencies": {
-        "@types/d3-time": "*"
+        "@types/d3-time": "^2"
       }
     },
+    "node_modules/@encodable/color/node_modules/@types/d3-time": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz",
+      "integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg=="
+    },
     "node_modules/@encodable/color/node_modules/d3-array": {
       "version": "2.12.1",
       "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -4599,17 +4599,25 @@
       }
     },
     "node_modules/@encodable/color/node_modules/d3-scale": {
-      "version": "3.2.3",
-      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.3.tgz",
-      "integrity": "sha512-8E37oWEmEzj57bHcnjPVOBS3n4jqakOeuv1EDdQSiSrYnMCBdMd3nc4HtKk7uia8DUHcY/CGuJ42xxgtEYrX0g==",
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
+      "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==",
       "dependencies": {
         "d3-array": "^2.3.0",
         "d3-format": "1 - 2",
         "d3-interpolate": "1.2.0 - 2",
-        "d3-time": "1 - 2",
+        "d3-time": "^2.1.1",
         "d3-time-format": "2 - 3"
       }
     },
+    "node_modules/@encodable/color/node_modules/d3-time": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+      "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+      "dependencies": {
+        "d3-array": "2"
+      }
+    },
     "node_modules/@encodable/format": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/@encodable/format/-/format-1.0.6.tgz",
@@ -4624,15 +4632,26 @@
         "d3-time-format": "^3.0.0"
       }
     },
+    "node_modules/@encodable/format/node_modules/d3-array": {
+      "version": "2.12.1",
+      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+      "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+      "dependencies": {
+        "internmap": "^1.0.0"
+      }
+    },
     "node_modules/@encodable/format/node_modules/d3-format": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz",
       "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA=="
     },
     "node_modules/@encodable/format/node_modules/d3-time": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.0.0.tgz",
-      "integrity": "sha512-2mvhstTFcMvwStWd9Tj3e6CEqtOivtD8AUiHT8ido/xmzrI9ijrUUihZ6nHuf/vsScRBonagOdj0Vv+SEL5G3Q=="
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+      "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+      "dependencies": {
+        "d3-array": "2"
+      }
     },
     "node_modules/@encodable/format/node_modules/d3-time-format": {
       "version": "3.0.0",
@@ -12593,12 +12612,12 @@
       }
     },
     "node_modules/@superset-ui/chart-controls": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.18.8.tgz",
-      "integrity": "sha512-D64i+VDq3aba9fqqFhlYvxQ71eq2YoyW9iiolaqOh+1hmqDmTVKrmo/bXZRRHewTQgPDMfkrKC6iuzoH3SkmSQ==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.18.9.tgz",
+      "integrity": "sha512-YN+N3JmesVMW+sYbs9jMbA7O7VG530dH7s7J7QGUIfWgEORc4p3if+NMzQTSjUdQtghW3YW7BjhGrDxH5rMetA==",
       "dependencies": {
         "@react-icons/all-files": "^4.1.0",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/core": "0.18.9",
         "lodash": "^4.17.15",
         "prop-types": "^15.7.2"
       },
@@ -12610,9 +12629,9 @@
       }
     },
     "node_modules/@superset-ui/core": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.18.8.tgz",
-      "integrity": "sha512-eRbqy57yhvX2xQgAb4Zf5wmF87BPa+1ua97GFJBbmR+Uc8672nNNolLQAmwP4xoqQxrWqhYIoZzwzQPkRVEGBg==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.18.9.tgz",
+      "integrity": "sha512-x+kqJI/I7ftFHJI7Ku1gLDDsG8HQwfKxYqwzVG+FpO2HwOv9eX+krWwV9+AnCntJs01pQTPyFz2QnVMvHLxaQg==",
       "dependencies": {
         "@babel/runtime": "^7.1.2",
         "@types/d3-format": "^1.3.0",
@@ -12697,12 +12716,12 @@
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-calendar": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.18.8.tgz",
-      "integrity": "sha512-L+I5O6Hl4/XSj+VeNnvWuBMOTmevykapE4DpqhqeuN83ql3Jqxuffk2pzX1xcCR6ED445p6Ea/8mifteJ0vy9g==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.18.9.tgz",
+      "integrity": "sha512-bHYFgGqhfvPOCm7MOSXh+HixTGNuUqs4iWUtrqwsT+3hcmoupcRoSlXPF26MClR35zpvgmdHkD8PZnZa0ty6hA==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3-array": "^2.0.3",
         "d3-selection": "^1.4.0",
         "d3-tip": "^0.9.1",
@@ -12721,24 +12740,24 @@
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-chord": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.18.8.tgz",
-      "integrity": "sha512-2HNvb0uGpCw4yvlCyc0T78CpR/JAoGwvUnnskUIbM2HEKX1kwP2OerxWr8B0eLIBlMN2oorZDVjSOrN4RJajtg==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.18.9.tgz",
+      "integrity": "sha512-dpYL2Nn8Gf24UAa95pNwUzLcAsqJj7wvRMFFz51vrkXbH77z5RBjh83gWqDtHHGW1WdOFi1Ct5L4eLY5nUDJ5g==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "prop-types": "^15.6.2",
         "react": "^16.13.1"
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-country-map": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.18.8.tgz",
-      "integrity": "sha512-jTk0ceADJW6UhZJctU1CEWOVXh4oVW6OhZkbLQRsOCQHTCHQAp0TwzSUZ8/i4A5HIR3r5cp/EZkBz+C8zzb43A==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.18.9.tgz",
+      "integrity": "sha512-kIJAoTqaqP70lMNz9ydwJb2IHykciZld4YqEPNt+GUALoD2/T/f2RVSl9Jqy3lNlkcCNsxzGj0ErpuCYxzPhsw==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "d3-array": "^2.0.3",
         "prop-types": "^15.6.2"
@@ -12753,13 +12772,13 @@
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-event-flow": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.18.8.tgz",
-      "integrity": "sha512-Z5YnUZatr+PjmFZDZ6Rqx3t7XlgDeMPlhw+xJDqjd2YMpCAW0gdtRB3+5iydwA9EAe9sCf9HmlnIyvER304LcQ==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.18.9.tgz",
+      "integrity": "sha512-lYo111sKVAAptEM9QcW4JaiI2bJp9uxtAyscxT7m2yfDdoCN11+fZTN6MB8aAhZbM6C7lVh760V7VdQBGtfq5A==",
       "dependencies": {
         "@data-ui/event-flow": "^0.0.84",
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "prop-types": "^15.6.2"
       },
       "peerDependencies": {
@@ -12767,12 +12786,12 @@
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-force-directed": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.18.8.tgz",
-      "integrity": "sha512-HNXlzxDzVnVVHwBv98AISyLv01wxAv4pA1fSWBKzPap90hBhDBV4Sfw55fqSA4CroGf6qC5lzaRNTx0z6f+XLQ==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.18.9.tgz",
+      "integrity": "sha512-G71bPCk2hKPynLaKZzLLaJ7JShbdxqGymvEG0CnnFtxVh2AOBzG1WJ1ns1CiGkISxZGax6zPzimqvQlrnE2OGQ==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "prop-types": "^15.7.2"
       },
@@ -12781,12 +12800,12 @@
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-heatmap": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.18.8.tgz",
-      "integrity": "sha512-n8iRZ9DBg10+5G9UfONLCRFzJ7yS8264CD8lOWexhokq364Ci1sNFLjPNthosV4E0ONIwgzNJnkOqI4lHnaW7A==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.18.9.tgz",
+      "integrity": "sha512-0tOGMFez0tjOp36oE524vfHP1YKUmYZAB0sOb2vghUVLxEgVeKYbZu33OZtBn4FRb3bsEx7GPSAANfdL2d5Jug==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "d3-svg-legend": "^1.x",
         "d3-tip": "^0.9.1",
@@ -12794,14 +12813,14 @@
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-histogram": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.18.8.tgz",
-      "integrity": "sha512-ZGYr6anCYpxQKHqau3Sf8RYHUOD6U57dM6Prp9t366dNhZvsAw5r3jGi3JaT4N5DhZhtfJB/0zjLZO6WV05DzA==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.18.9.tgz",
+      "integrity": "sha512-jayYp6SVw8pTgFVanNuO/VOv2Zsmah7NcAwOdiOUGbMqIEAzn7qvjdh7oW4Lg5Gk+TO0aysKgrT/DZXuw6zf8g==",
       "dependencies": {
         "@data-ui/histogram": "^0.0.84",
         "@data-ui/theme": "^0.0.84",
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "@vx/legend": "^0.0.198",
         "@vx/responsive": "^0.0.199",
         "@vx/scale": "^0.0.197",
@@ -12879,12 +12898,12 @@
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-horizon": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.18.8.tgz",
-      "integrity": "sha512-3ZD0pfE4KoCH24+9dtbXUa82Y60+MtjMFmLCT+lj4iGQXhlqqG6xqjqzMfjpwH7su+hRdI8xMOGxqvJa53vx8g==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.18.9.tgz",
+      "integrity": "sha512-ax4Hp5jNVT87rsgFs7iTiqq7nknhsn4mPVRr3v7w1eHoYCWCwokEFwrjHZFirTSumsLM8yjkcjjZ95aYPs/8CA==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3-array": "^2.0.3",
         "d3-scale": "^3.0.1",
         "prop-types": "^15.6.2"
@@ -12902,24 +12921,32 @@
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-horizon/node_modules/d3-scale": {
-      "version": "3.2.4",
-      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.4.tgz",
-      "integrity": "sha512-PG6gtpbPCFqKbvdBEswQcJcTzHC8VEd/XzezF5e68KlkT4/ggELw/nR1tv863jY6ufKTvDlzCMZvhe06codbbA==",
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
+      "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==",
       "dependencies": {
         "d3-array": "^2.3.0",
         "d3-format": "1 - 2",
         "d3-interpolate": "1.2.0 - 2",
-        "d3-time": "1 - 2",
+        "d3-time": "^2.1.1",
         "d3-time-format": "2 - 3"
       }
     },
+    "node_modules/@superset-ui/legacy-plugin-chart-horizon/node_modules/d3-time": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+      "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+      "dependencies": {
+        "d3-array": "2"
+      }
+    },
     "node_modules/@superset-ui/legacy-plugin-chart-map-box": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.18.8.tgz",
-      "integrity": "sha512-Q7rQFlRzsPyEKng8RzzAvrnvgnFSdDzAateD7q2scV8tGrZkoUktbs+T845v2jJbIvSHVBc5VUKbplqdgapB4w==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.18.9.tgz",
+      "integrity": "sha512-a+XT5Q1st6h8DjvRwECqcsNr1HhZNRW0C6VZ+G6V79U9bcOnS5FYgpC7nwR1Ul8UKqdVcXO711z3sOJkZOt6ug==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "mapbox-gl": "^0.53.0",
         "prop-types": "^15.6.2",
         "react-map-gl": "^4.0.10",
@@ -12931,12 +12958,12 @@
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-paired-t-test": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.18.8.tgz",
-      "integrity": "sha512-GxsyiMQpgzspXDZXqaosgIDIAG/ahUH7Zb25FWReKMm5+auE+jjCpt6nXDF27KZqdFWcaO6wZWEvQMUARZij5w==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.18.9.tgz",
+      "integrity": "sha512-OUI4ttmUp1kZtrP0eOyb560xsM6RWsVywjIj8TR6uw6+zLxNFW8eFNMzZHaCEJfK6D+RHYzfFBRvnns/8EJQ7g==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "distributions": "^1.0.0",
         "prop-types": "^15.6.2",
         "reactable": "^1.1.0"
@@ -12946,12 +12973,12 @@
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-parallel-coordinates": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.18.8.tgz",
-      "integrity": "sha512-S4hN5aENvRgkqx5ZDYsZaVw/ojQdnz7o1B20nlbFlCW5+qtW53CWsWQlOzbHYvCJt12Wv03+frlPP5YODzuc7g==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.18.9.tgz",
+      "integrity": "sha512-FCueHNN9CIPiPYajATJp064FZPVMDFhXg/5kuD4prsw2uKEnjgQ/YQt/qRWj4E+/MXBCRgcpuyLLSuSItAyyUg==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "prop-types": "^15.7.2"
       },
@@ -12960,12 +12987,12 @@
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-partition": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.18.8.tgz",
-      "integrity": "sha512-1effCYH6nZMp/3GgZLyHinW4W/S0IWklJddGYv9Ww0rpwtRKT7icoDCLVq7HkfYpGxcWdKyfWZkcbrT9emCY2w==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.18.9.tgz",
+      "integrity": "sha512-t/yIIYsJIQ51vIyAoi5uPvRk38vDDSBaofGTKYo6aZtEx7h9wDAfnx0pEHyudk6TpmH1gFqasZ/wNAt3/rkSHA==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "d3-hierarchy": "^1.1.8",
         "prop-types": "^15.6.2"
@@ -12975,24 +13002,24 @@
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-pivot-table": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.18.8.tgz",
-      "integrity": "sha512-cbSERwIqQuCWmrUCDUgp4ugoVxx8AyeUqmkLTSYqvoKkIaBHPoPbW5mR2lsGfqnh0nU1/uBjC4wIUhMkZjAUqg==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.18.9.tgz",
+      "integrity": "sha512-WQK2iKkVZXJzSyteksir2bC8qGhV3l7RsV82N3kEuK+AQSWpIpPEYCMfrQoSPLpSgnOLGT4iieIlxeYEud6hlg==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "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.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.18.8.tgz",
-      "integrity": "sha512-CKHVwr72/+zw6Vbfp/kWOMNZE+ufmUnmL35VH0Dm5gxBd3Nhl2SNg0LrtmzH2FCJuZqjijBCazGEZz+m0pEhcw==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.18.9.tgz",
+      "integrity": "sha512-gYeUYJ8Nbv2hFP6o0L98qo7AOha/n6ZSkL83j9IHIGXsd4lib1OVKwlFgOUBDU0Gs193MV1X34btXfOC/kb5Jg==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "nvd3": "1.8.6",
         "prop-types": "^15.6.2"
@@ -13002,12 +13029,12 @@
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-sankey": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.18.8.tgz",
-      "integrity": "sha512-wwHClWiY/btSu7OABp/7JrdJEh3Q36Dvl6IT2PXv8T2dybdQDnxd6PfSmypsCUBj+dq/0tLhVau5IG4zTpGPeA==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.18.9.tgz",
+      "integrity": "sha512-xZv4XT3IcGAlObu2PqoPrk3HXo6nrNgFHEGtqbbAB0lVnn7yvnHPTtOK2yyOZxTNIANZPyNTYiVJcGW7LlOGpQ==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "d3-sankey": "^0.4.2",
         "prop-types": "^15.6.2"
@@ -13017,47 +13044,47 @@
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-sankey-loop": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.18.8.tgz",
-      "integrity": "sha512-kOS5FNamyyxgu5VsFRYMjwC7pPlwjp+3MuyVZcamqWq21ydZt5DSRfJLi7QPt81lDxkp0PhFcy+SEhOwOKzQFQ==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.18.9.tgz",
+      "integrity": "sha512-DGQ9Jug4bDJPle6fSEieCfdkNQyXDq2xIf/aoRkSGd1Dt1MqMJIgR4JqwtFfyqQcB5gX+p9OFuCIiWB3eSNtdQ==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "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.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.18.8.tgz",
-      "integrity": "sha512-468nblN6ddB/Jmovq3nnknupGGMCNlBb/zhvU4H1CE7YWMqf0qCPu6cqN6CRc0RB0Fyz7JW5kDGGs4eOMt3gaw==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.18.9.tgz",
+      "integrity": "sha512-05Sq2JdE9ETsQIIbhxIbLIIamMVgQGaB6hDtUvGqNWvBKC+xA2OZXq1013vzvy8sXPKL8lRhubwyJWNkP/Zi1g==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "prop-types": "^15.6.2"
       }
     },
     "node_modules/@superset-ui/legacy-plugin-chart-treemap": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.18.8.tgz",
-      "integrity": "sha512-ClRPeCYsNwvpqpeubCcCh4CbY20EYeRGU5IjQsqrhmnCQ0icCx0S4cwb+065zmaybV/XXDzm4+aS2Gk9bHiEJA==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.18.9.tgz",
+      "integrity": "sha512-5YdOS4HNcPCgrojtLIPEofeMuLAMfe7iAl0kMME2M3UHiqocbWhOAXHa/l2yIjhCvPISy+UE6pyphTl7Lnszhw==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "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.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.18.8.tgz",
-      "integrity": "sha512-Sm48+qMyiRCZJ/Iu/JJq2PojktdQXyVyj7sETGwh2asQ+CjwPQvIqE9vqc0iyq64NLTGKuZgLbRmu8s3lSBCRA==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.18.9.tgz",
+      "integrity": "sha512-I2YRQL2V8/qal/9AOnay71xUsDarjkGlMezBzO/zPLABBiLF4m1HkKscKe+3qhxLJvOoSi+XrXC4PL5PMdFJsA==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "d3-array": "^2.4.0",
         "d3-color": "^1.4.1",
@@ -13077,13 +13104,13 @@
       }
     },
     "node_modules/@superset-ui/legacy-preset-chart-big-number": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.18.8.tgz",
-      "integrity": "sha512-V26mespxc5sHW6zZVEVI9l9QI3N7zdXKhO3RfbPSwXZgC7pL7YGRjwdA6KAQYrVaKgyk/7z3chYkmE7wOlQzrg==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.18.9.tgz",
+      "integrity": "sha512-9fTGToCCUFs56WwbVqoPZtCIYWgp/rg4YUaWulNon8W0vlV+5KTQSpfpfpzNLRWy3snK0B7bh6f7ifg80oe8YQ==",
       "dependencies": {
         "@data-ui/xy-chart": "^0.0.84",
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "@types/d3-color": "^1.2.2",
         "@types/shortid": "^0.0.29",
         "d3-color": "^1.2.3",
@@ -13259,13 +13286,13 @@
       }
     },
     "node_modules/@superset-ui/legacy-preset-chart-nvd3": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.18.8.tgz",
-      "integrity": "sha512-De/Cz3tieFWbORCY6fobWMgJlLtKniXQM65VQ/96hljN6RFjXfpss4Ov31bSRFcLcTrdMHWRrjqqvoQf9t9Oqw==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.18.9.tgz",
+      "integrity": "sha512-vOPjKky95NVwdzhE2MidyVWK8l9uDIZ0k84fCo8Uh/deeWYyChqz4E6x4URbOoKJHofU9g5ltQTG7YivnijzzQ==",
       "dependencies": {
         "@data-ui/xy-chart": "^0.0.84",
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "d3-tip": "^0.9.1",
         "dompurify": "^2.0.6",
@@ -13281,12 +13308,12 @@
       }
     },
     "node_modules/@superset-ui/plugin-chart-echarts": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.18.8.tgz",
-      "integrity": "sha512-Es/8rfftpFD+VHQQr2SIZHgTZQcf+Gw85Xt25WSrANqemymacfzL0kFSGJ0SChjMIxm0MPr/LXjthrZdoJ34IA==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.18.9.tgz",
+      "integrity": "sha512-/MeIgvwFVTd7iGdqx5mMchPOCU8OUtpL8lVjssFJaP5Npy2GPpPbBREqYaQdwelvWx1xgRAXKzHBv37CNA6csA==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3-array": "^1.2.0",
         "echarts": "^5.2.1",
         "lodash": "^4.17.15"
@@ -13296,12 +13323,12 @@
       }
     },
     "node_modules/@superset-ui/plugin-chart-pivot-table": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-pivot-table/-/plugin-chart-pivot-table-0.18.8.tgz",
-      "integrity": "sha512-pHtZ6yx3+T8/DHz/4etjMQESl1UkoUvRrurOiLwSX9X6UxsSAjn5aMHBK/9b3O5iVKkTkEAw7H5Prn5JbDpVNQ==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-pivot-table/-/plugin-chart-pivot-table-0.18.9.tgz",
+      "integrity": "sha512-zHHpupS8J0zda3uPqM9u0fcPyskqyttaPGpnoDHSPvZ1Hh7WH2uyhOq09eXZS8DQe8N+NdDeGSx0AV+0k/oIRA==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "@superset-ui/react-pivottable": "^0.12.12"
       },
       "peerDependencies": {
@@ -13310,13 +13337,13 @@
       }
     },
     "node_modules/@superset-ui/plugin-chart-table": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.18.8.tgz",
-      "integrity": "sha512-fet5M030OfL3cOVMEIgApqAvqVJ+Tl1gv6gNqgBgRPpRXATU+JnN3zdzsBuzuHNRLVrmzyos0OAr4OaQ4C0nFA==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.18.9.tgz",
+      "integrity": "sha512-R29s833J46ai6W0DFezMGPiuqPtYssfRab4qIZDiSugEovX5h1+hWJzSMo4rrdZa8SzdbM+b6wffmYJraW00Ew==",
       "dependencies": {
         "@react-icons/all-files": "^4.1.0",
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "@types/d3-array": "^2.9.0",
         "@types/react-table": "^7.0.29",
         "d3-array": "^2.4.0",
@@ -13341,12 +13368,12 @@
       }
     },
     "node_modules/@superset-ui/plugin-chart-word-cloud": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.18.8.tgz",
-      "integrity": "sha512-2YRleSs3VBM4NIXsKuwKfGJmc5FIFtstXKa6P69rjDnZiPZjUKOeiv6UuOxPcW5VOIHJB8LAqC1rpmZ0OzlCUg==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.18.9.tgz",
+      "integrity": "sha512-9hKkvKKycT+ODxRiI/Od8K3Prsbe/ILrOa/Dm7jRcke+DBt0d3EarVFTtt3oI/iGSFSTDtvSaNQkcf32JtQHIg==",
       "dependencies": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "@types/d3-cloud": "^1.2.1",
         "@types/d3-scale": "^2.0.2",
         "d3-cloud": "^1.2.5",
@@ -13367,26 +13394,34 @@
       }
     },
     "node_modules/@superset-ui/plugin-chart-word-cloud/node_modules/d3-scale": {
-      "version": "3.2.4",
-      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.4.tgz",
-      "integrity": "sha512-PG6gtpbPCFqKbvdBEswQcJcTzHC8VEd/XzezF5e68KlkT4/ggELw/nR1tv863jY6ufKTvDlzCMZvhe06codbbA==",
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
+      "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==",
       "dependencies": {
         "d3-array": "^2.3.0",
         "d3-format": "1 - 2",
         "d3-interpolate": "1.2.0 - 2",
-        "d3-time": "1 - 2",
+        "d3-time": "^2.1.1",
         "d3-time-format": "2 - 3"
       }
     },
+    "node_modules/@superset-ui/plugin-chart-word-cloud/node_modules/d3-time": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+      "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+      "dependencies": {
+        "d3-array": "2"
+      }
+    },
     "node_modules/@superset-ui/preset-chart-xy": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.18.8.tgz",
-      "integrity": "sha512-QjS/5OSkpadqUIwWJVuLK49fCc7CGF38kETkrQIHT6kj/xkcTOoQudcPU7RGxnBQimrelmXizWz01pe/zovnhg==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.18.9.tgz",
+      "integrity": "sha512-aC6i1wkXxeEdZqiz71tMJhjqTS/3RAIjgwW5QhVFdvMTa1IjmjtoThbHP0USgDUEuvDIbt5zpQiICHshWHGM7w==",
       "dependencies": {
         "@data-ui/theme": "^0.0.84",
         "@data-ui/xy-chart": "^0.0.84",
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "@vx/axis": "^0.0.198",
         "@vx/legend": "^0.0.198",
         "@vx/scale": "^0.0.197",
@@ -14156,22 +14191,22 @@
       "integrity": "sha512-sdBMGfNvLUkBypPMEhOcKcblTQfgHbqbYrUqRE31jOwdDHBJBxz4co2MDAq93S4Cp++phk4UiwoEg/1hK3xXAQ=="
     },
     "node_modules/@types/d3-cloud": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/@types/d3-cloud/-/d3-cloud-1.2.3.tgz",
-      "integrity": "sha512-VUcRA9YWM1V5Srg444/zVxgmhVyIYBrChIr5lg+0cPGNM4J4fLdBqEeAkk9ggRPM3Um4fUy/p/gtEKbAqL/hcw==",
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/@types/d3-cloud/-/d3-cloud-1.2.5.tgz",
+      "integrity": "sha512-vEIER9DsEBUOdpRiwCh3n1qE+cV6h4e1LhxhY2sLt+m8LPNAIkOOhTlqk0JDiBwD+ZPM8ynFAOU3AuPuVYBFBA==",
       "dependencies": {
         "@types/d3": "^3"
       }
     },
     "node_modules/@types/d3-color": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.1.tgz",
-      "integrity": "sha512-xkPLi+gbgUU9ED6QX4g6jqYL2KCB0/3AlM+ncMGqn49OgH0gFMY/ITGqPF8HwEiLzJaC+2L0I+gNwBgABv1Pvg=="
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.2.tgz",
+      "integrity": "sha512-fYtiVLBYy7VQX+Kx7wU/uOIkGQn8aAEY8oWMoyja3N4dLd8Yf6XgSIR/4yWvMuveNOH5VShnqCgRqqh/UNanBA=="
     },
     "node_modules/@types/d3-format": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.4.1.tgz",
-      "integrity": "sha512-ss9G2snEKmp2In5Z3T0Jpqv8QaDBc2xHltBw83KjnV5B5w+Iwphbvq5ph/Xnu4d03fmmsdt+o1aWch379rxIbA=="
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.4.2.tgz",
+      "integrity": "sha512-WeGCHAs7PHdZYq6lwl/+jsl+Nfc1J2W1kNcMeIMYzQsT6mtBDBgtJ/rcdjZ0k0rVIvqEZqhhuD5TK/v3P2gFHQ=="
     },
     "node_modules/@types/d3-interpolate": {
       "version": "1.4.2",
@@ -14187,22 +14222,22 @@
       "integrity": "sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ=="
     },
     "node_modules/@types/d3-scale": {
-      "version": "2.2.4",
-      "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-2.2.4.tgz",
-      "integrity": "sha512-wkQXT+IfgfAnKB5rtS1qMJg3FS32r1rVFHvqtiqk8pX8o5aQR3VwX1P7ErHjzNIicTlkWsaMiUTrYB+E75HFeA==",
+      "version": "2.2.6",
+      "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-2.2.6.tgz",
+      "integrity": "sha512-CHu34T5bGrJOeuhGxyiz9Xvaa9PlsIaQoOqjDg7zqeGj2x0rwPhGquiy03unigvcMxmvY0hEaAouT0LOFTLpIw==",
       "dependencies": {
         "@types/d3-time": "^1"
       }
     },
     "node_modules/@types/d3-scale-chromatic": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-1.5.1.tgz",
-      "integrity": "sha512-7FtJYrmXTEWLykShjYhoGuDNR/Bda0+tstZMkFj4RRxUEryv16AGh3be21tqg84B6KfEwiZyEpBcTyPyU+GWjg=="
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-2.0.1.tgz",
+      "integrity": "sha512-3EuZlbPu+pvclZcb1DhlymTWT2W+lYsRKBjvkH2ojDbCWDYavifqu1vYX9WGzlPgCgcS4Alhk1+zapXbGEGylQ=="
     },
     "node_modules/@types/d3-shape": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.5.tgz",
-      "integrity": "sha512-aPEax03owTAKynoK8ZkmkZEDZvvT4Y5pWgii4Jp4oQt0gH45j6siDl9gNDVC5kl64XHN2goN9jbYoHK88tFAcA==",
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.8.tgz",
+      "integrity": "sha512-gqfnMz6Fd5H6GOLYixOZP/xlrMtJms9BaS+6oWxTKHNqPGZ93BkWWupQSCYm6YHqx6h9wjRupuJb90bun6ZaYg==",
       "dependencies": {
         "@types/d3-path": "^1"
       }
@@ -21911,17 +21946,17 @@
       }
     },
     "node_modules/datatables.net": {
-      "version": "1.11.2",
-      "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.11.2.tgz",
-      "integrity": "sha512-Dg+Yeyky+BhXoooJqTDTGssLxkAxe73CljcjYd4630XQZKqqqy0akYk2r4RSHSJLyYCOGRZEZiUF/N39+5nqvg==",
+      "version": "1.11.3",
+      "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.11.3.tgz",
+      "integrity": "sha512-VMj5qEaTebpNurySkM6jy6sGpl+s6onPK8xJhYr296R/vUBnz1+id16NVqNf9z5aR076OGcpGHCuiTuy4E05oQ==",
       "dependencies": {
         "jquery": ">=1.7"
       }
     },
     "node_modules/datatables.net-bs": {
-      "version": "1.11.2",
-      "resolved": "https://registry.npmjs.org/datatables.net-bs/-/datatables.net-bs-1.11.2.tgz",
-      "integrity": "sha512-VJfRfJCmZ81XshwxQngdEX6M5YRijkWHKhM9VnefWwZqwbIGiK+ngnmGKoNvnXmvWk4BZ1K0yy3vtkEmhDvvRw==",
+      "version": "1.11.3",
+      "resolved": "https://registry.npmjs.org/datatables.net-bs/-/datatables.net-bs-1.11.3.tgz",
+      "integrity": "sha512-Db1YwAhO0QAWQbZTsKriUrOInT66+xaA+fV616KTKpQt5Zt+p6OsEKK+xv8LxLgG8qu5dPwMBlkhqSiS/hV2sg==",
       "dependencies": {
         "datatables.net": ">=1.10.25",
         "jquery": ">=1.7"
@@ -22590,9 +22625,9 @@
       }
     },
     "node_modules/dompurify": {
-      "version": "2.2.7",
-      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.7.tgz",
-      "integrity": "sha512-jdtDffdGNY+C76jvodNTu9jt5yYj59vuTUyx+wXdzcSwAGTYZDAQkQ7Iwx9zcGrA4ixC1syU4H3RZROqRxokxg=="
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.3.tgz",
+      "integrity": "sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg=="
     },
     "node_modules/domutils": {
       "version": "1.5.1",
@@ -22915,21 +22950,24 @@
       }
     },
     "node_modules/encodable/node_modules/d3-scale": {
-      "version": "3.2.3",
-      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.3.tgz",
-      "integrity": "sha512-8E37oWEmEzj57bHcnjPVOBS3n4jqakOeuv1EDdQSiSrYnMCBdMd3nc4HtKk7uia8DUHcY/CGuJ42xxgtEYrX0g==",
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
+      "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==",
       "dependencies": {
         "d3-array": "^2.3.0",
         "d3-format": "1 - 2",
         "d3-interpolate": "1.2.0 - 2",
-        "d3-time": "1 - 2",
+        "d3-time": "^2.1.1",
         "d3-time-format": "2 - 3"
       }
     },
     "node_modules/encodable/node_modules/d3-time": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.0.0.tgz",
-      "integrity": "sha512-2mvhstTFcMvwStWd9Tj3e6CEqtOivtD8AUiHT8ido/xmzrI9ijrUUihZ6nHuf/vsScRBonagOdj0Vv+SEL5G3Q=="
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+      "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+      "dependencies": {
+        "d3-array": "2"
+      }
     },
     "node_modules/encodeurl": {
       "version": "1.0.2",
@@ -24948,9 +24986,9 @@
       "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw=="
     },
     "node_modules/fast-safe-stringify": {
-      "version": "2.0.7",
-      "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
-      "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA=="
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+      "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
     },
     "node_modules/fastest-levenshtein": {
       "version": "1.0.12",
@@ -27243,6 +27281,14 @@
       "resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-3.1.1.tgz",
       "integrity": "sha512-Q0QaXjPjwIju/28TsugCHNEASwoCcJSyJV3uO1sOIQGI0jKgm9f41Lvz0DZj3n46cNCyAZTsEYoY4C2bVRUzyQ=="
     },
+    "node_modules/immutable": {
+      "version": "3.8.2",
+      "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
+      "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/import-fresh": {
       "version": "3.3.0",
       "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -39989,6 +40035,7 @@
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz",
       "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==",
+      "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.",
       "dev": true,
       "dependencies": {
         "chalk": "^2.4.1",
@@ -41418,9 +41465,9 @@
       }
     },
     "node_modules/ua-parser-js": {
-      "version": "0.7.25",
-      "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.25.tgz",
-      "integrity": "sha512-8NFExdfI24Ny8R3Vc6+uUytP/I7dpqk3JERlvxPWlrtx5YboqCgxAXYKPAifbPLV2zKbgmmPL53ufW7mUC/VOQ==",
+      "version": "0.7.28",
+      "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz",
+      "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==",
       "funding": [
         {
           "type": "opencollective",
@@ -46424,18 +46471,18 @@
       }
     },
     "@babel/runtime-corejs2": {
-      "version": "7.8.7",
-      "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.8.7.tgz",
-      "integrity": "sha512-R8zbPiv25S0pGfMqAr55dRRxWB8vUeo3wicI4g9PFVBKmsy/9wmQUV1AaYW/kxRHUhx42TTh6F0+QO+4pwfYWg==",
+      "version": "7.15.4",
+      "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.15.4.tgz",
+      "integrity": "sha512-TmuTI+n5HsMesW6Ah2WjvBwix9fBMXwbMxQV3c0ETLAzlmwN4OeRVbYMYwp9P4LEOlAxwGKdd9e8pMiLMAg/Mg==",
       "requires": {
         "core-js": "^2.6.5",
         "regenerator-runtime": "^0.13.4"
       },
       "dependencies": {
         "core-js": {
-          "version": "2.6.11",
-          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
-          "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
+          "version": "2.6.12",
+          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
+          "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
         }
       }
     },
@@ -46569,11 +46616,6 @@
           "requires": {
             "lodash": "^4.0.8"
           }
-        },
-        "immutable": {
-          "version": "3.8.2",
-          "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
-          "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM="
         }
       }
     },
@@ -47680,27 +47722,32 @@
       "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
     },
     "@encodable/color": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/@encodable/color/-/color-1.1.1.tgz",
-      "integrity": "sha512-3QlHqsaD+D4W4T6E4Wq4mp7MBpt5yCkCmgTh6AfsoUfJeAEogA92d8r3Y67Zuppcs/eepHW0ip8zfehS2jZNkQ==",
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@encodable/color/-/color-1.1.2.tgz",
+      "integrity": "sha512-Ag0C3vfYbl8qQcWpP6VYbM6qTIlonOhTa4zwRtPs9K6GbGFZM+XB/CHQeuklKD+HN2JLVtGH6YkfYKGbcstStw==",
       "requires": {
         "@encodable/registry": "^1.0.3",
         "@types/d3-interpolate": "^1.3.1",
         "@types/d3-scale": "^3.1.0",
-        "@types/d3-scale-chromatic": "^1.2.0",
+        "@types/d3-scale-chromatic": "^2.0.0",
         "d3-interpolate": "^2.0.1",
         "d3-scale": "^3.2.1",
         "d3-scale-chromatic": "^2.0.0"
       },
       "dependencies": {
         "@types/d3-scale": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.2.2.tgz",
-          "integrity": "sha512-qpQe8G02tzUwt9sdWX1h8A/W0Q1+N48wMnYXVOkrzeLUkCfvzJYV9Ee3aORCS4dN4ONRLFmMvaXdziQ29XGLjQ==",
+          "version": "3.3.2",
+          "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.3.2.tgz",
+          "integrity": "sha512-gGqr7x1ost9px3FvIfUMi5XA/F/yAf4UkUDtdQhpH92XCT0Oa7zkkRzY61gPVJq+DxpHn/btouw5ohWkbBsCzQ==",
           "requires": {
-            "@types/d3-time": "*"
+            "@types/d3-time": "^2"
           }
         },
+        "@types/d3-time": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz",
+          "integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg=="
+        },
         "d3-array": {
           "version": "2.12.1",
           "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -47718,16 +47765,24 @@
           }
         },
         "d3-scale": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.3.tgz",
-          "integrity": "sha512-8E37oWEmEzj57bHcnjPVOBS3n4jqakOeuv1EDdQSiSrYnMCBdMd3nc4HtKk7uia8DUHcY/CGuJ42xxgtEYrX0g==",
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
+          "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==",
           "requires": {
             "d3-array": "^2.3.0",
             "d3-format": "1 - 2",
             "d3-interpolate": "1.2.0 - 2",
-            "d3-time": "1 - 2",
+            "d3-time": "^2.1.1",
             "d3-time-format": "2 - 3"
           }
+        },
+        "d3-time": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+          "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+          "requires": {
+            "d3-array": "2"
+          }
         }
       }
     },
@@ -47745,15 +47800,26 @@
         "d3-time-format": "^3.0.0"
       },
       "dependencies": {
+        "d3-array": {
+          "version": "2.12.1",
+          "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+          "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+          "requires": {
+            "internmap": "^1.0.0"
+          }
+        },
         "d3-format": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz",
           "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA=="
         },
         "d3-time": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.0.0.tgz",
-          "integrity": "sha512-2mvhstTFcMvwStWd9Tj3e6CEqtOivtD8AUiHT8ido/xmzrI9ijrUUihZ6nHuf/vsScRBonagOdj0Vv+SEL5G3Q=="
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+          "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+          "requires": {
+            "d3-array": "2"
+          }
         },
         "d3-time-format": {
           "version": "3.0.0",
@@ -53523,20 +53589,20 @@
       }
     },
     "@superset-ui/chart-controls": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.18.8.tgz",
-      "integrity": "sha512-D64i+VDq3aba9fqqFhlYvxQ71eq2YoyW9iiolaqOh+1hmqDmTVKrmo/bXZRRHewTQgPDMfkrKC6iuzoH3SkmSQ==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.18.9.tgz",
+      "integrity": "sha512-YN+N3JmesVMW+sYbs9jMbA7O7VG530dH7s7J7QGUIfWgEORc4p3if+NMzQTSjUdQtghW3YW7BjhGrDxH5rMetA==",
       "requires": {
         "@react-icons/all-files": "^4.1.0",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/core": "0.18.9",
         "lodash": "^4.17.15",
         "prop-types": "^15.7.2"
       }
     },
     "@superset-ui/core": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.18.8.tgz",
-      "integrity": "sha512-eRbqy57yhvX2xQgAb4Zf5wmF87BPa+1ua97GFJBbmR+Uc8672nNNolLQAmwP4xoqQxrWqhYIoZzwzQPkRVEGBg==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.18.9.tgz",
+      "integrity": "sha512-x+kqJI/I7ftFHJI7Ku1gLDDsG8HQwfKxYqwzVG+FpO2HwOv9eX+krWwV9+AnCntJs01pQTPyFz2QnVMvHLxaQg==",
       "requires": {
         "@babel/runtime": "^7.1.2",
         "@types/d3-format": "^1.3.0",
@@ -53613,12 +53679,12 @@
       }
     },
     "@superset-ui/legacy-plugin-chart-calendar": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.18.8.tgz",
-      "integrity": "sha512-L+I5O6Hl4/XSj+VeNnvWuBMOTmevykapE4DpqhqeuN83ql3Jqxuffk2pzX1xcCR6ED445p6Ea/8mifteJ0vy9g==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.18.9.tgz",
+      "integrity": "sha512-bHYFgGqhfvPOCm7MOSXh+HixTGNuUqs4iWUtrqwsT+3hcmoupcRoSlXPF26MClR35zpvgmdHkD8PZnZa0ty6hA==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3-array": "^2.0.3",
         "d3-selection": "^1.4.0",
         "d3-tip": "^0.9.1",
@@ -53636,24 +53702,24 @@
       }
     },
     "@superset-ui/legacy-plugin-chart-chord": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.18.8.tgz",
-      "integrity": "sha512-2HNvb0uGpCw4yvlCyc0T78CpR/JAoGwvUnnskUIbM2HEKX1kwP2OerxWr8B0eLIBlMN2oorZDVjSOrN4RJajtg==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.18.9.tgz",
+      "integrity": "sha512-dpYL2Nn8Gf24UAa95pNwUzLcAsqJj7wvRMFFz51vrkXbH77z5RBjh83gWqDtHHGW1WdOFi1Ct5L4eLY5nUDJ5g==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "prop-types": "^15.6.2",
         "react": "^16.13.1"
       }
     },
     "@superset-ui/legacy-plugin-chart-country-map": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.18.8.tgz",
-      "integrity": "sha512-jTk0ceADJW6UhZJctU1CEWOVXh4oVW6OhZkbLQRsOCQHTCHQAp0TwzSUZ8/i4A5HIR3r5cp/EZkBz+C8zzb43A==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.18.9.tgz",
+      "integrity": "sha512-kIJAoTqaqP70lMNz9ydwJb2IHykciZld4YqEPNt+GUALoD2/T/f2RVSl9Jqy3lNlkcCNsxzGj0ErpuCYxzPhsw==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "d3-array": "^2.0.3",
         "prop-types": "^15.6.2"
@@ -53670,34 +53736,34 @@
       }
     },
     "@superset-ui/legacy-plugin-chart-event-flow": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.18.8.tgz",
-      "integrity": "sha512-Z5YnUZatr+PjmFZDZ6Rqx3t7XlgDeMPlhw+xJDqjd2YMpCAW0gdtRB3+5iydwA9EAe9sCf9HmlnIyvER304LcQ==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.18.9.tgz",
+      "integrity": "sha512-lYo111sKVAAptEM9QcW4JaiI2bJp9uxtAyscxT7m2yfDdoCN11+fZTN6MB8aAhZbM6C7lVh760V7VdQBGtfq5A==",
       "requires": {
         "@data-ui/event-flow": "^0.0.84",
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "prop-types": "^15.6.2"
       }
     },
     "@superset-ui/legacy-plugin-chart-force-directed": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.18.8.tgz",
-      "integrity": "sha512-HNXlzxDzVnVVHwBv98AISyLv01wxAv4pA1fSWBKzPap90hBhDBV4Sfw55fqSA4CroGf6qC5lzaRNTx0z6f+XLQ==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.18.9.tgz",
+      "integrity": "sha512-G71bPCk2hKPynLaKZzLLaJ7JShbdxqGymvEG0CnnFtxVh2AOBzG1WJ1ns1CiGkISxZGax6zPzimqvQlrnE2OGQ==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "prop-types": "^15.7.2"
       }
     },
     "@superset-ui/legacy-plugin-chart-heatmap": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.18.8.tgz",
-      "integrity": "sha512-n8iRZ9DBg10+5G9UfONLCRFzJ7yS8264CD8lOWexhokq364Ci1sNFLjPNthosV4E0ONIwgzNJnkOqI4lHnaW7A==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.18.9.tgz",
+      "integrity": "sha512-0tOGMFez0tjOp36oE524vfHP1YKUmYZAB0sOb2vghUVLxEgVeKYbZu33OZtBn4FRb3bsEx7GPSAANfdL2d5Jug==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "d3-svg-legend": "^1.x",
         "d3-tip": "^0.9.1",
@@ -53705,14 +53771,14 @@
       }
     },
     "@superset-ui/legacy-plugin-chart-histogram": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.18.8.tgz",
-      "integrity": "sha512-ZGYr6anCYpxQKHqau3Sf8RYHUOD6U57dM6Prp9t366dNhZvsAw5r3jGi3JaT4N5DhZhtfJB/0zjLZO6WV05DzA==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.18.9.tgz",
+      "integrity": "sha512-jayYp6SVw8pTgFVanNuO/VOv2Zsmah7NcAwOdiOUGbMqIEAzn7qvjdh7oW4Lg5Gk+TO0aysKgrT/DZXuw6zf8g==",
       "requires": {
         "@data-ui/histogram": "^0.0.84",
         "@data-ui/theme": "^0.0.84",
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "@vx/legend": "^0.0.198",
         "@vx/responsive": "^0.0.199",
         "@vx/scale": "^0.0.197",
@@ -53780,12 +53846,12 @@
       }
     },
     "@superset-ui/legacy-plugin-chart-horizon": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.18.8.tgz",
-      "integrity": "sha512-3ZD0pfE4KoCH24+9dtbXUa82Y60+MtjMFmLCT+lj4iGQXhlqqG6xqjqzMfjpwH7su+hRdI8xMOGxqvJa53vx8g==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.18.9.tgz",
+      "integrity": "sha512-ax4Hp5jNVT87rsgFs7iTiqq7nknhsn4mPVRr3v7w1eHoYCWCwokEFwrjHZFirTSumsLM8yjkcjjZ95aYPs/8CA==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3-array": "^2.0.3",
         "d3-scale": "^3.0.1",
         "prop-types": "^15.6.2"
@@ -53800,26 +53866,34 @@
           }
         },
         "d3-scale": {
-          "version": "3.2.4",
-          "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.4.tgz",
-          "integrity": "sha512-PG6gtpbPCFqKbvdBEswQcJcTzHC8VEd/XzezF5e68KlkT4/ggELw/nR1tv863jY6ufKTvDlzCMZvhe06codbbA==",
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
+          "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==",
           "requires": {
             "d3-array": "^2.3.0",
             "d3-format": "1 - 2",
             "d3-interpolate": "1.2.0 - 2",
-            "d3-time": "1 - 2",
+            "d3-time": "^2.1.1",
             "d3-time-format": "2 - 3"
           }
+        },
+        "d3-time": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+          "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+          "requires": {
+            "d3-array": "2"
+          }
         }
       }
     },
     "@superset-ui/legacy-plugin-chart-map-box": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.18.8.tgz",
-      "integrity": "sha512-Q7rQFlRzsPyEKng8RzzAvrnvgnFSdDzAateD7q2scV8tGrZkoUktbs+T845v2jJbIvSHVBc5VUKbplqdgapB4w==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.18.9.tgz",
+      "integrity": "sha512-a+XT5Q1st6h8DjvRwECqcsNr1HhZNRW0C6VZ+G6V79U9bcOnS5FYgpC7nwR1Ul8UKqdVcXO711z3sOJkZOt6ug==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "mapbox-gl": "^0.53.0",
         "prop-types": "^15.6.2",
         "react-map-gl": "^4.0.10",
@@ -53828,118 +53902,118 @@
       }
     },
     "@superset-ui/legacy-plugin-chart-paired-t-test": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.18.8.tgz",
-      "integrity": "sha512-GxsyiMQpgzspXDZXqaosgIDIAG/ahUH7Zb25FWReKMm5+auE+jjCpt6nXDF27KZqdFWcaO6wZWEvQMUARZij5w==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.18.9.tgz",
+      "integrity": "sha512-OUI4ttmUp1kZtrP0eOyb560xsM6RWsVywjIj8TR6uw6+zLxNFW8eFNMzZHaCEJfK6D+RHYzfFBRvnns/8EJQ7g==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "distributions": "^1.0.0",
         "prop-types": "^15.6.2",
         "reactable": "^1.1.0"
       }
     },
     "@superset-ui/legacy-plugin-chart-parallel-coordinates": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.18.8.tgz",
-      "integrity": "sha512-S4hN5aENvRgkqx5ZDYsZaVw/ojQdnz7o1B20nlbFlCW5+qtW53CWsWQlOzbHYvCJt12Wv03+frlPP5YODzuc7g==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.18.9.tgz",
+      "integrity": "sha512-FCueHNN9CIPiPYajATJp064FZPVMDFhXg/5kuD4prsw2uKEnjgQ/YQt/qRWj4E+/MXBCRgcpuyLLSuSItAyyUg==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "prop-types": "^15.7.2"
       }
     },
     "@superset-ui/legacy-plugin-chart-partition": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.18.8.tgz",
-      "integrity": "sha512-1effCYH6nZMp/3GgZLyHinW4W/S0IWklJddGYv9Ww0rpwtRKT7icoDCLVq7HkfYpGxcWdKyfWZkcbrT9emCY2w==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.18.9.tgz",
+      "integrity": "sha512-t/yIIYsJIQ51vIyAoi5uPvRk38vDDSBaofGTKYo6aZtEx7h9wDAfnx0pEHyudk6TpmH1gFqasZ/wNAt3/rkSHA==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "d3-hierarchy": "^1.1.8",
         "prop-types": "^15.6.2"
       }
     },
     "@superset-ui/legacy-plugin-chart-pivot-table": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.18.8.tgz",
-      "integrity": "sha512-cbSERwIqQuCWmrUCDUgp4ugoVxx8AyeUqmkLTSYqvoKkIaBHPoPbW5mR2lsGfqnh0nU1/uBjC4wIUhMkZjAUqg==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.18.9.tgz",
+      "integrity": "sha512-WQK2iKkVZXJzSyteksir2bC8qGhV3l7RsV82N3kEuK+AQSWpIpPEYCMfrQoSPLpSgnOLGT4iieIlxeYEud6hlg==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "datatables.net-bs": "^1.10.15",
         "prop-types": "^15.6.2"
       }
     },
     "@superset-ui/legacy-plugin-chart-rose": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.18.8.tgz",
-      "integrity": "sha512-CKHVwr72/+zw6Vbfp/kWOMNZE+ufmUnmL35VH0Dm5gxBd3Nhl2SNg0LrtmzH2FCJuZqjijBCazGEZz+m0pEhcw==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.18.9.tgz",
+      "integrity": "sha512-gYeUYJ8Nbv2hFP6o0L98qo7AOha/n6ZSkL83j9IHIGXsd4lib1OVKwlFgOUBDU0Gs193MV1X34btXfOC/kb5Jg==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "nvd3": "1.8.6",
         "prop-types": "^15.6.2"
       }
     },
     "@superset-ui/legacy-plugin-chart-sankey": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.18.8.tgz",
-      "integrity": "sha512-wwHClWiY/btSu7OABp/7JrdJEh3Q36Dvl6IT2PXv8T2dybdQDnxd6PfSmypsCUBj+dq/0tLhVau5IG4zTpGPeA==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.18.9.tgz",
+      "integrity": "sha512-xZv4XT3IcGAlObu2PqoPrk3HXo6nrNgFHEGtqbbAB0lVnn7yvnHPTtOK2yyOZxTNIANZPyNTYiVJcGW7LlOGpQ==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "d3-sankey": "^0.4.2",
         "prop-types": "^15.6.2"
       }
     },
     "@superset-ui/legacy-plugin-chart-sankey-loop": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.18.8.tgz",
-      "integrity": "sha512-kOS5FNamyyxgu5VsFRYMjwC7pPlwjp+3MuyVZcamqWq21ydZt5DSRfJLi7QPt81lDxkp0PhFcy+SEhOwOKzQFQ==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.18.9.tgz",
+      "integrity": "sha512-DGQ9Jug4bDJPle6fSEieCfdkNQyXDq2xIf/aoRkSGd1Dt1MqMJIgR4JqwtFfyqQcB5gX+p9OFuCIiWB3eSNtdQ==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3-sankey-diagram": "^0.7.3",
         "d3-selection": "^1.4.0",
         "prop-types": "^15.6.2"
       }
     },
     "@superset-ui/legacy-plugin-chart-sunburst": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.18.8.tgz",
-      "integrity": "sha512-468nblN6ddB/Jmovq3nnknupGGMCNlBb/zhvU4H1CE7YWMqf0qCPu6cqN6CRc0RB0Fyz7JW5kDGGs4eOMt3gaw==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.18.9.tgz",
+      "integrity": "sha512-05Sq2JdE9ETsQIIbhxIbLIIamMVgQGaB6hDtUvGqNWvBKC+xA2OZXq1013vzvy8sXPKL8lRhubwyJWNkP/Zi1g==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "prop-types": "^15.6.2"
       }
     },
     "@superset-ui/legacy-plugin-chart-treemap": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.18.8.tgz",
-      "integrity": "sha512-ClRPeCYsNwvpqpeubCcCh4CbY20EYeRGU5IjQsqrhmnCQ0icCx0S4cwb+065zmaybV/XXDzm4+aS2Gk9bHiEJA==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.18.9.tgz",
+      "integrity": "sha512-5YdOS4HNcPCgrojtLIPEofeMuLAMfe7iAl0kMME2M3UHiqocbWhOAXHa/l2yIjhCvPISy+UE6pyphTl7Lnszhw==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3-hierarchy": "^1.1.8",
         "d3-selection": "^1.4.0",
         "prop-types": "^15.6.2"
       }
     },
     "@superset-ui/legacy-plugin-chart-world-map": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.18.8.tgz",
-      "integrity": "sha512-Sm48+qMyiRCZJ/Iu/JJq2PojktdQXyVyj7sETGwh2asQ+CjwPQvIqE9vqc0iyq64NLTGKuZgLbRmu8s3lSBCRA==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.18.9.tgz",
+      "integrity": "sha512-I2YRQL2V8/qal/9AOnay71xUsDarjkGlMezBzO/zPLABBiLF4m1HkKscKe+3qhxLJvOoSi+XrXC4PL5PMdFJsA==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "d3-array": "^2.4.0",
         "d3-color": "^1.4.1",
@@ -53958,13 +54032,13 @@
       }
     },
     "@superset-ui/legacy-preset-chart-big-number": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.18.8.tgz",
-      "integrity": "sha512-V26mespxc5sHW6zZVEVI9l9QI3N7zdXKhO3RfbPSwXZgC7pL7YGRjwdA6KAQYrVaKgyk/7z3chYkmE7wOlQzrg==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.18.9.tgz",
+      "integrity": "sha512-9fTGToCCUFs56WwbVqoPZtCIYWgp/rg4YUaWulNon8W0vlV+5KTQSpfpfpzNLRWy3snK0B7bh6f7ifg80oe8YQ==",
       "requires": {
         "@data-ui/xy-chart": "^0.0.84",
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "@types/d3-color": "^1.2.2",
         "@types/shortid": "^0.0.29",
         "d3-color": "^1.2.3",
@@ -54119,13 +54193,13 @@
       }
     },
     "@superset-ui/legacy-preset-chart-nvd3": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.18.8.tgz",
-      "integrity": "sha512-De/Cz3tieFWbORCY6fobWMgJlLtKniXQM65VQ/96hljN6RFjXfpss4Ov31bSRFcLcTrdMHWRrjqqvoQf9t9Oqw==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.18.9.tgz",
+      "integrity": "sha512-vOPjKky95NVwdzhE2MidyVWK8l9uDIZ0k84fCo8Uh/deeWYyChqz4E6x4URbOoKJHofU9g5ltQTG7YivnijzzQ==",
       "requires": {
         "@data-ui/xy-chart": "^0.0.84",
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3": "^3.5.17",
         "d3-tip": "^0.9.1",
         "dompurify": "^2.0.6",
@@ -54138,35 +54212,35 @@
       }
     },
     "@superset-ui/plugin-chart-echarts": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.18.8.tgz",
-      "integrity": "sha512-Es/8rfftpFD+VHQQr2SIZHgTZQcf+Gw85Xt25WSrANqemymacfzL0kFSGJ0SChjMIxm0MPr/LXjthrZdoJ34IA==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.18.9.tgz",
+      "integrity": "sha512-/MeIgvwFVTd7iGdqx5mMchPOCU8OUtpL8lVjssFJaP5Npy2GPpPbBREqYaQdwelvWx1xgRAXKzHBv37CNA6csA==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "d3-array": "^1.2.0",
         "echarts": "^5.2.1",
         "lodash": "^4.17.15"
       }
     },
     "@superset-ui/plugin-chart-pivot-table": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-pivot-table/-/plugin-chart-pivot-table-0.18.8.tgz",
-      "integrity": "sha512-pHtZ6yx3+T8/DHz/4etjMQESl1UkoUvRrurOiLwSX9X6UxsSAjn5aMHBK/9b3O5iVKkTkEAw7H5Prn5JbDpVNQ==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-pivot-table/-/plugin-chart-pivot-table-0.18.9.tgz",
+      "integrity": "sha512-zHHpupS8J0zda3uPqM9u0fcPyskqyttaPGpnoDHSPvZ1Hh7WH2uyhOq09eXZS8DQe8N+NdDeGSx0AV+0k/oIRA==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "@superset-ui/react-pivottable": "^0.12.12"
       }
     },
     "@superset-ui/plugin-chart-table": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.18.8.tgz",
-      "integrity": "sha512-fet5M030OfL3cOVMEIgApqAvqVJ+Tl1gv6gNqgBgRPpRXATU+JnN3zdzsBuzuHNRLVrmzyos0OAr4OaQ4C0nFA==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.18.9.tgz",
+      "integrity": "sha512-R29s833J46ai6W0DFezMGPiuqPtYssfRab4qIZDiSugEovX5h1+hWJzSMo4rrdZa8SzdbM+b6wffmYJraW00Ew==",
       "requires": {
         "@react-icons/all-files": "^4.1.0",
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "@types/d3-array": "^2.9.0",
         "@types/react-table": "^7.0.29",
         "d3-array": "^2.4.0",
@@ -54188,12 +54262,12 @@
       }
     },
     "@superset-ui/plugin-chart-word-cloud": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.18.8.tgz",
-      "integrity": "sha512-2YRleSs3VBM4NIXsKuwKfGJmc5FIFtstXKa6P69rjDnZiPZjUKOeiv6UuOxPcW5VOIHJB8LAqC1rpmZ0OzlCUg==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.18.9.tgz",
+      "integrity": "sha512-9hKkvKKycT+ODxRiI/Od8K3Prsbe/ILrOa/Dm7jRcke+DBt0d3EarVFTtt3oI/iGSFSTDtvSaNQkcf32JtQHIg==",
       "requires": {
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "@types/d3-cloud": "^1.2.1",
         "@types/d3-scale": "^2.0.2",
         "d3-cloud": "^1.2.5",
@@ -54210,28 +54284,36 @@
           }
         },
         "d3-scale": {
-          "version": "3.2.4",
-          "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.4.tgz",
-          "integrity": "sha512-PG6gtpbPCFqKbvdBEswQcJcTzHC8VEd/XzezF5e68KlkT4/ggELw/nR1tv863jY6ufKTvDlzCMZvhe06codbbA==",
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
+          "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==",
           "requires": {
             "d3-array": "^2.3.0",
             "d3-format": "1 - 2",
             "d3-interpolate": "1.2.0 - 2",
-            "d3-time": "1 - 2",
+            "d3-time": "^2.1.1",
             "d3-time-format": "2 - 3"
           }
+        },
+        "d3-time": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+          "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+          "requires": {
+            "d3-array": "2"
+          }
         }
       }
     },
     "@superset-ui/preset-chart-xy": {
-      "version": "0.18.8",
-      "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.18.8.tgz",
-      "integrity": "sha512-QjS/5OSkpadqUIwWJVuLK49fCc7CGF38kETkrQIHT6kj/xkcTOoQudcPU7RGxnBQimrelmXizWz01pe/zovnhg==",
+      "version": "0.18.9",
+      "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.18.9.tgz",
+      "integrity": "sha512-aC6i1wkXxeEdZqiz71tMJhjqTS/3RAIjgwW5QhVFdvMTa1IjmjtoThbHP0USgDUEuvDIbt5zpQiICHshWHGM7w==",
       "requires": {
         "@data-ui/theme": "^0.0.84",
         "@data-ui/xy-chart": "^0.0.84",
-        "@superset-ui/chart-controls": "0.18.8",
-        "@superset-ui/core": "0.18.8",
+        "@superset-ui/chart-controls": "0.18.9",
+        "@superset-ui/core": "0.18.9",
         "@vx/axis": "^0.0.198",
         "@vx/legend": "^0.0.198",
         "@vx/scale": "^0.0.197",
@@ -54791,22 +54873,22 @@
       "integrity": "sha512-sdBMGfNvLUkBypPMEhOcKcblTQfgHbqbYrUqRE31jOwdDHBJBxz4co2MDAq93S4Cp++phk4UiwoEg/1hK3xXAQ=="
     },
     "@types/d3-cloud": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/@types/d3-cloud/-/d3-cloud-1.2.3.tgz",
-      "integrity": "sha512-VUcRA9YWM1V5Srg444/zVxgmhVyIYBrChIr5lg+0cPGNM4J4fLdBqEeAkk9ggRPM3Um4fUy/p/gtEKbAqL/hcw==",
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/@types/d3-cloud/-/d3-cloud-1.2.5.tgz",
+      "integrity": "sha512-vEIER9DsEBUOdpRiwCh3n1qE+cV6h4e1LhxhY2sLt+m8LPNAIkOOhTlqk0JDiBwD+ZPM8ynFAOU3AuPuVYBFBA==",
       "requires": {
         "@types/d3": "^3"
       }
     },
     "@types/d3-color": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.1.tgz",
-      "integrity": "sha512-xkPLi+gbgUU9ED6QX4g6jqYL2KCB0/3AlM+ncMGqn49OgH0gFMY/ITGqPF8HwEiLzJaC+2L0I+gNwBgABv1Pvg=="
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.2.tgz",
+      "integrity": "sha512-fYtiVLBYy7VQX+Kx7wU/uOIkGQn8aAEY8oWMoyja3N4dLd8Yf6XgSIR/4yWvMuveNOH5VShnqCgRqqh/UNanBA=="
     },
     "@types/d3-format": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.4.1.tgz",
-      "integrity": "sha512-ss9G2snEKmp2In5Z3T0Jpqv8QaDBc2xHltBw83KjnV5B5w+Iwphbvq5ph/Xnu4d03fmmsdt+o1aWch379rxIbA=="
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.4.2.tgz",
+      "integrity": "sha512-WeGCHAs7PHdZYq6lwl/+jsl+Nfc1J2W1kNcMeIMYzQsT6mtBDBgtJ/rcdjZ0k0rVIvqEZqhhuD5TK/v3P2gFHQ=="
     },
     "@types/d3-interpolate": {
       "version": "1.4.2",
@@ -54822,22 +54904,22 @@
       "integrity": "sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ=="
     },
     "@types/d3-scale": {
-      "version": "2.2.4",
-      "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-2.2.4.tgz",
-      "integrity": "sha512-wkQXT+IfgfAnKB5rtS1qMJg3FS32r1rVFHvqtiqk8pX8o5aQR3VwX1P7ErHjzNIicTlkWsaMiUTrYB+E75HFeA==",
+      "version": "2.2.6",
+      "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-2.2.6.tgz",
+      "integrity": "sha512-CHu34T5bGrJOeuhGxyiz9Xvaa9PlsIaQoOqjDg7zqeGj2x0rwPhGquiy03unigvcMxmvY0hEaAouT0LOFTLpIw==",
       "requires": {
         "@types/d3-time": "^1"
       }
     },
     "@types/d3-scale-chromatic": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-1.5.1.tgz",
-      "integrity": "sha512-7FtJYrmXTEWLykShjYhoGuDNR/Bda0+tstZMkFj4RRxUEryv16AGh3be21tqg84B6KfEwiZyEpBcTyPyU+GWjg=="
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-2.0.1.tgz",
+      "integrity": "sha512-3EuZlbPu+pvclZcb1DhlymTWT2W+lYsRKBjvkH2ojDbCWDYavifqu1vYX9WGzlPgCgcS4Alhk1+zapXbGEGylQ=="
     },
     "@types/d3-shape": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.5.tgz",
-      "integrity": "sha512-aPEax03owTAKynoK8ZkmkZEDZvvT4Y5pWgii4Jp4oQt0gH45j6siDl9gNDVC5kl64XHN2goN9jbYoHK88tFAcA==",
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.8.tgz",
+      "integrity": "sha512-gqfnMz6Fd5H6GOLYixOZP/xlrMtJms9BaS+6oWxTKHNqPGZ93BkWWupQSCYm6YHqx6h9wjRupuJb90bun6ZaYg==",
       "requires": {
         "@types/d3-path": "^1"
       }
@@ -61017,17 +61099,17 @@
       }
     },
     "datatables.net": {
-      "version": "1.11.2",
-      "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.11.2.tgz",
-      "integrity": "sha512-Dg+Yeyky+BhXoooJqTDTGssLxkAxe73CljcjYd4630XQZKqqqy0akYk2r4RSHSJLyYCOGRZEZiUF/N39+5nqvg==",
+      "version": "1.11.3",
+      "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.11.3.tgz",
+      "integrity": "sha512-VMj5qEaTebpNurySkM6jy6sGpl+s6onPK8xJhYr296R/vUBnz1+id16NVqNf9z5aR076OGcpGHCuiTuy4E05oQ==",
       "requires": {
         "jquery": ">=1.7"
       }
     },
     "datatables.net-bs": {
-      "version": "1.11.2",
-      "resolved": "https://registry.npmjs.org/datatables.net-bs/-/datatables.net-bs-1.11.2.tgz",
-      "integrity": "sha512-VJfRfJCmZ81XshwxQngdEX6M5YRijkWHKhM9VnefWwZqwbIGiK+ngnmGKoNvnXmvWk4BZ1K0yy3vtkEmhDvvRw==",
+      "version": "1.11.3",
+      "resolved": "https://registry.npmjs.org/datatables.net-bs/-/datatables.net-bs-1.11.3.tgz",
+      "integrity": "sha512-Db1YwAhO0QAWQbZTsKriUrOInT66+xaA+fV616KTKpQt5Zt+p6OsEKK+xv8LxLgG8qu5dPwMBlkhqSiS/hV2sg==",
       "requires": {
         "datatables.net": ">=1.10.25",
         "jquery": ">=1.7"
@@ -61577,9 +61659,9 @@
       }
     },
     "dompurify": {
-      "version": "2.2.7",
-      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.7.tgz",
-      "integrity": "sha512-jdtDffdGNY+C76jvodNTu9jt5yYj59vuTUyx+wXdzcSwAGTYZDAQkQ7Iwx9zcGrA4ixC1syU4H3RZROqRxokxg=="
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.3.tgz",
+      "integrity": "sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg=="
     },
     "domutils": {
       "version": "1.5.1",
@@ -61883,21 +61965,24 @@
           }
         },
         "d3-scale": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.3.tgz",
-          "integrity": "sha512-8E37oWEmEzj57bHcnjPVOBS3n4jqakOeuv1EDdQSiSrYnMCBdMd3nc4HtKk7uia8DUHcY/CGuJ42xxgtEYrX0g==",
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
+          "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==",
           "requires": {
             "d3-array": "^2.3.0",
             "d3-format": "1 - 2",
             "d3-interpolate": "1.2.0 - 2",
-            "d3-time": "1 - 2",
+            "d3-time": "^2.1.1",
             "d3-time-format": "2 - 3"
           }
         },
         "d3-time": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.0.0.tgz",
-          "integrity": "sha512-2mvhstTFcMvwStWd9Tj3e6CEqtOivtD8AUiHT8ido/xmzrI9ijrUUihZ6nHuf/vsScRBonagOdj0Vv+SEL5G3Q=="
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+          "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+          "requires": {
+            "d3-array": "2"
+          }
         }
       }
     },
@@ -63459,9 +63544,9 @@
       "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw=="
     },
     "fast-safe-stringify": {
-      "version": "2.0.7",
-      "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
-      "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA=="
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+      "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
     },
     "fastest-levenshtein": {
       "version": "1.0.12",
@@ -65238,6 +65323,11 @@
       "resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-3.1.1.tgz",
       "integrity": "sha512-Q0QaXjPjwIju/28TsugCHNEASwoCcJSyJV3uO1sOIQGI0jKgm9f41Lvz0DZj3n46cNCyAZTsEYoY4C2bVRUzyQ=="
     },
+    "immutable": {
+      "version": "3.8.2",
+      "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
+      "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM="
+    },
     "import-fresh": {
       "version": "3.3.0",
       "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -76423,9 +76513,9 @@
       "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw=="
     },
     "ua-parser-js": {
-      "version": "0.7.25",
-      "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.25.tgz",
-      "integrity": "sha512-8NFExdfI24Ny8R3Vc6+uUytP/I7dpqk3JERlvxPWlrtx5YboqCgxAXYKPAifbPLV2zKbgmmPL53ufW7mUC/VOQ=="
+      "version": "0.7.28",
+      "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz",
+      "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g=="
     },
     "ultimate-pagination": {
       "version": "1.0.0",
diff --git a/superset-frontend/package.json b/superset-frontend/package.json
index 2326996..34f8185 100644
--- a/superset-frontend/package.json
+++ b/superset-frontend/package.json
@@ -68,35 +68,35 @@
     "@emotion/cache": "^11.4.0",
     "@emotion/react": "^11.4.1",
     "@emotion/styled": "^11.3.0",
-    "@superset-ui/chart-controls": "^0.18.8",
-    "@superset-ui/core": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-calendar": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-chord": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-country-map": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-event-flow": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-force-directed": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-heatmap": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-histogram": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-horizon": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-map-box": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-partition": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-pivot-table": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-rose": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-sankey": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-sunburst": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-treemap": "^0.18.8",
-    "@superset-ui/legacy-plugin-chart-world-map": "^0.18.8",
-    "@superset-ui/legacy-preset-chart-big-number": "^0.18.8",
+    "@superset-ui/chart-controls": "^0.18.9",
+    "@superset-ui/core": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-calendar": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-chord": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-country-map": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-event-flow": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-force-directed": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-heatmap": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-histogram": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-horizon": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-map-box": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-partition": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-pivot-table": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-rose": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-sankey": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-sunburst": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-treemap": "^0.18.9",
+    "@superset-ui/legacy-plugin-chart-world-map": "^0.18.9",
+    "@superset-ui/legacy-preset-chart-big-number": "^0.18.9",
     "@superset-ui/legacy-preset-chart-deckgl": "^0.4.12",
-    "@superset-ui/legacy-preset-chart-nvd3": "^0.18.8",
-    "@superset-ui/plugin-chart-echarts": "^0.18.8",
-    "@superset-ui/plugin-chart-pivot-table": "^0.18.8",
-    "@superset-ui/plugin-chart-table": "^0.18.8",
-    "@superset-ui/plugin-chart-word-cloud": "^0.18.8",
-    "@superset-ui/preset-chart-xy": "^0.18.8",
+    "@superset-ui/legacy-preset-chart-nvd3": "^0.18.9",
+    "@superset-ui/plugin-chart-echarts": "^0.18.9",
+    "@superset-ui/plugin-chart-pivot-table": "^0.18.9",
+    "@superset-ui/plugin-chart-table": "^0.18.9",
+    "@superset-ui/plugin-chart-word-cloud": "^0.18.9",
+    "@superset-ui/preset-chart-xy": "^0.18.9",
     "@vx/responsive": "^0.0.195",
     "abortcontroller-polyfill": "^1.1.9",
     "antd": "^4.9.4",

[superset] 04/26: fix: handle mixed time-series error (#16928)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit b2b3cd73fd95ea209d61bf48fd575b4515c1abb3
Author: Youkyoung Cha <37...@users.noreply.github.com>
AuthorDate: Tue Oct 5 22:00:22 2021 +0900

    fix: handle mixed time-series error (#16928)
    
    * rebase to master
    
    * Fix line 402
    
    Co-authored-by: yougyoung <yo...@pubg.com>
    (cherry picked from commit 93ebe3d963c857ccc4185ef3ca5c6175397e17c9)
---
 superset-frontend/src/chart/chartAction.js | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/superset-frontend/src/chart/chartAction.js b/superset-frontend/src/chart/chartAction.js
index 5410caa..af5ae0c 100644
--- a/superset-frontend/src/chart/chartAction.js
+++ b/superset-frontend/src/chart/chartAction.js
@@ -395,13 +395,16 @@ export function exploreJSON(
       .then(({ response, json }) => {
         if (isFeatureEnabled(FeatureFlag.GLOBAL_ASYNC_QUERIES)) {
           // deal with getChartDataRequest transforming the response data
-          const result = 'result' in json ? json.result[0] : json;
+          const result = 'result' in json ? json.result : json;
           switch (response.status) {
             case 200:
               // Query results returned synchronously, meaning query was already cached.
-              return Promise.resolve([result]);
+              return Promise.resolve(result);
             case 202:
               // Query is running asynchronously and we must await the results
+              if (shouldUseLegacyApi(formData)) {
+                return waitForAsyncData(result[0]);
+              }
               return waitForAsyncData(result);
             default:
               throw new Error(

[superset] 07/26: fix(BigQuery): explicitly quote columns in select_star (#16822)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit ddb35a3633ac657bd3ebc81da2c4348b2a7629a9
Author: Beto Dealmeida <ro...@dealmeida.net>
AuthorDate: Wed Oct 6 07:43:32 2021 -0700

    fix(BigQuery): explicitly quote columns in select_star (#16822)
    
    * fix (BigQuery): explicitly quote columns in select_star
    
    * Fix test
    
    * Fix SELECT * in BQ
    
    * Add unit tests
    
    * Remove type changes
    
    (cherry picked from commit c993c5845f8a2ca001ba18d4af988edd3e53e8bf)
---
 superset/db_engine_specs/bigquery.py              | 120 ++++++++++++--
 superset/result_set.py                            |   4 +-
 tests/unit_tests/db_engine_specs/test_bigquery.py | 190 ++++++++++++++++++++++
 3 files changed, 296 insertions(+), 18 deletions(-)

diff --git a/superset/db_engine_specs/bigquery.py b/superset/db_engine_specs/bigquery.py
index 1b7b73e..a862df5 100644
--- a/superset/db_engine_specs/bigquery.py
+++ b/superset/db_engine_specs/bigquery.py
@@ -26,9 +26,10 @@ from apispec.ext.marshmallow import MarshmallowPlugin
 from flask_babel import gettext as __
 from marshmallow import fields, Schema
 from marshmallow.exceptions import ValidationError
-from sqlalchemy import literal_column
+from sqlalchemy import column
+from sqlalchemy.engine.base import Engine
 from sqlalchemy.engine.url import make_url
-from sqlalchemy.sql.expression import ColumnClause
+from sqlalchemy.sql import sqltypes
 from typing_extensions import TypedDict
 
 from superset.databases.schemas import encrypted_field_properties, EncryptedString
@@ -283,20 +284,6 @@ class BigQueryEngineSpec(BaseEngineSpec):
         }
 
     @classmethod
-    def _get_fields(cls, cols: List[Dict[str, Any]]) -> List[ColumnClause]:
-        """
-        BigQuery dialect requires us to not use backtick in the fieldname which are
-        nested.
-        Using literal_column handles that issue.
-        https://docs.sqlalchemy.org/en/latest/core/tutorial.html#using-more-specific-text-with-table-literal-column-and-column
-        Also explicility specifying column names so we don't encounter duplicate
-        column names in the result.
-        """
-        return [
-            literal_column(c["name"]).label(c["name"].replace(".", "__")) for c in cols
-        ]
-
-    @classmethod
     def epoch_to_dttm(cls) -> str:
         return "TIMESTAMP_SECONDS({col})"
 
@@ -425,3 +412,104 @@ class BigQueryEngineSpec(BaseEngineSpec):
         ma_plugin.converter.add_attribute_function(encrypted_field_properties)
         spec.components.schema(cls.__name__, schema=cls.parameters_schema)
         return spec.to_dict()["components"]["schemas"][cls.__name__]
+
+    @classmethod
+    def select_star(  # pylint: disable=too-many-arguments
+        cls,
+        database: "Database",
+        table_name: str,
+        engine: Engine,
+        schema: Optional[str] = None,
+        limit: int = 100,
+        show_cols: bool = False,
+        indent: bool = True,
+        latest_partition: bool = True,
+        cols: Optional[List[Dict[str, Any]]] = None,
+    ) -> str:
+        """
+        Remove array structures from `SELECT *`.
+
+        BigQuery supports structures and arrays of structures, eg:
+
+            author STRUCT<name STRING, email STRING>
+            trailer ARRAY<STRUCT<key STRING, value STRING>>
+
+        When loading metadata for a table each key in the struct is displayed as a
+        separate pseudo-column, eg:
+
+            - author
+            - author.name
+            - author.email
+            - trailer
+            - trailer.key
+            - trailer.value
+
+        When generating the `SELECT *` statement we want to remove any keys from
+        structs inside an array, since selecting them results in an error. The correct
+        select statement should look like this:
+
+            SELECT
+              `author`,
+              `author`.`name`,
+              `author`.`email`,
+              `trailer`
+            FROM
+              table
+
+        Selecting `trailer.key` or `trailer.value` results in an error, as opposed to
+        selecting `author.name`, since they are keys in a structure inside an array.
+
+        This method removes any array pseudo-columns.
+        """
+        if cols:
+            # For arrays of structs, remove the child columns, otherwise the query
+            # will fail.
+            array_prefixes = {
+                col["name"] for col in cols if isinstance(col["type"], sqltypes.ARRAY)
+            }
+            cols = [
+                col
+                for col in cols
+                if "." not in col["name"]
+                or col["name"].split(".")[0] not in array_prefixes
+            ]
+
+        return super().select_star(
+            database,
+            table_name,
+            engine,
+            schema,
+            limit,
+            show_cols,
+            indent,
+            latest_partition,
+            cols,
+        )
+
+    @classmethod
+    def _get_fields(cls, cols: List[Dict[str, Any]]) -> List[Any]:
+        """
+        Label columns using their fully qualified name.
+
+        BigQuery supports columns of type `struct`, which are basically dictionaries.
+        When loading metadata for a table with struct columns, each key in the struct
+        is displayed as a separate pseudo-column, eg:
+
+            author STRUCT<name STRING, email STRING>
+
+        Will be shown as 3 columns:
+
+            - author
+            - author.name
+            - author.email
+
+        If we select those fields:
+
+            SELECT `author`, `author`.`name`, `author`.`email` FROM table
+
+        The resulting columns will be called "author", "name", and "email", This may
+        result in a clash with other columns. To prevent that, we explicitly label
+        the columns using their fully qualified name, so we end up with "author",
+        "author__name" and "author__email", respectively.
+        """
+        return [column(c["name"]).label(c["name"].replace(".", "__")) for c in cols]
diff --git a/superset/result_set.py b/superset/result_set.py
index 1f92513..b95b5e6 100644
--- a/superset/result_set.py
+++ b/superset/result_set.py
@@ -25,7 +25,7 @@ import numpy as np
 import pandas as pd
 import pyarrow as pa
 
-from superset import db_engine_specs
+from superset.db_engine_specs import BaseEngineSpec
 from superset.typing import DbapiDescription, DbapiResult
 from superset.utils import core as utils
 
@@ -76,7 +76,7 @@ class SupersetResultSet:
         self,
         data: DbapiResult,
         cursor_description: DbapiDescription,
-        db_engine_spec: Type[db_engine_specs.BaseEngineSpec],
+        db_engine_spec: Type[BaseEngineSpec],
     ):
         self.db_engine_spec = db_engine_spec
         data = data or []
diff --git a/tests/unit_tests/db_engine_specs/test_bigquery.py b/tests/unit_tests/db_engine_specs/test_bigquery.py
new file mode 100644
index 0000000..dcf1de7
--- /dev/null
+++ b/tests/unit_tests/db_engine_specs/test_bigquery.py
@@ -0,0 +1,190 @@
+# 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.
+# pylint: disable=unused-argument, import-outside-toplevel, protected-access
+
+from flask.ctx import AppContext
+from pytest_mock import MockFixture
+from sqlalchemy import select
+from sqlalchemy.dialects.sqlite.base import SQLiteDialect
+from sqlalchemy.sql import sqltypes
+
+
+def test_get_fields(app_context: AppContext) -> None:
+    """
+    Test the custom ``_get_fields`` method.
+
+    The method adds custom labels (aliases) to the columns to prevent
+    collision when referencing record fields. Eg, if we had these two
+    columns:
+
+        name STRING
+        project STRUCT<name STRING>
+
+    One could write this query:
+
+        SELECT
+            `name`,
+            `project`.`name`
+        FROM
+            the_table
+
+    But then both columns would get aliased as "name".
+
+    The custom method will replace the fields so that the final query
+    looks like this:
+
+        SELECT
+            `name` AS `name`,
+            `project`.`name` AS project__name
+        FROM
+            the_table
+
+    """
+    from superset.db_engine_specs.bigquery import BigQueryEngineSpec
+
+    columns = [{"name": "limit"}, {"name": "name"}, {"name": "project.name"}]
+    fields = BigQueryEngineSpec._get_fields(columns)
+
+    # generic SQL
+    query = select(fields)
+    assert (
+        str(query)
+        == 'SELECT "limit" AS "limit", name AS name, "project.name" AS project__name'
+    )
+
+    # BigQuery-specific SQL
+    try:
+        from pybigquery.sqlalchemy_bigquery import BigQueryDialect
+    except ModuleNotFoundError:
+        return
+
+    assert str(query.compile(dialect=BigQueryDialect())) == (
+        "SELECT `limit` AS `limit`, `name` AS `name`, "
+        "`project`.`name` AS `project__name`"
+    )
+
+
+def test_select_star(mocker: MockFixture, app_context: AppContext) -> None:
+    """
+    Test the ``select_star`` method.
+
+    The method removes pseudo-columns from structures inside arrays. While these
+    pseudo-columns show up as "columns" for metadata reasons, we can't select them
+    in the query, as opposed to fields from non-array structures.
+    """
+    from superset.db_engine_specs.bigquery import BigQueryEngineSpec
+
+    cols = [
+        {
+            "name": "trailer",
+            "type": sqltypes.ARRAY(sqltypes.JSON()),
+            "nullable": True,
+            "comment": None,
+            "default": None,
+            "precision": None,
+            "scale": None,
+            "max_length": None,
+        },
+        {
+            "name": "trailer.key",
+            "type": sqltypes.String(),
+            "nullable": True,
+            "comment": None,
+            "default": None,
+            "precision": None,
+            "scale": None,
+            "max_length": None,
+        },
+        {
+            "name": "trailer.value",
+            "type": sqltypes.String(),
+            "nullable": True,
+            "comment": None,
+            "default": None,
+            "precision": None,
+            "scale": None,
+            "max_length": None,
+        },
+        {
+            "name": "trailer.email",
+            "type": sqltypes.String(),
+            "nullable": True,
+            "comment": None,
+            "default": None,
+            "precision": None,
+            "scale": None,
+            "max_length": None,
+        },
+    ]
+
+    # mock the database so we can compile the query
+    database = mocker.MagicMock()
+    database.compile_sqla_query = lambda query: str(
+        query.compile(dialect=SQLiteDialect())
+    )
+
+    # use SQLite dialect so we don't need the BQ dependency
+    engine = mocker.MagicMock()
+    engine.dialect = SQLiteDialect()
+
+    sql = BigQueryEngineSpec.select_star(
+        database=database,
+        table_name="my_table",
+        engine=engine,
+        schema=None,
+        limit=100,
+        show_cols=True,
+        indent=True,
+        latest_partition=False,
+        cols=cols,
+    )
+    assert (
+        sql
+        == """SELECT trailer AS trailer
+FROM my_table
+LIMIT ?
+OFFSET ?"""
+    )
+
+    # BigQuery-specific SQL
+    try:
+        from pybigquery.sqlalchemy_bigquery import BigQueryDialect
+    except ModuleNotFoundError:
+        return
+
+    database.compile_sqla_query = lambda query: str(
+        query.compile(dialect=BigQueryDialect())
+    )
+    engine.dialect = BigQueryDialect()
+
+    sql = BigQueryEngineSpec.select_star(
+        database=database,
+        table_name="my_table",
+        engine=engine,
+        schema=None,
+        limit=100,
+        show_cols=True,
+        indent=True,
+        latest_partition=False,
+        cols=cols,
+    )
+    assert (
+        sql
+        == """SELECT `trailer` AS `trailer`
+FROM `my_table`
+LIMIT :param_1"""
+    )

[superset] 02/26: fix: Revert "fix: RBAC hide right menu (#16902)" (#16968)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit a6a3cedf6e896c3758c1696bedbdd3950d1dcb8a
Author: Elizabeth Thompson <es...@gmail.com>
AuthorDate: Mon Oct 4 19:18:12 2021 -0700

    fix: Revert "fix: RBAC hide right menu (#16902)" (#16968)
    
    * Revert "fix: RBAC hide right menu (#16902)"
    
    This reverts commit 87baac7650bdb6a8f8f82cf4992567a2c5c73cba.
    
    * fix failing test
    
    (cherry picked from commit 5866d5ebb064648b6ac23c498cc252207f62dfd1)
---
 .../dashboard/components/menu/HoverMenu_spec.tsx   |   9 +-
 .../src/components/Menu/Menu.test.tsx              |  52 ----
 .../src/components/Menu/MenuRight.tsx              | 269 ++++++++++-----------
 superset-frontend/src/views/menu.tsx               |   7 +-
 4 files changed, 129 insertions(+), 208 deletions(-)

diff --git a/superset-frontend/spec/javascripts/dashboard/components/menu/HoverMenu_spec.tsx b/superset-frontend/spec/javascripts/dashboard/components/menu/HoverMenu_spec.tsx
index 220952b..24106f1 100644
--- a/superset-frontend/spec/javascripts/dashboard/components/menu/HoverMenu_spec.tsx
+++ b/superset-frontend/spec/javascripts/dashboard/components/menu/HoverMenu_spec.tsx
@@ -17,14 +17,13 @@
  * under the License.
  */
 import React from 'react';
-import { render } from 'spec/helpers/testing-library';
+import { shallow } from 'enzyme';
 
 import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
 
 describe('HoverMenu', () => {
-  it('should render a hover menu', () => {
-    const rendered = render(<HoverMenu />);
-    const hoverMenu = rendered.container.querySelector('.hover-menu');
-    expect(hoverMenu).toBeVisible();
+  it('should render a div.hover-menu', () => {
+    const wrapper = shallow(<HoverMenu />);
+    expect(wrapper.find('.hover-menu')).toExist();
   });
 });
diff --git a/superset-frontend/src/components/Menu/Menu.test.tsx b/superset-frontend/src/components/Menu/Menu.test.tsx
index 8dd8f56..f3a2fd1 100644
--- a/superset-frontend/src/components/Menu/Menu.test.tsx
+++ b/superset-frontend/src/components/Menu/Menu.test.tsx
@@ -17,32 +17,12 @@
  * under the License.
  */
 import React from 'react';
-import * as reactRedux from 'react-redux';
 import { render, screen } from 'spec/helpers/testing-library';
 import userEvent from '@testing-library/user-event';
 import { Menu } from './Menu';
 import { dropdownItems } from './MenuRight';
 
-const user = {
-  createdOn: '2021-04-27T18:12:38.952304',
-  email: 'admin',
-  firstName: 'admin',
-  isActive: true,
-  lastName: 'admin',
-  permissions: {},
-  roles: {
-    Admin: [
-      ['can_sqllab', 'Superset'],
-      ['can_write', 'Dashboard'],
-      ['can_write', 'Chart'],
-    ],
-  },
-  userId: 1,
-  username: 'admin',
-};
-
 const mockedProps = {
-  user,
   data: {
     menu: [
       {
@@ -156,27 +136,17 @@ const notanonProps = {
   },
 };
 
-const useSelectorMock = jest.spyOn(reactRedux, 'useSelector');
-
-beforeEach(() => {
-  // setup a DOM element as a render target
-  useSelectorMock.mockClear();
-});
-
 test('should render', () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   const { container } = render(<Menu {...mockedProps} />);
   expect(container).toBeInTheDocument();
 });
 
 test('should render the navigation', () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   render(<Menu {...mockedProps} />);
   expect(screen.getByRole('navigation')).toBeInTheDocument();
 });
 
 test('should render the brand', () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   const {
     data: {
       brand: { alt, icon },
@@ -188,7 +158,6 @@ test('should render the brand', () => {
 });
 
 test('should render all the top navbar menu items', () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   const {
     data: { menu },
   } = mockedProps;
@@ -199,7 +168,6 @@ test('should render all the top navbar menu items', () => {
 });
 
 test('should render the top navbar child menu items', async () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   const {
     data: { menu },
   } = mockedProps;
@@ -216,7 +184,6 @@ test('should render the top navbar child menu items', async () => {
 });
 
 test('should render the dropdown items', async () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   render(<Menu {...notanonProps} />);
   const dropdown = screen.getByTestId('new-dropdown-icon');
   userEvent.hover(dropdown);
@@ -244,14 +211,12 @@ test('should render the dropdown items', async () => {
 });
 
 test('should render the Settings', async () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   render(<Menu {...mockedProps} />);
   const settings = await screen.findByText('Settings');
   expect(settings).toBeInTheDocument();
 });
 
 test('should render the Settings menu item', async () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   render(<Menu {...mockedProps} />);
   userEvent.hover(screen.getByText('Settings'));
   const label = await screen.findByText('Security');
@@ -259,7 +224,6 @@ test('should render the Settings menu item', async () => {
 });
 
 test('should render the Settings dropdown child menu items', async () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   const {
     data: { settings },
   } = mockedProps;
@@ -270,19 +234,16 @@ test('should render the Settings dropdown child menu items', async () => {
 });
 
 test('should render the plus menu (+) when user is not anonymous', () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   render(<Menu {...notanonProps} />);
   expect(screen.getByTestId('new-dropdown')).toBeInTheDocument();
 });
 
 test('should NOT render the plus menu (+) when user is anonymous', () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   render(<Menu {...mockedProps} />);
   expect(screen.queryByTestId('new-dropdown')).not.toBeInTheDocument();
 });
 
 test('should render the user actions when user is not anonymous', async () => {
-  useSelectorMock.mockReturnValue({ roles: mockedProps.user.roles });
   const {
     data: {
       navbar_right: { user_info_url, user_logout_url },
@@ -302,13 +263,11 @@ test('should render the user actions when user is not anonymous', async () => {
 });
 
 test('should NOT render the user actions when user is anonymous', () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   render(<Menu {...mockedProps} />);
   expect(screen.queryByText('User')).not.toBeInTheDocument();
 });
 
 test('should render the Profile link when available', async () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   const {
     data: {
       navbar_right: { user_profile_url },
@@ -323,7 +282,6 @@ test('should render the Profile link when available', async () => {
 });
 
 test('should render the About section and version_string, sha or build_number when available', async () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   const {
     data: {
       navbar_right: { version_sha, version_string, build_number },
@@ -343,7 +301,6 @@ test('should render the About section and version_string, sha or build_number wh
 });
 
 test('should render the Documentation link when available', async () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   const {
     data: {
       navbar_right: { documentation_url },
@@ -356,7 +313,6 @@ test('should render the Documentation link when available', async () => {
 });
 
 test('should render the Bug Report link when available', async () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   const {
     data: {
       navbar_right: { bug_report_url },
@@ -369,7 +325,6 @@ test('should render the Bug Report link when available', async () => {
 });
 
 test('should render the Login link when user is anonymous', () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   const {
     data: {
       navbar_right: { user_login_url },
@@ -382,13 +337,6 @@ test('should render the Login link when user is anonymous', () => {
 });
 
 test('should render the Language Picker', () => {
-  useSelectorMock.mockReturnValue({ roles: user.roles });
   render(<Menu {...mockedProps} />);
   expect(screen.getByLabelText('Languages')).toBeInTheDocument();
 });
-
-test('should hide create button without proper roles', () => {
-  useSelectorMock.mockReturnValue({ roles: [] });
-  render(<Menu {...notanonProps} />);
-  expect(screen.queryByTestId('new-dropdown')).not.toBeInTheDocument();
-});
diff --git a/superset-frontend/src/components/Menu/MenuRight.tsx b/superset-frontend/src/components/Menu/MenuRight.tsx
index 348bc46..1626713 100644
--- a/superset-frontend/src/components/Menu/MenuRight.tsx
+++ b/superset-frontend/src/components/Menu/MenuRight.tsx
@@ -21,9 +21,6 @@ import { MainNav as Menu } from 'src/common/components';
 import { t, styled, css, SupersetTheme } from '@superset-ui/core';
 import { Link } from 'react-router-dom';
 import Icons from 'src/components/Icons';
-import findPermission from 'src/dashboard/util/findPermission';
-import { useSelector } from 'react-redux';
-import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
 import LanguagePicker from './LanguagePicker';
 import { NavBarProps, MenuObjectProps } from './Menu';
 
@@ -32,22 +29,16 @@ export const dropdownItems = [
     label: t('SQL query'),
     url: '/superset/sqllab?new=true',
     icon: 'fa-fw fa-search',
-    perm: 'can_sqllab',
-    view: 'Superset',
   },
   {
     label: t('Chart'),
     url: '/chart/add',
     icon: 'fa-fw fa-bar-chart',
-    perm: 'can_write',
-    view: 'Dashboard',
   },
   {
     label: t('Dashboard'),
     url: '/dashboard/new',
     icon: 'fa-fw fa-dashboard',
-    perm: 'can_write',
-    view: 'Chart',
   },
 ];
 
@@ -92,146 +83,134 @@ const RightMenu = ({
   settings,
   navbarRight,
   isFrontendRoute,
-}: RightMenuProps) => {
-  const { roles } = useSelector<any, UserWithPermissionsAndRoles>(
-    state => state.user,
-  );
-
-  // if user has any of these roles the dropdown will appear
-  const canSql = findPermission('can_sqllab', 'Superset', roles);
-  const canDashboard = findPermission('can_write', 'Dashboard', roles);
-  const canChart = findPermission('can_write', 'Chart', roles);
-  const showActionDropdown = canSql || canChart || canDashboard;
-  return (
-    <StyledDiv align={align}>
-      <Menu mode="horizontal">
-        {!navbarRight.user_is_anonymous && showActionDropdown && (
-          <SubMenu
-            data-test="new-dropdown"
-            title={
-              <StyledI data-test="new-dropdown-icon" className="fa fa-plus" />
-            }
-            icon={<Icons.TriangleDown />}
-          >
-            {dropdownItems.map(
-              menu =>
-                findPermission(menu.perm, menu.view, roles) && (
-                  <Menu.Item key={menu.label}>
-                    <a href={menu.url}>
-                      <i
-                        data-test={`menu-item-${menu.label}`}
-                        className={`fa ${menu.icon}`}
-                      />{' '}
-                      {menu.label}
-                    </a>
+}: RightMenuProps) => (
+  <StyledDiv align={align}>
+    <Menu mode="horizontal">
+      {!navbarRight.user_is_anonymous && (
+        <SubMenu
+          data-test="new-dropdown"
+          title={
+            <StyledI data-test="new-dropdown-icon" className="fa fa-plus" />
+          }
+          icon={<Icons.TriangleDown />}
+        >
+          {dropdownItems.map(menu => (
+            <Menu.Item key={menu.label}>
+              <a href={menu.url}>
+                <i
+                  data-test={`menu-item-${menu.label}`}
+                  className={`fa ${menu.icon}`}
+                />{' '}
+                {menu.label}
+              </a>
+            </Menu.Item>
+          ))}
+        </SubMenu>
+      )}
+      <SubMenu title="Settings" icon={<Icons.TriangleDown iconSize="xl" />}>
+        {settings.map((section, index) => [
+          <Menu.ItemGroup key={`${section.label}`} title={section.label}>
+            {section.childs?.map(child => {
+              if (typeof child !== 'string') {
+                return (
+                  <Menu.Item key={`${child.label}`}>
+                    {isFrontendRoute(child.url) ? (
+                      <Link to={child.url || ''}>{child.label}</Link>
+                    ) : (
+                      <a href={child.url}>{child.label}</a>
+                    )}
                   </Menu.Item>
-                ),
-            )}
-          </SubMenu>
-        )}
-        <SubMenu title="Settings" icon={<Icons.TriangleDown iconSize="xl" />}>
-          {settings.map((section, index) => [
-            <Menu.ItemGroup key={`${section.label}`} title={section.label}>
-              {section.childs?.map(child => {
-                if (typeof child !== 'string') {
-                  return (
-                    <Menu.Item key={`${child.label}`}>
-                      {isFrontendRoute(child.url) ? (
-                        <Link to={child.url || ''}>{child.label}</Link>
-                      ) : (
-                        <a href={child.url}>{child.label}</a>
-                      )}
-                    </Menu.Item>
-                  );
-                }
-                return null;
-              })}
-            </Menu.ItemGroup>,
-            index < settings.length - 1 && <Menu.Divider />,
-          ])}
+                );
+              }
+              return null;
+            })}
+          </Menu.ItemGroup>,
+          index < settings.length - 1 && <Menu.Divider />,
+        ])}
 
-          {!navbarRight.user_is_anonymous && [
-            <Menu.Divider key="user-divider" />,
-            <Menu.ItemGroup key="user-section" title={t('User')}>
-              {navbarRight.user_profile_url && (
-                <Menu.Item key="profile">
-                  <a href={navbarRight.user_profile_url}>{t('Profile')}</a>
-                </Menu.Item>
+        {!navbarRight.user_is_anonymous && [
+          <Menu.Divider key="user-divider" />,
+          <Menu.ItemGroup key="user-section" title={t('User')}>
+            {navbarRight.user_profile_url && (
+              <Menu.Item key="profile">
+                <a href={navbarRight.user_profile_url}>{t('Profile')}</a>
+              </Menu.Item>
+            )}
+            {navbarRight.user_info_url && (
+              <Menu.Item key="info">
+                <a href={navbarRight.user_info_url}>{t('Info')}</a>
+              </Menu.Item>
+            )}
+            <Menu.Item key="logout">
+              <a href={navbarRight.user_logout_url}>{t('Logout')}</a>
+            </Menu.Item>
+          </Menu.ItemGroup>,
+        ]}
+        {(navbarRight.version_string ||
+          navbarRight.version_sha ||
+          navbarRight.build_number) && [
+          <Menu.Divider key="version-info-divider" />,
+          <Menu.ItemGroup key="about-section" title={t('About')}>
+            <div className="about-section">
+              {navbarRight.show_watermark && (
+                <div css={versionInfoStyles}>
+                  {t('Powered by Apache Superset')}
+                </div>
               )}
-              {navbarRight.user_info_url && (
-                <Menu.Item key="info">
-                  <a href={navbarRight.user_info_url}>{t('Info')}</a>
-                </Menu.Item>
+              {navbarRight.version_string && (
+                <div css={versionInfoStyles}>
+                  Version: {navbarRight.version_string}
+                </div>
               )}
-              <Menu.Item key="logout">
-                <a href={navbarRight.user_logout_url}>{t('Logout')}</a>
-              </Menu.Item>
-            </Menu.ItemGroup>,
-          ]}
-          {(navbarRight.version_string || navbarRight.version_sha) && [
-            <Menu.Divider key="version-info-divider" />,
-            <Menu.ItemGroup key="about-section" title={t('About')}>
-              <div className="about-section">
-                {navbarRight.show_watermark && (
-                  <div css={versionInfoStyles}>
-                    {t('Powered by Apache Superset')}
-                  </div>
-                )}
-                {navbarRight.version_string && (
-                  <div css={versionInfoStyles}>
-                    Version: {navbarRight.version_string}
-                  </div>
-                )}
-                {navbarRight.version_sha && (
-                  <div css={versionInfoStyles}>
-                    SHA: {navbarRight.version_sha}
-                  </div>
-                )}
-                {navbarRight.build_number && (
-                  <div css={versionInfoStyles}>
-                    Build: {navbarRight.build_number}
-                  </div>
-                )}
-              </div>
-            </Menu.ItemGroup>,
-          ]}
-        </SubMenu>
-        {navbarRight.show_language_picker && (
-          <LanguagePicker
-            locale={navbarRight.locale}
-            languages={navbarRight.languages}
-          />
-        )}
-      </Menu>
-      {navbarRight.documentation_url && (
-        <StyledAnchor
-          href={navbarRight.documentation_url}
-          target="_blank"
-          rel="noreferrer"
-          title={t('Documentation')}
-        >
-          <i className="fa fa-question" />
-          &nbsp;
-        </StyledAnchor>
-      )}
-      {navbarRight.bug_report_url && (
-        <StyledAnchor
-          href={navbarRight.bug_report_url}
-          target="_blank"
-          rel="noreferrer"
-          title={t('Report a bug')}
-        >
-          <i className="fa fa-bug" />
-        </StyledAnchor>
-      )}
-      {navbarRight.user_is_anonymous && (
-        <StyledAnchor href={navbarRight.user_login_url}>
-          <i className="fa fa-fw fa-sign-in" />
-          {t('Login')}
-        </StyledAnchor>
+              {navbarRight.version_sha && (
+                <div css={versionInfoStyles}>
+                  SHA: {navbarRight.version_sha}
+                </div>
+              )}
+              {navbarRight.build_number && (
+                <div css={versionInfoStyles}>
+                  Build: {navbarRight.build_number}
+                </div>
+              )}
+            </div>
+          </Menu.ItemGroup>,
+        ]}
+      </SubMenu>
+      {navbarRight.show_language_picker && (
+        <LanguagePicker
+          locale={navbarRight.locale}
+          languages={navbarRight.languages}
+        />
       )}
-    </StyledDiv>
-  );
-};
+    </Menu>
+    {navbarRight.documentation_url && (
+      <StyledAnchor
+        href={navbarRight.documentation_url}
+        target="_blank"
+        rel="noreferrer"
+        title={t('Documentation')}
+      >
+        <i className="fa fa-question" />
+        &nbsp;
+      </StyledAnchor>
+    )}
+    {navbarRight.bug_report_url && (
+      <StyledAnchor
+        href={navbarRight.bug_report_url}
+        target="_blank"
+        rel="noreferrer"
+        title={t('Report a bug')}
+      >
+        <i className="fa fa-bug" />
+      </StyledAnchor>
+    )}
+    {navbarRight.user_is_anonymous && (
+      <StyledAnchor href={navbarRight.user_login_url}>
+        <i className="fa fa-fw fa-sign-in" />
+        {t('Login')}
+      </StyledAnchor>
+    )}
+  </StyledDiv>
+);
 
 export default RightMenu;
diff --git a/superset-frontend/src/views/menu.tsx b/superset-frontend/src/views/menu.tsx
index 68b72d7..35f6252 100644
--- a/superset-frontend/src/views/menu.tsx
+++ b/superset-frontend/src/views/menu.tsx
@@ -27,9 +27,6 @@ import { ThemeProvider } from '@superset-ui/core';
 import Menu from 'src/components/Menu/Menu';
 import { theme } from 'src/preamble';
 
-import { Provider } from 'react-redux';
-import { store } from './store';
-
 const container = document.getElementById('app');
 const bootstrapJson = container?.getAttribute('data-bootstrap') ?? '{}';
 const bootstrap = JSON.parse(bootstrapJson);
@@ -43,9 +40,7 @@ const app = (
   // @ts-ignore: emotion types defs are incompatible between core and cache
   <CacheProvider value={emotionCache}>
     <ThemeProvider theme={theme}>
-      <Provider store={store}>
-        <Menu data={menu} />
-      </Provider>
+      <Menu data={menu} />
     </ThemeProvider>
   </CacheProvider>
 );

[superset] 03/26: feat: upgrade docker image to py38 and add support for py39 (#16889)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 6ed84f2d35f1fd41194e160cb24802261c07f7c3
Author: Ville Brofeldt <33...@users.noreply.github.com>
AuthorDate: Tue Oct 5 10:35:14 2021 +0200

    feat: upgrade docker image to py38 and add support for py39 (#16889)
    
    * feat: upgrade docker image to py38 and add support for py39
    
    * update required tests
    
    (cherry picked from commit 82601abe1735bd8e25e7c0d967f8d02143a36e59)
---
 .github/workflows/superset-e2e.yml                      | 2 +-
 .github/workflows/superset-helm-lint.yml                | 2 +-
 .github/workflows/superset-python-integrationtest.yml   | 6 +++---
 .github/workflows/superset-python-misc.yml              | 6 +++---
 .github/workflows/superset-python-unittest.yml          | 2 +-
 .github/workflows/superset-translations.yml             | 2 +-
 Dockerfile                                              | 6 +++---
 Makefile                                                | 6 +++---
 RELEASING/Dockerfile.from_local_tarball                 | 2 +-
 RELEASING/Dockerfile.from_svn_tarball                   | 2 +-
 RELEASING/Dockerfile.make_docs                          | 2 +-
 RELEASING/Dockerfile.make_tarball                       | 2 +-
 docs/src/pages/docs/installation/installing_scratch.mdx | 2 +-
 requirements/base.txt                                   | 2 +-
 requirements/development.in                             | 2 +-
 requirements/development.txt                            | 4 ++--
 setup.py                                                | 3 ++-
 superset/utils/memoized.py                              | 4 +++-
 18 files changed, 30 insertions(+), 27 deletions(-)

diff --git a/.github/workflows/superset-e2e.yml b/.github/workflows/superset-e2e.yml
index ab0d61b..bc47d6a 100644
--- a/.github/workflows/superset-e2e.yml
+++ b/.github/workflows/superset-e2e.yml
@@ -67,7 +67,7 @@ jobs:
         if: steps.check.outcome == 'failure'
         uses: actions/setup-python@v2
         with:
-          python-version: "3.7"
+          python-version: "3.8"
       - name: OS dependencies
         if: steps.check.outcome == 'failure'
         uses: ./.github/actions/cached-dependencies
diff --git a/.github/workflows/superset-helm-lint.yml b/.github/workflows/superset-helm-lint.yml
index 272b7c9..a7f83f5 100644
--- a/.github/workflows/superset-helm-lint.yml
+++ b/.github/workflows/superset-helm-lint.yml
@@ -22,7 +22,7 @@ jobs:
 
       - uses: actions/setup-python@v2
         with:
-          python-version: 3.7
+          python-version: 3.8
 
       - name: Set up chart-testing
         uses: ./.github/actions/chart-testing-action
diff --git a/.github/workflows/superset-python-integrationtest.yml b/.github/workflows/superset-python-integrationtest.yml
index c006d22..43ef9e7 100644
--- a/.github/workflows/superset-python-integrationtest.yml
+++ b/.github/workflows/superset-python-integrationtest.yml
@@ -14,7 +14,7 @@ jobs:
     runs-on: ubuntu-20.04
     strategy:
       matrix:
-        python-version: [3.7]
+        python-version: [3.8]
     env:
       PYTHONPATH: ${{ github.workspace }}
       SUPERSET_CONFIG: tests.integration_tests.superset_test_config
@@ -77,7 +77,7 @@ jobs:
     runs-on: ubuntu-20.04
     strategy:
       matrix:
-        python-version: [3.7, 3.8]
+        python-version: [3.8, 3.9]
     env:
       PYTHONPATH: ${{ github.workspace }}
       SUPERSET_CONFIG: tests.integration_tests.superset_test_config
@@ -141,7 +141,7 @@ jobs:
     runs-on: ubuntu-20.04
     strategy:
       matrix:
-        python-version: [3.7]
+        python-version: [3.8]
     env:
       PYTHONPATH: ${{ github.workspace }}
       SUPERSET_CONFIG: tests.integration_tests.superset_test_config
diff --git a/.github/workflows/superset-python-misc.yml b/.github/workflows/superset-python-misc.yml
index fb365ca..9804ef2 100644
--- a/.github/workflows/superset-python-misc.yml
+++ b/.github/workflows/superset-python-misc.yml
@@ -14,7 +14,7 @@ jobs:
     runs-on: ubuntu-20.04
     strategy:
       matrix:
-        python-version: [3.7]
+        python-version: [3.8]
     steps:
       - name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
         uses: actions/checkout@v2
@@ -50,7 +50,7 @@ jobs:
     runs-on: ubuntu-20.04
     strategy:
       matrix:
-        python-version: [3.7]
+        python-version: [3.8]
     steps:
       - name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
         uses: actions/checkout@v2
@@ -76,7 +76,7 @@ jobs:
     runs-on: ubuntu-20.04
     strategy:
       matrix:
-        python-version: [3.7]
+        python-version: [3.8]
     steps:
       - name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
         uses: actions/checkout@v2
diff --git a/.github/workflows/superset-python-unittest.yml b/.github/workflows/superset-python-unittest.yml
index 69f96bc..8a64f09 100644
--- a/.github/workflows/superset-python-unittest.yml
+++ b/.github/workflows/superset-python-unittest.yml
@@ -14,7 +14,7 @@ jobs:
     runs-on: ubuntu-20.04
     strategy:
       matrix:
-        python-version: [3.7,3.8]
+        python-version: [3.8, 3.9]
     env:
       PYTHONPATH: ${{ github.workspace }}
     steps:
diff --git a/.github/workflows/superset-translations.yml b/.github/workflows/superset-translations.yml
index 029c914..93fe58b 100644
--- a/.github/workflows/superset-translations.yml
+++ b/.github/workflows/superset-translations.yml
@@ -35,7 +35,7 @@ jobs:
     runs-on: ubuntu-20.04
     strategy:
       matrix:
-        python-version: [3.7]
+        python-version: [3.8]
     steps:
       - name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
         uses: actions/checkout@v2
diff --git a/Dockerfile b/Dockerfile
index 8244f16..211ea55 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -18,7 +18,7 @@
 ######################################################################
 # PY stage that simply does a pip install on our requirements
 ######################################################################
-ARG PY_VER=3.7.9
+ARG PY_VER=3.8.12
 FROM python:${PY_VER} AS superset-py
 
 RUN mkdir /app \
@@ -73,7 +73,7 @@ RUN cd /app/superset-frontend \
 ######################################################################
 # Final lean image...
 ######################################################################
-ARG PY_VER=3.7.9
+ARG PY_VER=3.8.12
 FROM python:${PY_VER} AS lean
 
 ENV LANG=C.UTF-8 \
@@ -94,7 +94,7 @@ RUN mkdir -p ${PYTHONPATH} \
             libpq-dev \
         && rm -rf /var/lib/apt/lists/*
 
-COPY --from=superset-py /usr/local/lib/python3.7/site-packages/ /usr/local/lib/python3.7/site-packages/
+COPY --from=superset-py /usr/local/lib/python3.8/site-packages/ /usr/local/lib/python3.8/site-packages/
 # Copying site-packages doesn't move the CLIs, so let's copy them one by one
 COPY --from=superset-py /usr/local/bin/gunicorn /usr/local/bin/celery /usr/local/bin/flask /usr/bin/
 COPY --from=superset-node /app/superset/static/assets /app/superset/static/assets
diff --git a/Makefile b/Makefile
index 7593a28..a7066bd 100644
--- a/Makefile
+++ b/Makefile
@@ -15,8 +15,8 @@
 # limitations under the License.
 #
 
-# Python version installed; we need 3.8 or 3.7
-PYTHON=`command -v python3.8 || command -v python3.7`
+# Python version installed; we need 3.7-3.9
+PYTHON=`command -v python3.9 || command -v python3.8 || command -v python3.7`
 
 .PHONY: install superset venv pre-commit
 
@@ -62,7 +62,7 @@ update-js:
 
 venv:
 	# Create a virtual environment and activate it (recommended)
-	if ! [ -x "${PYTHON}" ]; then echo "You need Python 3.7 or 3.8 installed"; exit 1; fi
+	if ! [ -x "${PYTHON}" ]; then echo "You need Python 3.7, 3.8 or 3.9 installed"; exit 1; fi
 	test -d venv || ${PYTHON} -m venv venv # setup a python3 virtualenv
 	. venv/bin/activate
 
diff --git a/RELEASING/Dockerfile.from_local_tarball b/RELEASING/Dockerfile.from_local_tarball
index affb4d8..be08d6f 100644
--- a/RELEASING/Dockerfile.from_local_tarball
+++ b/RELEASING/Dockerfile.from_local_tarball
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-FROM python:3.7-buster
+FROM python:3.8-buster
 
 RUN useradd --user-group --create-home --no-log-init --shell /bin/bash superset
 
diff --git a/RELEASING/Dockerfile.from_svn_tarball b/RELEASING/Dockerfile.from_svn_tarball
index 3194ea5..5f9bde9 100644
--- a/RELEASING/Dockerfile.from_svn_tarball
+++ b/RELEASING/Dockerfile.from_svn_tarball
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-FROM python:3.7-buster
+FROM python:3.8-buster
 
 RUN useradd --user-group --create-home --no-log-init --shell /bin/bash superset
 
diff --git a/RELEASING/Dockerfile.make_docs b/RELEASING/Dockerfile.make_docs
index 1bbaa30..edb9c77 100644
--- a/RELEASING/Dockerfile.make_docs
+++ b/RELEASING/Dockerfile.make_docs
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-FROM python:3.7-buster
+FROM python:3.8-buster
 ARG VERSION
 
 RUN git clone --depth 1 --branch ${VERSION} https://github.com/apache/superset.git /superset
diff --git a/RELEASING/Dockerfile.make_tarball b/RELEASING/Dockerfile.make_tarball
index 49e3fc3..3e50eb4 100644
--- a/RELEASING/Dockerfile.make_tarball
+++ b/RELEASING/Dockerfile.make_tarball
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-FROM python:3.7-buster
+FROM python:3.8-buster
 
 RUN apt-get update -y
 RUN apt-get install -y jq
diff --git a/docs/src/pages/docs/installation/installing_scratch.mdx b/docs/src/pages/docs/installation/installing_scratch.mdx
index 2fb1b7a..7ea1f32 100644
--- a/docs/src/pages/docs/installation/installing_scratch.mdx
+++ b/docs/src/pages/docs/installation/installing_scratch.mdx
@@ -65,7 +65,7 @@ We don't recommend using the system installed Python. Instead, first install the
 brew install readline pkg-config libffi openssl mysql postgres
 ```
 
-You should install a recent version of Python (Superset uses 3.7.9). We'd recommend using a Python version manager like [pyenv](https://github.com/pyenv/pyenv) (and also [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv)).
+You should install a recent version of Python (the official docker image uses 3.8.12). We'd recommend using a Python version manager like [pyenv](https://github.com/pyenv/pyenv) (and also [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv)).
 
 Let's also make sure we have the latest version of `pip` and `setuptools`:
 
diff --git a/requirements/base.txt b/requirements/base.txt
index cbbc55f..3d24d52 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -257,7 +257,7 @@ sqlalchemy==1.3.24
     #   flask-sqlalchemy
     #   marshmallow-sqlalchemy
     #   sqlalchemy-utils
-sqlalchemy-utils==0.36.8
+sqlalchemy-utils==0.37.8
     # via
     #   apache-superset
     #   flask-appbuilder
diff --git a/requirements/development.in b/requirements/development.in
index db21623..d53e050 100644
--- a/requirements/development.in
+++ b/requirements/development.in
@@ -18,7 +18,7 @@
 -r base.in
 flask-cors>=2.0.0
 mysqlclient==1.4.2.post1
-pillow>=7.0.0,<8.0.0
+pillow>=8.3.1,<9
 pydruid>=0.6.1,<0.7
 pyhive[hive]>=0.6.1
 psycopg2-binary==2.8.5
diff --git a/requirements/development.txt b/requirements/development.txt
index 82133b4..3a62a28 100644
--- a/requirements/development.txt
+++ b/requirements/development.txt
@@ -1,4 +1,4 @@
-# SHA1:e4f3ea65026a8aec3735d6d9977f89fef4a1a4f9
+# SHA1:dbd3e93a11a36fc6b18d6194ac96ba29bd0ad2a8
 #
 # This file is autogenerated by pip-compile-multi
 # To update, run:
@@ -40,7 +40,7 @@ mysqlclient==1.4.2.post1
     # via -r requirements/development.in
 openpyxl==3.0.7
     # via tabulator
-pillow==7.2.0
+pillow==8.3.1
     # via -r requirements/development.in
 progress==1.6
     # via -r requirements/development.in
diff --git a/setup.py b/setup.py
index 7baf648..abf5143 100644
--- a/setup.py
+++ b/setup.py
@@ -106,7 +106,7 @@ setup(
         "simplejson>=3.15.0",
         "slackclient==2.5.0",  # PINNED! slack changes file upload api in the future versions
         "sqlalchemy>=1.3.16, <1.4, !=1.3.21",
-        "sqlalchemy-utils>=0.36.6, <0.37",
+        "sqlalchemy-utils>=0.37.8, <0.38",
         "sqlparse==0.3.0",  # PINNED! see https://github.com/andialbrecht/sqlparse/issues/562
         "tabulate==0.8.9",
         "typing-extensions>=3.10, <4",  # needed to support Literal (3.8) and TypeGuard (3.10)
@@ -169,5 +169,6 @@ setup(
     classifiers=[
         "Programming Language :: Python :: 3.7",
         "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
     ],
 )
diff --git a/superset/utils/memoized.py b/superset/utils/memoized.py
index 916984c..153542f 100644
--- a/superset/utils/memoized.py
+++ b/superset/utils/memoized.py
@@ -64,7 +64,9 @@ class _memoized:
         if not self.is_method:
             self.is_method = True
         # Support instance methods.
-        return functools.partial(self.__call__, obj)
+        func = functools.partial(self.__call__, obj)
+        func.__func__ = self.func  # type: ignore
+        return func
 
 
 def memoized(

[superset] 09/26: fix: rolling and cum operator on multiple series (#16945)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 19d2fef490d3597d8fcf659442f0829cf8ac734f
Author: Yongjie Zhao <yo...@gmail.com>
AuthorDate: Thu Oct 7 09:42:08 2021 +0100

    fix: rolling and cum operator on multiple series (#16945)
    
    * fix: rolling and cum operator on multiple series
    
    * add UT
    
    * updates
    
    (cherry picked from commit fd8461406d62e818c4bc88075ac25ddf44ba9caf)
---
 superset/utils/pandas_postprocessing.py            |  60 +++++++++--
 tests/integration_tests/fixtures/dataframes.py     |  16 +++
 .../pandas_postprocessing_tests.py                 | 118 +++++++++++++++++++++
 3 files changed, 185 insertions(+), 9 deletions(-)

diff --git a/superset/utils/pandas_postprocessing.py b/superset/utils/pandas_postprocessing.py
index 0667774..aa80cfd 100644
--- a/superset/utils/pandas_postprocessing.py
+++ b/superset/utils/pandas_postprocessing.py
@@ -131,6 +131,9 @@ def _flatten_column_after_pivot(
 def validate_column_args(*argnames: str) -> Callable[..., Any]:
     def wrapper(func: Callable[..., Any]) -> Callable[..., Any]:
         def wrapped(df: DataFrame, **options: Any) -> Any:
+            if options.get("is_pivot_df"):
+                # skip validation when pivot Dataframe
+                return func(df, **options)
             columns = df.columns.tolist()
             for name in argnames:
                 if name in options and not all(
@@ -223,6 +226,7 @@ def pivot(  # pylint: disable=too-many-arguments,too-many-locals
     marginal_distributions: Optional[bool] = None,
     marginal_distribution_name: Optional[str] = None,
     flatten_columns: bool = True,
+    reset_index: bool = True,
 ) -> DataFrame:
     """
     Perform a pivot operation on a DataFrame.
@@ -243,6 +247,7 @@ def pivot(  # pylint: disable=too-many-arguments,too-many-locals
     :param marginal_distribution_name: Name of row/column with marginal distribution.
            Default to 'All'.
     :param flatten_columns: Convert column names to strings
+    :param reset_index: Convert index to column
     :return: A pivot table
     :raises QueryObjectValidationError: If the request in incorrect
     """
@@ -300,7 +305,8 @@ def pivot(  # pylint: disable=too-many-arguments,too-many-locals
             _flatten_column_after_pivot(col, aggregates) for col in df.columns
         ]
     # return index as regular column
-    df.reset_index(level=0, inplace=True)
+    if reset_index:
+        df.reset_index(level=0, inplace=True)
     return df
 
 
@@ -343,13 +349,14 @@ def sort(df: DataFrame, columns: Dict[str, bool]) -> DataFrame:
 @validate_column_args("columns")
 def rolling(  # pylint: disable=too-many-arguments
     df: DataFrame,
-    columns: Dict[str, str],
     rolling_type: str,
+    columns: Optional[Dict[str, str]] = None,
     window: Optional[int] = None,
     rolling_type_options: Optional[Dict[str, Any]] = None,
     center: bool = False,
     win_type: Optional[str] = None,
     min_periods: Optional[int] = None,
+    is_pivot_df: bool = False,
 ) -> DataFrame:
     """
     Apply a rolling window on the dataset. See the Pandas docs for further details:
@@ -369,11 +376,16 @@ def rolling(  # pylint: disable=too-many-arguments
     :param win_type: Type of window function.
     :param min_periods: The minimum amount of periods required for a row to be included
                         in the result set.
+    :param is_pivot_df: Dataframe is pivoted or not
     :return: DataFrame with the rolling columns
     :raises QueryObjectValidationError: If the request in incorrect
     """
     rolling_type_options = rolling_type_options or {}
-    df_rolling = df[columns.keys()]
+    columns = columns or {}
+    if is_pivot_df:
+        df_rolling = df
+    else:
+        df_rolling = df[columns.keys()]
     kwargs: Dict[str, Union[str, int]] = {}
     if window is None:
         raise QueryObjectValidationError(_("Undefined window for rolling operation"))
@@ -405,10 +417,20 @@ def rolling(  # pylint: disable=too-many-arguments
                 options=rolling_type_options,
             )
         ) from ex
-    df = _append_columns(df, df_rolling, columns)
+
+    if is_pivot_df:
+        agg_in_pivot_df = df.columns.get_level_values(0).drop_duplicates().to_list()
+        agg: Dict[str, Dict[str, Any]] = {col: {} for col in agg_in_pivot_df}
+        df_rolling.columns = [
+            _flatten_column_after_pivot(col, agg) for col in df_rolling.columns
+        ]
+        df_rolling.reset_index(level=0, inplace=True)
+    else:
+        df_rolling = _append_columns(df, df_rolling, columns)
+
     if min_periods:
-        df = df[min_periods:]
-    return df
+        df_rolling = df_rolling[min_periods:]
+    return df_rolling
 
 
 @validate_column_args("columns", "drop", "rename")
@@ -524,7 +546,12 @@ def compare(  # pylint: disable=too-many-arguments
 
 
 @validate_column_args("columns")
-def cum(df: DataFrame, columns: Dict[str, str], operator: str) -> DataFrame:
+def cum(
+    df: DataFrame,
+    operator: str,
+    columns: Optional[Dict[str, str]] = None,
+    is_pivot_df: bool = False,
+) -> DataFrame:
     """
     Calculate cumulative sum/product/min/max for select columns.
 
@@ -535,9 +562,14 @@ def cum(df: DataFrame, columns: Dict[str, str], operator: str) -> DataFrame:
            `y2` based on cumulative values calculated from `y`, leaving the original
            column `y` unchanged.
     :param operator: cumulative operator, e.g. `sum`, `prod`, `min`, `max`
+    :param is_pivot_df: Dataframe is pivoted or not
     :return: DataFrame with cumulated columns
     """
-    df_cum = df[columns.keys()]
+    columns = columns or {}
+    if is_pivot_df:
+        df_cum = df
+    else:
+        df_cum = df[columns.keys()]
     operation = "cum" + operator
     if operation not in ALLOWLIST_CUMULATIVE_FUNCTIONS or not hasattr(
         df_cum, operation
@@ -545,7 +577,17 @@ def cum(df: DataFrame, columns: Dict[str, str], operator: str) -> DataFrame:
         raise QueryObjectValidationError(
             _("Invalid cumulative operator: %(operator)s", operator=operator)
         )
-    return _append_columns(df, getattr(df_cum, operation)(), columns)
+    if is_pivot_df:
+        df_cum = getattr(df_cum, operation)()
+        agg_in_pivot_df = df.columns.get_level_values(0).drop_duplicates().to_list()
+        agg: Dict[str, Dict[str, Any]] = {col: {} for col in agg_in_pivot_df}
+        df_cum.columns = [
+            _flatten_column_after_pivot(col, agg) for col in df_cum.columns
+        ]
+        df_cum.reset_index(level=0, inplace=True)
+    else:
+        df_cum = _append_columns(df, getattr(df_cum, operation)(), columns)
+    return df_cum
 
 
 def geohash_decode(
diff --git a/tests/integration_tests/fixtures/dataframes.py b/tests/integration_tests/fixtures/dataframes.py
index 28bc32f..2a49bd3 100644
--- a/tests/integration_tests/fixtures/dataframes.py
+++ b/tests/integration_tests/fixtures/dataframes.py
@@ -165,3 +165,19 @@ prophet_df = DataFrame(
         "b": [4, 3, 4.1, 3.95],
     }
 )
+
+single_metric_df = DataFrame(
+    {
+        "dttm": to_datetime(["2019-01-01", "2019-01-01", "2019-01-02", "2019-01-02",]),
+        "country": ["UK", "US", "UK", "US"],
+        "sum_metric": [5, 6, 7, 8],
+    }
+)
+multiple_metrics_df = DataFrame(
+    {
+        "dttm": to_datetime(["2019-01-01", "2019-01-01", "2019-01-02", "2019-01-02",]),
+        "country": ["UK", "US", "UK", "US"],
+        "sum_metric": [5, 6, 7, 8],
+        "count_metric": [1, 2, 3, 4],
+    }
+)
diff --git a/tests/integration_tests/pandas_postprocessing_tests.py b/tests/integration_tests/pandas_postprocessing_tests.py
index f242e32..7221130 100644
--- a/tests/integration_tests/pandas_postprocessing_tests.py
+++ b/tests/integration_tests/pandas_postprocessing_tests.py
@@ -35,6 +35,8 @@ from superset.utils.core import (
 from .base_tests import SupersetTestCase
 from .fixtures.dataframes import (
     categories_df,
+    single_metric_df,
+    multiple_metrics_df,
     lonlat_df,
     names_df,
     timeseries_df,
@@ -305,6 +307,23 @@ class TestPostProcessing(SupersetTestCase):
         )
         self.assertTrue(np.isnan(df["metric, 1, 1"][0]))
 
+    def test_pivot_without_flatten_columns_and_reset_index(self):
+        df = proc.pivot(
+            df=single_metric_df,
+            index=["dttm"],
+            columns=["country"],
+            aggregates={"sum_metric": {"operator": "sum"}},
+            flatten_columns=False,
+            reset_index=False,
+        )
+        #                metric
+        # country        UK US
+        # dttm
+        # 2019-01-01      5  6
+        # 2019-01-02      7  8
+        assert df.columns.to_list() == [("sum_metric", "UK"), ("sum_metric", "US")]
+        assert df.index.to_list() == to_datetime(["2019-01-01", "2019-01-02"]).to_list()
+
     def test_aggregate(self):
         aggregates = {
             "asc sum": {"column": "asc_idx", "operator": "sum"},
@@ -405,6 +424,60 @@ class TestPostProcessing(SupersetTestCase):
             window=2,
         )
 
+    def test_rolling_with_pivot_df_and_single_metric(self):
+        pivot_df = proc.pivot(
+            df=single_metric_df,
+            index=["dttm"],
+            columns=["country"],
+            aggregates={"sum_metric": {"operator": "sum"}},
+            flatten_columns=False,
+            reset_index=False,
+        )
+        rolling_df = proc.rolling(
+            df=pivot_df, rolling_type="sum", window=2, min_periods=0, is_pivot_df=True,
+        )
+        #         dttm  UK  US
+        # 0 2019-01-01   5   6
+        # 1 2019-01-02  12  14
+        assert rolling_df["UK"].to_list() == [5.0, 12.0]
+        assert rolling_df["US"].to_list() == [6.0, 14.0]
+        assert (
+            rolling_df["dttm"].to_list()
+            == to_datetime(["2019-01-01", "2019-01-02",]).to_list()
+        )
+
+        rolling_df = proc.rolling(
+            df=pivot_df, rolling_type="sum", window=2, min_periods=2, is_pivot_df=True,
+        )
+        assert rolling_df.empty is True
+
+    def test_rolling_with_pivot_df_and_multiple_metrics(self):
+        pivot_df = proc.pivot(
+            df=multiple_metrics_df,
+            index=["dttm"],
+            columns=["country"],
+            aggregates={
+                "sum_metric": {"operator": "sum"},
+                "count_metric": {"operator": "sum"},
+            },
+            flatten_columns=False,
+            reset_index=False,
+        )
+        rolling_df = proc.rolling(
+            df=pivot_df, rolling_type="sum", window=2, min_periods=0, is_pivot_df=True,
+        )
+        #         dttm  count_metric, UK  count_metric, US  sum_metric, UK  sum_metric, US
+        # 0 2019-01-01               1.0               2.0             5.0             6.0
+        # 1 2019-01-02               4.0               6.0            12.0            14.0
+        assert rolling_df["count_metric, UK"].to_list() == [1.0, 4.0]
+        assert rolling_df["count_metric, US"].to_list() == [2.0, 6.0]
+        assert rolling_df["sum_metric, UK"].to_list() == [5.0, 12.0]
+        assert rolling_df["sum_metric, US"].to_list() == [6.0, 14.0]
+        assert (
+            rolling_df["dttm"].to_list()
+            == to_datetime(["2019-01-01", "2019-01-02",]).to_list()
+        )
+
     def test_select(self):
         # reorder columns
         post_df = proc.select(df=timeseries_df, columns=["y", "label"])
@@ -557,6 +630,51 @@ class TestPostProcessing(SupersetTestCase):
             operator="abc",
         )
 
+    def test_cum_with_pivot_df_and_single_metric(self):
+        pivot_df = proc.pivot(
+            df=single_metric_df,
+            index=["dttm"],
+            columns=["country"],
+            aggregates={"sum_metric": {"operator": "sum"}},
+            flatten_columns=False,
+            reset_index=False,
+        )
+        cum_df = proc.cum(df=pivot_df, operator="sum", is_pivot_df=True,)
+        #         dttm  UK  US
+        # 0 2019-01-01   5   6
+        # 1 2019-01-02  12  14
+        assert cum_df["UK"].to_list() == [5.0, 12.0]
+        assert cum_df["US"].to_list() == [6.0, 14.0]
+        assert (
+            cum_df["dttm"].to_list()
+            == to_datetime(["2019-01-01", "2019-01-02",]).to_list()
+        )
+
+    def test_cum_with_pivot_df_and_multiple_metrics(self):
+        pivot_df = proc.pivot(
+            df=multiple_metrics_df,
+            index=["dttm"],
+            columns=["country"],
+            aggregates={
+                "sum_metric": {"operator": "sum"},
+                "count_metric": {"operator": "sum"},
+            },
+            flatten_columns=False,
+            reset_index=False,
+        )
+        cum_df = proc.cum(df=pivot_df, operator="sum", is_pivot_df=True,)
+        #         dttm  count_metric, UK  count_metric, US  sum_metric, UK  sum_metric, US
+        # 0 2019-01-01                 1                 2               5               6
+        # 1 2019-01-02                 4                 6              12              14
+        assert cum_df["count_metric, UK"].to_list() == [1.0, 4.0]
+        assert cum_df["count_metric, US"].to_list() == [2.0, 6.0]
+        assert cum_df["sum_metric, UK"].to_list() == [5.0, 12.0]
+        assert cum_df["sum_metric, US"].to_list() == [6.0, 14.0]
+        assert (
+            cum_df["dttm"].to_list()
+            == to_datetime(["2019-01-01", "2019-01-02",]).to_list()
+        )
+
     def test_geohash_decode(self):
         # decode lon/lat from geohash
         post_df = proc.geohash_decode(

[superset] 18/26: fix: escape bind-like strings in virtual table query (#17111)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 35dda573cf32593d5d68b25ca86ed095ceab88b8
Author: Ville Brofeldt <33...@users.noreply.github.com>
AuthorDate: Thu Oct 14 17:59:28 2021 +0200

    fix: escape bind-like strings in virtual table query (#17111)
    
    (cherry picked from commit 434b5767c910d984e2b39655999f96afd00b84a6)
---
 superset/connectors/sqla/models.py |  6 ++++--
 superset/utils/core.py             | 29 +++++++++++++++++++++++++++++
 2 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/superset/connectors/sqla/models.py b/superset/connectors/sqla/models.py
index a27a4a1..0f6b705 100644
--- a/superset/connectors/sqla/models.py
+++ b/superset/connectors/sqla/models.py
@@ -65,8 +65,8 @@ from sqlalchemy.orm import backref, Query, relationship, RelationshipProperty, S
 from sqlalchemy.orm.mapper import Mapper
 from sqlalchemy.schema import UniqueConstraint
 from sqlalchemy.sql import column, ColumnElement, literal_column, table, text
-from sqlalchemy.sql.elements import ColumnClause
-from sqlalchemy.sql.expression import Label, Select, TextAsFrom, TextClause
+from sqlalchemy.sql.elements import ColumnClause, TextClause
+from sqlalchemy.sql.expression import Label, Select, TextAsFrom
 from sqlalchemy.sql.selectable import Alias, TableClause
 
 from superset import app, db, is_feature_enabled, security_manager
@@ -809,6 +809,8 @@ class SqlaTable(Model, BaseDatasource):  # pylint: disable=too-many-public-metho
                     )
                 ) from ex
         sql = sqlparse.format(sql.strip("\t\r\n; "), strip_comments=True)
+        # we need to escape strings that SQLAlchemy interprets as bind parameters
+        sql = utils.escape_sqla_query_binds(sql)
         if not sql:
             raise QueryObjectValidationError(_("Virtual dataset query cannot be empty"))
         if len(sqlparse.split(sql)) > 1:
diff --git a/superset/utils/core.py b/superset/utils/core.py
index 71d5934..9f30c64 100644
--- a/superset/utils/core.py
+++ b/superset/utils/core.py
@@ -80,6 +80,7 @@ from sqlalchemy import event, exc, select, Text
 from sqlalchemy.dialects.mysql import MEDIUMTEXT
 from sqlalchemy.engine import Connection, Engine
 from sqlalchemy.engine.reflection import Inspector
+from sqlalchemy.sql.elements import TextClause
 from sqlalchemy.sql.type_api import Variant
 from sqlalchemy.types import TEXT, TypeDecorator, TypeEngine
 from typing_extensions import TypedDict, TypeGuard
@@ -131,6 +132,8 @@ JS_MAX_INTEGER = 9007199254740991  # Largest int Java Script can handle 2^53-1
 
 InputType = TypeVar("InputType")
 
+BIND_PARAM_REGEX = TextClause._bind_params_regex  # pylint: disable=protected-access
+
 
 class LenientEnum(Enum):
     """Enums with a `get` method that convert a enum value to `Enum` if it is a
@@ -1784,3 +1787,29 @@ def apply_max_row_limit(limit: int, max_limit: Optional[int] = None,) -> int:
     if limit != 0:
         return min(max_limit, limit)
     return max_limit
+
+
+def escape_sqla_query_binds(sql: str) -> str:
+    """
+    Replace strings in a query that SQLAlchemy would otherwise interpret as
+    bind parameters.
+
+    :param sql: unescaped query string
+    :return: escaped query string
+    >>> escape_sqla_query_binds("select ':foo'")
+    "select '\\\\:foo'"
+    >>> escape_sqla_query_binds("select 'foo'::TIMESTAMP")
+    "select 'foo'::TIMESTAMP"
+    >>> escape_sqla_query_binds("select ':foo :bar'::TIMESTAMP")
+    "select '\\\\:foo \\\\:bar'::TIMESTAMP"
+    >>> escape_sqla_query_binds("select ':foo :foo :bar'::TIMESTAMP")
+    "select '\\\\:foo \\\\:foo \\\\:bar'::TIMESTAMP"
+    """
+    matches = BIND_PARAM_REGEX.finditer(sql)
+    processed_binds = set()
+    for match in matches:
+        bind = match.group(0)
+        if bind not in processed_binds:
+            sql = sql.replace(bind, bind.replace(":", "\\:"))
+            processed_binds.add(bind)
+    return sql

[superset] 19/26: feat: Custom filters control (#17006)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 4e3fa1a141dc8527d7e9ddfc034186c50e398042
Author: simcha90 <56...@users.noreply.github.com>
AuthorDate: Fri Oct 15 14:14:04 2021 +0300

    feat: Custom filters control (#17006)
    
    * fix:fix get permission function
    
    * feat: customize filter control
    
    * fix: fix types
    
    * fix: fix merge
    
    * refactor: update according CR
    
    * refactor: fix CR notes
    
    (cherry picked from commit eebc953dd599ed13a4893e9c9c726bddb19f7666)
---
 .../FilterControl/AdhocFilterControl/index.jsx     |   6 +
 .../FilterControl/AdhocFilterEditPopover/index.jsx | 122 +++++++++++++--------
 .../index.tsx                                      |  46 ++++----
 .../FilterControl/AdhocFilterOption/index.jsx      |   6 +
 .../AdhocFilterPopoverTrigger/index.tsx            |   5 +
 5 files changed, 118 insertions(+), 67 deletions(-)

diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterControl/index.jsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterControl/index.jsx
index 4147454..44f04b1 100644
--- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterControl/index.jsx
+++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterControl/index.jsx
@@ -59,6 +59,8 @@ const selectedMetricType = PropTypes.oneOfType([
 const propTypes = {
   label: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
   name: PropTypes.string,
+  sections: PropTypes.arrayOf(PropTypes.string),
+  operators: PropTypes.arrayOf(PropTypes.string),
   onChange: PropTypes.func,
   value: PropTypes.arrayOf(adhocFilterType),
   datasource: PropTypes.object,
@@ -107,6 +109,8 @@ class AdhocFilterControl extends React.Component {
         adhocFilter={adhocFilter}
         onFilterEdit={this.onFilterEdit}
         options={this.state.options}
+        sections={this.props.sections}
+        operators={this.props.operators}
         datasource={this.props.datasource}
         onRemoveFilter={() => this.onRemoveFilter(index)}
         onMoveLabel={this.moveLabel}
@@ -324,6 +328,8 @@ class AdhocFilterControl extends React.Component {
   addNewFilterPopoverTrigger(trigger) {
     return (
       <AdhocFilterPopoverTrigger
+        operators={this.props.operators}
+        sections={this.props.sections}
         adhocFilter={new AdhocFilter({})}
         datasource={this.props.datasource}
         options={this.state.options}
diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/index.jsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/index.jsx
index 2fd29a6..afe501e 100644
--- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/index.jsx
+++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/index.jsx
@@ -46,6 +46,8 @@ const propTypes = {
   datasource: PropTypes.object,
   partitionColumn: PropTypes.string,
   theme: PropTypes.object,
+  sections: PropTypes.arrayOf(PropTypes.string),
+  operators: PropTypes.arrayOf(PropTypes.string),
 };
 
 const ResizeIcon = styled.i`
@@ -54,6 +56,11 @@ const ResizeIcon = styled.i`
 
 const startingWidth = 320;
 const startingHeight = 240;
+const SectionWrapper = styled.div`
+  .ant-select {
+    margin-bottom: ${({ theme }) => theme.gridUnit * 4}px;
+  }
+`;
 
 const FilterPopoverContentContainer = styled.div`
   .adhoc-filter-edit-tabs > .nav-tabs {
@@ -166,15 +173,51 @@ export default class AdhocFilterEditPopover extends React.Component {
       onResize,
       datasource,
       partitionColumn,
+      sections = ['SIMPLE', 'CUSTOM_SQL'],
       theme,
+      operators,
       ...popoverProps
     } = this.props;
 
     const { adhocFilter } = this.state;
 
+    const resultSections =
+      datasource?.type === 'druid'
+        ? sections.filter(s => s !== 'CUSTOM_SQL')
+        : sections;
+
     const stateIsValid = adhocFilter.isValid();
     const hasUnsavedChanges = !adhocFilter.equals(propsAdhocFilter);
 
+    const sectionRenders = {};
+
+    sectionRenders.CUSTOM_SQL = (
+      <ErrorBoundary>
+        <AdhocFilterEditPopoverSqlTabContent
+          adhocFilter={this.state.adhocFilter}
+          onChange={this.onAdhocFilterChange}
+          options={this.props.options}
+          height={this.state.height}
+          activeKey={this.state.activeKey}
+        />
+      </ErrorBoundary>
+    );
+
+    sectionRenders.SIMPLE = (
+      <ErrorBoundary>
+        <AdhocFilterEditPopoverSimpleTabContent
+          operators={operators}
+          adhocFilter={this.state.adhocFilter}
+          onChange={this.onAdhocFilterChange}
+          options={options}
+          datasource={datasource}
+          onHeightChange={this.adjustHeight}
+          partitionColumn={partitionColumn}
+          popoverRef={this.popoverContentRef.current}
+        />
+      </ErrorBoundary>
+    );
+
     return (
       <FilterPopoverContentContainer
         id="filter-edit-popover"
@@ -182,55 +225,38 @@ export default class AdhocFilterEditPopover extends React.Component {
         data-test="filter-edit-popover"
         ref={this.popoverContentRef}
       >
-        <Tabs
-          id="adhoc-filter-edit-tabs"
-          defaultActiveKey={adhocFilter.expressionType}
-          className="adhoc-filter-edit-tabs"
-          data-test="adhoc-filter-edit-tabs"
-          style={{ minHeight: this.state.height, width: this.state.width }}
-          allowOverflow
-          onChange={this.onTabChange}
-        >
-          <Tabs.TabPane
-            className="adhoc-filter-edit-tab"
-            key={EXPRESSION_TYPES.SIMPLE}
-            tab={t('Simple')}
-          >
-            <ErrorBoundary>
-              <AdhocFilterEditPopoverSimpleTabContent
-                adhocFilter={this.state.adhocFilter}
-                onChange={this.onAdhocFilterChange}
-                options={options}
-                datasource={datasource}
-                onHeightChange={this.adjustHeight}
-                partitionColumn={partitionColumn}
-                popoverRef={this.popoverContentRef.current}
-              />
-            </ErrorBoundary>
-          </Tabs.TabPane>
-          <Tabs.TabPane
-            className="adhoc-filter-edit-tab"
-            key={EXPRESSION_TYPES.SQL}
-            tab={t('Custom SQL')}
+        {resultSections.length > 1 ? (
+          <Tabs
+            id="adhoc-filter-edit-tabs"
+            defaultActiveKey={adhocFilter.expressionType}
+            className="adhoc-filter-edit-tabs"
+            data-test="adhoc-filter-edit-tabs"
+            style={{ minHeight: this.state.height, width: this.state.width }}
+            allowOverflow
+            onChange={this.onTabChange}
           >
-            <ErrorBoundary>
-              {!this.props.datasource ||
-              this.props.datasource.type !== 'druid' ? (
-                <AdhocFilterEditPopoverSqlTabContent
-                  adhocFilter={this.state.adhocFilter}
-                  onChange={this.onAdhocFilterChange}
-                  options={this.props.options}
-                  height={this.state.height}
-                  activeKey={this.state.activeKey}
-                />
-              ) : (
-                <div className="custom-sql-disabled-message">
-                  Custom SQL Filters are not available on druid datasources
-                </div>
-              )}
-            </ErrorBoundary>
-          </Tabs.TabPane>
-        </Tabs>
+            {resultSections.includes('SIMPLE') && (
+              <Tabs.TabPane
+                className="adhoc-filter-edit-tab"
+                key={EXPRESSION_TYPES.SIMPLE}
+                tab={t('Simple')}
+              >
+                {sectionRenders.SIMPLE}
+              </Tabs.TabPane>
+            )}
+            {resultSections.includes('CUSTOM_SQL') && (
+              <Tabs.TabPane
+                className="adhoc-filter-edit-tab"
+                key={EXPRESSION_TYPES.SQL}
+                tab={t('Custom SQL')}
+              >
+                {sectionRenders.CUSTOM_SQL}
+              </Tabs.TabPane>
+            )}
+          </Tabs>
+        ) : (
+          <SectionWrapper>{sectionRenders[resultSections[0]]}</SectionWrapper>
+        )}
         <div>
           <Button buttonSize="small" onClick={this.props.onClose} cta>
             {t('Close')}
diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx
index bc7b091..c576863 100644
--- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx
+++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx
@@ -38,6 +38,20 @@ import AdhocFilter, {
 } from 'src/explore/components/controls/FilterControl/AdhocFilter';
 import { Input } from 'src/common/components';
 
+const StyledInput = styled(Input)`
+  margin-bottom: ${({ theme }) => theme.gridUnit * 4}px;
+`;
+
+const SelectWithLabel = styled(Select)<{ labelText: string }>`
+  .ant-select-selector::after {
+    content: ${({ labelText }) => labelText || '\\A0'};
+    display: inline-block;
+    white-space: nowrap;
+    color: ${({ theme }) => theme.colors.grayscale.light1};
+    width: max-content;
+  }
+`;
+
 export interface SimpleColumnType {
   id: number;
   column_name: string;
@@ -81,6 +95,7 @@ export interface Props {
     filter_select: boolean;
   };
   partitionColumn: string;
+  operators?: Operators[];
 }
 export const useSimpleTabFilterProps = (props: Props) => {
   const isOperatorRelevant = (operator: Operators, subject: string) => {
@@ -280,7 +295,9 @@ const AdhocFilterEditPopoverSimpleTabContent: React.FC<Props> = props => {
   const operatorSelectProps = {
     placeholder: t(
       '%s operator(s)',
-      OPERATORS_OPTIONS.filter(op => isOperatorRelevant(op, subject)).length,
+      (props.operators ?? OPERATORS_OPTIONS).filter(op =>
+        isOperatorRelevant(op, subject),
+      ).length,
     ),
     value: operatorId,
     onChange: onOperatorChange,
@@ -310,16 +327,6 @@ const AdhocFilterEditPopoverSimpleTabContent: React.FC<Props> = props => {
   const labelText =
     comparator && comparator.length > 0 && createSuggestionsPlaceholder();
 
-  const SelectWithLabel = styled(Select)`
-    .ant-select-selector::after {
-      content: ${() => labelText || '\\A0'};
-      display: inline-block;
-      white-space: nowrap;
-      color: ${({ theme }) => theme.colors.grayscale.light1};
-      width: max-content;
-    }
-  `;
-
   useEffect(() => {
     const refreshComparatorSuggestions = () => {
       const { datasource } = props;
@@ -380,17 +387,18 @@ const AdhocFilterEditPopoverSimpleTabContent: React.FC<Props> = props => {
       />
       <Select
         css={theme => ({ marginBottom: theme.gridUnit * 4 })}
-        options={OPERATORS_OPTIONS.filter(op =>
-          isOperatorRelevant(op, subject),
-        ).map(option => ({
-          value: option,
-          label: OPERATOR_ENUM_TO_OPERATOR_TYPE[option].display,
-          key: option,
-        }))}
+        options={(props.operators ?? OPERATORS_OPTIONS)
+          .filter(op => isOperatorRelevant(op, subject))
+          .map(option => ({
+            value: option,
+            label: OPERATOR_ENUM_TO_OPERATOR_TYPE[option].display,
+            key: option,
+          }))}
         {...operatorSelectProps}
       />
       {MULTI_OPERATORS.has(operatorId) || suggestions.length > 0 ? (
         <SelectWithLabel
+          labelText={labelText}
           options={suggestions.map((suggestion: string) => ({
             value: suggestion,
             label: String(suggestion),
@@ -398,7 +406,7 @@ const AdhocFilterEditPopoverSimpleTabContent: React.FC<Props> = props => {
           {...comparatorSelectProps}
         />
       ) : (
-        <Input
+        <StyledInput
           data-test="adhoc-filter-simple-value"
           name="filter-value"
           ref={ref => {
diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterOption/index.jsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterOption/index.jsx
index faf16bf..c9f90b7 100644
--- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterOption/index.jsx
+++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterOption/index.jsx
@@ -36,6 +36,8 @@ const propTypes = {
       adhocMetricType,
     ]),
   ).isRequired,
+  sections: PropTypes.arrayOf(PropTypes.string),
+  operators: PropTypes.arrayOf(PropTypes.string),
   datasource: PropTypes.object,
   partitionColumn: PropTypes.string,
   onMoveLabel: PropTypes.func,
@@ -53,8 +55,12 @@ const AdhocFilterOption = ({
   onMoveLabel,
   onDropLabel,
   index,
+  sections,
+  operators,
 }) => (
   <AdhocFilterPopoverTrigger
+    sections={sections}
+    operators={operators}
     adhocFilter={adhocFilter}
     options={options}
     datasource={datasource}
diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger/index.tsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger/index.tsx
index e647786..15913e6 100644
--- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger/index.tsx
+++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger/index.tsx
@@ -22,8 +22,11 @@ import { OptionSortType } from 'src/explore/types';
 import AdhocFilterEditPopover from 'src/explore/components/controls/FilterControl/AdhocFilterEditPopover';
 import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
 import { ExplorePopoverContent } from 'src/explore/components/ExploreContentPopover';
+import { Operators } from 'src/explore/constants';
 
 interface AdhocFilterPopoverTriggerProps {
+  sections?: string[];
+  operators?: Operators[];
   adhocFilter: AdhocFilter;
   options: OptionSortType[];
   datasource: Record<string, any>;
@@ -90,6 +93,8 @@ class AdhocFilterPopoverTrigger extends React.PureComponent<
           partitionColumn={this.props.partitionColumn}
           onResize={this.onPopoverResize}
           onClose={closePopover}
+          sections={this.props.sections}
+          operators={this.props.operators}
           onChange={this.props.onFilterEdit}
         />
       </ExplorePopoverContent>

[superset] 23/26: fix: Filtering db names while creating dataset is not working (#17023)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit f3bde45e5cf9fe0ea01a7277f9bf91367c929597
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Thu Oct 7 16:23:51 2021 -0300

    fix: Filtering db names while creating dataset is not working (#17023)
---
 .../src/components/DatabaseSelector/index.tsx      |  1 +
 superset-frontend/src/components/Select/Select.tsx | 22 +++++++++++++++-------
 2 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/superset-frontend/src/components/DatabaseSelector/index.tsx b/superset-frontend/src/components/DatabaseSelector/index.tsx
index 117f6cb..dd6b824 100644
--- a/superset-frontend/src/components/DatabaseSelector/index.tsx
+++ b/superset-frontend/src/components/DatabaseSelector/index.tsx
@@ -257,6 +257,7 @@ export default function DatabaseSelector({
     return renderSelectRow(
       <Select
         ariaLabel={t('Select database or type database name')}
+        optionFilterProps={['database_name', 'value']}
         data-test="select-database"
         header={<FormLabel>{t('Database')}</FormLabel>}
         lazyLoading={false}
diff --git a/superset-frontend/src/components/Select/Select.tsx b/superset-frontend/src/components/Select/Select.tsx
index e705274..d08b669 100644
--- a/superset-frontend/src/components/Select/Select.tsx
+++ b/superset-frontend/src/components/Select/Select.tsx
@@ -180,6 +180,7 @@ const Select = ({
   mode = 'single',
   name,
   notFoundContent,
+  onError,
   onChange,
   onClear,
   optionFilterProps = ['label', 'value'],
@@ -327,11 +328,18 @@ const Select = ({
     setSearchedValue('');
   };
 
-  const onError = (response: Response) =>
-    getClientErrorObject(response).then(e => {
-      const { error } = e;
-      setError(error);
-    });
+  const internalOnError = useCallback(
+    (response: Response) =>
+      getClientErrorObject(response).then(e => {
+        const { error } = e;
+        setError(error);
+
+        if (onError) {
+          onError(error);
+        }
+      }),
+    [onError],
+  );
 
   const handleData = (data: OptionsType) => {
     let mergedData: OptionsType = [];
@@ -386,13 +394,13 @@ const Select = ({
             setAllValuesLoaded(true);
           }
         })
-        .catch(onError)
+        .catch(internalOnError)
         .finally(() => {
           setIsLoading(false);
           setIsTyping(false);
         });
     },
-    [allValuesLoaded, fetchOnlyOnSearch, options],
+    [allValuesLoaded, fetchOnlyOnSearch, internalOnError, options],
   );
 
   const handleOnSearch = useMemo(

[superset] 08/26: fix: check if owners are actually being updated in `PUT /datasets/` (#16941)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 4d33f7b7b6e41aa81c1d1674e720b5b37a5bddd8
Author: Hugh A. Miles II <hu...@gmail.com>
AuthorDate: Wed Oct 6 11:26:02 2021 -0700

    fix: check if owners are actually being updated in `PUT /datasets/<id>` (#16941)
    
    * check if owners are actually being updated
    
    * move logic to FE
    
    * fix
    
    * fix
    
    (cherry picked from commit 40861b3db3fc0a2ad8cd9c270d84e82eb104aa24)
---
 .../src/SqlLab/components/ResultSet/index.tsx      | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
index ec65d6e..7da6bce 100644
--- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
+++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
@@ -69,9 +69,17 @@ enum LIMITING_FACTOR {
 
 const LOADING_STYLES: CSSProperties = { position: 'relative', minHeight: 100 };
 
+interface DatasetOwner {
+  first_name: string;
+  id: number;
+  last_name: string;
+  username: string;
+}
+
 interface DatasetOptionAutocomplete {
   value: string;
   datasetId: number;
+  owners: [DatasetOwner];
 }
 
 interface ResultSetProps {
@@ -142,6 +150,7 @@ const updateDataset = async (
   datasetId: number,
   sql: string,
   columns: Array<Record<string, any>>,
+  owners: [number],
   overrideColumns: boolean,
 ) => {
   const endpoint = `api/v1/dataset/${datasetId}?override_columns=${overrideColumns}`;
@@ -149,6 +158,7 @@ const updateDataset = async (
   const body = JSON.stringify({
     sql,
     columns,
+    owners,
   });
 
   const data: JsonResponse = await SupersetClient.put({
@@ -269,6 +279,7 @@ export default class ResultSet extends React.PureComponent<
       datasetToOverwrite.datasetId,
       sql,
       results.selected_columns.map(d => ({ column_name: d.name })),
+      datasetToOverwrite.owners.map((o: DatasetOwner) => o.id),
       true,
     );
 
@@ -405,10 +416,13 @@ export default class ResultSet extends React.PureComponent<
         endpoint: '/api/v1/dataset',
       })(`q=${queryParams}`);
 
-      return response.result.map((r: { table_name: string; id: number }) => ({
-        value: r.table_name,
-        datasetId: r.id,
-      }));
+      return response.result.map(
+        (r: { table_name: string; id: number; owners: [DatasetOwner] }) => ({
+          value: r.table_name,
+          datasetId: r.id,
+          owners: r.owners,
+        }),
+      );
     }
 
     return null;

[superset] 10/26: fix(dashboard): Race condition when setting activeTabs with nested tabs (#17007)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 3d77daaf689e9f8ce252f042265749c19a3816c3
Author: Kamil Gabryjelski <ka...@gmail.com>
AuthorDate: Thu Oct 7 14:23:26 2021 +0200

    fix(dashboard): Race condition when setting activeTabs with nested tabs (#17007)
    
    * fix(dashboard): Race condition when setting activeTabs with nested tabs
    
    * Remove activeTabs prop from Tabs
    
    (cherry picked from commit 45908ff104efbd881fa1221278fba78989b43a52)
---
 superset-frontend/src/dashboard/actions/dashboardState.js      |  4 ++--
 .../src/dashboard/components/gridComponents/Tabs.jsx           | 10 ++--------
 superset-frontend/src/dashboard/reducers/dashboardState.js     |  9 ++++++++-
 3 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/superset-frontend/src/dashboard/actions/dashboardState.js b/superset-frontend/src/dashboard/actions/dashboardState.js
index 15d68fe..315ac7e 100644
--- a/superset-frontend/src/dashboard/actions/dashboardState.js
+++ b/superset-frontend/src/dashboard/actions/dashboardState.js
@@ -370,8 +370,8 @@ export function setDirectPathToChild(path) {
 }
 
 export const SET_ACTIVE_TABS = 'SET_ACTIVE_TABS';
-export function setActiveTabs(tabIds) {
-  return { type: SET_ACTIVE_TABS, tabIds };
+export function setActiveTabs(tabId, prevTabId) {
+  return { type: SET_ACTIVE_TABS, tabId, prevTabId };
 }
 
 export const SET_FOCUSED_FILTER_FIELD = 'SET_FOCUSED_FILTER_FIELD';
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx b/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx
index 1966dd5..289ee9a 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx
@@ -47,7 +47,6 @@ const propTypes = {
   editMode: PropTypes.bool.isRequired,
   renderHoverMenu: PropTypes.bool,
   directPathToChild: PropTypes.arrayOf(PropTypes.string),
-  activeTabs: PropTypes.arrayOf(PropTypes.string),
 
   // actions (from DashboardComponent.jsx)
   logEvent: PropTypes.func.isRequired,
@@ -74,7 +73,6 @@ const defaultProps = {
   availableColumnCount: 0,
   columnWidth: 0,
   directPathToChild: [],
-  activeTabs: [],
   setActiveTabs() {},
   onResizeStart() {},
   onResize() {},
@@ -133,15 +131,12 @@ export class Tabs extends React.PureComponent {
   }
 
   componentDidMount() {
-    this.props.setActiveTabs([...this.props.activeTabs, this.state.activeKey]);
+    this.props.setActiveTabs(this.state.activeKey);
   }
 
   componentDidUpdate(prevProps, prevState) {
     if (prevState.activeKey !== this.state.activeKey) {
-      this.props.setActiveTabs([
-        ...this.props.activeTabs.filter(tabId => tabId !== prevState.activeKey),
-        this.state.activeKey,
-      ]);
+      this.props.setActiveTabs(this.state.activeKey, prevState.activeKey);
     }
   }
 
@@ -410,7 +405,6 @@ function mapStateToProps(state) {
   return {
     nativeFilters: state.nativeFilters,
     directPathToChild: state.dashboardState.directPathToChild,
-    activeTabs: state.dashboardState.activeTabs,
   };
 }
 export default connect(mapStateToProps)(Tabs);
diff --git a/superset-frontend/src/dashboard/reducers/dashboardState.js b/superset-frontend/src/dashboard/reducers/dashboardState.js
index 3c3963a..4b0a401 100644
--- a/superset-frontend/src/dashboard/reducers/dashboardState.js
+++ b/superset-frontend/src/dashboard/reducers/dashboardState.js
@@ -150,9 +150,16 @@ export default function dashboardStateReducer(state = {}, action) {
       };
     },
     [SET_ACTIVE_TABS]() {
+      const prevActiveTabs = state.activeTabs ?? [];
+      const newActiveTabs = action.prevTabId
+        ? [
+            ...prevActiveTabs.filter(tabId => tabId !== action.prevTabId),
+            action.tabId,
+          ]
+        : [...prevActiveTabs, action.tabId];
       return {
         ...state,
-        activeTabs: action.tabIds,
+        activeTabs: newActiveTabs,
       };
     },
     [SET_FOCUSED_FILTER_FIELD]() {

[superset] 14/26: add logging on successful data uploads (#17065)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit ed98027adce1a45c059adbf11aede01f2d034fd6
Author: Elizabeth Thompson <es...@gmail.com>
AuthorDate: Mon Oct 11 16:28:07 2021 -0700

    add logging on successful data uploads (#17065)
    
    (cherry picked from commit c2e1ab65502a0e168c6f7e2f055df0cf99976176)
---
 superset/views/database/views.py            | 22 ++++++++--
 tests/integration_tests/csv_upload_tests.py | 67 ++++++++++++++++++++++++-----
 2 files changed, 75 insertions(+), 14 deletions(-)

diff --git a/superset/views/database/views.py b/superset/views/database/views.py
index 9e60e05..71f6f38 100644
--- a/superset/views/database/views.py
+++ b/superset/views/database/views.py
@@ -35,6 +35,7 @@ from superset import app, db, is_feature_enabled
 from superset.connectors.sqla.models import SqlaTable
 from superset.constants import MODEL_VIEW_RW_METHOD_PERMISSION_MAP, RouteMethod
 from superset.exceptions import CertificateException
+from superset.extensions import event_logger
 from superset.sql_parse import Table
 from superset.typing import FlaskResponse
 from superset.utils import core as utils
@@ -252,7 +253,12 @@ class CsvToDatabaseView(SimpleFormView):
             db_name=sqla_table.database.database_name,
         )
         flash(message, "info")
-        stats_logger.incr("successful_csv_upload")
+        event_logger.log_with_context(
+            action="successful_csv_upload",
+            database=form.con.data.name,
+            schema=form.schema.data,
+            table=form.name.data,
+        )
         return redirect("/tablemodelview/list/")
 
 
@@ -393,7 +399,12 @@ class ExcelToDatabaseView(SimpleFormView):
             db_name=sqla_table.database.database_name,
         )
         flash(message, "info")
-        stats_logger.incr("successful_excel_upload")
+        event_logger.log_with_context(
+            action="successful_excel_upload",
+            database=form.con.data.name,
+            schema=form.schema.data,
+            table=form.name.data,
+        )
         return redirect("/tablemodelview/list/")
 
 
@@ -540,5 +551,10 @@ class ColumnarToDatabaseView(SimpleFormView):
             db_name=sqla_table.database.database_name,
         )
         flash(message, "info")
-        stats_logger.incr("successful_columnar_upload")
+        event_logger.log_with_context(
+            action="successful_columnar_upload",
+            database=form.con.data.name,
+            schema=form.schema.data,
+            table=form.name.data,
+        )
         return redirect("/tablemodelview/list/")
diff --git a/tests/integration_tests/csv_upload_tests.py b/tests/integration_tests/csv_upload_tests.py
index 5c73e77..2e6f3c5 100644
--- a/tests/integration_tests/csv_upload_tests.py
+++ b/tests/integration_tests/csv_upload_tests.py
@@ -193,12 +193,15 @@ def mock_upload_to_s3(filename: str, upload_prefix: str, table: Table) -> str:
     return dest_dir
 
 
+@pytest.mark.usefixtures("setup_csv_upload")
+@pytest.mark.usefixtures("create_csv_files")
 @mock.patch(
     "superset.models.core.config",
     {**app.config, "ALLOWED_USER_CSV_SCHEMA_FUNC": lambda d, u: ["admin_database"]},
 )
 @mock.patch("superset.db_engine_specs.hive.upload_to_s3", mock_upload_to_s3)
-def test_import_csv_enforced_schema(setup_csv_upload, create_csv_files):
+@mock.patch("superset.views.database.views.event_logger.log_with_context")
+def test_import_csv_enforced_schema(mock_event_logger):
     if utils.backend() == "sqlite":
         pytest.skip("Sqlite doesn't support schema / database creation")
 
@@ -218,6 +221,12 @@ def test_import_csv_enforced_schema(setup_csv_upload, create_csv_files):
         extra={"schema": "admin_database", "if_exists": "replace"},
     )
     assert success_msg in resp
+    mock_event_logger.assert_called_with(
+        action="successful_csv_upload",
+        database=get_upload_db().name,
+        schema="admin_database",
+        table=CSV_UPLOAD_TABLE_W_SCHEMA,
+    )
 
     engine = get_upload_db().get_sqla_engine()
     data = engine.execute(
@@ -259,12 +268,17 @@ def test_import_csv_explore_database(setup_csv_upload, create_csv_files):
     assert table.database_id == utils.get_example_database().id
 
 
+@pytest.mark.usefixtures("setup_csv_upload")
+@pytest.mark.usefixtures("create_csv_files")
 @mock.patch("superset.db_engine_specs.hive.upload_to_s3", mock_upload_to_s3)
-def test_import_csv(setup_csv_upload, create_csv_files):
+@mock.patch("superset.views.database.views.event_logger.log_with_context")
+def test_import_csv(mock_event_logger):
     success_msg_f1 = (
         f'CSV file "{CSV_FILENAME1}" uploaded to table "{CSV_UPLOAD_TABLE}"'
     )
 
+    test_db = get_upload_db()
+
     # initial upload with fail mode
     resp = upload_csv(CSV_FILENAME1, CSV_UPLOAD_TABLE)
     assert success_msg_f1 in resp
@@ -282,6 +296,12 @@ def test_import_csv(setup_csv_upload, create_csv_files):
             CSV_FILENAME1, CSV_UPLOAD_TABLE, extra={"if_exists": "append"}
         )
         assert success_msg_f1 in resp
+        mock_event_logger.assert_called_with(
+            action="successful_csv_upload",
+            database=test_db.name,
+            schema=None,
+            table=CSV_UPLOAD_TABLE,
+        )
 
     # upload again with replace mode and specific columns
     resp = upload_csv(
@@ -324,7 +344,7 @@ def test_import_csv(setup_csv_upload, create_csv_files):
         extra={"null_values": '["", "john"]', "if_exists": "replace"},
     )
     # make sure that john and empty string are replaced with None
-    engine = get_upload_db().get_sqla_engine()
+    engine = test_db.get_sqla_engine()
     data = engine.execute(f"SELECT * from {CSV_UPLOAD_TABLE}").fetchall()
     assert data == [(None, 1, "x"), ("paul", 2, None)]
 
@@ -335,11 +355,16 @@ def test_import_csv(setup_csv_upload, create_csv_files):
     assert data == [("john", 1, "x"), ("paul", 2, None)]
 
 
+@pytest.mark.usefixtures("setup_csv_upload")
+@pytest.mark.usefixtures("create_excel_files")
 @mock.patch("superset.db_engine_specs.hive.upload_to_s3", mock_upload_to_s3)
-def test_import_excel(setup_csv_upload, create_excel_files):
+@mock.patch("superset.views.database.views.event_logger.log_with_context")
+def test_import_excel(mock_event_logger):
     if utils.backend() == "hive":
         pytest.skip("Hive doesn't excel upload.")
 
+    test_db = get_upload_db()
+
     success_msg = (
         f'Excel file "{EXCEL_FILENAME}" uploaded to table "{EXCEL_UPLOAD_TABLE}"'
     )
@@ -347,6 +372,12 @@ def test_import_excel(setup_csv_upload, create_excel_files):
     # initial upload with fail mode
     resp = upload_excel(EXCEL_FILENAME, EXCEL_UPLOAD_TABLE)
     assert success_msg in resp
+    mock_event_logger.assert_called_with(
+        action="successful_excel_upload",
+        database=test_db.name,
+        schema=None,
+        table=EXCEL_UPLOAD_TABLE,
+    )
 
     # upload again with fail mode; should fail
     fail_msg = f'Unable to upload Excel file "{EXCEL_FILENAME}" to table "{EXCEL_UPLOAD_TABLE}"'
@@ -365,22 +396,32 @@ def test_import_excel(setup_csv_upload, create_excel_files):
         EXCEL_FILENAME, EXCEL_UPLOAD_TABLE, extra={"if_exists": "replace"}
     )
     assert success_msg in resp
+    mock_event_logger.assert_called_with(
+        action="successful_excel_upload",
+        database=test_db.name,
+        schema=None,
+        table=EXCEL_UPLOAD_TABLE,
+    )
 
     # make sure that john and empty string are replaced with None
     data = (
-        get_upload_db()
-        .get_sqla_engine()
+        test_db.get_sqla_engine()
         .execute(f"SELECT * from {EXCEL_UPLOAD_TABLE}")
         .fetchall()
     )
     assert data == [(0, "john", 1), (1, "paul", 2)]
 
 
+@pytest.mark.usefixtures("setup_csv_upload")
+@pytest.mark.usefixtures("create_columnar_files")
 @mock.patch("superset.db_engine_specs.hive.upload_to_s3", mock_upload_to_s3)
-def test_import_parquet(setup_csv_upload, create_columnar_files):
+@mock.patch("superset.views.database.views.event_logger.log_with_context")
+def test_import_parquet(mock_event_logger):
     if utils.backend() == "hive":
         pytest.skip("Hive doesn't allow parquet upload.")
 
+    test_db = get_upload_db()
+
     success_msg_f1 = f'Columnar file "[\'{PARQUET_FILENAME1}\']" uploaded to table "{PARQUET_UPLOAD_TABLE}"'
 
     # initial upload with fail mode
@@ -398,6 +439,12 @@ def test_import_parquet(setup_csv_upload, create_columnar_files):
             PARQUET_FILENAME1, PARQUET_UPLOAD_TABLE, extra={"if_exists": "append"}
         )
         assert success_msg_f1 in resp
+        mock_event_logger.assert_called_with(
+            action="successful_columnar_upload",
+            database=test_db.name,
+            schema=None,
+            table=PARQUET_UPLOAD_TABLE,
+        )
 
     # upload again with replace mode and specific columns
     resp = upload_columnar(
@@ -418,8 +465,7 @@ def test_import_parquet(setup_csv_upload, create_columnar_files):
     assert success_msg_f1 in resp
 
     data = (
-        get_upload_db()
-        .get_sqla_engine()
+        test_db.get_sqla_engine()
         .execute(f"SELECT * from {PARQUET_UPLOAD_TABLE} ORDER BY b")
         .fetchall()
     )
@@ -433,8 +479,7 @@ def test_import_parquet(setup_csv_upload, create_columnar_files):
     assert success_msg_f2 in resp
 
     data = (
-        get_upload_db()
-        .get_sqla_engine()
+        test_db.get_sqla_engine()
         .execute(f"SELECT * from {PARQUET_UPLOAD_TABLE} ORDER BY b")
         .fetchall()
     )

[superset] 17/26: fix: Bump FAB to 3.3.4 (#17113)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 5bfa6e96cd34945b6b0222f67b97733f7cb2ad64
Author: Daniel Vaz Gaspar <da...@gmail.com>
AuthorDate: Thu Oct 14 12:08:48 2021 +0100

    fix: Bump FAB to 3.3.4 (#17113)
    
    (cherry picked from commit d94450387366647766840503feedf3487bab8c2c)
---
 requirements/base.txt | 2 +-
 setup.py              | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/requirements/base.txt b/requirements/base.txt
index 3d24d52..c8b9c69 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -77,7 +77,7 @@ flask==1.1.4
     #   flask-openid
     #   flask-sqlalchemy
     #   flask-wtf
-flask-appbuilder==3.3.3
+flask-appbuilder==3.3.4
     # via apache-superset
 flask-babel==1.0.0
     # via flask-appbuilder
diff --git a/setup.py b/setup.py
index abf5143..42eedd3 100644
--- a/setup.py
+++ b/setup.py
@@ -75,7 +75,7 @@ setup(
         "cryptography>=3.3.2",
         "deprecation>=2.1.0, <2.2.0",
         "flask>=1.1.0, <2.0.0",
-        "flask-appbuilder>=3.3.3, <4.0.0",
+        "flask-appbuilder>=3.3.4, <4.0.0",
         "flask-caching>=1.10.0",
         "flask-compress",
         "flask-talisman",

[superset] 26/26: fix: error alert levels again (#17027)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit e80c8ea980d17a776143102ed36a084e8a032949
Author: Erik Ritter <er...@airbnb.com>
AuthorDate: Mon Oct 11 12:28:36 2021 -0700

    fix: error alert levels again (#17027)
---
 .../components/ErrorMessage/BasicErrorAlert.tsx    | 13 +++++---
 .../src/components/ErrorMessage/ErrorAlert.tsx     | 39 ++++++----------------
 2 files changed, 19 insertions(+), 33 deletions(-)

diff --git a/superset-frontend/src/components/ErrorMessage/BasicErrorAlert.tsx b/superset-frontend/src/components/ErrorMessage/BasicErrorAlert.tsx
index 2817c50..fdc61b8 100644
--- a/superset-frontend/src/components/ErrorMessage/BasicErrorAlert.tsx
+++ b/superset-frontend/src/components/ErrorMessage/BasicErrorAlert.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import React from 'react';
-import { styled, supersetTheme } from '@superset-ui/core';
+import { styled, useTheme } from '@superset-ui/core';
 import Icons from 'src/components/Icons';
 import { ErrorLevel } from './types';
 
@@ -51,15 +51,18 @@ interface BasicErrorAlertProps {
 
 export default function BasicErrorAlert({
   body,
-  level,
+  level = 'error',
   title,
 }: BasicErrorAlertProps) {
+  const theme = useTheme();
+  const iconColor = theme.colors[level].base;
+
   return (
     <StyledContainer level={level} role="alert">
-      {!level || level === 'error' ? (
-        <Icons.ErrorSolid iconColor={supersetTheme.colors.error.base} />
+      {level === 'error' ? (
+        <Icons.ErrorSolid iconColor={iconColor} />
       ) : (
-        <Icons.WarningSolid iconColor={supersetTheme.colors[level].base} />
+        <Icons.WarningSolid iconColor={iconColor} />
       )}
       <StyledContent>
         <StyledTitle>{title}</StyledTitle>
diff --git a/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx b/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
index 0d37e8a..3340c8a 100644
--- a/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
+++ b/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import React, { useState, ReactNode } from 'react';
-import { styled, supersetTheme, t } from '@superset-ui/core';
+import { styled, useTheme, t } from '@superset-ui/core';
 import { noOp } from 'src/utils/common';
 import Modal from 'src/components/Modal';
 import Button from 'src/components/Button';
@@ -92,34 +92,27 @@ interface ErrorAlertProps {
 export default function ErrorAlert({
   body,
   copyText,
-  level,
+  level = 'error',
   source = 'dashboard',
   subtitle,
   title,
 }: ErrorAlertProps) {
+  const theme = useTheme();
+
   const [isModalOpen, setIsModalOpen] = useState(false);
   const [isBodyExpanded, setIsBodyExpanded] = useState(false);
 
   const isExpandable = ['explore', 'sqllab'].includes(source);
+  const iconColor = theme.colors[level].base;
 
   return (
     <ErrorAlertDiv level={level} role="alert">
       <div className="top-row">
         <LeftSideContent>
-          {!level || level === 'error' ? (
-            <Icons.ErrorSolid
-              className="icon"
-<<<<<<< HEAD
-              iconColor={supersetTheme.colors[level].base}
-=======
-              iconColor={theme.colors.error.base}
->>>>>>> ea700384b... fix: error alerts again (#17026)
-            />
+          {level === 'error' ? (
+            <Icons.ErrorSolid className="icon" iconColor={iconColor} />
           ) : (
-            <Icons.WarningSolid
-              className="icon"
-              iconColor={supersetTheme.colors[level].base}
-            />
+            <Icons.WarningSolid className="icon" iconColor={iconColor} />
           )}
           <strong>{title}</strong>
         </LeftSideContent>
@@ -173,20 +166,10 @@ export default function ErrorAlert({
           onHide={() => setIsModalOpen(false)}
           title={
             <div className="header">
-              {!level || level === 'error' ? (
-                <Icons.ErrorSolid
-                  className="icon"
-<<<<<<< HEAD
-                  iconColor={supersetTheme.colors[level].base}
-=======
-                  iconColor={theme.colors.error.base}
->>>>>>> ea700384b... fix: error alerts again (#17026)
-                />
+              {level === 'error' ? (
+                <Icons.ErrorSolid className="icon" iconColor={iconColor} />
               ) : (
-                <Icons.WarningSolid
-                  className="icon"
-                  iconColor={supersetTheme.colors[level].base}
-                />
+                <Icons.WarningSolid className="icon" iconColor={iconColor} />
               )}
               <div className="title">{title}</div>
             </div>

[superset] 11/26: bug fix (#17019)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit f56a3d8b1a517d6460b94c580864bba66aa479b8
Author: AAfghahi <48...@users.noreply.github.com>
AuthorDate: Fri Oct 8 13:19:04 2021 -0400

    bug fix (#17019)
    
    (cherry picked from commit e32a12fa0b45e7a00833d7733cb6c7a0cb3bb640)
---
 .../src/views/CRUD/data/database/DatabaseModal/index.tsx         | 4 ++++
 superset/db_engine_specs/gsheets.py                              | 9 ++++++---
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx
index 511591b..e086781 100644
--- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx
+++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx
@@ -552,6 +552,10 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
       });
       // cast the new encrypted extra object into a string
       dbToUpdate.encrypted_extra = JSON.stringify(additionalEncryptedExtra);
+      // this needs to be added by default to gsheets
+      if (dbToUpdate.engine === 'gsheets') {
+        dbToUpdate.impersonate_user = true;
+      }
     }
 
     if (dbToUpdate?.parameters?.catalog) {
diff --git a/superset/db_engine_specs/gsheets.py b/superset/db_engine_specs/gsheets.py
index 8136c17..3889e99 100644
--- a/superset/db_engine_specs/gsheets.py
+++ b/superset/db_engine_specs/gsheets.py
@@ -153,9 +153,12 @@ class GSheetsEngineSpec(SqliteEngineSpec):
         cls, parameters: GSheetsParametersType,
     ) -> List[SupersetError]:
         errors: List[SupersetError] = []
-        encrypted_credentials = json.loads(
-            parameters.get("service_account_info") or "{}"
-        )
+        encrypted_credentials = parameters.get("service_account_info") or "{}"
+
+        # On create the encrypted credentials are a string,
+        # at all other times they are a dict
+        if isinstance(encrypted_credentials, str):
+            encrypted_credentials = json.loads(encrypted_credentials)
 
         table_catalog = parameters.get("catalog", {})
 

[superset] 12/26: fix: Loading indicator of table and schema selectors (#17040)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit bd5d7872576c94f157317182ea4c6b37d93f9b5a
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Fri Oct 8 16:17:43 2021 -0300

    fix: Loading indicator of table and schema selectors (#17040)
    
    (cherry picked from commit 7c1c89c94b900ffe3de75c9cb314a75a3b559801)
---
 .../src/components/DatabaseSelector/index.tsx              | 14 ++++++++------
 superset-frontend/src/components/TableSelector/index.tsx   | 12 +++++++-----
 2 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/superset-frontend/src/components/DatabaseSelector/index.tsx b/superset-frontend/src/components/DatabaseSelector/index.tsx
index 5d8bccb..c79ab38 100644
--- a/superset-frontend/src/components/DatabaseSelector/index.tsx
+++ b/superset-frontend/src/components/DatabaseSelector/index.tsx
@@ -194,9 +194,9 @@ export default function DatabaseSelector({
       const queryParams = rison.encode({ force: refresh > 0 });
       const endpoint = `/api/v1/database/${currentDb.value}/schemas/?q=${queryParams}`;
 
-      try {
-        // TODO: Would be nice to add pagination in a follow-up. Needs endpoint changes.
-        SupersetClient.get({ endpoint }).then(({ json }) => {
+      // TODO: Would be nice to add pagination in a follow-up. Needs endpoint changes.
+      SupersetClient.get({ endpoint })
+        .then(({ json }) => {
           const options = json.result
             .map((s: string) => ({
               value: s,
@@ -210,10 +210,12 @@ export default function DatabaseSelector({
             onSchemasLoad(options);
           }
           setSchemaOptions(options);
+          setLoadingSchemas(false);
+        })
+        .catch(e => {
+          setLoadingSchemas(false);
+          handleError(t('There was an error loading the schemas'));
         });
-      } finally {
-        setLoadingSchemas(false);
-      }
     }
   }, [currentDb, onSchemasLoad, refresh]);
 
diff --git a/superset-frontend/src/components/TableSelector/index.tsx b/superset-frontend/src/components/TableSelector/index.tsx
index 06dad72..8e5abe9 100644
--- a/superset-frontend/src/components/TableSelector/index.tsx
+++ b/superset-frontend/src/components/TableSelector/index.tsx
@@ -189,8 +189,8 @@ const TableSelector: FunctionComponent<TableSelectorProps> = ({
         setPreviousRefresh(refresh);
       }
 
-      try {
-        SupersetClient.get({ endpoint }).then(({ json }) => {
+      SupersetClient.get({ endpoint })
+        .then(({ json }) => {
           const options: TableOption[] = [];
           let currentTable;
           json.options.forEach((table: Table) => {
@@ -213,10 +213,12 @@ const TableSelector: FunctionComponent<TableSelectorProps> = ({
             ),
           );
           setCurrentTable(currentTable);
+          setLoadingTables(false);
+        })
+        .catch(e => {
+          setLoadingTables(false);
+          handleError(t('There was an error loading the tables'));
         });
-      } finally {
-        setLoadingTables(false);
-      }
     }
     // We are using the refresh state to re-trigger the query
     // previousRefresh should be out of dependencies array

[superset] 13/26: fix: clear modal state after adding dataset (#17044)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 43e27b1137bdaebd367d05a9f85789902e748626
Author: Beto Dealmeida <ro...@dealmeida.net>
AuthorDate: Sun Oct 10 16:21:14 2021 -0700

    fix: clear modal state after adding dataset (#17044)
    
    * fix: clear modal state after adding dataset
    
    * Fix test
    
    * Small fixes
    
    (cherry picked from commit 16a1df75fcb5b2d0916abe648b717add36c43b3e)
---
 .../DatabaseSelector/DatabaseSelector.test.tsx     |  8 +++-
 .../src/components/DatabaseSelector/index.tsx      | 44 ++++++++++---------
 .../TableSelector/TableSelector.test.tsx           |  7 +++-
 .../src/components/TableSelector/index.tsx         | 49 +++++++++++-----------
 .../views/CRUD/data/dataset/AddDatasetModal.tsx    | 47 +++++++++++++--------
 5 files changed, 91 insertions(+), 64 deletions(-)

diff --git a/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx b/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx
index b5ebd04..af3fa96 100644
--- a/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx
+++ b/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx
@@ -26,7 +26,12 @@ import DatabaseSelector from '.';
 const SupersetClientGet = jest.spyOn(SupersetClient, 'get');
 
 const createProps = () => ({
-  db: { id: 1, database_name: 'test', backend: 'test-postgresql' },
+  db: {
+    id: 1,
+    database_name: 'test',
+    backend: 'test-postgresql',
+    allow_multi_schema_metadata_fetch: false,
+  },
   formMode: false,
   isDatabaseSelectEnabled: true,
   readOnly: false,
@@ -246,6 +251,7 @@ test('Sends the correct db when changing the database', async () => {
         id: 2,
         database_name: 'test-mysql',
         backend: 'mysql',
+        allow_multi_schema_metadata_fetch: false,
       }),
     ),
   );
diff --git a/superset-frontend/src/components/DatabaseSelector/index.tsx b/superset-frontend/src/components/DatabaseSelector/index.tsx
index c79ab38..117f6cb 100644
--- a/superset-frontend/src/components/DatabaseSelector/index.tsx
+++ b/superset-frontend/src/components/DatabaseSelector/index.tsx
@@ -63,21 +63,25 @@ type DatabaseValue = {
   id: number;
   database_name: string;
   backend: string;
+  allow_multi_schema_metadata_fetch: boolean;
+};
+
+export type DatabaseObject = {
+  id: number;
+  database_name: string;
+  backend: string;
+  allow_multi_schema_metadata_fetch: boolean;
 };
 
 type SchemaValue = { label: string; value: string };
 
 interface DatabaseSelectorProps {
-  db?: { id: number; database_name: string; backend: string };
+  db?: DatabaseObject;
   formMode?: boolean;
   getDbList?: (arg0: any) => {};
   handleError: (msg: string) => void;
   isDatabaseSelectEnabled?: boolean;
-  onDbChange?: (db: {
-    id: number;
-    database_name: string;
-    backend: string;
-  }) => void;
+  onDbChange?: (db: DatabaseObject) => void;
   onSchemaChange?: (schema?: string) => void;
   onSchemasLoad?: (schemas: Array<object>) => void;
   readOnly?: boolean;
@@ -165,20 +169,20 @@ export default function DatabaseSelector({
         if (result.length === 0) {
           handleError(t("It seems you don't have access to any database"));
         }
-        const options = result.map(
-          (row: { id: number; database_name: string; backend: string }) => ({
-            label: (
-              <SelectLabel
-                backend={row.backend}
-                databaseName={row.database_name}
-              />
-            ),
-            value: row.id,
-            id: row.id,
-            database_name: row.database_name,
-            backend: row.backend,
-          }),
-        );
+        const options = result.map((row: DatabaseObject) => ({
+          label: (
+            <SelectLabel
+              backend={row.backend}
+              databaseName={row.database_name}
+            />
+          ),
+          value: row.id,
+          id: row.id,
+          database_name: row.database_name,
+          backend: row.backend,
+          allow_multi_schema_metadata_fetch:
+            row.allow_multi_schema_metadata_fetch,
+        }));
         return {
           data: options,
           totalCount: options.length,
diff --git a/superset-frontend/src/components/TableSelector/TableSelector.test.tsx b/superset-frontend/src/components/TableSelector/TableSelector.test.tsx
index 0c5dbcd..51e40b5 100644
--- a/superset-frontend/src/components/TableSelector/TableSelector.test.tsx
+++ b/superset-frontend/src/components/TableSelector/TableSelector.test.tsx
@@ -26,7 +26,12 @@ import TableSelector from '.';
 const SupersetClientGet = jest.spyOn(SupersetClient, 'get');
 
 const createProps = () => ({
-  dbId: 1,
+  database: {
+    id: 1,
+    database_name: 'main',
+    backend: 'sqlite',
+    allow_multi_schema_metadata_fetch: false,
+  },
   schema: 'test_schema',
   handleError: jest.fn(),
 });
diff --git a/superset-frontend/src/components/TableSelector/index.tsx b/superset-frontend/src/components/TableSelector/index.tsx
index 8e5abe9..e7a99cc 100644
--- a/superset-frontend/src/components/TableSelector/index.tsx
+++ b/superset-frontend/src/components/TableSelector/index.tsx
@@ -27,7 +27,9 @@ import { styled, SupersetClient, t } from '@superset-ui/core';
 import { Select } from 'src/components';
 import { FormLabel } from 'src/components/Form';
 import Icons from 'src/components/Icons';
-import DatabaseSelector from 'src/components/DatabaseSelector';
+import DatabaseSelector, {
+  DatabaseObject,
+} from 'src/components/DatabaseSelector';
 import RefreshLabel from 'src/components/RefreshLabel';
 import CertifiedIcon from 'src/components/CertifiedIcon';
 import WarningIconWithTooltip from 'src/components/WarningIconWithTooltip';
@@ -76,22 +78,12 @@ const TableLabel = styled.span`
 
 interface TableSelectorProps {
   clearable?: boolean;
-  database?: {
-    id: number;
-    database_name: string;
-    backend: string;
-    allow_multi_schema_metadata_fetch: boolean;
-  };
-  dbId: number;
+  database?: DatabaseObject;
   formMode?: boolean;
   getDbList?: (arg0: any) => {};
   handleError: (msg: string) => void;
   isDatabaseSelectEnabled?: boolean;
-  onDbChange?: (db: {
-    id: number;
-    database_name: string;
-    backend: string;
-  }) => void;
+  onDbChange?: (db: DatabaseObject) => void;
   onSchemaChange?: (schema?: string) => void;
   onSchemasLoad?: () => void;
   onTableChange?: (tableName?: string, schema?: string) => void;
@@ -150,7 +142,6 @@ const TableOption = ({ table }: { table: Table }) => {
 
 const TableSelector: FunctionComponent<TableSelectorProps> = ({
   database,
-  dbId,
   formMode = false,
   getDbList,
   handleError,
@@ -165,7 +156,9 @@ const TableSelector: FunctionComponent<TableSelectorProps> = ({
   sqlLabMode = true,
   tableName,
 }) => {
-  const [currentDbId, setCurrentDbId] = useState<number | undefined>(dbId);
+  const [currentDatabase, setCurrentDatabase] = useState<
+    DatabaseObject | undefined
+  >(database);
   const [currentSchema, setCurrentSchema] = useState<string | undefined>(
     schema,
   );
@@ -176,13 +169,22 @@ const TableSelector: FunctionComponent<TableSelectorProps> = ({
   const [tableOptions, setTableOptions] = useState<TableOption[]>([]);
 
   useEffect(() => {
-    if (currentDbId && currentSchema) {
+    // reset selections
+    if (database === undefined) {
+      setCurrentDatabase(undefined);
+      setCurrentSchema(undefined);
+      setCurrentTable(undefined);
+    }
+  }, [database]);
+
+  useEffect(() => {
+    if (currentDatabase && currentSchema) {
       setLoadingTables(true);
       const encodedSchema = encodeURIComponent(currentSchema);
       const forceRefresh = refresh !== previousRefresh;
       // TODO: Would be nice to add pagination in a follow-up. Needs endpoint changes.
       const endpoint = encodeURI(
-        `/superset/tables/${currentDbId}/${encodedSchema}/undefined/${forceRefresh}/`,
+        `/superset/tables/${currentDatabase.id}/${encodedSchema}/undefined/${forceRefresh}/`,
       );
 
       if (previousRefresh !== refresh) {
@@ -223,7 +225,7 @@ const TableSelector: FunctionComponent<TableSelectorProps> = ({
     // We are using the refresh state to re-trigger the query
     // previousRefresh should be out of dependencies array
     // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [currentDbId, currentSchema, onTablesLoad, refresh]);
+  }, [currentDatabase, currentSchema, onTablesLoad, refresh]);
 
   function renderSelectRow(select: ReactNode, refreshBtn: ReactNode) {
     return (
@@ -241,12 +243,8 @@ const TableSelector: FunctionComponent<TableSelectorProps> = ({
     }
   };
 
-  const internalDbChange = (db: {
-    id: number;
-    database_name: string;
-    backend: string;
-  }) => {
-    setCurrentDbId(db?.id);
+  const internalDbChange = (db: DatabaseObject) => {
+    setCurrentDatabase(db);
     if (onDbChange) {
       onDbChange(db);
     }
@@ -263,7 +261,8 @@ const TableSelector: FunctionComponent<TableSelectorProps> = ({
   function renderDatabaseSelector() {
     return (
       <DatabaseSelector
-        db={database}
+        key={currentDatabase?.id}
+        db={currentDatabase}
         formMode={formMode}
         getDbList={getDbList}
         handleError={handleError}
diff --git a/superset-frontend/src/views/CRUD/data/dataset/AddDatasetModal.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDatasetModal.tsx
index 80debef..f3ad4e4 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/AddDatasetModal.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDatasetModal.tsx
@@ -19,10 +19,10 @@
 import React, { FunctionComponent, useState, useEffect } from 'react';
 import { styled, t } from '@superset-ui/core';
 import { useSingleViewResource } from 'src/views/CRUD/hooks';
-import { isEmpty, isNil } from 'lodash';
 import Modal from 'src/components/Modal';
 import TableSelector from 'src/components/TableSelector';
 import withToasts from 'src/components/MessageToasts/withToasts';
+import { DatabaseObject } from 'src/components/DatabaseSelector';
 
 type DatasetAddObject = {
   id: number;
@@ -50,9 +50,11 @@ const DatasetModal: FunctionComponent<DatasetModalProps> = ({
   onHide,
   show,
 }) => {
+  const [currentDatabase, setCurrentDatabase] = useState<
+    DatabaseObject | undefined
+  >();
   const [currentSchema, setSchema] = useState<string | undefined>('');
   const [currentTableName, setTableName] = useState('');
-  const [datasourceId, setDatasourceId] = useState<number>(0);
   const [disableSave, setDisableSave] = useState(true);
   const { createResource } = useSingleViewResource<Partial<DatasetAddObject>>(
     'dataset',
@@ -61,15 +63,11 @@ const DatasetModal: FunctionComponent<DatasetModalProps> = ({
   );
 
   useEffect(() => {
-    setDisableSave(isNil(datasourceId) || isEmpty(currentTableName));
-  }, [currentTableName, datasourceId]);
+    setDisableSave(currentDatabase === undefined || currentTableName === '');
+  }, [currentTableName, currentDatabase]);
 
-  const onDbChange = (db: {
-    id: number;
-    database_name: string;
-    backend: string;
-  }) => {
-    setDatasourceId(db.id);
+  const onDbChange = (db: DatabaseObject) => {
+    setCurrentDatabase(db);
   };
 
   const onSchemaChange = (schema?: string) => {
@@ -80,9 +78,24 @@ const DatasetModal: FunctionComponent<DatasetModalProps> = ({
     setTableName(tableName);
   };
 
+  const clearModal = () => {
+    setSchema('');
+    setTableName('');
+    setCurrentDatabase(undefined);
+    setDisableSave(true);
+  };
+
+  const hide = () => {
+    clearModal();
+    onHide();
+  };
+
   const onSave = () => {
+    if (currentDatabase === undefined) {
+      return;
+    }
     const data = {
-      database: datasourceId,
+      database: currentDatabase.id,
       ...(currentSchema ? { schema: currentSchema } : {}),
       table_name: currentTableName,
     };
@@ -94,7 +107,7 @@ const DatasetModal: FunctionComponent<DatasetModalProps> = ({
         onDatasetAdd({ id: response.id, ...response });
       }
       addSuccessToast(t('The dataset has been saved'));
-      onHide();
+      hide();
     });
   };
 
@@ -102,7 +115,7 @@ const DatasetModal: FunctionComponent<DatasetModalProps> = ({
     <Modal
       disablePrimaryButton={disableSave}
       onHandledPrimaryAction={onSave}
-      onHide={onHide}
+      onHide={hide}
       primaryButtonName={t('Add')}
       show={show}
       title={t('Add dataset')}
@@ -110,14 +123,14 @@ const DatasetModal: FunctionComponent<DatasetModalProps> = ({
       <TableSelectorContainer>
         <TableSelector
           clearable={false}
-          dbId={datasourceId}
           formMode
-          handleError={addDangerToast}
+          database={currentDatabase}
+          schema={currentSchema}
+          tableName={currentTableName}
           onDbChange={onDbChange}
           onSchemaChange={onSchemaChange}
           onTableChange={onTableChange}
-          schema={currentSchema}
-          tableName={currentTableName}
+          handleError={addDangerToast}
         />
       </TableSelectorContainer>
     </Modal>

[superset] 15/26: fix: Owners selection in dataset edit UX (#17063)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit c03771dc6dd1dbdb41c66eb59f71342012e197a5
Author: Hugh A. Miles II <hu...@gmail.com>
AuthorDate: Tue Oct 12 17:27:56 2021 -0700

    fix: Owners selection in dataset edit UX (#17063)
    
    * boilerplate
    
    * update owner select component
    
    * this is working
    
    * update onchange
    
    * refactorig
    
    * you need to useMemo or things break
    
    * update test
    
    * prettier
    
    * move logic into bootstrap data endpoint
    
    * address concerns
    
    * oops
    
    * oops
    
    * fix test
    
    (cherry picked from commit 959fd763a8b22367e7de5d3438e457f3448c6ceb)
---
 superset-frontend/spec/fixtures/mockDatasource.js  |  1 +
 .../dashboard/components/FiltersBadge_spec.tsx     |  1 -
 .../src/datasource/DatasourceEditor.jsx            | 60 ++++++++++++++--------
 .../src/datasource/DatasourceModal.tsx             |  4 +-
 .../DatasourceControl/DatasourceControl.test.tsx   |  1 +
 .../src/views/CRUD/data/dataset/DatasetList.tsx    |  3 +-
 superset/connectors/base/models.py                 | 12 +++++
 superset/views/core.py                             |  3 ++
 8 files changed, 61 insertions(+), 24 deletions(-)

diff --git a/superset-frontend/spec/fixtures/mockDatasource.js b/superset-frontend/spec/fixtures/mockDatasource.js
index 55d95eb..ca60a71 100644
--- a/superset-frontend/spec/fixtures/mockDatasource.js
+++ b/superset-frontend/spec/fixtures/mockDatasource.js
@@ -168,6 +168,7 @@ export default {
     id,
     granularity_sqla: [['ds', 'ds']],
     name: 'birth_names',
+    owners: [{ first_name: 'joe', last_name: 'man', id: 1 }],
     database: {
       allow_multi_schema_metadata_fetch: null,
       name: 'main',
diff --git a/superset-frontend/spec/javascripts/dashboard/components/FiltersBadge_spec.tsx b/superset-frontend/spec/javascripts/dashboard/components/FiltersBadge_spec.tsx
index f24b973..539aa67 100644
--- a/superset-frontend/spec/javascripts/dashboard/components/FiltersBadge_spec.tsx
+++ b/superset-frontend/spec/javascripts/dashboard/components/FiltersBadge_spec.tsx
@@ -139,7 +139,6 @@ describe('FiltersBadge', () => {
         wrapper.find('[data-test="incompatible-filter-count"]'),
       ).toHaveText('1');
       // to look at the shape of the wrapper use:
-      // console.log(wrapper.debug())
       expect(wrapper.find(Icons.AlertSolid)).toExist();
     });
   });
diff --git a/superset-frontend/src/datasource/DatasourceEditor.jsx b/superset-frontend/src/datasource/DatasourceEditor.jsx
index e502a3e..e725cdc 100644
--- a/superset-frontend/src/datasource/DatasourceEditor.jsx
+++ b/superset-frontend/src/datasource/DatasourceEditor.jsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import rison from 'rison';
-import React from 'react';
+import React, { useCallback } from 'react';
 import PropTypes from 'prop-types';
 import { Row, Col } from 'src/common/components';
 import { Radio } from 'src/components/Radio';
@@ -26,6 +26,8 @@ import Alert from 'src/components/Alert';
 import Badge from 'src/components/Badge';
 import shortid from 'shortid';
 import { styled, SupersetClient, t, supersetTheme } from '@superset-ui/core';
+import { Select } from 'src/components';
+import { FormLabel } from 'src/components/Form';
 import Button from 'src/components/Button';
 import Tabs from 'src/components/Tabs';
 import CertifiedIcon from 'src/components/CertifiedIcon';
@@ -40,9 +42,7 @@ import { getClientErrorObject } from 'src/utils/getClientErrorObject';
 
 import CheckboxControl from 'src/explore/components/controls/CheckboxControl';
 import TextControl from 'src/explore/components/controls/TextControl';
-import { Select } from 'src/components';
 import TextAreaControl from 'src/explore/components/controls/TextAreaControl';
-import SelectAsyncControl from 'src/explore/components/controls/SelectAsyncControl';
 import SpatialControl from 'src/explore/components/controls/SpatialControl';
 
 import CollectionTable from 'src/CRUD/CollectionTable';
@@ -374,12 +374,44 @@ const defaultProps = {
   onChange: () => {},
 };
 
+function OwnersSelector({ datasource, onChange }) {
+  const loadOptions = useCallback((search = '', page, pageSize) => {
+    const query = rison.encode({ filter: search, page, page_size: pageSize });
+    return SupersetClient.get({
+      endpoint: `/api/v1/dataset/related/owners?q=${query}`,
+    }).then(response => ({
+      data: response.json.result.map(item => ({
+        value: item.value,
+        label: item.text,
+      })),
+      totalCount: response.json.count,
+    }));
+  }, []);
+
+  return (
+    <Select
+      ariaLabel={t('Select owners')}
+      mode="multiple"
+      name="owners"
+      value={datasource.owners}
+      options={loadOptions}
+      onChange={onChange}
+      header={<FormLabel>{t('Owners')}</FormLabel>}
+      allowClear
+    />
+  );
+}
+
 class DatasourceEditor extends React.PureComponent {
   constructor(props) {
     super(props);
     this.state = {
       datasource: {
         ...props.datasource,
+        owners: props.datasource.owners.map(owner => ({
+          value: owner.id,
+          label: `${owner.first_name} ${owner.last_name}`,
+        })),
         metrics: props.datasource.metrics?.map(metric => {
           const {
             certified_by: certifiedByMetric,
@@ -717,23 +749,11 @@ class DatasourceEditor extends React.PureComponent {
             }
           />
         )}
-        <Field
-          fieldKey="owners"
-          label={t('Owners')}
-          description={t('Owners of the dataset')}
-          control={
-            <SelectAsyncControl
-              dataEndpoint="api/v1/dataset/related/owners"
-              multi
-              mutator={data =>
-                data.result.map(pk => ({
-                  value: pk.value,
-                  label: `${pk.text}`,
-                }))
-              }
-            />
-          }
-          controlProps={{}}
+        <OwnersSelector
+          datasource={datasource}
+          onChange={newOwners => {
+            this.onDatasourceChange({ ...datasource, owners: newOwners });
+          }}
         />
       </Fieldset>
     );
diff --git a/superset-frontend/src/datasource/DatasourceModal.tsx b/superset-frontend/src/datasource/DatasourceModal.tsx
index 75d1708..5cd6f3a 100644
--- a/superset-frontend/src/datasource/DatasourceModal.tsx
+++ b/superset-frontend/src/datasource/DatasourceModal.tsx
@@ -99,7 +99,6 @@ const DatasourceModal: FunctionComponent<DatasourceModalProps> = ({
       currentDatasource.schema;
 
     setIsSaving(true);
-
     SupersetClient.post({
       endpoint: '/datasource/save/',
       postPayload: {
@@ -119,6 +118,9 @@ const DatasourceModal: FunctionComponent<DatasourceModalProps> = ({
             }),
           ),
           type: currentDatasource.type || currentDatasource.datasource_type,
+          owners: currentDatasource.owners.map(
+            (o: { label: string; value: number }) => o.value,
+          ),
         },
       },
     })
diff --git a/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx b/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx
index 744b929..43ac29b 100644
--- a/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx
+++ b/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx
@@ -41,6 +41,7 @@ const createProps = () => ({
     name: 'channels',
     type: 'table',
     columns: [],
+    owners: [{ first_name: 'john', last_name: 'doe', id: 1, username: 'jd' }],
   },
   validationErrors: [],
   name: 'datasource',
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetList.tsx b/superset-frontend/src/views/CRUD/data/dataset/DatasetList.tsx
index e8f0583..bdc9f40 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/DatasetList.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/DatasetList.tsx
@@ -165,7 +165,6 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
         endpoint: `/api/v1/dataset/${id}`,
       })
         .then(({ json = {} }) => {
-          const owners = json.result.owners.map((owner: any) => owner.id);
           const addCertificationFields = json.result.columns.map(
             (column: ColumnObject) => {
               const {
@@ -181,7 +180,7 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
           );
           // eslint-disable-next-line no-param-reassign
           json.result.columns = [...addCertificationFields];
-          setDatasetCurrentlyEditing({ ...json.result, owners });
+          setDatasetCurrentlyEditing(json.result);
         })
         .catch(() => {
           addDangerToast(
diff --git a/superset/connectors/base/models.py b/superset/connectors/base/models.py
index 844dc48..711f1b7 100644
--- a/superset/connectors/base/models.py
+++ b/superset/connectors/base/models.py
@@ -119,6 +119,18 @@ class BaseDatasource(
         return DatasourceKind.VIRTUAL if self.sql else DatasourceKind.PHYSICAL
 
     @property
+    def owners_data(self) -> List[Dict[str, Any]]:
+        return [
+            {
+                "first_name": o.first_name,
+                "last_name": o.last_name,
+                "username": o.username,
+                "id": o.id,
+            }
+            for o in self.owners
+        ]
+
+    @property
     def is_virtual(self) -> bool:
         return self.kind == DatasourceKind.VIRTUAL
 
diff --git a/superset/views/core.py b/superset/views/core.py
index 8d6345b..6dfa630 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -856,6 +856,9 @@ class Superset(BaseSupersetView):  # pylint: disable=too-many-public-methods
         except (SupersetException, SQLAlchemyError):
             datasource_data = dummy_datasource_data
 
+        if datasource:
+            datasource_data["owners"] = datasource.owners_data
+
         bootstrap_data = {
             "can_add": slice_add_perm,
             "can_download": slice_download_perm,

[superset] 20/26: fix(filter-indicator): show filters handled by jinja as applied (#17140)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch 1.4
in repository https://gitbox.apache.org/repos/asf/superset.git

commit d90bfa65ef9f332430d306cdd1aa2fa032199a6e
Author: Ville Brofeldt <33...@users.noreply.github.com>
AuthorDate: Mon Oct 18 19:28:05 2021 +0200

    fix(filter-indicator): show filters handled by jinja as applied (#17140)
    
    (cherry picked from commit d7834f17e3e0da1404130018fea968f3e1a4001e)
---
 superset/common/query_actions.py               |  7 +++++--
 superset/common/query_context.py               |  1 +
 superset/common/utils.py                       |  9 ++++++++-
 superset/connectors/sqla/models.py             | 11 ++++++++++-
 superset/jinja_context.py                      |  8 ++++++++
 superset/models/helpers.py                     |  2 ++
 superset/viz.py                                | 25 +++++++++----------------
 tests/integration_tests/jinja_context_tests.py |  5 +++++
 8 files changed, 48 insertions(+), 20 deletions(-)

diff --git a/superset/common/query_actions.py b/superset/common/query_actions.py
index 86a687f..925d4a1 100644
--- a/superset/common/query_actions.py
+++ b/superset/common/query_actions.py
@@ -96,6 +96,7 @@ def _get_full(
     datasource = _get_datasource(query_context, query_obj)
     result_type = query_obj.result_type or query_context.result_type
     payload = query_context.get_df_payload(query_obj, force_cached=force_cached)
+    applied_template_filters = payload.get("applied_template_filters", [])
     df = payload["df"]
     status = payload["status"]
     if status != QueryStatus.FAILED:
@@ -113,12 +114,14 @@ def _get_full(
         datasource, query_obj.applied_time_extras
     )
     payload["applied_filters"] = [
-        {"column": col} for col in filter_columns if col in columns
+        {"column": col}
+        for col in filter_columns
+        if col in columns or col in applied_template_filters
     ] + applied_time_columns
     payload["rejected_filters"] = [
         {"reason": ExtraFiltersReasonType.COL_NOT_IN_DATASOURCE, "column": col}
         for col in filter_columns
-        if col not in columns
+        if col not in columns and col not in applied_template_filters
     ] + rejected_time_columns
 
     if result_type == ChartDataResultType.RESULTS and status != QueryStatus.FAILED:
diff --git a/superset/common/query_context.py b/superset/common/query_context.py
index c1162b9..eee2bbe 100644
--- a/superset/common/query_context.py
+++ b/superset/common/query_context.py
@@ -485,6 +485,7 @@ class QueryContext:
             "cached_dttm": cache.cache_dttm,
             "cache_timeout": self.cache_timeout,
             "df": cache.df,
+            "applied_template_filters": cache.applied_template_filters,
             "annotation_data": cache.annotation_data,
             "error": cache.error_message,
             "is_cached": cache.is_cached,
diff --git a/superset/common/utils.py b/superset/common/utils.py
index 77a6bab..d5cad68 100644
--- a/superset/common/utils.py
+++ b/superset/common/utils.py
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 import logging
-from typing import Any, Dict, Optional
+from typing import Any, Dict, List, Optional
 
 from flask_caching import Cache
 from pandas import DataFrame
@@ -51,6 +51,7 @@ class QueryCacheManager:
         df: DataFrame = DataFrame(),
         query: str = "",
         annotation_data: Optional[Dict[str, Any]] = None,
+        applied_template_filters: Optional[List[str]] = None,
         status: Optional[str] = None,
         error_message: Optional[str] = None,
         is_loaded: bool = False,
@@ -62,6 +63,7 @@ class QueryCacheManager:
         self.df = df
         self.query = query
         self.annotation_data = {} if annotation_data is None else annotation_data
+        self.applied_template_filters = applied_template_filters or []
         self.status = status
         self.error_message = error_message
 
@@ -88,6 +90,7 @@ class QueryCacheManager:
         try:
             self.status = query_result.status
             self.query = query_result.query
+            self.applied_template_filters = query_result.applied_template_filters
             self.error_message = query_result.error_message
             self.df = query_result.df
             self.annotation_data = {} if annotation_data is None else annotation_data
@@ -101,6 +104,7 @@ class QueryCacheManager:
             value = {
                 "df": self.df,
                 "query": self.query,
+                "applied_template_filters": self.applied_template_filters,
                 "annotation_data": self.annotation_data,
             }
             if self.is_loaded and key and self.status != QueryStatus.FAILED:
@@ -141,6 +145,9 @@ class QueryCacheManager:
                 query_cache.df = cache_value["df"]
                 query_cache.query = cache_value["query"]
                 query_cache.annotation_data = cache_value.get("annotation_data", {})
+                query_cache.applied_template_filters = cache_value.get(
+                    "applied_template_filters", []
+                )
                 query_cache.status = QueryStatus.SUCCESS
                 query_cache.is_loaded = True
                 query_cache.is_cached = cache_value is not None
diff --git a/superset/connectors/sqla/models.py b/superset/connectors/sqla/models.py
index 0f6b705..fcb40f2 100644
--- a/superset/connectors/sqla/models.py
+++ b/superset/connectors/sqla/models.py
@@ -103,6 +103,7 @@ VIRTUAL_TABLE_ALIAS = "virtual_table"
 
 
 class SqlaQuery(NamedTuple):
+    applied_template_filters: List[str]
     extra_cache_keys: List[Any]
     labels_expected: List[str]
     prequeries: List[str]
@@ -110,6 +111,7 @@ class SqlaQuery(NamedTuple):
 
 
 class QueryStringExtended(NamedTuple):
+    applied_template_filters: Optional[List[str]]
     labels_expected: List[str]
     prequeries: List[str]
     sql: str
@@ -755,7 +757,10 @@ class SqlaTable(Model, BaseDatasource):  # pylint: disable=too-many-public-metho
         sql = sqlparse.format(sql, reindent=True)
         sql = self.mutate_query_from_config(sql)
         return QueryStringExtended(
-            labels_expected=sqlaq.labels_expected, sql=sql, prequeries=sqlaq.prequeries
+            applied_template_filters=sqlaq.applied_template_filters,
+            labels_expected=sqlaq.labels_expected,
+            prequeries=sqlaq.prequeries,
+            sql=sql,
         )
 
     def get_query_str(self, query_obj: QueryObjectDict) -> str:
@@ -978,7 +983,9 @@ class SqlaTable(Model, BaseDatasource):  # pylint: disable=too-many-public-metho
         extra_cache_keys: List[Any] = []
         template_kwargs["extra_cache_keys"] = extra_cache_keys
         removed_filters: List[str] = []
+        applied_template_filters: List[str] = []
         template_kwargs["removed_filters"] = removed_filters
+        template_kwargs["applied_filters"] = applied_template_filters
         template_processor = self.get_template_processor(**template_kwargs)
         db_engine_spec = self.db_engine_spec
         prequeries: List[str] = []
@@ -1394,6 +1401,7 @@ class SqlaTable(Model, BaseDatasource):  # pylint: disable=too-many-public-metho
             labels_expected = [label]
 
         return SqlaQuery(
+            applied_template_filters=applied_template_filters,
             extra_cache_keys=extra_cache_keys,
             labels_expected=labels_expected,
             sqla_query=qry,
@@ -1491,6 +1499,7 @@ class SqlaTable(Model, BaseDatasource):  # pylint: disable=too-many-public-metho
             error_message = utils.error_msg_from_exception(ex)
 
         return QueryResult(
+            applied_template_filters=query_str_ext.applied_template_filters,
             status=status,
             df=df,
             duration=datetime.now() - qry_start_dttm,
diff --git a/superset/jinja_context.py b/superset/jinja_context.py
index e6a4cab..f21fbbb 100644
--- a/superset/jinja_context.py
+++ b/superset/jinja_context.py
@@ -96,10 +96,12 @@ class ExtraCache:
     def __init__(
         self,
         extra_cache_keys: Optional[List[Any]] = None,
+        applied_filters: Optional[List[str]] = None,
         removed_filters: Optional[List[str]] = None,
         dialect: Optional[Dialect] = None,
     ):
         self.extra_cache_keys = extra_cache_keys
+        self.applied_filters = applied_filters if applied_filters is not None else []
         self.removed_filters = removed_filters if removed_filters is not None else []
         self.dialect = dialect
 
@@ -323,6 +325,9 @@ class ExtraCache:
                 if remove_filter:
                     if column not in self.removed_filters:
                         self.removed_filters.append(column)
+                if column not in self.applied_filters:
+                    self.applied_filters.append(column)
+
                 if op in (
                     FilterOperator.IN.value,
                     FilterOperator.NOT_IN.value,
@@ -408,6 +413,7 @@ class BaseTemplateProcessor:
         table: Optional["SqlaTable"] = None,
         extra_cache_keys: Optional[List[Any]] = None,
         removed_filters: Optional[List[str]] = None,
+        applied_filters: Optional[List[str]] = None,
         **kwargs: Any,
     ) -> None:
         self._database = database
@@ -418,6 +424,7 @@ class BaseTemplateProcessor:
         elif table:
             self._schema = table.schema
         self._extra_cache_keys = extra_cache_keys
+        self._applied_filters = applied_filters
         self._removed_filters = removed_filters
         self._context: Dict[str, Any] = {}
         self._env = SandboxedEnvironment(undefined=DebugUndefined)
@@ -446,6 +453,7 @@ class JinjaTemplateProcessor(BaseTemplateProcessor):
         super().set_context(**kwargs)
         extra_cache = ExtraCache(
             extra_cache_keys=self._extra_cache_keys,
+            applied_filters=self._applied_filters,
             removed_filters=self._removed_filters,
             dialect=self._database.get_dialect(),
         )
diff --git a/superset/models/helpers.py b/superset/models/helpers.py
index 30d5ab9..1875d24 100644
--- a/superset/models/helpers.py
+++ b/superset/models/helpers.py
@@ -442,6 +442,7 @@ class QueryResult:  # pylint: disable=too-few-public-methods
         df: pd.DataFrame,
         query: str,
         duration: timedelta,
+        applied_template_filters: Optional[List[str]] = None,
         status: str = QueryStatus.SUCCESS,
         error_message: Optional[str] = None,
         errors: Optional[List[Dict[str, Any]]] = None,
@@ -449,6 +450,7 @@ class QueryResult:  # pylint: disable=too-few-public-methods
         self.df = df
         self.query = query
         self.duration = duration
+        self.applied_template_filters = applied_template_filters or []
         self.status = status
         self.error_message = error_message
         self.errors = errors or []
diff --git a/superset/viz.py b/superset/viz.py
index 5e22114..ecf4f63 100644
--- a/superset/viz.py
+++ b/superset/viz.py
@@ -102,11 +102,6 @@ METRIC_KEYS = [
     "size",
 ]
 
-# This regex is to get user defined filter column name, which is the first param in the
-# filter_values function. See the definition of filter_values template:
-# https://github.com/apache/superset/blob/24ad6063d736c1f38ad6f962e586b9b1a21946af/superset/jinja_context.py#L63
-FILTER_VALUES_REGEX = re.compile(r"filter_values\(['\"](\w+)['\"]\,")
-
 
 class BaseViz:  # pylint: disable=too-many-public-methods
 
@@ -143,6 +138,7 @@ class BaseViz:  # pylint: disable=too-many-public-methods
         self.status: Optional[str] = None
         self.error_msg = ""
         self.results: Optional[QueryResult] = None
+        self.applied_template_filters: List[str] = []
         self.errors: List[Dict[str, Any]] = []
         self.force = force
         self._force_cached = force_cached
@@ -270,6 +266,7 @@ class BaseViz:  # pylint: disable=too-many-public-methods
 
         # The datasource here can be different backend but the interface is common
         self.results = self.datasource.query(query_obj)
+        self.applied_template_filters = self.results.applied_template_filters or []
         self.query = self.results.query
         self.status = self.results.status
         self.errors = self.results.errors
@@ -459,14 +456,7 @@ class BaseViz:  # pylint: disable=too-many-public-methods
         filters = self.form_data.get("filters", [])
         filter_columns = [flt.get("col") for flt in filters]
         columns = set(self.datasource.column_names)
-        filter_values_columns = []
-
-        # if using virtual datasource, check filter_values
-        if self.datasource.sql:
-            filter_values_columns = (
-                re.findall(FILTER_VALUES_REGEX, self.datasource.sql)
-            ) or []
-
+        applied_template_filters = self.applied_template_filters or []
         applied_time_extras = self.form_data.get("applied_time_extras", {})
         applied_time_columns, rejected_time_columns = utils.get_time_filter_status(
             self.datasource, applied_time_extras
@@ -474,18 +464,18 @@ class BaseViz:  # pylint: disable=too-many-public-methods
         payload["applied_filters"] = [
             {"column": col}
             for col in filter_columns
-            if col in columns or col in filter_values_columns
+            if col in columns or col in applied_template_filters
         ] + applied_time_columns
         payload["rejected_filters"] = [
             {"reason": ExtraFiltersReasonType.COL_NOT_IN_DATASOURCE, "column": col}
             for col in filter_columns
-            if col not in columns and col not in filter_values_columns
+            if col not in columns and col not in applied_template_filters
         ] + rejected_time_columns
         if df is not None:
             payload["colnames"] = list(df.columns)
         return payload
 
-    def get_df_payload(
+    def get_df_payload(  # pylint: disable=too-many-statements
         self, query_obj: Optional[QueryObjectDict] = None, **kwargs: Any
     ) -> Dict[str, Any]:
         """Handles caching around the df payload retrieval"""
@@ -504,6 +494,9 @@ class BaseViz:  # pylint: disable=too-many-public-methods
                 try:
                     df = cache_value["df"]
                     self.query = cache_value["query"]
+                    self.applied_template_filters = cache_value.get(
+                        "applied_template_filters", []
+                    )
                     self.status = QueryStatus.SUCCESS
                     is_loaded = True
                     stats_logger.incr("loaded_from_cache")
diff --git a/tests/integration_tests/jinja_context_tests.py b/tests/integration_tests/jinja_context_tests.py
index b82adfa..31f8777 100644
--- a/tests/integration_tests/jinja_context_tests.py
+++ b/tests/integration_tests/jinja_context_tests.py
@@ -74,6 +74,7 @@ class TestJinja2Context(SupersetTestCase):
         ):
             cache = ExtraCache()
             self.assertEqual(cache.filter_values("name"), ["foo"])
+            self.assertEqual(cache.applied_filters, ["name"])
 
         with app.test_request_context(
             data={
@@ -94,6 +95,7 @@ class TestJinja2Context(SupersetTestCase):
         ):
             cache = ExtraCache()
             self.assertEqual(cache.filter_values("name"), ["foo", "bar"])
+            self.assertEqual(cache.applied_filters, ["name"])
 
     def test_get_filters_adhoc_filters(self) -> None:
         with app.test_request_context(
@@ -118,6 +120,7 @@ class TestJinja2Context(SupersetTestCase):
                 cache.get_filters("name"), [{"op": "IN", "col": "name", "val": ["foo"]}]
             )
             self.assertEqual(cache.removed_filters, list())
+            self.assertEqual(cache.applied_filters, ["name"])
 
         with app.test_request_context(
             data={
@@ -166,6 +169,7 @@ class TestJinja2Context(SupersetTestCase):
                 [{"op": "IN", "col": "name", "val": ["foo", "bar"]}],
             )
             self.assertEqual(cache.removed_filters, ["name"])
+            self.assertEqual(cache.applied_filters, ["name"])
 
     def test_filter_values_extra_filters(self) -> None:
         with app.test_request_context(
@@ -177,6 +181,7 @@ class TestJinja2Context(SupersetTestCase):
         ):
             cache = ExtraCache()
             self.assertEqual(cache.filter_values("name"), ["foo"])
+            self.assertEqual(cache.applied_filters, ["name"])
 
     def test_url_param_default(self) -> None:
         with app.test_request_context():