Просмотр исходного кода

feat(escalating-issues): Add ARCHIVE_UNTIL_ESCALATING to GroupHistory (#47437)

* Adds ARCHIVE_UNTIL_ESCALATING and ESCALATING to GroupHistoryStatus
* Sets the GroupHistory per the GroupSubStatus

WOR-2835, WOR-2964
Snigdha Sharma 1 год назад
Родитель
Сommit
a98feb8c27

+ 5 - 5
src/sentry/api/endpoints/team_issue_breakdown.py

@@ -15,8 +15,8 @@ from sentry.api.utils import get_date_range_from_params
 from sentry.models import Group, GroupHistory, GroupHistoryStatus, Project, Team
 from sentry.models.grouphistory import (
     ACTIONED_STATUSES,
-    status_to_string_lookup,
-    string_to_status_lookup,
+    STATUS_TO_STRING_LOOKUP,
+    STRING_TO_STATUS_LOOKUP,
 )
 
 
@@ -38,7 +38,7 @@ class TeamIssueBreakdownEndpoint(TeamEndpoint, EnvironmentMixin):  # type: ignor
 
         if "statuses" in request.GET:
             statuses = [
-                string_to_status_lookup[status] for status in request.GET.getlist("statuses")
+                STRING_TO_STATUS_LOOKUP[status] for status in request.GET.getlist("statuses")
             ]
             new_format = True
         else:
@@ -50,7 +50,7 @@ class TeamIssueBreakdownEndpoint(TeamEndpoint, EnvironmentMixin):  # type: ignor
         base_day_format = {"total": 0}
         if new_format:
             for status in statuses:
-                base_day_format[status_to_string_lookup[status]] = 0
+                base_day_format[STATUS_TO_STRING_LOOKUP[status]] = 0
         else:
             base_day_format["reviewed"] = 0
 
@@ -103,6 +103,6 @@ class TeamIssueBreakdownEndpoint(TeamEndpoint, EnvironmentMixin):  # type: ignor
             if not new_format and r["status"] != GroupHistoryStatus.UNRESOLVED:
                 bucket["reviewed"] += r["count"]
             if new_format:
-                bucket[status_to_string_lookup[r["status"]]] += r["count"]
+                bucket[STATUS_TO_STRING_LOOKUP[r["status"]]] += r["count"]
 
         return Response(agg_project_counts)

+ 2 - 1
src/sentry/api/helpers/group_index/update.py

@@ -45,7 +45,7 @@ from sentry.models import (
     remove_group_from_inbox,
 )
 from sentry.models.activity import ActivityIntegration
-from sentry.models.group import STATUS_UPDATE_CHOICES, SUBSTATUS_UPDATE_CHOICES, GroupSubStatus
+from sentry.models.group import STATUS_UPDATE_CHOICES
 from sentry.models.grouphistory import record_group_history_from_activity_type
 from sentry.models.groupinbox import GroupInboxRemoveAction, add_group_to_inbox
 from sentry.notifications.types import SUBSCRIPTION_REASON_MAP, GroupSubscriptionReason
@@ -54,6 +54,7 @@ from sentry.services.hybrid_cloud.user import RpcUser, user_service
 from sentry.signals import issue_mark_reviewed, issue_resolved
 from sentry.tasks.integrations import kick_off_status_syncs
 from sentry.types.activity import ActivityType
+from sentry.types.group import SUBSTATUS_UPDATE_CHOICES, GroupSubStatus
 from sentry.utils import metrics
 
 from . import ACTIVITIES_COUNT, BULK_MUTATION_LIMIT, SearchFunction, delete_group_list

+ 2 - 1
src/sentry/api/helpers/group_index/validators/group.py

@@ -4,7 +4,8 @@ from rest_framework import serializers
 
 from sentry.api.fields import ActorField
 from sentry.models import Actor, Team, User
-from sentry.models.group import STATUS_UPDATE_CHOICES, SUBSTATUS_UPDATE_CHOICES
+from sentry.models.group import STATUS_UPDATE_CHOICES
+from sentry.types.group import SUBSTATUS_UPDATE_CHOICES
 
 from . import InboxDetailsValidator, StatusDetailsValidator
 

+ 1 - 1
src/sentry/issues/status_change.py

@@ -12,11 +12,11 @@ from sentry.models import (
     User,
     record_group_history_from_activity_type,
 )
-from sentry.models.group import GroupSubStatus
 from sentry.notifications.types import GroupSubscriptionReason
 from sentry.signals import issue_ignored, issue_unignored, issue_unresolved
 from sentry.tasks.integrations import kick_off_status_syncs
 from sentry.types.activity import ActivityType
+from sentry.types.group import GroupSubStatus
 
 ActivityInfo = namedtuple("ActivityInfo", ("activity_type", "activity_data"))
 

+ 9 - 29
src/sentry/models/group.py

@@ -38,6 +38,11 @@ from sentry.issues.query import apply_performance_conditions
 from sentry.models.grouphistory import record_group_history_from_activity_type
 from sentry.snuba.dataset import Dataset
 from sentry.types.activity import ActivityType
+from sentry.types.group import (
+    IGNORED_SUBSTATUS_CHOICES,
+    UNRESOLVED_SUBSTATUS_CHOICES,
+    GroupSubStatus,
+)
 from sentry.utils.numbers import base32_decode, base32_encode
 from sentry.utils.strings import strip, truncatechars
 
@@ -149,24 +154,6 @@ class GroupStatus:
     MUTED = IGNORED
 
 
-class GroupSubStatus:
-    # GroupStatus.IGNORED
-    UNTIL_ESCALATING = 1
-
-    # GroupStatus.UNRESOLVED
-    ESCALATING = 2
-    ONGOING = 3
-
-
-UNRESOLVED_SUBSTATUS_CHOICES = {
-    GroupSubStatus.ONGOING,
-    GroupSubStatus.ESCALATING,
-}
-
-IGNORED_SUBSTATUS_CHOICES = {
-    GroupSubStatus.UNTIL_ESCALATING,
-}
-
 # Statuses that can be queried/searched for
 STATUS_QUERY_CHOICES: Mapping[str, int] = {
     "resolved": GroupStatus.RESOLVED,
@@ -196,12 +183,6 @@ STATUS_UPDATE_CHOICES = {
     "muted": GroupStatus.IGNORED,
 }
 
-SUBSTATUS_UPDATE_CHOICES = {
-    "until_escalating": GroupSubStatus.UNTIL_ESCALATING,
-    "escalating": GroupSubStatus.ESCALATING,
-    "ongoing": GroupSubStatus.ONGOING,
-}
-
 
 class EventOrdering(Enum):
     LATEST = ["-timestamp", "-event_id"]
@@ -747,11 +728,10 @@ def pre_save_group_default_substatus(instance, sender, *args, **kwargs):
                 extra={"status": instance.status, "substatus": instance.substatus},
             )
 
-        # IGNORED groups may have no substatus for now. We will be adding two more substatusesin the future to simplify this.
-        if instance.status == GroupStatus.IGNORED and instance.substatus not in [
-            None,
-            *IGNORED_SUBSTATUS_CHOICES,
-        ]:
+        if (
+            instance.status == GroupStatus.IGNORED
+            and instance.substatus not in IGNORED_SUBSTATUS_CHOICES
+        ):
             logger.exception(
                 "Invalid substatus for IGNORED group.", extra={"substatus": instance.substatus}
             )

+ 19 - 3
src/sentry/models/grouphistory.py

@@ -14,6 +14,7 @@ from sentry.db.models import (
     sane_repr,
 )
 from sentry.types.activity import ActivityType
+from sentry.types.group import GROUP_SUBSTATUS_TO_GROUP_HISTORY_STATUS
 
 if TYPE_CHECKING:
     from sentry.models import Group, Release, Team, User
@@ -38,12 +39,13 @@ class GroupHistoryStatus:
     DELETED_AND_DISCARDED = 9
     REVIEWED = 10
     ESCALATING = 14
+    ARCHIVED_UNTIL_ESCALATING = 15
     # Just reserving this for us with queries, we don't store the first time a group is created in
     # `GroupHistoryStatus`
     NEW = 20
 
 
-string_to_status_lookup = {
+STRING_TO_STATUS_LOOKUP = {
     "unresolved": GroupHistoryStatus.UNRESOLVED,
     "resolved": GroupHistoryStatus.RESOLVED,
     "set_resolved_in_release": GroupHistoryStatus.SET_RESOLVED_IN_RELEASE,
@@ -59,8 +61,10 @@ string_to_status_lookup = {
     "deleted_and_discarded": GroupHistoryStatus.DELETED_AND_DISCARDED,
     "reviewed": GroupHistoryStatus.REVIEWED,
     "new": GroupHistoryStatus.NEW,
+    "escalating": GroupHistoryStatus.ESCALATING,
+    "archived_until_escalating": GroupHistoryStatus.ARCHIVED_UNTIL_ESCALATING,
 }
-status_to_string_lookup = {status: string for string, status in string_to_status_lookup.items()}
+STATUS_TO_STRING_LOOKUP = {status: string for string, status in STRING_TO_STATUS_LOOKUP.items()}
 
 
 ACTIONED_STATUSES = [
@@ -72,9 +76,14 @@ ACTIONED_STATUSES = [
     GroupHistoryStatus.REVIEWED,
     GroupHistoryStatus.DELETED,
     GroupHistoryStatus.DELETED_AND_DISCARDED,
+    GroupHistoryStatus.ARCHIVED_UNTIL_ESCALATING,
 ]
 
-UNRESOLVED_STATUSES = (GroupHistoryStatus.UNRESOLVED, GroupHistoryStatus.REGRESSED)
+UNRESOLVED_STATUSES = (
+    GroupHistoryStatus.UNRESOLVED,
+    GroupHistoryStatus.REGRESSED,
+    GroupHistoryStatus.ESCALATING,
+)
 RESOLVED_STATUSES = (
     GroupHistoryStatus.RESOLVED,
     GroupHistoryStatus.SET_RESOLVED_IN_RELEASE,
@@ -95,6 +104,7 @@ PREVIOUS_STATUSES = {
     GroupHistoryStatus.ASSIGNED: (GroupHistoryStatus.UNASSIGNED,),
     GroupHistoryStatus.UNASSIGNED: (GroupHistoryStatus.ASSIGNED,),
     GroupHistoryStatus.REGRESSED: RESOLVED_STATUSES,
+    GroupHistoryStatus.ESCALATING: (GroupHistoryStatus.ARCHIVED_UNTIL_ESCALATING,),
 }
 
 ACTIVITY_STATUS_TO_GROUP_HISTORY_STATUS = {
@@ -206,6 +216,12 @@ def record_group_history_from_activity_type(
     maps to it
     """
     status = ACTIVITY_STATUS_TO_GROUP_HISTORY_STATUS.get(activity_type, None)
+
+    # Substatus-based GroupHistory should overritde activity-based GroupHistory since it's more specific.
+    if group.substatus:
+        status_str = GROUP_SUBSTATUS_TO_GROUP_HISTORY_STATUS.get(group.substatus, None)
+        status = STRING_TO_STATUS_LOOKUP.get(status_str, status)
+
     if status is not None:
         return record_group_history(group, status, actor, release)
 

+ 3 - 0
src/sentry/receivers/releases.py

@@ -153,6 +153,9 @@ def resolved_in_commit(instance, created, **kwargs):
                     resolved_at=current_datetime,
                     substatus=None,
                 )
+                group.status = GroupStatus.RESOLVED
+                group.substatus = None
+
                 remove_group_from_inbox(group, action=GroupInboxRemoveAction.RESOLVED)
                 record_group_history_from_activity_type(
                     group,

+ 1 - 1
src/sentry/tasks/clear_expired_snoozes.py

@@ -1,10 +1,10 @@
 from django.utils import timezone
 
 from sentry.models import Group, GroupSnooze, GroupStatus
-from sentry.models.group import GroupSubStatus
 from sentry.models.grouphistory import GroupHistoryStatus, record_group_history
 from sentry.signals import issue_unignored
 from sentry.tasks.base import instrumented_task
+from sentry.types.group import GroupSubStatus
 
 
 @instrumented_task(name="sentry.tasks.clear_expired_snoozes", time_limit=65, soft_time_limit=60)

+ 1 - 1
src/sentry/tasks/integrations/sync_status_inbound.py

@@ -2,9 +2,9 @@ from typing import Any, Mapping
 
 from sentry import analytics
 from sentry.models import Group, GroupStatus, Integration, Organization
-from sentry.models.group import GroupSubStatus
 from sentry.tasks.base import instrumented_task, retry, track_group_async_operation
 from sentry.types.activity import ActivityType
+from sentry.types.group import GroupSubStatus
 
 
 @instrumented_task(

+ 1 - 1
src/sentry/tasks/weekly_escalating_forecast.py

@@ -8,8 +8,8 @@ from sentry.issues.escalating import GroupsCountResponse, query_groups_past_coun
 from sentry.issues.escalating_group_forecast import EscalatingGroupForecast
 from sentry.issues.escalating_issues_alg import generate_issue_forecast
 from sentry.models import Group, GroupStatus
-from sentry.models.group import GroupSubStatus
 from sentry.tasks.base import instrumented_task
+from sentry.types.group import GroupSubStatus
 
 
 class GroupCount(TypedDict):

Некоторые файлы не были показаны из-за большого количества измененных файлов