/** * 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'); /** * @class ServiceConfigProperty */ App.ServiceConfigProperty = Em.Object.extend({ name: '', displayName: '', /** * value that is shown on IU * and is changing by user * @type {String|null} */ value: '', /** * value that is saved on cluster configs * and stored in /api/v1/clusters/{name}/configurations * @type {String|null} */ savedValue: null, /** * value that is returned from server as recommended * or stored on stack * @type {String|null} */ recommendedValue: null, /** * initial value of config. if value is saved it will be initial * otherwise first recommendedValue will be initial * @type {String|null} */ initialValue: null, /** * value that is shown on IU * and is changing by user * @type {boolean} */ isFinal: false, /** * value that is saved on cluster configs api * @type {boolean} */ savedIsFinal: null, /** * value that is returned from server as recommended * or stored on stack * @type {boolean} */ recommendedIsFinal: null, /** * @type {boolean} */ supportsFinal: false, /** * Hint message to display in tooltip. Tooltip will be wrapped on question mark icon. * If value is false no tooltip and question mark icon. * * @type {boolean|string} */ hintMessage: false, /** * Display label on the right side from input. In general used for checkbox only. * * @type {boolean} */ rightSideLabel: false, /** * type of property * @type {String[]} * @default empty array */ propertyType: [], /** * Text to be shown as placeholder * By default savedValue is shown as placeholder * @type {String} */ placeholderText: '', /** * type of widget View * @type {string} * @default null */ widgetType: null, /** * Placeholder used for configs with input type text */ placeholder: function() { if (this.isEditable) { return this.get('placeholderText') || this.get('savedValue'); } return null; }.property('isEditable', 'placeholderText', 'savedValue'), retypedPassword: '', description: '', displayType: 'string', // string, digits, number, directories, custom unit: '', category: 'General', isRequired: true, // by default a config property is required isReconfigurable: true, // by default a config property is reconfigurable isEditable: true, // by default a config property is editable disabledAsComponentAction: false, // is true for component action configs isNotEditable: Em.computed.not('isEditable'), hideFinalIcon: Em.computed.and('!isFinal', 'isNotEditable'), isVisible: true, isMock: false, // mock config created created only to displaying isRequiredByAgent: true, // Setting it to true implies property will be stored in configuration isSecureConfig: false, errorMessage: '', warnMessage: '', validationErrors: [], // stores messages from validation response marked as ERRROR validationWarnings: [], // stores message from validation response marked as WARN serviceConfig: null, // points to the parent App.ServiceConfig object filename: '', isOriginalSCP : true, // if true, then this is original SCP instance and its value is not overridden value. parentSCP: null, // This is the main SCP which is overridden by this. Set only when isOriginalSCP is false. overrides : null, overrideValues: [], group: null, // Contain group related to this property. Set only when isOriginalSCP is false. isUserProperty: null, // This property was added by user. Hence they get removal actions etc. isOverridable: true, compareConfigs: [], isComparison: false, hasCompareDiffs: false, showLabel: true, isConfigIdentity: false, copy: '', error: Em.computed.bool('errorMessage.length'), warn: Em.computed.bool('warnMessage.length'), hasValidationErrors: Em.computed.bool('validationErrors.length'), hasValidationWarnings: Em.computed.bool('validationWarnings.length'), isValid: Em.computed.equal('errorMessage', ''), previousValue: null, // cached value before changing config value /** * List of isFinal-values for overrides * Set in the controller * Should be empty array by default! * @type {boolean[]} */ overrideIsFinalValues: [], /** * true if property has warning or error * @type {boolean} */ hasIssues: Em.computed.or('error', 'warn', 'overridesWithIssues.length'), overridesWithIssues: Em.computed.filterBy('overrides', 'hasIssues', true), index: null, //sequence number in category editDone: false, //Text field: on focusOut: true, on focusIn: false isNotSaved: false, // user property was added but not saved hasInitialValue: false, //if true then property value is defined and saved to server isHiddenByFilter: false, //if true then hide this property (filtered out) rowStyleClass: null, // CSS-Class to be applied on the row showing this config showAsTextBox: false, /** * config is invisible since wrapper section is hidden * @type {boolean} */ hiddenBySection: false, /** * Determines config visibility on subsection level when wrapped. * @type {boolean} */ hiddenBySubSection: false, /** * Determines visibility state including section/subsection state. * When true means that property is shown and may affect validation process. * When false means that property won't affect validation. */ isActive: function() { return this.get('isVisible') && !this.get('hiddenBySubSection') && !this.get('hiddenBySection'); }.property('isVisible', 'hiddenBySubSection', 'hiddenBySection'), /** * @type {boolean} */ recommendedValueExists: function () { return !Em.isNone(this.get('recommendedValue')) && (this.get('recommendedValue') != "") && this.get('isRequiredByAgent') && !this.get('cantBeUndone'); }.property('recommendedValue'), /** * Usage example see on App.ServiceConfigRadioButtons.handleDBConnectionProperty() * * @property {Ember.View} additionalView - custom view related to property **/ additionalView: null, /** * If config is saved we should compare config value with savedValue to * find out if it was changed, but if config in not saved there is no savedValue, so * we should use initialValue instead. */ isNotInitialValue: function() { if (Em.isNone(this.get('savedValue')) && !Em.isNone(this.get('initialValue'))) { var value = this.get('value'), initialValue = this.get('initialValue'); if (this.get('stackConfigProperty.valueAttributes.type') == 'float') { initialValue = !Em.isNone(initialValue) ? '' + parseFloat(initialValue) : null; value = '' + parseFloat(value); } return initialValue !== value; } return false; }.property('initialValue', 'savedValue', 'value', 'stackConfigProperty.valueAttributes.type'), /** * Is property has active override with error */ isValidOverride: function () { return this.get('overrides.length') ? !this.get('overrides').find(function(o) { return Em.get(o, 'isEditable') && Em.get(o, 'errorMessage'); }) : true; }.property("overrides.@each.errorMessage"), /** * No override capabilities for fields which are not edtiable * and fields which represent master hosts. */ isPropertyOverridable: function () { var overrideable = this.get('isOverridable'); var editable = this.get('isEditable'); var overrides = this.get('overrides'); var dt = this.get('displayType'); return overrideable && (editable || !overrides || !overrides.length) && (!["componentHost", "password"].contains(dt)); }.property('isEditable', 'displayType', 'isOverridable', 'overrides.length'), isOverridden: function() { return (this.get('overrides') != null && this.get('overrides.length') > 0) || !this.get('isOriginalSCP'); }.property('overrides', 'overrides.length', 'isOriginalSCP'), isOverrideChanged: function () { if (Em.isNone(this.get('overrides')) && this.get('overrideValues.length') === 0) return false; return JSON.stringify(this.get('overrides').mapProperty('isFinal')) !== JSON.stringify(this.get('overrideIsFinalValues')) || JSON.stringify(this.get('overrides').mapProperty('value')) !== JSON.stringify(this.get('overrideValues')); }.property('overrides.@each.isNotDefaultValue', 'overrides.@each.overrideIsFinalValues', 'overrideValues.length'), isRemovable: function() { return this.get('isEditable') && this.get('isRequiredByAgent') && !(this.get('overrides.length') > 0) && (this.get('isUserProperty') || !this.get('isOriginalSCP')); }.property('isUserProperty', 'isOriginalSCP', 'overrides.length', 'isRequiredByAgent'), init: function () { this.setInitialValues(); this.set('viewClass', App.config.getViewClass(this.get("displayType"), this.get('dependentConfigPattern'), this.get('unit'))); this.set('validateErrors', App.config.getErrorValidator(this.get("displayType"))); this.set('validateWarnings', App.config.getWarningValidator(this.get("displayType"))); this.validate(); }, setInitialValues: function () { if (Em.isNone(this.get('value'))) { if (!Em.isNone(this.get('savedValue'))) { this.set('value', this.get('savedValue')); } else if (!Em.isNone(this.get('recommendedValue'))) { this.set('value', this.get('recommendedValue')); } } this.set('previousValue', this.get('value')); if (this.get('value') === null) { this.set('isVisible', false); } if (this.get("displayType") === "password") { this.set('retypedPassword', this.get('value')); this.set('recommendedValue', ''); } this.set('initialValue', this.get('value')); }, /** * updates configs list that belongs to config group */ updateGroupConfigs: function() { if (this.get('group')) { var o = this.get('group.properties').find(function(c) { return Em.get(c, 'name') === this.get('name') && Em.get(c, 'filename') === this.get('filename'); }, this); if (o) { Em.set(o, 'value', this.get('value')); } } }.observes('value'), /** * Indicates when value is not the default value. * Returns false when there is no default value. * * @type {boolean} */ isNotDefaultValue: function () { var value = this.get('value'), savedValue = this.get('savedValue'), supportsFinal = this.get('supportsFinal'), isFinal = this.get('isFinal'), savedIsFinal = this.get('savedIsFinal'); if (this.get('name') === 'kdc_type') { return App.router.get('mainAdminKerberosController.kdcTypesValues')[savedValue] !== value; } // ignore precision difference for configs with type of `float` which value may ends with 0 // e.g. between 0.4 and 0.40 if (this.get('stackConfigProperty') && this.get('stackConfigProperty.valueAttributes.type') == 'float') { savedValue = !Em.isNone(savedValue) ? '' + parseFloat(savedValue) : null; value = '' + parseFloat(value); } return (savedValue != null && value !== savedValue) || (supportsFinal && !Em.isNone(savedIsFinal) && isFinal !== savedIsFinal); }.property('value', 'savedValue', 'isEditable', 'isFinal', 'savedIsFinal'), /** * Don't show "Undo" for hosts on Installer Step7 */ cantBeUndone: Em.computed.existsIn('displayType', ["componentHost", "componentHosts", "radio button"]), validate: function () { if (!this.get('isEditable')) { this.set('errorMessage', ''); // do not perform validation for not editable configs } else if ((typeof this.get('value') != 'object') && ((this.get('value') + '').length === 0)) { var widgetType = this.get('widgetType'); this.set('errorMessage', (this.get('isRequired') && (!['test-db-connection','label'].contains(widgetType))) ? Em.I18n.t('errorMessage.config.required') : ''); } else { this.set('errorMessage', this.validateErrors(this.get('value'), this.get('name'), this.get('retypedPassword'))); } if (!this.get('widgetType') || ('text-field' === this.get('widgetType'))) { //temp conditions, since other warnings are calculated directly in widget view this.set('warnMessage', this.validateWarnings(this.get('value'), this.get('name'), this.get('filename'), this.get('stackConfigProperty'), this.get('unit'))); } }.observes('value', 'retypedPassword', 'isEditable'), viewClass: App.ServiceConfigTextField, validateErrors: function() { return '' }, validateWarnings: function() { return '' }, /** * Get override for selected group * * @param {String} groupName * @returns {App.ServiceConfigProperty|null} */ getOverride: function(groupName) { Em.assert('Group name should be defined string', (typeof groupName === 'string') && groupName); if (this.get('overrides.length')) { return this.get('overrides').findProperty('group.name', groupName); } return null; } });