You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by xi...@apache.org on 2015/04/03 03:09:09 UTC

ambari git commit: AMBARI-10338. Create Widget wizard: Show threshold and unit field for a widget type.(xiwang)

Repository: ambari
Updated Branches:
  refs/heads/trunk 6d0eb71da -> ff4598373


AMBARI-10338. Create Widget wizard: Show threshold and unit field for a widget type.(xiwang)


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

Branch: refs/heads/trunk
Commit: ff45983735c5346635af876233c9a02fcd8d247f
Parents: 6d0eb71
Author: Xi Wang <xi...@apache.org>
Authored: Thu Apr 2 15:30:20 2015 -0700
Committer: Xi Wang <xi...@apache.org>
Committed: Thu Apr 2 17:35:19 2015 -0700

----------------------------------------------------------------------
 .../service/widgets/create/step2_controller.js  | 112 ++++++++-
 ambari-web/app/models.js                        |   1 +
 ambari-web/app/models/widget.js                 |  10 +
 ambari-web/app/models/widget_property.js        | 249 +++++++++++++++++++
 .../app/styles/enhanced_service_dashboard.less  |  26 ++
 .../main/service/widgets/create/step2.hbs       |  16 ++
 .../create/widget_property_threshold.hbs        |  32 +++
 .../main/service/widgets/create/step2_view.js   |  18 ++
 8 files changed, 461 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/ff459837/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js b/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js
index 2438a80..62d935a 100644
--- a/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js
+++ b/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js
@@ -21,14 +21,120 @@ var App = require('app');
 App.WidgetWizardStep2Controller = Em.Controller.extend({
   name: "widgetWizardStep2Controller",
 
+  widgetProperties: [],
+  widgetMetrics: {},
+  widgetValues: {},
 
   //TODO: Following computed property needs to be implemented. Next button should be enabled when there is no validation error and all required fields are filled
-  isSubmitDisabled: function() {
-    return false;
-  }.property(''),
+  isSubmitDisabled: function () {
+    return this.get('widgetProperties').someProperty('isValid', false);
+  }.property('widgetProperties.@each.isValid'),
 
+  /*
+   * Generate the thresholds, unit, time range.etc object based on the widget type selected in previous step.
+   */
+  renderProperties: function () {
+    var widgetType = this.get('content.widgetType');
+    this.set("widgetProperties", {});
+    var widgetProperties = App.WidgetType.find().findProperty('name', widgetType).get('properties');
+    var properties = [];
+    switch (widgetType) {
+      case 'GAUGE':
+        properties = this.renderGaugeProperties(widgetProperties);
+        break;
+      case 'NUMBER':
+        properties = this.renderNumberProperties(widgetProperties);
+        break;
+      case 'GRAPH':
+        properties = this.renderGraphProperties(widgetProperties);
+        break;
+      case 'TEMPLATE':
+        properties = this.renderTemplateProperties(widgetProperties);
+        break;
+      default:
+        console.error('Incorrect Widget Type: ', widgetType);
+    }
+    this.set('widgetProperties', properties);
+  },
+
+  /**
+   * Render properties for gauge-type widget
+   * @method renderGaugeProperties
+   * @returns {App.WidgetProperties[]}
+   */
+  renderGaugeProperties: function (widgetProperties) {
+    var result = [];
+    result = result.concat([
+      App.WidgetProperties.Thresholds.PercentageThreshold.create({
+        smallValue: '0.7',
+        bigValue: '0.9',
+        isRequired: true
+      })
+    ]);
+    return result;
+  },
+
+  /**
+   * Render properties for number-type widget
+   * @method renderNumberProperties
+   * @returns {App.WidgetProperties[]}
+   */
+  renderNumberProperties: function (widgetProperties) {
+    var result = [];
+
+    result = result.concat([
+      App.WidgetProperties.Threshold.create({
+        smallValue: '10',
+        bigValue: '20',
+        isRequired: false
+      }),
+      App.WidgetProperties.Unit.create({
+        value: 'MB',
+        isRequired: false
+      })
+    ]);
+    return result;
+  },
 
+  /**
+   * Render properties for template-type widget
+   * @method renderTemplateProperties
+   * @returns {App.WidgetProperties[]}
+   */
+  renderTemplateProperties: function (widgetProperties) {
+    var result = [];
+    result = result.concat([
+      App.WidgetProperties.Unit.create({
+        value: 'MB',
+        isRequired: false
+      })
+    ]);
+    return result;
+  },
 
+  /**
+   * Render properties for graph-type widget
+   * @method renderGraphProperties
+   * @returns {App.WidgetProperties[]}
+   */
+  renderGraphProperties: function (widgetProperties) {
+    var result = [];
+    result = result.concat([
+      App.WidgetProperties.GraphType.create({
+        value: 'LINE',
+        isRequired: true
+      }),
+      App.WidgetProperties.TimeRange.create({
+        value: 'Last 1 hour',
+        isRequired: true
+      }),
+      App.WidgetProperties.Unit.create({
+        value: 'MB',
+        isRequired: false
+      })
+    ]);
+    return result;
+  },
 
   next: function () {
     if (!this.get('isSubmitDisabled')) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/ff459837/ambari-web/app/models.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models.js b/ambari-web/app/models.js
index e9de30f..f4e881e 100644
--- a/ambari-web/app/models.js
+++ b/ambari-web/app/models.js
@@ -69,4 +69,5 @@ require('models/configs/tab');
 require('models/configs/section');
 require('models/configs/sub_section');
 require('models/widget');
+require('models/widget_property');
 require('models/widget_layout');
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/ff459837/ambari-web/app/models/widget.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/widget.js b/ambari-web/app/models/widget.js
index 88a80e3..6147bf5 100644
--- a/ambari-web/app/models/widget.js
+++ b/ambari-web/app/models/widget.js
@@ -132,6 +132,11 @@ App.WidgetType.FIXTURES = [
       {
         property_name : 'time_range',
         isRequired: true
+      },
+      {
+        property_name : 'display_unit',
+        display_name: 'unit',
+        isRequired: false
       }
     ]
   },
@@ -141,6 +146,11 @@ App.WidgetType.FIXTURES = [
     display_name: 'Template',
     description: Em.I18n.t('widget.type.template.description'),
     properties: [
+      {
+        property_name : 'display_unit',
+        display_name: 'unit',
+        isRequired: false
+      }
     ]
   }
 ];

http://git-wip-us.apache.org/repos/asf/ambari/blob/ff459837/ambari-web/app/models/widget_property.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/widget_property.js b/ambari-web/app/models/widget_property.js
new file mode 100644
index 0000000..5b2db2d
--- /dev/null
+++ b/ambari-web/app/models/widget_property.js
@@ -0,0 +1,249 @@
+/**
+ * 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 validator = require('utils/validator');
+var numericUtils = require('utils/number_utils');
+
+App.WidgetProperty = Ember.Object.extend({
+
+  /**
+   * label to be shown for property
+   * @type {String}
+   */
+  label: '',
+
+  /**
+   * PORT|METRIC|AGGREGATE
+   * @type {String}
+   */
+  type: '',
+
+  /**
+   * config property value
+   * @type {*}
+   */
+  value: null,
+
+
+  /**
+   * input displayType
+   * one of 'textFields', 'textArea', 'select' or 'threshold'
+   * @type {String}
+   */
+  displayType: '',
+
+
+  /**
+   * space separated list of css class names to use
+   * @type {String}
+   */
+  classNames: '',
+
+  /**
+   * view class according to <code>displayType</code>
+   * @type {Em.View}
+   */
+  viewClass: function () {
+    var displayType = this.get('displayType');
+    switch (displayType) {
+      case 'textField':
+        return App.WidgetPropertyTextFieldView;
+      case 'threshold':
+        return App.WidgetPropertyThresholdView;
+      case 'select':
+        return App.WidgetPropertySelectView;
+      default:
+        console.error('Parsing Widget Property: Unable to find viewClass for displayType ', displayType);
+    }
+  }.property('displayType'),
+
+  /**
+   * Define whether property is valid
+   * Computed property
+   * Should be defined in child class
+   * @type {Boolean}
+   */
+  isValid: function () {
+    return true;
+  }.property(),
+
+  /**
+   * Define whether property is required by user
+   * @type {Boolean}
+   */
+  isRequired: true
+});
+
+App.WidgetProperties = {
+
+  WidgetName: App.WidgetProperty.extend({
+    name: 'widget_name',
+    label: 'Widget Name',
+    displayType: 'textField',
+    classNames: 'widget-property-text-input'
+  }),
+
+  Description: App.WidgetProperty.extend({
+    name: 'description',
+    label: 'Description',
+    displayType: 'textArea',
+    classNames: 'widget-property-text-area'
+  }),
+
+  Unit: App.WidgetProperty.extend({
+    name: 'display-unit',
+    label: 'Unit',
+    displayType: 'textField',
+    classNames: 'widget-property-unit',
+    isValid: function () {
+      return this.get('isRequired') ? this.get('value') : true;
+    }.property('value')
+  }),
+
+  GraphType: App.WidgetProperty.extend({
+    name: 'graph_type',
+    label: 'Graph Type',
+    displayType: 'select',
+    options: ["LINE", "STACK"]
+  }),
+
+  TimeRange: App.WidgetProperty.extend({
+    name: 'time_range',
+    label: 'Time Range',
+    displayType: 'select',
+    options: ["Last 1 hour", "Last 2 hours", "Last 4 hours", "Last 12 hours", "Last 24 hours",
+    "Last 1 week", "Last 1 month", "Last 1 year"]
+  }),
+
+
+  Threshold: App.WidgetProperty.extend({
+
+    name: 'threshold',
+    label: 'Thresholds',
+
+    /**
+     * threshold-value
+     * @type {string}
+     */
+    smallValue: '',
+    bigValue: '',
+    badgeOK: 'OK',
+    badgeWarning: 'WARNING',
+    badgeCritical: 'CRITICAL',
+
+    displayType: 'threshold',
+
+    classNames: 'widget-property-threshold',
+
+    apiProperty: [],
+
+    init: function () {
+      this._super();
+    },
+
+    /**
+     * Check if <code>smallValue</code> is valid float number
+     * @return {boolean}
+     */
+    isSmallValueValid: function () {
+      var value = this.get('smallValue');
+      if (!this.get('isRequired') && !this.get('smallValue') && !this.get('bigValue')) {
+        return true;
+      } else if (!this.get('smallValue')) {
+        return false;
+      }
+      value = ('' + value).trim();
+      return validator.isValidFloat(value) && value > 0;
+    }.property('smallValue', 'bigValue'),
+
+    /**
+     * Check if <code>bigValue</code> is valid float number
+     * @return {boolean}
+     */
+    isBigValueValid: function () {
+      var value = this.get('bigValue');
+      if (!this.get('isRequired') && !this.get('smallValue') && !this.get('bigValue')) {
+        return true;
+      } else if (!this.get('bigValue')) {
+        return false;
+      }
+      value = ('' + value).trim();
+      return validator.isValidFloat(value) && value > 0;
+    }.property('bigValue', 'smallValue'),
+
+    thresholdError: function () {
+      if (this.get('isSmallValueValid') && this.get('isBigValueValid')) {
+        return Number(this.get('smallValue')) > Number(this.get('bigValue'));
+      } else {
+        return false;
+      }
+    }.property('smallValue', 'bigValue', 'isSmallValueValid', 'isBigValueValid'),
+
+    isValid: function () {
+      return this.get('isSmallValueValid') && this.get('isBigValueValid') && (!this.get('thresholdError'));
+    }.property( 'isSmallValueValid', 'isBigValueValid', 'thresholdError'),
+
+    /**
+     * Define whether warning threshold < critical threshold
+     * @type {Boolean}
+     */
+    errorMsg: function () {
+      return this.get('thresholdError') ? "Threshold 1 should be smaller than threshold 2" : null;
+    }.property('thresholdError')
+
+  })
+};
+
+App.WidgetProperties.Thresholds = {
+
+  PercentageThreshold: App.WidgetProperties.Threshold.extend({
+
+    /**
+     * Check if <code>smallValue</code> is valid float number
+     * @return {boolean}
+     */
+    isSmallValueValid: function () {
+      var value = this.get('smallValue');
+      if (!this.get('isRequired') && !this.get('smallValue') && !this.get('bigValue')) {
+        return true;
+      } else if (!this.get('smallValue')) {
+        return false;
+      }
+      value = ('' + value).trim();
+      return validator.isValidFloat(value) && value > 0 && value <=1;
+    }.property('smallValue', 'bigValue'),
+
+    /**
+     * Check if <code>bigValue</code> is valid float number
+     * @return {boolean}
+     */
+    isBigValueValid: function () {
+      var value = this.get('bigValue');
+      if (!this.get('isRequired') && !this.get('smallValue') && !this.get('bigValue')) {
+        return true;
+      } else if (!this.get('bigValue')) {
+        return false;
+      }
+      value = ('' + value).trim();
+      return validator.isValidFloat(value) && value > 0 && value <= 1;
+    }.property('bigValue', 'smallValue')
+
+  })
+}
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/ff459837/ambari-web/app/styles/enhanced_service_dashboard.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/enhanced_service_dashboard.less b/ambari-web/app/styles/enhanced_service_dashboard.less
index 24e52a3..e2acbe2 100644
--- a/ambari-web/app/styles/enhanced_service_dashboard.less
+++ b/ambari-web/app/styles/enhanced_service_dashboard.less
@@ -108,3 +108,29 @@
     }
   }
 }
+
+#add-widget-step2 {
+  .badge-container {
+    height: 26px;
+    .OK, .WARNING, .CRITICAL {
+      line-height: 26px;
+      height: 26px;
+      display: inline-block;
+      width: 70px;
+      text-align: center;
+    }
+    .OK {
+      background-color: @health-status-green;
+    }
+    .WARNING {
+      background-color: @health-status-orange;
+    }
+    .CRITICAL {
+      background-color: @health-status-red;
+    }
+  }
+  .icon-asterisk {
+    color: red;
+    font-size: 8px;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/ff459837/ambari-web/app/templates/main/service/widgets/create/step2.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/widgets/create/step2.hbs b/ambari-web/app/templates/main/service/widgets/create/step2.hbs
index 50f7b86..8ab4c03 100644
--- a/ambari-web/app/templates/main/service/widgets/create/step2.hbs
+++ b/ambari-web/app/templates/main/service/widgets/create/step2.hbs
@@ -25,6 +25,22 @@
     </div>
   </div>
 
+  <div>
+    <form class="form-horizontal">
+      {{#each property in controller.widgetProperties}}
+        <div {{bindAttr class=":control-group property.name property.isValid::error"}}>
+          <label class="control-label">{{property.label}}
+            {{#if property.isRequired }}
+              <i class="icon-asterisk"></i>
+            {{/if}}
+          </label>
+          <div class="controls">
+            {{view property.viewClass propertyBinding="property"}}
+          </div>
+        </div>
+      {{/each}}
+    </form>
+  </div>
 
 
   <div class="btn-area">

http://git-wip-us.apache.org/repos/asf/ambari/blob/ff459837/ambari-web/app/templates/main/service/widgets/create/widget_property_threshold.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/widgets/create/widget_property_threshold.hbs b/ambari-web/app/templates/main/service/widgets/create/widget_property_threshold.hbs
new file mode 100644
index 0000000..ac8b194
--- /dev/null
+++ b/ambari-web/app/templates/main/service/widgets/create/widget_property_threshold.hbs
@@ -0,0 +1,32 @@
+{{!
+* 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.
+}}
+
+<div>
+    <div class="span2 badge-container"><span {{bindAttr class=":label view.property.badgeOK"}}>{{view.property.badgeOK}}</span>&nbsp;</div>
+    <div {{bindAttr class=":span2 property.isSmallValueValid::error"}}>
+      {{view Em.TextField valueBinding="view.property.smallValue" class ="span10"}}
+    </div>
+    <div class="span2 badge-container"><span {{bindAttr class=":label view.property.badgeWarning"}}>{{view.property.badgeWarning}}</span>&nbsp;</div>
+    <div {{bindAttr class=":span2 property.isBigValueValid::error"}}>
+      {{view Em.TextField valueBinding="view.property.bigValue" class ="span10"}}
+    </div>
+    <div class="span3 badge-container"><span {{bindAttr class=":label view.property.badgeCritical"}}>{{view.property.badgeCritical}}</span>&nbsp;</div>
+    <div {{bindAttr class="property.threshold:error"}}>
+      {{view.property.errorMsg}}
+    </div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/ff459837/ambari-web/app/views/main/service/widgets/create/step2_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/widgets/create/step2_view.js b/ambari-web/app/views/main/service/widgets/create/step2_view.js
index 610e98c..4bbcd44 100644
--- a/ambari-web/app/views/main/service/widgets/create/step2_view.js
+++ b/ambari-web/app/views/main/service/widgets/create/step2_view.js
@@ -22,8 +22,26 @@ App.WidgetWizardStep2View = Em.View.extend({
 
   didInsertElement: function () {
     var controller = this.get('controller');
+    controller.renderProperties();
   }
+});
+
+
+App.WidgetPropertyTextFieldView = Em.TextField.extend({
+  valueBinding: 'property.value',
+  classNameBindings: ['property.classNames', 'parentView.basicClass']
+});
 
+App.WidgetPropertyThresholdView = Em.View.extend({
+  templateName: require('templates/main/service/widgets/create/widget_property_threshold'),
+  classNameBindings: ['property.classNames', 'parentView.basicClass']
 });
 
+App.WidgetPropertySelectView = Em.Select.extend({
+  selectionBinding: 'property.value',
+  contentBinding: 'property.options',
+  classNameBindings: ['property.classNames', 'parentView.basicClass']
+});
+
+