Merged in release/2025-12-19 (pull request #2723)

feature/IO-3402-Import-Add-Notifiers - Fix
This commit is contained in:
Dave Richer
2025-12-18 23:26:55 +00:00
3 changed files with 27 additions and 14 deletions

View File

@@ -1156,8 +1156,11 @@
enable_manual: false enable_manual: false
update: update:
columns: columns:
- imexshopid
- timezone
- shopname - shopname
- notification_followers - notification_followers
- state
- md_order_statuses - md_order_statuses
retry_conf: retry_conf:
interval_sec: 10 interval_sec: 10

View File

@@ -2919,6 +2919,15 @@ exports.GET_BODYSHOP_BY_ID = `
} }
`; `;
exports.GET_BODYSHOP_WATCHERS_BY_ID = `
query GET_BODYSHOP_BY_ID($id: uuid!) {
bodyshops_by_pk(id: $id) {
id
notification_followers
}
}
`;
exports.GET_DOCUMENTS_BY_JOB = ` exports.GET_DOCUMENTS_BY_JOB = `
query GET_DOCUMENTS_BY_JOB($jobId: uuid!) { query GET_DOCUMENTS_BY_JOB($jobId: uuid!) {
jobs_by_pk(id: $jobId) { jobs_by_pk(id: $jobId) {

View File

@@ -4,11 +4,14 @@
* This module handles automatically adding watchers to new jobs based on the notifications_autoadd * 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. * 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. * 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 { client: gqlClient } = require("../graphql-client/graphql-client");
const { isEmpty } = require("lodash"); const { isEmpty } = require("lodash");
const { const {
GET_BODYSHOP_WATCHERS_BY_ID,
GET_JOB_WATCHERS_MINIMAL, GET_JOB_WATCHERS_MINIMAL,
GET_NOTIFICATION_WATCHERS, GET_NOTIFICATION_WATCHERS,
INSERT_JOB_WATCHERS INSERT_JOB_WATCHERS
@@ -26,10 +29,7 @@ const FILTER_SELF_FROM_WATCHERS = process.env?.FILTER_SELF_FROM_WATCHERS !== "fa
*/ */
const autoAddWatchers = async (req) => { const autoAddWatchers = async (req) => {
const { event, trigger } = req.body; const { event, trigger } = req.body;
const { const { logger } = req;
logger,
sessionUtils: { getBodyshopFromRedis }
} = req;
// Validate that this is an INSERT event, bail // Validate that this is an INSERT event, bail
if (trigger?.name !== "notifications_jobs_autoadd" || event.op !== "INSERT" || event.data.old) { if (trigger?.name !== "notifications_jobs_autoadd" || event.op !== "INSERT" || event.data.old) {
@@ -48,20 +48,20 @@ const autoAddWatchers = async (req) => {
const hasuraUserId = event?.session_variables?.["x-hasura-user-id"]; const hasuraUserId = event?.session_variables?.["x-hasura-user-id"];
try { try {
// Fetch bodyshop data from Redis // Fetch bodyshop data directly from DB (avoid Redis staleness)
const bodyshopData = await getBodyshopFromRedis(shopId); const bodyshopResponse = await gqlClient.request(GET_BODYSHOP_WATCHERS_BY_ID, { id: shopId });
let notificationFollowers = bodyshopData?.notification_followers; const bodyshopData = bodyshopResponse?.bodyshops_by_pk;
// Bail if notification_followers is missing or not an array const notificationFollowersRaw = bodyshopData?.notification_followers;
if (!notificationFollowers || !Array.isArray(notificationFollowers)) { const notificationFollowers = Array.isArray(notificationFollowersRaw)
return; ? [...new Set(notificationFollowersRaw.filter((id) => id))] // de-dupe + remove falsy
} : [];
// Execute queries in parallel // Execute queries in parallel
const [notificationData, existingWatchersData] = await Promise.all([ const [notificationData, existingWatchersData] = await Promise.all([
gqlClient.request(GET_NOTIFICATION_WATCHERS, { gqlClient.request(GET_NOTIFICATION_WATCHERS, {
shopId, shopId,
employeeIds: notificationFollowers.filter((id) => id) employeeIds: notificationFollowers
}), }),
gqlClient.request(GET_JOB_WATCHERS_MINIMAL, { jobid: jobId }) gqlClient.request(GET_JOB_WATCHERS_MINIMAL, { jobid: jobId })
]); ]);
@@ -73,7 +73,7 @@ const autoAddWatchers = async (req) => {
associationId: assoc.id associationId: assoc.id
})) || []; })) || [];
// Get users from notification_followers // Get users from notification_followers (employee IDs -> employee emails)
const followerEmails = const followerEmails =
notificationData?.employees notificationData?.employees
?.filter((e) => e.user_email) ?.filter((e) => e.user_email)
@@ -84,7 +84,7 @@ const autoAddWatchers = async (req) => {
// Combine and deduplicate emails (use email as the unique key) // Combine and deduplicate emails (use email as the unique key)
const usersToAdd = [...autoAddUsers, ...followerEmails].reduce((acc, user) => { const usersToAdd = [...autoAddUsers, ...followerEmails].reduce((acc, user) => {
if (!acc.some((u) => u.email === user.email)) { if (user?.email && !acc.some((u) => u.email === user.email)) {
acc.push(user); acc.push(user);
} }
return acc; return acc;
@@ -123,6 +123,7 @@ const autoAddWatchers = async (req) => {
message: error?.message, message: error?.message,
stack: error?.stack, stack: error?.stack,
jobId, jobId,
shopId,
roNumber roNumber
}); });
throw error; // Re-throw to ensure the error is logged in the handler throw error; // Re-throw to ensure the error is logged in the handler