Node.js For-Loop Inner Asynchronous Callback - node.js

I have to following function that assesses if a password is in the user's password history. Here is the code:
isPasswordInPwdHistory : function(redisClient, userId, encPassword, cb) {
var key = 'user:' + userId + ':pwdhistory';
redisClient.llen(key, function(err, reply) {
if (err) {
status.results = [ ];
xutils.addStatusResultsItem(status.results, err, null, null, null);
cb(status);
}
else {
var numPwds = reply;
var match = false;
var funcError;
for (i=0; i <= numPwds - 1; i++) {
var error;
var pwdValue;
redisClient.lindex(key, i, function(err, reply) {
if (err) {
console.log('lindex err = ' + err);
error = err;
}
else {
console.log('lindex reply = ' + reply);
console.log('lindex encPassword = ' + encPassword);
console.log('lindex (encPassword === reply) = ' + (encPassword === reply));
pwdValue = reply;
}
});
console.log('for-loop error = ' + error);
console.log('for-loop pwdValue = ' + pwdValue);
console.log('for-loop (encPassword === pwdValue) = ' + (encPassword === pwdValue));
if (error) {
funcError = error;
break;
}
else if (encPassword === pwdValue) {
console.log('passwords match');
match = true;
break;
}
}
console.log('funcError = ' + funcError);
console.log('match = ' + match);
if (funcError) {
status.results = [ ];
xutils.addStatusResultsItem(status.results, err, null, null, null);
cb(status);
}
else
cb(match);
}
});
}
Here is the console output:
for-loop error = undefined
for-loop pwdValue = undefined
for-loop (encPassword === pwdValue) = false
funcError = undefined
match = false
isPasswordInPwdHistory = false
lindex reply = 5f4f68ed57af9cb064217e7c28124d9b
lindex encPassword = 5f4f68ed57af9cb064217e7c28124d9b
lindex (encPassword === reply) = true
Once I leave the scope of the redisClient.lindex() call I lose the values. How can I pass these values for evaluation in the for-loop?
UPDATE
I refactored the code a bit to handle the matching of the new password (encPassword) vs. the existing password at index i when the redisClient.lindex() callback is issued.
isPasswordInPwdHistory : function(redisClient, userId, encPassword, cb) {
var status = new Object();
var key = 'user:' + userId + ':pwdhistory';
redisClient.llen(key, function(err, reply) {
if (err) {
status.results = [ ];
xutils.addStatusResultsItem(status.results, err, null, null, null);
cb(status);
}
else {
var numPwds = reply;
var loopCt = 0;
for (i=0; i <= numPwds - 1; i++) {
loopCt++;
redisClient.lindex(key, i, function(err, reply) {
if (err) {
status.results = [ ];
xutils.addStatusResultsItem(status.results, err, null, null, null);
cb(status);
}
else if (encPassword === reply) {
status.results = [ ];
xutils.addStatusResultsItem(status.results, null, 0, null, true);
cb(status);
}
else if (loopCt === numPwds && encPassword !== reply) {
status.results = [ ];
xutils.addStatusResultsItem(status.results, null, 0, null, false);
cb(status);
}
});
}
}
});
}
Unfortunately, caller sees status === undefined even though encPassword === reply is true and I issue the cb(status). How can I exit the for-loop for good after issuing cb(status)?

It looks like you have a mix of synchronous and asynchronous thinking in your logic. Your for loop is going to fire off requests to Redis as fast as it can, without waiting for Redis to respond (this is the nature of asynchronous IO). So, by the time your code runs console.log('funcError = ' + funcError); and console.log('match = ' + match);, Redis likely hasn't even responded for the very first value of i in the loop (which matches what you're finding in your output).
I would probably look into a library like async to help with this task; in particular, whilst looks like it might be a good fit. Perhaps something like:
var numPwds = reply;
...
var match = false;
var i = 0;
async.whilst(
// keep looping until we have a match or we're done iterating the list
function () { return match == false && i < numPwds; },
// this is our function to actually check Redis
function (callback) {
redisClient.lindex(key, i, function(err, reply) {
if (err) {
console.log('lindex err = ' + err);
error = err;
}
else {
if (encPassword === reply) { match = true; } // this will cause `whilst` to stop.
}
i++;
callback();
});
},
function (err) {
// this is called when our first function that calls
// return match == false && i < numPwds
// returns false, which will happen when we're out of list elements to check
// or we have a match. At this point, match being true or false
// let us know if we found the password.
}
);
As a final note, unless you need to support the same password showing up multiple times in a the "used passwords" list, you can save yourself a lot of heartache by using a set or a sorted set.

Related

How to get code to execute in order in node.js

I am trying to finish my script, but for some reason i don't know, it refuses to execute in the order i put it in.
I've tried placing a 'wait' function between the JoinRequest update function and the following code, but when run, it acts as if the function call and wait function were the other way round, countering the point of the wait().
const Roblox = require('noblox.js')
var fs = require('fs');
var joinRequests = []
...
function wait(ms) {
var d = new Date();
var d2 = null;
do { d2 = new Date(); }
while(d2-d < ms*1000);
};
...
function updateJReqs() {
Roblox.getJoinRequests(4745601).then((array) => {
var i;
var final = [];
for(i = 0; i < array.length; i++) {
final.push(array[i].username);
};
if(final === '') {
final = '-None';
};
joinRequests = final
console.log('Updated join requests.')
});
}
function check() {
setTimeout(() => {
fs.readFile('Request.txt',encoding = 'utf-8', function(err, data) {
if (err) {
check();
} else {
updateJReqs(); //for some reason this function is executed alongside the below, not before it.
// Tried putting wait(x) in here.
console.log('Request received: ' + data)
var solution = joinRequests
console.log('Fuffiling request with ' + solution)
fufillRequest(solution)
fs.unlink('Request.txt', function(err) {
if(err) throw err;
});
check();
}
});
}, 400)
}
check();
The script is supposed to wait until a file is created (accomplished), update the list of join requests (accomplished) and then create a new file with the list of join requests in(not accomplished).
if I understand your code you work with async code, you need to return a promise in updateJReqs and add a condition of leaving from the function because you have an infinite recursion
function updateJReqs() {
return new Promise(resolve => {
Roblox.getJoinRequests(4745601).then((array) => {
var i;
var final = [];
for(i = 0; i < array.length; i++) {
final.push(array[i].username);
};
if(final === '') {
final = '-None';
};
joinRequests = final
console.log('Updated join requests.')
resolve();
});
}
}
async function check() {
setTimeout(() => {
fs.readFile('Request.txt',encoding = 'utf-8', function(err, data) {
if (err) {
await check();
} else {
await updateJReqs();
// Tried putting wait(x) in here.
console.log('Request received: ' + data)
var solution = joinRequests
console.log('Fuffiling request with ' + solution)
fufillRequest(solution)
fs.unlink('Request.txt', function(err) {
if(err) throw err;
});
// you dont have an exit from your function check();
return 'Success';
}
});
}, 400)
}
check().then(res => console.log(res));

node.js how to get data from redis database inside in middleware

I'm doing middleware module that will extract data from redis and put to req.my_session.[here]
This is function that call inside app.use();
function parse_cookies(req){
if(req.headers.cookie != null){
var result = req.headers.cookie.match(new RegExp('m:[^=]*=[^; ]*', 'ig'));
if(result != null){
for(var i = 0; i < result.length; i++){
var result1 = result[i].split('=');
req.my_session[result1[0].substr(2)] = result1[1];
// get from redis value
client.get('sess:'+result1[1], function(err, reply) {
// reply is null when the key is missing
console.log(reply);
let li = i;
req.my_session[result1[0].substr(2)] = reply;
console.log('li = ' + li);
console.log('result1.lenght= ' + result.length);
if(i == result.length){
console.log('call the next');
}
});
}
}
}
} // parse_cookies
in console i outputs always 3, how can I get all data from database using redis.get and on last data call next() function for get out from my function?
problem it's get data from database in my middleware, I can't because redis has callback function
client.get("missingkey", function(err, reply) {
// reply is null when the key is missing
console.log(reply);
});
I think the issue is becuase of async in loop you can try the following
function parse_cookies(req){
if(req.headers.cookie != null){
var result = req.headers.cookie.match(new RegExp('m:[^=]*=[^; ]*', 'ig'));
if(result != null){
var promises = [];
for(var i = 0; i < result.length; i++){
var result1 = result[i].split('=');
promises.push(getFromRd(req, result1));
}
return Promise.all(promises)
.then(() => {
return next()
})
.catch((e) => {
return next({error: e})
})
}
}
} // parse_cookies
function getFromRd(req, result1) {
req.my_session[result1[0].substr(2)] = result1[1];
// get from redis value
return client.get('sess:'+result1[1], function(err, reply) {
if (err) {
throw Error(' failed to find ' + 'sess:' + result1[1])
}
// reply is null when the key is missing
console.log(reply);
let li = i;
req.my_session[result1[0].substr(2)] = reply;
console.log('li = ' + li);
console.log('result1.lenght= ' + result.length);
return {success:true}
});
}

findOne not executed

I have the code below:
exports.getMyPets = function (req, res){
var email = req.body.email;
var result = [{}];
Owner.findOne({"email" : email }, "_id", function(err, owner){
if(err)
res.send(err);
console.log("Owner: " + owner._id + "\n");
Pet.find({"OwnerId": owner._id}, function(err, pet) {
if(err)
res.send(err)
var index = pet.length;
var empty = false;
if(index > 0)
empty = true;
try{
for (let x = 0; x < index; x += 1){
PetPicture.findOne({"petId" : pet[x]._id}, function (err, petPic){
console.log('pet ID: ' + pet[x]._id);
result[x] = {
name: pet[x].name,
breed: pet[x].breed,
description: pet[x].description,
imageType: petPic.imageType,
imageData: petPic.imageType
};
}); //end of petPicture.findOne
console.log('X : ' + pet[x]._id);
}// end of for loop
} catch (error){
console.log(error);
}
}
while(true){
if (result.length === index)
break;
}
if(!empty){
res.json(pet);
} else {
res.json(result);
}
}); // end of Pet,find
}); // end of petOwner.findOne
};
The block of PetPicture.findOne (the whole try catch block) does not get executed when I try to call it on endpoint. It does not log anything, not even an error on the catch part. What can I do to debug this?

How can I use session token for repetitive calls of GEOTAB apis in node js wrapper?

I am using GEOTAB apis to sync vehicle data to my DB.In my software user can register and add geotab account to sync.for sync purpose i have used node js wrapper.With 3 minutes delay this process calls regularly.
I am getting the error "Global login limit exceeded"
var syncUsers = function () {
var i;
async.forEach(geotabUsers, function (geoUser, callback) {
//get index
i = geotabUsers.indexOf(geoUser);
if (apiInstanse[geotabUsers[i].userId] === undefined)
{
apiInstanse[geotabUsers[i].userId] = new API(geotabUsers[i].apiUsername, geotabUsers[i].apiPassword, geotabUsers[i].apiDatabase, js_lang.GEOTAB_SERVER);
}
//sync status data
syncStatusData(apiInstanse[geotabUsers[i].userId], i, userInfo[geotabUsers[i].userId].statusDataFromVersion, geotabUsers[i].userId, userInfo[geotabUsers[i].userId].currentCompany, geotabUsers[i].apiUsername, geotabUsers[i].apiPassword, geotabUsers[i].apiDatabase, userInfo[geotabUsers[i].userId].currentPressureUnit, userInfo[geotabUsers[i].userId].currentDateFormat, userInfo[geotabUsers[i].userId].currentTimeFormat, userInfo[geotabUsers[i].userId].currentTemperatureUnit);
//sync fault data
syncFaultData(apiInstanse[geotabUsers[i].userId], i, userInfo[geotabUsers[i].userId].faultDataFromVersion, geotabUsers[i].userId, userInfo[geotabUsers[i].userId].currentCompany, geotabUsers[i].apiUsername, geotabUsers[i].apiPassword, geotabUsers[i].apiDatabase,function(){
callback();
});
},function(err){
if(err)
{
console.log('done errro:',err);
}
console.log('sync done');
continueSync();
});
Function to sync status data:
var syncStatusData = function (api, i, fromVersion, userId, currentCompany, apiUsername, apiPassword, apiDatabase, currentPressureUnit, currentDateFormat, currentTimeFormat, currentTemperatureUnits)
{
try {
api.call(js_lang.GEOTAB_GETFEED_METHOD, {
typeName: js_lang.GEOTAB_STATUS_DATA,
resultsLimit: js_lang.GEOTAB_API_LIMIT,
fromVersion: fromVersion
}, function (err, data) {
if (err) {
console.log('api Call Error:', userId);
console.log('apiUsername:', apiUsername);
console.log('apiPassword:', apiPassword);
console.log('apiDatabase:', apiDatabase);
console.log('Error', err);
//apiInstanse[userId] = new API(apiUsername, apiPassword, apiDatabase, js_lang.GEOTAB_SERVER);
//throw err;
}
else {
var insertStatus = [];
var sql = "INSERT INTO " + js_lang.TABLE_STATUS_DATA + " (companyId,dateTime,deviceId ,diagnosticId,value,version,uniqueId,userId,unitOfMeasure ) VALUES ?";
//iterate data
if (data.data !== undefined)
{
for (var key in data.data) {
if (diagnosticList[data.data[key].diagnostic.id] === undefined)
{
continue;
}
var normalDate = FUNCTION_CLASS.getNormalDateFromUTC(data.data[key].dateTime);
//prepare data to insert
var insertRow = [
currentCompany,
normalDate,
data.data[key].device.id,
data.data[key].diagnostic.id,
data.data[key].data,
data.data[key].version,
data.data[key].id,
userId,
diagnosticList[data.data[key].diagnostic.id].unitOfMeasure
];
insertStatus.push(insertRow);
}
}
if (insertStatus.length > 0)
{
connection.query(sql, [insertStatus], function (err) {
if (err)
{
throw err;
}
// console.log('toversion:', data.toVersion);
console.log('insert:userId:', userId);
connection.query('UPDATE ' + js_lang.TABLE_USER + ' SET statusDataFromVersion = ? WHERE id = ?',
[data.toVersion, userId]);
});
}
else {
console.log('update user:', userId);
connection.query('UPDATE ' + js_lang.TABLE_USER + ' SET statusDataFromVersion = ? WHERE id = ?',
[data.toVersion, userId]);
}
}
if ((geotabUsers.length - 1) === i)
{
console.log('loop ended');
syncStatusDone = 1;
// continueSync();
}
});
}
catch (e) {
continueSync();
}
}
Function to sync Fault data:
var syncFaultData = function (api, i, fromVersion, userId, currentCompany, apiUsername, apiPassword, apiDatabase,callback)
{
try {
api.call(js_lang.GEOTAB_GETFEED_METHOD, {
typeName: js_lang.GEOTAB_FAULT_DATA,
resultsLimit: js_lang.GEOTAB_API_LIMIT,
fromVersion: fromVersion
}, function (err, data) {
if (err) {
console.log('api faultData Call Error:', userId);
console.log('apiUsername:', apiUsername);
console.log('apiPassword:', apiPassword);
console.log('apiDatabase:', apiDatabase);
console.log('Error', err);
//apiInstanse[userId] = new API(apiUsername, apiPassword, apiDatabase, js_lang.GEOTAB_SERVER);
//throw err;
}
else {
var insertStatus = [];
var sql = "INSERT INTO " + js_lang.TABLE_FAULT_DATA + " (amberWarningLamp,controllerId,count,dateTime,deviceId ,diagnosticId,dismissDateTime,dismissUser,failureModeId,faultLampState,faultState,flashCode,uniqueId,malfunctionLamp,protectWarningLamp,redStopLamp,version,companyId,userId,deleted) VALUES ?";
//iterate data
if (data.data !== undefined)
{
for (var key in data.data) {
if (diagnosticList[data.data[key].diagnostic.id] == undefined)
{
continue;
}
var normalDate = FUNCTION_CLASS.getNormalDateFromUTC(data.data[key].dateTime);
var thisDate = moment(new Date(data.data[key].dateTime)).tz(js_lang.TIMEZONE).format(js_lang.DATETIME_FORMAT);
var thisDayDate = moment(new Date(data.data[key].dateTime)).tz(js_lang.TIMEZONE).format(js_lang.DATE_FORMAT);
var controllerId = '';
if (data.data[key].controller !== undefined && data.data[key].controller.id !== undefined)
{
controllerId = data.data[key].controller.id;
}
else if (data.data[key].controller !== undefined)
{
controllerId = data.data[key].controller;
}
var faultcount = null;
if (data.data[key].count !== undefined)
{
faultcount = parseInt(data.data[key].count);
}
var dismissDateTime = '';
if (data.data[key].dismissDateTime !== undefined)
{
dismissDateTime = moment(new Date(data.data[key].dismissDateTime)).tz(js_lang.TIMEZONE).format(js_lang.DATETIME_FORMAT);
}
var dismissUser = '';
if (data.data[key].dismissUser !== undefined)
{
dismissUser = data.data[key].dismissUser;
}
var failureModeId = '';
if (data.data[key].failureModeId !== undefined && data.data[key].failureModeId.id !== undefined)
{
failureModeId = data.data[key].failureModeId.id;
}
var faultLampState = '';
if (data.data[key].faultLampState !== undefined)
{
faultLampState = data.data[key].faultLampState;
}
var faultState = data.data[key].faultState;
var flashCode = '';
if (data.data[key].flashCode !== undefined)
{
flashCode = data.data[key].flashCode;
}
var malfunctionLamp = data.data[key].malfunctionLamp;
var protectWarningLamp = data.data[key].protectWarningLamp;
var redStopLamp = data.data[key].redStopLamp;
var version = '';
if (data.data[key].version !== undefined)
{
version = data.data[key].version;
}
//prepare data to insert
var insertRow = [
data.data[key].amberWarningLamp,
controllerId,
faultcount,
normalDate,
data.data[key].device.id,
data.data[key].diagnostic.id,
dismissDateTime,
dismissUser,
failureModeId,
faultLampState,
faultState,
flashCode,
data.data[key].id,
malfunctionLamp,
protectWarningLamp,
redStopLamp,
version,
currentCompany,
userId,
0
];
insertStatus.push(insertRow);
}
}
// console.log(insertStatus);
if (insertStatus.length > 0)
{
connection.query(sql, [insertStatus], function (err) {
if (err)
{
throw err;
}
// console.log('toversion:', data.toVersion);
console.log('insert faultData:userId:', userId);
connection.query('UPDATE ' + js_lang.TABLE_USER + ' SET faultDataFromVersion = ? WHERE id = ?',
[data.toVersion, userId]);
callback();
});
}
else {
console.log('update faultData user:', userId);
//test code
//sendEmail(['ankkubosstest#gmail.com','webdeveloper#gmail.com'], 'emailSubject', 'emailText', '<div>html code<div>');
connection.query('UPDATE ' + js_lang.TABLE_USER + ' SET faultDataFromVersion = ? WHERE id = ?',
[data.toVersion, userId]);
callback();
}
}
if ((geotabUsers.length - 1) === i)
{
console.log('loop ended');
syncFaultDone = 1;
}
});
}
catch (e) {
continueSync();
}
}

node js gettting error of activity timeout or database connection lost

I am using node js script to sync data(for multiple users) from GeoTab server APIs to my local db and using below code
var syncUsers = function () {
for (var i = 0; i < geotabUsers.length; i++) {
if (apiInstanse[geotabUsers[i].userId] == undefined)
{
apiInstanse[geotabUsers[i].userId] = new API(geotabUsers[i].apiUsername, geotabUsers[i].apiPassword, geotabUsers[i].apiDatabase, js_lang.GEOTAB_SERVER);
}
syncStatusData(apiInstanse[geotabUsers[i].userId], i, userInfo[geotabUsers[i].userId].statusDataFromVersion, geotabUsers[i].userId, userInfo[geotabUsers[i].userId].currentCompany, geotabUsers[i].apiUsername, geotabUsers[i].apiPassword, geotabUsers[i].apiDatabase);
}
var syncStatusData = function (api, i, fromVersion, userId, currentCompany, apiUsername, apiPassword, apiDatabase){
try {
api.call(js_lang.GEOTAB_GETFEED_METHOD, {
typeName: js_lang.GEOTAB_STATUS_DATA,
resultsLimit: js_lang.GEOTAB_API_LIMIT,
fromVersion: fromVersion
}, function (err, data) {
if (err) {
console.log('api Call Error:', userId);
console.log('apiUsername:', apiUsername);
console.log('apiPassword:', apiPassword);
console.log('apiDatabase:', apiDatabase);
console.log('Error', err);
apiInstanse[userId] = new API(apiUsername, apiPassword, apiDatabase, js_lang.GEOTAB_SERVER);
return;
}
var insertStatus = [];
var sql = "INSERT INTO " + js_lang.TABLE_STATUS_DATA + " (companyId,dateTime,deviceId ,diagnosticId,value,version,uniqueId,userId,unitOfMeasure ) VALUES ?";
//iterate data
if (data.data != undefined)
{
for (var key in data.data) {
if (diagnosticList[data.data[key].diagnostic.id] == undefined)
{
continue;
}
var thisDate = moment(new Date(data.data[key].dateTime));
var thisDayDate = thisDate.format("YYYY-MM-DD");
//prepare data to insert
var insertRow = [
currentCompany,
thisDate.format("YYYY-MM-DD HH:MM:ss"),
data.data[key].device.id,
data.data[key].diagnostic.id,
data.data[key].data,
data.data[key].version,
data.data[key].id,
userId,
diagnosticList[data.data[key].diagnostic.id].unitOfMeasure
];
insertStatus.push(insertRow);
var todayDate = moment(new Date());
var todayDayDate = todayDate.format("YYYY-MM-DD");
//send alert in case of current day data
if (todayDayDate == thisDayDate)
{
//send mails in case of high pressure
if (diagnosticList[data.data[key].diagnostic.id].unitOfMeasure == js_lang.GEOTAB_PRESSURE_UNIT_STR &&
data.data[key].data > js_lang.MAX_PRESSURE &&
alertTemplates[userId] != undefined)
{
console.log('alert time');
if (alertTemplates[userId] != undefined)
{
for (var templateIndex = 0; templateIndex < alertTemplates[userId].length; templateIndex++) {
var template = alertTemplates[userId][templateIndex];
var res = template.devices.split(",");
var index = FUNCTION_CLASS.contains.call(res, data.data[key].device.id)
if (index)
{
var emailSubject = 'High Pressure Alert';
if (userDevices[userId][data.data[key].device.id].name != undefined)
{
var emailText = 'Vehicle:' + userDevices[userId][data.data[key].device.id].name;
}
else
{
var emailText = '';
}
var toEmails = template.emails;
var emailHtml = 'Vehicle:' + userDevices[userId][data.data[key].device.id].name + '<br>' + diagnosticList[data.data[key].diagnostic.id].name + ' <br> value:' + data.data[key].data + ' ' + js_lang.GEOTAB_PRESSURE_UNIT_PA + '<br>\
' + js_lang.DATE_TIME + ':' + thisDate.format("YYYY-MM-DD HH:MM:ss");
//call mail function
sendEmail(toEmails, emailSubject, emailText, emailHtml);
}
}
}
}
}
}
}
if (insertStatus.length > 0)
{
connection.query(sql, [insertStatus], function (err) {
if (err)
throw err;
connection.query('UPDATE ' + js_lang.TABLE_USER + ' SET statusDataFromVersion = ? WHERE id = ?',
[data.toVersion, userId]);
});
}
else {
console.log('update user:', userId);
connection.query('UPDATE ' + js_lang.TABLE_USER + ' SET statusDataFromVersion = ? WHERE id = ?',
[data.toVersion, userId]);
}
if ((geotabUsers.length - 1) == i)
{
console.log('loop ended');
continueSync();
}
});
}
catch (e) {
continueSync();
}
}
var continueSync = function () {
setTimeout(function () {
connection.end();
geotabUsers = [];
userDevices = [];
diagnosticList = [];
userInfo = [];
alertTemplates = {};
eventEmitter.emit('getUsers');
}, 60000);
}
Its work fine for few iteration but getting random errors like
events.js:85
throw er; // Unhandled 'error' event
^
Error: Quit inactivity timeout
Or
events.js:85
throw er; // Unhandled 'error' event
^
Error: Connection lost: The server closed the connection.
I was getting this error while executing app on local, To remove problem I have used pm2 node module for process management.Now its working fine.

Resources