0
votes

I am trying to display a user with his phone contacts that are also users to my app

I am storing the user phone contacts on Firestore "contacts" collection.

On each document i create on android, i am leaving "fuid" (friend UID) field as null. Using cloud functions, each time a new document is created i am checking if his "paPho" (parsed phone number e.g. +972123455) matching an existing user phone number. If yes, i will place his uid to the "fuid" matching document.

On android, i will display all user contact which fuid is not null and uid matching

Since each user might have more than 500 contacts (all added in very short time) i am using Blaze plan

It is working quite nicely but, although no error is found on log, it seems the onCreate is missing sometimes.

The reason i think it is missing since if i re-run the cloud function under same contact list couple of times, sometimes the missed document appears.

It might be relevant that these sometimes-missing contacts are close by name and having same phone number

const functions = require('firebase-functions');

exports.attachUserToNewContact = functions.firestore
    .document('contacts/{contactId}').onCreate((snap,contex) => {

        admin.auth().getUserByPhoneNumber(snap.data().paPho)
            .then(userRecord => {
                if (userRecord.uid) {
                    console.log(`userRecord phone ${snap.data().paPho} matching contact ${userRecord.uid}`);
                    admin.firestore().collection('contacts')
                    .doc(snap.data().id).update({fuid:userRecord.uid});
                }

                return 0;
            })
            .catch(error => {
                //There is no user record corresponding to the provided identifier
            });

        return 0;
    });
1

1 Answers

2
votes

You are not returning the promises returned by the asynchronous methods (getUserByPhoneNumber() and update()), potentially generating some "erratic" behavior of the Cloud Function.

As you will see in the three videos about "JavaScript Promises" from the official Firebase video series (https://firebase.google.com/docs/functions/video-series/) you MUST return a Promise or a value in a background triggered Cloud Function, to indicate to the platform that it has completed, and to avoid it is terminated before the asynchronous operations are done.

Concretely, it happens sometimes that your Cloud Function is terminated before the asynchronous operations are completed, because the return 0; at the end indicates to the Cloud Function platform that it can terminate the Function. The other times, the Cloud Function platform does not terminate the Function immediately and the asynchronous operations can be completed.

By modifying your code as follows, you will avoid this "erratic" behavior :

const functions = require('firebase-functions');

exports.attachUserToNewContact = functions.firestore
    .document('contacts/{contactId}').onCreate((snap,contex) => {

        return admin.auth().getUserByPhoneNumber(snap.data().paPho)
            .then(userRecord => {
                if (userRecord.uid) {
                    console.log(`userRecord phone ${snap.data().paPho} matching contact ${userRecord.uid}`);
                    return admin.firestore().collection('contacts')
                    .doc(snap.data().id).update({fuid:userRecord.uid});
                } else {
                    return null;
                } 
            })
            .catch(error => {
                //There is no user record corresponding to the provided identifier
                return null;
            });

    });

BTW, if you don’t do anything else than returning null in the catch block, you can totally remove it.