Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/common/index.ts
  • Loading branch information
miekh-s2b committed Oct 17, 2022
2 parents 8791f45 + debf437 commit 49471dc
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 11 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 0.2.0

- Feature: add support for using `DataSource` and `EntityManager` with transactional [#2](https://github.com/Aliheym/typeorm-transactional/issues/2)

# 0.3.0

- Feature: add maximum hook handlers options [#13](https://github.com/Aliheym/typeorm-transactional/issues/13)
- Fix: improve checks to support newest TypeORM versions [#15](https://github.com/Aliheym/typeorm-transactional/issues/9)
25 changes: 19 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ A `Transactional` Method Decorator for [typeorm](http://typeorm.io/) that uses [
See [Changelog](#CHANGELOG.md)

- [Typeorm Transactional](#typeorm-transactional)
- [It's a fork of typeorm-transactional-cls-hooked for new versions of TypeORM.](#its-a-fork-of-typeorm-transactional-cls-hooked-for-new-versions-of-typeorm)
- [Installation](#installation)
- [Initialization](#initialization)
- [Usage](#usage)
Expand All @@ -20,15 +21,15 @@ See [Changelog](#CHANGELOG.md)
- [Hooks](#hooks)
- [Unit Test Mocking](#unit-test-mocking)
- [API](#api)
- [Options](#options)
- [initializeTransactionalContext(): void](#initializetransactionalcontext-void)
- [Library Options](#library-options)
- [Transaction Options](#transaction-options)
- [initializeTransactionalContext(options): void](#initializetransactionalcontext-void)
- [addTransactionalDataSource(input): DataSource](#addtransactionaldatasourceinput-datasource)
- [runInTransaction(fn: Callback, options?: Options): Promise<...>](#runintransactionfn-callback-options-options-promise)
- [wrapInTransaction(fn: Callback, options?: Options): WrappedFunction](#wrapintransactionfn-callback-options-options-wrappedfunction)
- [runOnTransactionCommit(cb: Callback): void](#runontransactioncommitcb-callback-void)
- [runOnTransactionRollback(cb: Callback): void](#runontransactionrollbackcb-callback-void)
- [runOnTransactionComplete(cb: Callback): void](#runontransactioncompletecb-callback-void)

## Installation

```shell
Expand Down Expand Up @@ -291,7 +292,17 @@ Repositories, services, etc. can be mocked as usual.

## API

### Options
### Library Options

```typescript
{
maxHookHandlers?: number
}
```

- `maxHookHandlers` - Controls how many hooks (`commit`, `rollback`, `complete`) can be used simultaneously. If you exceed the number of hooks of same type, you get a warning. This is a useful to find possible memory leaks. You can set this options to `0` or `Infinity` to indicate an unlimited number of listeners. By default, it's `10`.

### Transaction Options

```typescript
{
Expand All @@ -305,14 +316,16 @@ Repositories, services, etc. can be mocked as usual.
- `isolationLevel`- isolation level for transactional context ([isolation levels](#isolation-levels) )
- `propagation`- propagation behaviors for nest transactional contexts ([propagation behaviors](#transaction-propagation))

### initializeTransactionalContext(): void
### initializeTransactionalContext(options): void

Initialize `cls-hooked` namespace.

```typescript
initializeTransactionalContext();
initializeTransactionalContext(options?: TypeormTransactionalOptions);
```

Optionally, you can set some [options](#library-options).

### addTransactionalDataSource(input): DataSource

Add TypeORM `DataSource` to transactional context.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "typeorm-transactional",
"version": "0.2.1",
"version": "0.3.0",
"description": "A Transactional Method Decorator for typeorm that uses cls-hooked to handle and propagate transactions between different repositories and service methods. Inpired by Spring Trasnactional Annotation and Sequelize CLS",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
41 changes: 39 additions & 2 deletions src/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,29 @@ import {
} from './constants';
import { EventEmitter } from 'events';
import { TypeOrmUpdatedPatchError } from '../errors/typeorm-updated-patch';
import { isDataSource } from '../utils';

export type DataSourceName = string | 'default';

/**
* Options to adjust and manage this library
*/
interface TypeormTransactionalOptions {
/**
* Controls how many hooks (`commit`, `rollback`, `complete`) can be used simultaneously.
* If you exceed the number of hooks of same type, you get a warning. This is a useful to find possible memory leaks.
* You can set this options to `0` or `Infinity` to indicate an unlimited number of listeners.
*/
maxHookHandlers: number;
}

/**
* Global data and state
*/
interface TypeormTransactionalData {
options: TypeormTransactionalOptions;
}

interface AddTransactionalDataSourceInput {
/**
* Custom name for data source
Expand All @@ -34,6 +54,15 @@ interface AddTransactionalDataSourceInput {
*/
const dataSources = new Map<DataSourceName, DataSource>();

/**
* Default library's state
*/
const data: TypeormTransactionalData = {
options: {
maxHookHandlers: 10,
},
};

export const getTransactionalContext = () => getNamespace(NAMESPACE_NAME);

export const getEntityManagerByDataSourceName = (context: Namespace, name: DataSourceName) => {
Expand Down Expand Up @@ -107,7 +136,15 @@ const patchDataSource = (dataSource: DataSource) => {
};
};

export const initializeTransactionalContext = () => {
const setTransactionalOptions = (options?: Partial<TypeormTransactionalOptions>) => {
data.options = { ...data.options, ...(options || {}) };
};

export const getTransactionalOptions = () => data.options;

export const initializeTransactionalContext = (options?: Partial<TypeormTransactionalOptions>) => {
setTransactionalOptions(options);

const patchManager = (repositoryType: unknown) => {
Object.defineProperty(repositoryType, 'manager', {
get() {
Expand Down Expand Up @@ -157,7 +194,7 @@ export const initializeTransactionalContext = () => {
};

export const addTransactionalDataSource = (input: DataSource | AddTransactionalDataSourceInput) => {
if (input instanceof DataSource) {
if (isDataSource(input)) {
input = { name: 'default', dataSource: input, patch: true };
}

Expand Down
11 changes: 10 additions & 1 deletion src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { EventEmitter } from 'events';
import { Namespace } from 'cls-hooked';

import { getHookInContext, getTransactionalContext, setHookInContext } from '../common';
import {
getHookInContext,
getTransactionalContext,
getTransactionalOptions,
setHookInContext,
} from '../common';

export const getTransactionalContextHook = () => {
const context = getTransactionalContext();
Expand Down Expand Up @@ -39,8 +44,12 @@ export const runAndTriggerHooks = async (hook: EventEmitter, cb: () => unknown)
};

export const createEventEmitterInNewContext = (context: Namespace) => {
const options = getTransactionalOptions();

return context.runAndReturn(() => {
const emitter = new EventEmitter();
emitter.setMaxListeners(options.maxHookHandlers);

context.bindEmitter(emitter);
return emitter;
});
Expand Down
9 changes: 9 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { DataSource } from 'typeorm';

export const isDataSource = (value: unknown): value is DataSource => {
if (!value || typeof value !== 'object') {
return false;
}

return value.constructor.name === DataSource.name;
};
2 changes: 1 addition & 1 deletion tests/simple.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('Common tests', () => {
return result?.is_transaction || false;
}

async function isQueryBuilderWithEntityTransactionActive(queryBuilder: QueryBuilder<unknown>) {
async function isQueryBuilderWithEntityTransactionActive(queryBuilder: QueryBuilder<any>) {
await queryBuilder.delete().from(Post).where('1 = 1').execute();

await queryBuilder.insert().into(Post).values({ message }).execute();
Expand Down

0 comments on commit 49471dc

Please sign in to comment.