step2_controller.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  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.get('expressions')[0] &&
  96. (this.get('expressions')[0].get('editMode') ||
  97. this.get('expressions')[0].get('data.length') === 0);
  98. case "GRAPH":
  99. return this.get('dataSets.length') > 0 &&
  100. (this.get('dataSets').someProperty('expression.editMode') ||
  101. this.get('dataSets').someProperty('expression.data.length', 0));
  102. case "TEMPLATE":
  103. return !this.get('templateValue') ||
  104. this.get('expressions.length') > 0 &&
  105. (this.get('expressions').someProperty('editMode') ||
  106. this.get('expressions').someProperty('data.length', 0));
  107. }
  108. return false;
  109. }.property('widgetPropertiesViews.@each.isValid',
  110. 'expressions.@each.editMode',
  111. 'dataSets.@each.expression'),
  112. /**
  113. * Add data set
  114. * @param {object|null} event
  115. * @param {boolean} isDefault
  116. * @returns {number} id
  117. */
  118. addDataSet: function(event, isDefault) {
  119. var id = (isDefault) ? 1 :(Math.max.apply(this, this.get('dataSets').mapProperty('id')) + 1);
  120. this.get('dataSets').pushObject(Em.Object.create({
  121. id: id,
  122. label: '',
  123. isRemovable: !isDefault,
  124. expression: {
  125. data: [],
  126. editMode: false
  127. }
  128. }));
  129. return id;
  130. },
  131. /**
  132. * Remove data set
  133. * @param {object} event
  134. */
  135. removeDataSet: function(event) {
  136. this.get('dataSets').removeObject(event.context);
  137. },
  138. /**
  139. * Add expression
  140. * @param {object|null} event
  141. * @param {boolean} isDefault
  142. * @returns {number} id
  143. */
  144. addExpression: function(event, isDefault) {
  145. var id = (isDefault) ? 1 :(Math.max.apply(this, this.get('expressions').mapProperty('id')) + 1);
  146. this.get('expressions').pushObject(Em.Object.create({
  147. id: id,
  148. isRemovable: !isDefault,
  149. data: [],
  150. alias: '{{' + this.get('EXPRESSION_PREFIX') + id + '}}',
  151. editMode: false
  152. }));
  153. return id;
  154. },
  155. /**
  156. * Remove expression
  157. * @param {object} event
  158. */
  159. removeExpression: function(event) {
  160. this.get('expressions').removeObject(event.context);
  161. },
  162. /**
  163. * initialize data
  164. * widget should have at least one expression or dataSet
  165. */
  166. initWidgetData: function() {
  167. this.set('widgetProperties', this.get('content.widgetProperties'));
  168. this.set('widgetValues', this.get('content.widgetValues'));
  169. this.set('widgetMetrics', this.get('content.widgetMetrics'));
  170. this.set('expressions', this.get('content.expressions').map(function (item) {
  171. return Em.Object.create(item);
  172. }, this));
  173. this.set('dataSets', this.get('content.dataSets').map(function (item) {
  174. return Em.Object.create(item);
  175. }, this));
  176. this.set('templateValue', this.get('content.templateValue'));
  177. if (this.get('expressions.length') === 0) {
  178. this.addExpression(null, true);
  179. }
  180. if (this.get('dataSets.length') === 0) {
  181. this.addDataSet(null, true);
  182. }
  183. },
  184. /**
  185. * update preview widget with latest expression data
  186. * Note: in order to draw widget it should be converted to API format of widget
  187. */
  188. updateExpressions: function () {
  189. var widgetType = this.get('content.widgetType');
  190. var expressionData = {
  191. values: [],
  192. metrics: []
  193. };
  194. if (this.get('expressions').length > 0 && this.get('dataSets').length > 0) {
  195. switch (widgetType) {
  196. case 'GAUGE':
  197. case 'NUMBER':
  198. expressionData = this.parseExpression(this.get('expressions')[0]);
  199. expressionData.values = [
  200. {
  201. value: expressionData.value
  202. }
  203. ];
  204. break;
  205. case 'TEMPLATE':
  206. expressionData = this.parseTemplateExpression(this);
  207. break;
  208. case 'GRAPH':
  209. expressionData = this.parseGraphDataset(this);
  210. break;
  211. }
  212. }
  213. this.set('widgetValues', expressionData.values);
  214. this.set('widgetMetrics', expressionData.metrics);
  215. }.observes('templateValue', 'dataSets.@each.label'),
  216. /**
  217. * parse Graph data set
  218. * @param {Ember.View} view
  219. * @returns {{metrics: Array, values: Array}}
  220. */
  221. parseGraphDataset: function (view) {
  222. var metrics = [];
  223. var values = [];
  224. view.get('dataSets').forEach(function (dataSet) {
  225. var result = this.parseExpression(dataSet.get('expression'));
  226. metrics.pushObjects(result.metrics);
  227. values.push({
  228. name: dataSet.get('label'),
  229. value: result.value
  230. });
  231. }, this);
  232. return {
  233. metrics: metrics,
  234. values: values
  235. };
  236. },
  237. /**
  238. * parse expression from template
  239. * @param {Ember.View} view
  240. * @returns {{metrics: Array, values: {value: *}[]}}
  241. */
  242. parseTemplateExpression: function (view) {
  243. var metrics = [];
  244. var self = this;
  245. var expression = view.get('templateValue').replace(/\{\{Expression[\d]\}\}/g, function (exp) {
  246. var result;
  247. if (view.get('expressions').someProperty('alias', exp)) {
  248. result = self.parseExpression(view.get('expressions').findProperty('alias', exp));
  249. metrics.pushObjects(result.metrics);
  250. return result.value;
  251. }
  252. return exp;
  253. });
  254. return {
  255. metrics: metrics,
  256. values: [
  257. {
  258. value: expression
  259. }
  260. ]
  261. };
  262. },
  263. /**
  264. *
  265. * @param {object} expression
  266. * @returns {{metrics: Array, value: string}}
  267. */
  268. parseExpression: function (expression) {
  269. var value = '';
  270. var metrics = [];
  271. if (expression.data.length > 0) {
  272. value = '${';
  273. expression.data.forEach(function (element) {
  274. if (element.isMetric) {
  275. metrics.push(element);
  276. }
  277. value += element.name;
  278. }, this);
  279. value += '}';
  280. }
  281. return {
  282. metrics: metrics,
  283. value: value
  284. };
  285. },
  286. /**
  287. * update properties of preview widget
  288. */
  289. updateProperties: function () {
  290. var result = {};
  291. this.get('widgetPropertiesViews').forEach(function(property){
  292. for (var key in property.valueMap) {
  293. result[property.valueMap[key]] = property.get(key);
  294. }
  295. });
  296. this.set('widgetProperties', result);
  297. }.observes('widgetPropertiesViews.@each.value', 'widgetPropertiesViews.@each.bigValue', 'widgetPropertiesViews.@each.smallValue'),
  298. /*
  299. * Generate the thresholds, unit, time range.etc object based on the widget type selected in previous step.
  300. */
  301. renderProperties: function () {
  302. var widgetProperties = App.WidgetType.find(this.get('content.widgetType')).get('properties');
  303. var propertiesData = this.get('widgetProperties');
  304. var result = [];
  305. widgetProperties.forEach(function (property) {
  306. var definition = App.WidgetPropertyTypes.findProperty('name', property.name);
  307. property = $.extend({}, property);
  308. //restore previous values
  309. for (var key in definition.valueMap) {
  310. if (propertiesData[definition.valueMap[key]]) {
  311. property[key] = propertiesData[definition.valueMap[key]];
  312. }
  313. }
  314. result.pushObject(App.WidgetProperty.create($.extend(definition, property)));
  315. });
  316. this.set('widgetPropertiesViews', result);
  317. },
  318. /**
  319. * convert data with model format to editable format
  320. * Note: in order to edit widget expression it should be converted to editable format
  321. * @param {App.Widget} content
  322. * @param {Ember.Controller} widgetController
  323. */
  324. convertData: function(content, widgetController) {
  325. var self = this;
  326. var expressionId = 0;
  327. this.get('expressions').clear();
  328. this.get('dataSets').clear();
  329. switch (content.get('widgetType')) {
  330. case 'NUMBER':
  331. case 'GAUGE':
  332. var id = this.addExpression(null, true);
  333. this.get('expressions').findProperty('id', id).set('data', this.parseValue(content.get('values')[0].value, content.get('metrics'))[0]);
  334. break;
  335. case 'TEMPLATE':
  336. this.parseValue(content.get('values')[0].value, content.get('metrics')).forEach(function(item, index) {
  337. var id = this.addExpression(null, (index === 0));
  338. this.get('expressions').findProperty('id', id).set('data', item);
  339. }, this);
  340. this.set('templateValue', content.get('values')[0].value.replace(this.get('EXPRESSION_REGEX'), function(){
  341. return '{{' + self.get('EXPRESSION_PREFIX') + ++expressionId + '}}';
  342. }));
  343. break;
  344. case 'GRAPH':
  345. content.get('values').forEach(function (value, index) {
  346. var id = this.addDataSet(null, (index === 0));
  347. var dataSet = this.get('dataSets').findProperty('id', id);
  348. dataSet.set('label', value.name);
  349. dataSet.set('expression.data', this.parseValue(value.value, content.get('metrics'))[0]);
  350. }, this);
  351. break;
  352. }
  353. widgetController.save('templateValue', this.get('templateValue'));
  354. widgetController.save('expressions', this.get('expressions'));
  355. widgetController.save('dataSets', this.get('dataSets'));
  356. },
  357. /**
  358. * parse value
  359. * @param value
  360. * @param metrics
  361. * @returns {Array}
  362. */
  363. parseValue: function(value, metrics) {
  364. var pattern = this.get('EXPRESSION_REGEX'),
  365. expressions = [],
  366. match;
  367. while (match = pattern.exec(value)) {
  368. expressions.push(this.getExpressionData(match[1], metrics));
  369. }
  370. return expressions;
  371. },
  372. /**
  373. * format values into expression data objects
  374. * @param {string} expression
  375. * @param {Array} metrics
  376. * @returns {Array}
  377. */
  378. getExpressionData: function(expression, metrics) {
  379. var str = '';
  380. var data = [];
  381. var id = 0;
  382. var metric;
  383. for (var i = 0, l = expression.length; i < l; i++) {
  384. if (this.get('OPERATORS').contains(expression[i])) {
  385. if (str.trim().length > 0) {
  386. metric = metrics.findProperty('name', str.trim());
  387. data.pushObject(Em.Object.create({
  388. id: ++id,
  389. name: str.trim(),
  390. isMetric: true,
  391. componentName: metric.component_name,
  392. serviceName: metric.service_name,
  393. metricPath: metric.metric_path
  394. }));
  395. str = '';
  396. }
  397. data.pushObject(Em.Object.create({
  398. id: ++id,
  399. name: expression[i],
  400. isOperator: true
  401. }));
  402. } else {
  403. str += expression[i];
  404. }
  405. }
  406. if (str.trim().length > 0) {
  407. metric = metrics.findProperty('name', str.trim());
  408. data.pushObject(Em.Object.create({
  409. id: ++id,
  410. name: str.trim(),
  411. isMetric: true,
  412. componentName: metric.component_name,
  413. serviceName: metric.service_name,
  414. metricPath: metric.metric_path
  415. }));
  416. }
  417. return data;
  418. },
  419. next: function () {
  420. if (!this.get('isSubmitDisabled')) {
  421. App.router.send('next');
  422. }
  423. }
  424. });