Browse Source

ref(heroku): Only handle on update (#45721)

Only complete `handle` when the Heroku webhook is "update" which means
the release has successfully finished. As it is, we're sending duplicate
emails on `create` and `update` for no good reason.

See
https://github.com/getsentry/sentry/issues/41228#issuecomment-1432653415
and the linked Heroku article:
https://help.heroku.com/JP3QR5I5/why-am-i-receiving-2-web-hook-events-for-a-single-release
Colleen O'Rourke 2 years ago
parent
commit
06f66afe7d
2 changed files with 35 additions and 8 deletions
  1. 9 8
      src/sentry_plugins/heroku/plugin.py
  2. 26 0
      tests/sentry_plugins/heroku/test_plugin.py

+ 9 - 8
src/sentry_plugins/heroku/plugin.py

@@ -75,14 +75,15 @@ class HerokuReleaseHook(ReleaseHook):
 
         commit = slug.get("commit")
         app_name = data.get("app", {}).get("name")
-        if app_name:
-            self.finish_release(
-                version=commit,
-                url=f"http://{app_name}.herokuapp.com",
-                owner_id=user.id if user else None,
-            )
-        else:
-            self.finish_release(version=commit, owner_id=user.id if user else None)
+        if data.get("action") == "update":
+            if app_name:
+                self.finish_release(
+                    version=commit,
+                    url=f"http://{app_name}.herokuapp.com",
+                    owner_id=user.id if user else None,
+                )
+            else:
+                self.finish_release(version=commit, owner_id=user.id if user else None)
 
     def set_refs(self, release, **values):
         if not values.get("owner_id", None):

+ 26 - 0
tests/sentry_plugins/heroku/test_plugin.py

@@ -139,6 +139,7 @@ class HookHandleTest(TestCase):
                 "user": {"email": user.email},
                 "slug": {"commit": "abcd123"},
                 "app": {"name": "example"},
+                "action": "update",
             }
         }
         req.body = bytes(json.dumps(body), "utf-8")
@@ -146,6 +147,28 @@ class HookHandleTest(TestCase):
         assert Release.objects.filter(version=body["data"]["slug"]["commit"]).exists()
         assert hook.set_refs.call_count == 1
 
+    def test_only_run_on_update(self):
+        user = self.create_user()
+        organization = self.create_organization(owner=user)
+        project = self.create_project(organization=organization)
+        hook = HerokuReleaseHook(project)
+        hook.is_valid_signature = Mock()
+        hook.set_refs = Mock()
+
+        req = Mock()
+        body = {
+            "data": {
+                "user": {"email": user.email},
+                "slug": {"commit": "abcd123"},
+                "app": {"name": "example"},
+                "action": "create",
+            }
+        }
+        req.body = bytes(json.dumps(body), "utf-8")
+        hook.handle(req)
+        assert not Release.objects.filter(version=body["data"]["slug"]["commit"]).exists()
+        assert hook.set_refs.call_count == 0
+
     def test_actor_email_success(self):
         user = self.create_user()
         organization = self.create_organization(owner=user)
@@ -160,6 +183,7 @@ class HookHandleTest(TestCase):
                 "actor": {"email": user.email},
                 "slug": {"commit": "abcd123"},
                 "app": {"name": "example"},
+                "action": "update",
             }
         }
         req.body = bytes(json.dumps(body), "utf-8")
@@ -180,6 +204,7 @@ class HookHandleTest(TestCase):
                 "user": {"email": "wrong@example.com"},
                 "slug": {"commit": "v999"},
                 "app": {"name": "example"},
+                "action": "update",
             }
         }
         req.body = bytes(json.dumps(body), "utf-8")
@@ -198,6 +223,7 @@ class HookHandleTest(TestCase):
                 "actor": {"email": user.email},
                 "slug": {"commit": ""},
                 "app": {"name": "example"},
+                "action": "update",
             }
         }
         req.body = bytes(json.dumps(body), "utf-8")