1

I have learned promise chaining with error handling to my satisfaction as per below first code section. But I am unable to get the following principle working with my Parse Cloud Code. My Cloud code works well but I have monster functions I need to refactor and organize.

My goal here

Is to have smaller cloud functions I can call without repeating code, as such, I want to have a function to trip an error if a user already is part of a group Parse.Role, otherwise I will add user to Role. Of course if error trips I want to stop the rest of the execution.

Please see second code snippets. I would appreciate some professional input.

function addOne(number) {
return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(number += 1);
        }, 1000);
    });
}

/// Throws error unless number is 5
var test = addOne(5);

test.then(function(currentValue) {
    currentValue ++;
    if (currentValue > 7) {
    return Promise.reject(Error("Initial value too high"));
    } else {
    return Promise.resolve(currentValue);
    }
    })
    
    .then(function(value) {
    value -= 2;
    if (value < 5) {
    return Promise.reject(Error("Initial value too low"));
    } else {
    return Promise.resolve(value);
    }
    })

    .then(function(finalValue) {
    finalValue ++;
    if (finalValue != 6) {
    return Promise.reject(Error("Not the sweet spot"));
    } else {
    console.log("You choose RIGHT!" + String(Promise.resolve("TEST")));
    }
    });
/// THIS IS ONE VERSION - NOT WORKING PROPERLY

/// Check if user is part of group
Parse.Cloud.define("mmDoesUserHaveGroup", async function(request) {
  let userID = request.params.user;
  // Search user class by user id parameter
  let userQuery = await new Parse.Query(Parse.User).equalTo('objectId', userID).find({useMasterKey: true});
  let userObject = userQuery[0];
  // Check for user in all Roles
  const query = await new Parse.Query(Parse.Role).equalTo('users', userObject).find({ useMasterKey: true })
  .then(function() {
    if (query.length != 0) {
      return Promise.reject(Error('User is part of group already'));
  } else {
    return Promise.resolve();
  }
  });
});

Parse.Cloud.define("ccCreateGroup", async function(request) {
  let userID = request.user.id;
  const doesUserHaveGroup = Parse.Cloud.run("mmDoesUserHaveGroup", {user: userID});
    doesUserHaveGroup
    .then(function() {
      return Promise.resolve("Create group action next");
    })
    .catch(error => Error(error.message));
});


/// ANOTHER VERSION - NOT WORKING EITHER

/// Returns false if user is not part of group or does not own group else error
Parse.Cloud.define("mmDoesUserHaveGroup", async function(request) {
  let userID = request.params.user;
  // Search user class by user id parameter
  let userQuery = await new Parse.Query(Parse.User).equalTo('objectId', userID).find({useMasterKey: true});
  let userObject = userQuery[0];
  // Check for user in all Roles
  const query = await new Parse.Query(Parse.Role).equalTo('users', userObject).find({ useMasterKey: true })
  .then(function() {
    if (query.length != 0) {
      return true;
  } else {
    return false;
  }
  });
});

Parse.Cloud.define("ccCreateGroup", async function(request) {
  let userID = request.user.id;
  const doesUserHaveGroup = Parse.Cloud.run("mmDoesUserHaveGroup", {user: userID});
    doesUserHaveGroup
    .then(function(groupStatus) {
      if (groupStatus) {
        return Promise.reject(Error("User already has group"));
      } else {
        return Promise.resolve("Create group action next");
      }
    });
});
RobbB
  • 1,214
  • 11
  • 39

3 Answers3

1

Even though I have no experience with Parse, judging only from syntax, I suppose that your problem lies is this section here:.

    const query = await new Parse.Query(Parse.Role)
      .equalTo('users', userObject)
      .find({ useMasterKey: true })
      .then(function() {
        if (query.length != 0) {
          return true;
        } else {
          return false;
        }
      )

Notice that as a general advice, it's not a good practice to mix Promise syntax with await unless you really know what you're doing.

In your case, inside the then part you are expecting that query is already set, which it won't be, since await is still, err, awaiting for the Promise to resolve (or at least that's what it seems to me).

What you probably want is something like this:

    const query = await new Parse.Query(Parse.Role)
      .equalTo('users', userObject)
      .find({ useMasterKey: true });
    return query? query.length > 0: false
Branco Medeiros
  • 729
  • 4
  • 10
  • Not too sure if the exact problem was there, however I got it fixed and totally refactored, I learned more about promises from Parse: https://docs.parseplatform.org/js/guide/#error-handling-with-promises Very good to know I can do that. Thanks for the input. Also regarding async await inside my Promise, I suppose I have no choice since most Parse actions are asynchronous. I did try in the past chaining promises but got into trouble. For now everything works, being very new I'm assuming that it all will continue working error free. If an error pops up I'll handle it ;) – RobbB Dec 01 '21 at 04:41
1

there are two things we can start with.

First

you need the newto create errs

// creates a new instance of an error
new Error();

Second

from

est.then(function(currentValue) {
    currentValue ++;
...

there to bottom there is no more promises as all is sequeensial

Best Practice Tip

never use things like

new  Promise(async ()=>{...})

//OR

const some = async () => await asyncThing()

// or 

const some = async () =>  {
  const result = await asyncStuff();
  return result
}

another problem in your code is that you use async with/out await. Eslint says it is an anti parttern

Parse.Cloud.define("ccCreateGroup", async function(request) {
  let userID = request.user.id;
  const doesUserHaveGroup = Parse.Cloud.run("mmDoesUserHaveGroup", {user: userID});
    doesUserHaveGroup
    .then(function(groupStatus) {
      if (groupStatus) {
        return Promise.reject(Error("User already has group"));
      } else {
        return Promise.resolve("Create group action next");
      }
    });
});

a better way of writting the method/function from avobe would be like

Parse.Cloud.define("ccCreateGroup", function({user: { id }}) {
 return new Promise ((resolve, reject)=>Parse.Cloud.run("mmDoesUserHaveGroup", {user: userID}).then(function(groupStatus) {
      if (groupStatus) {
         throws new Error("the err");
      }

        return resolve("Create group action next");
    });
}))

I might be missing some () or {}

Ernesto
  • 3,944
  • 1
  • 14
  • 29
  • This is very informative. I can apply much I've learned here. I have to say my pain right now is "only learning to do stuff" and not "learning the language concepts". I am not fighting you on this one (you are the pro and I'm the beginner) however I do not understand why I need async/await for my nested function "mmDoesUserHaveGroup". My cloud code "ccCreateGroup" does not work otherwise... I am under the understanding that some Parse actions only work with async/await...??? I will update my newly working code in my last answer. – RobbB Dec 01 '21 at 05:48
0

I ended up refactoring and learning more about proper promise chaining with Parse promises here

Here is my refactored code, might help someone else thats new at Javascript, or not. Better read what the pros had to say in other answers ;)

/// Check if user is part of group role or if user owns group
/// Returns false if user is not part of group or does not own group else error
async function mmDoesUserHaveGroup(userID) {
  return new Promise(async function (resolve, reject) {
    // Search user class by user id parameter
    let userQuery = await new Parse.Query(Parse.User).equalTo('objectId', userID).find({ useMasterKey: true });
    let userObject = userQuery[0];
    // Check for user in all Roles
    const query = await new Parse.Query(Parse.Role).equalTo('users', userObject).find({ useMasterKey: true });
    resolve(query);
  });
}

/// First checks if user already has group
/// If user already has group then returns error and does not continue
/// If user does not have group, creates role, adds self to role
/// Then creates sync status objects with user set to true
Parse.Cloud.define("ccCreateGroup", async function (request) {
  let user = request.user.id;
  await mmDoesUserHaveGroup(user)
    .then(
      // No Parse error - mini method completed successfully
      function (groupQueryCheck) {
        // Check group status, if has group return error
        if (groupQueryCheck.length != 0) {
          return Parse.Promise.error("User is already part of a group");
        }
      },
      function (groupQueryError) {
        return Parse.Promise.error(groupQueryError);
      })
    .then(
      function () {
        let acl = new Parse.ACL();
        acl.setReadAccess(request.user, true);
        acl.setWriteAccess(request.user, true);
        let adminRole = new Parse.Role("Group_" + user, acl);
        return adminRole.save(null, { useMasterKey: true });
      })
    .then(
      function (role) {
        role.getUsers().add(request.user);
        return role.save(null, { useMasterKey: true })
      })
    .then(
      function () {
        // Create Sync Status objects
        let masterSyncStatus = { user: user, syncStatus: true };
        let newUserSyncStatus = { user: user, syncStatus: true };
        let masterArray = [];
        let newUserArray = [];
        masterArray.push(masterSyncStatus);
        newUserArray.push(newUserSyncStatus);
        // Initialize Sync Status class object by groupID
        let SyncStatus = Parse.Object.extend("SyncStatus");
        let syncStatus = new SyncStatus();
        syncStatus.set("groupName", "Group_" + user);
        syncStatus.set("masterSyncStatus", masterArray);
        syncStatus.set("newUserSyncStatus", newUserArray);
        return syncStatus.save(null, { useMasterKey: true });
      },
       function (error) {
        return Parse.Promise.error(error);
      });
});
RobbB
  • 1,214
  • 11
  • 39