Home > OS >  How to properly use 'return' in a firebase cloud function when performing multiple actions
How to properly use 'return' in a firebase cloud function when performing multiple actions

Time:11-18

I am trying to understand how to properly use a cloud function..

The cloud function below achieves the desired result, however, I am not using a 'return' in it.

is this an issue? if yes, how do I solve it?

exports.onJobChangeToClosedAndSubstateIsCancelled = functions.firestore
    .document("job/{docId}")
    .onUpdate(async (change, eventContext) => {
        const afterSnapData = change.after.data();
        const afterJobState = afterSnapData["Job state"];
        const afterJobSubState = afterSnapData["Job substate"];

        if (afterJobSubState == "Cancelled" && afterJobState == "Closed") {

            // get all job related job applications
            const relatedJobApplicationsList = await admin.firestore().collection("job application").where("Job id", "==", change.before.id).get();
            // mark job applications closed so that other contractors cant waste their time
            relatedJobApplicationsList.forEach(async doc => {

                await admin.firestore().collection("job application").doc(doc.id).update({
                    "Job application state": "Closed",
                    "Job application substate": "Job cancelled by client",
                })

            })


        }
    });

My understanding from other questions such as the one below is that I should be returning the change on the document or the action taking place. ( is this correct? ) Can you help me?-firebase-function-onUpdate

Below is an example of a cloud function where I was able to place the 'return' statement:

// when a chat message is created
// send a notification to the user who is on the other side of the chat
// update the chat 'Chat last update' so that the Chats screen can put the chat at the top of the page
exports.onChatMessageCreate = functions.firestore
    .document("chat messages/{docId}")
    .onCreate(async (snapshot, context) => {
        const snapData = snapshot.data();
        // information about who sent the message
        const senderUserName = snapData["Chat message creator first name"];
        const senderUserId = snapData["User id"];
        // chat id to which chat messages belong to
        const chatId = snapData["Chat id"];

        // information about who should receive the message
        let receiverUserId;
        let receiverToken = "";

        // fetch user to send message to
        const chatData = await (await admin.firestore().collection("chat").doc(chatId).get()).data();
        const chatUsersData = chatData["User ids list"];

        chatUsersData[0] == senderUserId ? receiverUserId = chatUsersData[1] : receiverUserId = chatUsersData[0];

        receiverToken = await (await admin.firestore().collection("user token").doc(receiverUserId).get()).data()["token"];

        console.log("admin.firestore.FieldValue.serverTimestamp()::   "   admin.firestore.FieldValue.serverTimestamp());

        // set chat last update
        await admin.firestore().collection("chat").doc(chatId).update({
            "Chat last update": admin.firestore.FieldValue.serverTimestamp(),
        });

        const payload = {
            notification: {
                title: senderUserName,
                body: snapData["Chat message"],
                sound: "default",
            },
            data: {
                click_action: "FLUTTER_NOTIFICATION_CLICK",
                message: "Sample Push Message",
            },
        };

        return await admin.messaging().sendToDevice(receiverToken, payload);
    });

CodePudding user response:

Is this an issue?

Yes, by not returning a Promise (or a value since your callback function is async) you are incorrectly managing the life cycle of your Cloud Function.

In your case, since you have an if block, the easiest is to just do return null; (or return 0; or return;) at the end of the CF as follows.

In addition, you need to use Promise.all() since you want to execute a variable number of calls to an asynchronous method in parallel.

exports.onJobChangeToClosedAndSubstateIsCancelled = functions.firestore
    .document("job/{docId}")
    .onUpdate(async (change, eventContext) => {
        const afterSnapData = change.after.data();
        const afterJobState = afterSnapData["Job state"];
        const afterJobSubState = afterSnapData["Job substate"];

        if (afterJobSubState == "Cancelled" && afterJobState == "Closed") {

            // get all job related job applications
            const relatedJobApplicationsList = await admin.firestore().collection("job application").where("Job id", "==", change.before.id).get();
            
            // mark job applications closed so that other contractors cant waste their time
            const promises = [];

            relatedJobApplicationsList.forEach(doc => {

                promises.push(admin.firestore().collection("job application").doc(doc.id).update({
                    "Job application state": "Closed",
                    "Job application substate": "Job cancelled by client",
                }));

            })

            await Promise.all(promises);
        }
        
        return null;
    });

More info on how to properly manage CF life cycle here in the doc.

You may also be interested by watching the talk I gave at the I/O Extended UK & Ireland event in 2021. This talk was specifically dedicated to this subject.

  • Related