5

I am still a beginner in the Firebase world and I have been trying to figure out what the problem is with the below code but I failed in all possible ways.

The code is supposed to retrieve the uid from the user profile in the database, then use it to update the authentication profile, then again to update the database profile if the authentication profile update was successful.

In index.js I have defined an exported function to deal with POSTed params from HTML forms. The code below defines the handler function in another module file:

 exports.auUpdateUserByEmail = (req, res) => {
  // This handler function will retrieve the POSTed params of user profile
  // and will attempt to update the existing user authentication as well as
  // the database profiles.
  //
  // This function accepts the following params:
  // 1. User email   // 2. Phone number   // 3. password   // 4. Display name
  // 5. Photo url   // 6. Disabled Flag
  //

  var db = admin.firestore();

  var uEmail = req.body.userEmail;
  var dName = req.body.displayName;
  var userId = "";

  var newuser = {
    displayName: dName
  }

  console.log("Email passed: " + uEmail);

  // Fetch the user UID by user email...
  res.write('User UID: ' + userId);
  console.log('User UID: ' + userId);

  // attempt to update the user authentication profile...
  return db.collection('Users').where('email', '==', email).get()
  .then(snapshot => {
    snapshot.forEach(doc => {
        var d = doc.data();
        console.log("doc.id: " + doc.id + " - d.email: " + d.email);
        if(d.email == email)
        {
          userId = d.uid;
        }
    });

    return admin.auth().updateUser(userId, newuser);
  }).then(function(userRecord) {
    // The updating was successful... Attempt to update User Details in
    // database User Profile...
    console.log("User Updated Successfully. UID: " + userRecord.uid);
    retUid = userRecord.uid;

    // Create a reference to the Users Database...
    var docRef = db.collection('Users');

    // Update the user profile document.
    return docRef.doc(userRecord.uid).update(newuser);
  }).then(result => {
    // everything went fine... return User UID as a successful result...
    res.write(userId);

    return res.end();

  }).catch(function(error) {
    console.log("doc.update - Error updating user profile in database:", error);
    return res.end();

  });
}

In index.js, I have the following exports definition:

var appAuth = express();
//Here we are configuring express to use body-parser as middle-ware.
appAuth.use(bodyParser.urlencoded({ extended: false }));
appAuth.use(bodyParser.json());

appAuth.post('/updateUserByEmail', authusers.auUpdateUserByEmail);

exports.usersAuthFunctions = functions.https.onRequest(appAuth);

I have to say that I got it to work fine to get the uid, update the auth profile, and then update database profile, but it keeps on waiting for the function return.

Appreciate your valuable help. Thanks.


I have updated the code as below and it does the jobs but returns a blank page as the HTTPS exits before the promises are complete which fires "Error: write after end" error.

var fetch_uid = db.collection('Users').where('email', '==', uEmail).get()
  .then(snapshot => {
    // var userId = snapshot.data.uid;

    snapshot.forEach(doc => {
        var d = doc.data();
        console.log("doc.id: " + doc.id + " - d.email: " + d.email);
        if(d.email == uEmail)
        {
          userId = d.uid;

          res.write('User UID: ' + userId);
          console.log('User UID: ' + userId);

        }
    });

    return admin.auth().updateUser(userId, newuser);
  }).then(function(userRecord) {
    // The updating was successful... Attempt to update User Details in
    // database User Profile...
    console.log("User Updated Successfully. UID: " + userRecord.uid);
    retUid = userRecord.uid;

    // Create a reference to the Users Database...
    var docRef = db.collection('Users');

    // Update the user profile document.
    return docRef.doc(userRecord.uid).update(newuser);
  }).then(result => {
    // everything went fine... return User UID as a successful result...
    res.write(userId);

    return;

  }).catch(function(error) {
    console.log("doc.update - Error updating user profile in database:", error);
    return;

  });

  res.end();
Grimthorr
  • 6,856
  • 5
  • 41
  • 53
Badih Barakat
  • 131
  • 1
  • 11
  • Possible duplicate of [Cloud Functions for Firebase HTTP timeout](https://stackoverflow.com/questions/47154257/cloud-functions-for-firebase-http-timeout) – Grimthorr Nov 16 '17 at 11:07

1 Answers1

7

A previous answer of mine on Cloud Functions for Firebase HTTP timeout might be of help here:

Cloud Functions triggered by HTTP requests need to be terminated by ending them with a send(), redirect(), or end(), otherwise they will continue running and reach the timeout.

From your code examples, it looks like your then(){} promise returns are ending with res.end(), but the entire function is returning the Promise from:

return db.collection('Users').where('email', '==', email).get()

Which could be stopping it from ending when you want it to. With HTTPS triggers, you don't need to return a Promise to keep the function running, only a result.

Try removing the return statement from this line:

db.collection('Users').where('email', '==', email).get()

Then you just need to ensure that all exit routes (or termination points) end with res.end() or similar, so currently you have 2 termination points:

  }).then(result => {
    // everything went fine... return User UID as a successful result...
    res.write(userId);

    res.status(200).end();
  }).catch(function(error) {
    console.log("doc.update - Error updating user profile in database:", error);

    res.status(500).end();
  });
Grimthorr
  • 6,856
  • 5
  • 41
  • 53
  • I managed to get it to get the work done and exit the function, but it seems that it is exiting the function earlier than it should, while the promises are still pending, which is causing the res.write to shoot and error! – Badih Barakat Nov 16 '17 at 11:14
  • Ah, I may have been overzealous with the editing, you might only need to remove the `return` from the `db.collection('Users')[...]` line. – Grimthorr Nov 16 '17 at 11:15
  • ya, but still... I need it to write somethings... the returned page comes blank and I get the "Error: write after end" because the function exists before the promise of the db.collection...get() returns! how to make it wait till these promises complete before existing the http function? – Badih Barakat Nov 16 '17 at 11:21
  • Oh sorry, my initial thoughts were wrong, see my edited answer. You'll need to drop the `return` from [the promise which will run synchronously](https://stackoverflow.com/a/45590447), and then only use `end()` inside the promises of your final termination/exit points. – Grimthorr Nov 16 '17 at 11:31
  • ok... What I did is remove the return from db.collection...get() to make it synchronous. But where should the end() go? in place of the .then() before the last .catch()? and not in the http function ending? – Badih Barakat Nov 16 '17 at 11:36
  • 1
    You can still use the final `then()`, but it needs to end with `res.end()`, same for the `catch()`, and yes you **don't** want `res.end()` at the bottom of the function. – Grimthorr Nov 16 '17 at 11:40
  • it worked! :D As you have suggested, by removing the return of first promise and placing the end() in the last .then(), it was synchronous and it did display the info expected... Many many thanks... – Badih Barakat Nov 16 '17 at 11:43
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/159151/discussion-between-badih-barakat-and-grimthorr). – Badih Barakat Nov 16 '17 at 11:46