3

I attempting to send out push notifications using cloud functions on Firebase. The end goal is to push a notification to all users each time someone writes to a specific location in the database /model_outputs/real_estate. I've confirmed that the function activates when I manually create a node in the database. Nonetheless, when it runs, I'm seeing this error:

Error: Exactly one of topic, token or condition is required
    at FirebaseMessagingError.FirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:42:28)
    at FirebaseMessagingError.PrefixedFirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:88:28)
    at new FirebaseMessagingError (/srv/node_modules/firebase-admin/lib/utils/error.js:254:16)
    at Object.validateMessage (/srv/node_modules/firebase-admin/lib/messaging/messaging-types.js:46:15)
    at Messaging.send (/srv/node_modules/firebase-admin/lib/messaging/messaging.js:216:27)
    at children.map.child (/srv/index.js:59:37)
    at Array.map (<anonymous>)
    at admin.database.ref.once.then.then.children (/srv/index.js:50:24)
    at <anonymous>
    at process._tickDomainCallback (internal/process/next_tick.js:229:7)
  errorInfo: 
   { code: 'messaging/invalid-payload',
     message: 'Exactly one of topic, token or condition is required' },
  codePrefix: 'messaging'

Here's my function:

exports.sendNoti =
    functions.database.ref('/model_outputs/real_estate')
    .onWrite((change, context) => {

  return admin.database().ref("/fcmTokens").once('value')
      .then(snap => {
          let children = [];
          snap.forEach(child_snap => {
              children.push(child_snap.val());
              console.log('get token'); // build children
          });

          return children;
      })
      .then(children => {
          children.map(child => {
              let message = {
                  notification: {
                      title: "test",
                      body: "body"
                  },
                  token: child.device_token
              }

              admin.messaging().send(message).catch(console.log);
          });

          return null;
      })
      .then( () => { 
          return notif.ref.remove(); // consume send request
      })
      .catch(console.log);

});

I have to admit that I did not write this function, so if it can be improved, I'd greatly appreciate it. My angular and javascript knowledge is fairly basic.

You can see that I'm attempting to pass my user tokens into an array, and then send a message out to all of those users. The tokens are being stored at /fcmTokens in my database as such:

enter image description here

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Kellen
  • 1,072
  • 2
  • 15
  • 32
  • Hi @Kellen as in this similar case [here](https://stackoverflow.com/questions/60170040/firebase-admin-sdk-fcm-error-exactly-one-of-topic-token-or-condition-is-require), it seems that the error is thrown when you are sending an empty Token to FCM. Could you please check and confirm if you are not sending an empty one? This would explain the issue. – gso_gabriel Sep 03 '20 at 07:02
  • @gso_gabriel, I saw that post. How would you recommend verifying that? – Kellen Sep 03 '20 at 12:05
  • @gso_gabriel, so I was able to print out one of the tokens to the console using: `let children = []; snap.forEach(child_snap => { children.push(child_snap.val()); console.log(children[1]); });` It looks like it's coming through. From previous tests, it looks like the foreach loop is cycling twice, and there are only two tokens in the database at the moment. – Kellen Sep 03 '20 at 12:19
  • 1
    And if you print the value of `child.device_token`? Do you have a value? In your test you read the whole array, confirming that you have a token, but not in that specific part. Besides that, do you want to send to specific users in a specific situation right? It seems that you would like to use [Topic](https://firebase.google.com/docs/cloud-messaging/js/topic-messaging) notifications, but it's actually not. – gso_gabriel Sep 03 '20 at 13:32
  • @gso_gabriel, okay, so I logged `child.device_token` to the console right above the `let message` declaration, and it came back as undefined. I totally missed that when I copied this code over. I changed it to `child` and it appears to be logging the tokens now. It worked! – Kellen Sep 03 '20 at 20:41
  • Hi @Kellen that's great! I'm glad to hear thta I could help you. I have posted my comments with the instructions that you used as an answer, due to the fact that it solved your case. Please, consider upvoting / accepting it. :) – gso_gabriel Sep 07 '20 at 05:35

1 Answers1

1

Considering the message error, it seems that the value in the child.device_token variable is not correctly set and is being send without a value. I would recommend you to print it right before sending the value and confirming it.

Once you have done that and confirmed that the issue is there, just change the way you sending the value in token, so you can send the correct value in the message.

gso_gabriel
  • 4,199
  • 1
  • 10
  • 22