UNPKG

inceptum

Version:

hipages take on the foundational library for enterprise-grade apps written in NodeJS

173 lines 6.28 kB
"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