Integrate mysql in waterfall async - mysql

I need to save to db inside an async waterfall series.
I've tried to integrate these two function after the clean function
function connectDb(next) {
pool.getConnection(function(err, connection) {
if (err) console.log(err);
conn = connection;
}, next);
},
function saveDb(next) {
let sql = "UPDATE media SET media_url = ? WHERE media_url = ?";
conn.query(sql, [dstKey, srcKey], function (error, results, fields) {
if (error) {
conn.release();
console.log(error);
}else{
console.log("media db updated");
}
}, next)
}
The problem is that these two functions block the code execution. How can I integrate it in the function below? I've tried to wrap the function in promise but it is also not working.
async.waterfall([
function download(next) {
s3.getObject({
//param
},
next);
},
function transform(response, next) {
resizeMedia(response.Body ).then( ( file ) => { next();} ).catch( (err) => { reject(err) } ); }
},
function upload(next) {
var fileData = fs.createReadStream('/tmp/'+dstKey);
if (isVideo ) { var ContentType = 'video/mp4' }
if (isAudio ) { var ContentType = 'audio/mp3' }
s3.putObject({
//param
},
next);
},
function clean(next) {
// Stream the transformed image to a different S3 bucket.
fs.unlinkSync('/tmp/'+dstKey);
s3.deleteObject({
//param
},
next);
}
], function (err) {
if (err) {
console.error('Error');
callback(null, "Error");
return;
} else {
console.log('Success');
callback(null, "Done");
return;
}
callback(null, "Done");
return;
}
);

The purpose of async waterflow is to block the waterfall until the callback is called.
P.S. Usually you should not create a new db connection each time. The connection should be done once when the application start and get used whenever you need.
I highly recommend you to use knex.js instead, it return promises by default and if you like to use it inside async waterfall (and wait for resolve) you can call .asCallback.

I've found the problem, if someone fall into the same issue here my solution:
If a waterfall function has a response, this response is automatically added as first argument in the next function. In my code the mistake was simple (after night's sleep), the s3.deleteObject and s3.putObject has response, this response need to be setted as first argument and the callback as last, as you say I've used only the callback as argument (next) and this broke my code.
[...]
function upload(next) {
s3.putObject({
//param
},
next);
},
function clean(response, next) { // response in arguments
s3.deleteObject({
//param
},
next);
}
[...]

Related

Node.js with SQL: ERR_EMPTY_QUERY

I am trying to create a service with MySQL as database. I have my query stored in the database and am calling it in the node.js service. It is a two step process as the first query will give the query to be the run as the result to next connection. However my first part is working fine but when the connection moves to second part it gives ERR_EMPTY_QUERY.
Here is my code. The error is at the second part connection.query(dbresult, function (err, done)).
var async = require('async');
module.exports.getChart = function (chartcode,filter,callback){
var fuelweekquery= "SELECT * FROM t_chart_val where CHART_CODE=?"
if (null == filter || "week"==(filter)){
result= fuelweekquery;
}
async.parallel([
function (callback) {
//if(null==filter || "day"==(filter))
pool.getConnection(function (err, connection) {
connection.query(result,[chartcode], function (err, done) {
if (err) {
connection.release();
}
for (var i in done) {
dbresult = done[i].CHART_ATTR_VAL;
}
callback(err, done);
}
);
});
},
function (callback) {
//if(null==filter || "day"==(filter))
pool.getConnection(function (err, connection) {
connection.query(dbresult, function (err, done) {
if (err) {
connection.release();
}
//connection.release();
console.log(done);
callback(err, done);
}
);
});
},
], function (err, results) {
if (err) console.log(err);
//console.log('queries finished', results);
callback(null, results);
});
}
According to caolan async documentation (Source),
Run the tasks collection of functions in parallel, without waiting until the previous function has completed. If any of the functions pass an error to its callback, the main callback is immediately called with the value of the error. Once the tasks have completed, the results are passed to the final callback as an array.
Therefore it does not wait for the first in the array to complete before executing the second, for this I would recommend you use async waterfall
async.waterfall([
function(callback) {
//Your DB operation
callback(null, dbresult );
},
function(dbresult, callback) {
// use dbresult for your second operation
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});

Converting csv to json object returns undefined

I've found several examples on S/O and otherwise, but they don't seem to be helping me.
I'm creating a private module in node that takes in a csv and converts it into a json object. It is properly outputting the correct value onto the command line, but the object itself is undefined.
exports.csvToJSON = function(file, callback) {
converter.fromFile(file, function(err, result) {
if (err) {
console.log('error: ', error)
} else {
return result;
callback(err, result);
}
});
console.log(callback)
}
I'm currently using the csvtojson module and have tried other similar packages. Articles I've referenced:
Function Returns undefined object
Why does this callback return undefined?
Why is function return value undefined when returned in a loop? (although not entirely relevant as this is not a loop function)
Callback returning undefined
I'm unsure if I'm just not understanding the callback correctly, but even if I console.log(result.type), it returns back undefined with or without the callback. I've also tried defining the callback like so:
exports.csvToJSON = function(file, callback) {
csvtojson().fromFile(file, function(err, result) {
if (err) {
console.log('error: ', error)
}
return result;
callback(result);
});
}
Here's an example of the console output:
Mirandas-MacBook-Pro:zendesktool mirandashort$ node ./controllers/update.js
[ { ticket:
{ ids: '3280805',
requester_email: 'miranda#barkbox.com',
status: 'pending',
comment: [Object],
subject: 'sup dog',
custom_fields: [Object],
add_tags: 'update_via_csv, dogs_are_cool, sup' } } ] undefined
Right now, since my other functions are dependent on this working, I'm only calling it in the file with exports.csvToJSON('update2.csv') where update2.csv is an actual file. Eventually this will be called inside another function in which I will be utilizing async, but I need this to work first. Additionally, that output seems to be linked to console.log(err) when called by the second code block example, which I'm not to sure why.
Or if there's a way to do this altogether without csvtojson, that's fine too. The only requirement be that a file in csv format can be returned as an object array.
Got it. I just used waterfall to put the two individual modeles together:
exports.csvToJSON = function(file, callback) {
csvtojson().fromFile(file, function(err, result) {
if (err) {
console.log(err);
} else {
callback(null, result[0]);
}
});
}
exports.zdUpdateMany = function(data, callback) {
credentials.config.tickets.updateMany(3280805, data, function(err, result) {
if (err) {
console.log(err);
} else {
callback(false, result);
}
});
}
// function to update tickets
exports.processUpdate = function(file, callback) {
async.waterfall([
async.apply(exports.csvToJSON, file),
exports.zdUpdateMany
], function(err, result) {
if (err) {
console.log(err);
} else {
console.log(result);
}
callback(err, result);
});
}

nodejs- unable to return result to controller function

From my Model, I fetch some articles from a MySQL database for a user.
Model
var mysql = require('mysql');
var db = mysql.createPool({
host: 'localhost',
user: 'sampleUser',
password: '',
database: 'sampleDB'
});
fetchArticles: function (user, callback) {
var params = [user.userId];
var query = `SELECT * FROM articles WHERE userId = ? LOCK IN SHARE MODE`;
db.getConnection(function (err, connection) {
if (err) {
throw err;
}
connection.beginTransaction(function (err) {
if (err) {
throw err;
}
return connection.query(query, params, function (err, result) {
if (err) {
connection.rollback(function () {
throw err;
});
}
//console.log(result);
});
});
});
}
This is working and the function fetches the result needed. But it's not returning the result to the controller function (I am returning it but I'm not able to fetch it in the controller function. I guess, I did something wrong here).
When I did console.log(result) this is what I got.
[ RowDataPacket {
status: 'New',
article_code: 13362,
created_date: 2017-10-22T00:30:00.000Z,
type: 'ebook'} ]
My controller function looks like this:
var Articles = require('../models/Articles');
exports.getArticle = function (req, res) {
var articleId = req.body.articleId;
var article = {
userId: userId
};
Articles.fetchArticles(article, function (err, rows) {
if (err) {
res.json({ success: false, message: 'no data found' });
}
else {
res.json({ success: true, articles: rows });
}
});
};
Can anyone help me figure out what mistakes I made here?
I'm pretty new to nodejs. Thanks!
The simple answer is that you're not calling the callback function, anywhere.
Here's the adjusted code:
fetchArticles: function (user, callback) {
var params = [user.userId];
var query = `SELECT * FROM articles WHERE userId = ? LOCK IN SHARE MODE`;
db.getConnection(function (err, connection) {
if (err) {
// An error. Ensure `callback` gets called with the error argument.
return callback(err);
}
connection.beginTransaction(function (err) {
if (err) {
// An error. Ensure `callback` gets called with the error argument.
return callback(err);
}
return connection.query(query, params, function (err, result) {
if (err) {
// An error.
// Rollback
connection.rollback(function () {
// Once the rollback finished, ensure `callback` gets called
// with the error argument.
return callback(err);
});
} else {
// Query success. Call `callback` with results and `null` for error.
//console.log(result);
return callback(null, result);
}
});
});
});
}
There's no point in throwing errors inside the callbacks on the connection methods, since these functions are async.
Ensure you pass the error to the callback instead, and stop execution (using the return statement).
One more thing, without knowing the full requirements of this:
I'm not sure you need transactions for just fetching data from the database, without modifying it; so you can just do the query() and skip on using any beginTransaction(), rollback() and commit() calls.

Using Lodash to loop through https.get when parsing XML to JSON in Node

I'm trying to parse XML to JSON in Node. I'm using xml2js. I'd like to incorporate Lodash to loop through each number in an array and use the corresponding url to convert the XML to JSON. When I use the code below, I get a Non-whitespace before first tag error. Any idea what I'm doing wrong?
const no = [78787878,78787879, 787878780];
_.forEach(no, https.get('https://tsdrapi.uspto.gov/ts/cd/casestatus/'+no+'/info.xml', function (res) {
res.on('data', function (chunk) {
response_data += chunk;
});
res.on('end', function () {
parser.parseString(response_data, function (err, result) {
if (err) {
console.log('Got error: ' + err.message);
} else {
console.log(util.inspect(result, false, null));
}
});
});
res.on('error', function (err) {
console.log('Got error: ' + err.message);
});
}));
Honestly the forEach helper in LoDash is kind of silly. forEach is a prototype method of any Array instance. One problem is that these functional helpers are not designed to handle async flow control.
While there are a dozen ways to handle flow control the easiest would probably be to use caolan/async module's map() method.
You're code would look something like:
var no = [78787878,78787879, 787878780];
async.map(no, function(cb) {
https.get('https://tsdrapi.uspto.gov/ts/cd/casestatus/'+no+'/info.xml', function (res) {
var response_data = '';
res.on('data', function (chunk) {
response_data += chunk;
});
res.on('end', function () {
parser.parseString(response_data, function (err, result) {
if (err) {
cb(err);
} else {
cb(null, result);
}
});
});
res.on('error', cb);
})
}, function(err, results) {
if(err) {
console.log("Error occured: ", err);
}
else {
console.log("Results(array): ", results);
}
});
The difference here is that async maps the array to a function with a callback. This way you can gather the response from each request into an array and fire a callback when each request has responded. If one of them error's out the process stops and fires the final callback where the error is logged(or you can write logic to handle another way).

xml2js: put parser into a function

I got this code on my NodeJS server:
function GetXML() {
fs.readFile('../slideshow.xml.old', function(err, data) {
parser.parseString(data, function (err, result) {
var json = JSON.stringify(result);
console.log(json);
return json;
});
});
}
The console.log() is working well but this is not:
.get('/', function(req, res) {
res.end(GetXML());
};
It returns undefined which is quite logic because functions are nested (I think ?). But I don't know how to make GetXML() returning a value.
It's returning undefined because you're trying to execute synchronously an asynchronous task. You have to pass a callback to your GetXML() function, like:
function GetXML(cb) {
fs.readFile('../slideshow.xml.old', function(err, data) {
parser.parseString(data, function (err, result) {
var json = JSON.stringify(result);
cb(json);
});
});
}
, and call it properly in your .get function:
.get('/', function(req, res) {
GetXML(function (json) {
res.end(json);
});
};
You should take a look at this article that explains how callbacks work in node.js.
"GetXML" is not returning a value. It can be change to:
function GetXML() {
return fs.readFile('../slideshow.xml.old', function(err, data) {
parser.parseString(data, function (err, result) {
var json = JSON.stringify(result);
console.log(json);
return json;
});
});
}