Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Tags filter): Filter assets by tag ID #29412

Merged
merged 6 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions superset-frontend/src/components/ListView/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,10 @@ export enum FilterOperator {
DatasetIsCertified = 'dataset_is_certified',
DashboardHasCreatedBy = 'dashboard_has_created_by',
ChartHasCreatedBy = 'chart_has_created_by',
DashboardTags = 'dashboard_tags',
ChartTags = 'chart_tags',
SavedQueryTags = 'saved_query_tags',
DashboardTagByName = 'dashboard_tags',
DashboardTagById = 'dashboard_tag_id',
ChartTagByName = 'chart_tags',
ChartTagById = 'chart_tag_id',
SavedQueryTagByName = 'saved_query_tags',
SavedQueryTagById = 'saved_query_tag_id',
}
2 changes: 1 addition & 1 deletion superset-frontend/src/pages/ChartList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ function ChartList(props: ChartListProps) {
key: 'tags',
id: 'tags',
input: 'select',
operator: FilterOperator.ChartTags,
operator: FilterOperator.ChartTagById,
unfilteredLabel: t('All'),
fetchSelects: loadTags,
},
Expand Down
2 changes: 1 addition & 1 deletion superset-frontend/src/pages/DashboardList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ function DashboardList(props: DashboardListProps) {
key: 'tags',
id: 'tags',
input: 'select',
operator: FilterOperator.DashboardTags,
operator: FilterOperator.DashboardTagById,
unfilteredLabel: t('All'),
fetchSelects: loadTags,
},
Expand Down
2 changes: 1 addition & 1 deletion superset-frontend/src/pages/SavedQueryList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ function SavedQueryList({
id: 'tags',
key: 'tags',
input: 'select',
operator: FilterOperator.SavedQueryTags,
operator: FilterOperator.SavedQueryTagById,
fetchSelects: loadTags,
},
]
Expand Down
5 changes: 3 additions & 2 deletions superset/charts/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
ChartFilter,
ChartHasCreatedByFilter,
ChartOwnedCreatedFavoredByMeFilter,
ChartTagFilter,
ChartTagIdFilter,
ChartTagNameFilter,
)
from superset.charts.schemas import (
CHART_SCHEMAS,
Expand Down Expand Up @@ -238,7 +239,7 @@ def ensure_thumbnails_enabled(self) -> Optional[Response]:
],
"slice_name": [ChartAllTextFilter],
"created_by": [ChartHasCreatedByFilter, ChartCreatedByMeFilter],
"tags": [ChartTagFilter],
"tags": [ChartTagNameFilter, ChartTagIdFilter],
}
# Will just affect _info endpoint
edit_columns = ["slice_name"]
Expand Down
19 changes: 16 additions & 3 deletions superset/charts/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@
from superset.connectors.sqla.models import SqlaTable
from superset.models.core import FavStar
from superset.models.slice import Slice
from superset.tags.filters import BaseTagIdFilter, BaseTagNameFilter
from superset.utils.core import get_user_id
from superset.utils.filters import get_dataset_access_filters
from superset.views.base import BaseFilter
from superset.views.base_api import BaseFavoriteFilter, BaseTagFilter
from superset.views.base_api import BaseFavoriteFilter


class ChartAllTextFilter(BaseFilter): # pylint: disable=too-few-public-methods
Expand Down Expand Up @@ -60,16 +61,28 @@ class ChartFavoriteFilter(BaseFavoriteFilter): # pylint: disable=too-few-public
model = Slice


class ChartTagFilter(BaseTagFilter): # pylint: disable=too-few-public-methods
class ChartTagNameFilter(BaseTagNameFilter): # pylint: disable=too-few-public-methods
"""
Custom filter for the GET list that filters all dashboards that a user has favored
Custom filter for the GET list that filters all charts associated with
a certain tag (by its name).
"""

arg_name = "chart_tags"
class_name = "slice"
model = Slice


class ChartTagIdFilter(BaseTagIdFilter): # pylint: disable=too-few-public-methods
"""
Custom filter for the GET list that filters all charts associated with
a certain tag (by its ID).
"""

arg_name = "chart_tag_id"
class_name = "slice"
model = Slice


class ChartCertifiedFilter(BaseFilter): # pylint: disable=too-few-public-methods
"""
Custom filter for the GET list that filters all certified charts
Expand Down
5 changes: 3 additions & 2 deletions superset/dashboards/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
DashboardCreatedByMeFilter,
DashboardFavoriteFilter,
DashboardHasCreatedByFilter,
DashboardTagFilter,
DashboardTagIdFilter,
DashboardTagNameFilter,
DashboardTitleOrSlugFilter,
FilterRelatedRoles,
)
Expand Down Expand Up @@ -229,7 +230,7 @@ def ensure_thumbnails_enabled(self) -> Optional[Response]:
"dashboard_title": [DashboardTitleOrSlugFilter],
"id": [DashboardFavoriteFilter, DashboardCertifiedFilter],
"created_by": [DashboardCreatedByMeFilter, DashboardHasCreatedByFilter],
"tags": [DashboardTagFilter],
"tags": [DashboardTagIdFilter, DashboardTagNameFilter],
}

base_order = ("changed_on", "desc")
Expand Down
19 changes: 16 additions & 3 deletions superset/dashboards/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@
from superset.models.embedded_dashboard import EmbeddedDashboard
from superset.models.slice import Slice
from superset.security.guest_token import GuestTokenResourceType, GuestUser
from superset.tags.filters import BaseTagIdFilter, BaseTagNameFilter
from superset.utils.core import get_user_id
from superset.utils.filters import get_dataset_access_filters
from superset.views.base import BaseFilter
from superset.views.base_api import BaseFavoriteFilter, BaseTagFilter
from superset.views.base_api import BaseFavoriteFilter


class DashboardTitleOrSlugFilter(BaseFilter): # pylint: disable=too-few-public-methods
Expand Down Expand Up @@ -78,16 +79,28 @@ class DashboardFavoriteFilter( # pylint: disable=too-few-public-methods
model = Dashboard


class DashboardTagFilter(BaseTagFilter): # pylint: disable=too-few-public-methods
class DashboardTagNameFilter(BaseTagNameFilter): # pylint: disable=too-few-public-methods
"""
Custom filter for the GET list that filters all dashboards that a user has favored
Custom filter for the GET list that filters all dashboards associated with
a certain tag (by its name).
"""

arg_name = "dashboard_tags"
class_name = "Dashboard"
model = Dashboard


class DashboardTagIdFilter(BaseTagIdFilter): # pylint: disable=too-few-public-methods
"""
Custom filter for the GET list that filters all dashboards associated with
a certain tag (by its ID).
"""

arg_name = "dashboard_tag_id"
class_name = "Dashboard"
model = Dashboard


class DashboardAccessFilter(BaseFilter): # pylint: disable=too-few-public-methods
"""
List dashboards with the following criteria:
Expand Down
15 changes: 7 additions & 8 deletions superset/queries/saved_queries/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
from flask_appbuilder.models.sqla.interface import SQLAInterface
from flask_babel import ngettext

from superset import is_feature_enabled
from superset.commands.importers.exceptions import (
IncorrectFormatError,
NoValidFilesFoundError,
Expand All @@ -46,7 +45,8 @@
SavedQueryAllTextFilter,
SavedQueryFavoriteFilter,
SavedQueryFilter,
SavedQueryTagFilter,
SavedQueryTagIdFilter,
SavedQueryTagNameFilter,
)
from superset.queries.saved_queries.schemas import (
get_delete_ids_schema,
Expand Down Expand Up @@ -124,9 +124,10 @@ class SavedQueryRestApi(BaseSupersetModelRestApi):
"schema",
"sql",
"sql_tables",
"tags.id",
"tags.name",
"tags.type",
]
if is_feature_enabled("TAGGING_SYSTEM"):
list_columns += ["tags.id", "tags.name", "tags.type"]
list_select_columns = list_columns + ["changed_by_fk", "changed_on"]
add_columns = [
"db_id",
Expand Down Expand Up @@ -161,15 +162,13 @@ class SavedQueryRestApi(BaseSupersetModelRestApi):
"schema",
"created_by",
"changed_by",
"tags",
]
if is_feature_enabled("TAGGING_SYSTEM"):
search_columns += ["tags"]
search_filters = {
"id": [SavedQueryFavoriteFilter],
"label": [SavedQueryAllTextFilter],
"tags": [SavedQueryTagNameFilter, SavedQueryTagIdFilter],
}
if is_feature_enabled("TAGGING_SYSTEM"):
search_filters["tags"] = [SavedQueryTagFilter]

apispec_parameter_schemas = {
"get_delete_ids_schema": get_delete_ids_schema,
Expand Down
19 changes: 16 additions & 3 deletions superset/queries/saved_queries/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@
from sqlalchemy.orm.query import Query

from superset.models.sql_lab import SavedQuery
from superset.tags.filters import BaseTagIdFilter, BaseTagNameFilter
from superset.views.base import BaseFilter
from superset.views.base_api import BaseFavoriteFilter, BaseTagFilter
from superset.views.base_api import BaseFavoriteFilter


class SavedQueryAllTextFilter(BaseFilter): # pylint: disable=too-few-public-methods
Expand Down Expand Up @@ -56,16 +57,28 @@ class SavedQueryFavoriteFilter(BaseFavoriteFilter): # pylint: disable=too-few-p
model = SavedQuery


class SavedQueryTagFilter(BaseTagFilter): # pylint: disable=too-few-public-methods
class SavedQueryTagNameFilter(BaseTagNameFilter): # pylint: disable=too-few-public-methods
"""
Custom filter for the GET list that filters all dashboards that a user has favored
Custom filter for the GET list that filters all saved queries associated with
a certain tag (by its name).
"""

arg_name = "saved_query_tags"
class_name = "query"
model = SavedQuery


class SavedQueryTagIdFilter(BaseTagIdFilter): # pylint: disable=too-few-public-methods
"""
Custom filter for the GET list that filters all saved queries associated with
a certain tag (by its ID).
"""

arg_name = "saved_query_tag_id"
class_name = "query"
model = SavedQuery


class SavedQueryFilter(BaseFilter): # pylint: disable=too-few-public-methods
def apply(self, query: BaseQuery, value: Any) -> BaseQuery:
"""
Expand Down
54 changes: 54 additions & 0 deletions superset/tags/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,18 @@
# 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_babel import lazy_gettext as _
from sqlalchemy.orm import Query

from superset.connectors.sqla.models import SqlaTable
from superset.extensions import db
from superset.models.dashboard import Dashboard
from superset.models.slice import Slice
from superset.sql_lab import Query as SqllabQuery
from superset.tags.models import Tag, TagType
from superset.views.base import BaseFilter

Expand All @@ -37,3 +46,48 @@ def apply(self, query: Query, value: bool) -> Query:
if value is False:
return query.filter(Tag.type != TagType.custom)
return query


class BaseTagNameFilter(BaseFilter): # pylint: disable=too-few-public-methods
"""
Base Custom filter for the GET list that filters all dashboards, slices
and saved queries associated with a tag (by the tag name).
"""

name = _("Is tagged")
arg_name = ""
class_name = ""
""" The Tag class_name to user """
model: type[Dashboard | Slice | SqllabQuery | SqlaTable] = Dashboard
""" The SQLAlchemy model """

def apply(self, query: Query, value: Any) -> Query:
ilike_value = f"%{value}%"
tags_query = (
db.session.query(self.model.id)
.join(self.model.tags)
.filter(Tag.name.ilike(ilike_value))
)
return query.filter(self.model.id.in_(tags_query))


class BaseTagIdFilter(BaseFilter): # pylint: disable=too-few-public-methods
"""
Base Custom filter for the GET list that filters all dashboards, slices
and saved queries associated with a tag (by the tag ID).
"""

name = _("Is tagged")
arg_name = ""
class_name = ""
""" The Tag class_name to user """
model: type[Dashboard | Slice | SqllabQuery | SqlaTable] = Dashboard
""" The SQLAlchemy model """

def apply(self, query: Query, value: Any) -> Query:
tags_query = (
db.session.query(self.model.id)
.join(self.model.tags)
.filter(Tag.id == value)
)
return query.filter(self.model.id.in_(tags_query))
25 changes: 0 additions & 25 deletions superset/views/base_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
from sqlalchemy import and_, distinct, func
from sqlalchemy.orm.query import Query

from superset.connectors.sqla.models import SqlaTable
from superset.exceptions import InvalidPayloadFormatError
from superset.extensions import db, event_logger, security_manager, stats_logger_manager
from superset.models.core import FavStar
Expand All @@ -40,7 +39,6 @@
from superset.schemas import error_payload_content
from superset.sql_lab import Query as SqllabQuery
from superset.superset_typing import FlaskResponse
from superset.tags.models import Tag
from superset.utils.core import get_user_id, time_function
from superset.views.base import handle_api_exception

Expand Down Expand Up @@ -168,29 +166,6 @@ def apply(self, query: Query, value: Any) -> Query:
return query.filter(and_(~self.model.id.in_(users_favorite_query)))


class BaseTagFilter(BaseFilter): # pylint: disable=too-few-public-methods
"""
Base Custom filter for the GET list that filters all dashboards, slices
that a user has favored or not
"""

name = _("Is tagged")
arg_name = ""
class_name = ""
""" The Tag class_name to user """
model: type[Dashboard | Slice | SqllabQuery | SqlaTable] = Dashboard
""" The SQLAlchemy model """

def apply(self, query: Query, value: Any) -> Query:
ilike_value = f"%{value}%"
tags_query = (
db.session.query(self.model.id)
.join(self.model.tags)
.filter(Tag.name.ilike(ilike_value))
)
return query.filter(self.model.id.in_(tags_query))


class BaseSupersetApiMixin:
csrf_exempt = False

Expand Down
Loading
Loading