Skip to content

Commit

Permalink
feat(transactional): enable adapters to opt out of transactional prox…
Browse files Browse the repository at this point in the history
…y support (#160)
  • Loading branch information
Papooch authored Jun 29, 2024
1 parent e98711c commit 9542cdb
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,11 @@ class AccountService {

When a transaction is not active, the `Transaction` instance refers to the default non-transactional instance. However, if the CLS context is _not active_, the `Transaction` instance will be `undefined` instead, which could cause runtime errors.

Therefore, this feature works reliably only when the CLS context is active _prior to starting the transaction_, which should be the case in most cases, however, for that reason, this is an opt-in feature that must be explicitly enabled with the `enableTransactionProxy: true` option of the `ClsPluginTransactional` constructor.
Therefore, this feature works reliably only when the CLS context is active _prior to starting the transaction_.

Additionally, _some adapters do not support this feature_ due to the nature of how transactions work in the library they implement.

For these reasons, this is an opt-in feature that must be explicitly enabled with the `enableTransactionProxy: true` option of the `ClsPluginTransactional` constructor.

```ts
new ClsPluginTransactional({
Expand All @@ -223,7 +227,7 @@ new ClsPluginTransactional({
// highlight-start
enableTransactionProxy: true,
// highlight-end
}),
});
```

:::
Expand Down
12 changes: 11 additions & 1 deletion packages/transactional/src/lib/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export interface MergedTransactionalAdapterOptions<TTx, TOptions>
connectionName: string | undefined;
enableTransactionProxy: boolean;
defaultTxOptions: Partial<TOptions>;
onModuleInit?: () => void | Promise<void>;
}

export type TransactionalOptionsAdapterFactory<TConnection, TTx, TOptions> = (
Expand Down Expand Up @@ -59,6 +58,15 @@ export interface TransactionalAdapter<TConnection, TTx, TOptions>
TTx,
TOptions
>;

/**
* Whether this adapter support the {@link TransactionalPluginOptions.enableTransactionProxy} option.
*
* The default is `true`. Set to `false` to explicitly forbid this feature.
*
* When set to `false`, and {@link TransactionalPluginOptions.enableTransactionProxy} is `true`, an error will be thrown.
*/
supportsTransactionProxy?: boolean;
}

export interface TransactionalPluginOptions<TConnection, TTx, TOptions> {
Expand All @@ -78,6 +86,8 @@ export interface TransactionalPluginOptions<TConnection, TTx, TOptions> {
* Whether to enable injecting the Transaction instance directly using `@InjectTransaction()`
*
* Default: `false`
*
* Note: Not all adapters support this feature, please refer to the docs for the adapter you are using.
*/
enableTransactionProxy?: boolean;
}
Expand Down
12 changes: 12 additions & 0 deletions packages/transactional/src/lib/plugin-transactional.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getTransactionToken } from './inject-transaction.decorator';
import {
MergedTransactionalAdapterOptions,
OptionalLifecycleHooks,
TransactionalAdapter,
TransactionalPluginOptions,
} from './interfaces';
import {
Expand Down Expand Up @@ -60,6 +61,9 @@ export class ClsPluginTransactional implements ClsPlugin {
this.exports.push(transactionHostToken);

if (options.enableTransactionProxy) {
if (options.adapter.supportsTransactionProxy === false) {
throw new TransactionProxyUnsupportedError(options.adapter);
}
const transactionProxyToken = getTransactionToken(
options.connectionName,
);
Expand Down Expand Up @@ -98,3 +102,11 @@ export class ClsPluginTransactional implements ClsPlugin {
};
}
}

export class TransactionProxyUnsupportedError extends Error {
constructor(adapter: TransactionalAdapter<any, any, any>) {
super(
`The adapter ${adapter.constructor.name} does not support the "Transaction Proxy" feature, please disable the "enableTransactionProxy" option.`,
);
}
}
27 changes: 27 additions & 0 deletions packages/transactional/test/inject-transaction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Transaction,
Transactional,
TransactionHost,
TransactionProxyUnsupportedError,
} from '../src';
import {
MockDbConnection,
Expand Down Expand Up @@ -201,3 +202,29 @@ describe('InjectTransaction with multiple named connections', () => {
});
});
});

class TransactionAdapterMockWithoutTransactionProxySupport extends TransactionAdapterMock {
supportsTransactionProxy = false;
}

describe('Using enableTransactionProxy when the adapter does not support it', () => {
it('should throw an error', async () => {
const modulePromise = () =>
Test.createTestingModule({
imports: [
ClsModule.forRoot({
plugins: [
new ClsPluginTransactional({
enableTransactionProxy: true,
adapter:
new TransactionAdapterMockWithoutTransactionProxySupport(
{ connectionToken: MockDbConnection1 },
),
}),
],
}),
],
}).compile();
expect(modulePromise).toThrowError(TransactionProxyUnsupportedError);
});
});

0 comments on commit 9542cdb

Please sign in to comment.