AMBARI-18973 Service Auto Start: discard popup appears even when no changes 
were made. (atkach)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/29db2273
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/29db2273
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/29db2273

Branch: refs/heads/branch-feature-AMBARI-18634
Commit: 29db2273e77b8fd4f36c15a2001495a08099250a
Parents: 6088c17
Author: Andrii Tkach <atk...@apache.org>
Authored: Wed Nov 23 13:52:31 2016 +0200
Committer: Andrii Tkach <atk...@apache.org>
Committed: Wed Nov 23 15:25:58 2016 +0200

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   4 +-
 .../main/admin/service_auto_start.js            | 342 +++++++++++--------
 .../templates/main/admin/service_auto_start.hbs |  12 +-
 .../service_auto_start/component_auto_start.hbs |   2 +-
 .../app/views/main/admin/service_auto_start.js  |  68 +---
 .../service_auto_start/component_auto_start.js  |  15 +-
 .../main/admin/service_auto_start_test.js       | 326 ++++++++++++++++++
 .../component_auto_start_test.js                |  64 ++++
 .../views/main/admin/service_auto_start_test.js |  83 +++++
 9 files changed, 703 insertions(+), 213 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/29db2273/ambari-web/app/assets/test/tests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/test/tests.js 
b/ambari-web/app/assets/test/tests.js
index 959d1c6..4263016 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -65,7 +65,7 @@ var files = [
   'test/controllers/main/admin/kerberos/step6_controller_test',
   'test/controllers/main/admin/kerberos/step7_controller_test',
   'test/controllers/main/admin/kerberos/step8_controller_test',
-
+  'test/controllers/main/admin/service_auto_start_test',
   'test/controllers/main/admin/stack_and_upgrade_controller_test',
   'test/controllers/main/admin/stack_upgrade_history_controller_test',
   'test/controllers/main/admin/serviceAccounts_controller_test',
@@ -269,6 +269,8 @@ var files = [
   'test/views/main/admin/stack_upgrade/services_view_test',
   'test/views/main/admin/stack_upgrade/menu_view_test',
   'test/views/main/admin/stack_upgrade/failed_hosts_modal_view_test',
+  'test/views/main/admin/service_auto_start/component_auto_start_test',
+  'test/views/main/admin/service_auto_start_test',
   'test/views/main/dashboard/config_history_view_test',
   'test/views/main/dashboard/widget_test',
   'test/views/main/dashboard/widgets_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/29db2273/ambari-web/app/controllers/main/admin/service_auto_start.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/admin/service_auto_start.js 
b/ambari-web/app/controllers/main/admin/service_auto_start.js
index 3365cae..501e6bb 100644
--- a/ambari-web/app/controllers/main/admin/service_auto_start.js
+++ b/ambari-web/app/controllers/main/admin/service_auto_start.js
@@ -21,12 +21,87 @@ var App = require('app');
 App.MainAdminServiceAutoStartController = Em.Controller.extend({
   name: 'mainAdminServiceAutoStartController',
 
+  /**
+   * @type {?object}
+   * @default null
+   */
   clusterConfigs: null,
+
+  /**
+   * @type {Array}
+   */
   componentsConfigs: [],
+
+  /**
+   * @type {boolean}
+   * @default false
+   */
   isSaveDisabled: true,
-  valueChanged: false,
-  syncTrigger: false, // trigger status reset in switch view after "save"
-  revertTrigger: false, // trigger status reset in switch view after "discard"
+
+  /**
+   * Current value of servicesAutoStart
+   * @type {boolean}
+   */
+  servicesAutoStart: false,
+
+  /**
+   * Value of servicesAutoStart saved on server
+   * @type {boolean}
+   */
+  servicesAutoStartSaved: false,
+
+  /**
+   * config version tag,
+   * required to sync cluster configs locally without reload
+   * @type {?object}
+   */
+  tag: null,
+
+  /**
+   * @type {boolean}
+   */
+  isServicesAutoStartChanged: 
Em.computed.notEqualProperties('servicesAutoStart', 'servicesAutoStartSaved'),
+
+  valueChanged: function() {
+    var isSaveDisabled = true;
+
+    if (this.get('isServicesAutoStartChanged')) {
+      this.set('clusterConfigs.recovery_enabled', 
this.get('servicesAutoStart') + '');
+      isSaveDisabled = false;
+    }
+    this.get('tabs').forEach(function(tab) {
+      if (isSaveDisabled) {
+        isSaveDisabled = !tab.get('components').someProperty('isChanged');
+      }
+    });
+
+    this.set('isSaveDisabled', isSaveDisabled);
+  },
+
+  load: function() {
+    var self = this;
+    var dfd = $.Deferred();
+
+    return this.loadClusterConfig().done(function (data) {
+      var tag = [
+        {
+          siteName: 'cluster-env',
+          tagName: data.Clusters.desired_configs['cluster-env'].tag,
+          newTagName: null
+        }
+      ];
+      self.set('tag', tag);
+      
App.router.get('configurationController').getConfigsByTags(tag).done(function 
(data) {
+        var servicesAutoStart = data[0].properties.recovery_enabled === 'true';
+        self.set('clusterConfigs', data[0].properties);
+        self.set('servicesAutoStart', servicesAutoStart);
+        self.set('servicesAutoStartSaved', servicesAutoStart);
+        self.loadComponentsConfigs().done(dfd.resolve).fail(dfd.reject);
+      }).fail(dfd.reject);
+    }).fail(dfd.reject);
+
+    return dfd.promise();
+  },
 
   loadClusterConfig: function () {
     return App.ajax.send({
@@ -50,64 +125,53 @@ App.MainAdminServiceAutoStartController = 
Em.Controller.extend({
     this.set('componentsConfigs', data.items);
   },
 
+  saveClusterConfigs: function (clusterConfigs) {
+    return App.ajax.send({
+      name: 'admin.save_configs',
+      sender: this,
+      data: {
+        siteName: 'cluster-env',
+        properties: clusterConfigs
+      }
+    });
+  },
+
+  saveComponentSettingsCall: function(recoveryEnabled, components) {
+    return App.ajax.send({
+      name: 'components.update',
+      sender: this,
+      data: {
+        ServiceComponentInfo: {
+          recovery_enabled: recoveryEnabled
+        },
+        query: 'ServiceComponentInfo/component_name.in(' + 
components.join(',') + ')'
+      }
+    });
+  },
+
   tabs: function() {
     var services = {};
     var tabs = [];
-    this.get('componentsConfigs').forEach(function(component) {
+
+    this.get('componentsConfigs').forEach(function (component) {
       var serviceComponentInfo = component.ServiceComponentInfo;
       if (serviceComponentInfo.total_count) {
         if (serviceComponentInfo.category === "MASTER" || 
serviceComponentInfo.category === "SLAVE") {
-          var componentRecovery = Ember.Object.create({
-            display_name: App.format.role(serviceComponentInfo.component_name, 
false),
-            component_name: serviceComponentInfo.component_name,
-            recovery_enabled: serviceComponentInfo.recovery_enabled === 'true',
-            valueChanged: false,
-            service_name: serviceComponentInfo.service_name,
-            syncTrigger: false,
-            revertTrigger: false,
-          });
-          if (services[serviceComponentInfo.service_name]) {
-            
services[serviceComponentInfo.service_name].get('componentRecovery').push(componentRecovery);
-            
services[serviceComponentInfo.service_name].set('enabledComponents', 
services[serviceComponentInfo.service_name].get('enabledComponents') + 
(componentRecovery.get('recovery_enabled') ? 1 : 0));
-            services[serviceComponentInfo.service_name].set('totalComponents', 
services[serviceComponentInfo.service_name].get('totalComponents') + 1);
+          var componentRecovery = 
this.createRecoveryComponent(serviceComponentInfo);
+          var service = services[serviceComponentInfo.service_name];
+
+          if (service) {
+            service.get('components').pushObject(componentRecovery);
+            service.set('enabledComponents', 
services[serviceComponentInfo.service_name].get('enabledComponents') + 
(componentRecovery.get('recovery_enabled') ? 1 : 0));
+            service.set('totalComponents', 
services[serviceComponentInfo.service_name].get('totalComponents') + 1);
           } else {
-            services[serviceComponentInfo.service_name] = Ember.Object.create({
-              service_name: serviceComponentInfo.service_name,
-              display_name: App.format.role(serviceComponentInfo.service_name, 
true),
-              headingClass: "." + serviceComponentInfo.service_name,
-              isActive: false,
-              tooltip: function () {
-                var percentage = this.get('enabledComponents') / 
this.get('totalComponents');
-                var tooltip = '';
-                if (percentage === 1) {
-                  tooltip = 
Em.I18n.t('admin.serviceAutoStart.tooltip.text').format("All");
-                } else if (percentage === 0) {
-                  tooltip = 
Em.I18n.t('admin.serviceAutoStart.tooltip.text').format("No");
-                } else {
-                  tooltip = 
Em.I18n.t('admin.serviceAutoStart.tooltip.text').format(this.get('enabledComponents')
 + "/" + this.get('totalComponents'));
-                }
-                return tooltip;
-              }.property('enabledComponents', 'totalComponents'),
-              componentRecovery: [componentRecovery],
-              enabledComponents: componentRecovery.recovery_enabled ? 1 : 0,
-              totalComponents: 1,
-              indicator: function () {
-                var percentage = this.get('enabledComponents') / 
this.get('totalComponents');
-                var indicator = "icon-adjust";
-                if (percentage === 1) {
-                  indicator = "icon-circle";
-                } else if (percentage === 0) {
-                  indicator = "icon-circle-blank";
-                }
-                return indicator;
-              }.property('enabledComponents', 'totalComponents')
-            });
+            services[serviceComponentInfo.service_name] = 
this.createTab(serviceComponentInfo, componentRecovery);
           }
         }
       }
-    });
-    for (var service in services ) {
-      tabs.push(services[service]);
+    }, this);
+    for (var name in services ) {
+      tabs.push(services[name]);
     }
     if (tabs.length) {
       tabs[0].set('isActive', true);
@@ -115,54 +179,93 @@ App.MainAdminServiceAutoStartController = 
Em.Controller.extend({
     return tabs;
   }.property('componentsConfigs'),
 
-  checkValuesChange: function () {
-    var valuesChanged = this.get('valueChanged');
-    this.get('tabs').forEach(function (service) {
-      service.get('componentRecovery').forEach(function (component) {
-        valuesChanged = valuesChanged || component.get('valueChanged');
+  createRecoveryComponent: function(serviceComponentInfo) {
+    return Ember.Object.create({
+      displayName: App.format.role(serviceComponentInfo.component_name, false),
+      componentName: serviceComponentInfo.component_name,
+      recoveryEnabled: serviceComponentInfo.recovery_enabled === 'true',
+      recoveryEnabledSaved: serviceComponentInfo.recovery_enabled === 'true',
+      isChanged: Em.computed.notEqualProperties('recoveryEnabled', 
'recoveryEnabledSaved'),
+      serviceName: serviceComponentInfo.service_name
+    });
+  },
+
+  createTab: function(serviceComponentInfo, componentRecovery) {
+    return Ember.Object.create({
+      service_name: serviceComponentInfo.service_name,
+      display_name: App.format.role(serviceComponentInfo.service_name, true),
+      headingClass: "." + serviceComponentInfo.service_name,
+      isActive: false,
+      tooltip: function () {
+        var percentage = this.get('enabledComponents') / 
this.get('totalComponents');
+        var text = Em.I18n.t('admin.serviceAutoStart.tooltip.text');
+        if (percentage === 1) {
+          return text.format("All");
+        } else if (percentage === 0) {
+          return text.format("No");
+        } else {
+          return text.format(this.get('enabledComponents') + "/" + 
this.get('totalComponents'));
+        }
+      }.property('enabledComponents', 'totalComponents'),
+      components: Em.A([componentRecovery]),
+      enabledComponents: componentRecovery.recovery_enabled ? 1 : 0,
+      totalComponents: 1,
+      indicator: function () {
+        var percentage = this.get('enabledComponents') / 
this.get('totalComponents');
+        var indicator = "icon-adjust";
+        if (percentage === 1) {
+          indicator = "icon-circle";
+        } else if (percentage === 0) {
+          indicator = "icon-circle-blank";
+        }
+        return indicator;
+      }.property('enabledComponents', 'totalComponents')
+    });
+  },
+
+  filterTabsByRecovery: function(tabs, isRecoveryEnabled) {
+    var components = [];
+
+    tabs.forEach(function (service) {
+      service.get('components').forEach(function (component) {
+        if (component.get('isChanged') && component.get('recoveryEnabled') === 
isRecoveryEnabled) {
+          components.push(component.get('componentName'));
+        }
       });
     });
-    this.set('isSaveDisabled', !valuesChanged);
-  }.observes('valueChanged'),
+    return components;
+  },
 
   syncStatus: function () {
-    // component level switches
-    this.get('tabs').forEach(function (service) {
-      service.get('componentRecovery').forEach(function (component) {
-        component.set('valueChanged', false);
-        component.toggleProperty('syncTrigger');
+    var servicesAutoStart = this.get('servicesAutoStart');
+    this.set('servicesAutoStartSaved', servicesAutoStart);
+    
App.router.get('configurationController').getConfigsByTags(this.get('tag')).done(function(data)
 {
+      data[0].properties.recovery_enabled = servicesAutoStart + '';
+      App.router.get('configurationController').saveToDB(data);
+    });
+    this.get('tabs').forEach(function(tab) {
+      tab.get('components').forEach(function(component) {
+        component.set('recoveryEnabledSaved', 
component.get('recoveryEnabled'));
       });
     });
-    // service level switch
-    this.toggleProperty('syncTrigger');
-    this.set('valueChanged', false);
-    this.set('isSaveDisabled', true);
+    this.valueChanged();
   },
 
   revertStatus: function () {
-    // component level switches
-    this.get('tabs').forEach(function (service) {
-      service.get('componentRecovery').forEach(function (component) {
-        component.set('valueChanged', false);
-        component.toggleProperty('revertTrigger');
+    this.set('servicesAutoStart', this.get('servicesAutoStartSaved'));
+    this.get('tabs').forEach(function(tab) {
+      tab.get('components').forEach(function(component) {
+        component.set('recoveryEnabled', 
component.get('recoveryEnabledSaved'));
       });
     });
-    // service level switch
-    this.toggleProperty('revertTrigger');
-    this.set('valueChanged', false);
-    this.set('isSaveDisabled', true);
   },
 
   enableAll: function (event) {
-    event.context.get('componentRecovery').forEach(function (component) {
-      component.set('recoveryEnabled', true);
-    });
+    event.context.get('components').setEach('recoveryEnabled', true);
   },
 
   disableAll: function (event) {
-    event.context.get('componentRecovery').forEach(function (component) {
-      component.set('recoveryEnabled', false);
-    });
+    event.context.get('components').setEach('recoveryEnabled', false);
   },
 
   /**
@@ -193,79 +296,38 @@ App.MainAdminServiceAutoStartController = 
Em.Controller.extend({
       primary: Em.I18n.t('common.save'),
       secondary: Em.I18n.t('common.cancel'),
       onSave: function () {
-        //save cluster setting
-        if (self.get('valueChanged')) {
-          self.saveClusterConfigs(self.get('clusterConfigs'));
+        var clusterConfigsCall, enabledComponentsCall, disabledComponentsCall;
+
+        if (self.get('isServicesAutoStartChanged')) {
+          clusterConfigsCall = 
self.saveClusterConfigs(self.get('clusterConfigs'));
         }
-        //save component settings
-        var enabledComponents = [];
-        var disabledComponents = [];
-        self.get('tabs').forEach(function (service) {
-          service.get('componentRecovery').forEach(function (component) {
-            if (component.get('valueChanged')) {
-              if (component.get('recovery_enabled')) {
-                enabledComponents.push(component.get('component_name'));
-              } else {
-                disabledComponents.push(component.get('component_name'));
-              }
-            }
-          });
-        });
-        if (enabledComponents.length){
-          var promise1 = App.ajax.send({
-            name: 'components.update',
-            sender: this,
-            data: {
-              ServiceComponentInfo: {
-                recovery_enabled: "true"
-              },
-              query: 'ServiceComponentInfo/component_name.in(' + 
enabledComponents.join(',') + ')'
-            }
-          });
+        var enabledComponents = self.filterTabsByRecovery(self.get('tabs'), 
true);
+        var disabledComponents = self.filterTabsByRecovery(self.get('tabs'), 
false);
+
+        if (enabledComponents.length) {
+          enabledComponentsCall = self.saveComponentSettingsCall('true', 
enabledComponents);
         }
-        if (disabledComponents.length){
-          var promise2 = App.ajax.send({
-            name: 'components.update',
-            sender: this,
-            data: {
-              ServiceComponentInfo: {
-                recovery_enabled: "false"
-              },
-              query: 'ServiceComponentInfo/component_name.in(' + 
disabledComponents.join(',') + ')'
-            }
-          });
+        if (disabledComponents.length) {
+          disabledComponentsCall = self.saveComponentSettingsCall('false', 
disabledComponents);
         }
-        $.when(promise1, promise2).done(
-          function () {
-            if (typeof transitionCallback === 'function') {
-              transitionCallback();
-            }
-            self.syncStatus();
+        $.when(clusterConfigsCall, enabledComponentsCall, 
disabledComponentsCall).done(function () {
+          if (typeof transitionCallback === 'function') {
+            transitionCallback();
           }
-        );
+          self.syncStatus();
+        });
         this.hide();
       },
       onDiscard: function () {
+        self.revertStatus();
         if (typeof transitionCallback === 'function') {
           transitionCallback();
         }
-        self.revertStatus();
         this.hide();
       },
       onCancel: function () {
         this.hide();
       }
     });
-  },
-
-  saveClusterConfigs: function (clusterConfigs) {
-    return App.ajax.send({
-      name: 'admin.save_configs',
-      sender: this,
-      data: {
-        siteName: 'cluster-env',
-        properties: clusterConfigs
-      }
-    });
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/29db2273/ambari-web/app/templates/main/admin/service_auto_start.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/admin/service_auto_start.hbs 
b/ambari-web/app/templates/main/admin/service_auto_start.hbs
index 91d3706..be101e7 100644
--- a/ambari-web/app/templates/main/admin/service_auto_start.hbs
+++ b/ambari-web/app/templates/main/admin/service_auto_start.hbs
@@ -24,7 +24,7 @@
       <strong>{{t admin.serviceAutoStart.body.text}}</strong>
     </div>
     <div class="col-md-4">
-      {{view App.CheckboxView checkedBinding="view.switcherValue"}}
+      {{view App.CheckboxView checkedBinding="controller.servicesAutoStart"}}
     </div>
     <div class="col-md-6">
       <div class="pull-right operations-button">
@@ -33,7 +33,7 @@
       </div>
     </div>
   </div>
-    {{#if view.switcherValue}}
+    {{#if view.isLoaded}}
         <hr>
         <div class="row">
           <div class="col-md-4 col-lg-3"><h5>{{t common.service}}</h5></div>
@@ -59,20 +59,22 @@
             {{#each tab in controller.tabs}}
               <div {{bindAttr class=":tab-pane tab.isActive:active 
tab.service_name"}}>
                 <table class="table">
-                {{#each component in tab.componentRecovery}}
+                {{#each component in tab.components}}
                   <tr>
                     <td class="col-md-4">
                       {{component.display_name}}
                     </td>
                     <td class="col-md-8">
-                      {{view App.MainAdminServiceAutoStartComponentView 
recoveryEnabledBinding="component.recovery_enabled" 
componentBinding="component" tabBinding="tab"}}
+                      {{view App.MainAdminServiceAutoStartComponentView 
componentBinding="component" tabBinding="tab"}}
                     </td>
                   </tr>
                 {{/each}}
                   <tr>
                     <td class="col-md-4"></td>
                     <td class="col-md-8">
-                      <a href="#" class="enable-all-link" {{action enableAll 
tab target="controller"}}>{{t common.enableAll}}</a> |
+                      <a href="#" class="enable-all-link" {{action enableAll 
tab target="controller"}}>
+                          {{t common.enableAll}}
+                      </a> |
                       <a href="#" {{action disableAll tab 
target="controller"}}>{{t common.disableAll}}</a>
                     </td>
                   </tr>

http://git-wip-us.apache.org/repos/asf/ambari/blob/29db2273/ambari-web/app/templates/main/admin/service_auto_start/component_auto_start.hbs
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/templates/main/admin/service_auto_start/component_auto_start.hbs
 
b/ambari-web/app/templates/main/admin/service_auto_start/component_auto_start.hbs
index 06a17f8..a52dcfd 100644
--- 
a/ambari-web/app/templates/main/admin/service_auto_start/component_auto_start.hbs
+++ 
b/ambari-web/app/templates/main/admin/service_auto_start/component_auto_start.hbs
@@ -16,4 +16,4 @@
 * limitations under the License.
 }}
 
-{{view App.CheckboxView checkedBinding="view.recoveryEnabled"}}
+{{view App.CheckboxView checkedBinding="view.component.recoveryEnabled"}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/29db2273/ambari-web/app/views/main/admin/service_auto_start.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/admin/service_auto_start.js 
b/ambari-web/app/views/main/admin/service_auto_start.js
index 5b10d1b..7b4e46c 100644
--- a/ambari-web/app/views/main/admin/service_auto_start.js
+++ b/ambari-web/app/views/main/admin/service_auto_start.js
@@ -20,49 +20,26 @@ var App = require('app');
 
 App.MainAdminServiceAutoStartView = Em.View.extend({
   templateName: require('templates/main/admin/service_auto_start'),
+
   /**
-   * Value used in the checkbox.
-   *
-   * @property switcherValue
    * @type {boolean}
+   * @default false
    */
-  switcherValue: false,
-
-  savedRecoveryEnabled: false,
+  isLoaded: false,
 
   didInsertElement: function () {
     var self = this;
-    this.get('controller').loadClusterConfig().done(function (data) {
-      var tag = [
-        {
-          siteName: 'cluster-env',
-          tagName: data.Clusters.desired_configs['cluster-env'].tag,
-          newTagName: null
-        }
-      ];
-      
App.router.get('configurationController').getConfigsByTags(tag).done(function 
(data) {
-        self.set('controller.clusterConfigs', data[0].properties);
-        self.set('switcherValue', data[0].properties.recovery_enabled === 
'true');
-        self.set('savedRecoveryEnabled', self.get('switcherValue'));
-        // plugin should be initiated after applying binding for switcherValue
-        Em.run.later('sync', function() {
-          self.initSwitcher();
-        }.bind(self), 10);
-        self.get('controller').loadComponentsConfigs();
-      });
+    this.get('controller').load().then(function() {
+      self.set('isLoaded', true);
+      self.initSwitcher();
     });
   },
 
-  /**
-   * Init switcher plugin.
-   *
-   * @method initSwitcher
-   */
-  updateClusterConfigs: function (state){
-    this.set('switcherValue', state);
-    this.set('controller.clusterConfigs.recovery_enabled', '' + state);
-    this.set('controller.valueChanged', this.get('savedRecoveryEnabled') !== 
state);
-  },
+  onValueChange: function () {
+    if (this.get('switcher')) {
+      this.get('switcher').bootstrapSwitch('state', 
this.get('controller.servicesAutoStart'));
+    }
+  }.observes('controller.servicesAutoStart'),
 
   /**
    * Init switcher plugin.
@@ -71,33 +48,20 @@ App.MainAdminServiceAutoStartView = Em.View.extend({
    */
   initSwitcher: function () {
     var self = this;
-    if (this.$()) {
+    if (this.$) {
       this.set('switcher', this.$("input:eq(0)").bootstrapSwitch({
+        state: self.get('controller.servicesAutoStart'),
         onText: Em.I18n.t('common.enabled'),
         offText: Em.I18n.t('common.disabled'),
         offColor: 'default',
         onColor: 'success',
         handleWidth: Math.max(Em.I18n.t('common.enabled').length, 
Em.I18n.t('common.disabled').length) * 8,
         onSwitchChange: function (event, state) {
-          self.updateClusterConfigs(state);
+          self.set('controller.servicesAutoStart', state);
+          self.get('controller').valueChanged();
         }
       }));
     }
-  },
-
-  syncComponentRecoveryStatus: function () {
-    this.set('savedRecoveryEnabled', this.get('switcherValue'));
-  }.observes('controller.syncTrigger'),
-
-  revertComponentRecoveryStatus: function () {
-    this.set('switcherValue', this.get('savedRecoveryEnabled'));
-    if (this.get('controller.clusterConfigs')) {
-      this.set('controller.clusterConfigs.recovery_enabled', '' +  
this.get('savedRecoveryEnabled'));
-    }
-    var switcher = this.get('switcher');
-    if (switcher) {
-      switcher.bootstrapSwitch('state', this.get('savedRecoveryEnabled'));
-    }
-  }.observes('controller.revertTrigger')
+  }
 });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/29db2273/ambari-web/app/views/main/admin/service_auto_start/component_auto_start.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/views/main/admin/service_auto_start/component_auto_start.js 
b/ambari-web/app/views/main/admin/service_auto_start/component_auto_start.js
index ceafd21..8588b04 100644
--- a/ambari-web/app/views/main/admin/service_auto_start/component_auto_start.js
+++ b/ambari-web/app/views/main/admin/service_auto_start/component_auto_start.js
@@ -21,24 +21,13 @@ var App = require('app');
 App.MainAdminServiceAutoStartComponentView = Em.View.extend({
   templateName: 
require('templates/main/admin/service_auto_start/component_auto_start'),
 
-  recoveryEnabled: false,
-  savedRecoveryEnabled: false,
   tab: null,
   component: null,
 
   didInsertElement: function () {
-    this.set('savedRecoveryEnabled', this.get('recoveryEnabled'));
     this.initSwitcher();
   },
 
-  syncComponentRecoveryStatus: function () {
-    this.set('savedRecoveryEnabled', this.get('component.recoveryEnabled'))
-  }.observes('component.syncTrigger'),
-
-  revertComponentRecoveryStatus: function () {
-    this.set('component.recoveryEnabled', this.get('savedRecoveryEnabled'));
-  }.observes('component.revertTrigger'),
-
   onValueChange: function () {
     this.get('switcher').bootstrapSwitch('state', 
this.get('component.recoveryEnabled'));
   }.observes('component.recoveryEnabled'),
@@ -59,10 +48,8 @@ App.MainAdminServiceAutoStartComponentView = Em.View.extend({
         handleWidth: Math.max(Em.I18n.t('common.enabled').length, 
Em.I18n.t('common.disabled').length) * 8,
         onSwitchChange: function (event, state) {
           self.set('tab.enabledComponents', self.get('tab.enabledComponents') 
+ (state ? 1 : -1));
-          self.set('recoveryEnabled', state);
           self.set('component.recoveryEnabled', state);
-          self.set('component.valueChanged', self.get('savedRecoveryEnabled') 
!== state);
-          self.get('parentView.controller').checkValuesChange();
+          self.get('controller').valueChanged();
         }
       }));
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/29db2273/ambari-web/test/controllers/main/admin/service_auto_start_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/admin/service_auto_start_test.js 
b/ambari-web/test/controllers/main/admin/service_auto_start_test.js
new file mode 100644
index 0000000..8cb176e
--- /dev/null
+++ b/ambari-web/test/controllers/main/admin/service_auto_start_test.js
@@ -0,0 +1,326 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+var testHelpers = require('test/helpers');
+
+describe('App.MainAdminServiceAutoStartController', function() {
+  var controller;
+
+  beforeEach(function() {
+    controller = App.MainAdminServiceAutoStartController.create({
+      clusterConfigs: {}
+    });
+  });
+
+
+  describe('#valueChanged()', function() {
+
+    it('servicesAutoStart is changed', function() {
+      controller.reopen({
+        servicesAutoStart: true,
+        servicesAutoStartSaved: false,
+        tabs: []
+      });
+      controller.valueChanged();
+      expect(controller.get('isSaveDisabled')).to.be.false;
+    });
+
+    it('servicesAutoStart is not changed', function() {
+      controller.reopen({
+        servicesAutoStart: true,
+        servicesAutoStartSaved: true,
+        tabs: []
+      });
+      controller.valueChanged();
+      expect(controller.get('isSaveDisabled')).to.be.true;
+    });
+
+    it('components state is not changed', function() {
+      controller.reopen({
+        servicesAutoStart: true,
+        servicesAutoStartSaved: true,
+        tabs: [Em.Object.create({
+          components: [
+            Em.Object.create({
+              isChanged: false
+            })
+          ]
+        })]
+      });
+      controller.valueChanged();
+      expect(controller.get('isSaveDisabled')).to.be.true;
+    });
+
+    it('components state is changed', function() {
+      controller.reopen({
+        servicesAutoStart: true,
+        servicesAutoStartSaved: true,
+        tabs: [Em.Object.create({
+          components: [
+            Em.Object.create({
+              isChanged: true
+            })
+          ]
+        })]
+      });
+      controller.valueChanged();
+      expect(controller.get('isSaveDisabled')).to.be.false;
+    });
+  });
+
+  describe('#loadClusterConfig()', function() {
+
+    it('App.ajax.send should be called', function() {
+      controller.loadClusterConfig();
+      var args = testHelpers.findAjaxRequest('name', 'config.tags.site');
+      expect(args[0]).to.be.eql({
+        name: 'config.tags.site',
+        sender: controller,
+        data: {
+          site: 'cluster-env'
+        }
+      });
+    });
+  });
+
+  describe('#loadComponentsConfigs()', function() {
+
+    it('App.ajax.send should be called', function() {
+      controller.loadComponentsConfigs();
+      var args = testHelpers.findAjaxRequest('name', 
'components.get_category');
+      expect(args[0]).to.be.eql({
+        name: 'components.get_category',
+        sender: controller,
+        success: 'loadComponentsConfigsSuccess'
+      });
+    });
+  });
+
+  describe('#loadComponentsConfigsSuccess()', function() {
+
+    it('componentsConfigs should be set', function() {
+      controller.loadComponentsConfigsSuccess({items: {prop1: 'val1'}});
+      expect(controller.get('componentsConfigs')).to.be.eql({prop1: 'val1'});
+    });
+  });
+
+  describe('#saveClusterConfigs()', function() {
+    var clusterConfigs = {};
+
+    it('App.ajax.send should be called', function() {
+      controller.saveClusterConfigs(clusterConfigs);
+      var args = testHelpers.findAjaxRequest('name', 'admin.save_configs');
+      expect(args[0]).to.be.eql({
+        name: 'admin.save_configs',
+        sender: controller,
+        data: {
+          siteName: 'cluster-env',
+          properties: clusterConfigs
+        }
+      });
+    });
+  });
+
+  describe('#saveComponentSettingsCall()', function() {
+    it('App.ajax.send should be called', function() {
+      controller.saveComponentSettingsCall(true, ['c1', 'c2']);
+      var args = testHelpers.findAjaxRequest('name', 'components.update');
+      expect(args[0]).to.be.eql({
+        name: 'components.update',
+        sender: controller,
+        data: {
+          ServiceComponentInfo: {
+            recovery_enabled: true
+          },
+          query: 'ServiceComponentInfo/component_name.in(c1,c2)'
+        }
+      });
+    });
+  });
+
+  describe('#createRecoveryComponent()', function() {
+
+    it('should return recovery component', function() {
+      var serviceComponentInfo = {
+        component_name: 'c1',
+        recovery_enabled: 'true',
+        service_name: 's1'
+      };
+      
expect(controller.createRecoveryComponent(serviceComponentInfo)).to.be.an.object;
+    });
+  });
+
+  describe('#createTab()', function() {
+
+    it('should return tab', function() {
+      var serviceComponentInfo = {
+        component_name: 'c1',
+        recovery_enabled: 'true',
+        service_name: 's1'
+      };
+      expect(controller.createTab(serviceComponentInfo, {})).to.be.an.object;
+    });
+  });
+
+  describe('#filterTabsByRecovery()', function() {
+
+    it('should return empty when no components were changed', function() {
+      var tabs = [Em.Object.create({
+        components: [
+          Em.Object.create({
+            isChanged: false
+          })
+        ]
+      })];
+      expect(controller.filterTabsByRecovery(tabs, true)).to.be.empty;
+    });
+
+    it('should return enabled components ', function() {
+      var tabs = [Em.Object.create({
+        components: [
+          Em.Object.create({
+            isChanged: true,
+            recoveryEnabled: true
+          })
+        ]
+      })];
+      expect(controller.filterTabsByRecovery(tabs, true)).to.have.length(1);
+    });
+
+    it('should return disabled components ', function() {
+      var tabs = [Em.Object.create({
+        components: [
+          Em.Object.create({
+            isChanged: true,
+            recoveryEnabled: false
+          })
+        ]
+      })];
+      expect(controller.filterTabsByRecovery(tabs, false)).to.have.length(1);
+    });
+  });
+
+  describe('#syncStatus()', function() {
+    var mock = {
+      getConfigsByTags: function() {
+        return {
+          done: function(callback) {
+            callback([{properties: {}}]);
+          }
+        }
+      },
+      saveToDB: Em.K
+    };
+
+    beforeEach(function() {
+      sinon.stub(App.router, 'get').returns(mock);
+      sinon.spy(mock, 'saveToDB');
+      sinon.stub(controller, 'valueChanged');
+    });
+
+    afterEach(function() {
+      App.router.get.restore();
+      mock.saveToDB.restore();
+      controller.valueChanged.restore();
+    });
+
+    it('should save servicesAutoStart to local DB', function() {
+      controller.setProperties({
+        servicesAutoStart: true
+      });
+      controller.syncStatus();
+      expect(mock.saveToDB.getCall(0).args[0]).to.be.eql([{
+        properties: {
+          recovery_enabled: 'true'
+        }
+      }]);
+    });
+
+    it('recoveryEnabledSaved should be synced with recoveryEnabled', 
function() {
+      controller.reopen({
+        servicesAutoStart: true,
+        tabs: [Em.Object.create({
+          components: [
+            Em.Object.create({
+              recoveryEnabled: true,
+              recoveryEnabledSaved: false
+            })
+          ]
+        })]
+      });
+      controller.syncStatus();
+      
expect(controller.get('tabs')[0].get('components')[0].get('recoveryEnabledSaved')).to.be.true;
+    });
+
+    it('valueChanged should be called', function() {
+      controller.syncStatus();
+      expect(controller.valueChanged).to.be.calledonce;
+    });
+  });
+
+  describe('#revertStatus()', function() {
+
+    it('component recoveryEnabled should be reverted', function() {
+      controller.reopen({
+        tabs: [Em.Object.create({
+          components: [
+            Em.Object.create({
+              recoveryEnabled: true,
+              recoveryEnabledSaved: false
+            })
+          ]
+        })]
+      });
+      controller.revertStatus();
+      
expect(controller.get('tabs')[0].get('components')[0].get('recoveryEnabled')).to.be.false;
+    });
+
+    it('servicesAutoStart should be reverted', function() {
+      controller.reopen({
+        servicesAutoStart: true,
+        servicesAutoStartSaved: false
+      });
+      controller.revertStatus();
+      expect(controller.get('servicesAutoStart')).to.be.false;
+    });
+  });
+
+  describe('#enableAll()', function() {
+
+    it('should set each recoveryEnabled to true', function() {
+      var components = [
+        Em.Object.create({recoveryEnabled: false})
+      ];
+      controller.enableAll({context: Em.Object.create({components: 
components})});
+      expect(components[0].get('recoveryEnabled')).to.be.true;
+    });
+  });
+
+  describe('#disableAll()', function() {
+
+    it('should set each recoveryEnabled to false', function() {
+      var components = [
+        Em.Object.create({recoveryEnabled: true})
+      ];
+      controller.disableAll({context: Em.Object.create({components: 
components})});
+      expect(components[0].get('recoveryEnabled')).to.be.false;
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/29db2273/ambari-web/test/views/main/admin/service_auto_start/component_auto_start_test.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/test/views/main/admin/service_auto_start/component_auto_start_test.js
 
b/ambari-web/test/views/main/admin/service_auto_start/component_auto_start_test.js
new file mode 100644
index 0000000..70fd61e
--- /dev/null
+++ 
b/ambari-web/test/views/main/admin/service_auto_start/component_auto_start_test.js
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+var view;
+
+describe('App.MainAdminServiceAutoStartComponentView', function () {
+
+  beforeEach(function () {
+    view = App.MainAdminServiceAutoStartComponentView.create();
+  });
+
+
+  describe('#didInsertElement()', function() {
+
+    beforeEach(function() {
+      sinon.stub(view, 'initSwitcher');
+    });
+
+    afterEach(function() {
+      view.initSwitcher.restore();
+    });
+
+    it('initSwitcher should be called', function() {
+      view.didInsertElement();
+      expect(view.initSwitcher).to.be.calledOnce;
+    });
+  });
+
+  describe('#onValueChange()', function() {
+    var mock = {
+      bootstrapSwitch: Em.K
+    };
+
+    beforeEach(function() {
+      sinon.spy(mock, 'bootstrapSwitch');
+    });
+
+    afterEach(function() {
+      mock.bootstrapSwitch.restore();
+    });
+
+    it('bootstrapSwitch should be called', function() {
+      view.set('switcher', mock);
+      view.onValueChange();
+      expect(mock.bootstrapSwitch).to.be.calledOnce;
+    });
+  });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/29db2273/ambari-web/test/views/main/admin/service_auto_start_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/admin/service_auto_start_test.js 
b/ambari-web/test/views/main/admin/service_auto_start_test.js
new file mode 100644
index 0000000..f45f784
--- /dev/null
+++ b/ambari-web/test/views/main/admin/service_auto_start_test.js
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+var view;
+
+describe('App.MainAdminServiceAutoStartView', function () {
+
+  beforeEach(function () {
+    view = App.MainAdminServiceAutoStartView.create({
+      controller: Em.Object.create({
+        load: Em.K
+      })
+    });
+  });
+
+
+  describe('#didInsertElement()', function() {
+
+    beforeEach(function() {
+      sinon.stub(view, 'initSwitcher');
+      sinon.stub(view.get('controller'), 'load').returns({
+        then: Em.clb
+      })
+    });
+
+    afterEach(function() {
+      view.initSwitcher.restore();
+      view.get('controller').load.restore();
+    });
+
+    it('initSwitcher should be called', function() {
+      view.didInsertElement();
+      expect(view.initSwitcher).to.be.calledOnce;
+    });
+
+    it('isLoaded should be true', function() {
+      view.didInsertElement();
+      expect(view.get('isLoaded')).to.be.true;
+    });
+  });
+
+  describe('#onValueChange()', function() {
+    var mock = {
+      bootstrapSwitch: Em.K
+    };
+
+    beforeEach(function() {
+      sinon.spy(mock, 'bootstrapSwitch');
+    });
+
+    afterEach(function() {
+      mock.bootstrapSwitch.restore();
+    });
+
+    it('bootstrapSwitch should not be called', function() {
+      view.set('switcher', null);
+      view.onValueChange();
+      expect(mock.bootstrapSwitch).to.not.be.called;
+    });
+
+    it('bootstrapSwitch should be called', function() {
+      view.set('switcher', mock);
+      view.onValueChange();
+      expect(mock.bootstrapSwitch).to.be.calledOnce;
+    });
+  });
+});
\ No newline at end of file

Reply via email to