Maxime Crtgn
Maxime Crtgn

Reputation: 265

How to use async/await in a firebase cloud function with onCreate trigger?

I've been through the different topics related to my issue but couldn't fix it. My firebase cloud function with the onCreate trigger does not deploy. I get this error : Failed to load function definition from source: Failed to generate manifest from function source: SyntaxError: await is only valid in async functions and the top level bodies of modules

// add action to users when they have the objective already added
exports.addActionToUserWhenCreated = functions.firestore
  .document('actions/{documentUid}')
  .onCreate(async (snap, context) => {
  
    // get the objectives  
    let actionObjectives = snap.data().objectives
    let actionId = snap.id
    let objSelectedBy = []
    
    // For each objective, get the selectedBy field
    actionObjectives.forEach(objective => {
      const docSnap = await db.doc(`objectives/${objective}`).get()
      objSelectedBy = docSnap.data().selectedBy

      objSelectedBy.forEach(user => {
        // Add the actionId to the user's selectedActions
        db.doc(`users/${user}/selectedActions/${actionId}`).set({
          achievedRate: 0,
        })
      })

      // Add all the userIds to the action's selectedBy field
      db.doc(`actions/${actionId}`).update({
        selectedBy: objSelectedBy,
      }, {merge: true});

    })

    return;
});

Do you see the problem? Thanks! Max

Upvotes: 2

Views: 1298

Answers (1)

Maxime Crtgn
Maxime Crtgn

Reputation: 265

Got this sorted out but it took me some time and precious help from Puff!

First issue here : the forEach => await cannot be used in the forEach but it works with a for...of loop.

Second : Promise.all after the await => Original answer from Puff here.

The working case of this function is therefore :

To make it understandable, you can replace action with product and objective with category. It would be as if you want to automatically add a newly created product to every users that are following a specific category.

exports.addActionToUserWhenCreated = functions.firestore
  .document('actions/{documentUid}')
  .onCreate(async (snap, context) => {
  // get the objectives  
  let actionObjectives = snap.data().objectives
  let actionId = snap.id
  let actionSelectedBy = []


  // For each objective, 
  for (const objective of actionObjectives) {
    // get the selectedBy field (array of userIds)
    const snap = await db.doc(`objectives/${objective}`).get()
    const objSelectedBy = snap.data().selectedBy;

    console.log("objSelectedBy",objSelectedBy)
  
    // Add the actionId to the user's selectedActions
    Promise.all(objSelectedBy.map(user => {
      return db.doc(`users/${user}/selectedActions/${actionId}`).set({
        achievedRate: 0,
      })
    }))

    // Add all the objectives' userIds to the action's selectedBy field (removing duplicate):
    actionSelectedBy = [...new Set([...actionSelectedBy ,...objSelectedBy])]
  }

  // Update the action's selectedBy field :
  db.doc(`actions/${actionId}`).update({
    selectedBy: actionSelectedBy,
  }, {merge: true});
});

Upvotes: 2

Related Questions