// This will validate a value based on a list of rules. import _ from "underscore"; import { TObject } from "./dtypes"; export namespace dvalidation { // convert any string to array based on separation on comma export function getArraySafe(str: any): string[] { if (isNullOrUndefOrEmpty(str)) { return []; } if (_.isArray(str)) { return str; } if (_.isString(str)) { return str .split(",") .map((x) => x.trim()) .filter((x) => x.length > 0); } return []; } //return true if true export function isTrue(str: any) { if (_.isString(str)) { str = str.trim().toLowerCase(); return str == "true" || str == "1"; } return str == 1 || str == true; } // return false is false export function isFalse(str: any) { if (_.isString(str)) { str = str.trim().toLowerCase(); return str == "false" || str == "0"; } return str == 0 || str == false; } // validate only for bool export function validateConditionOrThrow(condition: boolean, msg: string) { if (condition == false) { throw new Error(msg); } } // Check if the input data is a true Object like {} export function isObjectOrThrow(input: any, errorMessage?: string): void { if (typeof input !== 'object' || Array.isArray(input) || input === null) { const message = errorMessage || 'Input is not an object.'; throw new Error(message); } } // Check if the input is array or not export function isArrayOrThrow(input: any, errorMessage?: string): void { if (!Array.isArray(input)) { const message = errorMessage || 'Input is not an array.'; throw new Error(message); } } // Check if the input is array of object or throw export function isArrayOfObjectOrThrow(input: any, errorMessage?: string): void { if (!Array.isArray(input)) { const message = errorMessage || 'Input is not an array.'; throw new Error(message); } if (!input.every(item => typeof item === 'object' && item !== null && !Array.isArray(item))) { const message = errorMessage || 'Input is not an array of objects.'; throw new Error(message); } } // check null or undef but dont throw // Not a null/ // not a undef. // not a empty string. // not a empty array // DONOT CONSISER BOOL as they are valid value export function isNullOrUndefOrEmpty(x: any) { if (_.isString(x)) { return x.trim().length == 0; } if (_.isArray(x)) { return x.length == 0; } return _.isNull(x) || x == undefined || _.isNaN(x); } // validate exist or throw export function validateExistOrThrow(input: TObject, field: Array) { for (let f of field) { if (isNullOrUndefOrEmpty(input[f])) { throw Error(`Input validation failed: Missing ${f}`); } } } export function validateAnyOneExistOrThrow(input: TObject, field: Array) { for (let f of field) { if (!isNullOrUndefOrEmpty(input[f])) { return; } } throw Error(`Input validation failed: You must pass any one of ${field.join(",")}`); } export function provideOptionalValueInObject(input: TObject, optionals: TObject) { for (const key in optionals) { if (input[key] == undefined) { input[key] = optionals[key]; } } } export function validateOrThrow(b: boolean, msg: string) { if (isNullOrUndefOrEmpty(b)) { throw Error(msg); } } export function getDate(dateStr: string, err: string): Date { const date = new Date(dateStr); if (date.toString() == "Invalid Date") { throw Error(err); } return date; } export function getOrThrow(data: any, err: string): any { if (!isNullOrUndefOrEmpty(data)) { return data; } else { throw Error(err); } } export function throwIfNotFullValidation(obj: TObject, rules: TObject) { let res = fullValidation(obj, rules); if (res != true) { throw new Error(res); } } export function throwIfNotPartialValidation(obj: TObject, rules: TObject) { let res = partialValidation(obj, rules); if (res != true) { throw new Error(res); } } // The Full Validation, The object must ensure all item exist as per the rule. // while considering the object might not enforce all the rules but // existing object items must holds rule. export function fullValidation(obj: TObject, rules: TObject) { if (obj && rules) { for (const [key, rule] of Object.entries(rules)) { let res = validate(key, obj[key], rule); if (res != true) { return res; } } } return true; } // In partial validation, we check object to be validated by rules // while considering the object might not enforce all the rules but // existing object items must holds rule. export function partialValidation(obj: TObject, rules: TObject) { if (obj && rules) { for (const [key, value] of Object.entries(obj)) { if (rules[key]) { let res = validate(key, value, rules[key]); if (res != true) { return res; } } } } return true; } /* Apply rules on the val rules can be like: required|string|number|bool|array|object|email|array_of_object|array_of_email|in:A,B| */ export function validate(key: string, val: any, rule: string): true | string { if (!rule) { return true; } let rules = rule.split("|").map((x) => x.trim()); for (var rule of rules) { switch (rule) { case "required": if (val == null || val == undefined) { return `Validation failed: The ${key} field is required`; } if (_.isString(val) && val.trim().length == 0) { return `Validation failed: The ${key} field must not empty`; } if (_.isArray(val) && val.length == 0) { return `Validation failed: The ${key} field must not empty`; } break; case "string": if (!val) { continue; } if (val && !_.isString(val)) { return `Validation failed: ${key} must be a string`; } break; case "number": case "int": case "integer": if (!val) { continue; } let isnum = /^\d+$/.test(val); if (!isnum) { return `Validation failed: ${key} must be a number`; } break; case "bool": if (!val) { continue; } if (val && !_.isBoolean(val)) { return `Validation failed: ${key} must be a boolean`; } break; case "array": if (!val) { continue; } if (val && !_.isArray(val)) { return `Validation failed: ${key} must be a array`; } break; case "object": if (!val) { continue; } if (val && !_.isObject(val)) { return `Validation failed: ${key} must be a object`; } break; case "array_of_object": if (!val) { continue; } if (!_.isArray(val)) { return `Validation failed: ${key} must be a array of objects`; } for (var x of val) { if (!_.isObject(x)) { return `Validation failed: ${key} must be a arry of objects`; } } break; case "email": if (!val) { continue; } const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; if (!re.test(String(val).toLowerCase())) { return `Validation failed: ${key} must be email`; } break; case "list_of_email": if (!val) { continue; } const re1 = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; for (var y of String(val) .split(",") .map((x) => x.trim())) { if (!re1.test(String(y).toLowerCase())) { return `Validation failed: ${key} must be list of email`; } } break; default: // Custom check if (rule.startsWith("in:")) { if (!val) { continue; } let in_rule = rule .replace("in:", "") .split(",") .map((x) => x.trim()) .filter((x) => x.length > 0); if (!_.contains(in_rule, val)) { return `Validation failed: ${key} should be either of [${in_rule}]`; } } else { return `Validation failed: Found Invalid Rule [${rule}]`; } break; // Wanna to add more validation rule.. Please keep adding here. // Please write UT before checkin. } } return true; } }