Get and return value outside function - Nodejs - node.js

I'm working on some method that have a callback with value, that I need to return and response, but value is null outside callback funciton
How can I solve?
async function createChannel(req) {
let user_id = req.query.user_id;
let chat_name = req.query.chat_name;
var chimesdkmessaging = new AWS.ChimeSDKMessaging({region: region});
var channel = null;
try {
let dateNow = new Date();
const params = {
Name: chat_name,
AppInstanceArn: config.aws_chime_app_istance,
ClientRequestToken: dateNow.getHours().toString() + dateNow.getMinutes().toString(),
ChimeBearer: await AppInstanceUserArn(user_id),
AppInstanceUserArn of the user making the API call.
Mode: 'RESTRICTED',
Privacy: 'PRIVATE'
};
chimesdkmessaging.createChannel(params, function (err, data) {
if (err) {
console.log(err, err.stack);
} // an error occurred
else {
console.log(data); // successful response
console.log('Assegno il canale: ' + data.ChannelArn);
channel = data.ChannelArn; // <----- VALUE I WANT TO RETURN OUTSIDE
}
});
} catch (e) {
return response({error: e}, 500);
}
return response({data: channel},200); // <---- but this channel is null
}

wrap with Promise
let user_id = req.query.user_id;
let chat_name = req.query.chat_name;
var chimesdkmessaging = new AWS.ChimeSDKMessaging({region: region});
let channel = null;
try {
let dateNow = new Date();
const params = {
Name: chat_name,
AppInstanceArn: config.aws_chime_app_istance,
ClientRequestToken: dateNow.getHours().toString() + dateNow.getMinutes().toString(),
ChimeBearer: await AppInstanceUserArn(user_id),
AppInstanceUserArn of the user making the API call.
Mode: 'RESTRICTED',
Privacy: 'PRIVATE'
};
channel = await new Promise((resolve,reject)=>{
chimesdkmessaging.createChannel(params, function (err, data) {
if (err) {
console.log(err, err.stack);
reject(err)
} // an error occurred
else {
console.log(data); // successful response
console.log('Assegno il canale: ' + data.ChannelArn);
resolve(data.ChannelArn)// <----- VALUE I WANT TO RETURN OUTSIDE
}
});
})
} catch (e) {
return response({error: e}, 500);
}
return response({data: channel},200); // <---- but this channel is null
}

Related

use async - await with socket.io nodejs

I'm developing a web application using nodejs socket.io and angular 9. In my backend code I have written sockets in socket.connect.service.
Follows is a socket I'm using
socket.on('request-to-sit-on-the-table', async function (data, callback) { //Previously Register
let table = persistence.getTable(tableToken);
if (typeof table === 'undefined') {
let errMsg = 'This table does not exists or already closed.'
callback(prepareResponse({}, errMsg, new Error(errMsg)));
return;
}
//TODO: Get the displayName from the token.
let guest = await guestUserService.getGuestUserByPlayerToken(JSON.parse(data.userToken));***//Here is the issue***
// let displayName = 'DisplayName-' + guest;
let displayName = 'DisplayName-' + Math.random();
//TODO: Check whether the seat is available
// If the new screen name is not an empty string
let isPlayerInCurrentTable = persistence.isPlayerInCurrentTable(tableToken, userToken);
if (displayName && !isPlayerInCurrentTable) {
var nameExists = false;
let currentPlayersTokenArr = persistence.getTableObjectPlayersToken(table)
for (var token in currentPlayersTokenArr) {
let gamePlayer = persistence.getPlayerPlayer(currentPlayersTokenArr[token])
if (typeof gamePlayer !== "undefined" && gamePlayer.public.name === displayName) {
nameExists = true;
break;
}
}
if (!nameExists) {
//Emit event to inform the admin for requesting to sit on the table.
let ownerToken = persistence.getTableObjectOwnerToken(table);
let ownerSocket = persistence.getPlayerSocket(ownerToken);
ownerSocket.emit('requested-to-sit', {
seat: data.seat,
secondaryUserToken: userToken,
displayName,
numberOfChips: envConfig.defaultNumberOfChips
});
callback(prepareResponse({userToken}, 'Player connected successfully.'));
} else {
callback(prepareResponse({}, 'This name is already taken'));
}
} else {
callback(prepareResponse({}, 'This user has already joined to a game. Try clear caching'));
}
});
In my code I'm getting data from another code in guest.user.service. But I get undefined to the value of "guest"
Follows are the methods I have used in guest.user.service
exports.findById = (id) => {
return new Promise(function(resolve, reject) {
guestUserModel.findById(id, (err, data) =>{
if(err){
reject(err);
} else {
resolve(data);
}
});
});
};
exports.getGuestUserByPlayerToken = (playerToken) => {
var player = playerService.findOne({ token: playerToken })
.then(function (data) {
return self.findById(data.guestUser._id.toString());
})
.then(function (guestUser) {
return guestUser.displayName;
})
.catch(function (err) {
throw new Error(err);
})
};
Although I get my displayName for the return value It is not passed to the "guest" in my socket.Is there any syntax issue to get data as I'm using promises.please help
exports.getGuestUserByPlayerToken = async playerToken => {
try {
let player = await playerService.findOne({token:playerToken});
return playerService.findById(player.guestUser._id)
} catch(error) {
console.log(error);
return null;
}
};
This is just handle error on awaited promise not returned one. You need to handle that in caller side.

pass mysql value from node js to angular

Hi i have this post function to call create function which will insert to database
router.post('/', async (req, res, next) =>
{
const body = req.body;
console.log("tu sam todo_task",req.body);
try
{
const todo_task = await TodoTaskService.create(body);
console.log(todo_task, "function")
// created the todo_task!
return res.status(201).json({ todo_task: todo_task });
}
catch(err)
{
}
});
Then in second snippet i have this create function in service class where i want to return result from mysql query
class TodoTaskService
{
static create(data)
{
console.log("tu sam service todo_task create");
var vres = todoTaskValidator.validate(data, todo_taskVSchema);
/* validation failed */
if(!(vres === true))
{
let errors = {}, item;
for(const index in vres)
{
item = vres[index];
errors[item.field] = item.message;
}
throw {
name: "ValidationError",
message: errors
};
}
console.log("tu sam service todo_task validation passed");
let todo_task = new TodoTaskModel(data.todo_task_name, data.todo_task_complete_f);
let connection = mysql.createConnection(dev);
// insert statment
let sql = `INSERT INTO todo_task (todo_task_name,todo_task_complete_f) VALUES (?);`
let values = [data.todo_task_name, data.todo_task_complete_f];
// execute the insert statment
connection.query(sql, [values], (err, results, fields) => {
if (err) {
global.query = "Database error";
console.log(err.message + "zasto");
return err
}
else{
// get inserted rows
global.query = "OK";
todo_task.todo_task_id = results.insertId
console.log("rows inserted",todo_task);
return todo_task;
}
});
}
}
How can i pass return todo_task from connection.query back to the first post function and send it to angular?
I've found a solution on my own, don't know how if it's the best practice but here is my solution, I'm using callback to pass the value
router.post('/', async (req, res, next) =>
{
const body = req.body;
try
{
return await TodoTaskService.create(body,function (result){
console.log(result, "function result")
todo_task = result;
return res.status(201).json({ todo_task: todo_task });
});
// created the todo_task!
//return res.status(201).json({ todo_task: todo_task });
}
returning callback with result
class TodoTaskService
{
static create(data,callback)
{
var vres = todoTaskValidator.validate(data, todo_taskVSchema);
/* validation failed */
if(!(vres === true))
{
let errors = {}, item;
for(const index in vres)
{
item = vres[index];
errors[item.field] = item.message;
}
throw {
name: "ValidationError",
message: errors
};
}
console.log("tu sam service todo_task validation passed");
let todo_task = new TodoTaskModel(data.todo_task_name, data.todo_task_complete_f);
/* todo_task.uid = 'c' + counter++;
todo_tasks[todo_task.uid] = todo_task; */
let connection = mysql.createConnection(dev);
// insert statment
let sql = `INSERT INTO todo_task (todo_task_name,todo_task_complete_f) VALUES (?);`
let values = [data.todo_task_name, data.todo_task_complete_f];
// execute the insert statment
connection.query(sql, [values], (err, results, fields) => {
if (err) {
global.query = "Database error";
console.log(err.message + "zasto");
return err
}
else{
// get inserted rows
global.query = "OK";
todo_task.todo_task_id = results.insertId
console.log("rows inserted",todo_task);
return callback(todo_task);
}
});
}

How to await on class constructor in Node js

I have two classes as below
class AClient
class AClient {
constructor(AHeaderObj) {
logger.info("inside tenant client constructor");
this.tenant_uri = tenantHeaderObj.tenant_uri || config.TENANT_URI;
this.refresh_token = tenantHeaderObj.refresh_token;
this.tenant_header = tenantHeaderObj.tenant_header;
}
Class BClient
class BClient {
constructor(b_client) {
logger.info("Inside mongoClient/constructor ");
this.tenant_client = tenant_client;
}
async find(db_name, collection_name, selectorQuery) {
try {
logger.info("Inside mongoClient find records");
let dbName = await getDB(db_name, this.tenant_client);
let db = await getConnection(dbName, this.tenant_client);
logger.info("selectorQuery"+JSON.stringify(selectorQuery));
let doc = db.collection(collection_name).find(selectorQuery).toArray();
logger.info("****************doc**********"+JSON.stringify(doc));
return await promiseWrapper(null, doc);
} catch (err) {
logger.info("Caught error in mongoClient find records ", err);
return await promiseWrapper(err, null);
}
}
async findSome(db_name, collection_name, query, projection, sort, limit){
logger.info("Inside mongoClient findSome records");
try{
let dbName = await gettDB(db_name, this.tenant_client);
let db = await getConnection(dbName, this.tenant_client);
var selector = [{ $match : query},
{ $project : projection},
{ $sort : sort}];
if(limit > 0){
selector.push({ $limit : limit})
}
let docs = db.collection(collection_name).aggregate(selector).toArray();
return await promiseWrapper(null, docs);
} catch (err) {
logger.info("Caught error in mongoClient findSome records ", err);
return await promiseWrapper(err, null);
}
}
Now I am using it in below 2 ways . One way it works another it is not.
Case1: When it is not working
var searchAll = function (type, text, sortBy, sort, Code, AHeaderObj) {
logger.info("Inside searchAll function.");
var selector = mongoQuery.searchAll(type, text, sortBy, sort, Code);
var AObj = new AClient(AHeaderObj);
logger.info("123");
var mongoObj = new BClient(AObj);
logger.info("1235");
var findSome = callbackify(mongoObj.findSome)
logger.info("12356");
return new Promise((resolve1, reject1) => {
logger.info("123567");
findSome(dBName, collectionName, selector.query, selector.projection, selector.sort, 999999999, function (err1, res1) {
if (err1) {
logger.error("Failed to find");
reject1(err1);
} else {
logger.info("Successfully found");
resolve1(res1)
}
});
})
}
Case2: Where it is working as charm.
var getId = function (id, headerObj, callback) {
logger.info("inside getAccountById() START:");
let selector = mongoQuery.GetAccByIdQueryWithIdModelVersion("Id", id, “version", "v2.1")
let index = 0;
var AObj = new AClient(AHeaderObj);
logger.info("123");
var mongoObj = new BClient(AObj);
logger.info("1235");
mongoObj.find(dBName, collectionName, selector).then((result) => {
logger.info("123568");
if (result && result.length > 0) {
logger.info("a");
logger.info("Found an with id: " + id);
if (result[index].credentials.length > 0) {
logger.info("getById() END:"+JSON.stringify(result));
callback(null, result);
}else {
logger.info("b");
let error = {
"message": "Unable to fetch the with id: " + id
};
logger.warn(error.message);
logger.info("getById() END:");
callback(error, null);
}
}).catch((err) => {
logger.info("c");
logger.info("error" + JSON.stringify(err));
callback(err, null);
});
}
Can someone help what am I doing wrong in Case1 . I observed that ion Case 1 object creation is not awaited and db function is been called where I get below error.
Caught error in mongoClient findSome records TypeError: Cannot read property 'tenant_client' of undefined
Instead of trying to await on class initialization, make the constructor empty and provide and async init method which does the same thing as what the constructor would do.
Make sure that other methods check if the object has been instantiated via the init method and throw an error if not :)

how to handle array of 'promis'

I built a database that contains a list of users who receive messages from firebase with a request key. Every time a new request with status 'open' inserted, I am trying to sort them all by the value of 'Timestamp' and send it by this order to the receivers(each receiver will get one message).
if the list of receivers is empty I want to hold it, until another receiver will be added to the list and continue to the next request.
I am not sure how to send each 'promise' separately one after another-
exports.notificationListener=functions.database.ref('requests')
.onWrite(event=>{
const ref = event.data.ref;
let requests= [];
var query=ref.orderByChild('TimeStamp');
query.once('value',function(snap){
snap.forEach(request=>{
if(request.val().Status==OPEN)
requests.push(request.key);
});
});
for (let key of requests) {
return getOnlinReceiversToken().then(token=>{
let msg = {
data: {
source: key
}
};
return admin.messaging().sendToDevice(token, msg);
)};
}
});
function getOnlinReceiversToken() {
let result = new Promise((resolve, reject) => {
receiverRef.once('value', (snap) => {
resolve(snap);
},
(err) => {
reject(err);
});
});
return result.then(snap => {
snap.forEach(child => {
if(child.Status == ONLINE){
let token = helper.getToken(child.key,db);
break;
}
});
return token;
});
}
try something like this
var promisesArray = [];
for (let key of requests) {
var currentPromise = getOnlinReceiversToken().then(token=>{
let msg = {
data: {
source: key
}
};
return admin.messaging().sendToDevice(token, msg);
});
promisesArray.push(currentPromise);
}
return Promise.all(promisesArray);
You could use a function that calls itself to iterate through the promises sequentially to send them one after the other
function runPromise(index) {
// jump out of loop if there are no more requests
if (index >= requests.length) {
return console.log('finished!');
}
return getOnlinReceiversToken().then((token) => {
let msg = {
data: { source: key }
};
admin.messaging().sendToDevice(token, msg);
// iterate to the next item
return runPromise(index + 1);
}).catch((err) => {
// break out of loop when an error occurs
console.log(err);
});
}
runPromise(0);

Completing loops inside of node.js promises

I am trying to complete a few loops over firebase objects using .forEach and I am also using promises. This isn't working out how I had planned it. My basic problem is that the loops inside of my promises complete well after the promise chain itself completes. Here is my function:
var queue = new Queue(productUpdateQueue, function(data, progress, resolve, reject) {
var incomingUpdateData = data;
var receiptID = incomingUpdateData.receiptID;
var userID = incomingUpdateData.userID;
var oldProductID = incomingUpdateData.oldProductID;
var newProductID = incomingUpdateData.newProductID;
var newReceipt = incomingUpdateData.newReceipt;
var postID = "";
var updateObject = {};
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null;
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = newReceipt;
clicks.child('VigLink').orderByChild('item').equalTo(oldProductID).once('value', function(cuidSnapshot) {
return cuidSnapshot.forEach(function(cuidSnapshot) {
var cuid = cuidSnapshot.key;
updateObject['clicks/VigLink/'+cuid+'/item'] = newProductID;
console.log('one');
progress(20);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = data
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+newProductID] = now
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+oldProductID] = null
};
console.log('two');
progress(40);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('shops').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/shops/'+oldProductID] = null;
updateObject['userReceiptMetrics/'+userID+'/shops/'+newProductID] = data;
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+newProductID] = now;
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+oldProductID] = null;
};
console.log('three');
progress(60);
});
}).then(function() {
posts.once('value', function(postSnapshot) {
// use Promise.all and Array#map to wait for all these queries to finish
var allPosts = postSnapshot.val()
var postKeys = Object.keys(allPosts)
return Promise.all(postKeys.map(function(postKey) {
var postID = postKey;
return posts.child(postID).child('items').child(oldProductID).once('value', function(itemSnapshot) {
return itemSnapshot.forEach(function(itemSnapshot) {
var itemData = itemSnapshot.val()
console.log('post snapshot'+ itemData);
updateObject['posts/'+postID+'/items/'+oldProductID] = null
updateObject['posts/'+postID+'/items/'+newProductID] = itemData
});
});
}));
});
}).then(function() {
// Move to next item
return console.log('hey look here'+updateObject['posts/'+postID+'/items/'+newProductID]);
return firebaseRoot.update(updateObject, function(error) {
if (error) {
console.log("Error updating data:", error);
reject()
} else {
progress(100);
// resolve();
console.log('four');
}
});
}).then(function() {
// Move to next item
return console.log('second one'+updateObject['posts/'+postID+'/items/'+newProductID]);
return firebaseRoot.update(updateObject, function(error) {
if (error) {
console.log("Error updating data:", error);
reject()
} else {
progress(100);
// resolve();
console.log('four');
}
});
});
// Finish the task asynchronously
setTimeout(function() {
reject();
}, 10000);
});
And here is the output:
one
two
three
hey look hereundefined
second oneundefined
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
I think I might be using promises incorrectly but I don't know.
I figured out that I should be using Promise.all() in order to wait for my forEach loops to complete. Here is the code I used to solve my problem, enjoy:
var queue = new Queue(productUpdateQueue, function(data, progress, resolve, reject) {
var incomingUpdateData = data;
var receiptID = incomingUpdateData.receiptID;
var userID = incomingUpdateData.userID;
var oldProductID = incomingUpdateData.oldProductID;
var newProductID = incomingUpdateData.newProductID;
var newReceipt = incomingUpdateData.newReceipt;
var postID = "-KZOO0UII67uOmYo6DJh";
var postKeys = [];
var updateObject = {};
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null;
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = newReceipt;
return clicks.child('VigLink').orderByChild('item').equalTo(oldProductID).once('value', function(cuidSnapshot) {
return cuidSnapshot.forEach(function(cuidSnapshot) {
var cuid = cuidSnapshot.key;
updateObject['clicks/VigLink/'+cuid+'/item'] = newProductID;
progress(10);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = data
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+newProductID] = now
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+oldProductID] = null
};
progress(25);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('shops').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/shops/'+oldProductID] = null;
updateObject['userReceiptMetrics/'+userID+'/shops/'+newProductID] = data;
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+newProductID] = now;
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+oldProductID] = null;
};
progress(40);
});
}).then(function() {
progress(55);
return posts.orderByChild('receipt').equalTo(receiptID).once('value');
}).then(function(postSnapshot) {
return postSnapshot.forEach(function(post) {
progress(70);
postKeys.push(post.key)
});
}).then(function() {
return Promise.all(postKeys.map(function(postKey) {
return posts.child(postKey).child('items').child(oldProductID).once('value', function(itemSnapshot) {
var itemData = itemSnapshot.val()
updateObject['posts/'+postKey+'/items/'+oldProductID] = null;
updateObject['posts/'+postKey+'/items/'+newProductID] = itemData;
});
})).then(function(results) {
progress(85);
return results;
});
}).then(function() {
return firebaseRoot.update(updateObject, function(error) {
if (error) {
console.log("Error updating data:", error);
reject()
} else {
progress(100);
resolve();
}
});
});
// Finish the task asynchronously
setTimeout(function() {
reject();
}, 10000);
});

Resources