Skip to content

Commit 45fe10f

Browse files
narsaynorathandrewshie-sentry
authored andcommitted
feat(dashboards): Expose All Projects ID through project filter (#95246)
Ensures that when a dashboard has "All Projects" selected in their dashboard filters (as opposed to specific projects, or "My Projects"), that the list endpoint also returns `[-1]` in the response. This was discovered because we weren't exposing the projects data before, but in the new table work it is visible and I noticed it wasn't coming through.
1 parent fd1b5bb commit 45fe10f

File tree

3 files changed

+43
-5
lines changed

3 files changed

+43
-5
lines changed

src/sentry/api/serializers/models/dashboard.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from django.db.models import prefetch_related_objects
88

99
from sentry.api.serializers import Serializer, register, serialize
10-
from sentry.constants import ALL_ACCESS_PROJECTS
1110
from sentry.models.dashboard import Dashboard, DashboardFavoriteUser
1211
from sentry.models.dashboard_permissions import DashboardPermissions
1312
from sentry.models.dashboard_widget import (
@@ -275,6 +274,10 @@ def get_attrs(self, item_list, user, **kwargs):
275274
result[dashboard]["is_favorited"] = dashboard.id in favorited_dashboard_ids
276275
result[dashboard]["projects"] = list(dashboard.projects.values_list("id", flat=True))
277276

277+
filters = dashboard.get_filters()
278+
if filters and filters.get("projects"):
279+
result[dashboard]["projects"] = filters["projects"]
280+
278281
return result
279282

280283
def serialize(self, obj, attrs, user, **kwargs) -> DashboardListResponse:
@@ -340,22 +343,21 @@ def get_attrs(self, item_list, user, **kwargs):
340343
def serialize(self, obj, attrs, user, **kwargs) -> DashboardDetailsResponse:
341344
from sentry.api.serializers.rest_framework.base import camel_to_snake_case
342345

346+
dashboard_filters = obj.get_filters()
343347
data: DashboardDetailsResponse = {
344348
"id": str(obj.id),
345349
"title": obj.title,
346350
"dateCreated": obj.date_added,
347351
"createdBy": user_service.serialize_many(filter={"user_ids": [obj.created_by_id]})[0],
348352
"widgets": attrs["widgets"],
349-
"projects": [project.id for project in obj.projects.all()],
353+
"projects": dashboard_filters.get("projects", []),
350354
"filters": {},
351355
"permissions": serialize(obj.permissions) if hasattr(obj, "permissions") else None,
352356
"isFavorited": user.id in obj.favorited_by,
353357
}
354358

359+
# TODO: The logic for obtaining these filters will be moved to the Dashboard model.
355360
if obj.filters is not None:
356-
if obj.filters.get("all_projects"):
357-
data["projects"] = list(ALL_ACCESS_PROJECTS)
358-
359361
for tl_key in ("environment", "period", "utc"):
360362
if obj.filters.get(tl_key) is not None:
361363
data[tl_key] = obj.filters[tl_key]

src/sentry/models/dashboard.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from django.utils import timezone
1111

1212
from sentry.backup.scopes import RelocationScope
13+
from sentry.constants import ALL_ACCESS_PROJECT_ID
1314
from sentry.db.models import FlexibleForeignKey, Model, region_silo_model, sane_repr
1415
from sentry.db.models.base import DefaultFieldsModel
1516
from sentry.db.models.fields.bounded import BoundedBigIntegerField
@@ -320,6 +321,23 @@ def incremental_title(cls, organization, name):
320321

321322
return f"{base_name} copy {next_copy_number}"
322323

324+
def get_filters(self) -> dict[str, Any]:
325+
"""
326+
Returns the filters for the dashboard.
327+
328+
This is used to colocate any specific logic for producing dashboard filters,
329+
such as handling the all_projects filter.
330+
"""
331+
projects = (
332+
[ALL_ACCESS_PROJECT_ID]
333+
if self.filters and self.filters.get("all_projects")
334+
else list(self.projects.values_list("id", flat=True))
335+
)
336+
337+
return {
338+
"projects": projects,
339+
}
340+
323341

324342
@region_silo_model
325343
class DashboardTombstone(Model):

tests/sentry/api/endpoints/test_organization_dashboards.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ def assert_equal_dashboards(self, dashboard, data):
4646
widget_displays.append(DashboardWidgetDisplayTypes.get_type_name(widget.display_type))
4747

4848
assert data["widgetDisplay"] == widget_displays
49+
50+
filters = dashboard.get_filters()
51+
if filters and filters.get("projects"):
52+
assert data.get("projects") == filters["projects"]
53+
4954
assert "widgets" not in data
5055

5156
def test_get(self):
@@ -718,6 +723,19 @@ def test_get_shared_dashboards_across_organizations(self):
718723
values = [row["title"] for row in response.data]
719724
assert values == ["General", "Initial dashboard"]
720725

726+
def test_get_with_all_projects_filter(self):
727+
Dashboard.objects.create(
728+
title="Dashboard with all projects filter",
729+
organization=self.organization,
730+
created_by_id=self.user.id,
731+
filters={"all_projects": True},
732+
)
733+
response = self.client.get(self.url, data={"query": "Dashboard with all projects filter"})
734+
assert response.status_code == 200, response.content
735+
assert len(response.data) == 1
736+
assert response.data[0]["title"] == "Dashboard with all projects filter"
737+
assert response.data[0].get("projects") == [-1]
738+
721739
def test_post(self):
722740
response = self.do_request("post", self.url, data={"title": "Dashboard from Post"})
723741
assert response.status_code == 201

0 commit comments

Comments
 (0)