Send bulk email using Parse.com Cloud Code - javascript

In the app I'm creating a user will be able to send a description of an object to a number of recipients (from 1 to 200). Using Parse Cloud Code I'll have to use a promise to wait for the email server response for every email (Mailgun).
Is there any other way where I stock all those created emails in some kind of array and send them in 1 shot to the email server? Otherwise I hit the max of 15 seconds a function can run.
Right now I use this:
Parse.Cloud.define("emailobject", function(request, response) {
// ... some company info parameters
var theTraders = request.params.traders;
var objectQ = new Parse.Query("objects");
objectQ.equalTo("objectId", theObjectId);
objectQ.first({
success:function(theObject) {
// ... more code
searchObjectPictures(theObject, {
success:function(pictureObjects) {
for (var a = 0; a < theTraders.length; a++) {
// create the parameters to create the email
var mailingParameters = {};
// ... create the parameters
// when the email html has been compiled
mailgun.sendWelcomeEmail({
to: traderEmail,
from: toString,
subject: subjectString,
html: mailing.getMailingHTML(mailingParameters)
}, {
success:function(httpResponse) {
console.log(httpResponse);
},
error:function(httpResponse) {
console.log(httpResponse);
}
});
}
// emailedObjectsToSave is an array of analytics objects
Parse.Object.saveAll(emailedObjectsToSave, {
success:function(list) {
response.success(true);
},
error:function(error) {
response.error(error);
}
});
},
error:function(error) {
response.error(error);
}
});
},
error:function(error){
response.error(error);
}
});
});
I know promises would be better for nested queries, but I'm still wrapping my head around this.
Thank you

After a lot of searching, trial and error, I have finally found the solution. As #danh said it's better to add these requests to a job queue. The objects are now saved to a "notifications" class that has a bool "notified" to check if this object has already been sent or not.
I'll post the code here for people who might also search for it.
First you add your objects to the new "notifications" class (queue).
Parse.Cloud.define("addToNotificationsQueue", function(request, response) {
var roleName = request.params.role;
var objects = request.params.objects;
var newEmailObjectsToSave = [];
var EmailedObjectClass = Parse.Object.extend("notifications");
var emailObjectACL = new Parse.ACL();
emailObjectACL.setPublicReadAccess(false);
emailObjectACL.setRoleReadAccess(roleName, true);
emailObjectACL.setRoleWriteAccess(roleName, true);
for (var i = 0; i < objects.length; i++) {
// define the parameters for the new objects based on the request params
var emailObject = new EmailedObjectClass();
// set all the details to your new emailObject
emailObject.setACL(emailObjectACL);
newEmailObjectsToSave.push(emailObject);
}
Parse.Object.saveAll(newEmailObjectsToSave, {
success:function(list) {
response.success(true);
},
error:function(error) {
response.error(error);
}
});
});
Then define your job. I run it every 15 minutes for now to test. When the mailserver starts acting faster, I'll set the limit and frequency higher.
Parse.Cloud.job("sendNotifications", function(request, status) {
Parse.Cloud.useMasterKey();
var numberOfNotificationsHandled;
var query = new Parse.Query("notifications");
query.limit(10); // limit because max 15 minutes, slow email server...
query.notEqualTo("notified", true);
query.find().then(function(notifications) {
numberOfNotificationsHandled = notifications.length;
var promise = Parse.Promise.as();
_.each(notifications, function(notification) {
var parameterDict = {};
// add all parameters the emailObject function needs
promise = promise.then(function(){
return Parse.Cloud.run("emailObject", parameterDict).then(function(result) {
notification.set("notified", true);
notification.save();
}, function(error) {
notification.set("notified", false);
notification.save();
});
});
});
return promise;
}).then(function() {
console.log("JOB COMPLETED");
status.success("Sent " + numberOfNotificationsHandled + " emails");
}, function(error) {
console.log("JOB ERROR " + error);
status.error("Job error " + error);
});
});
The emailObject method is just an httpRequest like any other. This method waits for the answers of those httpRequests.

Related

Parse database job is not getting all results from http request

I have the following code set up as a job in the Parse Cloud Code for my application.
Parse.Cloud.job("requestLocations", function (request, response) {Parse.Cloud.httpRequest({
url: 'https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=29.7030428,-98.1364808&radius=900&types=restaurant&key=AIzaSyCTg0x68Q6lrCAo6-A37zkxge81jDEKpvo'
}).then(function (httpResponse) {
// Success
response.success("Success");
var parsedData = JSON.parse(httpResponse.text);
var Location = Parse.Object.extend("Locations");
for (var i = 0; i < parsedData.results.length; i++) {
var restaurant = new Location();
var placeId = parsedData.results[i].place_id;
var name = parsedData.results[i].name;
var vicinity = parsedData.results[i].vicinity;
var point = new Parse.GeoPoint({
latitude: parsedData.results[i].geometry.location.lat,
longitude: parsedData.results[i].geometry.location.lng
});
restaurant.set("placeId", placeId);
restaurant.set("name", name);
restaurant.set("vicinity", vicinity);
restaurant.set("location", point);
restaurant.save(null, {
success: function (location) {
console.log("Object ID: " + location.id);
},
error: function (location, error) {
console.log("Failed to create object, with error code: " + error.message);
}
});
}
}, function (httpResponse) {
// Error
response.error('request failed with response code ' + httpResponse)
});});
As you can see, this HTTP request should return a total of 14 places. Unfortunately, it will only return 9 places and it would also seem that which 9 are return can change. I am assuming there is a problem with the way my function is put together. Can anyone help me remedy this issue. I would like to return as many places as I want based on the radius of the HTTP request.
Thank You
The http request is done right, with a promise that's fulfilled when the request is complete. But your then() block tries to create several objects in a loop, not waiting for them all to finish, and failing to call response.success. Fix it like this...
// break it into understandable chunks, too, so, here's a function
// to build a Locations object from the http data
function locationFromResult(result) {
var Location = Parse.Object.extend("Locations");
var restaurant = new Location();
var placeId = result.place_id;
var name = result.name;
var vicinity = result.vicinity;
var point = new Parse.GeoPoint({
latitude: result.geometry.location.lat,
longitude: result.geometry.location.lng
});
restaurant.set("placeId", placeId);
restaurant.set("name", name);
restaurant.set("vicinity", vicinity);
restaurant.set("location", point);
return restaurant;
}
Parse.Cloud.job("requestLocations", function (request, response) {
var url = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=29.7030428,-98.1364808&radius=900&types=restaurant&key=AIzaSyCTg0x68Q6lrCAo6-A37zkxge81jDEKpvo';
Parse.Cloud.httpRequest({url: url}).then(function (httpResponse) {
var parsedData = JSON.parse(httpResponse.text);
var locations = parsedData.results.map(function(result) {
return locationFromResult(result);
});
// this is important, saveAll of the new objects before returning
// this can also be accomplished by saving the objects individually and using Parse.Promise.when()
return Parse.Object.saveAll(locations);
}).then(function(result) {
response.success(JSON.stringify(result));
}, function(error) {
response.error(JSON.stringify(error));
});
});

Parse [Error]: success/error was not called (Code: 141, Version: 1.9.0)

I am trying to write a Cloud Code function that will allow me to edit the data of another user as I cannot do that in the application it self. What the code does (I should say tries to do as I don't know JS) is fetch a User object and a Group (a class I created) object using two separate queries based on the two object IDs inputed. Here is my code
Parse.Cloud.define("addInvite", function(request, response) {
Parse.Cloud.useMasterKey();
var userID = request.params.user;
var groupID = request.params.group;
var user;
var group;
var userQuery = new Parse.Query(Parse.User);
userQuery.equalTo("objectId", userID);
return userQuery.first
({
success: function(userRetrieved)
{
user = userRetrieved;
},
error: function(error)
{
response.error(error.message);
}
});
var groupObject = Parse.Object.extend("Group");
var groupQuery = new Parse.Query(groupObject);
groupQuery.equalTo("objectId", groupID);
return groupQuery.first
({
success: function(groupRetrieved)
{
group = groupRetrieved;
},
error: function(error)
{
response.error(error.message);
}
});
var relations = user.relation("invited");
relations.add(group);
user.save();
response.success();
});
Every time I execute the method I get the error:
[Error]: success/error was not called (Code: 141, Version: 1.9.0)
Can anyone help with this? Thanks.
Every function in Parse Cloud returns a Promise. This also includes any query functions which you run to retrieve some data. Clearly in your code you are returning a Promise when you execute a query which abruptly ends your cloud function when your query completes. As you do not call a response.success() or response.error() in any of the success blocks, your cloud function returns without setting a suitable response, something that Parse requires and hence the error. Your code needs to chain all the promises to ensure your code is executed correctly and return success/error in the last step:
Parse.Cloud.define("addInvite", function(request, response) {
Parse.Cloud.useMasterKey();
var userID = request.params.user;
var groupID = request.params.group;
var user;
var group;
var userQuery = new Parse.Query(Parse.User);
userQuery.equalTo("objectId", userID);
userQuery.first().then(function(userRetrieved) {
user = userRetrieved;
var groupObject = Parse.Object.extend("Group");
var groupQuery = new Parse.Query(groupObject);
groupQuery.equalTo("objectId", groupID);
return groupQuery.first();
}).then( function(groupRetrieved) {
//group = groupRetrieved;
var relations = user.relation("invited");
relations.add(groupRetrieved);
return user.save();
}).then(function() {
response.success();
}, function(error) {
response.error(error.message);
});
});

Parse.Cloud.job promise not working

What I am trying to do here are:
Remove all contents in a class first, because every day the events.json file will be updated. I have my first question here: is there a better way to remove all contents from a database class on Parse?
Then I will send a request to get the events.json and store "name" and "id" of the result into a 2D array.
Then I will send multiple requests to get json files of each "name" and "id" pairs.
Finally, I will store the event detail into database. (one event per row) But now my code will terminate before it downloaded the json files.
Code:
function newLst(results) {
var event = Parse.Object.extend("event");
for (var i = 0; i < results.length; i++){
Parse.Cloud.httpRequest({
url: 'https://api.example.com/events/'+ results[i].name +'/'+ results[i].id +'.json',
success: function(newLst) {
var newJson = JSON.parse(newLst.text);
var newEvent = new event();
newEvent.set("eventId",newJson.data.id);
newEvent.set("eventName",newJson.data.title);
newEvent.save(null, {
success: function(newEvent) {
alert('New object created with objectId: ' + newEvent.id);
},
error: function(newEvent, error) {
alert('Failed to create new object, with error code: ' + error.message);
}
});
},
error: function(newLst) {
}
});
}
};
Parse.Cloud.job("getevent", function(request, status) {
var event = Parse.Object.extend("event");
var query = new Parse.Query(event);
query.notEqualTo("objectId", "lol");
query.limit(1000);
query.find({
success: function(results) {
for (var i = 0; i < results.length; i++) {
var myObject = results[i];
myObject.destroy({
success: function(myObject) {
},
error: function(myObject, error) {
}
});
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
var params = { url: 'https://api.example.com/events.json'};
Parse.Cloud.httpRequest(params).then(function(httpResponse) {
var results = [];
var jsonobj = JSON.parse(httpResponse.text);
for (var i = 0; i < jsonobj.data.length; i++) {
var tmp2D = {"name":"id"}
tmp2D.name = [jsonobj.data[i].name];
tmp2D.id = [jsonobj.data[i].id];
results.push(tmp2D);
}
newLst(results);
}).then(function() {
status.success("run job");
}, function(error) {
status.error(error);
});
});
I think my original answer is correct as a standalone. Rather than make it unreadable with the additional code, here it is made very specific to your edit.
The key is to eliminate passed callback functions. Everything below uses promises. Another key idea is decompose the activities into logical chunks.
A couple of caveats: (1) There's a lot of code there, and the chances that either your code is mistaken or mine is are still high, but this should communicate the gist of a better design. (2) We're doing enough work in these functions that we might bump into a parse-imposed timeout. Start out by testing all this with small counts.
Start with your question about destroying all instances of class...
// return a promise to destroy all instances of the "event" class
function destroyEvents() {
// is your event class really named with lowercase? uppercase is conventional
var query = new Parse.Query("event");
query.notEqualTo("objectId", "lol"); // doing this because the OP code did it. not sure why
query.limit(1000);
return query.find().then(function(results) {
return Parse.Object.destroyAll(results);
});
}
Next, get remote events and format them as simple JSON. See the comment. I'm pretty sure your idea of a "2D array" was ill-advised, but I may be misunderstanding your data...
// return a promise to fetch remote events and format them as an array of objects
//
// note - this differs from the OP data. this will evaluate to:
// [ { "name":"someName0", id:"someId0" }, { "name":"someName1", id:"someId1" }, ...]
//
// original code was producing:
// [ { "name":["someName0"], id:["someId0"] }, { "name":["someName1"], id:["someId1"] }, ...]
//
function fetchRemoteEvents() {
var params = { url: 'https://api.example.com/events.json'};
return Parse.Cloud.httpRequest(params).then(function(httpResponse) {
var results = [];
var remoteEvents = JSON.parse(httpResponse.text).data;
for (var i = 0; i < remoteEvents.length; i++) {
var remoteEvent = { "name": remoteEvents[i].name, "id": remoteEvents[i].id };
results.push(remoteEvent);
}
return results;
});
}
Please double check all of my work above regarding the format (e.g. response.text, JSON.parse().data, etc).
Its too easy to get confused when you mix callbacks and promises, and even worse when you're generating promises in a loop. Here again, we break out a simple operation, to create a single parse.com object based on one of the single remote events we got in the function above...
// return a promise to create a new native event based on a remoteEvent
function nativeEventFromRemoteEvent(remoteEvent) {
var url = 'https://api.example.com/events/'+ remoteEvent.name +'/'+ remoteEvent.id +'.json';
return Parse.Cloud.httpRequest({ url:url }).then(function(response) {
var eventDetail = JSON.parse(response.text).data;
var Event = Parse.Object.extend("event");
var event = new Event();
event.set("eventId", eventDetail.id);
event.set("eventName", eventDetail.title);
return event.save();
});
}
Finally, we can bring it together in a job that is simple to read, certain to do things in the desired order, and certain to call success() when (and only when) it finishes successfully...
// the parse job removes all events, fetches remote data that describe events
// then builds events from those descriptions
Parse.Cloud.job("getevent", function(request, status) {
destroyEvents().then(function() {
return fetchRemoteEvents();
}).then(function(remoteEvents) {
var newEventPromises = [];
for (var i = 0; i < remoteEvents.length; i++) {
var remoteEvent = remoteEvents[i];
newEventPromises.push(nativeEventFromRemoteEvent(remoteEvent));
}
return Parse.Promise.when(newEventPromises);
}).then(function() {
status.success("run job");
}, function(error) {
status.error(error);
});
});
The posted code does just one http request so there's no need for an array of promises or the invocation of Promise.when(). The rest of what might be happening is obscured by mixing the callback parameters to httpRequest with the promises and the assignment inside the push.
Here's a clarified rewrite:
Parse.Cloud.job("getevent", function(request, status) {
var promises = [];
var params = { url: 'https://api.example.com'};
Parse.Cloud.httpRequest(params).then(function(httpResponse) {
var results = [];
var jsonobj = JSON.parse(httpResponse.text);
for (var i = 0; i < jsonobj.data.length; i++) {
// some code
}
}).then(function() {
status.success("run job");
}, function(error) {
status.error(error);
});
});
But there's a very strong caveat here: this works only if ("// some code") that appears in your original post doesn't itself try to do any asynch work, database or otherwise.
Lets say you do need to do asynch work in that loop. Move that work to a promise-returning function collect those in an array, and then use Promise.when(). e.g....
// return a promise to look up some object, change it and save it...
function findChangeSave(someJSON) {
var query = new Parse.Query("SomeClass");
query.equalTo("someAttribute", someJSON.lookupAttribute);
return query.first().then(function(object) {
object.set("someOtherAttribute", someJSON.otherAttribute);
return object.save();
});
}
Then, in your loop...
var jsonobj = JSON.parse(httpResponse.text);
var promises = [];
for (var i = 0; i < jsonobj.data.length; i++) {
// some code, which is really:
var someJSON = jsonobj.data[i];
promises.push(findChangeSave(someJSON));
}
return Parse.Promise.when(promises);

How to chain functions in Parse CloudCode?

I've done a parse job that checks every "X" time if "emailSent" is false, for each user. If it is, I call a function to send a email and change the "emailSent" to true. That works.
My problem is with the function "getMaxId". I need to return the maxid value to change each user "id_client" column, but I don't know how. I've tried this but it doesn't work. This is writing nothing: "console.log("Write somethingggg"); "
Here is the code...
Parse.Cloud.job("test", function(request, status) {
// Set up to modify user data
Parse.Cloud.useMasterKey();
var texto = "New verified emails:\n\t";
// Query for all users
var query = new Parse.Query(Parse.User);
//query.equalTo("emailVerified", true);
query.equalTo("emailSent", false);
query.each(function(user) {
user.set("emailSent", true);
user.save();
var datos = user.get("email")+"\n";
texto=texto+datos;
Parse.Cloud.run("getMaxId", {},{
success: function(results) {
console.log("Write somethingggg");
user.set("id_client", "gofoadasda");
user.save();
var datos = user.get("id_client")+"\n";
//console.log("id_client: "+datos);
response.success();
},
error: function(results, error) {
response.error(errorMessageMaker("running chained function",error));
}
}).then(function() {
// Set the job's success status
}, function(error) {
// Set the job's error status
status.error("Uh oh, something went wrong.");
});
Parse.Cloud.run("sendEmail",{
success: function(results) {
response.success(results);
},
error: function(results, error) {
response.error(errorMessageMaker("running chained function",error));
}
});
}).then(function() {
// Set the job's success status
console.log("texto: "+texto);
status.success("Migration completed successfully.");
}, function(error) {
// Set the job's error status
status.error("Uh oh, something went wrong.");
});
});
Parse.Cloud.define("sendEmail", function(request, response) {
Parse.Cloud.httpRequest({
url: 'http://www.example.com/sendemail.php',
params: {
email : 'email#email.com'
},
success: function(httpResponse) {
console.log(httpResponse.text);
},
error: function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
}
});
});
Parse.Cloud.define("getMaxId", function(request,response) {
var query = new Parse.Query(Parse.User);
query.descending("id_client");
query.find({
success: function(results) {
var idmax=results[0].get("id_client")
console.log("idmax: "+idmax);
response.success(idmax);
},
error: function() {
response.error(" is an error");
}
});
});
FIRST CHANGES:
After #danh help, I tried to do what I need, changing some code:
Important: id_client is a int value which it's unique for each user, it starts at 20000.
get all the users with the flag sentEmail=false.
For each of those users, getMaxId (this returns the actual max "id_client" value for all the users).
Change value of sentEmail to true, set user id_client to the actual max id.
Send email.
New code (sendEmail has no changes):
var _ = require('underscore');
// return a promise to get the max value of id_client in the user table
function getMaxId(user) {
var query = new Parse.Query(Parse.User);
//return query.count();
query.descending("id_client");
query.limit(1);
return query.find().then(function(users) {
if(users[0].get("id_client")<20000){ //No users yet.
user.set("id_client", 20000); //First id:20000
user.save();
return 20000;
}
else{ //There are users. Get the maxId and increment +1.
user.set("id_client", users[0].get("id_client")+1);
user.save();
return (users.length)? users[0].get("id_client")+1 : 0;
}
});
}
// return a promise for users with emailSent flag == false
function usersWithUnsentEmail() {
var query = new Parse.Query(Parse.User);
query.equalTo("emailSent", false);
return query.find();
}
// return a promise to send email to the given user, and to set its
// emailSent flag = true
function sendEmailToUser(user) {
return sendEmail(user.get("email")).then(function() {
user.set("emailSent", true);
return user.save();
});
}
Parse.Cloud.job("test", function(request, response) {
// Set up to modify user data
Parse.Cloud.useMasterKey();
usersWithUnsentEmail().then(function (users){
var emailPromises = _.map(users, function(user) {
//what I understand is that here, for each user, we call getMaxId, getting the actual max id_client, and then, we pass it to "sendEmailToUser".
return getMaxId(user).then(function(max){
return sendEmailToUser(user);
});
});
return Parse.Promise.when(emailPromises);//This means that we have looped all users, is it?
}).then(function(results) {
response.success(results);
}, function(error) {
response.error(error);
});
});
I've tested this with 2 users with the flag "sentEmail" = false and actual max id_client was 20001
Result:
sentEmail flags changed correctly.
2 emails sent correctly.
Error here: id_client for both users changed to 20002. It has to be 20002 and 20003.
Logs in parse:
I2015-04-22T09:44:13.433Z] v90: Ran job test with:
Input: {}
Result: undefined
E2015-04-22T09:44:29.005Z] v90: Ran job test with:
Input: {}
Failed with: Error: Job status message must be a string
at updateJobMessageAndReturn (<anonymous>:790:7)
at Object.success (<anonymous>:828:9)
at main.js:217:18
at e (Parse.js:3:8736)
at Parse.js:3:8185
at Array.forEach (native)
at Object.x.each.x.forEach [as _arrayEach] (Parse.js:1:661)
at c.extend.resolve (Parse.js:3:8136)
at Parse.js:3:8815
at e (Parse.js:3:8736)
EDITED:
We need their email and the id_client that we will assign them.
May be I haven't explained well, the email won't be sent to the user email, the email will be sent to a email that I've determined in the sendemail.php script, and it will be always the same.
I'll explain: You have a local database at home, and parse database. When this Parse.job is called, it will send an email to you (email of php) with a list of the email and the id_client of each user updated. Now you can manually update your local database with the email received info.
So, for this reason, it will be better to send only one email, at the end of all the updates. (I didn't say that because I had a lot of problems yet trying to understand how cloudCode works...)
There are a few things that need fixing in the code: (1) as a rule, use promises if you're doing more than two consecutive asynchronous things, (2) don't call Parse.Cloud.run from cloud code, it's what you call from clients who wish to invoke cloud functions, (3) style-wise, you'll go nuts trying to figure it out later on unless you break the code into small, promise-returning steps.
I've applied all three bits of advice to your code. I don't fully understand the logic as described in code and text, but hopefully I got close enough for you to make sense of it.
// using underscore js, which provides _.map below as well as loads of other useful stuff
var _ = require('underscore');
// return a promise to get the max value of id_client in the user table
function getMaxId() {
var query = new Parse.Query(Parse.User);
query.descending("id_client");
query.limit(1);
return query.find().then(function(users) {
return (users.length)? users[0].get("id_client") : 0;
});
}
// return a promise for users with emailSent flag == false
function usersWithUnsentEmail() {
var query = new Parse.Query(Parse.User);
query.equalTo("emailSent", false);
return query.find();
}
// return a promise to send email to the given user, and to set its
// emailSent flag = true, and to set its clientId to the passed value
function sendEmailToUser(user, idClient) {
return sendEmail(user.get("email")).then(function() {
user.set("emailSent", true);
user.set("id_client", idClient);
return user.save();
});
}
// return a promise to send email to the given email address via an http service
function sendEmail(email) {
var params = {url: 'http://www.example.com/sendemail.php', params: {email : email} };
return Parse.Cloud.httpRequest(params);
}
Parse.Cloud.job("test", function(request, response) {
// Set up to modify user data
Parse.Cloud.useMasterKey();
var maxIdClient;
getMaxId().then(function(result) {
maxIdClient = result;
return usersWithUnsentEmail();
}).then(function(users) {
var emailPromises = _.map(users, function(user) {
return sendEmailToUser(user, maxIdClient);
});
return Parse.Promise.when(emailPromises);
}).then(function(results) {
response.success(results);
}, function(error) {
response.error(error);
});
});
EDIT - we're kind of working on logic here particular to the app, as opposed to the concept of promises, but here goes anyway. To restate the functional requirement: We want a job to find users who have not yet been recorded in another database, represented by a flag called "emailSent". Our goal is to assign these users a unique id, and send their info (for now, we'll say email address and that id) via email to some fixed destination.
So
// getMaxId() from above is almost ready, except the minimum id_client
// value is 20000, so change the line that set this to:
return (users.length)? users[0].get("id_client") : 20000;
// usersWithUnsentEmail() from above is fine
// delete sendEmailToUser() since we're not doing that
// change sendEmail() to take an array of users to be conveyed to
// the other database. Send email about them, then change each user's
// emailSent status and save them
function sendEmail(users) {
var params = {url: 'http://www.example.com/sendemail.php', params: {users : JSON.stringify(users)} };
return Parse.Cloud.httpRequest(params).then(function() {
_.each(users, function(user) {user.set("emailSent", true);});
return Parse.Object.saveAll(users);
});
}
// add a function that takes an array of users, updates their
// id_client to be a new, unique value, and sends mail about them
// to a remote server
function synchUsers(users, idMax) {
_.each(users, function(user) {
user.set("id_client", idMax);
idMax += 1;
});
return sendEmail(users);
}
// update the job taking all this into account
Parse.Cloud.job("test", function(request, response) {
// Set up to modify user data
Parse.Cloud.useMasterKey();
var maxIdClient;
getMaxId().then(function(result) {
maxIdClient = result;
return usersWithUnsentEmail();
}).then(function(users) {
return synchUsers(users, maxIdClient+1);
}).then(function(results) {
response.success(results);
}, function(error) {
response.error(error);
});
});

status.success being called before my function is done

In my Parse background job, I want both the processUser(user); and matchCenterComparison(eBayResults); functions to be called before calling status.success.
I currently have matchCenterComparison(eBayResults); being returned at the end of processUser(user);, as you can see below.
I figured that since it's returned in processUser(user);, and status.success isn't called until after that function is done, it would therefore wait until matchCenterComparison(eBayResults); is finished before calling it. This doesn't seem to be the case.
In the Parse forums, I was told that I need to have the matchCenterComparison function return a Promise, and make sure that the function call is also part of my main promise chain so that the background job waits till it completes before status.success is called. I'm having trouble figuring out the correct syntax to accomplish this, since I can't return two functions in a row in userQuery.each.
Main Promise Chain:
Parse.Cloud.job("MatchCenterBackground", function(request, status) {
var usersQuery = new Parse.Query(Parse.User);
usersQuery.each(function (user) {
return processUser(user);
}).then(function() {
status.success("background job worked brah!");
}, function(error) {
status.error(error);
});
});
processUser function:
function processUser(user) {
// ... code to setup per-user query ...
var matchCenterItem = Parse.Object.extend("matchCenterItem");
var query = new Parse.Query(matchCenterItem);
// easy way to share multiple arrays
var shared = {
promises: [],
searchTerms: [],
};
return query.find().then(function(results) {
// process results, populate shared data (promises and searchTerms)
console.log('matchCenterItem query results:' + results);
if (results.length > 0) {
console.log('we have entered the matchcenteritem query');
for (i = 0; i < results.length; i++) {
console.log('we have also entered the loop inside the matchCenterItem query');
// later in your loop where you populate promises:
var searchTerm = results[i].get('searchTerm');
// add it to the array just like you add the promises:
shared.searchTerms.push(searchTerm);
url = 'http://svcs.ebay.com/services/search/FindingService/v1';
//push function containing criteria for every matchCenterItem into promises array
shared.promises.push((function() {
if (results[i].get('itemLocation') == 'US')
{
console.log('americuh!');
var httpRequestPromise = Parse.Cloud.httpRequest({
url: url,
params: {// httprequest params in here}
});
}
else if (results[i].get('itemLocation') == 'WorldWide')
{
console.log('Mr worlwide!');
var httpRequestPromise = Parse.Cloud.httpRequest({
url: url,
params: {// httprequest params in here}
});
}
return httpRequestPromise;
})());
}
}
//buildEbayRequestPromises(results, shared);
}).then(function() {
// process promises, return query promise
return Parse.Promise.when(shared.promises).then(function() {
// process the results of the promises, returning a query promise
// ... code here ...
console.log('were in the when.then of promise');
var eBayResults = [];
for (var i = 0; i < arguments.length; i++) {
var httpResponse = arguments[i];
// since they're in the same order, this is OK:
var searchTerm = shared.searchTerms[i];
// pass it as a param:
var top3 = buildEbayRequestPromises(httpResponse.text, searchTerm);
eBayResults.push(top3);
}
return matchCenterComparison(eBayResults);
});
});
}
matchCenterComparison function:
function matchCenterComparison(eBayResults) {
if (eBayResults.length > 0) {
console.log('yes the ebay results be longer than 0');
//Query users MComparisonArray with these criteria
var mComparisonArray = Parse.Object.extend("MComparisonArray");
var mComparisonQuery = new Parse.Query(mComparisonArray);
mComparisonQuery.contains('Name', 'MatchCenter');
//mComparisonQuery.contains("MCItems", eBayResults);
console.log('setup query criteria, about to run it');
mComparisonQuery.find({
success: function(results) {
console.log('MatchCenter comparison results :' + results);
// No new items
if (results.length > 0) {
console.log("No new items, you're good to go!");
}
// New items found
else if (results.length === 0) {
console.log('no matching mComparisonArray, lets push some new shit');
//replace MCItems array with contents of eBayResults
Parse.Object.destroyAll(mComparisonArray);
var newMComparisonArray = new mComparisonArray();
newMComparisonArray.set('Name', 'MatchCenter');
newMComparisonArray.set('MCItems', eBayResults);
//newMComparisonArray.set("parent", Parse.User());
console.log('yala han save il hagat');
// Save updated MComparisonArray
newMComparisonArray.save().then({
success: function() {
console.log('MComparisonArray successfully created!');
//status.success('MComparisonArray successfully created!');
},
error: function() {
console.log('nah no MComparisonArray saving for you bro:' + error);
//status.error('Request failed');
}
});
//send push notification
}
console.log('MatchCenter Comparison Success!');
},
error: function(error) {
console.log('nah no results for you bro:' + error);
}
});
}
}

Categories