step2_controller.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. var App = require('app');
  19. App.WidgetWizardStep2Controller = Em.Controller.extend({
  20. name: "widgetWizardStep2Controller",
  21. EXPRESSION_PREFIX: 'Expression',
  22. /**
  23. * @type {RegExp}
  24. * @const
  25. */
  26. EXPRESSION_REGEX: /\$\{([\w\s\.\,\+\-\*\/\(\)\:\=\[\]]*)\}/g,
  27. /**
  28. * list of operators that can be used in expression
  29. * @type {Array}
  30. * @constant
  31. */
  32. OPERATORS: ["+", "-", "*", "/", "(", ")"],
  33. /**
  34. * actual values of properties in API format
  35. * @type {object}
  36. */
  37. widgetProperties: {},
  38. /**
  39. * @type {Array}
  40. */
  41. widgetValues: [],
  42. /**
  43. * @type {Array}
  44. */
  45. widgetMetrics: [],
  46. /**
  47. * @type {Array}
  48. */
  49. expressions: [],
  50. /**
  51. * used only for GRAPH widget
  52. * @type {Array}
  53. */
  54. dataSets: [],
  55. /**
  56. * content of template of Template widget
  57. * @type {string}
  58. */
  59. templateValue: '',
  60. /**
  61. * views of properties
  62. * @type {Array}
  63. */
  64. widgetPropertiesViews: [],
  65. /**
  66. * @type {boolean}
  67. */
  68. isEditWidget: function () {
  69. return this.get('content.controllerName') === 'widgetEditController';
  70. }.property('content.controllerName'),
  71. /**
  72. * metrics filtered by type
  73. * @type {Array}
  74. */
  75. filteredMetrics: function () {
  76. var type = this.get('content.widgetType');
  77. return this.get('content.allMetrics').filter(function (metric) {
  78. if (type === 'GRAPH') {
  79. return metric.temporal;
  80. } else {
  81. return metric.point_in_time;
  82. }
  83. }, this);
  84. }.property('content.allMetrics'),
  85. /**
  86. * @type {boolean}
  87. */
  88. isSubmitDisabled: function() {
  89. if (this.get('widgetPropertiesViews').someProperty('isValid', false)) {
  90. return true;
  91. }
  92. switch (this.get('content.widgetType')) {
  93. case "NUMBER":
  94. case "GAUGE":
  95. return !this.isExpressionComplete(this.get('expressions')[0]);
  96. case "GRAPH":
  97. return !this.isGraphDataComplete(this.get('dataSets'));
  98. case "TEMPLATE":
  99. return !this.isTemplateDataComplete(this.get('expressions'), this.get('templateValue'));
  100. }
  101. return false;
  102. }.property(
  103. 'widgetPropertiesViews.@each.isValid',
  104. 'dataSets.@each.label',
  105. 'templateValue'
  106. ),
  107. /**
  108. * check whether data of expression is complete
  109. * @param {Em.Object} expression
  110. * @returns {boolean}
  111. */
  112. isExpressionComplete: function (expression) {
  113. return Boolean(expression && !expression.get('isInvalid') && !expression.get('isEmpty'));
  114. },
  115. /**
  116. * check whether data of graph widget is complete
  117. * @param dataSets
  118. * @returns {boolean} isComplete
  119. */
  120. isGraphDataComplete: function (dataSets) {
  121. var isComplete = Boolean(dataSets.length);
  122. for (var i = 0; i < dataSets.length; i++) {
  123. if (dataSets[i].get('label').trim() === '' || !this.isExpressionComplete(dataSets[i].get('expression'))) {
  124. isComplete = false;
  125. break;
  126. }
  127. }
  128. return isComplete;
  129. },
  130. /**
  131. * check whether data of template widget is complete
  132. * @param {Array} expressions
  133. * @param {string} templateValue
  134. * @returns {boolean} isComplete
  135. */
  136. isTemplateDataComplete: function (expressions, templateValue) {
  137. var isComplete = Boolean(expressions.length > 0 && templateValue.trim() !== '');
  138. if (isComplete) {
  139. for (var i = 0; i < expressions.length; i++) {
  140. if (!this.isExpressionComplete(expressions[i])) {
  141. isComplete = false;
  142. break;
  143. }
  144. }
  145. }
  146. return isComplete;
  147. },
  148. /**
  149. * Add data set
  150. * @param {object|null} event
  151. * @param {boolean} isDefault
  152. * @returns {number} id
  153. */
  154. addDataSet: function(event, isDefault) {
  155. var id = (isDefault) ? 1 :(Math.max.apply(this, this.get('dataSets').mapProperty('id')) + 1);
  156. this.get('dataSets').pushObject(Em.Object.create({
  157. id: id,
  158. label: '',
  159. isRemovable: !isDefault,
  160. expression: Em.Object.create({
  161. data: [],
  162. isInvalid: false,
  163. isEmpty: function () {
  164. return (this.get('data.length') === 0);
  165. }.property('data.length')
  166. })
  167. }));
  168. return id;
  169. },
  170. /**
  171. * Remove data set
  172. * @param {object} event
  173. */
  174. removeDataSet: function(event) {
  175. this.get('dataSets').removeObject(event.context);
  176. },
  177. /**
  178. * Add expression
  179. * @param {object|null} event
  180. * @param {boolean} isDefault
  181. * @returns {number} id
  182. */
  183. addExpression: function(event, isDefault) {
  184. var id = (isDefault) ? 1 :(Math.max.apply(this, this.get('expressions').mapProperty('id')) + 1);
  185. this.get('expressions').pushObject(Em.Object.create({
  186. id: id,
  187. isRemovable: !isDefault,
  188. data: [],
  189. alias: '{{' + this.get('EXPRESSION_PREFIX') + id + '}}',
  190. isInvalid: false,
  191. isEmpty: function () {
  192. return (this.get('data.length') === 0);
  193. }.property('data.length')
  194. }));
  195. return id;
  196. },
  197. /**
  198. * Remove expression
  199. * @param {object} event
  200. */
  201. removeExpression: function(event) {
  202. this.get('expressions').removeObject(event.context);
  203. },
  204. /**
  205. * initialize data
  206. * widget should have at least one expression or dataSet
  207. */
  208. initWidgetData: function() {
  209. this.set('widgetProperties', this.get('content.widgetProperties'));
  210. this.set('widgetValues', this.get('content.widgetValues'));
  211. this.set('widgetMetrics', this.get('content.widgetMetrics'));
  212. if (this.get('expressions.length') === 0) {
  213. this.addExpression(null, true);
  214. }
  215. if (this.get('dataSets.length') === 0) {
  216. this.addDataSet(null, true);
  217. }
  218. },
  219. /**
  220. * update preview widget with latest expression data
  221. * Note: in order to draw widget it should be converted to API format of widget
  222. */
  223. updateExpressions: function () {
  224. var widgetType = this.get('content.widgetType');
  225. var expressionData = {
  226. values: [],
  227. metrics: []
  228. };
  229. if (this.get('expressions').length > 0 && this.get('dataSets').length > 0) {
  230. switch (widgetType) {
  231. case 'GAUGE':
  232. case 'NUMBER':
  233. expressionData = this.parseExpression(this.get('expressions')[0]);
  234. expressionData.values = [
  235. {
  236. name: "",
  237. value: expressionData.value
  238. }
  239. ];
  240. break;
  241. case 'TEMPLATE':
  242. expressionData = this.parseTemplateExpression(this);
  243. break;
  244. case 'GRAPH':
  245. expressionData = this.parseGraphDataset(this);
  246. break;
  247. }
  248. }
  249. this.set('widgetValues', expressionData.values);
  250. this.set('widgetMetrics', expressionData.metrics);
  251. }.observes('templateValue', 'dataSets.@each.label'),
  252. /**
  253. * parse Graph data set
  254. * @param {Ember.View} view
  255. * @returns {{metrics: Array, values: Array}}
  256. */
  257. parseGraphDataset: function (view) {
  258. var metrics = [];
  259. var values = [];
  260. view.get('dataSets').forEach(function (dataSet) {
  261. var result = this.parseExpression(dataSet.get('expression'));
  262. metrics.pushObjects(result.metrics);
  263. values.push({
  264. name: dataSet.get('label'),
  265. value: result.value
  266. });
  267. }, this);
  268. return {
  269. metrics: metrics,
  270. values: values
  271. };
  272. },
  273. /**
  274. * parse expression from template
  275. * @param {Ember.View} view
  276. * @returns {{metrics: Array, values: {value: *}[]}}
  277. */
  278. parseTemplateExpression: function (view) {
  279. var metrics = [];
  280. var self = this;
  281. var expression = view.get('templateValue').replace(/\{\{Expression[\d]\}\}/g, function (exp) {
  282. var result;
  283. if (view.get('expressions').someProperty('alias', exp)) {
  284. result = self.parseExpression(view.get('expressions').findProperty('alias', exp));
  285. metrics.pushObjects(result.metrics);
  286. return result.value;
  287. }
  288. return exp;
  289. });
  290. return {
  291. metrics: metrics,
  292. values: [
  293. {
  294. value: expression
  295. }
  296. ]
  297. };
  298. },
  299. /**
  300. *
  301. * @param {object} expression
  302. * @returns {{metrics: Array, value: string}}
  303. */
  304. parseExpression: function (expression) {
  305. var value = '';
  306. var metrics = [];
  307. if (expression.data.length > 0) {
  308. value = '${';
  309. expression.data.forEach(function (element) {
  310. if (element.isMetric) {
  311. metrics.push(element);
  312. }
  313. value += element.name;
  314. }, this);
  315. value += '}';
  316. }
  317. return {
  318. metrics: metrics,
  319. value: value
  320. };
  321. },
  322. /**
  323. * update properties of preview widget
  324. */
  325. updateProperties: function () {
  326. var result = {};
  327. this.get('widgetPropertiesViews').forEach(function(property){
  328. for (var key in property.valueMap) {
  329. result[property.valueMap[key]] = property.get(key);
  330. }
  331. });
  332. this.set('widgetProperties', result);
  333. }.observes('widgetPropertiesViews.@each.value', 'widgetPropertiesViews.@each.bigValue', 'widgetPropertiesViews.@each.smallValue'),
  334. /*
  335. * Generate the thresholds, unit, time range.etc object based on the widget type selected in previous step.
  336. */
  337. renderProperties: function () {
  338. var widgetProperties = App.WidgetType.find(this.get('content.widgetType')).get('properties');
  339. var propertiesData = this.get('widgetProperties');
  340. var result = [];
  341. widgetProperties.forEach(function (property) {
  342. var definition = App.WidgetPropertyTypes.findProperty('name', property.name);
  343. property = $.extend({}, property);
  344. //restore previous values
  345. for (var key in definition.valueMap) {
  346. if (propertiesData[definition.valueMap[key]]) {
  347. property[key] = propertiesData[definition.valueMap[key]];
  348. }
  349. }
  350. result.pushObject(App.WidgetProperty.create($.extend(definition, property)));
  351. });
  352. this.set('widgetPropertiesViews', result);
  353. },
  354. /**
  355. * convert data with model format to editable format
  356. * Note: in order to edit widget expression it should be converted to editable format
  357. */
  358. convertData: function() {
  359. var self = this;
  360. var expressionId = 0;
  361. var widgetValues = this.get('content.widgetValues');
  362. var widgetMetrics = this.get('content.widgetMetrics');
  363. this.get('expressions').clear();
  364. this.get('dataSets').clear();
  365. if (widgetValues.length > 0) {
  366. switch (this.get('content.widgetType')) {
  367. case 'NUMBER':
  368. case 'GAUGE':
  369. var id = this.addExpression(null, true);
  370. this.get('expressions').findProperty('id', id).set('data', this.parseValue(widgetValues[0].value, widgetMetrics)[0]);
  371. break;
  372. case 'TEMPLATE':
  373. this.parseValue(widgetValues[0].value, widgetMetrics).forEach(function (item, index) {
  374. var id = this.addExpression(null, (index === 0));
  375. this.get('expressions').findProperty('id', id).set('data', item);
  376. }, this);
  377. this.set('templateValue', widgetValues[0].value.replace(this.get('EXPRESSION_REGEX'), function () {
  378. return '{{' + self.get('EXPRESSION_PREFIX') + ++expressionId + '}}';
  379. }));
  380. break;
  381. case 'GRAPH':
  382. widgetValues.forEach(function (value, index) {
  383. var id = this.addDataSet(null, (index === 0));
  384. var dataSet = this.get('dataSets').findProperty('id', id);
  385. dataSet.set('label', value.name);
  386. dataSet.set('expression.data', this.parseValue(value.value, widgetMetrics)[0]);
  387. }, this);
  388. break;
  389. }
  390. }
  391. },
  392. /**
  393. * parse value
  394. * @param value
  395. * @param metrics
  396. * @returns {Array}
  397. */
  398. parseValue: function(value, metrics) {
  399. var pattern = this.get('EXPRESSION_REGEX'),
  400. expressions = [],
  401. match;
  402. while (match = pattern.exec(value)) {
  403. expressions.push(this.getExpressionData(match[1], metrics));
  404. }
  405. return expressions;
  406. },
  407. /**
  408. * format values into expression data objects
  409. * @param {string} expression
  410. * @param {Array} metrics
  411. * @returns {Array}
  412. */
  413. getExpressionData: function(expression, metrics) {
  414. var str = '';
  415. var data = [];
  416. var id = 0;
  417. var metric;
  418. for (var i = 0, l = expression.length; i < l; i++) {
  419. if (this.get('OPERATORS').contains(expression[i])) {
  420. if (str.trim().length > 0) {
  421. metric = metrics.findProperty('name', str.trim());
  422. data.pushObject(Em.Object.create({
  423. id: ++id,
  424. name: str.trim(),
  425. isMetric: true,
  426. componentName: metric.component_name,
  427. serviceName: metric.service_name,
  428. metricPath: metric.metric_path,
  429. hostComponentCriteria: metric.host_component_criteria
  430. }));
  431. str = '';
  432. }
  433. data.pushObject(Em.Object.create({
  434. id: ++id,
  435. name: expression[i],
  436. isOperator: true
  437. }));
  438. } else {
  439. str += expression[i];
  440. }
  441. }
  442. if (str.trim().length > 0) {
  443. metric = metrics.findProperty('name', str.trim());
  444. data.pushObject(Em.Object.create({
  445. id: ++id,
  446. name: str.trim(),
  447. isMetric: true,
  448. componentName: metric.component_name,
  449. serviceName: metric.service_name,
  450. metricPath: metric.metric_path,
  451. hostComponentCriteria: metric.host_component_criteria
  452. }));
  453. }
  454. return data;
  455. },
  456. next: function () {
  457. if (!this.get('isSubmitDisabled')) {
  458. App.router.send('next');
  459. }
  460. }
  461. });