Calling object functions with variables - javascript

I'm building a simple node.js websocket server and I want to be able to send a request from a client to the server and have it just take care of things (nothing that could cause harm). Ideally the client will pass the server an object with 2 variables, one of them for the object and the other for the specific function in that object to call. Something like this:
var callObject = {
'obj': 'testObject',
'func':'testFunc'
}
var testObject = {
func: function(){
alert('it worked');
}
}
// I would expect to be able to call it with sometihng like.
console.log( window[callObject.obj] );
console.log( window[callObject.obj][callObject.func] );
I tried calling it with global (since node.js doesn't uses it instead of a browsers window) but it won't work, it always tells me that it can't find callObject.func of undefined. If I call a console.log on callObject.obj it shows the objects variable, as a string, as expected. If run a console.log on the object itself I get the object back.
I'm guessing this is something rather simple, but my Google-fu has failed me.

My recommendation is to resist that pattern and not have client code pick any function to call. If you are not careful you have built yourself a nice large security hole. Especially if you are considering using eval.
Instead have a more explicit mapping between data sent by the client and server code. (Similar to what routes in express what give you).
You might have something like this
const commands = { doSomething() { ... } );
// Then you should be able to say:
let clientCommand = 'doSomething'; // from client
commands[clientCommand](param);
This should be pretty close to what you want to achieve.
Just make sure doSomething validates any parameters passed in.
For two levels of indirection:
const commandMap = { room: { join() { ...} }, chat: { add() { ... } }};
// note this is ES6 syntax
let clientCmd = 'room';
let clientFn = 'join';
commandMap[clientCmd][clientFn]();
I think you might just have to find the right place to put the command map. Show your web socket handler code.

Related

Making a custom group of defined chaining methods in js

The question is related to general js programming, but I'll use nightwatch.js as an example to elaborate my query.
NightWatch JS provides various chaining methods for its browser components, like: -
browser
.setValue('input[name='email']','example#mail.com')
.setValue('input[name='password']', '123456')
.click('#submitButton')
But if I'm writing method to select an option from dropdown, it requires multiple steps, and if there are multiple dropdowns in a form, it gets really confusing, like: -
browser
.click(`#country`)
.waitForElementVisible(`#india`)
.click(`#india`)
.click(`#state`)
.waitForElementVisible(`#delhi`)
.click(`#delhi`)
Is it possible to create a custom chaining method to group these already defined methods? For example something like:
/* custom method */
const dropdownSelector = (id, value) {
return this
.click(`${id}`).
.waitForElementVisible(`${value}`)
.click(`${value}`)
}
/* So it can be used as a chaining method */
browser
.dropdownSelector('country', 'india')
.dropdownSelector('state', 'delhi')
Or is there any other way I can solve my problem of increasing reusability and readability of my code?
I'm somewhat new to JS so couldn't tell you an ideal code solution, would have to admit I don't know what a proxy is in this context. But in the world of Nightwatch and test-automation i'd normally wrap multiple steps I plan on reusing into a page object. Create a new file in a pageObject folder and fill it with the method you want to reuse
So your test...
browser
.click(`#country`)
.waitForElementVisible(`#india`)
.click(`#india`)
.click(`#state`)
.waitForElementVisible(`#delhi`)
.click(`#delhi`)
becomes a page object method in another file called 'myObject' like...
selectLocation(browser, country, state, city) {
browser
.click(`#country`) <== assume this never changes?
.waitForElementVisible(country)
.click(country)
.click(state)
.waitForElementVisible(city)
.click(city);
}
and then each of your tests inherit the method and define those values themselves, however you chose to manage that...
const myObject = require ('<path to the new pageObject file>')
module.exports = {
'someTest': function (browser) {
const country = 'something'
const state = 'something'
const city = 'something'
myObject.selectLocation(browser);
You can also set your country / state / city as variables in a globals file and set them as same for everything but I don't know how granular you want to be.
Hope that made some sense :)
This is a great place to use a Proxy. Given some class:
function Apple ()
{
this.eat = function ()
{
console.log("I was eaten!");
return this;
}
this.nomnom = function ()
{
console.log("Nom nom!");
return this;
}
}
And a set of "extension methods":
const appleExtensions =
{
eatAndNomnom ()
{
this.eat().nomnom();
return this;
}
}
We can create function which returns a Proxy to select which properties are retrieved from the extension object and which are retrieved from the originating object:
function makeExtendedTarget(target, extensions)
{
return new Proxy(target,
{
get (obj, prop)
{
if (prop in extensions)
{
return extensions[prop];
}
return obj[prop];
}
});
}
And we can use it like so:
let apple = makeExtendedTarget(new Apple(), appleExtensions);
apple
.eatAndNomnom()
.eat();
// => "I was eaten!"
// "Nom nom!"
// "I was eaten!"
Of course, this requires you to call makeExtendedTarget whenever you want to create a new Apple. However, I would consider this a plus, as it makes it abundantly clear you are created an extended object, and to expect to be able to call methods not normally available on the class API.
Of course, whether or not you should be doing this is an entirely different discussion!

Postman:how to set up library of (semi-)complicated reusable scripts for collection

Update
I've completely rewritten this question based on subsequent investigation. Hopefully this will generate some answers.
I'm new to Postman, and trying to figure out how to most efficiently build a collection of tests for a REST application. There are a bunch of utility functions that I'd like to have accessible in each of my test scripts, but cut-and-paste-ing them in to each test script seems like a horrible solution.
In looking at the various "scopes" that Postman allows you to squirrel away data (e.g. globals, environment, collection), it seems that all of these are merely string/number stores. In other words, it properly stores them if you can/do stringify the results. But it doesn't actually allow you to store proper objects or functions. This makes sense, since each script seems to be run as a separate execution, so the idea of sharing pointers to things between different scripts doesn't make sense.
It seems like the accepted way to share utility functions is to toString() the function in the defining script (e.g. the Collection Pre-Req script), and then eval() that stringified version in the test script. For instance:
Collection Pre-Req Script
const utilFunc = () => { console.log("I am a utility function"); };
pm.environment.set("utilFunc",utilFunc.toString() );
Test Script
const utilFunc = eval(pm.environment.get("utilFunc"));
utilFunc();
The test script will successfully print to console "I am a utility function".
I've seen people do more complicated things where, if they have more than one utility function, put them in to an object like utils.func1 and utils.func2, and have the overall function return the utils object, so the test script still only has to have a single line at the top importing the whole thing.
The problem I'm running in to is scoping - since the literal text of the function is executed in the Test Script, everything thing that the utility function has to have must be in that code, or otherwise exist at eval() time in the Test Script. For instance, if I do:
Collection Pre-Req Script
const baseUtilFunc = (foo) => { console.log(foo); };
const utilFunc1 = (param) => { baseUtilFunc("One: " + param); };
const utilFunc2 = (param) => { baseUtilFunc("Two: " + param); };
pm.environment.set("utilFunc1",utilFunc1.toString() );
pm.environment.set("utilFunc2",utilFunc2.toString() );
Test Script
const utilFunc1 = eval(pm.environment.get("utilFunc1"));
const utilFunc2 = eval(pm.environment.get("utilFunc2"));
utilFunc1("Test");
This fails because, in the Test Script, baseUtilFunc does not exist. Obviously, in this example, it'd be easy to fix. But in a more complicated world where the utility functions I expect to use in my Test Scripts are themselves built on top of underlying helper functions, it gets more difficult.
So what is the right way to handle this issue? Do people just cram all the relevant logic in to one big function that they then call toString() on? Do they embed an extraction-from-environment-and-then-eval in each util function within its definition, so that it works in the Test Script context? Do they export each individual method?
There are different ways to do it. The way I did recently for one of the projects is creating a project in Git and then using raw url to fetch the data. I have a sample created at below repo
https://github.com/tarunlalwani/postman-utils
To load the file you will need to associate the below code at collection level
if (typeof pmutil == "undefined") {
var url = "https://raw.githubusercontent.com/tarunlalwani/postman-utils/master/pmutils.js";
if (pm.globals.has("pmutiljs"))
eval(pm.globals.get("pmutiljs"))
else {
console.log("pmutil not found. loading from " + url);
pm.sendRequest(url, function (err, res) {
eval(res.text());
pm.globals.set('pmutiljs', res.text())
});
}
}
As shown in below screenshot
And the later in the tests or Pre-Requests you will run the below line of code to load it
eval(pm.globals.get("pmutiljs"))
And then you can use the functions easily in test.

Why is console.log() not working client-side when inside Meteor methods?

I want to define the same method for both client and server, so I have the following code inside common/methods.js.
Meteor.methods({
doSomething: function (obj) {
if(this.isSimulation) {
console.log('client');
}
if(!this.isSimulation) {
console.log('server');
}
console.log('both');
return "some string";
}
});
Then I called this method inside client/dev.js.
Meteor.call('doSomething', someObj, function (e, data) {
console.log(e);
console.log(data);
});
On the server's console, I can read:
I20150622-21:56:40.460(8)? server
I20150622-21:56:40.461(8)? both
On the client's (Chrome for Ubuntu v43.0.2357.125 (64-bit)) console, the e and data arguments are printed, but the console.log() from the Meteor method is not, where I expected it to output the strings
client
both
Why do console.log() not work on the client inside Meteor methods?
To debug, I split the Meteor.methods into separate client and server code. Then introducing a large loop so the server-side operation so it takes a long time to complete, while the client-side is very quick.
server
doSomething: function (obj) {
var x = 0;
for(var i = 0; i< 9999999; i++) {
x++;
}
console.log(x);
return "some string";
}
client
doSomething: function (obj) {
console.log('client');
}
Still, no message is printed on the client.
Thanks to #kainlite for helping me debug this together. It turns out the problem was a simple one of file load order.
I defined my methods in common/methods.js, whereas my client-side calls were made in client/dev.js, which gets loaded first.
So when the call was made the method wasn't defined, hence it won't run. Moving the methods.js file inside the /lib directory fixed the issue.
Methods are only executed on the server, they are the sync way of doing a remote call.
Methods
Methods are server functions that can be called from the client. They
are useful in situations where you want to do something more
complicated than insert, update or remove, or when you need to do data
validation that is difficult to achieve with just allow and deny.
http://docs.meteor.com/#/basic/Meteor-users

nodeJS prevent access to code for variable passed into a function

I'm creating a plugin system using the following:
function Plugin(thingy, code)
{
var GLOBAL = null;
var arguments = null;
var process = null;
var require = null;
eval(code);
};
plugins.push(new Plugin(thingy, code));
Please don't get too excited about the eval(), using ('vm') or a sandbox is not an option as this will be a long running object until the user unloads it. It will also be running in it's own nodeJS instance so they can't affect other users. I'd still have the same problem passing in this object reference to a sandbox system anyway.
What I am concerned about is someone seeing the code of the thingy object that has functions they need to use e.g shoot()
console.log(thingy.shoot.toString());
A way around this was the following:
function thingy()
{
// They can't see this
var _shoot = function(someone)
{
// Load weapon
// Aim
// Fire
};
// They can see this
this.shoot = function(someone)
{
_shoot(someone);
};
};
This way if they console.log(thingy.shoot.toString()) they'll only see _shoot(someone); and not the actual code that handles the shooting process.
Please could someone help me with the following:
Is there an easier way to limit access to a passed in variables code?
I'm setting GLOBAL, arguments, process and require to null; are there others I need to worry about?

Pass a JavaScript function through JSON

I have a server side Python script that returns a JSON string containing parameters for a client side JavaScript.
# Python
import simplejson as json
def server_script()
params = {'formatting_function': 'foobarfun'}
return json.dumps(params)
This foobarfun should refer to a JavaScript function. Here is my main client side script
// JavaScript
function client_script() {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, async=true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
options = JSON.parse(xhr.responseText);
options.formatting_function();
}
};
xhr.send(null);
}
function foobarfun() {
//do_something_funny_here...
}
Of course, options.formatting_function() will complain that "a string is not callable" or something to that effect.
Upon using Chrome's Inspect Element, under the Resources tab, and navigating the left sidebar for XHR > query, I find that client_script interprets options as below. foobarfun is seen as a string.
// JavaScript
options = {"formatting_function": "foobarfun"}
I would have liked client_script to see options as
// JavaScript
options = {"formatting function": foobarfun}
Of course, doing the following within Python will have it complaining that it doesn't know anything about foobarfun
# Python
params = {'formatting_function': foobarfun}
QUESTION:
How should I prepare my JSON string from the server side so that the client script can interpret it correctly? In this case, I want foobarfun to be interpreted as a function object, not as a string.
Or maybe it's something I should do on the client side?
There's nothing you can do in the JSON to get the result you want because JSON has no concept of functions, it's purely a data notation. But there are things you can do client-side.
If your foobarfun function is a global function (which I would recommend against, we'll come to that), then you can call it like this:
window[options.formatting_function]();
That works because global functions are properties of the window object, and you can access properties either by using dotted notation and literals (window.foobarfun), or by using bracketed notation and strings (window["foobarfun"]). In the latter case, of course, the string doesn't have to be a string literal, it can be a string from a property -- your options.formatting_function property, for instance.
But I don't recommend using global functions, the window object is already very crowded. Instead, I keep all of my functions (or as many as possible, in some edge cases) within a master scoping function so I don't add anything to the global namespace:
(function() {
function foobarfun() {
}
})();
Now, if you do that, you can't access foobarfun on window because the whole point of doing it is to avoid having it be on window. Instead, you can create your own object and make it a property of that:
(function() {
var myStuff = {};
myStuff.foobarfun = foobarfun;
function foobarfun() {
}
function client_script() {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, async=true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
options = JSON.parse(xhr.responseText);
myStuff[options.formatting_function](); // <== using it
}
};
xhr.send(null);
}
})();
Frequently, rather than this:
myStuff.foobarfun = foobarfun;
function foobarfun() {
}
you'll see people write:
myStuff.foobarfun = function() {
};
I don't recommend that, either, because then your function is anonymous (the property on myStuff that refers to the function has a name, but the function doesn't). Giving your functions names is a good thing, it helps your tools help you (showing you the names in call stacks, error messages, etc.).
You might also see:
myStuff.foobarfun = function foobarfun() {
};
and that should be valid, it's correct JavaScript. But unfortunately, various JavaScript implementations have various bugs around that (which is called a named function expression), most especially Internet Explorer prior to IE9, which will create two completely different functions at two different times.
All of that said, passing the names of functions around between the client and server usually suggests that you want to step back and look at the design again. Data should drive logic, but not in quite such a literal way. That said, though, there are definitely valid use cases for doing this, you may well have one in your situation.
This question seems like it may be helpful for you:
How to execute a JavaScript function when I have its name as a string
I think what I would do is store references to these methods in an object literal, and then access them through properties.
For example, if I wanted to call foobarfun, among other functions
var my_functions = {
foobarfun: function(){
},
...
};
...
var my_fn = my_functions[options.formatting_function];
my_fn();
may be you can think the return string as javascript not json, by set MIME type text/javascript
There is a trick you can do. Analyzing the code of the json module in python, I notice that it is possible to make the serializer believe that it is an int what is serializing that way __str__ method will be executed.
import json
class RawJS(int):
def __init__(self):
self.code = "null"
def __repr__(self):
return self.code
def __str__(self):
return self.__repr__()
#classmethod
def create(cls, code):
o = RawJS()
o.code = code
return o
js = RawJS.create("""function() {
alert("hello world!");
}""")
x = {
"func": js,
"other": 10
}
print json.dumps(x)
the output is:
{"other": 10, "func": function() {
alert("hello world!");
}}
The disadvantage of this method is that the output is not a valid JSON in python so it can't be deserialized, but it is a valid javascript.
I don't know whether it's possible to trick Python's JSON serializer to do what you want, but you can use eval() function to execute any JavaScript code stored in a string variable.
Well I am not a Python expert but I know some java script. From server you can pass the information to client only as string. If in any case you want to pass any java script function then you can pass that function name as string and evaluate that string on client side.
Ex. If you passes xyz as string from server and on client side you call
var funcName = "xyz"; // pursuing that variable would pass here some how
eval (funcName); // This line would make a call to java script function xyz
Hint: You can think of java script eval utility as a reflection utility in java.
Thanks
shaILU
I'm in the same situation (python backend, needing to pass a JS function through JSON to frontend).
And while the answers here are honestly mind-blowing, I'm asking myself whether passing a javascript function that needs to designed in python and snuck into a JSON is the right answer, from a design perspective.
My specific scenario I need to tell JS how to format a string (is it a percentage, or does it need unitary breakdown such as thousands, millions etc)
And I'm finding it's cleaner to just do:
python:
chart = {
y_axis: {
"formatter" : "percent"
}
}
JS:
format_type = chart["y_axis"]["formatter"]
switch(format_type) {
case "percent":
format_func = (value) => value.toFixed(0) +'%';
break;
}
chart["y_axis"]["formatter"] = format_func
I find this is overall cleaner than attempting to define your JS functions in Python. It's also more decoupled than passing a specific function name from python to JS
Which I guess it's quite similar to #jkeesh's solution

Categories

Resources