JavaScript chaining functions, last methods returns 'undefined' - javascript

My app receives a base64 encoded value that is also encrypted. The data can come in a few different ways so I wanted to create chain-able methods to keep the code clean and modular.
I want to be able write: decryptionChain.decodeBase64(b64Value).stringToBuffer().finallyDecrypt();
When I run the code, the last property method "finallyDecrypt" returns as undefined.
Why is the "finallyDecrypt" method coming back as undefined? The rest all works and if I run ecryptionChain.decodeBase64(b64Value).stringToBuffer() I get back the Buffer I expect. It is only when the finallyDecrypt is chained in that I error out.
Here is the code:
function decrypt(encrypted) {
var decipher = crypto.createDecipheriv(algorithm, password, iv);
decipher.setAuthTag(encrypted.tag);
var dec = decipher.update(encrypted.content, 'hex', 'utf8');
dec += decipher.final('utf8');
return dec;
}
var decryptionChain = {
currentValue:"",
decodeBase64: function (encryptedValue){
this.currentValue = new Buffer(encryptedValue.toString(), "base64");
return this;
},
stringToBuffer: function() {
if (this.currentValue) {
myBuffer = JSON.parse(this.currentValue, function (key, value) {
return value && value.type === 'Buffer'
? new Buffer(value.data)
: value;
});
}
return myBuffer;
},
finallyDecrypt : function(myBuffer){
if(myBuffer){
decrypt(myBuffer);
}
return this;
}
};

From the code shown, I have spotted some issues.First:
decryptionChain != decryptChain If its just a typo in the question, then disregard, if its not, please reffer to that.
Please, use the var to decrease possibilities of scope errors, or "scoped variables" being left behind.
Second, dont use a string as a Boolean.
Third, you seem to have an issue with the return value && value.type === 'Buffer' ? new Buffer(value.data) : value;, please assign before returning (not necessary, but simpler)

Related

JavaScript promise with .then inside .then doesn't work

Current state:
I have the following function in a link builder which is using a configuration service which returns a promise with some enviroment parameters:
_createLink: function (label, args) {
return LinkBuilder.builder()
.withLabel(label)
.withUrl(ConfigService.getConfig()
.then((env) => env.baseUrl
+ TranslationService.instant('MY.URL', args)))
.buildLink();
},
A few explanations:
withUrl(...) does the following:
withUrl: function (param) {
if (typeof param === 'string') {
builder.value.linkUrl = param;
} else if (typeof param === 'function') {
builder.value.linkCalculationFunction = param;
} else if (typeof param === 'object' && typeof param.lazyBuildUrl === 'function') {
builder.value.linkCalculationFunction = param.lazyBuildUrl();
} else if (typeof param === 'object' && typeof param.then === 'function') {
builder.value.linkCalculationFunction = () => param;
} else {
throw new Error('invalid url param ' + param);
}
return builder;
},
withLabel(label) will set a label which will be displayed as the URL text
withLabel: function (label) {builder.value.labelKey = label; return builder;}
.buildLink() is just returning builder.value, with the URL and all other params:
buildLink: function () {return builder.value;}
TranslationService will find the 'MY.URL' entry in a JSON file with translations. The value will be something like 'http://www.myserver.com#name={{name}}&age={{age}}'. The name and age parameters from args will be inserted there.
Problem:
I need to encode those args using an external service which also returns a promise and then to append the return value to my link. That external service will return something like 'data': {'encodedId': '123-456-789'}, and my final link should be: http://www.myserver.com#encodedId=123-456-789. Here is what I did:
My original attempt:
In the JSON file with translations I removed the parameters from the entry, so now it's only:
'http://www.myserver.com#'
The _createLink function now looks like this:
_createLink: function (label, args) {
let content = {
'name': args.name,
'age': args.age
};
return EncodingService.saveContent(content)
.then((data) => {
return LinkBuilder.builder()
.withLabel(label)
.withUrl(ConfigService.getConfig()
.then((env) => env.baseUrl
+ TranslationService.instant('MY.URL')
+ 'encodedId=' + data.encodedId))
.buildLink();
},
When I place a breakpoint at the last then line, I see that I have the correct data.encodedId, the correct env.baseUrl and the TranslationService is also correctly returning the JSON entry. But for some reason, I don't see the link in my page anymore. I guess that I'm doing something wrong with chaining the .thens, but I'm not sure what.
Another attempt which still doesn't work:
Here is the third version, which still doesn't work:
_createLink: function (label, args) {
let content = {
'name': args.name,
'age': args.age
};
return $q.all([
EncodingService.saveContent(content),
ConfigService.getConfig()
])
.then((data) => {
let url = data[1].baseUrl
+ TranslationService.instant('MY.URL')
+ 'encodedId=' + data[0].encodedId;
return LinkBuilder.builder()
.withLabel(label)
.withUrl(url)
.buildLink();
},
Again, I placed the breakpoint at the last return statement and there I have the url formed correctly...
Usage of `_createLink`:
This function is used like this:
getLeadLink: function (label, access, address) {
return myService._createLink(label, someService._createArguments(access, address));
},
...and getLeadLink is used in a component like this:
this.$onInit = () => {
...
this.leadLink = OtherService.getLeadLink('LINEINFO.LEAD.LINK', someData.access, someData.address);
...
};
...and leadLink is then displayed in the HTML file.
Note: None of these things was modified, so the usage of _createLink and the displaying of the link is still the same as it was before.
The third attempt is going in the right direction.
There is however no way you can get the future URL now. And so any attempt that in the end expects a simple function call to return the URL cannot be right. Such a function (like _createLink) is deemed to return before the asynchronous parts (i.e. any then callbacks) are executed. In your third attempt you improved things so that at least _createLink will return a promise.
Now remains to await the resolution of that promise.
So, any wrapping function execution around _createLink, should take into account the asynchronous pattern:
getLeadLink: function (label, access, address) {
return myService._createLink(label, someService._createArguments(access, address));
},
This is fine, but realise that _createLink does not return the URL, but a promise. And so also getLeadLink will return a promise.
You write that you want to display the URL, so you would do something like this (I don't know how you will display, so this is simplified):
OtherService.getLeadLink('LINEINFO.LEAD.LINK', someData.access, someData.address).then(function(builder) {
var $link = $("<a>").attr("href", builder.value.linkUrl)
.text(builder.value.labelKey);
$(document).append($link);
});
The key message is: don't try to get the URL as the return value of a function. It does not matter how many wrappers you write around the core logic; it will remain an asynchronous task, so you need to get and display the URL asynchronously as well. You'll need to do it in a then callback, or use await syntax, which makes anything following it asynchronous code.

How do you prevent a NodeJS server from potentially exposing function code?

Let's imagine you're building a banking app backend. You want to respond to a user with a string that returns the balance but you forgot to add ().
class User {
constructor() {console.log("ctor")}
balance() { console.log("secret balance code")}
}
Then when referencing the user, instead of writing this:
const userA = new User();
return `Your balance is $${userA.balance()}`;
I accidentally write this:
const userA = new User();
return `Your balance is $${userA.balance}`;
Which sadly outputs:
'Your balance is balance() { console.log("secret balance code")}'
Which leaks the source code.
You do not need to worry about it, if you forget something, then testing will help to find it. Nobody deploy in production without testing when he has a serious project. It is better to write tests than to try to correct language behavior.
One workaround is to override all functions' toString like so:
> Function.prototype.toString = () => {return "bla"}
[Function]
> '' + new User().balance
'bla'
When responding to a request, you're undoubtedly going to be running the response through some sort of serializer. JSON, CBOR, etc. Handle it on that layer.
Fortunately for you, if you're returning JSON data, it's already handled:
JSON.stringify(someFunction);
// undefined
If you really are returning plain text strings, you can still have such a layer that ensures you're not putting out functions.
I've a solution which is definitely slower than raw templates, but here it goes.
So basically I just send a context object which has all the string I want to resolve. And before the actual string replacement, I just check for the types of arguments.
function resolveTemplates(str, args){
if(args && Array.isArray(args) && args.length){
args.forEach((argument) => {
// check here for any unwanted types
if(typeof arg === 'function'){
throw new Error('Cannot send function to create raw Strings')
}
})
}
const rx = /\{([^{}]*)\}/g;
let match = {};
let matches = [];
while(match = rx.exec(str)){
matches.push(match)
}
matches.reverse();
matches.forEach(function(match){
const key = match[1];
const index = match.index;
str = str.slice(0, index) + args[key] + str.slice(index + 2 + key.length)
})
return str;
}
resolveTemplates('Hello! My name is {firstName} {lastName}', {firstName: 'Shobhit', lastName: 'Chittora'})
PS: Instead of throwing errors for functions as arguments, you can call the functions. But binding the functions to the correct context can be a overhead to think about and generally not suggested.

Getting textbox values from a CefSharp browser using javascript

I've got a winforms app that has a ChromiumWebBrowser control and some basic windows controls. I want to be able to click a button, call javascript to get the value of a textbox in the browser, and copy the returned value to a textbox in the winforms app. Here is my code:
string script = "(function() {return document.getElementById('Email');})();";
string returnValue = "";
var task = browser.EvaluateScriptAsync(script, new { });
await task.ContinueWith(t =>
{
if (!t.IsFaulted)
{
var response = t.Result;
if (response.Success && response.Result != null)
{
returnValue = (string)response.Result;
}
}
});
txtTarget.Text = returnValue;
The result that comes back however is just "{ }". I've loaded the same web page in Chrome and executed the same javascript in the dev tools and I get the textbox value as expected.
The demo I looked at had sample code, simply "return 1+1;", and when I tried that I was getting the value "2" returned instead of "{ }". Interestingly, when I tried
string script = "(function() {return 'hello';})()";
I was still getting "{ }", almost as though this doesn't work with strings.
I've been scratching my head at this for a while and haven't been able to figure out how to solve this. Am I making a very basic syntax error or is there something more complicated going on?
So I think I've figured it out:
string script = "(function() {return document.getElementById('Email').value;})();";
string returnValue = "";
var task = browser.EvaluateScriptAsync(script);
await task.ContinueWith(t =>
{
if (!t.IsFaulted)
{
var response = t.Result;
if (response.Success && response.Result != null)
{
returnValue = response.Result.ToString();
}
}
});
txtTarget.Text = returnValue;
Removing the args object from EvaluateScriptAsync seemed to fix the issue. Not sure what the problem was - perhaps it was trying to run the javascript function with an empty args object when it shouldn't take any parameters?
Either way, it's resolved now.
public void SetElementValueById(ChromiumWebBrowser myCwb, string eltId, string setValue)
{
string script = string.Format("(function() {{document.getElementById('{0}').value='{1}';}})()", eltId, setValue);
myCwb.ExecuteScriptAsync(script);
}
public string GetElementValueById(ChromiumWebBrowser myCwb, string eltId)
{
string script = string.Format("(function() {{return document.getElementById('{0}').value;}})();",
eltId);
JavascriptResponse jr = myCwb.EvaluateScriptAsync(script).Result;
return jr.Result.ToString();
}

Return value from helperClass.js not making it back to index.js

Preface: I'm new to JavaScript. If this question is startlingly stupid, that's (part of) the reason.
Begin Update
I found the answer to my question prior to the flag as a dupe. I solved it with a try-catch block. This answer does not reference a try-catch block.
How do I return the response from an asynchronous call?
End Update
I'm attempting to create an Alexa project from scratch (well, at least without one of Amazon's templates). I've written the "guts" of the app and I've tested my functions with chai. Things were going swimmingly until I tried to wire up some intents.
I can see my intents are being sent based on console.log statements I've thrown in the helperClass, but the return values aren't making it back to my index.js file.
Two questions:
What am I mucking up?
How do I fix it?
Here's what I've done:
Based on that, I dug around to see what's going on in my index.js file's headers and I saw this:
var Alexa = require('alexa-app');
So I went to alexa-app and saw that it uses bluebird, which suggests to me that I'm dealing with a promise problem. Further, I saw this in the log when I send a request that works:
preRequest fired
postRequest fired
When a request doesn't work, I only see:
preRequest fired
I'm using Big Nerd Ranch's "Developing Alexa Skills Locally with Node.js".
Here's my problematic intent in my index.js file:
app.intent('getDaysFromNow', {
'slots': {
'INPUTDATE' : 'AMAZON.DATE'
},
'utterances': ['{|number of|how many} {days} {until|from|since} {|now|today} {|is|was} {-|INPUTDATE}'] // almost perfect
},
function(req, res) {
console.log('app.intent getDaysFromNow fired');
//get the slot
var inputDate = req.slot('INPUTDATE');
var reprompt = 'Ask me how many days until or from a specified date.';
if (_.isEmpty(inputDate)) {
console.console.log('app.intent daysFromNow blank request');
var prompt = 'I didn\'t hear the date you want.';
res.say(prompt).reprompt(reprompt).shouldEndSession(false);
return true;
} else {
console.log('getDaysFromNow slot is not empty.');
var dateHelper = new DateHelper();
dateHelper.getDaysFromNow(inputDate).then(function(daysFromNow) {
console.log(daysFromNow);
res.say(dateHelper.formatDaysFromNowResponse(daysFromNow)).send(); // FIXME
}).catch(function(err) {
console.log(err.statusCode);
var prompt = 'Hmm...I don\'t have a date for that ' + inputDate;
res.say(prompt).reprompt(reprompt).shouldEndSession(false).send();
});
return false;
}
}
);
I know it's getting sent, but the value's not getting back to index.js. I think I've got a return problem. Here's the function in my helperClass.js whose return isn't getting back to index.js
// Takes AMAZON.DATE string as its argument
DateHelper.prototype.getDaysFromNow = function(inputDate) {
if (isValidDate(inputDate)) { // validate it
// if it's valid, run the function
inputDate = moment(inputDate, "YYYY-MM-DD").startOf('day'); // inputDate starts as a string, recast as a moment here
// create currentDate moment from current Date()
var currentDate = moment(new Date()).startOf('day');
// Calculate daysFromNow here
var daysFromNow = inputDate.diff(currentDate, 'days');
console.log("\t" + 'daysFromNow = ' + daysFromNow);
// ORIGINAL CODE
// return daysFromNow;
// EXPERIMENTAL CODE
return this.daysFromNow.then(
function(response) {
return response.body;
}
);
} else {
// throw an error
throw new Error("getDaysFromNow(): argument must be valid AMAZON.DATE string");
}
};
Thank you for reading. I welcome your suggestions.
It turns out it's not the way the return is sent from helperClass. The issue is how the call was made in the first place.
For the helperClass, I reverted to the original return.
// This is the return from `helperClass`
return daysFromNow;
In the index.js class, I put the call to helperClass in a try-catch block, like so:
app.intent('getDaysFromNow', {
'slots': {
'INPUTDATE': 'AMAZON.DATE'
},
'utterances': ['{|number of|how many} {days} {until|from|since} {|now|today} {|is|was} {-|INPUTDATE}'] // almost perfect
},
function(req, res) {
console.log('app.intent getDaysFromNow fired');
//get the slot
var inputDate = req.slot('INPUTDATE');
var reprompt = 'Ask me how many days until or from a specified date.';
if (_.isEmpty(inputDate)) {
console.log('app.intent daysFromNow blank request');
var prompt = 'I didn\'t hear the date you want.';
res.say(prompt).reprompt(reprompt).shouldEndSession(false);
return true;
} else {
// ** CHANGED: This is the part that changed. **
console.log('getDaysFromNow slot is not empty.');
var dateHelper = new DateHelper();
try {
var daysFromNow = dateHelper.getDaysFromNow(inputDate);
res.say(dateHelper.formatDaysFromNowResponse(daysFromNow)).send();
} catch (error) {
console.log("error", error);
var prompt = 'Hmm...I don\'t have a date for that ' + inputDate;
res.say(prompt).reprompt(reprompt).shouldEndSession(false).send();
}
return false;
}
}
);
Hopefully, someone finds this helpful if they run into the same problem.

accessing object values that are inside an array

I'm hoping someone can help with what is likely a simple answer - but I'm ready to bash my head against the wall....again.
I have a function which makes a JSON call to an API, and then pushes the results into an array. The function appears to work just fine as my console.log is showing that the array is populated correctly.
I'm struggling with how to access the values of the modified twichResult object (after the function has run), so that I can do 'stuff' with it. e.g. display the value of the 'status' property onscreen etc... I give some examples of what I've tried in the in the large commented out section.
I'd really appreciate some intelligence weighing in on this as I've exhausted my resources. Thanks in advance.
<script type="text/javascript">
$(document).ready(function() {
var twitchResult = {results:[]};
var channel = { logo:"", display_name:"", status:"", url:"" };
var finalUrl = "https://api.twitch.tv/kraken/streams/freecodecamp?callback=?"
getTwitchers (finalUrl, "freecodecamp");
console.log(twitchResult);
// How do I access the individual values in the object TwitchResult?
// I get "undefined" in the console if I try to access the object's property values
// I've tried every way I can think of to get 'into' the returned object :
// console.log(twitchResult.results);
// console.log(twitchResult["results"])
// console.log(twitchResult.results.status)
// console.log(twitchResult[0])
// console.log(twitchResult[0][0])
// etc etc
function getTwitchers (url, item) {
$.getJSON(url, function(data) {
var obj = data.stream;
// Check if the object is not valid using (obj == null) which is shorthand for both null and undefined
if (obj == null) {
if (obj === undefined) {
channel.display_name = item;
channel.status = "closed";
console.log ("this is undefined");
}
else {
channel.display_name = item;
channel.status = "offline";
console.log("this is null");
}
}
else {
channel.logo = obj.channel.logo;
channel.display_name = obj.channel.display_name;
channel.status = obj.channel.status;
channel.url = obj.channel.url;
console.log("valid entry");
}
twitchResult["results"].push(channel);
// twitchResult.results.push(channel);
// console.log(twitchResult);
});
}
});
</script>
$.getJSON is making an ajax-request. You must handle this request from within the request handler. When getTwichers returns, twichResults is not yet set.
There are methods to delay Program execution, until twichResults is done, but You should not think of using them, since they would delay program execution. The idea of ajax is to execute things asynchronously, without disturbing the rest of the execution flow. If the code You want to execute depends on the json, then You should add it to the handle in $.getJSON. Just write a new function (e.g. continue_execution(twichResult)) and invoke it right after twitchResult["results"].push(channel);. Just don't do anything after getTwitchers(...).
By the way: It is a good habit to define functions, before they are used, because it follows the flow the human eye reads the code and there are programming languages, which depend on this style of declaring function.
If this is unclear to You, then add a comment.

Categories