UNPKG

@kenniy/godeye-data-contracts

Version:

Enterprise-grade base repository architecture for GOD-EYE microservices with zero overhead and maximum code reuse

384 lines (383 loc) 14.1 kB
"use strict"; /** * Unified Validation System * Standardized validation decorators and pipes across all services */ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ValidationUtils = exports.QueryDto = exports.SearchDto = exports.PaginationDto = exports.ValidationPipe = void 0; exports.IsValidId = IsValidId; exports.IsRequiredEmail = IsRequiredEmail; exports.IsOptionalEmail = IsOptionalEmail; exports.IsPhoneNumber = IsPhoneNumber; exports.ToLowerCase = ToLowerCase; exports.Trim = Trim; exports.TransformDate = TransformDate; exports.TransformArray = TransformArray; exports.IsValidPagination = IsValidPagination; exports.IsValidSearch = IsValidSearch; exports.IsValidEntity = IsValidEntity; const common_1 = require("@nestjs/common"); const class_validator_1 = require("class-validator"); const class_transformer_1 = require("class-transformer"); const validation_constants_1 = require("../constants/validation.constants"); const pagination_constants_1 = require("../constants/pagination.constants"); /** * Unified Validation Pipe * Replaces separate validation pipes with single, configurable pipe */ let ValidationPipe = class ValidationPipe { constructor(options = {}) { this.options = { whitelist: true, forbidNonWhitelisted: true, transform: true, ...options }; } async transform(value, { metatype }) { if (!metatype || !this.toValidate(metatype)) { return value; } const object = (0, class_transformer_1.plainToClass)(metatype, value); const errors = await (0, class_validator_1.validate)(object); if (errors.length > 0) { const error_messages = this.buildErrorMessage(errors); throw new common_1.BadRequestException({ message: validation_constants_1.VALIDATION_MESSAGES.VALIDATION_FAILED, errors: error_messages, }); } return object; } toValidate(metatype) { const types = [String, Boolean, Number, Array, Object]; return !types.includes(metatype); } buildErrorMessage(errors) { const messages = []; errors.forEach(error => { if (error.constraints) { messages.push(...Object.values(error.constraints)); } if (error.children && error.children.length > 0) { messages.push(...this.buildErrorMessage(error.children)); } }); return messages; } }; exports.ValidationPipe = ValidationPipe; exports.ValidationPipe = ValidationPipe = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [Object]) ], ValidationPipe); /** * ID Validation Decorator * Replaces @IsUUID() for TypeORM and @IsValidObjectId() for Mongoose */ function IsValidId(options) { return function (object, propertyName) { (0, class_validator_1.registerDecorator)({ name: 'isValidId', target: object.constructor, propertyName: propertyName, options: { message: options?.message || validation_constants_1.VALIDATION_MESSAGES.INVALID_ID, ...options, }, validator: { validate(value) { if (!value) return false; // Check UUID format if (validation_constants_1.VALIDATION_PATTERNS.UUID.test(value)) return true; // Check MongoDB ObjectId format if (validation_constants_1.VALIDATION_PATTERNS.OBJECT_ID.test(value)) return true; // Check numeric format if (validation_constants_1.VALIDATION_PATTERNS.NUMERIC.test(value)) return true; return false; }, defaultMessage: () => validation_constants_1.VALIDATION_MESSAGES.INVALID_ID, }, }); }; } /** * Required Email Validation Decorator * Works with both simple email validation and complex business rules */ function IsRequiredEmail(options) { return function (object, propertyName) { (0, class_validator_1.registerDecorator)({ name: 'isRequiredEmail', target: object.constructor, propertyName: propertyName, options: { message: options?.message || validation_constants_1.VALIDATION_MESSAGES.INVALID_EMAIL, ...options, }, validator: { validate(value) { if (!value) return false; return validation_constants_1.VALIDATION_PATTERNS.EMAIL.test(value); }, defaultMessage: () => validation_constants_1.VALIDATION_MESSAGES.INVALID_EMAIL, }, }); }; } /** * Optional Email Validation Decorator */ function IsOptionalEmail(options) { return function (object, propertyName) { (0, class_validator_1.registerDecorator)({ name: 'isOptionalEmail', target: object.constructor, propertyName: propertyName, options: { message: options?.message || validation_constants_1.VALIDATION_MESSAGES.INVALID_EMAIL, ...options, }, validator: { validate(value) { if (!value || value === '') return true; // Allow empty return validation_constants_1.VALIDATION_PATTERNS.EMAIL.test(value); }, defaultMessage: () => validation_constants_1.VALIDATION_MESSAGES.INVALID_EMAIL, }, }); }; } /** * Phone Number Validation Decorator * Supports multiple country formats */ function IsPhoneNumber(countries, options) { return function (object, propertyName) { (0, class_validator_1.registerDecorator)({ name: 'isPhoneNumber', target: object.constructor, propertyName: propertyName, constraints: [countries], options: { message: options?.message || validation_constants_1.VALIDATION_MESSAGES.INVALID_PHONE, ...options, }, validator: { validate(value, args) { if (!value) return false; // Basic phone validation - can be enhanced for specific countries const phonePattern = /^\+?[\d\s\-\(\)]+$/; if (!phonePattern.test(value)) return false; // Remove all non-digit characters for length check const digitsOnly = value.replace(/\D/g, ''); return digitsOnly.length >= 10 && digitsOnly.length <= 15; }, defaultMessage: () => validation_constants_1.VALIDATION_MESSAGES.INVALID_PHONE, }, }); }; } /** * Transform to Lowercase Decorator */ function ToLowerCase() { return (0, class_transformer_1.Transform)(({ value }) => { if (typeof value === 'string') { return value.toLowerCase(); } return value; }); } /** * Trim Whitespace Decorator */ function Trim() { return (0, class_transformer_1.Transform)(({ value }) => { if (typeof value === 'string') { return value.trim(); } return value; }); } /** * Transform Date Decorator */ function TransformDate() { return (0, class_transformer_1.Transform)(({ value }) => { if (!value) return value; if (value instanceof Date) return value; if (typeof value === 'string' || typeof value === 'number') { const date = new Date(value); return isNaN(date.getTime()) ? value : date; } return value; }); } /** * Transform Array Decorator */ function TransformArray(options) { const separator = options?.separator || ','; return (0, class_transformer_1.Transform)(({ value }) => { if (Array.isArray(value)) return value; if (typeof value === 'string') { return value.split(separator).map(item => item.trim()).filter(item => item); } return value; }); } /** * Pagination Validation Decorator */ function IsValidPagination(options) { return function (object, propertyName) { (0, class_validator_1.registerDecorator)({ name: 'isValidPagination', target: object.constructor, propertyName: propertyName, options: { message: options?.message || validation_constants_1.VALIDATION_MESSAGES.INVALID_PAGINATION, ...options, }, validator: { validate(value) { if (value === undefined || value === null) return true; const numValue = Number(value); if (isNaN(numValue)) return false; // Check if it's a page number if (propertyName === 'page') { return numValue >= pagination_constants_1.PAGINATION_DEFAULTS.MIN_PAGE; } // Check if it's a limit if (propertyName === 'limit') { return numValue >= pagination_constants_1.PAGINATION_DEFAULTS.MIN_LIMIT && numValue <= pagination_constants_1.PAGINATION_DEFAULTS.MAX_LIMIT; } return true; }, defaultMessage: () => validation_constants_1.VALIDATION_MESSAGES.INVALID_PAGINATION, }, }); }; } /** * Search Validation Decorator */ function IsValidSearch(options) { return function (object, propertyName) { const validateSearch = (value) => { if (!value) return true; // Allow empty values by default if (typeof value !== 'string') return false; const length = value.length; return length >= validation_constants_1.VALIDATION_DEFAULTS.MIN_SEARCH_LENGTH && length <= validation_constants_1.VALIDATION_DEFAULTS.MAX_SEARCH_LENGTH; }; // Implementation would register with class-validator }; } /** * Entity Validation Decorator */ function IsValidEntity(entity_type, options) { return function (object, propertyName) { const validateEntity = (value) => { // Entity-specific validation logic based on entity_type if (!value) return false; // Basic validation - can be extended per entity type return typeof value === 'object' && value.id; }; // Implementation would register with class-validator }; } /** * Standard DTOs with validation */ class PaginationDto { constructor() { this.page = pagination_constants_1.PAGINATION_DEFAULTS.DEFAULT_PAGE; this.limit = pagination_constants_1.PAGINATION_DEFAULTS.DEFAULT_LIMIT; } } exports.PaginationDto = PaginationDto; __decorate([ (0, class_transformer_1.Transform)(({ value }) => parseInt(value)), IsValidPagination(), __metadata("design:type", Number) ], PaginationDto.prototype, "page", void 0); __decorate([ (0, class_transformer_1.Transform)(({ value }) => parseInt(value)), IsValidPagination(), __metadata("design:type", Number) ], PaginationDto.prototype, "limit", void 0); class SearchDto { } exports.SearchDto = SearchDto; __decorate([ IsValidSearch(), __metadata("design:type", String) ], SearchDto.prototype, "search", void 0); __decorate([ IsValidSearch(), __metadata("design:type", Array) ], SearchDto.prototype, "search_fields", void 0); class QueryDto extends PaginationDto { } exports.QueryDto = QueryDto; __decorate([ IsValidSearch(), __metadata("design:type", String) ], QueryDto.prototype, "search", void 0); /** * Validation utilities */ class ValidationUtils { /** * Validate ID format */ static isValidId(id) { return validation_constants_1.VALIDATION_PATTERNS.UUID.test(id) || validation_constants_1.VALIDATION_PATTERNS.OBJECT_ID.test(id) || validation_constants_1.VALIDATION_PATTERNS.NUMERIC.test(id); } /** * Validate email format */ static isValidEmail(email) { return validation_constants_1.VALIDATION_PATTERNS.EMAIL.test(email); } /** * Validate pagination parameters */ static validatePagination(page, limit) { const validPage = Math.max(page || pagination_constants_1.PAGINATION_DEFAULTS.DEFAULT_PAGE, pagination_constants_1.PAGINATION_DEFAULTS.MIN_PAGE); const validLimit = Math.min(Math.max(limit || pagination_constants_1.PAGINATION_DEFAULTS.DEFAULT_LIMIT, pagination_constants_1.PAGINATION_DEFAULTS.MIN_LIMIT), pagination_constants_1.PAGINATION_DEFAULTS.MAX_LIMIT); return { page: validPage, limit: validLimit }; } } exports.ValidationUtils = ValidationUtils;