async watefall doesn't call the functions - javascript

So i am actually woking on a simple program with node.Js and i have an issue using async.waterfall :
I created a function in my user model that connect the user by accessing the database, here is the code :
exports.connection = function (login,password) {
async.waterfall([
function getLogin(callback){
usersModel.findOne({ login: login }, function (err, res) {
if (err){
callback(err,null);
return;
}
if(res != null ){
// test a matching password if the user is found we compare both passwords
var userReceived = res.items[0].login;
callback(null,userReceived);
}
});
},
function getPassword(userReceived, callback){
console.log(userReceived);
callback(null,'done')
}
], function(err){
if (err) {
console.error(err);
}
console.log('success');
});
}
Using node-inspector i figured out that the main issue(I think) is that when it enters the waterfall function it doesn't execute the callback function of findOne it literally skips this and directly jump to the getPassword function (which isn't executed too).
so if someone could help me figuring out what's the problem that would be nice since i'm on it for around two days now.
Thank you
EDIT:
After adding the different missing cases of tests(which was why the callback didn't worked) I have this connection function:
exports.connection = function (login,password) {
async.waterfall([
function getLogin(callback){
usersModel.findOne({ login: login }, function (err, res) {
console.log('login: ',res.login);
console.log('erreur: ',err);
if (err){
callback(err,null);
return;
}
if(!res)
{
console.log('getLogin - returned empty res');
callback('empty res');
}
if(res != null ){
// test a matching password if the user is found we compare both passwords
var userReceived = res;
callback(null,userReceived);
}
});
},
function getPassword(userReceived, callback){
console.log('login received :',userReceived.login);
var Ulogin = userReceived.login;
var Upassword = userReceived.password;
// function that compare the received password with the encrypted
//one
bcrypt.compare(password, Upassword, function(err, isMatch) {
if (err) {
console.log(err);
callback(err,null);
return;
}
else if (isMatch) {
console.log('Match', isMatch);
callback(null,isMatch);
}
else {
console.log('the password dont match', isMatch);
callback('pwd error',null);
}
});
},
], function(err){
if (err) {
console.error('unexpected error while connecting', err);
return false;
}
console.log('connected successfully');
return true;
});
}
And in my main file server.js i'm doing currently doing :
var connect = users.connection(login,password);
//the goal is to use the connect variable to know if the connection
//failed or not but it's 'undefined'
if(connect){
res.send('youyou connecté');
}
else {
res.send('youyou problem');
}
this absolutely don't work so i tried to use Q library but I have an error saying
"TypeError: Cannot read property 'apply' of undefined at Promise.apply"
here is the code using Q:
app.post('/signup', function (req, res) {
var login = req.body.login;
var password = req.body.password;
Q.fcall(users.connection(login,password))
.then(function (connect) {
if(connect){
res.send('connected');
}
else {
res.send('problem');
}
})
.catch(function (error) {
throw error;
})
.done();
});
but i am a little bit astonished i thought that by using async.waterfall() i told the function to wait until it received all the callbacks return so i don't understand why the connect variable is 'undefined'?

What I don't understand is - what was the flow exactly? did 'usersModel.findOne' get called?
What I see that is missing here in the getLogin function is a callback in the case that both the 'if' statement return false. in this case you'll get stuck in the first function and you won't advance to 'getPassword' function.
If this still doesn't work, please try executing the following code and report what was printed:
exports.connection = function (login,password) {
async.waterfall([
function getLogin(callback){
usersModel.findOne({ login: login }, function (err, res) {
if (err){
console.log('getLogin - error has occured');
callback(err,null);
return;
}
if(!res)
{
console.log('getLogin - returned empty res');
callback('empty res');
}
console.log('getLogin - result seems OK');
// test a matching password if the user is found we compare both passwords
var userReceived = res.items[0].login;
callback(null,userReceived);
}
});
},
function getPassword(userReceived, callback){
console.log('getPassword');
console.log(userReceived);
callback(null,'done')
}
], function(err){
if (err) {
console.error(err);
}
console.log('success');
});
}

Related

My Node Script Hangs after functions are finished

I'm calling three functions, after the completion of these functions I want my script to close on it's own but it just hangs.
I've tried making the functions async/promise based, closing the database after each 'mongodb' type function, and using process.exit() within a function as a callback to the last called function.
Connecting to the (local - not Atlas) Database:
MongoClient.connect(local, {useNewUrlParser: true, useUnifiedTopology: true}, function(err, db) {
if (err) {
console.log(err)
}
else {
console.log('Connected to MongoDB...')
//Read in data from jsonfiles and store each file's contents into the database : This is where the functions are being called... within a successful connect to the MongoDB
insertJSON(db, jsonfiles, 'requests', jsonfilesSource)
insertJSON(db, issuedfiles, 'issuedLicenses', isssuedfilesSource)
insertLicenses(db)
}
db.close()
})
Function 1:
function insertJSON(db, dirBuf,collection, sourceFolder) {
var database = db.db('license-server')
var collection = database.collection(collection)
fs.readdir(dirBuf, function(err, files) {
if (err) {
console.log(err.message)
}
else {
files.forEach(function(filename) {
var text = fs.readFileSync(sourceFolder + filename);
var filecontents = JSON.parse(text)
//collection.insertOne(filecontents)
collection.findOne({"DisplayTitle" : filecontents.DisplayTitle, "NodeInformation" : filecontents.NodeInformation, "Date": filecontents.Date})
.then(function(result) {
if(result) {
console.log(`An Item could already be in the database: A file is unique if its display title, nodeinformation, and date are different.
the items display title is ${result.DisplayTitle}`)
return
}
else {
collection.insertOne(filecontents)
console.log(`Added ${filecontents.DisplayTitle} to database`)
}
})
.catch(function(error) {
console.log(error)
})
})
}
})
}
Function 2:
function insertLicenses(db) {
// Set up GridFS to import .lic and .licx files into the database
var database = db.db('license-server')
var collection = database.collection('fs.files')
var bucket = new mongodb.GridFSBucket(database);
var dirBuf = Buffer.from('../license-server/private/licenses')
fs.readdir(dirBuf, function(err, files) {
if (err) {
console.log(err.message)
}
else {
files.forEach(function(filename) {
collection.findOne({"filename": filename}).
then(function(result) {
if(result) {
console.log(`The file ${filename} is already in the database`)
return
}
else {
fs.createReadStream('./private/licenses/' + filename).
pipe(bucket.openUploadStream(filename)).
on('error', function(error) {
assert.ifError(error)
}).
on('finish', function() {
console.log(`Uploaded ${filename}`)
})
}
})
})
}
})
// I tried calling db.close() here since this is the last function to be called. No luck.
}
I'm guessing it has something to do with the mongodb functions having their own way to close themselves but I couldn't seem to find what I was looking for in previous attempts to resolve this issue.
The expected result should be the script closing itself, the actual result is a handing script.
All of these database calls are asynchronous -- the result of this code running is to immediately call db.close and then do the work in insertJSON and insertLicenses. If you were to rewrite this to use async/await (and you'd need to update your other functions as well) the db.close call would close the db, and that would allow the script to exit:
await insertJSON(db, jsonfiles, 'requests', jsonfilesSource)
await insertJSON(db, issuedfiles, 'issuedLicenses', isssuedfilesSource)
await insertLicenses(db)
db.close()
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Introducing
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

What would be necessary to code the callback function to receive the access token?

I am trying to retrieve the access token from an API (https://github.com/Axosoft/node-axosoft/)
To receive an access token we have to follow this process:
var axo = nodeAxosoft(axosoftUrl, credentials);
axo.Api.getLoginUrl(function(url) {
// open browser using authorizationUrl and get code parameter from
//redirected Url after login
var code = 'code received from redirect';
axo.Api.exchangeCodeForToken(code);
});
As I did not understood exactly how to get the code following that example nor what is the url parameter on getLoginUrl, I did it on my own.
I have a login route that redirects the user to the axosoft website for authentication and redirects the user to the /authorization-process route on my application.
On the /authorization-process I get the code returned by the login and call a function that should get the access token by calling:
axo.Api.exchangeCodeForToken(code);
Code:
var axosoft_code = req.query.code;
console.log(axosoft_code);
var token = request.exchangeAuthCodeForAccessToken(axosoft_code)
.then(function(token)
{
res.send(token);
})
The Method:
var connection = nodeAxosoft(client_url, credentials);
return new Promise(function(resolve, reject){
console.log("CODE: ", axosoft_code)
var token = connection.Api.exchangeCodeForToken(axosoft_code, function(token){
console.log(token);
resolve(token)
})
The problem is that returns null
I had a look at the API lib api.js and found that:
https://github.com/Axosoft/node-axosoft/blob/master/lib/api.js
function exchangeCodeForToken(code, callback) {
_credentials.code = code;
_access_token = '';
_authenticateCredentails(function (err) {
if (!err) {
callback(null, _access_token);
} else {
callback(err);
}
})
}
So I have two questions:
Does anyone has an Idea what am I doing wrong?
What would be necessary to code the callback function?
The method expects a callback function but I don't really know how to do it.
EDIT:
return new Promise(function(resolve, reject){
var token = connection.Api.exchangeCodeForToken(axosoft_code, function(response,err){
if(!err){
console.log("token",response)
resolve(token);
}
else{
console.log("error",err)
resolve(token);
}
});
})
OR
var token = connection.Api.exchangeCodeForToken(axosoft_code, function(response,err){
if(!err){
console.log("token",response.body)
return response.body;
}
else{
console.log("error",err)
return err;
}
});
I am giving to my callback function two args (response and err), my problem is that I am falling at the else condition.
The output of err is something similar to a token though the documentation here shows that it should be like that:
{
"error" : "invalid_request",
"error_description" : "One or more parameters are missing: client_secret"
}
Another point is that the page is frozen waiting for something to happen but nothing happens.
Given that this is the input:
function exchangeCodeForToken(code, callback) {
_credentials.code = code;
_access_token = '';
_authenticateCredentails(function (err) {
if (!err) {
callback(null, _access_token);
} else {
callback(err);
}
})
}
You should format your call as:
exchangeCodeForToken(axosoft_code, function(err, response) {
if (err) {
// Deal with error
} else {
// Deal with response
}
}
Node functions often pass through error variables first so that you have to receive them, which is considered good practice.

How can I run this 3 node.js functions asynchronously using async.js?

I have 3 functions which I want to run asynchronously and when they are all done run another function:
app.get('/', function (req, res) {
var homePosts = {
newsest: [],
reviewed: [],
mostPopuler: [],
viewed: []
};
// fetch newest pages and asign the result to 'homePosts.newest '
function fetchNewestPages() {
Post.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage", function (err, posts) {
if (err) {
req.flash('error', 'An unknown error was occured.');
res.redirect('back');
} else {
homePosts.newsest = posts;
}
}).limit(4).sort( { date : -1 } );
}
// fetch most reviewed pages and asign the result to 'homePosts.reviewd '
function fetchMostReviewedPages() {
Post.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage", function (err, posts) {
if (err) {
req.flash('error', 'An unknown error was occured.');
res.redirect('back');
} else {
homePosts.reviewed = posts;
}
}).limit(4).sort( { commentsNumber : -1 } );
}
// fetch most popular pages and asign the result to 'homePosts.mostPopuler '
function fetchMostPopularPages() {
Post.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage", function (err, posts) {
if (err) {
req.flash('error', 'An unknown error was occured.');
res.redirect('back');
} else {
homePosts.mostPopuler = posts;
}
}).limit(4).sort( { likesNumber : -1 } );
}
// now run all 3 functions and when they are done render home page with the homePosts object which contains proper pages
async.parallel([
fetchNewestPages,
fetchMostReviewedPages,
fetchMostPopularPages
], function (err) { // it doesn't run at all
if (err) throw err;
console.log(homePosts);
res.render("home", {homePosts}); // render home page with the proper pages
});
});
hope that you got what the code does, here is the description of what the code does:
there is homePosts object which will have the proper pages to be displayed on the home page
Then we have a function which will fetch 4 newest pages from database and then assign them to homePost.newest
Then a function which will assign the 4 pages that have mosts comments to homePost.reviewed
the third function like the 2 above functions assign most popular pages to homePost.mostPopular
Now async.js should do its job, run those 3 functions simultaneously and then render a home page with the homePosts object, this is the part I have problem
The last function which will render home page doesn't run at all. Where is my problem? is there any way to run those 3 functions simultaneously and then run the last function which will render a page?
UPDATE:
I've managed to do that in this way but they are not running simultaneously, they are running one after another.
// fetch newest pages and asign the result to 'homePosts.newest '
function fetchNewestPages(cb) {
Post.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage", function (err, posts) {
if (err) {
req.flash('error', 'An unknown error was occured.');
res.redirect('back');
} else {
homePosts.newsest = posts;
cb();
}
}).limit(4).sort( { date : -1 } );
}
// fetch most reviewed pages and asign the result to 'homePosts.reviewd '
function fetchMostReviewedPages(cb) {
Post.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage", function (err, posts) {
if (err) {
req.flash('error', 'An unknown error was occured.');
res.redirect('back');
} else {
homePosts.reviewed = posts;
cb();
}
}).limit(4).sort( { commentsNumber : -1 } );
}
// fetch most popular pages and asign the result to 'homePosts.mostPopuler '
function fetchMostPopularPages(cb) {
Post.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage", function (err, posts) {
if (err) {
req.flash('error', 'An unknown error was occured.');
res.redirect('back');
} else {
homePosts.mostPopuler = posts;
cb();
}
}).limit(4).sort( { likesNumber : -1 } );
}
fetchNewestPages(function () {
fetchMostReviewedPages(function () {
fetchMostPopularPages(function () {
res.render("home", {homePosts});
});
});
});
Your problem is you are not having a callback parameter in any of your functions. Remember, you have to call the callback method when one function's processing is completed.
What I do in my practice is use async.constant as the first method of async.waterfall or async.parallel and pass the data that is to be used in the async methods. In your case it can be the search criterion for all three methods.
If no data is to be used in async methods, then I just pass an empty JS object.
Using async.constant helps me in two things.
Pass data to be used in async methods.
Get results from async methods in that passed object.
In your case, the async.constant method will have homePosts object.
app.get('/', function (req, res) {
// fetch newest pages and asign the result to 'homePosts.newest '
function fetchNewestPages(data, callback) {
Post
.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage")
.limit(4)
.sort( { date : -1 } )
.exec(function (err, posts) {
if (err) {
//If we pass first parameter as non-null, the control is passed to the last optional callback skipping all other functions in case of async.waterfall and not waiting for other functions to complete in case of async.parallel
return callback('An unknown error was occured.');
} else {
data['newsest'] = posts; //since homePosts is data object inside this function
If this function is completed successfully then we pass first parameter as null (no error) and second parameter as our object. As the strategy is parallel, all three functions will be editing the same object 'homePosts'
return callback(null, data);
}
});
}
// fetch most reviewed pages and asign the result to 'homePosts.reviewd '
function fetchMostReviewedPages(data, callback) {
Post
.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage")
.limit(4)
.sort( { commentsNumber : -1 } )
.exec(function (err, posts) {
if (err) {
//read comment in first function
return callback('An unknown error was occured.');
} else {
data['reviewed'] = posts; //since homePosts is data object inside this function
//read comment in first function
return callback(null, data);
}
});
}
// fetch most popular pages and asign the result to 'homePosts.mostPopuler '
function fetchMostPopularPages(data, callback) {
Post
.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage")
.limit(4)
.sort( { likesNumber : -1 } )
.exec(function (err, posts) {
if (err) {
//read comment in first function
return callback('An unknown error was occured.');
} else {
data['reviewed'] = posts; //since homePosts is data object inside this function
//read comment in first function
return callback(null, data);
}
});
}
var homePosts = {
newsest: [],
reviewed: [],
mostPopuler: [],
viewed: []
};
// now run all 3 functions and when they are done render home page with the homePosts object which contains proper pages
async.parallel([
async.constant(homePosts),
fetchNewestPages,
fetchMostReviewedPages,
fetchMostPopularPages
], function (err, data) {
//once all functions complete their execution and their callback method is called, with or without error, this method will be called.
if (err) {
req.flash('error', err);
res.redirect('back');
} else {
console.log(data);
res.render("home", {data}); // render home page with the proper pages
}
});
});
Hope that solves your problem and clear your concept a bit more.
The async library works with functions that use callbacks. None of yours do.
Either rewrite them in callback form, or use something like Promise.all:
Promise.all(
[fetchNewestPages,
fetchMostReviewedPages,
fetchMostPopularPages])
.then(res => console.log(res[0], res[1], res[2]))
.catch(err => console.log(err))
Hope this helped you
console.log('start');
// fetch newest pages and asign the result to 'homePosts.newest '
function fetchNewestPages() {
console.log('1')
}
// fetch most reviewed pages and asign the result to 'homePosts.reviewd '
function fetchMostReviewedPages() {
console.log('2')
}
// fetch most popular pages and asign the result to 'homePosts.mostPopuler '
function fetchMostPopularPages() {
console.log('3')
}
fetchNewestPages();
console.log('1 DONE');
fetchMostReviewedPages();
console.log('2 DONE');
fetchMostPopularPages();
console.log('3 DONE');
I work also alot with interval. As example If I had alot of callbacks and something is suddenly not synchron then this trick can be nice
var obj = {}
// to somelong stuff here and the result is var result
var result = 'this was generated in the fictive long process above'
objectme.obj = result // those object string can be used anywhere in the script realy nice.
clearInterval(testinterval); // <-- do also here a clearinterval
var testinterval = setInterval(function(){
if (objectme.obj) {
clearInterval(testinterval);
//.. the interval only stop here at the if. you can do anything here. also you can make a timeout. This will force the script to run as you wish it
}
},10000);
REALLY IMPORTANT. If you plan to insert long code into clearinterval zone then you need to increase the interval time. If your inserted codes takes longer than the interval, then your code will be execuded 2 times.
However you should do like in the first example. because using interval can be very tricky.

Mongoose seems to satisfy wrong promise (node js)

I am trying to refactor some inherited code. In every endpoint was the same validation code. I want to pull it out into it's own method. I am new to promises, but I think that is what I want to use. The issues is prom seems to be resolved at the User.findOne call and exits with an undefined prom.promise.
cheers
bob
function validateUser(req) {
var prom = q.defer();
var token = getToken(req.headers);
if (token) {
console.log("have a token")
var decoded = jwt.decode(token, config.secret);
console.log("now going to look for the user")
//Problem exit is on next line
User.findOne({
name: decoded.name
}, function (err, user) {
if (err) throw err;
prom.reject(err);
if (!user) {
console.log("no user found")
prom.reject("Authentication failed. User not found.")
} else {
console.log("user found returning true")
prom.resolve(true);
}
})
} else {
console.log("no token found")
prom.reject("No token provided.")
}
return prom.promise;
}
why you are using promises when mongoose itself returns it.
function validateUser(req, callback) {
var token = getToken(req.headers);
if (token) {
var decoded = jwt.decode(token, config.secret);
User.findOne({
name: decoded.name
}, function (err, user) {
if (err) throw err;
callback(err);
if (!user) {
callback("Authentication failed. User not found.")
} else {
console.log("user found returning true")
callback(null, {status:true, userData:user});
}
})
} else {
callback("No token provided.")
}
}
In above code,
if token is not found callback is returned with an error in the first attempt. if token is found then it is decoded in a row and if matched in DB if the result is an error then the callback is called with err parameter else if no user is found or empty match then a custom message is sent in callback error part. But in final is returned as success with status and userData.

Sails.js. Check if user exists

Hello guys am new to Sails.js ( using MySQL )
Am trying to find if a user already exists before registration.
Here this is the code:
register:function(req, res, next){
var params = req.params.all();
User.find({
or : [
{ usrnm:params.usrname },
{ eml:params.eml }
]
})
.exec(function (err, user){
if (err) {
return res.negotiate(err);
}
if (user) {
res.status(400);
return res.json('User already exists!');
}
});
User.create(params, function(err, user){
if(err){
return next(err);
}
res.status(201);
res.json(user);
});
}
The problem is:
The response is always "User already exists!" with status code - 400
If user exists with the given username or/and email, the above message is displayed regardless and then something is getting logged in the console ( which I dont understand ) and user is not created as in my MySQL those two fields are unique.
**If user does not exists ** the user gets created behind but it still displays the above message.
I want to display the message only if user exists (ie if given credentials matches) else respond with 201
register:function(req, res, next){
var params = req.params.all();
User.find({
or : [
{ usrnm:params.usrname },
{ eml:params.eml }
]
})
.exec(function (err, users){
if (err) {
return res.negotiate(err);
}
if (users.length) {
res.status(400);
return res.json('User already exists!');
} else {
User.create(params, function(err, user){
if(err){
return next(err);
} else {
res.status(201);
res.json(user);
}
});
}
});
}
You should call the create user method if a user with those parameters do not already exist, and so should put it inside the callback.
The User.find() function returns an array, so you need to check its length to see if there are any matching objects.
Okay guys I figured out a solution, i will put it here in case if it helps someone
if (user) { // will be true even if user = []
res.status(400);
return res.json('User already exists!');
}
In case when user is not found in the DB , user is = [ ] , this means [ ] != false
, hence the message within the scope is getting displayed.

Categories

Resources