Skip to content

Commit

Permalink
updates #3 | adds: support for consumer using template, transaction s…
Browse files Browse the repository at this point in the history
…chemas for mail
  • Loading branch information
imsheth committed Jul 30, 2020
1 parent 38f3899 commit 5faf7b9
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 9 deletions.
98 changes: 90 additions & 8 deletions consumer/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,32 @@ const NotificationFactory = require('./factories/notificationFactory.js');
const MongoClient = require('mongodb').MongoClient;
const NotificationConfig = require('./configurations/configuration.json');
const NOTIFICATION_STATUSES = NotificationConfig.statuses;
let TIMEOUT = 1000*60*5;
const NOTIFICATION_ENABLED_FLAGS = NotificationConfig.enabled_flags;
let TIMEOUT = 1000;

// Processing the notifications based on their type
const sendTypeBasedNotifications = async (notification) => {
// console.log('sendTypeBasedNotifications');
// Processing the notifications based on their type and trigger_event
const typeBasedNotificationProcessing = async (notification) => {
// console.log('typeBasedNotificationProcessing notification', notification);
// Get class based on the type of notification from the factory
const notificationClassForType = NotificationFactory.getClassForType(notification.type);
// Wait until sendNotification resolves or rejects
await notificationClassForType.prototype.sendNotification(notification);
// Wait until event defined by trigger_event resolves or rejects
if (notification.trigger_event === 'send') {
await notificationClassForType.prototype.sendNotification(notification);
} else if (notification.trigger_event === 'create') {
await notificationClassForType.prototype.createNotification(notification);
} else {
return new Promise(async (resolve, reject) => {
resolve({});
});
}

}

// Processing the notifications
const processNotifications = async (toBeSentNotifications) => {
// console.log('processNotifications');
// Map array to promises
const toBeSentNotificationsPromises = toBeSentNotifications.map(sendTypeBasedNotifications);
const toBeSentNotificationsPromises = toBeSentNotifications.map(typeBasedNotificationProcessing);
// Wait until all toBeSentNotificationsPromises are resolved
return Promise.all(toBeSentNotificationsPromises);
}
Expand Down Expand Up @@ -69,12 +79,83 @@ const readNotifications = async (notificationConfig) => {
});
}





// Reset eligible failed notifications and read from schema
const createNotificationsFromSchema = async () => {
// console.log('createNotificationsFromSchema ');
const MONGOURL = process.env.MONGOURL || '';

// Verify
if (MONGOURL) {
// Connect
const mongoClient = await MongoClient.connect(MONGOURL, {
useUnifiedTopology: true
});
try {
// Read
const templatesDB = mongoClient.db(NotificationConfig['base']['template']['database_name']);
const templatesCollection = templatesDB.collection(NotificationConfig['base']['template']['schema_name']);
const readTemplatesResult = await templatesCollection.find({ is_enabled: NOTIFICATION_ENABLED_FLAGS['ENABLED'] }).toArray();

const transactionsDB = mongoClient.db(NotificationConfig['base']['transaction']['database_name']);
const transactionsCollection = transactionsDB.collection(NotificationConfig['base']['transaction']['schema_name']);
const readTransactionsResult = await transactionsCollection.find({ status: NOTIFICATION_STATUSES['NOT_PROCESSED'] }).limit(NotificationConfig['base']['transaction']['batch_size']).toArray();

// Update to PROCESSING
const updateStatusResult = await transactionsCollection.updateMany({ '_id': { $in: readTransactionsResult.map(x => x._id) } }, { $set: { status: NOTIFICATION_STATUSES['PROCESSING'] } });

// Create hash for templates
const readTemplatesObject = new Map(readTemplatesResult.map(i => [i.slug, i]));

// Insert the template into template key
readTransactionsResult.forEach((notificationTransaction) => {
notificationTransaction.template = readTemplatesObject.get(notificationTransaction.notification_template_slug);
});

return readTransactionsResult;
} catch (dualReadError) {
throw dualReadError;
} finally {
// Disconnect
mongoClient.close();
}
} else {
throw { message: 'Improper credentials, createNotificationsFromSchema failed' };
}
}

// Creating the notifications from the schema of objects in notificationConfig
const createNotifications = async (notificationConfig) => {
// console.log('createNotifications');
// Map array to promises
const fetchToBeSentNotificationsPromises = await createNotificationsFromSchema();

// const {client, db} = getDb();
// Wait until all fetchToBeSentNotificationsPromises are resolved
Promise.all(fetchToBeSentNotificationsPromises).then(async (toBeSentNotificationsResult) => {
// console.log('createNotifications.toBeSentNotificationsResult ', toBeSentNotificationsResult);
await processNotifications(toBeSentNotificationsResult.flat());
// client.close();
}).catch((toBeSentNotificationsError) => {
console.log('createNotifications.toBeSentNotificationsError ', toBeSentNotificationsError);
process.exit(-1);
});
}





// Entry point
const startNotifEngine = async () => {
// Execute after every TIMEOUT ms
setInterval(() => {
console.log('\nengine iteration invoked at ', new Date().toString());
console.time('iteration_time');
createNotifications(NotificationConfig);
readNotifications(NotificationConfig);
console.log('\nengine iteration returned at ', new Date().toString());
console.timeEnd('iteration_time');
Expand All @@ -83,4 +164,5 @@ const startNotifEngine = async () => {

// Fire up the engine
startNotifEngine();
// readNotifications(NotificationConfig);
// readNotifications(NotificationConfig);
// createNotifications(NotificationConfig);
116 changes: 116 additions & 0 deletions consumer/classes/notificationMail.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ const NOTIFICATION_STATUSES = NotificationConfig.statuses;
notificationMail = () => {
};

const replacementHandler = (input, transformationMapping) => {
transformationMapping["{{"] = '';
transformationMapping["}}"] = '';
// console.log(transformationMapping);
return input.replace(new RegExp(Object.keys(transformationMapping).join('|'), 'gi'), (matched) => {
return transformationMapping[matched];
});
};

notificationMail.prototype = {
// Send notification by PROCESSING => sendMail => SENDING_SUCCEEDED or SENDING_FAILED
sendNotification: (notification) => {
Expand Down Expand Up @@ -72,6 +81,113 @@ notificationMail.prototype = {
break;
}
});
},
// Create notifications for sending
createNotification: (notification) => {
return new Promise(async (resolve, reject) => {
const MONGOURL = process.env.MONGOURL || '';

// Verify
if (MONGOURL) {

const generatedMails = [];

// Process only if template and email_template exist
if (notification['template'] && notification['template']['email_template'] && notification['template']['email_template']['subject'] && notification['template']['email_template']['html']) {

// Generate mails to be stored
notification.data.forEach((toBeGeneratedNotificationsData) => {
let newGeneratedMail = {};
newGeneratedMail.trigger_event = 'send';
newGeneratedMail.type = notification.type;
newGeneratedMail.provider = notification.provider;
newGeneratedMail.notification_transaction_id = notification._id;
newGeneratedMail.user_id = toBeGeneratedNotificationsData.user_id;
newGeneratedMail.user_email = toBeGeneratedNotificationsData.user_email;

newGeneratedMail.data = {
from: NotificationConfig['dynamic']['mail']['from_string'],
to: newGeneratedMail.user_email,
subject: notification['template']['email_template']['subject'][toBeGeneratedNotificationsData.user_language],
html: replacementHandler(notification['template']['email_template']['html'][toBeGeneratedNotificationsData.user_language], toBeGeneratedNotificationsData) // String replace to generate the html
};

// Helps identify sending server in the mail subject
if (notification.server && notification.server !== 'production') {
newGeneratedMail.data.subject = newGeneratedMail.data.subject + ' ( ' + notification.server + ' ) ';
}

// Override attachments defined in the template with those in transaction
if (toBeGeneratedNotificationsData.attachments || notification['template']['email_template']['attachments']) {
newGeneratedMail.data.attachments = toBeGeneratedNotificationsData.attachments ? toBeGeneratedNotificationsData.attachments : notification['template']['email_template']['attachments'];
}

newGeneratedMail.status = NOTIFICATION_STATUSES['NOT_PROCESSED'];
newGeneratedMail.service_response = null;
newGeneratedMail.try_count = 0;

generatedMails.push(newGeneratedMail);
});

// Connect
const mongoClient = await MongoClient.connect(MONGOURL, {
useUnifiedTopology: true
});

try {
// Insert mails
const mailsDB = mongoClient.db(NotificationConfig['dynamic']['mail']['database_name']);
const createNotificationResult = generatedMails.length > 1 ? await mailsDB.collection(NotificationConfig['dynamic']['mail']['schema_name']).insertMany(generatedMails) : await mailsDB.collection(NotificationConfig['dynamic']['mail']['schema_name']).insertOne(generatedMails[0]);
// console.log('createNotificationResult ', createNotificationResult);

const notificationTransactionDB = mongoClient.db(NotificationConfig['base']['transaction']['database_name']);
const notificationTransactionCollection = notificationTransactionDB.collection(NotificationConfig['base']['transaction']['schema_name']);

if (!createNotificationResult) {
// Update flag and reject
const failedResult = await notificationTransactionCollection.updateOne({ _id: notification._id }, { $set: { status: NOTIFICATION_STATUSES['SENDING_FAILED'] } });
console.log('notif-engine.notificationMail.createNotification.!createNotificationResult ');
reject({ message: 'notif-engine.notificationMail.createNotification.!createNotificationResult ' });
} else {
// Update flag and resolve
const succeededResult = await notificationTransactionCollection.updateOne({ _id: notification._id }, { $set: { status: NOTIFICATION_STATUSES['SENDING_SUCCEEDED'] } });
console.log('notif-engine.notificationMail.createNotification.createNotificationResult notification_transaction_id', notification._id);
resolve(createNotificationResult);
}

} catch (error) {
// Catch all errors
console.log('notif-engine.notificationMail.createNotification.mongo.error ');
reject({ message: 'notif-engine.notificationMail.createNotification.error ' });
} finally {
// Disconnect
mongoClient.close();
}
} else {
// Update status to SENDING_FAILED
// Connect
const mongoClientForNotificationTransactionNoTemplate = await MongoClient.connect(MONGOURL, {
useUnifiedTopology: true
});

try {
const notificationTransactionDBNoTemplate = mongoClientForNotificationTransactionNoTemplate.db(NotificationConfig['base']['transaction']['database_name']);
const notificationTransactionCollectionNoTemplate = notificationTransactionDBNoTemplate.collection(NotificationConfig['base']['transaction']['schema_name']);
const failedResultNoTemplate = await notificationTransactionCollectionNoTemplate.updateOne({ _id: notification._id }, { $set: { status: NOTIFICATION_STATUSES['SENDING_FAILED'] } });
resolve(failedResultNoTemplate);
} catch (noTemplateError) {
console.log('notif-engine.notificationMail.createNotification.!notification[\'template\'] ');
reject({ message: 'notif-engine.notificationMail.createNotification.!notification[\'template\'] ', data: noTemplateError });
} finally {
// Disconnect
mongoClientForNotificationTransactionNoTemplate.close();
}
}

} else {
reject({ message: 'Improper credentials, notificationMail.createNotification failed' });
}
});
}
}

Expand Down
6 changes: 6 additions & 0 deletions consumer/classes/notificationPush.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ notificationPush.prototype = {
break;
}
});
},
// TODO : Create notifications for sending
createNotification: (notification) => {
return new Promise(async (resolve, reject) => {
resolve({});
});
}
}

Expand Down
18 changes: 17 additions & 1 deletion consumer/configurations/configuration.json.template
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"database_name": "<database_name_here>",
"schema_name": "<schema_name_here>",
"batch_size": 100,
"max_retries": 3
"max_retries": 3,
"from_string": "'Full Name' <email@address.com>"
},
"sms": {
"database_name": "<database_name_here>",
Expand All @@ -27,10 +28,25 @@
"max_retries": 3
}
},
"base": {
"template": {
"database_name": "<database_name_here>",
"schema_name": "<schema_name_here>"
},
"transaction": {
"database_name": "<database_name_here>",
"schema_name": "<schema_name_here>",
"batch_size": 100
}
},
"statuses": {
"SENDING_FAILED": -2,
"NOT_PROCESSED": -1,
"PROCESSING": 0,
"SENDING_SUCCEEDED": 1
},
"enabled_flags": {
"ENABLED": 1,
"DISABLED": 0
}
}

0 comments on commit 5faf7b9

Please sign in to comment.