Skip to content

Commit b313691

Browse files
authored
fix(aci): add support for time window offsets to open period serializer (#102840)
Do this so that metric issue charts do not have their open period markers offset by one time window. Edited the helper function that gets the offset time to make it simpler, as we don't currently do anything with threshold_period.
1 parent eb40d63 commit b313691

File tree

6 files changed

+60
-14
lines changed

6 files changed

+60
-14
lines changed

src/sentry/incidents/charts.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ def fetch_metric_issue_open_periods(
136136
open_period_identifier: int,
137137
time_period: Mapping[str, str],
138138
user: User | RpcUser | None = None,
139+
time_window: int = 0,
139140
) -> list[Any]:
140141
detector_id = open_period_identifier
141142
try:
@@ -157,6 +158,7 @@ def fetch_metric_issue_open_periods(
157158
path=f"/organizations/{organization.slug}/open-periods/",
158159
params={
159160
"detectorId": detector_id,
161+
"bucketSize": time_window,
160162
**time_period,
161163
},
162164
)
@@ -260,6 +262,7 @@ def build_metric_alert_chart(
260262
alert_context.action_identifier_id,
261263
time_period,
262264
user,
265+
snuba_query.time_window,
263266
),
264267
}
265268
else:

src/sentry/incidents/utils/process_update_helpers.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,7 @@ def get_comparison_aggregation_value(
219219
return (aggregation_value / comparison_aggregate) * 100
220220

221221

222-
def calculate_event_date_from_update_date(
223-
update_date: datetime, snuba_query: SnubaQuery, threshold_period: int = 1
224-
) -> datetime:
222+
def calculate_event_date_from_update_date(update_date: datetime, time_window: int) -> datetime:
225223
"""
226224
Calculates the date that an event actually happened based on the date that we
227225
received the update. This takes into account time window and threshold period.
@@ -230,8 +228,4 @@ def calculate_event_date_from_update_date(
230228
# Subscriptions label buckets by the end of the bucket, whereas discover
231229
# labels them by the front. This causes us an off-by-one error with event dates,
232230
# so to prevent this we subtract a bucket off of the date.
233-
update_date -= timedelta(seconds=snuba_query.time_window)
234-
# We want to also subtract `frequency * (threshold_period - 1)` from the date.
235-
# This allows us to show the actual start of the event, rather than the date
236-
# of the last update that we received.
237-
return update_date - timedelta(seconds=(snuba_query.resolution * (threshold_period - 1)))
231+
return update_date - timedelta(seconds=time_window)

src/sentry/workflow_engine/endpoints/organization_open_periods.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ def get(self, request: Request, organization: Organization) -> Response:
111111

112112
detector_id_param = request.GET.get("detectorId")
113113
group_id_param = request.GET.get("groupId")
114+
# determines the time we need to subtract off of each timestamp before returning the data
115+
bucket_size_param = request.GET.get("bucketSize", 0)
114116

115117
if not detector_id_param and not group_id_param:
116118
raise ValidationError({"detail": "Must provide either detectorId or groupId"})
@@ -145,6 +147,6 @@ def get(self, request: Request, organization: Organization) -> Response:
145147
request=request,
146148
queryset=open_periods,
147149
paginator_cls=OffsetPaginator,
148-
on_results=lambda x: serialize(x, request.user),
150+
on_results=lambda x: serialize(x, request.user, time_window=int(bucket_size_param)),
149151
count_hits=True,
150152
)

src/sentry/workflow_engine/endpoints/serializers/group_open_period_serializer.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import Any, TypedDict
55

66
from sentry.api.serializers import Serializer, register, serialize
7+
from sentry.incidents.utils.process_update_helpers import calculate_event_date_from_update_date
78
from sentry.models.groupopenperiod import GroupOpenPeriod, get_last_checked_for_open_period
89
from sentry.models.groupopenperiodactivity import GroupOpenPeriodActivity, OpenPeriodActivityType
910
from sentry.types.group import PriorityLevel
@@ -61,10 +62,15 @@ def get_attrs(self, item_list, user, **kwargs):
6162
def serialize(
6263
self, obj: GroupOpenPeriod, attrs: Mapping[str, Any], user, **kwargs
6364
) -> GroupOpenPeriodResponse:
65+
time_window = kwargs.get("time_window", 0)
6466
return GroupOpenPeriodResponse(
6567
id=str(obj.id),
66-
start=obj.date_started,
67-
end=obj.date_ended,
68+
start=calculate_event_date_from_update_date(obj.date_started, time_window),
69+
end=(
70+
calculate_event_date_from_update_date(obj.date_ended, time_window)
71+
if obj.date_ended is not None
72+
else None
73+
),
6874
isOpen=obj.date_ended is None,
6975
lastChecked=get_last_checked_for_open_period(obj.group),
7076
activities=attrs.get("activities"),

src/sentry/workflow_engine/models/incident_groupopenperiod.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def create_incident_for_open_period(cls, occurrence, alert_rule, group, open_per
149149
raise Exception("No source_id found in evidence_data for metric issue")
150150

151151
calculated_start_date = calculate_event_date_from_update_date(
152-
open_period.date_started, snuba_query
152+
open_period.date_started, snuba_query.time_window
153153
)
154154

155155
incident = create_incident(
@@ -336,7 +336,7 @@ def update_incident_based_on_open_period_status_change(
336336
)
337337
return
338338
calculated_date_closed = calculate_event_date_from_update_date(
339-
open_period.date_ended, snuba_query
339+
open_period.date_ended, snuba_query.time_window
340340
)
341341
update_incident_status(
342342
incident,

tests/sentry/incidents/test_charts.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from sentry.incidents.logic import CRITICAL_TRIGGER_LABEL
2323
from sentry.incidents.models.incident import Incident, IncidentActivityType, IncidentStatus
2424
from sentry.incidents.typings.metric_detector import AlertContext, OpenPeriodContext
25+
from sentry.incidents.utils.process_update_helpers import calculate_event_date_from_update_date
2526
from sentry.models.groupopenperiod import GroupOpenPeriod
2627
from sentry.models.groupopenperiodactivity import GroupOpenPeriodActivity, OpenPeriodActivityType
2728
from sentry.snuba.dataset import Dataset
@@ -30,6 +31,7 @@
3031
from sentry.testutils.helpers.features import with_feature
3132
from sentry.types.group import PriorityLevel
3233
from sentry.workflow_engine.models import DetectorGroup
34+
from tests.sentry.incidents.utils.test_metric_issue_base import BaseMetricIssueTest
3335

3436
now = "2022-05-16T20:00:00"
3537
frozen_time = f"{now}Z"
@@ -133,7 +135,7 @@ def test_eap_alert(self, mock_client_get: MagicMock, mock_generate_chart: MagicM
133135
assert mock_client_get.call_args[1]["params"]["query"] == "span.op:pageload"
134136

135137

136-
class FetchOpenPeriodsTest(TestCase):
138+
class FetchOpenPeriodsTest(BaseMetricIssueTest):
137139
@freeze_time(frozen_time)
138140
@with_feature("organizations:incidents")
139141
def test_get_incidents_from_detector(self) -> None:
@@ -209,3 +211,42 @@ def test_use_open_period_serializer(self) -> None:
209211
assert activities[0]["id"] == str(opened_gopa.id)
210212
assert activities[0]["type"] == OpenPeriodActivityType(opened_gopa.type).to_str()
211213
assert activities[0]["value"] == PriorityLevel(group.priority).to_str()
214+
215+
@freeze_time(frozen_time)
216+
@with_feature("organizations:incidents")
217+
@with_feature("organizations:new-metric-issue-charts")
218+
def test_use_open_period_serializer_with_offset(self) -> None:
219+
group = self.create_group(type=MetricIssue.type_id, priority=PriorityLevel.HIGH)
220+
221+
# Link detector to group
222+
DetectorGroup.objects.create(detector=self.detector, group=group)
223+
224+
group_open_period = GroupOpenPeriod.objects.get(group=group)
225+
226+
opened_gopa = GroupOpenPeriodActivity.objects.create(
227+
date_added=group_open_period.date_added,
228+
group_open_period=group_open_period,
229+
type=OpenPeriodActivityType.OPENED,
230+
value=group.priority,
231+
)
232+
233+
time_period = incident_date_range(
234+
60, group_open_period.date_started, group_open_period.date_ended
235+
)
236+
237+
chart_data = fetch_metric_issue_open_periods(
238+
self.organization,
239+
self.detector.id,
240+
time_period,
241+
time_window=self.snuba_query.time_window,
242+
)
243+
244+
assert chart_data[0]["id"] == str(group_open_period.id)
245+
assert chart_data[0]["start"] == calculate_event_date_from_update_date(
246+
group_open_period.date_started, self.snuba_query.time_window
247+
)
248+
249+
activities = chart_data[0]["activities"]
250+
assert activities[0]["id"] == str(opened_gopa.id)
251+
assert activities[0]["type"] == OpenPeriodActivityType(opened_gopa.type).to_str()
252+
assert activities[0]["value"] == PriorityLevel(group.priority).to_str()

0 commit comments

Comments
 (0)