diff --git a/src/modules/notification/dto/notificationDto.dto.ts b/src/modules/notification/dto/notificationDto.dto.ts index 0ea0bc9..3ba0b2b 100644 --- a/src/modules/notification/dto/notificationDto.dto.ts +++ b/src/modules/notification/dto/notificationDto.dto.ts @@ -1,6 +1,6 @@ import { IsString, IsEmail, IsArray, ValidateNested, IsObject, IsNotEmpty, ArrayMinSize, IsBoolean, IsOptional } from 'class-validator'; import { Type } from 'class-transformer'; -import { ApiProperty } from '@nestjs/swagger'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; export class EmailDTO { @@ -49,11 +49,24 @@ export class NotificationDto { @IsString() key: string; - @ApiProperty({ example: ['John Doe', 'How to use UI tools'] }) + // @ApiProperty({ example: ['John Doe', 'How to use UI tools'] }) + // @IsOptional() + // @IsArray() + // @ArrayMinSize(1) + // replacements: string[]; + @ApiPropertyOptional({ + description: 'Dynamic replacements for template tags', + example: { + "{eventName}": "How to use UI tools", + "{userName}": "John Doe", + "{courseTitle}": "How to use UI tools", + "{contactEmail}": "support@example.com" + } + }) @IsOptional() - @IsArray() - @ArrayMinSize(1) - replacements: string[]; + @IsObject() + // @ValidateReplacement() // Custom decorator to ensure at least one replacement + replacements?: { [key: string]: string }; @ApiProperty({ type: EmailDTO, description: 'Email notification details' }) @ValidateNested() diff --git a/src/modules/notification/notification.service.ts b/src/modules/notification/notification.service.ts index e5b98ef..d6b1546 100644 --- a/src/modules/notification/notification.service.ts +++ b/src/modules/notification/notification.service.ts @@ -73,23 +73,44 @@ export class NotificationService { } const results = await Promise.allSettled(promises); - const serverResponses = results.map((result) => { + // const serverResponses = results.map((result) => { + // if (result.status === 'fulfilled') { + // return { + // data: result.value + // }; + // } else { + // return { + // error: result.reason?.message, + // code: result.reason?.status + // }; + // } + // }); + const serverResponses = { + email: { data: [], errors: [] }, + sms: { data: [], errors: [] }, + push: { data: [], errors: [] } + }; + + results.forEach((result, index) => { + const channel = ['email', 'sms', 'push'][index]; if (result.status === 'fulfilled') { - return { - data: result.value - }; + serverResponses[channel].data.push(result.value); } else { - return { + serverResponses[channel].errors.push({ error: result.reason?.message, code: result.reason?.status - }; + }); } }); + // Filter out channels with empty data and errors arrays + const finalResponses = Object.fromEntries( + Object.entries(serverResponses).filter(([channel, { data, errors }]) => data.length > 0 || errors.length > 0 || errors.length > 0) + ); return APIResponse.success( response, apiId, - serverResponses, + finalResponses, HttpStatus.OK, 'Notification process completed' ); @@ -112,25 +133,32 @@ export class NotificationService { this.logger.error(`/Send ${channel} Notification`, `Template Config not found for this context: ${notificationDto.context}`, 'Not Found'); throw new BadRequestException(`Notification template config not defined for ${type}`); } - - let bodyText = notification_details[0].body; - const placeholders = (bodyText.match(/\{#var\d+#\}/g) || []).length; - if (!Array.isArray(replacements)) { - replacements = []; // Assuming default behavior if replacements is not provided - } - if (placeholders !== replacements.length) { - throw new BadRequestException(`Mismatch between placeholders and replacements: ${placeholders} placeholders and ${replacements.length} replacements.`); + let bodyText; + let subject; + bodyText = notification_details[0].body; + subject = notification_details[0].subject; + + // Ensure replacements are in the correct format + if (typeof replacements !== 'object' || replacements === null) { + replacements = {}; } - if (replacements && replacements.length > 0) { - replacements.forEach((replacement, index) => { - bodyText = bodyText.replace(`{#var${index}#}`, replacement); - }); - } + // Extract placeholders from the templates + const subjectPlaceholders = this.extractPlaceholders(subject); + const bodyPlaceholders = this.extractPlaceholders(bodyText); + + // Validate that all placeholders are present in the replacements object + this.validatePlaceholders([...subjectPlaceholders, ...bodyPlaceholders], replacements); + + + // Replace placeholders in subject and bodyText + subject = this.replacePlaceholders(subject, replacements); + bodyText = this.replacePlaceholders(bodyText, replacements); const notificationDataArray = recipients.map(recipient => { return { - subject: notification_details[0].subject, + // subject: notification_details[0].subject, + subject: subject, body: bodyText, recipient: recipient, key: notification_event.key, @@ -158,6 +186,33 @@ export class NotificationService { } }; + replacePlaceholders(template, replacements) { + return template.replace(/{(\w+)}/g, (match, key) => { + return replacements[match] || match; // Replace with the value or keep the placeholder + }); + } + + // Function to extract placeholders from the template + extractPlaceholders(template: string): string[] { + const regex = /{(\w+)}/g; + const matches = []; + let match; + while ((match = regex.exec(template)) !== null) { + matches.push(match[0]); + } + return matches; + } + + // Function to validate that all placeholders have corresponding replacements + validatePlaceholders(placeholders: string[], replacements: { [key: string]: string }): void { + const missingReplacements = placeholders.filter((placeholder) => !replacements.hasOwnProperty(placeholder)); + + if (missingReplacements.length > 0) { + throw new BadRequestException(`Missing replacements for placeholders: ${missingReplacements.join(', ')}`); + } + } + + //Provider which store in Queue async saveNotificationQueue(notificationDataArray) { @@ -299,7 +354,6 @@ export class NotificationService { } } - // // proper working function // async sendWhatsappMessage( // notificationData: NotificationWhatsapp,