inceptum
Version:
hipages take on the foundational library for enterprise-grade apps written in NodeJS
173 lines • 6.28 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const pg = require("pg");
const DBClient_1 = require("../db/DBClient");
const DBTransaction_1 = require("../db/DBTransaction");
const TransactionManager_1 = require("../transaction/TransactionManager");
const LogManager_1 = require("../log/LogManager");
const log = LogManager_1.LogManager.getLogger(__filename);
class PostgresTransaction extends DBTransaction_1.DBTransaction {
/**
*
* @param {PostgresClient} postgresClient
* @param transaction
*/
constructor(postgresClient, transaction) {
super(transaction);
this.postgresClient = postgresClient;
}
// tslint:disable-next-line:prefer-function-over-method
getConnectionPromise(connectionPool) {
return new Promise((resolve, reject) => {
connectionPool.getConnection((err, connection) => {
if (err) {
reject(err);
}
else {
resolve(connection);
}
});
});
}
// tslint:disable-next-line:prefer-function-over-method
runQueryOnPool(connection, sql, bindsArr) {
log.debug(`sql: ${sql} ${(bindsArr && (bindsArr.length > 0)) ? `| ${bindsArr}` : ''}`);
if (!Array.isArray(bindsArr)) {
bindsArr = [];
}
return new Promise((resolve, reject) => connection.query(sql, bindsArr, (err, res) => {
if (err) {
log.error({ err }, `SQL error for ${sql}`);
return reject(err);
}
return resolve(res.rows);
}));
}
async runQueryPrivate(sql, bindsArr) {
if (!this.connection) {
// tslint:disable-next-line:no-invalid-this
this.connection = await this.getConnectionPromise(this.postgresClient.getConnectionPoolForReadonly(this.transaction.isReadonly()));
}
return await this.runQueryOnPool(this.connection, `/* Transaction Id ${this.transaction.id} */ ${sql}`, bindsArr);
}
runQueryAssocPrivate(sql, bindsObj) {
if (sql.indexOf('::') < 0 || !bindsObj) {
// tslint:disable-next-line:no-invalid-this
return this.runQueryPrivate.call(this, sql, []);
}
sql.replace(/::(\w)+::/g, (substr, key) => {
if (bindsObj.hasOwnProperty(key)) {
return bindsObj[key];
}
return substr;
});
}
async doTransactionEnd() {
this.connection.release();
}
}
exports.PostgresTransaction = PostgresTransaction;
class PostgresConnectionPool {
// active: number;
// numConnections: number;
// enqueueTimes: Array<number>;
// durationHistogram: any; // todo
// config: PostgresPoolConfig;
constructor(config, name) {
this.instance = new pg.Pool(config);
}
end() {
return this.instance.end();
}
getConnection(cb) {
this.instance.connect((err, connection) => {
cb(err, connection);
});
}
}
/**
* A MySQL client you can use to execute queries against MySQL
*/
class PostgresClient extends DBClient_1.DBClient {
constructor() {
super();
this.configuration = {};
this.name = 'NotSet';
this.masterPool = null;
this.slavePool = null;
this.connectionPoolCreator = (config) => new PostgresConnectionPool(config, this.name);
}
// configuration and name are two properties set by MysqlConfigManager
initialise() {
if (this.configuration.master) {
this.masterPool = this.connectionPoolCreator(this.getFullPoolConfig(this.configuration.master));
}
if (this.configuration.slave) {
this.slavePool = this.connectionPoolCreator(this.getFullPoolConfig(this.configuration.slave));
}
if (!this.masterPool && !this.slavePool) {
throw new Error(`PostgresClient ${this.name} has no connections configured for either master or slave`);
}
}
/**
* Runs a function in a transaction. The function must receive one parameter that will be of class
* {PostgresTransaction} and that you need to use to run all queries in this transaction
*
* @param {boolean} readonly Whether the transaction needs to be readonly or not
* @param {Function} func A function that returns a promise that will execute all the queries wanted in this transaction
* @returns {Promise} A promise that will execute the whole transaction
*/
runInTransaction(readonly, func) {
const transaction = TransactionManager_1.TransactionManager.newTransaction(readonly);
const mysqlTransaction = new PostgresTransaction(this, transaction);
return mysqlTransaction.begin()
.then(() => func(mysqlTransaction))
.catch((err) => {
log.error({ err }, 'There was an error running in transaction');
transaction.markError(err);
throw err;
})
.then((result) => {
mysqlTransaction.end();
return result;
}, (reason) => {
mysqlTransaction.end();
throw reason;
});
}
shutdown() {
if (this.masterPool) {
this.masterPool.end();
}
if (this.slavePool) {
this.slavePool.end();
}
}
// tslint:disable-next-line:prefer-function-over-method
getFullPoolConfig(partial) {
const full = {
host: 'localhost',
port: 5432,
user: 'postgres',
password: '',
max: 10,
min: 1,
};
Object.assign(full, partial);
return full;
}
getConnectionPoolForReadonly(readonly) {
if (readonly && this.slavePool) {
return this.slavePool;
}
else if (this.masterPool) {
return this.masterPool;
}
throw new Error('Couldn\'t find an appropriate connection pool');
}
}
PostgresClient.startMethod = 'initialise';
PostgresClient.stopMethod = 'shutdown';
exports.PostgresClient = PostgresClient;
// export const TestUtil = { runQueryOnPool };
//# sourceMappingURL=PostgresClient.js.map