@tsrt/typeorm-transactions
Version:
TypeORM convenient transactions management
272 lines • 14 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TransactionManager = exports.UnitOfWork = exports.Transaction = exports.execInTransactionsNamespace = exports.bindTransactionsNamespace = exports.getTransactionsNamespace = exports.createTransactionsNamespace = void 0;
/* eslint-disable max-classes-per-file */
const typeorm_1 = require("typeorm");
const cls_hooked_1 = require("cls-hooked");
const NAMESPACE = '__typeorm_transactions_legacy_namespace__';
const TRANSACTION_KEY = '__typeorm_transaction_legacy_key__';
// const DEFAULT_TRANSACTION_ID = 'default';
const DEFAULT_TRANSACTION_ID = false;
function createTransactionsNamespace() {
var _a;
return (_a = cls_hooked_1.getNamespace(NAMESPACE)) !== null && _a !== void 0 ? _a : cls_hooked_1.createNamespace(NAMESPACE);
}
exports.createTransactionsNamespace = createTransactionsNamespace;
function getTransactionsNamespace() {
return cls_hooked_1.getNamespace(NAMESPACE);
}
exports.getTransactionsNamespace = getTransactionsNamespace;
function bindTransactionsNamespace(req, res, next) {
const ns = createTransactionsNamespace();
ns.bindEmitter(req);
ns.bindEmitter(res);
ns.run(() => next());
}
exports.bindTransactionsNamespace = bindTransactionsNamespace;
function execInTransactionsNamespace(cb) {
return __awaiter(this, void 0, void 0, function* () {
return createTransactionsNamespace().runPromise(cb);
});
}
exports.execInTransactionsNamespace = execInTransactionsNamespace;
function setTransactionIntoNs(key, transaction) {
const ns = getTransactionsNamespace();
if (ns && ns.active)
return ns.set(`${TRANSACTION_KEY}_${key}`, transaction);
}
function getTransactionFromNs(key) {
const ns = getTransactionsNamespace();
if (ns && ns.active)
return ns.get(`${TRANSACTION_KEY}_${key}`);
}
function removeTransactionFromNs(key) {
const ns = getTransactionsNamespace();
if (ns && ns.active)
ns.set(`${TRANSACTION_KEY}_${key}`, null);
}
function createRepositories(repositories, manager, constructorParams) {
let params = typeof constructorParams === 'function' ? constructorParams() : constructorParams;
if (!Array.isArray(params))
params = [params];
// Object.entries(repositories).forEach(([key, Repository]) => { result[key as keyof C] = new Repository(manager, ...params); });
// const instantiated: RepositoriesInstances<C> = { } as RepositoriesInstances<C>;
// Object.defineProperty(result, key, { get: () => {
// if (!instantiated[key]) instantiated[key as keyof C] = new Repository(manager, ...params);
// return instantiated[key];
// } });
const result = {};
Object.entries(repositories).forEach(([key, Repository]) => {
Object.defineProperty(result, key, {
enumerable: true,
configurable: true,
get: () => {
const value = new Repository(manager, ...params);
Object.defineProperty(result, key, { enumerable: true, configurable: false, value });
return value;
},
});
});
return result;
}
function getConnection(connectionFactory, connectionNameFactory) {
const connection = typeof connectionFactory === 'function' ? connectionFactory() : connectionFactory;
const connectionName = typeof connectionNameFactory === 'function' ? connectionNameFactory() : connectionNameFactory;
const result = connection !== null && connection !== void 0 ? connection : typeorm_1.getConnection(connectionName);
if (!result.isConnected)
throw Error('Please, initialize connection before creating `UnitOfWork` instance');
return result;
}
function hasProperty(object, prop) {
return !!Object.hasOwnProperty.call(object, prop);
}
function hasOnlyProperties(object, props) {
const existingProps = Object.keys(object);
return !(existingProps.find((item) => !props.includes(item)));
}
function hasOnlyOneOfProvidedProperties(object, props) {
return props.reduce((acc, curr) => (hasProperty(object, curr) ? acc + 1 : acc), 0) === 1;
}
class Transaction {
constructor(_options) {
var _a, _b, _c, _d;
this._options = _options;
const { connection, connectionName, manager, transaction } = _options;
if (transaction) {
this._connection = transaction.manager.connection;
this._transactionId = `${this._connection.name}__${transaction.id}`;
this._parentTransaction = transaction;
this._hasParentTransaction = true;
this._shouldUseNs = transaction.id !== false;
this._queryRunner = transaction.manager.queryRunner;
}
else {
this._connection = (_a = manager === null || manager === void 0 ? void 0 : manager.connection) !== null && _a !== void 0 ? _a : getConnection(connection, connectionName);
this._transactionId = `${this._connection.name}__${this.id}`;
this._parentTransaction = getTransactionFromNs(this._transactionId);
this._hasParentTransaction = !!(manager === null || manager === void 0 ? void 0 : manager.queryRunner) || !!this._parentTransaction;
this._shouldUseNs = this.id !== false;
this._queryRunner = (_d = (_b = manager === null || manager === void 0 ? void 0 : manager.queryRunner) !== null && _b !== void 0 ? _b : (_c = this._parentTransaction) === null || _c === void 0 ? void 0 : _c.manager.queryRunner) !== null && _d !== void 0 ? _d : this._connection.createQueryRunner();
}
if (this._shouldUseNs) {
this._validateNsAvailability();
setTransactionIntoNs(this._transactionId, this);
}
}
get repositories() {
if (!this._repositories) {
const { repositories, repositoryConstructorParams } = this._options;
this._repositories = createRepositories(repositories, this.manager, repositoryConstructorParams);
}
return this._repositories;
}
get manager() {
return this._queryRunner.manager;
}
get id() {
var _a;
return (_a = this._options.id) !== null && _a !== void 0 ? _a : DEFAULT_TRANSACTION_ID;
}
begin() {
return __awaiter(this, void 0, void 0, function* () {
if (this._hasParentTransaction)
return;
if (!this._queryRunner.isTransactionActive)
yield this._queryRunner.startTransaction(this._options.isolationLevel);
else
throw new Error('Transaction is already started');
});
}
commit() {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
if (this._hasParentTransaction)
return;
if (this._queryRunner.isTransactionActive && !((_a = this._options.manager) === null || _a === void 0 ? void 0 : _a.queryRunner))
yield this._queryRunner.commitTransaction();
if (!this._queryRunner.isReleased && !((_b = this._options.manager) === null || _b === void 0 ? void 0 : _b.queryRunner))
yield this._queryRunner.release();
if (this._shouldUseNs)
removeTransactionFromNs(this._transactionId);
});
}
rollback(error) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
if (this._hasParentTransaction)
return;
if (this._queryRunner.isTransactionActive && !((_a = this._options.manager) === null || _a === void 0 ? void 0 : _a.queryRunner))
yield this._queryRunner.rollbackTransaction();
if (!this._queryRunner.isReleased && !((_b = this._options.manager) === null || _b === void 0 ? void 0 : _b.queryRunner))
yield this._queryRunner.release();
if (this._shouldUseNs)
removeTransactionFromNs(this._transactionId);
if (error && typeof error === 'string')
throw new Error(error);
else if (error)
throw error;
});
}
_validateNsAvailability() {
if (!this._shouldUseNs)
return;
const ns = getTransactionsNamespace();
if (!ns)
throw new Error('Please, create Namespace using `createTransactionsNamespace` method.');
if (!ns.active)
throw new Error('Please, bind Namespace using `bindTransactionsNamespace` or `execInTransactionsNamespace` method.');
}
}
exports.Transaction = Transaction;
class UnitOfWork {
constructor(_options = {}) {
this._options = _options;
}
exec(cb) {
return __awaiter(this, void 0, void 0, function* () {
const { connection, connectionName, manager, isolationLevel } = this._options;
this._connection = getConnection(connection, connectionName);
const getRepositories = this._getRepositories.bind(this);
const execute = (mgr) => __awaiter(this, void 0, void 0, function* () { return cb({ manager: mgr, get repositories() { return getRepositories(mgr); } }); });
return manager ? execute(manager) : this._connection.transaction(isolationLevel, execute);
// return manager ? manager.transaction(isolationLevel, execute) : this._connection.transaction(isolationLevel, execute);
});
}
_getRepositories(manager) {
if (!this._repositories) {
const { repositories, repositoryConstructorParams } = this._options;
this._repositories = createRepositories(repositories, manager, repositoryConstructorParams);
}
return this._repositories;
}
}
exports.UnitOfWork = UnitOfWork;
class TransactionManager {
constructor(_options = {}) {
this._options = _options;
}
unitOfWork(cb, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
return this.createUnitOfWork(options).exec(cb);
});
}
createUnitOfWork(options = {}) {
return new UnitOfWork(this._getDefaults(options));
}
transaction(cbOrOptions, options) {
return __awaiter(this, void 0, void 0, function* () {
const opts = this._getDefaults(typeof cbOrOptions === 'function' ? options : cbOrOptions);
const transaction = this.createTransaction(opts);
if (!transaction.manager.queryRunner.isTransactionActive)
yield transaction.begin();
return typeof cbOrOptions === 'function'
? cbOrOptions(transaction)
.then((result) => __awaiter(this, void 0, void 0, function* () { yield transaction.rollback(); return result; }))
.catch((err) => __awaiter(this, void 0, void 0, function* () { return yield transaction.rollback(err); }))
: transaction;
});
}
createTransaction(options = {}) {
return new Transaction(this._getDefaults(options));
}
get connection() {
var _a;
const { manager, connection, connectionName } = this._options;
if (!this._connection)
this._connection = (_a = manager === null || manager === void 0 ? void 0 : manager.connection) !== null && _a !== void 0 ? _a : getConnection(connection, connectionName);
return this._connection;
}
get repositories() {
if (!this._repositories) {
const { repositories, repositoryConstructorParams } = this._options;
this._repositories = createRepositories(repositories, this.connection.manager, repositoryConstructorParams);
}
return this._repositories;
}
_getDefaults(options) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const result = Object.assign(Object.assign({}, options), { connection: this.connection, connectionName: this.connection.name, repositories: (_a = options === null || options === void 0 ? void 0 : options.repositories) !== null && _a !== void 0 ? _a : (_b = this._options) === null || _b === void 0 ? void 0 : _b.repositories, repositoryConstructorParams: (_c = options === null || options === void 0 ? void 0 : options.repositoryConstructorParams) !== null && _c !== void 0 ? _c : (_d = this._options) === null || _d === void 0 ? void 0 : _d.repositoryConstructorParams, isolationLevel: (_e = options === null || options === void 0 ? void 0 : options.isolationLevel) !== null && _e !== void 0 ? _e : (_f = this._options) === null || _f === void 0 ? void 0 : _f.isolationLevel, id: (_g = options === null || options === void 0 ? void 0 : options.id) !== null && _g !== void 0 ? _g : (_h = this._options) === null || _h === void 0 ? void 0 : _h.defaultTransactionId });
this._validateOptions(result);
return result;
}
_validateOptions(options) {
const { manager, transaction } = options;
if (manager && manager.connection.name !== this.connection.name) {
throw new Error('Provided `manager` option with invalid (another) connection');
}
if (transaction && transaction.manager.connection.name !== this.connection.name) {
throw new Error('Provided `transaction` option with invalid (another) connection');
}
}
}
exports.TransactionManager = TransactionManager;
//# sourceMappingURL=TransactionManager.js.map