├── .gitignore
├── .travis.yml
├── MANIFEST.in
├── Makefile
├── README.rst
├── conftest.py
├── sentry_hipchat
├── __init__.py
└── models.py
├── setup.cfg
├── setup.py
└── tests
├── __init__.py
└── hipchat_test.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.egg-info/
3 | /dist
4 | /build
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "2.7"
4 | install:
5 | - time make develop
6 | script:
7 | - py.test tests/ --cov sentry --cov-report term-missing
8 | notifications:
9 | irc:
10 | channels: "irc.freenode.org#sentry"
11 | on_success: change
12 | on_failure: change
13 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include setup.py README.rst MANIFEST.in LICENSE
2 | recursive-include sentry_hipchat/templates *
3 | global-exclude *~
4 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | develop:
2 | pip install -e .
3 | pip install "file://`pwd`#egg=sentry-hipchat[test]"
4 |
5 | test: develop
6 | py.test
7 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | sentry-hipchat
2 | ==============
3 |
4 | An extension for Sentry which integrates with Hipchat.
5 | It will send issues notification to Hipchat.
6 |
7 | Install
8 | -------
9 |
10 | Install the package via ``pip``::
11 |
12 | pip install sentry-hipchat
13 |
14 | Configuration
15 | -------------
16 |
17 | Go to your project's configuration page (Projects -> [Project]) and select the
18 | Hipchat tab. Enter the required credentials and click save changes.
19 |
20 |
--------------------------------------------------------------------------------
/conftest.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 |
4 | def pytest_configure(config):
5 | from django.conf import settings
6 |
7 | os.environ['DJANGO_SETTINGS_MODULE'] = 'sentry.conf.server'
8 | settings.DATABASES['default'].update({
9 | 'ENGINE': 'django.db.backends.sqlite3',
10 | 'NAME': ':memory:',
11 | })
12 |
--------------------------------------------------------------------------------
/sentry_hipchat/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | sentry_hipchat
3 | ~~~~~~~~~~~~~~
4 |
5 | :copyright: (c) 2011 by Linovia, see AUTHORS for more details.
6 | :license: BSD, see LICENSE for more details.
7 | """
8 |
9 | try:
10 | VERSION = __import__('pkg_resources') \
11 | .get_distribution('sentry_hipchat').version
12 | except Exception, e:
13 | VERSION = 'unknown'
14 |
--------------------------------------------------------------------------------
/sentry_hipchat/models.py:
--------------------------------------------------------------------------------
1 | """
2 | sentry_hipchat.models
3 | ~~~~~~~~~~~~~~~~~~~~~
4 |
5 | :copyright: (c) 2011 by Linovia, see AUTHORS for more details.
6 | :license: BSD, see LICENSE for more details.
7 | """
8 |
9 | from django import forms
10 | from django.conf import settings
11 | from django.utils.html import escape
12 |
13 | from sentry.plugins.bases.notify import NotifyPlugin
14 |
15 | import sentry_hipchat
16 |
17 | import urllib
18 | import urllib2
19 | import json
20 | import logging
21 |
22 |
23 | COLORS = {
24 | 'ALERT': 'red',
25 | 'ERROR': 'red',
26 | 'WARNING': 'yellow',
27 | 'INFO': 'green',
28 | 'DEBUG': 'purple',
29 | }
30 |
31 | DEFAULT_ENDPOINT = "https://api.hipchat.com/v1/rooms/message"
32 |
33 |
34 | class HipchatOptionsForm(forms.Form):
35 | token = forms.CharField(help_text="Your hipchat API v1 token.")
36 | room = forms.CharField(help_text="Room name or ID.")
37 | notify = forms.BooleanField(help_text='Notify message in chat window.', required=False)
38 | include_project_name = forms.BooleanField(help_text='Include project name in message.', required=False)
39 | endpoint = forms.CharField(help_text="Custom API endpoint to send notifications to.", required=False,
40 | widget=forms.TextInput(attrs={'placeholder': DEFAULT_ENDPOINT}))
41 |
42 |
43 | class HipchatMessage(NotifyPlugin):
44 | author = 'Xavier Ordoquy'
45 | author_url = 'https://github.com/linovia/sentry-hipchat'
46 | version = sentry_hipchat.VERSION
47 | description = "Event notification to Hipchat."
48 | resource_links = [
49 | ('Bug Tracker', 'https://github.com/linovia/sentry-hipchat/issues'),
50 | ('Source', 'https://github.com/linovia/sentry-hipchat'),
51 | ]
52 | slug = 'hipchat'
53 | title = 'Hipchat'
54 | conf_title = title
55 | conf_key = 'hipchat'
56 | project_conf_form = HipchatOptionsForm
57 | timeout = getattr(settings, 'SENTRY_HIPCHAT_TIMEOUT', 3)
58 |
59 | def is_configured(self, project):
60 | return all((self.get_option(k, project) for k in ('room', 'token')))
61 |
62 | def on_alert(self, alert, **kwargs):
63 | project = alert.project
64 | token = self.get_option('token', project)
65 | room = self.get_option('room', project)
66 | notify = self.get_option('notify', project) or False
67 | include_project_name = self.get_option('include_project_name', project) or False
68 | endpoint = self.get_option('endpoint', project) or DEFAULT_ENDPOINT
69 |
70 | if token and room:
71 | self.send_payload(
72 | endpoint=endpoint,
73 | token=token,
74 | room=room,
75 | message='[ALERT]%(project_name)s %(message)s %(link)s' % {
76 | 'project_name': (' %s' % escape(project.name)) if include_project_name else '',
77 | 'message': escape(alert.message),
78 | 'link': alert.get_absolute_url(),
79 | },
80 | notify=notify,
81 | color=COLORS['ALERT'],
82 | )
83 |
84 | def notify_users(self, group, event, fail_silently=False):
85 | project = event.project
86 | token = self.get_option('token', project)
87 | room = self.get_option('room', project)
88 | notify = self.get_option('notify', project) or False
89 | include_project_name = self.get_option('include_project_name', project) or False
90 | level = group.get_level_display().upper()
91 | link = group.get_absolute_url()
92 | endpoint = self.get_option('endpoint', project) or DEFAULT_ENDPOINT
93 |
94 | if token and room:
95 | self.send_payload(
96 | endpoint=endpoint,
97 | token=token,
98 | room=room,
99 | message='[%(level)s]%(project_name)s %(message)s [view]' % {
100 | 'level': escape(level),
101 | 'project_name': (' %s' % escape(project.name)).encode('utf-8') if include_project_name else '',
102 | 'message': escape(event.error()),
103 | 'link': escape(link),
104 | },
105 | notify=notify,
106 | color=COLORS.get(level, 'purple'),
107 | )
108 |
109 | def send_payload(self, endpoint, token, room, message, notify, color='red'):
110 | values = {
111 | 'auth_token': token,
112 | 'room_id': room.encode('utf-8'),
113 | 'from': 'Sentry',
114 | 'message': message.encode('utf-8'),
115 | 'notify': int(notify),
116 | 'color': color,
117 | }
118 | data = urllib.urlencode(values)
119 | request = urllib2.Request(endpoint, data)
120 | response = urllib2.urlopen(request, timeout=self.timeout)
121 | raw_response_data = response.read()
122 | response_data = json.loads(raw_response_data)
123 | if 'status' not in response_data:
124 | logger = logging.getLogger('sentry.plugins.hipchat')
125 | logger.error('Unexpected response')
126 | if response_data['status'] != 'sent':
127 | logger = logging.getLogger('sentry.plugins.hipchat')
128 | logger.error('Event was not sent to hipchat')
129 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [pytest]
2 | addopts=--tb=short
3 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """
3 | sentry-hipchat
4 | ==============
5 |
6 | An extension for Sentry which integrates with Hipchat. It will forwards
7 | notifications to an hipchat room.
8 |
9 | :copyright: (c) 2011 by the Linovia, see AUTHORS for more details.
10 | :license: BSD, see LICENSE for more details.
11 | """
12 | from setuptools import setup, find_packages
13 |
14 |
15 | tests_require = [
16 | 'pytest',
17 | 'mock',
18 | ]
19 |
20 | install_requires = [
21 | 'sentry>=6.0.0',
22 | ]
23 |
24 | setup(
25 | name='sentry-hipchat',
26 | version='0.6.0',
27 | author='Xavier Ordoquy',
28 | author_email='xordoquy@linovia.com',
29 | url='http://github.com/linovia/sentry-hipchat',
30 | description='A Sentry extension which integrates with Hipchat.',
31 | long_description=__doc__,
32 | license='BSD',
33 | packages=find_packages(exclude=['tests']),
34 | zip_safe=False,
35 | install_requires=install_requires,
36 | tests_require=tests_require,
37 | extras_require={'test': tests_require},
38 | test_suite='runtests.runtests',
39 | include_package_data=True,
40 | entry_points={
41 | 'sentry.apps': [
42 | 'sentry_hipchat = sentry_hipchat ',
43 | ],
44 | 'sentry.plugins': [
45 | 'hipchat = sentry_hipchat.models:HipchatMessage',
46 | ],
47 | },
48 | classifiers=[
49 | 'Framework :: Django',
50 | 'Intended Audience :: Developers',
51 | 'Intended Audience :: System Administrators',
52 | 'Operating System :: OS Independent',
53 | 'Topic :: Software Development'
54 | ],
55 | )
56 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linovia/sentry-hipchat/d0ca57b36b77ba960ade32952774ac5660a1076b/tests/__init__.py
--------------------------------------------------------------------------------
/tests/hipchat_test.py:
--------------------------------------------------------------------------------
1 | from mock import Mock, call
2 | import pytest
3 |
4 | from sentry_hipchat.models import HipchatMessage
5 | from django.conf import settings
6 |
7 |
8 | class PayLoadTest(object):
9 | pass
10 |
11 |
12 | DEFAULT_PLUGIN_CONFIGURATION = {
13 | 'token': 'abcdefghijklmn',
14 | 'room': 'test',
15 | 'notify': False,
16 | 'include_project_name': True,
17 | 'new_only': False,
18 | 'name': 'test project'
19 | }
20 |
21 |
22 | @pytest.fixture
23 | def event():
24 | result = Mock()
25 | for k, v in DEFAULT_PLUGIN_CONFIGURATION.items():
26 | setattr(result.project, k, v)
27 |
28 | result.message = 'An error has occured'
29 | result.error.return_value = result.message
30 |
31 | return result
32 |
33 |
34 | @pytest.fixture
35 | def plugin():
36 | def get_option(k, d):
37 | return getattr(d, k)
38 |
39 | plugin = HipchatMessage()
40 | plugin.get_option = get_option
41 |
42 | plugin.get_url = Mock()
43 | plugin.get_url.return_value = 'http://localhost/someurl'
44 |
45 | plugin.send_payload = Mock()
46 | plugin.send_payload.return_value = None
47 |
48 | return plugin
49 |
50 |
51 | class TestPostProcess(object):
52 | def test_notify_users(self, event, plugin):
53 | """
54 | Make sure known messages aren't sent again if the new_only option is on
55 | """
56 | group = Mock()
57 | group.id = 1
58 | group.project.slug = "demo"
59 | group.get_absolute_url.return_value = 'http://localhost/demo/group/1/'
60 | group.get_level_display.return_value = 'ERROR'
61 |
62 | plugin.notify_users(group, event)
63 |
64 | assert plugin.send_payload.mock_calls == [
65 | call('abcdefghijklmn', 'test',
66 | '[ERROR] test project An error has occured [view]',
67 | False, color='red')
68 | ]
69 |
--------------------------------------------------------------------------------