Socket.io: Client receiving empty object/array despite data existing on server - socket.io

I'm working with socket.io. I am having trouble receiving data from the server even though I can console.log() the data (an array of objects) right before I try to emit the data back to the calling client. If I hard code info into the emit, it will display on the client but when I use the dynamically created array of objects, it doesn't go through. My gut tells me its a asyn issue but I'm using bluebird to manage that. Perhaps its an encoding issue? I tried JSON.stringify and JSON.parse, no dice.
Server
socket.on('getClassList', function(){
sCon.getClassList()
.then(function(data){
console.log(data) //data is an array full of objects
socket.emit('STC_gotDatList', data)
})
})
Hard-Coded Expected Output:
classes['moof'] = {
accessList: [888],
connectedList: [],
firstName: "sallyburnsjellyworth"
}
Client
socket.on('STC_gotDatList', function(info){
console.log(info) //prints [] or {}
})
EDIT:
I remember reading somewhere that console.log() may not be printing data at the time the data is available/populated. Could that be it even though im using Promises? At the time I'm emitting the data, it just hasn't been populated into the array? How would i troubleshoot this scenario?
EDIT2:
A step closer. In order to get anything to return, for some reason I have to return each specific object in the 'classes' array. It wont allow me to send the entire array, for the example I gave above, to get data to the client, I have to return(classes['moof']), can't return(classes) to get the entire array... Not sure why.
EDIT3: Solution: You just can't do it this way. I had to put 'moof' inside classes as a property (className) and then I was able to pass the whole classes array.

How is the 'classes' array created?
The problem might be that it is being given properties dynamically, but has not been created as an object, but rather an array.
If you plan to dynamically add properties to it (like property moof), you should create it as an object (with {}) rather than an array (with []).
Try
var classes = {};//instead of classes = []
//then fill it however you do it
classes[property] = {
accessList: [888],
connectedList: [],
firstName: "sallyburnsjellyworth"
};
Also, the credit goes to Felix, I'm just paraphrasing his answer: https://stackoverflow.com/a/8865468/7573546

Related

Can I use data loader without batching

What I am really interested in with data-loader is the per request caching. For example say my graphql query needs to call getUser("id1") 3x. I would like something to dedupe that call.
However it seems like with data-loader I need to pass in an array of keys into my batch function, and multiple requests will be batched into one api calls.
This has me making a few assumptions that i dont like:
1.) That each service I am calling has a batch api (some of the ones im dealing with do not).
2.) What if multiple calls get batched into 1 api call, and that call fails because 1 of the items was not found. Normally I could handle this by returning null for that field, and that could be a valid case. Now however my entire call may fail, if the batch API decides to throw an error since 1 item was not found.
Is there anyway to use dataloader with single-key requests.
Both assumptions are wrong because the implementation of the batch function is ultimately left up to you. As indicated in the documentation, the only requirements when writing your batch function are as follows:
A batch loading function accepts an Array of keys, and returns a Promise which resolves to an Array of values or Error instances.
So there's no need for the underlying data source to also accept an array of IDs. And there's no need for one or more failed calls to cause the whole function to throw since you can return either null or an error instance for any particular ID in the array you return. In fact, your batch function should never throw and instead should always return an array of one or more errors.
In other words, your implementation of the batch function might look something like:
async function batchFn (ids) {
const result = await Promise.all(ids.map(async (id) => {
try {
const foo = await getFooById(id)
return foo
} catch (e) {
// either return null or the error
}
}))
}
It's worth noting that it's also possible to set the maxBatchSize to 1 to effectively disable batching. However, this doesn't change the requirements for how your batch function is implemented -- it always needs to take an array of IDs and always needs to return an array of values/errors of the same length as the array of IDs.
Daniel's solution is perfectly fine and is in fact what I've used so far, after extracting it into a helper function.
But I just found another solution that does not require that much boilerplate code.
new DataLoader<string, string>(async ([key]) => [await getEntityById(key)], {batch: false});
When we set batch: false then we should always get a key-array of size one passed as argument. We can therefore simply destructure it and return a one-sized array with the data. Notice the brackets arround the return value! If you omit those, this could go horribly wrong e.g. for string values.

Creating a simple, basic page object in Nightwatch.js

Ok, so I've read up on the use of page_objects in nightwatch.js, but I'm still getting issues with it (which I'm convinced is due to something obvious and/or simple).
Using http://nightwatchjs.org/guide/#page-objects as the guide, I added the the file cookieremoval.js in my page_objects folder.
module.exports = {
elements: {
removeCookies: {
selector: '.banner_continue--2NyXA'
}
}
}
In my nightwatch.conf.js file I have;
page_objects_path: "tests/functional/config/page_objects",
And in my test script I have;
module.exports = {
"/cars/road-tax redirects to /car-tax/ ": browser => {
browser.url(browser.launch_url + browser.globals.carReviews)
.assert.urlEquals(browser.launchUrl + "/car-reviews/")
.waitForElementPresent('#cookieRemove', 3000)
.click('#cookieRemove')
.end();
},
};
However, when I run the test, I keep getting an error reading;
Timed out while waiting for element <#cookieRemove>
Any ideas why this is not working?
Many thanks
First of all, you never instantiated your page object. You're asking the browser object to search for an unknown element, that's why it's timing out. Your code should look something like this in your test script: var cookieRemoval = browser.page.cookieremoval(); then use this object to access those variables and functions in your page object. For example, if you wanted to access the remove cookie element, then you would do this cookieRemoval.click('#removeCookies');.
Secondly, you will have to know when to use the global browser object and when to use your page object. If you need to access something within your page object, obviously use the page object to call a function or access a variable. Otherwise, browser won't know the element you're looking for exists. Hope this help you out, I would definitely spend some more time learning about objects and specifically how they're used in nightwatch.js.

parse.com - cloud code - what do i do with a pointer?

Someone doesn't understand what i want. I want to know what i can do with a pointer.
I have js cloud code.
I have a pointer.
What can i do with it?
Example:
var query = new Parse.Query("Messages"); //POINTER QUERY
console.log(userMessages[0].get("messageId"));
console.log("end2");
query.equalTo("objectId",userMessages[position].get("messageId"));
In the example, userMessages is the result of a prior cloud query.
This line
console.log(userMessages[0].get("messageId"));
helpfully outputs
{"__type":"Pointer","className":"Messages","objectId":"5J4eOletgz"}
This is less useful than you might imagine. I cannot seem to call the objectId from it, and the query
query.equalTo("objectId",userMessages[position].get("messageId"));
query.find ({ ... });
returns nothing. Note that the query should find the pointer-object the pointer-points-to, but instead it helpfully throws the error
Error: 102 bad special key: __type
Which is just about useless.
What can i do with a pointer?
Why don't the people at parse.com bother to write this stuff up anywhere?
That second question is more like a buddhist koan for them to meditate over, no need to respond!
you can use:
userMessagesQuery.include("messageId")
before you execute your query that returns userMessages and you will get the entire object in "messageId" instead of just a pointer.
Also you use
userMessages[0].get("messageId").fetch({success:function(){}})
to get the full object if you don't want to use "include"
Suggestion: I'd rename "messageId" to "message" to make it clear that it's an object pointer and not an ID field.
A pointer is a Parse object with just the minimal data used for linking. If you want to get the rest of the data for a pointer (fully populate it), use the fetch() method.
If you just want the objectId from a pointer, you retrieve it just like you would any other Parse object, by using the myPointer.objectId property.
In your case the following would work, but isn't the most optimal solution:
// I would suggest renaming messageId if you're actually storing pointers
var messagePointer = userMessages[position].get("messageId");
query.equalTo("objectId", messagePointer.objectId);
Instead, as stated by #RipRyness, you could just change your previous query to include() the full object, avoiding many extra queries.
userMessagesQuery.include("messageId");
// ... later in success handler ...
// now a fully populated Message object
var message = userMessages[position].get("messageId");
console.log(message);
Okay so a pointer is does not only point to a certain object, but it can BE that object.
In the query that returns the userMessages result, you can use the line .include("messageId") - this makes your query not only return a pointer to that object, it actually includes that object in place of the pointer.
After using the include statement, userMessages[0].get("messageId") will return the Message object linked that that userMessage object. You no longer need to query the Message collection to get the object.

How does a Meteor database mutator know if it's being called from a Meteor.method vs. normal code?

If one does something on the client like
Foo.insert({blah : 1})
where Foo is a Meteor collection, this actually triggers a Meteor.method call as documented in the last paragraph of http://docs.meteor.com/#meteor_methods that executes code on both the client and the server.
However, let's say I define a method (on client and server) that does the same thing:
Meteor.methods({
bar: function() {
Foo.insert({blah : 1})
}
});
Now, I trigger this on the client with
Meteor.call("bar");
Foo.insert will now be called both on the client and the server as a result of the method. However, how does the client-side invocation of insert know not to call the server again, since it is itself a method?
Moreover, is there a way to call insert on the client side without automatically triggering the canonical server-side latency-compensation call and resulting synchronization? (For why I'd want to do this, see Loading a Meteor client app with fake fire-and-forget data)
The client side one becomes a 'stub'. It has this isSimulation property set that makes it appear as if the data is inserted for latency compensation.
I think if a .method is run on the client the latency compensation is always enabled and it shouldn't enter in the database. So anything running on a client side method would be a 'fake' type simulation
If you try setting this.isSimulation to false you'll get some weird error that shows you that the client side insert starts to throw errors with the insert.
I'm not too sure how to run it in a false simulation. I think you'd have to run it off in some other method var dothis = function() {...} type method.
To do this 'fake fire' & forget and have client side only data, which is what I presume (please correct if I'm wrong so I change the answer) modify the _collection property in your client side method.
e.g if you have
mydata = new Meteor.Collection("something");
function something() {
mydata.insert({..});
}
Modify the method to do this instead
mydata._collection.insert({..});
This will make sure that the data isn't synchronized to the server, yet will have the local collection have this 'fake data'.
Hope this helps!

Meteor 0.5.9: replacement for using Session in a server method?

So, I was attempting to do something like the following:
if(Meteor.isServer){
Meteor.methods({connect_to_api: function(vars){
// get data from remote API
return data;
}});
}
if(Meteor.isClient){
Template.myTpl.content = function(){
Meteor.call('connect_to_api', vars, function(err,data){
Session.set('placeholder', data);
});
return Session.get('placeholder');
};
}
This seemed to be working fine, but, of course, now breaks in 0.5.9 as the Session object has been removed from the server. How in the world do you now create a reactive Template that uses a server-only (stuff we don't want loading on the client) method call and get data back from that Method call. You can't put any Session references in the callback function because it doesn't exist on the server, and I don't know of any other reactive data sources available for this scenario.
I'm pretty new to Meteor, so I'm really trying to pin down best-practices stuff that has the best chance of being future-proof. Apparently the above implementation was not it.
EDIT: To clarify, this is not a problem of when I'm returning from the Template function. This is a problem of Session existing on the server. The above code will generate the following error message on the server:
Exception while invoking method 'connect_to_api' ReferenceError: Session is not defined
at Meteor.methods.connect_to_api (path/to/file.js:#:#)
at _.extend.protocol_handlers.method.exception ... etc etc
Setting the session in the callback seems to work fine, see this project I created on github: https://github.com/jtblin/meteor_session_test. In this example, I return data in a server method, and set it in the session in the callback.
There are 2 issues with your code:
1) Missing closing brace placement in Meteor.methods. The code should be:
Meteor.methods({
connect_to_api: function(vars) {
// get data from remote API
return data;
}
});
2) As explained above, you return the value in the session, before the callback is completed, i.e. before the callback method had the time to set the session variable. I guess this is why you don't see any data in the session variable yet.
I feel like an idiot (not the first time, not the last). Thanks to jtblin for showing me that Session.set does indeed work in the callback, I went back and scoured my Meteor.method function. Turns out there was one spot buried in the code where I was using Session.get which was what was throwing the error. Once I passed that value in from the client rather than trying to get it in the method itself, all was right with the world.
Oh, and you can indeed order things as above without issue.

Resources