feature/IO-3096-GlobalNotifications - Code Review Part 1

This commit is contained in:
Dave Richer
2025-03-03 22:14:33 -05:00
parent b9df4c2587
commit a57abec81b
34 changed files with 124 additions and 77 deletions

View File

@@ -69,11 +69,14 @@ const sendServerEmail = async ({ subject, text }) => {
}
},
(err, info) => {
logger.log("server-email-failure", err ? "error" : "debug", null, null, { message: err?.message });
logger.log("server-email-failure", err ? "error" : "debug", null, null, {
message: err?.message,
stack: err?.stack
});
}
);
} catch (error) {
logger.log("server-email-failure", "error", null, null, { message: error?.message });
logger.log("server-email-failure", "error", null, null, { message: error?.message, stack: error?.stack });
}
};
@@ -92,11 +95,11 @@ const sendTaskEmail = async ({ to, subject, type = "text", html, text, attachmen
},
(err, info) => {
// (message, type, user, record, meta
logger.log("server-email", err ? "error" : "debug", null, null, { message: err?.message });
logger.log("server-email", err ? "error" : "debug", null, null, { message: err?.message, stack: err?.stack });
}
);
} catch (error) {
logger.log("server-email-failure", "error", null, null, { message: error?.message });
logger.log("server-email-failure", "error", null, null, { message: error?.message, stack: error?.stack });
}
};
@@ -125,7 +128,8 @@ const sendEmail = async (req, res) => {
cc: req.body.cc,
subject: req.body.subject,
templateStrings: req.body.templateStrings,
errorMessage: error?.message
errorMessage: error?.message,
errorStack: error?.stack
});
}
})
@@ -194,7 +198,8 @@ const sendEmail = async (req, res) => {
cc: req.body.cc,
subject: req.body.subject,
templateStrings: req.body.templateStrings,
errorMessage: err?.message
errorMessage: err?.message,
errorStack: err?.stack
});
logEmail(req, {
to: req.body.to,
@@ -202,7 +207,7 @@ const sendEmail = async (req, res) => {
subject: req.body.subject,
bodyshopid: req.body.bodyshopid
});
res.status(500).json({ success: false, errorMessage: err?.message });
res.status(500).json({ success: false, errorMessage: err?.message, stack: err?.stack });
}
}
);
@@ -270,14 +275,16 @@ ${body.bounce?.bouncedRecipients.map(
},
(err, info) => {
logger.log("sns-error", err ? "error" : "debug", "api", null, {
errorMessage: err?.message
errorMessage: err?.message,
errorStack: err?.stack
});
}
);
}
} catch (error) {
logger.log("sns-error", "ERROR", "api", null, {
errorMessage: error?.message
errorMessage: error?.message,
errorStack: error?.stack
});
}
res.sendStatus(200);

View File

@@ -2708,16 +2708,14 @@ exports.INSERT_AUDIT_TRAIL = `
exports.GET_JOB_WATCHERS = `
query GET_JOB_WATCHERS($jobid: uuid!) {
job_watchers_aggregate(where: { jobid: { _eq: $jobid } }) {
nodes {
user_email
user {
authid
employee {
id
first_name
last_name
}
job_watchers(where: { jobid: { _eq: $jobid } }) {
user_email
user {
authid
employee {
id
first_name
last_name
}
}
}

View File

@@ -22,7 +22,7 @@ async function processNotificationEvent(req, res, parserPath, successMessage) {
// Call scenarioParser but don't await it; log any error that occurs.
scenarioParser(req, parserPath).catch((error) => {
logger.log("notifications-error", "error", "notifications", null, { error: error?.message });
logger.log("notifications-error", "error", "notifications", null, { message: error?.message, stack: error?.stack });
});
return res.status(200).json({ message: successMessage });

View File

@@ -214,7 +214,7 @@ const loadAppQueue = async ({ pubClient, logger, redisHelpers, ioRedis }) => {
await pubClient.del(`app:consolidate:${jobId}`);
} catch (err) {
logger.log(`Consolidation error for jobId ${jobId}`, "ERROR", "notifications", "api", {
logger.log(`app-queue-consolidation-error`, "ERROR", "notifications", "api", {
message: err?.message,
stack: err?.stack
});
@@ -237,13 +237,13 @@ const loadAppQueue = async ({ pubClient, logger, redisHelpers, ioRedis }) => {
addWorker.on("completed", (job) => logger.logger.debug(`Add job ${job.id} completed`));
consolidateWorker.on("completed", (job) => logger.logger.debug(`Consolidate job ${job.id} completed`));
addWorker.on("failed", (job, err) =>
logger.log(`Add job ${job.id} failed:`, "ERROR", "notifications", "api", {
logger.log(`app-queue-notification-error`, "ERROR", "notifications", "api", {
message: err?.message,
stack: err?.stack
})
);
consolidateWorker.on("failed", (job, err) =>
logger.log(`Consolidate job ${job.id} failed:`, "ERROR", "notifications", "api", {
logger.log(`app-queue-consolidation-failed:`, "ERROR", "notifications", "api", {
message: err?.message,
stack: err?.stack
})

View File

@@ -146,7 +146,7 @@ const loadEmailQueue = async ({ pubClient, logger }) => {
await pubClient.del(recipientsSet);
await pubClient.del(`email:consolidate:${jobId}`);
} catch (err) {
logger.log(`Email Consolidation error for jobId ${jobId}`, "ERROR", "notifications", "api", {
logger.log(`email-queue-consolidation-error`, "ERROR", "notifications", "api", {
message: err?.message,
stack: err?.stack
});
@@ -170,13 +170,13 @@ const loadEmailQueue = async ({ pubClient, logger }) => {
emailAddWorker.on("completed", (job) => logger.logger.debug(`Email add job ${job.id} completed`));
emailConsolidateWorker.on("completed", (job) => logger.logger.debug(`Email consolidate job ${job.id} completed`));
emailAddWorker.on("failed", (job, err) =>
logger.log(`Email add job ${job.id} failed`, "ERROR", "notifications", "api", {
logger.log(`add-email-queue-failed`, "ERROR", "notifications", "api", {
message: err?.message,
stack: err?.stack
})
);
emailConsolidateWorker.on("failed", (job, err) =>
logger.log(`Email consolidate job ${job.id} failed:`, "ERROR", "notifications", "api", {
logger.log(`email-consolidation-job-failed`, "ERROR", "notifications", "api", {
message: err?.message,
stack: err?.stack
})

View File

@@ -24,8 +24,9 @@ const populateWatchers = (data, result) => {
/**
* Builds notification data for changes to alternate transport.
*/
// Verified
const alternateTransportChangedBuilder = (data) => {
const body = `The alternate transport status has been updated from ${data?.changedFields?.altTransport?.old}.`;
const body = `The Alternate Transport status has been updated to ${data?.data?.alt_transport}.`;
const result = {
app: {
jobId: data.jobId,
@@ -56,8 +57,12 @@ const alternateTransportChangedBuilder = (data) => {
/**
* Builds notification data for bill posted events.
*/
//verified
const billPostedHandler = (data) => {
const body = `A bill of $${data.data.clm_total} has been posted.`;
const facing = data?.data?.isinhouse ? "In-House" : "External";
const body = `An ${facing} Bill has been posted${data?.data?.is_credit_memo ? " (Credit Memo)" : ""}.`.trim();
const result = {
app: {
jobId: data.jobId,
@@ -66,7 +71,8 @@ const billPostedHandler = (data) => {
key: "notifications.job.billPosted",
body,
variables: {
clmTotal: data.data.clm_total
facing,
is_credit_memo: data?.data?.is_credit_memo
},
recipients: []
},
@@ -87,6 +93,7 @@ const billPostedHandler = (data) => {
/**
* Builds notification data for changes to critical parts status.
*/
// TODO: Needs change
const criticalPartsStatusChangedBuilder = (data) => {
const body = `The critical parts status has changed to ${data.data.queued_for_parts ? "queued" : "not queued"}.`;
const result = {
@@ -119,8 +126,9 @@ const criticalPartsStatusChangedBuilder = (data) => {
/**
* Builds notification data for completed intake or delivery checklists.
*/
// Verified
const intakeDeliveryChecklistCompletedBuilder = (data) => {
const checklistType = data.changedFields.intakechecklist ? "intake" : "delivery";
const checklistType = data?.changedFields?.intakechecklist ? "Intake" : "Delivery";
const body = `The ${checklistType.charAt(0).toUpperCase() + checklistType.slice(1)} checklist has been completed.`;
const result = {
app: {
@@ -152,6 +160,7 @@ const intakeDeliveryChecklistCompletedBuilder = (data) => {
/**
* Builds notification data for job assignment events.
*/
// Verified
const jobAssignedToMeBuilder = (data) => {
const body = `You have been assigned to ${getJobAssignmentType(data.scenarioFields?.[0])}`;
const result = {
@@ -183,8 +192,9 @@ const jobAssignedToMeBuilder = (data) => {
/**
* Builds notification data for jobs added to production.
*/
// Verified
const jobsAddedToProductionBuilder = (data) => {
const body = `Job has been added to production.`;
const body = `Has been added to Production.`;
const result = {
app: {
jobId: data.jobId,
@@ -212,6 +222,7 @@ const jobsAddedToProductionBuilder = (data) => {
/**
* Builds notification data for job status changes.
*/
// Verified
const jobStatusChangeBuilder = (data) => {
const body = `The status has changed from ${data.changedFields.status.old} to ${data.changedFields.status.new}`;
const result = {
@@ -244,8 +255,15 @@ const jobStatusChangeBuilder = (data) => {
/**
* Builds notification data for new media added or reassigned events.
*/
// Verified
const newMediaAddedReassignedBuilder = (data) => {
const body = `New media has been added.`;
// Determine if it's an image or document
const mediaType = data?.data?.type?.startsWith("image") ? "Image" : "Document";
// Determine if it's added or updated
const action = data.isNew ? "added" : "updated";
// Construct the body string
const body = `An ${mediaType} has been ${action}.`;
const result = {
app: {
jobId: data.jobId,
@@ -253,7 +271,10 @@ const newMediaAddedReassignedBuilder = (data) => {
bodyShopId: data.bodyShopId,
key: "notifications.job.newMediaAdded",
body,
variables: {},
variables: {
mediaType,
action
},
recipients: []
},
email: {
@@ -274,7 +295,7 @@ const newMediaAddedReassignedBuilder = (data) => {
* Builds notification data for new notes added to a job.
*/
const newNoteAddedBuilder = (data) => {
const body = `A new note has been added: "${data.data.text}"`;
const body = `An Note has been added: "${data.data.text}"`;
const result = {
app: {
jobId: data.jobId,
@@ -305,7 +326,10 @@ const newNoteAddedBuilder = (data) => {
* Builds notification data for new time tickets posted.
*/
const newTimeTicketPostedBuilder = (data) => {
const body = `A new time ticket has been posted.`;
consoleDir(data);
const type = data?.data?.cost_center;
const body = `An ${type} time ticket has been posted${data?.data?.flat_rate ? " (Flat Rate)" : ""}.`.trim();
const result = {
app: {
jobId: data.jobId,
@@ -313,7 +337,9 @@ const newTimeTicketPostedBuilder = (data) => {
bodyShopId: data.bodyShopId,
key: "notifications.job.newTimeTicketPosted",
body,
variables: {},
variables: {
type
},
recipients: []
},
email: {

View File

@@ -32,8 +32,8 @@ const notificationScenarios = [
{
key: "job-assigned-to-me",
table: "jobs",
fields: ["employee_pre", "employee_body", "employee_csr", "employee_refinish"],
matchToUserFields: ["employee_pre", "employee_body", "employee_csr", "employee_refinish"],
fields: ["employee_prep", "employee_body", "employee_csr", "employee_refinish"],
matchToUserFields: ["employee_prep", "employee_body", "employee_csr", "employee_refinish"],
builder: jobAssignedToMeBuilder
},
{
@@ -88,19 +88,17 @@ const notificationScenarios = [
// Good test for batching as this will hit multiple scenarios
key: "intake-delivery-checklist-completed",
table: "jobs",
fields: ["intakechecklist"],
fields: ["intakechecklist", "deliverchecklist"],
builder: intakeDeliveryChecklistCompletedBuilder
},
{
key: "payment-collected-completed",
key: "payment-added",
table: "payments",
onNew: true,
builder: paymentCollectedCompletedBuilder
},
{
// MAKE SURE YOU ARE NOT ON A LMS ENVIRONMENT
// Potential Callbacks / Save for last
// Not question mark for Non LMS Scenario
key: "new-media-added-reassigned",
table: "documents",
fields: ["jobid"],

View File

@@ -11,7 +11,7 @@ const eventParser = require("./eventParser");
const { client: gqlClient } = require("../graphql-client/graphql-client");
const queries = require("../graphql-client/queries");
const { isEmpty, isFunction } = require("lodash");
const { getMatchingScenarios } = require("./scenarioMapperr");
const { getMatchingScenarios } = require("./scenarioMapper");
const { dispatchEmailsToQueue } = require("./queues/emailQueue");
const { dispatchAppsToQueue } = require("./queues/appQueue");
@@ -59,7 +59,7 @@ const scenarioParser = async (req, jobIdField) => {
});
// Transform watcher data into a simplified format with email and employee details
let jobWatchers = watcherData?.job_watchers_aggregate?.nodes?.map((watcher) => ({
let jobWatchers = watcherData?.job_watchers?.map((watcher) => ({
email: watcher.user_email,
firstName: watcher?.user?.employee?.first_name,
lastName: watcher?.user?.employee?.last_name,