Issue with Node require in class - node.js

I'm fairly new to Node, and writing an app with tests, I faced a scenario, where I don't need to load 2 packages for testing (as it will fail to load the packages as they require some binaries, which won't be present in testing environment). I set an environment variable TEST to true to let the app know that it should't load those 2 packages, and the tests run perfectly. However, for production, I get that the package was not loaded.
This is my class code:
"use strict";
const config = require('../../config/mainConfigs');
...Other constants...
if (typeof process.env.TEST === 'undefined' || process.env.TEST === null){
const mssql = require('mssql');
const oracle = require('oracledb');
if (process.env.DB_PASS && process.env.DB_PASS != '') var db_pass = process.env.DB_PASS;
else if (config.Logging.DB.password != '') var db_pass = config.Logging.DB.password;
else {
console.error(`There's no database password set. Use either Enviroment Variable "DB_PASS" or set "password" under "Logging" > "DB" in configuration file.`);
process.exit(1);
}
}
class db {
constructor(){
this._pool = null;
}
get_pool(){
if (process.env.TEST) return new Promise((resolve)=>resolve());
if (config.Logging.DB.type == 'mssql'){
if (!this._pool) {
this._pool = new mssql.ConnectionPool(sqlDbOptions);
}
if (!this._pool.connected){
return this._pool.connect();
}
else{
return new Promise((resolve, reject) => {
resolve(this._pool);
})
}
}else if (config.Logging.DB.type == 'oracle'){
if (!this._pool || this._pool.connectionsOpen == 0){
return this._pool = oracle.getConnection(oracleDbOptions);
}
else{
return new Promise((resolve, reject) => {
resolve(this._pool);
})
}
}
}
... MORE CLASS OPERATIONS...
}
module.exports = db;
Then I use the DB in my app like this:
const db = require('./db_class');
const db_instance = new db();
When starting the app, I call the get_pool() method to establish the connection prior starting the server.
But for some reason, I am getting:
ReferenceError: mssql is not defined
at db.get_pool (C:\Users...\src\db.js:122:34)
If I move the requires outside the if (the one that checks if the env variable is set) it works just fine.
Aren't the requires synchronous?
Any idea how to solve this?

const is block scoped so your two const variables defined within the if statement block will only be defined and usable within that if statement block.
Basically, you can't conditionally assign to a const like you're trying to do and have the variable available outside the scope of the block. So, you have to settle for using a non-const type (var or let). I recommend using let so you can decide exactly which scope you want it declared in and declaring the variable in that explicit scope. You can then assign to the previously declared variable within your if block.
Here's one usual work-around:
let mssql, oracle;
if (typeof process.env.TEST === 'undefined' || process.env.TEST === null){
mssql = require('mssql');
oracle = require('oracledb');
if (process.env.DB_PASS && process.env.DB_PASS != '') var db_pass = process.env.DB_PASS;
else if (config.Logging.DB.password != '') var db_pass = config.Logging.DB.password;
else {
console.error(`There's no database password set. Use either Enviroment Variable "DB_PASS" or set "password" under "Logging" > "DB" in configuration file.`);
process.exit(1);
}
}

Related

How to solve Converting circular structure to JSON in node?

I was watching a course that showed how to make an console.log with custom configs, like the color or depending on your env mode, you show the log or not.
But i keep getting the error TypeError: Converting circular structure to JSON
and I don't know why this is happening and how to solve it.
In the course, that works fine, but it doesn't to me.
node version => v8.11.2
require('colors')
const _ = require('lodash')
const config = require('../config/config')
const noop = () => { }
const consoleLog = config.logging ? console.log.bind(console) : noop
const logger = {
log: () => {
const args = _.toArray(arguments)
.map(arg => {
if (typeof arg === 'object') {
let str = JSON.stringify(arg, 2)
return str.magenta
} else {
arg += ''
return arg.magenta
}
})
consoleLog.apply(console, args)
}
}
module.exports = logger
Edit1: arguments can be anything, since logger will be used to log things with different colors in the console.
logger.log('some thing you want to log')
logger.log() is an arrow function, so arguments are not arguments of this function (see Arrow functions: No binding of arguments), but arguments of a parent function, in this case — Node.js wrapper function that compiles modules and has arguments with circular dependencies.
Try to use common function here:
const logger = {
log() {
// ...
}
};

ember-electron use fs in third party npm module

I need a third party npm package in my ember-electron app, that requires access to the file system (ngraph.offline.layout, which also has several dependencies.
It uses the following function:
function createLayout(graph, options) {
options = options || {};
var iterations = typeof options.iterations === 'number' ? options.iterations : 500;
var saveEach = typeof options.saveEach === 'number' ? options.saveEach : 5;
var outDir = typeof options.outDir === 'string' ? options.outDir : './data';
var is2d = options.is2d ? true : false;
var coordinatesPerRecord = is2d ? 2 : 3;
var intSize = 4;
var layouter = is2d ? layout3d.get2dLayout : layout3d;
var layout = layouter(graph);
if (!fs.existsSync(outDir)) {
mkdirp.sync(outDir);
}
var lastIteration = getLastIteration(outDir);
return {
run: run,
lastIteration: getLastIteration
};
In my ember controller I have imported it like that:
import createLayout from 'npm:ngraph.offline.layout';
and then
g=graphGenerator.wattsStrogatz(1000, 10, 0.50);
var layout = createLayout(g);
layout.run();
But I always get the following error :
However when I do another request to the filesystem directly in my controller my own function works fine:
import toDot from 'npm:ngraph.todot';
var fs = requireNode('fs');
var stream = fs.createWriteStream('graphDot.txt',{
flags:'a'
});
toDot.write(g, function customWriter(line){
stream.write(line + '\n');
});
stream.end();
What exactly is the difference?
As the module performs an offline layout of a graph, it should be executed on the "server" and I was wondering if this is the correct approach?
EDIT1:
the import and requireNode resides in the ember controller of the specific route

Mocha testing javascript with inheritance

I am new to Mocha and Node but trying to write some Mocha tests on some JavaScript classes.
I have the following class:
function FormField() {
}
FormField.prototype.sanitizeFieldValue = function(value) {
if (value == null || value.replace == null) {
return null;
}
return value
.replace(/ /g, " ")
.replace(/&/g, '&')
.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/[\n\r]+/g, " ");
};
module.exports = FormField;
When I run my mocha tests on this file, everything works fine and the tests pass.
var expect = require("chai").expect;
var formfield = require("FormField");
describe("new Form Field Tests", function() {
var ff = new formfield();
describe("sanitizeFieldValue", function() {
it("escapes apostrophe", function() {
expect(ff.sanitizeFieldValue("'")).to.equal("\\\'");
});
});
});
However, I have another file which references the first:
TargetDateField.prototype = new FormField();
function TargetDateField() {
// some functions
}
module.exports = TargetDateField;
But I am not sure how to test this
I have tried the following but I keep getting FormField is not defined.
var expect = require("chai").expect;
var FormField = require("FormField").FormField;
var targetdatefield = require("TargetDateField");
Any ideas on how to resolve this?
Everywhere where you want to use FormField you require the module that defines it.... except in your TargetDateField.js file, where you don't require it. That's why you are getting an error. You need to require it there too:
var FormField = require("FormField");
TargetDateField.prototype = new FormField();
// etc...
By the way, I strongly suggest writing your code to use relative paths when you want to load other modules that are part of the same package. At first glance, I'd expect require("FormField") to load something from node_modules. (Like when you do require("chai").)

Exporting a new object in Node.js

How can I pass the variables port,host,database into this function?
//myjs.js
var redisCaller = function(port,host,database){
};
module.exports = new redisCaller();
if I do:
var myjs = require('./myjs');
how do I pass those variables?
seems like the only way to do it is like this:
module.exports = function(port,host,database){
return new redisCaller(port,host,database);
}
Change myjs.js to:
module.exports = redisCaller;
Then you can do:
var myjs = require('./myjs')(port,host,database);
You don't.
The way you've set up that code makes it impossible to pass variables in, unless you tweak the require. Which then makes you potentially have to know about the port/host/database in any file you use it in.
Instead, maybe just use an 'init'.
For example, app.js -
var redisCaller = require('./myjs');
redisCaller.init(port, host, database);
And the myjs..
var redisCaller = function(){
this.init = function (port,host,database) {
this.connection = ...
}
this.getConnection = function () {
if(!this.connection) { throw "Need to run init first"; }
return this.connection;
}
};
module.exports = new redisCaller();
Anywhere you need the connection...
var redisCaller = require('./myjs');
var conn = redisCaller.getConnection();
//or
var redisCaller = require('./myjs').getConnection();
It's a bit more code, but at least it's easy to reuse across files.. assuming that was your intention.

binding values while looping in constructor CoffeeScript

I have decided to use CoffeeScript and i have been trying to convert Node.js module of mine to CoffeeScript. So, here is the code in JS:
function Domain(obj){
var self = this;
for (var key in obj){
this[key] = obj[key]; //merge values
}
}
Domain.prototype.save = function(fn){
}
And my attempt to have the same in the CoffeeScript as following:
class Domain
consructor: (obj) ->
for own key, value of obj
#key = value
save: (fn) ->
module.exports = Domain
The following test fails:
require('coffee-script');
var should = require('should')
, Domain = require('../index');
should.exist(Domain);
var domain = new Domain({'attOne':1, 'attTwo':2});
domain.should.have.property('save');
domain.should.have.property('attOne');
domain.should.have.property('attTwo');
domain.save.should.be.an.instanceof(Function);
console.log('All tests passed');
The property 'attrOne' and 'attrTwo' is not binding to the Domain class. I have compiled the coffee code by 'coffee -c index.coffee' to see the js code:
(function() {
var Domain,
__hasProp = {}.hasOwnProperty;
Domain = (function() {
function Domain() {}
Domain.prototype.consructor = function(obj) {
var key, value, _results;
_results = [];
for (key in obj) {
if (!__hasProp.call(obj, key)) continue;
value = obj[key];
_results.push(this.key = value);
}
return _results;
};
Domain.prototype.save = function(fn) {};
return Domain;
})();
module.exports = Domain;
}).call(this);
From the compiled js, i see the '_result' array being generated and returned but never binded to 'this'. How would i bind the array into the class scope to pass the test?Thank You
Your line of CoffeeScript
#key = value
is not equivalent to the javascript you want
this[key] = obj[key];
The way # works is that it is simply replaced by either this or this.. Thus, #key compiles to this.key. You should instead use
#[key] = value
Additionally, you spelled constructor wrong (as consructor). In CoffeeScript, constructor is a special method that compiles differently from a normal method; if it is spelled wrong, CoffeeScript assumes you want an empty one.
consructor: (obj) ->
Domain.prototype.consructor
You are missing a t.
(I keep saying that)

Resources