How to know if value changed in Parse Cloud Code afterSave hook? - javascript

I want to send push notifications every time the value of a single key of my object changes in a parse cloud code afterSave hook.
Parse.Cloud.afterSave("Channel", function(request) {
var channel = request.object
// TODO: check if value of channel key "state" was changed
});
How can I check if the value of the key state was updated?
This is all data I can get from the request object: http://parseplatform.org/Parse-SDK-JS/api/v1.11.0/Parse.Cloud.html#.TriggerRequest
The solution suggested in this thread feels wrong: Parse Javascript API Cloud Code afterSave with access to beforeSave values
I know I can do this via the dirty method in the beforeSave hook. However this does not work for me. Why? If I do send push notifications to many users this takes some time. The clients receiving the push notifications start requesting the updated channel object from the server. However they might receive an old version of the object because as long as beforeSave has not finished sending all pushes the channel object is not persisted in the database.

You can use request.original. For example:
Parse.Cloud.afterSave("Channel", function(request) {
var channel = request.object;
var channel_orig = request.original;
if (channel.get("status") != channel_orig.get("status")) {
// Send push notification.
}
});
The documentation states about request.original, that: "If set, the object, as currently stored." I'm not sure in what cases it would be set, though. In my use cases it works as provided in the code snippet above.

Related

What is the recommended way to write then read data in my firebase app?

I was wondering the best way to write then read data in my firebase app?
I currently make a post request and use set to set some data at a new location. the user does this via a modal
I then close the modal and want the user to see the updated content below (without a page refresh) is there anyway to use the completion callback to send back the new data?
should I make an api request again straight after?
or should I handle this client side?
so far I had this:
firebaseApp
.database()
.ref(`users/${userID}/${endpoint}`)
.set(setSchema, (error) => {
if(error){
consolse.log('error')
} else {
console.log('success')
}
})
is there anywhere on the success line we can return data?
thanks
Is there anyway to use the completion callback to send back the new
data?
No, the set() method does not return the value that is written at the Realtime Database node.
Should I make an api request again straight after?
It depends, see the next question.
Or should I handle this client side?
Normally you should be able to handle that in the client since you know what value (Object) you have passed to the set() method.
The only case when this is not true is when you use a sentinel (placeholder) value through firebase.database.ServerValue.
For example is you have a property startedAt: firebase.database.ServerValue.TIMESTAMP in the object you pass to the set() method, the exact value for startedAt will be calculated by the server, therefore you don't know it in advance. If you want to get this value, you need to query the database for it.

Retrieve data in Firebase exactly once

I have a real time database with firebase and i'm using the following code to connect to the database and get some data from it.
window.onload = function(){
var databaseWebsites = firebase.database().ref('/websites').orderByChild('votes');
console.log(databaseWebsites);
databaseWebsites.on('value', function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var webTemp = document.getElementById(childSnapshot.val().id);
webTemp.style.order = order;
var webText = webTemp.getElementsByClassName('likeText');
webText[0].innerHTML = childSnapshot.val().votes;
order--;
});
order = 0;
});
It gets all the data, in order and uses it correctly.
The problem is, I don't want the data on the front end to update until the user refreshes the page. The system is a voting system that is ordered by votes value, if it was constantly updating it would be a bad user experience.
Any help will be appreciated.
Thanks
Change the on to once, Firebase on listens for changes in your database node and sends a response.
databaseWebsites.on('value', function(snapshot) {
to
databaseWebsites.once('value', function(snapshot) {
An excerpt from Firebase doc
The value event is called every time data is changed at the specified
database reference, including changes to children. To limit the size
of your snapshots, attach only at the lowest level needed for watching
changes.
Visit this url to read more
The accepted response is not correct (maybe outdated?) because once() requires to add a then()
It's actually
databaseWebsites.once('value').then(function(snapshot) {}
that replaces
databaseWebsites.on('value', function(snapshot) {}

Storing socket.io emits in global array + Node.js

Is there a way to store all emits of socket.io in a global array so that I can loop through the emits when a new user joins the website and can pickup where the 'canvas drawing' currently is. I want the new user to see what work has already been done and then collaborate on it.
Any other ways I could approach towards this?
If you just want the emits stored for the duration of the server running, you can simply declare a module level array and push each emit into that array. Then, at any future time during that server execution, you can consult the array.
If, you want the emits stored across server invocations, then you would need to store them to some persistent storage (file, database, etc...).
// save incoming data from one particular message
var emitsForSomeMessage = [];
io.on("someMessage", function(data) {
// save the incoming data for future reference
emitsForSomeMessage.push(data);
});
Or, if you're trying to store all outgoing .emit() data, then you can override that method and save away what is sent.
var outgoingEmits = [];
(function() {
var oldEmit = io.emit;
io.emit = function(msg, data) {
outgoingEmits.push({msg: msg, data: data});
return oldEmit.apply(this, arguments);
};
})();
Since there are many different messages that may be sent or received, you can add your own logic to decide which messages are saved in the array or not.

MeteorJS - No user system, how to filter data at the client end?

The title might sound strange, but I have a website that will query some data in a Mongo collection. However, there is no user system (no logins, etc). Everyone is an anonymouse user.
The issue is that I need to query some data on the Mongo collection based on the input text boxes the user gives. Hence I cannot use this.userId to insert a row of specifications, and the server end reads this specifications, and sends the data to the client.
Hence:
// Code ran at the server
if (Meteor.isServer)
{
Meteor.publish("comments", function ()
{
return comments.find();
});
}
// Code ran at the client
if (Meteor.isClient)
{
Template.body.helpers
(
{
comments: function ()
{
return comments.find()
// Add code to try to parse out the data that we don't want here
}
}
);
}
It seems possible that at the user end I filter some data based on some user input. However, it seems that if I use return comments.find() the server will be sending a lot of data to the client, then the client would take the job of cleaning the data.
By a lot of data, there shouldn't be much (10,000 rows), but let's assume that there are a million rows, what should I do?
I'm very new to MeteorJS, just completed the tutorial, any advice is appreciated!
My advice is to read the docs, in particular the section on Publish and Subscribe.
By changing the signature of your publish function above to one that takes an argument, you can filter the collection on the server, and limiting the data transferred to what is required.
Meteor.publish("comments", function (postId)
{
return comments.find({post_id: postId});
});
Then on the client you will need a subscribe call that passes a value for the argument.
Meteor.subscribe("comments", postId)
Ensure you have removed the autopublish package, or it will ignore this filtering.

Parse iOS SDK: Understanding Cloud Code

Scenario = I am slowly but surely wrapping my head around what is going on with Parse's cloud code features. I just need some help from those who would like to answer some short, relatively simple questions about what is going on in some sample cloud code functions.
The code I will use in this example is below
1) cloud code
Parse.Cloud.define('editUser', function(request, response) {
var userId = request.params.userId,
newColText = request.params.newColText;
var User = Parse.Object.extend('_User'),
user = new User({ objectId: userId });
user.set('new_col', newColText);
Parse.Cloud.useMasterKey();
user.save().then(function(user) {
response.success(user);
}, function(error) {
response.error(error)
});
});
2) called from iOS
[PFCloud callFunction:#"editUser" withParameters:#{
#"userId": #"someuseridhere",
#"newColText": #"new text!"
}];
This code was taken from here
Question 1 =
(request, response)
I am confused by what this is. Is this like typecasting in iOS where I am saying (in the iOS call) I want to pass an NSString into this function ("userId") and inside the cloud code function I'm going to call it "request"? Is that what's going on here?
Question 2 =
Parse.Object.extend('_User')
Is this grabbing the "User" class from the Parse database so that a "PFObject" of sorts can update it by creating a new "user" in the line below it?
Is this like a...
PFObject *userObject = [PFObject objectWithClassName:#"User"]?
Question 3 =
user.set('new_col', newColText)
This obviously 'sets' the values to be saved to the PFUser (~I think). I know that the "newColText" variable is the text that is to be set - but what is 'new_col'? Only thing I can think of is that this sets the name of a new column in the database of whatever type is being passed through the "request"?
Is this like a...
[[PFUser currentUser] setObject: forKey:]
Question 4 =
Parse.Cloud.useMasterKey()
Without getting too technical, is this basically all I have to type before I can edit a "User" object from another User?
Question 5 =
user.save().then(function(user) {
response.success(user);
}
Is this like a...
[user saveInBackgroundWithBlock:]?
and if so, is
function(error) {
response.error(error)
just setting what happens if there is an error in the saveInBackgroundWithBlock?
Please keep in mind, I know iOS - not JavaScript. So try to be as descriptive as possible to someone who understands the Apple realm.
Here's my take on your questions:
The request parameter is for you to access everything that is part of the request/call to your cloud function, it includes the parameters passed (request.params), the User that is authenticated on the client (request.user) and some other things you can learn about in the documentation. The response is for you to send information back to the calling code, you generally call response.success() or response.error() with an optional string/object/etc that gets included in the response, again documentation here.
That's a way of creating an instance of a User, which because it is a special internal class is named _User instead, same with _Role and _Installation. It is creating an instance of the user with an ID, not creating a new one (which wouldn't have an ID until saved). When you create an object this way you can "patch" it by just changing the properties you want updated.
Again, look at the documentation or an example, the first parameter is the column name (it will be created if it doesn't exist), the second value is what you want that column set to.
You have to do Parse.Cloud.useMasterKey() when you need to do something that the user logged into the client doesn't have permission to do. It means "ignore all security, I know what I'm doing".
You're seeing a promise chain, each step in the chain allows you to pass in a "success" handler and an optional "error" handler. There is some great documentation. It is super handy when you want to do a couple of things in order, e.g.
Sample code:
var post = new Parse.Object('Post');
var comment = new Parse.Object('Comment');
// assume we set a bunch of properties on the post and comment here
post.save().then(function() {
// we know the post is saved, so now we can reference it from our comment
comment.set('post', post);
// return the comment save promise, so we can keep chaining
return comment.save();
}).then(function() {
// success!
response.success();
}, function(error) {
// uh oh!
// this catches errors anywhere in the chain
response.error(error);
});
I'm pretty much at the same place as you are, but here are my thoughts:
No, these are the parameters received by the function. When something calls the editUser cloud function, you'll have those two objects to use: request & response. The request is basically what the iOS device sent to the server, and response is what the server will send to the iOS device.
Not quite that. It's like creating a subclass of _User.
Think of Parse objects types as a database table and it's instances as rows. The set will set (derp) the value of 'newColText' to the attribute/column 'new_col'.
Not sure, never used that function as I don't handle User objects. But might be that.
Pretty much that. But it's more sort of like (pseudo-code, mixing JS with Obj-C):
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error){
if(error){
response.error(error); // mark the function as failed and return the error object to the iOS device
}
else{
response.success(user); // mark the function call as successful and return the user object to the iOS device
}
}];

Categories