diff --git a/superset/dashboards/api.py b/superset/dashboards/api.py index 5039fa8d64978..eb4f796cd0d36 100644 --- a/superset/dashboards/api.py +++ b/superset/dashboards/api.py @@ -36,7 +36,7 @@ DashboardUpdateFailedError, ) from superset.dashboards.commands.update import UpdateDashboardCommand -from superset.dashboards.filters import DashboardFilter +from superset.dashboards.filters import DashboardFilter, DashboardTitleOrSlugFilter from superset.dashboards.schemas import ( DashboardPostSchema, DashboardPutSchema, @@ -104,6 +104,7 @@ class DashboardRestApi(BaseSupersetModelRestApi): "published", ] search_columns = ("dashboard_title", "slug", "owners", "published") + search_filters = {"dashboard_title": [DashboardTitleOrSlugFilter]} add_columns = edit_columns base_order = ("changed_on", "desc") diff --git a/superset/dashboards/filters.py b/superset/dashboards/filters.py index c72894aa1874b..05a6f6e4a844a 100644 --- a/superset/dashboards/filters.py +++ b/superset/dashboards/filters.py @@ -16,6 +16,7 @@ # under the License. from typing import Any +from flask_babel import lazy_gettext as _ from sqlalchemy import and_, or_ from sqlalchemy.orm.query import Query @@ -26,6 +27,22 @@ from superset.views.base import BaseFilter, get_user_roles +class DashboardTitleOrSlugFilter(BaseFilter): # pylint: disable=too-few-public-methods + name = _("Title or Slug") + arg_name = "title_or_slug" + + def apply(self, query: Query, value: Any) -> Query: + if not value: + return query + ilike_value = f"%{value}%" + return query.filter( + or_( + Dashboard.dashboard_title.ilike(ilike_value), + Dashboard.slug.ilike(ilike_value), + ) + ) + + class DashboardFilter(BaseFilter): # pylint: disable=too-few-public-methods """ List dashboards with the following criteria: diff --git a/tests/dashboards/api_tests.py b/tests/dashboards/api_tests.py index 881e897d51742..1d2241e089197 100644 --- a/tests/dashboards/api_tests.py +++ b/tests/dashboards/api_tests.py @@ -179,6 +179,43 @@ def test_get_dashboards_filter(self): db.session.delete(dashboard) db.session.commit() + def test_get_dashboards_custom_filter(self): + """ + Dashboard API: Test get dashboards custom filter + """ + admin = self.get_user("admin") + dashboard1 = self.insert_dashboard("foo", "ZY_bar", [admin.id]) + dashboard2 = self.insert_dashboard("zy_foo", "slug1", [admin.id]) + dashboard3 = self.insert_dashboard("foo", "slug1zy_", [admin.id]) + dashboard4 = self.insert_dashboard("bar", "foo", [admin.id]) + + arguments = { + "filters": [ + {"col": "dashboard_title", "opr": "title_or_slug", "value": "zy_"} + ] + } + self.login(username="admin") + uri = f"api/v1/dashboard/?q={prison.dumps(arguments)}" + rv = self.client.get(uri) + self.assertEqual(rv.status_code, 200) + data = json.loads(rv.data.decode("utf-8")) + self.assertEqual(data["count"], 3) + + self.logout() + self.login(username="gamma") + uri = f"api/v1/dashboard/?q={prison.dumps(arguments)}" + rv = self.client.get(uri) + self.assertEqual(rv.status_code, 200) + data = json.loads(rv.data.decode("utf-8")) + self.assertEqual(data["count"], 0) + + # rollback changes + db.session.delete(dashboard1) + db.session.delete(dashboard2) + db.session.delete(dashboard3) + db.session.delete(dashboard4) + db.session.commit() + def test_get_dashboards_no_data_access(self): """ Dashboard API: Test get dashboards no data access