You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by vi...@apache.org on 2022/04/25 07:47:21 UTC

[superset] branch 1.5 updated (90b08fa095 -> 397a182c2f)

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

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


    from 90b08fa095 chore: skip SIP-68 shadow writing for LTS
     add ccf296b786 update changelog
     new f8b202f670 fix: Filter dependencies are not being applied in default values (#19698)
     new 28aa69628f chore: Clean redundant dependency from useMemo dep array (#19732)
     new 2d7d2dd373 fix(permalink): remove memoize on get salt func (#19749)
     new 5df14ed8f9 fix(explore): make to show the null value as N/A in view result (#19603)
     new c9e3ca11e2 fix(dashboard): copy permalink to dashboard chart (#19772)
     new 87b51c26f4 fix(sql lab): replace the output column in the query history table (#19370)
     new 1d141be463 fix: SQL Lab UI Error: Objects are not valid as a React child (#19783)
     new 2b6e35e039 fix: dashboard standalone class not added when parameter set (#16619)
     new 8c8bbfb89f feat: add renameOperator (#19776)
     new 5567828ee8 fix(chart & explore): Show labels for `SliderControl` (#19765)
     new 4fa96d3a3b fix: lost renameOperator in mixed timeseries chart (#19802)
     new 387138eb9e Fix display of column config in table chart (#19806)
     new 1050fcbd3c fix(key_value): use longblob on mysql (#19805)
     new 397a182c2f fix(sql lab): when editing a saved query, the status is lost when switching tabs (#19448)

The 14 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:
 CHANGELOG.md                                       |   8 +-
 .../src/operators/flattenOperator.ts               |  17 +-
 .../src/operators/index.ts                         |   1 +
 .../src/operators/renameOperator.ts                |  89 +++++++++++
 .../ColumnConfigControl/ColumnConfigItem.tsx       |   4 +-
 .../test/utils/operators/flattenOperator.test.ts   |  31 ----
 .../test/utils/operators/renameOperator.test.ts    | 146 +++++++++++++++++
 .../src/query/types/PostProcessing.ts              |  13 ++
 .../src/MixedTimeseries/buildQuery.ts              |  18 ++-
 .../src/Timeseries/buildQuery.ts                   |   6 +
 superset-frontend/src/SqlLab/actions/sqlLab.js     |   1 +
 .../src/SqlLab/components/QueryHistory/index.tsx   |   2 +-
 .../src/SqlLab/components/QueryTable/index.tsx     |  15 +-
 .../src/components/URLShortLinkButton/index.jsx    |   8 +-
 .../components/SliceHeaderControls/index.tsx       |   6 +-
 .../components/menu/ShareMenuItems/index.tsx       |  28 ++--
 .../FiltersConfigForm/FiltersConfigForm.tsx        |  40 ++++-
 .../explore/components/DataTableControl/index.tsx  |   4 +-
 .../DndColumnSelectControl/DndColumnSelect.tsx     |   5 +-
 .../explore/components/controls/SliderControl.tsx  |  41 ++++-
 superset-frontend/src/utils/urlUtils.ts            |  14 +-
 superset/charts/schemas.py                         |  27 +---
 superset/key_value/shared_entries.py               |   2 -
 .../versions/6766938c6065_add_key_value_store.py   |   2 +-
 superset/utils/pandas_postprocessing/__init__.py   |   2 +
 superset/utils/pandas_postprocessing/flatten.py    |  18 ++-
 superset/utils/pandas_postprocessing/rename.py     |  58 +++++++
 superset/views/core.py                             |   3 +
 .../pandas_postprocessing/test_rename.py           | 175 +++++++++++++++++++++
 29 files changed, 654 insertions(+), 130 deletions(-)
 create mode 100644 superset-frontend/packages/superset-ui-chart-controls/src/operators/renameOperator.ts
 create mode 100644 superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/renameOperator.test.ts
 create mode 100644 superset/utils/pandas_postprocessing/rename.py
 create mode 100644 tests/unit_tests/pandas_postprocessing/test_rename.py


[superset] 10/14: fix(chart & explore): Show labels for `SliderControl` (#19765)

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

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

commit 5567828ee8005a5a4e0ebeb8d7df36a8136448ac
Author: smileydev <47...@users.noreply.github.com>
AuthorDate: Wed Apr 20 15:58:25 2022 -0400

    fix(chart & explore): Show labels for `SliderControl` (#19765)
    
    * fix(chart & explore): make to show label of slidercontrol
    
    * fix(chart & explore): make to update SliderControl props
    
    * fix(chart & explore): make to fix lint
    
    (cherry picked from commit dfbaba97c61c28ecde8ce134a1f6ec385467c383)
---
 .../explore/components/controls/SliderControl.tsx  | 41 +++++++++++++++++++---
 1 file changed, 36 insertions(+), 5 deletions(-)

diff --git a/superset-frontend/src/explore/components/controls/SliderControl.tsx b/superset-frontend/src/explore/components/controls/SliderControl.tsx
index 5907e26ba8..a2d3b7c2bc 100644
--- a/superset-frontend/src/explore/components/controls/SliderControl.tsx
+++ b/superset-frontend/src/explore/components/controls/SliderControl.tsx
@@ -18,19 +18,50 @@
  */
 import React from 'react';
 import Slider from 'src/components/Slider';
-import ControlHeader from 'src/explore/components/ControlHeader';
+import ControlHeader, {
+  ControlHeaderProps,
+} from 'src/explore/components/ControlHeader';
 
-type SliderControlProps = {
+type SliderControlProps = ControlHeaderProps & {
   onChange: (value: number) => void;
   value: number;
   default?: number;
 };
 
-export default function SliderControl(props: SliderControlProps) {
-  const { onChange = () => {}, default: defaultValue, ...rest } = props;
+export default function SliderControl({
+  default: defaultValue,
+  name,
+  label,
+  description,
+  renderTrigger,
+  rightNode,
+  leftNode,
+  validationErrors,
+  hovered,
+  warning,
+  danger,
+  onClick,
+  tooltipOnClick,
+  onChange = () => {},
+  ...rest
+}: SliderControlProps) {
+  const headerProps = {
+    name,
+    label,
+    description,
+    renderTrigger,
+    rightNode,
+    leftNode,
+    validationErrors,
+    onClick,
+    hovered,
+    tooltipOnClick,
+    warning,
+    danger,
+  };
   return (
     <>
-      <ControlHeader />
+      <ControlHeader {...headerProps} />
       <Slider {...rest} onChange={onChange} defaultValue={defaultValue} />
     </>
   );


[superset] 06/14: fix(sql lab): replace the output column in the query history table (#19370)

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

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

commit 87b51c26f496ed70779323a498b0ee3d056eda7b
Author: Diego Medina <di...@gmail.com>
AuthorDate: Wed Apr 13 12:22:03 2022 -0400

    fix(sql lab): replace the output column in the query history table (#19370)
    
    * fix(sql lab): replace the output column in the query history table
    
    * improvements
    
    (cherry picked from commit d4e7c65f6abc656625d827af362d2b6b0d0e1877)
---
 .../src/SqlLab/components/QueryHistory/index.tsx          |  2 +-
 .../src/SqlLab/components/QueryTable/index.tsx            | 15 ++++++---------
 2 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx b/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx
index 7cf9d6ba65..e614017e96 100644
--- a/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx
+++ b/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx
@@ -55,7 +55,7 @@ const QueryHistory = ({ queries, actions, displayLimit }: QueryHistoryProps) =>
         'progress',
         'rows',
         'sql',
-        'output',
+        'results',
         'actions',
       ]}
       queries={queries}
diff --git a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
index 142d8a1309..50a3b626a6 100644
--- a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
+++ b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
@@ -35,10 +35,12 @@ import ResultSet from '../ResultSet';
 import HighlightedSql from '../HighlightedSql';
 import { StaticPosition, verticalAlign, StyledTooltip } from './styles';
 
-interface QueryTableQuery extends Omit<Query, 'state' | 'sql' | 'progress'> {
+interface QueryTableQuery
+  extends Omit<Query, 'state' | 'sql' | 'progress' | 'results'> {
   state?: Record<string, any>;
   sql?: Record<string, any>;
   progress?: Record<string, any>;
+  results?: Record<string, any>;
 }
 
 interface QueryTableProps {
@@ -225,12 +227,12 @@ const QueryTable = ({
           </Card>
         );
         if (q.resultsKey) {
-          q.output = (
+          q.results = (
             <ModalTrigger
               className="ResultsModal"
               triggerNode={
                 <Label type="info" className="pointer">
-                  {t('View results')}
+                  {t('View')}
                 </Label>
               }
               modalTitle={t('Data preview')}
@@ -250,13 +252,8 @@ const QueryTable = ({
               responsive
             />
           );
-        } else {
-          // if query was run using ctas and force_ctas_schema was set
-          // tempTable will have the schema
-          const schemaUsed =
-            q.ctas && q.tempTable && q.tempTable.includes('.') ? '' : q.schema;
-          q.output = [schemaUsed, q.tempTable].filter(v => v).join('.');
         }
+
         q.progress =
           state === 'success' ? (
             <ProgressBar


[superset] 02/14: chore: Clean redundant dependency from useMemo dep array (#19732)

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

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

commit 28aa69628faa3e96e51a9939a0b0890c09f64eeb
Author: Kamil Gabryjelski <ka...@gmail.com>
AuthorDate: Fri Apr 15 14:56:34 2022 +0200

    chore: Clean redundant dependency from useMemo dep array (#19732)
    
    (cherry picked from commit b7759e6fd138aff83cc80eea23069268ecc717d9)
---
 .../components/controls/DndColumnSelectControl/DndColumnSelect.tsx   | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx
index 529d4c7c78..e7b25908cf 100644
--- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx
+++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx
@@ -126,10 +126,7 @@ export function DndColumnSelect(props: DndColumnSelectProps) {
     [onChange, optionSelector],
   );
 
-  const popoverOptions = useMemo(
-    () => Object.values(options),
-    [optionSelector.values, options],
-  );
+  const popoverOptions = useMemo(() => Object.values(options), [options]);
 
   const valuesRenderer = useCallback(
     () =>


[superset] 03/14: fix(permalink): remove memoize on get salt func (#19749)

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

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

commit 2d7d2dd373ade782427c32a63069a69fcbf18139
Author: Ville Brofeldt <33...@users.noreply.github.com>
AuthorDate: Mon Apr 18 14:26:21 2022 +0300

    fix(permalink): remove memoize on get salt func (#19749)
    
    (cherry picked from commit cf5145918ba6da3b8b803bed86ad7ca22d50494a)
---
 superset/key_value/shared_entries.py | 2 --
 1 file changed, 2 deletions(-)

diff --git a/superset/key_value/shared_entries.py b/superset/key_value/shared_entries.py
index 5dda89a7b3..5f4ded9498 100644
--- a/superset/key_value/shared_entries.py
+++ b/superset/key_value/shared_entries.py
@@ -20,7 +20,6 @@ from uuid import uuid3
 
 from superset.key_value.types import KeyValueResource, SharedKey
 from superset.key_value.utils import get_uuid_namespace, random_key
-from superset.utils.memoized import memoized
 
 RESOURCE = KeyValueResource.APP
 NAMESPACE = get_uuid_namespace("")
@@ -42,7 +41,6 @@ def set_shared_value(key: SharedKey, value: Any) -> None:
     CreateKeyValueCommand(resource=RESOURCE, value=value, key=uuid_key).run()
 
 
-@memoized
 def get_permalink_salt(key: SharedKey) -> str:
     salt = get_shared_value(key)
     if salt is None:


[superset] 08/14: fix: dashboard standalone class not added when parameter set (#16619)

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

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

commit 2b6e35e039acde88381cd43150a9a07a7dfe6856
Author: Cedric Gampert <cg...@gmail.com>
AuthorDate: Wed Apr 20 09:03:59 2022 +0200

    fix: dashboard standalone class not added when parameter set (#16619)
    
    Co-authored-by: Ville Brofeldt <vi...@gmail.com>
    (cherry picked from commit 5134c63ae289a583e52ddd692848461f227aec50)
---
 superset/views/core.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/superset/views/core.py b/superset/views/core.py
index 6e329d1f48..81e04c535f 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -2024,6 +2024,8 @@ class Superset(BaseSupersetView):  # pylint: disable=too-many-public-methods
             request.args.get(utils.ReservedUrlParameters.EDIT_MODE.value) == "true"
         )
 
+        standalone_mode = ReservedUrlParameters.is_standalone_mode()
+
         add_extra_log_payload(
             dashboard_id=dashboard.id,
             dashboard_version="v2",
@@ -2042,6 +2044,7 @@ class Superset(BaseSupersetView):  # pylint: disable=too-many-public-methods
             bootstrap_data=json.dumps(
                 bootstrap_data, default=utils.pessimistic_json_iso_dttm_ser
             ),
+            standalone_mode=standalone_mode,
         )
 
     @has_access


[superset] 01/14: fix: Filter dependencies are not being applied in default values (#19698)

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

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

commit f8b202f670a1d097ce14f46987fd26d0d4f3e335
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Thu Apr 14 12:58:34 2022 -0300

    fix: Filter dependencies are not being applied in default values (#19698)
    
    (cherry picked from commit 7bc9123fe569c47ebb9eb049c96ff833478f7ded)
---
 .../FiltersConfigForm/FiltersConfigForm.tsx        | 40 +++++++++++++++++++---
 1 file changed, 36 insertions(+), 4 deletions(-)

diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
index a493eb21a5..defba16e2f 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
@@ -73,7 +73,10 @@ import { waitForAsyncData } from 'src/middleware/asyncEvent';
 import { cacheWrapper } from 'src/utils/cacheWrapper';
 import { ClientErrorObject } from 'src/utils/getClientErrorObject';
 import { SingleValueType } from 'src/filters/components/Range/SingleValueType';
-import { getFormData } from 'src/dashboard/components/nativeFilters/utils';
+import {
+  getFormData,
+  mergeExtraFormData,
+} from 'src/dashboard/components/nativeFilters/utils';
 import {
   ALLOW_DEPENDENCIES as TYPES_SUPPORT_DEPENDENCIES,
   getFiltersConfigModalTestId,
@@ -346,10 +349,11 @@ const FiltersConfigForm = (
   const forceUpdate = useForceUpdate();
   const [datasetDetails, setDatasetDetails] = useState<Record<string, any>>();
   const defaultFormFilter = useMemo(() => ({}), []);
-  const formValues = form.getFieldValue('filters')?.[filterId];
+  const filters = form.getFieldValue('filters');
+  const formValues = filters?.[filterId];
   const formFilter = formValues || undoFormValues || defaultFormFilter;
 
-  const dependencies =
+  const dependencies: string[] =
     formFilter?.dependencies || filterToEdit?.cascadeParentIds;
 
   const nativeFilterItems = getChartMetadataRegistry().items;
@@ -439,6 +443,21 @@ const FiltersConfigForm = (
     forceUpdate();
   };
 
+  // Calculates the dependencies default values to be used
+  // to extract the available values to the filter
+  let dependenciesDefaultValues = {};
+  if (dependencies && dependencies.length > 0 && filters) {
+    dependencies.forEach(dependency => {
+      const extraFormData = filters[dependency]?.defaultDataMask?.extraFormData;
+      dependenciesDefaultValues = mergeExtraFormData(
+        dependenciesDefaultValues,
+        extraFormData,
+      );
+    });
+  }
+
+  const dependenciesText = JSON.stringify(dependenciesDefaultValues);
+
   const refreshHandler = useCallback(
     (force = false) => {
       if (!hasDataset || !formFilter?.dataset?.value) {
@@ -450,6 +469,9 @@ const FiltersConfigForm = (
         groupby: formFilter?.column,
         ...formFilter,
       });
+
+      formData.extra_form_data = dependenciesDefaultValues;
+
       setNativeFilterFieldValuesWrapper({
         defaultValueQueriesData: null,
         isDataDirty: false,
@@ -499,14 +521,19 @@ const FiltersConfigForm = (
           });
         });
     },
-    [filterId, forceUpdate, form, formFilter, hasDataset],
+    [filterId, forceUpdate, form, formFilter, hasDataset, dependenciesText],
   );
 
+  // TODO: refreshHandler changes itself because of the dependencies. Needs refactor.
+  // eslint-disable-next-line react-hooks/exhaustive-deps
+  useEffect(() => refreshHandler(), [dependenciesText]);
+
   const newFormData = getFormData({
     datasetId,
     groupby: hasColumn ? formFilter?.column : undefined,
     ...formFilter,
   });
+  newFormData.extra_form_data = dependenciesDefaultValues;
 
   const [hasDefaultValue, isRequired, defaultValueTooltip, setHasDefaultValue] =
     useDefaultValue(formFilter, filterToEdit);
@@ -1085,6 +1112,11 @@ const FiltersConfigForm = (
                 tooltip={defaultValueTooltip}
                 onChange={value => {
                   setHasDefaultValue(value);
+                  if (!value) {
+                    setNativeFilterFieldValues(form, filterId, {
+                      defaultDataMask: null,
+                    });
+                  }
                   formChanged();
                 }}
               >


[superset] 11/14: fix: lost renameOperator in mixed timeseries chart (#19802)

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

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

commit 4fa96d3a3b98a464e9371283fcc12da3f7066608
Author: Yongjie Zhao <yo...@gmail.com>
AuthorDate: Thu Apr 21 15:20:09 2022 +0800

    fix: lost renameOperator in mixed timeseries chart (#19802)
    
    (cherry picked from commit 108a2a4eafc3150f7b7c33ed734e843a5d5c9f62)
---
 .../src/MixedTimeseries/buildQuery.ts                  | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/buildQuery.ts b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/buildQuery.ts
index b85feb1eee..9adc149489 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/buildQuery.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/buildQuery.ts
@@ -22,7 +22,11 @@ import {
   QueryObject,
   normalizeOrderBy,
 } from '@superset-ui/core';
-import { flattenOperator, pivotOperator } from '@superset-ui/chart-controls';
+import {
+  pivotOperator,
+  renameOperator,
+  flattenOperator,
+} from '@superset-ui/chart-controls';
 
 export default function buildQuery(formData: QueryFormData) {
   const {
@@ -66,7 +70,11 @@ export default function buildQuery(formData: QueryFormData) {
       is_timeseries: true,
       post_processing: [
         pivotOperator(formData1, { ...baseQueryObject, is_timeseries: true }),
-        flattenOperator(formData1, { ...baseQueryObject, is_timeseries: true }),
+        renameOperator(formData1, {
+          ...baseQueryObject,
+          ...{ is_timeseries: true },
+        }),
+        flattenOperator(formData1, baseQueryObject),
       ],
     } as QueryObject;
     return [normalizeOrderBy(queryObjectA)];
@@ -78,7 +86,11 @@ export default function buildQuery(formData: QueryFormData) {
       is_timeseries: true,
       post_processing: [
         pivotOperator(formData2, { ...baseQueryObject, is_timeseries: true }),
-        flattenOperator(formData2, { ...baseQueryObject, is_timeseries: true }),
+        renameOperator(formData2, {
+          ...baseQueryObject,
+          ...{ is_timeseries: true },
+        }),
+        flattenOperator(formData2, baseQueryObject),
       ],
     } as QueryObject;
     return [normalizeOrderBy(queryObjectB)];


[superset] 12/14: Fix display of column config in table chart (#19806)

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

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

commit 387138eb9ed64300348c62a8d7359a5b1946a2b3
Author: Kamil Gabryjelski <ka...@gmail.com>
AuthorDate: Thu Apr 21 19:08:04 2022 +0200

    Fix display of column config in table chart (#19806)
    
    (cherry picked from commit 12bc30e2c7b6d0993b1b0cea9205e8592ec8a3d8)
---
 .../components/ColumnConfigControl/ColumnConfigItem.tsx               | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/ColumnConfigItem.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/ColumnConfigItem.tsx
index ee9c1ea17b..550fb56491 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/ColumnConfigItem.tsx
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/ColumnConfigItem.tsx
@@ -48,8 +48,10 @@ export default React.memo(function ColumnConfigItem({
     >
       <div
         css={{
+          display: 'flex',
+          alignItems: 'center',
           cursor: 'pointer',
-          padding: `${1.5 * gridUnit}px ${2 * gridUnit}px`,
+          padding: `${gridUnit}px ${2 * gridUnit}px`,
           borderBottom: `1px solid ${colors.grayscale.light2}`,
           position: 'relative',
           paddingRight: caretWidth,


[superset] 14/14: fix(sql lab): when editing a saved query, the status is lost when switching tabs (#19448)

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

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

commit 397a182c2f64e5fcba7c71884ba9b6318e812232
Author: Diego Medina <di...@gmail.com>
AuthorDate: Fri Apr 22 17:23:30 2022 -0400

    fix(sql lab): when editing a saved query, the status is lost when switching tabs (#19448)
    
    (cherry picked from commit 800ced5e257d5d83d6dbe4ced0e7318ac40d026f)
---
 superset-frontend/src/SqlLab/actions/sqlLab.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/superset-frontend/src/SqlLab/actions/sqlLab.js b/superset-frontend/src/SqlLab/actions/sqlLab.js
index 3d1298e6c3..41717dd174 100644
--- a/superset-frontend/src/SqlLab/actions/sqlLab.js
+++ b/superset-frontend/src/SqlLab/actions/sqlLab.js
@@ -1279,6 +1279,7 @@ export function popSavedQuery(saveQueryId) {
       .then(({ json }) => {
         const queryEditorProps = {
           ...convertQueryToClient(json.result),
+          loaded: true,
           autorun: false,
         };
         return dispatch(addQueryEditor(queryEditorProps));


[superset] 04/14: fix(explore): make to show the null value as N/A in view result (#19603)

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

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

commit 5df14ed8f9d642be81c0a811a0e87f098e49ec3a
Author: smileydev <47...@users.noreply.github.com>
AuthorDate: Mon Apr 18 21:26:15 2022 -0400

    fix(explore): make to show the null value as N/A in view result (#19603)
    
    * fix(explore): make to show the null value as N/A in view result
    
    * fix(explore): make to remove console
    
    * fix(explore): make to remove console in Cell
    
    * fix(explore): make to translate N/A
    
    (cherry picked from commit 34323f9b5fcb1768f172d634e166230b6689f0da)
---
 superset-frontend/src/explore/components/DataTableControl/index.tsx | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/superset-frontend/src/explore/components/DataTableControl/index.tsx b/superset-frontend/src/explore/components/DataTableControl/index.tsx
index c94c07cb74..2ae3521182 100644
--- a/superset-frontend/src/explore/components/DataTableControl/index.tsx
+++ b/superset-frontend/src/explore/components/DataTableControl/index.tsx
@@ -250,7 +250,9 @@ export const useFilteredTableData = (
   const rowsAsStrings = useMemo(
     () =>
       data?.map((row: Record<string, any>) =>
-        Object.values(row).map(value => value?.toString().toLowerCase()),
+        Object.values(row).map(value =>
+          value ? value.toString().toLowerCase() : t('N/A'),
+        ),
       ) ?? [],
     [data],
   );


[superset] 13/14: fix(key_value): use longblob on mysql (#19805)

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

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

commit 1050fcbd3c6012d15bed893823fb867cf3003d3b
Author: Ville Brofeldt <33...@users.noreply.github.com>
AuthorDate: Fri Apr 22 13:12:29 2022 +0300

    fix(key_value): use longblob on mysql (#19805)
    
    * fix(key_value): use longblob on mysql
    
    * set length
    
    (cherry picked from commit a1bd5b283cc3b766d54c7c61d6487b4bce7ce916)
---
 superset/migrations/versions/6766938c6065_add_key_value_store.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/superset/migrations/versions/6766938c6065_add_key_value_store.py b/superset/migrations/versions/6766938c6065_add_key_value_store.py
index 0a756386ae..26b1d28e0d 100644
--- a/superset/migrations/versions/6766938c6065_add_key_value_store.py
+++ b/superset/migrations/versions/6766938c6065_add_key_value_store.py
@@ -38,7 +38,7 @@ def upgrade():
         "key_value",
         sa.Column("id", sa.Integer(), nullable=False),
         sa.Column("resource", sa.String(32), nullable=False),
-        sa.Column("value", sa.LargeBinary(), nullable=False),
+        sa.Column("value", sa.LargeBinary(length=2**31), nullable=False),
         sa.Column("uuid", UUIDType(binary=True), default=uuid4),
         sa.Column("created_on", sa.DateTime(), nullable=True),
         sa.Column("created_by_fk", sa.Integer(), nullable=True),


[superset] 09/14: feat: add renameOperator (#19776)

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

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

commit 8c8bbfb89f1896fbe404c2b62d82d66e756ecae3
Author: Yongjie Zhao <yo...@gmail.com>
AuthorDate: Wed Apr 20 19:48:12 2022 +0800

    feat: add renameOperator (#19776)
    
    (cherry picked from commit 3c28cd4625fdeeaeeac3ed730907af1fb86bc86e)
---
 .../src/operators/flattenOperator.ts               |  17 +-
 .../src/operators/index.ts                         |   1 +
 .../src/operators/renameOperator.ts                |  89 +++++++++++
 .../test/utils/operators/flattenOperator.test.ts   |  31 ----
 .../test/utils/operators/renameOperator.test.ts    | 146 +++++++++++++++++
 .../src/query/types/PostProcessing.ts              |  13 ++
 .../src/Timeseries/buildQuery.ts                   |   6 +
 superset/charts/schemas.py                         |  27 +---
 superset/utils/pandas_postprocessing/__init__.py   |   2 +
 superset/utils/pandas_postprocessing/flatten.py    |  18 ++-
 superset/utils/pandas_postprocessing/rename.py     |  58 +++++++
 .../pandas_postprocessing/test_rename.py           | 175 +++++++++++++++++++++
 12 files changed, 512 insertions(+), 71 deletions(-)

diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/operators/flattenOperator.ts b/superset-frontend/packages/superset-ui-chart-controls/src/operators/flattenOperator.ts
index 1670a84170..5188b34f2f 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/operators/flattenOperator.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/operators/flattenOperator.ts
@@ -17,21 +17,12 @@
  * specific language governing permissions and limitationsxw
  * under the License.
  */
-import { ensureIsArray, PostProcessingFlatten } from '@superset-ui/core';
+import { PostProcessingFlatten } from '@superset-ui/core';
 import { PostProcessingFactory } from './types';
 
 export const flattenOperator: PostProcessingFactory<PostProcessingFlatten> = (
   formData,
   queryObject,
-) => {
-  const drop_levels: number[] = [];
-  if (ensureIsArray(queryObject.metrics).length === 1) {
-    drop_levels.push(0);
-  }
-  return {
-    operation: 'flatten',
-    options: {
-      drop_levels,
-    },
-  };
-};
+) => ({
+  operation: 'flatten',
+});
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/operators/index.ts b/superset-frontend/packages/superset-ui-chart-controls/src/operators/index.ts
index 28e7e70070..f39d649f88 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/operators/index.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/operators/index.ts
@@ -23,6 +23,7 @@ export { timeComparePivotOperator } from './timeComparePivotOperator';
 export { sortOperator } from './sortOperator';
 export { pivotOperator } from './pivotOperator';
 export { resampleOperator } from './resampleOperator';
+export { renameOperator } from './renameOperator';
 export { contributionOperator } from './contributionOperator';
 export { prophetOperator } from './prophetOperator';
 export { boxplotOperator } from './boxplotOperator';
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/operators/renameOperator.ts b/superset-frontend/packages/superset-ui-chart-controls/src/operators/renameOperator.ts
new file mode 100644
index 0000000000..66909047b8
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/operators/renameOperator.ts
@@ -0,0 +1,89 @@
+/* eslint-disable camelcase */
+/**
+ * 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 limitationsxw
+ * under the License.
+ */
+import {
+  PostProcessingRename,
+  ensureIsArray,
+  getMetricLabel,
+  ComparisionType,
+} from '@superset-ui/core';
+import { PostProcessingFactory } from './types';
+import { getMetricOffsetsMap, isValidTimeCompare } from './utils';
+
+export const renameOperator: PostProcessingFactory<PostProcessingRename> = (
+  formData,
+  queryObject,
+) => {
+  const metrics = ensureIsArray(queryObject.metrics);
+  const columns = ensureIsArray(queryObject.columns);
+  const { x_axis: xAxis } = formData;
+  // remove or rename top level of column name(metric name) in the MultiIndex when
+  // 1) only 1 metric
+  // 2) exist dimentsion
+  // 3) exist xAxis
+  // 4) exist time comparison, and comparison type is "actual values"
+  if (
+    metrics.length === 1 &&
+    columns.length > 0 &&
+    (xAxis || queryObject.is_timeseries) &&
+    !(
+      // todo: we should provide an approach to handle derived metrics
+      (
+        isValidTimeCompare(formData, queryObject) &&
+        [
+          ComparisionType.Difference,
+          ComparisionType.Ratio,
+          ComparisionType.Percentage,
+        ].includes(formData.comparison_type)
+      )
+    )
+  ) {
+    const renamePairs: [string, string | null][] = [];
+
+    if (
+      // "actual values" will add derived metric.
+      // we will rename the "metric" from the metricWithOffset label
+      // for example: "count__1 year ago" =>	"1 year ago"
+      isValidTimeCompare(formData, queryObject) &&
+      formData.comparison_type === ComparisionType.Values
+    ) {
+      const metricOffsetMap = getMetricOffsetsMap(formData, queryObject);
+      const timeOffsets = ensureIsArray(formData.time_compare);
+      [...metricOffsetMap.keys()].forEach(metricWithOffset => {
+        const offsetLabel = timeOffsets.find(offset =>
+          metricWithOffset.includes(offset),
+        );
+        renamePairs.push([metricWithOffset, offsetLabel]);
+      });
+    }
+
+    renamePairs.push([getMetricLabel(metrics[0]), null]);
+
+    return {
+      operation: 'rename',
+      options: {
+        columns: Object.fromEntries(renamePairs),
+        level: 0,
+        inplace: true,
+      },
+    };
+  }
+
+  return undefined;
+};
diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/flattenOperator.test.ts b/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/flattenOperator.test.ts
index e63525b82e..94a9b00687 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/flattenOperator.test.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/flattenOperator.test.ts
@@ -51,40 +51,9 @@ const queryObject: QueryObject = {
     },
   ],
 };
-const singleMetricQueryObject: QueryObject = {
-  metrics: ['count(*)'],
-  time_range: '2015 : 2016',
-  granularity: 'month',
-  post_processing: [
-    {
-      operation: 'pivot',
-      options: {
-        index: ['__timestamp'],
-        columns: ['nation'],
-        aggregates: {
-          'count(*)': {
-            operator: 'sum',
-          },
-        },
-      },
-    },
-  ],
-};
 
 test('should do flattenOperator', () => {
   expect(flattenOperator(formData, queryObject)).toEqual({
     operation: 'flatten',
-    options: {
-      drop_levels: [],
-    },
-  });
-});
-
-test('should add drop level', () => {
-  expect(flattenOperator(formData, singleMetricQueryObject)).toEqual({
-    operation: 'flatten',
-    options: {
-      drop_levels: [0],
-    },
   });
 });
diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/renameOperator.test.ts b/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/renameOperator.test.ts
new file mode 100644
index 0000000000..2c32e0791b
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/renameOperator.test.ts
@@ -0,0 +1,146 @@
+/**
+ * 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 { ComparisionType, QueryObject, SqlaFormData } from '@superset-ui/core';
+import { renameOperator } from '@superset-ui/chart-controls';
+
+const formData: SqlaFormData = {
+  x_axis: 'dttm',
+  metrics: ['count(*)'],
+  groupby: ['gender'],
+  time_range: '2015 : 2016',
+  granularity: 'month',
+  datasource: 'foo',
+  viz_type: 'table',
+};
+const queryObject: QueryObject = {
+  is_timeseries: true,
+  metrics: ['count(*)'],
+  columns: ['gender', 'dttm'],
+  time_range: '2015 : 2016',
+  granularity: 'month',
+  post_processing: [],
+};
+
+test('should skip renameOperator if exists multiple metrics', () => {
+  expect(
+    renameOperator(formData, {
+      ...queryObject,
+      ...{
+        metrics: ['count(*)', 'sum(sales)'],
+      },
+    }),
+  ).toEqual(undefined);
+});
+
+test('should skip renameOperator if does not exist series', () => {
+  expect(
+    renameOperator(formData, {
+      ...queryObject,
+      ...{
+        columns: [],
+      },
+    }),
+  ).toEqual(undefined);
+});
+
+test('should skip renameOperator if does not exist x_axis and is_timeseries', () => {
+  expect(
+    renameOperator(
+      {
+        ...formData,
+        ...{ x_axis: null },
+      },
+      { ...queryObject, ...{ is_timeseries: false } },
+    ),
+  ).toEqual(undefined);
+});
+
+test('should skip renameOperator if exists derived metrics', () => {
+  [
+    ComparisionType.Difference,
+    ComparisionType.Ratio,
+    ComparisionType.Percentage,
+  ].forEach(type => {
+    expect(
+      renameOperator(
+        {
+          ...formData,
+          ...{
+            comparison_type: type,
+            time_compare: ['1 year ago'],
+          },
+        },
+        {
+          ...queryObject,
+          ...{
+            metrics: ['count(*)'],
+          },
+        },
+      ),
+    ).toEqual(undefined);
+  });
+});
+
+test('should add renameOperator', () => {
+  expect(renameOperator(formData, queryObject)).toEqual({
+    operation: 'rename',
+    options: { columns: { 'count(*)': null }, inplace: true, level: 0 },
+  });
+});
+
+test('should add renameOperator if does not exist x_axis', () => {
+  expect(
+    renameOperator(
+      {
+        ...formData,
+        ...{ x_axis: null },
+      },
+      queryObject,
+    ),
+  ).toEqual({
+    operation: 'rename',
+    options: { columns: { 'count(*)': null }, inplace: true, level: 0 },
+  });
+});
+
+test('should add renameOperator if exist "actual value" time comparison', () => {
+  expect(
+    renameOperator(
+      {
+        ...formData,
+        ...{
+          comparison_type: ComparisionType.Values,
+          time_compare: ['1 year ago', '1 year later'],
+        },
+      },
+      queryObject,
+    ),
+  ).toEqual({
+    operation: 'rename',
+    options: {
+      columns: {
+        'count(*)': null,
+        'count(*)__1 year ago': '1 year ago',
+        'count(*)__1 year later': '1 year later',
+      },
+      inplace: true,
+      level: 0,
+    },
+  });
+});
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts b/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts
index 0ba7e4fc4a..315cdb8456 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts
@@ -201,6 +201,18 @@ export type PostProcessingResample =
   | _PostProcessingResample
   | DefaultPostProcessing;
 
+interface _PostProcessingRename {
+  operation: 'rename';
+  options: {
+    columns: Record<string, string | null>;
+    inplace?: boolean;
+    level?: number | string;
+  };
+}
+export type PostProcessingRename =
+  | _PostProcessingRename
+  | DefaultPostProcessing;
+
 interface _PostProcessingFlatten {
   operation: 'flatten';
   options?: {
@@ -228,6 +240,7 @@ export type PostProcessingRule =
   | PostProcessingCompare
   | PostProcessingSort
   | PostProcessingResample
+  | PostProcessingRename
   | PostProcessingFlatten;
 
 export function isPostProcessingAggregation(
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/buildQuery.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/buildQuery.ts
index c4cdaa9360..c2f603bfc4 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/buildQuery.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/buildQuery.ts
@@ -30,6 +30,7 @@ import {
   isValidTimeCompare,
   pivotOperator,
   resampleOperator,
+  renameOperator,
   contributionOperator,
   prophetOperator,
   timeComparePivotOperator,
@@ -91,7 +92,12 @@ export default function buildQuery(formData: QueryFormData) {
           rollingWindowOperator(formData, baseQueryObject),
           timeCompareOperator(formData, baseQueryObject),
           resampleOperator(formData, baseQueryObject),
+          renameOperator(formData, {
+            ...baseQueryObject,
+            ...{ is_timeseries },
+          }),
           flattenOperator(formData, baseQueryObject),
+          // todo: move contribution and prophet before flatten
           contributionOperator(formData, baseQueryObject),
           prophetOperator(formData, baseQueryObject),
         ],
diff --git a/superset/charts/schemas.py b/superset/charts/schemas.py
index 56219c143e..bdf423be4a 100644
--- a/superset/charts/schemas.py
+++ b/superset/charts/schemas.py
@@ -17,6 +17,7 @@
 # pylint: disable=too-many-lines
 from __future__ import annotations
 
+import inspect
 from typing import Any, Dict, Optional, TYPE_CHECKING
 
 from flask_babel import gettext as _
@@ -27,7 +28,7 @@ from marshmallow_enum import EnumField
 from superset import app
 from superset.common.chart_data import ChartDataResultFormat, ChartDataResultType
 from superset.db_engine_specs.base import builtin_time_grains
-from superset.utils import schema as utils
+from superset.utils import pandas_postprocessing, schema as utils
 from superset.utils.core import (
     AnnotationType,
     FilterOperator,
@@ -771,24 +772,12 @@ class ChartDataPostProcessingOperationSchema(Schema):
         description="Post processing operation type",
         required=True,
         validate=validate.OneOf(
-            choices=(
-                "aggregate",
-                "boxplot",
-                "contribution",
-                "cum",
-                "geodetic_parse",
-                "geohash_decode",
-                "geohash_encode",
-                "pivot",
-                "prophet",
-                "rolling",
-                "select",
-                "sort",
-                "diff",
-                "compare",
-                "resample",
-                "flatten",
-            )
+            choices=[
+                name
+                for name, value in inspect.getmembers(
+                    pandas_postprocessing, inspect.isfunction
+                )
+            ]
         ),
         example="aggregate",
     )
diff --git a/superset/utils/pandas_postprocessing/__init__.py b/superset/utils/pandas_postprocessing/__init__.py
index 3d180bc372..9755df984c 100644
--- a/superset/utils/pandas_postprocessing/__init__.py
+++ b/superset/utils/pandas_postprocessing/__init__.py
@@ -28,6 +28,7 @@ from superset.utils.pandas_postprocessing.geography import (
 )
 from superset.utils.pandas_postprocessing.pivot import pivot
 from superset.utils.pandas_postprocessing.prophet import prophet
+from superset.utils.pandas_postprocessing.rename import rename
 from superset.utils.pandas_postprocessing.resample import resample
 from superset.utils.pandas_postprocessing.rolling import rolling
 from superset.utils.pandas_postprocessing.select import select
@@ -46,6 +47,7 @@ __all__ = [
     "geodetic_parse",
     "pivot",
     "prophet",
+    "rename",
     "resample",
     "rolling",
     "select",
diff --git a/superset/utils/pandas_postprocessing/flatten.py b/superset/utils/pandas_postprocessing/flatten.py
index 3d5a003bf1..2874ac5797 100644
--- a/superset/utils/pandas_postprocessing/flatten.py
+++ b/superset/utils/pandas_postprocessing/flatten.py
@@ -81,14 +81,16 @@ def flatten(
     """
     if _is_multi_index_on_columns(df):
         df.columns = df.columns.droplevel(drop_levels)
-        # every cell should be converted to string
-        df.columns = [
-            FLAT_COLUMN_SEPARATOR.join(
-                # pylint: disable=superfluous-parens
-                [str(cell) for cell in (series if is_sequence(series) else [series])]
-            )
-            for series in df.columns.to_flat_index()
-        ]
+        _columns = []
+        for series in df.columns.to_flat_index():
+            _cells = []
+            for cell in series if is_sequence(series) else [series]:
+                if pd.notnull(cell):
+                    # every cell should be converted to string
+                    _cells.append(str(cell))
+            _columns.append(FLAT_COLUMN_SEPARATOR.join(_cells))
+
+        df.columns = _columns
 
     if reset_index and not isinstance(df.index, pd.RangeIndex):
         df = df.reset_index(level=0)
diff --git a/superset/utils/pandas_postprocessing/rename.py b/superset/utils/pandas_postprocessing/rename.py
new file mode 100644
index 0000000000..0e35a651a8
--- /dev/null
+++ b/superset/utils/pandas_postprocessing/rename.py
@@ -0,0 +1,58 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from typing import Dict, Optional, Union
+
+import pandas as pd
+from flask_babel import gettext as _
+from pandas._typing import Level
+
+from superset.exceptions import InvalidPostProcessingError
+from superset.utils.pandas_postprocessing.utils import validate_column_args
+
+
+@validate_column_args("columns")
+def rename(
+    df: pd.DataFrame,
+    columns: Dict[str, Union[str, None]],
+    inplace: bool = False,
+    level: Optional[Level] = None,
+) -> pd.DataFrame:
+    """
+    Alter column name of DataFrame
+
+    :param df: DataFrame to rename.
+    :param columns: The offset string representing target conversion.
+    :param inplace: Whether to return a new DataFrame.
+    :param level: In case of a MultiIndex, only rename labels in the specified level.
+    :return: DataFrame after rename
+    :raises InvalidPostProcessingError: If the request is unexpected
+    """
+    if not columns:
+        return df
+
+    try:
+        _rename_level = df.columns.get_level_values(level=level)
+    except (IndexError, KeyError) as err:
+        raise InvalidPostProcessingError from err
+
+    if all(new_name in _rename_level for new_name in columns.values()):
+        raise InvalidPostProcessingError(_("Label already exists"))
+
+    if inplace:
+        df.rename(columns=columns, inplace=inplace, level=level)
+        return df
+    return df.rename(columns=columns, inplace=inplace, level=level)
diff --git a/tests/unit_tests/pandas_postprocessing/test_rename.py b/tests/unit_tests/pandas_postprocessing/test_rename.py
new file mode 100644
index 0000000000..f49680a352
--- /dev/null
+++ b/tests/unit_tests/pandas_postprocessing/test_rename.py
@@ -0,0 +1,175 @@
+# 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 pandas as pd
+import pytest
+
+from superset.exceptions import InvalidPostProcessingError
+from superset.utils import pandas_postprocessing as pp
+from tests.unit_tests.fixtures.dataframes import categories_df
+
+
+def test_rename_should_not_side_effect():
+    _categories_df = categories_df.copy()
+    pp.rename(
+        df=_categories_df,
+        columns={
+            "constant": "constant_newname",
+            "category": "category_namename",
+        },
+    )
+    assert _categories_df.equals(categories_df)
+
+
+def test_rename():
+    new_categories_df = pp.rename(
+        df=categories_df,
+        columns={
+            "constant": "constant_newname",
+            "category": "category_newname",
+        },
+    )
+    assert list(new_categories_df.columns.values) == [
+        "constant_newname",
+        "category_newname",
+        "dept",
+        "name",
+        "asc_idx",
+        "desc_idx",
+        "idx_nulls",
+    ]
+    assert not new_categories_df.equals(categories_df)
+
+
+def test_should_inplace_rename():
+    _categories_df = categories_df.copy()
+    _categories_df_inplaced = pp.rename(
+        df=_categories_df,
+        columns={
+            "constant": "constant_newname",
+            "category": "category_namename",
+        },
+        inplace=True,
+    )
+    assert _categories_df_inplaced.equals(_categories_df)
+
+
+def test_should_rename_on_level():
+    iterables = [["m1", "m2"], ["a", "b"], ["x", "y"]]
+    columns = pd.MultiIndex.from_product(iterables, names=[None, "level1", "level2"])
+    df = pd.DataFrame(index=[0, 1, 2], columns=columns, data=1)
+    """
+                   m1          m2
+    level1  a     b     a     b
+    level2  x  y  x  y  x  y  x  y
+    0       1  1  1  1  1  1  1  1
+    1       1  1  1  1  1  1  1  1
+    2       1  1  1  1  1  1  1  1
+    """
+    post_df = pp.rename(
+        df=df,
+        columns={"m1": "new_m1"},
+        level=0,
+    )
+    assert post_df.columns.get_level_values(level=0).equals(
+        pd.Index(
+            [
+                "new_m1",
+                "new_m1",
+                "new_m1",
+                "new_m1",
+                "m2",
+                "m2",
+                "m2",
+                "m2",
+            ]
+        )
+    )
+
+
+def test_should_raise_exception_no_column():
+    with pytest.raises(InvalidPostProcessingError):
+        pp.rename(
+            df=categories_df,
+            columns={
+                "foobar": "foobar2",
+            },
+        )
+
+
+def test_should_raise_exception_duplication():
+    with pytest.raises(InvalidPostProcessingError):
+        pp.rename(
+            df=categories_df,
+            columns={
+                "constant": "category",
+            },
+        )
+
+
+def test_should_raise_exception_duplication_on_multiindx():
+    iterables = [["m1", "m2"], ["a", "b"], ["x", "y"]]
+    columns = pd.MultiIndex.from_product(iterables, names=[None, "level1", "level2"])
+    df = pd.DataFrame(index=[0, 1, 2], columns=columns, data=1)
+    """
+                   m1          m2
+    level1  a     b     a     b
+    level2  x  y  x  y  x  y  x  y
+    0       1  1  1  1  1  1  1  1
+    1       1  1  1  1  1  1  1  1
+    2       1  1  1  1  1  1  1  1
+    """
+
+    with pytest.raises(InvalidPostProcessingError):
+        pp.rename(
+            df=df,
+            columns={
+                "m1": "m2",
+            },
+            level=0,
+        )
+        pp.rename(
+            df=df,
+            columns={
+                "a": "b",
+            },
+            level=1,
+        )
+
+
+def test_should_raise_exception_invalid_level():
+    with pytest.raises(InvalidPostProcessingError):
+        pp.rename(
+            df=categories_df,
+            columns={
+                "constant": "new_constant",
+            },
+            level=100,
+        )
+        pp.rename(
+            df=categories_df,
+            columns={
+                "constant": "new_constant",
+            },
+            level="xxxxx",
+        )
+
+
+def test_should_return_df_empty_columns():
+    assert pp.rename(
+        df=categories_df,
+        columns={},
+    ).equals(categories_df)


[superset] 07/14: fix: SQL Lab UI Error: Objects are not valid as a React child (#19783)

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

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

commit 1d141be463fbf99aa6b77b3be797a89f7341a45e
Author: Diego Medina <di...@gmail.com>
AuthorDate: Wed Apr 20 02:18:46 2022 -0400

    fix: SQL Lab UI Error: Objects are not valid as a React child (#19783)
    
    (cherry picked from commit dfba9ea596605dc11b29ca1c82615db539e394b2)
---
 superset-frontend/src/SqlLab/components/QueryTable/index.tsx | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
index 50a3b626a6..f76f23bdfb 100644
--- a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
+++ b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
@@ -252,6 +252,8 @@ const QueryTable = ({
               responsive
             />
           );
+        } else {
+          q.results = <></>;
         }
 
         q.progress =


[superset] 05/14: fix(dashboard): copy permalink to dashboard chart (#19772)

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

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

commit c9e3ca11e2c015a14aaf5948e23a0d850946d506
Author: Ville Brofeldt <33...@users.noreply.github.com>
AuthorDate: Tue Apr 19 22:34:41 2022 +0300

    fix(dashboard): copy permalink to dashboard chart (#19772)
    
    * fix(dashboard): copy permalink to dashboard chart
    
    * lint
    
    * address comments
    
    (cherry picked from commit e061955fd077a9eab6f22f081aa02690801bfd3e)
---
 .../src/components/URLShortLinkButton/index.jsx    |  8 +++----
 .../components/SliceHeaderControls/index.tsx       |  6 +++--
 .../components/menu/ShareMenuItems/index.tsx       | 28 +++++++++-------------
 superset-frontend/src/utils/urlUtils.ts            | 14 +++++++----
 4 files changed, 28 insertions(+), 28 deletions(-)

diff --git a/superset-frontend/src/components/URLShortLinkButton/index.jsx b/superset-frontend/src/components/URLShortLinkButton/index.jsx
index 35795f81a1..4a03e02d3e 100644
--- a/superset-frontend/src/components/URLShortLinkButton/index.jsx
+++ b/superset-frontend/src/components/URLShortLinkButton/index.jsx
@@ -57,11 +57,11 @@ class URLShortLinkButton extends React.Component {
     if (this.props.dashboardId) {
       getFilterValue(this.props.dashboardId, nativeFiltersKey)
         .then(filterState =>
-          getDashboardPermalink(
-            String(this.props.dashboardId),
+          getDashboardPermalink({
+            dashboardId: this.props.dashboardId,
             filterState,
-            this.props.anchorLinkId,
-          )
+            hash: this.props.anchorLinkId,
+          })
             .then(this.onShortUrlSuccess)
             .catch(this.props.addDangerToast),
         )
diff --git a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx
index 09d646853d..86c73a09b6 100644
--- a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx
+++ b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx
@@ -213,6 +213,8 @@ class SliceHeaderControls extends React.PureComponent<
 
   render() {
     const {
+      componentId,
+      dashboardId,
       slice,
       isFullSize,
       cachedDttm = [],
@@ -221,7 +223,6 @@ class SliceHeaderControls extends React.PureComponent<
       addDangerToast = () => {},
       supersetCanShare = false,
       isCached = [],
-      formData,
     } = this.props;
     const crossFilterItems = getChartMetadataRegistry().items;
     const isTable = slice.viz_type === 'table';
@@ -310,13 +311,14 @@ class SliceHeaderControls extends React.PureComponent<
 
         {supersetCanShare && (
           <ShareMenuItems
+            dashboardId={dashboardId}
+            dashboardComponentId={componentId}
             copyMenuItemTitle={t('Copy permalink to clipboard')}
             emailMenuItemTitle={t('Share permalink by email')}
             emailSubject={t('Superset chart')}
             emailBody={t('Check out this chart: ')}
             addSuccessToast={addSuccessToast}
             addDangerToast={addDangerToast}
-            formData={formData}
           />
         )}
 
diff --git a/superset-frontend/src/dashboard/components/menu/ShareMenuItems/index.tsx b/superset-frontend/src/dashboard/components/menu/ShareMenuItems/index.tsx
index c70e47dc3d..b196100734 100644
--- a/superset-frontend/src/dashboard/components/menu/ShareMenuItems/index.tsx
+++ b/superset-frontend/src/dashboard/components/menu/ShareMenuItems/index.tsx
@@ -18,14 +18,10 @@
  */
 import React from 'react';
 import copyTextToClipboard from 'src/utils/copy';
-import { t, logging, QueryFormData } from '@superset-ui/core';
+import { t, logging } from '@superset-ui/core';
 import { Menu } from 'src/components/Menu';
-import {
-  getChartPermalink,
-  getDashboardPermalink,
-  getUrlParam,
-} from 'src/utils/urlUtils';
-import { RESERVED_DASHBOARD_URL_PARAMS, URL_PARAMS } from 'src/constants';
+import { getDashboardPermalink, getUrlParam } from 'src/utils/urlUtils';
+import { URL_PARAMS } from 'src/constants';
 import { getFilterValue } from 'src/dashboard/components/nativeFilters/FilterBar/keyValue';
 
 interface ShareMenuItemProps {
@@ -36,8 +32,8 @@ interface ShareMenuItemProps {
   emailBody: string;
   addDangerToast: Function;
   addSuccessToast: Function;
-  dashboardId?: string;
-  formData?: Pick<QueryFormData, 'slice_id' | 'datasource'>;
+  dashboardId: string | number;
+  dashboardComponentId?: string;
 }
 
 const ShareMenuItems = (props: ShareMenuItemProps) => {
@@ -49,23 +45,21 @@ const ShareMenuItems = (props: ShareMenuItemProps) => {
     addDangerToast,
     addSuccessToast,
     dashboardId,
-    formData,
+    dashboardComponentId,
     ...rest
   } = props;
 
   async function generateUrl() {
-    // chart
-    if (formData) {
-      // we need to remove reserved dashboard url params
-      return getChartPermalink(formData, RESERVED_DASHBOARD_URL_PARAMS);
-    }
-    // dashboard
     const nativeFiltersKey = getUrlParam(URL_PARAMS.nativeFiltersKey);
     let filterState = {};
     if (nativeFiltersKey && dashboardId) {
       filterState = await getFilterValue(dashboardId, nativeFiltersKey);
     }
-    return getDashboardPermalink(String(dashboardId), filterState);
+    return getDashboardPermalink({
+      dashboardId,
+      filterState,
+      hash: dashboardComponentId,
+    });
   }
 
   async function onCopyLink() {
diff --git a/superset-frontend/src/utils/urlUtils.ts b/superset-frontend/src/utils/urlUtils.ts
index be857517e0..bd570291f2 100644
--- a/superset-frontend/src/utils/urlUtils.ts
+++ b/superset-frontend/src/utils/urlUtils.ts
@@ -154,11 +154,15 @@ export function getChartPermalink(
   });
 }
 
-export function getDashboardPermalink(
-  dashboardId: string,
-  filterState: JsonObject,
-  hash?: string,
-) {
+export function getDashboardPermalink({
+  dashboardId,
+  filterState,
+  hash, // the anchor part of the link which corresponds to the tab/chart id
+}: {
+  dashboardId: string | number;
+  filterState: JsonObject;
+  hash?: string;
+}) {
   // only encode filter box state if non-empty
   return getPermalink(`/api/v1/dashboard/${dashboardId}/permalink`, {
     filterState,