| /** |
| * 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.WidgetWizardStep2Controller = Em.Controller.extend({ |
| name: "widgetWizardStep2Controller", |
| |
| EXPRESSION_PREFIX: 'Expression', |
| |
| /** |
| * @type {RegExp} |
| * @const |
| */ |
| EXPRESSION_REGEX: /\$\{([\w\s\.\,\+\-\*\/\(\)\:\=\[\]]*)\}/g, |
| |
| /** |
| * list of operators that can be used in expression |
| * @type {Array} |
| * @constant |
| */ |
| OPERATORS: ["+", "-", "*", "/", "(", ")"], |
| |
| /** |
| * actual values of properties in API format |
| * @type {object} |
| */ |
| widgetProperties: {}, |
| |
| /** |
| * @type {Array} |
| */ |
| widgetValues: [], |
| |
| /** |
| * @type {Array} |
| */ |
| widgetMetrics: [], |
| |
| /** |
| * @type {Array} |
| */ |
| expressions: [], |
| |
| /** |
| * used only for GRAPH widget |
| * @type {Array} |
| */ |
| dataSets: [], |
| |
| /** |
| * content of template of Template widget |
| * @type {string} |
| */ |
| templateValue: '', |
| |
| /** |
| * views of properties |
| * @type {Array} |
| */ |
| widgetPropertiesViews: [], |
| |
| /** |
| * @type {boolean} |
| */ |
| isEditWidget: Em.computed.equal('content.controllerName', 'widgetEditController'), |
| |
| /** |
| * metrics filtered by type |
| * @type {Array} |
| */ |
| filteredMetrics: function () { |
| var type = this.get('content.widgetType'); |
| return this.get('content.allMetrics').filter(function (metric) { |
| if (type === 'GRAPH') { |
| return metric.temporal; |
| } else { |
| return metric.point_in_time; |
| } |
| }, this); |
| }.property('content.allMetrics'), |
| |
| /** |
| * @type {boolean} |
| */ |
| isSubmitDisabled: function() { |
| if (this.get('widgetPropertiesViews').someProperty('isValid', false)) { |
| return true; |
| } |
| switch (this.get('content.widgetType')) { |
| case "NUMBER": |
| case "GAUGE": |
| return !this.isExpressionComplete(this.get('expressions')[0]); |
| case "GRAPH": |
| return !this.isGraphDataComplete(this.get('dataSets')); |
| case "TEMPLATE": |
| return this.get('isTemplateInvalid') || !this.isTemplateDataComplete(this.get('expressions'), this.get('templateValue')); |
| } |
| return false; |
| }.property( |
| 'widgetPropertiesViews.@each.isValid', |
| 'dataSets.@each.label', |
| 'templateValue', 'isTemplateInvalid' |
| ), |
| |
| /** |
| * check whether data of expression is complete |
| * @param {Em.Object} expression |
| * @returns {boolean} |
| */ |
| isExpressionComplete: function (expression) { |
| return Boolean(expression && !expression.get('isInvalid') && !expression.get('isEmpty') |
| && expression.get('data')&& expression.get('data').someProperty('isMetric')); |
| }, |
| |
| /** |
| * check whether data of graph widget is complete |
| * @param dataSets |
| * @returns {boolean} isComplete |
| */ |
| isGraphDataComplete: function (dataSets) { |
| var isComplete = Boolean(dataSets.length); |
| |
| for (var i = 0; i < dataSets.length; i++) { |
| if (dataSets[i].get('label').trim() === '' || !this.isExpressionComplete(dataSets[i].get('expression'))) { |
| isComplete = false; |
| break; |
| } |
| } |
| return isComplete; |
| }, |
| |
| /** |
| * check whether data of template widget is complete |
| * @param {Array} expressions |
| * @param {string} templateValue |
| * @returns {boolean} isComplete |
| */ |
| isTemplateDataComplete: function (expressions, templateValue) { |
| var isComplete = Boolean(expressions.length > 0 && templateValue.trim() !== ''); |
| |
| if (isComplete) { |
| for (var i = 0; i < expressions.length; i++) { |
| if (!this.isExpressionComplete(expressions[i])) { |
| isComplete = false; |
| break; |
| } |
| } |
| } |
| return isComplete; |
| }, |
| |
| /** |
| * Add data set |
| * @param {object|null} event |
| * @param {boolean} isDefault |
| * @returns {number} id |
| */ |
| addDataSet: function(event, isDefault) { |
| var id = (isDefault) ? 1 :(Math.max.apply(this, this.get('dataSets').mapProperty('id')) + 1); |
| |
| this.get('dataSets').pushObject(Em.Object.create({ |
| id: id, |
| label: Em.I18n.t('dashboard.widgets.wizard.step2.dataSeries').format(id), |
| isRemovable: !isDefault, |
| expression: Em.Object.create({ |
| id: id, |
| data: [], |
| isInvalid: false, |
| isEmpty: Em.computed.equal('data.length', 0) |
| }) |
| })); |
| return id; |
| }, |
| |
| /** |
| * Remove data set |
| * @param {object} event |
| */ |
| removeDataSet: function(event) { |
| this.get('dataSets').removeObject(event.context); |
| }, |
| |
| /** |
| * Add expression |
| * @param {object|null} event |
| * @param {boolean} isDefault |
| * @returns {number} id |
| */ |
| addExpression: function(event, isDefault) { |
| var id = (isDefault) ? 1 :(Math.max.apply(this, this.get('expressions').mapProperty('id')) + 1); |
| |
| this.get('expressions').pushObject(Em.Object.create({ |
| id: id, |
| isRemovable: !isDefault, |
| data: [], |
| alias: '{{' + this.get('EXPRESSION_PREFIX') + id + '}}', |
| isInvalid: false, |
| isEmpty: Em.computed.equal('data.length', 0) |
| })); |
| return id; |
| }, |
| |
| /** |
| * Remove expression |
| * @param {object} event |
| */ |
| removeExpression: function(event) { |
| this.get('expressions').removeObject(event.context); |
| }, |
| |
| /** |
| * initialize data |
| * widget should have at least one expression or dataSet |
| */ |
| initWidgetData: function() { |
| this.set('widgetProperties', this.get('content.widgetProperties')); |
| this.set('widgetValues', this.get('content.widgetValues')); |
| this.set('widgetMetrics', this.get('content.widgetMetrics')); |
| if (this.get('expressions.length') === 0) { |
| this.addExpression(null, true); |
| } |
| if (this.get('dataSets.length') === 0) { |
| this.addDataSet(null, true); |
| } |
| }, |
| |
| /** |
| * update preview widget with latest expression data |
| * Note: in order to draw widget it should be converted to API format of widget |
| */ |
| updateExpressions: function () { |
| var widgetType = this.get('content.widgetType'); |
| var expressionData = { |
| values: [], |
| metrics: [] |
| }; |
| |
| if (this.get('expressions').length > 0 && this.get('dataSets').length > 0) { |
| switch (widgetType) { |
| case 'GAUGE': |
| case 'NUMBER': |
| expressionData = this.parseExpression(this.get('expressions')[0]); |
| expressionData.values = [ |
| { |
| name: "", |
| value: expressionData.value |
| } |
| ]; |
| break; |
| case 'TEMPLATE': |
| expressionData = this.parseTemplateExpression(this.get('templateValue'), this.get('expressions')); |
| break; |
| case 'GRAPH': |
| expressionData = this.parseGraphDataset(this.get('dataSets')); |
| break; |
| } |
| } |
| this.set('widgetValues', expressionData.values); |
| this.set('widgetMetrics', expressionData.metrics); |
| }.observes('templateValue', 'dataSets.@each.label'), |
| |
| /** |
| * parse Graph data set |
| * @param {Array} dataSets |
| * @returns {{metrics: Array, values: Array}} |
| */ |
| parseGraphDataset: function (dataSets) { |
| var metrics = []; |
| var values = []; |
| |
| dataSets.forEach(function (dataSet) { |
| var result = this.parseExpression(dataSet.get('expression')); |
| metrics.pushObjects(result.metrics); |
| values.push({ |
| name: dataSet.get('label'), |
| value: result.value |
| }); |
| }, this); |
| |
| return { |
| metrics: metrics, |
| values: values |
| }; |
| }, |
| |
| /** |
| * parse expression from template |
| * @param {string} templateValue |
| * @param {Array} expressions |
| * @returns {{metrics: Array, values: {value: *}[]}} |
| */ |
| parseTemplateExpression: function (templateValue, expressions) { |
| var metrics = []; |
| var self = this; |
| |
| // check if there is invalid expression name eg. {{myExpre}} |
| var isTemplateInvalid = false; |
| var validExpressionName = /\{\{(Expression[\d])\}\}/g; |
| var expressionName = /\{\{((?!}}).)*\}\}/g; |
| if (templateValue) { |
| var expressionNames = templateValue.match(expressionName); |
| if (expressionNames) { |
| expressionNames.forEach(function(name) { |
| if (!name.match(validExpressionName)) { |
| isTemplateInvalid = true; |
| } |
| }); |
| } |
| } |
| this.set('isTemplateInvalid', isTemplateInvalid); |
| |
| var expression = templateValue.replace(/\{\{Expression[\d]\}\}/g, function (exp) { |
| var result; |
| if (expressions.someProperty('alias', exp)) { |
| result = self.parseExpression(expressions.findProperty('alias', exp)); |
| metrics.pushObjects(result.metrics); |
| return result.value; |
| } |
| return exp; |
| }); |
| return { |
| metrics: metrics, |
| values: [ |
| { |
| value: expression |
| } |
| ] |
| }; |
| }, |
| |
| /** |
| * |
| * @param {object} expression |
| * @returns {{metrics: Array, value: string}} |
| */ |
| parseExpression: function (expression) { |
| var value = ''; |
| var metrics = []; |
| |
| if (expression.data.length > 0) { |
| value = '${'; |
| expression.data.forEach(function (element) { |
| if (element.isMetric) { |
| var metricObj = { |
| "name": element.name, |
| "service_name": element.serviceName, |
| "component_name": element.componentName, |
| "metric_path": element.metricPath |
| }; |
| if (element.hostComponentCriteria) { |
| metricObj.host_component_criteria = element.hostComponentCriteria; |
| } |
| metrics.push(metricObj); |
| |
| } |
| value += element.name; |
| }, this); |
| value += '}'; |
| } |
| |
| return { |
| metrics: metrics, |
| value: value |
| }; |
| }, |
| |
| /** |
| * update properties of preview widget |
| */ |
| updateProperties: function () { |
| var result = {}; |
| |
| this.get('widgetPropertiesViews').forEach(function (property) { |
| for (var key in property.valueMap) { |
| result[property.valueMap[key]] = property.get(key); |
| } |
| }); |
| this.set('widgetProperties', result); |
| }.observes('widgetPropertiesViews.@each.value', 'widgetPropertiesViews.@each.bigValue', 'widgetPropertiesViews.@each.smallValue'), |
| |
| /* |
| * Generate the thresholds, unit, time range.etc object based on the widget type selected in previous step. |
| */ |
| renderProperties: function () { |
| var widgetProperties = App.WidgetType.find(this.get('content.widgetType')).get('properties'); |
| var propertiesData = this.get('widgetProperties'); |
| var result = []; |
| |
| widgetProperties.forEach(function (property) { |
| var definition = App.WidgetPropertyTypes.findProperty('name', property.name); |
| property = $.extend({}, property); |
| |
| //restore previous values |
| for (var key in definition.valueMap) { |
| if (propertiesData[definition.valueMap[key]]) { |
| property[key] = propertiesData[definition.valueMap[key]]; |
| } |
| } |
| result.pushObject(App.WidgetProperty.create($.extend(definition, property))); |
| }); |
| |
| this.set('widgetPropertiesViews', result); |
| }, |
| |
| /** |
| * convert data with model format to editable format |
| * Note: in order to edit widget expression it should be converted to editable format |
| */ |
| convertData: function() { |
| var self = this; |
| var expressionId = 0; |
| var widgetValues = this.get('content.widgetValues'); |
| var widgetMetrics = this.get('content.widgetMetrics'); |
| |
| this.get('expressions').clear(); |
| this.get('dataSets').clear(); |
| |
| if (widgetValues.length > 0) { |
| switch (this.get('content.widgetType')) { |
| case 'NUMBER': |
| case 'GAUGE': |
| var id = this.addExpression(null, true); |
| this.get('expressions').findProperty('id', id).set('data', this.parseValue(widgetValues[0].value, widgetMetrics)[0]); |
| break; |
| case 'TEMPLATE': |
| this.parseValue(widgetValues[0].value, widgetMetrics).forEach(function (item, index) { |
| var id = this.addExpression(null, (index === 0)); |
| this.get('expressions').findProperty('id', id).set('data', item); |
| }, this); |
| this.set('templateValue', widgetValues[0].value.replace(this.get('EXPRESSION_REGEX'), function () { |
| return '{{' + self.get('EXPRESSION_PREFIX') + ++expressionId + '}}'; |
| })); |
| break; |
| case 'GRAPH': |
| widgetValues.forEach(function (value, index) { |
| var id = this.addDataSet(null, (index === 0)); |
| var dataSet = this.get('dataSets').findProperty('id', id); |
| dataSet.set('label', value.name); |
| dataSet.set('expression.data', this.parseValue(value.value, widgetMetrics)[0]); |
| }, this); |
| break; |
| } |
| } |
| }, |
| |
| /** |
| * parse value |
| * @param value |
| * @param metrics |
| * @returns {Array} |
| */ |
| parseValue: function(value, metrics) { |
| var pattern = this.get('EXPRESSION_REGEX'), |
| expressions = [], |
| match; |
| |
| while (match = pattern.exec(value)) { |
| expressions.push(this.getExpressionData(match[1], metrics)); |
| } |
| |
| return expressions; |
| }, |
| |
| /** |
| * format values into expression data objects |
| * @param {string} expression |
| * @param {Array} metrics |
| * @returns {Array} |
| */ |
| getExpressionData: function(expression, metrics) { |
| var str = ''; |
| var data = []; |
| var id = 0; |
| |
| for (var i = 0, l = expression.length; i < l; i++) { |
| if (this.get('OPERATORS').contains(expression[i])) { |
| if (str.trim().length > 0) { |
| data.pushObject(this.getExpressionVariable(str.trim(), ++id, metrics)); |
| str = ''; |
| } |
| data.pushObject(Em.Object.create({ |
| id: ++id, |
| name: expression[i], |
| isOperator: true |
| })); |
| } else { |
| str += expression[i]; |
| } |
| } |
| if (str.trim().length > 0) { |
| data.pushObject(this.getExpressionVariable(str.trim(), ++id, metrics)); |
| } |
| return data; |
| }, |
| |
| /** |
| * get variable of expression |
| * could be name of metric "m1" or constant number "1" |
| * @param {string} name |
| * @param {Array} metrics |
| * @param {number} id |
| * @returns {Em.Object} |
| */ |
| getExpressionVariable: function (name, id, metrics) { |
| var metric; |
| |
| if (isNaN(Number(name))) { |
| metric = metrics.findProperty('name', name); |
| return Em.Object.create({ |
| id: id, |
| name: metric.name, |
| isMetric: true, |
| componentName: metric.component_name, |
| serviceName: metric.service_name, |
| metricPath: metric.metric_path, |
| hostComponentCriteria: metric.host_component_criteria |
| }); |
| } else { |
| return Em.Object.create({ |
| id: id, |
| name: name, |
| isNumber: true |
| }); |
| } |
| }, |
| |
| next: function () { |
| App.router.send('next'); |
| } |
| }); |
| |