AMBARI-17297 While deleting a service, confirmation popup took a long time (~30 seconds) to appear (akovalenko)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/59148381 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/59148381 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/59148381 Branch: refs/heads/branch-2.4 Commit: 5914838102040461e80a58015d3b4b1ed177834e Parents: 5889ffc Author: Aleksandr Kovalenko <akovale...@hortonworks.com> Authored: Fri Jun 17 16:57:20 2016 +0300 Committer: Aleksandr Kovalenko <akovale...@hortonworks.com> Committed: Fri Jun 17 20:15:48 2016 +0300 ---------------------------------------------------------------------- ambari-web/app/controllers/main/service/item.js | 47 ++++++++-- ambari-web/app/messages.js | 1 + .../app/mixins/common/configs/configs_saver.js | 6 +- ambari-web/app/styles/application.less | 14 +++ .../test/controllers/main/service/item_test.js | 95 ++++++++++++++++++-- 5 files changed, 149 insertions(+), 14 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/59148381/ambari-web/app/controllers/main/service/item.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/service/item.js b/ambari-web/app/controllers/main/service/item.js index 4f8d9009..79188aa 100644 --- a/ambari-web/app/controllers/main/service/item.js +++ b/ambari-web/app/controllers/main/service/item.js @@ -19,6 +19,7 @@ var App = require('app'); var batchUtils = require('utils/batch_scheduled_requests'); var blueprintUtils = require('utils/blueprint'); +var stringUtils = require('utils/string_utils'); App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDownload, App.InstallComponent, App.ConfigsSaverMixin, App.EnhancedConfigsMixin, { name: 'mainServiceItemController', @@ -83,6 +84,8 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow */ routeToConfigs: false, + deleteServiceProgressPopup: null, + isClientsOnlyService: function() { return App.get('services.clientOnly').contains(this.get('content.serviceName')); }.property('content.serviceName'), @@ -1341,7 +1344,26 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow * @function onPrimary */ onPrimary: function() { - self.deleteServiceCall([serviceName].concat(dependentServiceNames)); + var serviceNames = [serviceName].concat(dependentServiceNames), + serviceDisplayNames = serviceNames.map(function (serviceName) { + return App.Service.find(serviceName).get('displayName'); + }), + progressPopup = App.ModalPopup.show({ + classNames: ['delete-service-progress'], + header: Em.I18n.t('services.service.delete.popup.header'), + showFooter: false, + message: Em.I18n.t('services.service.delete.progressPopup.message').format(stringUtils.getFormattedStringFromArray(serviceDisplayNames)), + bodyClass: Em.View.extend({ + classNames: ['delete-service-progress-body'], + template: Em.Handlebars.compile('{{view App.SpinnerView}}<div class="progress-message">{{message}}</div>') + }), + onClose: function () { + self.set('deleteServiceProgressPopup', null); + this._super(); + } + }); + self.set('deleteServiceProgressPopup', progressPopup); + self.deleteServiceCall(serviceNames); this._super(); }, @@ -1458,7 +1480,8 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow * @override */ saveConfigs: function() { - var data = []; + var data = [], + progressPopup = this.get('deleteServiceProgressPopup'); this.get('stepConfigs').forEach(function(stepConfig) { var serviceConfig = this.getServiceConfigToSave(stepConfig.get('serviceName'), stepConfig.get('configs')); @@ -1468,16 +1491,24 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow }, this); if (Em.isArray(data) && data.length) { - this.putChangedConfigurations(data, 'confirmServiceDeletion'); + this.putChangedConfigurations(data, 'confirmServiceDeletion', function () { + if (progressPopup) { + progressPopup.onClose(); + } + }); } else { this.confirmServiceDeletion(); } }, confirmServiceDeletion: function() { - var msg = this.get('interDependentServices.length') - ? Em.I18n.t('services.service.delete.service.success.confirmation.plural').format(this.get('serviceNamesToDelete').join(',')) - : Em.I18n.t('services.service.delete.service.success.confirmation').format(this.get('content.serviceName')); + var progressPopup = this.get('deleteServiceProgressPopup'), + msg = this.get('interDependentServices.length') + ? Em.I18n.t('services.service.delete.service.success.confirmation.plural').format(this.get('serviceNamesToDelete').join(',')) + : Em.I18n.t('services.service.delete.service.success.confirmation').format(this.get('content.serviceName')); + if (progressPopup) { + progressPopup.onClose(); + } return App.showAlertPopup(Em.I18n.t('popup.confirmation.commonHeader'), msg, function() { window.location.reload(); }) @@ -1515,6 +1546,10 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow }, deleteServiceCallErrorCallback: function (jqXHR, ajaxOptions, error, opt) { + var progressPopup = this.get('deleteServiceProgressPopup'); + if (progressPopup) { + progressPopup.onClose(); + } App.ajax.defaultErrorHandler(jqXHR, opt.url, opt.type, jqXHR.status); } http://git-wip-us.apache.org/repos/asf/ambari/blob/59148381/ambari-web/app/messages.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js index 6465812..79fe36a 100644 --- a/ambari-web/app/messages.js +++ b/ambari-web/app/messages.js @@ -1845,6 +1845,7 @@ Em.I18n.translations = { 'services.service.confirmDelete.popup.body.type': 'Type "{0}" to confirm', 'services.service.confirmDelete.popup.body.dependent': 'You must confirm delete of <b>{0}</b> and <b>{1}</b> by typing "{2}"' + ' in the confirmation box. <b>This operation is not reversible and all configuration history will be lost.</b>', + 'services.service.delete.progressPopup.message': 'Deleting {0}...', 'services.service.delete.service.success.confirmation': 'Service {0} was successfully deleted', 'services.service.delete.service.success.confirmation.plural': 'Services {0} were successfully deleted', http://git-wip-us.apache.org/repos/asf/ambari/blob/59148381/ambari-web/app/mixins/common/configs/configs_saver.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/configs/configs_saver.js b/ambari-web/app/mixins/common/configs/configs_saver.js index f486180..a841951 100644 --- a/ambari-web/app/mixins/common/configs/configs_saver.js +++ b/ambari-web/app/mixins/common/configs/configs_saver.js @@ -603,10 +603,11 @@ App.ConfigsSaverMixin = Em.Mixin.create({ * contains the site name and tag to be used. * @param {Object[]} services * @param {String} [successCallback] + * @param {Function} [alwaysCallback] * @return {$.ajax} * @method putChangedConfigurations */ - putChangedConfigurations: function (services, successCallback) { + putChangedConfigurations: function (services, successCallback, alwaysCallback) { var ajaxData = { name: 'common.across.services.configurations', sender: this, @@ -618,6 +619,9 @@ App.ConfigsSaverMixin = Em.Mixin.create({ if (successCallback) { ajaxData.success = successCallback; } + if (alwaysCallback) { + ajaxData.callback = alwaysCallback; + } return App.ajax.send(ajaxData); }, http://git-wip-us.apache.org/repos/asf/ambari/blob/59148381/ambari-web/app/styles/application.less ---------------------------------------------------------------------- diff --git a/ambari-web/app/styles/application.less b/ambari-web/app/styles/application.less index d525fd4..4ae4c61 100644 --- a/ambari-web/app/styles/application.less +++ b/ambari-web/app/styles/application.less @@ -6420,3 +6420,17 @@ input[type="radio"].align-checkbox, input[type="checkbox"].align-checkbox { } } +.delete-service-progress { + .modal-body { + text-align: center; + .delete-service-progress-body { + display: inline-block; + .spinner, .progress-message { + float: left; + } + .progress-message { + line-height: @spinner-default-height; + } + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/59148381/ambari-web/test/controllers/main/service/item_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/main/service/item_test.js b/ambari-web/test/controllers/main/service/item_test.js index 4176b3d..5c2b34a 100644 --- a/ambari-web/test/controllers/main/service/item_test.js +++ b/ambari-web/test/controllers/main/service/item_test.js @@ -1494,18 +1494,99 @@ describe('App.MainServiceItemController', function () { describe("#confirmDeleteService()", function() { var mainServiceItemController; - beforeEach(function() { - mainServiceItemController = App.MainServiceItemController.create({}); - sinon.stub(App.ModalPopup, 'show'); + beforeEach(function () { + mainServiceItemController = App.MainServiceItemController.create(); }); - afterEach(function() { + + afterEach(function () { App.ModalPopup.show.restore(); }); - it("App.ModalPopup.show should be called", function() { - mainServiceItemController.confirmDeleteService('S1', [], ''); - expect(App.ModalPopup.show.calledOnce).to.be.true; + describe('confirmation popup', function () { + + beforeEach(function () { + sinon.stub(App.ModalPopup, 'show', Em.K); + mainServiceItemController.confirmDeleteService('S1', [], ''); + }); + + it("App.ModalPopup.show should be called", function() { + expect(App.ModalPopup.show.calledOnce).to.be.true; + }); + + }); + + describe('progress popup', function () { + + var cases = [ + { + serviceName: 'S0', + dependentServiceNames: [], + serviceNames: ['S0'], + message: 's0', + title: 'no dependent services' + }, + { + serviceName: 'S1', + dependentServiceNames: ['S2', 'S3', 'S4'], + serviceNames: ['S1', 'S2', 'S3', 'S4'], + message: 's1, s2, s3 and s4', + title: 'dependent services present' + } + ]; + + cases.forEach(function (item) { + + describe(item.title, function () { + + beforeEach(function () { + sinon.stub(App.ModalPopup, 'show', function (options) { + options._super = Em.K; + if (options.onPrimary) { + options.onPrimary(); + } + return options; + }); + sinon.stub(App.Service, 'find', function (serviceName) { + return Em.Object.create({ + displayName: serviceName.toLowerCase() + }); + }); + sinon.stub(mainServiceItemController, 'deleteServiceCall', Em.K); + mainServiceItemController.confirmDeleteService(item.serviceName, item.dependentServiceNames, ''); + }); + + afterEach(function () { + App.Service.find.restore(); + mainServiceItemController.deleteServiceCall.restore(); + }); + + it('modal popups display', function () { + expect(App.ModalPopup.show.calledTwice).to.be.true; + }); + + it('progress popup message', function () { + expect(mainServiceItemController.get('deleteServiceProgressPopup.message')).to.equal(Em.I18n.t('services.service.delete.progressPopup.message').format(item.message)); + }); + + it('delete service call', function () { + expect(mainServiceItemController.deleteServiceCall.calledOnce).to.be.true; + }); + + it('delete service call arguments', function () { + expect(mainServiceItemController.deleteServiceCall.calledWith(item.serviceNames)).to.be.true; + }); + + it('progress popup close', function () { + mainServiceItemController.get('deleteServiceProgressPopup').onClose(); + expect(mainServiceItemController.get('deleteServiceProgressPopup')).to.be.null; + }); + + }); + + }); + }); + }); describe('#interDependentServices', function() {