Node.js mssql data input layer with connection pool - sql-server

This question relates to a Node.js mssql API.
I've recently updated my code to use a SQL.ConnectionPool instead of sql.connect which when combined with an async / await function allowed me to get around connection.close() errors.
In my previous (OLD) executeQuery function, I was able to pass an array which I could push values into to use with "request.input(name, value)"
Function call example:
app.get('/api/route/:id', function (req, res) {
var id = req.params.id;
let x = []
if (id != null && id != NaN) {
x.push({
Name: 'id', Value: id
})
var query = `SELECT * from [Table] where ID = #id`
executeQuery(res, query, arr);
} else {
res.send(500)
}
})
OLD Function:
var executeQuery = function (res, query, arr) {
sql.connect(dbConfig, function (err) {
if (err) {
console.log('Error while connecting to the database: ' + err)
res.send(err)
} else {
// Create the request object
var request = new sql.Request();
if (arr != null) {
if (arr.length > 0) {
for (var obj of arr) {
request.input(obj.Name, obj.Value)
}
}
}
request.query(query, function (err, rs) {
if (err) {
sql.close();
console.log('Error while querying the database : ' + err);
res.send(err);
} else {
sql.close();
console.log(rs)
res.send(rs)
}
})
}
})
}
NEW Function:
var executeQuery = async function(res, query, arr){
const pool = new sql.ConnectionPool(dbConfig);
pool.on('error', err => {
console.log('sql errors ', err);
});
try {
await pool.connect();
let result = await pool.request().query(query);
console.log('success')
res.send(result);
return {success: result};
} catch (err) {
console.log('error')
console.log(err)
res.send(err);
return {err: err};
} finally {
pool.close();
}
}
Question
How do I go about achieving the same request.input process with a ConnectionPool as I did with my previous function ( like the below )
var request = new sql.Request();
if (arr != null) {
if (arr.length > 0) {
for (var obj of arr) {
request.input(obj.Name, obj.Value)
}
}
}
Thank you.

Related

Why is the data being added to the same table twice?

I have a database that has a queues table that holds queues names and ids, and the two queue tables (queue 1, and queue 2).
now i have a function that is supposed to add orders to the right queue, but it's only adding to the second queue and i'm already using a for loop to change the queue that should be used.
here's the code
for (int i = 0; i < tmpQueuesIds.length; i++){
await http.post(GlobalState.ADDTOQUEUE, body: {
'queueId': tmpQueuesIds[i].toString(),
'timeinmin': tmpTimes[tmpQueuesIds[i]].toString(),
'resId': _globalState.get('resId').toString(),
'userId': userId.toString(),
});
print("QueueId: " + tmpQueuesIds[i].toString());
print("OrderTime: " + tmpTimes[tmpQueuesIds[i]].toString());
Future.delayed(Duration(milliseconds: 500));
}
and here's the output:
I/flutter ( 3238): QueueId: 1
I/flutter ( 3238): OrderTime: 6
I/flutter ( 3238): QueueId: 2
I/flutter ( 3238): OrderTime: 10
as you can see i have two queues and the values are being changed, but it's inserting the value 10 twice to the queue 2, it should be inserting value 6 to queue 1 and value 10 to queue 2.
and here's the api code
try {
router.post('/', (req, res, next) => {
queueId = req.body.queueId;
resId = req.body.resId;
timeinmin = req.body.timeinmin;
userId = req.body.userId;
var orderId;
sqlQuery = `SELECT id FROM orders where userid = ${userId} and isdone = false`;
con.query(sqlQuery, (err, rows) => {
try {
lastIndex = rows['rows'].length - 1;
orderId = rows['rows'][lastIndex]['id'];
} catch{
console.log('something wrong with setting orderId or lastIndex value in addToQueue.js');
}
if (!err) {
sqlQuery2 = `SELECT name FROM queues where id = ${queueId}`;
try {
con.query(sqlQuery2, function (err, rows) {
try {
queueName = rows['rows'][0]['name'];
} catch{
console.log('something wrong with setting queueName value in addToQueue.js');
}
if (!err) {
sqlQuery2 = `INSERT INTO ${queueName} (timeinmin, resid, orderid) VALUES(${timeinmin}, ${resId}, ${orderId})`;
try {
con.query(sqlQuery2, function (err, rows) {
if (!err) {
res.sendStatus(201);
console.log('added to queue');
} else {
console.error("Failed to add to queue");
console.log(err);
res.sendStatus(202);
}
});
} catch{
console.log('Something went down.');
}
} else {
console.error("Failure");
console.log(err);
res.sendStatus(202);
}
});
} catch{
console.log('Something went down.');
}
} else {
console.log(err);
res.sendStatus(202);
}
})
});
} catch{
console.log('Error');
}
help me please, thanks.
I can't find any problems with your code.
My advice is to try restarting your IDEs and if the problem still persists try restarting your whole computer.

Array populated in debug more but not in in normal mode in Node.js

In the code below, when I run in debug mode with a break-point at this line: content.push(data.Body.toString()); I can see that data is inserted to the content array.
However when I run the code normally, content comes back empty.
How can I get it to populate the array for downstream use?
var params = { Bucket: "thebucket", Prefix: "theprefix/" }
var content = [];
function getS3Data()
{
var s3 = new aws.S3();
s3.listObjects(params, function (err, data)
{
if (err) throw err; // an error occurred
else
{
var i;
for (i = 0; i < data.Contents.length; i++)
{
var currentValue = data.Contents[i];
if(currentValue.Key.endsWith(params.Prefix) == false)
{
var goParams = { Bucket: params.Bucket, Key: currentValue.Key };
s3.getObject(goParams, function(err, data)
{
if (err) throw err; //error
content.push(data.Body.toString());
});
};
};
}//else
});//listObjects
}//getS3Data
getS3Data();
console.log(content); //prints empty here when run in non-debug.
The line:
console.log(content)
is being executed before the line:
content.push(data.Body.toString());
the function you are passing as a 2nd argument to s3.listObjects will be executed asynchronously. If you want to log out content you need to do it within the callback function meaning:
s3.listObjects(params, function (err, data) {
if (err) throw err;
else {
// ...
console.log(content)
}
});
A better approach would be to implement getS3Data with Promise so you can run code after the object listing is done for sure.
function getS3Data() {
return new Promise((resolve, reject) => {
if (err) {
reject(err)
} else {
const promises = []
for (const i = 0; i < data.Contents.length; i++) {
const currentValue = data.Contents[i];
if (currentValue.Key.endsWith(params.Prefix) == false) {
const goParams = { Bucket: params.Bucket, Key: currentValue.Key };
promises.push(new Promise((res, rej) => {
s3.getObject(goParams, function (err, data) {
if (err) {
rej(err); //error
} else {
res(data.Body.toString());
}
});
}));
}
}
Promise.all(promises).then(resolve);
}
});
}
getS3Data()
.then(result => { // this will actually be `content` from your code example
console.log(result);
}).catch(error => {
console.error(error);
})
Node.js' documentation has an example very similar to the problem you are experiencing:
Dangers of Mixing Blocking and Non-Blocking Code
The issue arises because the variable content is not set as soon as getS3Data has finished, because it is an asynchronous function. content will be set some time later. But your call to console.log(content); will execute immediately after getS3Data has finished, so at that point content has not been set yet.
You can test that by adding an extra log:
s3.getObject(goParams, function(err, data)
{
if (err) throw err; //error
content.push(data.Body.toString());
console.log("Content has been assigned");
});
And then change the bottom to:
getS3Data();
console.log("getS3Data has finished", content);
It's likely you'll get the messages in this order:
getS3Data has finished
Content has been assigned

Promises not running T-SQL query in node js

I am trying to write a function in node js that will run a SQL query using mssql and return a promise. For some reason it gets to the line
console.log("got to the run proc functions");
but won't run any code after that. Any help with this issue would be greatly appreciated.
runProc: function (params) {
var sql = require("mssql");
sql.Promise = require('promise');
return new Promise((resolve, reject) => {
var dbConfig = {
server:"ip",
database: "db",
user:"user",
password: "pw"
}
console.log("got to the run proc functions");
var keys = Object.keys(params);
sql.connect(dbConfig).then(pool => {
console.log("got connected");
const request = pool.request()
for (var i = 0; i < keys.length; i++) {
if (keys[i].substring(0,6)=="Common") {
request.input(keys[i],sql.Bit,params[keys[i]]);
console.log("set the bit parameters");
}
else {
request.input(keys[i],params[keys[i]]);
console.log("set the other parameters");
}
}
request.execute("storedprocedure")
return request;
}).then(result => {
resolve(result)
}).catch(err => {
reject(Error(err))
});
sql.close();
});
}
Look where you call sql.close(). i.e. it's being called before request=pool.request etc! So, that would fail for a start.
Also, you are returning request (which isn't a promise) rather than request.execute() (which is a promise) so, the promise would resolve before execute completes
And finally, no need to wrap a promise inside a Promise constructor (not that this breaks your code, but there's no need for it)
Given all that, your code is
runProc: function (params) {
var sql = require("mssql");
sql.Promise = require('promise');
// removed new Promise constructor as sql.connect already returns a promise we can chain to and return
var dbConfig = {
server:"ip",
database: "db",
user:"user",
password: "pw"
}
console.log("got to the run proc functions");
var keys = Object.keys(params);
return sql.connect(dbConfig)
.then(pool => {
console.log("got connected");
const request = pool.request()
for (var i = 0; i < keys.length; i++) {
if (keys[i].substring(0,6)=="Common") {
request.input(keys[i],sql.Bit,params[keys[i]]);
console.log("set the bit parameters");
}
else {
request.input(keys[i],params[keys[i]]);
console.log("set the other parameters");
}
}
// need to return the result of execute, not the request object
return request.execute("storedprocedure")
})
.catch(err => throw new Error(err))
// use Promise#finally - available in all good promise libraries
.finally(sql.close); // sql.close AFTER we're done with it, not before
}
Using the sparkly new async/await promise sugar simplifies the code even more
runProc: async function (params) {
var sql = require("mssql");
sql.Promise = require('promise');
var dbConfig = {
server:"ip",
database: "db",
user:"user",
password: "pw"
}
console.log("got to the run proc functions");
var keys = Object.keys(params);
try {
const pool = await sql.connect(dbConfig);
const request = pool.request();
for (var i = 0; i < keys.length; i++) {
if (keys[i].substring(0,6)=="Common") {
request.input(keys[i],sql.Bit,params[keys[i]]);
console.log("set the bit parameters");
}
else {
request.input(keys[i],params[keys[i]]);
console.log("set the other parameters");
}
}
// need to return the result of execute, not the request object
return await request.execute("storedprocedure");
} catch (err) {
throw new Error(err);
} finally {
sql.close();
}
}

How to clear an array in MongoDB

I have an emails object that contains an array in a mongodb database. However, when I try to use $set to make the array empty it doesn't work. How am I supposed to clear the array?
exports.clearEmails = function(req, res, next) {
var listId = req.params.id;
var errors = req.validationErrors();
if (errors) {
return res.status(400).send(errors);
}
EmailList.update({'_id': listId}, {$set: {'emails': []}}, function(err,results) {
if (err) {
return res.status(400).send(err);
} else {
return res.status(200).send(results);
}
});
}

Iterate from mongodb collection and make api calls and add new keys and send data

I'am using below code to get data from a collection
Marketing.find({
shopId: req.params.shopId,
locationId: req.params.locationId,
}).exec(function (err, campaigns) {
if (err) {
return next(err);
} else if (!campaigns) {
return next(new Error('Failed to load Campaigns '+ req.params.shopId));
}
I want to make a api call on campaigns object i use the below code
campaigns.forEach(function(item) {
async.waterfall([
function (done) {
item.opens = "-";
item.requests = "-";
var currentDate = new Date();
var formatedDate = currentDate.toISOString().slice(0,10);
var request = sg.emptyRequest();
request.queryParams.aggregated_by = 'day';
request.queryParams.limit = '1';
request.queryParams.start_date = '2016-01-01';
request.queryParams.end_date = formatedDate;
request.queryParams.offset = '1';
request.queryParams.categories = item._id;
request.method = 'GET';
request.path = '/v3/categories/stats';
sg.API(request, function (response) {
response.body = JSON.parse(response.body);
done(err,response.body)
});
},
function (data,done) {
for(var i=0;i<data.length;i++){
unique_opens = parseInt(unique_opens)+parseInt(data[i].stats[0].metrics.unique_opens);
opens = parseInt(opens)+parseInt(data[i].stats[0].metrics.opens);
requests = parseInt(requests)+parseInt(data[i].stats[0].metrics.requests);
}
if(unique_opens>=1 && requests>=1){
item.clickrate = (unique_opens/opens)*100;
}
else{
item.clickrate = 0;
}
item.opens = opens;
item.requests = requests;
console.log(item.opens);
opens = 0;
unique_opens = 0;
requests = 0;
console.log(item);
},
], function (error) {
if (error) {
//handle readFile error or processFile error here
}
});
});
And at the end i do
res.json(campaigns);
But it doesn't add the two new keys in each index [ opens & request ]
Use map() as follows:
Marketing.find({
shopId: req.params.shopId,
locationId: req.params.locationId,
}).lean().exec(function (err, campaigns) {
if (err) {
return next(err);
} else if (!campaigns) {
return next(new Error('Failed to load Campaigns '+ req.params.shopId));
}
campaigns = campaigns.map(function(item){
return {
opens: 'SOMELOGIC',
requests: 'SOMELOGIC',
opens: item.opens,
requests: item.requests,
};
});
res.json(campaigns);
}

Resources