Uncaught TypeError: Cannot read properties of null (reading 'filter') - function

I'm seeing these two errors:
Uncaught TypeError:
Cannot read properties of null (reading 'filter')
along with this - Uncaught ReferenceError: $ is not defined
I've tried running it a few times and it gave me the same answer each time. I can't seem to figure out where the issue is..
This is the code:
function scrollToBottom(){
bottom = document.body.scrollHeight;
current = window.innerHeight+ document.body.scrollTop;
if((bottom-current) >0){
window.scrollTo(0, bottom);
//wait a few
setTimeout ( 'scrollToBottom()', 1500 );
}
(function() {
var placeholderString,
placeholderWords,
contactName,
companyName,
customNote,
jobDescription,
jobUrl,
jobInfo,
str = '';
$('.job_listings')
.filter(function(_, elem) {
return ($(elem).attr('data-force-note') );
})
.each(function(_, elem) {
// Find the URL for each job listing.
$(elem)
.find('.top a[href]')
.each( function(idx, value) { str += $(value).attr('href') + "\n"; });
// Get the company and contact info
placeholderString = $(elem)
.find('.interested-note').attr('placeholder');
// Split placeholder string into words:
placeholderWords = placeholderString.split(' ');
// Grab name of recruiter/contact
contactName = placeholderWords[4];
// Grab company name
companyName = $(elem).find('.startup-link').text();
// Build personalized note
customNote = "Hi " + contactName + "! Would love to join " + companyName + " using my diverse set of skills. Let's chat!";
// .header-info .tagline (text)
jobDescription = $(elem).find('.tagline').text();
// .header-info .startup-link (href attr)
jobUrl = $(elem).find('.startup-link').attr('href');
// Compile and format job information
jobInfo = companyName + '\n' + jobDescription + '\n' + str + '\n\n';
// Get job data for your own records
console.log(jobInfo);
// Comment this out to verify your customNote
// console.log(customNote + '\n');
// Add your custom note.
// Comment these lines out to debug
$(elem)
.find('.interested-note').text( customNote );
//Comment these lines out to debug
$(elem)
.find('.interested-with-note-button')
.each( function(idx, button) { $(button).click(); });
});
// Print all of the company and job info to the console.
return jobInfo;
})();
};
scrollToBottom();
Is anyone able to help me here?
Thank you!

Related

Unexpected Error: write callback called multiple times executing Gulp.js tasks inside a loop

I'm trying to execute some gulp tasks inside a map loop to generate css files for each sass theme I defined.
Finally, I return all merged tasks with merge-stream package.
Unfortunately, callback seems sent for each iteration / gulp task of my loop and i'm getting the following message :
Error: write callback called multiple times
#Task('build')
build() {
var self = this;
var tasks = this.themes.map((theme) => {
this.foldersXml = getAttributes('jntFolderWithExternalProvider');
this.filesXml = getAttributes('jntFolder');
this.foldersXml[theme] = []; this.foldersXml[theme].push(getAttributes('jntFolderWithExternalProvider'));
this.filesXml[theme] = []; this.filesXml[theme].push(getAttributes('jntFolderWithExternalProvider'));
fs.readFile(this.themesFolder + '/' + theme + '/' + this.fileName, 'utf8', (err: Error, data: string & Buffer) => {
if (err) {
throw new gutil.PluginError({
plugin: 'build',
message: 'Main file doesn\'t exist for the theme: ' + theme
});
} else {
var vars = data.match(/\$(.*?)\:/g);
this.requiredVars.map(requiredVar => {
if(vars !== null){
if(!vars.contains(requiredVar)){
throw new gutil.PluginError({
plugin: 'build',
message: 'Required variable ' + requiredVar + ' is not defined'
});
};
}
});
}
});
return gulp.src(this.templatesFolder + '/' + this.pattern)
.pipe(header('#import \'' + this.themesFolder + '/' + theme + '/' + this.fileName + '\';'))
.pipe(sass.sync().on('error', gutil.log))
.pipe(rename(function (path: any) {
var file = path.basename + path.extname;
var folderXml = getAttributes('jntFolderWithExternalProvider') as any;
folderXml[file] = [ getAttributes('jntCssFile') ];
self.foldersXml[theme][0][file] = []; self.foldersXml[theme][0][file].push(folderXml);
var fileXml = getAttributes('jntFolderWithExternalProvider') as any;
fileXml['jcr:content'] = [ getAttributes('jntRessource') ];
self.filesXml[theme][0][file] = []; self.filesXml[theme][0][file].push(fileXml);
path.dirname += '/' + file;
}))
.pipe(gulp.dest(this.themesFolder + '/' + theme))
});
return merge(tasks);
}
#SequenceTask()
run(){
return ['build'];
}
I just want to get rid of this message and be sure that callback is invoked only at the end. What's the best approach for you ?

Connecting Alexa skill to mysql database using node.js and aws lambda

I am trying to connect my Alexa skill to an Amazon RDS mySQL database using node.js in AWS Lambda. I tested the connection before uploading it to lambda and it worked but when I upload it I get a 'process exited before completing request' or a 'There was a problem with the skills response' error.
'use strict';
const Alexa = require('alexa-sdk');
const APP_ID = 'amzn1.ask.skill.11069fc0-53bc-4cd0-8961-dd41e2d812f8';
var testSQL = 'SELECT weight, height from users where pin=1100';
//=========================================================================================================================================
//Database connection settings
//=========================================================================================================================================
var mysql = require('mysql');
var config = require('./config.json');
// Add connection details for dB
var pool = mysql.createPool({
host : config.dbhost,
user : config.dbuser,
password : config.dbpassword,
database : config.dbname
});
// var dbHeight, dbWeight, dbMuscle, dbExerciseOne, dbExerciseTwo, dbExerciseThree, dbExerciseFour;
var dbResult;
function searchDB(quest) {
pool.getConnection(function(err, connection) {
// Use the connection
console.log(quest);
connection.query(quest, function (error, results, fields) {
// And done with the connection.
connection.release();
// Handle error after the release.
if (!!error) {
console.log('error')
}
else {
console.log(results[0]);
dbResult = results[0];
return dbResult;
console.log(dbResult.height);
}
process.exit();
});
});
};
//searchDB(testSQL);
//=========================================================================================================================================
//TODO: The items below this comment need your attention.
//=========================================================================================================================================
const SKILL_NAME = 'My Application';
const GET_FACT_MESSAGE = "Here's your fact: ";
const HELP_MESSAGE = 'You can say tell me a space fact, or, you can say exit... What can I help you with?';
const HELP_REPROMPT = 'What can I help you with?';
const STOP_MESSAGE = 'Goodbye!';
var name, pinNumber;
//=========================================================================================================================================
//Editing anything below this line might break your skill.
//=========================================================================================================================================
const handlers = {
'LaunchRequest': function () {
if(Object.keys(this.attributes).length === 0){
this.attributes.userInfo = {
'userName': '',
'pinNo': 0
}
this.emit('GetPinIntent');
}
else{
name = this.attributes.userInfo.userName;
pinNumber = this.attributes.userInfo.pinNo;
var sql = "";
//var result = searchDB(sql);
//var uWeight = result.weight;
//var uHeight = result.height;
var speechOutput = 'Welcome ' + name + 'Please select an option: Check My BMI, Create Exercise Plan, Create Meal Plan, Update Height and Weight, Update workout status?';
this.emit(':ask', speechOutput);
}
},
'GetPinIntent': function (){
this.emit(':ask','Welcome to my Application, as this is your first time please say your name followed by your pin. For example, my name is Jason and my pin is zero one zero one');
//this.emit(':responseReady');
},
'RememberNameID': function (){
var filledSlots = delegateSlotCollection.call(this);
this.attributes.userInfo.userName = this.event.request.intent.slots.name.value;
this.attributes.userInfo.pinNo = this.event.request.intent.slots.pin.value;
var speechOutput = 'Welcome ' + this.attributes.userInfo.userName + ' we have stored your Pin Number and we will call you by name next time. Please select an option: BMI or exercise';
this.response.speak(speechOutput);
this.emit(':responseReady');
},
'CheckBMI': function(){
var sql = 'SELECT height, weight FROM users WHERE pin=' + this.attributes.userInfo.pinNo;
var heightWeight = searchDB(sql);
dbHeight = parseInt(heightWeight.height);
dbWeight = parseInt(heightWeight.weight);
var speechOutput = bmiCalculator(dbHeight, dbWeight);
this.emit(':ask', speechOutput);
},
'AMAZON.HelpIntent': function () {
const speechOutput = HELP_MESSAGE;
const reprompt = HELP_REPROMPT;
this.response.speak(speechOutput).listen(reprompt);
this.emit(':responseReady');
},
'AMAZON.CancelIntent': function () {
this.response.speak(STOP_MESSAGE);
this.emit(':responseReady');
},
'AMAZON.StopIntent': function () {
this.response.speak(STOP_MESSAGE);
this.emit(':responseReady');
},
'SessionEndedRequest': function() {
console.log('session ended!');
this.emit(':saveState', true);
}
};
exports.handler = function (event, context, callback) {
var alexa = Alexa.handler(event, context, callback);
alexa.APP_ID = APP_ID;
alexa.dynamoDBTableName = 'fitnessDB';
alexa.registerHandlers(handlers);
alexa.execute();
};
function delegateSlotCollection(){
console.log("in delegateSlotCollection");
console.log("current dialogState: "+this.event.request.dialogState);
if (this.event.request.dialogState === "STARTED") {
console.log("in Beginning");
var updatedIntent=this.event.request.intent;
//optionally pre-fill slots: update the intent object with slot values for which
//you have defaults, then return Dialog.Delegate with this updated intent
// in the updatedIntent property
this.emit(":delegate", updatedIntent);
} else if (this.event.request.dialogState !== "COMPLETED") {
console.log("in not completed");
// return a Dialog.Delegate directive with no updatedIntent property.
this.emit(":delegate");
} else {
console.log("in completed");
console.log("returning: "+ JSON.stringify(this.event.request.intent));
// Dialog is now complete and all required slots should be filled,
// so call your normal intent handler.
return this.event.request.intent;
}
};
function bmiCalculator (userHeight, userWeight ) {
var speechOutput = " ";
var h = userHeight/100;
var calcBMI = 0;
calcBMI = userWeight / (h*h);
calcBMI = calcBMI.toFixed(2);
if (calcBMI < 18.5) {
speechOutput += "Based on your weight of " +weight+ " kilograms and your height of " + height + " metres, your BMI is " +calcBMI+ ". Meaning you are currently underweight.";
speechOutput += " I would advise you to increase your calorie intake, whilst remaining active.";
return speechOutput;
}
else if (calcBMI >=18.5 && calcBMI < 25){
speechOutput += "Based on your weight of " +weight+ " kilograms and your height of" + height + " metres, your BMI is " +calcBMI+ ". Meaning you are currently at a normal weight.";
speechOutput += " I would advise you to stay as you are but ensure you keep a healthy diet and lifestyle to avoid falling above or below this.";
this.response.speak(speechOutput);
return speechOutput;
}
else if (calcBMI >=25 && calcBMI < 29.9){
speechOutput += "Based on your weight of " +weight+ " kilograms and your height of" + height + " metres, your BMI is " +calcBMI+ ". Meaning you are currently overweight.";
speechOutput += " I would advise you to exercise more to fall below this range. A healthy BMI is ranged between 18.5 and 24.9";
this.response.speak(speechOutput);
return speechOutput;
}
else{
speechOutput += "Based on your weight of " +weight+ " kilograms and your height of" + height + " metres, your BMI is " +calcBMI+ ". Meaning you are currently obese.";
speechOutput += " I would advise you to reduce your calorie intake, eat more healthy and exercise more. A healthy BMI is ranged between 18.5 and 24.9";
return speechOutput;
}
};
The code outlines my database connection. I created the connection query as a function as I will need to make varying queries to the database based on the context. Is there a way to create a function within the exports.handler function that will only call the query when needed?
Or are there any other solutions with regards to connecting to the database in such a way.
You are running into multiple issues, without using a Promise or await, your call is running async and you will never get an answer immediately from the RDS for the lambda call. you need to create a function that will wait for an answer before continuing its logic.
The other issue you will run into is the MySQL RDS instance is it running constantly there may be a cold start issue.
The last thing is in the AWS lambda console be sure to allocate enough resources in compute and time to run this function the default 128 mb of memory and the time to run the function can be adjusted to improve performance
Use sync-mysql to make synchronous queries to a mysql database. It worked for me.

Getting response with NodeJS request module

I just started using the twitch kraken api and I have a few questions.
Whenever I attempt to get a JSON object there is no response. I am attempting to run this function through Amazon AWS Lambda, and don't have access to a console.
In the code below my callback function will always print out "SUCCESS got streamers ERROR". I am pretty certain right now the "ERROR" comes from my initial setting of result.
How come result does not get changed into the proper JSON?
I have used postman and it returns the proper thing with the query and param, and headers:
function getJSON(callback){
var result = "ERROR";
request.get(url(games[0]),function(error,response,body){
console.log("requested for url: " + url(games[0]));
var d = JSON.parse(body);
result = d.streams[0];//.channel.display_name;
// for(var i = 0; i < limit; i++){
// streamers.push(d.streams[i].channel.display_name)
// }
streamers.push(result);
});
if (streamers.length < 0){
callback("ERROR");
}else{
callback("SUCCESS got streamers " + result);
}
}
function url(game){
return {
url: "https://api.twitch.tv/kraken/streams/",//twitchlimit,
qs : {
'game' : 'overwatch',
'limit' : 2
},
headers: {
'Client-ID': clientID,
'Accept': 'application/json',
'Accept-Charset': 'utf-8',
}
};
}
I think your streamers code
if (streamers.length < 0){
callback("ERROR");
}else{
callback("SUCCESS got streamers " + result);
}
should be included in the request callback because currently it's not waiting for the request to finish, it's just carrying on so therefore the value of result will not change. Also the array length cannot be less than 0 so it will always go to the else and say "SUCCESS got streamers ERROR"
Thank you guys for the suggestions. I did have a few oversights and attempted to fix them.
I have implemented you suggestions and it seems to have worked a bit. I ended up putting the json.parse into a try/catch block, and moved the if/else statements inside the getJSON method. However, now I don't get any output.
This is how I am invoking the getJSON method:
function handleGameResponse(intent,session,callback){
//gets the game
var game = intent.slots.game.value;
if (!games.includes(game)){
var speechOutput = "You asked for: " + intent.slots.game.value;
//var speechOutput = "You asked for: " + games[game] + " That game is currently not an option. These are your current options: " + arrayToString(games)
var repromptText = "Please ask one from the current options.";
var header = "Invalid Game";
}else {
getJSON(function(data){
if(data !== "ERROR"){
var speechOutput = data; //capitalizeFirst(game) + " top three streamers are: " + arrayToString(streamers) + '.';
var repromptText = "Do you want to hear more about games?";
var header = capitalizeFirst(game);
}else{
var speechOutput = "I'm sorry, something went wrong and I could not get the streamers.";
}
//speechOutput = data;
});
//speechOutput = games[0] + " games[0], game= " + game; //this executes so the getJSON isn't executing
}
var shouldEndSession = false;
callback(session.attributes,buildSpeechletResponse(header,speechOutput,repromptText,shouldEndSession));
}
Does the above execute the same way? As in the shouldEndSession and callback execute before the getJSON has time to give a response?
For ref, this is the getJSON method now:
function getJSON(callback){
var result = "ERROR";
request.get(url(games[0]),function(error,response,body){
try{
var d = JSON.parse(body);
} catch (err){
callback("Sorry, something seems to have malfunctioned while getting the streamers");
}
result = d.streams[0].channel.display_name;
// for(var i = 0; i < limit; i++){
// streamers.push(d.streams[i].channel.display_name)
// }
streamers.push(result);
if (streamers.length <= 0){
callback("ERROR");
}else{
callback("SUCCESS got streamers " + result);
}
});
}

multiple async mongo request generate messed up returns

I'm trying to build a JSON out of multiple requests on my mongodb.
since I'm not using DBRef, I have to build the "table joints" by myself, and that's how I ended up in this mess.
This is the code that is giving me the headaches from a couple of days now.
(the mongo part is done with mongoskin)
var getUserFeed = function(thelimit, out) {
userfeed = db.collection("userfeed");
apparel = db.collection("apparel");
store = db.collection("stores");
if(thelimit)
args = {limit:thelimit, sort: [['date',-1]]};
userfeed.find({},args).toArray(function(e, feed) {
if (e) console.log("error: ", e);
// gather aparel infos
var i=0;
var ret_feeds = [];
feed.forEach(function(cur_feed) {
var outfits=[];
console.log("beginning with: " + cur_feed.url);
var resfeed = "";
resfeed = cur_feed;
resfeed.url = baseurl + snapurl + resfeed.url + "_small.jpg";
i=0;
cur_feed.apparel_ids.forEach(function(item) {
/*>>*/ apparel.find({"_id": item},{limit:1}).toArray(function(e, results) {
console.log(">>>>>>>>>>> APPAREL_FIND { i:" + i + "}");
if (e) console.log("error: ", e);
results = results[0];
if(results.apparel_cat == 1)
url_subcat = "pants/";
else if(results.apparel_cat == 2)
url_subcat = "shirts/";
else if(results.apparel_cat == 2)
url_subcat = "tshirts/";
results.thumb = baseurl + outfiturl + url_subcat + results.apparel_id + "/front.jpg";
results.size = "M"; ///// TOBE REAL VERY SOON
results.gallery = [
baseurl + outfiturl + url_subcat + results.apparel_id + "/model.jpg",
baseurl + outfiturl + url_subcat + results.apparel_id + "/front.jpg"
];
outfits.push(results); // quick and dirty, 2 b refined..
i++;
if(i>=cur_feed.apparel_ids.length)
{
// pack it up
// resfeed.url = resfeed.url;
resfeed.outfits = outfits;
resfeed.fav = false;
resfeed.bough = false;
// retrieve store infos
/*>>>*/ store.find({"_id":resfeed.store_id}, {limit: 1}).toArray(function(e, resstore) {
console.log("\t############# STORE_FIND { i:" + i + "}");
if (e) console.log("error: ", e);
resfeed.store = resstore[0];
resfeed.store.class = "hem";
ret_feeds.push(resfeed);
if(ret_feeds.length >= feed.length)
{
console.log("\t\t######################calling return [ ret_feeds.length = " + ret_feeds.length + " feed.length = " + feed.length);
out.send(ret_feeds);
}
});
}
});
});
});
});
}
This code fails, because returns the json before finishing its task, so the next time that it tries to return another json it crashes miserably due to the fact the the headers have already been sent.
Now as you can see, I have 3 collections: userfeed, apparel and stores.
the goal of this function is to retrieve all the items in the userfeed collection, extract the outfits (based on the outfit_id array that is part of the userfeed collection), and also extract the store infos related in the same way to each userfeed entry, like so:
I know that async.js or equivalent is the way to go: I've red like a gazillion of other posts here on SO, but I still can't get my head around it, probably because the whole mechanism behind the async.js or flow control in general it's still out of focus in my mind.
I'm still a noob at node :)
UPDATE
I think I found the right path for understanding here: http://www.sebastianseilund.com/nodejs-async-in-practice
this guy made a terrific job in describing use-case by use-case all the ways to apply async.js to your code.
I'll post the solution as soon as I get around it.
UPDATE 2
Thanks to the above dude I could work out a working solution, below is the answer.
After so much struggling I have finally managed to get a solution.
async.js was the answer as I was (obviously) suspecting.
FYI here's the working code.
If you like to point out improvements or anything else, you are more than welcome
var getUserFeed = function(thelimit, out) {
userfeed = db.collection("userfeed");
apparel = db.collection("apparel");
store = db.collection("stores");
var args;
if(thelimit)
args = {limit:thelimit, sort: [['date',-1]]};
var outfits=[];
var feeds = array();
async.series([
// userfeed find
function(callback) {
userfeed.find({},args).toArray(function(e, feed) {
if(e) callback(e);
feeds = array(feed);
console.log(feeds.length + " retrieved. stepping in");
callback(null, null);
});
},
// join
function(callback) {
async.forEach(feeds, function(thefeed, callback) {
var i = feeds.indexOf(thefeed);
async.parallel([
// load apparel infos
function(callback) {
console.log("\t >>> analyzing thefeed id " + thefeed._id);
async.forEach(thefeed.apparel_ids, function(apparel_id, callback) {
apparel.find({"_id": apparel_id},{limit:1}).toArray(function(e, results) {
if (e) console.log("error: ", e);
results = results[0];
if(results.apparel_cat == 1)
url_subcat = "pants/";
else if(results.apparel_cat == 2)
url_subcat = "shirts/";
else if(results.apparel_cat == 2)
url_subcat = "tshirts/";
results.thumb = baseurl + outfiturl + url_subcat + results.apparel_id + "/front.jpg";
results.size = "M"; ///// TOBE REAL VERY SOON
results.gallery = [
baseurl + outfiturl + url_subcat + results.apparel_id + "/model.jpg",
baseurl + outfiturl + url_subcat + results.apparel_id + "/front.jpg"
];
console.log("\t\t### pushing data into thefeed_index: " + i);
if(!util.isArray(feeds[i].oufits)) feeds[i].outfits = array();
feeds[i].outfits.push(results);
callback(null, null);
});
}, callback);
},
// load store infos
function(callback) {
store.find({"_id":thefeed.store_id}, {limit: 1}).toArray(function(e, resstore) {
console.log("\t### STORE_FIND");
if (e) console.log("error: ", e);
feeds[i].store = resstore[0];
feeds[i].store.class = "hem";
callback(null, null);
});
}
], callback);
}, callback);
}
// MAIN
], function(err, result) {
console.log("feed retrieval completed. stepping out");
if (err) return next(err);
out.send(feeds);
});
};

typeof fails on trigger object property

I am trying to add an dumpObject function to a Spreadsheet Container bound Script.
Ideally, it is for visibility into variables passed through triggers.
I can run it all day long from within the Script Editor, but when setup as either an onEdit event or onEdit Installible trigger, it dies with no error.
I did some trial and error toast messages and confirmed the code in dumpObject is being executed from the Trigger.
If you take this code below, setup onEdit2 as an installable trigger, you might see it.
To see it work as a Trigger, uncommment the first line //e of onEdit2.
Best I can figure, is something in the e object coming from the trigger that is not quite what is expected of an object?
This test should be limiting the maxDepth to 5, so I don't think I'm hitting the 1000 depth limit.
UPDATE: The problem is calling typeof on the trigger object properties. For example, "typeof e.user" reports the following error: Invalid JavaScript value of type
Thanks,
Jim
function onEdit2(e) {
//e = {fish:{a:"1",b:"2"},range:SpreadsheetApp.getActiveSpreadsheet().getActiveRange(),B:"2"};
Browser.msgBox(typeof e);
Browser.msgBox("U:" + Utilities.jsonStringify(e));
e.range.setComment("Edited at: " + new Date().toTimeString());
Browser.msgBox("ShowOBJ:"+dumpObject(e, 5));
}
function dumpObject(obj, maxDepth) {
var dump = function(obj, name, depth, tab){
if (depth > maxDepth) {
return name + ' - Max depth\n';
}
if (typeof obj === 'object') {
var child = null;
var output = tab + name + '\n';
tab += '\t';
for(var item in obj){
child = obj[item];
if (typeof child === 'object') {
output += dump(child, item, depth + 1, tab);
} else {
output += tab + item + ': ' + child + '\n';
}
}
}
return output;
};
return dump(obj, '', 0, '');
}
You're not getting quite what you expect from the event object. If you throw in:
for(var q in e) {
Logger.log(q + " = " + e[q])
}
and then check the View->Logs menu item in the script editor you get
source = Spreadsheet
user = <your user>
So, checking the docs, you can come up with this as an alternative to your e.range.setComment("Edited at: " + new Date().toTimeString());:
e.source.getActiveSheet().getActiveCell().setComment("Edited at: " + new Date().toTimeString());
note: you can debug an error like you were (secretly) getting by wrapping your statement in a try catch like so:
try {
e.range.setComment("Edited at: " + new Date().toTimeString());
} catch (ex) {
Logger.log(ex);
}
and then checking the logs as mentioned above (or dumping to Browser.msgBox(), if you prefer).
This might not be a great "answer" but it works.
I found that replacing typeof with Object.prototype.toString.call(obj) I got something usable.
Of note, the e object returns [object Object] but the properties (e.user) return [object JavaObject]
if (Object.prototype.toString.call(obj).indexOf("object") != -1) {
var child = null;
var output = tab + name + '\n';
tab += '\t';
for(var item in obj){
child = obj[item];
if (Object.prototype.toString.call(child).indexOf("object") != -1) {
output += dump(child, item, depth + 1, tab);