From d6fba12cd9d13c211501b119f825681c6fcd95b1 Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 18 Dec 2025 13:27:24 -0500 Subject: [PATCH 1/2] feature/IO-3402-Import-Add-Notifiers - Fix Auto Notifiers --- .../shop-info/shop-info.notifications-autoadd.component.jsx | 6 +----- hasura/metadata/tables.yaml | 4 ++++ .../endpoints/partsManagementProvisioning.js | 2 ++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/client/src/components/shop-info/shop-info.notifications-autoadd.component.jsx b/client/src/components/shop-info/shop-info.notifications-autoadd.component.jsx index ae99e40fe..cdf120912 100644 --- a/client/src/components/shop-info/shop-info.notifications-autoadd.component.jsx +++ b/client/src/components/shop-info/shop-info.notifications-autoadd.component.jsx @@ -39,14 +39,10 @@ export default function ShopInfoNotificationsAutoadd({ bodyshop }) { (value || []).filter((id) => typeof id === "string" && id.trim() !== "")} options={employeeOptions} placeholder={t("bodyshop.fields.notifications.placeholder")} showEmail={true} - onChange={(value) => { - // Filter out null or invalid values before passing to Form - const cleanedValue = value?.filter((id) => id != null && typeof id === "string" && id.trim() !== ""); - return cleanedValue; - }} /> ) : ( diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index a52ea634f..77084c96c 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -1157,6 +1157,7 @@ update: columns: - shopname + - notification_followers - md_order_statuses retry_conf: interval_sec: 10 @@ -3698,6 +3699,7 @@ - deliverchecklist - depreciation_taxes - dms_allocation + - dms_id - driveable - employee_body - employee_csr @@ -3975,6 +3977,7 @@ - deliverchecklist - depreciation_taxes - dms_allocation + - dms_id - driveable - employee_body - employee_csr @@ -4264,6 +4267,7 @@ - deliverchecklist - depreciation_taxes - dms_allocation + - dms_id - driveable - employee_body - employee_csr diff --git a/server/integrations/partsManagement/endpoints/partsManagementProvisioning.js b/server/integrations/partsManagement/endpoints/partsManagementProvisioning.js index 5f626e3d9..374002a79 100644 --- a/server/integrations/partsManagement/endpoints/partsManagementProvisioning.js +++ b/server/integrations/partsManagement/endpoints/partsManagementProvisioning.js @@ -241,6 +241,8 @@ const partsManagementProvisioning = async (req, res) => { "phone", "userEmail" ]); + + // TODO add in check for early access await ensureExternalIdUnique(body.external_shop_id); logger.log("admin-create-shop-user", "debug", body.userEmail, null, { From c010665ea9590dfc363f30937b3cedbb11e9a03b Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 18 Dec 2025 18:26:02 -0500 Subject: [PATCH 2/2] feature/IO-3402-Import-Add-Notifiers - Fix --- hasura/metadata/tables.yaml | 3 +++ server/graphql-client/queries.js | 9 ++++++++ server/notifications/autoAddWatchers.js | 29 +++++++++++++------------ 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 77084c96c..9900fa00b 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -1156,8 +1156,11 @@ enable_manual: false update: columns: + - imexshopid + - timezone - shopname - notification_followers + - state - md_order_statuses retry_conf: interval_sec: 10 diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index e3b3b0977..b51c73c49 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -2926,6 +2926,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 = ` query GET_DOCUMENTS_BY_JOB($jobId: uuid!) { jobs_by_pk(id: $jobId) { diff --git a/server/notifications/autoAddWatchers.js b/server/notifications/autoAddWatchers.js index 704ca2550..803c44984 100644 --- a/server/notifications/autoAddWatchers.js +++ b/server/notifications/autoAddWatchers.js @@ -4,11 +4,14 @@ * 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 @@ -26,10 +29,7 @@ const FILTER_SELF_FROM_WATCHERS = process.env?.FILTER_SELF_FROM_WATCHERS !== "fa */ const autoAddWatchers = async (req) => { const { event, trigger } = req.body; - const { - logger, - sessionUtils: { getBodyshopFromRedis } - } = req; + const { logger } = req; // Validate that this is an INSERT event, bail 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"]; try { - // Fetch bodyshop data from Redis - const bodyshopData = await getBodyshopFromRedis(shopId); - let notificationFollowers = bodyshopData?.notification_followers; + // 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; - // Bail if notification_followers is missing or not an array - if (!notificationFollowers || !Array.isArray(notificationFollowers)) { - return; - } + 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.filter((id) => id) + employeeIds: notificationFollowers }), gqlClient.request(GET_JOB_WATCHERS_MINIMAL, { jobid: jobId }) ]); @@ -73,7 +73,7 @@ const autoAddWatchers = async (req) => { associationId: assoc.id })) || []; - // Get users from notification_followers + // Get users from notification_followers (employee IDs -> employee emails) const followerEmails = notificationData?.employees ?.filter((e) => e.user_email) @@ -84,7 +84,7 @@ const autoAddWatchers = async (req) => { // 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)) { + if (user?.email && !acc.some((u) => u.email === user.email)) { acc.push(user); } return acc; @@ -123,6 +123,7 @@ const autoAddWatchers = async (req) => { message: error?.message, stack: error?.stack, jobId, + shopId, roNumber }); throw error; // Re-throw to ensure the error is logged in the handler