enhanced_configs.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  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. var blueprintUtils = require('utils/blueprint');
  20. App.EnhancedConfigsMixin = Em.Mixin.create({
  21. /**
  22. * this value is used for observing
  23. * whether recommendations for dependent properties was received from server
  24. * @type {number}
  25. */
  26. recommendationTimeStamp: null,
  27. /**
  28. * this property is used to force update min/max values
  29. * for not default config groups
  30. * @type {boolean}
  31. */
  32. forceUpdateBoundaries: false,
  33. /**
  34. * flag is true when Ambari changes some of the dependent properties
  35. * @type {boolean}
  36. */
  37. hasChangedDependencies: function() {
  38. return App.get('isClusterSupportsEnhancedConfigs') && this.get('isControllerSupportsEnhancedConfigs') && this.get('changedProperties.length') > 0;
  39. }.property('changedProperties.length'),
  40. /**
  41. * defines is block with changed dependent configs should be shown
  42. * rely on controller
  43. * @type {boolean}
  44. */
  45. isControllerSupportsEnhancedConfigs: function() {
  46. return ['wizardStep7Controller','mainServiceInfoConfigsController'].contains(this.get('name'));
  47. }.property('name'),
  48. /**
  49. * defines if initialValue of config can be used on current controller
  50. * if not savedValue is used instead
  51. * @param {String} serviceName
  52. * @return {boolean}
  53. * @method useInitialValue
  54. */
  55. useInitialValue: function(serviceName) {
  56. return ['wizardStep7Controller'].contains(this.get('name')) && !App.Service.find().findProperty('serviceName', serviceName);
  57. },
  58. dependenciesGroupMessage: Em.I18n.t('popup.dependent.configs.dependencies.for.groups'),
  59. /**
  60. * message fro alert box for dependent configs
  61. * @type {string}
  62. */
  63. dependenciesMessage: function() {
  64. var changedProperties = this.get('changedProperties').filterProperty('saveRecommended');
  65. var changedServices = changedProperties.mapProperty('serviceName').uniq();
  66. var cfgLenSuffix = changedProperties.length === 1 ? 'singular' : 'plural';
  67. var sLenSuffix = changedServices.length === 1 ? 'singular' : 'plural';
  68. return Em.I18n.t('popup.dependent.configs.dependencies.config.' + cfgLenSuffix).format(changedProperties.length)
  69. + Em.I18n.t('popup.dependent.configs.dependencies.service.' + sLenSuffix).format(changedServices.length);
  70. }.property('changedProperties.length'),
  71. /**
  72. * values for dependent configs
  73. * @type {Object[]}
  74. * ex:
  75. * {
  76. * saveRecommended: {boolean}, //by default is true (checkbox binding)
  77. * saveRecommendedDefault: {boolean}, used for cancel operation to restore previous state
  78. * toDelete: {boolean} [true], // defines if property should be deleted
  79. * toAdd: {boolean} [false], // defines if property should be added
  80. * isDeleted: {boolean} [true], // defines if property was deleted, but was present in initial configs
  81. * fileName: {string}, //file name without '.xml'
  82. * propertyName: {string},
  83. * parentConfigs: {string[]} // name of the parent configs
  84. * configGroup: {string},
  85. * value: {string},
  86. * serviceName: {string},
  87. * allowChangeGroup: {boolean}, //used to disable group link for current service
  88. * serviceDisplayName: {string},
  89. * recommendedValue: {string}
  90. * }
  91. * @private
  92. */
  93. _dependentConfigValues: Em.A([]),
  94. /**
  95. * dependent properties that was changed by Ambari
  96. * @type {Object[]}
  97. */
  98. changedProperties: function() {
  99. return this.get('_dependentConfigValues').filter(function(dp) {
  100. return (this.get('selectedConfigGroup.isDefault') && Em.get(dp, 'configGroup').contains('Default'))
  101. || [this.get('selectedConfigGroup.name'), this.get('selectedConfigGroup.dependentConfigGroups') && this.get('selectedConfigGroup.dependentConfigGroups')[Em.get(dp, 'serviceName')]].contains(Em.get(dp, 'configGroup'));
  102. }, this);
  103. }.property('_dependentConfigValues.@each.saveRecommended', 'selectedConfigGroup'),
  104. /**
  105. * defines if change dependent group message should be shown
  106. * @type {boolean}
  107. */
  108. showSelectGroupsPopup: function() {
  109. return !this.get('selectedConfigGroup.isDefault') && this.get('selectedService.dependentServiceNames.length');
  110. }.property('selectedConfigGroup.isDefault'),
  111. /**
  112. * set default values for dependentGroups
  113. * @method setDependentGroups
  114. */
  115. setDependentGroups: function () {
  116. if (this.get('selectedConfigGroup') && this.get('isControllerSupportsEnhancedConfigs') && !this.get('selectedConfigGroup.isDefault') && this.get('selectedService.dependentServiceNames.length')) {
  117. this.get('selectedService.dependentServiceNames').forEach(function (serviceName) {
  118. if (!this.get('selectedConfigGroup.dependentConfigGroups')[serviceName]) {
  119. var stepConfig = this.get('stepConfigs').findProperty('serviceName', serviceName);
  120. if (stepConfig) {
  121. stepConfig.get('configGroups').filterProperty('isDefault', false).forEach(function (configGroup) {
  122. this.get('selectedService.configGroups').filterProperty('isDefault', false).forEach(function (currentServiceGroup) {
  123. if (currentServiceGroup.get('dependentConfigGroups')[serviceName] != configGroup.get('name')) {
  124. var dependentGroups = $.extend({},this.get('selectedConfigGroup.dependentConfigGroups'));
  125. dependentGroups[serviceName] = configGroup.get('name');
  126. this.set('selectedConfigGroup.dependentConfigGroups', dependentGroups);
  127. }
  128. }, this);
  129. }, this);
  130. }
  131. }
  132. }, this);
  133. }
  134. }.observes('selectedConfigGroup'),
  135. /******************************METHODS THAT WORKS WITH DEPENDENT CONFIGS *************************************/
  136. /**
  137. * clear values for dependent configs
  138. * @method clearDependentConfigs
  139. * @private
  140. */
  141. clearDependentConfigs: function() {
  142. this.setProperties({
  143. _dependentConfigValues: []
  144. });
  145. },
  146. /**
  147. * clear values for dependent configs for given services
  148. * @method clearDependentConfigs
  149. * @private
  150. */
  151. clearDependentConfigsByService: function(serviceNames) {
  152. var cleanDependencies = this.get('_dependentConfigValues').reject(function(c) {
  153. return serviceNames.contains(c.serviceName);
  154. }, this);
  155. this.set('_dependentConfigValues', cleanDependencies);
  156. },
  157. /**
  158. * Remove configs from <code>_dependentConfigValues</code> which depends between installed services only.
  159. *
  160. * @param {String[]} installedServices
  161. * @param {App.ServiceConfig[]} stepConfigs
  162. */
  163. clearDependenciesForInstalledServices: function(installedServices, stepConfigs) {
  164. var allConfigs = stepConfigs.mapProperty('configs').filter(function(item) {
  165. return item.length;
  166. }).reduce(function(p, c) {
  167. if (p) {
  168. return p.concat(c);
  169. }
  170. });
  171. var cleanDependencies = this.get('_dependentConfigValues').reject(function(item) {
  172. if (installedServices.contains(Em.get(item, 'serviceName'))) {
  173. var stackProperty = App.StackConfigProperty.find().findProperty("name", item.propertyName);
  174. var parentConfigs = stackProperty && stackProperty.get('propertyDependsOn');
  175. if (!parentConfigs || !parentConfigs.length) {
  176. return true;
  177. } else {
  178. parentConfigs = parentConfigs.mapProperty('name');
  179. }
  180. // check that all parent properties from installed service
  181. return !parentConfigs.reject(function(parentConfigName) {
  182. var property = allConfigs.findProperty('name', parentConfigName);
  183. if (!property) {
  184. return false;
  185. }
  186. return installedServices.contains(Em.get(property, 'serviceName'));
  187. }).length;
  188. }
  189. return false;
  190. });
  191. this.set('_dependentConfigValues', cleanDependencies);
  192. },
  193. /**
  194. * get config group object for current service
  195. * @param serviceName
  196. * @returns {App.ConfigGroup|null}
  197. */
  198. getGroupForService: function(serviceName) {
  199. if (!this.get('stepConfigs') || this.get('stepConfigs.length') === 0) {
  200. return null;
  201. }
  202. if (this.get('selectedService.serviceName') === serviceName) {
  203. return this.get('selectedConfigGroup');
  204. } else {
  205. var stepConfig = this.get('stepConfigs').findProperty('serviceName', serviceName);
  206. if (stepConfig) {
  207. var groups = stepConfig.get('configGroups');
  208. if (this.get('selectedConfigGroup.isDefault')) {
  209. return groups.length ? groups.findProperty('isDefault', true) : null;
  210. } else {
  211. return groups.length ? groups.findProperty('name', this.get('selectedConfigGroup.dependentConfigGroups')[serviceName]) : null;
  212. }
  213. } else {
  214. return null;
  215. }
  216. }
  217. },
  218. /**
  219. * disable saving recommended value for current config
  220. * @param config
  221. * @param {boolean} saveRecommended
  222. * @method removeCurrentFromDependentList
  223. */
  224. removeCurrentFromDependentList: function (config, saveRecommended) {
  225. var current = this.get('_dependentConfigValues').find(function(dependentConfig) {
  226. return Em.get(dependentConfig, 'propertyName') == config.get('name') && Em.get(dependentConfig, 'fileName') == App.config.getConfigTagFromFileName(config.get('filename'));
  227. });
  228. if (current) {
  229. Em.setProperties(current, {
  230. 'saveRecommended': !!saveRecommended,
  231. 'saveRecommendedDefault': !!saveRecommended
  232. });
  233. }
  234. },
  235. /**
  236. * sends request to get values for dependent configs
  237. * @param {{type: string, name: string}[]} changedConfigs - list of changed configs to track recommendations
  238. * @param {Boolean} initial
  239. * @param {Function} onComplete
  240. * @param {App.ConfigGroup|null} [configGroup=null]
  241. * @returns {$.ajax|null}
  242. */
  243. getRecommendationsForDependencies: function(changedConfigs, initial, onComplete, configGroup) {
  244. if (Em.isArray(changedConfigs) && changedConfigs.length > 0 || initial) {
  245. if (!configGroup) {
  246. configGroup = this.get('selectedConfigGroup');
  247. }
  248. var recommendations = this.get('hostGroups');
  249. recommendations.blueprint.configurations = blueprintUtils.buildConfigsJSON(this.get('services'), this.get('stepConfigs'));
  250. delete recommendations.config_groups;
  251. var dataToSend = {
  252. recommend: 'configurations',
  253. hosts: this.get('hostNames'),
  254. services: this.get('serviceNames')
  255. };
  256. if (App.get('isClusterSupportsEnhancedConfigs')) {
  257. if (changedConfigs) {
  258. dataToSend.recommend = 'configuration-dependencies';
  259. dataToSend.changed_configurations = changedConfigs;
  260. }
  261. if (!configGroup.get('isDefault') && configGroup.get('hosts.length') > 0) {
  262. var configGroups = this.buildConfigGroupJSON(this.get('selectedService.configs'), configGroup);
  263. recommendations.config_groups = [configGroups];
  264. }
  265. }
  266. dataToSend.recommendations = recommendations;
  267. return App.ajax.send({
  268. name: 'config.recommendations',
  269. sender: this,
  270. data: {
  271. stackVersionUrl: App.get('stackVersionURL'),
  272. dataToSend: dataToSend,
  273. selectedConfigGroup: configGroup.get('isDefault') ? null : configGroup.get('name'),
  274. initial: initial
  275. },
  276. success: 'dependenciesSuccess',
  277. error: 'dependenciesError',
  278. callback: function() {
  279. if (onComplete) {
  280. onComplete()
  281. }
  282. }
  283. });
  284. } else {
  285. return null;
  286. }
  287. },
  288. /**
  289. * generates JSON with config group info to send it for recommendations
  290. * @param configs
  291. * @param configGroup
  292. * @returns {{configurations: Object[], hosts: string[]}}
  293. */
  294. buildConfigGroupJSON: function(configs, configGroup) {
  295. Em.assert('configGroup can\'t be null', configGroup);
  296. var hosts = configGroup.get('hosts');
  297. var configurations = {};
  298. var overrides = configs.forEach(function(cp) {
  299. var override = cp.get('overrides') && cp.get('overrides').findProperty('group.name', configGroup.get('name'));
  300. if (override) {
  301. var tag = App.config.getConfigTagFromFileName(cp.get('filename'));
  302. if (!configurations[tag]) {
  303. configurations[tag] = { properties: {} };
  304. }
  305. configurations[tag].properties[cp.get('name')] = override.get('value');
  306. }
  307. });
  308. return {
  309. configurations: [configurations],
  310. hosts: hosts
  311. }
  312. },
  313. /**
  314. * shows popup with results for recommended value
  315. * if case properties that was changes belongs to not default group
  316. * user should pick to what config group from dependent service dependent properties will be saved
  317. * @param data
  318. * @param opt
  319. * @param params
  320. * @method dependenciesSuccess
  321. */
  322. dependenciesSuccess: function (data, opt, params) {
  323. this._saveRecommendedValues(data, params.initial, params.dataToSend.changed_configurations, params.selectedConfigGroup);
  324. this.set("recommendationsConfigs", Em.get(data.resources[0] , "recommendations.blueprint.configurations"));
  325. if (!params.initial) {
  326. this.updateDependentConfigs();
  327. }
  328. },
  329. /**
  330. * method to show popup with dependent configs
  331. * @method showChangedDependentConfigs
  332. */
  333. showChangedDependentConfigs: function(event, callback, secondary) {
  334. var self = this;
  335. if (this.get('_dependentConfigValues.length') > 0) {
  336. App.showDependentConfigsPopup(this.get('changedProperties'), function() {
  337. self.updateDependentConfigs();
  338. if (callback) {
  339. callback();
  340. }
  341. }, secondary);
  342. } else {
  343. if (callback) {
  344. callback();
  345. }
  346. }
  347. },
  348. /**
  349. *
  350. */
  351. changedDependentGroup: function() {
  352. var dependentServices = this.get('stepConfigs').filter(function(stepConfig) {
  353. return this.get('selectedService.dependentServiceNames').contains(stepConfig.get('serviceName'));
  354. }, this);
  355. App.showSelectGroupsPopup(this.get('selectedService.serviceName'),
  356. this.get('selectedService.configGroups').findProperty('name', this.get('selectedConfigGroup.name')),
  357. dependentServices, this.get('_dependentConfigValues'))
  358. },
  359. /**
  360. *
  361. * @param jqXHR
  362. * @param ajaxOptions
  363. * @param error
  364. * @param opt
  365. */
  366. dependenciesError: function(jqXHR, ajaxOptions, error, opt) {
  367. this.set('recommendationTimeStamp', (new Date).getTime());
  368. App.ajax.defaultErrorHandler(jqXHR, opt.url, opt.method, jqXHR.status);
  369. },
  370. /**
  371. * saves values from response for dependent config properties to <code>_dependentConfigValues<code>
  372. * @param data
  373. * @param [updateOnlyBoundaries=false]
  374. * @param [changedConfigs=null]
  375. * @method saveRecommendedValues
  376. * @private
  377. */
  378. _saveRecommendedValues: function(data, updateOnlyBoundaries, changedConfigs, selectedConfigGroup) {
  379. Em.assert('invalid data - `data.resources[0].recommendations.blueprint.configurations` not defined ', data && data.resources[0] && Em.get(data.resources[0], 'recommendations.blueprint.configurations'));
  380. var configObject = data.resources[0].recommendations.blueprint.configurations;
  381. if (!selectedConfigGroup) {
  382. this.parseConfigsByTag(configObject, updateOnlyBoundaries, changedConfigs, selectedConfigGroup);
  383. } else if (data.resources[0].recommendations['config-groups']){
  384. var configFroGroup = data.resources[0].recommendations['config-groups'][0];
  385. this.parseConfigsByTag(configFroGroup.configurations, updateOnlyBoundaries, changedConfigs, selectedConfigGroup);
  386. this.parseConfigsByTag(configFroGroup.dependent_configurations, updateOnlyBoundaries, changedConfigs, selectedConfigGroup);
  387. }
  388. },
  389. /**
  390. * saves values from response for dependent configs to <code>_dependentConfigValues<code>
  391. * @param configObject - JSON response from `recommendations` endpoint
  392. * @param updateOnlyBoundaries
  393. * @param selectedConfigGroup
  394. * @param {App.ServiceConfigProperty[]} parentConfigs - config properties for which recommendations were received
  395. * @method saveRecommendedValues
  396. * @private
  397. */
  398. parseConfigsByTag: function(configObject, updateOnlyBoundaries, parentConfigs, selectedConfigGroup) {
  399. var notDefaultGroup = !!selectedConfigGroup;
  400. var parentPropertiesNames = parentConfigs ? parentConfigs.mapProperty('name') : [];
  401. /** get all configs by config group **/
  402. for (var key in configObject) {
  403. /** defines main info for file name (service name, config group, config that belongs to filename) **/
  404. var service = App.config.getServiceByConfigType(key);
  405. var serviceName = service.get('serviceName');
  406. var stepConfig = this.get('stepConfigs').findProperty('serviceName', serviceName);
  407. if (stepConfig) {
  408. var initialValue;
  409. var configProperties = stepConfig ? stepConfig.get('configs').filterProperty('filename', App.config.getOriginalFileName(key)) : [];
  410. var group = this.getGroupForService(serviceName);
  411. for (var propertyName in configObject[key].properties) {
  412. var dependentProperty = this.get('_dependentConfigValues').filterProperty('propertyName', propertyName).filterProperty('fileName', key).findProperty('configGroup', group && Em.get(group,'name'));
  413. var cp = configProperties.findProperty('name', propertyName);
  414. var override = (notDefaultGroup && group && cp && cp.get('overrides')) ? cp.get('overrides').findProperty('group.name', group.get('name')) : null;
  415. var value = override ? override.get('value') : cp && cp.get('value');
  416. if (this.useInitialValue(serviceName)) {
  417. initialValue = override ? override.get('initialValue') : cp && cp.get('initialValue');
  418. } else {
  419. initialValue = override ? override.get('savedValue') : cp && cp.get('savedValue');
  420. }
  421. initialValue = Em.isNone(initialValue) ? value : initialValue;
  422. var recommendedValue = configObject[key].properties[propertyName];
  423. var isNewProperty = (!notDefaultGroup && Em.isNone(cp)) || (notDefaultGroup && group && Em.isNone(override));
  424. var parsedInit = parseFloat(initialValue);
  425. var parsedRecommended = parseFloat(recommendedValue);
  426. if (!isNaN(parsedInit) && !isNaN(parsedRecommended)) {
  427. initialValue = parsedInit.toString();
  428. recommendedValue = parsedRecommended.toString();
  429. }
  430. if (!updateOnlyBoundaries && !parentPropertiesNames.contains(propertyName) && initialValue != recommendedValue) { //on first initial request we don't need to change values
  431. if (dependentProperty) {
  432. Em.set(dependentProperty, 'value', initialValue);
  433. Em.set(dependentProperty, 'recommendedValue', recommendedValue);
  434. Em.set(dependentProperty, 'toDelete', false); // handled in <code>saveRecommendedAttributes</code>
  435. Em.set(dependentProperty, 'toAdd', isNewProperty);
  436. Em.set(dependentProperty, 'parentConfigs', dependentProperty.parentConfigs.concat(parentPropertiesNames).uniq());
  437. } else {
  438. this.get('_dependentConfigValues').pushObject({
  439. saveRecommended: true,
  440. saveRecommendedDefault: true,
  441. toDelete: false,
  442. isDeleted: false, // handled in <code>saveRecommendedAttributes</code>
  443. toAdd: isNewProperty,
  444. fileName: key,
  445. propertyName: propertyName,
  446. configGroup: group ? group.get('name') : "",
  447. value: initialValue,
  448. parentConfigs: parentPropertiesNames,
  449. serviceName: serviceName,
  450. allowChangeGroup: !this.get('selectedService.isDefault') && service.get('serviceName') != stepConfig.get('serviceName') && stepConfig.get('configGroups.length') > 1,
  451. serviceDisplayName: service.get('displayName'),
  452. recommendedValue: recommendedValue
  453. });
  454. }
  455. }
  456. /**
  457. * saving recommended value to service config properties
  458. * this value can be used as marker on slider widget
  459. */
  460. if (notDefaultGroup) {
  461. override && override.set('recommendedValue', recommendedValue);
  462. } else {
  463. cp && cp.set('recommendedValue', recommendedValue);
  464. }
  465. if (!updateOnlyBoundaries) {
  466. /**
  467. * clear _dependentPropertyValues from
  468. * properties that wasn't changed while recommendations
  469. */
  470. if ((initialValue == recommendedValue) || (Em.isNone(initialValue) && Em.isNone(recommendedValue))) {
  471. /** if recommended value same as default we shouldn't show it in popup **/
  472. if (notDefaultGroup) {
  473. if (override) {
  474. if (override.get('isNotSaved')) {
  475. cp.get('overrides').removeObject(override);
  476. } else {
  477. override.set('value', initialValue);
  478. }
  479. if (dependentProperty) {
  480. this.get('_dependentConfigValues').removeObject(dependentProperty);
  481. }
  482. }
  483. } else {
  484. cp.set('value', initialValue);
  485. if (!this.useInitialValue(serviceName)) {
  486. cp.set('savedValue', initialValue);
  487. }
  488. if (dependentProperty) {
  489. this.get('_dependentConfigValues').removeObject(dependentProperty);
  490. }
  491. }
  492. }
  493. }
  494. }
  495. }
  496. this._saveRecommendedAttributes(configObject, parentPropertiesNames, updateOnlyBoundaries, selectedConfigGroup);
  497. }
  498. },
  499. /**
  500. * Save property attributes received from recommendations. These attributes are minimum, maximum,
  501. * increment_step. Attributes are stored in <code>App.StackConfigProperty</code> model.
  502. *
  503. * @param {Object[]} configs
  504. * @param parentPropertiesNames
  505. * @param updateOnlyBoundaries
  506. * @private
  507. */
  508. _saveRecommendedAttributes: function(configs, parentPropertiesNames, updateOnlyBoundaries, selectedConfigGroup) {
  509. var self = this;
  510. Em.keys(configs).forEach(function (siteName) {
  511. var service = App.config.getServiceByConfigType(siteName);
  512. var serviceName = service.get('serviceName');
  513. var group = self.getGroupForService(serviceName);
  514. var stepConfig = self.get('stepConfigs').findProperty('serviceName', serviceName);
  515. var configProperties = stepConfig ? stepConfig.get('configs').filterProperty('filename', App.config.getOriginalFileName(siteName)) : [];
  516. var properties = configs[siteName].property_attributes || {};
  517. Em.keys(properties).forEach(function (propertyName) {
  518. var cp = configProperties.findProperty('name', propertyName);
  519. var stackProperty = App.StackConfigProperty.find().findProperty('id', App.config.configId(propertyName, siteName));
  520. var attributes = properties[propertyName] || {};
  521. Em.keys(attributes).forEach(function (attributeName) {
  522. if (attributeName == 'delete') {
  523. if (!updateOnlyBoundaries) {
  524. var dependentProperty = self.get('_dependentConfigValues').filterProperty('propertyName', propertyName).filterProperty('fileName', siteName).findProperty('configGroup', group && Em.get(group,'name'));
  525. if (dependentProperty) {
  526. Em.set(dependentProperty, 'toDelete', true);
  527. Em.set(dependentProperty, 'toAdd', false);
  528. Em.set(dependentProperty, 'recommendedValue', null);
  529. } else {
  530. self.get('_dependentConfigValues').pushObject({
  531. saveRecommended: true,
  532. saveRecommendedDefault: true,
  533. propertyValue: cp && (self.useInitialValue(serviceName) ? cp.get('initialValue') : cp.get('savedValue')),
  534. toDelete: true,
  535. toAdd: false,
  536. isDeleted: true,
  537. fileName: siteName,
  538. propertyName: propertyName,
  539. configGroup: group ? group.get('name') : "",
  540. parentConfigs: parentPropertiesNames,
  541. serviceName: service.get('serviceName'),
  542. allowChangeGroup: !self.get('selectedService.isDefault') && service.get('serviceName') != stepConfig.get('serviceName') && stepConfig.get('configGroups.length') > 1,
  543. serviceDisplayName: service.get('displayName'),
  544. recommendedValue: null
  545. });
  546. }
  547. }
  548. } else if (stackProperty) {
  549. if (selectedConfigGroup) {
  550. if (!stackProperty.get('valueAttributes')[selectedConfigGroup]) {
  551. /** create not default group object for updating such values as min/max **/
  552. Em.set(stackProperty.get('valueAttributes'), selectedConfigGroup, {});
  553. }
  554. if (stackProperty.get('valueAttributes')[selectedConfigGroup][attributeName] != attributes[attributeName]) {
  555. Em.set(stackProperty.get('valueAttributes')[selectedConfigGroup], attributeName, attributes[attributeName]);
  556. self.toggleProperty('forceUpdateBoundaries');
  557. }
  558. } else {
  559. Em.set(stackProperty.get('valueAttributes'), attributeName, attributes[attributeName]);
  560. }
  561. }
  562. });
  563. });
  564. });
  565. },
  566. /**
  567. * save values that are stored in <code>_dependentConfigValues<code>
  568. * to step configs
  569. */
  570. updateDependentConfigs: function() {
  571. var self = this;
  572. this.get('stepConfigs').forEach(function(serviceConfigs) {
  573. var selectedGroup = self.getGroupForService(serviceConfigs.get('serviceName'));
  574. if (selectedGroup) {
  575. self._updateRecommendedValues(serviceConfigs, selectedGroup);
  576. self._addRecommendedProperties(serviceConfigs, selectedGroup);
  577. self._removeUnRecommendedProperties(serviceConfigs, selectedGroup);
  578. }
  579. });
  580. this.set('recommendationTimeStamp', (new Date).getTime());
  581. },
  582. /**
  583. * Add and remove dependencies based on recommendations
  584. *
  585. * @param {String[]} [serviceNames=undefined] - list of services to apply changes
  586. */
  587. addRemoveDependentConfigs: function(serviceNames) {
  588. var self = this;
  589. this.get('stepConfigs').forEach(function(serviceConfigs) {
  590. if (serviceNames && !serviceNames.contains(serviceConfigs.get('serviceName'))) {
  591. return;
  592. }
  593. var selectedGroup = self.getGroupForService(serviceConfigs.get('serviceName'));
  594. if (selectedGroup) {
  595. self._addRecommendedProperties(serviceConfigs, selectedGroup);
  596. self._removeUnRecommendedProperties(serviceConfigs, selectedGroup);
  597. }
  598. });
  599. },
  600. /**
  601. * add configs that was recommended and wasn't present in stepConfigs
  602. * @param stepConfigs
  603. * @param selectedGroup
  604. * @private
  605. */
  606. _addRecommendedProperties: function(stepConfigs, selectedGroup) {
  607. var propertiesToAdd = this.get('_dependentConfigValues').filterProperty('toAdd').filterProperty('serviceName', stepConfigs.get('serviceName')).filterProperty('configGroup', selectedGroup.get('name'));
  608. if (propertiesToAdd.length > 0) {
  609. propertiesToAdd.forEach(function(propertyToAdd) {
  610. if (!selectedGroup || selectedGroup.get('isDefault')) {
  611. if (Em.get(propertyToAdd, 'isDeleted')) {
  612. this.get('_dependentConfigValues').removeObject(propertyToAdd);
  613. }
  614. var originalFileName = App.config.getOriginalFileName(Em.get(propertyToAdd, 'fileName'));
  615. var predefinedProperty = App.config.get('preDefinedSiteProperties')
  616. .filterProperty('filename', originalFileName)
  617. .findProperty('name', Em.get(propertyToAdd, 'propertyName'));
  618. var addedProperty = App.ServiceConfigProperty.create({
  619. name: Em.get(propertyToAdd, 'propertyName'),
  620. displayName: Em.get(propertyToAdd, 'propertyName'),
  621. value: Em.get(propertyToAdd, 'recommendedValue'),
  622. recommendedValue: Em.get(propertyToAdd, 'recommendedValue'),
  623. savedValue: null,
  624. category: 'Advanced ' + Em.get(propertyToAdd, 'fileName'),
  625. serviceName: stepConfigs.get('serviceName'),
  626. filename: originalFileName,
  627. isNotSaved: !Em.get(propertyToAdd, 'isDeleted'),
  628. isRequired: predefinedProperty ? Em.getWithDefault(predefinedProperty, 'isRequired', true) : true
  629. });
  630. stepConfigs.get('configs').pushObject(addedProperty);
  631. addedProperty.validate();
  632. } else {
  633. var cp = stepConfigs.get('configs').filterProperty('name', Em.get(propertyToAdd, 'propertyName')).findProperty('filename', App.config.getOriginalFileName(Em.get(propertyToAdd, 'fileName')));
  634. if (Em.get(propertyToAdd, 'isDeleted')) {
  635. this.get('_dependentConfigValues').removeObject(propertyToAdd);
  636. }
  637. var overriddenProperty = cp.get('overrides') && cp.get('overrides').findProperty('group.name', selectedGroup.get('name'));
  638. if (overriddenProperty) {
  639. overriddenProperty.set('value', Em.get(propertyToAdd, 'recommendedValue'));
  640. overriddenProperty.set('recommendedValue', Em.get(propertyToAdd, 'recommendedValue'));
  641. } else {
  642. var overridePlainObject = {
  643. "value": Em.get(propertyToAdd, 'recommendedValue'),
  644. "recommendedValue": Em.get(propertyToAdd, 'recommendedValue'),
  645. "isNotSaved": !Em.get(propertyToAdd, 'isDeleted'),
  646. "isEditable": true
  647. };
  648. App.config.createOverride(cp, overridePlainObject, selectedGroup);
  649. }
  650. }
  651. Em.setProperties(propertyToAdd, {
  652. isDeleted: Em.get(propertyToAdd, 'isDeleted'),
  653. toAdd: false,
  654. toDelete: false
  655. });
  656. }, this);
  657. }
  658. },
  659. /**
  660. * remove configs that was recommended to delete from stepConfigs
  661. * @param stepConfigs
  662. * @param selectedGroup
  663. * @private
  664. */
  665. _removeUnRecommendedProperties: function(stepConfigs, selectedGroup) {
  666. var propertiesToDelete = this.get('_dependentConfigValues').filterProperty('toDelete').filterProperty('serviceName', stepConfigs.get('serviceName')).filterProperty('configGroup', selectedGroup.get('name'));
  667. if (propertiesToDelete.length > 0) {
  668. propertiesToDelete.forEach(function(propertyToDelete) {
  669. var cp = stepConfigs.get('configs').filterProperty('name', Em.get(propertyToDelete, 'propertyName')).findProperty('filename', App.config.getOriginalFileName(Em.get(propertyToDelete, 'fileName')));
  670. if (cp) {
  671. if (!selectedGroup || selectedGroup.get('isDefault')) {
  672. if (cp.get('isNotSaved')) {
  673. this.get('_dependentConfigValues').removeObject(propertyToDelete);
  674. }
  675. stepConfigs.get('configs').removeObject(cp);
  676. if (!cp.get('isNotSaved')) {
  677. Em.set(propertyToDelete, 'isDeleted', true);
  678. }
  679. } else {
  680. var overriddenConfig = cp.get('overrides') && cp.get('overrides').findProperty('group.name', selectedGroup.get('name'));
  681. if (overriddenConfig) {
  682. if (overriddenConfig.get('isNotSaved')) {
  683. this.get('_dependentConfigValues').removeObject(propertyToDelete);
  684. }
  685. cp.removeObject(overriddenConfig);
  686. if (!overriddenConfig.get('isNotSaved')) {
  687. Em.set(propertyToDelete, 'isDeleted', true);
  688. }
  689. }
  690. }
  691. Em.setProperties(propertyToDelete, {
  692. toAdd: false,
  693. toDelete: false
  694. });
  695. } else {
  696. this.get('_dependentConfigValues').removeObject(propertyToDelete);
  697. }
  698. }, this);
  699. }
  700. },
  701. /**
  702. * update config to their recommended values
  703. * @param stepConfigs
  704. * @param selectedGroup
  705. * @private
  706. */
  707. _updateRecommendedValues: function(stepConfigs, selectedGroup) {
  708. var propertiesToUpdate = this.get('_dependentConfigValues').filter(function(p) {
  709. return !Em.get(p, 'toDelete') && !Em.get(p, 'toAdd') && Em.get(p, 'serviceName') == stepConfigs.get('serviceName') && Em.get(p, 'configGroup') == selectedGroup.get('name');
  710. });
  711. if (propertiesToUpdate.length > 0) {
  712. stepConfigs.get('configs').forEach(function (cp) {
  713. var propertyToUpdate = propertiesToUpdate.filterProperty('propertyName', cp.get('name')).findProperty('fileName', App.config.getConfigTagFromFileName(cp.get('filename')));
  714. if (propertyToUpdate) {
  715. var valueToSave = propertyToUpdate.saveRecommended ? propertyToUpdate.recommendedValue : propertyToUpdate.value;
  716. if (!selectedGroup || selectedGroup.get('isDefault')) {
  717. if (propertyToUpdate.saveRecommended || cp.get('value') == propertyToUpdate.recommendedValue) {
  718. cp.set('value', valueToSave);
  719. }
  720. cp.set('recommendedValue', propertyToUpdate.recommendedValue);
  721. } else {
  722. var overriddenConfig = cp.get('overrides') && cp.get('overrides').findProperty('group.name', selectedGroup.get('name'));
  723. if (overriddenConfig) {
  724. if (propertyToUpdate.saveRecommended || overriddenConfig.get('value') == propertyToUpdate.recommendedValue) {
  725. overriddenConfig.set('value', valueToSave);
  726. }
  727. overriddenConfig.set('recommendedValue', propertyToUpdate.recommendedValue);
  728. }
  729. }
  730. }
  731. }, this);
  732. }
  733. },
  734. /**
  735. * On first load on installer and add service <code>initialValue<code> of <code>serviceConfigProperty<code> object
  736. * that contains value from stack should be overriden by dynamic recommendation.
  737. * Do this only for not installed services as in this case <code>initialValue<code> is not used.
  738. * @param configObject
  739. */
  740. updateInitialValue: function(configObject) {
  741. for (var key in configObject) {
  742. /** defines main info for file name (service name, config group, config that belongs to filename) **/
  743. var service = App.config.getServiceByConfigType(key);
  744. if (App.Service.find().filterProperty('serviceName', service.get('serviceName'))) {
  745. var stepConfig = this.get('stepConfigs').findProperty('serviceName', service.get('serviceName'));
  746. if (stepConfig) {
  747. var configProperties = stepConfig ? stepConfig.get('configs').filterProperty('filename', App.config.getOriginalFileName(key)) : [];
  748. for (var propertyName in configObject[key].properties) {
  749. var configProperty = configProperties.findProperty('name', propertyName);
  750. if (configProperty) {
  751. configProperty.set('initialValue', configObject[key].properties[propertyName]);
  752. }
  753. }
  754. }
  755. }
  756. }
  757. },
  758. /**
  759. * Helper method to get property from the <code>stepConfigs</code>
  760. *
  761. * @param {String} name - config property name
  762. * @param {String} fileName - config property filename
  763. * @return {App.ServiceConfigProperty|Boolean} - App.ServiceConfigProperty instance or <code>false</code> when property not found
  764. */
  765. findConfigProperty: function(name, fileName) {
  766. if (!name && !fileName) return false;
  767. if (this.get('stepConfigs') && this.get('stepConfigs.length')) {
  768. return this.get('stepConfigs').mapProperty('configs').filter(function(item) {
  769. return item.length;
  770. }).reduce(function(p, c) {
  771. if (p) {
  772. return p.concat(c);
  773. }
  774. }).filterProperty('filename', fileName).findProperty('name', name);
  775. }
  776. return false;
  777. }
  778. });