136 lines
4.8 KiB
JavaScript
136 lines
4.8 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.
|
|
*
|
|
* NOTE: Bodyshop notification_followers is fetched directly from the DB (Hasura) to avoid stale Redis cache.
|
|
*/
|
|
|
|
const { client: gqlClient } = require("../graphql-client/graphql-client");
|
|
const { isEmpty } = require("lodash");
|
|
const {
|
|
GET_BODYSHOP_WATCHERS_BY_ID,
|
|
GET_JOB_WATCHERS_MINIMAL,
|
|
GET_NOTIFICATION_WATCHERS,
|
|
INSERT_JOB_WATCHERS
|
|
} = require("../graphql-client/queries");
|
|
|
|
// 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, bail
|
|
if (trigger?.name !== "notifications_jobs_autoadd" || event.op !== "INSERT" || event.data.old) {
|
|
return;
|
|
}
|
|
|
|
const jobId = event?.data?.new?.id;
|
|
const shopId = event?.data?.new?.shopid;
|
|
const roNumber = event?.data?.new?.ro_number || "unknown";
|
|
const createdUserEmail = event?.data?.new?.created_user_email || "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 bodyshop data directly from DB (avoid Redis staleness)
|
|
const bodyshopResponse = await gqlClient.request(GET_BODYSHOP_WATCHERS_BY_ID, { id: shopId });
|
|
const bodyshopData = bodyshopResponse?.bodyshops_by_pk;
|
|
|
|
const notificationFollowersRaw = bodyshopData?.notification_followers;
|
|
const notificationFollowers = Array.isArray(notificationFollowersRaw)
|
|
? [...new Set(notificationFollowersRaw.filter((id) => id))] // de-dupe + remove falsy
|
|
: [];
|
|
|
|
// Execute queries in parallel
|
|
const [notificationData, existingWatchersData] = await Promise.all([
|
|
gqlClient.request(GET_NOTIFICATION_WATCHERS, {
|
|
shopId,
|
|
employeeIds: notificationFollowers,
|
|
createdUserEmail
|
|
}),
|
|
gqlClient.request(GET_JOB_WATCHERS_MINIMAL, { jobid: jobId })
|
|
]);
|
|
|
|
// Get users with notifications_autoadd: true
|
|
const autoAddUsers =
|
|
notificationData?.associations?.map((assoc) => ({
|
|
email: assoc.useremail,
|
|
associationId: assoc.id
|
|
})) || [];
|
|
|
|
// Get users from notification_followers (employee IDs -> employee emails)
|
|
const followerEmails =
|
|
notificationData?.employees
|
|
?.filter((e) => e.user_email)
|
|
?.map((e) => ({
|
|
email: e.user_email,
|
|
associationId: null
|
|
})) || [];
|
|
|
|
// Combine and deduplicate emails (use email as the unique key)
|
|
const usersToAdd = [...autoAddUsers, ...followerEmails].reduce((acc, user) => {
|
|
if (user?.email && !acc.some((u) => u.email === user.email)) {
|
|
acc.push(user);
|
|
}
|
|
return acc;
|
|
}, []);
|
|
|
|
if (isEmpty(usersToAdd)) {
|
|
return;
|
|
}
|
|
|
|
// Check existing watchers to avoid duplicates
|
|
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") {
|
|
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)) {
|
|
return;
|
|
}
|
|
|
|
// Insert new watchers
|
|
await gqlClient.request(INSERT_JOB_WATCHERS, { watchers: newWatchers });
|
|
} catch (error) {
|
|
logger.log("Error adding auto-add watchers", "error", "notifications", null, {
|
|
message: error?.message,
|
|
stack: error?.stack,
|
|
jobId,
|
|
shopId,
|
|
roNumber
|
|
});
|
|
throw error; // Re-throw to ensure the error is logged in the handler
|
|
}
|
|
};
|
|
|
|
module.exports = { autoAddWatchers };
|