Description:
I believe the work my Cloud Firestore Triggers are supposed to perform is being overwritten by each other. I am attempting to use a Cloud Firestore Trigger to handle adding data to firestore after specific webhooks from a 3rd party API are processed. The webhook is first handled by creating a new document in an events collection i.e. events/{event}. The incoming webhooks are processed correctly and an event document is created for each webhook received.
I then use an onCreate trigger to add data to a single document within a collection of documents called "courses" and each document has a subcollection of "chapters". Each event document (which triggers the onCreate) may point to either the course document or a chapter document. My function within the trigger (setMetadata) checks which of these two documents the event document is referencing and updates the required fields. Each of my tests consists of 1 course and 2 chapter (which are nested in the course document) documents to be updated (3 webhooks received and in turn 3 onCreate triggers) almost at the same exact time.
Problem:
The work, after the Cloud Firestore Triggers are completed, produces inconsistent results. Some attempts add the metadata to the course document but not the chapter documents, other times to the chapter documents but not the course. And finally very few times all documents are updated correctly. No errors are produced. I have attempted to use onWrite instead as well as update instead of set. How do I ensure all documents are updated accordingly? Is this a race condition with the triggers overwriting the previous work from another trigger?
Goal
The goal is to have every initiated trigger to correctly update the corresponding document's fields. Please let me know if I need to provide any further info or context.
Code:
onCreate Trigger:
exports.trigger = functions.firestore
.document('events/{event}')
.onCreate((snap, context) => {
const data = snap.data();
return setMetadata(data);
})
setMetadata function:
const getUploadId = async(data) => {
return db.collection('events')
.where('data.asset_id', '==', data.object.id)
.get()
.then((snapshot) => {
let uploadId = '';
snapshot.forEach((doc) => {
uploadId = doc.data().object.id;
})
return uploadId;
})
}
const chapterCheck = async(data, uploadId) => {
return db.collectionGroup('chapters')
.where('video', '==', uploadId)
.get()
.then((snapshot) => {
if (snapshot.empty) {
return null;
}
snapshot.forEach((doc) => {
return doc.ref.set({
//set metadata for chapter here
}, {merge: true});
})
})
}
const courseCheck = async(data, uploadId) => {
return db.collection('courses')
.where('preview', '==', uploadId)
.get()
.then((snapshot) => {
if (snapshot.empty) {
return null;
}
snapshot.forEach((doc) => {
return doc.ref.set({
//sets metadata for course
}, {merge: true});
})
})
}
exports.setMetadata = async (data) => {
try {
const uploadResponse = await getUploadId(data).then((value) => {
return value;
});
const courseResponse = await courseCheck(data, uploadResponse);
const chapterResponse = await chapterCheck(data, uploadResponse);
return courseResponse, chapterResponse;
} catch (error) {
console.log(error);
return error;
}
}
Example of db schema:
*events*
-event01
-event02
-event03
*courses*
-course01
--*chapters*
------chapter01
------chapter02