Sequelize v4 | Instance Methods not working - node.js

I've been trying to update my code to accommodate the newest upgrades to Sequelize. I'm using
Sequelize: 4.2.0
Node: 7.10.0
NPM: 5.0.3
The Problem
I can't seem to set the User model properly. I've implemented some instance methods that don't seem to be working. The class must not be instantiated properly.
user.js
module.exports = (sequelize, DataTypes) => {
var User = sequelize.define('user', {
attributes ....
}, {
hooks: {
afterCreate(user, options) {
user.testFunction();
}
}
});
// Instance methods
User.prototype.testFunction = () => {
this.firstName = "John";
}
// Class methods
User.anotherTestFunction = () => {
User.findOne().then(() => doSomething());
}
return User;
}
index.js
var sequelize;
sequelize = new Sequelize(config.DATABASE_URL);
db.User = sequelize.import(__dirname + '/user.js');
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
usersController.js
var db = require('../path/to/db');
function create_post_function = (req, res) => {
var body = getBody();
db.User.create(body).then(user => respondSuccess());
}
Now, everything in this example works perfectly EXCEPT the instance method!!!
I'm continually getting TypeError: Cannot set property 'firstName' of undefined
For some reason, it's not applying the instance method to the sequelize Model. Very strange, but I'm probably doing something noticeably wrong and not seeing it.
Really appreciate any help!

You can't use arrow functions since they can't access this in the appropriate context (it will reference the parent scope). Try writing them like this -
// Instance methods
User.prototype.testFunction = function testFunction() {
this.firstName = "John";
}
// Class methods
User.anotherTestFunction = function anotherTestFunction() {
User.findOne().then(() => doSomething());
}

Related

Dependecy Injection using Class into Express

I'm using Express into a TypeScript project and I have the following situation
This is my route file
...
import findAllUsersFactory from "src/factory/FindAllUsers";
routes.get("/users", findAllUsersFactory().handle);
...
This is the factory where I do a sequence of injections
const findAllUsersFactory = () => {
const findAllUserRepository = new PrismaUsersRepository();
const findAllUsersBusiness = new FindAllUsersBusiness(findAllUserRepository);
const findAllUsersController = new FindAllUsersController(findAllUsersBusiness);
return findAllUsersController;
};
This is my Controller
class FindAllUsersController {
constructor(private findUserBusiness: FindAllUsersBusiness) { }
async handle(request: Request, response: Response) {
const allUsers = await this.findUserBusiness.execute();
return response.status(200).send({ allUsers });
}
}
And finally my Business
class FindAllUsersBusiness {
constructor(private usersRepository: IUsersRepository) {}
async execute() {
return this.usersRepository.findAll();
}
}
The problem is that I'm getting an error "Cannot read property 'execute' of undefined" because the findUserBusiness into handle function is undefined. And what I can't understand is that if I change my route to
routes.get("/users", (request, response) => {
findAllUsersFactory().handle(request, response);
});
it works
I've tried to log the functions, but I can say why findUserBusiness is undefined since it came from the constructor, and since the handle functions came from an instance of FindAllUsersController it should have it "defined"
You need to make some adjustments in order to adapt your factory to the way router.get expects its parameters.
const findAllUsersFactory = (req, res) => {
const findAllUserRepository = new PrismaUsersRepository();
const findAllUsersBusiness = new FindAllUsersBusiness(findAllUserRepository);
const findAllUsersController = new FindAllUsersController(findAllUsersBusiness);
return findAllUsersController.handle(req, res)
};
Then in your router you need to do the following:
routes.get("/users", findAllUsersFactory);

Sinon Stub depedent class in node.js module

I have one class as below
nx-user.js
class NXUser {
constructor() {}
view(guid, data) {
//do something
}
}
Then I have user controller module as below which has dependency of NxUser class
userController.js
const userDb = new NXUser();
import NXUser from "../../../persistence/nx-user";
const allUsers = () => {
return userDb.view()
}
export {allUsers }
I have below code written for stubbing view function of NxUser class for controller unit tests. But its not working. It always calling actual one instated of stubbed one
userController-test.js
let userdb=NXUser();
describe("user controller", function () {
let stubValue = [{
"name": "Urvashi Parmar",
"email": "urvashi.parmar#nationalexpress.com"]}
it("Should create user", () => {
sinon.stub(userdb, 'create').resolves(stubValue);
userController.allUsers ().then((body) => {
expect(body[0].name).to.equal(stubValue .name);
done();
});
})
}
Because I can not comment yet, so I need to give full answer.
Confusion: at your userController-test.js, you are trying to test NXUser.create, while at file nx-user.js has no definition of create.
Assume: you are trying to test NXUser.view.
This example is created based on your code, and is working fine. Console log "Called" will not get called.
Highlight:
Stub NXUser view directly, not userdb.create;
I use async-await inside test.
const sinon = require('sinon');
const { expect } = require('chai');
class NXUser {
constructor() {}
view(guid, data) {
console.log('Called');
return new Promise((resolve) => resolve([]));
}
// Add this only for dummy.
create() {
return new Promise((resolve) => resolve([]));
}
}
const userController = {
allUsers() {
const userDb = new NXUser();
return userDb.view();
}
}
describe('user controller', function () {
// Suppose you test view user.
it('should view user', async function () {
const stubValue = [{
name: 'Urvashi Parmar',
email: 'urvashi.parmar#nationalexpress.com'
}];
// Suppose you stub method view and not create.
const stubUserDBView = sinon.stub(NXUser.prototype, 'view');
stubUserDBView.resolves(stubValue);
const body = await userController.allUsers();
expect(body).to.be.an('array').that.have.lengthOf(1);
expect(body[0]).to.have.property('name', stubValue[0].name);
// Restore stub.
stubUserDBView.restore();
});
});
$ npx mocha stackoverflow.js
user controller
✓ should view user
1 passing (11ms)
$
Hope this helps.

Sequelize - Cannot read property 'list' of undefined

Im just learn to use sequelize for my node.js project. For summary my project is ExpressJS+Typescript with Sequelize as ORM and Webpack as module bundler.
Below is my project structure.
src
-router
-server
--config
config.json
--controllers
index.ts
User.ts
--migrations
--models
index.js
user.js
--seeders
App.ts
index.ts
(sorry can not post picture yet, new user to stackoverflow)
I have build some simple router '/user' and expect it should call the user controller and call sequelize method findAll() from my models module, but the result is its error says Cannot read property 'list' of undefined. Below is my code:
models/index.js
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(module.filename);
const env = process.env.NODE_ENV || 'development';
const config = require(`${__dirname}/../config/config.json`)[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable]);
} else {
sequelize = new Sequelize(
config.database, config.username, config.password, config
);
}
fs
.readdirSync(__dirname)
.filter(file =>
(file.indexOf('.') !== 0) &&
(file !== basename) &&
(file.slice(-3) === '.js'))
.forEach(file => {
const model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
export default db;
models/user.js
export default function(sequelize, DataTypes) {
var user = sequelize.define('user', {
username: DataTypes.STRING,
name: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING,
phone: DataTypes.STRING,
wallet: DataTypes.DECIMAL
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
user.hasMany(models.top_up);
}
}
});
return user;
};
controllers/User.ts
let user = require('../models').user;
export default {
list(req, res) {
return user
.findAll()
.then(topUp => res.status(200).send(topUp))
.catch(error => res.status(400).send(error));
}
};
controllers/Index.ts
import users from './User'
export default {
users
}
router/router.ts
import * as express from 'express';
const userController = require('../server/controllers').users;
// Init express router
let router = express.Router();
// Setting API URL
router.get('/', (req, res, next) => {
res.json({
message: 'Hello World!'
});
});
router.get('/about',(req, res, next) => {
res.send('<p>This is about about</p>');
});
router.get('/user', userController.list());
export default router
Fyi, all of my project configuration for start express server, typescript compile and webpack bundle is fine already, and the other route for '/' and '/about' is work fine. I know there is something I'm missing, im still new to sequelize, thanks for help.
TL;DR: The server/controllers/index.ts does not export a binding named users, itexports a binding named default.
You are importing the controllers/Index.ts module using the require function. In a CommonJS environment, this imports the entire module.exports object. As currently transpiled by TypeScript, every named export of the required module is exposed as a property of the import.
An export default clause implies an export named default. As per the ES Module specification, there is a shorthand for importing the default export of a module. That shorthand is
import userController from '../server/controllers';
On the other hand, the syntax
import userController = require('../server/controllers');
or
const userController = require('../server/controllers'); // (or let or var)
imports an object with a property corresponding to each export. In this case it has the shape
{ default }
So if you use require, you need to write
import userController = require('../server/controllers').default;
or
import userController from '../server/controllers';
All ES Module style exports are named, including the default which is named default.
To illustrate this, consider the following, more verbose but semantically identical form
import {default as userController} from '../server/controllers';
If you would prefer to stick with CommonJS style exports, eschewing ES Modules when working in NodeJS, the idiomatic way to export a single object as the entire module (the object returned by require)
You may write
// ../server/controllers/index.ts
export = {
list(req, res) {
return user
.findAll()
.then(topUp => res.status(200).send(topUp))
.catch(error => res.status(400).send(error));
}
};
Personally, I would stick with what you have and write
import userController from '../server/controllers';

Pass 'this' to a class variable inside constructor

I am interested, how to pass this to a class variable inside a constructor of a parent, so I could use parents methods and access other variables of the parent and call their methods?
Here is my parent class:
var async = require('async');
var Rater = require('./rater')
var Similars = require('./similars')
var Suggestions = require('./suggestions');
module.exports = class Engine {
constructor() {
this.likes = new Rater(this,'likes');
this.dislikes = new Rater(this,'dislikes');
this.similars = new Similars(this);
this.suggestions = new Suggestions(this);
}
And here is the example of usage and where is get the following error:
Cannot read property 'engine' of undefined
at --\\classes\rater.js:89:19
module.exports = class Rater {
constructor(engine,kind) {
this.type = kind;
this.engine = engine;
if(kind == 'likes') //database schemes
this.db = Likes_db;
else if(kind == 'dislikes')
this.db = Dislikes_db;
else if(kind == 'similars')
this.db = Similars_db;
else if(kind == 'suggestions')
this.db = Suggestions_db;
}
//..
//other methods
//..
remove(user,item,done) {
this.db.remove({user: user,item: item},(err) => {
if(err)
return done(err);
async.series([
function(done) {
this.engine.similars.update(user,done); //error-cant enter the method
},
function(done) {
this.engine.suggestions.update(user,done);
}
],function(done) {
});
});
}
}
It has nothing to do with the constructor.
The problem appears because you are using a regular function as the callback and the context switches (you get another this in there).
Use an arrow function instead to keep the same context.
async.series([
(done) => {
this.engine.similars.update(user,done); //error-cant enter the method
},
(done) => {
this.engine.suggestions.update(user,done);
}
],function(done) {
});
Simply doing this works fine:
class Rather {
constructor(engine: Engine) {
engine.method();
}
}
class Engine {
constructor() {
new Rather(this);
}
method() {
console.log('ENgine');
}
}
new Engine();
You can see a working example here.
Note: As an OOP design decision though this is not very clean, you are introducing a cyclic dependency. Try going injection or at least introduce an interface to separate the 2 classes.
Try to define a _this var and then give it to parameter:
module.exports = class Engine {
var _this = this, _constructor = (<any>this).constructor;
constructor() {
this.likes = new Rater(_this,'likes');
this.dislikes = new Rater(_this,'dislikes');
this.similars = new Similars(_this);
this.suggestions = new Suggestions(_this);
}

Bookshelf circular dependency with ES5

I have the following code:
const bookshelf = require('../config/bookshelf');
const BaseModel = require('bookshelf-modelbase')(bookshelf);
const moment = require("moment");
const User = require("./User");
const Meta = require("./Meta");
const Log = require("./Log");
class Session extends BaseModel {
get tableName() {
return "sessions";
}
get hasTimestamps() {
return false;
}
user() {
return this.belongsTo(User);
}
meta() {
return this.belongsTo(Meta);
}
logs() {
return this.hasMany(Log);
}
};
module.exports = Session;
and
const bookshelf = require('../config/bookshelf');
const BaseModel = require('bookshelf-modelbase')(bookshelf);
const Session = require("./Session");
const moment = require("moment");
class Log extends BaseModel {
get tableName() {
return "logs";
}
get hasTimestamps() {
return false;
}
session() {
return this.belongsTo(Session);
}
getDate() {
return moment(this.get("date")).format("MMM DD, YYYY - HH:mm:ss")
}
};
module.exports = Log;
belongsTo relation works properly, but when I try hasMany, I get: "Unhandled rejection Error: A valid target model must be defined for the sessions hasMany relation" error.
I had a look at https://github.com/tgriesser/bookshelf/wiki/Plugin:-Model-Registry but it is being done using pre-ES5 syntax.
I guess I need to make sure "Log" class is available before I appoint into a hasMany relationship but stuck here.
Any ideas?
Edit: Doing
logs() {
var log = require("./Log");
return this.hasMany(log);
}
works but it looks bad.
You may use Bookshelf registry. It exactly fits your needs.
You can load Bookshelf registry plugin like this :
const knex = require('knex')({
client: 'pg',
connection: {
host: config.db_host,
user: config.db_user, // user name for your database
password: config.db_password, // user password
database: config.db_database, // database name
charset: 'utf8',
},
});
const bookshelf = require('bookshelf')(knex);
bookshelf.plugin('registry');
module.exports = {
knex,
bookshelf,
};
Then, register your models in module.exports like this :
module.exports = {
session: db.bookshelf.model('Session', Session),
log: db.bookshelf.model('Log', Log),
db,
};

Resources