UNPKG

config-srv

Version:

API and REST interface for editing a structured set of parameters

271 lines (262 loc) 7.92 kB
/* eslint-disable max-len,no-bitwise */ /* Standard data types for parameters Each data type has a javascript data type correspondence map and the function of normalizing/checking the value of this type - "validator". */ const __ = require('./lib.js'); const MIN_INT = -2147483648; const MAX_INT = 2147483647; const MIN_LONG = -9223372036854775808; // eslint-disable-next-line no-loss-of-precision,@typescript-eslint/no-loss-of-precision const MAX_LONG = 9223372036854775807; const numberValidator = (newVal, schemaItem, error = {}, isFractional = false) => { if (newVal == null) { return null; } const rT = typeof newVal; let val = newVal; if (rT === 'number') { if (Number.isNaN(newVal)) { return null; } } else if (rT === 'string' && newVal) { if (isFractional) { val = parseFloat(val); } else { if (typeof val === 'string') { val = val.replace(/L$/i, ''); } val = Number(val); } if (Number.isNaN(val)) { error.reason = `Failed to convert string to number: «${newVal}»`; return null; } } else { error.reason = `The expected js type is «number» or a string that can be converted to a number. Received type: «${rT}»`; return null; } if (val > Number.MAX_VALUE) { error.reason = `Number value is very big: «${val}»`; return null; } if (!isFractional) { val = Math.round(Math.max(Math.min(val, MAX_LONG), MIN_LONG)); } else { const { precision, type } = schemaItem; if (type === 'float') { val = Math.fround(val); if (typeof newVal === 'string') { newVal = newVal.replace(/f$/i, ''); } if (`${val}`.startsWith(`${newVal}`)) { val = typeof newVal === 'string' ? parseFloat(newVal) : newVal; } } if (precision != null) { val = Number(val.toFixed(precision)); } } return val === 0 ? 0 : val; }; const booleanValidator = (newVal, schemaItem, error = {}) => { if (newVal == null) { return null; } const rT = typeof newVal; if (rT === 'boolean') { return newVal; } if (rT === 'string') { const val = newVal.toLowerCase(); if (val === 'true') { return true; } if (val === 'false') { return false; } error.reason = `Expected string representation of «true» | «false». Received: «${newVal}»`; return null; } if (rT === 'number') { if (Math.abs(newVal) === 1) { return true; } if (newVal === 0) { return false; } error.reason = `A numeric representation of -1|1|0 is expected. Received: ${newVal}`; return null; } error.reason = `The expected type is «boolean» or «number» (-1|1|0) or a string ( «true»| «false»). Received type: «${rT}»`; return null; }; module.exports = { json: { jsTypes: ['null', 'object', 'array', 'string', 'number', 'boolean'], /** * Function of validation and normalization of a new value * * @param {any} newVal - new meaning * @param {schemaItemType} schemaItem - fragment of the schema containing the new value * @param {Object} error - container for sending validation error messages * @return {null|any} - normalized value */ validator: (newVal, schemaItem, error = {}) => { if (!newVal) { return newVal; } try { if (typeof newVal === 'string') { if (/^\s*\[.*]\s*$|^\s*{.*}\s*$/i.test(newVal)) { try { return JSON.parse(newVal); } catch (err) { return JSON.parse(newVal.replace(/(['"])?([a-zA-Z0-9_]+)(['"])?:/g, '"$2": ')); } } return newVal; } return JSON.parse(JSON.stringify(newVal)); } catch (err) { error.reason = `Failed to normalize JSON value Error: ${err.message}`; return null; } }, }, section: { jsTypes: ['null', 'array', 'object'], validator: (newVal, schemaItem, error = {}) => { error.reason = `Cannot set a value for a 'section'. Path «${schemaItem.path}». Value: «${newVal}»`; return undefined; }, }, array: { jsTypes: ['null', 'array'], validator: (newVal, schemaItem, error = {}) => { if (newVal == null) { return null; } if (Array.isArray(newVal)) { return newVal; } error.reason = `Type mismatch. «Array» expected, received: «${typeof newVal}»`; return null; }, }, string: { jsTypes: ['any'], validator: (newVal) => { if (newVal == null) { return null; } return (typeof newVal === 'string' ? newVal : JSON.stringify(newVal)); }, }, text: { jsTypes: ['null', 'string'], validator: (newVal) => { if (newVal == null) { return null; } return (typeof newVal === 'string' ? newVal : JSON.stringify(newVal)); }, }, date: { jsTypes: ['null', 'string'], validator: (newVal, schemaItem, error = {}) => { if (newVal == null) { return null; } const dt = __.parseAndValidateDate(newVal, error); return dt ? dt.substr(0, 10) : null; }, }, time: { jsTypes: ['null', 'string'], validator: (newVal, schemaItem, error = {}) => { if (newVal == null) { return null; } const dt = __.parseAndValidateDate(`2000-01-01T${String(newVal)}`, error); return dt ? dt.substr(11) : null; }, }, datetime: { jsTypes: ['null', 'string'], validator: (newVal, schemaItem, error = {}) => { if (newVal == null) { return null; } return __.parseAndValidateDate(newVal, error); }, }, email: { jsTypes: ['null', 'string'], validator: (newVal, schemaItem, error = {}) => { if (newVal == null) { return null; } const rT = typeof newVal; if (rT !== 'string') { error.reason = `A string representation of the email is expected. Received type: «${rT}»`; return null; } newVal = newVal.trim(); const match = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.exec(newVal); if (!match) { error.reason = `The string representation of the email does not match the pattern`; return null; } return newVal; }, }, number: { jsTypes: ['null', 'number', 'string'], validator: (newVal, schemaItem, error = {}) => numberValidator(newVal, schemaItem, error, true), }, int: { jsTypes: ['null', 'number', 'string'], validator: (newVal, schemaItem, error = {}) => { const validated = numberValidator(newVal, schemaItem, error); if (validated == null) { return null; } return Math.round(Math.max(Math.min(validated, MAX_INT), MIN_INT)); }, }, long: { jsTypes: ['null', 'number', 'string'], validator: (newVal, schemaItem, error = {}) => { const validated = numberValidator(newVal, schemaItem, error); return validated == null ? null : validated; }, }, float: { jsTypes: ['null', 'number', 'string'], validator: (newVal, schemaItem, error = {}) => { const validated = numberValidator(newVal, schemaItem, error, true); if (validated == null) { return null; } return validated; }, }, double: { jsTypes: ['null', 'number', 'string'], validator: (newVal, schemaItem, error = {}) => numberValidator(newVal, schemaItem, error, true), }, money: { jsTypes: ['null', 'number', 'string'], validator: (newVal, schemaItem, error = {}) => numberValidator(newVal, schemaItem, error, true), }, boolean: { jsTypes: ['null', 'boolean'], validator: (newVal, schemaItem, error = {}) => booleanValidator(newVal, schemaItem, error), }, bool: { jsTypes: ['null', 'boolean'], validator: (newVal, schemaItem, error = {}) => booleanValidator(newVal, schemaItem, error), }, };