Search code examples
javascriptparse-platformpromiseparse-cloud-codebefore-save

JavaScript function response and chained promises


I have a Parse CloudCode beforeSave function, which roughly does the following:

  • Runs a query to check if there's a duplicate user to the one being saved;
  • If there is NO duplicate, I call a response.success(), which means the code can go ahead and allow saving the new user;
  • If, however, there is a duplicate, I want to take the existing user, fetch a group object and add the existing user to the group.

For this purpose, I am using chained promises to make the code cleaner. The code is below:

Parse.Cloud.beforeSave("Contact", function(request, response) {

    var facebookID = request.object.get("facebookID");
    var email = request.object.get("email");

    var queryFb;
    if (facebookID) {
        queryFb = new Parse.Query(Parse.User);
        queryFb.equalTo("facebookID", facebookID);
    }

    var queryEmail;
    if (email) {
        queryEmail = new Parse.Query(Parse.User);
        queryEmail.equalTo("email", email);
    }

    var query;
    if (facebookID && email) {
        query = new Parse.Query.or(queryFb, queryEmail);
    } else if (facebookID) {
        query = queryFb;
    } else {
        query = queryEmail;
    }

    var user;

    query.first().then(function(user) {

        if (!user) {

            response.success();

        } else {

            var groupQuery = new Parse.Query("Group");
            groupQuery.equalTo("title", "ssAll");
            groupQuery.equalTo("owner", request.user);
            return groupQuery.first();
        }

    }).then(function(group) {

        group.addUnique("contacts", user);
        return group.save();

    }).then(function(success) {

        response.error("NOT ERROR - new object was NOT created");

    }, function(error) {

        response.error(error);
    });
});

In my test case, the query returns !user, so the response.success() message is called - all good. However, this response seems to then travel down the promise chain, which is intended for the case when the query returns a user object. And so, my function terminates with an error on line group.addUnique("contacts", user); because, obviously, the group object is undefined.

How do I work around this issue?


Solution

  • The code needed a few improvements. The key improvement was to provide consistent starting conditions to the second promise's resolution (the second then block). The OP code called response.success() in the case where there was no existing user. This is fine, except the execution still falls through to the next resolution, in one case with an undefined group parameter.

    The new code fixes that by returning either the existingUser (after the group has been updated) or null. Null tells the next promise resolution to call success() and allow the save to proceed, otherwise, block the save.

    Also note, it is a mistake for the first block's user parameter to conflict with the var user in the enclosing scope. I tried to use variable naming below to highlight the two different types of users the code considers...

    Parse.Cloud.beforeSave("Contact", function(request, response) {
        var facebookID = request.object.get("facebookID");
        var email = request.object.get("email");
        // moved into a function so we can test and deal with it tersely
        findUserByEmailOrFB(email, facebookID).then(function(existingUser) {
            return (existingUser)? addContactToGroupOwnedBy(request.user, existingUser) : null;
        }).then(function(existingUser) {
            if (existingUser) {
                response.error("NOT ERROR - new object was NOT created");
            } else {
                response.success();
            }
        }, function(error) {
            response.error(error);
        });
    });
    
    // find the group owned by ownerUser, add contactUser to its contacts return a promise fulfilled as contactUser
    function addContactToGroupOwnedBy(ownerUser, contactUser) {
        var groupQuery = new Parse.Query("Group");
        groupQuery.equalTo("title", "ssAll");
        groupQuery.equalTo("owner", ownerUser);
        return groupQuery.first().then(function(group) {
            group.addUnique("contacts", contactUser);
            return group.save().then(function() { return contactUser; });
        });
    }
    
    function findUserByEmailOrFB(email, facebookID) {
        var queryFb;
        if (facebookID) {
            queryFb = new Parse.Query(Parse.User);
            queryFb.equalTo("facebookID", facebookID);
        }
        var queryEmail;
        if (email) {
            queryEmail = new Parse.Query(Parse.User);
            queryEmail.equalTo("email", email);
        }
        var query;
        if (facebookID && email) {
            query = new Parse.Query.or(queryFb, queryEmail);
        } else if (facebookID) {
            query = queryFb;
        } else {
            query = queryEmail;
        }
        return query.first();
    }