Better notifications UI

Fixes #369
This commit is contained in:
Joseph Schorr 2015-08-17 16:30:15 -04:00
parent 3f6f5162e8
commit 84276ee945
10 changed files with 89 additions and 12 deletions

View file

@ -722,6 +722,7 @@ class RepositoryNotification(BaseModel):
repository = ForeignKeyField(Repository, index=True)
event = ForeignKeyField(ExternalNotificationEvent)
method = ForeignKeyField(ExternalNotificationMethod)
title = CharField(null=True)
config_json = TextField()

View file

@ -113,12 +113,12 @@ def delete_matching_notifications(target, kind_name, **kwargs):
notification.delete_instance()
def create_repo_notification(repo, event_name, method_name, config):
def create_repo_notification(repo, event_name, method_name, config, title=None):
event = ExternalNotificationEvent.get(ExternalNotificationEvent.name == event_name)
method = ExternalNotificationMethod.get(ExternalNotificationMethod.name == method_name)
return RepositoryNotification.create(repository=repo, event=event, method=method,
config_json=json.dumps(config))
config_json=json.dumps(config), title=title)
def get_repo_notification(uuid):

View file

@ -26,7 +26,8 @@ def notification_view(note):
'uuid': note.uuid,
'event': note.event.name,
'method': note.method.name,
'config': config
'config': config,
'title': note.title,
}
@ -55,7 +56,11 @@ class RepositoryNotificationList(RepositoryParamResource):
'config': {
'type': 'object',
'description': 'JSON config information for the specific method of notification'
}
},
'title': {
'type': 'string',
'description': 'The human-readable title of the notification',
},
}
},
}
@ -78,7 +83,8 @@ class RepositoryNotificationList(RepositoryParamResource):
raise request_error(message=ex.message)
new_notification = model.notification.create_repo_notification(repo, parsed['event'],
parsed['method'], parsed['config'])
parsed['method'], parsed['config'],
parsed.get('title', None))
resp = notification_view(new_notification)
log_action('add_repo_notification', namespace,

View file

@ -11,14 +11,14 @@
</div>
<div class="modal-body">
<!-- Creating spinner -->
<div class="quay-spinner" ng-show="status == 'creating' || status == 'authorizing-email'"></div>
<div class="cor-loader" ng-show="status == 'creating' || status == 'authorizing-email'"></div>
<!-- Authorize e-mail view -->
<div ng-show="status == 'authorizing-email-sent'">
An e-mail has been sent to <code>{{ currentConfig.email }}</code>. Please click the link contained
in the e-mail.
<br><br>
Waiting... <span class="quay-spinner"></span>
<span class="cor-loader-inline"></span>
</div>
<!-- Authorize e-mail view -->
@ -30,6 +30,14 @@
<!-- Create View -->
<table style="width: 100%" ng-show="status == ''">
<tr>
<td style="width: 120px">Notification title:</td>
<td style="padding-right: 21px;">
<input class="form-control" type="text" placeholder="(Optional Title)" ng-model="currentTitle"
style="margin: 10px;">
</td>
</tr>
<tr>
<td style="width: 120px">When this occurs:</td>
<td>

View file

@ -26,6 +26,7 @@
<table class="co-table permissions" ng-if="notifications.length">
<thead>
<tr>
<td>Title</td>
<td>Event</td>
<td>Notification</td>
<td class="options-col"></td>
@ -34,6 +35,10 @@
<tbody>
<tr class="notification-row" ng-repeat="notification in notifications">
<td>
{{ notification.title || '(Untitled)' }}
</td>
<td>
<span class="notification-event">
<i class="fa fa-lg" ng-class="getEventInfo(notification).icon"></i>
@ -53,6 +58,16 @@
<span class="cor-option" option-click="testNotification(notification)">
<i class="fa fa-send"></i> Test Notification
</span>
<span class="cor-option" option-click="showNotifyInfo(notification, 'url')"
ng-if="getMethodInfo(notification).id == 'webhook'">
<i class="fa fa-link"></i>
View Webhook URL
</span>
<span class="cor-option" option-click="showNotifyInfo(notification, 'email')"
ng-if="getMethodInfo(notification).id == 'email'">
<i class="fa fa-envelope"></i>
View E-mail Address
</span>
<span class="cor-option" option-click="showWebhookInfo(notification)"
ng-if="getMethodInfo(notification).id == 'webhook'">
<i class="fa fa-book"></i>

View file

@ -88,7 +88,8 @@ angular.module('quay').directive('createExternalNotificationDialog', function ()
var data = {
'event': $scope.currentEvent.id,
'method': $scope.currentMethod.id,
'config': $scope.currentConfig
'config': $scope.currentConfig,
'title': $scope.currentTitle
};
ApiService.createRepoNotification(data, params).then(function(resp) {

View file

@ -64,6 +64,25 @@ angular.module('quay').directive('repositoryEventsTable', function () {
}, ApiService.errorDisplay('Cannot delete notification'));
};
$scope.showNotifyInfo = function(notification, field) {
var dom = document.createElement('input');
dom.setAttribute('type', 'text');
dom.setAttribute('class', 'form-control');
dom.setAttribute('value', notification.config[field]);
dom.setAttribute('readonly', 'readonly');
bootbox.dialog({
'title': (notification.title || 'Notification') + ' ' + field,
'message': dom.outerHTML,
'buttons': {
"Done": {
className: "btn-primary",
callback: function() {}
},
}
});
};
$scope.showWebhookInfo = function(notification) {
var eventId = notification.event;
document.location = 'http://docs.quay.io/guides/notifications.html#webhook_' + eventId;

Binary file not shown.

View file

@ -1888,6 +1888,8 @@ class TestRepositoryNotifications(ApiTestCase):
self.assertEquals('repo_push', json['event'])
self.assertEquals('webhook', json['method'])
self.assertEquals('http://example.com', json['config']['url'])
self.assertIsNone(json['title'])
wid = json['uuid']
# Get the notification.
@ -1897,6 +1899,7 @@ class TestRepositoryNotifications(ApiTestCase):
self.assertEquals(wid, json['uuid'])
self.assertEquals('repo_push', json['event'])
self.assertEquals('webhook', json['method'])
self.assertIsNone(json['title'])
# Verify the notification is listed.
json = self.getJsonResponse(RepositoryNotificationList,
@ -1915,6 +1918,29 @@ class TestRepositoryNotifications(ApiTestCase):
params=dict(repository=ADMIN_ACCESS_USER + '/simple', uuid=wid),
expected_code=404)
# Add another notification.
json = self.postJsonResponse(RepositoryNotificationList,
params=dict(repository=ADMIN_ACCESS_USER + '/simple'),
data=dict(config={'url': 'http://example.com'}, event='repo_push',
method='webhook', title='Some Notification'),
expected_code=201)
self.assertEquals('repo_push', json['event'])
self.assertEquals('webhook', json['method'])
self.assertEquals('http://example.com', json['config']['url'])
self.assertEquals('Some Notification', json['title'])
wid = json['uuid']
# Get the notification.
json = self.getJsonResponse(RepositoryNotification,
params=dict(repository=ADMIN_ACCESS_USER + '/simple', uuid=wid))
self.assertEquals(wid, json['uuid'])
self.assertEquals('repo_push', json['event'])
self.assertEquals('webhook', json['method'])
self.assertEquals('Some Notification', json['title'])
class TestListAndGetImage(ApiTestCase):
def test_listandgetimages(self):

View file

@ -13,10 +13,11 @@ def run_slackwebhook_migration():
encountered = set()
while True:
found = list(RepositoryNotification.select().where(
RepositoryNotification.method == slack_method,
RepositoryNotification.config_json ** "%subdomain%",
~(RepositoryNotification.config_json ** "%url%")))
found = list(RepositoryNotification.select(RepositoryNotification.uuid,
RepositoryNotification.config_json)
.where(RepositoryNotification.method == slack_method,
RepositoryNotification.config_json ** "%subdomain%",
~(RepositoryNotification.config_json ** "%url%")))
found = [f for f in found if not f.uuid in encountered]