Can you edit data in beforeSave callback and if yes, how?
Looking at various docs, it seem like this code should work (using "_User" or Parse.User) and I can see Ran beforeSave on a User and new user! in my logs but the key aaa is still empty..
I've tried to add user.save() after user.set but still no luck.
Any idea?
Parse.Cloud.beforeSave("_User", function(request, response) {
console.log('Ran beforeSave on a User');
var user = request.object;
if(user.existed()) {
response.success();
} else {
console.log('new user!');
user.set('aaa', 'xxx');
response.success();
}
});
EDIT 2015/12/03: Maybe critical info, this code is running on heroku. http://blog.parse.com/announcements/introducing-heroku-parse/
EDIT 2015/12/05: It seem to be a bug with Heroku / Parse integration. beforeSave hook are called but doesn't seem to allow you to edit the request.object..
EDIT 2015/12/07: I've created an issue on the CloudCode-Express repo https://github.com/ParsePlatform/CloudCode-Express/issues/6
Using Webhooks, you need to pass back the changed object:
response.success(object);
Change your code to use isNew() method:
Parse.Cloud.beforeSave(Parse.User, function(request, response) {
console.log('Ran beforeSave on a User');
if(!request.object.isNew()) {
response.success();
} else {
console.log('new user!');
request.object.set('aaa', 'xxx');
response.success();
}
});
Related
I am trying to login to single user with multi OAuth (facebook, google) login service. Here is what I try.
In Client:
'click #signInByFacebook': function (e) {
e.preventDefault();
Meteor.loginWithFacebook({requestPermissions: ['public_profile', 'email', 'user_about_me', 'user_photos']}, function (err, res) {
if (err) {
showError($('.alert'), err, 'login');
return;
}
showSuccess($('.alert'), 'login');
Session.set('notAdmin', !Roles.userIsInRole(Meteor.user(), ["admin"]));
Router.go('/');
});
},
'click #signInByGoogle': function (e) {
e.preventDefault();
Meteor.loginWithGoogle({requestPermissions: ['profile', 'email', 'openid']}, function (err, res) {
if (err) {
showError($('.alert'), err, 'login');
return;
}
showSuccess($('.alert'), 'login');
Session.set('notAdmin', !Roles.userIsInRole(Meteor.user(), ["admin"]));
Router.go('/');
});
}
In Server:
Accounts.onCreateUser(function (options, user) {
if (options.profile) {
user.profile = options.profile;
}
var sameuser = Meteor.users.findOne({$or: [{'emails.address': getEmail(user)}, {'services.facebook.email': getEmail(user)}, {'services.google.email': getEmail(user)}]});
console.log(sameuser);
if (sameuser) {
if (user.services.facebook) {
console.log("facebook");
Meteor.users.update({_id: sameuser._id}, {$set: {'services.facebook': user.services.facebook}});
}
if (user.services.google) {
console.log("google");
Meteor.users.update({_id: sameuser._id}, {$set: {'services.google': user.services.google}});
}
return;
}
console.log('register success');
return user;
});
This code will check if any user logined with facebook/google has the
same email or not with current sign in. If they are the same, just
update information to old account. If not, create new user.
This works great, but there is a problem with the 'return ;' in server code. I dont know what should I return to stop create user and auto login to the user that has same email. Anybody can help this issue ? Thank you.
The only way to stop creation of the new user is to throw an exception, but that will also prevent logging in as the existing user.
However, your general approach is insecure. Consider a user who has a Google account with a strong password and a Facebook account with a weak one. When he uses the Google account to authenticate with your app, he doesn't (and shouldn't) expect that someone who gains access to his Facebook account will be able access your app as him.
A better approach is to require that the user be logged into both services simultaneously before merging the services. The good news is that this also means that you don't need to worry about logging in after preventing the creation of the new user, because the user will already be logged in. Something like this might work:
Accounts.onCreateUser(function (options, user) {
if (options.profile) {
user.profile = options.profile;
}
var currentUser = Meteor.user();
console.log(currentUser);
if (currentUser) {
if (user.services.facebook) {
console.log("facebook");
Meteor.users.update({_id: currentUser._id}, {$set: {'services.facebook': user.services.facebook}});
}
if (user.services.google) {
console.log("google");
Meteor.users.update({_id: currentUser._id}, {$set: {'services.google': user.services.google}});
}
throw new Meteor.Error(Accounts.LoginCancelledError.numericError, "Service added to existing user (or something similar)");;
}
console.log('register success');
return user;
});
There are still a couple loose ends. First, I think Meteor expects OAuth credentials to be "pinned" to the user that they are associated with, so you probably need to repin the credentials you are copying.
Second, the above approach bypasses the validateLoginAttempt() callbacks. If you, or any package you are using, has registered any such callbacks, they won't be called when logging in using the second service, so they won't be able to prevent any such logins that they might consider invalid.
You can address both of these issues and skip the onCreateUser() callback as well, by just adding my brettle:accounts-add-service package to your app.
I have the following code, where I have a myBool (a boolean) in my Data Browser initially set to false,
however sometime while I'm still viewing my page I have code set to turn it to true.
How can I make a real time update that will automatically hide my #div when myBool turns to true?
var myBool = currentUser.get("myBool");
if(myBool) {
$('#div').hide();
}
I did some research and found that the Parse.Cloud.afterSave() function may be useful, but I don't see how it will update the content automatically?
Hope I've been clear!
Thanks.
Edit:
Possibly something like this in my main.js?
Parse.Cloud.afterSave("setBool", function() {
var query = new Parse.Query(Parse.Installation);
query.equalTo('myBool', true);
Parse.Push.send({
where: query,
}, {
success: function() {
$('#div').hide();
},
error: function(error) {
$('#div').show();
}
});
});
Your problem with your afterSave function is that your calling it for a function rather than a class.
AfterSave is called after an object from a certain class is saved. If your bool
Parse.Cloud.afterSave(Parse.Installation, function(request) {
// Send push here, use request to target correct user
});
Additionally your push listener should be the one modifying the divs, not the CloudCode.
I have an afterSave method that takes the authData from a user signing up with Facebook and retrieves his friends. This has been working fine until I later defined a beforeSave method on the User, the authData was no longer present in the afterSave method. Example code below:
Parse.Cloud.beforeSave(Parse.User, function (request, response){
response.success();
});
Even though that method doesn't do anything, it prevents authData from being present in the afterSave from the same chain:
Parse.Cloud.afterSave(Parse.User, function(request) {
Parse.Cloud.useMasterKey();
var user = request.object;
user.fetch();
if (!user.existed()) {
Parse.Cloud.httpRequest({
url:'https://graph.facebook.com/me/friends?access_token='+user.get('authData').facebook.access_token,
success:function(httpResponse){
// process user friends
},
error:function(httpResponse){
console.error(httpResponse);
}
});
}
});
i have used
Parse.Cloud.useMasterKey();
and
user.fetch();
still got the following error in the log
Result: TypeError: Cannot read property 'facebook' of undefined
Please be careful as fetch() is an async call, so should be chained:
user.fetch().then(function (user) {
// now have access to updated user
});
In your code your fetch will have no effect on the code after it, since you're not waiting for it to finish.
As many who came before me, I'm trying to run a bit of cloud code that will check for uniqueness and then insert or update the object as necessary.
The code correctly determines whether or not there is an existing object in the db with the same device token as the request.object
However, the existing object will not update it's countdownValue to 200.
I have tried adding and omitting the object.save() function
I have tried adding, omitting, and exchanging the response.error and response.success functions
The preexisting object remains untouched in all cases.
I have tried Updating existing Parse object in Cloud Code and many others.
Any help or thoughts would be greatly appreciated.
var Countdown = Parse.Object.extend("Countdown");
Parse.Cloud.beforeSave("Countdown", function(request, response) {
if (!request.object.get("devicetoken")) {
response.error('A Countdown must have a devicetoken.');
} else {
var query = new Parse.Query(Countdown);
query.equalTo("devicetoken", request.object.get("devicetoken"));
query.first({
success: function(object) {
if (object) {
object.set("countdownValue", "200");
object.save();
response.error("Failing on purpose");
}
else
{
response.success();
}
},
error: function(error) {
response.error("Could not validate uniqueness for this Countdown object.");
}
});
}
});
I'm using the Facebook SDK to auth a user, and trying to save the user's email to the record after authenticating. However, I keep getting an error on the save call.
The code in question:
Parse.FacebookUtils.logIn({
access_token: authResponse.access_token,
expiration_date: expire_date.toISOString(),
id: response.id
},
{
success: function(user) {
console.log("success!");
user.set({"email":response.email});
user.save();
window.App.navigate("#myplaces", {trigger:true});
},
...
That user.save() call returns error occurred: http://www.parsecdn.com/js/parse-1.1.14.min.js:1: TypeError: 'undefined' is not an object.
According to the docs, I have to be in an authentication call (".logIn", etc.) to perform save(), so I'm wondering if this still works with Parse.FacebookUtils.logIn. Seems like it should.
Ideas as to why this isn't working? The ideal behavior is to log the user in, retrieve information from the FB response, and save that back to the user record on Parse.
Thanks!
Justin
Not sure about this but I had the same problem in Cloud Code and I used success/error callbacks when calling save(...):
user.save(null, {
success: function(){
// Code
},
error: function(){
// Code
}
});
See also here: https://parse.com/questions/saving-a-relation-on-the-current-user