Skip to content

Commit e4a228e

Browse files
authored
Merge pull request #3411 from bluewave-labs/fix/notification-controller-validation
fix: implement NotificationController missing validation
2 parents 842fa92 + 76f5508 commit e4a228e

File tree

11 files changed

+60
-68
lines changed

11 files changed

+60
-68
lines changed

server/src/controllers/notificationController.ts

Lines changed: 30 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
11
import { Request, Response, NextFunction } from "express";
22

3-
import { Notification } from "@/types/index.js";
4-
import { createNotificationBodyValidation } from "@/validation/notificationValidation.js";
3+
import {
4+
createNotificationBodyValidation,
5+
deleteNotificationParamValidation,
6+
getNotificationByIdParamValidation,
7+
testNotificationBodyValidation,
8+
editNotificationParamValidation,
9+
testAllNotificationsBodyValidation,
10+
} from "@/validation/notificationValidation.js";
511
import { AppError } from "@/utils/AppError.js";
612
import { IMonitorsRepository } from "@/repositories/index.js";
713
import { INotificationsService } from "@/service/index.js";
8-
import { requireTeamId } from "./controllerUtils.js";
14+
import { requireTeamId, requireUserId } from "./controllerUtils.js";
915

1016
const SERVICE_NAME = "NotificationController";
1117

1218
class NotificationController {
13-
static SERVICE_NAME = SERVICE_NAME;
1419
private notificationsService: INotificationsService;
1520
private monitorsRepository: IMonitorsRepository;
1621
constructor(notificationsService: INotificationsService, monitorsRepository: IMonitorsRepository) {
1722
this.notificationsService = notificationsService;
1823
this.monitorsRepository = monitorsRepository;
1924
}
2025

21-
get serviceName() {
22-
return NotificationController.SERVICE_NAME;
23-
}
24-
2526
testNotification = async (req: Request, res: Response, next: NextFunction) => {
2627
try {
27-
const notification: Notification = req.body;
28+
const notification = testNotificationBodyValidation.parse(req.body);
2829
const success = await this.notificationsService.sendTestNotification(notification);
2930

3031
if (!success) {
@@ -34,6 +35,7 @@ class NotificationController {
3435
return res.status(200).json({
3536
success: true,
3637
msg: "Notification sent successfully",
38+
details: { service: SERVICE_NAME },
3739
});
3840
} catch (error) {
3941
next(error);
@@ -42,23 +44,12 @@ class NotificationController {
4244

4345
createNotification = async (req: Request, res: Response, next: NextFunction) => {
4446
try {
45-
createNotificationBodyValidation.parse(req.body);
46-
47-
const body = req.body;
48-
49-
const teamId = req?.user?.teamId;
50-
if (!teamId) {
51-
throw new AppError({ message: "Team ID is required", status: 400 });
52-
}
47+
const validatedBody = createNotificationBodyValidation.parse(req.body);
5348

54-
const userId = req?.user?.id;
55-
if (!userId) {
56-
throw new AppError({ message: "User ID is required", status: 400 });
57-
}
58-
body.userId = userId;
59-
body.teamId = teamId;
49+
const teamId = requireTeamId(req.user?.teamId);
50+
const userId = requireUserId(req.user?.id);
6051

61-
const notification = await this.notificationsService.createNotification(body);
52+
const notification = await this.notificationsService.createNotification(validatedBody, userId, teamId);
6253
return res.status(200).json({
6354
success: true,
6455
msg: "Notification created successfully",
@@ -71,11 +62,7 @@ class NotificationController {
7162

7263
getNotificationsByTeamId = async (req: Request, res: Response, next: NextFunction) => {
7364
try {
74-
const teamId = req?.user?.teamId;
75-
if (!teamId) {
76-
throw new AppError({ message: "Team ID is required", status: 400 });
77-
}
78-
65+
const teamId = requireTeamId(req.user?.teamId);
7966
const notifications = await this.notificationsService.findNotificationsByTeamId(teamId);
8067

8168
return res.status(200).json({
@@ -90,17 +77,10 @@ class NotificationController {
9077

9178
deleteNotification = async (req: Request, res: Response, next: NextFunction) => {
9279
try {
93-
const teamId = req?.user?.teamId;
94-
if (!teamId) {
95-
throw new AppError({ message: "Team ID is required", status: 400 });
96-
}
97-
98-
const notificationId = req.params.id as string;
99-
if (!notificationId) {
100-
throw new AppError({ message: "Notification ID is required", status: 400 });
101-
}
80+
const teamId = requireTeamId(req.user?.teamId);
81+
const validatedParams = deleteNotificationParamValidation.parse(req.params);
10282

103-
await this.notificationsService.deleteById(notificationId, teamId);
83+
await this.notificationsService.deleteById(validatedParams.id, teamId);
10484
return res.status(200).json({
10585
success: true,
10686
msg: "Notification deleted successfully",
@@ -113,12 +93,9 @@ class NotificationController {
11393
getNotificationById = async (req: Request, res: Response, next: NextFunction) => {
11494
try {
11595
const teamId = requireTeamId(req.user?.teamId);
116-
const notificationId = req.params.id as string;
117-
if (!notificationId) {
118-
throw new AppError({ message: "Notification ID is required", status: 400 });
119-
}
96+
const validatedParams = getNotificationByIdParamValidation.parse(req.params);
12097

121-
const notification = await this.notificationsService.findById(notificationId, teamId);
98+
const notification = await this.notificationsService.findById(validatedParams.id, teamId);
12299

123100
return res.status(200).json({
124101
success: true,
@@ -132,14 +109,13 @@ class NotificationController {
132109

133110
editNotification = async (req: Request, res: Response, next: NextFunction) => {
134111
try {
135-
createNotificationBodyValidation.parse(req.body);
112+
const validatedBody = createNotificationBodyValidation.parse(req.body);
113+
const validatedParams = editNotificationParamValidation.parse(req.params);
136114

137115
const teamId = requireTeamId(req.user?.teamId);
138-
const notificationId = req.params.id as string;
139-
if (!notificationId) {
140-
throw new AppError({ message: "Notification ID is required", status: 400 });
141-
}
142-
const editedNotification = await this.notificationsService.updateById(notificationId, teamId, req.body);
116+
const notificationId = validatedParams.id;
117+
118+
const editedNotification = await this.notificationsService.updateById(notificationId, teamId, validatedBody);
143119
return res.status(200).json({
144120
success: true,
145121
msg: "Notification updated successfully",
@@ -152,13 +128,11 @@ class NotificationController {
152128

153129
testAllNotifications = async (req: Request, res: Response, next: NextFunction) => {
154130
try {
155-
const monitorId = req.body.monitorId;
156-
const teamId = req?.user?.teamId;
157-
if (!teamId) {
158-
throw new AppError({ message: "Team ID is required", status: 400 });
159-
}
131+
const validatedBody = testAllNotificationsBodyValidation.parse(req.body);
132+
133+
const teamId = requireTeamId(req.user?.teamId);
160134

161-
const monitor = await this.monitorsRepository.findById(monitorId, teamId);
135+
const monitor = await this.monitorsRepository.findById(validatedBody.monitorId, teamId);
162136

163137
const notifications = monitor.notifications;
164138
if (notifications.length === 0) {

server/src/service/infrastructure/notificationProviders/INotificationProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ import type { NotificationMessage } from "@/types/notificationMessage.js";
33

44
export interface INotificationProvider {
55
sendMessage: (notification: Notification, message: NotificationMessage) => Promise<boolean>;
6-
sendTestAlert(notification: Notification): Promise<boolean>;
6+
sendTestAlert(notification: Partial<Notification>): Promise<boolean>;
77
}

server/src/service/infrastructure/notificationProviders/discord.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class DiscordProvider implements INotificationProvider {
1313
this.logger = logger;
1414
}
1515

16-
sendTestAlert = async (notification: Notification) => {
16+
sendTestAlert = async (notification: Partial<Notification>) => {
1717
if (!notification.address) {
1818
return false;
1919
}

server/src/service/infrastructure/notificationProviders/email.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export class EmailProvider implements INotificationProvider {
1414
this.logger = logger;
1515
}
1616

17-
async sendTestAlert(notification: Notification): Promise<boolean> {
17+
async sendTestAlert(notification: Partial<Notification>): Promise<boolean> {
1818
const subject = "Test notification";
1919
const html = await buildTestEmail(this.emailService);
2020

server/src/service/infrastructure/notificationProviders/matrix.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class MatrixProvider implements INotificationProvider {
1313
this.logger = logger;
1414
}
1515

16-
sendTestAlert = async (notification: Notification) => {
16+
sendTestAlert = async (notification: Partial<Notification>) => {
1717
const { homeserverUrl, accessToken, roomId } = notification;
1818
if (!homeserverUrl || !accessToken || !roomId) {
1919
return false;

server/src/service/infrastructure/notificationProviders/pagerduty.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export class PagerDutyProvider implements INotificationProvider {
1414
this.logger = logger;
1515
}
1616

17-
async sendTestAlert(notification: Notification): Promise<boolean> {
17+
async sendTestAlert(notification: Partial<Notification>): Promise<boolean> {
1818
try {
1919
await got.post("https://events.pagerduty.com/v2/enqueue", {
2020
json: {

server/src/service/infrastructure/notificationProviders/slack.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class SlackProvider implements INotificationProvider {
1313
this.logger = logger;
1414
}
1515

16-
async sendTestAlert(notification: Notification): Promise<boolean> {
16+
async sendTestAlert(notification: Partial<Notification>): Promise<boolean> {
1717
if (!notification.address) {
1818
return false;
1919
}

server/src/service/infrastructure/notificationProviders/teams.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export class TeamsProvider implements INotificationProvider {
6565
this.logger = logger;
6666
}
6767

68-
async sendTestAlert(notification: Notification): Promise<boolean> {
68+
async sendTestAlert(notification: Partial<Notification>): Promise<boolean> {
6969
if (!notification.address) {
7070
return false;
7171
}

server/src/service/infrastructure/notificationProviders/webhook.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export class WebhookProvider implements INotificationProvider {
9898
};
9999
}
100100

101-
sendTestAlert = async (notification: Notification) => {
101+
sendTestAlert = async (notification: Partial<Notification>) => {
102102
if (!notification.address) {
103103
return false;
104104
}

server/src/service/infrastructure/notificationsService.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import { ILogger } from "@/utils/logger.js";
88
import type { INotificationMessageBuilder } from "@/service/infrastructure/notificationMessageBuilder.js";
99

1010
export interface INotificationsService {
11-
createNotification: (notificationData: Partial<Notification>) => Promise<Notification>;
11+
createNotification: (notificationData: Partial<Notification>, userId: string, teamId: string) => Promise<Notification>;
1212
findById: (id: string, teamId: string) => Promise<Notification>;
1313
findNotificationsByTeamId: (teamId: string) => Promise<Notification[]>;
1414
updateById(id: string, teamId: string, updateData: Partial<Notification>): Promise<Notification>;
1515
deleteById: (id: string, teamId: string) => Promise<Notification>;
1616
handleNotifications: (monitor: Monitor, monitorStatusResponse: MonitorStatusResponse, decision: MonitorActionDecision) => Promise<boolean>;
1717

18-
sendTestNotification: (notification: Notification) => Promise<boolean>;
18+
sendTestNotification: (notification: Partial<Notification>) => Promise<boolean>;
1919
testAllNotifications: (notificationIds: string[]) => Promise<boolean>;
2020
}
2121

@@ -141,7 +141,7 @@ export class NotificationsService implements INotificationsService {
141141
return await this.sendNotifications(monitor, monitorStatusResponse, decision);
142142
};
143143

144-
sendTestNotification = async (notification: Notification) => {
144+
sendTestNotification = async (notification: Partial<Notification>) => {
145145
switch (notification.type) {
146146
case "email":
147147
return await this.emailProvider.sendTestAlert(notification);
@@ -174,7 +174,9 @@ export class NotificationsService implements INotificationsService {
174174
return true;
175175
};
176176

177-
createNotification = async (notificationData: Partial<Notification>): Promise<Notification> => {
177+
createNotification = async (notificationData: Partial<Notification>, userId: string, teamId: string): Promise<Notification> => {
178+
notificationData.userId = userId;
179+
notificationData.teamId = teamId;
178180
return await this.notificationsRepository.create(notificationData);
179181
};
180182

0 commit comments

Comments
 (0)