2

I'm using Parse to represent the state of a beer keg (among other things). I'd like to check the user's notifications, stored in a "Notifications" table, to see if they'd like to receive a notification when the keg is filled.

I have all of the logic for setting the user's notification settings as well as sending notifications in cloud/notifications.js. All of the logic for updating the keg is in cloud/beer.js. I created an exported function called "sendKegRefillNotification" which performs a query.find() on the Notifications table and gets called from beer.js.

The problem is that it doesn't seem to be executing query.find() when I call the function from beer.js, however when I call the same function from a job within notifications.js, it works just fine.

main.js:

require("cloud/beer.js");
require("cloud/notifications.js");

beer.js:

var notify = require("cloud/notifications.js");

var Keg = Parse.Object.extend("Keg");

var fillKeg = function(beerName) {
    var promise = new Parse.Promise();

    var keg = new Keg();
    keg.set("beerName", beerName)
    keg.set("kickedReports", []);
    keg.save(null, { useMasterKey: true }).then(function(keg) {
        console.log("Keg updated to " + beerName + ".");
        promise.resolve(keg);
        notify.sendKegRefillNotification(keg);
    },
    function(keg, error) {
        promise.reject(error);
    });

    return promise;
}

Parse.Cloud.define("beerFillKeg", function(request, response) {
    var beerName = request.params.name;
    if (!beerName) {
        response.error("No beer was specified.");
        return;
    }
    if (!util.isUserAdmin(request.user)) {
        response.error("User does not have permission to update the keg.");
        return;
    }
    fillKeg(beerName).then(function(keg) {
        kegResponse(keg).then(function(result) {
            response.success(result);
        });
    },
    function(error) {
        response.error(error);
    });
});

function kegResponse(keg) {
    var promise = new Parse.Promise();

    var result = {
        id: keg.id,
        beer: {
            name: keg.get("beerName")
        },
        filled: keg.createdAt,
        kickedReports: []
    };

    var kickedReports = keg.get("kickedReports");
    if (!kickedReports || kickedReports.length == 0) {
        promise.resolve(result);
    } else {
        util.findUsers(kickedReports).then(function(users) {
            result.kickedReports = util.infoForUsers(users);
            promise.resolve(result);
        }, function(users, error) {
            console.log(error);
            promise.resolve(result);
        });
    }
    return promise;
}

notifications.js:

var Keg = Parse.Object.extend("Keg");
var Notification = Parse.Object.extend("Notifications");

exports.sendKegRefillNotification = function(keg) {
    var beerName = keg.get("beerName");
    console.log("Sending notifications that keg is refilled to '" + beerName + "'.");

    var promise = new Parse.Promise();

    var query = new Parse.Query(Notification);
    query.include("user");
    query.equalTo("keg_filled", true);
    query.find({ useMasterKey: true }).then(function(notifications) {
        console.log("Found notifications!");
        promise.resolve("Found notifications!");
    },
    function(notifications, error) {
        console.error("No notifications");
        console.error(error);
        promise.reject(error);
    });

    return promise;
}

Parse.Cloud.job("beerSendRefillNotification", function(request, status) {
    var query = new Parse.Query(Keg);
    query.descending("createdAt");
    query.first().then(function(keg) {
        if (!keg) {
            status.error("No keg");
            return;
        }

        exports.sendKegRefillNotification(keg);
    },
    function(keg, error) {
        response.error(error);
    });
});

When I run the job "beerSendRefillNotification" from the Parse dashboard, I can tell that query.find() is getting called because it prints "Found notifications!":

E2015-02-23T06:59:49.006Z]v1564 Ran job beerSendRefillNotification with:
  Input: {}
  Result: success/error was not called
I2015-02-23T06:59:49.055Z]false
I2015-02-23T06:59:49.190Z]Sending notifications that keg is refilled to 'test'.
I2015-02-23T06:59:49.243Z]Found notifications!

However, when I call the cloud function "beerFillKeg", it isn't because it's not printing "Found notifications!" or "No notifications":

I2015-02-23T07:00:17.414Z]v1564 Ran cloud function beerFillKeg for user HKePOEWZvC with:
  Input: {"name":"Duff"}
  Result: {"beer":{"name":"Duff"},"filled":{"__type":"Date","iso":"2015-02-23T07:00:17.485Z"},"id":"olLXh0F54E","kickedReports":[]}
I2015-02-23T07:00:17.438Z]false
I2015-02-23T07:00:17.523Z]Keg updated to Duff.
I2015-02-23T07:00:17.525Z]Sending notifications that keg is refilled to 'Duff'.
Mel
  • 959
  • 5
  • 19

2 Answers2

2

I finally understand it. In sendKegRefillNotification, you're calling query.find({...}), then returning an object. That find is asynchronous, and you're doing nothing to wait for the result. I think you need to return the find function call, rather than an object you set within that method.

In other words, you're running along, leaving some async running code behind you.

Edit: I understand what you tried to do. It sort of makes sense. You defined a promise, and thought the caller would wait for the promise. The problem is, the promise is defined in an asynchronous block. It doesn't yet have any meaning at the moment the caller gets it.

piojo
  • 6,351
  • 1
  • 26
  • 36
  • 1
    Yes, I understand now. It was a mistake to try to create a promise and set it but rather I should chain the promises together. That fixed it, thank you! – Mel Feb 23 '15 at 10:33
0

It looks like Parse doesn't allow you to run a query from inside a callback from save(). When I moved "notify.sendKegRefillNotification(keg);" to outside of the callback, it worked.

var fillKeg = function(beerName) {
    var promise = new Parse.Promise();

    var keg = new Keg();
    keg.set("beerName", beerName)
    keg.set("kickedReports", []);
    keg.save(null, { useMasterKey: true }).then(function(keg) {
        console.log("Keg updated to " + beerName + ".");
        console.log("Send notifications.");
        promise.resolve(keg);
    },
    function(keg, error) {
        promise.reject(error);
    });

    notify.sendKegRefillNotification(keg); // Now this works

    return promise;
}

Can anyone shed some more light on why this worked?

Mel
  • 959
  • 5
  • 19
  • That answer smells wrong to me. A limitation of that sort would be completely going against the Parse spirit. (I'm using queries in my own save callbacks, but the logic is a rare case, and I can't actually be positive it works.) – piojo Feb 23 '15 at 08:00