@63pokupki/nodejs-common
Version:
common nodejs functionality
756 lines (596 loc) • 22 kB
text/typescript
// Библиотеки
import * as _ from 'lodash';
const utf8 = require('utf8');
// Глобальные сервисы
import MainRequest from '../System/MainRequest';
// Системные сервисы
import { ErrorSys } from './ErrorSys';
/**
* Системный сервис валидации данных для моделей
*/
export class ModelValidatorSys {
protected okResult: boolean; // Статус проверки
protected abValidOK: any; // поля успешно прошедшие проверку
protected data: any; // Входящие данные
protected aResult: any; // Отфильтрованные проверенные данные
protected aResultType: any;
protected aMsg: string[]; // Сообщения валидации
/**
* Система регистрации ошибок
*
* @var Sys\ErrorSys
*/
protected errorSys: ErrorSys;
constructor(req: MainRequest) {
this.errorSys = req.sys.errorSys;
}
/**
* Валидирует и экранирует строковое значени
*
* @param string sKey - ключ в базе данных
* @param string sTpl - регулярное выражение по которому проверять
* @return boolean
*/
protected fValidString(sKey: string, sTpl: RegExp): boolean {
let bSuccess = false;
let s = String(this.data[sKey]).trim();
if ( s || s === '' ) {
s = utf8.encode(s);
if (sTpl instanceof RegExp) { // Проверка на регулярное выражение
if (sTpl.exec(s)) {
this.aResult[sKey] = s;
bSuccess = true;
}
} else {
this.aResult[sKey] = s;
bSuccess = true;
}
}
return bSuccess;
}
/**
* Экранирует текст
*
* @param string sKey
* @return boolean
*/
protected fValidText(sKey: string): boolean {
let bSuccess = false;
let s = String(this.data[sKey]).trim();
if ( s || s === '' ) {
this.aResult[sKey] = s;
bSuccess = true;
}
return bSuccess;
}
/**
* Валидирует булевую переменную
*
* @param string sKey
* @param string sTpl
* @return boolean
*/
protected fValidBool(sKey: string): boolean {
let bSuccess = false;
let i = Number(this.data[sKey]);
if (!isNaN(i)) {
if (i == 0 || i == 1) {
this.aResult[sKey] = i;
bSuccess = true;
} else {
bSuccess = false;
}
}
return bSuccess;
}
/**
* Проверяет числовые значения
*
* @param string sKey
* @param string sTpl
* @return boolean
*/
protected fValidInt(sKey: string): boolean {
let bSuccess = false;
let i = Math.round(Number(this.data[sKey]));
if (!isNaN(i)) {
this.aResult[sKey] = i;
bSuccess = true;
}
return bSuccess;
}
/**
* Проверяет числовые значения - 2.22
*
* @param string sKey
* @return boolean
*/
protected fValidDecimal(sKey: string): boolean {
let bSuccess = false;
let i = parseFloat(Number(this.data[sKey]).toFixed(2));
if (!isNaN(i)) {
this.aResult[sKey] = i;
bSuccess = true;
}
return bSuccess;
}
/**
* Проверяет массив чисел
*
* @param string sKey
* @return boolean
*/
protected fValidArrayNumbers(sKey: string): boolean {
let bSuccess = false;
let checkArray = true;
let array = this.data[sKey];
if (_.isArray(array)) {
for(let i = 0; i < array.length; i++){
if (checkArray) {
array[i] = Number(array[i]);
if (!array[i]) {
checkArray = false;
}
}
}
} else {
checkArray = false;
}
if (checkArray) {
this.aResult[sKey] = array;
bSuccess = true;
}
return bSuccess;
}
/**
* Проверка Enum параметров
*
* @param sKey - ключ значения
* @param aEnumList - Список возможных значений
*/
protected fValidEnum(sKey: string, aEnumList: any[]) {
let bSuccess = false;
let v: any = this.data[sKey];
if (_.indexOf(aEnumList, v) >= 0) {
let index = _.indexOf(aEnumList, this.data[sKey]);
this.aResult[sKey] = aEnumList[index];
bSuccess = true;
}
return bSuccess;
}
/**
* Экранирует JSON и проверяет
* Если это массив конвертирует в JSON
*
* @param string sKey
* @return boolean
*/
protected fValidJson(sKey: string): boolean {
let vJsonValue = this.data[sKey];
let sJsonValue = '';
let bSuccess = false;
if (vJsonValue) {
// Проверка на массив
if (_.isObject(vJsonValue)) {
sJsonValue = JSON.stringify(vJsonValue);
} else {
sJsonValue = vJsonValue;
}
// Проверка строки на корректный JSON
try {
let obj = null;
obj = JSON.parse(sJsonValue);
if (obj) {
this.aResult[sKey] = sJsonValue;
bSuccess = true;
}
} catch (e) {
this.errorSys.errorEx(e, sKey + '_json_parse', sKey + ' - неверный формат json поля');
}
}
return bSuccess;
}
/**
* Проверяет объект ли это
*
* @param string sKey
* @return boolean
*/
protected fValidObject(sKey: string): boolean {
let bSuccess = false;
if ( _.isObject(this.data[sKey]) ) {
this.aResult[sKey] = this.data[sKey];
bSuccess = true;
}
return bSuccess;
}
/**
* Проверяет массив ли это
*
* @param string sKey
* @return boolean
*/
protected fValidArray(sKey: string): boolean {
let bSuccess = false;
if ( _.isArray(this.data[sKey]) ) {
this.aResult[sKey] = this.data[sKey];
bSuccess = true;
}
return bSuccess;
}
// ================================================================
// Логические проверки
// ================================================================
/**
* Проверяет на больше
*
* @param string sKey
* @param string sTpl
* @return boolean
*/
protected fValidMore(sKey: string, iVal: number): boolean {
let bSuccess = false;
let i = Number(this.aResult[sKey])
if (!_.isNaN(i)) {
if (i > iVal) { // Если значение больше - все хорошо
this.aResult[sKey] = i;
bSuccess = true;
}
}
return bSuccess;
}
/**
* Проверяет на больше или равно
*
* @param string sKey
* @param string sTpl
* @return boolean
*/
protected fValidMoreOrEqual(sKey: string, iVal: number): boolean {
let bSuccess = false;
let i = Number(this.aResult[sKey])
if (!isNaN(i)) {
if (i >= iVal) { // Если значение больше - все хорошо
this.aResult[sKey] = i;
bSuccess = true;
}
}
return bSuccess;
}
/**
* Проверяет на меньше
*
* @param string sKey
* @param string sTpl
* @return boolean
*/
protected fValidLess(sKey: string, iVal: number): boolean {
let bSuccess = false;
let i = Number(this.aResult[sKey]);
if (!_.isNaN(i)) {
if (i < iVal) { // Если значение меньше - все хорошо
this.aResult[sKey] = i;
bSuccess = true;
}
}
return bSuccess;
}
/**
* Проверяет на меньше или равно
*
* @param string sKey
* @param string sTpl
* @return boolean
*/
protected fValidLessOrEqual(sKey: string, iVal: number): boolean {
let bSuccess = false;
let i = Number(this.aResult[sKey]);
if ( !isNaN(i) ) {
if (i <= iVal) { // Если значение меньше - все хорошо
this.aResult[sKey] = i;
bSuccess = true;
}
}
return bSuccess;
}
/**
* Проверяет на макс количесво символов
*
* @param string sKey
* @param string sTpl
* @return boolean
*/
protected fValidMaxLen(sKey: string, iLen: number): boolean {
let bSuccess = false;
if (this.aResult[sKey] || this.aResult[sKey] == '') {
let s = String(this.aResult[sKey]);
if (s.length <= iLen) { // Если значение меньше - все хорошо
this.aResult[sKey] = s;
bSuccess = true;
}
}
if (bSuccess) {
return true;
} else {
return false;
}
}
/**
* Проверяет минимальное количество символов
*
* @param string stringKey
* @param number checkValue
* @return boolean
*/
protected fValidMinLen(stringKey: string, checkValue: number): boolean {
if (this.aResult[stringKey]) {
const preparedInputString = String(this.aResult[stringKey]);
if (preparedInputString.length >= checkValue) {
this.aResult[stringKey] = preparedInputString;
return true;
}
}
return false;
}
/**
* Проверить существование значения
* @param val - Значение
*/
private checkExist(val: any): boolean {
let resp = true;
if (val == undefined) {
resp = false;
}
if (val == null) {
resp = false;
}
return resp;
}
// ================================================================
/**
* Получить проверенные отфильтрованные данные в качестве массива
*
* @return array|null
*/
public getResult(): {[key:string]:any}|any{ // Получить проверенные отфильтрованные данные
return this.aResult;
}
public getStatus(){ // Получиь статус проверки
return this.okResult;
}
public getMsg(): string[]{
return this.aMsg;
}
//ФОРМАТ ПРАВИЛА [
// key_field :[0:type, 1:condition, 2:required, 3:depend, 4:msg_error]
//]
public fValid(aRules:any, data:{[key:string]:any}){ // Проверка данных
this.data = data;
this.okResult = true; // [true/false] - Успешно или нет прошла валидация
this.abValidOK = {};
this.aResult = {};
this.aMsg = [];
_.forEach(aRules, (v, k) => {
this.abValidOK[k] = true;
//Подстановка значений по умолчанию, если значения нет
if( this.okResult && v['def'] && !this.data[k] ){
this.data[k] = v['def'];
}
if( 'error_key' in v ){ // Если указываем ключ ошибки декларируем ее
let errorKey:any = {};
errorKey[v['error_key']['key']] = v['error_key']['msg'];
this.errorSys.declareEx(errorKey);
}
//Проверка существования данных
let bExist = this.checkExist(this.data[k]);
//Проверка зависимостей
let bDpend = true;
if( v['depend'] ){
this.errorSys.decl('valid_'+k+'_depend', k+' - поле не прошло проверку зависимостей');
_.forEach(v['depend'], (vDepend, kDepend) => {
if( this.okResult && this.abValidOK[kDepend] ){
if( this.abValidOK[kDepend] && this.data[kDepend] ){
if( !(this.data[kDepend] == vDepend || vDepend == '*') ){
bDpend = false;
this.errorSys.error('valid_'+k+'_depend', k+' - поле не прошло проверку зависимостей');
}
}
}//if
}); //foreach
}//if
//Проверка - обязательного поля
if( v['require'] ){
this.errorSys.decl('valid_'+k+'_require', k+' - поле обязательно для заполнения');
if( !this.checkExist(this.data[k]) ){
this.okResult = false;
this.abValidOK[k] = false;
this.errorSys.error('valid_'+k+'_require', k+' - поле обязательно для заполнения');
}
}
// Обработка [string] значений
if( bExist && bDpend && v['type'] == 'str' ){
this.errorSys.decl('valid_'+k+'_str', v['error']+' Ошибка string = '+this.data[k]);
if( !this.fValidString(k, v['if']) ){
this.okResult = false;
this.abValidOK[k] = false;
this.errorSys.error('valid_'+k+'_str', v['error']+' Ошибка string = '+this.data[k]);
}
}
// Обработка [boolean] значений
if( bExist && bDpend && v['type'] == 'boolean' ){
this.errorSys.decl('valid_'+k+'_bool', v['error']+' Ошибка boolean = '+this.data[k]);
if( !this.fValidBool(k) ){
this.okResult = false;
this.abValidOK[k] = false;
this.errorSys.error('valid_'+k+'_bool', v['error']+' Ошибка boolean = '+this.data[k]);
}
}
// Обработка [integer] значений
if( bExist && bDpend && v['type'] == 'int' ){
this.errorSys.decl('valid_'+k+'_int', v['error']+' Ошибка int = '+this.data[k]);
if( !this.fValidInt(k) ){
this.okResult = false;
this.abValidOK[k] = false;
this.errorSys.error('valid_'+k+'_int', v['error']+' Ошибка int = '+this.data[k]);
}
}
// Обработка [enum] значений
if( bExist && bDpend && v['type'] == 'enum' ){
this.errorSys.decl('valid_'+k+'_enum', v['error']+' Ошибка enum = '+this.data[k]);
if( !this.fValidEnum(k, v['if']) ){
this.okResult = false;
this.abValidOK[k] = false;
this.errorSys.error('valid_'+k+'_enum', v['error']+' Ошибка enum = '+this.data[k]);
}
}
// Обработка [text] значений
if( bExist && bDpend && v['type'] == 'text' ){
this.errorSys.decl('valid_'+k+'_text', v['error']+' Ошибка text = '+this.data[k]);
if( !this.fValidText(k) ){
this.okResult = false;
this.abValidOK[k] = false;
this.errorSys.error('valid_'+k+'_text', v['error']+' Ошибка text = '+this.data[k]);
}
}
// Обработка [json] значений
if( bExist && bDpend && v['type'] == 'json' ){
this.errorSys.decl('valid_'+k+'_json', v['error']+' Ошибка json = '+this.data[k]);
if( !this.fValidJson(k) ){
this.okResult = false;
this.abValidOK[k] = false;
this.errorSys.error('valid_'+k+'_json', v['error']+' Ошибка json = '+this.data[k]);
}
}
// Обработка [decimal] значений
if( bExist && bDpend && v['type'] == 'decimal' ){
this.errorSys.decl('valid_'+k+'_decimal', v['error']+' Ошибка decimal = '+this.data[k]);
if( !this.fValidDecimal(k) ){
this.okResult = false;
this.abValidOK[k] = false;
this.errorSys.error('valid_'+k+'_decimal', v['error']+' Ошибка decimal = '+this.data[k]);
}
}
// Обработка [arrayNumbers] значений
if (bExist && bDpend && v['type'] === 'arrayNumbers') {
this.errorSys.decl('valid_' + k + '_arrayNumbers', v['error'] + ' Ошибка arrayNumbers = ' + this.data[k]);
if (!this.fValidArrayNumbers(k)) {
this.okResult = false;
this.abValidOK[k] = false;
this.errorSys.error('valid_' + k + '_arrayNumbers', v['error'] + ' Ошибка arrayNumbers = ' + this.data[k]);
}
}
// Обработка [object] значений
if( bExist && bDpend && v['type'] == 'object' ){
this.errorSys.decl('valid_'+k+'_object', v['error']+' Ошибка object = '+this.data[k]);
if( !this.fValidObject(k) ){
this.okResult = false;
this.abValidOK[k] = false;
this.errorSys.error('valid_'+k+'_object', v['error']+' Ошибка object = '+this.data[k]);
}
}
// Обработка [array] значений
if( bExist && bDpend && v['type'] == 'array' ){
this.errorSys.decl('valid_'+k+'_array', v['error']+' Ошибка array = '+this.data[k]);
if( !this.fValidArray(k) ){
this.okResult = false;
this.abValidOK[k] = false;
this.errorSys.error('valid_'+k+'_array', v['error']+' Ошибка array = '+this.data[k]);
}
}
// =================================================
// Логические проверки
// =================================================
// Обработка [more] значений - Проверка на больше
if( bExist && 'more' in v ){
this.errorSys.decl('valid_'+k+'_more', v['error']+' Число слишком маленькое = '+this.data[k]);
if( v['type'] == 'int' || v['type'] == 'decimal' ){
if( !this.fValidMore(k, v['more']) ){
this.okResult = false;
this.abValidOK[k] = false;
this.errorSys.error('valid_'+k+'_more', v['error']+' Число слишком маленькое = '+this.data[k]);
}
} else {
this.errorSys.error('valid_'+k+'_more_no_number', v['error']+' Поле не является числом');
}
}
// Обработка [more_or_equal] значений - Проверка на больше или равно
if (bExist && 'more_or_equal' in v) {
this.errorSys.decl('valid_' + k + '_more_or_equal', 'errorValidate');
if (v['type'] == 'int' || v['type'] == 'decimal') {
if( !this.fValidMoreOrEqual(k, v['more_or_equal']) ){
this.abValidOK[k] = false;
this.okResult = false;
this.errorSys.error('valid_' + k + '_more_or_equal', v['error'] + ' Число слишком маленькое = ' + this.data[k]);
}
} else {
this.errorSys.error('valid_' + k + '_more_or_equal_no_number', v['error'] + ' Поле не является числом');
}
}
// Обработка [less] значений - Проверка на меньше
if( bExist && 'less' in v ){
this.errorSys.decl('valid_'+k+'_less');
if( v['type'] == 'int' || v['type'] == 'decimal' ){
if( !this.fValidLess(k, v['less']) ){
this.okResult = false;
this.abValidOK[k] = false;
this.errorSys.error('valid_'+k+'_less', v['error']+' Число слишком большое = '+this.data[k]);
}
} else {
this.errorSys.error('valid_'+k+'_less_no_number', v['error']+' Поле не является числом');
}
}
// Обработка [less_or_equal] значений - Проверка на меньше или равно
if (bExist && 'less_or_equal' in v) {
this.errorSys.decl('valid_' + k + '_less_or_equal', 'errorValidate');
if (v['type'] == 'int' || v['type'] == 'decimal') {
if ( !this.fValidLessOrEqual(k, v['less_or_equal']) ){
this.abValidOK[k] = false;
this.okResult = false;
this.errorSys.error('valid_' + k + '_less_or_equal', v['error'] + ' Число слишком большое = ' + this.data[k]);
}
} else {
this.errorSys.error('valid_' + k + '_less_or_equal_no_number', v['error'] + ' Поле не является числом');
}
}
// Обработка [max_len] значений - Проверка на большее
if( bExist && 'max_len' in v ){
this.errorSys.decl('valid_'+k+'_max_len');
this.errorSys.decl('valid_'+k+'_max_len_no_string');
// Проверка является ли поле текстовым
if( v['type'] == 'text' || v['type'] == 'str' ){
if( !this.fValidMaxLen(k, v['max_len']) ){
this.okResult = false;
this.abValidOK[k] = false;
this.errorSys.error('valid_'+k+'_max_len', v['error']+' Превышено количество символов = '+this.data[k] );
}
} else {
this.errorSys.error('valid_'+k+'_max_len_no_string', 'Поле не является строкой');
}
}
// Обработка [min_len] значений - Проверка на меньшее
if (bExist && 'min_len' in v) {
this.errorSys.decl(`valid_${k}_min_len`);
this.errorSys.decl(`valid_${k}_min_len_no_string`);
// Проверка является ли поле текстовым
if (v.type === 'text' || v.type === 'str') {
if (this.fValidMinLen(k, v.min_len)) {
this.abValidOK[k] = true;
} else {
this.okResult = false;
this.errorSys.error(
`valid_'${k}_min_len`,
`${v.error} Количество символов меньше минимального значения = ${this.data[k]}`);
}
} else {
this.errorSys.error(`valid_${k}_min_len_no_string`, 'Поле не является строкой');
}
}
// ============================================
// Кастомная ошибка на поле [error_key]
if( !this.abValidOK[k] && 'error_key' in v ){ // Вызываем кастомную ошибку, если она произошла и была указана
this.errorSys.error(v['error_key']['key'], v['error_key']['msg']);
}
}); // foreach
return this.okResult;
}
}