Last active
July 23, 2017 11:51
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} | |
); | |
}); |
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
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();
});