Browse Source

feat(plugins): add opsgenie api client (#16921)

Stephen Cefali 5 years ago
parent
commit
34daddfaf2

+ 28 - 0
src/sentry_plugins/opsgenie/client.py

@@ -0,0 +1,28 @@
+from __future__ import absolute_import
+
+from sentry_plugins.client import ApiClient
+
+
+class OpsGenieApiClient(ApiClient):
+    monitoring_tool = "sentry"
+    plugin_name = "opsgenie"
+    allow_redirects = False
+
+    def __init__(self, api_key, alert_url, recipients=None):
+        self.api_key = api_key
+        self.alert_url = alert_url
+        self.recipients = recipients
+        super(OpsGenieApiClient, self).__init__()
+
+    def build_url(self, _path):
+        return self.alert_url
+
+    def request(self, data):
+        headers = {"Authorization": "GenieKey " + self.api_key}
+        return self._request(path="", method="post", data=data, headers=headers)
+
+    def trigger_incident(self, payload):
+        if self.recipients:
+            payload = payload.copy()
+            payload["recipients"] = self.recipients
+        return self.request(payload)

+ 16 - 18
src/sentry_plugins/opsgenie/plugin.py

@@ -5,13 +5,14 @@ import sentry
 import six
 
 from django import forms
-from requests import HTTPError
 
-from sentry import http
+from sentry_plugins.base import CorePluginMixin
 from sentry.plugins.bases import notify
 from sentry.utils import json
 from sentry.integrations import FeatureDescription, IntegrationFeatures
 
+from .client import OpsGenieApiClient
+
 
 class OpsGenieOptionsForm(notify.NotificationConfigurationForm):
     api_key = forms.CharField(
@@ -35,7 +36,7 @@ class OpsGenieOptionsForm(notify.NotificationConfigurationForm):
     )
 
 
-class OpsGeniePlugin(notify.NotificationPlugin):
+class OpsGeniePlugin(CorePluginMixin, notify.NotificationPlugin):
     author = "Sentry Team"
     author_url = "https://github.com/getsentry"
     title = "OpsGenie"
@@ -69,8 +70,7 @@ class OpsGeniePlugin(notify.NotificationPlugin):
             "source": "Sentry",
             "details": {
                 "Sentry ID": six.text_type(group.id),
-                "Sentry Group": getattr(group, "message_short", group.message).encode("utf-8"),
-                "Checksum": group.checksum,
+                "Sentry Group": getattr(group, "title", group.message).encode("utf-8"),
                 "Project ID": group.project.slug,
                 "Project Name": group.project.name,
                 "Logger": group.logger,
@@ -92,17 +92,15 @@ class OpsGeniePlugin(notify.NotificationPlugin):
         if not self.is_configured(group.project):
             return
 
-        api_key = self.get_option("api_key", group.project)
-        recipients = self.get_option("recipients", group.project)
-        alert_url = self.get_option("alert_url", group.project)
-
+        client = self.get_client(group.project)
         payload = self.build_payload(group, event, triggering_rules)
-
-        headers = {"Authorization": "GenieKey " + api_key}
-
-        if recipients:
-            payload["recipients"] = recipients
-
-        resp = http.safe_urlopen(alert_url, json=payload, headers=headers)
-        if not resp.ok:
-            raise HTTPError("Unsuccessful response from OpsGenie: %s" % resp.json())
+        try:
+            client.trigger_incident(payload)
+        except Exception as e:
+            self.raise_error(e)
+
+    def get_client(self, project):
+        api_key = self.get_option("api_key", project)
+        alert_url = self.get_option("alert_url", project)
+        recipients = self.get_option("recipients", project)
+        return OpsGenieApiClient(api_key, alert_url, recipients)

+ 1 - 0
tests/sentry_plugins/opgsenie/__init__.py

@@ -0,0 +1 @@
+from __future__ import absolute_import

+ 78 - 0
tests/sentry_plugins/opgsenie/test_plugin.py

@@ -0,0 +1,78 @@
+from __future__ import absolute_import
+
+import responses
+import six
+
+from exam import fixture
+from sentry.models import Rule
+from sentry.plugins.base import Notification
+from sentry.testutils import PluginTestCase
+from sentry.utils import json
+
+from sentry_plugins.opsgenie.plugin import OpsGeniePlugin
+
+
+class OpsGeniePluginTest(PluginTestCase):
+    @fixture
+    def plugin(self):
+        return OpsGeniePlugin()
+
+    def test_conf_key(self):
+        assert self.plugin.conf_key == "opsgenie"
+
+    def test_entry_point(self):
+        self.assertPluginInstalled("opsgenie", self.plugin)
+
+    def test_is_configured(self):
+        assert self.plugin.is_configured(self.project) is False
+        self.plugin.set_option("api_key", "abcdef", self.project)
+        assert self.plugin.is_configured(self.project) is False
+        self.plugin.set_option("alert_url", "https://api.opsgenie.com/v2/alerts", self.project)
+        assert self.plugin.is_configured(self.project) is True
+
+    @responses.activate
+    def test_simple_notification(self):
+        responses.add("POST", "https://api.opsgenie.com/v2/alerts")
+        self.plugin.set_option("api_key", "abcdef", self.project)
+        self.plugin.set_option("alert_url", "https://api.opsgenie.com/v2/alerts", self.project)
+        self.plugin.set_option("recipients", "me", self.project)
+
+        event = self.store_event(
+            data={
+                "message": "Hello world",
+                "level": "warning",
+                "platform": "python",
+                "culprit": "foo.bar",
+            },
+            project_id=self.project.id,
+        )
+        group = event.group
+
+        rule = Rule.objects.create(project=self.project, label="my rule")
+
+        notification = Notification(event=event, rule=rule)
+
+        with self.options({"system.url-prefix": "http://example.com"}):
+            self.plugin.notify(notification)
+
+        request = responses.calls[0].request
+        payload = json.loads(request.body)
+        group_id = six.text_type(group.id)
+        assert payload == {
+            "recipients": "me",
+            "tags": ["level:warning"],
+            "entity": "foo.bar",
+            "alias": "sentry: %s" % group_id,
+            "details": {
+                "Project Name": self.project.name,
+                "Triggering Rules": '["my rule"]',
+                "Sentry Group": "Hello world",
+                "Sentry ID": group_id,
+                "Logger": "",
+                "Level": "warning",
+                "Project ID": "bar",
+                "URL": "http://example.com/organizations/baz/issues/%s/" % group_id,
+            },
+            "message": "Hello world",
+            "source": "Sentry",
+        }