I'm using Firebase scheduled function to periodically check data in Firestore if users' detail are added to SendGrid contact list. After they're successfully added to SendGrid, I want to update the value in firestore addToContact
from false to true.
exports.addContact = functions.region(region).pubsub.schedule('every 4 minutes').onRun(async(context) => {
// Query data from firestore
const querySnapshot = await admin.firestore().collection('users')
.where('metadata.addToContact', '==', false)
.get();
// Call SendGrid API to add contact
// If success, change metadata.addToContact to true
if (response) {
const docRef = admin.firestore().collection('users').doc(""+context.params.id);
await docRef.update( {'metadata.sendToSg': true }, { merge: true } );
}
}
I want to access context.params.id
but I realise that context that passed in .onRun isn't the same with the context passed in a callable function.
NOTE: If I pass context.params.id
to .doc
without ""+
, I got an error Value for argument "documentPath" is not a valid resource path. Path must be a non-empty string. After google the answer, I tried using ""+context.params.id
then I can console.log the context value. I only got
context {
eventId: '<eventID>',
timestamp: '2020-11-21T09:05:38.861Z',
eventType: 'google.pubsub.topic.publish',
params: {}
}
I thought I'd get document ID from params object here but it's empty.
Is there a way to get Document ID from firestore in a scheduled function?
The scheduled Cloud Function itself has no notion of a Firestore document ID since it is triggered by the Google Cloud Scheduler and not by an event occurring on a Firestore document. This is why the corresponding context
object is different from the context
object of a Firestore triggered Cloud Function.
I understand that you want to update the unique document corresponding to your first query (admin.firestore().collection('users').where('metadata.addToContact', '==', false).get();
).
If this understanding is correct, do as follows:
exports.addContact = functions.region(region).pubsub.schedule('every 4 minutes').onRun(async (context) => {
// Query data from firestore
const querySnapshot = await admin.firestore().collection('users')
.where('metadata.addToContact', '==', false)
.get();
// Call SendGrid API to add contact
// If success, change metadata.addToContact to true
if (response) {
const docRef = querySnapshot.docs[0].ref;
await docRef.update({ 'metadata.sendToSg': true }, { merge: true });
} else {
console.log('No response');
}
return null;
});
Update following your comment:
You should not use async/await
with forEach()
, see here for more details. Use Promise.all()
instead, as follows:
exports.addContact = functions.region(region).pubsub.schedule('every 4 minutes').onRun(async (context) => {
// Query data from firestore
const querySnapshot = await admin.firestore().collection('users')
.where('metadata.addToContact', '==', false)
.get();
// Call SendGrid API to add contact
// If success, change metadata.addToContact to true
if (response) {
const promises = [];
querySnapshot.forEach((doc) => {
const { metadata } = doc.data();
if (metadata.sendToSg == false) {
promises.push(doc.ref.update({ 'metadata.sendToSg': true }, { merge: true }));
}
})
await Promise.all(promises);
} else {
console.log('No response');
}
return null;
});