diff --git a/src/sentry/api/serializers/models/dashboard.py b/src/sentry/api/serializers/models/dashboard.py index 66d5cd7211c294..4607c1ca40e2da 100644 --- a/src/sentry/api/serializers/models/dashboard.py +++ b/src/sentry/api/serializers/models/dashboard.py @@ -516,7 +516,6 @@ def get_filters(self, obj: Dashboard) -> tuple[PageFilters, DashboardFilters]: class DashboardListSerializer(Serializer, DashboardFiltersMixin): def get_attrs(self, item_list, user, **kwargs): - organization = kwargs.get("context", {}).get("organization") item_dict = {i.id: i for i in item_list} prefetch_related_objects( item_list, "projects__organization", "dashboardlastvisited_set__member" @@ -582,19 +581,7 @@ def get_attrs(self, item_list, user, **kwargs): result[dashboard]["permissions"] = serialize(permission) for dashboard in item_dict.values(): - if features.has( - "organizations:dashboards-starred-reordering", - organization, - actor=user, - ): - visit = dashboard.dashboardlastvisited_set.filter( - dashboard=dashboard, - member__user_id=user.id, - member__organization=organization, - ).first() - result[dashboard]["last_visited"] = visit.last_visited if visit else None - else: - result[dashboard]["last_visited"] = dashboard.last_visited + result[dashboard]["last_visited"] = dashboard.last_visited result[dashboard]["created_by"] = serialized_users.get(str(dashboard.created_by_id)) result[dashboard]["is_favorited"] = dashboard.id in favorited_dashboard_ids diff --git a/src/sentry/api/urls.py b/src/sentry/api/urls.py index 4cd2c97bbceb6c..b90085c229beb9 100644 --- a/src/sentry/api/urls.py +++ b/src/sentry/api/urls.py @@ -173,10 +173,6 @@ OrganizationDashboardWidgetDetailsEndpoint, ) from sentry.dashboards.endpoints.organization_dashboards import OrganizationDashboardsEndpoint -from sentry.dashboards.endpoints.organization_dashboards_starred import ( - OrganizationDashboardsStarredEndpoint, - OrganizationDashboardsStarredOrderEndpoint, -) from sentry.data_export.endpoints.data_export import DataExportEndpoint from sentry.data_export.endpoints.data_export_details import DataExportDetailsEndpoint from sentry.data_secrecy.api.waive_data_secrecy import WaiveDataSecrecyEndpoint @@ -1593,16 +1589,6 @@ def create_group_urls(name_prefix: str) -> list[URLPattern | URLResolver]: OrganizationDashboardWidgetDetailsEndpoint.as_view(), name="sentry-api-0-organization-dashboard-widget-details", ), - re_path( - r"^(?P[^/]+)/dashboards/starred/$", - OrganizationDashboardsStarredEndpoint.as_view(), - name="sentry-api-0-organization-dashboard-starred", - ), - re_path( - r"^(?P[^/]+)/dashboards/starred/order/$", - OrganizationDashboardsStarredOrderEndpoint.as_view(), - name="sentry-api-0-organization-dashboard-starred-order", - ), re_path( r"^(?P[^/]+)/dashboards/generate/$", OrganizationDashboardGenerateEndpoint.as_view(), diff --git a/src/sentry/dashboards/endpoints/organization_dashboard_details.py b/src/sentry/dashboards/endpoints/organization_dashboard_details.py index 8417581fe624bd..6807a3172fc13f 100644 --- a/src/sentry/dashboards/endpoints/organization_dashboard_details.py +++ b/src/sentry/dashboards/endpoints/organization_dashboard_details.py @@ -30,7 +30,6 @@ from sentry.dashboards.endpoints.organization_dashboards import OrganizationDashboardsPermission from sentry.models.dashboard import ( Dashboard, - DashboardFavoriteUser, DashboardLastVisited, DashboardRevision, ) @@ -280,23 +279,6 @@ def put(self, request: Request, organization: Organization, dashboard: Dashboard is_favorited = request.data.get("isFavorited") - if features.has( - "organizations:dashboards-starred-reordering", organization, actor=request.user - ): - if is_favorited: - DashboardFavoriteUser.objects.insert_favorite_dashboard( - organization=organization, - user_id=request.user.id, - dashboard=dashboard, - ) - else: - DashboardFavoriteUser.objects.unfavorite_dashboard( - organization=organization, - user_id=request.user.id, - dashboard=dashboard, - ) - return Response(status=204) - current_favorites = set(dashboard.favorited_by) if is_favorited and request.user.id not in current_favorites: diff --git a/src/sentry/dashboards/endpoints/organization_dashboards.py b/src/sentry/dashboards/endpoints/organization_dashboards.py index 4ea58953ba4e84..e2a0d9a89ec5a9 100644 --- a/src/sentry/dashboards/endpoints/organization_dashboards.py +++ b/src/sentry/dashboards/endpoints/organization_dashboards.py @@ -7,14 +7,10 @@ from django.db import IntegrityError, router, transaction from django.db.models import ( Case, - Count, Exists, - F, IntegerField, OrderBy, OuterRef, - Q, - Subquery, Value, When, ) @@ -49,7 +45,7 @@ from sentry.auth.superuser import is_active_superuser from sentry.db.models.fields.text import CharField from sentry.locks import locks -from sentry.models.dashboard import Dashboard, DashboardFavoriteUser, DashboardLastVisited +from sentry.models.dashboard import Dashboard, DashboardFavoriteUser from sentry.models.organization import Organization from sentry.organizations.services.organization.model import ( RpcOrganization, @@ -463,28 +459,7 @@ def get(self, request: Request, organization: Organization) -> Response: ] elif sort_by == "recentlyViewed": - if features.has( - "organizations:dashboards-starred-reordering", organization, actor=request.user - ): - dashboards = dashboards.annotate( - user_last_visited=Subquery( - DashboardLastVisited.objects.filter( - dashboard=OuterRef("pk"), - member__user_id=request.user.id, - member__organization=organization, - ).values("last_visited") - ) - ) - order_by = [ - ( - F("user_last_visited").asc(nulls_last=True) - if desc - else F("user_last_visited").desc(nulls_last=True) - ), - "-date_added", - ] - else: - order_by = ["last_visited" if desc else "-last_visited"] + order_by = ["last_visited" if desc else "-last_visited"] elif sort_by == "mydashboards": user_name_dict = { @@ -525,21 +500,6 @@ def get(self, request: Request, organization: Organization) -> Response: "-last_visited", ] - elif sort_by == "mostFavorited" and features.has( - "organizations:dashboards-starred-reordering", organization, actor=request.user - ): - dashboards = dashboards.annotate( - favorites_count=Count( - "dashboardfavoriteuser", - filter=Q(dashboardfavoriteuser__favorited=True), - distinct=True, - ) - ) - order_by = [ - "favorites_count" if desc else "-favorites_count", - "-date_added", - ] - else: order_by = ["title"] @@ -641,21 +601,6 @@ def post(self, request: Request, organization: Organization, retry: int = 0) -> dashboard = serializer.save() - if features.has( - "organizations:dashboards-starred-reordering", - organization, - actor=request.user, - ): - if serializer.validated_data.get("is_favorited"): - try: - DashboardFavoriteUser.objects.insert_favorite_dashboard( - organization=organization, - user_id=request.user.id, - dashboard=dashboard, - ) - except Exception as e: - sentry_sdk.capture_exception(e) - return Response(serialize(dashboard, request.user), status=201) except IntegrityError: if retry >= MAX_RETRIES: diff --git a/src/sentry/dashboards/endpoints/organization_dashboards_starred.py b/src/sentry/dashboards/endpoints/organization_dashboards_starred.py deleted file mode 100644 index bbd1a2f8cafc71..00000000000000 --- a/src/sentry/dashboards/endpoints/organization_dashboards_starred.py +++ /dev/null @@ -1,113 +0,0 @@ -import sentry_sdk -from django.db import IntegrityError, router, transaction -from rest_framework import status -from rest_framework.exceptions import ParseError -from rest_framework.request import Request -from rest_framework.response import Response - -from sentry import features -from sentry.api.api_owners import ApiOwner -from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint -from sentry.api.bases.organization import OrganizationEndpoint, OrganizationPermission -from sentry.api.paginator import GenericOffsetPaginator -from sentry.api.serializers.base import serialize -from sentry.api.serializers.models.dashboard import DashboardListSerializer -from sentry.api.serializers.rest_framework.dashboard import DashboardStarredOrderSerializer -from sentry.models.dashboard import Dashboard, DashboardFavoriteUser -from sentry.models.organization import Organization - - -class MemberPermission(OrganizationPermission): - scope_map = { - "GET": ["member:read", "member:write"], - "PUT": ["member:read", "member:write"], - } - - -@cell_silo_endpoint -class OrganizationDashboardsStarredEndpoint(OrganizationEndpoint): - publish_status = {"GET": ApiPublishStatus.PRIVATE} - owner = ApiOwner.DASHBOARDS - permission_classes = (MemberPermission,) - - def has_feature(self, organization: Organization, request: Request) -> bool: - return features.has( - "organizations:dashboards-starred-reordering", organization, actor=request.user - ) - - def get(self, request: Request, organization: Organization) -> Response: - if not request.user.is_authenticated: - return Response(status=status.HTTP_400_BAD_REQUEST) - - if not self.has_feature(organization, request): - return self.respond(status=status.HTTP_404_NOT_FOUND) - - favorites = DashboardFavoriteUser.objects.get_favorite_dashboards( - organization=organization, user_id=request.user.id - ).select_related("dashboard") - - if favorites.filter(position__isnull=True).exists(): - # Commit the order of the dashboards of this current response if there are any - # that don't have a position assigned - DashboardFavoriteUser.objects.reorder_favorite_dashboards( - organization=organization, - user_id=request.user.id, - new_dashboard_positions=[favorite.dashboard.id for favorite in favorites], - ) - - def data_fn(offset: int, limit: int) -> list[Dashboard]: - return [favorite.dashboard for favorite in favorites[offset : offset + limit]] - - return self.paginate( - request=request, - paginator=GenericOffsetPaginator(data_fn=data_fn), - on_results=lambda x: serialize( - x, - request.user, - serializer=DashboardListSerializer(), - context={"organization": organization}, - ), - default_per_page=25, - ) - - -@cell_silo_endpoint -class OrganizationDashboardsStarredOrderEndpoint(OrganizationEndpoint): - publish_status = {"PUT": ApiPublishStatus.PRIVATE} - owner = ApiOwner.DASHBOARDS - permission_classes = (MemberPermission,) - - def has_feature(self, organization: Organization, request: Request) -> bool: - return features.has( - "organizations:dashboards-starred-reordering", organization, actor=request.user - ) - - def put(self, request: Request, organization: Organization) -> Response: - if not request.user.is_authenticated: - return Response(status=status.HTTP_400_BAD_REQUEST) - - if not self.has_feature(organization, request): - return self.respond(status=status.HTTP_404_NOT_FOUND) - - serializer = DashboardStarredOrderSerializer( - data=request.data, context={"organization": organization, "user": request.user} - ) - - if not serializer.is_valid(): - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - dashboard_ids = serializer.validated_data["dashboard_ids"] - - try: - with transaction.atomic(using=router.db_for_write(DashboardFavoriteUser)): - DashboardFavoriteUser.objects.reorder_favorite_dashboards( - organization=organization, - user_id=request.user.id, - new_dashboard_positions=dashboard_ids, - ) - except (IntegrityError, ValueError) as e: - sentry_sdk.capture_exception(e) - raise ParseError("Mismatch between existing and provided starred dashboards.") - - return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/src/sentry/features/temporary.py b/src/sentry/features/temporary.py index 83c6cedf3fe8c0..a63da612c72f5f 100644 --- a/src/sentry/features/temporary.py +++ b/src/sentry/features/temporary.py @@ -80,8 +80,6 @@ def register_temporary_features(manager: FeatureManager) -> None: manager.add("organizations:dashboards-import", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) # Enable metrics enhanced performance for AM2+ customers as they transition from AM2 to AM3 manager.add("organizations:dashboards-metrics-transition", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) - # Enable starred dashboards with reordering - manager.add("organizations:dashboards-starred-reordering", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) # Enable drilldown flow for dashboards manager.add("organizations:dashboards-drilldown-flow", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) # Enable prebuilt dashboards for insights modules diff --git a/static/app/utils/api/knownSentryApiUrls.generated.ts b/static/app/utils/api/knownSentryApiUrls.generated.ts index a25a807986fe0e..cbee902f322b6a 100644 --- a/static/app/utils/api/knownSentryApiUrls.generated.ts +++ b/static/app/utils/api/knownSentryApiUrls.generated.ts @@ -223,8 +223,6 @@ export type KnownSentryApiUrls = | '/organizations/$organizationIdOrSlug/dashboards/$dashboardId/revisions/$revisionId/restore/' | '/organizations/$organizationIdOrSlug/dashboards/$dashboardId/visit/' | '/organizations/$organizationIdOrSlug/dashboards/generate/' - | '/organizations/$organizationIdOrSlug/dashboards/starred/' - | '/organizations/$organizationIdOrSlug/dashboards/starred/order/' | '/organizations/$organizationIdOrSlug/dashboards/widgets/' | '/organizations/$organizationIdOrSlug/data-conditions/' | '/organizations/$organizationIdOrSlug/data-export/' diff --git a/tests/sentry/dashboards/endpoints/test_organization_dashboard_details.py b/tests/sentry/dashboards/endpoints/test_organization_dashboard_details.py index 6157157c427ca2..730eabd0236657 100644 --- a/tests/sentry/dashboards/endpoints/test_organization_dashboard_details.py +++ b/tests/sentry/dashboards/endpoints/test_organization_dashboard_details.py @@ -14,7 +14,6 @@ from sentry.explore.translation.dashboards_translation import translate_dashboard_widget from sentry.models.dashboard import ( Dashboard, - DashboardFavoriteUser, DashboardLastVisited, DashboardRevision, ) @@ -4797,130 +4796,3 @@ def test_favorite_dashboard_no_dashboard_edit_access(self) -> None: response = self.do_request("put", self.url(self.dashboard.id), data={"isFavorited": False}) assert response.status_code == 204 assert self.user_2.id not in self.dashboard.favorited_by - - -class OrganizationDashboardFavoriteReorderingTest(OrganizationDashboardDetailsTestCase): - """ - These tests are intended to cover and eventually replace the existing - OrganizationDashboardFavoriteTest cases. - - They are updated as necessary to match the new functionality and - constraints regarding the position maintenance of the dashboard favorites. - """ - - features = ["organizations:dashboards-starred-reordering"] - - def do_request(self, *args, **kwargs): - with self.feature(self.features): - return super().do_request(*args, **kwargs) - - def setUp(self) -> None: - super().setUp() - # Create two additional users - self.user_1 = self.create_user(email="user1@example.com") - self.user_2 = self.create_user(email="user2@example.com") - self.create_member(user=self.user_1, organization=self.organization) - self.create_member(user=self.user_2, organization=self.organization) - - # Both users have favorited the dashboard - DashboardFavoriteUser.objects.insert_favorite_dashboard( - organization=self.organization, - user_id=self.user_1.id, - dashboard=self.dashboard, - ) - DashboardFavoriteUser.objects.insert_favorite_dashboard( - organization=self.organization, - user_id=self.user_2.id, - dashboard=self.dashboard, - ) - - def url(self, dashboard_id): - return reverse( - "sentry-api-0-organization-dashboard-favorite", - kwargs={ - "organization_id_or_slug": self.organization.slug, - "dashboard_id": dashboard_id, - }, - ) - - # PUT tests - def test_favorite_dashboard(self) -> None: - assert self.user.id not in self.dashboard.favorited_by - self.login_as(user=self.user) - - # Insert an initial starred dashboard for this user - initial_dashboard = Dashboard.objects.create( - title="Other Dashboard", - created_by_id=self.user.id, - organization=self.organization, - ) - DashboardFavoriteUser.objects.insert_favorite_dashboard( - organization=self.organization, - user_id=self.user.id, - dashboard=initial_dashboard, - ) - response = self.do_request("put", self.url(self.dashboard.id), data={"isFavorited": "true"}) - assert response.status_code == 204 - - # Assert that the dashboard is added to the end of the list by its position - assert list( - DashboardFavoriteUser.objects.filter( - organization=self.organization, - user_id=self.user.id, - ) - .order_by("position") - .values_list("dashboard_id", flat=True) - ) == [ - initial_dashboard.id, - self.dashboard.id, - ] - - def test_unfavorite_dashboard(self) -> None: - assert self.user_1.id in self.dashboard.favorited_by - self.login_as(user=self.user_1) - response = self.do_request("put", self.url(self.dashboard.id), data={"isFavorited": False}) - assert response.status_code == 204 - assert ( - DashboardFavoriteUser.objects.get_favorite_dashboard( - organization=self.organization, - user_id=self.user_1.id, - dashboard=self.dashboard, - ) - is None - ) - - def test_favorite_dashboard_no_dashboard_edit_access(self) -> None: - DashboardPermissions.objects.create(is_editable_by_everyone=False, dashboard=self.dashboard) - self.login_as(user=self.user_2) - dashboard_detail_url = reverse( - "sentry-api-0-organization-dashboard-details", - kwargs={ - "organization_id_or_slug": self.organization.slug, - "dashboard_id": self.dashboard.id, - }, - ) - response = self.do_request("put", dashboard_detail_url, data={"title": "New Dashboard 9"}) - # assert user cannot edit dashboard - assert response.status_code == 403 - - # assert if user can edit the favorite status of the dashboard - assert ( - DashboardFavoriteUser.objects.get_favorite_dashboard( - organization=self.organization, - user_id=self.user_2.id, - dashboard=self.dashboard, - ) - is not None - ) - response = self.do_request("put", self.url(self.dashboard.id), data={"isFavorited": False}) - - # The dashboard was successfully unfavorited - assert response.status_code == 204 - assert ( - DashboardFavoriteUser.objects.get_favorite_dashboard( - organization=self.organization, - user_id=self.user_2.id, - dashboard=self.dashboard, - ) - is None - ) diff --git a/tests/sentry/dashboards/endpoints/test_organization_dashboards.py b/tests/sentry/dashboards/endpoints/test_organization_dashboards.py index e439636483b30d..65814739b62493 100644 --- a/tests/sentry/dashboards/endpoints/test_organization_dashboards.py +++ b/tests/sentry/dashboards/endpoints/test_organization_dashboards.py @@ -13,7 +13,6 @@ from sentry.models.dashboard import ( Dashboard, DashboardFavoriteUser, - DashboardLastVisited, ) from sentry.models.dashboard_widget import ( DashboardWidget, @@ -21,7 +20,6 @@ DashboardWidgetQuery, DashboardWidgetTypes, ) -from sentry.models.organizationmember import OrganizationMember from sentry.testutils.cases import OrganizationDashboardWidgetTestCase from sentry.testutils.helpers.datetime import before_now from sentry.testutils.helpers.options import override_options @@ -152,49 +150,6 @@ def test_get_sortby_recently_viewed(self) -> None: assert values == expected - def test_get_sortby_recently_viewed_user_last_visited(self) -> None: - dashboard_a = Dashboard.objects.create( - title="A", - created_by_id=self.user.id, - organization=self.organization, - ) - dashboard_b = Dashboard.objects.create( - title="B", - created_by_id=self.user.id, - organization=self.organization, - ) - DashboardLastVisited.objects.create( - dashboard=dashboard_a, - member=OrganizationMember.objects.get( - organization=self.organization, user_id=self.user.id - ), - last_visited=before_now(minutes=5), - ) - DashboardLastVisited.objects.create( - dashboard=dashboard_b, - member=OrganizationMember.objects.get( - organization=self.organization, user_id=self.user.id - ), - last_visited=before_now(minutes=0), - ) - - for forward_sort in [True, False]: - sorting = "recentlyViewed" if forward_sort else "-recentlyViewed" - - with self.feature("organizations:dashboards-starred-reordering"): - response = self.client.get(self.url, data={"sort": sorting}) - - assert response.status_code == 200 - values = [row["title"] for row in response.data] - expected = ["B", "A"] - - if not forward_sort: - expected = list(reversed(expected)) - - # Only A, B are sorted by their last visited entry, Dashboard 1 - # and Dashboard 2 are by default sorted by their date created - assert values == expected + ["Dashboard 2", "Dashboard 1"] - def test_get_sortby_mydashboards(self) -> None: user_1 = self.create_user(username="user_1") self.create_member(organization=self.organization, user=user_1) @@ -887,93 +842,6 @@ def test_get_last_visited_field_without_reordering_flag(self) -> None: visited_at = [row.get("lastVisited") for row in response.data] assert visited_at == [now, one_hour_ago] - def test_get_with_last_visited(self) -> None: - # Clean up existing dashboards setup for this test. - Dashboard.objects.all().delete() - - Dashboard.objects.create( - title="Dashboard without last visited", - organization=self.organization, - created_by_id=self.user.id, - ) - dashboard_2 = Dashboard.objects.create( - title="Dashboard with last visited", - organization=self.organization, - created_by_id=self.user.id, - ) - now = before_now(minutes=0) - DashboardLastVisited.objects.create( - dashboard=dashboard_2, - member=OrganizationMember.objects.get( - organization=self.organization, user_id=self.user.id - ), - last_visited=now, - ) - - with self.feature("organizations:dashboards-starred-reordering"): - response = self.client.get(self.url, data={"sort": "recentlyViewed"}) - assert response.status_code == 200, response.content - assert len(response.data) == 2 - - titles = [row["title"] for row in response.data] - assert titles == [ - "Dashboard with last visited", - "Dashboard without last visited", - ] - - visited_at = [row.get("lastVisited") for row in response.data] - assert visited_at == [now, None] - - def test_get_recently_viewed_sort_with_favorites_from_other_user(self) -> None: - other_user = self.create_user(username="other_user") - self.create_member(organization=self.organization, user=other_user) - - Dashboard.objects.all().delete() - dashboard_1 = Dashboard.objects.create( - title="Dashboard 1", - created_by_id=other_user.id, - organization=self.organization, - ) - - # Both users have the same dashboard in their favorites - DashboardFavoriteUser.objects.insert_favorite_dashboard( - organization=self.organization, - user_id=self.user.id, - dashboard=dashboard_1, - ) - DashboardFavoriteUser.objects.insert_favorite_dashboard( - organization=self.organization, - user_id=other_user.id, - dashboard=dashboard_1, - ) - - # Both users have recently visited the dashboard - DashboardLastVisited.objects.create( - dashboard=dashboard_1, - member=OrganizationMember.objects.get( - organization=self.organization, user_id=self.user.id - ), - last_visited=before_now(minutes=0), - ) - DashboardLastVisited.objects.create( - dashboard=dashboard_1, - member=OrganizationMember.objects.get( - organization=self.organization, user_id=other_user.id - ), - last_visited=before_now(minutes=2), - ) - - with self.feature("organizations:dashboards-starred-reordering"): - response = self.client.get( - self.url, data={"sort": "recentlyViewed", "pin": "favorites"} - ) - assert response.status_code == 200, response.content - - # Assert that the dashboard did not receive a duplicate entry due to being - # favorited by another user - assert len(response.data) == 1 - self.assert_equal_dashboards(dashboard_1, response.data[0]) - def test_post(self) -> None: response = self.do_request("post", self.url, data={"title": "Dashboard from Post"}) assert response.status_code == 201 @@ -2006,80 +1874,6 @@ def test_response_includes_project_ids(self) -> None: starred_dashboard = response.data[1] assert starred_dashboard["projects"] == [] - def test_automatically_favorites_dashboard_when_isFavorited_is_true(self) -> None: - data = { - "title": "Dashboard with errors widget", - "isFavorited": True, - } - with self.feature("organizations:dashboards-starred-reordering"): - response = self.do_request("post", self.url, data=data) - assert response.status_code == 201, response.data - dashboard = Dashboard.objects.get( - organization=self.organization, title="Dashboard with errors widget" - ) - assert response.data["isFavorited"] is True - - assert ( - DashboardFavoriteUser.objects.get_favorite_dashboard( - organization=self.organization, user_id=self.user.id, dashboard=dashboard - ) - is not None - ) - - def test_does_not_automatically_favorite_dashboard_when_isFavorited_is_false(self) -> None: - data = { - "title": "Dashboard with errors widget", - "isFavorited": False, - } - with self.feature("organizations:dashboards-starred-reordering"): - response = self.do_request("post", self.url, data=data) - assert response.status_code == 201, response.data - dashboard = Dashboard.objects.get( - organization=self.organization, title="Dashboard with errors widget" - ) - assert response.data["isFavorited"] is False - - assert ( - DashboardFavoriteUser.objects.get_favorite_dashboard( - organization=self.organization, user_id=self.user.id, dashboard=dashboard - ) - is None - ) - - def test_order_by_most_favorited(self) -> None: - Dashboard.objects.all().delete() - - # A mapping from dashboard title to the number of times it was favorited - dashboards = { - "Dashboard 1": 0, - "Dashboard 2": 2, - "Dashboard 3": 1, - } - - # Set up a favorite entry for each dashboard by the number of times it was favorited - for title, favorited in dashboards.items(): - dashboard = self.create_dashboard(title=title, organization=self.organization) - if favorited: - for _ in range(favorited): - user = self.create_user() - DashboardFavoriteUser.objects.create( - dashboard=dashboard, - user_id=user.id, - organization=self.organization, - ) - - with self.feature("organizations:dashboards-starred-reordering"): - response = self.do_request( - "get", self.url, {"sort": "mostFavorited", "pin": "favorites"} - ) - - assert response.status_code == 200, response.content - assert [dashboard["title"] for dashboard in response.data] == [ - "Dashboard 2", - "Dashboard 3", - "Dashboard 1", - ] - @patch("sentry.quotas.backend.get_dashboard_limit") def test_dashboard_limit_prevents_creation(self, mock_get_dashboard_limit) -> None: mock_get_dashboard_limit.return_value = 1 diff --git a/tests/sentry/dashboards/endpoints/test_organization_dashboards_starred.py b/tests/sentry/dashboards/endpoints/test_organization_dashboards_starred.py deleted file mode 100644 index 98a17eeb74f450..00000000000000 --- a/tests/sentry/dashboards/endpoints/test_organization_dashboards_starred.py +++ /dev/null @@ -1,186 +0,0 @@ -from django.urls import reverse -from rest_framework.exceptions import ErrorDetail - -from sentry.models.dashboard import DashboardFavoriteUser -from sentry.testutils.cases import OrganizationDashboardWidgetTestCase - - -class StarredDashboardTestCase(OrganizationDashboardWidgetTestCase): - def create_dashboard_favorite(self, dashboard, user, organization, position): - DashboardFavoriteUser.objects.create( - dashboard=dashboard, user_id=user.id, organization=organization, position=position - ) - - def do_request(self, *args, **kwargs): - with self.feature("organizations:dashboards-starred-reordering"): - return super().do_request(*args, **kwargs) - - -class OrganizationDashboardsStarredTest(StarredDashboardTestCase): - def setUp(self) -> None: - super().setUp() - self.login_as(self.user) - self.url = reverse( - "sentry-api-0-organization-dashboard-starred", - kwargs={"organization_id_or_slug": self.organization.slug}, - ) - self.dashboard_1 = self.create_dashboard(title="Dashboard 1") - self.dashboard_2 = self.create_dashboard(title="Dashboard 2") - self.dashboard_3 = self.create_dashboard(title="Dashboard 3") - - def test_get_favorite_dashboards(self) -> None: - self.create_dashboard_favorite(self.dashboard_1, self.user, self.organization, 2) - self.create_dashboard_favorite(self.dashboard_2, self.user, self.organization, 0) - self.create_dashboard_favorite(self.dashboard_3, self.user, self.organization, 1) - - # Add a dashboard starred by another user to verify that it is not returned - other_user = self.create_user("other@example.com") - other_dashboard = self.create_dashboard(title="Other Dashboard") - self.create_dashboard_favorite(other_dashboard, other_user, self.organization, 0) - - response = self.do_request("get", self.url) - assert response.status_code == 200 - assert len(response.data) == 3 - assert [int(dashboard["id"]) for dashboard in response.data] == [ - self.dashboard_2.id, - self.dashboard_3.id, - self.dashboard_1.id, - ] - - def test_get_request_assigns_positions_if_missing(self) -> None: - self.create_dashboard_favorite(self.dashboard_1, self.user, self.organization, None) - self.create_dashboard_favorite(self.dashboard_2, self.user, self.organization, 2) - self.create_dashboard_favorite(self.dashboard_3, self.user, self.organization, None) - - response = self.do_request("get", self.url) - assert response.status_code == 200 - assert len(response.data) == 3 - assert [int(dashboard["id"]) for dashboard in response.data] == [ - self.dashboard_2.id, - self.dashboard_1.id, - self.dashboard_3.id, - ] - - assert ( - DashboardFavoriteUser.objects.get( - organization=self.organization, user_id=self.user.id, dashboard=self.dashboard_1 - ).position - == 1 - ) - assert ( - DashboardFavoriteUser.objects.get( - organization=self.organization, user_id=self.user.id, dashboard=self.dashboard_3 - ).position - == 2 - ) - - # Positioned dashboards are at the top of the list in this operation - assert ( - DashboardFavoriteUser.objects.get( - organization=self.organization, user_id=self.user.id, dashboard=self.dashboard_2 - ).position - == 0 - ) - - -class OrganizationDashboardsStarredOrderTest(StarredDashboardTestCase): - def setUp(self) -> None: - super().setUp() - self.login_as(self.user) - self.url = reverse( - "sentry-api-0-organization-dashboard-starred-order", - kwargs={"organization_id_or_slug": self.organization.slug}, - ) - self.dashboard_1 = self.create_dashboard(title="Dashboard 1") - self.dashboard_2 = self.create_dashboard(title="Dashboard 2") - self.dashboard_3 = self.create_dashboard(title="Dashboard 3") - - def test_reorder_dashboards(self) -> None: - self.create_dashboard_favorite(self.dashboard_1, self.user, self.organization, 0) - self.create_dashboard_favorite(self.dashboard_2, self.user, self.organization, 1) - self.create_dashboard_favorite(self.dashboard_3, self.user, self.organization, 2) - - assert list( - DashboardFavoriteUser.objects.filter( - organization=self.organization, user_id=self.user.id - ) - .order_by("position") - .values_list("dashboard_id", flat=True) - ) == [ - self.dashboard_1.id, - self.dashboard_2.id, - self.dashboard_3.id, - ] - - # Reorder the favorited dashboards - response = self.do_request( - "put", - self.url, - data={"dashboard_ids": [self.dashboard_3.id, self.dashboard_1.id, self.dashboard_2.id]}, - ) - assert response.status_code == 204 - - assert list( - DashboardFavoriteUser.objects.filter( - organization=self.organization, user_id=self.user.id - ) - .order_by("position") - .values_list("dashboard_id", flat=True) - ) == [ - self.dashboard_3.id, - self.dashboard_1.id, - self.dashboard_2.id, - ] - - def test_throws_an_error_if_dashboard_ids_are_not_unique(self) -> None: - self.create_dashboard_favorite(self.dashboard_1, self.user, self.organization, 0) - self.create_dashboard_favorite(self.dashboard_2, self.user, self.organization, 1) - self.create_dashboard_favorite(self.dashboard_3, self.user, self.organization, 2) - - response = self.do_request( - "put", - self.url, - data={"dashboard_ids": [self.dashboard_1.id, self.dashboard_1.id, self.dashboard_2.id]}, - ) - assert response.status_code == 400 - assert response.data == { - "dashboard_ids": ["Single dashboard cannot take up multiple positions"] - } - - def test_throws_an_error_if_reordered_dashboard_ids_are_not_complete(self) -> None: - self.create_dashboard_favorite(self.dashboard_1, self.user, self.organization, 0) - self.create_dashboard_favorite(self.dashboard_2, self.user, self.organization, 1) - self.create_dashboard_favorite(self.dashboard_3, self.user, self.organization, 2) - - response = self.do_request( - "put", - self.url, - data={"dashboard_ids": [self.dashboard_1.id, self.dashboard_2.id]}, - ) - assert response.status_code == 400 - assert response.data == { - "detail": ErrorDetail( - string="Mismatch between existing and provided starred dashboards.", - code="parse_error", - ) - } - - def test_allows_reordering_even_if_no_initial_positions(self) -> None: - self.create_dashboard_favorite(self.dashboard_1, self.user, self.organization, 0) - self.create_dashboard_favorite(self.dashboard_2, self.user, self.organization, 1) - self.create_dashboard_favorite(self.dashboard_3, self.user, self.organization, 2) - - response = self.do_request( - "put", - self.url, - data={"dashboard_ids": [self.dashboard_3.id, self.dashboard_1.id, self.dashboard_2.id]}, - ) - assert response.status_code == 204 - - assert list( - DashboardFavoriteUser.objects.filter( - organization=self.organization, user_id=self.user.id - ) - .order_by("position") - .values_list("dashboard_id", flat=True) - ) == [self.dashboard_3.id, self.dashboard_1.id, self.dashboard_2.id]