Browse Source

ref: Add is:reprocessing query, move to using contexts instead of tags (#22404)

Markus Unterwaditzer 4 years ago
parent
commit
a54f469a3d

+ 0 - 11
src/sentry/api/endpoints/group_details.py

@@ -22,7 +22,6 @@ from sentry.models import (
     Group,
     GroupRelease,
     GroupSeen,
-    GroupStatus,
     Release,
     ReleaseEnvironment,
     ReleaseProject,
@@ -40,16 +39,6 @@ from sentry.utils.compat import zip
 delete_logger = logging.getLogger("sentry.deletions.api")
 
 
-STATUS_CHOICES = {
-    "resolved": GroupStatus.RESOLVED,
-    "unresolved": GroupStatus.UNRESOLVED,
-    "ignored": GroupStatus.IGNORED,
-    "resolvedInNextRelease": GroupStatus.UNRESOLVED,
-    # TODO(dcramer): remove in 9.0
-    "muted": GroupStatus.IGNORED,
-}
-
-
 class GroupDetailsEndpoint(GroupEndpoint, EnvironmentMixin):
     def _get_activity(self, request, group, num):
         activity_items = set()

+ 5 - 13
src/sentry/api/helpers/group_index.py

@@ -50,7 +50,7 @@ from sentry.models import (
     UserOption,
 )
 from sentry.models.groupinbox import add_group_to_inbox, get_inbox_details
-from sentry.models.group import looks_like_short_id
+from sentry.models.group import looks_like_short_id, STATUS_UPDATE_CHOICES
 from sentry.api.issue_search import convert_query_values, InvalidSearchQuery, parse_search_query
 from sentry.signals import (
     issue_deleted,
@@ -151,16 +151,6 @@ def get_by_short_id(organization_id, is_short_id_lookup, query):
             pass
 
 
-STATUS_CHOICES = {
-    "resolved": GroupStatus.RESOLVED,
-    "unresolved": GroupStatus.UNRESOLVED,
-    "ignored": GroupStatus.IGNORED,
-    "resolvedInNextRelease": GroupStatus.UNRESOLVED,
-    # TODO(dcramer): remove in 9.0
-    "muted": GroupStatus.IGNORED,
-}
-
-
 class InCommitValidator(serializers.Serializer):
     commit = serializers.CharField(required=True)
     repository = serializers.CharField(required=True)
@@ -251,7 +241,9 @@ class InboxDetailsValidator(serializers.Serializer):
 class GroupValidator(serializers.Serializer):
     inbox = serializers.BooleanField()
     inboxDetails = InboxDetailsValidator()
-    status = serializers.ChoiceField(choices=zip(STATUS_CHOICES.keys(), STATUS_CHOICES.keys()))
+    status = serializers.ChoiceField(
+        choices=zip(STATUS_UPDATE_CHOICES.keys(), STATUS_UPDATE_CHOICES.keys())
+    )
     statusDetails = StatusDetailsValidator()
     hasSeen = serializers.BooleanField()
     isBookmarked = serializers.BooleanField()
@@ -726,7 +718,7 @@ def update_groups(request, projects, organization_id, search_fn, has_inbox=False
         result.update({"status": "resolved", "statusDetails": status_details})
 
     elif status:
-        new_status = STATUS_CHOICES[result["status"]]
+        new_status = STATUS_UPDATE_CHOICES[result["status"]]
 
         with transaction.atomic():
             happened = queryset.exclude(status=new_status).update(status=new_status)

+ 2 - 2
src/sentry/api/issue_search.py

@@ -12,7 +12,7 @@ from sentry.api.event_search import (
     SearchValue,
     SearchVisitor,
 )
-from sentry.constants import STATUS_CHOICES
+from sentry.models.group import STATUS_QUERY_CHOICES
 from sentry.search.utils import (
     parse_actor_value,
     parse_user_value,
@@ -49,7 +49,7 @@ class IssueSearchVisitor(SearchVisitor):
             "linked": (SearchKey("linked"), SearchValue(True)),
             "unlinked": (SearchKey("linked"), SearchValue(False)),
         }
-        for status_key, status_value in STATUS_CHOICES.items():
+        for status_key, status_value in STATUS_QUERY_CHOICES.items():
             is_filter_translators[status_key] = (SearchKey("status"), SearchValue(status_value))
         return is_filter_translators
 

+ 0 - 8
src/sentry/constants.py

@@ -57,14 +57,6 @@ STATUS_UNRESOLVED = 0
 STATUS_RESOLVED = 1
 STATUS_IGNORED = 2
 
-STATUS_CHOICES = {
-    "resolved": STATUS_RESOLVED,
-    "unresolved": STATUS_UNRESOLVED,
-    "ignored": STATUS_IGNORED,
-    # TODO(dcramer): remove in 9.0
-    "muted": STATUS_IGNORED,
-}
-
 # Normalize counts to the 15 minute marker. This value MUST be less than 60. A
 # value of 0 would store counts for every minute, and is the lowest level of
 # accuracy provided.

+ 27 - 0
src/sentry/models/group.py

@@ -133,6 +133,33 @@ class GroupStatus(object):
     MUTED = IGNORED
 
 
+# Statuses that can be queried/searched for
+STATUS_QUERY_CHOICES = {
+    "resolved": GroupStatus.RESOLVED,
+    "unresolved": GroupStatus.UNRESOLVED,
+    "ignored": GroupStatus.IGNORED,
+    # TODO(dcramer): remove in 9.0
+    "muted": GroupStatus.IGNORED,
+    "reprocessing": GroupStatus.REPROCESSING,
+}
+
+# Statuses that can be updated from the regular "update group" API
+#
+# Differences over STATUS_QUERY_CHOICES:
+#
+# reprocessing is missing as it is its own endpoint and requires extra input
+# resolvedInNextRelease is added as that is an action that can be taken, but at
+# the same time it can't be queried for
+STATUS_UPDATE_CHOICES = {
+    "resolved": GroupStatus.RESOLVED,
+    "unresolved": GroupStatus.UNRESOLVED,
+    "ignored": GroupStatus.IGNORED,
+    "resolvedInNextRelease": GroupStatus.UNRESOLVED,
+    # TODO(dcramer): remove in 9.0
+    "muted": GroupStatus.IGNORED,
+}
+
+
 class EventOrdering(Enum):
     LATEST = ["-timestamp", "-event_id"]
     OLDEST = ["timestamp", "event_id"]

+ 7 - 12
src/sentry/reprocessing2.py

@@ -31,7 +31,7 @@ How reprocessing works
    redirected at this point. The new group can either:
 
    a. Have events by itself, but also show a success message based on the data in activity.
-   b. Be totally empty but suggest a search for original_group_id based on data in activity.
+   b. Be totally empty but suggest a search for original_issue_id based on data in activity.
 
    However, there's no special flag for whether that new group has been a
    result of reprocessing.
@@ -89,6 +89,7 @@ from sentry import nodestore, features, eventstore
 from sentry.attachments import CachedAttachment, attachment_cache
 from sentry import models
 from sentry.utils import snuba
+from sentry.utils.safe import set_path, get_path
 from sentry.utils.cache import cache_key_for_event
 from sentry.utils.redis import redis_clusters
 from sentry.eventstore.processing import event_processing_store
@@ -150,7 +151,6 @@ def delete_unprocessed_events(project_id, event_ids):
 
 def reprocess_event(project_id, event_id, start_time):
 
-    from sentry.event_manager import set_tag
     from sentry.tasks.store import preprocess_event_from_reprocessing
     from sentry.ingest.ingest_consumer import CACHE_TIMEOUT
 
@@ -187,7 +187,7 @@ def reprocess_event(project_id, event_id, start_time):
 
     # Step 1: Fix up the event payload for reprocessing and put it in event
     # cache/event_processing_store
-    set_tag(data, "original_group_id", event.group_id)
+    set_path(data, "contexts", "reprocessing", "original_issue_id", value=event.group_id)
     cache_key = event_processing_store.store(data)
 
     # Step 2: Copy attachments into attachment cache
@@ -255,16 +255,11 @@ def _copy_attachment_into_cache(attachment_id, attachment, cache_key, cache_time
 
 
 def is_reprocessed_event(data):
-    from sentry.event_manager import get_tag
+    return bool(_get_original_issue_id(data))
 
-    return bool(get_tag(data, "original_group_id"))
 
-
-def _get_original_group_id(data):
-    from sentry.event_manager import get_tag
-
-    # XXX: Have real snuba column
-    return get_tag(data, "original_group_id")
+def _get_original_issue_id(data):
+    return get_path(data, "contexts", "reprocessing", "original_issue_id")
 
 
 def _get_sync_redis_client():
@@ -283,7 +278,7 @@ def mark_event_reprocessed(data):
     if not is_reprocessed_event(data):
         return
 
-    key = _get_sync_counter_key(_get_original_group_id(data))
+    key = _get_sync_counter_key(_get_original_issue_id(data))
     _get_sync_redis_client().decr(key)
 
 

+ 5 - 5
src/sentry/search/utils.py

@@ -7,7 +7,7 @@ import six
 from django.db import DataError
 from django.utils import timezone
 
-from sentry.constants import STATUS_CHOICES
+from sentry.models.group import STATUS_QUERY_CHOICES
 from sentry.models import EventUser, KEYWORD_MAP, Release, Team, User
 from sentry.search.base import ANY
 from sentry.utils.auth import find_users
@@ -33,9 +33,9 @@ def get_user_tag(projects, key, value):
 
 
 def parse_status_value(value):
-    if value in STATUS_CHOICES:
-        return STATUS_CHOICES[value]
-    if value in STATUS_CHOICES.values():
+    if value in STATUS_QUERY_CHOICES:
+        return STATUS_QUERY_CHOICES[value]
+    if value in STATUS_QUERY_CHOICES.values():
         return value
     raise ValueError("Invalid status value")
 
@@ -444,7 +444,7 @@ def parse_query(projects, query, user, environments):
                     results["linked"] = False
                 else:
                     try:
-                        results["status"] = STATUS_CHOICES[value]
+                        results["status"] = STATUS_QUERY_CHOICES[value]
                     except KeyError:
                         raise InvalidQuery(u"'is:' had unknown status code '{}'.".format(value))
             elif key == "assigned":

+ 9 - 0
src/sentry/snuba/events.py

@@ -291,3 +291,12 @@ class Columns(Enum):
         "contexts[trace.parent_span_id]",
         "trace.parent_span",
     )
+
+    # Reprocessing context
+    REPROCESSING_ORIGINAL_GROUP_ID = Column(
+        "events.contexts[reprocessing.original_issue_id]",
+        "contexts[reprocessing.original_issue_id]",
+        "contexts[reprocessing.original_issue_id]",
+        "contexts[reprocessing.original_issue_id]",
+        "reprocessing.original_issue_id",
+    )

+ 1 - 1
src/sentry/static/sentry/app/views/organizationGroupDetails/reprocessingForm.tsx

@@ -47,7 +47,7 @@ class ReprocessingForm extends React.Component<Props, State> {
   handleSuccess = () => {
     const {orgSlug, group} = this.props;
     browserHistory.push(
-      `/organizations/${orgSlug}/issues/?query=tags[original_group_id]:${group.id}`
+      `/organizations/${orgSlug}/issues/?query=reprocessing.original_issue_id:${group.id}`
     );
   };
 

+ 3 - 3
tests/sentry/api/test_issue_search.py

@@ -18,7 +18,7 @@ from sentry.api.issue_search import (
     parse_search_query,
     value_converters,
 )
-from sentry.constants import STATUS_CHOICES
+from sentry.models.group import STATUS_QUERY_CHOICES
 from sentry.testutils import TestCase
 
 
@@ -73,7 +73,7 @@ class ParseSearchQueryTest(TestCase):
         ]
 
     def test_is_query_status(self):
-        for status_string, status_val in STATUS_CHOICES.items():
+        for status_string, status_val in STATUS_QUERY_CHOICES.items():
             assert parse_search_query("is:%s" % status_string) == [
                 SearchFilter(
                     key=SearchKey(name="status"), operator="=", value=SearchValue(status_val)
@@ -178,7 +178,7 @@ class ConvertQueryValuesTest(TestCase):
 
 class ConvertStatusValueTest(TestCase):
     def test_valid(self):
-        for status_string, status_val in STATUS_CHOICES.items():
+        for status_string, status_val in STATUS_QUERY_CHOICES.items():
             filters = [SearchFilter(SearchKey("status"), "=", SearchValue(status_string))]
             result = convert_query_values(filters, [self.project], self.user, None)
             assert result[0].value.raw_value == status_val

Some files were not shown because too many files changed in this diff