Skip to content
Merged
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
53 changes: 53 additions & 0 deletions apps/app/src/jobs/tasks/notifications/risk-task-notification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { db } from "@bubba/db";
import { NotificationTypes, TriggerEvents, trigger } from "@bubba/notifications";
import { logger, schemaTask } from "@trigger.dev/sdk/v3";
import { formatDistance } from "date-fns";
import { z } from "zod";

export const sendRiskTaskNotification = schemaTask({
id: "send-risk-task-notification",
schema: z.object({
task: z.object({
id: z.string(),
title: z.string(),
dueDate: z.date(),
owner: z.object({
id: z.string(),
email: z.string(),
organizationId: z.string(),
}),
riskId: z.string(),
})
}),
run: async (payload) => {
const { task } = payload;

try {
const owner = task.owner;

const timeUntilDue = task.dueDate ? formatDistance(task.dueDate, new Date(), { addSuffix: true }) : "soon";

await db.riskMitigationTask.update({
where: { id: task.id },
data: { notifiedAt: new Date() },
});

await trigger({
name: TriggerEvents.TaskReminderInApp,
user: {
subscriberId: `${owner.organizationId}_${owner.id}`,
email: owner.email,
fullName: owner.email,
organizationId: owner.organizationId,
},
payload: {
description: `${task.title} is due ${timeUntilDue}`,
recordId: `/risk/${task.riskId}/tasks/${task.id}`,
type: NotificationTypes.Task,
},
});
} catch (error) {
logger.error(`Error sending risk task notification: ${error}`);
}
},
});
95 changes: 0 additions & 95 deletions apps/app/src/jobs/tasks/notifications/risk-task-notifications.ts

This file was deleted.

81 changes: 81 additions & 0 deletions apps/app/src/jobs/tasks/notifications/risk-task-schedule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { db } from "@bubba/db";
import { logger, schedules } from "@trigger.dev/sdk/v3";
import { sendRiskTaskNotification } from "./risk-task-notification";

export const sendRiskTaskSchedule = schedules.task({
id: "risk-task-schedule",
cron: "0 * * * *",
run: async () => {
const now = new Date();
const upcomingThreshold = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);

logger.info(
`Sending risk task notifications from now: ${now} to ${upcomingThreshold}`,
);

const tasks = await db.riskMitigationTask.findMany({
where: {
dueDate: { gte: now, lte: upcomingThreshold },
status: { in: ["open", "pending"] },
notifiedAt: null,
},
select: {
id: true,
dueDate: true,
notifiedAt: true,
riskId: true,
title: true,
owner: {
select: {
id: true,
email: true,
name: true,
organizationId: true,
},
},
},
});

Comment on lines +16 to +38
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

Consider pagination for large result sets.

The findMany query might return a large number of tasks. Consider implementing pagination to handle the results in batches.

+    const BATCH_SIZE = 100;
+    let processedCount = 0;
+    
     const tasks = await db.riskMitigationTask.findMany({
       where: {
         dueDate: { gte: now, lte: upcomingThreshold },
         status: { in: ["open", "pending"] },
         notifiedAt: null,
       },
+      take: BATCH_SIZE,
       select: {
         id: true,
         dueDate: true,
         notifiedAt: true,
         riskId: true,
         title: true,
         owner: {
           select: {
             id: true,
             email: true,
             name: true,
             organizationId: true,
           },
         },
       },
     });
+    processedCount = tasks.length;
+    
+    logger.info(`Processing ${processedCount} tasks in this batch`);
📝 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
const tasks = await db.riskMitigationTask.findMany({
where: {
dueDate: { gte: now, lte: upcomingThreshold },
status: { in: ["open", "pending"] },
notifiedAt: null,
},
select: {
id: true,
dueDate: true,
notifiedAt: true,
riskId: true,
title: true,
owner: {
select: {
id: true,
email: true,
name: true,
organizationId: true,
},
},
},
});
const BATCH_SIZE = 100;
let processedCount = 0;
const tasks = await db.riskMitigationTask.findMany({
where: {
dueDate: { gte: now, lte: upcomingThreshold },
status: { in: ["open", "pending"] },
notifiedAt: null,
},
take: BATCH_SIZE,
select: {
id: true,
dueDate: true,
notifiedAt: true,
riskId: true,
title: true,
owner: {
select: {
id: true,
email: true,
name: true,
organizationId: true,
},
},
},
});
processedCount = tasks.length;
logger.info(`Processing ${processedCount} tasks in this batch`);

const triggerPayloads = tasks
.filter((task): task is (typeof task & {
owner: { id: string; email: string; organizationId: string }
}) => Boolean(task.owner?.email && task.owner.organizationId))
.map(task => ({
payload: {
task: {
id: task.id,
title: task.title,
dueDate: task.dueDate || new Date(),
owner: task.owner,
riskId: task.riskId,
}
}
}));

if (triggerPayloads.length > 0) {
try {

await sendRiskTaskNotification.batchTrigger(
triggerPayloads,
);

logger.info(`Triggered ${triggerPayloads.length} task notifications`);
} catch (error) {
logger.error(`Failed to trigger batch notifications: ${error}`);

return {
success: false,
totalTasks: tasks.length,
triggeredTasks: triggerPayloads.length,
error: error instanceof Error ? error.message : String(error),
};
}
}

return {
success: true,
totalTasks: tasks.length,
triggeredTasks: triggerPayloads.length,
};
}
});