UNPKG

@nestjs-cls/transactional

Version:

A nestjs-cls plugin for transactional decorators

190 lines 8.53 kB
"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