diff --git a/data/database.py b/data/database.py index 674aeebe8..1a8291d5a 100644 --- a/data/database.py +++ b/data/database.py @@ -212,4 +212,4 @@ class QueueItem(BaseModel): all_models = [User, Repository, Image, AccessToken, Role, RepositoryPermission, Visibility, RepositoryTag, EmailConfirmation, FederatedLogin, LoginService, QueueItem, - RepositoryBuild, Team, TeamMember, TeamRole] + RepositoryBuild, Team, TeamMember, TeamRole, Webhook] diff --git a/data/model.py b/data/model.py index 9d10b19b3..28c910d28 100644 --- a/data/model.py +++ b/data/model.py @@ -973,3 +973,8 @@ def list_webhooks(namespace_name, repository_name): joined = Webhook.select().join(Repository) return joined.where(Repository.namespace == namespace_name, Repository.name == repository_name) + + +def delete_webhook(namespace_name, repository_name, public_id): + webhook = get_webhook(namespace_name, repository_name, public_id) + webhook.delete_instance() diff --git a/endpoints/api.py b/endpoints/api.py index 1d95bacc4..bab7ee9f0 100644 --- a/endpoints/api.py +++ b/endpoints/api.py @@ -841,7 +841,7 @@ def webhook_view(webhook): @api_login_required @parse_repository_name def create_webhook(namespace, repository): - permission = ModifyRepositoryPermission(namespace, repository) + permission = AdministerRepositoryPermission(namespace, repository) if permission.can(): repo = model.get_repository(namespace, repository) webhook = model.create_webhook(repo, request.get_json()) @@ -849,6 +849,7 @@ def create_webhook(namespace, repository): repo_string = '%s/%s' % (namespace, repository) resp.headers['Location'] = url_for('get_webhook', repository=repo_string, public_id=webhook.public_id) + return resp abort(403) # Permissions denied @@ -858,7 +859,7 @@ def create_webhook(namespace, repository): @api_login_required @parse_repository_name def get_webhook(namespace, repository, public_id): - permission = ModifyRepositoryPermission(namespace, repository) + permission = AdministerRepositoryPermission(namespace, repository) if permission.can(): webhook = model.get_webhook(namespace, repository, public_id) return jsonify(webhook_view(webhook)) @@ -870,7 +871,7 @@ def get_webhook(namespace, repository, public_id): @api_login_required @parse_repository_name def list_webhooks(namespace, repository): - permission = ModifyRepositoryPermission(namespace, repository) + permission = AdministerRepositoryPermission(namespace, repository) if permission.can(): webhooks = model.list_webhooks(namespace, repository) return jsonify({ @@ -880,6 +881,19 @@ def list_webhooks(namespace, repository): abort(403) # Permission denied +@app.route('/api/repository//webhook/', + methods=['DELETE']) +@api_login_required +@parse_repository_name +def delete_webhook(namespace, repository, public_id): + permission = AdministerRepositoryPermission(namespace, repository) + if permission.can(): + model.delete_webhook(namespace, repository, public_id) + return make_response('No Content', 204) + + abort(403) # Permission denied + + @app.route('/api/filedrop/', methods=['POST']) @api_login_required def get_filedrop_url(): diff --git a/static/css/quay.css b/static/css/quay.css index e466e43d6..d2ba879f9 100644 --- a/static/css/quay.css +++ b/static/css/quay.css @@ -524,11 +524,13 @@ font-size: .4em; } -form input.ng-invalid.ng-dirty { +form input.ng-invalid.ng-dirty, +*[ng-form] input.ng-invalid.ng-dirty { background-color: #FDD7D9; } -form input.ng-valid.ng-dirty { +form input.ng-valid.ng-dirty, +*[ng-form] input.ng-valid.ng-dirty { background-color: #DDFFEE; } diff --git a/static/js/controllers.js b/static/js/controllers.js index e4e3d5d4f..696b2b4e9 100644 --- a/static/js/controllers.js +++ b/static/js/controllers.js @@ -559,7 +559,7 @@ function RepoAdminCtrl($scope, Restangular, $routeParams, $rootScope) { }); }; - $scope.roles = [ + $scope.roles = [ { 'id': 'read', 'title': 'Read', 'kind': 'success' }, { 'id': 'write', 'title': 'Write', 'kind': 'success' }, { 'id': 'admin', 'title': 'Admin', 'kind': 'primary' } @@ -700,6 +700,31 @@ function RepoAdminCtrl($scope, Restangular, $routeParams, $rootScope) { $scope.loading = false; }); + $scope.webhooksLoading = true; + $scope.loadWebhooks = function() { + $scope.webhooksLoading = true; + var fetchWebhooks = Restangular.one('repository/' + namespace + '/' + name + '/webhook/'); + fetchWebhooks.get().then(function(resp) { + $scope.webhooks = resp.webhooks; + $scope.webhooksLoading = false; + }); + }; + + $scope.createWebhook = function() { + var newWebhook = Restangular.one('repository/' + namespace + '/' + name + '/webhook/'); + newWebhook.customPOST($scope.newWebhook).then(function(resp) { + $scope.webhooks.push(resp); + $scope.newWebhook.url = ''; + $scope.newWebhookForm.$setPristine(); + }); + }; + + $scope.deleteWebhook = function(webhook) { + var deleteWebhookReq = Restangular.one('repository/' + namespace + '/' + name + '/webhook/' + webhook.public_id); + deleteWebhookReq.customDELETE().then(function(resp) { + $scope.webhooks.splice($scope.webhooks.indexOf(webhook), 1); + }); + }; } function UserAdminCtrl($scope, $timeout, $location, Restangular, PlanService, UserService, KeyService, $routeParams) { diff --git a/static/partials/repo-admin.html b/static/partials/repo-admin.html index ada422d74..e0846a6a5 100644 --- a/static/partials/repo-admin.html +++ b/static/partials/repo-admin.html @@ -23,6 +23,7 @@
@@ -148,6 +149,46 @@
+ +
+
+ Loading webhooks: +
+ +
+
+ + + + + + + + + + + + + + + + + + +
Webhook URL
{{ webhook.parameters.url }} + + + + +
+ + + +
+
+
+
+
diff --git a/test/data/test.db b/test/data/test.db index d35f34afe..6a2cb0c86 100644 Binary files a/test/data/test.db and b/test/data/test.db differ