Node.js / express, javascript callback function not getting executed - javascript

/* GET home page. */
router.get('/home/', function(req, res, next) {
var code = req.query.code;
req.SC.authorize(code, function(err, accessToken) {
if ( err ) {
throw err;
} else {
req.session.oauth_token = accessToken;
// Client is now authorized and able to make API calls
//res.render('home', { token: accessToken });
var url = 'https://api.soundcloud.com/me?oauth_token=' + accessToken;
requestify.get(url).then(function(response){
var user = response.getBody();
req.session.user = user;
var user_url = config.base_url + '/api/users/add';
var options = { user: user };
requestify.post(user_url, options).then(function(response){
console.log("done with users/add")
var href = 'https://api.soundcloud.com/users/' + user.id
+ '/favorites?client_id=' + config.auth.client_id + '&linked_partitioning=1&limit=200';
soundcloud.getCollection(req, res, [], href, function(collection){
console.log("can't get here...");
//console.log(collection);
res.json(collection);
//return collection;
});
/*
var collection_url = config.base_url + '/api/collections/add';
requestify.post(collection_url, options).then(function(response){
console.log("done with collections/add")
res.json(response);
})
*/
});
});
}
});
});
function getCollection(req, res, collection, next_href, done){
console.log("here");
requestify.get(next_href).then(function(response){
var updatedCollection = collection.concat(response.getBody().collection);
if (next_href && updatedCollection.length < 500){
var href = response.getBody().next_href;
getCollection(req, res, updatedCollection, href);
}
else {
console.log("done");
done(updatedCollection);
}
//res.json(response.getBody());
});
}
Behavior I'm seeing is, the collection is properly built up, the console.log("done") is showing up in the console, but after I call done(updatedCollection), the callback function I pass in does not get executed. No print statement, no json rendering. Do you guys see what the issue is?

You're recursively calling the getCollection function without the callback, so the next time it's called, done is undefined.
Pass on the callback to the recursive calls as well
function getCollection(req, res, collection, next_href, done) {
requestify.get(next_href).then(function(response){
var updatedCollection = collection.concat(response.getBody().collection);
if (next_href && updatedCollection.length < 500){
var href = response.getBody().next_href;
getCollection(req, res, updatedCollection, href, done); // <- HERE
} else {
console.log("done");
done(updatedCollection);
}
//res.json(response.getBody());
});
}

Related

Problem using async functions in NodeJs middleware

I am running into an issue when I try to load the initial data for my blacklist from a Redis DB in my middleware code. Since the DB request takes some time it starts to fail.
Below is my code which gets fired when app starts via app.use(blacklist.blockRequests());.
When I try to make the function async I get the error that new TypeError('app.use() requires a middleware function').
One of the side effects is also that my array is empty when it's called again.
blockRequests: function() {
this.read();
this.logEvent('info', 'There are ' + this.blacklist.length + ' address(es) on the blacklist');
var self = this;
var interceptor = function(request, response, next) {
var ip = request.headers['x-forwarded-for'] || request.connection.remoteAddress;
if (self.isInBlacklist(ip)) {
self.logEvent('warn', 'Rejecting request from ' + ip + ', path and query was ' + request.originalUrl);
response.status(403).send();
} else {
next();
}
}
return interceptor;
},
And here is my read() function code:
read: function() {
try {
// get all records with prefix block:: from redis
redis.redis.keys('block::*', function (err, reply) {
// reply is null when the key is missing
if(err){}
else {
this.blacklist = []
for (let i = 0; i < reply.length; i++) {
let ipInt = reply[i].substring(7)
let ipStr = ipToInt(ipInt).toIP()
this.blacklist.push(ipStr)
}
}
});
} catch (error) {
if (error) {
this.blacklist = [];
}
}
}
If you're trying to make blockRequests() async, then it will start returning a promise and you can't use its return value directly in app.use(). Because then you'd be doing app.use(somePromise) and Express will balk because you have to pass it a function reference, not a promise.
Instead, you will have to use .then() or await to get the return value which is the function which you could then use with app.use().
If you show the larger calling context here (like where you're calling blockRequests() from), then we could offer more ideas on a fuller solution.
Here's a conceptual idea for how you could do this:
blockRequests: function() {
const self = this;
const interceptor = function(request, response, next) {
const ip = request.headers['x-forwarded-for'] || request.connection.remoteAddress;
if (self.isInBlacklist(ip)) {
self.logEvent('warn', 'Rejecting request from ' + ip + ', path and query was ' + request.originalUrl);
response.status(403).send();
} else {
next();
}
}
return interceptor;
},
read: function() {
// get all records with prefix block:: from redis
return new Promise((resolve, reject) => {
redis.redis.keys('block::*', (err, reply) => {
if (err) {
this.blacklist = [];
reject(err);
} else {
this.blacklist = [];
for (let i = 0; i < reply.length; i++) {
let ipInt = reply[i].substring(7)
let ipStr = ipToInt(ipInt).toIP()
this.blacklist.push(ipStr)
}
}
this.logEvent('info', 'There are ' + this.blacklist.length + ' address(es) on the blacklist');
resolve();
});
});
}
// register middleware for using blacklist
app.use(blacklist.blockRequests());
// now read the blacklist and when that is in place, then start the server
blacklist.read().then(() => {
// now we know that blacklist.blacklist is up-to-date
// start your server here
}).catch(err => {
console.log("Unable to start server - error in reading blacklist");
process.exit(1);
});

How does "break;" work in async function?

I was using pbkdf2-password module in my login process, and some of those code seems been stuck while running. I used async hashing function to verify users, and problem happens when user inputs wrong passwd. Just loading doesnt stop.
I tried redirecting and return when user inputs wrong passwd and it worked.
but I want to know why the break didnt work.
app.post('/login', (req, res) => {
console.log(req.body);
let userid = req.body.id;
let password = req.body.password;
console.log('userid = ', userid);
console.log('password = ', password);
console.log('userlist = ', sampleUserList);
let bFound = false;
for (let i = 0; i < sampleUserList.length; i++) {
let user = sampleUserList[i];
console.log(sampleUserList[i]);
if (userid === user.userid) {
console.log('[found] userid = ', userid);
bFound = true;
/* here's where I verify users. */
return hasher({
password: password,
salt: user.salt
}, function(err, pass, salt, hash) {
if (err) {
console.log('ERR : ', err);
}
if (hash === user.password) {
console.log('INFO : ', userid, ' logged in successfully')
req.session.user = sampleUserList[i];
req.session.save(function() {
res.redirect('/carlist');
})
return;
/* here's the code for when users input wrong passwd but still have ID */
} else {
console.log('Wrong passwd.');
}
});
}
if (bFound) break;
}
if (!bFound) {
console.log('Theres no such ID.');
}
res.redirect('/login_form');
});
else {
console.log('Wrong passwd.');
res.redirect('/login_form');
return;
}
Problem solved with this code but I want to know why the break didnt work.
no error msg but infinite loading.
The break will never be reached. The return inside the if statement will prevent all subsequent code from executing.
It also breaks out of the loop, so you can feel free to remove break, your function already does what you intended the break to do.
app.post('/login', (req, res) => {
let userid = req.body.id;
let password = req.body.password;
let bFound = false;
for (let i = 0; i < sampleUserList.length; i++) {
let user = sampleUserList[i];
if (userid === user.userid) {
bFound = true;
return hasher(/* args */); // <-- this ends execution of the login handler
}
if (bFound) break;
}
if (!bFound) {
console.log('Theres no such ID.');
}
res.redirect('/login_form');
});

Node.js mssql return query result to ajax

I'm new to learning Node.js, so I'm still getting used to asynchronous programming and callbacks. I'm trying to insert a record into a MS SQL Server database and return the new row's ID to my view.
The mssql query is working correctly when printed to console.log. My problem is not knowing how to properly return the data.
Here is my mssql query - in addJob.js:
var config = require('../../db/config');
async function addJob(title) {
var sql = require('mssql');
const pool = new sql.ConnectionPool(config);
var conn = pool;
let sqlResult = '';
let jobID = '';
conn.connect().then(function () {
var req = new sql.Request(conn);
req.query(`INSERT INTO Jobs (Title, ActiveJD) VALUES ('${title}', 0) ; SELECT ##IDENTITY AS JobID`).then(function (result) {
jobID = result['recordset'][0]['JobID'];
conn.close();
//This prints the correct value
console.log('jobID: ' + jobID);
}).catch(function (err) {
console.log('Unable to add job: ' + err);
conn.close();
});
}).catch(function (err) {
console.log('Unable to connect to SQL: ' + err);
});
// This prints a blank
console.log('jobID second test: ' + jobID)
return jobID;
}
module.exports = addJob;
This is my front end where a modal box is taking in a string and passing it to the above query. I want it to then receive the query's returned value and redirect to another page.
// ADD NEW JOB
$("#navButton_new").on(ace.click_event, function() {
bootbox.prompt("New Job Title", function(result) {
if (result != null) {
var job = {};
job.title = result;
$.ajax({
type: 'POST',
data: JSON.stringify(job),
contentType: 'application/json',
url: 'jds/addJob',
success: function(data) {
// this just prints that data is an object. Is that because I'm returning a promise? How would I unpack that here?
console.log('in success:' + data);
// I want to use the returned value here for a page redirect
//window.location.href = "jds/edit/?jobID=" + data;
return false;
},
error: function(err){
console.log('Unable to add job: ' + err);
}
});
} else {
}
});
});
And finally here is the express router code calling the function:
const express = require('express');
//....
const app = express();
//....
app.post('/jds/addJob', function(req, res){
let dataJSON = JSON.stringify(req.body)
let parsedData = JSON.parse(dataJSON);
const addJob = require("../models/jds/addJob");
let statusResult = addJob(parsedData.title);
statusResult.then(result => {
res.send(req.body);
});
});
I've been reading up on promises and trying to figure out what needs to change here, but I'm having no luck. Can anyone provide any tips?
You need to actually return a value from your function for things to work. Due to having nested Promises you need a couple returns here. One of the core features of promises is if you return a Promise it participates in the calling Promise chain.
So change the following lines
jobID = result['recordset'][0]['JobID'];
to
return result['recordset'][0]['JobID']
and
req.query(`INSERT INTO Jobs (Title, ActiveJD) VALUES ('${title}', 0) ; SELECT ##IDENTITY AS JobID`).then(function (result) {
to
return req.query(`INSERT INTO Jobs (Title, ActiveJD) VALUES ('${title}', 0) ; SELECT ##IDENTITY AS JobID`).then(function (result) {
and
conn.connect().then(function () {
to
return conn.connect().then(function () {
You may need to move code around that is now after the return. You would also be well served moving conn.close() into a single .finally on the end of the connect chain.
I recommend writing a test that you can use to play around with things until you get it right.
const jobId = await addJob(...)
console.log(jobId)
Alternatively rewrite the code to use await instead of .then() calls.

call synchrounously process in node.js

I am stuck in nodejs during calling of zendesk api.
As i called zendesk.tickets.incremental Api, it provides me ticketId and that used in another function for getting any change from previous by calling zendesk.tickets.exportAudit.
I also get response too but during fetching the data another ticketId called so previously flag an error in response "error: item not found " and than fetch the data for new ticketId and so on.
What I need, I need it block the process until data of first Id completely .
This is my code.
//Calling ticketIncremental Details ticketId (likes 1, 2 etc)
app.get('/', function (req, res) {
zendesk.tickets.incremental(0, function(err, statusList, body, responseList, resultList) {
if (err) {
console.log(err);
return;
}
var ticketIncreDetails = (JSON.stringify(body));
res.end(ticketIncreDetails);
for (var i=0; i< body.length; i++ ) {
ticketValues(body[i].id) //within this function another API of zendek calling for exportAudit
}
});
//This is for exportAudit
function ticketValues(ticketId) {
zendesk.tickets.exportAudit(ticketId, function(err,statusList, body, responseList, resultList) {
if(err) {
console.log(err);
return;
}
console.log("ticketExportAudit: " + JSON.stringify(body)) });
As #qxz say, it's better to check out if there is sync package or not, or you need to handle this focusing on callback because zendesk.tickets.exportAudit need time to complete its work, but for loop wouldn't act like that, the code below handle this problem with callback, you could have a look.
//Calling ticketIncremental Details ticketId (likes 1, 2 etc)
app.get('/', function (req, res) {
zendesk.tickets.incremental(0, function(err, statusList, body, responseList, resultList) {
if (err) {
console.log(err);
return;
}
var ticketIncreDetails = (JSON.stringify(body));
res.end(ticketIncreDetails);
ticketValues(body,body.length,0);
//ticketValues(body,body.length,0,function(){..if you wanna do something after..});
});
});
//This is for exportAudit
function ticketValues(ticket,length,index,callback) {
zendesk.tickets.exportAudit(ticke[index].id, function(err,statusList, body, responseList, resultList) {
if(index<length){
if(err) {
console.log(err);
return;
}else{
console.log("ticketExportAudit: " + JSON.stringify(body));
index++;
ticketValues(ticket,length,index,callback);
}
}else{
if(callback)
callback();
}
});
}

Node/Express - How to wait until For Loop is over to respond with JSON

I have a function in my express app that makes multiple queries within a For Loop and I need to design a callback that responds with JSON when the loop is finished. But, I'm not sure how to do this in Node yet. Here is what I have so far, but it's not yet working...
exports.contacts_create = function(req, res) {
var contacts = req.body;
(function(res, contacts) {
for (var property in contacts) { // for each contact, save to db
if( !isNaN(property) ) {
contact = contacts[property];
var newContact = new Contact(contact);
newContact.user = req.user.id
newContact.save(function(err) {
if (err) { console.log(err) };
}); // .save
}; // if !isNAN
}; // for
self.response();
})(); // function
}; // contacts_create
exports.response = function(req, res, success) {
res.json('finished');
};
There are a few problems with your code besides just the callback structure.
var contacts = req.body;
(function(res, contacts) {
...
})(); // function
^ you are redefining contacts and res in the parameter list, but not passing in any arguments, so inside your function res and contacts will be undefined.
Also, not sure where your self variable is coming from, but maybe you defined that elsewhere.
As to the callback structure, you're looking for something like this (assuming contacts is an Array):
exports.contacts_create = function(req, res) {
var contacts = req.body;
var iterator = function (i) {
if (i >= contacts.length) {
res.json('finished'); // or call self.response() or whatever
return;
}
contact = contacts[i];
var newContact = new Contact(contact);
newContact.user = req.user.id
newContact.save(function(err) {
if (err)
console.log(err); //if this is really a failure, you should call response here and return
iterator(i + 1); //re-call this function with the next index
});
};
iterator(0); //start the async "for" loop
};
However, you may want to consider performing your database saves in parallel. Something like this:
var savesPending = contacts.length;
var saveCallback = function (i, err) {
if (err)
console.log('Saving contact ' + i + ' failed.');
if (--savesPending === 0)
res.json('finished');
};
for (var i in contacts) {
...
newContact.save(saveCallback.bind(null, i));
}
This way you don't have to wait for each save to complete before starting the next round-trip to the database.
If you're unfamiliar with why I used saveCallback.bind(null, i), it's basically so the callback can know which contact failed in the event of an error. See Function.prototype.bind if you need a reference.

Categories