Unable to send Push notification to Installation via Parse CloudCode - parse-platform

I'm attempting to send a push notification to a specific user's installation.
In the user class I have a column which is a pointer to an installation instance. Here's my code:
Parse.Cloud.beforeSave("Notification", function(request, response) {
var user;
if (!request.object.get("reciever")) {
response.error("Please include a user");
} else {
user = request.object.get("reciever");
}
var message = request.object.get("message");
var query = new Parse.Query(Parse.User);
query.include("installation");
query.equalTo("objectID", user.get("objectID"));
query.find({
success: function(result) {
var obj = result[0];
var intst = obj.get("installation");
console.log(intst);
Parse.Push.send({
where: intst,
data: {
alert: message
}
}, {
success: function() {
console.log("Push was successful");
response.success();
},
error: function(error) {
console.error(error);
response.error(error);
}
});
},
error: function(error) {
response.error(error);
}
});
});
From looking at my logs, the installation instance was received correctly. When sending, I get the following error:
Failed to create new object, with error code:
{"code":115,"message":"Missing the push channels."}
What am I doing wrong?

The Parse documentation specifys this as an example for sending Push notificatons using Javascript
Parse.Push.send({
channels: [ "Giants", "Mets" ],
data: { alert: "The Giants won against the Mets 2-3." }
}, {
success: function() {
// Push was successful
}, error: function(error) {
// Handle error
}
});
What you are missing is the 'channels: ["Giants", "Mets"]' section of this push notifcation. If you go into your installation table in parse you will notice there is a Channels column and this is what defines who the pushes get sent to. In this example the push will go to anyone who has signed up for the channels 'Giants' and 'Mets'.

Related

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);
}
});
});

Parse Cloud job

I have a job in the parse cloud, inside the job I've a Parse.Cloud.run, when I run this function works fine and parse data base is update, but in the in the cloud job statuses appears failed. Here's my code:
Thanks in advance.
Parse.Cloud.job("updateTopsThreeJob", function(request, status) {
Parse.Cloud.useMasterKey();
var query = new Parse.Query("_User");
query.descending("followersOfMe");
query.limit(3);
query.find({
success: function(results) {
var TestJS = Parse.Object.extend("testJS");
var test = new TestJS();
var listTops = [];
for (var i = 0; i < results.length; i++) {
var object = results[i].get("username");
listTops.push(object);
}
Parse.Cloud.run("updateTopsThree", {objects: listTops}, {
success: function(result) {
status.success("Migration completed successfully.");
response.success(result)
},
error: function(error) {
status.error("Uh oh, something went wrong.");
}
});
response.success(listTops);
},
error: function(error) {
response.error("failed");
}
});
});
Parse.Cloud.define("updateTopsThree", function(request, response) {
var tops = Parse.Object.extend("testJS");
var query = new Parse.Query(tops);
query.get(ObjIDs.topsThreeID(), {
success: function(topsThree) {
topsThree.set("topsThree", request.params.objects);
topsThree.save();
response.success(topsThree);
},
error: function(object, error) {
response.error(error);
}
});
});
The Parse Cloud Code runs much like any other javascript file. In order to declare another function to be called within the parse .js file, such as in this case, you do not need to define the function using Parse syntax. Define and call it just as you would a normal Javascript function.
Use this to call the function within your Parse.job:
updateTopsThree(topThreeObjects);
Define function:
function updateTopsThree(topObjects) {
var tops = Parse.Object.extend("testJS");
var query = new Parse.Query(tops);
query.get(ObjIDs.topsThreeID(), {
success: function(topsThree) {
topsThree.set("topsThree", topObjects);
topsThree.save();
response.success(topsThree);
},
error: function(object, error) {
response.error(error);
}
});
}
Thanks, but finally I´ve solved my problem as follows: I´ve created a cloud function like this:
Parse.Cloud.define("setLikesInDB", function(request, response) {
var query = new Parse.Query("testJS");
query.get(ObjIDs.topsLikesID(), {
success: function(topsThree) {
topsThree.set("topsLikes", "likes");
topsThree.save();
response.success(topsThree)
},
error: function(object, error) {
response.error(error);
}
});
});
And then into my Parse.Cloud.Job I´ve called a cloud function like this:
Parse.Cloud.run('setLikesInDB', {obj : listTops}, {
success: function(result) {
response.success(result);
},
error: function(error) {
response.error('some error')
}
});
This way works fine.
I hope this helps someone else.

Meteor Store Image URL as Avatar

I have an image uploader that uses amazon s3 to store files. I am trying to update my users.avatar value to the amazon url upon upload.
Here is my upload event:
Template.avatarUpload.events({
"click button.upload": function(){
event.preventDefault();
var files = $("input.avatar-upload")[0].files
S3.upload({
files: files,
path: "avatars"
}, function(e,r) {
console.log(r);
});
}
})
I tried something like the following based on a few other stackoverflow QA's:
First this, but then I ran into a problem where I couldn't store files.url without getting an error (update failed: MongoError: '$set' is empty. You must specify a field like so: {$mod: {: ...}})
Meteor.users.update({_id:Meteor.user()._id}, {$set:{"profile.name": files.url}})
Then like this:
var set = {avatar: {}};
set.avatar[files.url];
Meteor.users.update({
_id: Meteor.user()._id
}, {$set: set});
But no luck. Basically trying to take the URL and store it in users.avatar. My json looks like this:
users = [{
username: "normcore",
avatar: "avatar_url"
}]
Post the whole code for the helper.
Overall, this is something your code should look like:
Template.avatarUpload.events({
"click button.upload": function(){
event.preventDefault();
var files = $("input.avatar-upload")[0].files
S3.upload({
files: files,
path: "avatars"
}, function(e,r) {
if(e) {
console.log(r);
} else if(r) {
console.log(r);
Meteor.users.update(Meteor.user(), {$set: {avatar: r.url}}); // Correct this to actual URL in response. If multiple files
}
});
},
})
You'll have to figure out how to handle the case of multiple files upload. One way is to not enable multi in your input tag.
This is how we got it working. I'll leave this here for anyone having the same issue:
Client side:
Template.avatarUpload.events({
"click button.upload": function(){
event.preventDefault();
var files = $("input.avatar-upload")[0].files
S3.upload({
files: files,
path: "avatars"
}, function(e,r) {
console.log(r);
console.log(Meteor.user());
var user = Meteor.user();
var student = {"_id": user._id };
console.log(student);
var url = r.secure_url;
Meteor.call('updateAvatar', student, url, function(error) {
if (error) {
return console.log(error);
} else {
return console.log('success');
}
});
});
}
});
Template.avatarUpload.helpers({
"files": function(){
return S3.collection.find();
}
});
Server Side:
Meteor.methods({
updateAvatar: function(user, url) {
console.log(user);
check(user, {
_id: String
});
check(url, String);
return Meteor.users.update({_id:Meteor.user()._id},
{
$set: {
"profile.avatar": url
}
}, function(error) {
if (error) {
return console.log(error);
}
});
}
});

Parse.com Cloud Code Failing

I am trying to use the following cloud code
Parse.Cloud.job("sendAlert", function(sendAlert, status) {
Parse.Push.send({
data: {
"content-available": 1,
}
}, {
success: function() {
status.success("Push Worked!!!");
},
error: function(error) {
status.error("Uh oh, something went wrong.");
}
});
});
to send a silent push alert.
It fails and calls the error function. What is the issue?
Allow me to answer my own question, I got the following code to work.
var query = new Parse.Query(Parse.Installation);
Parse.Cloud.job("sendAlert", function(sendAlert, status) {
Parse.Push.send({
where: query, // Set our Installation query
data: {
alert: "Broadcast to everyone"
}
}, {
success: function() {
status.success("Push Worked!!!");
},
error: function(error) {
status.error("Uh oh, something went wrong.")
}
});
});

Parse - Get object from pointer on beforeSave?

I have an Offer object that I send to the server, when this offer is about to be created I need to send a push notification to the user. Offer has a pointer to User an the field is called "to".
How can I fetch an object from a pointer?
Parse.Cloud.beforeSave("Request", function(request, response) {
var userQuery = new Parse.Query("User")
userQuery.get(request.object.get("to").id, {
success: function (user) {
console.log("user: ", user);
var installationQuery = new Parse.Query("Installation");
installationQuery.equalTo("user", user);
Parse.Push.send({
where : installationQuery,
data : {
alert : "HEllo"
},
success : function() {
},
error : function() {
console.log("error finding installation: " + error);
}
});
},
error : function (error) {
console.log("ERRRRRRRRRR");
}
});
response.success();
});
To answer your question directly, you can use Parse.Query.get() or Parse.Object.fetch() to retrieve the object.
I'm assuming that the problem you see is that the object saves but the push notification isn't happening. The cause is that you're not waiting for the get() to complete before calling response.success() and returning.
Here's a couple ways to reconcile that:
Your existing code but with response.success() moved up:
Parse.Cloud.beforeSave("Request", function(request, response) {
var userQuery = new Parse.Query("User")
userQuery.get(request.object.get("to").id, {
success: function (user) {
console.log("user: ", user);
var installationQuery = new Parse.Query("Installation");
installationQuery.equalTo("user", user);
Parse.Push.send({
where : installationQuery,
data : {
alert : "HEllo"
},
success : function() {
response.success();
},
error : function() {
console.log("error finding installation: " + error);
}
});
},
error : function (error) {
console.log("ERRRRRRRRRR");
}
});
});
Simplified with Promises
Parse.Cloud.beforeSave("Request", function(request, response) {
request.object.get("to").fetch().then(function(user) {
var installationQuery = new Parse.Query("Installation");
installationQuery.equalTo("user", user);
return Parse.Push.send({
where : installationQuery,
data : {
alert : "HEllo"
}
});
}).then(function() {
response.success();
}, response.error);
});
Further simplified. If you're not using the data within user, you shouldn't need to fetch it just to pass a pointer to a query.
Parse.Cloud.beforeSave("Request", function(request, response) {
var installationQuery = new Parse.Query("Installation");
installationQuery.equalTo("user", request.object.get("to"));
return Parse.Push.send({
where : installationQuery,
data : {
alert : "HEllo"
}
}).then(function() {
response.success();
}, response.error);
});

Resources