File

src/notification/notification.service.ts

Index

Properties
Methods

Constructor

constructor(prisma: PrismaService, temporalProvider: TemporalProvider, emailProvider: EmailProvider)
Parameters :
Name Type Optional
prisma PrismaService No
temporalProvider TemporalProvider No
emailProvider EmailProvider No

Methods

Async create
create(createNotificationDto: CreateNotificationDto)

Creates a new notification.

Parameters :
Name Type Optional Description
createNotificationDto CreateNotificationDto No
  • The data transfer object containing the details of the notification to create.

A Promise that resolves to the created notification.

Async createNotification
createNotification(notificationParams: literal type)
Parameters :
Name Type Optional
notificationParams literal type No
Returns : unknown
Async delete
delete(id: number)

Soft deletes a notification by setting its status to 'DELETED'.

Parameters :
Name Type Optional Description
id number No
  • The ID of the notification to delete.

A Promise that resolves to the soft-deleted notification.

Async deliver
deliver(notification: Notification)

Delivers the given notification.

Parameters :
Name Type Optional Description
notification Notification No
  • The notification to deliver.
Returns : Promise<any>

A Promise that resolves to the channel provider response.

Async findAllByUserId
findAllByUserId(userId: string, queryOptions: literal type)

Retrieves notifications for a specific user with pagination.

Parameters :
Name Type Optional Default value Description
userId string No
  • The ID of the user to retrieve notifications for.
queryOptions literal type No {}

A Promise that resolves to an array of notifications for the specified user.

Async findMany
findMany(criteria: Prisma.NotificationWhereInput)

Fetches multiple notifications based on provided criteria.

Parameters :
Name Type Optional Description
criteria Prisma.NotificationWhereInput No

Criteria to filter notifications.

Array of notifications matching the criteria.

Async getNotification
getNotification(notificationId: number)

Finds and returns the notification with the specified ID.

Parameters :
Name Type Optional Description
notificationId number No
  • The ID of the notification to find.

A Promise that resolves to the found notification object.

Async notify
notify(notificationId: number)

Starts the notification delivery workflow.

Parameters :
Name Type Optional Description
notificationId number No
  • The ID of the notification to deliver.
Returns : Promise<any>

Result of temporal workflow runAsync.

Async triggerNotification
triggerNotification(eventConfig: any, projectId: number, entityId: number, eventType: EventType, entityType: NotificationEntityType, event: any, templateVars: any, userIds: string[], extraData?: any)
Parameters :
Name Type Optional
eventConfig any No
projectId number No
entityId number No
eventType EventType No
entityType NotificationEntityType No
event any No
templateVars any No
userIds string[] No
extraData any Yes
Returns : any
Async update
update(id: number, updateNotificationDto: UpdateNotificationDto)

Updates an existing notification.

Parameters :
Name Type Optional Description
id number No
  • The ID of the notification to update.
updateNotificationDto UpdateNotificationDto No
  • The data transfer object containing the updated details of the notification.

A Promise that resolves to the updated notification.

Properties

Public channelProviderMap
Type : object
Default value : { [NotificationChannel.APP]: null, [NotificationChannel.EMAIL]: this.emailProvider, }
Private NOTIFICATION_DELIVERY_WORKFLOW
Type : string
Default value : "deliverNotificationWorkflow"
import { Injectable } from "@nestjs/common";
import { PrismaService } from "../common/prisma/prisma.service";
import { CreateNotificationDto } from "./dto/create-notification.dto";
import { Notification } from "./entities/notification.entity";
import {
  NotificationChannel,
  NotificationEntityType,
  NotificationStatus,
} from "./types/notification.enums";
import { UpdateNotificationDto } from "./dto/update-notification.dto";
import { Prisma } from "@prisma/client";
import { EmailProvider } from "../providers/email/email.provider";
import { TemporalProvider } from "../providers/temporal/temporal.provider";
import {
  getNamesFromEmail,
  getTextFromTemplate,
  getUuidSlug,
} from "../common/helper";
import { EventIdentifiers, EventType } from "../common/events";
import { User } from "../iam";

@Injectable()
export class NotificationService {
  constructor(
    private prisma: PrismaService,
    private readonly temporalProvider: TemporalProvider,
    private emailProvider: EmailProvider,
  ) {}

  private NOTIFICATION_DELIVERY_WORKFLOW = "deliverNotificationWorkflow";

  public channelProviderMap = {
    [NotificationChannel.APP]: null,
    [NotificationChannel.EMAIL]: this.emailProvider,
  };

  /**
   * Creates a new notification.
   *
   * @param {CreateNotificationDto} createNotificationDto - The data transfer object containing the details of the notification to create.
   * @returns {Promise<Notification>} A Promise that resolves to the created notification.
   * @throws {Error} If there are issues during the notification creation process.
   */
  async create(
    createNotificationDto: CreateNotificationDto,
  ): Promise<Notification> {
    const notification = await this.prisma.notification.create({
      data: {
        ...createNotificationDto,
      },
    });
    return new Notification(notification);
  }

  async createNotification(notificationParams: {
    event: EventIdentifiers;
    channel: NotificationChannel;
    eventIdentifierConfig: any;
    user: User;
    eventType: EventType;
    projectId: number;
    templateVars: Record<string, any>;
    entityId: number;
    entityType: NotificationEntityType;
    extraData?: Record<string, any>;
  }) {
    const config =
      notificationParams.eventIdentifierConfig[notificationParams.event];
    const template = config.channelConfig[notificationParams.channel];

    const subject = getTextFromTemplate(
      template.subject,
      notificationParams.templateVars,
    );
    const body = getTextFromTemplate(
      template.body,
      notificationParams.templateVars,
    );

    const notificationData = {
      projectId: notificationParams.projectId,
      subject,
      body,
      severity: config.severity,
      channel: notificationParams.channel,
      userId: notificationParams.user.id,
      entityId: notificationParams.entityId,
      entityType: notificationParams.entityType,
      eventType: notificationParams.eventType,
      eventIdentifier: notificationParams.event,
      extraData: notificationParams.extraData,
    };

    return this.create(notificationData);
  }

  /**
   * Finds and returns the notification with the specified ID.
   * @param {number} notificationId - The ID of the notification to find.
   * @returns {Promise<Notification>} A Promise that resolves to the found notification object.
   * @throws {Error} If there are issues finding the notification.
   */
  async getNotification(notificationId: number): Promise<Notification> {
    const notification = await this.prisma.notification.findFirst({
      where: { id: notificationId },
    });
    return new Notification(notification);
  }

  /**
   * Delivers the given notification.
   * @param {Notification} notification - The notification to deliver.
   * @returns {Promise<any>} A Promise that resolves to the channel provider response.
   * @throws {Error} If there are issues delivering the notification.
   */
  async deliver(notification: Notification): Promise<any> {
    const channelProvider = this.channelProviderMap[notification.channel];
    return channelProvider.deliver({
      to: notification.userId,
      from: notification.notifier ?? undefined,
      subject: notification.subject,
      body: notification.body,
    });
  }

  /**
   * Starts the notification delivery workflow.
   * @param {number} notificationId - The ID of the notification to deliver.
   * @returns {Promise<any>} Result of temporal workflow runAsync.
   * @throws {Error} If there are issues in starting the workflow.
   */
  async notify(notificationId: number): Promise<any> {
    return this.temporalProvider.runAsync(
      this.NOTIFICATION_DELIVERY_WORKFLOW,
      [notificationId],
      process.env.DEFAULT_QUEUE_NAME,
      `workflow-${this.NOTIFICATION_DELIVERY_WORKFLOW}-${getUuidSlug()}`,
    );
  }

  /**
   * Retrieves notifications for a specific user with pagination.
   *
   * @param {string} userId - The ID of the user to retrieve notifications for.
   * @param {number} page - The page number for pagination (default is 1).
   * @param {number} limit - The number of notifications per page (default is 10).
   * @returns {Promise<Notification[]>} A Promise that resolves to an array of notifications for the specified user.
   * @throws {Error} If there are issues during the notification search process.
   */
  async findAllByUserId(
    userId: string,
    queryOptions: {
      pageSize?: number;
      pageOffset?: number;
      startId?: number;
    } = {},
  ): Promise<Notification[]> {
    const pageSize = +queryOptions.pageSize || 30;
    const pageOffset = +queryOptions.pageOffset || 0;
    const findArgs = {
      where: {
        userId: userId,
        channel: { in: [NotificationChannel.APP, NotificationChannel.PING] },
        NOT: {
          OR: [{ status: { equals: NotificationStatus.DELETED } }],
        },
      },
      take: Math.min(100, pageSize), // Limit the number of messages returned
      skip: pageOffset * pageSize, // Offset based on the page number and page size
    };
    const notifications = await this.prisma.notification.findMany({
      orderBy: {
        createdAt: "desc",
      },
      ...findArgs,
    });
    return notifications.map((notification) => new Notification(notification));
  }

  /**
   * Fetches multiple notifications based on provided criteria.
   * @param {Prisma.NotificationWhereInput} criteria Criteria to filter notifications.
   * @returns {Promise<Notification[]>} Array of notifications matching the criteria.
   */
  async findMany(
    criteria: Prisma.NotificationWhereInput,
  ): Promise<Notification[]> {
    return this.prisma.notification.findMany({
      where: criteria,
    });
  }

  /**
   * Updates an existing notification.
   *
   * @param {number} id - The ID of the notification to update.
   * @param {UpdateNotificationDto} updateNotificationDto - The data transfer object containing the updated details of the notification.
   * @returns {Promise<Notification>} A Promise that resolves to the updated notification.
   * @throws {Error} If there are issues during the notification update process.
   */
  async update(
    id: number,
    updateNotificationDto: UpdateNotificationDto,
  ): Promise<Notification> {
    const notification = await this.prisma.notification.update({
      where: { id },
      data: {
        ...updateNotificationDto,
      },
    });

    return new Notification(notification);
  }

  /**
   * Soft deletes a notification by setting its status to 'DELETED'.
   *
   * @param {number} id - The ID of the notification to delete.
   * @returns {Promise<Notification>} A Promise that resolves to the soft-deleted notification.
   * @throws {Error} If there are issues during the notification deletion process.
   */
  async delete(id: number): Promise<Notification> {
    const notification = await this.prisma.notification.update({
      where: { id },
      data: { status: NotificationStatus.DELETED },
    });

    return new Notification(notification);
  }

  async triggerNotification(
    eventConfig: any,
    projectId: number,
    entityId: number,
    eventType: EventType,
    entityType: NotificationEntityType,
    event: any,
    templateVars: any,
    userIds: string[],
    extraData?: any,
  ) {
    const config = eventConfig[event].channelConfig;

    const severity = eventConfig[event].severity;

    const notificationPromises = [];

    for (const channel in config) {
      userIds.forEach((userId) => {
        templateVars["sendToName"] = getNamesFromEmail(userId);
        templateVars["sendToEmail"] = userId;

        const subject = getTextFromTemplate(
          config[channel]["subject"],
          templateVars,
        );
        const body = getTextFromTemplate(config[channel]["body"], templateVars);

        const notificationData = {
          projectId: projectId,
          subject: subject,
          body: body,
          severity: severity,
          channel: NotificationChannel[channel],
          userId: userId,
          entityId: entityId,
          entityType: entityType,
          eventType: eventType,
          eventIdentifier: event,
          extraData: extraData ?? {},
        };

        notificationPromises.push(this.create(notificationData));
      });
    }

    const notifications = (await Promise.all(notificationPromises))
      .flat()
      .flat();

    notifications.map((notification) => {
      if (notification.channel != NotificationChannel.APP) {
        this.notify(notification.id);
      }
    });
  }
}

results matching ""

    No results matching ""