Skip to content

Commit

Permalink
fix(dashboard): Cross-filters not working properly for new dashboards (
Browse files Browse the repository at this point in the history
  • Loading branch information
kgabryje authored Mar 1, 2023
1 parent aba6900 commit 7196e87
Show file tree
Hide file tree
Showing 4 changed files with 288 additions and 71 deletions.
25 changes: 10 additions & 15 deletions superset-frontend/src/dashboard/actions/dashboardState.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ import {
SAVE_TYPE_OVERWRITE,
SAVE_TYPE_OVERWRITE_CONFIRMED,
} from 'src/dashboard/util/constants';
import { isCrossFiltersEnabled } from 'src/dashboard/util/crossFilters';
import {
getCrossFiltersConfiguration,
isCrossFiltersEnabled,
} from 'src/dashboard/util/crossFilters';
import {
addSuccessToast,
addWarningToast,
Expand Down Expand Up @@ -277,25 +280,17 @@ export function saveDashboardRequest(data, id, saveType) {

const handleChartConfiguration = () => {
const {
dashboardLayout,
charts,
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 getCrossFiltersConfiguration(
dashboardLayout.present,
chart_configuration,
charts,
);
return chartConfiguration;
};

const onCopySuccess = response => {
Expand Down
66 changes: 11 additions & 55 deletions superset-frontend/src/dashboard/actions/hydrate.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@
* specific language governing permissions and limitations
* under the License.
*/
/* eslint-disable camelcase */
import { Behavior, getChartMetadataRegistry } from '@superset-ui/core';

import { chart } from 'src/components/Chart/chartReducer';
import { initSliceEntities } from 'src/dashboard/reducers/sliceEntities';
import { getInitialState as getInitialNativeFilterState } from 'src/dashboard/reducers/nativeFilters';
import { applyDefaultFormData } from 'src/explore/store';
import { buildActiveFilters } from 'src/dashboard/util/activeDashboardFilters';
import { findPermission } from 'src/utils/findPermission';
import { canUserEditDashboard } from 'src/dashboard/util/permissionUtils';
import { isCrossFiltersEnabled } from 'src/dashboard/util/crossFilters';
import {
getCrossFiltersConfiguration,
isCrossFiltersEnabled,
} from 'src/dashboard/util/crossFilters';
import {
DASHBOARD_FILTER_SCOPE_GLOBAL,
dashboardFilter,
Expand Down Expand Up @@ -54,7 +54,6 @@ import { ResourceStatus } from 'src/hooks/apiResources/apiResources';
import { FeatureFlag, isFeatureEnabled } from '../../featureFlags';
import extractUrlParams from '../util/extractUrlParams';
import { updateColorSchema } from './dashboardInfo';
import { getChartIdsInFilterScope } from '../util/getChartIdsInFilterScope';
import updateComponentParentsList from '../util/updateComponentParentsList';
import { FilterBarOrientation } from '../types';

Expand Down Expand Up @@ -124,7 +123,7 @@ export const hydrateDashboard =

charts.forEach(slice => {
const key = slice.slice_id;
const form_data = {
const formData = {
...slice.form_data,
url_params: {
...slice.form_data.url_params,
Expand All @@ -134,7 +133,7 @@ export const hydrateDashboard =
chartQueries[key] = {
...chart,
id: key,
form_data: applyDefaultFormData(form_data),
form_data: applyDefaultFormData(formData),
};

slices[key] = {
Expand Down Expand Up @@ -311,54 +310,11 @@ export const hydrateDashboard =
);

if (isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS)) {
// If user just added cross filter to dashboard it's not saving it scope on server,
// so we tweak it until user will update scope and will save it in server
Object.values(dashboardLayout.present).forEach(layoutItem => {
const chartId = layoutItem.meta?.chartId;
const behaviors =
(
getChartMetadataRegistry().get(
chartQueries[chartId]?.form_data?.viz_type,
) ?? {}
)?.behaviors ?? [];

if (!metadata.chart_configuration) {
metadata.chart_configuration = {};
}
if (behaviors.includes(Behavior.INTERACTIVE_CHART)) {
if (!metadata.chart_configuration[chartId]) {
metadata.chart_configuration[chartId] = {
id: chartId,
crossFilters: {
scope: {
rootPath: [DASHBOARD_ROOT_ID],
excluded: [chartId], // By default it doesn't affects itself
},
},
};
}
metadata.chart_configuration[chartId].crossFilters.chartsInScope =
getChartIdsInFilterScope(
metadata.chart_configuration[chartId].crossFilters.scope,
chartQueries,
dashboardLayout.present,
);
}
if (
behaviors.includes(Behavior.INTERACTIVE_CHART) &&
!metadata.chart_configuration[chartId]
) {
metadata.chart_configuration[chartId] = {
id: chartId,
crossFilters: {
scope: {
rootPath: [DASHBOARD_ROOT_ID],
excluded: [chartId], // By default it doesn't affects itself
},
},
};
}
});
metadata.chart_configuration = getCrossFiltersConfiguration(
dashboardLayout.present,
metadata.chart_configuration,
chartQueries,
);
}

const { roles } = user;
Expand Down
207 changes: 207 additions & 0 deletions superset-frontend/src/dashboard/util/crossFilters.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/**
* 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 sinon from 'sinon';
import { Behavior, FeatureFlag } from '@superset-ui/core';
import * as core from '@superset-ui/core';
import { getCrossFiltersConfiguration } from './crossFilters';

const DASHBOARD_LAYOUT = {
'CHART-1': {
children: [],
id: 'CHART-1',
meta: {
chartId: 1,
sliceName: 'Test chart 1',
height: 1,
width: 1,
uuid: '1',
},
parents: ['ROOT_ID', 'GRID_ID', 'ROW-6XUMf1rV76'],
type: 'CHART',
},
'CHART-2': {
children: [],
id: 'CHART-2',
meta: {
chartId: 2,
sliceName: 'Test chart 2',
height: 1,
width: 1,
uuid: '2',
},
parents: ['ROOT_ID', 'GRID_ID', 'ROW-6XUMf1rV76'],
type: 'CHART',
},
};

const CHARTS = {
'1': {
id: 1,
form_data: {
datasource: '2__table',
viz_type: 'echarts_timeseries_line',
slice_id: 1,
},
chartAlert: null,
chartStatus: 'rendered' as const,
chartUpdateEndTime: 0,
chartUpdateStartTime: 0,
lastRendered: 0,
latestQueryFormData: {},
sliceFormData: {
datasource: '2__table',
viz_type: 'echarts_timeseries_line',
},
queryController: null,
queriesResponse: [{}],
triggerQuery: false,
},
'2': {
id: 2,
form_data: {
datasource: '2__table',
viz_type: 'echarts_timeseries_line',
slice_id: 2,
},
chartAlert: null,
chartStatus: 'rendered' as const,
chartUpdateEndTime: 0,
chartUpdateStartTime: 0,
lastRendered: 0,
latestQueryFormData: {},
sliceFormData: {
datasource: '2__table',
viz_type: 'echarts_timeseries_line',
},
queryController: null,
queriesResponse: [{}],
triggerQuery: false,
},
};

const INITIAL_CHART_CONFIG = {
'1': {
id: 1,
crossFilters: {
scope: {
rootPath: ['ROOT_ID'],
excluded: [1],
},
chartsInScope: [2],
},
},
};

test('Generate correct cross filters configuration without initial configuration', () => {
// @ts-ignore
global.featureFlags = {
[FeatureFlag.DASHBOARD_CROSS_FILTERS]: true,
};
const metadataRegistryStub = sinon
.stub(core, 'getChartMetadataRegistry')
.callsFake(() => ({
// @ts-ignore
get: () => ({
behaviors: [Behavior.INTERACTIVE_CHART],
}),
}));
expect(
getCrossFiltersConfiguration(DASHBOARD_LAYOUT, undefined, CHARTS),
).toEqual({
'1': {
id: 1,
crossFilters: {
scope: {
rootPath: ['ROOT_ID'],
excluded: [1],
},
chartsInScope: [2],
},
},
'2': {
id: 2,
crossFilters: {
scope: {
rootPath: ['ROOT_ID'],
excluded: [2],
},
chartsInScope: [1],
},
},
});
metadataRegistryStub.restore();
});

test('Generate correct cross filters configuration with initial configuration', () => {
// @ts-ignore
global.featureFlags = {
[FeatureFlag.DASHBOARD_CROSS_FILTERS]: true,
};
const metadataRegistryStub = sinon
.stub(core, 'getChartMetadataRegistry')
.callsFake(() => ({
// @ts-ignore
get: () => ({
behaviors: [Behavior.INTERACTIVE_CHART],
}),
}));
expect(
getCrossFiltersConfiguration(
DASHBOARD_LAYOUT,
INITIAL_CHART_CONFIG,
CHARTS,
),
).toEqual({
'1': {
id: 1,
crossFilters: {
scope: {
rootPath: ['ROOT_ID'],
excluded: [1],
},
chartsInScope: [2],
},
},
'2': {
id: 2,
crossFilters: {
scope: {
rootPath: ['ROOT_ID'],
excluded: [2],
},
chartsInScope: [1],
},
},
});
metadataRegistryStub.restore();
});

test('Return undefined if DASHBOARD_CROSS_FILTERS feature flag is disabled', () => {
// @ts-ignore
global.featureFlags = {
[FeatureFlag.DASHBOARD_CROSS_FILTERS]: false,
};
expect(
getCrossFiltersConfiguration(
DASHBOARD_LAYOUT,
INITIAL_CHART_CONFIG,
CHARTS,
),
).toEqual(undefined);
});
Loading

0 comments on commit 7196e87

Please sign in to comment.