UNPKG

config-srv

Version:

API and REST interface for editing a structured set of parameters

324 lines (303 loc) 9.83 kB
/* eslint-disable class-methods-use-this, max-len, max-classes-per-file, no-prototype-builtins, no-bitwise */ const __ = require('./lib.js'); const Params = require('./Params.js'); const _isRootNode_ = Symbol.for('_isRootNode_'); const _parentSchemaItem_ = Symbol.for('_parentSchemaItem_'); module.exports = class API extends Params { constructor (serviceOptions = {}) { super(serviceOptions); const { noThrow } = serviceOptions; if (noThrow && typeof noThrow === 'object') { this.noThrow = noThrow; } else if (noThrow === true) { this.noThrow = { set: true, getEx: true, get: true, value: true, getSchema: true, getSchemaAsync: true, plainParamsList: true, getTranslationTemplate: true, }; } else { this.noThrow = {}; } } /** * Save a parameter value at a given path * * During the storage process, checks are carried out on the validity of the stored data: * - matching types specified in the Schema * - the presence of Schemas for the transferred properties * In case of discrepancies, an error is generated. * * @param {propPathType} paramPath * @param {any} paramValue * @param {Object} options */ // TODO async set (paramPath, paramValue, options = {}) { try { this._addFrom(options, 'set'); const configName = this._setValue(paramPath, paramValue, options); if (this.serviceOptions.storageType === 'postgres') return; /** Вызывается только при записи в файл */ await this._saveNamedConfig(configName); } catch (err) { if (!this.noThrow.set) { throw err; } } } async setArr (paramArr, options) { try { this._addFrom(options, 'set'); const configNames = paramArr.map((item) => { const { path, value } = item; const configName = this._setValue(path, value, options); return configName; }); if (this.serviceOptions.storageType === 'postgres') return; /** Вызывается только при записи в файл */ await this._saveNamedConfigArr(configNames); } catch (err) { if (!this.noThrow.set) { throw err; } } } _setValue (paramPath, paramValue, options) { const { pathArr, configName, } = this._parseParamPathFragment(paramPath, options); if (!configName) { throw this._error(`The path must begin with a named configuration identifier. Function «${options.callFrom}»`); } if (!this.configNames.includes(configName)) { throw this._error(`There is no named configuration «${configName}» in the schema. Function «${options.callFrom}»`); } this._fillSchemaWithValues(pathArr, paramValue, options); return configName; } /** * Get the value of a configuration parameter along its path * The parameter value is passed in the property 'paramValue' of the object * and is accompanied by additional information: * defaultValue, * isNamedConfig, * paramName, * paramPath, * * @param {propPathType} paramPath * @param {Object} options * @return {*} */ getEx (paramPath, options = {}) { try { this._addFrom(options, 'getEx'); const { paramPath: paramPath_, pathArr, lastParamName, schemaItem, } = this._parseParamPath(paramPath, options); const parentSchemaItem = schemaItem[_parentSchemaItem_]; const configValuesFromSchema = this._getValues(pathArr); const result = { value: configValuesFromSchema, defaultValue: __.get(this.defaults, pathArr), paramPath: paramPath_, paramName: lastParamName, }; if (schemaItem[_isRootNode_]) { result.isRoot = true; } else if (parentSchemaItem && parentSchemaItem[_isRootNode_]) { result.isNamedConfig = true; } return result; } catch (err) { if (!this.noThrow.getEx) { throw err; } } } /** * Get the value of a configuration parameter along its path * Unlike the getEx method, this function returns value as is, without additional information. * * @param {propPathType} paramPath * @param {Object} options * @return {*} */ get (paramPath, options = {}) { try { this._addFrom(options, 'get'); return this._getValues(paramPath, options); } catch (err) { if (!this.noThrow.get) { throw err; } } } /** * Synonym for get. * * @param {propPathType} paramPath * @param {Object} options * @return {*} */ value (paramPath, options = {}) { try { this._addFrom(options, 'value'); return this._getValues(paramPath, options); } catch (err) { if (!this.noThrow.value) { throw err; } } } /** * Returns a Schema for the specified parameter path. * If the first argument is empty, the entire schema is returned. * If the second argument is passed - 'lng', then the 'title' properties are replaced with * localized headers in accordance with the value of the 't' parameter (if specified) * * @param {propPathType} paramPath * @param {String} lng - translation language * @param {Object} options * @return {schemaItemType} */ getSchema (paramPath, lng, options = {}) { try { this._addFrom(options, 'getSchema'); const localizedSchema = this._getSchemaByLanguage(lng); if (!paramPath) { return localizedSchema; } const { paramPath: paramPath_, pathArr, } = this._parseParamPathFragment(paramPath, options); const schemaItemLocalized = this._getSchemaFragment(pathArr, localizedSchema, options); if (!__.isSchemaItem(schemaItemLocalized)) { throw this._error(`Failed to get translated schema. Path: «${paramPath_}». Function «${options.callFrom}»`); } return schemaItemLocalized; } catch (err) { if (!this.noThrow.getSchema) { throw err; } } } async getSchemaAsync (paramPath, lng, options = {}) { try { this._addFrom(options, 'getSchemaAsync'); // noinspection UnnecessaryLocalVariableJS const res = this.getSchema(paramPath, lng, options); return res; } catch (err) { if (!this.noThrow.getSchemaAsync) { throw err; } } } /** * Returns a list of named configuration IDs * @return {String[]} */ list () { return this.configNames; } /** * Simple parameter data { path, value } * * @typedef {Object} propSimpleType * @property {propPathStrType} path - parameter path * @property {schemaValueType} value - parameter value */ /** * Extended parameter data { id, path, type, value, defaultValue ...<other schema item properties> } * * @typedef {Object} propExtendedType */ /** * Returns plain list of parameters, which types is not "section" as Simple or Extended parameter data * * @param {propPathStrType} paramPath * @param {Object} options * @param {Boolean} options.isExtended * @return {propSimpleType[]|propExtendedType[]} */ plainParamsList (paramPath, options = {}) { try { this._addFrom(options, 'plainParamsList'); const { schemaItem } = this._parseParamPath(paramPath, options); const propList = []; const traverseOptions = {}; const propertyCallback = (propertyTypeSchemaItem) => { const { path, value } = propertyTypeSchemaItem; const prop = options.isExtended ? this.cloneDeep(propertyTypeSchemaItem, { removeSymbols: true, pureObj: true }) : { path, value }; propList.push(prop); }; this.traverseSchema(schemaItem, traverseOptions, null, null, propertyCallback); return propList; } catch (err) { if (!this.noThrow.plainParamsList) { throw err; } } } /** * Schema properties * * @typedef {Object} getTranslationTemplateOptionsType * @property {String} lng - language id * @property {Boolean} [onlyStandardPaths = true] - * If `onlyStandardPaths = false` - the paths for translation id specified in * the scheme will be added to the resulting object, * even if they differ from the standard ones. * For example: * for the `config1.div13.v_json = {t: 'cs: config1.vjson.title'}` property, * the `cs:config1.vjson.title` property will be created. * If `onlyStandardPaths = true` is specified, only * then the standard property `config1.div13.v_json.title` will be created. * @property {Boolean} [addPaths = false] - * If `addPaths = true` - next to the title property is * placed the `t` property containing the translation id. * This is convenient to use by immediately copying and * substituting the translation identifier in the code. */ /** * Returns a translation template * * @param {getTranslationTemplateOptionsType} options * @return {Object} */ getTranslationTemplate (options) { try { this._addFrom(options, 'getTranslationTemplate'); options.root = {}; return this._getTranslationTemplate({ container: options.root }, options); } catch (err) { if (!this.noThrow.getTranslationTemplate) { throw err; } } } /** * @param {getTranslationTemplateOptionsType} options * @param {string} fnName * @private */ _addFrom (options, fnName) { if (!options.callFrom) { options.callFrom = fnName; } else { options.callFrom += ` > ${fnName}`; } } };