I have written a cloud code to change a Boolean value in table. The code is getting executed and the values are getting updated as well. But the issue is that I get the following error printed in my console. I am worried if this might cause a problem if the number of rows increase. Following is the error which is being printed on the console
I2015-09-15T06:15:48.317Z]v11: Ran job hello with:
Input: {}
Failed with: Error: Job status message must be a string
at updateJobMessageAndReturn (<anonymous>:1157:7)
at Object.success (<anonymous>:1211:9)
at e.obj.save.success (main.js:25:30)
at e.<anonymous> (Parse.js:12:27827)
at e.s (Parse.js:12:26759)
at e.n.value (Parse.js:12:26178)
at e.<anonymous> (Parse.js:12:26831)
at e.s (Parse.js:12:26759)
at e.n.value (Parse.js:12:26178)
at e.s (Parse.js:12:26887)
Following is the cloud code:
Parse.Cloud.job("hello", function(request, response) {
Parse.Cloud.useMasterKey();
var presentDate = new Date();
// presentDate.setDate(presentDate.getDate()-1);
presentDate.setHours(0,0,0,0);
var usersValid = new Parse.Query(Parse.User);
usersValid.equalTo("emailVerified", true);
//usersValid.greaterThan("updatedAt", presentDate);
var users = new Parse.Query("Properties");
users.matchesQuery("user",usersValid);
users.equalTo("verified", false);
users.limit(1000);
users.find({
success: function(results) {
console.log("Total new properties "+ results.length);
for (var i = 0; i < results.length; i++) {
var obj = results[i];
obj.set("verified", true);
obj.save(null,{
success: function (object) {
console.log("Success - "+i);
response.success(object);
},
error: function (object, error) {
console.log("Failed - "+i);
response.error(error);
}
});
}
},
error: function(error) {
console.log("failed");
}
});
When you call
response.success(object);
you're passing the full object that was just saved - but you shouldn't be. You can just call success with a simple status string, like 'OK', or with some element from the saved object, like its object id.
The more serious issue is that you're requesting 1000 items in the query and then updating and saving each individually - and in the save completion handler you're calling success or error. So, as soon as the first of those 1000 objects is saved you're telling the job it's complete and it can stop processing the rest.
You should change your job to use promises instead of old style callbacks and you should put all of the save promises into an array and wait for them to complete after your loop before you call success or error.
Related
I have a function in my cloud code, which works, but I'm not sure how to fix a problem related to it.
Original Problem:
Parse.Cloud.define("assignTokenToUser", function(request, response) {
console.log("Inside assignTokenToUser");
var token = Math.random().toString(30).substring(7);
query = new Parse.Query("User"),
email = request.params.email;
query.equalTo("username", email);
query.find({ useMasterKey: true }).then(function(results) {
query.first({
success: function(user) {
// Successfully retrieved the object.
user.set("emailToken", token);
user.save();
console.log("success...");
response.success(token);
},
error: function(error) {
console.log("error 1...");
response.error(error);
}
});
}, function(error) {
console.log("error 2...");
response.error(error);
});
});
This seemed to be a common problem after scanning the internet, and my analysis is that the useMasterKey needs to be passed each time we use the query object. Correspondingly, my log file shows that when trying to save the user, it gives a Code 206 error.
Log file output:
Inside assignTokenToUser
success...
^[[32minfo^[[39m: Ran cloud function assignTokenToUser for user undefined with:
Input: {"email":"maryam.zafar#emumba.com"}
Result: "p66qm34jd80p0j6ne03fe1q7f" functionName=assignTokenToUser, email=maryam.zafar#emumba.com, user=undefined
going to send an email... with result: p66qm34jd80p0j6ne03fe1q7f
fullLink: https://beatthegym.com/emailVerified?username=maryam.zafar#emumba.com&token=p66qm34jd80p0j6ne03fe1q7f
^[[31merror^[[39m: Error generating response. ParseError { code: 206, message: 'Cannot modify user 4m0VZFsKVt.' } code=206, message=Cannot modify user 4m0VZFsKVt.
[object Object]
So I went on to change my code to the following:
Code:
Parse.Cloud.define("assignTokenToUser", function(request, response) {
console.log("Inside assignTokenToUser");
var token = Math.random().toString(30).substring(7);
query = new Parse.Query("User"),
email = request.params.email;
query.equalTo("username", email);
query.find({ useMasterKey: true }).then(function(results) {
console.log("inside query.find...");
query.first(null, { useMasterKey: true }).then(function(user) {
console.log("inside query.first...");
// Successfully retrieved the object.
user.set("emailToken", token);
user.save(null, { useMasterKey: true }).then(function() {
console.log("inside user.save...");
response.success();
}, function(error) {
response.error(error);
});
response.success(token);
},
function(error) {
console.log("error 1...");
response.error(error);
});
}, function(error) {
console.log("error 2...");
response.error(error);
});
});
Log file:
Inside assignTokenToUser
inside query.find...
inside query.first...
^[[32minfo^[[39m: Ran cloud function assignTokenToUser for user undefined with:
Input: {"email":"maryam.zafar#emumba.com"}
Result: "tqc8m9lo2tcsrqn69c3q0e1q7f" functionName=assignTokenToUser, email=maryam.zafar#emumba.com, user=undefined
inside user.save...
^[[32minfo^[[39m: Ran cloud function assignTokenToUser for user undefined with:
Input: {"email":"maryam.zafar#emumba.com"}
Result: undefined functionName=assignTokenToUser, email=maryam.zafar#emumba.com, user=undefined
[object Object]
Now, the log file gives me a user as "undefined", and the call to the function gives me a pending status in the Chrome Network tab in the Inspector tool, until it turns into 502, and then the request is auto generated by the browser again. All other requests get a correct 200 response.
However, the data seems to be saved.. the record against this email address saves the token generated correctly. But the request from the browser fails and the user is "undefined" while in the original log file, I see the correct user Id... everytime it fails, the function automatically runs again (because the browser is generating another request everytime it gets a 502) and since it is actually supposed to send an email, it's running again and again keeps on generating infinate emails...
Thank you in advance..
Understood this finally:
The user will remain undefined until and unlesss I obtain it using the Parse.User.current() method. The data does save into the database because it is a forced update to the record, however until the user is aunthenticated using the current() method, it will remain undefined.
I see this is an old post but I spotted clear error in the code:
query = new Parse.Query("User")
Should be:
query = new Parse.Query(Parse.User)
Or at least:
query = new Parse.Query("_User")
As User is a predefined class in Parse.
I am using Cloud Code to update all users, everyday. It used to work, but now getting error after 5 minute processing. "the service is currently unavailable" without any reason. I have checked status.parse.com and there is no relevant down. I have 10 000 users.
Parse.Cloud.job("makeUsersPassiveAndSendPushes", function(request, status) {
Parse.Cloud.useMasterKey();
var activeUsers = [];
var limitDoneUsers = [];
var nowDate=new Date();
var updatedUsers = [];
var query = new Parse.Query(Parse.User);
query.equalTo("passive",false);
query.each(function(user) {
if(user.get("passive") === false){
activeUsers.push(user);
user.set("passive", true);
user.set("passiveDate",nowDate);
}
if(user.get("isLimitDone")){
limitDoneUsers.push(user);
}
user.set("isLimitDone",false);
user.set("activeMatch",null);
user.set("canGetMatch",true);
user.set("dailyMatchEndCount",0);
//user.set("lastMatchLimit",false);
user.set("todaysMatches",[]);
updatedUsers.push(user);
return user.save();
})
Could you help me? Thanks.
You may want to try modifying the last line from:
return user.save();
to use callbacks for the save function, to ensure they are firing in sequence:
return user.save(null, {
success: function (user) {
return user;
},
error: function (error) {
return Parse.Promise.error(new Error("error"));
}
});
Another alternative would be to use the saveAll function like this:
return Parse.Object.saveAll(updatedUsers).then(function() {
//code that fires after all objects are saved
});
Also, are you using the hosted Parse.com environment or have you transitioned to another provider like Heroku & mLab?
As a fellow Parse user with this same issue (background job failing with this error when performing many inserts), I look forward to any comments you may have.
I have two objects in Parse. The first one is notification where I save every challenge my user get. The second one is UserChallenge where I store every challenge a user completes. I tried to do a combined query where I only get results of notifications for challenges the user didn't complete and I get nothing in return. I checked my data and I should get two objects back.
Does anyone can tell what I did wrong in the query?
var Completed = Parse.Object.extend("Picok_User_Challenge");
var completedQuery = new Parse.Query(Completed);
completedQuery.equalTo("picok_user_id",currentUser);
var Challenges = Parse.Object.extend("Picok_Notifications");
var challengesQuery = new Parse.Query(Challenges);
challengesQuery.include("new_challenge_id");
challengesQuery.equalTo("type", "CHALLENGE");
challengesQuery.equalTo("who_recived", currentUser);
challengesQuery.doesNotMatchKeyInQuery("new_challenge_id","picok_challenge_id",
completedQuery);
challengesQuery.find({
success: function(results) {
console.log("createNotifiction success count: " + results.length);
response.success("YAY");
},
error: function(object, error)
{
console.log(error);
alert('Failed to get challenge not completed for: ' + request.params.user);
response.error('Failed to get user challenges to complete for: ' + request.params.user);`
}
});
I need to check the original object on before save to see what value has changed. currently i have this:
Parse.Cloud.beforeSave("Stats", function(request, response) {
if (!request.object.isNew()){
var query = new Parse.Query("Stats");
query.get(request.object.id, {
success: function(oldObject) {
alert('OLD:' + oldObject.get("Score") + "new:" + request.object.get("Score"));
/*Do My Stuff Here*/
response.success();
},
error: function(oldObject, error) {
response.error(error.message);
}
});
}else{
response.success();
}
});
The problem is that oldObject is equal to request.object.
Also the alert result is this: OLD:10 new:10, but the real old score was 5. Also according to the before save input log the original is really 5.
Any ideia what i am doing wrong?
Edit:
Here is the before save log.
before_save triggered for Stats as master:
Input: {"original":{"achievements":[],"Score":"100"updatedAt":"2015-11-02T10:09:24.170Z"},"update":{"Score":"110"}}
Result: Update changed to {"Score":"110"}
Edit2:
Is there any way to get the dirty value?
console.log("dirty: "+ request.object.dirty("score"));
I2015-11-03T14:23:12.198Z]dirty: true
The official way to know if a field was modified is to call dirty().
My guess is that querying for that particular object somehow updates all "instances" of that object in the current scope, and that's way dirty() works only if called before the query.
An option to get both the old and the new value is to mantain it in a separate field. For example, from your client you could call (pseudocoding, but you get the point):
// OldScore here is 0
statObject.put("Score") = 100;
statObject.saveInBackground();
Then in Cloud Code:
Parse.Cloud.beforeSave("Stats", function(request, response) {
var stat = request.object;
if (!stat.isNew()){
if (stat.dirty("Score")) {
var newValue = stat.get("Score") // 100
var oldValue = stat.get("OldScore") // 0
// Do other stuff here
stat.put("OldScore", newValue);
response.success();
} else {
response.success();
}
} else {
response.success();
}
});
However the issue you are describing is somewhat strange. Maybe you could try with the fetch() command; it should return the old value. However it will invalidate every other dirty field.
I send a request to parse that includes a Comment object that has a pointer to a User named "from".
In afterSave I need to read this and I'm having all kinds of problems. beforeSave works just fine, but I want to execute this code in afterSave;
Parse.Cloud.afterSave("Comment", function(request) {
var userQuery = new Parse.Query("User");
userQuery.get(request.object.get("from").id, {
success: function(user) {
},
error : function(error) {
console.error("errrrrrrrr" + error);
}
});
});
Here is the log I'm seeing on parse
errrrrrrrrr [object Object]
EDIT:
I also tried
var userQuery = new Parse.Query("_User");
Seems like I had to call useMasterKey, since I was fetching a user data.
I'm not entirely sure about this though so I'll keep this question open.
Parse.Cloud.useMasterKey();
Have you tried this?
var userQuery = new Parse.Query(Parse.User);
Try to fetch the pointer directly:
var fromUserPointer = request.object.get("from");
fromUserPointer.fetch().then(function(fetchedFromUser){
},function(error){
});
Slightly different approach.
This assumes that you have the comment object available right there, or at least its id.
Instead of querying the User collection, how about this:
var commentQuery = new Parse.Query("Comment");
commentQuery.include("from");
commentQuery.get(<commentId>, {
success: function (comment)
{
var user = comment.get("from"); // Here you have the user object linked to the comment :)
},
error: function (error)
{
console.log("ERROR: ");
console.log(error);
}
});