Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 2 additions & 13 deletions src/sentry/api/serializers/rest_framework/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,8 @@ def validate_id(self, value):


def is_table_display_type(display_type):
return (
display_type
== DashboardWidgetDisplayTypes.as_text_choices()[DashboardWidgetDisplayTypes.TABLE][0]
return display_type == DashboardWidgetDisplayTypes.get_type_name(
DashboardWidgetDisplayTypes.TABLE
)


Expand Down Expand Up @@ -412,16 +411,6 @@ def validate(self, data):
if data.get("display_type") == DashboardWidgetDisplayTypes.TEXT:
return self._validate_text_widget(data)

if (
not data.get("id")
and data.get("display_type") in DashboardWidgetDisplayTypes.DEPRECATED_TYPES
):
raise serializers.ValidationError(
{
"display_type": f"{DashboardWidgetDisplayTypes.get_type_name(data['display_type'])} is no longer a supported display type."
}
)

query_errors = []
all_columns: set[str] = set()
has_columns = False
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"rage_and_dead_clicks",
"wheel",
"agents_traces_table",
"stacked_area",
}

# Most of these are deprecated, not selectable in the UI, or don't make sense for generated dashboards.
Expand Down
2 changes: 0 additions & 2 deletions src/sentry/integrations/slack/unfurl/dashboards.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ class DashboardsUnfurlArgs(TypedDict):
_TIMESERIES_DISPLAY_TYPES = {
DashboardWidgetDisplayTypes.LINE_CHART: "line",
DashboardWidgetDisplayTypes.AREA_CHART: "area",
DashboardWidgetDisplayTypes.STACKED_AREA_CHART: "area",
DashboardWidgetDisplayTypes.BAR_CHART: "bar",
DashboardWidgetDisplayTypes.TOP_N: "area",
}


Expand Down
11 changes: 0 additions & 11 deletions src/sentry/models/dashboard_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,9 @@ def as_text_choices(cls):
class DashboardWidgetDisplayTypes(TypesClass):
LINE_CHART = 0
AREA_CHART = 1
STACKED_AREA_CHART = 2
BAR_CHART = 3
TABLE = 4
BIG_NUMBER = 6
TOP_N = 7
DETAILS = 8
CATEGORICAL_BAR_CHART = 9
WHEEL = 10
Expand All @@ -192,11 +190,9 @@ class DashboardWidgetDisplayTypes(TypesClass):
TYPES = [
(LINE_CHART, "line"),
(AREA_CHART, "area"),
(STACKED_AREA_CHART, "stacked_area"),
(BAR_CHART, "bar"),
(TABLE, "table"),
(BIG_NUMBER, "big_number"),
(TOP_N, "top_n"),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enum removal breaks serialization for unmigrated DB rows

High Severity

Removing STACKED_AREA_CHART = 2 and TOP_N = 7 from DashboardWidgetDisplayTypes.TYPES causes get_type_name() to return None for any database rows that still store those integer values. The backfill migration 1080 is marked is_post_deployment = True and requires manual execution, so there is a window after this code deploys — and potentially an indefinite one if the migration hasn't been triggered yet — where API responses will contain "displayType": null for affected widgets. This breaks dashboard rendering for any organization with unmigrated deprecated widgets.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 40e1fcc. Configure here.

(DETAILS, "details"),
(CATEGORICAL_BAR_CHART, "categorical_bar"),
(WHEEL, "wheel"),
Expand All @@ -207,13 +203,6 @@ class DashboardWidgetDisplayTypes(TypesClass):
]
TYPE_NAMES = [t[1] for t in TYPES]

# Display types that the frontend no longer exposes in either the widget
# builder dropdown or the widget library. TOP_N is converted to AREA at
# every UI entry point (widget library, builder URL deserialization). New
# widgets should not be created with these, but existing widgets remain
# editable.
DEPRECATED_TYPES: list[int] = [STACKED_AREA_CHART, TOP_N]


@cell_silo_model
class DashboardWidgetQuery(Model):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1696,45 +1696,6 @@ def test_update_widget_title(self) -> None:
widgets = self.get_widgets(self.dashboard.id)
self.assert_serialized_widget(data["widgets"][0], widgets[0])

def test_update_widget_with_deprecated_display_type_is_allowed(self) -> None:
# Existing widgets created via the API with deprecated display types
# should remain editable; the rejection only applies to new widgets.
deprecated_widget = DashboardWidget.objects.create(
dashboard=self.dashboard,
order=4,
title="Stacked area",
display_type=DashboardWidgetDisplayTypes.STACKED_AREA_CHART,
widget_type=DashboardWidgetTypes.DISCOVER,
interval="1d",
limit=5,
)
DashboardWidgetQuery.objects.create(
widget=deprecated_widget,
name="Transactions",
fields=["count()"],
columns=[],
aggregates=["count()"],
conditions="event.type:transaction",
order=0,
)

data: dict[str, Any] = {
"title": "First dashboard",
"widgets": [
{"id": str(self.widget_1.id)},
{"id": str(self.widget_2.id)},
{"id": str(self.widget_3.id)},
{"id": str(self.widget_4.id)},
{"id": str(deprecated_widget.id), "title": "Renamed stacked area"},
],
}
response = self.do_request("put", self.url(self.dashboard.id), data=data)
assert response.status_code == 200, response.data

deprecated_widget.refresh_from_db()
assert deprecated_widget.title == "Renamed stacked area"
assert deprecated_widget.display_type == DashboardWidgetDisplayTypes.STACKED_AREA_CHART

def test_update_widget_add_query(self) -> None:
data: dict[str, Any] = {
"title": "First dashboard",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2469,33 +2469,3 @@ def test_post_validate_only_error_for_invalid_dashboard(self) -> None:
assert not Dashboard.objects.filter(
organization=self.organization, title="Invalid Dashboard"
).exists()

def test_post_with_deprecated_display_type_rejected(self) -> None:
data: dict[str, Any] = {
"title": "Dashboard with Deprecated Widget",
"widgets": [
{
"displayType": "stacked_area",
"interval": "5m",
"title": "Stacked area",
"queries": [
{
"name": "Transactions",
"fields": ["count()"],
"columns": [],
"aggregates": ["count()"],
"conditions": "event.type:transaction",
}
],
},
],
}
response = self.do_request("post", self.url, data=data)
assert response.status_code == 400, response.data
assert (
"stacked_area is no longer a supported display type."
in response.data["widgets"][0]["displayType"][0]
)
assert not Dashboard.objects.filter(
organization=self.organization, title="Dashboard with Deprecated Widget"
).exists()
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from sentry.models.dashboard_widget import DashboardWidget, DashboardWidgetDisplayTypes
from sentry.testutils.cases import TestMigrations

# Display type IDs that existed before the 1080 backfill removed them.
_LEGACY_STACKED_AREA_CHART = 2
_LEGACY_TOP_N = 7


class BackfillDeprecatedDashboardWidgetDisplayTypesTest(TestMigrations):
migrate_from = "1079_purge_scm_legacy_org_options"
Expand All @@ -15,28 +19,28 @@ def setup_before_migration(self, apps):
self.top_n_null_limit = DashboardWidget.objects.create(
dashboard=dashboard,
title="top_n null limit",
display_type=DashboardWidgetDisplayTypes.TOP_N,
display_type=_LEGACY_TOP_N,
order=0,
limit=None,
)
self.top_n_explicit_limit = DashboardWidget.objects.create(
dashboard=dashboard,
title="top_n explicit limit",
display_type=DashboardWidgetDisplayTypes.TOP_N,
display_type=_LEGACY_TOP_N,
order=1,
limit=3,
)
self.stacked_area_null_limit = DashboardWidget.objects.create(
dashboard=dashboard,
title="stacked_area null limit",
display_type=DashboardWidgetDisplayTypes.STACKED_AREA_CHART,
display_type=_LEGACY_STACKED_AREA_CHART,
order=2,
limit=None,
)
self.stacked_area_explicit_limit = DashboardWidget.objects.create(
dashboard=dashboard,
title="stacked_area explicit limit",
display_type=DashboardWidgetDisplayTypes.STACKED_AREA_CHART,
display_type=_LEGACY_STACKED_AREA_CHART,
order=3,
limit=8,
)
Expand Down
Loading