Browse Source

chore(various): Tiny fixes to grouping code and event serializer (#73257)

This is a bunch of small fixes, mostly related to grouping code, which I've come across and made over the last few weeks. No behavior changes - just trying to clear the decks in my local repo a bit.

Included changes:

- Fix a docstring typo and clarify some comments in our fingerprinting code.

- Clean up our issue merging request handler - clarify a few variable names, add a docstring, add a TODO.

- Clarify a variable name in FE unmerge handler.

- Fix the location of a comment in `deletions.defaults.group.py`.

- Add a comment explaining where the issue merge task's `EXTRA_MERGE_MODELS` value is populated.

- Remove obsolete `__init__.py` file in Seer test folder. (It was needed when there was a file called `test_utils.py` in the folder. Without an `__init__.py` file, pytest only considers basenames, and there are other `test_utils.py` files in the repo, so it gets confused. Now that `test_utils.py` lives in a subfolder, though, the original `__init__.py` is no longer necessary.)

- Remove extraneous blank line in event manager grouping test.

- Remove unnecessary `continue` statement in event serializer, and apply auto-fomatting to the file.
Katie Byers 8 months ago
parent
commit
0a89da9c0a

+ 16 - 7
src/sentry/api/endpoints/group_hashes.py

@@ -55,18 +55,27 @@ class GroupHashesEndpoint(GroupEndpoint):
             paginator=GenericOffsetPaginator(data_fn=data_fn),
         )
 
+    # TODO: Shouldn't this be a PUT rather than a DELETE?
     def delete(self, request: Request, group) -> Response:
-        id_list = request.GET.getlist("id")
-        if not id_list:
+        """
+        Perform an unmerge by reassigning events with hash values corresponding to the given
+        grouphash ids from being part of the given group to being part of a new group.
+
+        Note that if multiple grouphash ids are given, all their corresponding events will end up in
+        a single new group together, rather than each hash's events ending in their own new group.
+        """
+        grouphash_ids = request.GET.getlist("id")
+        if not grouphash_ids:
             return Response()
 
-        hash_list = list(
-            GroupHash.objects.filter(project_id=group.project_id, group=group.id, hash__in=id_list)
+        grouphashes = list(
+            GroupHash.objects.filter(
+                project_id=group.project_id, group=group.id, hash__in=grouphash_ids
+            )
             .exclude(state=GroupHash.State.LOCKED_IN_MIGRATION)
             .values_list("hash", flat=True)
         )
-        if not hash_list:
-            # respond with an error that it's already being merged
+        if not grouphashes:
             return Response({"detail": "Already being unmerged"}, status=409)
 
         metrics.incr(
@@ -77,7 +86,7 @@ class GroupHashesEndpoint(GroupEndpoint):
         )
 
         unmerge.delay(
-            group.project_id, group.id, None, hash_list, request.user.id if request.user else None
+            group.project_id, group.id, None, grouphashes, request.user.id if request.user else None
         )
 
         return Response(status=202)

+ 5 - 4
src/sentry/api/serializers/models/event.py

@@ -268,9 +268,11 @@ class EventSerializer(Serializer):
             "platform": obj.platform,
             "dateReceived": received,
             "errors": errors,
-            "occurrence": convert_dict_key_case(occurrence.to_dict(), snake_to_camel_case)
-            if occurrence
-            else None,
+            "occurrence": (
+                convert_dict_key_case(occurrence.to_dict(), snake_to_camel_case)
+                if occurrence
+                else None
+            ),
             "_meta": {
                 "entries": attrs["_meta"]["entries"],
                 "message": message_meta,
@@ -577,5 +579,4 @@ def map_device_class_tags(tags):
         if tag["key"] == "device.class":
             if device_class := map_device_class_level(tag["value"]):
                 tag["value"] = device_class
-            continue
     return tags

+ 1 - 1
src/sentry/deletions/defaults/group.py

@@ -13,6 +13,7 @@ from ..base import BaseDeletionTask, BaseRelation, ModelDeletionTask, ModelRelat
 # be safe to delete/mutate within a single transaction for user-triggered
 # actions (delete/reprocess/merge/unmerge)
 DIRECT_GROUP_RELATED_MODELS = (
+    # prioritize GroupHash
     models.GroupHash,
     models.GroupAssignee,
     models.GroupCommitResolution,
@@ -37,7 +38,6 @@ DIRECT_GROUP_RELATED_MODELS = (
 )
 
 _GROUP_RELATED_MODELS = DIRECT_GROUP_RELATED_MODELS + (
-    # prioritize GroupHash
     models.UserReport,
     models.EventAttachment,
 )

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

@@ -373,13 +373,13 @@ def get_grouping_variants_for_event(
         else:
             rv["custom-fingerprint"] = CustomFingerprintVariant(fingerprint, fingerprint_info)
 
-    # If the fingerprints are unsalted, we can return them right away.
+    # If only the default is referenced, we can use the variants as is
     elif defaults_referenced == 1 and len(fingerprint) == 1:
         rv = {}
         for key, component in components.items():
             rv[key] = ComponentVariant(component, context.config)
 
-    # Otherwise we need to salt each of the components.
+    # Otherwise we need to "salt" our variants with the custom fingerprint value(s)
     else:
         rv = {}
         fingerprint = resolve_fingerprint_values(fingerprint, event.data)

+ 1 - 1
src/sentry/grouping/variants.py

@@ -143,7 +143,7 @@ def expose_fingerprint_dict(values, info=None):
 
 
 class CustomFingerprintVariant(BaseVariant):
-    """A user defined custom fingerprint."""
+    """A user-defined custom fingerprint."""
 
     type = "custom-fingerprint"
 

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

@@ -14,7 +14,7 @@ from sentry.tsdb.base import TSDBModel
 logger = logging.getLogger("sentry.merge")
 delete_logger = logging.getLogger("sentry.deletions.async")
 
-
+# populated in `TagStorage.setup_merge`
 EXTRA_MERGE_MODELS: list[type[Model]] = []
 
 

+ 5 - 5
static/app/stores/groupingStore.tsx

@@ -474,7 +474,7 @@ const storeConfig: GroupingStoreDefinition = {
   },
 
   onUnmerge({groupId, loadingMessage, orgSlug, successMessage, errorMessage}) {
-    const ids = Array.from(this.state.unmergeList.keys()) as Array<string>;
+    const grouphashIds = Array.from(this.state.unmergeList.keys()) as Array<string>;
 
     return new Promise((resolve, reject) => {
       if (this.isAllUnmergedSelected()) {
@@ -486,26 +486,26 @@ const storeConfig: GroupingStoreDefinition = {
       this.state = {...this.state, unmergeDisabled: true};
 
       // Disable rows
-      this.setStateForId('unmergeState', ids, {checked: false, busy: true});
+      this.setStateForId('unmergeState', grouphashIds, {checked: false, busy: true});
       this.triggerUnmergeState();
       addLoadingMessage(loadingMessage);
 
       this.api.request(`/organizations/${orgSlug}/issues/${groupId}/hashes/`, {
         method: 'DELETE',
         query: {
-          id: ids,
+          id: grouphashIds,
         },
         success: () => {
           addSuccessMessage(successMessage);
 
           // Busy rows after successful Unmerge
-          this.setStateForId('unmergeState', ids, {checked: false, busy: true});
+          this.setStateForId('unmergeState', grouphashIds, {checked: false, busy: true});
           this.state.unmergeList.clear();
         },
         error: error => {
           errorMessage = error?.responseJSON?.detail || errorMessage;
           addErrorMessage(errorMessage);
-          this.setStateForId('unmergeState', ids, {checked: true, busy: false});
+          this.setStateForId('unmergeState', grouphashIds, {checked: true, busy: false});
         },
         complete: () => {
           this.state = {...this.state, unmergeDisabled: false};

+ 0 - 1
tests/sentry/event_manager/test_event_manager_grouping.py

@@ -379,7 +379,6 @@ class EventManagerGroupingMetricsTest(TestCase):
                 with self.feature(
                     {"organizations:grouping-suppress-unnecessary-secondary-hash": has_flag}
                 ):
-
                     mock_metrics_incr.reset_mock()
 
                     project.update_option("sentry:grouping_config", primary_config)

+ 0 - 0
tests/sentry/seer/__init__.py