refactor: Removes the deprecated DASHBOARD_NATIVE_FILTERS feature flag (#26329)

This commit is contained in:
Michael S. Molina 2024-01-19 17:07:38 -03:00 committed by GitHub
parent 3acda145f1
commit 69b57016b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 110 additions and 289 deletions

View File

@ -84,7 +84,6 @@ These features flags currently default to True and **will be removed in a future
[//]: # "PLEASE KEEP THE LIST SORTED ALPHABETICALLY"
- DASHBOARD_CROSS_FILTERS
- DASHBOARD_NATIVE_FILTERS
- ENABLE_JAVASCRIPT_CONTROLS
- GENERIC_CHART_AXES
- KV_STORE

View File

@ -43,6 +43,7 @@ assists people when migrating to a new version.
- [26637](https://github.com/apache/superset/issues/26637): Sets the `DRILL_BY` feature flag to `True` by default given that the feature has been tested for a while and reached a stable state.
- [26462](https://github.com/apache/superset/issues/26462): Removes the Profile feature given that it's not actively maintained and not widely used.
- [26377](https://github.com/apache/superset/pull/26377): Removes the deprecated Redirect API that supported short URLs used before the permalink feature.
- [26329](https://github.com/apache/superset/issues/26329): Removes the deprecated `DASHBOARD_NATIVE_FILTERS` feature flag. The previous value of the feature flag was `True` and now the feature is permanently enabled.
### Potential Downtime

View File

@ -29,7 +29,6 @@ export enum FeatureFlag {
CONFIRM_DASHBOARD_DIFF = 'CONFIRM_DASHBOARD_DIFF',
/** @deprecated */
DASHBOARD_CROSS_FILTERS = 'DASHBOARD_CROSS_FILTERS',
DASHBOARD_NATIVE_FILTERS = 'DASHBOARD_NATIVE_FILTERS',
DASHBOARD_VIRTUALIZATION = 'DASHBOARD_VIRTUALIZATION',
DASHBOARD_RBAC = 'DASHBOARD_RBAC',
DATAPANEL_CLOSED_BY_DEFAULT = 'DATAPANEL_CLOSED_BY_DEFAULT',

View File

@ -21,26 +21,22 @@ import {
ControlSetItem,
ControlSetRow,
} from '@superset-ui/chart-controls';
import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core';
import { t } from '@superset-ui/core';
import { PAGE_SIZE_OPTIONS } from '../../consts';
export const serverPaginationControlSetRow: ControlSetRow =
isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS) ||
isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS)
? [
{
name: 'server_pagination',
config: {
type: 'CheckboxControl',
label: t('Server pagination'),
description: t(
'Enable server side pagination of results (experimental feature)',
),
default: false,
},
},
]
: [];
export const serverPaginationControlSetRow: ControlSetRow = [
{
name: 'server_pagination',
config: {
type: 'CheckboxControl',
label: t('Server pagination'),
description: t(
'Enable server side pagination of results (experimental feature)',
),
default: false,
},
},
];
export const serverPageLengthControlSetItem: ControlSetItem = {
name: 'server_page_length',

View File

@ -21,11 +21,9 @@ import React from 'react';
import {
ChartDataResponseResult,
ensureIsArray,
FeatureFlag,
GenericDataType,
hasGenericChartAxes,
isAdhocColumn,
isFeatureEnabled,
isPhysicalColumn,
QueryFormColumn,
QueryMode,
@ -290,22 +288,19 @@ const config: ControlPanelConfig = {
},
},
],
isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS) ||
isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS)
? [
{
name: 'server_pagination',
config: {
type: 'CheckboxControl',
label: t('Server pagination'),
description: t(
'Enable server side pagination of results (experimental feature)',
),
default: false,
},
},
]
: [],
[
{
name: 'server_pagination',
config: {
type: 'CheckboxControl',
label: t('Server pagination'),
description: t(
'Enable server side pagination of results (experimental feature)',
),
default: false,
},
},
],
[
{
name: 'row_limit',

View File

@ -20,7 +20,6 @@ import React from 'react';
import fetchMock from 'fetch-mock';
import { render } from 'spec/helpers/testing-library';
import { fireEvent, within } from '@testing-library/react';
import * as uiCore from '@superset-ui/core';
import DashboardBuilder from 'src/dashboard/components/DashboardBuilder/DashboardBuilder';
import useStoredSidebarWidth from 'src/components/ResizableSidebar/useStoredSidebarWidth';
import {
@ -247,35 +246,20 @@ describe('DashboardBuilder', () => {
expect(await findByAltText('Loading...')).toBeVisible();
});
describe('when nativeFiltersEnabled', () => {
let isFeatureEnabledMock: jest.MockInstance<boolean, [string]>;
beforeAll(() => {
isFeatureEnabledMock = jest
.spyOn(uiCore, 'isFeatureEnabled')
.mockImplementation(
flag => flag === uiCore.FeatureFlag.DASHBOARD_NATIVE_FILTERS,
);
});
afterAll(() => {
isFeatureEnabledMock.mockRestore();
});
it('should set FilterBar width by useStoredSidebarWidth', () => {
const expectedValue = 200;
const setter = jest.fn();
(useStoredSidebarWidth as jest.Mock).mockImplementation(() => [
expectedValue,
setter,
]);
const { getByTestId } = setup({
dashboardInfo: {
...mockState.dashboardInfo,
dash_edit_perm: true,
},
});
const filterbar = getByTestId('dashboard-filters-panel');
expect(filterbar).toHaveStyleRule('width', `${expectedValue}px`);
it('should set FilterBar width by useStoredSidebarWidth', () => {
const expectedValue = 200;
const setter = jest.fn();
(useStoredSidebarWidth as jest.Mock).mockImplementation(() => [
expectedValue,
setter,
]);
const { getByTestId } = setup({
dashboardInfo: {
...mockState.dashboardInfo,
dash_edit_perm: true,
},
});
const filterbar = getByTestId('dashboard-filters-panel');
expect(filterbar).toHaveStyleRule('width', `${expectedValue}px`);
});
});

View File

@ -21,11 +21,9 @@
import React, { FC, useCallback, useEffect, useMemo, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
FeatureFlag,
Filter,
Filters,
getCategoricalSchemeRegistry,
isFeatureEnabled,
SupersetClient,
useComponentDidUpdate,
} from '@superset-ui/core';
@ -104,10 +102,7 @@ const DashboardContainer: FC<DashboardContainerProps> = ({ topLevelTabs }) => {
}, [dashboardLayout, directPathToChild]);
useEffect(() => {
if (
!isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS) ||
nativeFilterScopes.length === 0
) {
if (nativeFilterScopes.length === 0) {
return;
}
const scopes = nativeFilterScopes.map(filterScope => {

View File

@ -17,7 +17,6 @@
* under the License.
*/
import { useSelector } from 'react-redux';
import { isFeatureEnabled, FeatureFlag } from '@superset-ui/core';
import { useCallback, useEffect, useState } from 'react';
import { URL_PARAMS } from 'src/constants';
import { getUrlParam } from 'src/utils/urlUtils';
@ -42,8 +41,7 @@ export const useNativeFilters = () => {
);
const nativeFiltersEnabled =
isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS) &&
(canEdit || (!canEdit && filterValues.length !== 0));
canEdit || (!canEdit && filterValues.length !== 0);
const requiredFirstFilter = filterValues.filter(
filter => filter.requiredFirst,

View File

@ -133,10 +133,6 @@ describe('FiltersBadge', () => {
});
it('shows the indicator when filters have been applied', () => {
// @ts-ignore
global.featureFlags = {
[SupersetUI.FeatureFlag.DASHBOARD_NATIVE_FILTERS]: true,
};
const store = getMockStoreWithNativeFilters();
// start with basic dashboard state, dispatch an event to simulate query completion
store.dispatch({

View File

@ -24,12 +24,8 @@ import userEvent from '@testing-library/user-event';
import fetchMock from 'fetch-mock';
import { HeaderDropdownProps } from 'src/dashboard/components/Header/types';
import injectCustomCss from 'src/dashboard/util/injectCustomCss';
import { FeatureFlag } from '@superset-ui/core';
import * as uiCore from '@superset-ui/core';
import HeaderActionsDropdown from '.';
let isFeatureEnabledMock: jest.MockInstance<boolean, [feature: FeatureFlag]>;
const createProps = () => ({
addSuccessToast: jest.fn(),
addDangerToast: jest.fn(),
@ -135,7 +131,7 @@ test('should render the menu items', async () => {
test('should render the menu items in edit mode', async () => {
setup(editModeOnProps);
expect(screen.getAllByRole('menuitem')).toHaveLength(5);
expect(screen.getAllByRole('menuitem')).toHaveLength(4);
expect(screen.getByText('Set auto-refresh interval')).toBeInTheDocument();
expect(screen.getByText('Edit properties')).toBeInTheDocument();
expect(screen.getByText('Edit CSS')).toBeInTheDocument();
@ -150,56 +146,14 @@ test('should render the menu items in Embedded mode', async () => {
expect(screen.getByText('Set auto-refresh interval')).toBeInTheDocument();
});
describe('with native filters feature flag disabled', () => {
beforeAll(() => {
isFeatureEnabledMock = jest
.spyOn(uiCore, 'isFeatureEnabled')
.mockImplementation(
(featureFlag: FeatureFlag) =>
featureFlag !== FeatureFlag.DASHBOARD_NATIVE_FILTERS,
);
});
afterAll(() => {
// @ts-ignore
isFeatureEnabledMock.restore();
});
it('should render filter mapping in edit mode if explicit filter scopes undefined', async () => {
setup(editModeOnProps);
expect(screen.getByText('Set filter mapping')).toBeInTheDocument();
});
it('should render filter mapping in edit mode if explicit filter scopes defined', async () => {
setup(editModeOnWithFilterScopesProps);
expect(screen.getByText('Set filter mapping')).toBeInTheDocument();
});
test('should not render filter mapping in edit mode if explicit filter scopes undefined', async () => {
setup(editModeOnProps);
expect(screen.queryByText('Set filter mapping')).not.toBeInTheDocument();
});
describe('with native filters feature flag enabled', () => {
beforeAll(() => {
isFeatureEnabledMock = jest
.spyOn(uiCore, 'isFeatureEnabled')
.mockImplementation(
(featureFlag: FeatureFlag) =>
featureFlag === FeatureFlag.DASHBOARD_NATIVE_FILTERS,
);
});
afterAll(() => {
// @ts-ignore
isFeatureEnabledMock.restore();
});
it('should not render filter mapping in edit mode if explicit filter scopes undefined', async () => {
setup(editModeOnProps);
expect(screen.queryByText('Set filter mapping')).not.toBeInTheDocument();
});
it('should render filter mapping in edit mode if explicit filter scopes defined', async () => {
setup(editModeOnWithFilterScopesProps);
expect(screen.getByText('Set filter mapping')).toBeInTheDocument();
});
test('should render filter mapping in edit mode if explicit filter scopes defined', async () => {
setup(editModeOnWithFilterScopesProps);
expect(screen.getByText('Set filter mapping')).toBeInTheDocument();
});
test('should show the share actions', async () => {

View File

@ -19,12 +19,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { isEmpty } from 'lodash';
import {
isFeatureEnabled,
FeatureFlag,
SupersetClient,
t,
} from '@superset-ui/core';
import { SupersetClient, t } from '@superset-ui/core';
import { Menu } from 'src/components/Menu';
import { URL_PARAMS } from 'src/constants';
import ShareMenuItems from 'src/dashboard/components/menu/ShareMenuItems';
@ -361,18 +356,14 @@ class HeaderActionsDropdown extends React.PureComponent {
</Menu>
)
) : null}
{editMode &&
!(
isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS) &&
isEmpty(dashboardInfo?.metadata?.filter_scopes)
) && (
<Menu.Item key={MENU_KEYS.SET_FILTER_MAPPING}>
<FilterScopeModal
className="m-r-5"
triggerNode={t('Set filter mapping')}
/>
</Menu.Item>
)}
{editMode && !isEmpty(dashboardInfo?.metadata?.filter_scopes) && (
<Menu.Item key={MENU_KEYS.SET_FILTER_MAPPING}>
<FilterScopeModal
className="m-r-5"
triggerNode={t('Set filter mapping')}
/>
</Menu.Item>
)}
<Menu.Item key={MENU_KEYS.AUTOREFRESH_MODAL}>
<RefreshIntervalModal

View File

@ -23,7 +23,7 @@ import userEvent from '@testing-library/user-event';
import { stateWithoutNativeFilters } from 'spec/fixtures/mockStore';
import * as mockCore from '@superset-ui/core';
import { testWithId } from 'src/utils/testUtils';
import { FeatureFlag, Preset } from '@superset-ui/core';
import { Preset } from '@superset-ui/core';
import { TimeFilterPlugin, SelectFilterPlugin } from 'src/filters/components';
import fetchMock from 'fetch-mock';
import { FilterBarOrientation } from 'src/dashboard/types';
@ -73,11 +73,6 @@ const getModalTestId = testWithId<string>(FILTERS_CONFIG_MODAL_TEST_ID, true);
const FILTER_NAME = 'Time filter 1';
// @ts-ignore
global.featureFlags = {
[FeatureFlag.DASHBOARD_NATIVE_FILTERS]: true,
};
const addFilterFlow = async () => {
// open filter config modal
userEvent.click(screen.getByTestId(getTestId('collapsable')));
@ -291,10 +286,6 @@ describe('FilterBar', () => {
});
it('create filter and apply it flow', async () => {
// @ts-ignore
global.featureFlags = {
[FeatureFlag.DASHBOARD_NATIVE_FILTERS]: true,
};
renderWrapper(openedBarProps, stateWithoutNativeFilters);
expect(screen.getByTestId(getTestId('apply-button'))).toBeDisabled();

View File

@ -212,23 +212,20 @@ const FilterControls: FC<FilterControlsProps> = ({
selectedCrossFilters.at(-1),
),
}));
if (isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS)) {
const nativeFiltersInScope = filtersInScope.map((filter, index) => ({
id: filter.id,
element: (
<div
className="filter-item-wrapper"
css={css`
flex-shrink: 0;
`}
>
{renderer(filter, index)}
</div>
),
}));
return [...crossFilters, ...nativeFiltersInScope];
}
return [...crossFilters];
const nativeFiltersInScope = filtersInScope.map((filter, index) => ({
id: filter.id,
element: (
<div
className="filter-item-wrapper"
css={css`
flex-shrink: 0;
`}
>
{renderer(filter, index)}
</div>
),
}));
return [...crossFilters, ...nativeFiltersInScope];
}, [filtersInScope, renderer, rendererCrossFilter, selectedCrossFilters]);
const renderHorizontalContent = () => (

View File

@ -17,14 +17,7 @@
* under the License.
*/
/* eslint-disable no-param-reassign */
import {
FeatureFlag,
css,
isFeatureEnabled,
styled,
t,
useTheme,
} from '@superset-ui/core';
import { css, styled, t, useTheme } from '@superset-ui/core';
import React, { FC, useMemo } from 'react';
import Icons from 'src/components/Icons';
import Button from 'src/components/Button';
@ -124,7 +117,7 @@ const Header: FC<HeaderProps> = ({ toggleFiltersBar }) => {
<Icons.Expand iconColor={theme.colors.grayscale.base} />
</HeaderButton>
</TitleArea>
{canEdit && isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS) && (
{canEdit && (
<AddFiltersButtonContainer>
<FilterConfigurationLink
dashboardId={dashboardId}

View File

@ -17,7 +17,7 @@
* under the License.
*/
import React, { useMemo } from 'react';
import React from 'react';
import {
DataMaskStateWithId,
FeatureFlag,
@ -129,12 +129,6 @@ const HorizontalFilterBar: React.FC<HorizontalBarProps> = ({
: [];
const hasFilters = filterValues.length > 0 || selectedCrossFilters.length > 0;
const actionsElement = useMemo(
() =>
isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS) ? actions : null,
[actions],
);
return (
<HorizontalBar {...getFilterBarTestId()}>
<HorizontalBarContent>
@ -143,7 +137,7 @@ const HorizontalFilterBar: React.FC<HorizontalBarProps> = ({
) : (
<>
<FilterBarSettings />
{canEdit && isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS) && (
{canEdit && (
<FiltersLinkContainer hasFilters={hasFilters}>
<FilterConfigurationLink
dashboardId={dashboardId}
@ -164,7 +158,7 @@ const HorizontalFilterBar: React.FC<HorizontalBarProps> = ({
onFilterSelectionChange={onSelectionChange}
/>
)}
{actionsElement}
{actions}
</>
)}
</HorizontalBarContent>

View File

@ -16,8 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { FeatureFlag, NativeFilterType } from '@superset-ui/core';
import { NativeFilterType } from '@superset-ui/core';
import React from 'react';
import { render, screen, waitFor } from 'spec/helpers/testing-library';
import HorizontalBar from './Horizontal';
@ -32,11 +31,6 @@ const defaultProps = {
onSelectionChange: jest.fn(),
};
// @ts-ignore
global.featureFlags = {
[FeatureFlag.DASHBOARD_NATIVE_FILTERS]: true,
};
const renderWrapper = (overrideProps?: Record<string, any>) =>
waitFor(() =>
render(<HorizontalBar {...defaultProps} {...overrideProps} />, {

View File

@ -196,12 +196,6 @@ const VerticalFilterBar: React.FC<VerticalBarProps> = ({
[],
);
const actionsElement = useMemo(
() =>
isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS) ? actions : null,
[actions],
);
return (
<FilterBarScrollContext.Provider value={isScrolling}>
<BarWrapper
@ -234,12 +228,11 @@ const VerticalFilterBar: React.FC<VerticalBarProps> = ({
<div css={tabPaneStyle} onScroll={onScroll}>
<>
{crossFilters}
{isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS) &&
filterControls}
{filterControls}
</>
</div>
)}
{actionsElement}
{actions}
</Bar>
</BarWrapper>
</FilterBarScrollContext.Provider>

View File

@ -358,34 +358,31 @@ export const selectNativeIndicatorsForChart = (
return cachedNativeIndicatorsForChart[chartId];
}
let nativeFilterIndicators: any = [];
if (isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS)) {
nativeFilterIndicators =
nativeFilters &&
Object.values(nativeFilters)
.filter(
nativeFilter =>
nativeFilter.type === NativeFilterType.NATIVE_FILTER &&
nativeFilter.chartsInScope?.includes(chartId),
)
.map(nativeFilter => {
const column = nativeFilter.targets?.[0]?.column?.name;
const filterState = dataMask[nativeFilter.id]?.filterState;
const label = extractLabel(filterState);
return {
const nativeFilterIndicators =
nativeFilters &&
Object.values(nativeFilters)
.filter(
nativeFilter =>
nativeFilter.type === NativeFilterType.NATIVE_FILTER &&
nativeFilter.chartsInScope?.includes(chartId),
)
.map(nativeFilter => {
const column = nativeFilter.targets?.[0]?.column?.name;
const filterState = dataMask[nativeFilter.id]?.filterState;
const label = extractLabel(filterState);
return {
column,
name: nativeFilter.name,
path: [nativeFilter.id],
status: getStatus({
label,
column,
name: nativeFilter.name,
path: [nativeFilter.id],
status: getStatus({
label,
column,
rejectedColumns,
appliedColumns,
}),
value: label,
};
});
}
rejectedColumns,
appliedColumns,
}),
value: label,
};
});
let crossFilterIndicators: any = [];
if (isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS)) {

View File

@ -55,41 +55,7 @@ describe('nativeFilterGate', () => {
});
});
describe('with only native filters feature flag enabled', () => {
beforeAll(() => {
isFeatureEnabledMock = jest
.spyOn(uiCore, 'isFeatureEnabled')
.mockImplementation(
(featureFlag: FeatureFlag) =>
featureFlag === FeatureFlag.DASHBOARD_NATIVE_FILTERS,
);
});
afterAll(() => {
// @ts-ignore
isFeatureEnabledMock.restore();
});
it('should return true for regular chart', () => {
expect(nativeFilterGate([])).toEqual(true);
});
it('should return true for cross filter chart', () => {
expect(nativeFilterGate([Behavior.INTERACTIVE_CHART])).toEqual(true);
});
it('should return false for native filter chart with cross filter support', () => {
expect(
nativeFilterGate([Behavior.NATIVE_FILTER, Behavior.INTERACTIVE_CHART]),
).toEqual(false);
});
it('should return false for native filter behavior', () => {
expect(nativeFilterGate([Behavior.NATIVE_FILTER])).toEqual(false);
});
});
describe('with native filters and experimental feature flag enabled', () => {
describe('with cross filters and experimental feature flag enabled', () => {
beforeAll(() => {
isFeatureEnabledMock = jest
.spyOn(uiCore, 'isFeatureEnabled')

View File

@ -16,13 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import {
DataMask,
isFeatureEnabled,
FeatureFlag,
FilterConfiguration,
Filters,
} from '@superset-ui/core';
import { DataMask, FilterConfiguration, Filters } from '@superset-ui/core';
import { getInitialDataMask } from './reducer';
export const CLEAR_DATA_MASK_STATE = 'CLEAR_DATA_MASK_STATE';
@ -73,14 +67,10 @@ export function updateDataMask(
filterId: string | number,
dataMask: DataMask,
): UpdateDataMask {
// Only apply data mask if one of the relevant features is enabled
const isFeatureFlagActive =
isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS) ||
isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS);
return {
type: UPDATE_DATA_MASK,
filterId,
dataMask: isFeatureFlagActive ? dataMask : {},
dataMask,
};
}

View File

@ -428,7 +428,6 @@ DEFAULT_FEATURE_FLAGS: dict[str, bool] = {
"LISTVIEWS_DEFAULT_CARD_VIEW": False,
# When True, this escapes HTML (rather than rendering it) in Markdown components
"ESCAPE_MARKDOWN_HTML": False,
"DASHBOARD_NATIVE_FILTERS": True, # deprecated
"DASHBOARD_CROSS_FILTERS": True, # deprecated
"DASHBOARD_VIRTUALIZATION": True,
"GLOBAL_ASYNC_QUERIES": False,

View File

@ -69,7 +69,6 @@ FEATURE_FLAGS = {
"SHARE_QUERIES_VIA_KV_STORE": True,
"ENABLE_TEMPLATE_PROCESSING": True,
"ALERT_REPORTS": True,
"DASHBOARD_NATIVE_FILTERS": True,
"DRILL_TO_DETAIL": True,
"DRILL_BY": True,
"HORIZONTAL_FILTER_BAR": True,