From 90ad7742004dcb1c2b53038613259e3a7e219100 Mon Sep 17 00:00:00 2001 From: Lucas Palm Date: Thu, 10 Mar 2016 14:25:00 -0600 Subject: [PATCH] Do not allow deleting a listener that has a default pool This change adds checks to the listener delete service to not allow deleting a listener that currently has a default pool associated with it. In the case of the Delete batch action, an error message is displayed, while the delete row action is simply just not displayed/available if a pool exists. Partially-Implements: blueprint horizon-lbaas-v2-ui Change-Id: Iddee0a5cee5ebbead9438a2add8ebcf57f69d348 --- .../actions/delete/delete.action.service.js | 53 +++++++++++++++---- .../delete/delete.action.service.spec.js | 19 ++++++- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.js index fed45b46..eee835d4 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.js +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.js @@ -48,13 +48,15 @@ * @param toast The horizon message service. * @param qExtensions Horizon extensions to the $q service. * @param gettext The horizon gettext function for translation. - * @returns The load balancers table delete service. + * @returns The listeners table delete service. */ function deleteService( $q, $location, $route, deleteModal, api, policy, toast, qExtensions, gettext ) { var loadbalancerId, statePromise; + var notAllowedMessage = gettext('The following listeners will not be deleted ' + + 'due to existing pools: %s.'); var context = { labels: { title: gettext('Confirm Delete Listeners'), @@ -87,19 +89,19 @@ } function perform(items) { - if (!angular.isArray(items)) { - items = [items]; + if (angular.isArray(items)) { + qExtensions.allSettled(items.map(checkPermission)).then(afterCheck); + } else { + deleteModal.open({ $emit: actionComplete }, [items], context); } - deleteModal.open({ $emit: actionComplete }, items, context); } - function allowed(/*item*/) { - return $q.all([ - statePromise, - // This rule is made up and should therefore always pass. I assume at some point there - // will be a valid rule similar to this that we will want to use. - policy.ifAllowed({ rules: [['neutron', 'delete_listener']] }) - ]); + function allowed(item) { + var promises = [policy.ifAllowed({ rules: [['neutron', 'delete_listener']] }), statePromise]; + if (item) { + promises.push(qExtensions.booleanAsPromise(!item.default_pool_id)); + } + return $q.all(promises); } function deleteItem(id) { @@ -122,5 +124,34 @@ } } + function checkPermission(item) { + return { promise: canBeDeleted(item), context: item }; + } + + function afterCheck(result) { + if (result.fail.length > 0) { + toast.add('error', getMessage(notAllowedMessage, result.fail)); + } + if (result.pass.length > 0) { + deleteModal.open({ $emit: actionComplete }, result.pass.map(getEntity), context); + } + } + + function canBeDeleted(item) { + return qExtensions.booleanAsPromise(!item.default_pool_id); + } + + function getMessage(message, entities) { + return interpolate(message, [entities.map(getName).join(", ")]); + } + + function getName(result) { + return getEntity(result).name; + } + + function getEntity(result) { + return result.context; + } + } })(); diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.spec.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.spec.js index d4851f75..a28f3353 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.spec.js +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.spec.js @@ -96,11 +96,15 @@ expect(allowed()).toBe(true); }); - it('should not allow deleting listener from load balancer in a PENDING state', function() { + it('should not allow deleting a listener from load balancer in a PENDING state', function() { service.init('1', makePromise(true)); expect(allowed()).toBe(false); }); + it('should not allow deleting a listener that has a default pool', function() { + expect(allowed({default_pool_id: 'pool1'})).toBe(false); + }); + it('should open the delete modal', function() { spyOn(modal, 'open'); service.perform(items[0]); @@ -157,5 +161,18 @@ expect(toast.add).toHaveBeenCalledWith('success', 'Deleted listeners: First.'); }); + it('should show message if any selected items do not allow for delete (batch)', function() { + spyOn(modal, 'open'); + spyOn(toast, 'add'); + items[0].default_pool_id = 'pool1'; + items[1].default_pool_id = 'pool2'; + service.perform(items); + $scope.$apply(); + expect(modal.open).not.toHaveBeenCalled(); + expect(toast.add).toHaveBeenCalledWith('error', + 'The following listeners will not be deleted ' + + 'due to existing pools: First, Second.'); + }); + }); })();