Skip to content

Commit

Permalink
fix(Tags filter): Filter assets by tag ID (apache#29412)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vitor-Avila authored Jul 11, 2024
1 parent dd74757 commit 33b934c
Show file tree
Hide file tree
Showing 18 changed files with 636 additions and 109 deletions.
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 @@ -60,7 +60,8 @@
DashboardCreatedByMeFilter,
DashboardFavoriteFilter,
DashboardHasCreatedByFilter,
DashboardTagFilter,
DashboardTagIdFilter,
DashboardTagNameFilter,
DashboardTitleOrSlugFilter,
FilterRelatedRoles,
)
Expand Down Expand Up @@ -244,7 +245,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.error_handling 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

0 comments on commit 33b934c

Please sign in to comment.