Loopback send email with attachment is not working with dynamic - node.js

I have model where i can store the values and send an email. I need to send an email with attachment but its is not working it is throwing some error.
can anyone help me how to send an email with attachment.
career.js
'use strict';
const app = require('../../server/server');
module.exports = function(Career) {
Career.afterRemote('create', function(context, remoteMethodOutput, next) {
next();
// console.log(context.result)
Career.app.models.Email.send({
to: 'lakshmipriya.l#gmail.com',
from: 'lakshmipriya.l#gmail.com',
subject: 'Career Form',
html: '<em>Hi,</em>',
attachments: [
{ // utf-8 string as an attachment
path: './files/resume/860e032e-a8e6-478a-beeb-6a7225ead701.docx'
}
],
},
function(err, mail) {
// console.log(context.result.email)
console.log('email sent!');
console.log(err);
});
});

Per the stack trace, this line is calling an undefined function: cb(err).
To get the reason for the mail failure you'll want to print the err.

Instead of cb you should call next, there is no cb function defined in your code (probably you took the code from 2 different examples, that's why cb is there).
Secondly, before you will call next(err) I would check if the error exists, otherwise, your code will call the next tick if there is no error.

Related

Twilio messages not firing on cron lamba

I've been working on this issue for a few hours with no success. I am triggering the below function to run every 15 minutes. I can see from the CloudWatch that the function is firing. From my logs, I know that the function successfully getting appointments that require notification be sent. I am using 2 try/catch blocks to try and get some error message coming through, but no errors are being logged.
When I run this function using sls invoke local, it works fine and sends the expected text message to the correct numbers. However, when deployed and run on a cron-basis, its not working but also not erroring out - so I am at a loss.
I think it's an async/await issue. Basically, the console logs show up in the Cloudwatch logs, but nothing inside the twilioClient.messages.create function comes out in the logs.
Any help would be appreciated - I am sure it's something simple, but been staring at this for a few hours now without any success!
function sendNotifications(appointments) {
console.log('require notifications', appointments.length);
appointments.forEach(function(appointment) {
// Create options to send the message
// Appointments show in the logs
console.log('appt', appointment);
console.log('from', process.env.TWILIO_PHONE_NUMBER);
try {
const options = {
to: `${appointment.meta.contact.number}`,
from: process.env.TWILIO_PHONE_NUMBER,
/* eslint-disable max-len */
body: `Hi ${appointment.meta.contact.name}. Just a reminder that you have an property viewing for ${appointment.meta.property.name} at ${appointment.date}, ${appointment.time}. Please reply with CONFIRM to confirm that you'll be attending this viewing or CANCEL BOOKING to cancel this viewing.`
/* eslint-enable max-len */
};
// Send the message! - this log displays
console.log('about to send message');
twilioClient.messages.create(options, function(err, response, callback) {
// Nothing in this block gets printed to the logs
if (err) {
// Just log it for now
console.log('ERROR', err);
} else {
// Log the last few digits of a phone number
let masked = appointment.meta.contact.number.substr(
0,
appointment.meta.contact.number.length - 5
);
masked += '*****';
console.log(`Message sent to ${masked}`);
try {
updateBooking({ booking: appointment, message: response });
} catch (e) {
console.log(e.message);
}
}
// Don't wait on success/failure, just indicate all messages have been
// queued for delivery
if (callback) {
callback.call();
}
});
} catch (e) {
console.log('ERR', e.message, appointment);
}
});
}
From the doc https://www.twilio.com/docs/sms/quickstart/node
twilioClient.messages.create does not have a callback params, but returns a Promise
Try to execute the Lambda with:
twilioClient.messages
.create({
body: 'just for test',
from: 'a verified number',
to: 'a verified number'
})
.then(message => console.log(message.sid));
If this works, then that was the issue.
Otherwise could be process.env.TWILIO_PHONE_NUMBER is not set correctly or ${appointment.meta.contact.number} may be missing or invalid. Maybe print those also.
Twilio could also not have been correctly set up. Make sure accountSid and authToken are set.

Nodemailer - Sendmail transporter Error: TypeError: this._spawn is not a function

Following the online guide of Nodemailer and using the package, I created a basic sendmail transporter in my React project, looking like this:
handleSubmit(event) {
event.preventDefault();
console.log("ok")
let transporter = nodemailer.createTransport({"sendmail": true});
transporter.sendMail({
from: '"Firstname Lastname" <me#example.com>', // sender address
to: "me#example.com", // list of receivers
subject: "Hello", // Subject line
html: "<b>My mail from React!</b>" // html body
});
}
However, when clicking on the submit, button, I receive following error:
TypeError: this._spawn is not a function
Does someone now why this problem occurs? Whenever I find other examples about sendmail, they all look semi-exaclty like the above.
Error code
Location:
node_modules/nodemailer/lib/sendmail-transport/index.js
Code block
try {
sendmail = this._spawn(this.path, args); <<<<<<<<<<<< This is where the error occurs <<<<<<<<<<<<
} catch (E) {
this.logger.error(
{
err: E,
tnx: 'spawn',
messageId
},
'Error occurred while spawning sendmail. %s',
E.message
);
return callback(E);
}

Getting Mailgun error when sending email with attachment as part of async series

I have a NodeJs application that runs a variation of tasks but one of them is to produce a report and email it to my client. I am using Mailgun to send the email and have no issues with it sending email elswhere in my code.
As the report needs to be generated into a CSV file which could be lengthy and then attached to the email I have written them into an async series.
The problem is when I put everything together, I get an error message from Mailgun
{ message: '\'from\' parameter is missing' }
My code is very simple as below
async.series([
function(callback) {
User.find({}, function(err, foundUsers){
if(err) {
console.log(err);
} else {
User.csvReadStream(foundUsers)
.pipe(fs.createWriteStream('dailyReport.csv'));
callback(null, 'Created Report');
}
});
},
function(callback) {
sendReport();
callback(null, 'Sent Email');
}
],
function(err, results) {
console.log(results);
});
With the function here
function sendReport () {
var filepath = path.join(__dirname, '../', 'dailyReport.csv');
var data = {
from: "mailgun#myemail.co.uk",
to: 'me#myemail.co.uk',
subject: 'Daily Report',
text: 'Daily CSV report from ...',
attachment: filepath
};
console.log(data);
mg.messages().send(data, function (error, body) {
console.log(body);
});
console.log("Report Sent");
return;
}
I have sent a support message to Mailgun but they have been unable to help so far.
The strange thing is, if I call the function outside of the series, the email is sent. If I call the function inside the series but without the attachment element, it also sends the email!
I am wondering if it's something fundamental with the way the series (I have also tried this with async waterfall) handles the variables.

If statement running after function being called

I'm setting up a mailer to send an email to the relevant recipient about some details of an appointment request.
I'm having an issue where the IF statement that runs to decide who the necessary recipient should be is running after the email transporter and is giving me an error saying that no recipient has been defined.
Here is the code
let recipientEmail;
if (careHome === 'ACareHome') {
admin.database().ref('managers').once("value").then((snapshot) => {
let managerEmail = snapshot.child("Manager Name").val();
recipientEmail = managerEmail;
console.log(`Recipient is ${recipientEmail}`);
});
}
const mailOptions = {
from: '*****', // sender address
subject: `An appointment has been requested at ${ACareHome}.`,
html: `Hello, an appointment has been booked at ${ACareHome} on ${date} at ${time}. The requestors name is, ${firstName}. You can email them on ${email}.`
};
mailOptions.to = recipientEmail;
transporter.sendMail(mailOptions, function(error, info){
if(error){
return console.log(error);
}
console.log('Message sent: ' + info.response);
})
In the Firebase logs, I am getting these errors
It's showing that the IF statement is being run after the transporter function causing it to error.
I've tried all manor of things but cant seem to get it to play ball!
Help is appreciated, cheers!
Pulling data from your database is async, your current code ignores that and tries to continue before the promise has resolved.
This is quite a common error when people aren't used to promises. Promises are basically pretty callbacks, instead of (err, info) you get a function of .then and .catch (and a few others).
You need to put your email code inside the .then function. So it'll become something like this:
function sendEmail() {
let recipientEmail;
if (careHome === 'ACareHome') {
admin.database().ref('managers').once("value").then((snapshot) => {
let managerEmail = snapshot.child("Manager Name").val();
recipientEmail = managerEmail;
console.log(`Recipient is ${recipientEmail}`);
const mailOptions = {
from: '*****', // sender address
subject: `An appointment has been requested at ${ACareHome}.`,
html: `Hello, an appointment has been booked at ${ACareHome} on ${date} at ${time}. The requestors name is, ${firstName}. You can email them on ${email}.`
};
mailOptions.to = recipientEmail;
return new Promise(function promise(resolve, reject) {
transporter.sendMail(mailOptions, function(error, info){
if (error) {
return console.log(error);
return reject(error);
}
console.log('Message sent: ' + info.response);
return resolve(info);
});
});
});
}
}
I've done three things here.
I've wrapped everything in a function for ease.
I've moved the email sending code into the promise .then function. This means once something is fetched from the database you're sending the email, so you're now waiting for the response.
I've wrapped the transporter.sendMail in a function. There are utilities for doing this but for clarity I've shown you how to do this by hand. This means the new function from point 1 now returns a promise. You can now use the top function sendMail the same way you've done your database code, by calling sendMail().then(result...).catch(error...).
It's often more idiomatic to not mix callback and promise code by wrapping up any callback oriented code with things like Bluebird.promisify, or wrapping up a callback oriented function by hand the way I have. This means you're just dealing with .then and .catch type code.
I apologies if you already knew about Promises vs Callbacks, it's just a common theme I see on questions so I've answered it in full.

Async code inside node uncaughtException handler

I have a node app (version 8.11.1 using Typescript 2.8.1) that catches uncaught exceptions using the uncaughtException hook like this:
process.on('uncaughtException', (err) => {
await sendEmail(recipient, subject, body);
});
I'm calling an asynchronous method inside the handler to send out an email and the code isn't working. It appears that the node process dies before the async call can finish and I never receive the email.
It looks like you may not be able to successfully use async methods inside this handler. The documentation doesn't say that outright but it implies it stating
The correct use of 'uncaughtException' is to perform synchronous
cleanup of allocated resources
I'm not trying resume operation or do anything funky. All I want to do is send out and email stating that the system crashed. I am not able to find any libraries that will synchronously send emails so I'm at a bit of a loss on how to handle this.
I've seen one suggestion to synchronously write the data to a file (or database) and have some outside processing polling for the existence of that file and sending the email if it exists. I suppose that would work but it's pretty hacky. Any better solutions?
Update:
Okay well after running some more tests it looks like you can actually run async code from inside the uncaughtException handler just fine. The following works:
const mailer = require('nodemailer');
process.on('uncaughtException', async err => {
const transport = mailer.createTransport({
service: 'gmail',
auth: {
user: 'someone#email.com',
pass: 'abc'
}
});
const mailOptions = {
from: 'someone#email.com',
to: 'someone.else#email.com',
subject: 'hi',
text: 'there'
};
transport.sendMail(mailOptions, (error, info) => {
if (error) {
console.log(error);
}
else {
console.log(info);
}
});
});
throw new Error('boom');
The above code works fine as a standalone app, but if I copy it into my codebase, it doesn't work. The code executes but I don't get an email (presumably the app dies before it can finish sending). So there must be something else going on in my environment that is preventing it from working. I'll keep digging.
I don't know what library you use to send an email and what version of node js you use but if you are using node js version greater than 7 you can use async/await and send an email as below
var mailgun = require('mailgun-js')({apiKey: api_key, domain: domain});
process.on('uncaughtException', async (err) => {
var data = {
from: 'User <me#samples.mailgun.org>',
to: 'serobnic#mail.ru',
subject: 'ERROR MESSAGE',
text: `Caught exception: ${err}\n`,
};
var body = await mailgun.messages().send(data);
console.log(body);
});
// using callback - supported for all versions
process.on('uncaughtException', (err) => {
var data = {
from: 'User <me#samples.mailgun.org>',
to: 'serobnic#mail.ru',
subject: 'ERROR MESSAGE',
text: 'Caught exception:' + err,
};
mailgun.messages().send(data, function (err, body) {
console.log(body);
});
});

Resources