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/12/11 00:14:38 UTC

[superset] 01/03: fix(Dashboard): Copy dashboard with duplicating charts 500 error (#17707)

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 4c00bd4332860912ddecfa9ca02927b514621e3d
Author: Geido <60...@users.noreply.github.com>
AuthorDate: Fri Dec 10 01:25:01 2021 +0200

    fix(Dashboard): Copy dashboard with duplicating charts 500 error (#17707)
    
    * Fix copy dashboard with charts
    
    * Fix Cypress test
---
 .../src/dashboard/actions/dashboardState.js        | 160 +++++++++++++++++++++
 1 file changed, 160 insertions(+)

diff --git a/superset-frontend/src/dashboard/actions/dashboardState.js b/superset-frontend/src/dashboard/actions/dashboardState.js
index 315ac7e..3eb193f 100644
--- a/superset-frontend/src/dashboard/actions/dashboardState.js
+++ b/superset-frontend/src/dashboard/actions/dashboardState.js
@@ -189,6 +189,166 @@ export function saveDashboardRequest(data, id, saveType) {
     const serializedFilters = serializeActiveFilterValues(getActiveFilters());
     // serialize filter scope for each filter field, grouped by filter id
     const serializedFilterScopes = serializeFilterScopes(dashboardFilters);
+    const {
+      certified_by,
+      certification_details,
+      css,
+      dashboard_title,
+      owners,
+      roles,
+      slug,
+    } = data;
+
+    const hasId = item => item.id !== undefined;
+
+    // making sure the data is what the backend expects
+    const cleanedData = {
+      ...data,
+      certified_by: certified_by || '',
+      certification_details:
+        certified_by && certification_details ? certification_details : '',
+      css: css || '',
+      dashboard_title: dashboard_title || t('[ untitled dashboard ]'),
+      owners: ensureIsArray(owners).map(o => (hasId(o) ? o.id : o)),
+      roles: !isFeatureEnabled(FeatureFlag.DASHBOARD_RBAC)
+        ? undefined
+        : ensureIsArray(roles).map(r => (hasId(r) ? r.id : r)),
+      slug: slug || null,
+      metadata: {
+        ...data.metadata,
+        color_namespace: data.metadata?.color_namespace || undefined,
+        color_scheme: data.metadata?.color_scheme || '',
+        expanded_slices: data.metadata?.expanded_slices || {},
+        label_colors: data.metadata?.label_colors || {},
+        refresh_frequency: data.metadata?.refresh_frequency || 0,
+        timed_refresh_immune_slices:
+          data.metadata?.timed_refresh_immune_slices || [],
+      },
+    };
+
+    const handleChartConfiguration = () => {
+      const {
+        dashboardInfo: {
+          metadata: { chart_configuration = {} },
+        },
+      } = getState();
+      const chartConfiguration = Object.values(chart_configuration).reduce(
+        (prev, next) => {
+          // If chart removed from dashboard - remove it from metadata
+          if (
+            Object.values(layout).find(
+              layoutItem => layoutItem?.meta?.chartId === next.id,
+            )
+          ) {
+            return { ...prev, [next.id]: next };
+          }
+          return prev;
+        },
+        {},
+      );
+      return chartConfiguration;
+    };
+
+    const onCopySuccess = response => {
+      const lastModifiedTime = response.json.last_modified_time;
+      if (lastModifiedTime) {
+        dispatch(saveDashboardRequestSuccess(lastModifiedTime));
+      }
+      if (isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS)) {
+        const chartConfiguration = handleChartConfiguration();
+        dispatch(setChartConfiguration(chartConfiguration));
+      }
+      dispatch(addSuccessToast(t('This dashboard was saved successfully.')));
+      return response;
+    };
+
+    const onUpdateSuccess = response => {
+      const updatedDashboard = response.json.result;
+      const lastModifiedTime = response.json.last_modified_time;
+      // synching with the backend transformations of the metadata
+      if (updatedDashboard.json_metadata) {
+        const metadata = JSON.parse(updatedDashboard.json_metadata);
+        dispatch(
+          dashboardInfoChanged({
+            metadata,
+          }),
+        );
+        if (metadata.chart_configuration) {
+          dispatch({
+            type: SET_CHART_CONFIG_COMPLETE,
+            chartConfiguration: metadata.chart_configuration,
+          });
+        }
+      }
+      if (lastModifiedTime) {
+        dispatch(saveDashboardRequestSuccess(lastModifiedTime));
+      }
+      // redirect to the new slug or id
+      window.history.pushState(
+        { event: 'dashboard_properties_changed' },
+        '',
+        `/superset/dashboard/${slug || id}/`,
+      );
+
+      dispatch(addSuccessToast(t('This dashboard was saved successfully.')));
+      return response;
+    };
+
+    const onError = async response => {
+      const { error, message } = await getClientErrorObject(response);
+      let errorText = t('Sorry, an unknown error occured');
+
+      if (error) {
+        errorText = t(
+          'Sorry, there was an error saving this dashboard: %s',
+          error,
+        );
+      }
+      if (typeof message === 'string' && message === 'Forbidden') {
+        errorText = t('You do not have permission to edit this dashboard');
+      }
+      dispatch(addDangerToast(errorText));
+    };
+
+    if (saveType === SAVE_TYPE_OVERWRITE) {
+      let chartConfiguration = {};
+      if (isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS)) {
+        chartConfiguration = handleChartConfiguration();
+      }
+      const updatedDashboard = {
+        certified_by: cleanedData.certified_by,
+        certification_details: cleanedData.certification_details,
+        css: cleanedData.css,
+        dashboard_title: cleanedData.dashboard_title,
+        slug: cleanedData.slug,
+        owners: cleanedData.owners,
+        roles: cleanedData.roles,
+        json_metadata: safeStringify({
+          ...(cleanedData?.metadata || {}),
+          default_filters: safeStringify(serializedFilters),
+          filter_scopes: serializedFilterScopes,
+          chart_configuration: chartConfiguration,
+        }),
+      };
+
+      return SupersetClient.put({
+        endpoint: `/api/v1/dashboard/${id}`,
+        headers: { 'Content-Type': 'application/json' },
+        body: JSON.stringify(updatedDashboard),
+      })
+        .then(response => onUpdateSuccess(response))
+        .catch(response => onError(response));
+    }
+    // changing the data as the endpoint requires
+    const copyData = { ...cleanedData };
+    if (copyData.metadata) {
+      delete copyData.metadata;
+    }
+    const finalCopyData = {
+      ...copyData,
+      // the endpoint is expecting the metadata to be flat
+      ...(cleanedData?.metadata || {}),
+    };
     return SupersetClient.post({
       endpoint: `/superset/${path}/${id}/`,
       postPayload: {