Skip to content

Commit dbd339c

Browse files
skaastenclaude
andcommitted
ref(dashboards): Extract dashboard revision logic into module-level functions
Move snapshot creation and pruning out of the put() method into two dedicated functions, _take_dashboard_snapshot and _save_dashboard_revision, to keep the request handler focused on HTTP concerns. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 648187c commit dbd339c

File tree

1 file changed

+49
-23
lines changed

1 file changed

+49
-23
lines changed

src/sentry/dashboards/endpoints/organization_dashboard_details.py

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,52 @@
4343
DASHBOARD_REVISION_RETENTION_LIMIT = 10
4444

4545

46+
def _take_dashboard_snapshot(
47+
organization: Organization,
48+
dashboard: Dashboard | dict[Any, Any] | None,
49+
user: Any,
50+
) -> dict[str, Any] | None:
51+
"""
52+
Serialize the current dashboard state as a snapshot, or return None if the
53+
revisions feature is disabled or the dashboard has no DB record to snapshot.
54+
55+
Must be called outside any transaction.atomic block because the serializer
56+
makes hybrid-cloud RPC calls (user_service.serialize_many) that cannot run
57+
inside a transaction.
58+
"""
59+
if not isinstance(dashboard, Dashboard):
60+
return None
61+
if not features.has(REVISIONS_FEATURE, organization, actor=user):
62+
return None
63+
return serialize(dashboard, user)
64+
65+
66+
def _save_dashboard_revision(
67+
dashboard: Dashboard,
68+
user: Any,
69+
snapshot: dict[str, Any],
70+
) -> None:
71+
"""
72+
Create a DashboardRevision for the given snapshot and prune any revisions
73+
beyond the retention limit. Must be called inside a transaction.atomic block.
74+
"""
75+
revision = DashboardRevision.objects.create(
76+
dashboard=dashboard,
77+
created_by_id=user.id if user.is_authenticated else None,
78+
title=dashboard.title,
79+
snapshot=snapshot,
80+
snapshot_schema_version=DASHBOARD_REVISION_SNAPSHOT_SCHEMA_VERSION,
81+
)
82+
old_revision_ids = list(
83+
DashboardRevision.objects.filter(dashboard=dashboard)
84+
.exclude(id=revision.id)
85+
.order_by("-date_added")
86+
.values_list("id", flat=True)[DASHBOARD_REVISION_RETENTION_LIMIT - 1 :]
87+
)
88+
if old_revision_ids:
89+
DashboardRevision.objects.filter(id__in=old_revision_ids).delete()
90+
91+
4692
class OrganizationDashboardBase(OrganizationEndpoint):
4793
owner = ApiOwner.DASHBOARDS
4894
permission_classes = (OrganizationDashboardsPermission,)
@@ -212,32 +258,12 @@ def put(
212258
{"detail": "Cannot change the title of prebuilt Dashboards."}, status=409
213259
)
214260

215-
# Serialize outside the transaction to avoid making RPC calls inside a transaction.
216-
# The snapshot captures the dashboard state before the update is applied.
217-
create_revision = features.has(
218-
REVISIONS_FEATURE, organization, actor=request.user
219-
) and isinstance(dashboard, Dashboard)
220-
snapshot = serialize(dashboard, request.user) if create_revision else None
261+
snapshot = _take_dashboard_snapshot(organization, dashboard, request.user)
221262

222263
try:
223264
with transaction.atomic(router.db_for_write(DashboardTombstone)):
224-
if create_revision and snapshot is not None:
225-
revision = DashboardRevision.objects.create(
226-
dashboard=dashboard,
227-
created_by_id=(request.user.id if request.user.is_authenticated else None),
228-
title=dashboard.title,
229-
snapshot=snapshot,
230-
snapshot_schema_version=DASHBOARD_REVISION_SNAPSHOT_SCHEMA_VERSION,
231-
)
232-
# Keep only the most recent revisions; delete the rest
233-
old_revision_ids = list(
234-
DashboardRevision.objects.filter(dashboard=dashboard)
235-
.exclude(id=revision.id)
236-
.order_by("-date_added")
237-
.values_list("id", flat=True)[DASHBOARD_REVISION_RETENTION_LIMIT - 1 :]
238-
)
239-
if old_revision_ids:
240-
DashboardRevision.objects.filter(id__in=old_revision_ids).delete()
265+
if snapshot is not None:
266+
_save_dashboard_revision(dashboard, request.user, snapshot)
241267
serializer.save()
242268
if tombstone:
243269
DashboardTombstone.objects.get_or_create(

0 commit comments

Comments
 (0)