瀏覽代碼

AMBARI-10215. Support config-groups functionality for enhanced-configs (onechiporenko)

Oleg Nechiporenko 10 年之前
父節點
當前提交
5d035d51e7

+ 1 - 0
ambari-web/app/assets/test/tests.js

@@ -165,6 +165,7 @@ var files = ['test/init_model_test',
   'test/utils/ui_effects_test',
   'test/utils/updater_test',
   'test/views/common/chart/linear_time_test',
+  'test/views/common/configs/widgets/config_widget_view_test',
   'test/views/common/configs/widgets/list_config_widget_view_test',
   'test/views/common/configs/widgets/slider_config_widget_view_test',
   'test/views/common/ajax_default_error_popup_body_test',

+ 1 - 0
ambari-web/app/mixins.js

@@ -26,6 +26,7 @@ require('mixins/common/serverValidator');
 require('mixins/common/table_server_view_mixin');
 require('mixins/common/table_server_mixin');
 require('mixins/main/host/details/host_components/decommissionable');
+require('mixins/main/service/configs/config_overridable');
 require('mixins/routers/redirections');
 require('mixins/wizard/wizardProgressPageController');
 require('mixins/wizard/wizardDeployProgressController');

+ 59 - 0
ambari-web/app/mixins/main/service/configs/config_overridable.js

@@ -0,0 +1,59 @@
+/**
+ * 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');
+
+App.ConfigOverridable = Em.Mixin.create({
+
+  /**
+   *
+   * @method createOverrideProperty
+   */
+  createOverrideProperty: function (event) {
+    var serviceConfigProperty = event.contexts[0];
+    var serviceConfigController = this.get('controller');
+    var selectedConfigGroup = serviceConfigController.get('selectedConfigGroup');
+    var isInstaller = (this.get('controller.name') === 'wizardStep7Controller');
+    var configGroups = (isInstaller) ? serviceConfigController.get('selectedService.configGroups') : serviceConfigController.get('configGroups');
+
+    //user property is added, and it has not been saved, not allow override
+    if (serviceConfigProperty.get('isUserProperty') && serviceConfigProperty.get('isNotSaved') && !isInstaller) {
+      App.ModalPopup.show({
+        header: Em.I18n.t('services.service.config.configOverride.head'),
+        body: Em.I18n.t('services.service.config.configOverride.body'),
+        secondary: false
+      });
+      return;
+    }
+    if (selectedConfigGroup.get('isDefault')) {
+      // Launch dialog to pick/create Config-group
+      App.config.launchConfigGroupSelectionCreationDialog(this.get('service.serviceName'),
+        configGroups, serviceConfigProperty, function (selectedGroupInPopup) {
+          console.log("launchConfigGroupSelectionCreationDialog(): Selected/Created:", selectedGroupInPopup);
+          if (selectedGroupInPopup) {
+            serviceConfigController.set('overrideToAdd', serviceConfigProperty);
+            serviceConfigController.set('selectedConfigGroup', selectedGroupInPopup);
+          }
+        }, isInstaller);
+    }
+    else {
+      serviceConfigController.addOverrideProperty(serviceConfigProperty, selectedConfigGroup);
+    }
+  }
+
+});

+ 6 - 4
ambari-web/app/styles/widgets.less

@@ -17,6 +17,12 @@
  */
 @import 'common.less';
 
+.widget {
+  .action-button {
+    margin-left: 20px;
+  }
+}
+
 .list-widget {
   li, li:active {
     a, a:hover, a:focus, a:active, a:visited {
@@ -67,10 +73,6 @@
     box-shadow: none;
   }
 
-  .undo-button {
-    margin-left: 20px;
-  }
-
 }
 
 .spinner-input-widget {

+ 4 - 1
ambari-web/app/templates/common/configs/service_config_layout_tab.hbs

@@ -30,7 +30,10 @@
                     <td class="config-subsection">
                       {{#each config in subsection.configs}}
                         {{#if config.view}}
-                          {{view config.widget configBinding="config"}}
+                          {{#unless config.isHiddenByFilter}}
+                            <p>{{config.name}}</p>
+                            {{view config.widget configBinding="config"}}
+                          {{/unless}}
                         {{/if}}
                       {{/each}}
                     </td>

+ 58 - 0
ambari-web/app/templates/common/configs/widgets/overrides/slider_config_widget_override.hbs

@@ -0,0 +1,58 @@
+{{!
+* 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.
+}}
+{{#each overriddenSCP in view.serviceConfigProperty.overrides}}
+{{! Here serviceConfigBinding should ideally be serviceConfigPropertyBinding }}
+  <div {{bindAttr class="overriddenSCP.errorMessage:error: :control-group :overrideField"}}>
+    {{view App.SliderConfigWidgetView configBinding="overriddenSCP"}}
+    {{#if overriddenSCP.supportsFinal}}
+      <a href="#" data-toggle="tooltip"
+        {{bindAttr class=":btn :btn-small :btn-final overriddenSCP.isFinal:active overriddenSCP.hideFinalIcon:hidden" disabled="overriddenSCP.isNotEditable"}}
+        {{action "toggleFinalFlag" overriddenSCP target="view"}}
+        {{translateAttr data-original-title="services.service.config.final"}}>
+        <i class="icon-lock"></i>
+      </a>
+    {{/if}}
+    {{#if view.isDefaultGroupSelected}}
+      {{#if overriddenSCP.group}}
+        <a href="#" data-toggle="tooltip" {{bindAttr data-original-title="overriddenSCP.group.switchGroupTextFull" }} class="action" {{action selectConfigGroup overriddenSCP.group target="controller"}}>
+          {{overriddenSCP.group.switchGroupTextShort}}
+        </a>
+      {{/if}}
+    {{else}}
+      {{#if overriddenSCP.isEditable}}
+        {{#if isNotDefaultValue}}
+          <a class="btn btn-small" href="#" data-toggle="tooltip"
+            {{action "doRestoreDefaultValue" overriddenSCP target="view"}}
+            {{translateAttr data-original-title="common.undo"}}>
+            <i class="icon-undo"></i>
+          </a>
+        {{/if}}
+        {{#isAccessible ADMIN}}
+          <a class="btn btn-small" href="#" data-toggle="tooltip"
+            {{action "removeOverride" overriddenSCP target="view"}}
+            {{translateAttr data-original-title="common.remove"}}>
+            <i class="icon-minus-sign"></i>
+          </a>
+        {{/isAccessible}}
+        <span class="help-inline">{{overriddenSCP.errorMessage}}</span>
+      {{else}}
+        <a class="action">{{overriddenSCP.group.switchGroupTextShort}}</a> <i class="icon-spinner"></i>
+      {{/if}}
+    {{/if}}
+  </div>
+{{/each}}

+ 20 - 7
ambari-web/app/templates/common/configs/widgets/slider_config_widget.hbs

@@ -17,30 +17,43 @@
 }}
 
 <div class="widget slider-widget">
-  <p>{{view.config.name}}</p>
-
-  <p>{{view.config.description}}</p>
-
   <div>
     <div {{bindAttr class="view.isMirrorValueValid::error :control-group :pull-left"}}>
       <div {{bindAttr class="view.config.stackConfigProperty.valueAttributes.unit:input-append"}}>
-        {{view Ember.TextField valueBinding="view.mirrorValue" class="input-mini"}}
+        {{view Ember.TextField valueBinding="view.mirrorValue" class="input-mini" disabledBinding="view.disabled"}}
         {{#if view.config.stackConfigProperty.valueAttributes.unit}}
           <span class="add-on">{{view.config.stackConfigProperty.valueAttributes.unit}}</span>
         {{/if}}
       </div>
     </div>
-    {{#if view.valueIsChanged}}
-      <div class="pull-left undo-button">
+    {{#if view.undoAllowed}}
+      <div class="pull-left action-button">
         <a class="btn btn-small" href="#" {{action "restoreValue" target="view"}}>
           <i class="icon-undo"></i>
         </a>
       </div>
     {{/if}}
+    {{#if view.overrideAllowed}}
+      {{#isAccessible ADMIN}}
+        <div class="pull-left action-button">
+          <a class="btn btn-small" href="#" data-toggle="tooltip"
+            {{action "createOverrideProperty" view target="view.parentView"}}
+            {{translateAttr data-original-title="common.override"}}>
+            <i class="icon-plus-sign"></i>
+          </a>
+        </div>
+      {{/isAccessible}}
+    {{/if}}
     <div class="clearfix"></div>
   </div>
   <div class="ui-slider-wrapper">
     {{view Ember.TextField valueBinding="view.config.value" class="input-mini slider-input"}}
   </div>
+  {{#if view.config.isOverridden}}
+    {{view App.SliderConfigWidgetOverrideView
+      serviceConfigPropertyBinding="view.config"
+      isDefaultGroupSelectedBinding="controller.selectedConfigGroup.isDefault"
+    }}
+  {{/if}}
   <div class="clearfix"></div>
 </div>

+ 1 - 0
ambari-web/app/views.js

@@ -55,6 +55,7 @@ require('views/common/configs/widgets/config_widget_view');
 require('views/common/configs/widgets/list_config_widget_view');
 require('views/common/configs/widgets/slider_config_widget_view');
 require('views/common/configs/widgets/time_interval_spinner_view');
+require('views/common/configs/widgets/overrides/slider_config_widget_override_view');
 require('views/common/configs/service_config_layout_tab_view');
 require('views/common/filter_combobox');
 require('views/common/filter_combo_cleanable');

+ 1 - 0
ambari-web/app/views/common/configs/overriddenProperty_view.js

@@ -45,6 +45,7 @@ App.ServiceConfigView.SCPOverriddenRowsView = Ember.View.extend({
     Em.$('body>.tooltip').remove();
     if (this.get('isDefaultGroupSelected')) {
       var overrides = this.get('serviceConfigProperty.overrides');
+      if (!overrides) return;
       overrides.forEach(function(overriddenSCP) {
         overriddenSCP.get('group').set('switchGroupTextShort',
           Em.I18n.t('services.service.config_groups.switchGroupTextShort').format(overriddenSCP.get('group.displayName')));

+ 13 - 1
ambari-web/app/views/common/configs/service_config_layout_tab_view.js

@@ -18,7 +18,14 @@
 
 var App = require('app');
 
-App.ServiceConfigLayoutTabView = Em.View.extend({
+App.ServiceConfigLayoutTabView = Em.View.extend(App.ConfigOverridable, {
+
+  /**
+   * @type {App.Service}
+   */
+  service: function () {
+    return this.get('controller.selectedService');
+  }.property('controller.selectedService'),
 
   templateName: require('templates/common/configs/service_config_layout_tab'),
 
@@ -63,6 +70,11 @@ App.ServiceConfigLayoutTabView = Em.View.extend({
               Em.assert('Unknown config widget view for config ' + configProperty.get('id') + ' with type ' + configWidgetType, widget);
               configProperty.set('widget', widget);
               configProperty.set('stackConfigProperty', config);
+              if (configProperty.get('overrides'))
+              configProperty.get('overrides').forEach(function (override) {
+                override.set('widget', widget);
+                override.set('stackConfigProperty', config);
+              });
             });
           });
         });

+ 1 - 37
ambari-web/app/views/common/configs/service_configs_by_category_view.js

@@ -22,7 +22,7 @@ var validator = require('utils/validator');
 var stringUtils = require('utils/string_utils');
 require('utils/configs/modification_handlers/modification_handler');
 
-App.ServiceConfigsByCategoryView = Em.View.extend(App.UserPref, {
+App.ServiceConfigsByCategoryView = Em.View.extend(App.UserPref, App.ConfigOverridable, {
 
   templateName: require('templates/common/configs/service_config_category'),
 
@@ -620,42 +620,6 @@ App.ServiceConfigsByCategoryView = Em.View.extend(App.UserPref, {
     }
     this.configChangeObserver(serviceConfigProperty);
     Em.$('body>.tooltip').remove(); //some tooltips get frozen when their owner's DOM element is removed
-  },
-
-  /**
-   *
-   * @method createOverrideProperty
-   */
-  createOverrideProperty: function (event) {
-    var serviceConfigProperty = event.contexts[0];
-    var serviceConfigController = this.get('controller');
-    var selectedConfigGroup = serviceConfigController.get('selectedConfigGroup');
-    var isInstaller = (this.get('controller.name') === 'wizardStep7Controller');
-    var configGroups = (isInstaller) ? serviceConfigController.get('selectedService.configGroups') : serviceConfigController.get('configGroups');
-
-    //user property is added, and it has not been saved, not allow override
-    if (serviceConfigProperty.get('isUserProperty') && serviceConfigProperty.get('isNotSaved') && !isInstaller) {
-      App.ModalPopup.show({
-        header: Em.I18n.t('services.service.config.configOverride.head'),
-        body: Em.I18n.t('services.service.config.configOverride.body'),
-        secondary: false
-      });
-      return;
-    }
-    if (selectedConfigGroup.get('isDefault')) {
-      // Launch dialog to pick/create Config-group
-      App.config.launchConfigGroupSelectionCreationDialog(this.get('service.serviceName'),
-        configGroups, serviceConfigProperty, function (selectedGroupInPopup) {
-          console.log("launchConfigGroupSelectionCreationDialog(): Selected/Created:", selectedGroupInPopup);
-          if (selectedGroupInPopup) {
-            serviceConfigController.set('overrideToAdd', serviceConfigProperty);
-            serviceConfigController.set('selectedConfigGroup', selectedGroupInPopup);
-          }
-        }, isInstaller);
-    }
-    else {
-      serviceConfigController.addOverrideProperty(serviceConfigProperty, selectedConfigGroup);
-    }
   }
 
 });

+ 26 - 1
ambari-web/app/views/common/configs/widgets/config_widget_view.js

@@ -37,12 +37,37 @@ App.ConfigWidgetView = Em.View.extend({
     return this.get('config.value') != this.get('config.defaultValue');
   }.property('config.value'),
 
+  /**
+   * @type {boolean}
+   */
+  isComparison: false,
+
   /**
    * Reset config-value to its default
    * @method restoreValue
    */
   restoreValue: function () {
     this.set('config.value', this.get('config.defaultValue'));
-  }
+  },
+
+  /**
+   * Determines if override is allowed for <code>config</code>
+   * @type {boolean}
+   */
+  overrideAllowed: function () {
+    var config = this.get('config');
+    if (!config) return false;
+    return config.get('isOriginalSCP') && config.get('isPropertyOverridable') && !this.get('isComparison');
+  }.property('config.isOriginalSCP', 'config.isPropertyOverridable', 'isComparison'),
+
+  /**
+   * Determines if undo is allowed for <code>config</code>
+   * @type {boolean}
+   */
+  undoAllowed: function () {
+    var config = this.get('config');
+    if (!config) return false;
+    return !config.get('cantBeUndone') && config.get('isNotDefaultValue');
+  }.property('config.cantBeUndone', 'config.isNotDefaultValue')
 
 });

+ 25 - 0
ambari-web/app/views/common/configs/widgets/overrides/slider_config_widget_override_view.js

@@ -0,0 +1,25 @@
+/**
+ * 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');
+
+App.SliderConfigWidgetOverrideView = App.ServiceConfigView.SCPOverriddenRowsView.extend({
+
+  templateName: require('templates/common/configs/widgets/overrides/slider_config_widget_override')
+
+});

+ 17 - 0
ambari-web/app/views/common/configs/widgets/slider_config_widget_view.js

@@ -35,6 +35,12 @@ App.SliderConfigWidgetView = App.ConfigWidgetView.extend({
    */
   slider: null,
 
+  /**
+   * Determines if widget controls should be disabled
+   * @type {boolean}
+   */
+  disabled: false,
+
   /**
    * Mirror of the config-value shown in the input on the left of the slider
    * @type {number}
@@ -66,6 +72,16 @@ App.SliderConfigWidgetView = App.ConfigWidgetView.extend({
     return this.get('config.stackConfigProperty.valueAttributes.type') === 'int' ? validator.isValidInt : validator.isValidFloat;
   }.property('config.stackConfigProperty.valueAttributes.type'),
 
+  /**
+   * Enable/disable slider state
+   * @method toggleWidgetState
+   */
+  toggleWidgetState: function () {
+    var slider = this.get('slider');
+    this.get('config.isEditable') ? slider.enable() : slider.disable();
+    this.set('disabled', !this.get('config.isEditable'));
+  }.observes('config.isEditable'),
+
   willInsertElement: function () {
     this._super();
     this.addObserver('mirrorValue', this, this.mirrorValueObs);
@@ -76,6 +92,7 @@ App.SliderConfigWidgetView = App.ConfigWidgetView.extend({
     this.set('mirrorValue', this.get('config.value'));
     this.prepareValueAttributes();
     this.initSlider();
+    this.toggleWidgetState();
   },
 
   /**

+ 152 - 0
ambari-web/test/views/common/configs/widgets/config_widget_view_test.js

@@ -0,0 +1,152 @@
+/**
+ * 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.ConfigWidgetView', function () {
+
+  beforeEach(function () {
+    view = App.ConfigWidgetView.create({
+      config: Em.Object.create({
+        isOriginalSCP: false,
+        isPropertyOverridable: false,
+        cantBeUndone: false,
+        isNotDefaultValue: false
+      })
+    });
+  });
+
+  describe('#undoAllowed', function () {
+
+    Em.A([
+      {
+        cfg: {
+          cantBeUndone: false,
+          isNotDefaultValue: false
+        },
+        e: false
+      },
+      {
+        cfg: {
+          cantBeUndone: true,
+          isNotDefaultValue: false
+        },
+        e: false
+      },
+      {
+        cfg: {
+          cantBeUndone: false,
+          isNotDefaultValue: true
+        },
+        e: true
+      },
+      {
+        cfg: {
+          cantBeUndone: true,
+          isNotDefaultValue: true
+        },
+        e: false
+      }
+    ]).forEach(function (test, index) {
+        it('test #' + index, function () {
+          view.get('config').setProperties(test.cfg);
+          expect(view.get('undoAllowed')).to.equal(test.e);
+        });
+      });
+
+  });
+
+  describe('#overrideAllowed', function () {
+
+    Em.A([
+        {
+          cfg: {
+            isOriginalSCP: false,
+            isPropertyOverridable: false
+          },
+          isComparison: false,
+          e: false
+        },
+        {
+          cfg: {
+            isOriginalSCP: true,
+            isPropertyOverridable: false
+          },
+          isComparison: false,
+          e: false
+        },
+        {
+          cfg: {
+            isOriginalSCP: false,
+            isPropertyOverridable: true
+          },
+          isComparison: false,
+          e: false
+        },
+        {
+          cfg: {
+            isOriginalSCP: true,
+            isPropertyOverridable: true
+          },
+          isComparison: false,
+          e: true
+        },
+        {
+          cfg: {
+            isOriginalSCP: false,
+            isPropertyOverridable: false
+          },
+          isComparison: true,
+          e: false
+        },
+        {
+          cfg: {
+            isOriginalSCP: true,
+            isPropertyOverridable: false
+          },
+          isComparison: true,
+          e: false
+        },
+        {
+          cfg: {
+            isOriginalSCP: false,
+            isPropertyOverridable: true
+          },
+          isComparison: true,
+          e: false
+        },
+        {
+          cfg: {
+            isOriginalSCP: true,
+            isPropertyOverridable: true
+          },
+          isComparison: true,
+          e: false
+        }
+      ]).forEach(function (test, index) {
+        it('test #' + index, function () {
+          view.get('config').setProperties(test.cfg);
+          view.set('isComparison', test.isComparison);
+          expect(view.get('overrideAllowed')).to.equal(test.e);
+        });
+      });
+
+  });
+
+});

+ 10 - 0
ambari-web/test/views/common/configs/widgets/slider_config_widget_view_test.js

@@ -25,6 +25,11 @@ describe('App.SliderConfigWidgetView', function () {
   beforeEach(function () {
     viewInt = App.SliderConfigWidgetView.create({
       initSlider: Em.K,
+      slider: {
+        enable: Em.K,
+        disable: Em.K,
+        setValue: Em.K
+      },
       config: Em.Object.create({
         name: 'a.b.c',
         description: 'A B C',
@@ -44,6 +49,11 @@ describe('App.SliderConfigWidgetView', function () {
     viewInt.didInsertElement();
     viewFloat = App.SliderConfigWidgetView.create({
       initSlider: Em.K,
+      slider: {
+        enable: Em.K,
+        disable: Em.K,
+        setValue: Em.K
+      },
       config: Em.Object.create({
         name: 'a.b.c2',
         description: 'A B C 2',