How can I assign multiple roles to one user in Parse? - parse-platform

I've written a bit of Cloud Code which executes after every user is saved. Inside, I would like to add the user to two roles, Alpha and Free, but this code only successfully adds new users to the Alpha role; the Free role has no data in the users table. Is there a way in Parse to assign users multiple roles?
Here is my Cloud Code.
Parse.Cloud.afterSave(Parse.User, function(request, response) {
Parse.Cloud.useMasterKey(); // grant administrative access to write to roles
var user = request.object;
query = new Parse.Query(Parse.Role);
query.equalTo("name", "Alpha");
query.first ( {
success: function(object) {
object.relation("users").add(user);
object.save();
response.success("The user has been authorized.");
},
error: function(error) {
response.error("User authorization failed!");
}
});
query = new Parse.Query(Parse.Role);
query.equalTo("name", "Free");
query.first ( {
success: function(object) {
object.relation("users").add(user);
object.save();
response.success("The user has been authorized.");
},
error: function(error) {
response.error("User authorization failed!");
}
});
});

The problem is sequencing. We need all of the queries and saves to complete before response.success() is called. As it is now, the timing of actions in the code is not deterministic. Clean it up by using the promises returned by the parse sdk...
Parse.Cloud.afterSave(Parse.User, function(request, response) {
Parse.Cloud.useMasterKey(); // grant administrative access to write to roles
var user = request.object;
query = new Parse.Query(Parse.Role);
query.equalTo("name", "Alpha");
query.first().then(function(object) {
object.relation("users").add(user);
return object.save();
}).then(function() {
query = new Parse.Query(Parse.Role);
query.equalTo("name", "Free");
return query.first();
}).then(function(object) {
object.relation("users").add(user);
return object.save();
}).then(function() {
response.success("The user has been authorized.");
}, function(error) {
response.error("error: " + error.message);
});
});

Related

Verify current user authenticity from Cloud Code

I have a Parse Cloud Code function that returns some user private data given a user id. How can I verify that the given user id is indeed the id of the currently logged in user?
I ended up creating this function and calling it from my other Cloud Code functions.
// Verifies that the given user is an authentic user
// Parameters: user
// Return: Promise
function verifyUserAuthenticity(user)
{
var promise = new Parse.Promise();
var userSessionToken = user.getSessionToken();
var query = new Parse.Query(Parse.Session);
query.equalTo("user", user);
query.equalTo("sessionToken", userSessionToken);
query.find({ useMasterKey: true }).then(
function(results)
{
if(results.length > 0)
{
promise.resolve("User is authentic.");
}
else
{
promise.reject("User is not authentic.");
}
},
function(error)
{
promise.reject("Error verifying user's authenticity: " + error);
}
);
return promise;
}

Update user object from Parse cloud code is not saving

Currently I am trying to update the username and password from parse cloud code, but In the parse.com console I am seeing the success messages, but the object is not actually saved in the parse.com database. Here is the contents of cloud/main.js
// code to update username
Parse.Cloud.define("updateUserName", function(request, response){
if(!request.user){
response.error("Must be signed in to update the user");
return;
}
Parse.Cloud.useMasterKey();
var userId = request.params.id;
var userName = request.params.userName;
// var User = Parse.Object.extend("User");
var updateQuery = new Parse.Query(Parse.User);
updateQuery.get(userId,{
success: function(userRecord){
console.log(userRecord.get("id"));
userRecord.set("username", userName);
// userRecord.set("resetToken", "Apple");
userRecord.save(null,{
success: function(successData){
response.success("username updated successfully.");
// userRecord.fetch();
},
error: function(errorData){
console.log("Error while updating the username: ",errorData);
}
});
},
error: function(errorData){
console.log("Error: ",errorData);
}
});
});
Parse.Cloud.define("resetPassword", function(request, response){
var successMsg = "";
if(!request.user){
response.error("Must be signed in to update the user");
return;
}
Parse.Cloud.useMasterKey();
var resetToken = request.params.resetToken;
var password = request.params.password;
// var User = Parse.Object.extend("User");
var updateQuery = new Parse.Query(Parse.User);
// updateQuery.equalTo("resetToken", resetToken);
updateQuery.get(resetToken,{
success: function(userRecord){
// console.log(userRecord.get("id"));
// userRecord.set("password",password)
userRecord.set("password",password);
userRecord.save(null, {
success: function(successData){
successMsg = "Password Changed !";
console.log("Password changed!");
userRecord.set("resetToken", "");
userRecord.save();
},
error: function(errorData){
response.error("Uh oh, something went wrong");
}
})
},
error: function(errorData){
console.log("Error: ",errorData);
}
});
response.success(successMsg);
});
The code actually runs without any error, but it is not updating the values in the database. Here is how I am calling these cloud functions in js/index.js
$(".update-user").click(function(){
Parse.Cloud.run("updateUserName", {id: $(this).data("id"), username: $(".uname").val()},
{
success: function(successData){
console.log("username updated successfully.");
$("#editModal").modal("hide");
$(".edit-modal").hide();
},
error: function(errorData){
}
});
});
The contents that I see in the firefox console username updated successfully.
The contents that I see in parse.com console
I2015-12-11T06:15:13.361Z]v106 Ran cloud function updateUserName for user chfgGhaPEl with:
Input: {"id":"MAvm9FlGgg","username":"testuser12"}
Result: username updated successfully.
But this line of code userRecord.set("resetToken", "Apple"); is updating the resetToken column in the database, but why not it is not letting me update the username/password(or other columns that I didn't try updating) columns ?
I analyse your code. Instead of using get to retrieve correlated user, use first where you specify the user id as query constraint. One example (working) code is below where user information is updated in Parse User table. Hope this helps.
Regards.
Parse.Cloud.define("updateUser", function(request, response)
{
Parse.Cloud.useMasterKey();
var query = new Parse.Query(Parse.User);
var objectId = request.params.objectId;
var username = request.params.username;
var email = request.params.email;
var userType = request.params.userType;
var password = request.params.password;
query.equalTo("objectId", objectId);
query.first({
success: function(object)
{
object.set("username", username);
object.set("email", email);
object.set("userType", userType);
object.set("password", password);
object.save();
response.success("Success");
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
response.error("Error");
}
});
});
What worked for me is
//cloud/main.js
Parse.Cloud.define("updateUserName", function(request, response){
if(!request.user){
response.error("Must be signed in to update the user");
return;
}
if(request.params.IsAdmin == false){
response.error("Only the administrators can edit username.");
return;
}
Parse.Cloud.useMasterKey();
// var userId = request.params.Id; --> I guess he was the main culprit, the params and the actual column value should match. Instead of passing Id from my client code(see below) I just passed objectId and it worked.
var userId = request.params.objectId;
// var userName = request.params.username;
var name = request.params.name;
// var User = Parse.Object.extend("User");
var updateQuery = new Parse.Query(Parse.User);
console.log("id from params: "+userId);
updateQuery.equalTo("objectId", userId);
updateQuery.first({
success: function(userRecord){
// userRecord.set("username", userName);
userRecord.set("name", name);
userRecord.save(null,{
success: function(successData){
response.success("username updated successfully.");
userRecord.fetch();
},
error: function(errorData){
console.log("Error while updating the username: ",errorData);
}
});
},
error: function(errorData){
console.log("Error: ",errorData);
response.error(errorData);
}
});
});
// js/index.js
/* initially I was using Id instead of objectId, the field names are case
sensitive
Parse.Cloud.run("updateUserName", {id: $(this).data("id"), name: $(".name").val(),IsAdmin: Parse.User.current().get("IsAdmin") },*/
Parse.Cloud.run("updateUserName", {objectId: $(this).data("id"), name: $(".name").val(),IsAdmin: Parse.User.current().get("IsAdmin") },
{
success: function(successData){
console.log("username updated successfully.");
$("#editModal").modal("hide");
$(".edit-user-server-error").html("");
$(".edit-modal").hide();
location.reload();
},
error: function(errorData){
console.log(errorData);
$(".edit-user-server-error").html("");
$(".edit-user-server-error").html(errorData.message);
}
});
So I had referred to this post and it gave me an hint, based on that I just did one trial and error thing and got it working.
You can pass any sort of values in the parameters to this Cloud Function, so you might want to specify exactly which properties you wish to update in this manner. Also, don't forget to actually validate that request.user is allowed to perform such an operation.
Parse doesn't support HTTP PUT requests to save the data. Hence we need to use PUSH request containing a method call to save the data.
However it has to have a the line, Parse.Cloud.useMasterKey(); before calling the save method.

Parse.Push.send success probably called too soon

I'm trying to implement one time PUSH notification subscriptions in the Parse Cloud service. Here's my model:
Installation.user - pointer to User
User - team (String)
Subscription - email (String), status (String), user (pointer to User)
As a user, I can say - send PUSH notification(s), to all my devices, when user with email became status and this user is in my team. User can have many subscriptions. User can unsubscribe manually (via OS X, iOS, Android, ... apps) or automatically - when user receives PUSH notification, subscription should be automatically cancelled. He can subscribe again, no problem, but auto cancellation is a must. These subscriptions are user based, user can see them on devices.
I wrote Parse Cloud function which consists of two pieces.
Send PUSH
Parse.Push.send({
where : query,
data : pushData
},
{ success: function() {
response.success('OK');
}, error: function(err) {
console.log(err);
response.error({ error: err });
}});
This does work. My query works and PUSH received.
Delete Subscriptions
deleteSubscriptionQuery.find({
success: function(objects) {
Parse.Object.destroyAll(objects, {
success: function() {
response.success('OK');
},
error: function(err) {
console.log(err);
response.error(err);
}
});
},
error: function(err) {
console.log(err);
response.error(err);
},
});
This does work. My deleteSubscriptionQuery works and correct subscriptions deleted.
Combined Together
Parse.Push.send({
where : query,
data : pushData
},
{ success: function() {
deleteSubscriptionQuery.find({
success: function(objects) {
Parse.Object.destroyAll(objects, {
success: function() {
response.success('OK');
},
error: function(err) {
console.log(err);
response.error(err);
}
});
},
error: function(err) {
console.log(err);
response.error(err);
},
});
}, error: function(err) {
console.log(err);
response.error({ error: err });
}});
This doesn't work and success is reported. It seems that the problem lies in Parse.Push.send, ie. success is called too soon. I assume Parse.Push.send doesn't really send notifications, but just schedules them and success is called upon successful schedule. If so, should be renamed to Parse.Push.schedule. Also I assume that they are scheduled with my query (query saved, not really executed), so, I suspect this solution doesn't work because of:
Parse.Push.send - saves my query and calls success,
Subscription objects are deleted in success,
Parse started to process my pushes with saved query, executes the query and it returns zero objects, because I already deleted Subscription objects in success of Parse.Push.send.
Am I right? Does anyone know Parse internals? If I am right, what do you propose I should do to implement one time PUSH notifications based on custom queries?
Here's the solution based on #Wain's proposal.
Fetch Subscription objects based on request parameters.
User is included in this query, so, it's fetched along with Subscription objects.
Make separate Installation query based on User objects from Subscription objects.
Send PUSH to separate Installation query.
Delete already fetched Subscription objects upon success.
I can safely delete Subscription objects in this way and it doesn't interfere with Installation query for PUSH notifications. Does work as expected.
Parse.Cloud.define("status", function(request, response) {
//
// Input validation
//
if (!request.params.hasOwnProperty("status")) {
response.error("Missing status parameter");
return;
}
var statusQuo = request.params["status"]
if (!statusQuo.hasOwnProperty("email") || !statusQuo.hasOwnProperty("team") || !statusQuo.hasOwnProperty("status")) {
response.error("Invalid status dictionary");
return;
}
var status = statusQuo["status"]
if ( status != "Available" ) {
response.success('OK');
return;
}
var email = statusQuo["email"]
var team = statusQuo["team"]
Parse.Cloud.useMasterKey();
//
// Find subscriptions
//
var usersQuery = new Parse.Query(Parse.User);
usersQuery.equalTo('team', team);
var Subscription = Parse.Object.extend("Subscription");
var subscriptionsQuery = new Parse.Query(Subscription);
subscriptionsQuery.include('user');
subscriptionsQuery.equalTo('status', status);
subscriptionsQuery.equalTo('email', email);
subscriptionsQuery.matchesQuery('user', usersQuery);
subscriptionsQuery.find({
success: function(subscriptions) {
var users = subscriptions.map(function(subscription) {
return subscription.get('user');
});
//
// Query for notifications / installations
//
var query = new Parse.Query(Parse.Installation);
query.equalTo('channels', 'status');
query.containedIn('user', users);
//
// Push notifications data
//
var pushData = { 'status' : statusQuo };
var apsData = { 'sound' : '', 'alert' : email + ' became Available.' };
pushData['aps'] = apsData
Parse.Push.send({
where : query,
data : pushData
},
{ success: function() {
Parse.Object.destroyAll(subscriptions, {
success: function() {
response.success('OK');
},
error: function(err) {
console.log(err);
response.error(err);
}
});
}, error: function(err) {
console.log(err);
response.error({ error: err });
}});
}, error: function(err) {
console.log(err);
response.error(err);
}
});
});

Object from Pointer in Parse Cloud Code

I'm attempting to create my first Parse Cloud Code function and am running into an issue:
Parse.Cloud.afterSave("Message", function(request) {
var fromUser = request.object.get("fromUser");
var toUser = request.object.get("toUser");
console.log(fromUser); // user pointer
console.log(toUser); // user pointer
});
As you can see both fromUser and toUser is a pointer when what I actually want is the user objects themselves. What is the best way to do this?
You can create a new query to get user informations.
var query = new Parse.Query(Parse.User);
query.get(request.object.get('fromUser').id, {
success: function(user) {
// What you want with user informations
},
error: function() {}
});
You can try this, but I've never try.
var query = new Parse.Query(Parse.User);
query.equalTo('objectId', request.object.get('fromUser').id);
query.equalTo('objectId', request.object.get('toUser').id);
query.find({
success: function(users) {
// What you want with users information
},
error: function() {}
});
I am too late, but I hope this will work
you can use
Parse.Cloud.beforeSave("Message", function(request, response) { ....
or
Parse.Cloud.afterSave("Message", function(request) { ....
.
this is how to use beforeSave
Parse.Cloud.beforeSave("Message", function(request, response) {
var message = request.object;
var fromUser = message.get("fromUser"); // you must have this User object, if it's null, then the object is null in the table
var toUser = message.get("toUser");
// fromUser and toUser columns must be Pointer<User> and have values
}).catch(function(error) {
response.error("Error finding message " + error.code + ": " + error.message);
});
});

Delete a user in parse User table from windows 8.1 app

I am developing a windows 8.1 app using Parse as back-end, I want to delete a user from windows app,How to achieve this
You can delete a user using Cloud Code.
Parse.Cloud.define("deleteUser", function(request, response) {
var query = new Parse.Query(Parse.User);
query.equalTo("objectId", reques.parsams.userId);
query.first({
success: function(result) {
result.destroy({
success: function() {
response.success("Success");
},
error: function(error) {
response.error("Could not delete object");
}
});
},
error: function() {
response.error("Could not run query");
}
});
});
You'd call the above function in Windows 8, where xxxxxxx is the objectId of the user you'd like to delete.
IDictionary<string, object> params = new Dictionary<string, object>
{
{ "userId", "xxxxxxx" }
};
var result = await ParseCloud.CallFunctionAsync<IDictionary<string, object>>("deleteUser", params);

Resources