Review fixes

This commit is contained in:
x032205
2025-09-05 22:03:57 -04:00
parent 3350121635
commit ad42fb4721
12 changed files with 226 additions and 169 deletions
@@ -69,7 +69,7 @@ type TSecretApprovalRequestServiceFactoryDep = {
projectSlackConfigDAL: Pick<TProjectSlackConfigDALFactory, "getIntegrationDetailsByProject">;
microsoftTeamsService: Pick<TMicrosoftTeamsServiceFactory, "sendNotification">;
projectMicrosoftTeamsConfigDAL: Pick<TProjectMicrosoftTeamsConfigDALFactory, "getIntegrationDetailsByProject">;
notificationService: Pick<TNotificationServiceFactory, "createUserNotification">;
notificationService: Pick<TNotificationServiceFactory, "createUserNotifications">;
};
export const accessApprovalRequestServiceFactory = ({
@@ -279,15 +279,15 @@ export const accessApprovalRequestServiceFactory = ({
}
});
for await (const approver of approverUsers) {
await notificationService.createUserNotification({
await notificationService.createUserNotifications(
approverUsers.map((approver) => ({
userId: approver.id,
type: NotificationType.ACCESS_APPROVAL_REQUEST,
title: "Access Approval Request",
body: `**${requesterFullName}** (${requestedByUser.email}) has requested ${isTemporary ? "temporary" : "permanent"} access to **${secretPath}** in the **${envSlug}** environment for project **${project.name}**.`,
link: approvalPath
});
}
}))
);
await smtpService.sendMail({
recipients: approverUsers.filter((approver) => approver.email).map((approver) => approver.email!),
@@ -406,7 +406,8 @@ export const accessApprovalRequestServiceFactory = ({
const requesterFullName = `${requestedByUser.firstName} ${requestedByUser.lastName}`;
const editorFullName = `${editedByUser.firstName} ${editedByUser.lastName}`;
const approvalUrl = `${cfg.SITE_URL}/projects/secret-management/${project.id}/approval`;
const approvalPath = `/projects/secret-management/${project.id}/approval`;
const approvalUrl = `${cfg.SITE_URL}${approvalPath}`;
await triggerWorkflowIntegrationNotification({
input: {
@@ -437,27 +438,43 @@ export const accessApprovalRequestServiceFactory = ({
}
});
await smtpService.sendMail({
recipients: policy.approvers
.filter((approver) => Boolean(approver.email) && approver.userId !== editedByUser.id)
.map((approver) => approver.email!),
subjectLine: "Access Approval Request Updated",
substitutions: {
projectName: project.name,
requesterFullName,
requesterEmail: requestedByUser.email,
isTemporary: true,
expiresIn: msFn(ms(temporaryRange || ""), { long: true }),
secretPath,
environment: envSlug,
permissions: accessTypes,
approvalUrl,
editNote,
editorFullName,
editorEmail: editedByUser.email
},
template: SmtpTemplates.AccessApprovalRequestUpdated
});
await notificationService.createUserNotifications(
policy.approvers
.filter((approver) => Boolean(approver.userId) && approver.userId !== editedByUser.id)
.map((approver) => ({
userId: approver.userId!,
type: NotificationType.ACCESS_APPROVAL_REQUEST_UPDATED,
title: "Access Approval Request Updated",
body: `**${editorFullName}** (${editedByUser.email}) has updated the access request submitted by **${requesterFullName}** (${requestedByUser.email}) for **${secretPath}** in the **${envSlug}** environment for project **${project.name}**.`,
link: approvalPath
}))
);
const recipients = policy.approvers
.filter((approver) => Boolean(approver.email) && approver.userId !== editedByUser.id)
.map((approver) => approver.email!);
if (recipients.length > 0) {
await smtpService.sendMail({
recipients,
subjectLine: "Access Approval Request Updated",
substitutions: {
projectName: project.name,
requesterFullName,
requesterEmail: requestedByUser.email,
isTemporary: true,
expiresIn: msFn(ms(temporaryRange || ""), { long: true }),
secretPath,
environment: envSlug,
permissions: accessTypes,
approvalUrl,
editNote,
editorFullName,
editorEmail: editedByUser.email
},
template: SmtpTemplates.AccessApprovalRequestUpdated
});
}
return approvalRequest;
});
+1 -1
View File
@@ -318,7 +318,7 @@ export type TQueueJobTypes = {
};
[QueueName.UserNotification]: {
name: QueueJobs.UserNotification;
payload: TCreateUserNotificationDTO;
payload: { notifications: TCreateUserNotificationDTO[] };
};
};
@@ -64,15 +64,23 @@ export const registerNotificationRouter = async (server: FastifyZodProvider) =>
});
server.route({
url: "/user/:notificationId/mark-as-read",
url: "/user/:notificationId",
config: {
rateLimit: writeLimit
},
method: "POST",
method: "PATCH",
schema: {
params: z.object({
notificationId: z.string()
})
}),
body: z.object({
isRead: z.boolean()
}),
response: {
200: z.object({
notification: UserNotificationsSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
@@ -80,10 +88,13 @@ export const registerNotificationRouter = async (server: FastifyZodProvider) =>
throw new UnauthorizedError({ message: "This endpoint can only be accessed by users" });
}
await server.services.notification.markUserNotificationAsRead({
const notification = await server.services.notification.updateUserNotification({
notificationId: req.params.notificationId,
userId: req.auth.userId
userId: req.auth.userId,
...req.body
});
return { notification };
}
});
@@ -4,43 +4,36 @@ import { TCreateUserNotificationDTO } from "./notification-types";
import { TUserNotificationDALFactory } from "./user-notification-dal";
type TNotificationQueueServiceFactoryDep = {
userNotificationDAL: Pick<TUserNotificationDALFactory, "create">;
userNotificationDAL: Pick<TUserNotificationDALFactory, "batchInsert">;
queueService: TQueueServiceFactory;
};
export type TNotificationQueueServiceFactory = {
pushUserNotification: (data: TCreateUserNotificationDTO) => Promise<void>;
pushUserNotifications: (data: TCreateUserNotificationDTO[]) => Promise<void>;
};
export const notificationQueueServiceFactory = async ({
userNotificationDAL,
queueService
}: TNotificationQueueServiceFactoryDep): Promise<TNotificationQueueServiceFactory> => {
const pushUserNotification = async (data: TCreateUserNotificationDTO) => {
await queueService.queuePg(QueueJobs.UserNotification, data);
const pushUserNotifications = async (data: TCreateUserNotificationDTO[]) => {
await queueService.queuePg(QueueJobs.UserNotification, { notifications: data });
};
await queueService.startPg(
QueueJobs.UserNotification,
async ([job]) => {
const { userId, type, title, body, link } = job.data as TCreateUserNotificationDTO;
await userNotificationDAL.create({
userId,
type,
title,
body,
link
});
const { notifications } = job.data as { notifications: TCreateUserNotificationDTO[] };
await userNotificationDAL.batchInsert(notifications);
},
{
batchSize: 100,
workerCount: 5,
pollingIntervalSeconds: 2
batchSize: 1,
workerCount: 2,
pollingIntervalSeconds: 1
}
);
return {
pushUserNotification
pushUserNotifications
};
};
@@ -29,8 +29,8 @@ export const notificationServiceFactory = ({
return notifications;
};
const createUserNotification = async (data: TCreateUserNotificationDTO) => {
return notificationQueue.pushUserNotification(data);
const createUserNotifications = async (data: TCreateUserNotificationDTO[]) => {
return notificationQueue.pushUserNotifications(data);
};
const deleteUserNotification = async ({ userId, notificationId }: { userId: string; notificationId: string }) => {
@@ -47,23 +47,34 @@ export const notificationServiceFactory = ({
await userNotificationDAL.markAllNotificationsAsRead(userId);
};
const markUserNotificationAsRead = async ({ userId, notificationId }: { userId: string; notificationId: string }) => {
await userNotificationDAL.update(
const updateUserNotification = async ({
userId,
notificationId,
isRead
}: {
userId: string;
notificationId: string;
isRead: boolean;
}) => {
const [updatedNotification] = await userNotificationDAL.update(
{
id: notificationId,
userId
},
{
isRead: true
isRead
}
);
if (!updatedNotification) throw new NotFoundError({ message: "Notification not found" });
return updatedNotification;
};
return {
listUserNotifications,
createUserNotification,
createUserNotifications,
deleteUserNotification,
markUserNotificationsAsRead,
markUserNotificationAsRead
updateUserNotification
};
};
@@ -1,5 +1,6 @@
export enum NotificationType {
ACCESS_APPROVAL_REQUEST = "access-approval-request"
ACCESS_APPROVAL_REQUEST = "access-approval-request",
ACCESS_APPROVAL_REQUEST_UPDATED = "access-approval-request-updated"
}
export interface TCreateUserNotificationDTO {
@@ -21,7 +21,7 @@ export const userNotificationDALFactory = (db: TDbClient) => {
userId,
startDate,
endDate,
limit = 10000,
limit = 1000,
offset = 0
}: {
userId: string;