Simon Kibsgård
Simon Kibsgård

Reputation: 532

Firebase Cloud Database Trigger Functions not always completing

I have a database triggered function, that triggers when a team administrator adds new members to his team. The function is supposed to create authentication in Firebase and an object, where the new user can store his personal settings. My problem is, that when a lot of members are added simultaneously via an import feature, my function doesn't always complete. Since they seem to be triggered alright when I look at the log, I suspect my implementation of chained promises to be the error cause. Here is a copy of the code. Please help me correct the errors.

// When a team adds a new member, we should also create authentication and a record for his user data...
exports.createNewUserAndAuthOnNewMember = functions
  .database
  .ref('/Members/{team}/{memberId}/createNewUser')
  .onCreate(event => {

  const memberRef = admin.database().ref('/Members/'+event.params.team+'/'+event.params.memberId);

  memberRef.once('value')
  .then((memberSnap) => {
    const memberEmail = memberSnap.child('email').val();
    const preferredLanguage = memberSnap.child('preferredLanguage').val();

    // Creating authentication for new system user...
    //since we want to update the /user object later on even if the authentication creation fails (because user already exists), this promise is inside the top promise chain
    admin.auth().createUser({
      uid: event.params.memberId,
      email: memberEmail,
      emailVerified: false,
      password: '[random password generated]',
      disabled: false
    })
    .then(function(userRecord) {
      console.log("Successfully created new user:", userRecord.uid);
      return preferredLanguage;
    })
    .catch(function(error) {
      console.log("Error creating new user:", error);
      return preferredLanguage;
    });

  })
  .then(preferredLanguage => {
    // Creating the personal user object in the database
    admin.database().ref('/users/'+event.params.memberId).update({'team': event.params.team, 'preferredLanguage': preferredLanguage});
  })
  .then(() => {
    //we did the job and should remove the trigger from the member object in the database
    memberRef.child('createNewUser').remove();
  })
  .then(() => {
    console.log('Created /users/'+event.params.memberId);
    return true;    
  });
});

Upvotes: 0

Views: 327

Answers (1)

Renaud Tarnec
Renaud Tarnec

Reputation: 83093

This should work:

exports.createNewUserAndAuthOnNewMember = functions
    .database
    .ref('/Members/{team}/{memberId}/createNewUser')
    .onCreate(event => {

        let preferredLanguage;

        const memberRef = admin.database().ref('/Members/' + event.params.team + '/' + event.params.memberId);

        return memberRef.once('value')
            .then((memberSnap) => {
                const memberEmail = memberSnap.child('email').val();
                preferredLanguage = memberSnap.child('preferredLanguage').val();

                // Creating authentication for new system user...
                //since we want to update the /user object later on even if the authentication creation fails (because user already exists), this promise is inside the top promise chain
                return admin.auth().createUser({
                    uid: event.params.memberId,
                    email: memberEmail,
                    emailVerified: false,
                    password: '[random password generated]',
                    disabled: false
                })
            })
            .then(() => {
                // Creating the personal user object in the database
                return admin.database().ref('/users/' + event.params.memberId).update({'team': event.params.team, 'preferredLanguage': preferredLanguage});
            })
            .then(() => {
                //we did the job and should remove the trigger from the member object in the database
                return memberRef.child('createNewUser').remove();
            })
            .catch(error => {
                console.log(error);
                //...
            });
    });

You have to return the promise in each then() when chaining them, and you only need one catch at the end of the chain.


In addition, note that you are using the "old" syntax for Cloud Functions. Since version 1.0.+ there is a new syntax, see https://firebase.google.com/docs/functions/beta-v1-diff

Upvotes: 1

Related Questions