Checking if relationship exists using CloudCode Parse.com - javascript

How would I check to see if a given PFUser is friends with another user given a PFUser and an array of other PFUsers in the cloud?
Something like:
Parse.Cloud.beforeSave("Message", function(request, response) {
if (request.object.get("creator") != NULL) {
var user = request.object.get("creator");
var error = false;
for (somepfuser in request.object.get("recipients")
if (user and somepfuser are NOT in friends table)
error = true;
if (error)
response.error("you must be friends to send message");
else
response.success();
});

I suggest you to make a promise series where each promise check the relationship between the users pair in the friends table. Moreover, you should do it in a custom function instead of an event listener (like beforeSave or afterSave ... ) due the Parse restrictions on the time execution limit. For event listener like these you have 3 seconds maximum, in a custom function you'll have up to 7 seconds before parse drop the request.
see promise series reference: https://parse.com/docs/js_guide#promises-series
and promise series example: wait for query to finish before sending response
hope it helps

Related

Javascript Unique Array for each Socket User

i have a Problem!
When two users playing a game and they click very near at the same time, all two users get what only the 2nd user must get.
Is it possible to make an array by his userid or something?
I also have for anti spam like that
anti_spam[socket.request.session.passport.user.id]
But thats a json
If i try the same or array, i get error SyntaxError: Unexpected token [
How can i make sure, that each user get only his own items and not items from another user when opening at the same time?
I use sockets, here is a unique userid socket.request.session.passport.user.id
This is the array
var WinItem = [];
And if two users start like milliseconds same time, than the second overwrite the first...
! CODE NOT TESTED!
Wait wait wait wait...
So I only know ws (Node.js) but there you first get a onConnection event.
In that function you add a UID to the connection element. Then if you receive a message from the client you should have you're connection object and therefore an UID. You now can store the won item in the connection object (if you want) maybe like so:
ws.onConnection = con => {
con.UID = generateUID();
con.inventory = [];
con.onMessage = msg => {
[..STUFF/CODE/YAY...]
con.inventory.push(item);
con.send("You've won wooohoo");
});
});
Did you mean something like this??
Otherwise please be more specific about what you want.
(You can also store the stuff somewhere else together with the UID but that would add quiet some code)

NodeJS Event Emitter Blocking Issue

I have a node application handling some ZeroMQ events coming from another application utilizing the Node-ZMQ bindings found here: https://github.com/JustinTulloss/zeromq.node
The issue I am running into is one of the operations from an event takes a long time to process and this appears to be blocking any other event from being processed during this time. Although the application is not currently clustered, doing so would only afford a few more threads and doesn't really solve the issue. I am wondering if there is a way of allowing for these async calls to not block other incoming requests while they process, and how I might go about implementing them.
Here is a highly condensed/contrived code example of what I am doing currently:
var zmq = require('zmq');
var zmqResponder = zmq.socket('rep');
var Client = require('node-rest-client').Client;
var client = new Client();
zmqResponder.on('message', function (msg, data) {
var parsed = JSON.parse(msg);
logging.info('ZMQ Request received: ' + parsed.event);
switch (parsed.event) {
case 'create':
//Typically short running process, not an issue
case 'update':
//Long running process this is the issue
serverRequest().then(function(response){
zmqResponder.send(JSON.stringify(response));
});
}
});
function serverRequest(){
var deferred = Q.defer();
client.get(function (data, response) {
if (response.statusCode !== 200) {
deferred.reject(data.data);
} else {
deferred.resolve(data.data);
}
});
return deferred.promise;
}
EDIT** Here's a gist of the code: https://gist.github.com/battlecow/cd0c2233e9f197ec0049
I think, through the comment thread, I've identified your issue. REQ/REP has a strict synchronous message order guarantee... You must receive-send-receive-send-etc. REQ must start with send and REP must start with receive. So, you're only processing one message at a time because the socket types you've chosen enforce that.
If you were using a different, non-event-driven language, you'd likely get an error telling you what you'd done wrong when you tried to send or receive twice in a row, but node lets you do it and just queues the subsequent messages until it's their turn in the message order.
You want to change REQ/REP to DEALER/ROUTER and it'll work the way you expect. You'll have to change your logic slightly for the ROUTER socket to get it to send appropriately, but everything else should work the same.
Rough example code, using the relevant portions of the posted gist:
var zmqResponder = zmq.socket('router');
zmqResponder.on('message', function (msg, data) {
var peer_id = msg[0];
var parsed = JSON.parse(msg[1]);
switch (parsed.event) {
case 'create':
// build parsedResponse, then...
zmqResponder.send([peer_id, JSON.stringify(parsedResponse)]);
break;
}
});
zmqResponder.bind('tcp://*:5668', function (err) {
if (err) {
logging.error(err);
} else {
logging.info("ZMQ awaiting orders on port 5668");
}
});
... you need to grab the peer_id (or whatever you want to call it, in ZMQ nomenclature it's the socket ID of the socket you're sending from, think of it as an "address" of sorts) from the first frame of the message you receive, and then use send it as the first frame of the message you send back.
By the way, I just noticed in your gist you are both connect()-ing and bind()-ing on the same socket (zmq.js lines 52 & 143, respectively). Don't do that. Inferring from other clues, you just want to bind() on this side of the process.

Parse JavaScript SDK and Promise Chaining

I have created a Background Job in Parse Cloud Code that sends out email notifications based on a date in one of my Parse classes.
Here is the idea: Query the class that contains the date. Iterate over each object returned and check the date field. If the date is equal to today, send out an email notification, change the date to null and save it back to Parse.
However, it seems that not all the objects are saved back to Parse. I suspect this an issue with my promise chains, but I am having a hard time diagnosing the exact issue or how to fix it. Below is relevant code
Parse.Cloud.job("job", function(request, status) {
// Query for all users
var query = new Parse.Query(className);
query.each(function(object) {
if (condition) {
object.set(key, false);
object.save(null, {
success:function(object){
// This never executes!
},
error: function(error){
}
}).then(function(){
// This never executes
console.log("Save objects successful");
},
function(error){
status.error("Uh oh, something went wrong saving object");
});
// Set hiatus end successful
Mailgun.sendEmail({
});
}
});
});
This line console.log("Save objects successful"); in the objects.save() promise chain does not ever get executed - even when the subscription object is successfully saved to Parse.
Additionally, if there are more than 5 objects returned by the query, only the first 5 are successfully saved back to Parse. Any additional saves are not executed and email notifications are not sent.
I'd clean it up as follows, relying on Promise.when ...
var savePromises = []; // this will collect save promises
var emailPromises = []; // this will collect email promises
// your code to setup the query here
// notice that this uses find() here, not each()
query.find(function(subscriptions) {
_.each(subscriptions, function(subscription) { // or a for loop, if you don't use underscore
// modify each subscription, then
savePromises.push(subscription.save());
// prepare each email then
var emailPromise = Mailgun.sendEmail({ /* your email params object here */ });
emailPromises.push(emailPromise);
});
// now do the saves
return Parse.Promise.when(savePromises);
}).then(function() {
// now do the emails
return Parse.Promise.when(emailPromises);
}).then(function() {
// Set the job's success status
status.success("Subscriptions successfully fetched");
// and so on with your code
You might also consider combining the saves and the emails into one big array of promises, but it might be better to do it in two batches serially since they have different failure modes.

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

Youtube Data API v. 3 - Display channel author name of private / deleted video

This is one of them Youtube internal decisions that is highly counter-productive. Correct me if I'm wrong, but they currently don't want to expose the channel name of private / deleted videos. They even call it a 'bug' if it happens ( http://code.google.com/p/gdata-issues/issues/detail?id=5893 ). So what happens if I solely request video IDs that were public beforehand but then later became private or deleted? I will get, via usage of Youube Data API v3, a "response is undefined" error message in console. WHat happens when that message happens? My code breaks!
This is the code I currently use:
function DisplayThemVideos(yeah) {
var yeah = $("#ThoseMissingIDs").text();
var vidrequestOptions = {
id: yeah,
part: 'snippet',
fields: 'items(id),items(snippet(channelId)),items(snippet(channelTitle)),
items(snippet(title)),items(snippet(thumbnails(default)))'
};
var vidrequest = gapi.client.youtube.videos.list(vidrequestOptions);
vidrequest.execute(function(response) {
var videoIdItems = response.result.items;
if (videoIdItems) { // If results
displayResults(videoIdItems);
} else { // if NO results
alert('Sawwy, YouLose, thx to Youtube!'); // This alert never fires!
}
});
}
Now the undefined "response" is actually an empty request sent back to my app from Youtube server. Youtube answers back with an empty "item" tag which obviously doesn't help much for the displayResults(videoIdItems) function which gets fired without any item to display! They should at least let the channel name and channel ID filter through so a User could click on a link in order to access the remaining public videos of that channel (wouldn't that be productive Jeff P?).
SO my dilemma is to get the else section working like so:
displayResults(videoIdItems);
} else { // if NO results or if results return EMPTY or response "undefined"
alert('Sorry pal but these IDs ___________ are currently missing.
Click the Channel link to access the public videos of that channel.');
}
The else part works as it should for similar API calls as demonstrated in Youtube Data API v.3 sample code, but I guess it currently isn't able to handle empty requests.
So what am I to do? WIll I have to use an ajax call with success fail error handling? Like I said beforehand, the API returns an empty request so the response is legit but the response content comes empty for private/deleted videos, hence, "undefined" with code breaking along the wway.
Any hints leading to a working solution would help! Thx for guidance.
I can only partly answer my question. Easiest is to use Catch of Try - Catch method which detects everything except syntax errors, helpful in detecting an empty response (undefined) and activating your function that can access the Youtube player for displaying current availability status of the missing ID:
...
var vidrequest = gapi.client.youtube.videos.list(vidrequestOptions);
vidrequest.execute(function(response) {
try {
var videoIdItems = response.result.items;
if (videoIdItems) { // If results
displayResults(videoIdItems);
return false;
}
} catch (e) {
// put code here that handles errors such as empty or undefined response
// which otherwise breaks your code
} finally {
// optionally, put code here that will always trigger
}
});
Hope this helps others.

Categories