I have written a series of Parse Promises and am now getting error 141 when I make a request to this cloud code function. I have tried placing success: / error: all over the function where I think they belong based on the Parse DOCS.
Request
{
"projectDescription": "Testing saveProject",
"projectTitle": "This is only a test, in the event of a real post this will have an actual description",
"isEmailEnabled": true,
"shareEmails": [
"max#gmail.com",
"nat#gmail.com",
"noob#gmail.com"
],
"userId": "sLmOf4fZFL"
}
Parse.Cloud.define("saveProject", function(request, response) {
var emails = request.params.shareEmails;
var user = request.params.userId;
var projectDescription = request.params.projectDescription;
var projectTitle = request.params.projectTitle;
var emailStatus = request.params.isEmailEnabled;
var ProjectClass = Parse.Object.extend("Project");
var EmailsClass = Parse.Object.extend("Email");
var EmailsClassAssignment = Parse.Object.extend("EmailAssignment");
var project = new ProjectClass();
var projectO;
project.set("title", projectTitle);
project.set("createdBy", {
"__type": "Pointer",
"className": "_User",
"objectId": user
});
project.set("description", projectDescription);
project.set("status", true);
project.set("emailShareEnabled", emailStatus);
project.save().then(function(results) {
projectO = results;
console.log(projectO);
return Parse.Promise.when(emails.map(function(emailAddress) {
var email = new EmailsClass();
email.set("address", emailAddress);
return email.save();
}));
}).then(function() {
return Parse.Promise.when(emails.map(function(emailQuery) {
var queryEmail = new Parse.Query("Email");
queryEmail.equalTo("address", emailQuery);
return queryEmail.find().then(function(results) {
var emailJSON = results[0].toJSON();
var emailObjectId = emailJSON.objectId;
var projectJSON = projectO.toJSON();
var projectId = projectJSON.objectId;
var assignment = new EmailsClassAssignment();
assignment.set("createdBy", {
"__type": "Pointer",
"className": "_User",
"objectId": user
});
assignment.set("email", {
"__type": "Pointer",
"className": "Email",
"objectId": emailObjectId
});
assignment.set("project", {
"__type": "Pointer",
"className": "Project",
"objectId": projectId
});
assignment.save(null, {
success: function() {
console.log("Successfully saved project");
},
error: function(error) {
console.log("There was an error saving" + error.message);
}
});
});
}));
}).then( function() {
response.success();
});
});
The basic ideas look okay, but the code is kind of a jumble of callback parameters and promises. I took the liberty of refactoring into simpler, promise-returning logical chunks so we could see what's going on.
You highlighted the .map functions in the post. Not sure what the issue was there, so the code I suggest uses underscorejs, which can be easily included in the cloud as follows:
var _ = require('underscore');
First, return a promise to save a "project" given most of the params to your cloud function:
function createProject(params) {
var ProjectClass = Parse.Object.extend("Project");
var project = new ProjectClass();
var emails = request.params.shareEmails;
var user = request.params.userId;
var projectDescription = request.params.projectDescription;
var projectTitle = request.params.projectTitle;
var emailStatus = request.params.isEmailEnabled;
project.set("title", projectTitle);
project.set("createdBy", {
"__type": "Pointer",
"className": "_User",
"objectId": user
});
project.set("description", projectDescription);
project.set("status", true);
project.set("emailShareEnabled", emailStatus);
return project.save();
}
Next, create "Email"'s (which are objects) given an array of email address strings. (You would do well to more carefully distinguish the objects and the strings in your naming, but I tried to hew to original nomenclature in the code)
function createEmails(emails) {
var EmailsClass = Parse.Object.extend("Email");
var toSave = _.map(emails, function(emailAddress) {
var email = new EmailsClass();
email.set("address", emailAddress);
return email;
});
// like the when() function, but (possibly) fewer requests
return Parse.Object.saveAll(toSave);
}
This is where the original code took a turn for the worse. In it, the code just finished creating the Email objects, then for some reason, it attempts to query those objects. But we have them in hand already, on the fulfullment of the promises to save.
The method below, takes already built email objects (named pedantically, to emphasize that they are objects) and other ingredients to an "EmailClassAssignment". Notice how we can assign pointers directly with objects when we have a PFObject in hand:
function createEmailClassAssignments(emailObjects, project, userId) {
var EmailsClassAssignment = Parse.Object.extend("EmailAssignment");
var toSave = _.map(emailObjects, function(emailObject) {
var assignment = new EmailsClassAssignment();
// the real objects can be used as parameters to set for pointer columns
assignment.set("email", emailObject);
assignment.set("project", project);
// we only have the userId, not a user object, so we can either query
// for the user or take the shortcut that you've been taking
project.set("createdBy", {
"__type": "Pointer",
"className": "_User",
"objectId": user
});
return assignment;
});
return Parse.Object.saveAll(toSave);
}
With all that done, the cloud function becomes more legible:
Parse.Cloud.define("saveProject", function(request, response) {
var project;
createProject(params).then(function(result) {
project = result;
return createEmails(request.params.shareEmails);
}).then(function(emailObjects) {
return createEmailClassAssignments(emailObjects, project, request.params.userId);
}).then(function() {
console.log("Successfully saved project");
// I took the liberty of returning the new project to the caller
response.success(project);
}, function(error) {
console.log("There was an error saving" + error.message);
resoonse.error(error);
});
});
CAUTION: obviously, there's no way for me to test any of the foregoing. I strongly urge you to test the functions yourself, preferably individually before expecting the combination to work. Hopefully, the refactor demonstrates a cleaner way to use promises and a reasonable decomposition of parts to test and use individually.
From the looks of your code, you simply need to add a return in front of assignment.save() as you aren't waiting for that to finish otherwise.
Lastly you should add an error catcher at the very end:
.then(null, function(error) {
console.log(error);
response.error(error);
});
Related
I want to make a bot that can restrict new user that join to the group, below is my code, but when I run this, it seemed that it is not working. I guess I misunderstood the way of how 'chatpermission' work, but can't find it.
Here is the 'restrict' method from telegram web: link
(I had already set the telegram webhook to this google app script)
e={
message={
new_chat_member={
language_code=zh-hans,
is_bot=false,
username=jinyulink34,
first_name=Jinyulink,
id=1.292889543E9
},
date=1.58726641E9,
from={
language_code=zh-hans,
is_bot=false,
username=Jinyulink,
first_name=Jinyulinm,
id=9.50729481E8
},
message_id=222.0,
new_chat_participant={
username=jinyulink34,
first_name=Jinyulink,
id=1.292889543E9,
language_code=zh-hans,
is_bot=false
},
new_chat_members=[
Ljava.lang.Object;#6957c279,
chat={
type=supergroup,
username=jinyulin,
id=-1.001365615879E12,
title=Test
}
},
update_id=3995756.0
}
function identificar(e){
var message = e.message.text;
if (e.message.new_chat_members){
var ChatPermissions = {
"can_send_messages":false,
"can_send_media_messages":false,
"can_send_other_messages":false,
"can_add_web_page_previews":false,
"can_send_polls":false,
"can_change_info":false,
"can_invite_users":false,
"can_pin_messages":false
}
var userid=JSON.parse(e.message.from.id);/*logger.log(userid)->shows(950729481)*/
var restrict = {
"method": "restrictChatMember",
"chat_id": String(e.message.chat.id),
"user_id": userid,
"permissions":JSON.stringify(ChatPermissions)
}
start(restrict);
}
}
function start(payload) {
var data = {
"method": "post",
"payload": payload
}
var returned = UrlFetchApp.fetch("https://api.telegram.org/bot-token/", data);
}
I'm getting the following error:
{"ok":false,"error_code":400,"description":"Bad Request: wrong user_id specified"}
I'm really confused at this point with losing data and can't figure out why. So I write a service to send GPS info from a device to an endpoint.
I'm using pm2 to launch my processes but the problem is this service sometimes don't send the info to the endpoint, and the device is sending data. The solution until now was restarting the instance in pm2. But this sometimes is not viable because I create a crontab to restart the GPS instance in pm2 every 45 minutes but it happens to lose information in a time window < 45 min this will not work...
I can't figure this out. Why I lose the data and restarting everything is okay? I saw a post in stack overflow about lost data in node.js process when the child sends the data to the parent and I read about 2 possible causes:
The child is not reading the GPS data info quickly enough in time to make a post and send info.
The child needs to make a JSON.stringify of the content sent to parent and parent needs to make a JSON.parse of the info received.
Here's my code:
var child_process = require("child_process");
var argv = require('minimist')(process.argv.slice(2));
//ex: nsGPSService.js -d 1111-11-11-111
var deviceId = argv.d;
var processDevices = [];
function runParent() {
setTimeout(function() {
return Database.Devices.getDevices().then(function(devices) {
return new Promise(function(resolve, reject) {
async.each(devices, function(device, callback) {
var result = _.filter(processDevices, { "id": device.id });
if (result.length == 0) {
var process = child_process.fork(__dirname + '/nsGPSService.js', ["-d", device.id]);
processDevices.push({ "process": process, "id": device.id });
process.on('message', function(data) {
//receber mensagens do filho
if (data.reason == "deleted") {
//child end process and alerts parent to remove from the list
var index = _.findIndex(processDevices, { "id": data.deviceId });
processDevices.splice(index, 1);
}
});
process.on('exit', function(code) {});
process.on("uncaughtException", function(error) {
process.exit(1);
});
}
callback();
}, function(error) {
error ? reject(error) : resolve();
});
}).then(function() {
runParent()
}).catch(function(error) {
runParent()
});
});
}, 5000);
}
if (!deviceId) {
return runParent();
}
function runChild(id) {
setTimeout(function() {
return Database.Devices.getDeviceById(id).then(function(device) {
if (!device) {
proccess.send({
"deviceId": id,
"reason": "deleted"
});
process.exit();
return;
}
return Controllers.Gps.getRadioInfo('gps', 'info', {}, device).then(function(data) {
return Controllers.Gps.sendDeviceInfo(data, device);
}).then(function() {
return runChild(id);
}).catch(function(e) {
return runChild(id);
});
});
}, 5000);
}
I really need to figure this out because I never know when I need to restart the service because I'm not getting info when in reality I'm receiving...
Which solution is really viable in my scenario and anyone can figure this problem?
When a user shares a post I want to be able to give them a reward point for doing so. I am calling the cloud function from xcode like this.
PFCloud.callFunction(inBackground: "shares", withParameters: ["objectID" : "z2pU3UDFrh"])
I hardcoded an object id for now just to check if its working.
Here is my cloud code function that gets called
Parse.Cloud.define("shares", function(request, response) {
var shareQuery = new Parse.Query("Parse.POSTS");
shareQuery.get(request.params.objectID, {
success: function(object) {
console.log(object)
object.increment("score");
object.save();
},
error: function(error) { },
useMasterKey: true
});
});
when I check the logs it prints "undefined" and the score remains unchanged
Replace var shareQuery = new Parse.Query("Parse.POSTS");
with var shareQuery = new Parse.Query("POSTS");
Parse.Cloud.define("shares", function(request, response) {
var shareQuery = new Parse.Query("POSTS");
shareQuery.get(request.params.objectID, {
success: function(object) {
console.log(object)
object.increment("score");
object.save();
},
error: function(error) {
console.error(error)
},
useMasterKey: true
});
});
newbie here.
I am trying to understand how I need to structure asynchronous calls within my controller to fit my specific use case:
Consider the following code snippet from an Angular Module in "service.js" within my project:
function getSearchObjects(projectName, title) {
var payload = JSON.stringify({
"title": title
});
var request = $http({
method: 'post',
url: URL + '/search/' + projectName,
data: payload
});
return request.then(handleSuccess, handleError);
};
function runQuery(projectName, fromDate, toDate, sort, direction, columns) {
var from = Date.parse(fromDate);
var to = Date.parse(toDate);
var payload = JSON.stringify({
"fromDate": from,
"toDate": to,
"sort": sort,
"direction": direction,
"columns": columns
});
console.log(payload);
var request = $http({
method: 'post',
url: URL + '/query/' + projectName,
data: payload
});
return request.then(handleSuccess, handleError);
}
function handleSuccess(response) {
return response.data;
};
function handleError(response) {
if (!angular.isObject( response.data ) || !response.data.error) {
return( $q.reject( "An unknown error occurred." ) );
}
return $q.reject( response.data.error );
};
});
Within my controller, I am trying to troubleshoot the following function:
$scope.submit = function() {
var objectProperties = exportsStorageService.getSearchObjects($scope.selected.project.name, $scope.selected.search)
.then(function(result) {
exportsStorageService.runQuery($scope.selected.project.name, $scope.selected.start_date, $scope.selected.end_date, objectProperties.sort, objectProperties.direction, objectProperties.columns)
},
function(error) {
console.log(error);
});
};
getSearchObjects matches a title ($scope.selected.search) selected in my UI and grabs the following more detailed object from an API call:
{ title: 'Duplication Example',
sort: '#_traac-timestamp',
direction: 'desc',
columns: [ '#_traac-remote_ip', 'c-platform-m-distinct-id_s', '_type' ] }
I am trying to grab the properties returned from getSearchObjects and pass them along with a few user selected values from my UI to runQuery, which then returns data from a database to the user, but when I check the values passed to runQuery using the above logic in my controller, I get the following values. All of the objectProperties values I am attempting to pass to runQuery are undefined:
project_name: "Example Project"
start_date: 1499770800000
end_date: 1499943600000
sort: undefined
direction: undefined
columns: undefined
I have been trying to debug this, but I am too new to using Angular and asynchronous calls to really understand what I am doing wrong. My best guess currently is that I am calling runQuery before the values retrieved from getSearchObjects are attached to objectProperties. Either that or I am incorrectly referencing the properties within the objectProperties variable.
Could someone help me troubleshoot this issue, and better understand what I am doing wrong?
Thank you in advance for your help!
When you do this:
var objectProperties = some async function...
You are assigning the promise of the async function to the variable, not the result of it.
The result is coming in the .then, like you declared:
.then(function(result) { ... }
So, instead of objectProperties.sort, objectProperties.direction, objectProperties.columns, try using result.sort, result.direction, result.columns :)
If you are new to Promises, take a look at this simple, but great tutorial.
EDIT
Based on your comment, you are receiving, inside the response.data, the following object:
{"objectMatch": {
"title": "doc-event",
"sort": "#_traac-timestamp",
"direction": "desc",
"columns": [
"m-doc-name_s",
"m-user_s",
"m-full-action-type_s",
"m-event-action-descriptor_s"
]}
}
So you have: response > data > objectMatch > properties you want.
The response.data you are extracting on your handleSuccess function:
function handleSuccess(response) {
return response.data;
};
So here, your result is response.data, containing the property objectMatch.
$scope.submit = function() {
var objectProperties = exportsStorageService.getSearchObjects($scope.selected.project.name, $scope.selected.search)
.then(function(result) {
...
},
...
If all of that is correct, you should be able to access the values you want using result.objectMatch.<sort, direction or columns>, like:
exportsStorageService.runQuery($scope.selected.project.name, $scope.selected.start_date, $scope.selected.end_date,
result.objectMatch.sort, result.objectMatch.direction, result.objectMatch.columns)
I'm trying to make a cloud function which saves the sender's objectId and username as an array, inside the array "request", for the target and have the target's objectId and username saved as an array, in the array "pending" for the sender.
Parse.Cloud.define("newGameRequest", function(request, response) {//A
// Get the user who called the function
var user = request.user;
var target;
var query = new Parse.Query(Parse.User);
query.get(request.params.friendId, {
success: function(object) {
var target = object;
var friendInfo = [target.objectId, target.username];
var userInfo = [user.objectId, user.username];
user.add("pending",friendInfo);
target.add("request",userInfo);
Parse.Object.saveAll([user, target], { useMasterKey: true });
response.success("Success");
},
error: function(object, error) {
response.error(error);
}
});
});
Looking in the data browser shows that the arrays for each respective user were saved, but saved with null values only ([[null,null]] for both).
The call is from an iOS device and is the following:
[PFCloud callFunctionInBackground:#"newGameRequest"
withParameters:#{#"friendId": self.friend.objectId}
block:^(NSString *result, NSError *error) {
if (!error) {
}
else {
NSLog(#"%#",result);
}
}];
self.friend.objectId has been tested and is the right result.
What is the issue with my cloud code?
I'm an idiot.
getting the object Id of user is the like the following:
user.id
and getting the username is done like this:
user.getUsername()