firebase cloud functions oncall returns null - javascript

wondering what's the weird error.
I am using the onCall method from firebase cloud functions, but when I read it from my app it returns null value. I am trying to return some test data but it doesn't seem to be working. Am i returning the data wrongly?
index.js
exports.handleMassFollowAnalytics = functions.https.onCall((data, context) => {
const brandArray = data.brandArray;
const followed = data.followed;
let done = 0;
for (var i = 0; i < brandArray.length; i++) {
let brand = brandArray[i];
admin.database()
.ref(`brands/${brand}/followers`)
.transaction(function(post) {
if (post !== null) {
post--;
}
return post;
},
function(error, committed, snapshot) {
done++;
if (done === brandArray.length) {
// returning result.
return {
data: "testabc",
};
}
}
);
}
});
app.js
const handleMassFollowAnalytics = firebase
.functions()
.httpsCallable("handleMassFollowAnalytics");
handleMassFollowAnalytics({
brandArray: array,
followed: true,
}).then((result) => {
console.log("result: ", result) // returns null everytime
});

Your function needs to return a promise that resolves with the data to send to the client. Right now, your function returns nothing. The return statement inside the transaction callback is not returning from the main function.
Also, the code is ignoring the promises returned by the transactions you're performing. The final promise returned from the function must resolves only after all the other promises resolve.

So, I used Doug's information and arrived at the following answer, for reference to anyone in future.
This seems to return correctly for me.
Return individual promises
Return final promise
index.js
exports.handleMassFollowAnalytics = functions.https.onCall((data, context) => {
const brandArray = data.brandArray;
const followed = data.followed;
var promises = [];
for (var i = 0; i < brandArray.length; i++) {
let brand = brandArray[i];
promises.push(admin.database()
.ref(`brands/${brand}/followers`)
.transaction(function(post) {
if (post !== null) {
post--;
}
return post;
});
);
}
return Promise.all(promisess).then((result)=>{
return {
data: "testabc",
}
})
});

Related

Ionic Data Storage wait for a promise to finish

I am using Data Storage from Ionic Framework. In my code, I have my auth guard which checks user info is stored or not. Code is :
this.storage.get('user_info').then((data) => {
let user_info = JSON.parse(data);
if(user_info == null)
{
this._userIsLoggedIn = false;
} else {
this._userIsLoggedIn = user_info.isLogin;
}
});
return this._userIsLoggedIn;
I am only getting the default value that I set. How do I wait for the promise to finish before it is returning?
If you using promises in a method and want to return the result of the promise, you should also promisify your method too. You can achieve this in two way:
Return a promise
Or use async-await
Approach 1 :
return new Promise( resolve => {
this.storage.get('user_info').then((data) => {
let user_info = JSON.parse(data);
if(user_info == null)
{
this._userIsLoggedIn = false;
} else {
this._userIsLoggedIn = user_info.isLogin;
}
return resolve(this._userIsLoggedIn);
});
});
Approach 2 (Which is cleaner):
const data = await this.storage.get('user_info');
let user_info = JSON.parse(data);
if(user_info == null)
{
this._userIsLoggedIn = false;
} else {
this._userIsLoggedIn = user_info.isLogin;
}
return this._userIsLoggedIn;
Also, note that you should modify your function with async keyword in order to use await.

Issue with mongoose.save never returning inside of promise

Update !!
I fixed my initial issue with the help of Dacre Denny answer below however when writing tests for my code it turned out that the changes were not being saved before the server responded therefor the company collection in my test database was empty, I fixed this issue with the following code
Companies.find({ company_name: company.company_name }).then(found => {
if (found.length !== 0) {
return res.status(400).json({ error: "Company already exists" });
}
var userForms = company.users;
company.users = [];
const finalCompany = new Companies(company);
console.log(finalCompany);
var userPromises = [];
for (var x = 0; x < userForms.length; x++) {
var user = userForms[x].user;
user.company = finalCompany._id;
userPromises.push(userCreation(user));
}
return Promise.all(userPromises).then(responses => {
for (var x in responses) {
if (!responses[x].errors) {
finalCompany.addUser(responses[x]._id);
} else {
res.status(400).json(responses[x]);
}
}
return finalCompany;
});
})
// I moved the save in here !!!
.then((finalCompany) => {
finalCompany.save().then(()=>{
res.status(200).json({signup:"Successful"});
})
},(err) => {
res.json({error: err});
});
});
Original Issue
I am trying to create a mongoose document to represent a company, this code saves the model in my db however it does not seem to be responding with a status code or reply to postman when I make a request
I've used a debugger to step through the code but I am very rusty on my JS and I am afraid I've gone into deep water with promises thats gone over my head.
router.post('/c_signup', auth.optional, (req, res, next) => {
const { body: { company } } = req;
var error_json = cbc(company);
if( error_json.errors.length > 0 ){
return res.status(422).json(error_json);
}
Companies.find({company_name: company.company_name})
.then((found) => {
if (found.length !== 0) {
return res.status(400).json({error: "Company already exists"});
}
var userForms = company.users;
company.users = [];
const finalCompany = new Companies(company);
var userPromises = [];
for (var x =0; x < userForms.length; x ++) {
var user = userForms[x].user;
user.company = finalCompany._id;
userPromises.push(userCreation(user));
}
Promise.all(userPromises).then((responses) => {
for (var x in responses){
if (!responses[x].errors){
finalCompany.addUser(responses[x]._id);
}
else {
res.status(400).json(responses[x]);
}
}
console.log("h2");
finalCompany.save(function () {
console.log("h3");
return res.status(200);
});
})
});
return res.status(404);
});
This is the output from the debug but the execution is hanging here
h2
h3
There are a few issues here:
First, the save() function is asynchronous. You'll need to account for that by ensuring the promise that save() returns, is returned to the handler that it's is called in.
The same is true with the call to Promise.all() - you'll need to add that promise to the respective promise chain by returning that promise to the enclosing handler (see notes below).
Also, make sure the request handler returns a response either via res.json(), res.send(), etc, or by simply calling res.end(). That signals that the request has completed and should address the "hanging behaviour".
Although your code includes res.json(), there are many cases where it's not guaranteed to be called. In such cases, the hanging behaviour would result. One way to address this would be to add res.end() to the end of your promise chain as shown below:
Companies.find({ company_name: company.company_name }).then(found => {
if (found.length !== 0) {
return res.status(400).json({ error: "Company already exists" });
}
var userForms = company.users;
company.users = [];
const finalCompany = new Companies(company);
var userPromises = [];
for (var x = 0; x < userForms.length; x++) {
var user = userForms[x].user;
user.company = finalCompany._id;
userPromises.push(userCreation(user));
}
/* Add return, ensure that the enclosing then() only resolves
after "all promises" here have completed */
return Promise.all(userPromises).then(responses => {
for (var x in responses) {
if (!responses[x].errors) {
finalCompany.addUser(responses[x]._id);
} else {
res.status(400).json(responses[x]);
}
}
console.log("h2");
/* Add return, ensure that the enclosing then() only resolves
after the asnyc "save" has completed */
return finalCompany.save(function() {
console.log("h3");
return res.status(200);
});
});
})
.then(() => {
res.end();
},(err) => {
console.error("Error:",err);
res.end();
});

can't get setState inside a firebase function out of it

first i set listaa as a global variable
i'm trying to take out the variable(array) "listaa" from the firebase function that sets it... I'm plannig to use it later to make some maths.
I used console.log(listaa) inside the firebase function and it worked,
but then when i try to use it() outside(the firebase function) it doens't work.
I tried to use setState to make it usable outside but didnt work too
if (funcionar == 0) {
//============================================================== ANTES ===================================================
var newRef = firebase
.database()
.ref()
.child("TransferenciasPraVendedores/")
.push({
vendedor: vend,
dia: functions.dataHoje(),
hora: functions.horaHoje()
});
var fire = database.ref("EstoqueCadaVendedor/" + vend);
fire.once("value", snap => {
items = [];
snap.forEach(data => {
items.push({
key: data.key,
data: data.val()
});
});
console.log("items:");
console.log(items);
for (j = 0; j < prod.length; j++) {
// var a = parseInt(snap.val().quantidade);
console.log("d: " + items[j].data);
listaa.push(items[j].data);
}
// this.setState({ lista:listaa })
// console.log("lista:");
// console.log(this.state.lista[2]);
console.log("listaa");
console.log(listaa);
});
console.log("listaaaaaaaaaaaaaa");
console.log(listaa);
Ill use 'listaa' here:
for (i = 0; i < prod.length; i++) {
firebase
.database()
.ref()
.child("EstoqueCadaVendedor/" + vend + "/")
.update({
[prod[i].key]: parseInt(quantNova[i]) + listaa[i]
});
}
// this.setState({ quantidade: [], vendedor: "" });
}
Tuan over here.
The reason why you don't see it is because your code is asynchronous, so when you print console.log() at the end. The firebase calls haven't finished yet.This is a typical race condition scenario.
FYI, I personally prefer to use the promise style rathen than the callback, I think it makes the code cleaner and easier to read. Try this:
let firebaseDB = firebase.database();
if (funcionar == 0) {
return firebaseDB.ref("TransferenciasPraVendedores/")
.push({
vendedor: vend,
dia: functions.dataHoje(),
hora: functions.horaHoje()
})
.then(() => {
return firebaseDB.ref("EstoqueCadaVendedor/" + vend).once('value');
.then(snapshot => {
let snap = snapshot.val();
items = [];
snap.forEach(data => {
items.push({
key: data.key,
data: data.val()
});
});
return {snap, items};
})
.then({snap, items} => {
for (j = 0; j < prod.length; j++) {
listaa.push(items[j].data);
}
//console.log(lista)// NOW your lista should have all the items
// I would dispatch your redux action here.
})
.catch(error => {
throw error
}
Let me know if it helped. I can look into it more in depth. Saludos :)
Did you review the order of the log messages ? if you look carefully, you will recognize the data gets available after later in the process. You probably need to use async/promise to wait until the data gets available or you can use a call back function
async
function to make sure the data is available in the list before you use it.
An async function can contain an await expression, that pauses the
execution of the function and waits for the passed Promise's
resolution, and then resumes the async function's execution and
returns the resolved value.
In the example below, I am passing a call back function that gets called when data is available :
export function listReleases(callback) {
//console.log("hello from list releases ");
releasesRef.once("value").then(
function(snapshot) {
const releases = snapshot.val();
if (releases) {
callback("releases", releases);
}
},
function(error) {
// The Promise was rejected.
console.error(error);
}
);
}
+++++++++++++++++++++++++++
//call the function to get the data and pass a call back function
releases = listReleases(this.myCallback);
myCallback = (key, value) => {
//do whatever you need to do in here
};

Combining two async results from MongoDB collection in NodeJS

I'm sure this was asked before, but for some reason I am not able to make it work. Started using NodeJS with Mongoose just lately (getting used to doing everything async)
One of the things I'm trying to achieve is combining two results from two different collections according to some logic.
So assuming I have this get function, it should go fetch asynchronously all skills (for example), then from another collection, i should fetch all specific user skills, and combine them into one set of results, which will add a "isSelected:true" property when it's found in the userSkills collection. This is written in ES6:
exports.get = (req, res) => {
const combineSkills = (allSkills)=>{
const { userid } = req.headers;
return UserSkills.GetUserSkills(userid).then((response)=>{
for(var i=0;i<=response.length-1;i++){
var userSkill = response[i];
var found = allSkills.filter(e=>e.id==userSkill.skillId);
if(found.length>0){
found.isSelected=true;
}
}
return allSkills;
});
}
const respond = (response) => {
res.json({
ReturnCode: 2,
ReturnObject: response
})
}
// error occured
const onError = (error) => {
res.status(403).json({
ReturnCode: 0,
ReturnObject: error.message
})
}
Skills.GetAll()
.then(combineSkills)
.then(respond)
.catch(onError)
}
As you can see, I'm trying to call Skills.GetAll() skills, then get results to combineSkills object, do some logic and return the new object.
I know my problem is in the combineSkills function, where my return statement returns the object before the logic change.
My question is what is the right syntax for such scenario?
filter function return an array, you have to return the needed skills using find method like :
const combineSkills = (allSkills) => {
const { userid } = req.headers;
return UserSkills.GetUserSkills(userid).then((response) => {
for (var i = 0; i <= response.length - 1; i++) {
var userSkill = response[i];
var found = allSkills.find(e => e.id == userSkill.skillId);
if (found) {
found.isSelected = true;
}
}
return allSkills;
});
}

issue with pushing data into a new array while in a promise chain

I'm having trouble figuring out why my data is not being push into my new array, "results". newArr[0].mscd.g[i] is a list of several objects.
var axios = require('axios');
var moment = require('moment');
var _ = require('lodash');
var getData = function() {
return getNBASchedule().then(function(payload) {
return filterByMonth('January', payload);
}).then(function(result) {
return result
});
}
....
getData grabs the data from baseURL and returns a list of objects.
var getMonthlySchedule = function(data){
var results = [];
var newArr = data.slice(0, data.length);
for (var i = 0; i <= newArr[0].mscd.g.length; i++) {
if (newArr[0].mscd.g[i].v.tid === 1610612744 || newArr[0].mscd.g[i].h.tid === 1610612744) {
results.push(newArr[0].mscd.g[i]); <---- //does not seem to work
// however if I were to console.log(newArr[0].mscd.g[i],
// I would see the list of objects)
}
}
return results; <-- //when i console at this point here, it is blank
};
var getSchedule = function () {
return getData().then(function(pl) {
return getMonthlySchedule(pl)
})
};
var monthlyResults = function() {
return getSchedule().then(function(r) {
console.log("result", r)
return r
});
};
monthlyResults();
You don't know when getSchedule() is done unless you use a .then() handler on it.
getSchedule().then(function(data) {
// in here results are valid
});
// here results are not yet valid
You are probably trying to look at your higher scoped results BEFORE the async operation has finished. You HAVE to use .then() so you know when the operation is done and the data is valid.
Your code should simplify as follows :
var getData = function() {
return getNBASchedule().then(function(payload) {
return filterByMonth('January', payload);
});
}
var getMonthlySchedule = function(data) {
return data[0].mscd.g.filter(function(item) {
return item.v.tid === 1610612744 || item.h.tid === 1610612744;
});
};
var monthlyResults = function() {
return getData()
.then(getMonthlySchedule)
.then(function(r) {
console.log('result', r);
return r;
});
};
monthlyResults();
This may fix the problem. If not, then :
Check the filter test. Maybe those .tid properties are String, not Number?
Check that data[0].mscd.g is the right thing to filter.

Categories

Resources