IO-3166-Global-Notifications-Part-2 - Checkpoint
This commit is contained in:
@@ -3280,8 +3280,6 @@
|
|||||||
- name: notifications_joblines
|
- name: notifications_joblines
|
||||||
definition:
|
definition:
|
||||||
enable_manual: false
|
enable_manual: false
|
||||||
insert:
|
|
||||||
columns: '*'
|
|
||||||
update:
|
update:
|
||||||
columns:
|
columns:
|
||||||
- critical
|
- critical
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const {
|
|||||||
supplementImportedBuilder,
|
supplementImportedBuilder,
|
||||||
partMarkedBackOrderedBuilder
|
partMarkedBackOrderedBuilder
|
||||||
} = require("./scenarioBuilders");
|
} = require("./scenarioBuilders");
|
||||||
|
const logger = require("../utils/logger");
|
||||||
const { isFunction } = require("lodash");
|
const { isFunction } = require("lodash");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,7 +29,8 @@ const { isFunction } = require("lodash");
|
|||||||
* - onNew {boolean|Array<boolean>}: Indicates whether the scenario should be triggered on new data.
|
* - onNew {boolean|Array<boolean>}: Indicates whether the scenario should be triggered on new data.
|
||||||
* - builder {Function}: A function to handle the scenario.
|
* - builder {Function}: A function to handle the scenario.
|
||||||
* - onlyTruthyValues {boolean|Array<string>}: Specifies fields that must have truthy values for the scenario to match.
|
* - onlyTruthyValues {boolean|Array<string>}: Specifies fields that must have truthy values for the scenario to match.
|
||||||
* */
|
* - filterCallback {Function}: Optional callback (sync or async) to further filter the scenario based on event data (returns boolean).
|
||||||
|
*/
|
||||||
const notificationScenarios = [
|
const notificationScenarios = [
|
||||||
{
|
{
|
||||||
key: "job-assigned-to-me",
|
key: "job-assigned-to-me",
|
||||||
@@ -116,7 +118,19 @@ const notificationScenarios = [
|
|||||||
key: "part-marked-back-ordered",
|
key: "part-marked-back-ordered",
|
||||||
table: "joblines",
|
table: "joblines",
|
||||||
fields: ["status"],
|
fields: ["status"],
|
||||||
builder: partMarkedBackOrderedBuilder
|
builder: partMarkedBackOrderedBuilder,
|
||||||
|
filterCallback: async ({ eventData, getBodyshopFromRedis }) => {
|
||||||
|
try {
|
||||||
|
const bodyshop = await getBodyshopFromRedis(eventData.bodyShopId);
|
||||||
|
return eventData?.data?.status !== bodyshop?.md_order_statuses?.default_bo;
|
||||||
|
} catch (err) {
|
||||||
|
logger.log("notifications-error-parts-marked-back-ordered", "error", "notifications", "mapper", {
|
||||||
|
message: err?.message,
|
||||||
|
stack: err?.stack
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// -------------- Difficult ---------------
|
// -------------- Difficult ---------------
|
||||||
// Holding off on this one for now
|
// Holding off on this one for now
|
||||||
@@ -129,6 +143,7 @@ const notificationScenarios = [
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of scenarios that match the given event data.
|
* Returns an array of scenarios that match the given event data.
|
||||||
|
* Supports asynchronous callbacks for additional filtering.
|
||||||
*
|
*
|
||||||
* @param {Object} eventData - The parsed event data.
|
* @param {Object} eventData - The parsed event data.
|
||||||
* Expected properties:
|
* Expected properties:
|
||||||
@@ -137,28 +152,16 @@ const notificationScenarios = [
|
|||||||
* - isNew: boolean indicating whether the record is new or updated
|
* - isNew: boolean indicating whether the record is new or updated
|
||||||
* - data: the new data object (used to check field values)
|
* - data: the new data object (used to check field values)
|
||||||
* - (other properties may be added such as jobWatchers, bodyShopId, etc.)
|
* - (other properties may be added such as jobWatchers, bodyShopId, etc.)
|
||||||
*
|
* @param {Function} getBodyshopFromRedis - Function to retrieve bodyshop data from Redis.
|
||||||
* @returns {Array<Object>} An array of matching scenario objects.
|
* @returns {Promise<Array<Object>>} A promise resolving to an array of matching scenario objects.
|
||||||
*/
|
*/
|
||||||
/**
|
const getMatchingScenarios = async (eventData, getBodyshopFromRedis) => {
|
||||||
* Returns an array of scenarios that match the given event data.
|
const matches = [];
|
||||||
*
|
for (const scenario of notificationScenarios) {
|
||||||
* @param {Object} eventData - The parsed event data.
|
|
||||||
* Expected properties:
|
|
||||||
* - table: an object with a `name` property (e.g. { name: "tasks", schema: "public" })
|
|
||||||
* - changedFieldNames: an array of changed field names (e.g. [ "description", "updated_at" ])
|
|
||||||
* - isNew: boolean indicating whether the record is new or updated
|
|
||||||
* - data: the new data object (used to check field values)
|
|
||||||
* - (other properties may be added such as jobWatchers, bodyShopId, etc.)
|
|
||||||
*
|
|
||||||
* @returns {Array<Object>} An array of matching scenario objects.
|
|
||||||
*/
|
|
||||||
const getMatchingScenarios = (eventData) =>
|
|
||||||
notificationScenarios.filter((scenario) => {
|
|
||||||
// If eventData has a table, then only scenarios with a table property that matches should be considered.
|
// If eventData has a table, then only scenarios with a table property that matches should be considered.
|
||||||
if (eventData.table) {
|
if (eventData.table) {
|
||||||
if (!scenario.table || eventData.table.name !== scenario.table) {
|
if (!scenario.table || eventData.table.name !== scenario.table) {
|
||||||
return false;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,9 +169,9 @@ const getMatchingScenarios = (eventData) =>
|
|||||||
// Allow onNew to be either a boolean or an array of booleans.
|
// Allow onNew to be either a boolean or an array of booleans.
|
||||||
if (Object.prototype.hasOwnProperty.call(scenario, "onNew")) {
|
if (Object.prototype.hasOwnProperty.call(scenario, "onNew")) {
|
||||||
if (Array.isArray(scenario.onNew)) {
|
if (Array.isArray(scenario.onNew)) {
|
||||||
if (!scenario.onNew.includes(eventData.isNew)) return false;
|
if (!scenario.onNew.includes(eventData.isNew)) continue;
|
||||||
} else {
|
} else {
|
||||||
if (eventData.isNew !== scenario.onNew) return false;
|
if (eventData.isNew !== scenario.onNew) continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +179,7 @@ const getMatchingScenarios = (eventData) =>
|
|||||||
if (scenario.fields && scenario.fields.length > 0) {
|
if (scenario.fields && scenario.fields.length > 0) {
|
||||||
const hasMatchingField = scenario.fields.some((field) => eventData.changedFieldNames.includes(field));
|
const hasMatchingField = scenario.fields.some((field) => eventData.changedFieldNames.includes(field));
|
||||||
if (!hasMatchingField) {
|
if (!hasMatchingField) {
|
||||||
return false;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,30 +199,37 @@ const getMatchingScenarios = (eventData) =>
|
|||||||
);
|
);
|
||||||
// If no fields in onlyTruthyValues match the scenario’s fields or changed fields, skip this scenario
|
// If no fields in onlyTruthyValues match the scenario’s fields or changed fields, skip this scenario
|
||||||
if (fieldsToCheck.length === 0) {
|
if (fieldsToCheck.length === 0) {
|
||||||
return false;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Invalid onlyTruthyValues (not true or a non-empty array), skip this scenario
|
// Invalid onlyTruthyValues (not true or a non-empty array), skip this scenario
|
||||||
return false;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure all fields to check have truthy new values
|
// Ensure all fields to check have truthy new values
|
||||||
const allTruthy = fieldsToCheck.every((field) => Boolean(eventData.data[field]));
|
const allTruthy = fieldsToCheck.every((field) => Boolean(eventData.data[field]));
|
||||||
if (!allTruthy) {
|
if (!allTruthy) {
|
||||||
return false;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the callback if defined, passing eventData, and filter based on its return value
|
// Execute the callback if defined, supporting both sync and async, and filter based on its return value
|
||||||
if (isFunction(scenario?.callback)) {
|
if (isFunction(scenario?.filterCallback)) {
|
||||||
const shouldInclude = scenario.callback(eventData);
|
const shouldFilter = await Promise.resolve(
|
||||||
if (!shouldInclude) {
|
scenario.filterCallback({
|
||||||
return false;
|
eventData,
|
||||||
|
getBodyshopFromRedis
|
||||||
|
})
|
||||||
|
);
|
||||||
|
if (shouldFilter) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
matches.push(scenario);
|
||||||
});
|
}
|
||||||
|
return matches;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
notificationScenarios,
|
notificationScenarios,
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ const FILTER_SELF_FROM_WATCHERS = process.env?.FILTER_SELF_FROM_WATCHERS !== "fa
|
|||||||
*/
|
*/
|
||||||
const scenarioParser = async (req, jobIdField) => {
|
const scenarioParser = async (req, jobIdField) => {
|
||||||
const { event, trigger, table } = req.body;
|
const { event, trigger, table } = req.body;
|
||||||
const { logger } = req;
|
const {
|
||||||
|
logger,
|
||||||
|
sessionUtils: { getBodyshopFromRedis }
|
||||||
|
} = req;
|
||||||
|
|
||||||
// Step 1: Validate we know what user committed the action that fired the parser
|
// Step 1: Validate we know what user committed the action that fired the parser
|
||||||
// console.log("Step 1");
|
// console.log("Step 1");
|
||||||
@@ -118,12 +121,15 @@ const scenarioParser = async (req, jobIdField) => {
|
|||||||
// Step 7: Identify scenarios that match the event data and job context
|
// Step 7: Identify scenarios that match the event data and job context
|
||||||
// console.log("Step 7");
|
// console.log("Step 7");
|
||||||
|
|
||||||
const matchingScenarios = getMatchingScenarios({
|
const matchingScenarios = await getMatchingScenarios(
|
||||||
...eventData,
|
{
|
||||||
jobWatchers,
|
...eventData,
|
||||||
bodyShopId,
|
jobWatchers,
|
||||||
bodyShopName
|
bodyShopId,
|
||||||
});
|
bodyShopName
|
||||||
|
},
|
||||||
|
getBodyshopFromRedis
|
||||||
|
);
|
||||||
|
|
||||||
// Exit early if no matching scenarios are identified
|
// Exit early if no matching scenarios are identified
|
||||||
if (isEmpty(matchingScenarios)) {
|
if (isEmpty(matchingScenarios)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user