Update Array of Objects using cloud functions - javascript

Hi i'm new to cloud functions, i was trying to update name in the collection when there is a Chang(update) on family document, but i cannot update name in the collection, seems like line is not executing after first console log.
exports.onClassroomUpdate = functions.firestore.document("family/{id}").onUpdate((change, context) => {
const documentId = context.params.id;
const after = change.after.data();
let users = admin.firestore().collection('users').where('family.id', '==', documentId);
return users.get().then((querySnapshot) => {
let chain = Promise.resolve();
console.log('work');
querySnapshot.forEach((doc) => {
console.log('not working');
chain = chain.then(() => {
return admin.firestore().collection('users').doc(doc.id).update({
name: after.name
});
});
});
return Promise.all([chain]);
})
}).catch(err => {
console.log(err);
return Promise.reject(err);
});
)}

Related

Read array from Firebase Document

I have an array of URLS stored within a document that i'd like to read and display as individual cards. All I'm getting is the return of the whole array, I'm not mapping it correctly and I don't know where I'm going wrong.
Currently, it's displaying "https://website1.com, https://website2.com". Where as I would like it to be individual items.
const getInternalLinks = async () => {
try {
const query = await db
.collection("internallinks")
.get()
.then((snapshot) => {
const tempData = [];
snapshot.forEach((doc) => {
const data = doc.data();
tempData.push(data);
});
setInternalLinks(tempData);
});
} catch (err) {
console.error(err);
};
};
useEffect(() => {
getInternalLinks()
},[])
return (
{internalLinks.map((doc, index) => {
<Card>
<p>{doc.urls.urls}</p>
</Card>
}))
);
Firebase Collection Structure
Try adding it directly to the state:
const [internalLinks, setInternalLinks] = useState([]);
const getInternalLinks = async () => {
try {
const query = await db
.collection("internallinks")
.get()
.then((snapshot) => {
snapshot.forEach((doc) => {
const data = doc.data();
setInternalLinks([ ...internalLinks, data ]);
});
});
} catch (err) {
console.error(err);
};
};

Await sql and push to array before executing the result

I'm using mysql npm package to get data from the database.
My goal is to add every list(id) from lists Table and push it into lists array.
I'm getting the correct data from the database but when I result the query lists array is empty.
I think that I have to add async and await to the code to make it work. Tried in several places but I didn't make it work. Any idea what I'm doing wrong?
// GET - get all grocery_lists
Grocery_list.getAll = (result) => {
let lists = []; // <--- List Array
sql.query("SELECT id FROM lists", (err, res) => { // <--- Get all id from 'lists' table
if (err) {
console.log(err);
}
res.forEach(list => { // <--- Loop thru all lists
sql.query(`
SELECT items.id, items.name, items_x_lists.purchased
FROM items_x_lists
INNER JOIN items ON items_x_lists.itemId = items.id
WHERE items_x_lists.listId = ${list.id};
`, (err, res) => { // <--- Get all items for ${list.id} from 'items' table
if (err) {
console.log(err);
}
const list = {};
list.id = res.id;
console.log(list); // <--- { id: 1 } ... { id: 2 }
lists.push(list);
});
});
result(null, lists); // <--- returning empty array instead of [{ id: 1 }, { id: 2 }]
});
};
I think you can do it simply like this (not the exact code) but I think you've got the idea:
// GET - get all grocery_lists
Grocery_list.getAll = (result) => {
getData().then(data => {
// call result(data);
}).catch(err => {
// handle error for no data or any other errors
})
}
const getData = async () => {
try {
var res = await sql.query("SELECT id FROM lists");
// Handle error if no data found
if (!res) { Promise.reject("No data found"); }
} catch (error) {
return Promise.reject(error);
}
const listIds = res.map(id => id); // array of ids
try {
var data = await sql.query(`
SELECT items.id, items.name, items_x_lists.purchased
FROM items_x_lists
INNER JOIN items ON items_x_lists.itemId = items.id
WHERE items_x_lists.listId in ${listIds};`);
// Handle error if no data found
if (!data) { return Promise.reject("No data found") }
} catch (error) {
return Promise.reject(error);
}
return Promise.resolve(data)
}
this mighht be the solution
Grocery_list.getAll = async (result) => { return sql.query("SELECT id FROM lists", (err, res) => { // <--- Get all id from 'lists' table if (err) { console.log(err); } }); };
I’m outside, so I can only edit with my mobile phone. If the layout is strange, I apologize for it.
First of all, if it is me, I will adjust a few places
1.Create an async function and use Promise.all technology
async function queryAll(querys){
return await Promise.all(querys);
}
2.Create a Promise function to execute each sql
const queryPromise = id => {
return new Promise((resolve, reject) => {
sql.query(
'SELECT items.id, items.name, items_x_lists.purchased FROM items_x_lists INNER JOIN items ON items_x_lists.itemId = items.id WHERE items_x_lists.listId = ? ;',
id,
(err, rows, fields) => {
console.log(rows);
if (err) reject(err);
else resolve(rows);
}
);
});
};
3.Adjust the internal logic of the getAll event
const querys = []
res.forEach(list => {
querys.push(queryPromise(list.id));
});
const querysResults = await queryAll(querys);// remember to use async for function
querysResults.forEach(querysResult => {
lists.push({id:querysResult['id']});
});
Because there is no computer on hand, there may be some situations that need to be judged, but roughly this will work normally.
Hope this helps you :)

convert multiple objects into an array of objects

I have created a function where i have passed an object as an argument and there are multiple objects which is passed on one by one and the output is an object but it comes as an individual object which is fine because I have written the code in that way now i wanted to store the objects into an array so it becomes an array of objects.and store for all objects
here is the tried code which gives single object example
const {db} = require('./constant')
const momentTz = require("moment-timezone");
const { getAuth } = require("./constant");
const officeStatus = async officeActivity => {
let output = {};
const maileventQuerySnapshot = await db
.collection("MailEvents")
.where("office", "==", officeActivity.office)
.where("event", "==", "delivered")
.limit(1).get();
output["office"] = officeActivity.office;
output["First Contact"] = officeActivity.attachment["First Contact"].value;
output["Second Contact"] = officeActivity.attachment["Second Contact"].value;
output["Office Creation Date"] = momentTz
.tz(officeActivity.timestamp, officeActivity.attachment.Timezone.value)
.format("DD MMM YYYY");
output["Reports Sent"] = maileventQuerySnapshot.docs.length ? true:false
const officeSubcollectionSnapshot = await db
.collection(`Offices/${officeActivity.officeId}/Addendum`)
.where("date","==",parseInt( momentTz(new Date()).subtract(1, "days").format("DD")))
.where('month', '==', parseInt( momentTz(new Date()).subtract(1, 'days').format("MM"))-1)
.where('year', '==',parseInt( momentTz(new Date()).subtract(1, "days").format("YYYY")))
.orderBy("user").orderBy('timestamp')
.get();
output['Number of unique Checkin Yesterday'] =
officeSubcollectionSnapshot.docs.length;
const activitiesRefSnapshot = await db
.collection("Activities")
.where("office", "==", officeActivity.office)
.where("template", "==", "subscription")
.where("attachment.Template.value", "==", "check-in")
.get();
const phoneNumberArray = [];
activitiesRefSnapshot.forEach(doc => {
phoneNumberArray.push(doc.data().attachment["Phone Number"].value);
});
const userRecord = await Promise.all(phoneNumberArray.map(getAuth));
output["Number of checkin Subscription Auth"] = userRecord.filter(
doc => doc !== undefined
).length;
output["Number of Checkin Subscription No Auth"] = userRecord.filter(
doc => doc === undefined
).length;
return { output};
};
module.exports = { officeStatus };
and the other file where i have queried the office and passed objects as an argument
const {admin,db} = require('./constant');
const { officeStatus } = require("./officeStatus");
let execute = async () => {
try {
let office = await db.collection("Offices").where("status", "==", "PENDING").get();
office.forEach(doc => {
officeStatus(doc.data())
.then(e => {
console.log(JSON.stringify(e.output));
})
.catch(error => {
console.log(error);
});
});
return;
} catch (error) {
console.log(error);
}
return;
};
execute();
admin.apps[0].delete();
I have get output in this way
{}
{}
{}....
and I wanted the output in this way
[{},{},{}]
The promise inside forEach is not correct. It wouldn't give you expected results. You shouldn't use that as it is not supported. You should consider using for..of or Promise.all. In your case I would suggest to use Promise.all as you need result in array.
Promise.all(office.docs.map(doc => {
return officeStatus(doc.data())
.then(e => {
return e.output;
})
.catch(error => {
console.log(error);
});
}))
.then(res => console.log(res));
Try doing that :
// and the other file where i have queried the office and passed objects as an argument
const {admin,db} = require('./constant');
const { officeStatus } = require("./officeStatus");
let execute = async () => {
try {
let office = await db.collection("Offices").where("status", "==", "PENDING").get();
let arr = new Array();
office.forEach(doc => {
officeStatus(doc.data())
.then(e => {
arr.push(e)
})
.catch(error => {
console.log(error);
});
});
console.log(arr)
return;
} catch (error) {
console.log(error);
}
return;
};
execute();
admin.apps[0].delete();
I just created an array where we push e when we did officeStatus. Then, at the end of the code, we log arr ; the declared array.
const {admin,db} = require('./constant');
const { officeStatus } = require("./officeStatus");
let execute = async () => {
let office = await db.collection("Offices").where("status", "==", "PENDING").get()
const arrayofObject = await Promise.all(office.docs.map(doc => {
return officeStatus(doc.data())
.then(e => {
// console.log(JSON.stringify(e.output));
return e.output;
})
.catch(error => {
console.log(error);
});
}))
console.log(arrayofObject)
return;
};
execute();
admin.apps[0].delete();

Un-nest JavaScript Firestore Functions

How can I rewrite this code to not be nested in itself? I also need access to the values obtained in the previous functions calls.
return docRef2.doc(`/users_stripe/${context.params.userID}`).get()
.then(snapshot => {
console.log("augu", snapshot);
return stripe.customers.createSource( jsonParser(snapshot._fieldsProto.id, "stringValue"),
{ source: jsonParser(snap._fieldsProto.token, "stringValue") },
function(err, card) {
console.log("listen people", card);
return docRef2.doc(`/users_stripe/${context.params.userID}/ptypes/ptypes`)
.set(card);
});
})
I am not sure what your code is doing here. I tried to write a pseudo/sample code that might give you an idea.
My code is not checked, so might contain problem.
let fun1 = () => {
return new Promise((resolve, reject) => {
docRef2.doc('/route').get().then(snapshot => {
if( snapshot ) resolve(snapshot);
else reject(snapshot);
})
})
}
let fun2 = (snapshot) => {
return new Promies((resolve, reject)=>{
stripe.customers.createSource(jsonParser(snapshot._fieldsProto.id, "stringValue"),
{ source: jsonParser(snap._fieldsProto.token, "stringValue") },
function (err, card) {
if (err) reject(false);// or whatever you wanna return
else resolve(card);
});
})
}
async function fun(){
let res1 = await fun1(); // Should contain snapshot
let res2 = await fun2(res1); // Should contain card
return docRef2.doc(`/users_stripe/${context.params.userID}/ptypes/ptypes`)
.set(card);
}

Using Promise.All with Firebase Cloud Fxs to fill array with FCM tokens before sendToDevice called

I was wondering if someone could help enlighten me as to how I can successfully execute this code where it waits until the entire array is filled with FCM tokens before firing sendToDevice().
I've been using these links (listed below) as references to try and resolve this but I still can not figure it out so alas here I am on SO seeking guidance. I just need to pass the tokens array once it is filled completely. I've gotten it where it fired multiple times on each push but never where it asynchronously loads and then fires ><
Firebase Real Time Database Structure for File Upload
Promise.all with Firebase DataSnapshot.forEach
https://aaronczichon.de/2017/03/13/firebase-cloud-functions/
exports.sendVenueAnnouncement = functions.database.ref(`/venueAnnouncements/{venueUid}/announcement`).onCreate(event => {
const venueUid = event.params.venueUid;
const announcement = event.data.val();
const getVenueDisplaynamePromise = admin.database().ref(`verifiedVenues/${venueUid}/displayname`).once('value');
return getVenueDisplaynamePromise.then(snapshot => {
const displayname = snapshot.val();
const payload = {
notification: {
title: `${displayname}`,
body: `${announcement}`
}
};
const getSubscriberTokensPromise = admin.database().ref(`subscribers/${venueUid}`).once('value');
return getSubscriberTokensPromise.then(snapshot => {
const tokens = [];
snapshot.forEach(function(childSnapshot) {
const key = childSnapshot.key;
const token = admin.database().ref(`pushTokens/` + key).once('value');
tokens.push(token);
});
return Promise.all(tokens);
}, function(error) {
console.log(error.toString())
}).then(function(values) {
return admin.messaging().sendToDevice(values, payload).then(response => {
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
if (error.code === 'messaging/invalid-registration-token' || error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove)
});
})
})
})
You almost have an understanding of Promises. It looks like you're also mixing callbacks with Promises. Firebase and Cloud Functions for Firebase are completely Promise based so there is no need.
With that said, you're code should look something like the following:
exports.sendVenueAnnouncement = functions.database
.ref(`/venueAnnouncements/${venueUid}/announcement`)
.onCreate(event => {
const venueUid = event.params.venueUid
const announcement = event.data.val()
let payload
let tokens = []
return admin.database()
.ref(`verifiedVenues/${venueUid}/displayname`)
.once('value')
.then(snapshot => {
const displayname = snapshot.val()
payload = {
notification: {
title: `${displayname}`,
body: `${announcement}`
}
}
return admin.database().ref(`subscribers/${venueUid}`).once('value')
})
.then(snapshot => {
snapshot.forEach((childSnapshot) => {
const key = childSnapshot.key
const token = admin.database().ref(`pushTokens/` + key).once('value')
tokens.push(token)
})
return Promise.all(tokens)
})
.then(values => {
return admin.messaging().sendToDevice(values, payload)
})
.then(response => {
const tokensToRemove = []
response.results.forEach((result, index) => {
const error = result.error
if (error) {
if (error.code === 'messaging/invalid-registration-token' || error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove())
}
}
})
return Promise.all(tokensToRemove)
})
})
Notice I don't assign the Promise to a variable. Just return it and chain a then. A Promise can return another Promise.
I suggest watching this Firecast for a better understanding of Promises.

Categories