@nestjs-cls/transactional
Version:
A nestjs-cls plugin for transactional decorators
190 lines • 8.53 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var TransactionHost_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.TransactionHost = void 0;
exports.getTransactionHostToken = getTransactionHostToken;
exports.InjectTransactionHost = InjectTransactionHost;
const common_1 = require("@nestjs/common");
const nestjs_cls_1 = require("nestjs-cls");
const inject_transaction_decorator_1 = require("./inject-transaction.decorator");
const propagation_1 = require("./propagation");
const symbols_1 = require("./symbols");
let TransactionHost = TransactionHost_1 = class TransactionHost {
/**
* Get a singleton instance of the TransactionHost outside of DI.
*
* @param connectionName The name of the connection. If omitted, the default instance is used.
*/
static getInstance(connectionName) {
const instanceSymbol = (0, symbols_1.getTransactionClsKey)(connectionName);
const instance = this._instanceMap.get(instanceSymbol);
if (!instance) {
throw new Error('TransactionHost not initialized, Make sure that the `ClsPluginTransactional` is properly registered and that the correct `connectionName` is used.');
}
return instance;
}
constructor(_options) {
this._options = _options;
this.cls = nestjs_cls_1.ClsServiceManager.getClsService();
this.logger = new common_1.Logger(TransactionHost_1.name);
this.transactionInstanceSymbol = (0, symbols_1.getTransactionClsKey)(this._options.connectionName);
TransactionHost_1._instanceMap.set(this.transactionInstanceSymbol, this);
}
/**
* The instance of the transaction object.
*
* Depending on the adapter, this may be a transaction reference, a database client, or something else.
* The type is defined by the adapter.
*
* If no transaction is active, this will return the fallback (non-transactional) instance defined by the adapter.
*/
get tx() {
if (!this.cls.isActive()) {
return this._options.getFallbackInstance();
}
return (this.cls.get(this.transactionInstanceSymbol) ??
this._options.getFallbackInstance());
}
withTransaction(firstParam, secondParam, thirdParam) {
let propagation;
let options;
let fn;
if (thirdParam) {
propagation = firstParam;
options = secondParam;
fn = thirdParam;
}
else if (secondParam) {
fn = secondParam;
if (typeof firstParam === 'string') {
propagation = firstParam;
}
else {
options = firstParam;
}
}
else {
fn = firstParam;
}
propagation ??= propagation_1.Propagation.Required;
options = { ...this._options.defaultTxOptions, ...options };
return this.decidePropagationAndRun(propagation, options, fn);
}
decidePropagationAndRun(propagation, options, fn) {
const fnName = fn.name || 'anonymous';
switch (propagation) {
case propagation_1.Propagation.Required:
if (this.isTransactionActive()) {
if (isNotEmpty(options)) {
this.logger.warn(`Transaction options are ignored because a transaction is already active and the propagation mode is ${propagation} (for method ${fnName}).`);
}
return this.cls.run({ ifNested: 'inherit' }, fn);
}
else {
return this.runWithTransaction(options, fn);
}
case propagation_1.Propagation.RequiresNew:
return this.runWithTransaction(options, fn);
case propagation_1.Propagation.NotSupported:
if (isNotEmpty(options)) {
this.logger.warn(`Transaction options are ignored because the propagation mode is ${propagation} (for method ${fnName}).`);
}
return this.withoutTransaction(fn);
case propagation_1.Propagation.Mandatory:
if (!this.isTransactionActive()) {
throw new propagation_1.TransactionNotActiveError(fnName);
}
if (isNotEmpty(options)) {
this.logger.warn(`Transaction options are ignored because the propagation mode is ${propagation} (for method ${fnName}).`);
}
return fn();
case propagation_1.Propagation.Never:
if (this.isTransactionActive()) {
throw new propagation_1.TransactionAlreadyActiveError(fnName);
}
return this.withoutTransaction(fn);
case propagation_1.Propagation.Supports:
if (this.isTransactionActive()) {
if (isNotEmpty(options)) {
this.logger.warn(`Transaction options are ignored because the propagation mode is ${propagation} (for method ${fnName}).`);
}
return fn();
}
return this.withoutTransaction(fn);
default:
throw new propagation_1.TransactionPropagationError(`Unknown propagation mode ${propagation}`);
}
}
runWithTransaction(options, fn) {
return this.cls.run({ ifNested: 'inherit' }, () => this._options
.wrapWithTransaction(options, fn, this.setTxInstance.bind(this))
.finally(() => this.setTxInstance(undefined)));
}
/**
* Wrap a function call to run outside of a transaction.
*
* @param fn The function to run outside of a transaction.
* @returns Whatever the passed function returns
*/
withoutTransaction(fn) {
return this.cls.run({ ifNested: 'inherit' }, () => {
this.setTxInstance(undefined);
return fn().finally(() => this.setTxInstance(undefined));
});
}
/**
* @returns `true` if a transaction is currently active, `false` otherwise.
*/
isTransactionActive() {
if (!this.cls.isActive()) {
return false;
}
return !!this.cls.get(this.transactionInstanceSymbol);
}
setTxInstance(txInstance) {
this.cls.set(this.transactionInstanceSymbol, txInstance);
if (this._options.enableTransactionProxy) {
this.cls.setProxy((0, inject_transaction_decorator_1.getTransactionToken)(this._options.connectionName), txInstance);
}
}
};
exports.TransactionHost = TransactionHost;
TransactionHost._instanceMap = new Map();
exports.TransactionHost = TransactionHost = TransactionHost_1 = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, common_1.Inject)(symbols_1.TRANSACTIONAL_ADAPTER_OPTIONS)),
__metadata("design:paramtypes", [Object])
], TransactionHost);
function isNotEmpty(obj) {
return obj && Object.keys(obj).length > 0;
}
/**
* Get the injection token for a TransactionHost for a named connection.
* If name is omitted, the default instance is used.
*/
function getTransactionHostToken(connectionName) {
return connectionName
? Symbol.for(`${TransactionHost.name}_${connectionName}`)
: TransactionHost;
}
/**
* Inject a TransactionHost for a named connection. Only needed if you want to inject a named instance.
*
* A shorthand for `Inject(getTransactionHostToken(connectionName))`
*/
function InjectTransactionHost(connectionName) {
return (0, common_1.Inject)(getTransactionHostToken(connectionName));
}
//# sourceMappingURL=transaction-host.js.map