artooras
artooras

Reputation: 6795

JavaScript function response and chained promises

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

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?

Upvotes: 0

Views: 146

Answers (2)

lex82
lex82

Reputation: 11297

The problem is that you are always resolving the first promise regardless of whether checking for the user was successful (no such user yet) or not. However, actually you don't ever have to resolve the promise. I suggest you separate the error case like this:

query.first().then(function(user) {
    if (!user) {
        response.success();
    } else {
        addUserToGroup(request.user).then(function() {
            response.error("NOT ERROR - new object was NOT created");
        }, function(error) {
            response.error(error);
        });
    }
});

function addUserToGroup(user) {
    var groupQuery = new Parse.Query("Group");
    groupQuery.equalTo("title", "ssAll");
    groupQuery.equalTo("owner", user);
    return groupQuery.first().then(function(group) {
        group.addUnique("contacts", user);
       return group.save();
    });
}

As you can see, the first promise doesn't ever have to be resolved because the result is not used anyway.

Upvotes: 1

danh
danh

Reputation: 62686

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();
}

Upvotes: 1

Related Questions