0

I made a cloud function (using google :() that sends push notifications when adding a document in firestore, but I have the error that you see in the image and the notifications do not arrive but I do not understand what may be wrong in my code, can someone help me?

enter image description here

mi code:

exports.cambiaColeccion = functions.firestore
  .document('sendMessage/{docId}')
  .onCreate((snap, context) => { 
    const nuevoMensaje= snap.data();
    
    console.log('id', nuevoMensaje);
    console.log('titulo', nuevoMensaje.titulo)
    enviaMensage();   
  });

  async function enviaMensage() {
    console.log('en enviaMensaje');
    const payload ={
        notification: {
            title: "Titulo del mensaje",
            body: "Texto del mensaje ... ",
            sound: 'default',
            badge: '1',
    }

    // Get the list of device tokens.
    const allTokens = await admin.firestore().collection('FCMTokens').get();
    const tokens = [];
    allTokens.forEach((tokenDoc) => {
        tokens.push(tokenDoc.id);
    });

    if (tokens.length > 0) {
        // Send notifications to all tokens.
        const response = await admin.messaging().sendToDevice(tokens, payload);        
        //await cleanupTokens(response, tokens);
        console.log('Notifications have been sent and tokens cleaned up.');
    }
    return true
}

// Cleans up the tokens that are no longer valid.
function cleanupTokens(response, tokens) {
    // For each notification we check if there was an error.
    const tokensDelete = [];
    response.results.forEach((result, index) => {
        const error = result.error;
        if (error) {
            console.error('Failure sending notification to', tokens[index], error);
            // Cleanup the tokens who are not registered anymore.
            if (error.code === 'messaging/invalid-registration-token' ||
                error.code === 'messaging/registration-token-not-registered') {
                const deleteTask = admin.firestore().collection('FCMTokens').doc(tokens[index]).delete();
                tokensDelete.push(deleteTask);
            }
        }
    });
    return Promise.all(tokensDelete);
}

1 Answers1

0

You need to return a Promise in your Cloud Function, in such a way the Cloud Functions instance running your function does not shut down before your function successfully reaches its terminating condition or state. See the doc for more details.

In your case you are not returning anything in the Cloud Function itself. Since async functions always return a Promise, you can adapt your code as follows:

exports.cambiaColeccion = functions.firestore
    .document('sendMessage/{docId}')
    .onCreate((snap, context) => {
        const nuevoMensaje = snap.data();

        console.log('id', nuevoMensaje);
        console.log('titulo', nuevoMensaje.titulo)
        return enviaMensage();
    });

async function enviaMensage() {
    console.log('en enviaMensaje');
    const payload = {
        notification: {
            title: "Titulo del mensaje",
            body: "Texto del mensaje ... ",
            sound: 'default',
            badge: '1',
        }
    }

    // Get the list of device tokens.
    const allTokens = await admin.firestore().collection('FCMTokens').get();
    if (allTokens.size > 0) {  // allTokens is a QuerySnapshot
        const tokens = allTokens.docs.map(tokenDoc => tokenDoc.id);
        await admin.messaging().sendToDevice(tokens, payload);
    }

}

Note that it would be good to add some try/catch block in order to capture and debug potential errors.


Update following your comment on the cleanupTokens function.

Your cleanupTokens function is correct. It is asynchronous since it returns a Promise (returned by Promise.all(tokensDelete);).

The way you call it should work correctly:

    const allTokens = await admin.firestore().collection('FCMTokens').get();
    if (allTokens.size > 0) {  // allTokens is a QuerySnapshot
        const tokens = allTokens.docs.map(tokenDoc => tokenDoc.id);
        const response = await admin.messaging().sendToDevice(tokens, payload);        
        await cleanupTokens(response, tokens);
    }
Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
  • Thank you very much It worked, I still confused with promises and async/await :(, now I need to call the function: cleanupTokens(response, tokens) to delete the wrong tokens in the collection, how can I do that in the best way?, thanks a lot again – Marcos Galaviz Jul 19 '21 at 16:41
  • Hi @MarcosGalaviz, I've adapted the answer. Please upvote and accept the answer if it gave you the solution to your problem. Thx. – Renaud Tarnec Jul 20 '21 at 16:42
  • Thanks for your help, but I got this errror: cambiaColeccion TypeError: Cannot read property 'results' of undefined at cleanupTokens , but i solved like this: what do you think? await admin.messaging().sendToDevice(tokens, payload) .then(response => { cleanupTokens(response, tokens); console.log('Notifications have been sent and tokens cleaned up.'); }) – Marcos Galaviz Jul 23 '21 at 17:30