In firebase cloud function the child is self-deleting - node.js

When I add a transaction in the function below, the child of the "guncelLig" is automatically deleted. When I remove the second transaction there is no problem, it works very well.
this code work correct :
for (let i = 0; i < users.length; i++) {
const userID = users[i].id;
database.ref().child("users").child("profilData").child(userID).child("guncelLig").transaction(function (value) {
if (value > 1) {
database.ref().child("users").child("profilData").child(userID).update({
"lastUpdate": toSwiftDate(),
});
return value - 1;
}
return value;
});
}
but when I add the new part to the code below, "guncelLig" is completely deleted from the firebase realtime databse:
for (let i = 0; i < users.length; i++) {
const userID = users[i].id;
database.ref().child("users").child("profilData").child(userID).child("guncelLig").transaction(function (value) {
if (value > 1) {
database.ref().child("users").child("profilData").child(userID).update({
"lastUpdate": toSwiftDate(),
});
/// NEW CODE ERROR SOURCHE
database.ref().child("users").child("profilData").child(userID).child("EnYukselLig").transaction(function (EnYukselLig) {
if (EnYukselLig > value - 1) {
return value - 1;
}
return EnYukselLig;
});
return value - 1; // arttır
}
return value; /// ALWAYS RETURN NULL
});
}
Where am I doing wrong? please help me

You should write your code as async/await. With await you can wait for the nested transaction to finish.
for (let i = 0; i < users.length; i++) {
const userID = users[i].id;
database
.ref()
.child("users")
.child("profilData")
.child(userID)
.child("guncelLig")
.transaction(async (value) => {
if (value > 1) {
await database
.ref()
.child("users")
.child("profilData")
.child(userID)
.update({
lastUpdate: toSwiftDate(),
});
/// NEW CODE ERROR SOURCHE
await database
.ref()
.child("users")
.child("profilData")
.child(userID)
.child("EnYukselLig")
.transaction(function (EnYukselLig) {
if (EnYukselLig > value - 1) {
return value - 1;
}
return EnYukselLig;
});
return value - 1; // arttır
}
// No handler if the value is NULL
return value; /// ALWAYS RETURN NULL
});
}
You also don't handle the case if value is already null.
It is not recommmended to nest transactions like that. Transacions by nature can run multiple times and there is no guaranty that they finish as expected depending on the amount of updates that happen on the same location.

Related

Calculate the sum of a specific array data

After calling all the data from my database, I would like to try and calculate the individual values of my array. I have made 2 users state as "banned"
//get all banned status for all users
res.data.data.forEach((dataItem, index) => {
console.log(`Banned ${index}`, dataItem.banned);
I would like to try and get the sum of "banned" and "not banned" which in this case is "banned 2" and "NotBanned 2"
tried this but dint work
for (const dataItem of res.data.data) {
let NotBanned = 0;
if(dataItem.banned === false){
NotBanned++;
console.log("Not Banned", NotBanned);
}
}
for (const dataItem of res.data.data) {
var BannedUsers = 0;
if(dataItem.banned === true){
BannedUsers++;
console.log("Not Banned",BannedUsers);
}
}
let
bannedUsers = 0,
unBannedUsers = 0;
const response = res.data.data
for(let users = 0; users < response.length; users ++ ) {
if(!response.banned) {
unBannedUsers += users;
}
else {
bannedUsers += users;
}
}
console.log(`Banned Users : ${bannedUsers} , UnBanned Users : ${unBannedUsers}` )

roles.forEach not a function

So basically what is happening, is this code is running when a command is run. If the user's string (UserID) fits into the array, it responds as true and sets the access level to 6. If that is false, it checks for roles and sets the access level if they have certain roles by the role names. However, the role names part is not working. For example, a user with the Moderator role (normally access level 3 / perms.three), is showing up as access level 0 and I cannot figure out why.
The way this is supposed to work is that it checks the ID of a user. If that ID is in a pre-defined array located in a .json file, it overrides all other access levels by giving an access level of 6. If the array doesn't exist it moves onto the roles function and searches to see if the role exists in the persons list of roles.
This is what I use to check the roles/userID.
if (command.accessLevel > perms.getAccess(message.member.roles) || command.accessLevel > perms.getAccess(message.member.id)){
return message.channel.send("``Error: Access Denied.``");
}
This is the permissions.js file:
const bot = require('./bot').skynet;
const config = require('./config.json');
const perms = require('./permissions.json').access;
function arrayHas(arr, val){
var result = false;
for (var item in arr){
if (arr[item] == val){
result = true;
break;
}
}
return result;
}
function getAccess(user){
var access = 0;
if (typeof(user) == 'string'){
if (arrayHas(perms.six, user)){
return 6;
}
}
var roles = user;
roles.forEach(function(role){
if (arrayHas(perms.five, role.name)){
access = 5;
return;
}
else if (arrayHas(perms.four, role.name)){
access = 4;
return;
}
else if (arrayHas(perms.three, role.name)){
access = 3;
return;
}
else if (arrayHas(perms.two, role.name)){
access = 2;
return;
}
else if (arrayHas(perms.one, role.name)){
access = 1;
return;
}
});
return access;
}
bot.on('message', message => {
console.log(getAccess(message.member.id));
console.log(getAccess(message.member.roles));
})
module.exports = {
arrayHas: arrayHas,
getAccess: getAccess,
};
In the first if statement, you should return a value after the inside if statement. Otherwise, you'll continue to the following code roles.forEach for a string which will fail (roles.forEach is not a function)
Or place the non-string logic inside an else statement:
function getAccess(user){
var access = 0;
if (typeof(user) == 'string'){
if (arrayHas(perms.six, user)){
return 6;
}
} else {
var roles = user;
roles.forEach(function(role){
if (arrayHas(perms.five, role.name)){
access = 5;
return;
}
else if (arrayHas(perms.four, role.name)){
access = 4;
return;
}
else if (arrayHas(perms.three, role.name)){
access = 3;
return;
}
else if (arrayHas(perms.two, role.name)){
access = 2;
return;
}
else if (arrayHas(perms.one, role.name)){
access = 1;
return;
}
});
}
return access;
}
In your function getAccess(), you're setting the access variable but never actually returning the value. Try this revised version.
const Discord = require('discord.js');
const { skynet: bot } = require('./bot');
const config = require('./config.json');
const { access: perms } = require('./permissions.json');
/**
* Return the access level of a member.
* #param {Discord.GuildMember} member
*/
function getAccess(member) {
if (!(member instanceof Discord.GuildMember)) return new TypeError('Expected a GuildMember.');
if (arrayHas(perms.six, member.user.id)) return 6;
const roles = member.roles;
for (const role of roles) {
if (arrayHas(perms.five, role.name)) return 5;
if (arrayHas(perms.four, role.name)) return 4;
if (arrayHas(perms.three, role.name)) return 3;
if (arrayHas(perms.two, role.name)) return 2;
if (arrayHas(perms.one, role.name)) return 1;
}
return 0;
}

Create mongoose plugin with typescript

I have found on the internet this interesting article about plugins in mongoose. From that site I got this code:
function HidePlugin(schema) {
var toHide = [];
schema.eachPath(function(pathname, schemaType) {
if (schemaType.options && schemaType.options.hide) {
toHide.push(pathname);
}
});
schema.options.toObject = schema.options.toObject || {};
schema.options.toObject.transform = function(doc, ret) {
// Loop over all fields to hide
toHide.forEach(function(pathname) {
// Break the path up by dots to find the actual
// object to delete
var sp = pathname.split('.');
var obj = ret;
for (var i = 0; i < sp.length - 1; ++i) {
if (!obj) {
return;
}
obj = obj[sp[i]];
}
// Delete the actual field
delete obj[sp[sp.length - 1]];
});
return ret;
};
}
The problem is that I use typescript together with node.js. The typescript compiler gives me errors because there are no explicit types. In fact, I do not know exactly what type I have to attribute to the variables in the code

Recursively get documents in Azure Cosmos Stored Procedure

I'm trying to generate a content tree for a simple wiki. Each page has a Children property that stores the id of other wiki pages. I'm trying to write a SPROC that gets all of the documents, then iterate over each page's Children property and replace each item with an actual wiki page document. I'm able to get the first set of documents, but the wikiChildQuery returns undefined and I'm not quite sure why.
I've been able to get a document with the query alone but for some reason, it doesn't work within the SPROC. Is there something I'm missing here?
function GetWikiMetadata(prefix) {
var context = getContext();
var collection = context.getCollection();
var metadataQuery = 'SELECT {\"ExternalId\": p.ExternalId, \"Title\": p.Title, \"Slug\": p.Slug, \"Children\": p.Children} FROM Pages p'
var metadata = collection.queryDocuments(collection.getSelfLink(), metadataQuery, {}, function (err, documents, options) {
if (err) throw new Error('Error: ', + err.message);
if (!documents || !documents.length) {
throw new Error('Unable to find any documents');
} else {
var response = getContext().getResponse();
for (var i = 0; i < documents.length; i++) {
var children = documents[i]['$1'].Children;
if (children.length) {
for (var j = 0; j < children.length; j++) {
var child = children[j];
children[j] = GetWikiChildren(child);
}
}
}
response.setBody(documents);
}
});
if (!metadata) throw new Error('Unable to get metadata from the server');
function GetWikiChildren(child) {
var wikiChildQuery = metadataQuery + ' WHERE p.ExternalId = \"' + child + '\"';
var wikiChild = collection.queryDocuments(collection.getSelfLink(), wikiChildQuery, {}, function(err, document, options) {
if (err) throw new Error('Error: ', + err.message);
if (!document) {
throw new Error('Unable to find child Wiki');
} else {
var children = document.Children;
if (children) {
for (var k = 0; k < children.length; k++) {
var child = children[k];
children[k] = GetWikiChildren(child);
}
} else {
return document;
}
}
if (!wikChild) throw new Error('Unable to get child Wiki details');
});
}
}
1.I'm able to get the first set of documents, but the wikiChildQuery
returns undefined and I'm not quite sure why.
Firstly, here should be corrected. In the first loop, you get children array with documents[i]['$1'].Children, however , in the GetWikiChildren function you want to get children array with document.Children? Surely,it is undefined. You need to use var children = document[0]['$1'].Children;
2.It seems that you missed the replaceDocument method.
You could refer to the snippet code of your metaDataQuery function:
for (var i = 0; i < documents.length; i++) {
var children = documents[i]['$1'].Children;
if (children.length) {
for (var j = 0; j < children.length; j++) {
var child = children[j];
children[j] = GetWikiChildren(child);
}
}
documents[i]['$1'].Children = children;
collection.replaceDocument(doc._self,doc,function(err) {
if (err) throw err;
});
}
3.Partial update is not supported by Cosmos db SQL Api so far but it is hitting the road.
So, if your sql doesn't cover your whole columns,it can't be done for your replace purpose.(feedback) It is important to note that the columns which is not mentioned in the replace object would be devoured while using replaceDocument method.

Node acting synchronously, waiting for the loop to finish

I have encounted a few issues in the project I am doing since node is async, but now I have a for loop and I do something after the loop ends or breaks, and it seems to be working synchronously and I want to know why.
I have this code:
var newItem = true;
for (var i = 0; i < cart.length; i++) {
if (cart[i].title == slug) {
cart[i].qty++;
newItem = false;
break;
}
}
if (newItem) {
cart.push({
title: slug
});
}
And all this works as expected! the newItem variable always has the correct value.
How come newitem is not always true?
I never have more than 3 items in the cart array, is this why? Would it behave differently if I had a 1000 items?
The code you have provide is running synchronously, if you want to test whether it works in an asynchronous condition try running this code
var newItem = true;
for (var i = 0; i < cart.length; i++) {
setTimeout(function(){
if (cart[i].title == slug) {
cart[i].qty++;
newItem = false;
break;
}
}, 1000);
}
if (newItem) {
cart.push({
title: slug
}
}

Resources