I am attempting to stream the data from a procedure that returns multiple data sets using node and mssql. If using as a stand a long function it works, but I need it to return the dataset from the route i am using.
handler: function(request, reply) {
var inputValues = request.payload.inputParams;
var procName = request.params.procedureName;
var request = new sql.Request(mainsettings.connection);
request.stream = true;
var newGroup = [];
var count = 0;
var recordSetArr = [];
for(var key in inputValues) {
var currentParam = inputValues[key];
var paramType = getParamType(sql, currentParam.paramType);
try {
request.input(currentParam.paramName, paramType, currentParam.paramValue);
}
catch(err) {
console.error(err);
}
}
request.execute(procName);
request.on('recordset', function(columns) {
// Emitted once for each recordset in a query
count++;
if(count > 1) {
recordSetArr.push(newGroup);
}
newGroup = [];
});
request.on('row', function(row) {
// Emitted for each row in a recordset
newGroup.push(row);
});
request.on('error', function(err) {
// May be emitted multiple times
console.error(err);
});
request.on('done', function(returnValue) {
// Always emitted as the last one
return (recordSetArr);
});
}
I am getting the following error.
Debug: internal, implementation, error
Error: handler method did not return a value, a promise, or throw an error
at module.exports.internals.Manager.execute (F:\FARA_API\node_modules\hapi\l
ib\toolkit.js:52:29)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
180406/194833.505, (1523044113505:MISOM-DEV-002:6976:jfod4ysa:10140) [error] mes
sage: handler method did not return a value, a promise, or throw an error, stack
: Error: handler method did not return a value, a promise, or throw an error
at module.exports.internals.Manager.execute (F:\FARA_API\node_modules\hapi\l
ib\toolkit.js:52:29)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
Any suggestions on how I am supposed to return the data set properly?
Also tried the following
handler: function(request, reply) {
recordRouteCall("procedure");
var inputValues = request.payload.inputParams;
var procName = request.params.procedureName;
sql.connect(mainsettings.connection, err => {
var request = new sql.Request();
request.stream = true;
var newGroup = [];
var count = 0;
var recordSetArr = [];
for(var key in inputValues) {
var currentParam = inputValues[key];
var paramType = getParamType(sql, currentParam.paramType);
try {
request.input(currentParam.paramName, paramType, currentParam.paramValue);
}
catch(err) {
console.error(err);
}
}
request.execute(procName);
request.on('recordset', columns => {
// Emitted once for each recordset in a query
count++;
if(count > 1) {
recordSetArr.push(newGroup);
}
newGroup = [];
});
request.on('row', row => {
// Emitted for each row in a recordset
newGroup.push(row);
});
request.on('error', err => {
// May be emitted multiple times
console.error(err);
});
request.on('done', result => {
// Always emitted as the last one
console.log(recordSetArr);
return (recordSetArr);
});
});
sql.on('error', err => {
console.error(err);
return err;
});
}
Related
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
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();
}
}
I want to return a boolean promise from this function, any help please?
getSectionsList() {
let sections: string[] = [];
var ref = this.db.database.ref(`instructors/jDlPCWMMJ8YmD9tMO3shTshM5sx1/sections`);
return ref.once('value').then(function (snap) {
var array = snap.val();
for (var i in array) {
var value = array[i];
sections.push(value.section)
//console.log(value.section)
}
if (sections.includes('SPRING_2018_CMPS333_L01'))
return (true)
else
return (false)
}).catch(function (err) {
console.log(err)
})
}
Try injecting the AngularJS $q service...
if (sections.includes('SPRING_2018_CMPS333_L01'))
return $q.resolve(true);
else
return $q.resolve(false);
you can also $q.reject(false)
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);
}
When i insert on my database , it inserting same data twice.
Table Create
var oOptions = {
keyPath: account.primaryKey,
autoIncrement: true
};
var oStore = dbHandle.createObjectStore(account.tableName, oOptions);
var oIxOptions = {
unique: false
};
account.fields.forEach(function(item) {
oStore.createIndex(item + "Index", item, oIxOptions);
});
Insert
var defered = $q.defer();
try {
var objectStore = config.database.transaction(tableName, "readwrite").objectStore(tableName);
var result = objectStore.add(entity);
result.onerror = function(e) {
defered.reject("Can't insert into account");
throw e;
}
result.onsuccess = function(e) {
defered.resolve();
}
} catch (e) {
defered.reject("Can't insert into account");
throw e;
}
return defered.promise;
Retrive
var defered = $q.defer();
try {
var req = $window.indexedDB.open(config.databaseName, 1.0);
req.onsuccess = function(evt) {
config.database = evt.target.result;
var transaction = config.database.transaction(account.tableName, IDBTransaction.READ_ONLY);
var objectStore = transaction.objectStore(account.tableName);
var tmpData = [];
objectStore.openCursor().onsuccess = function(event) {
var cursor = event.target.result;
if (!cursor) {
defered.resolve(tmpData);
return;
}
tmpData.push(cursor.value);
cursor.continue();
};
}
} catch (e) {
defered.reject("Can't pull from account");
throw e;
}
return defered.promise;
Any suggestion ??
This might be less of a problem with indexedDB and more a problem with your use of try/catch and promises. Have you tested without try/catch and without promises? What is your rationale for using promises here? Consider that you don't need try/catch and you don't need promises to perform these simple tasks.