Simple node.js module with init method - javascript

I'm trying to sort out a good simple pattern for node.js with an init method for the use of 'models' when connecting to mongodb collections. Basically, each collection has a 'model'.
Here's what I have, setup as a singleton, any reason not go to with this method or is there more recommended method within the context of node?
module.exports = User;
function User () {
if ( arguments.callee.instance ) {
return arguments.callee.instance;
} else {
arguments.callee.instance = this;
}
//Init our database collection
}
User.findOne = function (user_id) {
}
User.addUser = function () {
}
return new User();
Thank you!

Um, you should put User.prototype.method.
User.prototype.findOne = function (user_id) {
}
User.prototype.addUser = function () {
}
Instead of 'return new User()' you probably meant,
module.exports = new User();
One reason not to export a 'new' User() is if you want to create more than one user in your nodejs program. If you only have one user, then a singleton is fine. Also, if you want to pass data to this module, you may want to do so via a constructor argument, and if you only construct the User in the module, then you can't pass to the constructor in your app.
I use a pattern that assigns module.exports to a function. The first argument of all my exports is an object shared amongst all exports, so they can add events/listeners and talk to each other in various different ways.
For example ('events' and 'app' are psuedo, not based on any real API),
function doLogin(app) {
return function (e) {
// some code to do login
}
}
module.exports = function login (app) {
app.events.on('login', doLogin(app));
}
Now all of the other modules can trigger a login via
app.events.login({'user': '..','pass': '..'});

Related

What is the difference between these two singleton approaches in JS?

The way to solve the singleton pattern in JS (TS actually) is looks like this (the best approach if you ask me):
export default class Singleton {
private static _instance: Selection
constructor() {
if (Selection._instance) {
return Singleton._instance
} else {
Singleton._instance = this
}
}
}
And then:
import Singleton from './Singleton.ts'
const singleton_1 = new Singleton()
const singleton_2 = new Singleton()
singleton_1 === singleton_2 // true
But in this scenario I have to create new variables every time I need that class.
I can achieve exactly the same the easier way:
class Singleton {
constructor() {
// some logic
}
}
export default new Singleton()
import Singleton from './Singleton.ts'
const wut = Singleton.field
Singleton.method('do something')
Am I getting something wrong or the first approach is a little bit excessive and complicated and the second one just do the same thing in more obvious way?
I understand that if I have static fields in my class, I couldn't use it that way, but cases when you really need static fields are rare.
It is essential in a scenario where only one instance needs to be created, for example, a database connection. It is only possible to create an instance when the connection is closed or you make sure to close the open instance before opening a new one. This pattern is also referred to as strict pattern, one drawback associated with this pattern is its daunting experience in testing because of its hidden dependencies objects which are not easily singled out for testing.
let databaseInstance = null;
// tracks the number of instances created at a certain time
let count = 0;
function init() {
console.log(`Opening database #${count + 1}`);
//now perform operation
}
function createIntance() {
if(databaseInstance == null) {
databaseInstance = init();
}
return databaseInstance;
}
function closeIntance() {
console.log('closing database');
databaseInstance = null;
}
return {
open: createIntance,
close: closeIntance
}
}
const database = DatabseConnection();
database.open(); //Open database #1
database.open(); //Open database #1
database.open(); //Open database #1
database.close(); //close database
source: https://codesource.io/javascript-design-patterns/

ES6 class context in NodeJs? Or using 'this' inside a class in NodeJs?'

I am new to node js and i am actually not able to understand how to call methods inside a class except statically or by creating a new object each time i'm iterating over functions.
This is kind of crazy. For example:
class UserController
{
methodOne (req, res) {
this.methodTwo (req);
}
methodTwo (data) {
return data;
}
}
That's how i want to call my function but, every time i do this, I end up with error of this is undefined.
I know fat arrow functions don't follow the same context like in javascript. But i just wanna make sure if i'm doing it right or not.
And that's how i achieve above code.
class UserController
{
static methodOne (req, res) {
UserController.methodTwo (req);
}
static methodTwo (data) {
// Manipulates request before calling
return UserController.methodThree (data);
}
static methodThree (data) {
return data;
}
}
i.e. calling each method statically with class name UserController.
So if there's any better way to do it, I need you to suggest.
Thanks in advance.
P.S: Above code is just an example guys.
The reason for your problem is that you lost the function context
class UserController {
async methodOne() {
return this.methodTwo()
}
async methodTwo() {
return Promise.resolve("methodtwo")
}
}
const obj = new UserController();
const methodOne = obj.methodOne;
methodOne(); // ----> This will throw the Error
methodOne.call(obj); // ----> This Works
// Or you can call the method directly from the obj
obj.methodOne(); // Works!!
// If you want to cache the method in a variable and preserve its context use `bind()`
const methodOneBound = obj.methodOne.bind(obj);
methodOneBound(); // ----> This works

Mock Es6 classes using Jest

I'm trying to mock an ES6 class with a constructor that receives parameters, and then mock different class functions on the class to continue with testing, using Jest.
Problem is I can't find any documents on how to approach this problem. I've already seen this post, but it doesn't resolve my problem, because the OP in fact didn't even need to mock the class! The other answer in that post also doesn't elaborate at all, doesn't point to any documentation online and will not lead to reproduceable knowledge, since it's just a block of code.
So say I have the following class:
//socket.js;
module.exports = class Socket extends EventEmitter {
constructor(id, password) {
super();
this.id = id;
this.password = password;
this.state = constants.socket.INITIALIZING;
}
connect() {
// Well this connects and so on...
}
};
//__tests__/socket.js
jest.mock('./../socket');
const Socket = require('./../socket');
const socket = new Socket(1, 'password');
expect(Socket).toHaveBeenCalledTimes(1);
socket.connect()
expect(Socket.mock.calls[0][1]).toBe(1);
expect(Socket.mock.calls[0][2]).toBe('password');
As obvious, the way I'm trying to mock Socket and the class function connect on it is wrong, but I can't find the right way to do so.
Please explain, in your answer, the logical steps you make to mock this and why each of them is necessary + provide external links to Jest official docs if possible!
Thanks for the help!
Update:
All this info and more has now been added to the Jest docs in a new guide, "ES6 Class Mocks."
Full disclosure: I wrote it. :-)
The key to mocking ES6 classes is knowing that an ES6 class is a function. Therefore, the mock must also be a function.
Call jest.mock('./mocked-class.js');, and also import './mocked-class.js'.
For any class methods you want to track calls to, create a variable that points to a mock function, like this: const mockedMethod = jest.fn();. Use those in the next step.
Call MockedClass.mockImplementation(). Pass in an arrow function that returns an object containing any mocked methods, each set to its own mock function (created in step 2).
The same thing can be done using manual mocks (__mocks__ folder) to mock ES6 classes. In this case, the exported mock is created by calling jest.fn().mockImplementation(), with the same argument described in (3) above. This creates a mock function. In this case, you'll also need to export any mocked methods you want to spy on.
The same thing can be done by calling jest.mock('mocked-class.js', factoryFunction), where factoryFunction is again the same argument passed in 3 and 4 above.
An example is worth a thousand words, so here's the code.
Also, there's a repo demonstrating all of this, here:
https://github.com/jonathan-stone/jest-es6-classes-demo/tree/mocks-working
First, for your code
if you were to add the following setup code, your tests should pass:
const connectMock = jest.fn(); // Lets you check if `connect()` was called, if you want
Socket.mockImplementation(() => {
return {
connect: connectMock
};
});
(Note, in your code: Socket.mock.calls[0][1] should be [0][0], and [0][2] should be [0][1]. )
Next, a contrived example
with some explanation inline.
mocked-class.js. Note, this code is never called during the test.
export default class MockedClass {
constructor() {
console.log('Constructed');
}
mockedMethod() {
console.log('Called mockedMethod');
}
}
mocked-class-consumer.js. This class creates an object using the mocked class. We want it to create a mocked version instead of the real thing.
import MockedClass from './mocked-class';
export default class MockedClassConsumer {
constructor() {
this.mockedClassInstance = new MockedClass('yo');
this.mockedClassInstance.mockedMethod('bro');
}
}
mocked-class-consumer.test.js - the test:
import MockedClassConsumer from './mocked-class-consumer';
import MockedClass from './mocked-class';
jest.mock('./mocked-class'); // Mocks the function that creates the class; replaces it with a function that returns undefined.
// console.log(MockedClass()); // logs 'undefined'
let mockedClassConsumer;
const mockedMethodImpl = jest.fn();
beforeAll(() => {
MockedClass.mockImplementation(() => {
// Replace the class-creation method with this mock version.
return {
mockedMethod: mockedMethodImpl // Populate the method with a reference to a mock created with jest.fn().
};
});
});
beforeEach(() => {
MockedClass.mockClear();
mockedMethodImpl.mockClear();
});
it('The MockedClassConsumer instance can be created', () => {
const mockedClassConsumer = new MockedClassConsumer();
// console.log(MockedClass()); // logs a jest-created object with a mockedMethod: property, because the mockImplementation has been set now.
expect(mockedClassConsumer).toBeTruthy();
});
it('We can check if the consumer called the class constructor', () => {
expect(MockedClass).not.toHaveBeenCalled(); // Ensure our mockClear() is clearing out previous calls to the constructor
const mockedClassConsumer = new MockedClassConsumer();
expect(MockedClass).toHaveBeenCalled(); // Constructor has been called
expect(MockedClass.mock.calls[0][0]).toEqual('yo'); // ... with the string 'yo'
});
it('We can check if the consumer called a method on the class instance', () => {
const mockedClassConsumer = new MockedClassConsumer();
expect(mockedMethodImpl).toHaveBeenCalledWith('bro');
// Checking for method call using the stored reference to the mock function
// It would be nice if there were a way to do this directly from MockedClass.mock
});
For me this kind of Replacing Real Class with mocked one worked.
// Content of real.test.ts
jest.mock("../RealClass", () => {
const mockedModule = jest.requireActual(
"../test/__mocks__/RealClass"
);
return {
...mockedModule,
};
});
var codeTest = require("../real");
it("test-real", async () => {
let result = await codeTest.handler();
expect(result).toMatch(/mocked.thing/);
});
// Content of real.ts
import {RealClass} from "../RealClass";
export const handler = {
let rc = new RealClass({doing:'something'});
return rc.realMethod("myWord");
}
// Content of ../RealClass.ts
export class RealClass {
constructor(something: string) {}
async realMethod(input:string) {
return "The.real.deal "+input;
}
// Content of ../test/__mocks__/RealClass.ts
export class RealClass {
constructor(something: string) {}
async realMethod(input:string) {
return "mocked.thing "+input;
}
Sorry if I misspelled something, but I'm writing it on the fly.

Modifying some features in a public npm package

I am trying to overload/replace functions in the ami-io npm package. That package is created to talk to asterisk AMI, a socket interface.
I need to talk to a service that has almost the exact same interface, but it presents a different greeting string upon login, and it requires an extra field in the logon. All the rest is the same. Instead of just plain copying the 600 LOC ami-io package, and modifying two or three lines, I want to override the function that detects the greeting string, and the login function, and keep using the ami-io package.
Inside the ami-io package there is a file index.js which contains the following function:
Client.prototype.auth = function (data) {
this.logger.debug('First message:', data);
if (data.match(/Asterisk Call Manager/)) {
this._setVersion(data);
this.socket.on('data', function (data) {
this.splitMessages(data);
}.bind(this));
this.send(new Action.Login(this.config.login, this.config.password), function (error, response) {
if (response && response.response === 'Success') this.emit('connected');
else this.emit('incorrectLogin');
}.bind(this));
} else {
this.emit('incorrectServer', data);
}
};
Now I want to match not on Asterisk Call Manager, but on MyService, and I want to define and use Action.LoginExt(this.config.login, this.config.password) with another one with an extra parameter.
Is this possible? I tried this in my own module:
var AmiIo = require('ami-io');
var amiio = AmiIo.createClient({port:5038, host:'x.x.x.x', login:'system', password:'admin'});
amiio.prototype.auth = function (data) {
this.logger.debug('First message:', data);
if (data.match(/MyService Version/)) {
this._setVersion(data);
this.socket.on('data', function (data) {
this.splitMessages(data);
}.bind(this));
this.send(new Action.LoginExt(this.config.login, this.config.password, this.config.extra), function (error, response) {
if (response && response.response === 'Success') this.emit('connected');
else this.emit('incorrectLogin');
}.bind(this));
} else {
this.emit('incorrectServer', data);
}
};
...but it resulted in TypeError: Cannot set property 'auth' of undefined, and now I am clueless.
Also, can I define a new Action.LoginExt object in my own module? How?
The action.js module defines the Action objects as follows:
function Action(name) {
Action.super_.bind(this)();
this.id = this.getId();
this.set('ActionID', this.id);
this.set('Action', name);
}
(function(){
var Message = require('./message.js');
var util = require('util');
util.inherits(Action, Message);
})();
Action.prototype.getId = (function() {
var id = 0;
return function() {
return ++id;
}
})();
function Login(username, secret) {
Login.super_.bind(this, 'Login')();
this.set('Username', username);
this.set('Secret', secret );
}
... more functions ...
(function() {
var actions = [
Login,
... more functions ...
];
var util = require('util');
for (var i = 0; i < actions.length; i++) {
util.inherits(actions[i], Action);
exports[actions[i].name] = actions[i];
}
exports.Action = Action;
})();
What I think I understand is that Action is subclassed from Message. The Login function in its turn is subclassed from Action, and exported (in the last code block).
So I think in my code I could try something similar:
// extend ami-io with LoginExt function
function LoginExt(username, secret, company) {
Login.super_.bind(this, 'LoginExt')();
this.set('Username', username);
this.set('Secret', secret );
this.set('Company', company);
}
var util = require('util');
util.inherits(LoginExt, amiio.Action);
But util.inherits fails with undefined. I've also opened a issue on ami-io.
You can use:
var AmiIo = require('ami-io');
AmiIo.Action.Login = function NewConstructor(){}; //to override Login action
//new constructor shold extend AmiIo.Action.Action(actionName)
//and also, you can use
AmiIo.Action.SomeNewAction = function SomeNewAction(){};//to create new actuion
//it also should extend AmiIo.Action.Action(actionName);
AmiIo.Action is just an Object. All constructors are fields of it.
To create new events you don't need to do anything, because it is just an object. If server send to you
Event: Armageddon
SomeField: 123
ami-io will create event with name 'Armageddon'.
To override Client#auth() method, you just should do
var AmiIo = require('ami-io');
AmiIo.Client.prototype.auth = function (){};//new function
amiio is an instance of a Client. The prototype property is only meaningful on constructor functions, such as Client. It is not meaningful on the result of a constructor function (except in the uncommon case that the instance happens to also be a function itself -- but even in that case, altering the instance's prototype does not influence its parent constructor).
Instead, you need to get the instance's prototype with Object.getPrototypeOf:
Object.getPrototypeOf(amiio).auth = function() { ... }
If you don't need to change this for every client, but only a single client, you don't need to change the prototype at all. Changing the instance's auth is sufficient:
amiio.auth = function() { ... }
Note that you code will not work if Action.LoginExt is local to the module scope. If the module exports it, you can probably do AmiIo.Action.LoginExt instead. If it does not export LoginExt, you will need to copy the code that implements it a re-implement it in your importing scope. It may be simpler to modify the module itself.
Here's the solution I applied that worked:
// Override the AmiIo auth procedure, because the other login is slightly different
// Write our own Login function (which adds a company)
function Login(username, secret, company) {
Login.super_.bind(this, 'Login')();
this.set('Username', username);
this.set('Secret', secret );
this.set('Company', company);
}
// This function should inherit from Action
var util = require('util');
util.inherits(Login, AmiIo.Action.Action);
AmiIo.Action.Login = Login;
// replace the auth with our own, to add the company. Also
// this sends a slightly different greeting: "Service Version 1.0"
AmiIo.Client.prototype.auth = function (data) {
if (data.match(/Service Version/)) {
this._setVersion(data);
this.socket.on('data', function (data) {
this.splitMessages(data);
}.bind(this));
this.send(new AmiIo.Action.Login(this.config.login, this.config.password, this.config.company), function (error, response) {
if (response && response.response === 'Success') this.emit('connected');
else this.emit('incorrectLogin');
}.bind(this));
} else {
this.emit('incorrectServer', data);
}
};
// our own function to grab the version number from the new greeting
AmiIo.Client.prototype._setVersion = function(version){
var v = version.match(/Service Version ([\d\.]*[\-\w\d\.]*)/i);
if (v){
this.version = v[1];
}
};
So it turns out this was as doable as I hoped it would be. Both answers by #NumminorihSF and #apsillers helped me here, but I could mark only one of them as the best answer.

Node js difference b/w directly calling a function (using module.export) and using prototype

First approach using module.export without creating object
File:AuthFilter.js
function callbackAuthService() {
....AUTH SERVICE CALLBACK CODE....
}
module.exports = {
checkAuthentication: function() {
.....HTTP REQ TO AUTH SERVICE......
}
File app.js(using checkAuthentication)
var authFilter = require('./filters/AuthFilter');
/* Before Filter */
app.all('*', authFilter.checkAuthentication);
Second approach using prototype by creating object
File:AuthFilter.js
var authFilter = function()
{
function callbackAuthService() {
....AUTH SERVICE CALLBACK CODE....
}
this.checkAuthentication=function() {
.....HTTP REQ TO AUTH SERVICE......
}
}
module.exports = authFilter;
File app.js(using checkAuthentication)
var authFilter = require('./filters/AuthFilter');
var authFilterObj=new(authFilter);
/* Before Filter */
app.all('*', authFilterObj.checkAuthentication);
Which way is better ?
as far as i know in node js functions are object so in first way we are exporting function object and calling it (it is not static function call).
Please correct if i am wrong (please consider design style and performance in mind).
Also while using lib like sha1,mongo,redis,sql,request i observed that i don't have to create the object and the use their functions(means they are using first approach)

Categories