Skip to content

Commit

Permalink
Merge pull request #3 from poojakarma/change_Responses_and_DB_tables_…
Browse files Browse the repository at this point in the history
…names

( PS-1260) Added validation on template and send notification API
  • Loading branch information
vijaykhollam authored Jul 9, 2024
2 parents 48f2fac + 48ac5d1 commit 748b881
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 71 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"dotenv": "^16.0.1",
"fcm-node": "^1.6.1",
"notifme-sdk": "^1.11.0",
"pg": "^8.11.3",
"reflect-metadata": "^0.1.13",
Expand Down
5 changes: 4 additions & 1 deletion src/modules/notification/adapters/smsService.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class SmsAdapter implements NotificationServiceInterface {

private readonly accountSid;
private readonly authToken;
private readonly smsFrom;

constructor(
@Inject(forwardRef(() => NotificationService)) private readonly notificationServices: NotificationService,
Expand All @@ -26,6 +27,8 @@ export class SmsAdapter implements NotificationServiceInterface {
) {
this.accountSid = this.configService.get('TWILIO_ACCOUNT_SID');
this.authToken = this.configService.get('TWILIO_AUTH_TOKEN');
this.smsFrom = this.configService.get('SMS_FROM');

}
async sendNotification(notificationDataArray) {
const results = [];
Expand Down Expand Up @@ -81,7 +84,7 @@ export class SmsAdapter implements NotificationServiceInterface {
const twilio = require('twilio');
const client = twilio(this.accountSid, this.authToken);
const message = await client.messages.create({
from: '+12563056567',
from: `${this.smsFrom}`,
to: `+91${notificationData.recipient}`,
body: notificationData.body,
});
Expand Down
7 changes: 6 additions & 1 deletion src/modules/notification/dto/notificationDto.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,16 @@ export class NotificationDto {
@IsBoolean()
isQueue: boolean;

@ApiProperty({ example: 'EVENT2', description: 'Context of the notification' })
@ApiProperty({ example: 'EVENT', description: 'Context of the notification' })
@IsNotEmpty()
@IsString()
context: string;

@ApiProperty({ example: 'OnAfterAttendeeEnrolled', description: 'Key of the notification' })
@IsNotEmpty()
@IsString()
key: string;

@ApiProperty({ example: ['John Doe', 'How to use UI tools'] })
@IsOptional()
@IsArray()
Expand Down
36 changes: 18 additions & 18 deletions src/modules/notification/notification.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,25 @@ export class NotificationController {
return this.notificationService.sendNotification(notificationDto, response);
}

@Post('subscribetotopic')
@ApiInternalServerErrorResponse({ description: 'Server Error' })
@ApiBadRequestResponse({ description: 'Invalid Request' })
@UsePipes(new ValidationPipe({ transform: true }))
@ApiCreatedResponse({ description: 'Sunscribed the topic' })
@ApiBody({ type: SubscribeToDeviceTopicDto })
async subscribeToDeviceTopic(@Body() requestBody: SubscribeToDeviceTopicDto) {
return await this.notificationService.subscribeToDeviceTopicFromDB(requestBody);
}
// @Post('subscribetotopic')
// @ApiInternalServerErrorResponse({ description: 'Server Error' })
// @ApiBadRequestResponse({ description: 'Invalid Request' })
// @UsePipes(new ValidationPipe({ transform: true }))
// @ApiCreatedResponse({ description: 'Sunscribed the topic' })
// @ApiBody({ type: SubscribeToDeviceTopicDto })
// async subscribeToDeviceTopic(@Body() requestBody: SubscribeToDeviceTopicDto) {
// return await this.notificationService.subscribeToDeviceTopicFromDB(requestBody);
// }

@Post('unsubscribetotopic')
@ApiInternalServerErrorResponse({ description: 'Server Error' })
@ApiBadRequestResponse({ description: 'Invalid Request' })
@UsePipes(new ValidationPipe({ transform: true }))
@ApiCreatedResponse({ description: 'Sunscribed the topic' })
@ApiBody({ type: SubscribeToDeviceTopicDto })
async subscribeToDeviceTopicFromDB(@Body() requestBody: SubscribeToDeviceTopicDto) {
return await this.notificationService.unsubscribeFromTopic(requestBody);
}
// @Post('unsubscribetotopic')
// @ApiInternalServerErrorResponse({ description: 'Server Error' })
// @ApiBadRequestResponse({ description: 'Invalid Request' })
// @UsePipes(new ValidationPipe({ transform: true }))
// @ApiCreatedResponse({ description: 'Sunscribed the topic' })
// @ApiBody({ type: SubscribeToDeviceTopicDto })
// async subscribeToDeviceTopicFromDB(@Body() requestBody: SubscribeToDeviceTopicDto) {
// return await this.notificationService.unsubscribeFromTopic(requestBody);
// }


@Post('sendTopicNotification')
Expand Down
78 changes: 39 additions & 39 deletions src/modules/notification/notification.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ export class NotificationService {
async sendNotification(notificationDto: NotificationDto, response: Response): Promise<APIResponse> {
const apiId = APIID.SEND_NOTIFICATION;
try {
const { email, push, sms, context, replacements } = notificationDto;
const { email, push, sms, context, replacements, key } = notificationDto;
const promises = [];
const notification_event = await this.notificationActions.findOne({ where: { context } });
const notification_event = await this.notificationActions.findOne({ where: { context, key } });

if (!notification_event) {
this.logger.error('/Send Notification', 'Template not found', context);
Expand Down Expand Up @@ -213,45 +213,45 @@ export class NotificationService {
}
}

async subscribeToDeviceTopicFromDB(requestBody: SubscribeToDeviceTopicDto) {
try {
const { deviceId, topicName } = requestBody;
await this.fcm.subscribeToTopic(deviceId, topicName, (err, response) => {
if (err) {
console.error('Error subscribing to topic:', err);
throw err;
}
});
} catch (error) {
this.logger.error(
`Failed to Subscribe to topic ${requestBody.topicName}`,
error,
'/Not able to subscribe to topic',
);
throw error;
}
// async subscribeToDeviceTopicFromDB(requestBody: SubscribeToDeviceTopicDto) {
// try {
// const { deviceId, topicName } = requestBody;
// await this.fcm.subscribeToTopic(deviceId, topicName, (err, response) => {
// if (err) {
// console.error('Error subscribing to topic:', err);
// throw err;
// }
// });
// } catch (error) {
// this.logger.error(
// `Failed to Subscribe to topic ${requestBody.topicName}`,
// error,
// '/Not able to subscribe to topic',
// );
// throw error;
// }

}
// }

async unsubscribeFromTopic(requestBody: SubscribeToDeviceTopicDto) {
try {
const { deviceId, topicName } = requestBody;
if (deviceId.length > 0) {
await this.fcm.unsubscribeToTopic(deviceId, topicName, (err, response) => {
if (err) {
throw err; // Handle the error as needed
}
});
}
} catch (error) {
this.logger.error(
`Failed to UnSubscribe to topic ${requestBody.topicName}`,
error,
'/Not able to Unsubscribe to topic',
);
throw error;
}
}
// async unsubscribeFromTopic(requestBody: SubscribeToDeviceTopicDto) {
// try {
// const { deviceId, topicName } = requestBody;
// if (deviceId.length > 0) {
// await this.fcm.unsubscribeToTopic(deviceId, topicName, (err, response) => {
// if (err) {
// throw err; // Handle the error as needed
// }
// });
// }
// } catch (error) {
// this.logger.error(
// `Failed to UnSubscribe to topic ${requestBody.topicName}`,
// error,
// '/Not able to Unsubscribe to topic',
// );
// throw error;
// }
// }

async sendTopicNotification(requestBody: TopicNotification) {
try {
Expand Down
3 changes: 2 additions & 1 deletion src/modules/notification_events/dto/createTemplate.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class EmailDto {
@IsNotEmpty()
subject: string;

@ApiProperty({ example: 'This is body of Email', description: "Email body" })
@ApiProperty({ example: 'This is body of {#var0#} Notification', description: "Email body" })
@IsString()
@IsNotEmpty()
body: string;
Expand Down Expand Up @@ -87,6 +87,7 @@ export class CreateEventDto {
"description": "Name of Campaign Promoter"
}]
})
@IsOptional()
@ValidateNested({ each: true })
@Type(() => ReplacementTagDto)
@IsArray()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer";
import { IsNotEmpty, IsString, ValidateNested, } from "class-validator";
import { IsNotEmpty, IsOptional, IsString, ValidateNested, } from "class-validator";

export class SearchDto {

@ApiProperty({ example: 'EVENT' })
@IsString()
@IsNotEmpty()
context: string;

@ApiProperty({ example: 'EVENT' })
@IsOptional()
@IsString()
@IsNotEmpty()
key: string;
}

export class SearchFilterDto {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,6 @@ export class UpdateEventDto {
@IsOptional()
replacementTags: ReplacementTagDto[];

@ApiProperty({ example: "en", description: "en" })
@IsString()
@IsNotEmpty()
@IsOptional()
language: string;

@ApiProperty({ example: "published", description: "Status" })
@IsEnum(['published', 'unpublished'], {
message: 'Status must be one of: published, unpublished',
Expand Down
43 changes: 40 additions & 3 deletions src/modules/notification_events/notification_events.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ export class NotificationEventsService {
async createTemplate(userId: string, data: CreateEventDto, response: Response): Promise<Response> {
const apiId = APIID.TEMPLATE_CREATE;
try {

const existingTemplate =
await this.notificationTemplatesRepository.findOne({
where: { context: data.context },
where: { context: data.context, key: data.key },
});
if (existingTemplate) {
this.logger.log(
Expand All @@ -36,6 +37,10 @@ export class NotificationEventsService {
);
throw new BadRequestException('Template Already exist');
}
this.validateConfigBody(data.email);
this.validateConfigBody(data.push);
this.validateConfigBody(data.sms);

const notificationTemplate = new NotificationActions();
notificationTemplate.title = data.title;
notificationTemplate.key = data.key;
Expand Down Expand Up @@ -102,6 +107,12 @@ export class NotificationEventsService {
);
throw new BadRequestException('Template not existing');
}

// Validate email, push, and sms bodies
this.validateConfigBody(updateEventDto.email);
this.validateConfigBody(updateEventDto.push);
this.validateConfigBody(updateEventDto.sms);

Object.assign(existingTemplate, updateEventDto);
const result = await this.notificationTemplatesRepository.save(existingTemplate);
const createConfig = async (type: string, configData?: any) => {
Expand Down Expand Up @@ -165,12 +176,18 @@ export class NotificationEventsService {
}
}



async getTemplatesTypesForEvent(searchFilterDto: SearchFilterDto, response: Response) {
const apiId = APIID.TEMPLATE_LIST
try {
const context = searchFilterDto.filters.context;
const { context, key } = searchFilterDto.filters;
let whereCondition: any = { context };
if (key) {
whereCondition.key = key;
}
const result = await this.notificationTemplatesRepository.find({
where: { context },
where: whereCondition,
relations: ["templateconfig"],
});

Expand Down Expand Up @@ -242,4 +259,24 @@ export class NotificationEventsService {

}


private validateVariables(body: string) {
const variablePattern = /{#var(\d+)#}/g;
const variables = [...body.matchAll(variablePattern)].map(match => parseInt(match[1]));
// Check if variables are sequentially ordered
for (let i = 0; i < variables.length; i++) {
if (variables[i] !== i) {
throw new BadRequestException(`Variables should be in sequential order starting from {#var0#}. Found: {#var${variables[i]}#}`);
}
}
}

private validateConfigBody(config: any) {
if (config && config.body) {
this.validateVariables(config.body);
}
}

}


0 comments on commit 748b881

Please sign in to comment.