<>
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx
index 61014a6e0f8dc..f41f9bc3aaff6 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx
@@ -304,7 +304,6 @@ const FilterBar: React.FC
= ({
filtersOpen={verticalConfig.filtersOpen}
filterValues={filterValues}
isInitialized={isInitialized}
- isDisabled={isApplyDisabled}
height={verticalConfig.height}
offset={verticalConfig.offset}
onSelectionChange={handleFilterSelectionChange}
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts
index dde47514768ef..76183403d89c2 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts
@@ -24,17 +24,11 @@ import {
DataMaskWithId,
Filter,
Filters,
- FilterSets as FilterSetsType,
} from '@superset-ui/core';
import { useEffect, useMemo, useState } from 'react';
import { ChartsState, RootState } from 'src/dashboard/types';
import { NATIVE_FILTER_PREFIX } from '../FiltersConfigModal/utils';
-export const useFilterSets = () =>
- useSelector(
- state => state.nativeFilters.filterSets || {},
- );
-
export const useFilters = () => {
const preselectedNativeFilters = useSelector(
state => state.dashboardState?.preselectNativeFilters,
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/types.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/types.ts
index e146690a99c14..828badf50e019 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/types.ts
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/types.ts
@@ -58,11 +58,4 @@ export type HorizontalBarProps = CommonFiltersBarProps & {
export type VerticalBarProps = Omit &
CommonFiltersBarProps &
- VerticalBarConfig & {
- isDisabled: boolean;
- };
-
-export enum TabIds {
- AllFilters = 'allFilters',
- FilterSets = 'filterSets',
-}
+ VerticalBarConfig;
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/utils.ts b/superset-frontend/src/dashboard/components/nativeFilters/utils.ts
index 7194e2435f3d6..9e1108366d3ef 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/utils.ts
+++ b/superset-frontend/src/dashboard/components/nativeFilters/utils.ts
@@ -28,6 +28,7 @@ import {
Filter,
getChartMetadataRegistry,
QueryFormData,
+ t,
} from '@superset-ui/core';
import { DashboardLayout } from 'src/dashboard/types';
import extractUrlParams from 'src/dashboard/util/extractUrlParams';
@@ -234,3 +235,21 @@ export const findTabsWithChartsInScope = (
}
return tabsInScope;
};
+
+export const getFilterValueForDisplay = (
+ value?: string[] | null | string | number | object,
+): string => {
+ if (value === null || value === undefined) {
+ return '';
+ }
+ if (typeof value === 'string' || typeof value === 'number') {
+ return `${value}`;
+ }
+ if (Array.isArray(value)) {
+ return value.join(', ');
+ }
+ if (typeof value === 'object') {
+ return JSON.stringify(value);
+ }
+ return t('Unknown value');
+};
diff --git a/superset-frontend/src/dashboard/containers/DashboardPage.tsx b/superset-frontend/src/dashboard/containers/DashboardPage.tsx
index 7dd618850e9c8..761843eb858bb 100644
--- a/superset-frontend/src/dashboard/containers/DashboardPage.tsx
+++ b/superset-frontend/src/dashboard/containers/DashboardPage.tsx
@@ -21,9 +21,7 @@ import { Global } from '@emotion/react';
import { useHistory } from 'react-router-dom';
import {
CategoricalColorNamespace,
- FeatureFlag,
getSharedLabelColor,
- isFeatureEnabled,
SharedLabelColorSource,
t,
useTheme,
@@ -44,7 +42,6 @@ import setupPlugins from 'src/setup/setupPlugins';
import { LocalStorageKeys, setItem } from 'src/utils/localStorageHelpers';
import { URL_PARAMS } from 'src/constants';
import { getUrlParam } from 'src/utils/urlUtils';
-import { getFilterSets } from 'src/dashboard/actions/nativeFilters';
import { setDatasetsStatus } from 'src/dashboard/actions/dashboardState';
import {
getFilterValue,
@@ -106,11 +103,6 @@ export const DashboardPage: FC = ({ idOrSlug }: PageProps) => {
const readyToRender = Boolean(dashboard && charts);
const { dashboard_title, css, metadata, id = 0 } = dashboard || {};
- // Filter sets depend on native filters
- const filterSetEnabled =
- isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS_SET) &&
- isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS);
-
useEffect(() => {
// mark tab id as redundant when user closes browser tab - a new id will be
// generated next time user opens a dashboard and the old one won't be reused
@@ -160,10 +152,6 @@ export const DashboardPage: FC = ({ idOrSlug }: PageProps) => {
if (readyToRender) {
if (!isDashboardHydrated.current) {
isDashboardHydrated.current = true;
- if (filterSetEnabled) {
- // only initialize filterset once
- dispatch(getFilterSets(id));
- }
}
dispatch(
hydrateDashboard({
diff --git a/superset-frontend/src/dashboard/fixtures/mockNativeFilters.ts b/superset-frontend/src/dashboard/fixtures/mockNativeFilters.ts
index 9ace211b9b3e6..8d6ded58f14cc 100644
--- a/superset-frontend/src/dashboard/fixtures/mockNativeFilters.ts
+++ b/superset-frontend/src/dashboard/fixtures/mockNativeFilters.ts
@@ -33,14 +33,6 @@ export const mockDataMaskInfo: DataMaskStateWithId = {
};
export const nativeFiltersInfo: NativeFiltersState = {
- filterSets: {
- '1': {
- id: 1,
- name: 'Set name',
- nativeFilters: {},
- dataMask: mockDataMaskInfo,
- },
- },
filters: {
DefaultsID: {
cascadeParentIds: [],
diff --git a/superset-frontend/src/dashboard/reducers/nativeFilters.ts b/superset-frontend/src/dashboard/reducers/nativeFilters.ts
index 3c53473351022..c68ff3c19bd92 100644
--- a/superset-frontend/src/dashboard/reducers/nativeFilters.ts
+++ b/superset-frontend/src/dashboard/reducers/nativeFilters.ts
@@ -20,26 +20,19 @@ import {
AnyFilterAction,
SET_FILTER_CONFIG_COMPLETE,
SET_IN_SCOPE_STATUS_OF_FILTERS,
- SET_FILTER_SETS_COMPLETE,
SET_FOCUSED_NATIVE_FILTER,
UNSET_FOCUSED_NATIVE_FILTER,
SET_HOVERED_NATIVE_FILTER,
UNSET_HOVERED_NATIVE_FILTER,
UPDATE_CASCADE_PARENT_IDS,
} from 'src/dashboard/actions/nativeFilters';
-import {
- FilterSet,
- FilterConfiguration,
- NativeFiltersState,
-} from '@superset-ui/core';
+import { FilterConfiguration, NativeFiltersState } from '@superset-ui/core';
import { HYDRATE_DASHBOARD } from '../actions/hydrate';
export function getInitialState({
- filterSetsConfig,
filterConfig,
state: prevState,
}: {
- filterSetsConfig?: FilterSet[];
filterConfig?: FilterConfiguration;
state?: NativeFiltersState;
}): NativeFiltersState {
@@ -55,17 +48,6 @@ export function getInitialState({
} else {
state.filters = prevState?.filters ?? {};
}
-
- if (filterSetsConfig) {
- const filterSets = {};
- filterSetsConfig.forEach(filtersSet => {
- const { id } = filtersSet;
- filterSets[id] = filtersSet;
- });
- state.filterSets = filterSets;
- } else {
- state.filterSets = prevState?.filterSets ?? {};
- }
state.focusedFilterId = undefined;
return state as NativeFiltersState;
}
@@ -73,7 +55,6 @@ export function getInitialState({
export default function nativeFilterReducer(
state: NativeFiltersState = {
filters: {},
- filterSets: {},
},
action: AnyFilterAction,
) {
@@ -81,19 +62,12 @@ export default function nativeFilterReducer(
case HYDRATE_DASHBOARD:
return {
filters: action.data.nativeFilters.filters,
- filterSets: action.data.nativeFilters.filterSets,
};
case SET_FILTER_CONFIG_COMPLETE:
case SET_IN_SCOPE_STATUS_OF_FILTERS:
return getInitialState({ filterConfig: action.filterConfig, state });
- case SET_FILTER_SETS_COMPLETE:
- return getInitialState({
- filterSetsConfig: action.filterSets,
- state,
- });
-
case SET_FOCUSED_NATIVE_FILTER:
return {
...state,
diff --git a/superset-frontend/src/dashboard/reducers/types.ts b/superset-frontend/src/dashboard/reducers/types.ts
index 56322670c1208..cdca6bbd4fdc5 100644
--- a/superset-frontend/src/dashboard/reducers/types.ts
+++ b/superset-frontend/src/dashboard/reducers/types.ts
@@ -18,7 +18,6 @@
*/
import componentTypes from 'src/dashboard/util/componentTypes';
-import { JsonObject } from '@superset-ui/core';
export enum Scoping {
All = 'All',
@@ -80,16 +79,3 @@ export type LayoutItem = {
width: number;
};
};
-
-export type FilterSetFullData = {
- changed_by_fk: string | null;
- changed_on: string | null;
- created_by_fk: string | null;
- created_on: string | null;
- dashboard_id: number;
- description: string | null;
- name: string;
- owner_id: number;
- owner_type: string;
- params: JsonObject;
-};
diff --git a/superset/commands/dashboard/filter_set/__init__.py b/superset/commands/dashboard/filter_set/__init__.py
deleted file mode 100644
index 13a83393a9124..0000000000000
--- a/superset/commands/dashboard/filter_set/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# 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.
diff --git a/superset/commands/dashboard/filter_set/base.py b/superset/commands/dashboard/filter_set/base.py
deleted file mode 100644
index 24abe2509acba..0000000000000
--- a/superset/commands/dashboard/filter_set/base.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# 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 logging
-from typing import cast, Optional
-
-from flask_appbuilder.models.sqla import Model
-
-from superset import security_manager
-from superset.commands.dashboard.exceptions import DashboardNotFoundError
-from superset.commands.dashboard.filter_set.exceptions import (
- FilterSetForbiddenError,
- FilterSetNotFoundError,
-)
-from superset.common.not_authorized_object import NotAuthorizedException
-from superset.daos.dashboard import DashboardDAO
-from superset.dashboards.filter_sets.consts import USER_OWNER_TYPE
-from superset.models.dashboard import Dashboard
-from superset.models.filter_set import FilterSet
-from superset.utils.core import get_user_id
-
-logger = logging.getLogger(__name__)
-
-
-class BaseFilterSetCommand:
- # pylint: disable=C0103
- _dashboard: Dashboard
- _filter_set_id: Optional[int]
- _filter_set: Optional[FilterSet]
-
- def __init__(self, dashboard_id: int):
- self._dashboard_id = dashboard_id
-
- def run(self) -> Model:
- pass
-
- def _validate_filterset_dashboard_exists(self) -> None:
- self._dashboard = DashboardDAO.get_by_id_or_slug(str(self._dashboard_id))
- if not self._dashboard:
- raise DashboardNotFoundError()
-
- def validate_exist_filter_use_cases_set(self) -> None: # pylint: disable=C0103
- self._validate_filter_set_exists_and_set_when_exists()
- self.check_ownership()
-
- def _validate_filter_set_exists_and_set_when_exists(self) -> None:
- self._filter_set = self._dashboard.filter_sets.get(
- cast(int, self._filter_set_id), None
- )
- if not self._filter_set:
- raise FilterSetNotFoundError(str(self._filter_set_id))
-
- def check_ownership(self) -> None:
- try:
- if not security_manager.is_admin():
- filter_set: FilterSet = cast(FilterSet, self._filter_set)
- if filter_set.owner_type == USER_OWNER_TYPE:
- if get_user_id() != filter_set.owner_id:
- raise FilterSetForbiddenError(
- str(self._filter_set_id),
- "The user is not the owner of the filter_set",
- )
- elif not security_manager.is_owner(self._dashboard):
- raise FilterSetForbiddenError(
- str(self._filter_set_id),
- "The user is not an owner of the filter_set's dashboard",
- )
- except NotAuthorizedException as err:
- raise FilterSetForbiddenError(
- str(self._filter_set_id),
- "user not authorized to access the filterset",
- ) from err
- except FilterSetForbiddenError as err:
- raise err
diff --git a/superset/commands/dashboard/filter_set/create.py b/superset/commands/dashboard/filter_set/create.py
deleted file mode 100644
index 49edb3172e28a..0000000000000
--- a/superset/commands/dashboard/filter_set/create.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# 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 logging
-from typing import Any
-
-from flask_appbuilder.models.sqla import Model
-
-from superset import security_manager
-from superset.commands.dashboard.filter_set.base import BaseFilterSetCommand
-from superset.commands.dashboard.filter_set.exceptions import (
- DashboardIdInconsistencyError,
- FilterSetCreateFailedError,
- UserIsNotDashboardOwnerError,
-)
-from superset.daos.dashboard import FilterSetDAO
-from superset.dashboards.filter_sets.consts import (
- DASHBOARD_ID_FIELD,
- DASHBOARD_OWNER_TYPE,
- OWNER_ID_FIELD,
- OWNER_TYPE_FIELD,
-)
-from superset.utils.core import get_user_id
-
-logger = logging.getLogger(__name__)
-
-
-class CreateFilterSetCommand(BaseFilterSetCommand):
- # pylint: disable=C0103
- def __init__(self, dashboard_id: int, data: dict[str, Any]):
- super().__init__(dashboard_id)
- self._properties = data.copy()
-
- def run(self) -> Model:
- self.validate()
- self._properties[DASHBOARD_ID_FIELD] = self._dashboard.id
- return FilterSetDAO.create(attributes=self._properties, commit=True)
-
- def validate(self) -> None:
- self._validate_filterset_dashboard_exists()
- if self._properties[OWNER_TYPE_FIELD] == DASHBOARD_OWNER_TYPE:
- self._validate_owner_id_is_dashboard_id()
- self._validate_user_is_the_dashboard_owner()
- else:
- self._validate_owner_id_exists()
-
- def _validate_owner_id_exists(self) -> None:
- owner_id = self._properties[OWNER_ID_FIELD]
- if not (get_user_id() == owner_id or security_manager.get_user_by_id(owner_id)):
- raise FilterSetCreateFailedError(
- str(self._dashboard_id), "owner_id does not exists"
- )
-
- def _validate_user_is_the_dashboard_owner(self) -> None:
- if not security_manager.is_owner(self._dashboard):
- raise UserIsNotDashboardOwnerError(str(self._dashboard_id))
-
- def _validate_owner_id_is_dashboard_id(self) -> None:
- if (
- self._properties.get(OWNER_ID_FIELD, self._dashboard_id)
- != self._dashboard_id
- ):
- raise DashboardIdInconsistencyError(str(self._dashboard_id))
diff --git a/superset/commands/dashboard/filter_set/delete.py b/superset/commands/dashboard/filter_set/delete.py
deleted file mode 100644
index ce2bf6fce49cf..0000000000000
--- a/superset/commands/dashboard/filter_set/delete.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# 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 logging
-
-from superset.commands.dashboard.filter_set.base import BaseFilterSetCommand
-from superset.commands.dashboard.filter_set.exceptions import (
- FilterSetDeleteFailedError,
- FilterSetForbiddenError,
- FilterSetNotFoundError,
-)
-from superset.daos.dashboard import FilterSetDAO
-from superset.daos.exceptions import DAODeleteFailedError
-
-logger = logging.getLogger(__name__)
-
-
-class DeleteFilterSetCommand(BaseFilterSetCommand):
- def __init__(self, dashboard_id: int, filter_set_id: int):
- super().__init__(dashboard_id)
- self._filter_set_id = filter_set_id
-
- def run(self) -> None:
- self.validate()
- assert self._filter_set
-
- try:
- FilterSetDAO.delete([self._filter_set])
- except DAODeleteFailedError as err:
- raise FilterSetDeleteFailedError(str(self._filter_set_id), "") from err
-
- def validate(self) -> None:
- self._validate_filterset_dashboard_exists()
- try:
- self.validate_exist_filter_use_cases_set()
- except FilterSetNotFoundError as err:
- if FilterSetDAO.find_by_id(self._filter_set_id): # type: ignore
- raise FilterSetForbiddenError(
- f"the filter-set does not related to dashboard {self._dashboard_id}"
- ) from err
- raise err
diff --git a/superset/commands/dashboard/filter_set/exceptions.py b/superset/commands/dashboard/filter_set/exceptions.py
deleted file mode 100644
index ade0bbbe9090a..0000000000000
--- a/superset/commands/dashboard/filter_set/exceptions.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# 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 Optional
-
-from flask_babel import lazy_gettext as _
-
-from superset.commands.exceptions import (
- CreateFailedError,
- DeleteFailedError,
- ForbiddenError,
- ObjectNotFoundError,
- UpdateFailedError,
-)
-
-
-class FilterSetNotFoundError(ObjectNotFoundError):
- def __init__(
- self, filterset_id: Optional[str] = None, exception: Optional[Exception] = None
- ) -> None:
- super().__init__("FilterSet", filterset_id, exception)
-
-
-class FilterSetCreateFailedError(CreateFailedError):
- base_message = 'CreateFilterSetCommand of dashboard "%s" failed: '
-
- def __init__(
- self, dashboard_id: str, reason: str = "", exception: Optional[Exception] = None
- ) -> None:
- super().__init__((self.base_message % dashboard_id) + reason, exception)
-
-
-class FilterSetUpdateFailedError(UpdateFailedError):
- base_message = 'UpdateFilterSetCommand of filter_set "%s" failed: '
-
- def __init__(
- self, filterset_id: str, reason: str = "", exception: Optional[Exception] = None
- ) -> None:
- super().__init__((self.base_message % filterset_id) + reason, exception)
-
-
-class FilterSetDeleteFailedError(DeleteFailedError):
- base_message = 'DeleteFilterSetCommand of filter_set "%s" failed: '
-
- def __init__(
- self, filterset_id: str, reason: str = "", exception: Optional[Exception] = None
- ) -> None:
- super().__init__((self.base_message % filterset_id) + reason, exception)
-
-
-class UserIsNotDashboardOwnerError(FilterSetCreateFailedError):
- reason = (
- "cannot create dashboard owner filterset based when"
- " the user is not the dashboard owner"
- )
-
- def __init__(
- self, dashboard_id: str, exception: Optional[Exception] = None
- ) -> None:
- super().__init__(dashboard_id, self.reason, exception)
-
-
-class DashboardIdInconsistencyError(FilterSetCreateFailedError):
- reason = (
- "cannot create dashboard owner filterset based when the"
- " ownerid is not the dashboard id"
- )
-
- def __init__(
- self, dashboard_id: str, exception: Optional[Exception] = None
- ) -> None:
- super().__init__(dashboard_id, self.reason, exception)
-
-
-class FilterSetForbiddenError(ForbiddenError):
- message_format = 'Changing FilterSet "{}" is forbidden: {}'
-
- def __init__(
- self, filterset_id: str, reason: str = "", exception: Optional[Exception] = None
- ) -> None:
- super().__init__(_(self.message_format.format(filterset_id, reason)), exception)
diff --git a/superset/commands/dashboard/filter_set/update.py b/superset/commands/dashboard/filter_set/update.py
deleted file mode 100644
index 5ce9f1fea63ac..0000000000000
--- a/superset/commands/dashboard/filter_set/update.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# 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 logging
-from typing import Any
-
-from flask_appbuilder.models.sqla import Model
-
-from superset.commands.dashboard.filter_set.base import BaseFilterSetCommand
-from superset.commands.dashboard.filter_set.exceptions import FilterSetUpdateFailedError
-from superset.daos.dashboard import FilterSetDAO
-from superset.daos.exceptions import DAOUpdateFailedError
-from superset.dashboards.filter_sets.consts import OWNER_ID_FIELD, OWNER_TYPE_FIELD
-
-logger = logging.getLogger(__name__)
-
-
-class UpdateFilterSetCommand(BaseFilterSetCommand):
- def __init__(self, dashboard_id: int, filter_set_id: int, data: dict[str, Any]):
- super().__init__(dashboard_id)
- self._filter_set_id = filter_set_id
- self._properties = data.copy()
-
- def run(self) -> Model:
- try:
- self.validate()
- assert self._filter_set
-
- if (
- OWNER_TYPE_FIELD in self._properties
- and self._properties[OWNER_TYPE_FIELD] == "Dashboard"
- ):
- self._properties[OWNER_ID_FIELD] = self._dashboard_id
- return FilterSetDAO.update(self._filter_set, self._properties, commit=True)
- except DAOUpdateFailedError as err:
- raise FilterSetUpdateFailedError(str(self._filter_set_id), "") from err
-
- def validate(self) -> None:
- self._validate_filterset_dashboard_exists()
- self.validate_exist_filter_use_cases_set()
diff --git a/superset/config.py b/superset/config.py
index 348baef5454af..fcc5fcaecf66e 100644
--- a/superset/config.py
+++ b/superset/config.py
@@ -444,8 +444,6 @@ class D3Format(TypedDict, total=False):
"ESCAPE_MARKDOWN_HTML": False,
"DASHBOARD_NATIVE_FILTERS": True, # deprecated
"DASHBOARD_CROSS_FILTERS": True,
- # Feature is under active development and breaking changes are expected
- "DASHBOARD_NATIVE_FILTERS_SET": False, # deprecated
"DASHBOARD_FILTERS_EXPERIMENTAL": False, # deprecated
"DASHBOARD_VIRTUALIZATION": False,
"GLOBAL_ASYNC_QUERIES": False,
diff --git a/superset/daos/dashboard.py b/superset/daos/dashboard.py
index b98252070dc4f..e0dffa73c3f64 100644
--- a/superset/daos/dashboard.py
+++ b/superset/daos/dashboard.py
@@ -31,21 +31,12 @@
DashboardNotFoundError,
)
from superset.daos.base import BaseDAO
-from superset.dashboards.filter_sets.consts import (
- DASHBOARD_ID_FIELD,
- DESCRIPTION_FIELD,
- JSON_METADATA_FIELD,
- NAME_FIELD,
- OWNER_ID_FIELD,
- OWNER_TYPE_FIELD,
-)
from superset.dashboards.filters import DashboardAccessFilter, is_uuid
from superset.exceptions import SupersetSecurityException
from superset.extensions import db
from superset.models.core import FavStar, FavStarClassName
from superset.models.dashboard import Dashboard, id_or_slug_filter
from superset.models.embedded_dashboard import EmbeddedDashboard
-from superset.models.filter_set import FilterSet
from superset.models.slice import Slice
from superset.utils.core import get_user_id
from superset.utils.dashboard_filter_scopes_converter import copy_filter_scopes
@@ -387,29 +378,3 @@ def create(
At least, until we are ok with more than one embedded item per dashboard.
"""
raise NotImplementedError("Use EmbeddedDashboardDAO.upsert() instead.")
-
-
-class FilterSetDAO(BaseDAO[FilterSet]):
- @classmethod
- def create(
- cls,
- item: FilterSet | None = None,
- attributes: dict[str, Any] | None = None,
- commit: bool = True,
- ) -> FilterSet:
- if not item:
- item = FilterSet()
-
- if attributes:
- setattr(item, NAME_FIELD, attributes[NAME_FIELD])
- setattr(item, JSON_METADATA_FIELD, attributes[JSON_METADATA_FIELD])
- setattr(item, DESCRIPTION_FIELD, attributes.get(DESCRIPTION_FIELD, None))
- setattr(
- item,
- OWNER_ID_FIELD,
- attributes.get(OWNER_ID_FIELD, attributes[DASHBOARD_ID_FIELD]),
- )
- setattr(item, OWNER_TYPE_FIELD, attributes[OWNER_TYPE_FIELD])
- setattr(item, DASHBOARD_ID_FIELD, attributes[DASHBOARD_ID_FIELD])
-
- return super().create(item, commit=commit)
diff --git a/superset/dashboards/filter_sets/__init__.py b/superset/dashboards/filter_sets/__init__.py
deleted file mode 100644
index 13a83393a9124..0000000000000
--- a/superset/dashboards/filter_sets/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# 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.
diff --git a/superset/dashboards/filter_sets/api.py b/superset/dashboards/filter_sets/api.py
deleted file mode 100644
index ee7297ef4c80b..0000000000000
--- a/superset/dashboards/filter_sets/api.py
+++ /dev/null
@@ -1,381 +0,0 @@
-# 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 logging
-from typing import Any, cast
-
-from flask import request, Response
-from flask_appbuilder.api import (
- expose,
- get_list_schema,
- permission_name,
- protect,
- rison,
- safe,
-)
-from flask_appbuilder.models.sqla.interface import SQLAInterface
-from marshmallow import ValidationError
-
-from superset.commands.dashboard.exceptions import DashboardNotFoundError
-from superset.commands.dashboard.filter_set.create import CreateFilterSetCommand
-from superset.commands.dashboard.filter_set.delete import DeleteFilterSetCommand
-from superset.commands.dashboard.filter_set.exceptions import (
- FilterSetCreateFailedError,
- FilterSetDeleteFailedError,
- FilterSetForbiddenError,
- FilterSetNotFoundError,
- FilterSetUpdateFailedError,
- UserIsNotDashboardOwnerError,
-)
-from superset.commands.dashboard.filter_set.update import UpdateFilterSetCommand
-from superset.commands.exceptions import ObjectNotFoundError
-from superset.daos.dashboard import DashboardDAO
-from superset.dashboards.filter_sets.consts import (
- DASHBOARD_FIELD,
- DASHBOARD_ID_FIELD,
- DESCRIPTION_FIELD,
- FILTER_SET_API_PERMISSIONS_NAME,
- JSON_METADATA_FIELD,
- NAME_FIELD,
- OWNER_ID_FIELD,
- OWNER_OBJECT_FIELD,
- OWNER_TYPE_FIELD,
- PARAMS_PROPERTY,
-)
-from superset.dashboards.filter_sets.filters import FilterSetFilter
-from superset.dashboards.filter_sets.schemas import (
- FilterSetPostSchema,
- FilterSetPutSchema,
-)
-from superset.extensions import event_logger
-from superset.models.filter_set import FilterSet
-from superset.views.base_api import (
- BaseSupersetModelRestApi,
- requires_json,
- statsd_metrics,
-)
-
-logger = logging.getLogger(__name__)
-
-
-class FilterSetRestApi(BaseSupersetModelRestApi):
- # pylint: disable=arguments-differ
- include_route_methods = {"get_list", "put", "post", "delete"}
- datamodel = SQLAInterface(FilterSet)
- resource_name = "dashboard"
- class_permission_name = FILTER_SET_API_PERMISSIONS_NAME
- allow_browser_login = True
- csrf_exempt = False
- add_exclude_columns = [
- "id",
- OWNER_OBJECT_FIELD,
- DASHBOARD_FIELD,
- JSON_METADATA_FIELD,
- ]
- add_model_schema = FilterSetPostSchema()
- edit_model_schema = FilterSetPutSchema()
- edit_exclude_columns = [
- "id",
- OWNER_OBJECT_FIELD,
- DASHBOARD_FIELD,
- JSON_METADATA_FIELD,
- ]
- list_columns = [
- "id",
- "created_on",
- "changed_on",
- "created_by_fk",
- "changed_by_fk",
- NAME_FIELD,
- DESCRIPTION_FIELD,
- OWNER_TYPE_FIELD,
- OWNER_ID_FIELD,
- DASHBOARD_ID_FIELD,
- PARAMS_PROPERTY,
- ]
- show_columns = [
- "id",
- NAME_FIELD,
- DESCRIPTION_FIELD,
- OWNER_TYPE_FIELD,
- OWNER_ID_FIELD,
- DASHBOARD_ID_FIELD,
- PARAMS_PROPERTY,
- ]
- search_columns = ["id", NAME_FIELD, OWNER_ID_FIELD, DASHBOARD_ID_FIELD]
- base_filters = [[OWNER_ID_FIELD, FilterSetFilter, ""]]
-
- def __init__(self) -> None:
- self.datamodel.get_search_columns_list = lambda: []
- super().__init__()
-
- def _init_properties(self) -> None:
- super(BaseSupersetModelRestApi, self)._init_properties()
-
- @expose("//filtersets", methods=("GET",))
- @protect()
- @safe
- @permission_name("get")
- @rison(get_list_schema)
- def get_list(self, dashboard_id: int, **kwargs: Any) -> Response:
- """Get a dashboard's list of filter sets.
- ---
- get:
- summary: Get a dashboard's list of filter sets
- parameters:
- - in: path
- schema:
- type: integer
- name: dashboard_id
- description: The id of the dashboard
- responses:
- 200:
- description: FilterSets
- content:
- application/json:
- schema:
- type: array
- items:
- type: object
- properties:
- name:
- description: Name of the Filter set
- type: string
- json_metadata:
- description: metadata of the filter set
- type: string
- description:
- description: A description field of the filter set
- type: string
- owner_id:
- description: A description field of the filter set
- type: integer
- owner_type:
- description: the Type of the owner ( Dashboard/User)
- type: integer
- parameters:
- description: JSON schema defining the needed parameters
- 302:
- description: Redirects to the current digest
- 400:
- $ref: '#/components/responses/400'
- 401:
- $ref: '#/components/responses/401'
- 404:
- $ref: '#/components/responses/404'
- """
- if not DashboardDAO.find_by_id(cast(int, dashboard_id)):
- return self.response(404, message=f"dashboard '{dashboard_id}' not found")
- rison_data = kwargs.setdefault("rison", {})
- rison_data.setdefault("filters", [])
- rison_data["filters"].append(
- {"col": "dashboard_id", "opr": "eq", "value": str(dashboard_id)}
- )
- return self.get_list_headless(**kwargs)
-
- @expose("//filtersets", methods=("POST",))
- @protect()
- @safe
- @statsd_metrics
- @event_logger.log_this_with_context(
- action=lambda self, *args, **kwargs: f"{self.__class__.__name__}.post",
- log_to_statsd=False,
- )
- @requires_json
- def post(self, dashboard_id: int) -> Response:
- """Create a new dashboard's filter set.
- ---
- post:
- summary: Create a new dashboard's filter set
- parameters:
- - in: path
- schema:
- type: integer
- name: dashboard_id
- description: The id of the dashboard
- requestBody:
- description: Filter set schema
- required: true
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/{{self.__class__.__name__}}.post'
- responses:
- 201:
- description: Filter set added
- content:
- application/json:
- schema:
- type: object
- properties:
- id:
- type: number
- result:
- $ref: '#/components/schemas/{{self.__class__.__name__}}.post'
- 302:
- description: Redirects to the current digest
- 400:
- $ref: '#/components/responses/400'
- 401:
- $ref: '#/components/responses/401'
- 404:
- $ref: '#/components/responses/404'
- 500:
- $ref: '#/components/responses/500'
- """
- try:
- item = self.add_model_schema.load(request.json)
- new_model = CreateFilterSetCommand(dashboard_id, item).run()
- return self.response(
- 201, **self.show_model_schema.dump(new_model, many=False)
- )
- except ValidationError as error:
- return self.response_400(message=error.messages)
- except UserIsNotDashboardOwnerError:
- return self.response_403()
- except FilterSetCreateFailedError as error:
- return self.response_400(message=error.message)
- except DashboardNotFoundError:
- return self.response_404()
-
- @expose("//filtersets/", methods=("PUT",))
- @protect()
- @safe
- @statsd_metrics
- @event_logger.log_this_with_context(
- action=lambda self, *args, **kwargs: f"{self.__class__.__name__}.put",
- log_to_statsd=False,
- )
- @requires_json
- def put(self, dashboard_id: int, pk: int) -> Response:
- """Update a dashboard's filter set.
- ---
- put:
- summary: Update a dashboard's filter set
- parameters:
- - in: path
- schema:
- type: integer
- name: dashboard_id
- - in: path
- schema:
- type: integer
- name: pk
- requestBody:
- description: Filter set schema
- required: true
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/{{self.__class__.__name__}}.put'
- responses:
- 200:
- description: Filter set changed
- content:
- application/json:
- schema:
- type: object
- properties:
- id:
- type: number
- result:
- $ref: '#/components/schemas/{{self.__class__.__name__}}.put'
- 400:
- $ref: '#/components/responses/400'
- 401:
- $ref: '#/components/responses/401'
- 403:
- $ref: '#/components/responses/403'
- 404:
- $ref: '#/components/responses/404'
- 422:
- $ref: '#/components/responses/422'
- 500:
- $ref: '#/components/responses/500'
- """
- try:
- item = self.edit_model_schema.load(request.json)
- changed_model = UpdateFilterSetCommand(dashboard_id, pk, item).run()
- return self.response(
- 200, **self.show_model_schema.dump(changed_model, many=False)
- )
- except ValidationError as error:
- return self.response_400(message=error.messages)
- except (
- ObjectNotFoundError,
- FilterSetForbiddenError,
- FilterSetUpdateFailedError,
- ) as err:
- logger.error(err)
- return self.response(err.status)
-
- @expose("//filtersets/", methods=("DELETE",))
- @protect()
- @safe
- @statsd_metrics
- @event_logger.log_this_with_context(
- action=lambda self, *args, **kwargs: f"{self.__class__.__name__}.delete",
- log_to_statsd=False,
- )
- def delete(self, dashboard_id: int, pk: int) -> Response:
- """Delete a dashboard's filter set.
- ---
- delete:
- summary: Delete a dashboard's filter set
- parameters:
- - in: path
- schema:
- type: integer
- name: dashboard_id
- - in: path
- schema:
- type: integer
- name: pk
- responses:
- 200:
- description: Filter set deleted
- content:
- application/json:
- schema:
- type: object
- properties:
- message:
- type: string
- 401:
- $ref: '#/components/responses/401'
- 403:
- $ref: '#/components/responses/403'
- 404:
- $ref: '#/components/responses/404'
- 422:
- $ref: '#/components/responses/422'
- 500:
- $ref: '#/components/responses/500'
- """
- try:
- DeleteFilterSetCommand(dashboard_id, pk).run()
- return self.response(200, id=dashboard_id)
- except ValidationError as error:
- return self.response_400(message=error.messages)
- except FilterSetNotFoundError:
- return self.response(200)
- except (
- ObjectNotFoundError,
- FilterSetForbiddenError,
- FilterSetDeleteFailedError,
- ) as err:
- logger.error(err)
- return self.response(err.status)
diff --git a/superset/dashboards/filter_sets/filters.py b/superset/dashboards/filter_sets/filters.py
deleted file mode 100644
index 3578e8b0b48af..0000000000000
--- a/superset/dashboards/filter_sets/filters.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# 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 __future__ import annotations
-
-from typing import Any, TYPE_CHECKING
-
-from sqlalchemy import and_, or_
-
-from superset import security_manager
-from superset.dashboards.filter_sets.consts import DASHBOARD_OWNER_TYPE, USER_OWNER_TYPE
-from superset.models.dashboard import dashboard_user
-from superset.models.filter_set import FilterSet
-from superset.utils.core import get_user_id
-from superset.views.base import BaseFilter
-
-if TYPE_CHECKING:
- from sqlalchemy.orm.query import Query
-
-
-class FilterSetFilter(BaseFilter): # pylint: disable=too-few-public-methods)
- def apply(self, query: Query, value: Any) -> Query:
- if security_manager.is_admin():
- return query
-
- filter_set_ids_by_dashboard_owners = ( # pylint: disable=C0103
- query.from_self(FilterSet.id)
- .join(dashboard_user, FilterSet.owner_id == dashboard_user.c.dashboard_id)
- .filter(
- and_(
- FilterSet.owner_type == DASHBOARD_OWNER_TYPE,
- dashboard_user.c.user_id == get_user_id(),
- )
- )
- )
-
- return query.filter(
- or_(
- and_(
- FilterSet.owner_type == USER_OWNER_TYPE,
- FilterSet.owner_id == get_user_id(),
- ),
- FilterSet.id.in_(filter_set_ids_by_dashboard_owners),
- )
- )
diff --git a/superset/dashboards/filter_sets/schemas.py b/superset/dashboards/filter_sets/schemas.py
deleted file mode 100644
index 30742a446301a..0000000000000
--- a/superset/dashboards/filter_sets/schemas.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# 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 collections.abc import Mapping
-from typing import Any, cast
-
-from marshmallow import fields, post_load, Schema, ValidationError
-from marshmallow.validate import Length, OneOf
-
-from superset.dashboards.filter_sets.consts import (
- DASHBOARD_OWNER_TYPE,
- JSON_METADATA_FIELD,
- OWNER_ID_FIELD,
- OWNER_TYPE_FIELD,
- USER_OWNER_TYPE,
-)
-
-
-class JsonMetadataSchema(Schema):
- nativeFilters = fields.Mapping(required=True, allow_none=False)
- dataMask = fields.Mapping(required=False, allow_none=False)
-
-
-class FilterSetSchema(Schema):
- json_metadata_schema: JsonMetadataSchema = JsonMetadataSchema()
-
- def _validate_json_meta_data(self, json_meta_data: str) -> None:
- try:
- self.json_metadata_schema.loads(json_meta_data)
- except Exception as ex:
- raise ValidationError("failed to parse json_metadata to json") from ex
-
-
-class FilterSetPostSchema(FilterSetSchema):
- json_metadata_schema: JsonMetadataSchema = JsonMetadataSchema()
- name = fields.String(
- required=True,
- allow_none=False,
- validate=Length(0, 500),
- )
- description = fields.String(
- required=False, allow_none=True, validate=[Length(1, 1000)]
- )
- json_metadata = fields.String(allow_none=False, required=True)
-
- owner_type = fields.String(
- required=True, validate=OneOf([USER_OWNER_TYPE, DASHBOARD_OWNER_TYPE])
- )
- owner_id = fields.Int(required=False)
-
- @post_load
- def validate(
- self, data: Mapping[Any, Any], *, many: Any, partial: Any
- ) -> dict[str, Any]:
- self._validate_json_meta_data(data[JSON_METADATA_FIELD])
- if data[OWNER_TYPE_FIELD] == USER_OWNER_TYPE and OWNER_ID_FIELD not in data:
- raise ValidationError("owner_id is mandatory when owner_type is User")
- return cast(dict[str, Any], data)
-
-
-class FilterSetPutSchema(FilterSetSchema):
- name = fields.String(required=False, allow_none=False, validate=Length(0, 500))
- description = fields.String(
- required=False, allow_none=False, validate=[Length(1, 1000)]
- )
- json_metadata = fields.String(required=False, allow_none=False)
- owner_type = fields.String(
- allow_none=False, required=False, validate=OneOf([DASHBOARD_OWNER_TYPE])
- )
-
- @post_load
- def validate(
- self, data: Mapping[Any, Any], *, many: Any, partial: Any
- ) -> dict[str, Any]:
- if JSON_METADATA_FIELD in data:
- self._validate_json_meta_data(data[JSON_METADATA_FIELD])
- return cast(dict[str, Any], data)
-
-
-def validate_pair(first_field: str, second_field: str, data: dict[str, Any]) -> None:
- if first_field in data and second_field not in data:
- raise ValidationError(
- f"{first_field} must be included alongside {second_field}"
- )
diff --git a/superset/dashboards/schemas.py b/superset/dashboards/schemas.py
index 615d830d42420..a208bae0f9d6d 100644
--- a/superset/dashboards/schemas.py
+++ b/superset/dashboards/schemas.py
@@ -113,8 +113,6 @@ class DashboardJSONMetadataSchema(Schema):
# global_chart_configuration keeps data about global cross-filter scoping
# for charts - can be overridden by chart_configuration for each chart
global_chart_configuration = fields.Dict()
- # filter_sets_configuration is for dashboard-native filters
- filter_sets_configuration = fields.List(fields.Dict(), allow_none=True)
timed_refresh_immune_slices = fields.List(fields.Integer())
# deprecated wrt dashboard-native filters
filter_scopes = fields.Dict()
diff --git a/superset/initialization/__init__.py b/superset/initialization/__init__.py
index 09212120ece16..c18cf8a059f74 100644
--- a/superset/initialization/__init__.py
+++ b/superset/initialization/__init__.py
@@ -135,7 +135,6 @@ def init_views(self) -> None:
)
from superset.css_templates.api import CssTemplateRestApi
from superset.dashboards.api import DashboardRestApi
- from superset.dashboards.filter_sets.api import FilterSetRestApi
from superset.dashboards.filter_state.api import DashboardFilterStateRestApi
from superset.dashboards.permalink.api import DashboardPermalinkRestApi
from superset.databases.api import DatabaseRestApi
@@ -222,7 +221,6 @@ def init_views(self) -> None:
appbuilder.add_api(ExploreRestApi)
appbuilder.add_api(ExploreFormDataRestApi)
appbuilder.add_api(ExplorePermalinkRestApi)
- appbuilder.add_api(FilterSetRestApi)
appbuilder.add_api(ImportExportRestApi)
appbuilder.add_api(QueryRestApi)
appbuilder.add_api(ReportScheduleRestApi)
diff --git a/superset/dashboards/filter_sets/consts.py b/superset/migrations/versions/2023-12-27_13-14_59a1450b3c10_drop_filter_sets_table.py
similarity index 62%
rename from superset/dashboards/filter_sets/consts.py
rename to superset/migrations/versions/2023-12-27_13-14_59a1450b3c10_drop_filter_sets_table.py
index ff60a4f8bc3ed..e7abd2d014dcb 100644
--- a/superset/dashboards/filter_sets/consts.py
+++ b/superset/migrations/versions/2023-12-27_13-14_59a1450b3c10_drop_filter_sets_table.py
@@ -14,17 +14,30 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-USER_OWNER_TYPE = "User"
-DASHBOARD_OWNER_TYPE = "Dashboard"
-
-NAME_FIELD = "name"
-DESCRIPTION_FIELD = "description"
-JSON_METADATA_FIELD = "json_metadata"
-OWNER_ID_FIELD = "owner_id"
-OWNER_TYPE_FIELD = "owner_type"
-DASHBOARD_ID_FIELD = "dashboard_id"
-OWNER_OBJECT_FIELD = "owner_object"
-DASHBOARD_FIELD = "dashboard"
-PARAMS_PROPERTY = "params"
-
-FILTER_SET_API_PERMISSIONS_NAME = "FilterSets"
+"""drop_filter_sets_table
+
+Revision ID: 59a1450b3c10
+Revises: 06dd9ff00fe8
+Create Date: 2023-12-27 13:14:27.268232
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = "59a1450b3c10"
+down_revision = "06dd9ff00fe8"
+
+from importlib import import_module
+
+from alembic import op
+
+module = import_module(
+ "superset.migrations.versions.2021-03-29_11-15_3ebe0993c770_filterset_table"
+)
+
+
+def upgrade():
+ module.downgrade()
+
+
+def downgrade():
+ module.upgrade()
diff --git a/superset/models/dashboard.py b/superset/models/dashboard.py
index b965c8f6a0a07..ab98b74352108 100644
--- a/superset/models/dashboard.py
+++ b/superset/models/dashboard.py
@@ -55,7 +55,6 @@
)
from superset.daos.datasource import DatasourceDAO
from superset.extensions import cache_manager
-from superset.models.filter_set import FilterSet
from superset.models.helpers import AuditMixinNullable, ImportExportMixin
from superset.models.slice import Slice
from superset.models.user_attributes import UserAttribute
@@ -63,7 +62,6 @@
from superset.tasks.utils import get_current_user
from superset.thumbnails.digest import get_dashboard_digest
from superset.utils import core as utils
-from superset.utils.core import get_user_id
from superset.utils.decorators import debounce
metadata = Model.metadata # pylint: disable=no-member
@@ -185,9 +183,6 @@ class Dashboard(AuditMixinNullable, ImportExportMixin, Model):
back_populates="dashboard",
cascade="all, delete-orphan",
)
- _filter_sets = relationship(
- "FilterSet", back_populates="dashboard", cascade="all, delete"
- )
export_fields = [
"dashboard_title",
"position_json",
@@ -231,28 +226,6 @@ def datasources(self) -> set[BaseDatasource]:
.all()
}
- @property
- def filter_sets(self) -> dict[int, FilterSet]:
- return {fs.id: fs for fs in self._filter_sets}
-
- @property
- def filter_sets_lst(self) -> dict[int, FilterSet]:
- if security_manager.is_admin():
- return self._filter_sets
- filter_sets_by_owner_type: dict[str, list[Any]] = {"Dashboard": [], "User": []}
- for fs in self._filter_sets:
- filter_sets_by_owner_type[fs.owner_type].append(fs)
- user_filter_sets = list(
- filter(
- lambda filter_set: filter_set.owner_id == get_user_id(),
- filter_sets_by_owner_type["User"],
- )
- )
- return {
- fs.id: fs
- for fs in user_filter_sets + filter_sets_by_owner_type["Dashboard"]
- }
-
@property
def charts(self) -> list[str]:
return [slc.chart for slc in self.slices]
diff --git a/superset/models/filter_set.py b/superset/models/filter_set.py
deleted file mode 100644
index e2b19f32a04cb..0000000000000
--- a/superset/models/filter_set.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# 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 __future__ import annotations
-
-import json
-import logging
-from typing import Any
-
-from flask_appbuilder import Model
-from sqlalchemy import Column, ForeignKey, Integer, MetaData, String, Text
-from sqlalchemy.orm import relationship
-from sqlalchemy_utils import generic_relationship
-
-from superset import app, db
-from superset.models.helpers import AuditMixinNullable
-
-metadata = Model.metadata # pylint: disable=no-member
-config = app.config
-logger = logging.getLogger(__name__)
-
-
-class FilterSet(Model, AuditMixinNullable):
- __tablename__ = "filter_sets"
- id = Column(Integer, primary_key=True)
- name = Column(String(500), nullable=False, unique=True)
- description = Column(Text, nullable=True)
- json_metadata = Column(Text, nullable=False)
- dashboard_id = Column(Integer, ForeignKey("dashboards.id"))
- dashboard = relationship("Dashboard", back_populates="_filter_sets")
- owner_id = Column(Integer, nullable=False)
- owner_type = Column(String(255), nullable=False)
- owner_object = generic_relationship(owner_type, owner_id)
-
- def __repr__(self) -> str:
- return f"FilterSet<{self.name or self.id}>"
-
- @property
- def url(self) -> str:
- return f"/api/filtersets/{self.id}/"
-
- @property
- def sqla_metadata(self) -> None:
- # pylint: disable=no-member
- with self.get_sqla_engine_with_context() as engine:
- meta = MetaData(bind=engine)
- meta.reflect()
-
- @property
- def changed_by_name(self) -> str:
- if not self.changed_by:
- return ""
- return str(self.changed_by)
-
- def to_dict(self) -> dict[str, Any]:
- return {
- "id": self.id,
- "name": self.name,
- "description": self.description,
- "params": self.params,
- "dashboard_id": self.dashboard_id,
- "owner_type": self.owner_type,
- "owner_id": self.owner_id,
- }
-
- @classmethod
- def get(cls, _id: int) -> FilterSet:
- session = db.session()
- qry = session.query(FilterSet).filter(_id)
- return qry.one_or_none()
-
- @classmethod
- def get_by_name(cls, name: str) -> FilterSet:
- session = db.session()
- qry = session.query(FilterSet).filter(FilterSet.name == name)
- return qry.one_or_none()
-
- @classmethod
- def get_by_dashboard_id(cls, dashboard_id: int) -> FilterSet:
- session = db.session()
- qry = session.query(FilterSet).filter(FilterSet.dashboard_id == dashboard_id)
- return qry.all()
-
- @property
- def params(self) -> dict[str, Any]:
- if self.json_metadata:
- return json.loads(self.json_metadata)
- return {}
diff --git a/tests/integration_tests/dashboards/filter_sets/__init__.py b/tests/integration_tests/dashboards/filter_sets/__init__.py
deleted file mode 100644
index 13a83393a9124..0000000000000
--- a/tests/integration_tests/dashboards/filter_sets/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# 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.
diff --git a/tests/integration_tests/dashboards/filter_sets/conftest.py b/tests/integration_tests/dashboards/filter_sets/conftest.py
deleted file mode 100644
index b19e929f9d1f7..0000000000000
--- a/tests/integration_tests/dashboards/filter_sets/conftest.py
+++ /dev/null
@@ -1,286 +0,0 @@
-# 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 __future__ import annotations
-
-import json
-from collections.abc import Generator
-from typing import Any, TYPE_CHECKING
-
-import pytest
-
-from superset import db, security_manager as sm
-from superset.dashboards.filter_sets.consts import (
- DESCRIPTION_FIELD,
- JSON_METADATA_FIELD,
- NAME_FIELD,
- OWNER_ID_FIELD,
- OWNER_TYPE_FIELD,
- USER_OWNER_TYPE,
-)
-from superset.models.dashboard import Dashboard
-from superset.models.filter_set import FilterSet
-from tests.integration_tests.dashboards.filter_sets.consts import (
- ADMIN_USERNAME_FOR_TEST,
- DASHBOARD_OWNER_USERNAME,
- FILTER_SET_OWNER_USERNAME,
- REGULAR_USER,
-)
-from tests.integration_tests.dashboards.superset_factory_util import (
- create_dashboard,
- create_database,
- create_datasource_table,
- create_slice,
-)
-from tests.integration_tests.test_app import app
-
-if TYPE_CHECKING:
- from flask.ctx import AppContext
- from flask.testing import FlaskClient
- from flask_appbuilder.security.manager import BaseSecurityManager
- from flask_appbuilder.security.sqla.models import (
- PermissionView,
- Role,
- User,
- ViewMenu,
- )
- from sqlalchemy.orm import Session
-
- from superset.connectors.sqla.models import SqlaTable
- from superset.models.core import Database
- from superset.models.slice import Slice
-
-
-security_manager: BaseSecurityManager = sm
-
-
-@pytest.fixture(autouse=True, scope="module")
-def test_users() -> Generator[dict[str, int], None, None]:
- usernames = [
- ADMIN_USERNAME_FOR_TEST,
- DASHBOARD_OWNER_USERNAME,
- FILTER_SET_OWNER_USERNAME,
- REGULAR_USER,
- ]
- with app.app_context():
- filter_set_role = build_filter_set_role()
- admin_role: Role = security_manager.find_role("Admin")
- usernames_to_ids = create_test_users(admin_role, filter_set_role, usernames)
- yield usernames_to_ids
- delete_users(usernames_to_ids)
-
-
-def delete_users(usernames_to_ids: dict[str, int]) -> None:
- for username in usernames_to_ids.keys():
- db.session.delete(security_manager.find_user(username))
- db.session.commit()
-
-
-def create_test_users(
- admin_role: Role, filter_set_role: Role, usernames: list[str]
-) -> dict[str, int]:
- users: list[User] = []
- for username in usernames:
- user = build_user(username, filter_set_role, admin_role)
- users.append(user)
- return {user.username: user.id for user in users}
-
-
-def build_user(username: str, filter_set_role: Role, admin_role: Role) -> User:
- roles_to_add = (
- [admin_role] if username == ADMIN_USERNAME_FOR_TEST else [filter_set_role]
- )
- user: User = security_manager.add_user(
- username, "test", "test", username, roles_to_add, password="general"
- )
- if not user:
- user = security_manager.find_user(username)
- if user is None:
- raise Exception(f"Failed to build the user {username}")
- return user
-
-
-def build_filter_set_role() -> Role:
- filter_set_role: Role = security_manager.add_role("filter_set_role")
- filterset_view_name: ViewMenu = security_manager.find_view_menu("FilterSets")
- all_datasource_view_name: ViewMenu = security_manager.find_view_menu(
- "all_datasource_access"
- )
- pvms: list[PermissionView] = security_manager.find_permissions_view_menu(
- filterset_view_name
- ) + security_manager.find_permissions_view_menu(all_datasource_view_name)
- for pvm in pvms:
- security_manager.add_permission_role(filter_set_role, pvm)
- return filter_set_role
-
-
-@pytest.fixture
-def client() -> Generator[FlaskClient[Any], None, None]:
- with app.test_client() as client:
- yield client
-
-
-@pytest.fixture
-def dashboard(app_context) -> Generator[Dashboard, None, None]:
- dashboard_owner_user = security_manager.find_user(DASHBOARD_OWNER_USERNAME)
- database = create_database("test_database_filter_sets")
- datasource = create_datasource_table(
- name="test_datasource", database=database, owners=[dashboard_owner_user]
- )
- slice_ = create_slice(
- datasource=datasource, name="test_slice", owners=[dashboard_owner_user]
- )
- dashboard = create_dashboard(
- dashboard_title="test_dashboard",
- published=True,
- slices=[slice_],
- owners=[dashboard_owner_user],
- )
- db.session.add(dashboard)
- db.session.commit()
-
- yield dashboard
-
- db.session.delete(dashboard)
- db.session.delete(slice_)
- db.session.delete(datasource)
- db.session.delete(database)
- db.session.commit()
-
-
-@pytest.fixture
-def dashboard_id(dashboard: Dashboard) -> Generator[int, None, None]:
- yield dashboard.id
-
-
-@pytest.fixture
-def filtersets(
- dashboard_id: int, test_users: dict[str, int], dumped_valid_json_metadata: str
-) -> Generator[dict[str, list[FilterSet]], None, None]:
- first_filter_set = FilterSet(
- name="filter_set_1_of_" + str(dashboard_id),
- dashboard_id=dashboard_id,
- json_metadata=dumped_valid_json_metadata,
- owner_id=dashboard_id,
- owner_type="Dashboard",
- )
- second_filter_set = FilterSet(
- name="filter_set_2_of_" + str(dashboard_id),
- json_metadata=dumped_valid_json_metadata,
- dashboard_id=dashboard_id,
- owner_id=dashboard_id,
- owner_type="Dashboard",
- )
- third_filter_set = FilterSet(
- name="filter_set_3_of_" + str(dashboard_id),
- json_metadata=dumped_valid_json_metadata,
- dashboard_id=dashboard_id,
- owner_id=test_users[FILTER_SET_OWNER_USERNAME],
- owner_type="User",
- )
- fourth_filter_set = FilterSet(
- name="filter_set_4_of_" + str(dashboard_id),
- json_metadata=dumped_valid_json_metadata,
- dashboard_id=dashboard_id,
- owner_id=test_users[FILTER_SET_OWNER_USERNAME],
- owner_type="User",
- )
- db.session.add(first_filter_set)
- db.session.add(second_filter_set)
- db.session.add(third_filter_set)
- db.session.add(fourth_filter_set)
- db.session.commit()
-
- yield {
- "Dashboard": [first_filter_set, second_filter_set],
- FILTER_SET_OWNER_USERNAME: [third_filter_set, fourth_filter_set],
- }
-
- db.session.delete(first_filter_set)
- db.session.delete(second_filter_set)
- db.session.delete(third_filter_set)
- db.session.delete(fourth_filter_set)
- db.session.commit()
-
-
-@pytest.fixture
-def filterset_id(filtersets: dict[str, list[FilterSet]]) -> int:
- return filtersets["Dashboard"][0].id
-
-
-@pytest.fixture
-def valid_json_metadata() -> dict[str, Any]:
- return {"nativeFilters": {}}
-
-
-@pytest.fixture
-def dumped_valid_json_metadata(valid_json_metadata: dict[str, Any]) -> str:
- return json.dumps(valid_json_metadata)
-
-
-@pytest.fixture
-def exists_user_id() -> int:
- return 1
-
-
-@pytest.fixture
-def valid_filter_set_data_for_create(
- dashboard_id: int, dumped_valid_json_metadata: str, exists_user_id: int
-) -> dict[str, Any]:
- name = "test_filter_set_of_dashboard_" + str(dashboard_id)
- return {
- NAME_FIELD: name,
- DESCRIPTION_FIELD: "description of " + name,
- JSON_METADATA_FIELD: dumped_valid_json_metadata,
- OWNER_TYPE_FIELD: USER_OWNER_TYPE,
- OWNER_ID_FIELD: exists_user_id,
- }
-
-
-@pytest.fixture
-def valid_filter_set_data_for_update(
- dashboard_id: int, dumped_valid_json_metadata: str, exists_user_id: int
-) -> dict[str, Any]:
- name = "name_changed_test_filter_set_of_dashboard_" + str(dashboard_id)
- return {
- NAME_FIELD: name,
- DESCRIPTION_FIELD: "changed description of " + name,
- JSON_METADATA_FIELD: dumped_valid_json_metadata,
- }
-
-
-@pytest.fixture
-def not_exists_dashboard_id(dashboard_id: int) -> Generator[int, None, None]:
- yield dashboard_id + 1
-
-
-@pytest.fixture
-def not_exists_user_id() -> int:
- return 99999
-
-
-@pytest.fixture()
-def dashboard_based_filter_set_dict(
- filtersets: dict[str, list[FilterSet]]
-) -> dict[str, Any]:
- return filtersets["Dashboard"][0].to_dict()
-
-
-@pytest.fixture()
-def user_based_filter_set_dict(
- filtersets: dict[str, list[FilterSet]]
-) -> dict[str, Any]:
- return filtersets[FILTER_SET_OWNER_USERNAME][0].to_dict()
diff --git a/tests/integration_tests/dashboards/filter_sets/consts.py b/tests/integration_tests/dashboards/filter_sets/consts.py
deleted file mode 100644
index f54f00fea8b75..0000000000000
--- a/tests/integration_tests/dashboards/filter_sets/consts.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# 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.
-FILTER_SET_URI = "api/v1/dashboard/{dashboard_id}/filtersets"
-
-ADMIN_USERNAME_FOR_TEST = "admin@filterset.com"
-DASHBOARD_OWNER_USERNAME = "dash_owner_user@filterset.com"
-FILTER_SET_OWNER_USERNAME = "fs_owner_user@filterset.com"
-REGULAR_USER = "regular_user@filterset.com"
diff --git a/tests/integration_tests/dashboards/filter_sets/create_api_tests.py b/tests/integration_tests/dashboards/filter_sets/create_api_tests.py
deleted file mode 100644
index 9891266101677..0000000000000
--- a/tests/integration_tests/dashboards/filter_sets/create_api_tests.py
+++ /dev/null
@@ -1,629 +0,0 @@
-# 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 __future__ import annotations
-
-from typing import Any
-
-from flask.testing import FlaskClient
-
-from superset.dashboards.filter_sets.consts import (
- DASHBOARD_OWNER_TYPE,
- DESCRIPTION_FIELD,
- JSON_METADATA_FIELD,
- NAME_FIELD,
- OWNER_ID_FIELD,
- OWNER_TYPE_FIELD,
- USER_OWNER_TYPE,
-)
-from tests.integration_tests.dashboards.filter_sets.consts import (
- ADMIN_USERNAME_FOR_TEST,
- DASHBOARD_OWNER_USERNAME,
- FILTER_SET_OWNER_USERNAME,
-)
-from tests.integration_tests.dashboards.filter_sets.utils import (
- call_create_filter_set,
- get_filter_set_by_dashboard_id,
- get_filter_set_by_name,
-)
-from tests.integration_tests.test_app import login
-
-
-def assert_filterset_was_not_created(filter_set_data: dict[str, Any]) -> None:
- assert get_filter_set_by_name(str(filter_set_data["name"])) is None
-
-
-def assert_filterset_was_created(filter_set_data: dict[str, Any]) -> None:
- assert get_filter_set_by_name(filter_set_data["name"]) is not None
-
-
-class TestCreateFilterSetsApi:
- def test_with_extra_field__400(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create["extra"] = "val"
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 400
- assert response.json["message"]["extra"][0] == "Unknown field."
- assert_filterset_was_not_created(valid_filter_set_data_for_create)
-
- def test_with_id_field__400(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create["id"] = 1
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 400
- assert response.json["message"]["id"][0] == "Unknown field."
- assert_filterset_was_not_created(valid_filter_set_data_for_create)
-
- def test_with_dashboard_not_exists__404(
- self,
- not_exists_dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # act
- login(client, "admin")
- response = call_create_filter_set(
- client, not_exists_dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 404
- assert_filterset_was_not_created(valid_filter_set_data_for_create)
-
- def test_without_name__400(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create.pop(NAME_FIELD, None)
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 400
- assert get_filter_set_by_dashboard_id(dashboard_id) == []
-
- def test_with_none_name__400(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create[NAME_FIELD] = None
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_created(valid_filter_set_data_for_create)
-
- def test_with_int_as_name__400(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create[NAME_FIELD] = 4
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_created(valid_filter_set_data_for_create)
-
- def test_without_description__201(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create.pop(DESCRIPTION_FIELD, None)
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 201
- assert_filterset_was_created(valid_filter_set_data_for_create)
-
- def test_with_none_description__201(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create[DESCRIPTION_FIELD] = None
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 201
- assert_filterset_was_created(valid_filter_set_data_for_create)
-
- def test_with_int_as_description__400(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create[DESCRIPTION_FIELD] = 1
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_created(valid_filter_set_data_for_create)
-
- def test_without_json_metadata__400(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create.pop(JSON_METADATA_FIELD, None)
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_created(valid_filter_set_data_for_create)
-
- def test_with_invalid_json_metadata__400(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create[DESCRIPTION_FIELD] = {}
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_created(valid_filter_set_data_for_create)
-
- def test_without_owner_type__400(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create.pop(OWNER_TYPE_FIELD, None)
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_created(valid_filter_set_data_for_create)
-
- def test_with_invalid_owner_type__400(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = "OTHER_TYPE"
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_created(valid_filter_set_data_for_create)
-
- def test_without_owner_id_when_owner_type_is_user__400(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = USER_OWNER_TYPE
- valid_filter_set_data_for_create.pop(OWNER_ID_FIELD, None)
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_created(valid_filter_set_data_for_create)
-
- def test_without_owner_id_when_owner_type_is_dashboard__201(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = DASHBOARD_OWNER_TYPE
- valid_filter_set_data_for_create.pop(OWNER_ID_FIELD, None)
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 201
- assert_filterset_was_created(valid_filter_set_data_for_create)
-
- def test_with_not_exists_owner__400(
- self,
- dashboard_id: int,
- valid_filter_set_data_for_create: dict[str, Any],
- not_exists_user_id: int,
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = USER_OWNER_TYPE
- valid_filter_set_data_for_create[OWNER_ID_FIELD] = not_exists_user_id
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_created(valid_filter_set_data_for_create)
-
- def test_when_caller_is_admin_and_owner_is_admin__201(
- self,
- dashboard_id: int,
- test_users: dict[str, int],
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = USER_OWNER_TYPE
- valid_filter_set_data_for_create[OWNER_ID_FIELD] = test_users[
- ADMIN_USERNAME_FOR_TEST
- ]
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 201
- assert_filterset_was_created(valid_filter_set_data_for_create)
-
- def test_when_caller_is_admin_and_owner_is_dashboard_owner__201(
- self,
- dashboard_id: int,
- test_users: dict[str, int],
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = USER_OWNER_TYPE
- valid_filter_set_data_for_create[OWNER_ID_FIELD] = test_users[
- DASHBOARD_OWNER_USERNAME
- ]
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 201
- assert_filterset_was_created(valid_filter_set_data_for_create)
-
- def test_when_caller_is_admin_and_owner_is_regular_user__201(
- self,
- dashboard_id: int,
- test_users: dict[str, int],
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = USER_OWNER_TYPE
- valid_filter_set_data_for_create[OWNER_ID_FIELD] = test_users[
- FILTER_SET_OWNER_USERNAME
- ]
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 201
- assert_filterset_was_created(valid_filter_set_data_for_create)
-
- def test_when_caller_is_admin_and_owner_type_is_dashboard__201(
- self,
- dashboard_id: int,
- test_users: dict[str, int],
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = DASHBOARD_OWNER_TYPE
- valid_filter_set_data_for_create[OWNER_ID_FIELD] = dashboard_id
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 201
- assert_filterset_was_created(valid_filter_set_data_for_create)
-
- def test_when_caller_is_dashboard_owner_and_owner_is_admin__201(
- self,
- dashboard_id: int,
- test_users: dict[str, int],
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, DASHBOARD_OWNER_USERNAME)
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = USER_OWNER_TYPE
- valid_filter_set_data_for_create[OWNER_ID_FIELD] = test_users[
- ADMIN_USERNAME_FOR_TEST
- ]
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 201
- assert_filterset_was_created(valid_filter_set_data_for_create)
-
- def test_when_caller_is_dashboard_owner_and_owner_is_dashboard_owner__201(
- self,
- dashboard_id: int,
- test_users: dict[str, int],
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, DASHBOARD_OWNER_USERNAME)
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = USER_OWNER_TYPE
- valid_filter_set_data_for_create[OWNER_ID_FIELD] = test_users[
- DASHBOARD_OWNER_USERNAME
- ]
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 201
- assert_filterset_was_created(valid_filter_set_data_for_create)
-
- def test_when_caller_is_dashboard_owner_and_owner_is_regular_user__201(
- self,
- dashboard_id: int,
- test_users: dict[str, int],
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, DASHBOARD_OWNER_USERNAME)
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = USER_OWNER_TYPE
- valid_filter_set_data_for_create[OWNER_ID_FIELD] = test_users[
- FILTER_SET_OWNER_USERNAME
- ]
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 201
- assert_filterset_was_created(valid_filter_set_data_for_create)
-
- def test_when_caller_is_dashboard_owner_and_owner_type_is_dashboard__201(
- self,
- dashboard_id: int,
- test_users: dict[str, int],
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, DASHBOARD_OWNER_USERNAME)
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = DASHBOARD_OWNER_TYPE
- valid_filter_set_data_for_create[OWNER_ID_FIELD] = dashboard_id
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 201
- assert_filterset_was_created(valid_filter_set_data_for_create)
-
- def test_when_caller_is_regular_user_and_owner_is_admin__201(
- self,
- dashboard_id: int,
- test_users: dict[str, int],
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, FILTER_SET_OWNER_USERNAME)
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = USER_OWNER_TYPE
- valid_filter_set_data_for_create[OWNER_ID_FIELD] = test_users[
- ADMIN_USERNAME_FOR_TEST
- ]
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 201
- assert_filterset_was_created(valid_filter_set_data_for_create)
-
- def test_when_caller_is_regular_user_and_owner_is_dashboard_owner__201(
- self,
- dashboard_id: int,
- test_users: dict[str, int],
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, FILTER_SET_OWNER_USERNAME)
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = USER_OWNER_TYPE
- valid_filter_set_data_for_create[OWNER_ID_FIELD] = test_users[
- DASHBOARD_OWNER_USERNAME
- ]
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 201
- assert_filterset_was_created(valid_filter_set_data_for_create)
-
- def test_when_caller_is_regular_user_and_owner_is_regular_user__201(
- self,
- dashboard_id: int,
- test_users: dict[str, int],
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, FILTER_SET_OWNER_USERNAME)
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = USER_OWNER_TYPE
- valid_filter_set_data_for_create[OWNER_ID_FIELD] = test_users[
- FILTER_SET_OWNER_USERNAME
- ]
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 201
- assert_filterset_was_created(valid_filter_set_data_for_create)
-
- def test_when_caller_is_regular_user_and_owner_type_is_dashboard__403(
- self,
- dashboard_id: int,
- test_users: dict[str, int],
- valid_filter_set_data_for_create: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, FILTER_SET_OWNER_USERNAME)
- valid_filter_set_data_for_create[OWNER_TYPE_FIELD] = DASHBOARD_OWNER_TYPE
- valid_filter_set_data_for_create[OWNER_ID_FIELD] = dashboard_id
-
- # act
- response = call_create_filter_set(
- client, dashboard_id, valid_filter_set_data_for_create
- )
-
- # assert
- assert response.status_code == 403
- assert_filterset_was_not_created(valid_filter_set_data_for_create)
diff --git a/tests/integration_tests/dashboards/filter_sets/delete_api_tests.py b/tests/integration_tests/dashboards/filter_sets/delete_api_tests.py
deleted file mode 100644
index 41d7ea59f71f7..0000000000000
--- a/tests/integration_tests/dashboards/filter_sets/delete_api_tests.py
+++ /dev/null
@@ -1,210 +0,0 @@
-# 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 __future__ import annotations
-
-from typing import Any, TYPE_CHECKING
-
-from tests.integration_tests.dashboards.filter_sets.consts import (
- DASHBOARD_OWNER_USERNAME,
- FILTER_SET_OWNER_USERNAME,
- REGULAR_USER,
-)
-from tests.integration_tests.dashboards.filter_sets.utils import (
- call_delete_filter_set,
- collect_all_ids,
- get_filter_set_by_name,
-)
-from tests.integration_tests.test_app import login
-
-if TYPE_CHECKING:
- from flask.testing import FlaskClient
-
- from superset.models.filter_set import FilterSet
-
-
-def assert_filterset_was_not_deleted(filter_set_dict: dict[str, Any]) -> None:
- assert get_filter_set_by_name(filter_set_dict["name"]) is not None
-
-
-def assert_filterset_deleted(filter_set_dict: dict[str, Any]) -> None:
- assert get_filter_set_by_name(filter_set_dict["name"]) is None
-
-
-class TestDeleteFilterSet:
- def test_with_dashboard_exists_filterset_not_exists__200(
- self,
- dashboard_id: int,
- filtersets: dict[str, list[FilterSet]],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- filter_set_id = max(collect_all_ids(filtersets)) + 1
-
- response = call_delete_filter_set(client, {"id": filter_set_id}, dashboard_id)
- # assert
- assert response.status_code == 200
-
- def test_with_dashboard_not_exists_filterset_not_exists__404(
- self,
- not_exists_dashboard_id: int,
- filtersets: dict[str, list[FilterSet]],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- filter_set_id = max(collect_all_ids(filtersets)) + 1
-
- response = call_delete_filter_set(
- client, {"id": filter_set_id}, not_exists_dashboard_id
- )
- # assert
- assert response.status_code == 404
-
- def test_with_dashboard_not_exists_filterset_exists__404(
- self,
- not_exists_dashboard_id: int,
- dashboard_based_filter_set_dict: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
-
- # act
- response = call_delete_filter_set(
- client, dashboard_based_filter_set_dict, not_exists_dashboard_id
- )
- # assert
- assert response.status_code == 404
- assert_filterset_was_not_deleted(dashboard_based_filter_set_dict)
-
- def test_when_caller_is_admin_and_owner_type_is_user__200(
- self,
- test_users: dict[str, int],
- user_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- # act
- response = call_delete_filter_set(client, user_based_filter_set_dict)
-
- # assert
- assert response.status_code == 200
- assert_filterset_deleted(user_based_filter_set_dict)
-
- def test_when_caller_is_admin_and_owner_type_is_dashboard__200(
- self,
- test_users: dict[str, int],
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- # act
- response = call_delete_filter_set(client, dashboard_based_filter_set_dict)
-
- # assert
- assert response.status_code == 200
- assert_filterset_deleted(dashboard_based_filter_set_dict)
-
- def test_when_caller_is_dashboard_owner_and_owner_is_other_user_403(
- self,
- test_users: dict[str, int],
- user_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, DASHBOARD_OWNER_USERNAME)
-
- # act
- response = call_delete_filter_set(client, user_based_filter_set_dict)
-
- # assert
- assert response.status_code == 403
- assert_filterset_was_not_deleted(user_based_filter_set_dict)
-
- def test_when_caller_is_dashboard_owner_and_owner_type_is_dashboard__200(
- self,
- test_users: dict[str, int],
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, DASHBOARD_OWNER_USERNAME)
-
- # act
- response = call_delete_filter_set(client, dashboard_based_filter_set_dict)
-
- # assert
- assert response.status_code == 200
- assert_filterset_deleted(dashboard_based_filter_set_dict)
-
- def test_when_caller_is_filterset_owner__200(
- self,
- test_users: dict[str, int],
- user_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, FILTER_SET_OWNER_USERNAME)
-
- # act
- response = call_delete_filter_set(client, user_based_filter_set_dict)
-
- # assert
- assert response.status_code == 200
- assert_filterset_deleted(user_based_filter_set_dict)
-
- def test_when_caller_is_regular_user_and_owner_type_is_user__403(
- self,
- test_users: dict[str, int],
- user_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, REGULAR_USER)
-
- # act
- response = call_delete_filter_set(client, user_based_filter_set_dict)
-
- # assert
- assert response.status_code == 403
- assert_filterset_was_not_deleted(user_based_filter_set_dict)
-
- def test_when_caller_is_regular_user_and_owner_type_is_dashboard__403(
- self,
- test_users: dict[str, int],
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, REGULAR_USER)
-
- # act
- response = call_delete_filter_set(client, dashboard_based_filter_set_dict)
-
- # assert
- assert response.status_code == 403
- assert_filterset_was_not_deleted(dashboard_based_filter_set_dict)
diff --git a/tests/integration_tests/dashboards/filter_sets/get_api_tests.py b/tests/integration_tests/dashboards/filter_sets/get_api_tests.py
deleted file mode 100644
index 71c985310d0a0..0000000000000
--- a/tests/integration_tests/dashboards/filter_sets/get_api_tests.py
+++ /dev/null
@@ -1,132 +0,0 @@
-# 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 __future__ import annotations
-
-from typing import Any, TYPE_CHECKING
-
-from tests.integration_tests.dashboards.filter_sets.consts import (
- DASHBOARD_OWNER_USERNAME,
- FILTER_SET_OWNER_USERNAME,
- REGULAR_USER,
-)
-from tests.integration_tests.dashboards.filter_sets.utils import (
- call_get_filter_sets,
- collect_all_ids,
-)
-from tests.integration_tests.test_app import login
-
-if TYPE_CHECKING:
- from flask.testing import FlaskClient
-
- from superset.models.filter_set import FilterSet
-
-
-class TestGetFilterSetsApi:
- def test_with_dashboard_not_exists__404(
- self,
- not_exists_dashboard_id: int,
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
-
- # act
- response = call_get_filter_sets(client, not_exists_dashboard_id)
-
- # assert
- assert response.status_code == 404
-
- def test_dashboards_without_filtersets__200(
- self, dashboard_id: int, client: FlaskClient[Any]
- ):
- # arrange
- login(client, "admin")
-
- # act
- response = call_get_filter_sets(client, dashboard_id)
-
- # assert
- assert response.status_code == 200
- assert response.is_json and response.json["count"] == 0
-
- def test_when_caller_admin__200(
- self,
- dashboard_id: int,
- filtersets: dict[str, list[FilterSet]],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- expected_ids: set[int] = collect_all_ids(filtersets)
-
- # act
- response = call_get_filter_sets(client, dashboard_id)
-
- # assert
- assert response.status_code == 200
- assert response.is_json and set(response.json["ids"]) == expected_ids
-
- def test_when_caller_dashboard_owner__200(
- self,
- dashboard_id: int,
- filtersets: dict[str, list[FilterSet]],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, DASHBOARD_OWNER_USERNAME)
- expected_ids = collect_all_ids(filtersets["Dashboard"])
-
- # act
- response = call_get_filter_sets(client, dashboard_id)
-
- # assert
- assert response.status_code == 200
- assert response.is_json and set(response.json["ids"]) == expected_ids
-
- def test_when_caller_filterset_owner__200(
- self,
- dashboard_id: int,
- filtersets: dict[str, list[FilterSet]],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, FILTER_SET_OWNER_USERNAME)
- expected_ids = collect_all_ids(filtersets[FILTER_SET_OWNER_USERNAME])
-
- # act
- response = call_get_filter_sets(client, dashboard_id)
-
- # assert
- assert response.status_code == 200
- assert response.is_json and set(response.json["ids"]) == expected_ids
-
- def test_when_caller_regular_user__200(
- self,
- dashboard_id: int,
- filtersets: dict[str, list[int]],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, REGULAR_USER)
- expected_ids: set[int] = set()
-
- # act
- response = call_get_filter_sets(client, dashboard_id)
-
- # assert
- assert response.status_code == 200
- assert response.is_json and set(response.json["ids"]) == expected_ids
diff --git a/tests/integration_tests/dashboards/filter_sets/update_api_tests.py b/tests/integration_tests/dashboards/filter_sets/update_api_tests.py
deleted file mode 100644
index a6e895a460732..0000000000000
--- a/tests/integration_tests/dashboards/filter_sets/update_api_tests.py
+++ /dev/null
@@ -1,520 +0,0 @@
-# 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 __future__ import annotations
-
-import json
-from typing import Any, TYPE_CHECKING
-
-from superset.dashboards.filter_sets.consts import (
- DESCRIPTION_FIELD,
- JSON_METADATA_FIELD,
- NAME_FIELD,
- OWNER_TYPE_FIELD,
- PARAMS_PROPERTY,
-)
-from tests.integration_tests.dashboards.filter_sets.consts import (
- DASHBOARD_OWNER_USERNAME,
- FILTER_SET_OWNER_USERNAME,
- REGULAR_USER,
-)
-from tests.integration_tests.dashboards.filter_sets.utils import (
- call_update_filter_set,
- collect_all_ids,
- get_filter_set_by_name,
-)
-from tests.integration_tests.test_app import login
-
-if TYPE_CHECKING:
- from flask.testing import FlaskClient
-
- from superset.models.filter_set import FilterSet
-
-
-def merge_two_filter_set_dict(
- first: dict[Any, Any], second: dict[Any, Any]
-) -> dict[Any, Any]:
- for d in [first, second]:
- if JSON_METADATA_FIELD in d:
- if PARAMS_PROPERTY not in d:
- d.setdefault(PARAMS_PROPERTY, json.loads(d[JSON_METADATA_FIELD]))
- d.pop(JSON_METADATA_FIELD)
- return {**first, **second}
-
-
-def assert_filterset_was_not_updated(filter_set_dict: dict[str, Any]) -> None:
- assert filter_set_dict == get_filter_set_by_name(filter_set_dict["name"]).to_dict()
-
-
-def assert_filterset_updated(
- filter_set_dict_before: dict[str, Any], data_updated: dict[str, Any]
-) -> None:
- expected_data = merge_two_filter_set_dict(filter_set_dict_before, data_updated)
- assert expected_data == get_filter_set_by_name(expected_data["name"]).to_dict()
-
-
-class TestUpdateFilterSet:
- def test_with_dashboard_exists_filterset_not_exists__404(
- self,
- dashboard_id: int,
- filtersets: dict[str, list[FilterSet]],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- filter_set_id = max(collect_all_ids(filtersets)) + 1
-
- response = call_update_filter_set(
- client, {"id": filter_set_id}, {}, dashboard_id
- )
- # assert
- assert response.status_code == 404
-
- def test_with_dashboard_not_exists_filterset_not_exists__404(
- self,
- not_exists_dashboard_id: int,
- filtersets: dict[str, list[FilterSet]],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- filter_set_id = max(collect_all_ids(filtersets)) + 1
-
- response = call_update_filter_set(
- client, {"id": filter_set_id}, {}, not_exists_dashboard_id
- )
- # assert
- assert response.status_code == 404
-
- def test_with_dashboard_not_exists_filterset_exists__404(
- self,
- not_exists_dashboard_id: int,
- dashboard_based_filter_set_dict: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, {}, not_exists_dashboard_id
- )
- # assert
- assert response.status_code == 404
- assert_filterset_was_not_updated(dashboard_based_filter_set_dict)
-
- def test_with_extra_field__400(
- self,
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_update["extra"] = "val"
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 400
- assert response.json["message"]["extra"][0] == "Unknown field."
- assert_filterset_was_not_updated(dashboard_based_filter_set_dict)
-
- def test_with_id_field__400(
- self,
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_update["id"] = 1
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 400
- assert response.json["message"]["id"][0] == "Unknown field."
- assert_filterset_was_not_updated(dashboard_based_filter_set_dict)
-
- def test_with_none_name__400(
- self,
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_update[NAME_FIELD] = None
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_updated(dashboard_based_filter_set_dict)
-
- def test_with_int_as_name__400(
- self,
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_update[NAME_FIELD] = 4
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_updated(dashboard_based_filter_set_dict)
-
- def test_without_name__200(
- self,
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_update.pop(NAME_FIELD, None)
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 200
- assert_filterset_updated(
- dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- def test_with_none_description__400(
- self,
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_update[DESCRIPTION_FIELD] = None
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_updated(dashboard_based_filter_set_dict)
-
- def test_with_int_as_description__400(
- self,
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_update[DESCRIPTION_FIELD] = 1
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_updated(dashboard_based_filter_set_dict)
-
- def test_without_description__200(
- self,
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_update.pop(DESCRIPTION_FIELD, None)
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 200
- assert_filterset_updated(
- dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- def test_with_invalid_json_metadata__400(
- self,
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_update[DESCRIPTION_FIELD] = {}
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_updated(dashboard_based_filter_set_dict)
-
- def test_with_json_metadata__200(
- self,
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- valid_json_metadata: dict[Any, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_json_metadata["nativeFilters"] = {"changed": "changed"}
- valid_filter_set_data_for_update[JSON_METADATA_FIELD] = json.dumps(
- valid_json_metadata
- )
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 200
- assert_filterset_updated(
- dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- def test_with_invalid_owner_type__400(
- self,
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_update[OWNER_TYPE_FIELD] = "OTHER_TYPE"
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_updated(dashboard_based_filter_set_dict)
-
- def test_with_user_owner_type__400(
- self,
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_update[OWNER_TYPE_FIELD] = "User"
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 400
- assert_filterset_was_not_updated(dashboard_based_filter_set_dict)
-
- def test_with_dashboard_owner_type__200(
- self,
- user_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- valid_filter_set_data_for_update[OWNER_TYPE_FIELD] = "Dashboard"
-
- # act
- response = call_update_filter_set(
- client, user_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 200
- user_based_filter_set_dict["owner_id"] = user_based_filter_set_dict[
- "dashboard_id"
- ]
- assert_filterset_updated(
- user_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- def test_when_caller_is_admin_and_owner_type_is_user__200(
- self,
- test_users: dict[str, int],
- user_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- # act
- response = call_update_filter_set(
- client, user_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 200
- assert_filterset_updated(
- user_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- def test_when_caller_is_admin_and_owner_type_is_dashboard__200(
- self,
- test_users: dict[str, int],
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, "admin")
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 200
- assert_filterset_updated(
- dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- def test_when_caller_is_dashboard_owner_and_owner_is_other_user_403(
- self,
- test_users: dict[str, int],
- user_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, DASHBOARD_OWNER_USERNAME)
-
- # act
- response = call_update_filter_set(
- client, user_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 403
- assert_filterset_was_not_updated(user_based_filter_set_dict)
-
- def test_when_caller_is_dashboard_owner_and_owner_type_is_dashboard__200(
- self,
- test_users: dict[str, int],
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, DASHBOARD_OWNER_USERNAME)
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 200
- assert_filterset_updated(
- dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- def test_when_caller_is_filterset_owner__200(
- self,
- test_users: dict[str, int],
- user_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, FILTER_SET_OWNER_USERNAME)
-
- # act
- response = call_update_filter_set(
- client, user_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 200
- assert_filterset_updated(
- user_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- def test_when_caller_is_regular_user_and_owner_type_is_user__403(
- self,
- test_users: dict[str, int],
- user_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, REGULAR_USER)
-
- # act
- response = call_update_filter_set(
- client, user_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 403
- assert_filterset_was_not_updated(user_based_filter_set_dict)
-
- def test_when_caller_is_regular_user_and_owner_type_is_dashboard__403(
- self,
- test_users: dict[str, int],
- dashboard_based_filter_set_dict: dict[str, Any],
- valid_filter_set_data_for_update: dict[str, Any],
- client: FlaskClient[Any],
- ):
- # arrange
- login(client, REGULAR_USER)
-
- # act
- response = call_update_filter_set(
- client, dashboard_based_filter_set_dict, valid_filter_set_data_for_update
- )
-
- # assert
- assert response.status_code == 403
- assert_filterset_was_not_updated(dashboard_based_filter_set_dict)
diff --git a/tests/integration_tests/dashboards/filter_sets/utils.py b/tests/integration_tests/dashboards/filter_sets/utils.py
deleted file mode 100644
index d728bf6fc3d71..0000000000000
--- a/tests/integration_tests/dashboards/filter_sets/utils.py
+++ /dev/null
@@ -1,102 +0,0 @@
-# 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 __future__ import annotations
-
-from typing import Any, TYPE_CHECKING
-
-from superset.models.filter_set import FilterSet
-from tests.integration_tests.dashboards.filter_sets.consts import FILTER_SET_URI
-from tests.integration_tests.test_app import app
-
-if TYPE_CHECKING:
- from flask import Response
- from flask.testing import FlaskClient
-
-
-def call_create_filter_set(
- client: FlaskClient[Any], dashboard_id: int, data: dict[str, Any]
-) -> Response:
- uri = FILTER_SET_URI.format(dashboard_id=dashboard_id)
- return client.post(uri, json=data)
-
-
-def call_get_filter_sets(client: FlaskClient[Any], dashboard_id: int) -> Response:
- uri = FILTER_SET_URI.format(dashboard_id=dashboard_id)
- return client.get(uri)
-
-
-def call_delete_filter_set(
- client: FlaskClient[Any],
- filter_set_dict_to_update: dict[str, Any],
- dashboard_id: int | None = None,
-) -> Response:
- dashboard_id = (
- dashboard_id
- if dashboard_id is not None
- else filter_set_dict_to_update["dashboard_id"]
- )
- uri = "{}/{}".format(
- FILTER_SET_URI.format(dashboard_id=dashboard_id),
- filter_set_dict_to_update["id"],
- )
- return client.delete(uri)
-
-
-def call_update_filter_set(
- client: FlaskClient[Any],
- filter_set_dict_to_update: dict[str, Any],
- data: dict[str, Any],
- dashboard_id: int | None = None,
-) -> Response:
- dashboard_id = (
- dashboard_id
- if dashboard_id is not None
- else filter_set_dict_to_update["dashboard_id"]
- )
- uri = "{}/{}".format(
- FILTER_SET_URI.format(dashboard_id=dashboard_id),
- filter_set_dict_to_update["id"],
- )
- return client.put(uri, json=data)
-
-
-def get_filter_set_by_name(name: str) -> FilterSet:
- with app.app_context():
- return FilterSet.get_by_name(name)
-
-
-def get_filter_set_by_id(id_: int) -> FilterSet:
- with app.app_context():
- return FilterSet.get(id_)
-
-
-def get_filter_set_by_dashboard_id(dashboard_id: int) -> FilterSet:
- with app.app_context():
- return FilterSet.get_by_dashboard_id(dashboard_id)
-
-
-def collect_all_ids(
- filtersets: dict[str, list[FilterSet]] | list[FilterSet]
-) -> set[int]:
- if isinstance(filtersets, dict):
- filtersets_lists: list[list[FilterSet]] = list(filtersets.values())
- ids: set[int] = set()
- lst: list[FilterSet]
- for lst in filtersets_lists:
- ids.update(set(map(lambda fs: fs.id, lst)))
- return ids
- return set(map(lambda fs: fs.id, filtersets))