Dan.code
Dan.code

Reputation: 441

Variables in Firebase Cloud Function returning "Undefined"

I am playing around with firebase cloud functions and I can't quite get it to run without an error. It keeps saying that userID is undefined.

enter image description here

Here is the structure for the database:

enter image description here

And here is the code I am running for the function:

exports.observeMentions = functions.database.ref('/notifications/{uid}/{notificationId}/mentioned').onCreate((snapshot, context) => {
  var uid = context.params.uid;
  var notificationId = context.params.notificationId;

  return admin.database().ref('/notifications/' + uid + '/' + notificationId + '/uid').once('value', snapshot => {
    var userUID = snapshot.val();

    return admin.database().ref('/users/' + userID).once('value', snapshot => {
      var userThatMentioned = snapshot.val();

      return admin.database().ref('/notifications/' + uid + '/' + notificationId + '/uid').once('value', snapshot => {
        var type = snapshot.val();

         return admin.database().ref('/users/' + uid).once('value', snapshot => {
          var user = snapshot.val();


    if ((type = 3)){
        var payload = {
          notification: {
            body: userThatMentioned.username + ' Mentioned you in a comment'
          }
        };
    }else{
        var payload = {
          notification: {
            body: userThatMentioned.username + ' Mentioned you in their post'
          }
        };
    }


    if (userUID != uid){
        admin.messaging().sendToDevice(user.fcmToken, payload)
          .then(function(response) {
            // Response is a message ID string.
            console.log('Successfully sent message:', response);
          })
          .catch(function(error) {
          console.log('Error sending message:', error);
            }); 
          }
       })
     })
   })
  })
})

Any help would be greatly appreciated!

Thank you

Edit/Attempt:

exports.observeMentioned = functions.database.ref('/notifications/{uid}/{notificationId}/mentioned').onCreate((snapshot, context) => {
    var uid = context.params.uid;
    var notificationId = context.params.notificationId;

    return admin.database().ref('/notifications/' + uid + '/' + notificationId + '/uid').once('value')
        .then(snapshot => {

            var userID = snapshot.val();
            return admin.database().ref('/users/' + userID).once('value');
        })
        .then(snapshot => {

            var userThatMentioned = snapshot.val();
            return admin.database().ref('/notifications/' + uid + '/' + notificationId + '/type').once('value');
        })
    .then(snapshot => {

            var type = snapshot.val();
            return admin.database().ref('/users/' + uid).once('value');
        })
        .then(snapshot => {

            var user = snapshot.val();
    if ((type == 3)){
        var payload = {
          notification: {
            body: userThatMentioned.username + ' Mentioned you in a comment'
          }
        };
    }else{
        var payload = {
          notification: {
            body: userThatMentioned.username + ' Mentioned you in their post'
          }
        };
    }

            return payload
        })  
        .then(snapshot => {
            var payload = snapshot.val();
            return admin.messaging().sendToDevice(user.fcmToken, payload);
        })
        .catch(error => {
            console.log('Error sending message:', error);
        });
});

This keeps getting the error:

Error sending message: ReferenceError: Type is not defined.

I don't understand why type is not defined?

Upvotes: 0

Views: 370

Answers (2)

kamillamagna
kamillamagna

Reputation: 216

I think you have a scoping problem. Your variables user, userThatMentioned, and type are defined in the chained promises but are out of scope by the time you need to use them in later Promises.

I'd recommend declaring user, userThatMentioned, and type up where you're defining uid and notificationId. That way these variables can be accessed anywhere inside your chained Promises. You don't even have to define them at that point, just hoist them up!

Likewise, the declaration of var payload is only valid inside the scope of the if/else statement. I've corrected these scope problems so you can see the difference.

IMPORTANT

In the long-run you should definitely consider installing a linter in the code editor you use. It will catch these problems for you. My go-to, ESLint, underlines the problem and hovering over it will tell you exactly what's going on.

You also absolutely need familiarize yourself with the concept of scope because it's a very important part of writing successful code in any language.

Bonne chance!

const { firestore } = require("firebase")
const { functions } = require("firebase")

const admin = firestore.admin()
exports.observeMentioned = functions.database
  .ref("/notifications/{uid}/{notificationId}/mentioned")
  .onCreate((snapshot, context) => {
    var uid = context.params.uid
    var notificationId = context.params.notificationId
    var user                // HOISTED
    var type                // HOISTED
    var userThatMentioned   // HOISTED

    return admin
      .database()
      .ref("/notifications/" + uid + "/" + notificationId + "/uid")
      .once("value")
      .then((snapshot) => {
        var userID = snapshot.val()
        return admin
          .database()
          .ref("/users/" + userID)
          .once("value")
      })
      .then((snapshot) => {
        userThatMentioned = snapshot.val()
        return admin
          .database()
          .ref("/notifications/" + uid + "/" + notificationId + "/type")
          .once("value")
      })
      .then((snapshot) => {
        type = snapshot.val()
        return admin
          .database()
          .ref("/users/" + uid)
          .once("value")
      })
      .then((snapshot) => {
        user = snapshot.val()
        var payload // HOISTED
        if (type == 3) {
          payload = {
            notification: {
              body: userThatMentioned.username + " Mentioned you in a comment",
            },
          }
        } else {
          payload = {
            notification: {
              body: userThatMentioned.username + " Mentioned you in their post",
            },
          }
        }

        return payload // RETURNING AN OBJECT...
      })
      .then((payload) => { // ... TO THE NEXT PROMISE
        return admin.messaging().sendToDevice(user.fcmToken, payload)
      })
      .catch((error) => {
        console.log("Error sending message:", error)
      })
  })

Upvotes: 1

Renaud Tarnec
Renaud Tarnec

Reputation: 83048

There are several parts of your code that need to be corrected:

  1. You define a variable as var userUID = snapshot.val(); and in the next line you use userID (return admin.database().ref('/users/' + userID)) => Hence the "Undefined" error.
  2. In a if check you should use the equality operator (== or ===)
  3. Instead of using the callback version of the once() method, use the Promise version and chain the promises returned by the once() method calls with then(), as shown below.
  4. Don't forget to return the promise returned by the sendToDevice() method. See the doc for more details on how important this last point is.

exports.observeMentions = functions.database.ref('/notifications/{uid}/{notificationId}/mentioned').onCreate((snapshot, context) => {
    var uid = context.params.uid;
    var notificationId = context.params.notificationId;

    return admin.database().ref('/notifications/' + uid + '/' + notificationId + '/uid').once('value')
        .then(snapshot => {

            var userID = snapshot.val();
            return admin.database().ref('/users/' + userID).once('value');
        })
        .then(snapshot => {

            var userThatMentioned = snapshot.val();
            return admin.database().ref('/notifications/' + uid + '/' + notificationId + '/uid').once('value');
        })
        .then(snapshot => {
            // ....
            // return a Promise
        })
        .then(snapshot => {
            // ....
            return admin.messaging().sendToDevice(....);
        })
        .catch(error => {
            // ...
        });
});

Upvotes: 1

Related Questions