web-crawling-utils
Version:
Common useful utils for web crawling and automation scripts
141 lines (126 loc) • 6.32 kB
text/typescript
import { Schema } from "./validationSchema";
/**
* Validates data against a defined schema, checking for required fields, data types,
* and additional constraints (e.g., min/max values, pattern matching).
*
* @function validate
* @param {Schema} schema - A validation schema specifying rules for each field,
* including type, required status, minimum/maximum constraints, and pattern matching.
* @param {any} data - The input data to be validated against the schema.
* @returns {string[]} - An array of validation error messages. If validation passes,
* the array will be empty.
*
* @description This function iterates through each key in the provided schema to validate
* the corresponding field in the data. It performs the following checks:
* - **Required Fields**: Ensures required fields are present.
* - **Type Validation**: Validates the type (e.g., string, number, boolean, array, object, date).
* - **String Constraints**: Checks min/max length and pattern matching for strings.
* - **Number Constraints**: Enforces min/max values for numbers.
* - **Array Validation**: Validates arrays of strings or numbers.
* - **Object Validation**: Supports nested object validation through recursive calls.
* - **Enum Validation**: Confirms values match one of a specified list.
*
* @example
* const schema = {
* name: { type: "string", required: true, minLength: 3 },
* age: { type: "number", min: 18, max: 99 },
* preferences: { type: "array:string" },
* };
*
* const data = { name: "Alice", age: 25, preferences: ["reading", "swimming"] };
* const errors = validate(schema, data);
*
* @remarks
* This function provides a centralized validation approach to enforce data integrity and
* prevent invalid inputs from entering the application, supporting a uniform data format.
* If validation fails, it returns an array of descriptive error messages, highlighting the
* fields that do not meet the specified criteria.
*/
export function validate(schema: Schema, data: any): string[] {
try {
let errors: string[] = [];
for (const key in schema) {
const rules = schema[key];
const value = data[key];
// Check if the field is required and missing
if (rules.required && (value === undefined || value === null)) {
errors.push(`${key} is required.`);
continue;
}
// Skip further checks if the value is not required and is not present
if (value === undefined || value === null) {
continue;
}
// Check the type of the value
if (rules.type) {
switch (rules.type) {
case "string":
if (typeof value !== "string") {
errors.push(`${key} must be a string.`);
}
if (rules.minLength && value.length < rules.minLength) {
errors.push(`${key} must be at least ${rules.minLength} characters long.`);
}
if (rules.maxLength && value.length > rules.maxLength) {
errors.push(`${key} must be no more than ${rules.maxLength} characters long.`);
}
if (rules.pattern && !rules.pattern.test(value)) {
errors.push(`${key} must match the pattern ${rules.pattern}.`);
}
break;
case "number":
if (typeof value !== "number") {
errors.push(`${key} must be a number.`);
}
if (rules.min && value < rules.min) {
errors.push(`${key} must be at least ${rules.min}.`);
}
if (rules.max && value > rules.max) {
errors.push(`${key} must be no more than ${rules.max}.`);
}
break;
case "boolean":
if (typeof value !== "boolean") {
errors.push(`${key} must be a boolean.`);
}
break;
case "array:string":
if (!Array.isArray(value) || !value.every(item => typeof item === "string")) {
errors.push(`${key} must be an array of strings.`);
}
break;
case "array:number":
if (!Array.isArray(value) || !value.every(item => typeof item === "number")) {
errors.push(`${key} must be an array of numbers.`);
}
break;
case "object":
if (typeof value !== "object" || Array.isArray(value)) {
errors.push(`${key} must be an object.`);
} else if (rules.properties) {
// Recursively validate nested object
const nestedErrors = validate(rules.properties, value);
errors.push(...nestedErrors.map(err => `${key}.${err}`));
}
break;
case "date":
if (isNaN(Date.parse(value))) {
errors.push(`${key} must be a valid date.`);
}
break;
case "enum":
if (rules.enum && !rules.enum.includes(value)) {
errors.push(`${key} must be one of [${rules.enum.join(", ")}].`);
}
break;
default:
errors.push(`${key} has an unknown type: ${rules.type}.`);
}
}
}
return errors;
} catch (error) {
console.log(error);
throw error;
}
}