Foreach loop not getting completed - javascript

I'm using Node js, nodemailer and firebase functions in my application.
I've a list of emails in an array emailConfig
const emailConfig = [
{
"name": "A",
"Email": "A#gmail.com"
},
{
"name": "B",
"Email": "B#gmail.com"
}
];
So I need to send an email to all in emailConfig.
So I'm doing this,
function sendMail() {
emailConfig.forEach(email => {
const mailOptions = {
from: 'abc#gmail.com',
to: email.Email,
subject: 'Sending Email using Node.js',
text: `That was easy! ${email.name}`,
attachments: [
{
filename: `${email.name}_KeyMetrics.xlsx`,
path: `${tempath}/${email.name}_KeyMetrics.xlsx`
},
{
filename: `${email.name}_MandatoryCourses.xlsx`,
path: `${tempath}/${email.name}_MandatoryCourses.xlsx`
},
]
};
return transporter.sendMail(mailOptions, (erro, info) => {
if (erro) {
return res.send(erro.toString());
}
return res.send('Sended');
});
});
}
I'm calling the sendMail() on request.
Issue is, I'm getting multiple emails and finally error in terminal Error: Function timed out.
sendMail() is not getting finished. What i'm doing wrong here ?

you cannot call send() after its first call on the single response (assuming your res is a Response socket).
Array.forEach is synchronous, your callbacks will no be handled properly.

Function in forEach is does not return any result. So since you call return keyword, your thread stop at there. Try to remove return keyword like this:
function sendMail() {
emailConfig.forEach(email => {
const mailOptions = {
from: 'abc#gmail.com',
to: email.Email,
subject: 'Sending Email using Node.js',
text: `That was easy! ${email.name}`,
attachments: [
{
filename: `${email.name}_KeyMetrics.xlsx`,
path: `${tempath}/${email.name}_KeyMetrics.xlsx`
},
{
filename: `${email.name}_MandatoryCourses.xlsx`,
path: `${tempath}/${email.name}_MandatoryCourses.xlsx`
},
]
};
//remove return below
transporter.sendMail(mailOptions, (erro, info) => {
if (erro) {
return res.send(erro.toString());
}
return res.send('Sended');
});
});
}
Or if you need an array of result. Try .map() prototype instead:
function sendMail() {
emailConfig.map(email => { //replace forEach by map
const mailOptions = {
from: 'abc#gmail.com',
to: email.Email,
subject: 'Sending Email using Node.js',
text: `That was easy! ${email.name}`,
attachments: [
{
filename: `${email.name}_KeyMetrics.xlsx`,
path: `${tempath}/${email.name}_KeyMetrics.xlsx`
},
{
filename: `${email.name}_MandatoryCourses.xlsx`,
path: `${tempath}/${email.name}_MandatoryCourses.xlsx`
},
]
};
return transporter.sendMail(mailOptions, (erro, info) => {
if (erro) {
return res.send(erro.toString());
}
return res.send('Sended');
});
});
}

Related

can not recognize the property of a variable while using the nodemailer module in node.js

I am creating a mailer through the node.js nodemailer module.
I created mails as arrays, and I created properties for each array through an object literal.
And when I started Mailer, I was having trouble recognizing mailList[i].name.
What's wrong with my code?
mailer code
const nodemailer = require("nodemailer");
nodemailer.createTestAccount((err, account) => {
let transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: "test#gmail.com",
pass: "123456"
}
});
let mails = ["test1#gmail.com", "test2#gmail.com"];
let mailList = {
"mails[1]": {
name: "Tom",
},
"mails[2]": {
name: "Lexy",
}
};
let go = () => {
for (let i = 1; i < 30; i++) {
let setting = {
from: "test#gmail.com",
to: mails[i],
subject: "Title",
text: ` Hi, ${mailList[i].name}`
};
}
};
transporter.sendMail(go(), (error, response) => {
if (error) {
return console.log(error);
}
console.log(response);
});
});
The problem there, is that you are calling mailList[i].name and i will never be a valid index for that object. Instead, you should do:
let setting = {
from: "test#gmail.com",
to: mails[i],
subject: "Title",
text: ` Hi, ${mailList["mails[" + i + "]"].name}`
};
But here, I would like to make a suggestion. Why use two structures, when you could easily this data in an array of objects? Then, your code would become:
nodemailer.createTestAccount((err, account) => {
let transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: "test#gmail.com",
pass: "123456"
}
});
let mails = [
{
name: "Tom",
email: "test1#gmail.com"
},
{
name: "Tom",
email: "test1#gmail.com"
}
]
let go = () => {
for (const mail of mails) {
let setting = {
from: "test#gmail.com",
to: mail.email,
subject: "Title",
text: ` Hi, ${mail.name}`
};
}
};
transporter.sendMail(go(), (error, response) => {
if (error) {
return console.log(error);
}
console.log(response);
});
});
This way, you fit all the information of the contact into a single object, instead of having pieces of it all around your code.
nodemailer.createTestAccount((err, account) => {
let transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: "test#gmail.com",
pass: "123456"
}
});
let mails = [
{
name: "Tom",
email: "test1#gmail.com"
},
{
name: "Tom",
email: "test1#gmail.com"
}
]
let go = () => {
for (const mail of mails) {
let setting = {
from: "test#gmail.com",
to: mail.email,
subject: "Title",
text: ` Hi, ${mail.name}`
};
//validate setting object is set correctly before calling the sendMail method
transporter.sendMail(setting, (error, response) => {
if (error) {
return console.log(error);
}
console.log(response);
});
}
};
go();
});

Iterate items in array through NodeJS script

I am trying to find a solution to creating multiple assets in Contentful using the contentful-management API.
The nodeJS script to achieve a single creation of an asset is
const client = contentful.createClient({
accessToken: '<content_management_api_key>'
})
client.getSpace('<space_id>')
.then((space) => space.getEnvironment('<environment-id>'))
.then((environment) => environment.createAssetWithId('<asset_id>', {
title: {
'en-US': 'Example 1'
},
file: {
'en-US': {
contentType: 'image/jpeg',
fileName: 'example1.jpeg',
upload: 'https://example.com/example1.jpg'
}
}
}))
.then((asset) => asset.processForAllLocales())
.then((asset) => asset.publish())
.then((asset) => console.log(asset))
.catch(console.error)
Which is quite simple and easily implemented. However, when wanting to create multiple assets, this does not work.
After many hours looking for a documented way to achieve this, with no avail, I came to
const contentful = require('contentful-management');
const assets = require('./assetObject.js');
async () => {
const client = contentful.createClient({
accessToken: '<content_management_api_key>'
});
const space = await client.getSpace('<space_id>');
const environment = await space.getEnvironment('<environment-id>');
const createdAssets = await Promise.all(
assets.map(
asset =>
new Promise(async () => {
let cmsAsset;
try {
cmsAsset = await environment.createAssetWithId(asset.postId, {
fields: {
title: {
'en-US': asset.title
},
description: {
'en-US': asset.description
},
file: {
'en-US': {
contentType: 'image/jpeg',
fileName: asset.filename,
upload: asset.link
}
}
}
});
} catch (e) {
throw Error(e);
}
try {
await cmsAsset.processForAllLocales();
} catch (e) {
throw Error(e);
}
try {
await cmsAsset.publish();
} catch (e) {
throw Error(e);
}
})
)
);
return createdAssets;
};
assetObject.js
[
{
link: 'https://example.com/example1.jpg',
title: 'Example 1',
description: 'Description of example 1',
postId: '1234567890',
filename: 'example1.jpeg'
}, ... // Many more
]
This, when running, produces no errors, nor does it do anything. What have I done wrong? Is this the method I should use?
A new promise need to be "resolved" and "rejected" so for me the code should look like
const createdAssets = await Promise.all(
assets.map(
asset =>
new Promise(async (resolve, reject) => {
try {
const cmsAsset = await environment.createAssetWithId(asset.postId, {
fields: {
title: {
'en-US': asset.title
},
description: {
'en-US': asset.description
},
file: {
'en-US': {
contentType: 'image/jpeg',
fileName: asset.filename,
upload: asset.link
}
}
}
});
await cmsAsset.processForAllLocales();
await cmsAsset.publish();
resolve(cmsAsset);
} catch (e) {
reject(e);
}
})
)
);
return createdAssets;
Hop it can help

How can I access a function within a router javascript file via node?

I'm handling with an webapplication that is not mine and now I've got to send a hundred e-mails.
Unfortunately, the code is not documented and not so well written, that means i have to go testing it to discover what I am able to do and what I'm not. but I don't know how to access this function that is on the code via node. Is it actually possible to do it? Here's the code:
router.post('/aprovadosemail', miPermiso("3"), (req, res) => {
var templatesDir = path.resolve(__dirname, '..', 'templates');
var emailTemplates = require('email-templates');
// Prepare nodemailer transport object
emailTemplates(templatesDir, function(err, template) {
if (err) {
console.log(err);
} else {
var users = [];
projetoSchema.find({"aprovado":true, "categoria":"Fundamental II (6º ao 9º anos)"}, function (err, docs) {
if (err) throw err;
//console.log(docs);
docs.forEach(function(usr) {
let url = "http://www.movaci.com.b/projetos/confirma/"+usr._id+"/2456";
let url2 = "http://www.movaci.com.br/projetos/confirma/"+usr._id+"/9877";
users.push({'email': usr.email, 'projeto': usr.nomeProjeto, 'url': url, 'url2': url2});
});
for (var i = 0; i < users.length; i++) {
console.log(users[i]);
}
const transporter = nodemailer.createTransport(smtpTransport({
host: 'smtp.zoho.com',
port: 587,
auth: {
user: "generic#mail.com",
pass: "genericpassword"
},
getSocket: true
}));
var Render = function(locals) {
this.locals = locals;
this.send = function(err, html, text) {
if (err) {
console.log(err);
} else {
transporter.sendMail({
from: 'no-reply4#movaci.com.br',
to: locals.email,
subject: 'MOVACI - Projeto aprovado!',
html: html,
text: text
}, function(err, responseStatus) {
if (err) {
console.log(err);
} else {
console.log(responseStatus.message);
}
});
}
};
this.batch = function(batch) {
batch(this.locals, templatesDir, this.send);
};
};
// Load the template and send the emails
template('rateada', true, function(err, batch) {
for(var user in users) {
var render = new Render(users[user]);
render.batch(batch);
};
});
res.send('ok');
});
};
});
});
Seems like previous dev did not knew email-templates package deeply (at least have not read how it works).
So in fact it has send method, You can create an email object from email-templates and pass necessary defaults, then You call .send method of it by passing dynamical parts - it just simply merge additional params passed in send arguments, sends mail using nodemailer inside of promise which it returns.
If it's interesting for You - read source code of it: https://github.com/niftylettuce/email-templates/blob/master/index.js
I tried to simplify it modular parts using promises.
I've not debugged it, but You may check my solution and fix it as You wish.
Have 2 files (to routing from handler separately, it may have variables that may conflict and etc):
1) methods/users/aprovadosEmail.js:
const
Email = require('email-templates'),
const
emailTemplatesDir = path.resolve(__dirname + '/../../templates'),
smtpTransportConfig = {
host: 'smtp.zoho.com',
port: 587,
secure: false,
auth: {
user: "no-reply4#movaci.com.br",
pass: "some-password-here"
}
},
createEmail = (template, subject) => {
return new Email({
views: {
root: emailTemplatesDir,
},
transport: smtpTransportConfig,
template,
message: {
from: 'no-reply4#movaci.com.br',
subject
}
});
},
getApprovedUsers = () => {
return new Promise((resolve, reject) => {
const criteria = {
aprovado: true,
categoria:"Fundamental II (6º ao 9º anos)"
};
projetoSchema.find(
criteria,
(error, docs) => {
if(error) return reject(error);
const users = docs.map(doc => {
return {
email: doc.email,
projeto: doc.nomeProjeto,
url: "http://www.movaci.com.b/projetos/confirma/"+doc._id+"/2456",
url2: "http://www.movaci.com.br/projetos/confirma/"+doc._id+"/9877"
};
});
resolve(users);
});
});
},
sendMailToUser = (mail, user) => {
return mail.send({
message: {
to: user.email
},
locals: user
});
},
broadcastMailToUsers = (mail, users) => {
return Promise
.all(
users
.map(user => sendMailToUser(mail, user))
);
};
module.exports = (req, res) => {
const mail = createEmail('rateada', 'MOVACI - Projeto aprovado!'); // mail object
getApprovedUsers()
.then(users => broadcastMailToUsers(mail, users))
.then(result => {
console.log('Result of broadcast:', result);
res.send('ok');
})
.catch(error => {
res.status(500).send(error);
});
};
2) current routes file where routing part that uses module file:
router.post(
'/aprovadosemail',
miPermiso("3"),
require(__dirname+'/../methods/users/aprovadosEmail')
);

Using async.js for deep populating sails.js

I have a big issue with my function in sails.js (v12). I'm trying to get all userDetail using async (v2.3) for deep populating my user info:
UserController.js:
userDetail: function (req, res) {
var currentUserID = authToken.getUserIDFromToken(req);
async.auto({
//Find the User
user: function (cb) {
User
.findOne({ id: req.params.id })
.populate('userFollowing')
.populate('userFollower')
.populate('trips', { sort: 'createdAt DESC' })
.exec(function (err, foundedUser) {
if (err) {
return res.negotiate(err);
}
if (!foundedUser) {
return res.badRequest();
}
// console.log('foundedUser :', foundedUser);
cb(null, foundedUser);
});
},
//Find me
me: function (cb) {
User
.findOne({ id: currentUserID })
.populate('myLikedTrips')
.populate('userFollowing')
.exec(function (err, user) {
var likedTripIDs = _.pluck(user.myLikedTrips, 'id');
var followingUserIDs = _.pluck(user.userFollowing, 'id');
cb(null, { likedTripIDs, followingUserIDs });
});
},
populatedTrip: ['user', function (results, cb) {
Trip.find({ id: _.pluck(results.user.trips, 'id') })
.populate('comments')
.populate('likes')
.exec(function (err, tripsResults) {
if (err) {
return res.negotiate(err);
}
if (!tripsResults) {
return res.badRequest();
}
cb(null, _.indexBy(tripsResults, 'id'));
});
}],
isLiked: ['populatedTrip', 'me', 'user', function (results, cb) {
var me = results.me;
async.map(results.user.trips, function (trip, callback) {
trip = results.populatedTrip[trip.id];
if (_.contains(me.likedTripIDs, trip.id)) {
trip.hasLiked = true;
} else {
trip.hasLiked = false;
}
callback(null, trip);
}, function (err, isLikedTrip) {
if (err) {
return res.negotiate(err);
}
cb(null, isLikedTrip);
});
}]
},
function finish(err, data) {
if (err) {
console.log('err = ', err);
return res.serverError(err);
}
var userFinal = data.user;
//userFinal.trips = data.isLiked;
userFinal.trips = "test";
return res.json(userFinal);
}
);
},
I tried almost everthing to get this fix but nothing is working...
I am able to get my array of trips(data.isLiked) but I couldn't get my userFInal trips.
I try to set string value on the userFinal.trips:
JSON response
{
"trips": [], // <-- my pb is here !!
"userFollower": [
{
"user": "5777fce1eeef472a1d69bafb",
"follower": "57e44a8997974abc646b29ca",
"id": "57efa5cf605b94666aca0f11"
}
],
"userFollowing": [
{
"user": "57e44a8997974abc646b29ca",
"follower": "5777fce1eeef472a1d69bafb",
"id": "5882099b9c0c9543706d74f6"
}
],
"email": "test2#test.com",
"userName": "dany",
"isPrivate": false,
"bio": "Hello",
"id": "5777fce1eeef472a1d69bafb"
}
Question
How should I do to get my array of trips (isLiked) paste to my user trips array?
Why my results is not what I'm expecting to have?
Thank you for your answers.
Use .toJSON() before overwriting any association in model.
Otherwise default toJSON implementation overrides any changes made to model associated data.
var userFinal = data.user.toJSON(); // Use of toJSON
userFinal.trips = data.isLiked;
return res.json(userFinal);
On another note, use JS .map or _.map in place of async.map as there is not asynchronous operation in inside function. Otherwise you may face RangeError: Maximum call stack size exceeded issue.
Also, it might be better to return any response from final callback only. (Remove res.negotiate, res.badRequest from async.auto's first argument). It allows to make response method terminal

How to send push notifications form javascript using quickblox

I am trying to send push notifications messages through Quickblox from my backend server. The code that does this goes like the following:
app.post('/requests', function(req, res) {
var mobileNumber = req.param('mobile_number');
if (typeof mobileNumber === 'undefined') {
return res.badRequest("Parameters missing: [mobile_number]");
}
var query = {
international_number: mobileNumber
}
User.findOne(query, function(err, user) {
if (err) {
return res.dbError(err);
}
if (!user) {
console.log("User not found");
return res.apiError("NOT_FOUND");
}
var request = new Request();
request.sender_id = req.user._id;
request.receiver_id = user._id;
request.status = 'pending';
request.save(function(err) {
if (err) {
return res.dbError(err);
}
var response = {};
response.image_url = user.image_url;
response.id = request._id;
// ¡TODO! Notify the end user -- Quickblox
QB.createSession(function(err, result) {
if (err) {
console.log(err);
return res.apiError();
}
console.log("**** SESSION CREATE ****")
console.log(result);
var params = {
login: req.user.qb_username,
password: req.user.qb_password
}
console.log("LOGIN PARAMS");
console.log(params);
QB.login(params, function(err, result) {
if (err) {
console.log(err);
return res.apiError();
}
console.log("**** USER LOGIN ****")
console.log(result);
var params = {
notification_type: 'push',
environment: 'production',
user : {
ids: user.qb_id
},
message: 'SSBsb3ZlIE0mTSdzISBFc3BlY2lhbGx5IHJlZCBvbmUh',
push_type: user.device.notification_channel
}
console.log("EVENTS CREATE PARAMS");
console.log(params);
QB.messages.events.create(params, function(err, result) {
if (err) {
console.log(err);
return res.apiError();
}
console.log("**** MESSAGE EVENT CREATE ****");
console.log(result);
console.log(result.event.subscribers_selector);
QB.messages.events.list(function(err, result) {
if (err) {
console.log(err);
return res.apiError();
}
console.log(result);
console.log("**** EVENTS LIST ****");
console.log(result.items);
res.apiSend(response);
});
});
});
});
});
});
});
Note that I am logging the response after every single Quickblox request. So the log after QB.messages.events.create() is the following:
**** MESSAGE EVENT CREATE ****
{ event:
{ active: true,
application_id: 18113,
created_at: '2015-01-13T10:32:45Z',
date: null,
end_date: null,
event_type: 'one_shot',
id: 1809320,
message: 'data.message=SStsb3ZlK00lMjZNJTI3cyUyMStFc3BlY2lhbGx5K3JlZCtvbmUlMjE=',
name: null,
occured_count: 0,
period: null,
updated_at: '2015-01-13T10:32:45Z',
user_id: 2185263,
notification_channel: { name: 'gcm' },
subscribers_selector:
{ environment: 'production',
tags_query: null,
user_ids: [Object] } } }
and when i list the events using QB.messages.events.list() i get the following response:
{ current_page: 1,
per_page: 10,
total_entries: 19,
items:
[ { event: [Object] },
{ event: [Object] },
{ event: [Object] },
{ event: [Object] },
{ event: [Object] },
{ event: [Object] },
{ event: [Object] },
{ event: [Object] },
{ event: [Object] },
{ event: [Object] } ] }
Therefore it says that there are 19 entries in the messages queue and everything seems to be OK.
However when I login to my Quickblox account and check the messages queue it is always empty and therefore no messages are scheduled to be sent. Note as well that subscriptions show the users subscribed to push notification services such as 'gcm' and 'apns'. Can anyone help me find out why is this happening please?
You need encode the message to base64.
You need to make sure your mobile app would know to understand the decoded message.
For example,
sending push notification to android qb_user_id: 20290
(and from me - my qb_user_id: 12121):
function b64EncodeUnicode(str) {
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
return String.fromCharCode('0x' + p1);
}));
}
function send_push() {
var params = {
notification_type: 'push',
push_type: 'gcm',
user: {ids: [20290]},
environment: "production",
message: b64EncodeUnicode('{"message":"HELLO WORLD","user_id":12121,"device_type":"WEB","message_qb_id":"563a55a44cedaa83885724cf","message_type":"Text","send_status":"BeingProcessed","send_time":1446663588607}')
};
QB.messages.events.create(params, function(err, response) {
if (err) {
console.log("QB.messages.events.create::error:" +err);
} else {
console.log("QB.messages.events.create::response:" + response);
}
});
}
In this example, the mobile app is looking for a message in this format:
{"message","user_id","device_type","message_qb_id","message_type","send_status","send_time"}

Categories

Resources