Loopback console script does not exit - node.js

I'm writing a Loopback script that is supposed to be called by cron.
In order to obtain the app object, I do
var app = require('./server/server');
# Script logic
console.log('done');
However, the script does not exit once it finishes execution. How do I make it exit?
Reference: http://docs.strongloop.com/display/public/LB/Working+with+LoopBack+objects

Finally found out the cause of this issue.
The problem is due to database connection (in my case, mongodb via loopback-connector-mongodb) is still connected.
To disconnect database connection, and subsequently exiting the console script
var app = require('./server/server');
app.dataSources.DATASOURCENAME.disconnect();

In some places I've read that the issue is the http server preventing the script from shutting down.
I ended up with a module which does not even start an http server, I named it loopback-init.js and I usually import it from migrations and scripts (the important part is the custom callback passed to boot()):
'use strict';
const Promise = require('bluebird');
const loopback = require('loopback');
const boot = require('loopback-boot');
const logger = require('logger');
const app = loopback();
boot(app, __dirname + '/../server', err => {
if (err) throw err;
logger.debug('Loopback initialized.');
app.start = function() {
app.close = function(cb) {
app.removeAllListeners('started');
app.removeAllListeners('loaded');
if (cb) cb();
};
};
});
const autoMigrate = Promise.promisify(
app.dataSources.db.automigrate,
{context: app.dataSources.db}
);
app.autoMigrate = autoMigrate;
module.exports = app;
and my db-migrate scripts look like this:
'use strict';
var dbm;
var type;
var seed;
/**
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db) {
const lb = require('loopback-init');
return lb.autoMigrate('Item')
.then(lb.close, lb.close);
};
exports.down = function(db) {
return db.dropTable('item');
};
exports._meta = {
"version": 1
};

Related

Execute a middleware one-time only at server startup in Koa v2

I created this middleware which executing only once when any route in the website gets the first hit from a visitor:
// pg-promise
const db = require('./db/pgp').db;
const pgp = require('./db/pgp').pgp;
app.use(async (ctx, next) => {
try {
ctx.db = db;
ctx.pgp = pgp;
} catch (err) {
debugErr(`PGP ERROR: ${err.message}` || err);
}
await next();
});
// One-Time middleware
// https://github.com/expressjs/express/issues/2457
const oneTime = (fn) => {
try {
let done = false;
const res = (ctx, next) => {
if (done === false) {
fn(ctx, next);
done = true;
}
next();
};
return res;
} catch (err) {
debugErr(`oneTime ERROR: ${err.message}` || err);
}
};
const oneTimeQuery = async (ctx) => {
const result = await ctx.db.proc('version', [], a => a.version);
debugLog(result);
};
app.use(oneTime(oneTimeQuery));
This code executing on the first-time only when a user visiting the website, resulting:
app:log Listening on port 3000 +13ms
app:req GET / 200 - 24ms +2s
23:07:15 connect(postgres#postgres)
23:07:15 SELECT * FROM version()
23:07:15 disconnect(postgres#postgres)
app:log PostgreSQL 9.6.2, compiled by Visual C++ build 1800, 64-bit +125ms
My problem is that I want to execute it at the server start, when there's no any visit on the site.
The future purpose of this code will be to check the existence of tables in the database.
Solution:
Placing this in ./bin/www before the const server = http.createServer(app.callback()); declaration helped:
const query = async () => {
const db = require('../db/pgp').db;
const pgp = require('../db/pgp').pgp;
const result = await db.proc('version', [], a => a.version);
debugLog(`www: ${result}`);
pgp.end(); // for immediate app exit, closing the connection pool (synchronous)
};
query();
You could start your application using a js script that requires your app and uses node's native http module to fire up the server. Exactly like in koa-generator (click).
This is in your app.js file:
const app = require('koa')();
...
module.exports = app;
And then this is in your script to fire up the server:
const app = require('./app');
const http = require('http');
[this is the place where you should run your code before server starts]
const server = http.createServer(app.callback());
server.listen(port);
Afterwards you start your application with:
node [script_name].js
Of course keep in mind the async nature of node when doing it this way. What I mean by that - run the 'listen' method on 'server' variable in callback/promise.

Re Initialization express Application

I have node.js application, which was built on express.js framework.
const app = express();
require('./config')(app);
require('./services')(app);
./config/config.js we instantiate config:
module.exports = function (app) {
const conf = {APIKey: 1234567890, url: '<someurl>'};
app.set('config', conf);
};
./services/APIService.js we create service instance(singleton)
module.exports = (app) => {
app.set('apiService', new APIService(app));
};
function APIService(app) {
const config = app.get('config');
this.key = config.APIKey;
};
APIService.prototype.sendRequest = () => {
const config = app.get('config');
this._send(config.url, 'some text');
};
Or, service2
module.exports = function(app) {
const config = app.get('config');
const myMod = require('myMod')(config.APIKey);
}
Cool, all works correct. But sometime administrator will change some config data. So, we create new config, set him to
newConf = {APIKey: 1234000000, url: '<some_new_url>'};
app.set('config', newConf);
APIService.sendRequest, will send request to CHANGED url, but APIService.key still unchanged. And myMod already instantiated with old config data.
We need write some setter methods, like this
//for APIService
APIService.prototype.setConfig = () => {
const config = app.get('config');
this.key = config.APIKey;
};
//for service 2
/* change const myMod to let myMod and create method for overriding */
or bang! kill and restart node.js server process. Bad idea. Maybe exist some method for this goal, something like app.restart() for safely reinitializing application(or, maybe, his parts)?
Did you try to call app.set('apiService', new APIService(app)); again ? or just have getter and setter on the prototype for your params.
Better way should be to have a new APIService object at each new request with a middleware, somehting like :
app.use(function (req, res, next){
req.api = new APIService(app);
next();
});
And use req.api.

NodeJS- ReferenceError: wagner is not defined

I don't have experience at all with nodejs and I am learning. I have a code, which I am testing and giving me the following errors, and I don't understand why:
1) Part 3 Assessment Tests "before all" hook:
ReferenceError: wagner is not defined
at Object. (fx.js:2:31)
at Object. (dependencies.js:3:10)
at Context. (test.js:29:20)
2) Part 3 Assessment Tests "after all" hook:
TypeError: Cannot read property 'close' of undefined
at Context. (test.js:65:11)
These are the programs involved in the issue:
test.js --------------
var assert = require('assert');
var express = require('express');
var fs = require('fs');
var status = require('http-status');
var superagent = require('superagent');
var wagner = require('wagner-core');
var URL_ROOT = 'http://localhost:3000';
var PRODUCT_ID = '000000000000000000000001';
describe('Part 3 Assessment Tests', function() {
var server;
var app;
var succeeded = 0;
var finalCharge;
var Category;
var Config;
var fx;
var Product;
var Stripe;
var User;
before(function() {
app = express();
// Bootstrap server
models = require('./models')(wagner);
dependencies = require('./dependencies')(wagner);
// Make models available in tests
var deps = wagner.invoke(function(Category, fx, Product, Stripe, User, Config) {
return {
Category: Category,
fx: fx,
Product: Product,
Stripe: Stripe,
User: User,
Config: Config
};
});
...
dependencies.js -------------
var wagner = require('wagner-core');
var fs = require('fs');
var fx = require('./fx')(wagner);
var Stripe = require('stripe');
module.exports = function(wagner) {
var stripe =
// TODO: Make Stripe depend on the Config service and use its `stripeKey`
// property to get the Stripe API key.
wagner.factory('Stripe', function() {
return Stripe(Config.stripeKey);
});
wagner.factory('fx', fx);
wagner.factory('Config', function() {
return JSON.parse(fs.readFileSync('./config.json').toString());
});
var Config = wagner.invoke(function(Config) {
return Config;
});
};
fx.js --------------------------
var superagent = require('superagent');
var _ = require('underscore')(wagner);
module.exports = function(Config) {
...
};
I think I shouldn't have any problem because wagner is defined on test.js and passed as parameter to dependencies.js, and it is passing it on to fx.js.
1- Could you tell me what is wrong in the code ?
2- The second error, I have not cue why is it happening.
Please, help
In your dependencies.js, in the fx definition, you dont need to pass wagner as parameter, because that variable doesnt exist there:
var fx = require('./fx');
As i see, you already defined the Config factory, and you dont need to assign it to a variable for use it, because wagner will autoinject your "Config" factory, allowing you access to it:
wagner.factory('Stripe', function(Config) {
return Stripe(Config.stripeKey);
});
...
wagner.factory('Config', function() {
return JSON.parse(fs.readFileSync('./config.json').toString());
});
Then in your fx.js, when you wanna use the Config parameter you just pass it:
module.exports = function(Config) {
var url = 'http://openexchangerates.org/api/latest.json?app_id=' +
Config.openExchangeRatesKey;
...
}
Exactly the same you should do if you wanna use the "Config" factory in any other file:
[auth.js]
function setupAuth(User, Config, app) {
...
}
You're trying to use wagner in fx.js before you execute the exported function and outside of the scope that wagner is passed to. Also, Config seems a bit misleading if you're passing wagner to that function instead.
If you can defer loading underscore, you could do something like:
var superagent = require('superagent');
var _;
module.exports = function(Config) {
if (!_)
_ = require('underscore')(Config);
// ...
};

NodeJS - How to test index.js without module.exports

I'm testing my NodeJs project using Mocha and I have a file, index.js that is the main file without module.exports that is run like a CLI
index.js
// CLI tools
var bluebird = require('bluebird');
var gigatool = require('./lib/gigatool');
var debug = require('debug')('index');
var size = 20;
var page = process.env.BATCH;
var startDate = process.env.START;
var dataDir = process.env.DATADIR;
debug(page, startDate, dataDir);
// requires parameters
if (!process.env.BATCH) {
throw new Error('BATCH environment variable is needed');
}
tool = gigatool(size, page, dataDir);
bluebird.all([tool.clean(startDate), tool.continuous()])
.finally(function(){
process.exit(0);
});
test.js
'use strict';
var chai = require('chai');
var fs = require('fs');
var noop = require('lodash.noop');
var rimraf = require('rimraf');
var async = require('async');
var rimraf = require('rimraf');
var expect = chai.expect;
describe.only('Integration', function() {
var dataDir = './countries';
var path = dataDir + '/Albania';
describe('clean run', function() {
this.timeout(10000);
before(function() {
process.env.BATCH = 1;
process.env.DEBUG = '*';
require('../../index');
});
after(function(done) {
// rimraf(dataDir, done);
});
});
});
if I run require('./index'), it will run the module and then continue to move forward, how can i wait for it to end before i run test cases?
Note: It is calling some apis
You need to test your whole application at once, this is still testing but hardly "unit" testing unless your code is a unit ("the unix way"). For this reason your code should start with:
var Promise= require("bluebird");
var exec= Promise.promisify(require("child_process").exec);
var run = function(args){
return exec("node", ["../../index.js"].concat(args)).get("stdout");
};
Which would make your tests test the actual inputs on the file:
describe('your code', function() {
it('should work with params a,b', function(){
return run(['a','b']).then(function(out){ // note the Mocha promise syntax
assert.equal(out, 'your expected stdout');
});
});
});
Unfortunately, there is no way to unit test individual aspects of a CLI Node script as you have it. Instead, what I've done in the past is have conditional execution based on whether the script was used via require or called from the command line:
// index.js
var foo = require('foo');
var bar = require('bar');
// ...
// determine if this script is being required as a module or is CLI
var IS_EXECUTING = (require.main === module);
var methods = {
init: function(args) {
methods.auditArgs(args);
methods.doSomeStuff(arg1, arg2);
methods.doOtherStuff();
},
auditArgs: function(args) {/* ... */},
doSomeStuff: function(arg1, arg2) {/* ... */},
// ...
};
// At the bottom we either begin execution or return a function which can
// be called in a test harness when ready...
if (IS_EXECUTING) {
methods.init(process.argv);
} else {
module.exports = function (mockMethods) {
// you could have some code here to mock out provided methods
// for example:
methods.auditArgs = mockMethods.auditArgs || methods.auditArgs;
// then return the "API" for this script...
return methods;
};
}
In your test harness then you would simply require the file and when ready, use it like you would any other module. But when called from the command line the code will just execute normally:
// in test.js
'use strict';
var chai = require('chai');
// ...
var appFactory = require('index');
var expect = chai.expect;
describe('initialization', function() {
var app;
beforeEach(function() {
app = appFactory({
auditArgs = chai.spy(function() { });
// other mock method implementations, spies, etc
});
});
it('should call necessary methods on init', function() {
expect(app.auditArgs).to.have.been.called(1);
// ...
});
});

How to use knexjs globally in a sails js application

how do i get the knex object in my controllers or any other model files if i am not using waterline.
for eg.:
in my api/models/Users.js
module.exports = {
find : function(id){
// my knex query
},
insert : function(data){
// my knex query again
}
}
So in my controllers i will just do:
var result = Users.find(id);
or
var result = Users.insert({username : 'sailsjs'});
or the knex object will be available globally with out being used in the model files itself... so that i can do the knex query in the controller it self
// UsersController/index
index : function(req, res){
// my knex query
}
Thanks
Arif
//config/bootstrap.js
module.exports.bootstrap = function (cb) {
var Knex = require('knex');
var knex = Knex.initialize({
client : "mysql",
connection : {
host :'localhost',
user :'root',
database : 'sales_force',
password : '*******'
}
});
knex.instanceId = new Date().getTime();
sails.config.knex = knex;
// It's very important to trigger this callack method when you are finished
// with the bootstrap! (otherwise your server will never lift, since it's waiting on the bootstrap)
cb();
};
// in the controller
var knex = sails.config.knex
this returns the knex object. the knex.instanceId shows that the same connection is used all over.
Please suggest if this might cause any problems.
Thanks
Arif
Best Option to use Knex Js globally in Sails Js (Tested for Version 1+) is to create a file named knex.js inside config directory, like this:
/**
* Knex Js, Alternate DB Adapter, In case needed, it is handy for doing migrations
* (sails.config.knex)
*
*
* For all available options, see:
* http://knexjs.org/
*/
const developmentDBConfig = require('./datastores');
const stagingDBConfig = require('./env/staging');
const productionDBConfig = require('./env/production');
function getConnectionString() {
let dbConnectionString = developmentDBConfig.datastores.default.url;
if (process.env.NODE_ENV === 'staging') {
dbConnectionString = stagingDBConfig.datastores.default.url;
}
if (process.env.NODE_ENV === 'production') {
dbConnectionString = productionDBConfig.datastores.default.url;
}
return dbConnectionString;
}
module.exports.knex = require('knex')({
client: 'postgresql',
connection: getConnectionString()
});
Now, in any file(helpers/controllers/views etc..) you can set and use knex as:
// Now use this knex object for anything like:
let user = await sails.config.knex('user').select('*').first();

Resources