/** * Parses an event by comparing old and new data to determine which fields have changed. * * This function analyzes the differences between previous (`oldData`) and current (`newData`) * data states to identify changed fields. It determines if the event is a new entry or an update * and optionally extracts a `jobId` based on a specified field. The result includes details * about changed fields, the event type, and associated metadata. * * @param {Object} options - Configuration options for parsing the event. * @param {Object} [options.oldData] - The previous state of the data (undefined for new entries). * @param {Object} options.newData - The current state of the data. * @param {string} options.trigger - The type of event trigger (e.g., 'INSERT', 'UPDATE'). * @param {string} options.table - The name of the table associated with the event. * @param {string} [options.jobIdField] - The field name used to extract the jobId (optional). * @returns {Object} An object containing the parsed event details: * - {Array} changedFieldNames - List of field names that have changed. * - {Object} changedFields - Map of changed fields with their old and new values. * - {boolean} isNew - True if the event is a new entry (no oldData provided). * - {Object} data - The current data state (`newData`). * - {string} trigger - The event trigger type. * - {string} table - The table name. * - {string|null} jobId - The extracted jobId or null if not applicable. */ const eventParser = async ({ oldData, newData, trigger, table, jobIdField }) => { const isNew = !oldData; // True if no old data exists, indicating a new entry let changedFields = {}; let changedFieldNames = []; if (isNew) { // For new entries, all fields in newData are considered "changed" (from undefined to their value) changedFields = Object.fromEntries( Object.entries(newData).map(([key, value]) => [key, { old: undefined, new: value }]) ); changedFieldNames = Object.keys(newData); // All keys are new } else { // Compare oldData and newData to detect updates for (const key in newData) { if (Object.prototype.hasOwnProperty.call(newData, key)) { // Check if the field is new or its value has changed if ( !Object.prototype.hasOwnProperty.call(oldData, key) || // Field didn’t exist before JSON.stringify(oldData[key]) !== JSON.stringify(newData[key]) // Values differ (deep comparison) ) { changedFields[key] = { old: oldData[key], // Undefined if field wasn’t in oldData new: newData[key] }; changedFieldNames.push(key); } } } // Identify fields removed in newData (present in oldData but absent in newData) for (const key in oldData) { if (Object.prototype.hasOwnProperty.call(oldData, key) && !Object.prototype.hasOwnProperty.call(newData, key)) { changedFields[key] = { old: oldData[key], new: null // Mark as removed }; changedFieldNames.push(key); } } } // Extract jobId if jobIdField is provided let jobId = null; if (jobIdField) { let keyName = jobIdField; const prefix = "req.body.event.new."; // Strip prefix if present to isolate the actual field name if (keyName.startsWith(prefix)) { keyName = keyName.slice(prefix.length); } // Look for jobId in newData first, then fallback to oldData if necessary jobId = newData[keyName] || (oldData && oldData[keyName]) || null; } return { changedFieldNames, // Array of fields that changed changedFields, // Object with old/new values for changed fields isNew, // Boolean indicating if this is a new entry data: newData, // Current data state trigger, // Event trigger (e.g., 'INSERT', 'UPDATE') table, // Associated table name jobId // Extracted jobId or null }; }; module.exports = eventParser;