Files
bodyshop/server/notifications/autoAddWatchers.js
2025-05-05 15:02:44 -04:00

135 lines
5.0 KiB
JavaScript

/**
* @module autoAddWatchers
* @description
* This module handles automatically adding watchers to new jobs based on the notifications_autoadd
* boolean field in the associations table and the notification_followers JSON field in the bodyshops table.
* It ensures users are not added twice and logs the process.
*/
const { client: gqlClient } = require("../graphql-client/graphql-client");
const queries = require("../graphql-client/queries");
const { isEmpty } = require("lodash");
// If true, the user who commits the action will NOT receive notifications; if false, they will.
const FILTER_SELF_FROM_WATCHERS = process.env?.FILTER_SELF_FROM_WATCHERS !== "false";
/**
* Adds watchers to a new job based on notifications_autoadd and notification_followers.
*
* @param {Object} req - The request object containing event data and logger.
* @returns {Promise<void>} Resolves when watchers are added or if no action is needed.
* @throws {Error} If critical data (e.g., jobId, shopId) is missing.
*/
const autoAddWatchers = async (req) => {
const { event, trigger } = req.body;
const { logger } = req;
// Validate that this is an INSERT event
if (trigger?.name !== "notifications_jobs_autoadd" || event.op !== "INSERT" || event.data.old) {
logger.log("Invalid event for auto-add watchers, skipping", "info", "notifications");
return;
}
const jobId = event?.data?.new?.id;
const shopId = event?.data?.new?.shopid;
const roNumber = event?.data?.new?.ro_number || "unknown";
if (!jobId || !shopId) {
throw new Error(`Missing jobId (${jobId}) or shopId (${shopId}) for auto-add watchers`);
}
const hasuraUserRole = event?.session_variables?.["x-hasura-role"];
const hasuraUserId = event?.session_variables?.["x-hasura-user-id"];
try {
// Fetch auto-add users and notification followers
const autoAddData = await gqlClient.request(queries.GET_AUTOADD_NOTIFICATION_USERS, { shopId });
// Get users with notifications_autoadd: true
const autoAddUsers =
autoAddData?.associations?.map((assoc) => ({
email: assoc.useremail,
associationId: assoc.id
})) || [];
// Get users from notification_followers (array of association IDs)
const notificationFollowers = autoAddData?.bodyshops_by_pk?.notification_followers || [];
let followerEmails = [];
if (notificationFollowers.length > 0) {
// Fetch associations for notification_followers
const followerAssociations = await gqlClient.request(queries.GET_NOTIFICATION_ASSOCIATIONS, {
emails: [], // Filter by association IDs
shopid: shopId
});
followerEmails = followerAssociations.associations
.filter((assoc) => notificationFollowers.includes(assoc.id))
.map((assoc) => ({
email: assoc.useremail,
associationId: assoc.id
}));
}
// Combine and deduplicate emails (use email as the unique key)
const usersToAdd = [...autoAddUsers, ...followerEmails].reduce((acc, user) => {
if (!acc.some((u) => u.email === user.email)) {
acc.push(user);
}
return acc;
}, []);
if (isEmpty(usersToAdd)) {
logger.log(`No users to auto-add for jobId "${jobId}" (RO: ${roNumber})`, "info", "notifications");
return;
}
// Check existing watchers to avoid duplicates
const existingWatchersData = await gqlClient.request(queries.GET_JOB_WATCHERS, { jobid: jobId });
const existingWatcherEmails = existingWatchersData?.job_watchers?.map((w) => w.user_email) || [];
// Filter out already existing watchers and optionally the user who created the job
const newWatchers = usersToAdd
.filter((user) => !existingWatcherEmails.includes(user.email))
.filter((user) => {
if (FILTER_SELF_FROM_WATCHERS && hasuraUserRole === "user") {
// Fetch user email for hasuraUserId to compare
const userData = existingWatchersData?.job_watchers?.find((w) => w.user?.authid === hasuraUserId);
return userData ? user.email !== userData.user_email : true;
}
return true;
})
.map((user) => ({
jobid: jobId,
user_email: user.email
}));
if (isEmpty(newWatchers)) {
logger.log(
`No new watchers to add after filtering for jobId "${jobId}" (RO: ${roNumber})`,
"info",
"notifications"
);
return;
}
// Insert new watchers
await gqlClient.request(queries.INSERT_JOB_WATCHERS, { watchers: newWatchers });
logger.log(
`Added ${newWatchers.length} auto-add watchers for jobId "${jobId}" (RO: ${roNumber})`,
"info",
"notifications",
null,
{ addedEmails: newWatchers.map((w) => w.user_email) }
);
} catch (error) {
logger.log("Error adding auto-add watchers", "error", "notifications", null, {
message: error?.message,
stack: error?.stack,
jobId,
roNumber
});
throw error; // Re-throw to ensure the error is logged in the handler
}
};
module.exports = { autoAddWatchers };