Closed kodiidok closed 6 months ago
The folder backend\libs\core\src\entities\dto
contains all the dto files. The notification entities that were created,
needs create.ts
and update.ts
files in order for them to compatible with the notification service. Given below are the dto files for Notification
, and RequestNotification
. Follow the pattern given in them to create the dto files.
import { Field, InputType } from '@nestjs/graphql';
@InputType()
export class CreateNotificationInput {
@Field()
message!: string;
@Field()
senderId!: string;
}
import { Field, InputType } from '@nestjs/graphql';
@InputType()
export class UpdateNotificationInput {
@Field({ nullable: true })
message?: string;
@Field({ nullable: true })
senderId?: string;
}
import { PaginateResult } from './paginate-result.dto';
import { ObjectType } from '@nestjs/graphql';
import { Notification } from '../notification.entity';
@ObjectType()
export class NotificationPage extends PaginateResult(Notification) {}
import { Field, InputType } from '@nestjs/graphql';
import { CreateNotificationInput } from './create.notification';
@InputType()
export class CreateRequestNotificationInput extends CreateNotificationInput {
@Field()
requestId: string;
}
import { Field, InputType } from '@nestjs/graphql';
import { UpdateNotificationInput } from './update.notification';
@InputType()
export class UpdateRequestNotificationInput extends UpdateNotificationInput {
@Field({ nullable: true })
requestId?: string;
}
import { PaginateResult } from './paginate-result.dto';
import { ObjectType } from '@nestjs/graphql';
import { RequestNotification } from '../request-notification.entity';
@ObjectType()
export class RequestNotificationPage extends PaginateResult(RequestNotification) {}
All the CRUD operations related to child entities must be added into the notification.service.ts
file in the backend\libs\core\src\services\
folder.
Given below is the code for the CRUD operations of RequestNotification
child entity. There are 6 methods given for the RequestNotification`. All such 6 methods must be implemented for all the child entities.
NOTE that the RequestNotification has been explicitly injected into the notification service using @InjectRepository()
. Without this, the services will not work and the app will break.
@InjectRepository(RequestNotification)
private readonly requestNotificationRepository: Repository<RequestNotification>,
To inject into the notification service, the notification.module.ts
must have the entity features exposed as follows for the relevant entities. In this instance, note that Notification
, RequestNotification
and NotificationReciver
entities have been exposed by TypeORM.forFeature([])
.
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Notification } from '../entities/notification.entity';
import { RequestNotification } from '../entities/request-notification.entity';
import { NotificationReciever } from '../entities/notification-reciever.entity';
@Injectable()
export class NotificationService {
constructor(
@InjectRepository(Notification)
private readonly notificationRepository: Repository<Notification>,
@InjectRepository(RequestNotification)
private readonly requestNotificationRepository: Repository<RequestNotification>,
@InjectRepository(NotificationReciever)
private readonly notificationRecieverRepository: Repository<NotificationReciever>
) { }
// Request Notifications
async findAllRequestNotifications(skip: number, take: number): Promise<RequestNotification[]> {
const data = await this.requestNotificationRepository.find({
skip,
take,
relations: { recievers: true, request: true },
});
return data;
}
async findRequestNotificationById(id: string): Promise<RequestNotification | null> {
return await this.requestNotificationRepository.findOne({
relations: { recievers: true, request: true },
where: { id },
});
}
async createRequestNotification(input: Partial<RequestNotification>): Promise<RequestNotification | null> {
const requestNotification = this.requestNotificationRepository.create(input);
const createdRequestNotification = await this.requestNotificationRepository.save(requestNotification);
return await this.requestNotificationRepository.findOne({
relations: { recievers: true, request: true },
where: { id: createdRequestNotification.id },
});
}
async updateRequestNotification(id: string, input: Partial<RequestNotification>): Promise<RequestNotification | null> {
const requestNotification = await this.requestNotificationRepository.findOne({
relations: { recievers: true, request: true },
where: { id },
});
// If the request notification doesn't exist, throw NotFoundException
if (!requestNotification) {
throw new NotFoundException(`Request Notification with id ${id} not found`);
}
Object.assign(requestNotification, input);
await this.requestNotificationRepository.save(requestNotification);
return await this.findRequestNotificationById(id);
}
async deleteRequestNotification(id: string): Promise<RequestNotification | null> {
const requestNotification = await this.requestNotificationRepository.findOne({
relations: { recievers: true, request: true },
where: { id },
});
await this.requestNotificationRepository.delete(id);
return requestNotification;
}
async softDeleteRequestNotification(id: string): Promise<RequestNotification | null> {
const requestNotification = await this.requestNotificationRepository.findOne({
relations: { recievers: true, request: true },
where: { id },
});
await this.requestNotificationRepository.softDelete(id);
return requestNotification;
}
}
The notification.service.ts
file has a method named createRequestNotificationWithReceivers()
which handles creating a notification of type RequestNotification
and then assigning the NotificationRecievers
.
async createRequestNotificationWithReceivers(
requestId: string,
senderId: string,
message: string,
sendTo?: string[],
): Promise<RequestNotification| null> {
try {
const notification = await this.createRequestNotification({
requestId: requestId,
message: message,
senderId: senderId,
});
const receivers = [...(sendTo ?? []), senderId].map((receiverId) => ({
isRead: false,
recieverId: receiverId,
notificationId: notification.id,
}));
const createdReceivers = await this.createNotificationReceivers(receivers);
notification.recievers = createdReceivers;
return notification;
} catch (error: any) {
console.error(`Error creating request notification with recievers: ${error.message}`);
return null;
}
}
Such similar functions must be implemented in this file, for all the child entities created.
The notification.resolver.ts
file contains the resolver methods for the notification services. Given below is the code for the notification resolver file with methods for the RequestNotification
entity.
import {
Resolver,
Query,
Mutation,
Args,
ResolveField,
Parent,
Int,
ResolveReference,
} from '@nestjs/graphql';
import { Request } from '../entities/request.entity';
import { NotificationService } from '../services/notifiation.service';
import { Notification } from '../entities/notification.entity';
import { RequestNotification } from '../entities/request-notification.entity';
import { RequestNotificationPage } from '../entities/dto/request-notification-page.dto';
import { CreateRequestNotificationInput } from '../entities/dto/create.request-notification';
import { UpdateRequestNotificationInput } from '../entities/dto/update.request-notification';
@Resolver()
export class NotificationResolver {
constructor(
private readonly notificationService: NotificationService
) { }
// Request Notification
@Query(() => RequestNotification, { name: 'requestNotification' })
async getRequestNotificationById(@Args('id') id: string): Promise<RequestNotification> {
try {
const requestNotification = this.notificationService.findRequestNotificationById(id);
if (!requestNotification) {
throw new Error(`Request Notification with ID ${id} not found`);
}
return requestNotification;
} catch (error: any) {
throw new Error(`Error fetching Request Notification: ${error.message}`);
}
}
@Query(() => RequestNotificationPage, { name: 'requestNotifications' })
async getUsers(
@Args('page', { type: () => Int, defaultValue: 1 }) page: number,
@Args('pageSize', { type: () => Int, defaultValue: 10 }) pageSize: number,
): Promise<RequestNotificationPage> {
try {
const skip = (page - 1) * pageSize;
const requestNotifications = await this.notificationService.findAllRequestNotifications(skip, pageSize);
const requestNotificationPage: RequestNotificationPage = { data: requestNotifications, totalItems: requestNotifications.length };
return requestNotificationPage;
} catch (error: any) {
throw new Error(`Error fetching request notifications: ${error.message}`);
}
}
@Mutation(() => RequestNotification, { name: 'createRequestNotification' })
async createRequestNotification(@Args('input') input: CreateRequestNotificationInput): Promise<RequestNotification | null> {
try {
return await this.notificationService.createRequestNotification(input);
} catch (error: any) {
throw new Error(`Error creating request notification: ${error.message}`);
}
}
@Mutation(() => RequestNotification, { name: 'updateRequestNotification' })
async updateRequestNotification(
@Args('id') id: string,
@Args('input') input: UpdateRequestNotificationInput,
): Promise<RequestNotification | null> {
try {
return await this.notificationService.updateRequestNotification(id, input);
} catch (error: any) {
throw new Error(`Error updating request notification: ${error.message}`);
}
}
@Mutation(() => RequestNotification, { name: 'deleteRequestNotification' })
async deleteRequestNotification(@Args('id') id: string): Promise<RequestNotification | null> {
try {
return await this.notificationService.deleteRequestNotification(id);
} catch (error: any) {
throw new Error(`Error deleting request notification: ${error.message}`);
}
}
@Mutation(() => RequestNotification, { name: 'softDeleteRequestNotification' })
async softDeleteRequestNotification(@Args('id') id: string): Promise<RequestNotification | null> {
try {
return await this.notificationService.softDeleteRequestNotification(id);
} catch (error: any) {
throw new Error(`Error soft-deleting request notification: ${error.message}`);
}
}
}
Such similar methods must be implemented for all the child entities inside this file. Note that, only a single service file, notification.service.ts
has been injected.
Notifications implementation depends on the following methodology.
1. Parent and Child Entities
There are several child entities. The following entities must be creted in the
backend\libs\core\src\entities
foldernotification.entity.ts
(Don't Edit This File)
This is the super class of all notification types. This is a
Entity()
with@TableInheritance()
. Table inheritance is set so that any child entities created will be saved in a single table instead of multiple tables.e.g. If there are
ChildEntity()
such asRequestNotification
,FileNotification
,UserNotification
etc., instead of saving the records in 3 separate tables, all these data will be saved in a singleNotification
table. Thetype
column specifies the type of the notification.request.entity.ts
(Don't Edit This File)
The
RequestNotification
has already been implemented, and the other entities should be implemented in the same way.file path:
backend\libs\core\src\entities\request-notification.entity.ts
The class should be of
@ChildEntity()
instead of@Entity()
.@Entity()
creates a seprate table, where as@ChildEntity()
adds records to parent table.The body of the class can vary based on the notification type. Make sure to map with correct relationships
Make sure to add the reverse map also. In this case, a reverse mapping from
Request
is added.notification-reciever.entity.ts
file path:
backend\libs\core\src\entities\notification-reciever.entity.ts
The keeps track of recievers who recievees notifications. Without this entity, the created notifications will not reach any user or part of the system.
2. Modules for Parent and Child Entities
The following modules must be creted in the
backend\libs\core\src\modules
folder. This way, the entities can be exposed to the Notifications service and the resolver.The
request-notification.module.ts
and thenotification-reiver.module.ts
has already been created.request-notification.module.ts
notification-reciever.module.ts
notification.module.ts
3. Connecting the Notification entities to the Procurement App
procurement-plugin.module.ts
file path:
backend\apps\procurement-plugin\src\procurement-plugin.module.ts
The created child entities and the parent entity must be added into the
entities
section of the TypeORM configurations. This creates the relevant tables and exposes the entities to other entities. Note that, there is also another entity namedNotificationReciever
. This must also be addded.procurement.module.ts
file path: ``
There are no separate
.service.ts
and.resolver.ts
files for each individual child entity. Instead a commonnotitification.service.ts
andnotification.resolver.ts
has already been added and connected to the procurement app.Once all the entities have been created and connected to the procurement app, leave a comment "DONE" to proceed with the next steps.