Parse iOS SDK: Understanding Cloud Code - javascript

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

Related

Get _id after Accounts.createUser

I'm trying to create a new user and use their _id for another collection right after it's created.
var newUserId = Accounts.createUser({username: "w/e", password:"w/e"});
doesn't work as I thought.
I know if you insert something into a collection it returns the _id, so I'd assume this would be same, but apparently it's not.
"newUserId" ends up being undefined.
I'm not sure if this matter, but I'm creating the user via server side.
Any helps is appreciated, thanks.
*Solved:
Got the code to do what it needed to do.
Accounts.validateNewUser(function (user){
//do something after user creation
});
slap that code into the account.js in the server side.
Once the user was created via
Accounts.createUser({})
the method:
Account.validateNewUser()
fire immediately afterward. Used the user argument to get whatever the new user properties I need (in this case the _id and username) and plugged that into another collection meteor method.
Thanks again!
PS: turns out
Accounts.createUser({})
Actually does return the _id, but you can only see it in the server console, but not the client console, so I apologize for any confusion. That was my mistake.
To debug this, specify a callback then look at the error that is being returned.
Accounts.createUser({username: "w/e", password: "w/e", function(err){
if ( err ) console.log(err);
}

How do I access the data given in the onHttpRequest function in the Firefox Add-on SDK?

I am trying to read the response headers 'name' and 'value'. The end goal is to compare them to some pre-set name and a value to see if they match.
Here is what I have so far, it's the function that run every time I get a response header.
var observer = require("observer-service");
observer.add("http-on-examine-response", onHttpRequest);
function onHttpRequest(subject, data)
{
console.log("request subject...." + subject);
console.log("request data...." + data);
}
The output is as follows:
request subject....[xpconnect wrapped nsISupports]
request data....null
I was hoping to know how to get the rest of the data out of the response.
Any help would be great, thanks.
The subject for http-on-examime-response implements nsIHttpChannel, among some other things. You may use .QueryInterface() or instanceof (which internally kinda uses QueryInteface, so that this works as well) to get to that interface.
const {Ci} = require("chrome");
if (subject instanceof Ci.nsIHttpChannel) {
console.log("content-type", subject.getResponseHeader("content-type"));
subject.visitResponseHeaders(function(header, value) {
console.log(header, value);
});
}
There are a couple of other questions around here going into more detail on how to use these notifications... Also, mxr can help a lot checkout out what interfaces there are, how it fits together and how one could use it (in particular the existing tests are great to see some uses for all kinds of stuff).
There is also the "nsITraceableChannel, Intercept HTTP Traffic" article going into more details, e.g. on how to use nsITraceableChannel to get the payload data from such a channel.

node.js how to handle change of objects and variables in callbacks

I have just started a large project with node.js and I have stumbled upon some callback nightmare issues. I have done some node.js development before, but only small stuff based on tutorials.
I have a user model and a owner model, who is the owner for that user...basically the part I am building in node.js will be a REST service so I need to send a json containing a user and it's owner name. My problem is trying to get the owner name like I would do it in ruby or php and setting it as a property..but it doesn't work.
How do people handle this kind of logic in node.js where you need to change objects in callback functions? Also I do not want it to affect performance.
I am using mysql as the database because this was a requirement. So the code is this:
//find all
req.models.users.find({
'id' : req.query.id
}, function(err, users) {
if (err) console.log(err); //do something better here
else {
var json = [];
users.forEach(function(user) {
user.getOwner(function(err, owner) {
user.accountOwner = owner.name;
json.push(user;
});
});
res.type('application/json');
res.send(json);
}
});
I want to send something like this:
[{
'id': user.id,
'username": user.username,
'email': user.email,
'owner': user.owner.name'
},
{...}]
The problem is you are not understanding the control flow of node code. It doesn't go line by line top to bottom in chronological order. So your issue is your res.send(json) happens BEFORE (in time) your user.getOwner callback executes, so you send your empty json, then you stuff things into the json array after it's already been sent. You need to use something like async.each to do your joins, wait for all of the user`s owners to be populated, and then send the response. Or you could actually let the database join the data by writing a SQL join instead of doing N+1 queries against your database.

Why is node.js variable persisting

I'm making a temporary fake API and am trying to set up a simple request response script in node using express.js to achieve this. It's very strraightforward, A request comes in, is validated and, if valid, is merged with a .json template file and the result returned, thus giving the impression the user was successfully created.
app.post('/agent/user', function(req, res){
var responseTemplate = new jsonRequired('post_user');
var errorTemplate = new jsonRequired('post_user_error');
var payload = req.body;
var responseData;
var hasErrors = false;
console.log('Creating new user');
//Recursive Merge from http://stackoverflow.com/a/383245/284695
responseData = new mergeRecursive(responseTemplate,payload);
if(!payload.username){
hasErrors = true;
errorTemplate.errors.username.push('A username is required.');
}
if (hasErrors){
res.send(errorTemplate,422);
}else{
res.send(responseData,200);
}
});
The problem I'm having is that data is persisting between calls. So if I define a username and name[first] in 1 request and just a username in the 2nd one, both requests come back with the name[first] property.
I have a feeling it's something to do with js closures. Unfortunately, every tutorial I find seems to be about making closures, not avoiding them.
It should work like this:
The client POST's username=user1&name[first]=joe&name[last]=bloggs
The Server loads a json file containing a prepopulated user object: e.g.
{"username":"demo","name":{"first":"John","last":"Doe"}...}
mergeRecursive() merges the payload from the POST request over the template object and returns the new object as the POST response text.
The problem is that with every new request, the server is using the result of step 3 in step 2 instead of reloading the .json file.
That mergeRecursive function has the same caveat as jQuery.extend: it modifies the first object sent into it. In fact, you don't even need to use its return value.
You didn't show the code of jsonRequired function (it's not even clear why you've used new when invoking it), but it looks like this function doesn't create a new object each time it's called, instead fetching this object from some outer repository. Obviously, mergeRecursive modifications for it won't be lost after that function ends.
The solution is using your data object for merging. Like this:
var responseData = {};
...
mergeRecursive(responseData, responseTemplate);
mergeRecursive(responseData, payload);
Merging two objects will make this for you.
If your responseTemplate has parameter, which actual request did not have, then you will end up having it there.
Check definition of word merge ;)
While this doesn't resolve the issue I had, I have found a workaround using the cloneextend package available via npm:
$ npm install cloneextend
This allows me to use the following js:
var ce = require('cloneextend');
...
ce.extend(responseData, responseTemplate);
ce.extend(responseData, payload);

'POST 400 bad request' error when updating parse.com table

I am getting a 'POST 400 bad request' error when trying to update a table on parse.com using JS SDK.
var Gallery = Parse.Object.extend("Gallery");
var gallery = new Gallery();
var activeArtworks = 0;
gallery.save(null, {
success: function(gallery) {
gallery.set("activeArtworks", activeArtworks);
gallery.save();
}
});
Please help!
I can't see how this is any different to the sample code provided by parse here
The sample code you reference creates all of its parameters before setting up the save() method. This is the step you're missing; you need to create the activeArtworks parameter on your gallery instance. Your update is failing because you're trying to update a property that was never created.
I would expect this code to work, though I didn't test it because parse.com requires you to set up an account to run any code, which is silly, and I didn't feel like creating one:
var Gallery = Parse.Object.extend("Gallery");
var gallery = new Gallery();
var activeArtworks = 0;
gallery.activeArtworks = []; // or some more appropriate default if you have one.
gallery.save(null, {
success: function(gallery) {
gallery.set("activeArtworks", activeArtworks);
gallery.save();
}
});
It might also be worth checking if there's any info in the headers of the 400 error (the debug console in your browser will show these in its Network tab). I would expect Parse to give you some sort of information to help you debug issues, and that's the only place it would fit for an HTTP error.
If you had use user.logIn(callback) function with Parse JS, maybe your session invalid. Please check your callback function error code, if error.code==209 (invalid session token), use Parse.User.logOut() and re-login again.
Like this:
if (error.code == 209) {
Parse.User.logOut();
user.logIn(loginCallback);
return;
}
I had this same issue and it was resolved after realising that the data type that I was posting was different to that specified in the columns that I had created.
In my case, I was trying to save an object when I had specified an array when creating the column in the class.

Categories