@kumologica/kumologica-contrib-sendgrid
Version:
Node that send email using SendGrid platform
286 lines (265 loc) • 9.92 kB
JavaScript
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);
};