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"); /** * Parses an event and determines matching scenarios for notifications. * Queries job watchers and notification settings before triggering scenario builders. * * @param {Object} req - The request object containing event data. * @param {string} jobIdField - The field used to identify the job ID. */ const scenarioParser = async (req, jobIdField) => { const { event, trigger, table } = req.body; if (!event?.data || !trigger || !table) { throw new Error("Missing required request fields: event data, trigger, or table."); } // Step 1: Parse event data to extract necessary details. // console.log(`1`); const eventData = await eventParser({ newData: event.data.new, oldData: event.data.old, trigger, table, jobIdField }); // Step 2: Query job watchers for the given job ID. // console.log(`2`); const watcherData = await gqlClient.request(queries.GET_JOB_WATCHERS, { jobid: eventData.jobId }); const jobWatchers = watcherData?.job_watchers_aggregate?.nodes?.map((watcher) => ({ email: watcher.user_email, firstName: watcher?.user?.employee?.first_name, lastName: watcher?.user?.employee?.last_name, employeeId: watcher?.user?.employee?.id })); if (isEmpty(jobWatchers)) { return; } // Step 3: Retrieve body shop information from the job. // console.log(`3`); const bodyShopId = watcherData?.job?.bodyshop?.id; const bodyShopName = watcherData?.job?.bodyshop?.shopname; if (!bodyShopId || !bodyShopName) { throw new Error("No bodyshop data found for this job."); } // Step 4: Determine matching scenarios based on event data. // console.log(`4`); const matchingScenarios = getMatchingScenarios({ ...eventData, jobWatchers, bodyShopId, bodyShopName }); if (isEmpty(matchingScenarios)) { return; } const finalScenarioData = { ...eventData, jobWatchers, bodyShopId, bodyShopName, matchingScenarios }; // Step 5: Query notification settings for job watchers. // console.log(`5`); const associationsData = await gqlClient.request(queries.GET_NOTIFICATION_ASSOCIATIONS, { emails: jobWatchers.map((x) => x.email), shopid: bodyShopId }); if (isEmpty(associationsData?.associations)) { return; } // Step 6: Filter scenario watchers based on enabled notification methods. // console.log(`6`); finalScenarioData.matchingScenarios = finalScenarioData.matchingScenarios.map((scenario) => ({ ...scenario, scenarioWatchers: associationsData.associations .filter((assoc) => { const settings = assoc.notification_settings && assoc.notification_settings[scenario.key]; return settings && (settings.app || settings.email || settings.fcm); }) .map((assoc) => { const settings = assoc.notification_settings[scenario.key]; const watcherEmail = assoc.user || assoc.useremail; const matchingWatcher = jobWatchers.find((watcher) => watcher.email === watcherEmail); return { user: watcherEmail, email: settings.email, app: settings.app, fcm: settings.fcm, firstName: matchingWatcher ? matchingWatcher.firstName : undefined, lastName: matchingWatcher ? matchingWatcher.lastName : undefined, employeeId: matchingWatcher ? matchingWatcher.employeeId : undefined }; }) })); if (isEmpty(finalScenarioData?.matchingScenarios)) { return; } // Step 7: Trigger scenario builders for matching scenarios with eligible watchers. // console.log(`7`); for (const scenario of finalScenarioData.matchingScenarios) { if (isEmpty(scenario.scenarioWatchers) || !isFunction(scenario.builder)) { continue; } let eligibleWatchers = scenario.scenarioWatchers; // Ensure watchers are only notified if they are assigned to the changed field. if (scenario.matchToUserFields && scenario.matchToUserFields.length > 0) { eligibleWatchers = scenario.scenarioWatchers.filter((watcher) => scenario.matchToUserFields.some( (field) => eventData.changedFieldNames.includes(field) && eventData.data[field]?.includes(watcher.employeeId) ) ); } if (isEmpty(eligibleWatchers)) { continue; } // Step 8: Filter scenario fields to only include changed fields. // console.log(`8`); const filteredScenarioFields = scenario.fields?.filter((field) => eventData.changedFieldNames.includes(field)) || []; scenario.builder({ trigger: finalScenarioData.trigger.name, bodyShopId: finalScenarioData.bodyShopId, bodyShopName: finalScenarioData.bodyShopName, scenarioKey: scenario.key, scenarioTable: scenario.table, scenarioFields: filteredScenarioFields, scenarioBuilder: scenario.builder, scenarioWatchers: eligibleWatchers, jobId: finalScenarioData.jobId, isNew: finalScenarioData.isNew, changedFieldNames: finalScenarioData.changedFieldNames, changedFields: finalScenarioData.changedFields, data: finalScenarioData.data }); } }; module.exports = scenarioParser;