Nodejs Multiple pools created on refresh - node.js

I have several DBs for which i am using connection pools in node.js. Every time i refresh page i think pools are created again. i refresh page 3 times and 3 times promises resolved. i have removed several databases just to make it little bit easier to read here.
and if i un-comment connection close line my app crashes. i can't seem to figure out why
const config = require("../config/config");
const oracledb = require("oracledb");
var crm1connPromise = new Promise((resolve, reject) => {
oracledb.createPool({
user: config.crm1.user,
password: config.crm1.password,
connectString: config.crm1.connectString,
poolAlias: config.crm1.poolAlias,
poolMin: 0,
poolMax: 10,
poolTimeout: 300
}, (error, pool) => {
if (error) {
reject(err);
}
resolve("CRM1 Promise resolved")
});
});
var query2connPromise = new Promise((resolve, reject) => {
oracledb.createPool({
user: config.query2.user,
password: config.query2.password,
connectString: config.query2.connectString,
poolAlias: config.query2.poolAlias,
poolMin: 0,
poolMax: 10,
poolTimeout: 300
}, (error, pool) => {
if (error) {
reject(err);
}
resolve("QUERY2 Promise resolved --------")
});
});
var promiseArray = [crm1connPromise, crm2connPromise, crm3connPromise, crm4connPromise, csfp1connPromise, csfp2connPromise, csfp3connPromise, csfp4connPromise, cact1connPromise, cact2connPromise, cact3connPromise, cact4connPromise, cospconnPromise, cchnconnPromise, bbaseconnPromise, bcdrconnPromise, vcdbconnPromise, crptconnPromise, query2connPromise];
function getDBConnection (dbname) {
return new Promise((resolve, reject) => {
try {
Promise.all(promiseArray).then((message) => {
console.log(message);
const pool = oracledb.getPool(dbname);
pool.getConnection( (err, connection) => {
if (err) {
reject(err);
console.log(err);
}
resolve(connection);
});
});
} catch (error) {
reject(error);
}
});
}
module.exports.query = function(dbname, sql, bind = []){
return new Promise ((resolve,reject) =>{
var conn
try {
getDBConnection(dbname).then((connection) =>{
connection.execute(sql,bind,(err,result)=>{
if (err){
reject(err);
}
resolve(result);
})
//connection.close(0);
})
} catch (error) {
reject(error);
}
})
}

you can use 'Singleton'
please google 'Singleton pattern' and examples.
like this:
dataBaseManager.js:
'use strict'
var Singleton = (function () {
var instance;
function createInstance() {
var object = new dataBaseManager();
return object;
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
function dataBaseManager() {
this.connected = false;
this.client = null;
this.dataBase = null;
//public methods
this.connect = function () {
try {
your_database.connect({}, (err, client) => {
if (err) {
this.connected = false;
this.client = null;
this.dataBase = null;
return;
}
this.connected = true;
this.client = client;
this.dataBase = client.db();
});
} catch (error) {
}
};
this.disconnect = function () {
try {
if (this.client) {
this.client.close();
this.connected = false;
this.client = null;
this.dataBase = null;
}
} catch (error) {
}
}
}
module.exports = Singleton;
repository.js:
const dataBaseManager = require("./dataBaseManager").getInstance();
your_get_dample_data_from_data_base_func = function (data) {
dataBaseManager.dataBase
.find({})
.toArray(function (err, result) {
if (err) {
return callback(err, null);
}
callback(null, result);
});
};
index.js:
const dataBaseManager = require("./dataBaseManager").getInstance();
function connect() {
dataBaseManager.connect();
}
function disconnect() {
dataBaseManager.disconnect();
}

Look at the node-oracledb example webappawait.js which starts the pool outside the web listener code path.
async function init() {
try {
await oracledb.createPool({
user: dbConfig.user,
password: dbConfig.password,
connectString: dbConfig.connectString
});
const server = http.createServer();
server.on('error', (err) => {
console.log('HTTP server problem: ' + err);
});
server.on('request', (request, response) => {
handleRequest(request, response);
});
await server.listen(httpPort);
console.log("Server is running at http://localhost:" + httpPort);
} catch (err) {
console.error("init() error: " + err.message);
}
}
async function handleRequest(request, response) {
. . .
}

Related

Transaction Global in Node-Firebird

I'm using "node-firebird" in my Firebird 2.5 project and I would like to use a single transaction for multiple inserts or update in some batches, but I couldn't use a global transaction. Could someone help me with this?
This is the single form in the official example:
Firebird.attach(options, function(err, db) {
if (err)
throw err;
// db = DATABASE
db.transaction(Firebird.ISOLATION_READ_COMMITED, function(err, transaction) {
transaction.query('INSERT INTO users VALUE(?,?)', [1, 'Janko'], function(err, result) {
if (err) {
transaction.rollback();
return;
}
transaction.commit(function(err) {
if (err)
transaction.rollback();
else
db.detach();
});
});
});
});
I trying this
const NewTransaction=()=>{
return new Promise((resolve, reject) => {
firebirdPool.get((err, db) => {
if (err) {
reject(err);
return;
}
DBGlobal=db;
DBGlobal.transaction(Firebird.ISOLATION_READ_COMMITED,
function(err, transaction) {
//here i trying save the transaction
TransactionGlobal=transaction;
if (err) {
reject(err);
return;
}
resolve(TransactionGlobal)
});
});//firebirdpool
});//promisse
}//function
const CommitTransaction=()=>{
return new Promise((resolve, reject) => {
TransactionGlobal.commit(function(err) {
if (err){
transaction.rollback();
reject(err);
return;
}
else {
DBGlobal.detach();
resolve(true);
}
});//transaction
});//promisse
}
const RollbackTransaction=()=>{
return new Promise((resolve, reject) => {
try{
TransactionGlobal.rollback();
resolve(true);
}
catch(err){
reject(err)
}
});//promisse
}
//usado com commit
const QueryExecTransaction = (sql,arrayparams=[]) => {
return new Promise((resolve, reject) => {
TransactionGlobal.query(sql,arrayparams,function(err, result) {
if (err) {
console.log('erro na execução da query');
TransactionGlobal.rollback();
reject(err);
return;
}
resolve(result);
return;
});//query
});//promisse
}
I run with this test
async function test(){
await NewTransaction();
console.log('Transacao Global',TransactionGlobal);
QueryExecTransaction(`insert into tabparametros(codigo,nome,valor) values (0,'teste1','')`);
CommitTransaction();
}
test();
But i received this error:
(node:9232) UnhandledPromiseRejectionWarning: Error: invalid transaction handle (expecting explicit transaction start)
I managed to solve, the alteration was in "commitretaining" and others small alters
below the code
//variaveis de "ambiente" salvas na raiz do projeto
//.env e .env.testing
require('dotenv').config({
path: (process.env.NODE_ENV === "test")||(process.env.NODE_ENV === "development") ? ".env.testing" : ".env"
})
var Firebird = require('node-firebird');
var fs = require('fs');
var options = {};
options.host = process.env.DB_HOST;
options.port = process.env.DB_PORT;
options.database = process.env.DB_DATABASE;
options.user = process.env.DB_USER;
options.password = process.env.DB_PASSWORD;
options.lowercase_keys = false; // set to true to lowercase keys
options.role = null; // default
options.pageSize = 4096; // default when creating database
//console.log(options);
//Conexao
// 5 = the number is count of opened sockets
var firebirdPool = Firebird.pool(5, options);
const NewTransaction=()=>{
return new Promise((resolve, reject) => {
firebirdPool.get((err, db) => {
if (err) {
reject(err);
return;
}
//aqui eu salvo o DB retornando
//DBGlobal=db;
db.transaction(Firebird.ISOLATION_READ_COMMITED,
function(err, transaction) {
//aqui eu salvo a transacao retornada
//TransactionGlobal=transaction;
if (err) {
reject(err);
return;
}
resolve({transaction,db});
});
});//firebirdpool
});//promisse
}//function
const CommitTransaction=(transaction,db)=>{
return new Promise((resolve, reject) => {
transaction.commitRetaining(function(err) {
if (err){
transaction.rollback();
reject(err);
return;
}
else {
db.detach();
resolve(true);
}
});//transaction
});//promisse
}
const RollbackTransaction=(transaction,db)=>{
return new Promise((resolve, reject) => {
try{
transaction.rollback();
db.detach();
resolve(true);
}
catch(err){
reject(err)
}
});//promisse
}
//usado com commit
const QueryExecTransaction = (transaction,sql,arrayparams=[]) => {
return new Promise((resolve, reject) => {
transaction.query(sql,arrayparams,function(err, result) {
if (err) {
console.log('erro na execução da query');
transaction.rollback();
reject(err);
return;
}
resolve(result);
return;
});//query
});//promisse
}
async function testa(){
const {transaction,db}=await NewTransaction();
//console.log('Transacao Global',transaction);
let psql='';
try{
for (let i=1;i<101;i++){
psql=`insert into tabparametros(codigo,nome,valor) values (0,'teste${i}-${new Date()}','')`
if (i==79){
//psql='forcando o erro';
}
await QueryExecTransaction(transaction,psql);
}
await CommitTransaction(transaction,db);
}
catch(e){
console.log('Erro no SQL');
console.log(e);
await RollbackTransaction(transaction,db);
}
console.log('Finalizado')
}
testa();

Streaming data from oracle db to browser with node.js

I am learning node.js and database. I am trying to stream heavy data about 7,700,000 rows and 96 columns from oracle to client. Later i use that data for virtual table. But in client it is showing only one row and then in node command error is displaying "Cannot set headers after they are sent to the client". How to stream data in client. Please help
var oracledb = require('oracledb');
const cors = require('cors');
var express = require('express');
var app = express();
app.use(cors());
oracledb.outFormat = oracledb.ARRAY;
oracledb.getConnection({
user: 'user',
password: 'password',
connectString: 'some string'
},
(err, connection) => {
if (err) {
console.error(err.message);
return;
}
var rowsProcessed = 0;
var startTime = Date.now();
var dataSize = 0;
var stream = connection.queryStream(
'SELECT * FROM table',
);
// stream.on('data', function (data) {
// rowsProcessed++;
// // console.log(JSON.stringify(data));
// // console.log(data);
// dataSize = dataSize + data.length;
// // oracleData.push(data);
// // console.log("pushing");
// // console.log(oracleData);
// // app.get('/data', (req, res) => {
// // res.send(data);
// // })
// // console.log(data);
// });
app.get('/data', (req, res) => {
stream.on('data', (data) => {
rowsProcessed++;
dataSize = dataSize + data.length;
res.send(JSON.stringify(data));
})
})
stream.on('end', function () {
var t = ((Date.now() - startTime) / 1000);
console.log('queryStream(): rows: ' + rowsProcessed +
', seconds: ' + t);
// console.log(dataSize + ' bytes');
connection.close(
function (err) {
if (err) {
console.error(err.message);
} else {
console.log("connection closed")
}
}
)
})
}
);
app.listen(5000, () => {
console.log('Listening at 5000')
})
I tried using above approach. But it is failing. How can I achieve the output?
The browser is freezing if I output entire data at single time that's why I am trying to use streaming and in the node command prompt it is displaying out of memory if I load entire data at single time.
Thank you.
The first thing you'll want to do is organize your app a little better. Separation of concerns is important, you should have a connection pool, etc. Have a look at this series for some ideas: https://jsao.io/2018/03/creating-a-rest-api-with-node-js-and-oracle-database/
Once you get the organization figured out, incorporate this example of streaming a large result set out.
const oracledb = require('oracledb');
async function get(req, res, next) {
try {
const conn = await oracledb.getConnection();
const stream = await conn.queryStream('select * from employees', [], {outFormat: oracledb.OBJECT});
res.writeHead(200, {'Content-Type': 'application/json'});
res.write('[');
stream.on('data', (row) => {
res.write(JSON.stringify(row));
res.write(',');
});
stream.on('end', () => {
res.end(']');
});
stream.on('close', async () => {
try {
await conn.close();
} catch (err) {
console.log(err);
}
});
stream.on('error', async (err) => {
next(err);
try {
await conn.close();
} catch (err) {
console.log(err);
}
});
} catch (err) {
next(err);
}
}
module.exports.get = get;
If you find you're doing this a lot, simplify things by creating a reusable transform stream:
const oracledb = require('oracledb');
const { Transform } = require('stream');
class ToJSONArray extends Transform {
constructor() {
super({objectMode: true});
this.push('[');
}
_transform (row, encoding, callback) {
if (this._prevRow) {
this.push(JSON.stringify(this._prevRow));
this.push(',');
}
this._prevRow = row;
callback(null);
}
_flush (done) {
if (this._prevRow) {
this.push(JSON.stringify(this._prevRow));
}
this.push(']');
delete this._prevRow;
done();
}
}
async function get(req, res, next) {
try {
const toJSONArray = new ToJSONArray();
const conn = await oracledb.getConnection();
const stream = await conn.queryStream('select * from employees', [], {outFormat: oracledb.OBJECT});
res.writeHead(200, {'Content-Type': 'application/json'});
stream.pipe(toJSONArray).pipe(res);
stream.on('close', async () => {
try {
await conn.close();
} catch (err) {
console.log(err);
}
});
stream.on('error', async (err) => {
next(err);
try {
await conn.close();
} catch (err) {
console.log(err);
}
});
} catch (err) {
next(err);
}
}
module.exports.get = get;

amqp.connect is not able to maintain connection alive forever

My code is as shown below :
rabbitmq.js
const connectRabbitMq = () => {
amqp.connect(process.env.CLOUDAMQP_MQTT_URL, function (err, conn) {
if (err) {
console.error(err);
console.log('[AMQP] reconnecting in 1s');
setTimeout(connectRabbitMq, 1);
return;
}
conn.createChannel((err, ch) => {
if (!err) {
console.log('Channel created');
channel = ch;
connection = conn;
}
});
conn.on("error", function (err) {
if (err.message !== "Connection closing") {
console.error("[AMQP] conn error", err.message);
}
});
conn.on("close", function () {
console.error("[AMQP] reconnecting");
connectRabbitMq();
});
})
};
const sendMessage = () => {
let data = {
user_id: 1,
test_id: 2
};
if (channel) {
channel.sendToQueue(q, new Buffer(JSON.stringify(data)), {
persistent: true
});
}
else {
connectRabbitMq(() => {
channel.sendToQueue(q, new Buffer(JSON.stringify(data)), {
persistent: true
});
})
}
};
const receiveMessage = () => {
if (channel) {
channel.consume(q, function (msg) {
// ch.ack(msg);
console.log(" [x] Received %s", msg.content.toString());
});
}
else {
connectRabbitMq(() => {
channel.consume(q, function (msg) {
// ch.ack(msg);
console.log(" [x] Received %s", msg.content.toString());
});
})
}
}
scheduler.js
let cron = require('node-cron');
const callMethodForeverRabbitMq = () => {
cron.schedule('*/1 * * * * *', function () {
rabbitMqClientPipeline.receiveMessage();
});
};
app.js
rabbitmq.sendMessage();
now what happens here is , the code is not able to maintain the connection alive forever . so is there any way I can keep it alive forever ?
I am not sure if you are using Promise api or callback API.
With Promise API you can do it like this:
const amqp = require('amqplib');
const delay = (ms) => new Promise((resolve => setTimeout(resolve, ms)));
const connectRabbitMq = () => amqp.connect('amqp://127.0.0.1:5672')
.then((conn) => {
conn.on('error', function (err) {
if (err.message !== 'Connection closing') {
console.error('[AMQP] conn error', err.message);
}
});
conn.on('close', function () {
console.error('[AMQP] reconnecting');
connectRabbitMq();
});
//connection = conn;
return conn.createChannel();
})
.then(ch => {
console.log('Channel created');
//channel = ch;
})
.catch((error) => {
console.error(error);
console.log('[AMQP] reconnecting in 1s');
return delay(1000).then(() => connectRabbitMq())
});
connectRabbitMq();
With callback API like this:
const amqp = require('amqplib/callback_api');
const connectRabbitMq = () => {
amqp.connect('amqp://127.0.0.1:5672', function (err, conn) {
if (err) {
console.error(err);
console.log('[AMQP] reconnecting in 1s');
setTimeout(connectRabbitMq, 1000);
return;
}
conn.createChannel((err, ch) => {
if (!err) {
console.log('Channel created');
//channel = ch;
//connection = conn;
}
});
conn.on("error", function (err) {
if (err.message !== "Connection closing") {
console.error("[AMQP] conn error", err.message);
}
});
conn.on("close", function () {
console.error("[AMQP] reconnecting");
connectRabbitMq();
});
})
};
connectRabbitMq();
UPDATE new code with request buffering
const buffer = [];
let connection = null;
let channel = null;
const connectRabbitMq = () => {
amqp.connect('amqp://127.0.0.1:5672', function (err, conn) {
if (err) {
console.error(err);
console.log('[AMQP] reconnecting in 1s');
setTimeout(connectRabbitMq, 1000);
return;
}
conn.createChannel((err, ch) => {
if (!err) {
console.log('Channel created');
channel = ch;
connection = conn;
while (buffer.length > 0) {
const request = buffer.pop();
request();
}
}
});
conn.once("error", function (err) {
channel = null;
connection = null;
if (err.message !== "Connection closing") {
console.error("[AMQP] conn error", err.message);
}
});
conn.once("close", function () {
channel = null;
connection = null;
console.error("[AMQP] reconnecting");
connectRabbitMq();
});
})
};
const sendMessage = () => {
let data = {
user_id: 1,
test_id: 2
};
if (channel) {
channel.sendToQueue(q, new Buffer(JSON.stringify(data)), {
persistent: true
});
}
else {
buffer.push(() => {
channel.sendToQueue(q, new Buffer(JSON.stringify(data)), {
persistent: true
});
});
}
};
const receiveMessage = () => {
if (channel) {
channel.consume(q, function (msg) {
// ch.ack(msg);
console.log(" [x] Received %s", msg.content.toString());
});
}
else {
buffer.push(() => {
channel.consume(q, function (msg) {
// ch.ack(msg);
console.log(" [x] Received %s", msg.content.toString());
});
})
}
};
There are edge cases where this code won't work - for example it won't reestablish queue.consume unless it's called explicitly. But overall this hopefully gives you idea on how to implement proper recovery...

Unhandled rejection Error: Can't set headers after they are sent. Node.js

When trying run below code I get Unhandled rejection Error: Can't set headers after they are sent when return GetData(); is invoked it starts execution of the function but immediately returns the control back with the error. Observed during debugging.
Basically the code tries to fetch key from MySQL DB if doesn't exist in redis DB
All database and redis related modules are written in separate file so as to reuse it.
somefile.js
var express = require('express');
var router = express.Router();
var dbModules = require('../common/database');
var redisModules = require("../common/redismodule");
function getSettings(request, response)
{
return GetData();
function GetData()
{
return redisModules.GetRedisValue("key")
.then(function (result)
{
if (!result)
return SetData();
else
return result;
})
.then(function (result)
{
response.status(200).send({ value : result });
})
.catch(function (e)
{
response.status(500).send();
};
}
function SetData()
{
return dbModules.executeQuery('query')
.then(function(results)
{
// some code
return 'some_key';
})
.then(function (result)
{
redisModules.setRedisValue('key', result);
});
}
}
database.js
File for handling database connections
var mysql = require('promise-mysql');
pool = mysql.createPool({
host: '',
user: '',
password: '',
database: '',
connectionLimit: 4
});
module.exports = {
getSqlConnection: function()
{
return pool.getConnection().disposer(function(connection)
{
console.log("came here in releasing connection function");
pool.releaseConnection(connection);
});
},
executeQuery: function(sqlQuery)
{
return Promise.using(module.exports.getSqlConnection(), function(connection)
{
return connection.query(sqlQuery)
.then(function(results)
{
return results;
});
});
}
};
redismodule.js
File for handling redis get, set concepts
var Promise = require('bluebird');
var constants = require('../common/contants');
var redisClient; // Global (Avoids Duplicate Connections)
module.exports =
{
OpenRedisConnection : function()
{
if (redisClient == null)
{
redisClient = require("redis").createClient(6379, 'localhost');
}
},
isRedisConnectionOpened : function()
{
if (redisClient && redisClient.connected == true)
{
return true;
}
else
{
if(redisClient)
redisClient.end(); // End and open once more
module.exports.OpenRedisConnection();
return true;
}
},
GetRedisValue: function (key)
{
return new Promise(function (resolve, reject)
{
if(!module.exports.isRedisConnectionOpened())
reject("Redis connection failure");
redisClient.get(key, function (error, result)
{
if (error)
{
reject(error);
}
else
{
if (result == null)
resolve(); // Key not present so create
else
resolve(result);
}
});
});
},
SetRedisValue: function (key, value)
{
return new Promise(function (resolve, reject)
{
if(!module.exports.isRedisConnectionOpened())
reject("Redis connection failure");
redisClient.set(key, value, 'EX', 1000,
function(err,reply)
{
if (reply == 'OK')
resolve(value); // Send the value
else
reject(err);
});
});
}
};
The execution starts when getSettings function is called.
I have just included all the code so that if its correct it might be useful for others.
Corrected Answer
somefile.js
var Promise = require('bluebird');
var dbModules = require('database');
var redisModules = Promise.promisifyAll(require("redismodule"));
async function getSettings(request, response) {
try {
var data = redisModules.GetRedisValue("key");
if (!data)
data = await SetData();
return response.status(200).send({
value: data
});
} catch (error) {
return response.status(500).send({
'error': 'Try after some time'
});
}
function SetData() {
let result = dbModules.executeQuery('query')
return redisModules.setRedisValue('key', result);
}
}
database.js
var mysql = require('promise-mysql');
var pool = mysql.createPool({
host: '',
user: '',
password: '',
database: '',
connectionLimit: 4
});
function getSqlConnection() {
return pool.getConnection().disposer(function (connection) {
console.log("came here in releasing connection function");
pool.releaseConnection(connection);
});
}
module.exports = {
executeQuery: function (sqlQuery) {
return Promise.using(getSqlConnection(), function (connection) {
return connection.query(sqlQuery)
.then(function (results) {
return results;
});
});
}
};
redismodule.js
var redisClient; // Global (Avoids Duplicate Connections)
// Making the below functions are private
function openRedisConnection() {
if (redisClient && redisClient.connected == true) {
return;
} else {
if (redisClient)
redisClient.end(); // End and open once more
redisClient = require("redis").createClient(6379,
process.env.REDIS_URL, {
auth_pass: process.env.REDIS_PASS
});
redisClient.selected_db = 1;
}
}
module.exports = {
GetRedisValue: function (key) {
openRedisConnection();
redisClient.get(key, function (error, result) {
if (error) {
return error;
} else {
if (result)
return result;
else
return null;
}
});
},
SetRedisValue: function (key, value) {
openRedisConnection();
redisClient.set(key, value, 'EX', 1000,
function (err, reply) {
if (reply == 'OK')
resolve(value); // Send the value
else
reject(err);
});
}
};
This is the way I see it :
somefile.js
var dbModules = require('../common/database');
var redisModules = require("../common/redismodule");
function getSettings(request, response) {
function getData() {
return redisModules.getRedisValue('key')
.then(function (result) {
return result || setData();
});
}
function setData() {
return dbModules.executeQuery('query')
.then(function(results) {
return redisModules.setRedisValue('key', results);
});
}
return getData()
.then(function(result) {
response.status(200).send({ value: result });
}).catch(function (e) {
response.status(500).send();
});
}
database.js
var mysql = require('promise-mysql');
var pool = mysql.createPool({
host: '',
user: '',
password: '',
database: '',
connectionLimit: 4
});
function getSqlConnection() {
return pool.getConnection().disposer(function(connection) {
console.log("came here in releasing connection function");
pool.releaseConnection(connection);
});
}
module.exports = {
'executeQuery': function(sqlQuery) {
return Promise.using(getSqlConnection(), function(connection) {
return connection.query(sqlQuery);
});
}
};
redismodule.js
var Promise = require('bluebird');
var redis = Promise.promisifyAll(require('redis'));
var redisClient = null;
function openRedisConnection() {
if (!redisClient || !redisClient.connected) {
if (redisClient) {
redisClient.end(); // End and open once more
}
redisClient = redis.createClient(6379, process.env.REDIS_URL, {
auth_pass: process.env.REDIS_PASS
});
redisClient.selected_db = 1;
}
return redisClient;
}
module.exports = {
'getRedisValue': function(key) {
return openRedisConnection().getAsync(key); // here we call the promise-returning .getAsync() method, created by Promise.promisifyAll()
},
'setRedisValue': function(key, value) {
return openRedisConnection().setAsync(key, value, 'EX', 1000); // here we call the promise-returning .setAsync() method, created by Promise.promisifyAll()
}
};
My main contributions are in somefile.js and redismodule.js. The third module, database.js has been tidied but nothing more than that.
Things like dbModules.executeQuery('query') and redisModules.getRedisValue('key') need to be addressed but I guess you know what you are doing there.

Why does this promise code never run?

As you can see in the .gif below, I call tape.js twice in my integration tests and it works fine. But the second time I run the tests, after I've commented out the code to insert test data into my database, the body of the tape.js function never runs.
Why does this happen?
I've put a breakpoint in my database code and it finishes and calls resolve() before the next tape.js test runs so I don't get it.
Here's my test class, followed by database class:
import tape = require('tape');
const testData = require('../../../helper/data');
import * as db from '../../../helper/integration/database';
helper.setTestEnvironmentVariableOn();
const startedServer : hapi.Server = require('../../../../src/app');
(async function runTests() {
recreateDatabase();
await db.runQuery(getInsertGetRolesTestPermissionsSql());
await test_getRoles_returnsAllRoles();
recreateDatabase();
await db.runQuery(getInsertGetUsersRolesTestPermissionsSql());
await test_getUsersRoles_userCanGetHerOwnRoles();
await startedServer.stop();
})();
function test_getRoles_returnsAllRoles() {
return new Promise( (resolve, reject) => {
tape(testeeName + 'GET /roles returns all, and only, Ekaya roles', async (assert) => {
await helper.createNewUser();
superagent
.get(testData.url + `roles`)
.set('token', testData.token)
.end( (error: any , result: superagent.Response) => {
assert.equals(error, null);
assert.end();
resolve();
});
});
});
}
function test_getUsersRoles_userCanGetHerOwnRoles() {
return new Promise( (resolve, reject) => {
tape(testeeName + 'GET /users/id/roles gives a user her own roles', async (assert) => {
const userid = '635de6dc-0df9-43f4-96dc-922bca541515';
const token = await helper.getTokenForUser(userid);
superagent
.get(testData.url + `users/`+userid+`/roles`)
.set('token', token)
.end( (error: any , result: superagent.Response) => {
assert.equals(error, null);
assert.end();
resolve();
});
});
});
}
function getInsertGetUsersRolesTestPermissionsSql() : string {
return `
INSERT INTO enum.permissions(permissions_id, name,...
`;
}
function getInsertGetRolesTestPermissionsSql() {
return `
delete fr...`;
}
Database class:
import * as pg from "pg";
import 'reflect-metadata';
const Config = require('../../../src/config');
const config = new Config();
export function runQuery(sql: string) : Promise<any> {
return new Promise<any>(async (resolve, reject) => {
try {
const _db = new pg.Client(config.ekaya_local_connection_string);
await connect(_db);
_db.query(sql, function(error, result) {
if (error) {
console.error('Error running query', error);
_db.end();
reject(error);
return;
}
_db.end();
resolve(result);
});
}
catch (error) {
reject(error);
}
});
}
function connect(_db: pg.Client) : Promise<void> {
return new Promise<void>( function(resolve, reject) {
try {
_db.connect(function(error) {
if (error) {
console.error('Could not connect to postgres in integration test helper', error);
reject(error);
return;
}
resolve(null);
});
}
catch (error) {
reject(error);
}
});
}
UPDATE - after vitaly-t's suggestion
New database code, no effect on the problem:
import 'reflect-metadata';
const Config = require('../../../src/config');
const config = new Config();
const pgp = require("pg-promise")();
export function runQuery(sql: string) : any {
return new Promise<any>(async (resolve, reject) => {
try {
const _db = pgp(config.ekaya_local_connection_string);
const result = await _db.query(sql);
pgp.end();
resolve({rows: result});
}
catch (error) {reject(error);}
});
}

Resources