I am new to JS and I have a simple question. I am writing a node_redis code to connect to the db.
I have created a db module in which there is an init function to start the connection.
the module also has another function which queries the db. for the query, i will need the connection (dbConnection) object from the first function and then use it in the 2nd function. how do i do this? I can get it done by using global variables but most places tell me its a bad idea.
Sorry if the question is stupid, I am learning how to code. results in google tell me that it can be done by passing it as an object property. But i don't know if it is the proper way to do things in my context, or even how to do it.
var redis = require('redis');
module.exports = redisDb = {
// Initialize the module. Invokes callback when ready (or on error)
init: function(config, callback) {
// Open the database connection
var dbConnection = redis.createClient(config.db.port, config.db.host, {no_ready_check: true});
dbConnection.auth(config.db.authKey, function() {
console.log("Connected!");
console.log(dbConnection.keys('*'));
});
dbConnection.on('connect' , log('connect'));
dbConnection.on('ready' , log('ready'));
dbConnection.on('reconnecting', log('reconnecting'));
dbConnection.on('error' , log('error'));
dbConnection.on('idle' , log('idle...'));
dbConnection.on('end' , log('end'));
function log(type) {
return function() {
console.log(type, arguments);
}
}
callback("callback - Connected");
},
getValue: function(key, callback) {
dbConnection.hgetall("hosts", function (err, obj) {
console.dir(obj);
});
}
};
EDIT:
tried another way. still failing.
module.exports = redisDb = (function() {
var config = require('../config');
var redis = require('redis');
return {
connection: function(config) {
var dbConnection = redis.createClient(config.db.port, config.db.host, {no_ready_check: true});
dbConnection.auth(config.db.authKey, function() {
console.log("Authenticated!");
});
return dbConnection
},
getValue: function(connection, callback) {
connection.hgetall("hosts", function (err, obj) {
console.dir(obj);
});
}
}
})();
Now one way is as you said to make dbConnection an object Property.
The other way is to call init() in getValue() which results in establishing different connection every time you want some value.
Because a DB Connection is a valuable resource, I'd say it's better to use the second variant.
Of course then you'll need to return dbConnection from init().
PS: Global variables were made initially for such things and then people saw that fewer they are the better. That's why using global vars is considered a bad style.
I made the code work by simply declaring the connection in the parent function.
module.exports = _redisDb = (function() {
var redis = require('redis');
var config = require('../config');
var dbConnection = redis.createClient(config.db.port, config.db.host, {no_ready_check: true});
dbConnection.auth(config.db.authKey, function() {
console.log("Authenticated!");
});
var getValue = function() {
dbConnection.hgetall("hosts", function (err, obj) {
console.dir(obj);
});
}
return {
getValue: getValue
}
})();
While the above work,s I'd still like for someone to tell me how can I pass variables/objects between functions off the same module.
Related
Please see my code below:
I am trying to assign the recordset to a variable, can use index.js to call this variable out.
I am able to console.log the recordset. But when I call this IIFE, it is always says undefined.
var mssql = require('mssql');
var dbcon = require('./dbcon');
var storage = (function () {
var connection = new mssql.Connection(dbcon);
var request = new mssql.Request(connection);
connection.connect(function (recordset) {
request.query('select getdate()', function (err, recordset) {
console.dir(recordset);
});
connection.close();
});
})();
module.exports = storage;
index.js
var storage = require('./storage');
"AMAZON.HelpIntent": function (intent, session, response) {
storage(function (recordset){
var speechOutput = 'Your result is '+recordset;
response.ask(speechOutput);
});
However, I can't get the recordset. I got "Your result is {object, object}. "
that's because the IIFE is executing right away, try returning a function instead and then executing that function when you import that module,
var storage = (function(mssql, dbcon) {
return function() {
var connection = new mssql.Connection(dbcon);
var request = new mssql.Request(connection);
connection.connect(function(recordset) {
request.query('select getdate()', function(err, recordset) {
console.dir(recordset);
});
connection.close();
});
}
})(mssql, dbcon);
and I don't understand why you need the IIFE, why don't you just assign the function to the variable?
If you're trying to assign the variable "recordset" to "storage" then this will never work as "connection.connect" is an asynchronous function, and in that case you should think about callback functions or promises.
Update
Based on your request, here's an implementation with a callback function and how it's used
var mssql = require('mssql');
var dbcon = require('./dbcon');
var storage = function(callback) {
var connection = new mssql.Connection(dbcon);
var request = new mssql.Request(connection);
connection.connect(function(recordset) {
request.query('select getdate()', function(err, recordset) {
if(!err && callback){
callback(recordset);
}
connection.close();
});
});
}
module.exports = storage;
// --------------------------------------------------
// implementation in another module
var storage = require("module_path"); // (1)
var answer;
storage(function(recordset){ // (2)
answer = recordset;
console.log(answer); // actual data, (3)
// implement your logic here
});
console.log(answer); // undefined (4)
// --------------------------------------------------
How this code works:
- You start by calling the storage method and sending it a callback method.
- The whole point of the callback function is that you won't wait for the result, your code will actually continue working at the same time that the storage method is connecting to the database and trying to get the data, ans since db operations are much slower, line(4) will execute before line(3).
- The flow of work will be as follows:
line (1)
line (2)
line (4)
line (3) at sometime in the future when the data is retrieved from database
- To see this more clearly, try doing this at the last line,
setTimeout(function(){console.log(answer);}, 3000);
This will wait for sometime until the data comes back;
Im working with an node handler in AWS lambda and i need to make another files with integration tests from that function, but i cant mock the transporter with sinon or mockery.
the index.js function:
var nodemailer = require('nodemailer');
exports.handler = (event, context, callback) =>
{
var transporter=createTransporter();
transporter.sendMail(data, function (error, success) {
console.log(error);
response = getResponse(404, error);
}
callback(null, response);
});
}
function createTransporter() {
return nodemailer.createTransport({
service: "SMTP",
auth: {
user: "XXXX#XXX",
pass: "XXXX"
}
});
}
the purpose is to mock the function createTransporter() so that it doesnt send any email when it is called in javascript file test with mocha and expect:
var mockery = require('mockery');
var nodemailerMock = require('nodemailer-mock');
var index = require("../index.js");
describe("The handler function tests", function () {
before(function () {
mockery.enable({
warnOnUnregistered: false
});
mockery.registerMock('nodemailer', nodemailerMock);
});
it('JSON error html ', function () {
var callback = function (name, response) {
expect(JSON.stringify(response.statusCode)).to.be('404');
};
var context = {};
index.handler(event, context, callback);
});
});
I wrote nodemailer-mock :)
The problem you're having is that you are calling var index = require("../index.js"); before you are mocking nodemailer via mockery, so it is already in the module cache. I included // Make sure anything that uses nodemailer is loaded here, after it is mocked... in the examples in the README, but should probably make it more clear.
Move the require("../index.js") after nodemailer is mocked and it will be work as expected.
var mockery = require('mockery');
var nodemailerMock = require('nodemailer-mock');
// don't require here since you will get the real nodemailer and cache it
var index;
describe("The handler function tests", function () {
before(function () {
mockery.enable({
warnOnUnregistered: false
});
mockery.registerMock('nodemailer', nodemailerMock);
// do the require() here after nodemailer is mocked
index = require("../index.js");
});
// your tests here should now use nodemailer-mock
it('JSON error html ', function () {
var callback = function (name, response) {
expect(JSON.stringify(response.statusCode)).to.be('404');
};
var context = {};
index.handler(event, context, callback);
});
});
Another option is to use the { useCleanCache: true } option with calls to mockery.resetCache();, though I have had mixed results. See Controlling the Module Cache in the mockery documentation.
I'm not 100% sure why this would fail, but I suggest one of two things:
Try doing var createTransporter = function()... there's a slight difference here that might be your issue
exporting createTransporter so you can assign a new value to it, either a mock or not. This isn't very "keep implementation details private", it does work
Have your module return a class, or object anyway, where you can set some "use this transporter method" value. (ie dependency injection)
You can use the following option from Jest:
jest.mock('nodemailer').setMock(/* function mock for module */)
Remember to use this at the top of the file, before import or require statements.
Here is the official Jest documentation: https://jestjs.io/docs/manual-mocks#mocking-node-modules.
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 */
})
I've had no trouble testing my own route handlers but in this case I want to test express's static handler. I can't for the life of me figure out why it's hanging. Clearly there's some callback I'm missing or some event I need to emit.
I tried to make the smallest example I could.
var events = require('events');
var express = require('express');
var stream = require('stream');
var util = require('util');
function MockResponse(callback) {
stream.Writable.call(this);
this.headers = {};
this.statusCode = -1;
this.body = undefined;
this.setHeader = function(key, value) {
this.headers[key] = value;
}.bind(this);
this.on('finish', function() {
console.log("finished response");
callback();
});
};
util.inherits(MockResponse, stream.Writable);
MockResponse.prototype._write = function(chunk, encoding, done) {
if (this.body === undefined) {
this.body = "";
}
this.body += chunk.toString(encoding !== 'buffer' ? encoding : undefined);
done();
};
function createRequest(req) {
var emitter = new events.EventEmitter();
req.on = emitter.on.bind(emitter);
req.once = emitter.once.bind(emitter);
req.addListener = emitter.addListener.bind(emitter);
req.emit = emitter.emit.bind(emitter);
return req;
};
describe('test', function() {
var app;
before(function() {
app = express();
app.use(express.static(__dirname));
});
it('gets test.js', function(done) {
var req = createRequest({
url: "http://foo.com/test.js",
method: 'GET',
headers: {
},
});
var res = new MockResponse(responseDone);
app(req, res);
function responseDone() {
console.log("done");
done();
}
});
});
Setup,
mkdir foo
cd foo
mkdir test
cat > test/test.js # copy and paste code above
^D
npm install express
npm install mocha
node node_modules/mocha/bin/mocha --recursive
it just times out.
What am I missing?
I also tried making the request a Readable stream. No change
var events = require('events');
var express = require('express');
var stream = require('stream');
var util = require('util');
function MockResponse(callback) {
stream.Writable.call(this);
this.headers = {};
this.statusCode = -1;
this.body = undefined;
this.setHeader = function(key, value) {
this.headers[key] = value;
}.bind(this);
this.on('finish', function() {
console.log("finished response");
callback();
});
};
util.inherits(MockResponse, stream.Writable);
MockResponse.prototype._write = function(chunk, encoding, done) {
if (this.body === undefined) {
this.body = "";
}
this.body += chunk.toString(encoding !== 'buffer' ? encoding : undefined);
done();
};
function MockMessage(req) {
stream.Readable.call(this);
var self = this;
Object.keys(req).forEach(function(key) {
self[key] = req[key];
});
}
util.inherits(MockMessage, stream.Readable);
MockMessage.prototype._read = function() {
this.push(null);
};
describe('test', function() {
var app;
before(function() {
app = express();
app.use(express.static(__dirname));
});
it('gets test.js', function(done) {
var req = new MockMessage({
url: "http://foo.com/test.js",
method: 'GET',
headers: {
},
});
var res = new MockResponse(responseDone);
app(req, res);
function responseDone() {
console.log("done");
done();
}
});
});
I've still been digging. Look inside static-server I see it creates a Readable stream by calling fs.createReadStream. It does effectively
var s = fs.createReadStream(filename);
s.pipe(res);
So trying that myself works just fine
it('test stream', function(done) {
var s = fs.createReadStream(__dirname + "/test.js");
var res = new MockResponse(responseDone);
s.pipe(res);
function responseDone() {
console.log("done");
done();
}
});
I thought maybe it's something about express waiting for the input stream to finish but that doesn't seem to be it either. If I consume the mock input stream with the response it works just fine
it('test msg->res', function(done) {
var req = new MockMessage({});
var res = new MockResponse(responseDone);
req.pipe(res);
function responseDone() {
console.log("done");
done();
}
});
Any insight what I might be missing would be helpful
Note: while suggestions for 3rd party mocking libraries are appreciated I'm still really looking to understand what I'm missing to do it myself. Even if I eventually switch to some library I still want to know why this isn't working.
I found two issues that prevent the finish callback from being executed.
serve-static uses send module which is used to create file readstream from the path and pipe it to res object. But that module uses on-finished module which checks if finished attribute is set to false in response object, otherwise it destroys the file readstream. So filestream never gets a chance to emit data event.
express initialization overwrites the response object prototype. So the default stream methods like end() method is overwritten by http response prototype:
exports.init = function(app){
return function expressInit(req, res, next){
...
res.__proto__ = app.response;
..
};
};
To prevent this, I added another middleware right before static middleware to reset it back to MockResponse prototype:
app.use(function(req, res, next){
res.__proto__ = MockResponse.prototype; //change it back to MockResponse prototype
next();
});
Here are the changes made to make it work with MockResponse:
...
function MockResponse(callback) {
...
this.finished = false; // so `on-finished` module doesn't emit finish event prematurely
//required because of 'send' module
this.getHeader = function(key) {
return this.headers[key];
}.bind(this);
...
};
...
describe('test', function() {
var app;
before(function() {
app = express();
//another middleware to reset the res object
app.use(function(req, res, next){
res.__proto__ = MockResponse.prototype;
next();
});
app.use(express.static(__dirname));
});
...
});
EDIT:
As #gman pointed out, it is possible to use direct property instead of prototype method. In that case the extra middleware to overwrite prototype isn't necessary:
function MockResponse(callback) {
...
this.finished = false; // so `on-finished` module doesn't emit finish event prematurely
//required because of 'send' module
this.getHeader = function(key) {
return this.headers[key];
}.bind(this);
...
//using direct property for _write, write, end - since all these are changed when prototype is changed
this._write = function(chunk, encoding, done) {
if (this.body === undefined) {
this.body = "";
}
this.body += chunk.toString(encoding !== 'buffer' ? encoding : undefined);
done();
};
this.write = stream.Writable.prototype.write;
this.end = stream.Writable.prototype.end;
};
It appears my answer is not complete. For some reason the app works only if the file is not found. First thing to debug is do the following in your shell (or cmd):
export DEBUG=express:router,send
then run the test, you'll get more info.
Meanwhile I am still looking into this, for now, ignore my answer below.
----------- ignore this till I verify that it does work -----------
It seems like express static does not favor the absolute path you give it (__dirname).
Try:
app.use(express.static('.'));
and it will work. Note that your current dir for the mocha runner is 'test/'
I have to admit this is quite a mistery. I tried 'fulling' it by doing:
app.use(express.static(__dirname + '/../test')
but still it didn't work. Even specifying a full path did not solve this. Strange.
I'd like to create a model to handle everything related to users, starting with a findOne() function.
app.js:
var u = new User(client);
u.findOne(function(error, user) {
console.log(error, user);
});
models/User.js:
var User = function (client) {
this.client = client
};
User.prototype.findOne = function (id, callback) {
client.connect();
client.get('testkey', function(error, result) {
var test = "hello#world.com";
callback(null, test);
client.close();
});
};
module.exports = User;
node.js complains findOne() would be undefined.
What's the correct way of creating such models and providing them with objects, like database pools etc.?
Your code contains various errors:
You do not use new when creating the instance
You mixed a function with the object literal syntax:
var User = function (client) {
client: client
};
You want this.client = client; instead. Right now the function body does nothing as it just defines a label called client does nothing with the variable client.
I would suggest you to search for an existing ORM for node.js instead of trying to write one on your own.