MongoDB cursor causes my function to skip step - node.js

This is my check permission function, and it work fine, but i can't return result because function skip one step and first return "access" variable, then execute cursor function to check permission. I do not idea what i do wrong. Console logs approve that:
Console result:
1
5 return here
2
3
3
3
4
function permissionChecker(guildID, reqUserID, checkPexArray) {
console.log("1")
let access = false
let pexUserCheckCursor = db.db("MainDB").collection("Permissions").find({GuildID: guildID}).toArray(function (err, result) {
console.log("2")
let serverDB = result[0]
let serverPexGroups = serverDB.PexGroups
let serverPexGroupsNames = Object.keys(serverPexGroups)
//Object of user's pexes.
let serverPexUsers = serverDB.PexUsers
//Looking for at least one of required pexes.
for (let reqPex of checkPexArray) {
for (let pexName in serverPexUsers) {
console.log("3")
//If reqPex has in DB
if (reqPex == pexName) {
for (let userID of serverPexUsers[pexName]) {
//If req user has req pex.
if (userID == reqUserID) {
console.log("4")
access = true
return
} else {
access = false
return
}
}
}
}
}
})
console.log("5 return here")
}

Your console output is correct, cause the db read is asynchronous.
Consider changing permissionChecker to an async function and then await the db callback result.
Or change the permissionChecker function to return a new Promise and then resolve(access) after the loops.

Related

nodejs discord get nickname from a users ID

My goals are to obtain the users nickname by using their ID.
Their ID's are stored as variables which are being collected from a reaction collector.
I have tried a few methods and failed, most of which either return nothing or errors.
The below code returns nothing, the getnames() function is empty. This method was recommended to me buy 2 people from a nodejs discord server which aims to help solve issues, similar to here.
// returns player ID's
function getPlayers() {
let players = [];
players.push(queue.tank[0]); // First (1) in TANK queue
players.push(queue.heal[0]); // First (1) in HEAL queue
players.push(queue.dps[0]); // First (2) in DPS queue
players.push(queue.dps[1]);
return players;
}
// get nick names from ID's
function getnames() {
let players = getPlayers();
let playerNicks = [];
let newPlayer = "";
players.forEach(async player => {
newPlayer = await message.guild.members.fetch(player).then(function (user) {return user.displayName });
playerNicks.push(newPlayer)
return playerNicks;
})}
//formats nicknames into string
function formatnicknames() {
let formatted_string2 = '';
let playerNicks = getnames();
if (playerNicks)
formatted_string2 = `${playerNicks[0]} \n${playerNicks[1]} \n${playerNicks[2]} \n${playerNicks[3]}`;
return formatted_string2;
}
I have also tried a few variations of the below code, still unable to obtain nickname.
message.guild.members.cache.get(user.id)
Edit #1
now tried the following code with no success. (boost1ID contains the ID of 1 user)
var mem1 = message.guild.members.fetch(boost1ID).nickname
Edit #2
tried a new method of obtaining displayname from ID.
var guild = client.guilds.cache.get('guildid');
var mem1 = guild.member(boost1ID);
var mem2 = guild.member(boost2ID);
var mem3 = guild.member(boost3ID);
var mem4 = guild.member(boost4ID);
var nickname1 = mem1 ? mem1.displayName : null;
var nickname2 = mem2 ? mem2.displayName : null;
var nickname3 = mem3 ? mem3.displayName : null;
var nickname4 = mem4 ? mem4.displayName : null;
var Allnicknames = `${nickname1} ${nickname2} ${nickname3} ${nickname4}`
message.channel.send(`testing nicknames: ${Allnicknames}`)
I managed to only return my own name since i dont have a nickname on this server, but the other three users who does have a nickname returned null.
This is the simplest solution:
// your users ids
const IDs = [ '84847448498748974', '48477847847844' ];
const promises = IDs.map((userID) => {
return new Promise(async (resolve) => {
const member = message.guild.member(userID) || await message.guild.members.fetch(userID);
resolve(member.displayName || member.user.username);
});
});
const nicknames = await Promise.all(promises);
// you now have access to ALL the nicknames, even if the members were not cached!
The members you are trying to get the nicknames of are not necessarily cached, and this fixes that.
I made an example that could help you.
let testUsers = [];
module.exports = class extends Command {
constructor(...args) {
super(...args, {
description: 'Testing.',
category: "Information",
});
}
async run(message) {
function getNicknames(userArr, guild) {
let playerNicks = [];
for(var i = 0; i < userArr.length; i++) {
playerNicks.push(guild.member(userArr[i]).displayName);
}
return playerNicks;
}
let testUser = message.guild.members.cache.get(message.author.id);
testUsers.push(testUser);
let guild = message.guild;
console.log(getNicknames(testUsers, guild));
}
}
I created a function getNicknames that takes in two parameters. The first one is an Array of users (as you get one from your function getPlayers()) and the second one is the guild you are playing in. You need to provide the guild, because every user should be a GuildMember, because you want to use .displayName. I created a user Array outside of my command code, because otherwise there will only be one user in the Array everytime you use the command. Inside of the getNicknames() function I have created a new Array playerNicks that I basically fill with the user nicknames we get from our provided user Array.
Now you have to implement that into your code.
The call of the function getNicknames(), for your code should look like this:
getNicknames(getPlayers(), message.guild);

How to call recursively a function that returns a promise?

I want to extract all child-Folders and child-Docs querying a node-js Service, which every time it is called, returns an array of such items. I do not know the depth fo the folders-tree so I want to recursively call a function that in the end will return an array that will contain all child-folders and child-docs, starting from a list of root-Folders. Each folder is identified by a folder id.
So I have made a "recPromise(fId)" which returns a promise. Inside, this function calls recursively the recFun(folderId).I start invoking the "recPromise(fId)" from a rootFolder so once all root-promises are resolved I can go on.
rootFolders.map( folderOfRootlevel =>{
var folderContentPromise = recPromise(folderOfRootlevel.id);
folderContentPromises.push(folderContentPromise);
})
$q.all(folderContentPromises)
.then(function(folderContent) {
// Do stuff with results.
}
function recPromise(fId){
return new Promise((resolve, reject) => {
var items = [];
function recFun( folderId) { // asynchronous recursive function
function handleFolderContent( asyncResult) { // process async result and decide what to do
items.push(asyncResult);
//Now I am in a leaf-node, no child-Folders exist so I return
if (asyncResult.data.childFolders.length === 0){
return items;
}
else {
//child_folders exist. So call again recFun() for every child-Folder
for(var item of asyncResult.data.childFolders) {
return recFun(item._id);
}
}
}
// This is the service that returns the array of child-Folders and child-Docs
return NodeJSService.ListFolders(folderId).then(handleFolderContent);
}
resolve(recFun(fId));
})
}
It works almost as expected except the loop inside else, where I call again recFun().
The NodeJSService will return an array of sub-Folders so I wish to call recfun() for every sub-Folder.
Now, I only get the result of the 1st sub-Folder of the loop,
which makes sense since I have a return statement there.
If I remove the return statement and call like this "recFun(item._id);"
then it breaks the $q.all().
Finally, I decided to remove the Promise wrapper function and make use of async-await.
var items = [];
(async() => {
for(var item of rootFolders) {
await recFun(item.id)
}
// Do stuff with items
// go on form here..
})()
function listFolders(folderId) {
return new Promise( function( resolve, reject) {
resolve(FolderService.ListFolders(folderId));
})
}
async function recFun(folderId) {
var foldersResponse= await listFolders(folderId);
items.push(foldersResponse);
if (foldersResponse.data.childFolders.length === 0){
return items ;
}
else {
for(var item of foldersResponse.data.childFolders) {
await recFun(item._id);
}
}
}

Not able to figure out how to wait until forEach loop iterates completely and use that result

I am new to cloud functions (node js with typescript). I am using it to fetch data from Firebase database( as the code below). But Not able to figure out how to wait until forEach loop iterates completely and use that result.
const reference = admin.database().ref('/books')
var path_key:string
var total_count:number = 0
reference .forEach(function (snapshot) {
path_key= snapshot(key).val()
ref_users_advance_bookings.child(path_key)
.once('value').then((snapshot2)=>{
if(condidtion met){
return response.send("failed")
}
else{
total_count++
}
)}
return true
})
// i want to use total_count value here
return response.send("count :"+total_count) // but every time I gets 0, as it get executed before forEach has ended
)}
My guess is that you're trying to wait for a number of items to load from the database. For that you'll want to use Promise.all(), in something like:
var promises = [];
ref.forEach(function (snapshot) {
path_key= snapshot(key).val()
promises.push(ref_users_advance_bookings.child(path_key).once('value'));
});
Promise.all(promises).then(function(snapshots) {
var failed = false;
snapshot.forEach(function(snapshot2) {
if(condition met){
failed = true;
}
else {
total_count++;
}
)}
if (failed) {
return response.status(500).send("ERROR");
}
else {
return response.send("count :"+total_count);
}
});

Return Variable After Running forEach

i just recently try learn nodejs, i choose adonisjs framework, because i think it's will be easier for me to learn, because on the nutshell have some similarities with laravel, which i used to code with.
but now, i have some problem, i can't solve, i have function like this :
async getAllPeople() {
let allPeople = await People.all()
let myArray = []
let checkChild = async (people) => {
let eachPeopleChild = await people.children().fetch()
if (eachPeopleChild.rows.length > 0) {
return people
}
return false
}
allPeople.rows.forEach(async (people) => {
let res = await checkChild(people)
if (res !== false) {
myArray.push(res)
}
})
console.log(myArray)
}
when i run this function, it's show an empty array [], i know because of nodejs or js actually have asynchronous behavior, it run this : console.log(myArray) first
what i expected is, how to execute, or return myArray after all the looping or other process is done?
-- problem is on the way i loop the array is not on the way i made callback, promise, because i already using async - await which is returning promise and it's behavior. map or forEach does not allow what i expected.. and the solution is clear : for ( item of items )
thank you
actually i just found the solution, thx for the guy at the other place that told me, that i can't use forEach and just use for (let item of anArray) instead.
here is my code now :
async getAllPeople() {
let allPeople = await People.all()
let myArray = []
let checkChild = async (people) => {
let eachPeopleChild = await people.children().fetch()
if (eachPeopleChild.rows.length > 0) {
return people
}
return false
}
for (let people of allPeople.rows) {
let res = await checkChild(people)
if (res !== false) {
myArray.push(res)
}
}
return myArray
}
everyhing works now..!!

http call in backbone promise

Hi I have a backbone web app using Jquery and NodeJs/mongo as the server side framework. I'm having problems with making a http get call with a foreah loop and the results of the get call being iteratively added to each row of the loop.
var eventid = this.model.get("_id");
var inPromise = $.get("/registrants/list?eventid="+eventid,null,null,"json").then(
function (result){
var temp;
var finalVal = '';
var tempfinalVal = "";
var loop = 0
percentage = 0;
$.each(result.registrants,function(index,registrant){
temp = JSON.parse(registrant.fields);
for (var key in temp) {
if(key =="Email"){
if(temp[key] != ""){
$.get("/stats/registrant?userid="+temp[key]+"&eventid="+eventid,null,null,"json").then(function(result2){
percentage = (result2.Stats.type ===undefined || result2.Stats.type ==null) ? "0": result2.Stats.type;
finalVal +=percentage+"\n";
}).fail(function(){
percentage = "0";
});
}
}else if(key =="eventid"){
loop++;
finalVal = finalVal.slice(0, - 1);
finalVal +='\n';
}
finalVal +=temp[key] + ',';
}
});
//promises.push(inPromise);
}
).done(function(finalVal){
$("#webcast-download-registrants-tn").attr("href",'data:text/csv;charset=utf-8;filename=registration.csv",'+encodeURIComponent(finalVal));
console.log("DONE");
}).fail(function(){
console.log("fail");
});
// promise.done(function () {
// console.log(" PROMISE DONE");
// });
So I have the loop through a collection and the last item of the docuemnt gets a content froma nother http call and when all is fone it will create a CSV file. The problem is that THE "DONE" text echos firts then the "CALL" text is displayed
Rick, your problem is not the simplest due to :
the need for nested asynchronous gets
the need to build each CSV data row partly synchronously, partly asynchronously.
the need for a mechanism to handle the fulfilment of multiple promises generated in the inner loop.
From what you've tried, I guess you already know that much.
One important thing to note is that you can't rely on for (var key in temp) to deliver properties in any particular order. Only arrays have order.
You might try something like this :
var url = "/stats/registrant",
data = { 'eventid': this.model.get('_id') },
rowTerminator = "\n",
fieldNames = ['firstname','lastname','email','company','score'];
function getScore(email) {
return $.get(url, $.extend({}, data, {'userid':email}), null, "json").then(function(res) {
return res.Stats ? res.Stats.type || 0 : 0;
}, function() {
//ajax failure - assume score == 0
return $.when(0);
});
}
$.get("/registrants/list", data, null, "json").then(function(result) {
var promises = [];//An array in which to accumulate promises of CSV rows
promises.push($.when(fieldNames)); //promise of CSV header row
if(result.registrants) {
$.each(result.registrants, function(index, registrant) {
if(registrant.fields) {
// Synchronously initialize row with firstname, lastname, email and company
// (omitting score for now).
var row = fieldNames.slice(0,-1).map(function(fieldName, i) {
return registrant.fields[fieldName] || '';
});
//`row` remains available to inner functions due to closure
var promise;
if(registrant.fields.Email) {
// Fetch the registrant's score ...
promise = getScore(registrant.fields.Email).then(function(score) {
//... and asynchronously push the score onto row
row.push(score);
return row;
});
} else {
//or synchronously push zero onto row ...
row.push(0);
//... and create a resolved promise
promise = $.when(row);
}
promises.push(promise);//Accumulate promises of CSV data rows (still in array form), in the correct order.
}
});
}
return $.when.apply(null, promises).then(function() {
//Join all the pieces, in nested arrays, together into one long string.
return [].slice.apply(arguments).map(function(row) {
return row.join(); //default glue is ','
}).join(rowTerminator);
});
}).done(function(str) {
$("#webcast-download-registrants-tn").attr("href",'data:text/csv;charset=utf-8;filename=registration.csv",'+encodeURIComponent(str));
console.log("DONE");
}).fail(function() {
console.log("fail");
});
partially tested
See comments in code for explanation and please ask if there's anything you don't follow.

Resources