TypeError thrown when library is included in express - javascript

I've come across a very peculiar bug that I can't seem to solve. In Node.js, I have an API wrapper library called Markitable:
var request = require('request');
var Q = require('q');
/**
* Markit on Demand API Client
* #constructor
* #author Chandler Freeman <chandler.freeman#gmail.com>
*/
var Markitable = function() {
};
module.exports = Markitable;
endpoints = {
lookup: 'http://dev.markitondemand.com/Api/v2/Lookup',
quote: 'http://dev.markitondemand.com/Api/v2/Quote',
interactivechart: 'http://dev.markitondemand.com/Api/v2/InteractiveChart'
};
/**
* Company lookup
* #httpMethod GET
* #param {ticker} String A string containing the ticker for the stock
* #param {callback} callback (Optional) If callback is passed, than the function will use callbacks
* #promises yes By default, this function uses promises
*/
Markitable.prototype.lookup = function(ticker, callback) {
var options = {
url: endpoints.lookup,
headers: this.headers,
qs: { 'input': ticker.toUpperCase() },
method: 'GET',
json: true
};
if (!callback) {
var d = Q.defer();
request(options, function(err, httpResponse, body) {
if (err) return d.reject(err);
d.resolve(body);
});
return d.promise;
}
else
request(options, callback);
};
As you can see, all it does is fetch data from the Markit on Demand API and return a Q promise. However, every time I include the
var Markitable = require('markitable');
statement anywhere in my code, when I navigate to the / route of my application I receive
Failure on / route: TypeError: Cannot read property 'user' of undefined
I am using Express I have absolutely no idea at all why this is happening; I've reread the source several times, checked source control changes, everything, yet I can't find the root of the issue. This behavior only persists when the library is included; as soon as I remove that statement, everything works perfectly. I don't understand because the code for this library was the exact same code I used from another library I wrote, and the first one looks great. Here is the code for my routes file:
var User = require('../app/models/user');
var Robinhood = require('marian');
var util = require('util');
var Q = require('q');
module.exports = function(app, passport) {
// =========================================
// Table of Contents(Search by name)
//
// 1. Root
//
// =========================================
// ********************* 1. Root *********************
app.get('/', isLoggedIn, asyncCallback(function*(req, res) {
rh = new Robinhood(req.user.local.rhtoken);
var viewData = { user : req.user };
try {
// e: <Cannot read property user of undefined>
viewData.rhUser = yield rh.getUser();
viewData.rhUser.basic_info = yield rh.getBasicInfo();
viewData.rhPortfolio = yield rh.getPortfolio();
viewData.getOrders = yield rh.getOrders();
viewData.rhAccount = yield rh.getAccount();
viewData.active = { page : 'dashboard' };
res.render('main_pages/maindashboard.ejs', viewData);
}
catch(e) {
console.log("Failure on / route: " + e);
res.render('meta/500.ejs');
}
}));
};
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.redirect('/login');
}
// Code originally from James Long
// http://jlongster.com/A-Study-on-Solving-Callbacks-with-JavaScript-Generators
function asyncCallback(gen) {
return function() {
return Q.async(gen).apply(null, arguments).done();
};
}
Any ideas about why this strange behavior may be occurring? I can't imagine why importing a library would affect the 'req' object, but somehow it does. Could this be a bug in Express?
EDIT:
Forgot the stack trace:
TypeError: Cannot read property 'user' of undefined
at Robinhood.getUser (/vagrant/StockFire/node_modules/marian/index.js:110:26)
at /vagrant/StockFire/app/routes.js:28:40
at next (native)
at Function.continuer (/vagrant/StockFire/node_modules/q/q.js:1278:45)
at /vagrant/StockFire/node_modules/q/q.js:1305:16
at /vagrant/StockFire/app/routes.js:226:29
at Layer.handle [as handle_request] (/vagrant/StockFire/node_modules/express/lib/router/layer.js:95:5)
at next (/vagrant/StockFire/node_modules/express/lib/router/route.js:131:13)
at isLoggedIn (/vagrant/StockFire/app/routes.js:217:16)
at Layer.handle [as handle_request] (/vagrant/StockFire/node_modules/express/lib/router/layer.js:95:5)
at next (/vagrant/StockFire/node_modules/express/lib/router/route.js:131:13)
at Route.dispatch (/vagrant/StockFire/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/vagrant/StockFire/node_modules/express/lib/router/layer.js:95:5)
at /vagrant/StockFire/node_modules/express/lib/router/index.js:277:22
at Function.process_params (/vagrant/StockFire/node_modules/express/lib/router/index.js:330:12)
at next (/vagrant/StockFire/node_modules/express/lib/router/index.js:271:10)

Solved the issue. Simple bug, but very hard to find. I discovered that a variable named endpoints existed in both libraries, but neither was declared with var, meaning that the variable was overwritten since they both existed in the javascript global scope. Lessons learned: Always check variable scope.

Related

req is undefined when using req.accept inside nested function

I've recently come across a problem when working with the built-in req.accepts, req.acceptsLanguages, req.acceptsCharsets, and req.acceptsEncodings functions in express.
I have an express middleware function like this:
function acceptCheckpoint(acceptOpts) {
// Calling the following function results in a TypeError.
function checkAccept(req, res, opts) {
let acceptFunction = null;
switch (opts.whichAccept) {
case "type":
acceptFunction = req.accepts;
break;
case "lang":
acceptFunction = req.acceptsLanguages;
break;
case "charset":
acceptFunction = req.acceptsCharsets;
break;
case "encoding":
acceptFunction = req.acceptsEncodings;
break;
default:
acceptFunction = req.accepts;
break;
}
return acceptFunction(opts.acceptedTypes);
}
return (req, res, next) => {
const accepted = [];
Object.getOwnPropertyNames(acceptOpts).forEach(key => {
if (key === "ignoreAcceptMismatch") { return; }
const acceptsType = checkAccept(req, res, {
whichAccept: key,
acceptedTypes: acceptOpts[key]
});
accepted.push(acceptsType);
});
if (accepted.some(type => !type) && !acceptOpts.ignoreAcceptMismatch) {
res.type("html");
res.status(406);
res.send("<h1>406 Not Acceptable.</h1>");
return;
}
next();
};
}
Which, in theory, should work. But the program keeps complaining and logs this error:
TypeError: Cannot read property 'headers' of undefined
at new Accepts (/Users/hortoncheng/Desktop/Programs/colonialwars/dev/node_modules/accepts/index.js:37:22)
at Accepts (/Users/hortoncheng/Desktop/Programs/colonialwars/dev/node_modules/accepts/index.js:34:12)
at req.accepts (/Users/hortoncheng/Desktop/Programs/colonialwars/dev/node_modules/express/lib/request.js:133:16)
at checkAccept (/Users/hortoncheng/Desktop/Programs/colonialwars/dev/Lib/middleware.js:208:12)
at /Users/hortoncheng/Desktop/Programs/colonialwars/dev/Lib/middleware.js:216:27
at Array.forEach (<anonymous>)
at /Users/hortoncheng/Desktop/Programs/colonialwars/dev/Lib/middleware.js:214:44
at Layer.handle [as handle_request] (/Users/hortoncheng/Desktop/Programs/colonialwars/dev/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/Users/hortoncheng/Desktop/Programs/colonialwars/dev/node_modules/express/lib/router/index.js:317:13)
at /Users/hortoncheng/Desktop/Programs/colonialwars/dev/node_modules/express/lib/router/index.js:284:7
The thing is, when I use req.accepts or one of those .accepts functions in the main function (acceptCheckpoint), like this:
// Pretend we're in acceptCheckpoint...
// This works.
accepted.push(req.accepts("html"));
It works. And, when I log the req object in either of those functions, it returns the expected value. I've also tried logging the req object in the request.js file of the express module, and there, it returned undefined. So that led me to believe that it was a problem with express itself. I tried deleting package-lock.json and node_modules, and then running npm install. Didn't fix it. And yes, I'm calling the express middleware function correctly. Any idea why this is happening?
I'm using express v4.17.1, Node.JS v12.18.1, and NPM v6.14.5.
The function is presumably trying to get req from its this context. But you're not passing functions with context.
Change this line:
return acceptFunction(opts.acceptedTypes);
to:
return acceptFunction.call(req, opts.acceptedTypes);
The first argument to the call() method is the object that should be used as this in the called function.

Node.js variable use outside of function

I'm trying to make it so that I can pass my trends variable from its function into a renderer for my Pug template, and I can't seem to do it.
var express = require('express');
var router = express.Router();
var googleTrends = require('google-trends-api');
var auth = require('http-auth');
var ustrends;
var uktrends;
const Console = require('console').Console;
var basic = auth.basic({
realm: "Web."
}, function (username, password, callback) { // Custom authentication method.
callback(username === "user" && password === "pass");
}
);
var find = ',';
var regex = new RegExp(find, 'g');
googleTrends.hotTrends('US').then(function(trends){
ustrends = trends
});
googleTrends.hotTrends('EU').then(function(trends1) {
uktrends = trends1
});
console.log(ustrends);
/* GET home page. */
router.get('/', auth.connect(basic), function(req, res, next) {
res.render('index', {trends: ustrends.toString().replace(regex, ", "), trends1: uktrends.toString().replace(regex, ", "), title: 'Trends in the U.S & U.K'});
});
module.exports = router;
As you can see, I'm trying to pass the "ustrends" and "uktrends" variables into the renderer. Any help is appreciated.
Remember that hotTrends will return a promise, as it's getting results from Google's API. Since the renderer is outside of the callbacks wherein ustrends and uktrends are set to values, there's no guarantee these values will be set prior to the renderer being called.
You could use several nested callbacks, but that would lead to some code pushed pretty far to the right; I recommend the async library, which has a function called series that allows you to pass in 1) an array of functions to be executed in order and 2) a callback that will be executed after the functions have completed that takes an error if there was one and the result of the functions as an argument. In the snippet below, the trends API returns results prior to the renderer being called:
async.series([
function(cb) {
googleTrends.hotTrends('US').then(function(trends){
ustrends = trends;
cb();
})
},
function(cb) {
googleTrends.hotTrends('EU').then(function(trends1) {
uktrends = trends1;
cb();
});
}
], function(err, results) {
/* handle errors, do rendering stuff */
})

Getting "globals is not defined" error when globals variable is clearly defined

I'm currently in the process of reorganizing the routes in my web application (I stupidly defined all the routes in index.js) and for some reason in several of these files I'm having this inexplicable problem: I'm getting errors saying the "globals" variable is undefined when it is, in fact, defined.
This is one of the offending files:
http://pastebin.com/7Q5ExZDa
At line 37 I log the contents of globals.DB_URL, and it exists. The very next line I get an error that globals isn't defined. What am I doing wrong?
mongodb://localhost:27017/[redacted_db_name] // console log output
--- Error: ReferenceError: globals is not defined ---
Location: function (err){
utilities.logError(err, arguments.callee.toString());
res.redirect("/");
return;
}
UPDATE:
First problem was solved: I wasn't importing globals.js in utilities.js, and was trying to call a function that needed data from globals to function.
Unfortunately, now I get this error:
--- Error: TypeError: Cannot call method 'connect' of undefined ---
Location: function (err){
utilities.logError(err, arguments.callee.toString());
res.redirect("/");
return;
}
This error happens at the second promise. I think it may have something to do with the code in utilities, specifically the identifyUserByToken function.
/**
* identifyUserByToken
* Compares a given session token against the session tokens collection
* executes a given callback function if a match is found
* #param {String} userToken The session token to search for
* #param {function(Object, String)} The callback function to be called upon success, failure, or error
*/
function identifyUserByToken(userToken, callback){
var user_tokens;
var users
var promise = new Promise(function(resolve, reject){
mongoClient.connect(globals.DB_URL)
.then(function(db){ // Search user_tokens for userToken
user_tokens = db.collection("user_tokens");
users = db.collection("users");
return user_tokens.find({token : userToken}).toArray();
})
.then(function(result){ // Search users for the returned userID
var userID = result[0].userid;
return users.find({ userid : userID }).toArray();
})
.then(function(matchingUsers){ // Pass returned user object to callback
var user = matchingUsers[0];
if(callback != undefined) callback(undefined, user);
resolve(user);
})
.catch(function(err){
if(callback != undefined) callback(err, undefined);
reject(err);
});
});
return promise;
}
I know this means mongodb is undefined, but I'm importing it in the file
var globals = require("./globals");
/* == Third Party Libraries == */
var chalk = require("chalk"); /* usage: console output coloring */
var crypto = require("crypto"); /* usage: cryptograpgic primitives (password hashing, etc...) */
var mongodb = require("mongodb"); /* usage: data storage schema */
var mongoClient = mongodb.mongoClient;
EDIT: Solved TypeError
Simple typo. In utilities I was assigning the mongoClient variable incorrectly
How it was being defined: var mongoClient = mongodb.mongoClient;
How it needed to be defined: var mongoClient = mongodb.MongoClient;
Sorry! My bad.
The issue is with  var mongoClient = mongodb.mongoClient; it should be with a capital M:
 var mongoClient = mongodb.MongoClient;

How read stream .pipe(myfunction())

How read stream .pipe(myfunction())?
I try , but give errors. How read stream of gulp.src('./userdata.json') and .pipe()? I not know how is it make.
gulpfile.js
var upmodul = require("modul-json");
//......
return gulp.src('./userdata.json')
.pipe(upmodul());
......//
node_modules / modul-json / index.js
'use strict';
var Stream = require('stream');
var loger = function () {
var readable = new Stream.Readable({
read: function (n) {
this.push("ll");
}
});
}
module.exports = loger;
Error
[00:19:39] TypeError: Cannot read property 'on' of undefined
at DestroyableTransform.Readable.pipe (E:\Developers\WebDeveloper\OpenServer
-WebProg\domains\progectapi2\node_modules\vinyl-fs\node_modules\readable-stream\
lib\_stream_readable.js:516:7)
at Gulp.<anonymous> (E:\Developers\WebDeveloper\OpenServer-WebProg\domains\p
rogectapi2\gulpfile.js:159:9)
at module.exports (E:\Developers\WebDeveloper\OpenServer-WebProg\domains\pro
gectapi2\node_modules\orchestrator\lib\runTask.js:34:7)
at Gulp.Orchestrator._runTask (E:\Developers\WebDeveloper\OpenServer-WebProg
\domains\progectapi2\node_modules\orchestrator\index.js:273:3)
at Gulp.Orchestrator._runStep (E:\Developers\WebDeveloper\OpenServer-WebProg
\domains\progectapi2\node_modules\orchestrator\index.js:214:10)
at Gulp.Orchestrator.start (E:\Developers\WebDeveloper\OpenServer-WebProg\do
mains\progectapi2\node_modules\orchestrator\index.js:134:8)
at C:\Users\Tiki
\AppData\Roaming\npm\node_modules\gulp\bin\gulp.js:129:20
at nextTickCallbackWith0Args (node.js:433:9)
at process._tickCallback (node.js:362:13)
at Function.Module.runMain (module.js:432:11)
The gulp documentation has some information on building a plugin that might be useful to you. Just a sample from that page talks about transforming streams.
All gulp plugins essentially boil down to this:
var Transform = require('stream').Transform;
module.exports = function() {
// Monkey patch Transform or create your own subclass,
// implementing `_transform()` and optionally `_flush()`
var transformStream = new Transform({objectMode: true});
/**
* #param {Buffer|string} file
* #param {string=} encoding - ignored if file contains a Buffer
* #param {function(Error, object)} callback - Call this function (optionally with an
* error argument and data) when you are done processing the supplied chunk.
*/
transformStream._transform = function(file, encoding, callback) {
var error = null,
output = doSomethingWithTheFile(file);
callback(error, output);
});
return transformStream;
};

Errors when using socket.io in AngularJs on user updates

Hi I'm trying to make an automated update on a list of articles whenever the user changes his preferred language.
The way I'm trying to do this is by having a IO socket update whenever the user changes in the database.
However I seem to be unsuccesfull in my endeavors, and I have no idea as to why.
Since I'm new to socket.io I thought I'll ask the coding gods in here for some help.
May the software be with you ^^
PS: the project is a Angular fullstack project, scaffolded with Yeoman
Code time!
client/ components/ articlebar/ articlebar.controller.js
'use strict';
angular.module('unityAcademyApp')
.controller('ArticlebarCtrl', function ($scope, $location, Auth, socket) {
$scope.articles = {};
function populateArticles(){
...
Some functionality where $scope.articles are set
...
};
socket.syncUpdates('user', $scope.articles, function() {
console.log('hit');
populateArticles();
});
});
client/ components/ socket/ socket.service.js
/* global io */
'use strict';
angular.module('unityAcademyApp')
.factory('socket', function(socketFactory) {
// socket.io now auto-configures its connection when we ommit a connection url
var ioSocket = io('', {
// Send auth token on connection, you will need to DI the Auth service above
// 'query': 'token=' + Auth.getToken()
path: '/socket.io-client'
});
var socket = socketFactory({
ioSocket: ioSocket
});
return {
socket: socket,
/**
* Register listeners to sync an array with updates on a model
*
* Takes the array we want to sync, the model name that socket updates are sent from,
* and an optional callback function after new items are updated.
*
* #param {String} modelName
* #param {Array} array
* #param {Function} cb
*/
syncUpdates: function (modelName, array, cb) {
cb = cb || angular.noop;
/**
* Syncs item creation/updates on 'model:save'
*/
socket.on(modelName + ':save', function (item) {
var oldItem = _.find(array, {_id: item._id});
var index = array.indexOf(oldItem); // this is line 39
var event = 'created';
// replace oldItem if it exists
// otherwise just add item to the collection
if (oldItem) {
array.splice(index, 1, item);
event = 'updated';
} else {
array.push(item);
}
cb(event, item, array);
});
/**
* Syncs removed items on 'model:remove'
*/
socket.on(modelName + ':remove', function (item) {
var event = 'deleted';
_.remove(array, {_id: item._id});
cb(event, item, array);
});
},
/**
* Removes listeners for a models updates on the socket
*
* #param modelName
*/
unsyncUpdates: function (modelName) {
socket.removeAllListeners(modelName + ':save');
socket.removeAllListeners(modelName + ':remove');
}
};
});
server/ config/ socketio.js
/**
* Socket.io configuration
*/
'use strict';
var config = require('./environment');
// When the user disconnects.. perform this
function onDisconnect(socket) {}
// When the user connects.. perform this
function onConnect(socket) {
// When the client emits 'info', this listens and executes
socket.on('info', function (data) {
console.info('[%s] %s', socket.address, JSON.stringify(data, null, 2));
});
// Insert sockets below
require('../api/translation/translation.socket').register(socket);
require('../api/comment/comment.socket').register(socket);
require('../api/article/article.socket').register(socket);
require('../api/language/language.socket').register(socket);
require('../api/thing/thing.socket').register(socket);
require('../api/user/user.socket').register(socket);
}
module.exports = function (socketio) {
// socket.io (v1.x.x) is powered by debug.
// In order to see all the debug output, set DEBUG (in server/config/local.env.js) to including the desired scope.
//
// ex: DEBUG: "http*,socket.io:socket"
// We can authenticate socket.io users and access their token through socket.handshake.decoded_token
//
// 1. You will need to send the token in `client/components/socket/socket.service.js`
//
// 2. Require authentication here:
// socketio.use(require('socketio-jwt').authorize({
// secret: config.secrets.session,
// handshake: true
// }));
socketio.on('connection', function (socket) {
socket.address = socket.handshake.address !== null ?
socket.handshake.address.address + ':' + socket.handshake.address.port :
process.env.DOMAIN;
socket.connectedAt = new Date();
// Call onDisconnect.
socket.on('disconnect', function () {
onDisconnect(socket);
console.info('[%s] DISCONNECTED', socket.address);
});
// Call onConnect.
onConnect(socket);
console.info('[%s] CONNECTED', socket.address);
});
};
server/ api/ user/ user.socket.js
/**
* Broadcast updates to client when the model changes
*/
'use strict';
var User = require('./user.model');
exports.register = function(socket) {
User.schema.post('save', function (doc) {
onSave(socket, doc);
});
User.schema.post('remove', function (doc) {
onRemove(socket, doc);
});
}
function onSave(socket, doc, cb) {
socket.emit('user:save', doc);
}
function onRemove(socket, doc, cb) {
socket.emit('user:remove', doc);
}
Errors encountered so far
So far I get the following error when running the code
TypeError: array.indexOf is not a function
at Socket.<anonymous> (socket.service.js:39)
at socket.js:24
at angular.js:17782
at completeOutstandingRequest (angular.js:5490)
at angular.js:5762
(anonymous function) # angular.js:12416
$get # angular.js:9203
(anonymous function) # angular.js:17785
completeOutstandingRequest # angular.js:5490
(anonymous function) # angular.js:5762
I'm not sure why you get that error, but I think I know why your data isn't updating.
You have to wrap your callback functions inside a $timeout function in order to trigger your changes. For example, you could do this:
$timeout(function(){
cb(event, item, array);
}, 0);
Remember to include $timeout directive in your socket factory.
what's 'undescore' mean? I'm not sure about the 'undescore', but I guess that's alias for 'this'. I think you should init var _ = this.
I just guessing.
I found the problem.
The error where due to it looking for a user in a list of articles, which returned undefined due to there not being any match. The solution was therefore to change the code in client/ components/ articlebar/ articlebar.controller.js
from
socket.syncUpdates('user', $scope.articles, function() {
console.log('hit');
populateArticles();
});
to
socket.syncUpdates('user', $scope.users, function() {
console.log('hit');
populateArticles();
});

Categories