import { Injectable } from '@nestjs/common'; import { AttributeMasterService } from 'src/module/meta/service/attribute-master.service'; import { AttributeMaster } from '../entity/attribute-master.entity'; import { EntityManager } from 'typeorm'; import { ConfigService } from '@nestjs/config'; interface ValidationError { field: string; message: string; } @Injectable() export class EntityValidationService { constructor( private readonly attributeMasterService: AttributeMasterService, private entityManager: EntityManager, private configService: ConfigService, ) { } schema = this.configService.get('DB_SCHEMA'); /** * Validates required fields based on attribute metadata. */ validateRequiredFields( entityData: Record, attributeData: AttributeMaster[], ): ValidationError[] { const errors: ValidationError[] = []; attributeData .filter((attr) => attr.required) .forEach((attr) => { const value = entityData[attr.attribute_key]; if (!this.hasValidValue(value)) { errors.push({ field: attr.name, message: `Field ${attr.name} is required.`, }); } }); return errors; } /** * Validates uniqueness of fields based on attribute metadata. */ async validateUniqueFields( entityData: Record, attributeData: AttributeMaster[], entityType: string, db_table_name: string, loggedInUser: any, ): Promise { const errors: ValidationError[] = []; const currentId = entityData.id; // assuming 'id' is the primary key for (const attr of attributeData.filter((a) => a.is_unique)) { const value = entityData[attr.attribute_key]; if (this.hasValidValue(value)) { let qb = this.entityManager .createQueryBuilder() .select('*') .from(`${this.schema}.${db_table_name}`, db_table_name) .where(`${db_table_name}.${attr.attribute_key} = :value`, { value }); // Add AND condition for organization_id/level_type/level_id if present if (entityType !== 'ORG' && entityType !== 'ORGP') { const level_type = loggedInUser.level_type; const level_id = loggedInUser.level_id; if (level_type !== undefined && level_type !== null) { qb = qb.andWhere( `${db_table_name}.level_type = :level_type AND ${db_table_name}.level_id = :level_id`, { level_type, level_id, }, ); } } // Skip the current record when checking for uniqueness during update if (currentId) { qb = qb.andWhere(`${db_table_name}.id != :id`, { id: currentId }); } const existing = await qb.limit(1).getRawOne(); if (existing) { errors.push({ field: attr.name, message: `Field ${attr.name} must be unique. Value ${value} already exists.`, }); } } } return errors; } /** * Validates regex patterns for fields based on attribute metadata. */ async validateRegexFields( entityData: Record, attributeData: AttributeMaster[], ): Promise { const errors: ValidationError[] = []; for (const attr of attributeData.filter((a) => a.regex)) { const value = entityData[attr.attribute_key]; if (this.hasValidValue(value)) { const regex = new RegExp(attr.regex); if (!regex.test(value)) { errors.push({ field: attr.name, message: `Field ${attr.name} does not match the required pattern.`, }); } } } return errors; } /** * Validates both required and unique fields for a given entity type. */ async validateEntityData( entityData: Record, entityMaster, loggedInUser: any, ): Promise { const attributes = await this.attributeMasterService.findAttributesByMappedEntityType( entityMaster.mapped_entity_type, loggedInUser, ); const requiredErrors = this.validateRequiredFields(entityData, attributes); const uniqueErrors = await this.validateUniqueFields( entityData, attributes, entityData.entity_type, entityMaster.db_table_name, loggedInUser, ); const regexErros = await this.validateRegexFields(entityData, attributes); return [...requiredErrors, ...uniqueErrors, ...regexErros]; } // async validateExcelEntityData( // entityData: Record, // entityMaster, // ): Promise { // const attributes = // await this.attributeMasterService.findAttributesByMappedEntityType( // entityData.entity_type, // ); // const requiredErrors = this.validateRequiredFields(entityData, attributes); // const regexErros = await this.validateRegexFields(entityData, attributes); // return [...requiredErrors, ...regexErros]; // } async validateImportData( entityData: Record, entityMaster, loggedInUser: any, ): Promise { const attributes = await this.attributeMasterService.findAttributesByMappedEntityType( entityMaster.mapped_entity_type, loggedInUser, ); const requiredErrors = this.validateRequiredFields(entityData, attributes); const regexErros = await this.validateRegexFields(entityData, attributes); return [...requiredErrors, ...regexErros]; } private hasValidValue(value: any): boolean { if (value === null || value === undefined) return false; if (typeof value === 'string' && value.trim() === '') return false; return true; } }