diff --git a/listener/package.json b/listener/package.json index 50575c9..1782113 100644 --- a/listener/package.json +++ b/listener/package.json @@ -7,7 +7,8 @@ "build": "node ./node_modules/typescript/bin/tsc", "start": "node dist/index.js", "test": "node ./node_modules/jest/bin/jest.js", - "migrate": "ts-node src/scripts/migrate-db.ts" + "migrate": "ts-node src/scripts/migrate-db.ts", + "validate:batch": "ts-node src/utils/batch-validator.ts" }, "keywords": [], "author": "", @@ -29,4 +30,4 @@ "ts-node": "^10.9.2", "typescript": "^6.0.3" } -} +} \ No newline at end of file diff --git a/listener/reports/last-validation-run.json b/listener/reports/last-validation-run.json new file mode 100644 index 0000000..6c3bc37 --- /dev/null +++ b/listener/reports/last-validation-run.json @@ -0,0 +1,8 @@ +{ + "isValid": false, + "processedCount": 0, + "errors": [ + "Item at index [1]: Duplicate recipient detected ('discord_channel_alpha'). Batch throttling enforced.", + "Item at index [2]: Missing required fields. (Must contain 'id', 'recipient', 'channel', 'message')" + ] +} \ No newline at end of file diff --git a/listener/src/utils/batch-validator.ts b/listener/src/utils/batch-validator.ts new file mode 100644 index 0000000..d61e1c4 --- /dev/null +++ b/listener/src/utils/batch-validator.ts @@ -0,0 +1,83 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +export interface NotificationPayload { + id: string; + recipient: string; + channel: 'discord' | 'webhook' | 'email'; + message: string; +} + +export interface BatchValidationResult { + isValid: boolean; + processedCount: number; + errors: string[]; +} + +export class BatchValidator { + public static validateBatch(batch: any[]): BatchValidationResult { + const result: BatchValidationResult = { isValid: true, processedCount: 0, errors: [] }; + const seenRecipients = new Set(); + + if (!Array.isArray(batch) || batch.length === 0) { + result.errors.push("Invalid batch structure: Batch must be a non-empty array."); + result.isValid = false; + return result; + } + + batch.forEach((payload, index) => { + const locationId = `Item at index [${index}]`; + + if (!payload.id || !payload.recipient || !payload.channel || !payload.message) { + result.errors.push(`${locationId}: Missing required fields. (Must contain 'id', 'recipient', 'channel', 'message')`); + result.isValid = false; + return; + } + + if (seenRecipients.has(payload.recipient)) { + result.errors.push(`${locationId}: Duplicate recipient detected ('${payload.recipient}'). Batch throttling enforced.`); + result.isValid = false; + } else { + seenRecipients.add(payload.recipient); + } + }); + + if (result.isValid) { + result.processedCount = batch.length; + } + + return result; + } +} + +function runTerminalSimulation() { + const sampleMockBatch = [ + { id: "evt_001", recipient: "discord_channel_alpha", channel: "discord", message: "TaskCreated: Bounty #42 active." }, + { id: "evt_002", recipient: "discord_channel_alpha", channel: "discord", message: "WorkSubmitted: Task completed." }, + { id: "evt_003", recipient: "", channel: "webhook", message: "Missing recipient details" } + ]; + + console.log("šŸš€ Running NotifyChain Batch Validation Check..."); + const validationReport = BatchValidator.validateBatch(sampleMockBatch); + + const reportsDir = path.join(__dirname, '../../reports'); + if (!fs.existsSync(reportsDir)) { + fs.mkdirSync(reportsDir, { recursive: true }); + } + + fs.writeFileSync( + path.join(reportsDir, 'last-validation-run.json'), + JSON.stringify(validationReport, null, 2), + 'utf-8' + ); + + console.log(`\nšŸ“Š Execution Results Logged:`); + console.log(` Status: ${validationReport.isValid ? '🟩 PASSED' : '🟄 REJECTED'}`); + console.log(` Errors Found: ${validationReport.errors.length}`); + validationReport.errors.forEach(err => console.log(` āš ļø ${err}`)); + console.log(`\nšŸ’¾ Saved audit report to: listener/reports/last-validation-run.json`); +} + +if (require.main === module) { + runTerminalSimulation(); +}