Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "crud-app",
"name": "notification",
"version": "0.0.1",
"description": "",
"author": "",
Expand All @@ -21,26 +21,26 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@golevelup/nestjs-rabbitmq": "^5.3.0",
"@aws-sdk/client-sns": "^3.750.0",
"@nestjs-plus/rabbitmq": "^1.4.4",
"@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.3.0",
"@nestjs/core": "^8.4.7",
"jwt-decode": "^4.0.0",
"@nestjs/platform-express": "^8.0.0",
"@golevelup/nestjs-rabbitmq": "^5.7.0",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/swagger": "^7.3.1",
"@nestjs/typeorm": "^8.1.4",
"@nestjs/typeorm": "^10.0.0",
"@sendgrid/mail": "^8.1.5",
"@types/amqplib": "^0.10.5",
"amqp-connection-manager": "^4.1.14",
"axios": "^1.6.5",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"dotenv": "^16.0.1",
"firebase-admin": "^12.6.0",
"jwt-decode": "^4.0.0",
"notifme-sdk": "^1.11.0",
"pg": "^8.11.3",
"reflect-metadata": "^0.1.13",
"reflect-metadata": "^0.2.2",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"twilio": "^5.0.0-rc.1",
Expand All @@ -51,7 +51,7 @@
"devDependencies": {
"@nestjs/cli": "^8.0.0",
"@nestjs/schematics": "^8.0.0",
"@nestjs/testing": "^8.0.0",
"@nestjs/testing": "^10.0.0",
Comment on lines 52 to +54
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Version mismatch between runtime and dev dependencies

NestJS runtime packages are upgraded to v10, but dev dependencies (@nestjs/cli, @nestjs/schematics) remain at v8. This version mismatch could cause compatibility issues during development and testing.

Update dev dependencies to match:

-"@nestjs/cli": "^8.0.0",
-"@nestjs/schematics": "^8.0.0",
+"@nestjs/cli": "^10.0.0",
+"@nestjs/schematics": "^10.0.0",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@nestjs/cli": "^8.0.0",
"@nestjs/schematics": "^8.0.0",
"@nestjs/testing": "^8.0.0",
"@nestjs/testing": "^10.0.0",
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
🤖 Prompt for AI Agents
In package.json around lines 52 to 54, the dev dependencies @nestjs/cli and
@nestjs/schematics are at version 8 while runtime packages are at version 10,
causing a version mismatch. Update the versions of @nestjs/cli and
@nestjs/schematics to "^10.0.0" to align with the runtime dependencies and
ensure compatibility.

"@types/express": "^4.17.13",
"@types/jest": "27.5.0",
"@types/node": "^16.0.0",
Expand Down
4 changes: 3 additions & 1 deletion src/common/utils/constant.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ export const ERROR_MESSAGES = {
SMS_NOTIFICATION_FAILED: 'Failed to Send SMS Notification',
ALREADY_EXIST_KEY_FOR_CONTEXT: 'Already Exist key with this context',
ALREADY_EXIST_KEY_FOR_CONTEXT_ENTER_ANOTHER: 'Key already exist for this context. Please enter another key',
NOT_EMPTY_SUBJECT_OR_BODY: 'Subject and body cannot be empty.'
NOT_EMPTY_SUBJECT_OR_BODY: 'Subject and body cannot be empty.',
PUSH_ADAPTER_NOT_INITIALIZED: 'PushAdapter is not initialized. Check Firebase configuration.',
EMAIL_ADAPTER_NOT_CONFIGURED: 'EmailAdapter is not configured. Check Email configuration.'
}

export const SMS_PROVIDER = {
Expand Down
73 changes: 63 additions & 10 deletions src/modules/notification/adapters/emailService.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { NotificationLog } from "../entity/notificationLogs.entity";
import { NotificationService } from "../notification.service";
import { LoggerUtil } from "src/common/logger/LoggerUtil";
import { ERROR_MESSAGES, SUCCESS_MESSAGES } from "src/common/utils/constant.util";
import { ConfigService } from "@nestjs/config";
import * as sgMail from '@sendgrid/mail';

/**
* Interface for raw email data
Expand All @@ -24,10 +26,44 @@ export interface RawEmailData {

@Injectable()
export class EmailAdapter implements NotificationServiceInterface {
private provider: 'smtp' | 'sendgrid' = 'smtp';
private isConfigured = false;
constructor(
@Inject(forwardRef(() => NotificationService)) private readonly notificationServices: NotificationService
) { }
@Inject(forwardRef(() => NotificationService)) private readonly notificationServices: NotificationService,
private readonly configService: ConfigService
) {
this.provider = (this.configService.get('EMAIL_PROVIDER') || 'smtp') as 'smtp' | 'sendgrid';
if (this.provider === 'smtp' && this.hasSmtpConfig()) {
this.isConfigured = true;
}

if (this.provider === 'sendgrid' && this.hasSendGridConfig()) {
sgMail.setApiKey(this.configService.get('SENDGRID_API_KEY'));
this.isConfigured = true;
}

if (!this.isConfigured) {
console.warn('EmailAdapter not configured: Missing required settings.');
LoggerUtil.error(ERROR_MESSAGES.EMAIL_ADAPTER_NOT_CONFIGURED);
Comment on lines +46 to +47
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Inconsistent logging approach

Using both console.warn and LoggerUtil.error. Maintain consistency with the logging approach used throughout the application.

-console.warn('EmailAdapter not configured: Missing required settings.');
-LoggerUtil.error(ERROR_MESSAGES.EMAIL_ADAPTER_NOT_CONFIGURED);
+LoggerUtil.warn('EmailAdapter not configured: Missing required settings.');
+LoggerUtil.error(ERROR_MESSAGES.EMAIL_ADAPTER_NOT_CONFIGURED);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.warn('EmailAdapter not configured: Missing required settings.');
LoggerUtil.error(ERROR_MESSAGES.EMAIL_ADAPTER_NOT_CONFIGURED);
LoggerUtil.warn('EmailAdapter not configured: Missing required settings.');
LoggerUtil.error(ERROR_MESSAGES.EMAIL_ADAPTER_NOT_CONFIGURED);
🤖 Prompt for AI Agents
In src/modules/notification/adapters/emailService.adapter.ts at lines 46 to 47,
the code uses both console.warn and LoggerUtil.error for logging, which is
inconsistent with the application's logging approach. Replace the console.warn
call with an appropriate LoggerUtil method (such as LoggerUtil.warn) to maintain
consistent logging practices throughout the codebase.

}
}
Comment on lines +46 to +49
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add guard check for adapter configuration

The adapter continues to work even when not configured properly. Consider adding a guard check in the send methods.

Would you like me to generate code that adds initialization checks to all public methods to prevent operations when the adapter is not properly configured?

🤖 Prompt for AI Agents
In src/modules/notification/adapters/emailService.adapter.ts around lines 46 to
49, the adapter lacks a guard check to prevent operations when it is not
properly configured. Add a guard clause at the start of all public send methods
that checks if the adapter is configured; if not, log an error or warning and
return early to avoid proceeding with sending emails when settings are missing.



private hasSmtpConfig(): boolean {
return (
!!this.configService.get('EMAIL_HOST') &&
!!this.configService.get('EMAIL_PORT') &&
!!this.configService.get('EMAIL_USER') &&
!!this.configService.get('EMAIL_PASS')
);
}

private hasSendGridConfig(): boolean {
return !!(
this.configService.get('SENDGRID_API_KEY') &&
this.configService.get('EMAIL_FROM')
);
}
/**
* Sends notifications using template-based approach
* @param notificationDataArray Array of notification data objects
Expand Down Expand Up @@ -155,7 +191,7 @@ export class EmailAdapter implements NotificationServiceInterface {
email: {
providers: [
{
type: process.env.EMAIL_TYPE,
type: process.env.EMAIL_PROVIDER || 'smtp',
host: process.env.EMAIL_HOST,
port: process.env.EMAIL_PORT,
secure: false,
Expand Down Expand Up @@ -186,17 +222,34 @@ export class EmailAdapter implements NotificationServiceInterface {
*/
async send(notificationData) {
const notificationLogs = this.createNotificationLog(notificationData, notificationData.subject, notificationData.key, notificationData.body, notificationData.recipient);
let result;
try {
const emailConfig = this.getEmailConfig(notificationData.context);
const notifmeSdk = new NotifmeSdk(emailConfig);
const result = await notifmeSdk.send({
email: {
from: emailConfig.email.from,
if (this.provider === 'smtp') {
const emailConfig = this.getEmailConfig(notificationData.context);
const notifmeSdk = new NotifmeSdk(emailConfig);
result = await notifmeSdk.send({
email: {
from: emailConfig.email.from,
to: notificationData.recipient,
subject: notificationData.subject,
html: notificationData.body,
},
});
}
else if (this.provider === 'sendgrid') {
const msg = {
to: notificationData.recipient,
from: this.configService.get('EMAIL_FROM'),
subject: notificationData.subject,
html: notificationData.body,
},
});
};
const sgResponse = await sgMail.send(msg);
result = {
status: sgResponse[0]?.statusCode === 202 ? 'success' : 'error',
id: sgResponse[0]?.headers['x-message-id'] || null,
errors: sgResponse[0]?.statusCode !== 202 ? sgResponse[0]?.body : undefined,
};
}
if (result.status === 'success') {
notificationLogs.status = true;
await this.notificationServices.saveNotificationLogs(notificationLogs);
Expand Down
42 changes: 30 additions & 12 deletions src/modules/notification/adapters/pushService.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,47 @@ import { NotificationLog } from "../entity/notificationLogs.entity";
import { NotificationService } from "../notification.service";
import { ERROR_MESSAGES, SUCCESS_MESSAGES } from "src/common/utils/constant.util";
import { LoggerUtil } from "src/common/logger/LoggerUtil";
@Injectable()
export class PushAdapter implements NotificationServiceInterface {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Missing @Injectable() decorator

The @Injectable() decorator was removed from the PushAdapter class. If this class is still being injected via NestJS DI container, it needs the decorator.


🏁 Script executed:

#!/bin/bash
# Check if PushAdapter is being injected anywhere in the codebase
ast-grep --pattern 'PushAdapter' | head -20
rg -A 5 "PushAdapter" --type ts

Length of output: 6813


Add @Injectable() to PushAdapter for NestJS DI

NestJS requires classes registered in module providers to be decorated with @Injectable() so the DI container can resolve and instantiate them. PushAdapter is listed in both NotificationModule and NotificationQueueModule but is missing this decorator.

• File: src/modules/notification/adapters/pushService.adapter.ts

 import { ConfigService } from '@nestjs/config';
+import { Injectable, Inject, forwardRef } from '@nestjs/common';

+@Injectable()
 export class PushAdapter implements NotificationServiceInterface {
     private readonly fcmurl: string;
     private isInitialized = false;
     constructor(
         @Inject(forwardRef(() => NotificationService)) private readonly notificationServices: NotificationService,
         private readonly configService: ConfigService
     ) { … }

Ensure you import and apply @Injectable() above the class declaration so NestJS DI can wire it up correctly.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export class PushAdapter implements NotificationServiceInterface {
import { ConfigService } from '@nestjs/config';
import { Injectable, Inject, forwardRef } from '@nestjs/common';
@Injectable()
export class PushAdapter implements NotificationServiceInterface {
private readonly fcmurl: string;
private isInitialized = false;
constructor(
@Inject(forwardRef(() => NotificationService))
private readonly notificationServices: NotificationService,
private readonly configService: ConfigService
) { }
}
🤖 Prompt for AI Agents
In src/modules/notification/adapters/pushService.adapter.ts at line 10, the
PushAdapter class lacks the @Injectable() decorator required by NestJS for
dependency injection. Fix this by importing Injectable from '@nestjs/common' and
adding the @Injectable() decorator immediately above the PushAdapter class
declaration.

private readonly fcmurl: string;
private isInitialized = false;
constructor(
@Inject(forwardRef(() => NotificationService)) private readonly notificationServices: NotificationService,
private readonly configService: ConfigService
) {
this.fcmurl = this.configService.get('FCM_URL');
if (this.hasRequiredConfig()) {
//Initialize Firebase Admin SDK with environment variables
const serviceAccount = {
projectId: this.configService.get('FIREBASE_PROJECT_ID'),
clientEmail: this.configService.get('FIREBASE_CLIENT_EMAIL'),
privateKey: this.configService.get('FIREBASE_PRIVATE_KEY').replace(/\\n/g, '\n'), // Replace escaped newlines
};

// Initialize Firebase Admin SDK with environment variables
const serviceAccount = {
projectId: this.configService.get('FIREBASE_PROJECT_ID'),
clientEmail: this.configService.get('FIREBASE_CLIENT_EMAIL'),
privateKey: this.configService.get('FIREBASE_PRIVATE_KEY').replace(/\\n/g, '\n'), // Replace escaped newlines
};

if (admin.apps.length === 0) {
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
if (admin.apps.length === 0) {
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
}
this.isInitialized = true;
}
else {
console.warn('PushAdapter not initialized: Missing Firebase config.');
LoggerUtil.error(ERROR_MESSAGES.PUSH_ADAPTER_NOT_INITIALIZED);
Comment on lines +34 to +35
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Inconsistent logging approach

Using both console.warn and LoggerUtil.error for the same issue. Stick to one logging approach for consistency.

-console.warn('PushAdapter not initialized: Missing Firebase config.');
-LoggerUtil.error(ERROR_MESSAGES.PUSH_ADAPTER_NOT_INITIALIZED);
+LoggerUtil.warn('PushAdapter not initialized: Missing Firebase config.');
+LoggerUtil.error(ERROR_MESSAGES.PUSH_ADAPTER_NOT_INITIALIZED);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.warn('PushAdapter not initialized: Missing Firebase config.');
LoggerUtil.error(ERROR_MESSAGES.PUSH_ADAPTER_NOT_INITIALIZED);
LoggerUtil.warn('PushAdapter not initialized: Missing Firebase config.');
LoggerUtil.error(ERROR_MESSAGES.PUSH_ADAPTER_NOT_INITIALIZED);
🤖 Prompt for AI Agents
In src/modules/notification/adapters/pushService.adapter.ts around lines 34 to
35, the code uses both console.warn and LoggerUtil.error to log the same issue,
which is inconsistent. Choose one logging method, preferably LoggerUtil.error
for error-level logs, and remove the other to maintain consistent logging
throughout the file.

}
}

private hasRequiredConfig(): boolean {
return !!(
this.configService.get('FIREBASE_PROJECT_ID') &&
this.configService.get('FIREBASE_CLIENT_EMAIL') &&
this.configService.get('FIREBASE_PRIVATE_KEY')
);
}
async sendNotification(notificationDataArray) {
if (!this.isInitialized) {
LoggerUtil.error(ERROR_MESSAGES.PUSH_ADAPTER_NOT_INITIALIZED);
throw new Error('PushAdapter is not initialized. Check Firebase configuration.');
}
const results = [];
for (const notificationData of notificationDataArray) {
try {
Expand Down
2 changes: 1 addition & 1 deletion src/modules/notification/notification.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Response } from "express";
import { NotificationActions } from "../notification_events/entity/notificationActions.entity";
import { NotificationActionTemplates } from "../notification_events/entity/notificationActionTemplates.entity";
import { NotificationQueue } from "../notification-queue/entities/notificationQueue.entity";
import { AmqpConnection, RabbitSubscribe } from "@nestjs-plus/rabbitmq";
import { AmqpConnection, RabbitSubscribe } from "@golevelup/nestjs-rabbitmq";
import { NotificationQueueService } from "../notification-queue/notificationQueue.service";
import { APIID } from "src/common/utils/api-id.config";
import {EmailAdapter} from "src/modules/notification/adapters/emailService.adapter";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,12 @@ export class NotificationActionTemplates {
@Column({ type: 'uuid' })
createdBy: string;

@Column()
@Column({ nullable: true })
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

LGTM! Schema changes add appropriate flexibility.

Making the image and link columns nullable is a sensible change since not all notification templates require these fields.

Ensure that a database migration is created for this schema change to avoid runtime issues:

Also applies to: 34-34


🏁 Script executed:

#!/bin/bash
# Description: Check if database migration files exist for this schema change
# Expected: Migration files should exist for nullable column changes

echo "Checking for recent migration files..."
fd -t f -e ts -e js migration | head -10

echo "Checking for nullable column changes in migrations..."
rg -A 3 -B 3 "nullable.*true" --type ts

Length of output: 7478


Add missing database migration for nullable columns

The schema changes to make image and link nullable in NotificationActionTemplates aren’t reflected in any migration file. Without a migration, deployments will fail when syncing the database.

Action items:

  • Create and commit a migration (e.g. with TypeORM CLI) that alters the notification_action_templates table:
    • ALTER the image column to allow NULL
    • ALTER the link column to allow NULL
  • Verify the generated migration includes both up and down methods for these changes.

Files needing attention:

  • src/modules/notification_events/entity/notificationActionTemplates.entity.ts (columns at lines 31 & 34)
  • New migration file under your project’s migrations directory
🤖 Prompt for AI Agents
In src/modules/notification_events/entity/notificationActionTemplates.entity.ts
at lines 31 and 34, the columns `image` and `link` were updated to be nullable
but no migration was created. Use the TypeORM CLI to generate a new migration
that alters the `notification_action_templates` table to make these two columns
nullable. Ensure the migration file includes both `up` and `down` methods to
apply and revert these changes, then commit the migration file along with the
updated entity.

image: string;

@Column()
@Column({ nullable: true })
link: string;


@Column({ type: 'uuid', nullable: true })
updatedBy: string;

Expand Down
2 changes: 1 addition & 1 deletion src/modules/rabbitmq/rabbitmq.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Module } from '@nestjs/common';
import { RabbitMQModule } from '@nestjs-plus/rabbitmq';
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
import { ConfigService } from '@nestjs/config';

@Module({
Expand Down