4

I have written a firebase function to send notification whenever a like occurs in my android app. The notification functionality works good most of the times but sometimes does not work.

I receive this error always ( whether it is working or not):

Function returned undefined, expected Promise or value

Here's the code of my like function:

exports.sendactorLikeNotification = functions.database.ref('/Likes/{post_id}/{user_id}')
.onWrite(event => {


   if (event.data.exists()) 
    {

      const message = event.data.val();
      const userUid = event.params.user_id;

      const ownerUid = message.owner_id;
      console.log("Owner id", ownerUid);
      const userPic = message.thumb_image;
      const userName = message.name;
      const post_key = event.params.post_id;
      const timestamp = admin.database.ServerValue.TIMESTAMP;

      if(ownerUid == userUid){return null;}

      const Promise1= admin.database().ref(`/notifs/${ownerUid}`).push({
        thumb_image: userPic,
        name: userName,
        user_id: userUid,
        post_id: post_key,
        text: "liked your post",
        type: "Like",
        read: "false",
        time: timestamp
     });

      const Promise2=admin.database().ref(`/Users/${ownerUid}/device_token`).once('value');

      const Promise3= Promise2.then(function(snapshot) {
          const getrealDeviceTokensPromise = snapshot.val();
          console.log("Device Token", getrealDeviceTokensPromise);

          // Notification details.
          const payload = {
                            notification: {
                                              title: 'Appname',
                                              body:  userName + ' has liked your post.',
                                              icon: "default",
                                              sound: "default",
                                              click_action: "OPEN_ACTIVITY_1"
                                          }
                        };

          const Promise4= admin.messaging().sendToDevice(getrealDeviceTokensPromise, payload)
                   .then(function (response) {
                       console.log("Successfully sent message:", response);
                       return Promise.all([Promise1,Promise3,Promise4]);
                   })
                   .catch(function (error) {
                       console.log("Error sending message:", error);
                       return null;
                   });

      }, function(error) {
      // The Promise was rejected.
          console.error(error);
          return null;
      });

    }

    else
    {
      return null;
    } 


});

I don't understand where I am going wrong. Please help!

Maverick7
  • 1,087
  • 12
  • 22
  • A couple observations. 1) Your code is difficult to follow the way it's formatted here. 2) You're using a really old version of the firebase-functions SDK - consider upgrading to the latest 1.x version and migrate your code to the new APIs. – Doug Stevenson Jun 26 '18 at 21:45
  • Hey, sorry for the shabby formatting. I have improved it in my edit. – Maverick7 Jun 26 '18 at 21:58
  • I don't think you have all the code in there. Last time you had a call to Promise.all. – Doug Stevenson Jun 26 '18 at 22:01
  • Yes, I missed it. Here's the the entire code now. Kindly look into it – Maverick7 Jun 26 '18 at 22:03

3 Answers3

2
exports.sendactorLikeNotification = functions.database.ref('/Likes/{post_id}/{user_id}').onWrite(event => {
  if (event.data.exists()) {

  const promises=[];



  const message = event.data.val();
  const userUid = event.params.user_id;

  const ownerUid = message.owner_id;
  console.log("Owner id", ownerUid);
  const userPic = message.thumb_image;
  const userName = message.name;
  const post_key = event.params.post_id;
  const timestamp = admin.database.ServerValue.TIMESTAMP;

  if(ownerUid == userUid){return null;}

    const a1=admin.database().ref(`/notifs/${ownerUid}`).push({
    thumb_image: userPic,
    name: userName,
    user_id: userUid,
    post_id: post_key,
    text: "liked your post",
    type: "Like",
    read: "false",
    time: timestamp
 });

  promises.push(a1);




   const a2= admin.database().ref(`/Users/${ownerUid}/device_token`).once('value').then(function(snapshot) {






      const getrealDeviceTokensPromise = snapshot.val();

      console.log("Device Token", getrealDeviceTokensPromise);

      // Notification details.
      const payload = {
        notification: {
          title: 'Appname',
          body:  userName + ' has liked your post.',
          icon: "default",
          sound: "default",
          click_action: "OPEN_ACTIVITY_1"
        }
      };

      const a3=admin.messaging().sendToDevice(getrealDeviceTokensPromise, payload)
               .then(function (response) {
                   console.log("Successfully sent message:", response);
               })
               .catch(function (error) {
                   console.log("Error sending message:", error);
               });


               promises.push(a3);


  }, function(error) {

      console.error(error);
  });
   promises.push(a1);
  return Promise.all(promises);

}

else
{
  return null;
}

});

This code solved the problem for me!

Maverick7
  • 1,087
  • 12
  • 22
1

You're returning undefined when:

  1. event.data.exists() returns false
  2. ownerUid == userUid

You're also not dealing with the promise returned by sendToDevice().then().catch(). The function needs to wait until that work is done before terminating.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • If event.data.exists() returns false or ownerUid == userUid happens, then the notification will not be sent. The problem here is the notification is sent and then I get the error Function returned undefined, expected Promise or value. I have edited the code in the question to include a Promise4 now too which takes care of sendToDevice().then(), still it does not work. – Maverick7 Jun 26 '18 at 22:22
  • 1
    But in those cases, your function will return undefined, and you'll see that message you're complaining about. Return null or a promise instead. The point is that you need to make sure you're returning something in every branch of your code, not just the primary success case. – Doug Stevenson Jun 26 '18 at 22:23
  • As suggested by you, I returned null in both the cases 1) event.data.exists() returns false and 2) ownerUid == userUid. I am still getting the same error. My first comment was to emphasize on the fact that when I am testing the code, I make sure the success case occurs which in turn is giving me the aforementioned error. – Maverick7 Jun 26 '18 at 22:27
  • I have edited the code in the question to reflect the changes too. Please look into it. – Maverick7 Jun 26 '18 at 22:38
0

Please test the following changes and let me know, also I recommend updating the firebase-functions SDK:

exports.sendactorLikeNotification = functions.database.ref('/Likes/{post_id}/{user_id}')
  .onWrite(event => {
    if (event.data.exists()) {
      const promises = [];
      const message = event.data.val();
      const userUid = event.params.user_id;
      const ownerUid = message.owner_id;
      const userPic = message.thumb_image;
      const userName = message.name;
      const post_key = event.params.post_id;
      const timestamp = admin.database.ServerValue.TIMESTAMP;
      if (ownerUid === userUid) return null;
      return Promise.all([admin.database().ref(`/Users/${ownerUid}/device_token`).once('value')]).then(r => {
        const cO = r[0];
        const aP = admin.database().ref(`/notifs/${ownerUid}`).push({
          thumb_image: userPic,
          name: userName,
          user_id: userUid,
          post_id: post_key,
          text: "liked your post",
          type: "Like",
          read: "false",
          time: timestamp
        });
        promises.push(aP);
        const payload = {
          notification: {
            title: 'Appname',
            body: userName + ' has liked your post.',
            icon: "default",
            sound: "default",
            click_action: "OPEN_ACTIVITY_1"
          }
        };
        const tokensList = Object.keys(cO.val());
        promises.push(admin.messaging().sendToDevice(tokensList, payload));
        return Promise.all(promises);
      });
    }
    return null;
  });
Diego P
  • 1,728
  • 13
  • 28
  • Your answer has helped me to derive the correct answer. I wasn't aware about the promises.push function. Your function returns a Promise and the error goes away but the notification is not sent. Thanks anyways. – Maverick7 Jun 27 '18 at 22:13
  • Great, if device_token is a string with only one token use: `promises.push(admin.messaging().sendToDevice(cO.val(), payload));` – Diego P Jun 27 '18 at 22:19