UNPKG

@kumologica/kumologica-contrib-sendgrid

Version:

Node that send email using SendGrid platform

286 lines (265 loc) 9.92 kB
module.exports = function (App) { const { KLNode } = require('@kumologica/devkit'); class SendGridError extends Error { constructor(error) { super(error); if (error) { this.name = 'SendGrid Client Failed'; this.message = error.message; } } } class SendGrid extends KLNode { constructor(props) { super(App, props); this.operation = props.operation; this.to = props.to || ''; this.from = props.from; this.cc = props.cc || ''; this.bcc = props.bcc || ''; this.subject = props.subject; this.body = props.body; this.content = props.content; this.replyto = props.replyto; this.templateid = props.templateid; this.contactid = props.contactid; this.unsub = props.unsub; this.group = props.group; this.groupdisplay = props.groupdisplay; this.apikey = props.apikey; this.timeout = parseInt(props.timeout) || 5000; this.sgMail; // Method bindings this.handle = this.handle.bind(this); this.getSendGridClient = this.getSendGridClient.bind(this); } /** * * @param {*} options * @returns a sendGrind client or undefined if an error occurred */ async getSendGridClient(options) { // Reuse existing connection if (this.sgMail) { return this.sgMail; } // New connection is required try { if (options === 'Regular') { this.sgMail = require('@sendgrid/mail'); } else { this.sgMail = require('node-fetch'); } return this.sgMail; } catch (err) { throw err; } } async handle(msg) { let APIkey = App.util.evaluateDynamicField(this.apikey, msg, this) // let Timeout = App.util.evaluateDynamicField(this.timeout, msg, this) if (this.operation === 'Regular') { let To = App.util.evaluateDynamicField(this.to, msg, this); let From = App.util.evaluateDynamicField(this.from, msg, this); let Cc = App.util.evaluateDynamicField(this.cc, msg, this); let Bcc = App.util.evaluateDynamicField(this.bcc, msg, this); let Subject = App.util.evaluateDynamicField(this.subject, msg, this); let Body = App.util.evaluateDynamicField(this.body, msg, this); // Build the sendgrid payload const message = { cc: Cc.split(','), bcc: Bcc.split(','), to: To.split(','), from: From, // Use the email address or domain you verified above subject: Subject, text: Body, html: Body, }; try { let sgClient = await this.getSendGridClient(this.operation); // Timeout will be min between platform (lambda | ifaas | local) and user defined let providerTimeout = App.util.getRemainingTimeInMillis(msg); if (providerTimeout) { sgClient.setTimeout(Math.min(this.timeout, providerTimeout - 50)); } else { sgClient.setTimeout(this.timeout); } sgClient.setApiKey(APIkey); await sgClient.send(message); this.send(msg); } catch (err) { this.sendError(new SendGridError(err), msg); } } else if (this.operation === 'DynamicTemplate') { let abtimeout; let To = App.util.evaluateDynamicField(this.to, msg, this); let From = App.util.evaluateDynamicField(this.from, msg, this); let Cc = App.util.evaluateDynamicField(this.cc, msg, this); let Bcc = App.util.evaluateDynamicField(this.bcc, msg, this); let ReplyTo = App.util.evaluateDynamicField(this.replyto, msg, this); let Subject = App.util.evaluateDynamicField(this.subject, msg, this); let Content = App.util.evaluateDynamicField(this.content, msg, this) || {}; let Group = App.util.evaluateDynamicField(this.group, msg, this); let Groupdisplay = App.util.evaluateDynamicField(this.groupdisplay, msg, this); let Templateid = App.util.evaluateDynamicField( this.templateid, msg, this ); //Content.subject = Subject; const AbortController = require('abort-controller'); // For implementing timeout in node-fetch let controller = new AbortController(); // Timeout let adjustedTimeout = this.timeout; // Override timeout if running in the cloud and remaining timeout if less than user defined let providerTimeout = App.util.getRemainingTimeInMillis(msg); if (providerTimeout) { adjustedTimeout = Math.min(this.timeout, providerTimeout - 50); } // TODO: Have you tested what happen when timeout? is there sendError being sent? abtimeout = setTimeout(() => { controller.abort(); }, adjustedTimeout); let Toarr = To.split(',').map((element) => { return { email: element }; }); let Ccarr; if (Cc) { Ccarr = Cc.split(',').map((element) => { return { email: element }; }); } let Bcarr if (Bcc) { Bcarr = Bcc.split(',').map((element) => { return { email: element }; }); } let body={}; body = { personalizations: [ { to: Toarr, cc: Ccarr, bcc: Bcarr, dynamic_template_data: Content || {}, subject: Subject, }, ], from: { email: From }, template_id: Templateid }; if(this.unsub){ body.asm={}; body.asm.group_id = parseInt(this.group); body.asm.groups_to_display = [parseInt(this.groupdisplay)]; } if(ReplyTo != ''){ body.reply_to = { email: ReplyTo } } try { let sgClient = await this.getSendGridClient(this.operation); let sendgridres = await sgClient('https://api.sendgrid.com/v3/mail/send', { signal: controller.signal, method: 'post', body: JSON.stringify(body), headers: { 'Content-Type': 'application/json', authorization: 'Bearer ' + APIkey, }, }); msg.header.sendgrid = {}; msg.header.sendgrid.statusCode = sendgridres.status; msg.header.sendgrid.statusText = sendgridres.statusText; this.send(msg); return; } catch (error) { //Only the connection related fauilure is caputred as the sendgrid email is not giving any // meaning full response for negative condition. it always respond with {size:0} this.sendError(new SendGridError(error), msg); } finally { // TODO: Do we sendError here?? - Arun : We will not send error here. clearTimeout(abtimeout); } } else if (this.operation === 'GetContactsById') { let abtimeout; const AbortController = require('abort-controller'); // For implementing timeout in node-fetch let controller = new AbortController(); // Timeout let adjustedTimeout = this.timeout; // Override timeout if running in the cloud and remaining timeout if less than user defined let providerTimeout = App.util.getRemainingTimeInMillis(msg); if (providerTimeout) { adjustedTimeout = Math.min(this.timeout, providerTimeout - 50); } abtimeout = setTimeout(() => { controller.abort(); }, adjustedTimeout); let Contactid = App.util.evaluateDynamicField( this.contactid, msg, this ); try { let sgClient = await this.getSendGridClient(this.operation); let res = await sgClient( `https://api.sendgrid.com/v3/marketing/contacts/${Contactid}`, { signal: controller.signal, method: 'get', headers: { authorization: 'Bearer ' + APIkey, }, } ); msg.payload = await res.json(); this.send(msg); } catch (err) { this.sendError(new SendGridError(err), msg); } finally { clearTimeout(abtimeout); } } else if (this.operation === 'GetAllContacts') { let abtimeout; const AbortController = require('abort-controller'); // For implementing timeout in node-fetch let controller = new AbortController(); // Timeout let adjustedTimeout = this.timeout; // Override timeout if running in the cloud and remaining timeout if less than user defined let providerTimeout = App.util.getRemainingTimeInMillis(msg); if (providerTimeout) { adjustedTimeout = Math.min(this.timeout, providerTimeout - 50); } abtimeout = setTimeout(() => { controller.abort(); }, adjustedTimeout); try { let sgClient = await this.getSendGridClient(this.operation); let res = await sgClient( 'https://api.sendgrid.com/v3/marketing/contacts', { signal: controller.signal, method: 'get', headers: { authorization: 'Bearer ' + APIkey, }, } ); if (res) { msg.payload = await res.json(); } else { this.warn('Sendgrid response was empty'); } this.send(msg); } catch (err) { this.sendError(new SendGridError(err), msg); } finally { clearTimeout(abtimeout); } } else { this.sendError(`Invalid operation: ${this.operation}`, msg); } } } App.nodes.registerType('SendGrid', SendGrid); };