Skip to content

Instantly share code, notes, and snippets.

@drewlarsen
Last active July 23, 2017 11:51
Show Gist options
  • Save drewlarsen/71520e2c272987c0f907 to your computer and use it in GitHub Desktop.
Save drewlarsen/71520e2c272987c0f907 to your computer and use it in GitHub Desktop.
A pattern for a Parse.com Cloud Code function using Promises, including validations, fetches, saves.
// A cloud Code function that is called from iOS, Android and web
// each time an athlete logs into the app
// Updates the athlete and creates an entry in the "TB_Login" class
// NOTE: isDefined is a utility function defined elsewhere
Parse.Cloud.define("athleteDidLogin", function(request, response) {
// req'd params
var athId = request.params.athId;
// opt'l params
var platform = request.params.platform;
var version = request.params.version;
// validate function params
var validations = function() {
// opt'l
if (!isDefined(platform)) { platform = 'unknown'; }
if (!isDefined(version)) { version = 'unknown'; }
// req'd
var errMsg;
if (!isDefined(athId)) { errMsg = 'Missing athlete id.'; }
return (isDefined(errMsg)) ? Parse.Promise.error(new Error(errMsg)) : Parse.Promise.as();
};
// validations
validations()
// fetch the athlete
.then(function() {
var athQuery = new Parse.Query('Athlete');
return athQuery.get(athId);
})
// update the athlete
.then(function(athlete) {
// can this athlete login?
if (!athlete.get('isLoginEnabled')) {
// nope! return an error
return Parse.Promise.error(new Error('This athlete is not allowed to login.'));
}
athlete.set("lastLogin", new Date());
athlete.increment("loginCount", 1);
return athlete.save(null, { useMasterKey: true });
})
// create the new 'login' entry
.then(function(athlete) {
// NOTE: models.TBLogin() is defined elsewhere
// it extends Parse.Object
newLogin = new models.TB_Login();
newLogin.set("athlete", athlete);
newLogin.set("platform", platform);
newLogin.set("version", version);
// ACL
var acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(false);
newLogin.setACL(acl);
return newLogin.save(null, { useMasterKey: true });
})
// response
.then(
function() {
response.success({});
},
function(error) {
response.error(error.message);
}
);
});
@jeffhuangtw
Copy link

The Promises pattern looks good to me.
Some suggestions:

This line: return ath.save(null, { useMasterKey: true });
should be "return athlete.save(null, { useMasterKey: true });"

The 'Athlete' class has public read ACL. This means everyone can read the data of 'Athlete'.
It's not secure to have login by "athId" only. Should have a hashed password in the other private class.

If there is other place can create new 'Athlete', I would like to move ACL into the 'Athlete' beforeSave()
e.g:
Parse.Cloud.beforeSave("Athlete", function(request, response) {
if (!request.object.existed()) {
var acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(false);
request.object.setACL(acl);
}
return response.success();
});

@drewlarsen
Copy link
Author

Thanks for the feedback Jeff. Good points. Good catch on the ath/athlete typo.

One thing, Athlete is not a subclass of ParseUser. It is a separate entity that contains no private information. In my app, once the user validates via the usual ParseUser mechanisms, the apps rely on 'Athlete' as the primary 'person' object, not ParseUser. The 'isLoginEnabled' example was just made up to demonstrate the pattern, that isn't a real attribute of Athlete.

Maybe there is a better way to do this, but I leave 'Athlete' as public read to aid in client-side queries (e.g. 'find a friend'). I didn't want to have to rely on cloud code for queries (no caching, etc.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment