Add support for the Hipchat room notification API

This commit is contained in:
Joseph Schorr 2014-08-19 17:40:36 -04:00
parent 35bd28a77e
commit 32ea1d194f
7 changed files with 156 additions and 3 deletions

View file

@ -15,6 +15,13 @@ class NotificationEvent(object):
def __init__(self):
pass
def get_level(self, event_data, notification_data):
"""
Returns a 'level' representing the severity of the event.
Valid values are: 'info', 'warning', 'error', 'primary'
"""
raise NotImplementedError
def get_summary(self, event_data, notification_data):
"""
Returns a human readable one-line summary for the given notification data.
@ -55,6 +62,9 @@ class RepoPushEvent(NotificationEvent):
def event_name(cls):
return 'repo_push'
def get_level(self, event_data, notification_data):
return 'info'
def get_summary(self, event_data, notification_data):
return 'Repository %s updated' % (event_data['repository'])
@ -87,6 +97,9 @@ class BuildQueueEvent(NotificationEvent):
@classmethod
def event_name(cls):
return 'build_queued'
def get_level(self, event_data, notification_data):
return 'info'
def get_sample_data(self, repository):
build_uuid = 'fake-build-id'
@ -127,6 +140,9 @@ class BuildStartEvent(NotificationEvent):
def event_name(cls):
return 'build_start'
def get_level(self, event_data, notification_data):
return 'info'
def get_sample_data(self, repository):
build_uuid = 'fake-build-id'
@ -155,6 +171,9 @@ class BuildSuccessEvent(NotificationEvent):
def event_name(cls):
return 'build_success'
def get_level(self, event_data, notification_data):
return 'primary'
def get_sample_data(self, repository):
build_uuid = 'fake-build-id'
@ -183,6 +202,9 @@ class BuildFailureEvent(NotificationEvent):
def event_name(cls):
return 'build_failure'
def get_level(self, event_data, notification_data):
return 'error'
def get_sample_data(self, repository):
build_uuid = 'fake-build-id'

View file

@ -240,3 +240,65 @@ class FlowdockMethod(NotificationMethod):
return False
return True
class HipchatMethod(NotificationMethod):
""" Method for sending notifications to Hipchat via the API:
https://www.hipchat.com/docs/apiv2/method/send_room_notification
"""
@classmethod
def method_name(cls):
return 'hipchat'
def validate(self, repository, config_data):
if not config_data.get('notification_token', ''):
raise CannotValidateNotificationMethodException('Missing Hipchat Room Notification Token')
if not config_data.get('room_id', ''):
raise CannotValidateNotificationMethodException('Missing Hipchat Room ID')
def perform(self, notification, event_handler, notification_data):
config_data = json.loads(notification.config_json)
token = config_data.get('notification_token', '')
room_id = config_data.get('room_id', '')
if not token or not room_id:
return False
owner = model.get_user(notification.repository.namespace)
if not owner:
# Something went wrong.
return False
url = 'https://api.hipchat.com/v2/room/%s/notification?auth_token=%s' % (room_id, token)
level = event_handler.get_level(notification_data['event_data'], notification_data)
color = {
'info': 'gray',
'warning': 'yellow',
'error': 'red',
'primary': 'purple'
}.get(level, 'gray')
headers = {'Content-type': 'application/json'}
payload = {
'color': color,
'message': event_handler.get_message(notification_data['event_data'], notification_data),
'notify': level == 'error',
'message_format': 'html',
}
try:
resp = requests.post(url, data=json.dumps(payload), headers=headers)
if resp.status_code/100 != 2:
logger.error('%s response for hipchat to url: %s' % (resp.status_code,
url))
logger.error(resp.content)
return False
except requests.exceptions.RequestException as ex:
logger.exception('Hipchat method was unable to be sent: %s' % ex.message)
return False
return True

View file

@ -252,6 +252,7 @@ def initialize_database():
ExternalNotificationMethod.create(name='webhook')
ExternalNotificationMethod.create(name='flowdock')
ExternalNotificationMethod.create(name='hipchat')
NotificationKind.create(name='repo_push')
NotificationKind.create(name='build_queued')

View file

@ -4566,6 +4566,13 @@ i.flowdock-icon {
height: 16px;
}
i.hipchat-icon {
background-image: url(/static/img/hipchat.png);
background-size: 16px;
width: 16px;
height: 16px;
}
.external-notification-view-element {
margin: 10px;
padding: 6px;

View file

@ -88,8 +88,8 @@
allowed-entities="['user', 'team', 'org']"
ng-switch-when="entity"></div>
<div ng-if="field.help_url" style="margin-top: 10px">
See: <a href="{{ field.help_url }}" target="_blank">{{ field.help_url }}</a>
<div ng-if="getHelpUrl(field, currentConfig)" style="margin-top: 10px">
See: <a href="{{ getHelpUrl(field, currentConfig) }}" target="_blank">{{ getHelpUrl(field, currentConfig) }}</a>
</div>
</div>
</td>

BIN
static/img/hipchat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -513,6 +513,41 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
$provide.factory('StringBuilderService', ['$sce', 'UtilService', function($sce, UtilService) {
var stringBuilderService = {};
stringBuilderService.buildUrl = function(value_or_func, metadata) {
var url = value_or_func;
if (typeof url != 'string') {
url = url(metadata);
}
// Find the variables to be replaced.
var varNames = [];
for (var i = 0; i < url.length; ++i) {
var c = url[i];
if (c == '{') {
for (var j = i + 1; j < url.length; ++j) {
var d = url[j];
if (d == '}') {
varNames.push(url.substring(i + 1, j));
i = j;
break;
}
}
}
}
// Replace all variables found.
for (var i = 0; i < varNames.length; ++i) {
var varName = varNames[i];
if (!metadata[varName]) {
return null;
}
url = url.replace('{' + varName + '}', metadata[varName]);
}
return url;
};
stringBuilderService.buildString = function(value_or_func, metadata) {
var fieldIcons = {
'username': 'user',
@ -1089,6 +1124,23 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
'help_url': 'https://www.flowdock.com/account/tokens'
}
]
},
{
'id': 'hipchat',
'title': 'HipChat Room Notification',
'icon': 'hipchat-icon',
'fields': [
{
'name': 'room_id',
'type': 'string',
'title': 'Room ID #'
},
{
'name': 'notification_token',
'type': 'string',
'title': 'Notification Token'
}
]
}
];
@ -4830,7 +4882,7 @@ quayApp.directive('createExternalNotificationDialog', function () {
'counter': '=counter',
'notificationCreated': '&notificationCreated'
},
controller: function($scope, $element, ExternalNotificationData, ApiService, $timeout) {
controller: function($scope, $element, ExternalNotificationData, ApiService, $timeout, StringBuilderService) {
$scope.currentEvent = null;
$scope.currentMethod = null;
$scope.status = '';
@ -4930,6 +4982,15 @@ quayApp.directive('createExternalNotificationDialog', function () {
}, 1000);
};
$scope.getHelpUrl = function(field, config) {
var helpUrl = field['help_url'];
if (!helpUrl) {
return null;
}
return StringBuilderService.buildUrl(helpUrl, config);
};
$scope.$watch('counter', function(counter) {
if (counter) {
$scope.clearCounter++;