reading google spreadsheet data using node js not working..? - javascript

I am using postman to call api. I am trying to read google spread sheet row using node js. Response is printing on console but its not returning to postman.
index.js file
app.post('/myapi/getClientkey',async (req, res) => {
var response = null;
console.log("Inside myapi");
try {
response = await spreadsheet.getRecord();
} catch(err){
}
res.send(response);
});
spreadsheet.js
var config = require('./config.json');
var GoogleSpreadsheet = require('google-spreadsheet');
var creds = {
client_email: config.client_email,
private_key: config.private_key
}
var doc = new GoogleSpreadsheet(config.GOOGLE_SHEET_ID)
var sheet = null;
exports.getRecord = async function () {
console.log('Inside - getRecord');
var name = null;
var jsonObj = {};
try {
doc.useServiceAccountAuth(creds, async function (err) {
// Get all of the rows from the spreadsheet.
await doc.getRows(1, async function (err, rows) {
if(rows != null){
var oneRow = rows[0];
name = oneRow.name;
console.log("name :"+name);
jsonObj.client_name = name.toString();
}
console.log(jsonObj);
});
});
}catch(err){
console.log("err :"+err);
}
return jsonObj;
};
How to wait till response is returned from getRecord Function

Move res.send(response); inside try block and read about how to return response from an async call.
Also return jsonObj should be inside try block

I used promise call now it working.
exports.getRecord = async function () {
console.log('Inside - getRecord');
var name = null;
return new Promise( async function(resolve,reject){
try {
var jsonObj = {};
doc.useServiceAccountAuth(creds, async function (err) {
// Get all of the rows from the spreadsheet.
await doc.getRows(1, async function (err, rows) {
if(rows != null){
var oneRow = rows[0];
name = oneRow.name;
console.log("name :"+name);
jsonObj.client_name = name.toString();
}
console.log(jsonObj);
resolve(jsonObj);
});
});
}catch(err){
console.log("err :"+err);
reject();
}
}); // end of promise
};

Related

JavaScript object value undefined

it passes the object to the function but I don't know why, despite the fact that it is visible there, I cannot return individual elements of this object because it shows "undefined" in the console logs. Anyone have any idea what I should do or am I doing wrong?
Code:
async function getMonitors(){
try {
let res = await fetch(API_URL+"/"+API_VERSION+"/status/monitors/"+location.hostname+"/list");
var data = await res.json();
if(data.success){
delete data.success;
return data;
}else{
e=data.message;
}
} catch(error){
e=error;
}
if(typeof e!="undefined"&&e!==null){system.error(e);}
}
function getMonitorsData(data){
let totalMonitors = data.totalMonitors;
console.log(data);
}
var m = getMonitors();
var data = getMonitorsData(m);
Screen of the returned object in the function Object
You have missed await keyword while calling getMonitors() function.
async function getMonitors(){
try {
let res = await fetch(API_URL+"/"+API_VERSION+"/status/monitors/"+location.hostname+"/list");
var data = await res.json();
if(data.success){
delete data.success;
return data;
}else{
e=data.message;
}
} catch(error){
e=error;
}
if(typeof e!="undefined"&&e!==null){system.error(e);}
}
function getMonitorsData(data){
let totalMonitors = data.totalMonitors;
console.log(data);
}
async function init() {
var m = await getMonitors();
var data = getMonitorsData(m);
}
init();
you have forgotten to call the function and pass the data that are response from your API
async function getMonitors(){
try {
let res = await fetch("https://www.7timer.info/bin/astro.php?lon=113.2&lat=23.1&ac=0&unit=metric&output=json&tzshift=0");
var data = await res.json();
getMonitorsData(data) // here is what i added for passing response to function
if(data.success){
delete data.success;
return data;
}else{
e=data.message;
}
} catch(error){
e=error;
}
if(typeof e!="undefined"&&e!==null){system.error(e);}
}
function getMonitorsData(data){
let totalMonitors = data.totalMonitors;
console.log(data);
}
var m = getMonitors();
var data = getMonitorsData(m);

Cannot await for sqlite3.Database.get() function completion in Node.js

I'm struggling with some basic async/await problem in node.js using node-sqlite3.
My objective is to select some value from SQLite DB, check it for some condition and take some actions in case the condition is met. Here's the code:
const sqlite3 = require('sqlite3').verbose();
main();
async function main() {
let ordersDb = await createDbConnection('./ProcessedOrders.db');
var orderProcessed = await orderAlreadyProcessed(ordersDb, "555");
console.log("orderProcessed = " + orderProcessed);
if (!orderProcessed) {
console.log("So condition is met!");
}
}
async function orderAlreadyProcessed(ordersDb, orderNumberStr) {
console.log('starting orderAlreadyProcessed function'); //DEBUG
var result;
var query = 'select count(SoldOrderNumber) as "recsCount" from ProcessedSoldOrders where SoldOrderNumber = ?;';
await ordersDb.get(query
,[orderNumberStr]
,(err, row) => {
console.log('Row with count = ' + row); //DEBUG
console.log('row.recsCount = ' + row.recsCount); //DEBUG
result = typeof row !== 'undefined' && row.recsCount > 0;
});
console.log('Returning ' + result); //DEBUG
return result;
}
async function createDbConnection(dbFileName) {
let db = new sqlite3.Database(dbFileName, (err) => {
if (err) {
console.log(err.message);
}
});
return db;
}
But what I get is code executing further, not awaiting for Database.get() method at all! As a result, here's what I see printing in console:
starting orderAlreadyProcessed function
Returning undefined
orderProcessed = undefined
So IF condition met!
Row with count = [object Object]
row.recsCount = 1
As we can see, we return from orderAlreadyProcessed too early with return value = 'undefined'. So condition is met, actions taken, and only then Database.get() returns. But if it was properly awaited, condition would not be met.
How can I make it await for result value?
Since you want to use async/await, and the node-sqlite3 (sqlite3) library does not support the Promise API, you need to use the node-sqlite (sqlite) library, which is a wrapper over sqlite3 and adds support for the Promise API. Then, your code will look something like this:
const sqlite3 = require('sqlite3');
const { open } = require('sqlite');
async function main() {
try {
sqlite3.verbose();
const ordersDb = await createDbConnection('./ProcessedOrders.db');
const orderProcessed = await orderAlreadyProcessed(ordersDb, "555");
console.log("orderProcessed = " + orderProcessed);
if (!orderProcessed) {
console.log("So condition is met!");
}
} catch (error) {
console.error(error);
}
}
async function orderAlreadyProcessed(ordersDb, orderNumberStr) {
try {
console.log('Starting orderAlreadyProcessed function');
const query = 'SELECT COUNT(SoldOrderNumber) as `recsCount` from ProcessedSoldOrders where SoldOrderNumber = ?;'
const row = await ordersDb.get(query, [orderNumberStr]);
console.log('Row with count =', row);
console.log('row.recsCount =', row.recsCount);
const result = typeof row !== 'undefined' && row.recsCount > 0;
console.log('Returning ' + result);
return result;
} catch (error) {
console.error(error);
throw error;
}
}
function createDbConnection(filename) {
return open({
filename,
driver: sqlite3.Database
});
}
main();
I specifically did not remove your console.log and other parts of the code so as not to confuse the original logic of your program.
If we don't to use another library
then we can return a new Promise function & use await, as below:
Note: Below has example for INSERT/run, instead of SELECT/get, but promise/await works same
const sqlite3 = require("sqlite3").verbose();
let db;
db = new sqlite3.Database('./Chinook.db');
function insert() {
return new Promise((resolve, reject) => { // return new Promise here <---
const userId = uuid4();
let sql = `INSERT INTO Users(id) VALUES (?)`; // INSERT <----
let params = [userId];
return db.run(sql, params, function (err, res) { // .run <----
if (err) {
console.error("DB Error: Insert failed: ", err.message);
return reject(err.message);
}
return resolve("done");
});
});
}
let result = await insert(); // now await works fine <------
res.send({ result });

Node.js on AWS Lambda and DynamoDB

I'm trying to create a Lambda function to generate a .js script (in order to use with Chart.JS).
This script sends a query to a table in DynamoDB and outputs the results in .js file (which is stored in an S3 bucket).
I try for many hours to make it functional, but I'm stuck with classical problems on Node.js: order on callback functions and variables scope.
Here is the code I used:
var AWS = require('aws-sdk');
AWS.config.update({region: 'eu-west-1'});
var s3 = new AWS.S3();
var tweetValue ;
var neutralValue ;
var destBucket = "twitterappfront1";
var ddb = new AWS.DynamoDB.DocumentClient({apiVersion: '2012-08-10'});
function sentimentVal(inputparams) {
// function resultrequest()
ddb.get(inputparams, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data.Item);
//Catch tweets number in DynamoTB table and store un descriptor
var numtweets = (JSON.parse(JSON.stringify(AWS.DynamoDB.Converter.marshall(data.Item)))).tweets ;
var tweetsObject = Object.getOwnPropertyDescriptor(numtweets, 'N') ;
tweetValue = tweetsObject.value ;
console.log ("test stringify = ", numtweets) ;
console.log (tweetsObject.value) ;
console.log ("Value = ", tweetValue) ;
return tweetValue ;
}
});
}
exports.handler = (event) => {
// Read options from the event.
var paramsNeutral = {
TableName: 'twitterSentiment',
Key: { 'sentiment':'NEUTRAL' }
};
// Call sentimentVal function with paramsNeutral, and setNeutralValue callback function
//
sentimentVal(paramsNeutral, setNeutralValue);
function setNeutralValue (error, tweetValue) {
if (error) console.error('ERROR !', error) ;
else console.log ('callback tweetValue = ', tweetValue) ;
}
};
My problem is that it seems the callback function is never used: I have no console output "ERROR" or "Callback tweetValue ="
And I don't understand how to catch the value from the sentvimentVal function. I tried a "return", but I don't know if it is the right way.
Can you please help me ?
Thank you
You are not waiting for the update to DynamoDB to finish.
Update it to return a promise and use async/await
async function sentimentVal(inputparams) {
try {
// function resultrequest()
const data = await ddb.get(inputparams).promise()
console.log("Success", data.Item);
//Catch tweets number in DynamoTB table and store un descriptor
var numtweets = (JSON.parse(JSON.stringify(AWS.DynamoDB.Converter.marshall(data.Item)))).tweets ;
var tweetsObject = Object.getOwnPropertyDescriptor(numtweets, 'N') ;
tweetValue = tweetsObject.value ;
console.log ("test stringify = ", numtweets) ;
console.log (tweetsObject.value) ;
console.log ("Value = ", tweetValue) ;
return tweetValue ;
} catch (err) {
console.log("Error", err);
throw err
}
}
And await for it in handler
exports.handler = (event) => {
// Read options from the event.
var paramsNeutral = {
TableName: 'twitterSentiment',
Key: { 'sentiment':'NEUTRAL' }
};
// Call sentimentVal function with paramsNeutral, and setNeutralValue callback function
//
const tweet = await sentimentVal(paramsNeutral, setNeutralValue);
function setNeutralValue (error, tweetValue) {
if (error) console.error('ERROR !', error) ;
else console.log ('callback tweetValue = ', tweetValue) ;
}
};
I'm not sure what setNeutralValue is supposed to do.

Push into an array from foreach and make it available outside foreach

I stuck by looping through an array that receive values from a promise and push values into a new array which is available outside the foreach.
What i have:
app.post('/submit', function (req, res) {
uploadPics(req, res, function (err) {
if (err instanceof multer.MulterError) {
res.send(JSON.stringify({UploadResult: err.message}));
console.log(err.message + ' ' +'Redirect /home');
} else if (err) {
console.log(err);
} else {
res.send(JSON.stringify({UploadResult: 'Success'}));
var filesarray = req.files;
var picinfos = [];
filesarray.forEach(function(file){
GetFileMetaInfo.filemetainfo(file.path).then(function (metadata){
//Stuck here! Can push values into an array (picinfos) but only available in the foreach. not outside..
})
})
//I need picinfos array here....
}
})
})
How i receive my metadata:
var exif = require('exif-parser');
var fs = require('fs');
exports.filemetainfo = function (filepath) {
return new Promise((resolve) => {
var file = filepath;
var buffer = fs.readFileSync(file);
var parser = exif.create(buffer);
var result = parser.parse();
resolve (result);
}).then(function (metadata){
if (metadata.tags.CreateDate !== undefined){
date = new Date (metadata.tags.CreateDate*1000);
datevalues = [
date.getFullYear(),
date.getMonth()+1,
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds(),
];
CreateDate = date.getFullYear()+'-'+(date.getMonth()+1)+'-'+date.getDate();
CreateTime = date.getHours()+':'+date.getMinutes()+':'+date.getSeconds();
console.log("CrDate:" +CreateDate, "CrTime:" +CreateTime );
} else {
console.log("No Metadata Creation Infos found in " +filepath);
CreateDate = "";
CretaeTime = "";
}
if (metadata.tags.GPSLatitude !== undefined){
GPSLat = metadata.tags.GPSLatitude;
GPSLon = metadata.tags.GPSLongitude;
console.log("GPSLat:" + GPSLat , "GPSLon:" +GPSLon);
}
else {
console.log("No Metadata GPS Infos found in " +filepath)
GPSLat = "";
GPSLon = "";
}
return MetaData = {
GPSLat: GPSLat ,
GPSLon: GPSLon,
CreateDate: CreateDate,
CreateTime: CreateTime,
}
})
}
May i ask someone to give a hand. How can i make my array available outside the foreach. thank you very much!
The reason you're getting empty array at the end of forEach is because, GetFileMetaInfo.filemetainfo() returns a promise and forEach won't wait for async actions.
You could use async/await with for...of loop to get your desired result.
app.post('/submit', function (req, res) {
uploadPics(req, res, async function (err) { // note async here
if (err instanceof multer.MulterError) {
res.send(JSON.stringify({UploadResult: err.message}));
console.log(err.message + ' ' +'Redirect /home');
} else if (err) {
console.log(err);
} else {
res.send(JSON.stringify({UploadResult: 'Success'}));
var filesarray = req.files;
var picinfos = [];
for(let file of filesarray) {
const metadata = await GetFileMetaInfo.filemetainfo(file.path);
// push metadata into your array here
picinfos.push(metadata);
}
// You will have picinfos here
}
})
})
Although the question is already answered by Dinesh Pandiyan there are still some adjustments that can be made. The following code in his answer runs sequential, meaning that every async request is made after the previously returned result is resolved.
for(let file of filesarray) {
const metadata = await GetFileMetaInfo.filemetainfo(file.path);
// ^- pauses the execution of the current running code
// push metadata into your array here
picinfos.push(metadata);
}
async call #1 ╌╌await╌╌> async call #2 ╌╌await╌╌> async call #3 ╌╌await╌╌> result
You could make the code concurrent by first executing all async statements and then wait until all results are resolved. This can be done by simply changing the following:
// execute all the async functions first, reducing the wait time
for(let file of filesarray) {
const metadata = GetFileMetaInfo.filemetainfo(file.path);
// ^- remove the await
// push metadata into your array here
picinfos.push(metadata);
}
// wait for all results to be resolved
picinfos = await Promise.all(picinfos);
// ^- instead await here
async call #1 ╌╌┐
async call #2 ╌╌┼╌╌await all╌╌> result
async call #3 ╌╌┘
The above could be further simplified by simply using an Array.map() in combination with the already shown Promise.all().
var filesarray = req.files;
var picinfos = await Promise.all(filesarray.map(file => {
return GetFileMetaInfo.filemetainfo(file.path);
}));
// picinfos should be present
Or if you want to avoid working with async/await:
var filesarray = req.files;
Promise.all(filesarray.map(file => {
return GetFileMetaInfo.filemetainfo(file.path);
})).then(picinfos => {
// picinfos should be present
});

Node.js - wait for multiple async calls

I'm trying to make multiple MongoDB queries before I render a Jade template, but I can't quite figure out how to wait until all the Mongo Queries are completed before rendering the template.
exports.init = function(req, res){
var NYLakes = {};
var NJLakes = {};
var filterNY = {"State" : "NY"};
db.collection('lakes').find(filterNY).toArray(function(err, result) {
if (err) throw err;
NYLakes = result;
});
var filterNJ = {"State" : "NJ"};
db.collection('lakes').find(filterNJ).toArray(function(err, result) {
if (err) throw err;
NJLakes = result;
});
res.render('explore/index', {
NYlakes: NYLakes,
NJlakes: NJLakes
});
};
I'm a big fan of underscore/lodash, so I usually use _.after, which creates a function that only executes after being called a certain number of times.
var finished = _.after(2, doRender);
asyncMethod1(data, function(err){
//...
finished();
});
asyncMethod2(data, function(err){
//...
finished();
})
function doRender(){
res.render(); // etc
}
Since javascript hoists the definition of functions defined with the function funcName() syntax, your code reads naturally: top-to-bottom.
Assuming you want to run the two operations in parallel rather than waiting for one to finish before starting the next, you'll need to track how many operations have completed in each callback.
In raw node.js javascript, one way to do this would be this:
exports.init = function(req, res){
var NYLakes = null;
var NJLakes = null;
var filterNY = {"State" : "NY"};
db.collection('lakes').find(filterNY).toArray(function(err, result) {
if (err) throw err;
NYLakes = result;
complete();
});
var filterNJ = {"State" : "NJ"};
db.collection('lakes').find(filterNJ).toArray(function(err, result) {
if (err) throw err;
NJLakes = result;
complete();
});
function complete() {
if (NYLakes !== null && NJLakes !== null) {
res.render('explore/index', {
NYlakes: NYLakes,
NJlakes: NJLakes
});
}
}
};
Basically what's happening here is that you check at the end of each operation if all of them have finished, and at that point you finish off the operation.
If you're doing a lot of these things, take a look at the async library as an example of a tool to make it easier to manage this sort of thing.
You can use async module:
var states = [{"State" : "NY"},{"State" : "NJ"}];
var findLakes = function(state,callback){
db.collection('lakes').find(state).toArray(callback);
}
async.map(states, findLakes , function(err, results){
// do something with array of results
});
Wait.for https://github.com/luciotato/waitfor
using Wait.for:
exports.init = function(req, res){
var NYLakes = {};
var NJLakes = {};
var coll = db.collection('lakes');
var filterNY = {"State" : "NY"};
var a = wait.forMethod(coll,'find',filterNY);
NYLakes = wait.forMethod(a,'toArray');
var filterNJ = {"State" : "NJ"};
var b = wait.forMethod(coll,'find',filterNJ);
NJLakes = wait.forMethod(b,'toArray');
res.render('explore/index',
{
NYlakes: NYLakes,
NJlakes: NJLakes
}
);
};
Requesting in parallel using wait.for parallel map:
exports.init = function(req, res){
var coll = db.collection('lakes');
//execute in parallel, wait for results
var result = wait.parallel.map(
[{coll:coll,filter:{"State" : "NY"}}
, {coll:coll,filter:{"State" : "NJ"}}]
, getData);
res.render('explore/index',
{
NYlakes: result[0],
NJlakes: result[1]
}
);
};
//map function
function getData(item,callback){
try{
var a = wait.forMethod(item.coll,'find',item.filter);
var b = wait.forMethod(a,'toArray');
callback (null, b);
} catch(err){
callback(err);
}
I'm not familiar with mongo, so you may have to adjust the calls.
This seems like the least lines of code using await:
var async = require("async"); //include async module
...
async function getData() { //make sure to use async function
var NYlakes = await db.collection('lakes').find(filterNY); //can append additional logic after the find()
var NJlakes = await db.collection('lakes').find(filterNJ);
res.json({"NYLakes": NYLakes, "NJLakes": NJLakes}); //render response
}
getData();
Side note: In this case await is serving as a Promise.all() be careful not to abuse the await function.

Categories

Resources