async/await not working with mongoose instance methods - node.js

I'm try to create user mongoose document which require a storage path. I want to await for directory to be created and path resolved. But it is not working and after user is saved, user.storagePath is still undefined. please figure out problem.
Following is code of createStorage()
const getMountRoot = require('../configuration/configuration').getMountRoot
const path = require('path')
const fs = require('fs')
const Logger = require('../configuration/configuration').getLogger
module.exports = (email, firstName, secondName) => {
email = String(email).toLowerCase().replace(/[#_\.\-]/g, '')
firstName = String(firstName).toLowerCase().replace(/[#_\.\-]/g, '')
secondName = String(secondName).toLowerCase().replace(/[#_\.\-]/g, '')
let storagePath = path.join(getMountRoot(), `${secondName}${email}${firstName}`)
return fs.promises.mkdir(storagePath, { recursive: true })
.then(() => { return storagePath })
.catch(() => {Logger.log(err); return storagePath})
}
And following is instance method
const createStorage = require('../extra/create-storage')
userSchema.methods.createStorage = async function() {
this.storagePath = await createStorage(this.email, this.firstName, this.secondName)
}
Kindly note that I call createStorage() on User instance before calling the save()

As #qqilihq figured, I need to await at instance method call. Doing that worked correctly.

Related

Send message to channel in a function

I have an event handler for the ready event.
ready.js:
const { Events } = require('discord.js');
const db = require("../config/database");
const itemdb = require("../config/itemdb");
const items = require("../models/items");
const AHItems = require('../models/ahitems.js');
const RSS = require('../models/regionserversettings.js');
module.exports = {
name: Events.ClientReady,
once: true,
execute(client) {
console.log(`Ready! Logged in as ${client.user.tag}`);
db.authenticate()
.then(() => {
console.log('Logged in to DB!');
AHItems.init(db);
AHItems.sync();
RSS.init(db);
RSS.sync();
})
.catch(err => console.log(err));
itemdb.authenticate()
.then(() => {
console.log('Logged in to Item DB!');
items.init(itemdb);
items.sync();
})
.catch(err => console.log(err));
},
};
From inside the execute block I can use client.channels.cache.get('xxxxxx').send('Hello');
I want to use the send method in another File:
const AHItems = require("../models/ahitems");
const RSS = require("../models/regionserversettings");
const getprice = require("../api/getcurrentPrice");
const client = require("../events/ready");
const pricealarm = async function()
{
let ahitems = await AHItems.findAll({attributes: ['guildID', 'itemID']});
for (let i = 0; i < ahitems.length; i++) {
const guild = ahitems[i].dataValues.guildID;
const RSSData = await RSS.findOne({where: {guildID: guild}});
const item = ahitems[i].dataValues.itemID;
const access_token = RSSData.AccessToken;
const server = RSSData.serverID;
const price = await getprice(access_token, item, server);
const channel = client.channels.cache.get('x').send('test');
console.log(channel);
}
}
module.exports = pricealarm;
But if I try to do this, it tells me 'Unresolved function or method send()'
I think I am requiring the wrong file, but am unsure, which one I have to require
The issue with your code is that you are trying to use the send() method from an object client that has not been properly instantiated in the file where you want to use it. In your ready.js file, you correctly initialize the client object and can use its send() method inside the execute block.
However, in the other file where you want to use the send() method, you import the ready.js file, but you are only importing the module, not the instantiated client object. Therefore, the send() method is unresolved and cannot be called.
To fix this issue, you need to modify the ready.js file to export the client object in addition to the module.exports statement. For example, you can add the following line at the end of the execute block:
module.exports.client = client;
Then, in your other file, you can import the client object by requiring the ready.js file and accessing the client property of the exported module. For example:
const ready = require("../events/ready");
const client = ready.client;
// Now you can use client.channels.cache.get('xxxxxx').send('Hello');
With these modifications, you should be able to properly use the send() method from the client object in both files.

Firebase Function onCreate add to new collection works, but update does not

I have this onCreate Trigger, I am using it to aggregate and add record or update record. First it takes minutes to add the record and then the update never runs just keeps adding, not sure why my query is not bringing back a record to update.
Any suggestion would be great.
exports.updateTotals = functions.runWith({tinmeoutSeconds: 540})
.firestore.document("user/{userID}/CompletedTasks/{messageId}")
.onCreate(async (snap, context) => {
const mycompleted = snap.data();
const myuserid = context.params.userID;
console.log("USER: "+myuserid);
const mygroup = mycompleted.groupRef;
const myuser = mycompleted.userRef;
const newPoints = mycompleted.pointsEarned;
console.log("POINTS: "+newPoints);
const data = {
groupRef: mygroup,
userRef: myuser,
pointsTotal: newPoints,
};
const mytotalsref = db.collection("TaskPointsTotals")
.where("groupRef", "==", mygroup)
.where("userRef", "==", myuser);
const o = {};
await mytotalsref.get().then(async function(thisDoc) {
console.log("NEW POINTS: "+thisDoc.pointsTotal);
const newTotalPoints = thisDoc.pointsTotal + newPoints;
console.log("NEW TOTAL: "+newTotalPoints);
if (thisDoc.exists) {
console.log("MYDOC: "+thisDoc.id);
o.pointsTotal = newTotalPoints;
await mytotalsref.update(o);
} else {
console.log("ADDING DOCUMENT");
await db.collection("TaskPointsTotals").doc().set(data);
}
});
});
You are experiencing this behavior because while querying for updates you are getting more than 1 document and you are using thisDoc.exists on more than one document. If you have used typescript this could have been catched while writing the code.
So for the update query, if you are confident that only unique documents exist with those filters then here’s the updated code that I have recreated using in my environment.
functions/index.ts :
exports.updateTotals = functions.runWith({timeoutSeconds: 540})
.firestore.document("user/{userId}/CompletedTasks/{messageId}")
.onCreate(async (snap, context) => {
const mycompleted = snap.data();
const myuserid = context.params.userID;
console.log("USER: "+myuserid);
const mygroup = mycompleted.groupRef;
const myuser = mycompleted.userRef;
const newPoints = mycompleted.pointsEarned;
console.log("POINTS: "+newPoints);
const data = {
groupRef: mygroup,
userRef: myuser,
pointsTotal: newPoints,
};
const mytotalsref = admin.firestore()
.collection("TaskPointsTotals")
.where("groupRef", "==", mygroup)
.where("userRef", "==", myuser);
await mytotalsref.get().then(async function(thisDoc) {
if (!thisDoc.empty) { // check if the snapshot is empty or not
const doc = thisDoc.docs[0];
if(doc.exists){
const newTotalPoints = doc.data()?.pointsTotal + newPoints;
const id = doc.id;
await db.collection("TaskPointsTotals").doc(id).update({pointsTotal: newTotalPoints});
}
} else {
await db.collection("TaskPointsTotals").doc().set(data);
}
});
});
For more information about QuerySnapshot methods check this docs

API Request passing ID fetched from mongodb

I´m a Java Dev so I need help from NodeJS guys!
Task: create a script that retrieves '_id', 'document', and 'corporateName' from MongoDB, then take the retrieved '_id', and pass it as a parameter to an API request. The last part should be taking 'document', 'corporateName' + 'client_id', 'client_secret' and export it into a single csv file.
It might be a very simple script! Therefore I´ve done this till now:
const {MongoClient} = require('mongodb');
const uri = "mongodb+srv://<privateInfo>/";
const client = new MongoClient(uri);
async function run() {
try {
const database = client.db("merchant-profile");
const ecs = database.collection("merchant_wallet");
const api = `https://<prodAPI>/v1/merchant/wallet/${id}/oauth2`;
const ecOpt = {_id: 1, document: 1, corporateName: 1};
const credOpt = {client_id: 1, client_secret: 1};
const ec = ecs.find({}).project(ecOpt);
let id = ec.forEach(id => cred._id);
const cred = api.find({}).project(credOpt);
await cred.forEach(console.dir);
} finally {
await client.close();
}
}
run().catch(console.dir);
I´m trying to understand how can I take '_id' fetched in 'ec' and pass it as a param to the 'cred' call.
This would already be awesome!
If you could help me out with the CSV issue as well it would be perfect.
So I don´t want just the answer, but understand how to do this.
Thank you all in advance!
This is the way I found to do it:
const { default: axios } = require("axios");
const { MongoClient } = require("mongodb");
const uri = "mongodb+srv://admin:sLKJdsdRp4LrsVtLsnkR#pp-core-prd.fy3aq.mongodb.net/";
const client = new MongoClient(uri);
async function run() {
try {
const database = client.db("merchant-profile");
const ecs = database.collection("merchant_wallet");
const data = [];
await ecs.find({}).forEach(async function teste(response) {
const id = response._id;
const api = `https://api.pedepronto.com.br/v1/merchant/wallet/${id}/oauth2`;
try{
const res = await axios.get(api);
data.push({client_secret: res.data[0].client_secret, client_id: res.data[0].client_id})
}catch(e){
console.log(e);
}
})
} finally {
await client.close();
}
}
run().catch(console.dir);
It iterates over the find method and appends the fetched id to the uri.

Get data from firestore document and use in cloud function

In the user's collection, each user has a document with a customer_id.
I would like to retrieve this customer_id and use it to create a setup intent.
The following code has worked for me in the past. However, all of a sudden it throws the error:
Object is possibly 'undefined'
The error is on the following line under snapshot.data() in this line:
const customerId = snapshot.data().customer_id;
Here is the entire code snippet:
exports.createSetupIntent = functions.https.onCall(async (data, context) => {
const userId = data.userId;
const snapshot = await db
.collection("development")
.doc("development")
.collection("users")
.doc(userId).get();
const customerId = snapshot.data().customer_id;
const setupIntent = await stripe.setupIntents.create({
customer: customerId,
});
const clientSecret = setupIntent.client_secret;
const intentId = setupIntent.id;
return {
clientsecret: clientSecret,
intentId: intentId,
};
});
Any help is appreciated :)
this is because snapshot.data() may return undefined
there are 2 ways to solve this
first is assert as non-null, if you have high confident that the data exist
const customerId = snapshot.data()!.customer_id;
second if check for undefined
const customerId = snapshot.data()?.customer_id;
if(customerId){
// ....
}
I recommend the 2nd method, it is safer
I can see you are using a sub collection order,You need to loop through the snapshot data using the forEach loop.
const customerId = snapshot.data()
customerId.forEach((id)=> {
console.log(id.customer_id)
});
Try this out but.
The document you're trying to load may not exist, in which case calling data() on the snapshot will return null, and thus this line would give an error:
const customerId = snapshot.data().customer_id;
The solution is to check whether the document you loaded exists, and only then force to get the data from it:
if (snapshot.exists()) {
const customerId = snapshot.data()!.customer_id;
...
}
if you want to fetch user data from docId then you can use something like this:
const functions = require("firebase-functions");
var admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
var db = admin.firestore();
db.settings({ timestampsInSnapshots: true });
exports.demoFunction = functions.https.onRequest((request, response) => {
var userId = request.body.userId;
db.collection("user").doc(userId).get().then(snapshot => {
if (snapshot) {
var data = snapshot.data();
// use data to get firestore data
var yourWantedData = data.name;
// use it in your functionality
}
});
});

Why would a dictionary variable be undefined in async function

I'm trying to figure out why the variable ROOT_FOLDER_LOCATION is undefined. The function is running as a google "cloud function" and it is triggered from ZOHO CRM in which it is sent URL encoded data containing the variables 'ID', 'firstName', 'lastName', 'Salesman'. I have tried getting this to work by 'awaiting' pretty much every line in the try-catch block, moving the salesmanID variable inside of the function, but it doesn't work. The salesman and workingRoot variables are both defined (salesman='Jim Schwartz' in my testing and workingRoot='abcdefgXv2'), so I don't understand why the ROOT_FOLDER_LOCATION variable is undefined.
const {google} = require('googleapis');
const axios = require('axios').default;
const FormData = require('form-data');
const SCOPES = ['https://www.googleapis.com/auth/drive'];
//IDs for client folders of salesmen
const salesmanID = {'Jim Schwartz': 'abcdefgXv2', 'Marks Shared Folder': '1M6NUf-abcdefg', 'Samanthas Shared Folder': 'abcdefgVZSMzhoVBU5wJ'};
exports.myFunction = async (req, res) => {
try{
const params = new URLSearchParams(req.body);
let zohoLeadID = params.get('ID');
let firstName = params.get('firstName');
let lastName = params.get('lastName');
let CLIENT_NAME = `${lastName}, ${firstName}`
let salesman = params.get('Salesman');
let ROOT_FOLDER_LOCATION = salesmanID[salesman];
let workingRoot = salesmanID['Jim Schwartz'];
console.log(salesman);
console.log(`workingRoot: ${workingRoot}`);
console.log(`${ROOT_FOLDER_LOCATION}`);
res.status(200).send('Success');
} catch (err) {
console.log(err);
res.status(500).send('Failed...');
}
};

Resources