Bookshelf circular dependency with ES5 - node.js

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,
};

Related

How to access parent query arguments in GraphQL and Nestjs execution context

Let's say we have a bookshop and an author entity, to show the author their earnings stat, we want to check if the authenticated user is indeed the author themselves. So we have:
#UseGuards(GqlAuthGuard)
#ResolveField(() => [Eearning], { name: 'earnings' })
async getEarnings(
#Parent() author: Author,
#GqlUser() user: User,
) {
if (user.id !== author.id)
throw new UnauthorizedException(
'Each author can only view their own data',
);
// rest of the function implementation
}
We could query this:
query {
author(id: "2bd79-6d7f-76a332b06b") {
earnings {
sells
}
}
}
Now imagine we want to use a custom Guard instead of that if statement. Something like below:
#Injectable()
export class AutherGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const ctx = GqlExecutionContext.create(context);
// const artistId = ?
}
}
How can I access the id argument given to the author query when AutherGuard is used for the getEarnings handler?
Not sure how documented is that but the parent object can be accessed through the getRoot method:
const gqlContext = GqlExecutionContext.create(context);
const root = gqlContext.getRoot();
const authorId = root.id;
In fact, we have a helper function that we use like this:
export function getArgs(context: ExecutionContext): any {
if (context.getType<GqlContextType>() === "graphql") {
const gqlContext = GqlExecutionContext.create(context);
return { ...gqlContext.getArgs(), $parent: gqlContext.getRoot() };
} else if (context.getType() === "http") {
return context.switchToHttp().getRequest().params;
}
}
...
const args = getArgs(context);
const authorId = _.get(args, "$parent.id");

TypeError: Class extends value undefined is not a constructor or null Custom environment NODE

I'm writing a custom environment for e2e testing with Prisma, and first I encountered an error with NODE's promisify that apparently can be resolved with opitional[?]. Now it's giving an error with NodeEnvironment. Can someone help me?
My Custom test environment
import type { Config } from '#jest/types';
import { exec } from 'node:child_process';
import * as dotenv from 'dotenv';
import NodeEnvironment from 'jest-environment-node';
import { Client } from 'pg';
import util from 'node:util';
import crypto from 'node:crypto';
dotenv.config({ path: __dirname + '/.env.test' });
const execSync = util?.promisify(exec);
const prismaBinary = './node_modules/.bin/prisma';
export default class PrismaTestEnvironment extends NodeEnvironment {
private schema: string;
private connectionString: string;
constructor(config: Config.ProjectConfig) {
super(config);
const dbUser = process.env.DATABASE_USER;
const dbPass = process.env.DATABASE_PASS;
const dbHost = process.env.DATABASE_HOST;
const dbPort = process.env.DATABASE_PORT;
const dbName = process.env.DATABASE_NAME;
this.schema = `test_${crypto.randomUUID()}`;
this.connectionString = `postgresql://${dbUser}:${dbPass}#${dbHost}:${dbPort}/${dbName}?schema=${this.schema}`;
}
async setup() {
process.env.DATABASE_URL = this.connectionString;
this.global.process.env.DATABASE_URL = this.connectionString;
await execSync(`${prismaBinary} migrate deploy`);
return super.setup();
}
async teardown() {
const client = new Client({
connectionString: this.connectionString,
});
await client.connect();
await client.query(`DROP SCHEMA IF EXISTS "${this.schema}" CASCADE`);
await client.end();
}
}
Error
enter image description here

How can I use two different databases in one single node app?

I have install Hbase client and PostgreSql client install but how to connect two databases in a single node application
import { error } from "console";
const hbase = require('hbase');
export class Db {
private conn = hbase();
private config = { host: '0.0.0.0', port: 8080 };
public client = new hbase.Client(this.config);
constructor() {
this.conn = new hbase.Client(this.config);
}
public conection() {
this.conn.table('messages').exists((error: string, succuss: string) => {
if (!succuss) {
this.createTable('messages', 'message_data');
}
});
}
private createTable(TblName: string, CF: string) {
this.conn.table(TblName).create(CF, function (error: string, success: string) {
console.log(success);
return success
});
}
}
I would suggest creating two different classes for Hbase and PostgreSql. ANd use them in your application whenever needed.
Another thing also use dependency injection in the constructor instead of defining configs in class. That way you can inject any DB configuration in an instance.
Here's code example
Create Class to manage HBaseDB connection
import { error } from "console";
const hbase = require('hbase');
export class HBaseDB {
//Inject this config object in your class constructor
//private config = { host: '0.0.0.0', port: 8080 };
//Here we have injected config object
constructor(config) {
this.conn = new hbase.Client(config);
}
public conection() {
this.conn.table('messages').exists((error: string, succuss: string) => {
if (!succuss) {
this.createTable('messages', 'message_data');
}
});
}
private createTable(TblName: string, CF: string) {
this.conn.table(TblName).create(CF, function (error: string, success: string) {
console.log(success);
return success
});
}
}
Create Class to manage PostgreSQL connection
const pg = require('pg');
export class PostgresqlDB {
constructor(config) {
//config contains database,username,password etc... configs
this.pool = new pg.Pool({ config })
this.conn = undefined
}
public async conection() {
//If connection is already connected, no need to connect again
//This will save time
if (this.conn === undefined)
this.conn = await this.pool.connect()
}
public async query(query) {
await this.conection()
const response = await this.conn.query(query)
return response
}
}
Now you can use them in code like
const pgDB = require('./PostgresqlDB')
const hbaseDB = require('./HBaseDB')
const hbaseDBConn = new hbaseDB({ host: '0.0.0.0', port: 8080 })
const pgDBConn = new pgDB({ database: 'test', user:'test',password:'test'})
Note: Above code is for understanding purposes only, You may need to add validations and correct some syntax for actual use

sequelize ORM asynchronous method calls

How can I call methods asynchronously in sequelize ORM? (because I have to use returned value inside other methods).
user.dao.js:
var User = require('./user.model');
class UserDao {
constructor() {}
insert(user) {
var pk;
User.sync({ force: false }).then(() => {
User.create(user).then(function(user) {
console.log('Entry successful from dao: ' +
JSON.stringify(user));
//return generated pk
pk = user.id;
console.log('ID: ' + pk);
});
});
return pk;
}
user.test.js:
class UserDaoTest {
constructor() {
this.userDao = new UserDao();
this.compare = new UtilsObject();
}
/*
all CRUD method calls
*/
testAll() {
this.testInsert();
this.testUpdate();
//this.testDelete();
//this.testRead();
//this.compare();
}
/*
insert method
*/
testInsert() {
// composite form
var user = {
name: 'nisha',
email: 'nisha#gmail.com',
phoneNo: 8978,
picUrl: 'nisha',
description: 'SI',
status: 'active',
waitingTime: 10,
rating: 7
};
/*
calling insert user with above data
*/
var pk = this.userDao.insert(user);
console.log('pk value: ' + pk);
//var obj1 = this.userDao.readById(pk);
console.log('obj1 value: ' + user);
//this.testReadById(obj1);
}
testReadById(obj1) {
var obj2 = this.userDao.readById(obj1);
this.compare.compare(obj1, obj2);
this.testDelete(obj1);
}
}
export default UserDaoTest;
Here in user.test.js, in testInsert() method want to get the value of pk which is returned from insert() method of user.dao.js, but right now I am getting pk value as undefined.
Use a promise chain.
Suppose you need to get an entry for a particular user & do some operations on it.
Model.User.findById(someId)
.then((user) => {
// Do something with user.
})
You shouldn't be calling methods synchronously, NodeJs is not designed this way. It works with callbacks or promises.
Your code won't work because it is async code.
Watch the famous Youtube video about the event loop
But in short, if you will run the following example, which is like your code but without your logic:
var User = require('./user.model');
class UserDao {
constructor() {}
insert(user) {
var pk;
console.log('1');
User.sync({ force: false }).then(() => {
pk = 123;
console.log('3');
});
console.log('2');
return pk;
}
The variable pk will be undefined and your console will look like this:
1
2
3
If you want it to work, you should "wait" for the async functions like this:
var User = require('./user.model');
class UserDao {
constructor() {}
// #return Promise
insert(user) {
return User.sync({ force: false }).then(() => {
return User.create(user)
}).then((user) => {
console.log('Entry successful from dao: ' + JSON.stringify(user));
return user.id
})
}
And when you use it:
class UserDaoTest {
constructor() {
this.userDao = new UserDao();
this.compare = new UtilsObject();
}
/*
all CRUD method calls
*/
testAll() {
// if testInsert and testUpdate can run simultaneously you can keep it like this.
// Otherwise, use Promise.then as well
this.testInsert();
this.testUpdate();
}
/*
insert method
*/
testInsert() {
var user = {
// ...
};
/*
calling insert user with above data
*/
this.userDao.insert(user).then((userId) => {
// YOUR COMPARE CODE
}).then(done); // Where done is a function to let you test framework that you async code is done
}
}
export default UserDaoTest;
Another way of doing that is using the new async and await. That way you will get a code which is more readable and maintainable.
You can read more here

Sequelize v4 | Instance Methods not working

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());
}

Resources