step2_controller.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  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. this.set('expressions', this.get('content.expressions').map(function (item) {
  213. return Em.Object.create(item);
  214. }, this));
  215. this.set('dataSets', this.get('content.dataSets').map(function (item) {
  216. item.expression = Em.Object.create(item.expression);
  217. return Em.Object.create(item);
  218. }, this));
  219. this.set('templateValue', this.get('content.templateValue'));
  220. if (this.get('expressions.length') === 0) {
  221. this.addExpression(null, true);
  222. }
  223. if (this.get('dataSets.length') === 0) {
  224. this.addDataSet(null, true);
  225. }
  226. },
  227. /**
  228. * update preview widget with latest expression data
  229. * Note: in order to draw widget it should be converted to API format of widget
  230. */
  231. updateExpressions: function () {
  232. var widgetType = this.get('content.widgetType');
  233. var expressionData = {
  234. values: [],
  235. metrics: []
  236. };
  237. if (this.get('expressions').length > 0 && this.get('dataSets').length > 0) {
  238. switch (widgetType) {
  239. case 'GAUGE':
  240. case 'NUMBER':
  241. expressionData = this.parseExpression(this.get('expressions')[0]);
  242. expressionData.values = [
  243. {
  244. name: "",
  245. value: expressionData.value
  246. }
  247. ];
  248. break;
  249. case 'TEMPLATE':
  250. expressionData = this.parseTemplateExpression(this);
  251. break;
  252. case 'GRAPH':
  253. expressionData = this.parseGraphDataset(this);
  254. break;
  255. }
  256. }
  257. this.set('widgetValues', expressionData.values);
  258. this.set('widgetMetrics', expressionData.metrics);
  259. }.observes('templateValue', 'dataSets.@each.label'),
  260. /**
  261. * parse Graph data set
  262. * @param {Ember.View} view
  263. * @returns {{metrics: Array, values: Array}}
  264. */
  265. parseGraphDataset: function (view) {
  266. var metrics = [];
  267. var values = [];
  268. view.get('dataSets').forEach(function (dataSet) {
  269. var result = this.parseExpression(dataSet.get('expression'));
  270. metrics.pushObjects(result.metrics);
  271. values.push({
  272. name: dataSet.get('label'),
  273. value: result.value
  274. });
  275. }, this);
  276. return {
  277. metrics: metrics,
  278. values: values
  279. };
  280. },
  281. /**
  282. * parse expression from template
  283. * @param {Ember.View} view
  284. * @returns {{metrics: Array, values: {value: *}[]}}
  285. */
  286. parseTemplateExpression: function (view) {
  287. var metrics = [];
  288. var self = this;
  289. var expression = view.get('templateValue').replace(/\{\{Expression[\d]\}\}/g, function (exp) {
  290. var result;
  291. if (view.get('expressions').someProperty('alias', exp)) {
  292. result = self.parseExpression(view.get('expressions').findProperty('alias', exp));
  293. metrics.pushObjects(result.metrics);
  294. return result.value;
  295. }
  296. return exp;
  297. });
  298. return {
  299. metrics: metrics,
  300. values: [
  301. {
  302. value: expression
  303. }
  304. ]
  305. };
  306. },
  307. /**
  308. *
  309. * @param {object} expression
  310. * @returns {{metrics: Array, value: string}}
  311. */
  312. parseExpression: function (expression) {
  313. var value = '';
  314. var metrics = [];
  315. if (expression.data.length > 0) {
  316. value = '${';
  317. expression.data.forEach(function (element) {
  318. if (element.isMetric) {
  319. metrics.push(element);
  320. }
  321. value += element.name;
  322. }, this);
  323. value += '}';
  324. }
  325. return {
  326. metrics: metrics,
  327. value: value
  328. };
  329. },
  330. /**
  331. * update properties of preview widget
  332. */
  333. updateProperties: function () {
  334. var result = {};
  335. this.get('widgetPropertiesViews').forEach(function(property){
  336. for (var key in property.valueMap) {
  337. result[property.valueMap[key]] = property.get(key);
  338. }
  339. });
  340. this.set('widgetProperties', result);
  341. }.observes('widgetPropertiesViews.@each.value', 'widgetPropertiesViews.@each.bigValue', 'widgetPropertiesViews.@each.smallValue'),
  342. /*
  343. * Generate the thresholds, unit, time range.etc object based on the widget type selected in previous step.
  344. */
  345. renderProperties: function () {
  346. var widgetProperties = App.WidgetType.find(this.get('content.widgetType')).get('properties');
  347. var propertiesData = this.get('widgetProperties');
  348. var result = [];
  349. widgetProperties.forEach(function (property) {
  350. var definition = App.WidgetPropertyTypes.findProperty('name', property.name);
  351. property = $.extend({}, property);
  352. //restore previous values
  353. for (var key in definition.valueMap) {
  354. if (propertiesData[definition.valueMap[key]]) {
  355. property[key] = propertiesData[definition.valueMap[key]];
  356. }
  357. }
  358. result.pushObject(App.WidgetProperty.create($.extend(definition, property)));
  359. });
  360. this.set('widgetPropertiesViews', result);
  361. },
  362. /**
  363. * convert data with model format to editable format
  364. * Note: in order to edit widget expression it should be converted to editable format
  365. * @param {App.Widget} content
  366. * @param {Ember.Controller} widgetController
  367. */
  368. convertData: function(content, widgetController) {
  369. var self = this;
  370. var expressionId = 0;
  371. this.get('expressions').clear();
  372. this.get('dataSets').clear();
  373. switch (content.get('widgetType')) {
  374. case 'NUMBER':
  375. case 'GAUGE':
  376. var id = this.addExpression(null, true);
  377. this.get('expressions').findProperty('id', id).set('data', this.parseValue(content.get('values')[0].value, content.get('metrics'))[0]);
  378. break;
  379. case 'TEMPLATE':
  380. this.parseValue(content.get('values')[0].value, content.get('metrics')).forEach(function(item, index) {
  381. var id = this.addExpression(null, (index === 0));
  382. this.get('expressions').findProperty('id', id).set('data', item);
  383. }, this);
  384. this.set('templateValue', content.get('values')[0].value.replace(this.get('EXPRESSION_REGEX'), function(){
  385. return '{{' + self.get('EXPRESSION_PREFIX') + ++expressionId + '}}';
  386. }));
  387. break;
  388. case 'GRAPH':
  389. content.get('values').forEach(function (value, index) {
  390. var id = this.addDataSet(null, (index === 0));
  391. var dataSet = this.get('dataSets').findProperty('id', id);
  392. dataSet.set('label', value.name);
  393. dataSet.set('expression.data', this.parseValue(value.value, content.get('metrics'))[0]);
  394. }, this);
  395. break;
  396. }
  397. widgetController.save('templateValue', this.get('templateValue'));
  398. widgetController.save('expressions', this.get('expressions'));
  399. widgetController.save('dataSets', this.get('dataSets'));
  400. },
  401. /**
  402. * parse value
  403. * @param value
  404. * @param metrics
  405. * @returns {Array}
  406. */
  407. parseValue: function(value, metrics) {
  408. var pattern = this.get('EXPRESSION_REGEX'),
  409. expressions = [],
  410. match;
  411. while (match = pattern.exec(value)) {
  412. expressions.push(this.getExpressionData(match[1], metrics));
  413. }
  414. return expressions;
  415. },
  416. /**
  417. * format values into expression data objects
  418. * @param {string} expression
  419. * @param {Array} metrics
  420. * @returns {Array}
  421. */
  422. getExpressionData: function(expression, metrics) {
  423. var str = '';
  424. var data = [];
  425. var id = 0;
  426. var metric;
  427. for (var i = 0, l = expression.length; i < l; i++) {
  428. if (this.get('OPERATORS').contains(expression[i])) {
  429. if (str.trim().length > 0) {
  430. metric = metrics.findProperty('name', str.trim());
  431. data.pushObject(Em.Object.create({
  432. id: ++id,
  433. name: str.trim(),
  434. isMetric: true,
  435. componentName: metric.component_name,
  436. serviceName: metric.service_name,
  437. metricPath: metric.metric_path,
  438. hostComponentCriteria: metric.host_component_criteria
  439. }));
  440. str = '';
  441. }
  442. data.pushObject(Em.Object.create({
  443. id: ++id,
  444. name: expression[i],
  445. isOperator: true
  446. }));
  447. } else {
  448. str += expression[i];
  449. }
  450. }
  451. if (str.trim().length > 0) {
  452. metric = metrics.findProperty('name', str.trim());
  453. data.pushObject(Em.Object.create({
  454. id: ++id,
  455. name: str.trim(),
  456. isMetric: true,
  457. componentName: metric.component_name,
  458. serviceName: metric.service_name,
  459. metricPath: metric.metric_path,
  460. hostComponentCriteria: metric.host_component_criteria
  461. }));
  462. }
  463. return data;
  464. },
  465. next: function () {
  466. if (!this.get('isSubmitDisabled')) {
  467. App.router.send('next');
  468. }
  469. }
  470. });