diff --git a/humbug/urls.py b/humbug/urls.py
index 910db8b874..288398641d 100644
--- a/humbug/urls.py
+++ b/humbug/urls.py
@@ -136,6 +136,7 @@ urlpatterns += patterns('zephyr.views',
url(r'^api/v1/external/github$', 'api_github_landing'),
url(r'^api/v1/external/jira$', 'api_jira_webhook'),
url(r'^api/v1/external/pivotal$', 'api_pivotal_webhook'),
+ url(r'^api/v1/external/newrelic$', 'api_newrelic_webhook'),
)
v1_api_and_json_patterns = patterns('zephyr.views',
diff --git a/templates/zephyr/integrations.html b/templates/zephyr/integrations.html
index 987d0a80dd..6ada2b9fc6 100644
--- a/templates/zephyr/integrations.html
+++ b/templates/zephyr/integrations.html
@@ -52,6 +52,7 @@
Jira (hosted or v5.2+)
Jira (locally installed)
Nagios
+ New Relic
Pivotal Tracker
Subversion
Trac
@@ -394,6 +395,29 @@ key=NAGIOS_BOT_API_KEY
^ Back to top
+{#--------------------------------------------------------------------#}
+
+
New Relic
+
+
New Relic can send messages to a Zulip stream for alerts and deploys.
+ In your Account Settings page, click "Integrations", then
+ "Alerting notifications". On the "Webhook" tab, enter the following
+ webhook URL:
+
+
https://api.humbughq.com/v1/external/newrelic?api_key=abcdefgh&stream=newrelic
+
+
where api_key is the API key of the user you
+ want updates to be sent as, and stream is the stream name you want the notifications sent to. The stream must already exist.
+
+

+
+
Congratulations! You're done!
Your New Relic events will
+ appear in Zulip:
+
+
+ ^ Back to top
+
+
{#--------------------------------------------------------------------#}
Subversion
diff --git a/zephyr/fixtures/newrelic/newrelic_alert.txt b/zephyr/fixtures/newrelic/newrelic_alert.txt
new file mode 100644
index 0000000000..21a2ac3fb3
--- /dev/null
+++ b/zephyr/fixtures/newrelic/newrelic_alert.txt
@@ -0,0 +1 @@
+alert=%7B%22created_at%22%3A%222013-07-23T20%3A14%3A15%2B00%3A00%22%2C%22application_name%22%3A%22Application%20name%22%2C%22account_name%22%3A%22Account%20name%22%2C%22severity%22%3A%22critical%22%2C%22message%22%3A%22Apdex%20score%20fell%20below%20critical%20level%20of%200.90%22%2C%22short_description%22%3A%22%5Bapplication%20name%5D%20alert%20opened%22%2C%22long_description%22%3A%22Alert%20opened%20on%20%5Bapplication%20name%5D%3A%20Apdex%20score%20fell%20below%20critical%20level%20of%200.90%22%2C%22alert_url%22%3A%22https%3A%2F%2Frpm.newrelc.com%2Faccounts%2F%5Baccount_id%5D%2Fapplications%2F%5Bapplication_id%5D%2Fincidents%2F%5Bincident_id%5D%22%7D
\ No newline at end of file
diff --git a/zephyr/fixtures/newrelic/newrelic_deployment.txt b/zephyr/fixtures/newrelic/newrelic_deployment.txt
new file mode 100644
index 0000000000..cfa343d7ae
--- /dev/null
+++ b/zephyr/fixtures/newrelic/newrelic_deployment.txt
@@ -0,0 +1 @@
+deployment=%7B%22created_at%22%3A%222013-07-23T20%3A14%3A25Z%22%2C%22deployed_by%22%3A%22Zulip%20Test%22%2C%22revision%22%3A%221242%22%2C%22description%22%3A%22Description%20sent%20via%20curl%22%2C%22application_name%22%3A%22Test%20App%22%2C%22account_name%22%3A%22Zulip%20Test%22%2C%22changelog%22%3A%22Changelog%20string%22%2C%22deployment_url%22%3A%22https%3A%2F%2Frpm.newrelic.com%2Faccounts%2F382116%2Fapplications%2F2135922%2Fdeployments%2F679885%22%7D
\ No newline at end of file
diff --git a/zephyr/static/images/integrations/newrelic/001.png b/zephyr/static/images/integrations/newrelic/001.png
new file mode 100644
index 0000000000..f34b95d2fb
Binary files /dev/null and b/zephyr/static/images/integrations/newrelic/001.png differ
diff --git a/zephyr/static/images/integrations/newrelic/002.png b/zephyr/static/images/integrations/newrelic/002.png
new file mode 100644
index 0000000000..9c0bcdb59b
Binary files /dev/null and b/zephyr/static/images/integrations/newrelic/002.png differ
diff --git a/zephyr/tests.py b/zephyr/tests.py
index 3d3e4429b1..aed7e648d3 100644
--- a/zephyr/tests.py
+++ b/zephyr/tests.py
@@ -2988,6 +2988,28 @@ class PivotalHookTests(AuthedTestCase):
self.assertEqual(msg.content, 'Leo Franchi edited "My new Feature story" \
[(view)](https://www.pivotaltracker.com/s/projects/807213/stories/48276573)')
+class NewRelicHookTests(AuthedTestCase):
+ def send_new_relic_message(self, name):
+ email = "hamlet@humbughq.com"
+ api_key = self.get_api_key(email)
+ return self.send_json_payload(email, "/api/v1/external/newrelic?api_key=%s&stream=%s" % (api_key,"newrelic"),
+ self.fixture_data('newrelic', name, file_type='txt'),
+ stream_name="newrelic",
+ content_type="application/x-www-form-urlencoded")
+
+ def test_alert(self):
+ msg = self.send_new_relic_message('alert')
+ self.assertEqual(msg.subject, "Apdex score fell below critical level of 0.90")
+ self.assertEqual(msg.content, 'Alert opened on [application name]: \
+Apdex score fell below critical level of 0.90\n\
+[View alert](https://rpm.newrelc.com/accounts/[account_id]/applications/[application_id]/incidents/[incident_id])')
+
+ def test_deployment(self):
+ msg = self.send_new_relic_message('deployment')
+ self.assertEqual(msg.subject, 'Test App deploy')
+ self.assertEqual(msg.content, '`1242` deployed by **Zulip Test**\n\
+Description sent via curl\n\nChangelog string')
+
class RateLimitTests(AuthedTestCase):
def setUp(self):
diff --git a/zephyr/views.py b/zephyr/views.py
index 80650b37aa..298dc36e12 100644
--- a/zephyr/views.py
+++ b/zephyr/views.py
@@ -1829,6 +1829,45 @@ def api_beanstalk_webhook(request, user_profile,
return json_error(ret)
return json_success()
+@csrf_exempt
+@has_request_variables
+def api_newrelic_webhook(request, alert=REQ(converter=json_to_dict, default=None),
+ deployment=REQ(converter=json_to_dict, default=None)):
+ try:
+ api_key = request.GET['api_key']
+ stream = request.GET['stream']
+ except (AttributeError, KeyError):
+ return json_error("Missing api_key or stream parameter.")
+
+ try:
+ user_profile = UserProfile.objects.get(api_key=api_key)
+ request.user = user_profile
+ except UserProfile.DoesNotExist:
+ return json_error("Failed to find user with API key: %s" % (api_key,))
+
+ rate_limit_user(request, user_profile, domain='all')
+
+ if alert:
+ # Use the message as the subject because it stays the same for
+ # "opened", "acknowledged", and "closed" messages that should be
+ # grouped.
+ subject = alert['message']
+ content = "%(long_description)s\n[View alert](%(alert_url)s)" % (alert)
+ elif deployment:
+ subject = "%s deploy" % (deployment['application_name'])
+ content = """`%(revision)s` deployed by **%(deployed_by)s**
+%(description)s
+
+%(changelog)s""" % (deployment)
+ else:
+ return json_error("Unknown webhook request")
+
+ subject = elide_subject(subject)
+ ret = check_send_message(user_profile, get_client("API"), "stream", [stream], subject, content)
+ if ret is not None:
+ return json_error(ret)
+ return json_success()
+
def get_status_list(requesting_user_profile):
return {'presences': get_status_dict(requesting_user_profile),
'server_timestamp': time.time()}