diff --git a/README.md b/README.md new file mode 100644 index 0000000..332ca3f --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +# About package +This package able to manager easily way to database transaction, now you can start and commit transaction wherever you are. +_**WARNING: Package running in REQUEST SCOPE**_ + +## Installation +```bash +npm install --save @antyper/database-session typeorm @nestjs/common rxjs +# you have to install a database driver for e.g: +npm install --save pg +``` + +## Configuration + +```typescript +@Module({ + providers: [], + imports: [ + TypeOrmModule.forRoot({ + type: 'postgres', + host: 'localhost', + port: 5432, + password: 'postgres', + username: 'postgres', + synchronize: true, + entities: [ExampleModel], + }), + DatabaseSessionModule.forRoot(), + ], + controllers: [], +}) +export class AppModule {} +``` + +## Use case +```typescript +@Injectable() +export class ExampleRepository { + private databaseSession: DatabaseSession; + constructor( + @InjectDatabaseSessionManager() + private readonly databaseSessionManager: DatabaseSessionManager, + ) { + this.databaseSession = this.databaseSessionManager.getDatabaseSession(); + } + + async save(exampleModel: Partial): Promise { + const repository = this.databaseSession.getRepository(ExampleModel); + return await repository.save(exampleModel); + } +} + +@Controller('transactions') +export class TransactionController { + private readonly databaseSession: DatabaseSession; + constructor( + private readonly exampleRepository: ExampleRepository, + @InjectDatabaseSessionManager() + private readonly databaseSessionManager: DatabaseSessionManager, + ) { + this.databaseSession = this.databaseSessionManager.getDatabaseSession(); + } + + @Post() + async commitTransaction( + @Body() data: { value: string }, + ): Promise { + try { + // starting transacrtion + await this.databaseSession.transactionStart(); + const result = await this.exampleRepository.save(data); + + // commiting transaction + await this.databaseSession.transactionCommit(); + return result; + } catch (e) { + + // rollback transaction + await this.databaseSession.transactionRollback(); + throw e; + } + } +} +``` + +```typescript +// getting DatabaseSession for "default" connection +const databaseSession = this.databaseSessionManager.getDatabaseSession(); + +const connectionName = "secondDatabaseConnectionName"; +const databaseSessionSecondDatabase = this.databaseSessionManager.getDatabaseSession( + connectionName, +); +``` diff --git a/docker-compose.yaml b/docker-compose.yaml index b0082e7..14ae820 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -6,4 +6,11 @@ services: environment: POSTGRES_PASSWORD: postgres ports: - - 5432:5432 \ No newline at end of file + - 5432:5432 + + db2: + image: library/postgres:13.2-alpine + environment: + POSTGRES_PASSWORD: postgres + ports: + - 5433:5432 diff --git a/libs/database-session/src/database-session.manager.ts b/libs/database-session/src/database-session.manager.ts new file mode 100644 index 0000000..4aa88bc --- /dev/null +++ b/libs/database-session/src/database-session.manager.ts @@ -0,0 +1,26 @@ +import { ConnectionManager } from 'typeorm'; +import { DatabaseSession } from './database-session'; +import { composeDatabaseSessionProviderName } from './inject-decorators'; +import { TypeOrmDatabaseSession } from './type-orm.database-session'; + +export class DatabaseSessionManager { + private databaseSessions: Map = new Map< + string, + DatabaseSession + >(); + + constructor(connectionManager: ConnectionManager) { + connectionManager.connections.forEach((connection) => { + this.databaseSessions.set( + composeDatabaseSessionProviderName(connection.name), + new TypeOrmDatabaseSession(connection), + ); + }); + } + + getDatabaseSession(connectionName?: string): DatabaseSession { + return this.databaseSessions.get( + composeDatabaseSessionProviderName(connectionName), + ); + } +} diff --git a/libs/database-session/src/database-session.module.ts b/libs/database-session/src/database-session.module.ts index 64c2294..6b7c9c9 100644 --- a/libs/database-session/src/database-session.module.ts +++ b/libs/database-session/src/database-session.module.ts @@ -1,48 +1,62 @@ -import { DynamicModule, Global, Module, Scope } from '@nestjs/common'; -import { TypeOrmDatabaseSession } from './type-orm.database-session'; -import { DATABASE_SESSION, SESSION_QUERY_RUNNER } from './inject-decorators'; -import { Connection } from 'typeorm'; -import { FactoryProvider } from '@nestjs/common/interfaces/modules/provider.interface'; +import { + DynamicModule, + FactoryProvider, + Global, + Module, + Scope, +} from '@nestjs/common'; +import { DATABASE_SESSION_MANAGER } from './inject-decorators'; +import { ConnectionManager, getConnectionManager } from 'typeorm'; +import { Type } from '@nestjs/common/interfaces/type.interface'; +import { ForwardReference } from '@nestjs/common/interfaces/modules/forward-reference.interface'; +import { DatabaseSessionManager } from './database-session.manager'; +import { Provider } from '@nestjs/common/interfaces/modules/provider.interface'; @Global() @Module({}) export class DatabaseSessionModule { - static forRootAsync(factory: DatabaseSessionModuleOptions): DynamicModule { - return DatabaseSessionModule.forRoot(factory); + private static readonly DATABASE_SESSION_OPTIONS_PROVIDER = + 'DATABASE_SESSION_OPTIONS_PROVIDER'; + + static async forRoot(): Promise { + return this.forRootAsync(); } - private static forRoot(factory: DatabaseSessionModuleOptions): DynamicModule { - return { - providers: [ - { - useFactory: factory.useFactory, - inject: factory.inject, - provide: 'DatabaseSessionOptions', - }, - { - provide: DATABASE_SESSION, - useFactory: async (connection: Connection) => { - return new TypeOrmDatabaseSession(connection); - }, - scope: Scope.REQUEST, - inject: ['DatabaseSessionOptions'], + static forRootAsync(options?: DatabaseSessionModuleOptions) { + const providers: Provider[] = [ + { + provide: DATABASE_SESSION_MANAGER, + useFactory: (connectionManager?: ConnectionManager) => { + connectionManager = connectionManager ?? getConnectionManager(); + return new DatabaseSessionManager(connectionManager); }, - { - provide: SESSION_QUERY_RUNNER, - useFactory: (typeOrmDatabaseSession: TypeOrmDatabaseSession) => { - return typeOrmDatabaseSession.getQueryRunner(); - }, - inject: [DATABASE_SESSION], - }, - ], - exports: [DATABASE_SESSION, SESSION_QUERY_RUNNER], - imports: factory.imports, + scope: Scope.REQUEST, + inject: options?.inject ?? [], + }, + ]; + if (options) { + providers.push({ + provide: this.DATABASE_SESSION_OPTIONS_PROVIDER, + useFactory: options.useFactory, + inject: options.inject, + }); + } + + return { + providers, + exports: [DATABASE_SESSION_MANAGER], module: DatabaseSessionModule, + imports: options?.imports ?? [], }; } } export interface DatabaseSessionModuleOptions - extends Omit>, 'provide' | 'scope'> { - imports?: any[]; + extends Omit< + FactoryProvider>, + 'provide' | 'scope' + > { + imports?: Array< + Type | DynamicModule | Promise | ForwardReference + >; } diff --git a/libs/database-session/src/inject-decorators.ts b/libs/database-session/src/inject-decorators.ts index fad8587..d17197f 100644 --- a/libs/database-session/src/inject-decorators.ts +++ b/libs/database-session/src/inject-decorators.ts @@ -1,10 +1,18 @@ import { Inject } from '@nestjs/common'; export const DATABASE_SESSION = 'DatabaseSession'; -export const SESSION_QUERY_RUNNER = 'SessionQueryRunner'; +export const DATABASE_SESSION_MANAGER = 'DatabaseSessionManager'; -export const InjectDatabaseSession: () => ParameterDecorator = () => - Inject(DATABASE_SESSION); +export const composeDatabaseSessionProviderName = ( + connectionName = 'default', +) => { + return `${DATABASE_SESSION}_connection_${connectionName}`; +}; -export const InjectSessionQueryRunner: () => ParameterDecorator = () => - Inject(SESSION_QUERY_RUNNER); +export const InjectDatabaseSession: ( + connectionName?: string, +) => ParameterDecorator = (connectionName?: string) => + Inject(composeDatabaseSessionProviderName(connectionName)); + +export const InjectDatabaseSessionManager = () => + Inject(DATABASE_SESSION_MANAGER); diff --git a/libs/database-session/test/database-session.module.spec.ts b/libs/database-session/test/database-session.module.spec.ts new file mode 100644 index 0000000..146e30e --- /dev/null +++ b/libs/database-session/test/database-session.module.spec.ts @@ -0,0 +1,133 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import request from 'supertest'; +import { Connection } from 'typeorm'; +import { getConnectionToken } from '@nestjs/typeorm'; +import { + DatabaseSessionTestModule, + SECOND_DATABASE_CONNECTION, +} from './module/database-session-test.module'; +import { ExampleModel } from './module/example.model'; + +describe('DatabaseSessionModule', () => { + let app: INestApplication; + let connection: Connection; + let connectionSecondDatabase: Connection; + + beforeAll(async () => { + const moduleRef: TestingModule = await Test.createTestingModule({ + imports: [DatabaseSessionTestModule], + }).compile(); + + app = moduleRef.createNestApplication(); + connection = app.get(getConnectionToken()); + connectionSecondDatabase = app.get( + getConnectionToken(SECOND_DATABASE_CONNECTION), + ); + await app.init(); + await clearExampleTable(connection); + await clearExampleTable(connectionSecondDatabase); + }); + + afterEach(async () => { + await clearExampleTable(connection); + await clearExampleTable(connectionSecondDatabase); + }); + + const clearExampleTable = async (connection: Connection) => { + await connection.query('delete from example_model;'); + await connection.query('alter sequence example_model_id_seq restart 1'); + }; + + const getLastRow = async (connection: Connection): Promise => { + return await connection + .getRepository(ExampleModel) + .findOne({ order: { id: 'DESC' } }); + }; + + const getRows = async (connection: Connection): Promise => { + return await connection.getRepository(ExampleModel).find(); + }; + + describe('one database session', () => { + it(`should commit transaction`, async () => { + const response = await request(app.getHttpServer()) + .post('/transactions') + .send({ value: 'test value' }); + + const lastRow: ExampleModel = await getLastRow(connection); + + expect(response.status).toBe(201); + expect(lastRow).toMatchObject({ id: 1, value: 'test value' }); + }); + + it(`should rollback transaction`, async () => { + const result = await request(app.getHttpServer()) + .delete('/transactions') + .send({ value: 'test value' }); + + const rows: ExampleModel[] = await getRows(connection); + + expect(result.status).toBe(500); + expect(rows.length).toBe(1); + expect(rows[0]).toMatchObject({ + id: 2, + value: 'rollback transaction', + }); + expect(rows[1]).toBeUndefined(); + }); + }); + + describe('two database sessions', () => { + it(`should commit transaction`, async () => { + const response = await request(app.getHttpServer()) + .post('/transactions/second-database') + .send({ value: 'second-database' }); + + const lastRow: ExampleModel = await getLastRow(connectionSecondDatabase); + + expect(response.status).toBe(201); + expect(lastRow).toMatchObject({ id: 1, value: 'second-database' }); + }); + + it(`should rollback transaction`, async () => { + const result = await request(app.getHttpServer()) + .delete('/transactions/second-database') + .send({ value: 'second-database' }); + + const rows: ExampleModel[] = await getRows(connectionSecondDatabase); + + expect(result.status).toBe(500); + expect(rows.length).toBe(1); + expect(rows[0]).toMatchObject({ + id: 2, + value: 'rollback transaction in second database', + }); + expect(rows[1]).toBeUndefined(); + }); + }); + + describe('combine of two database transaction', () => { + it('should commit database transaction in default database connection and rollback transaction in second database', async () => { + await request(app.getHttpServer()) + .post('/transactions/combine') + .send({ value: 'default database' }); + + const lastRowFromSecondDatabase: ExampleModel = await getLastRow( + connectionSecondDatabase, + ); + const lastRowFromDefaultDatabase: ExampleModel = await getLastRow( + connection, + ); + + expect(lastRowFromSecondDatabase).toMatchObject({ + id: 2, + value: 'rollback transaction in second database', + }); + expect(lastRowFromDefaultDatabase).toMatchObject({ + id: 1, + value: 'default database', + }); + }); + }); +}); diff --git a/libs/database-session/test/database-session/database-session.module.spec.ts b/libs/database-session/test/database-session/database-session.module.spec.ts deleted file mode 100644 index a6a207f..0000000 --- a/libs/database-session/test/database-session/database-session.module.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import request from 'supertest'; -import { Connection } from 'typeorm'; -import { getConnectionToken } from '@nestjs/typeorm'; -import { DatabaseSessionTestModule } from './module/database-session-test.module'; -import { ExampleModel } from './module/example.model'; - -describe('DatabaseSessionModule', () => { - let app: INestApplication; - let connection: Connection; - - beforeAll(async () => { - const moduleRef: TestingModule = await Test.createTestingModule({ - imports: [DatabaseSessionTestModule], - }).compile(); - - app = moduleRef.createNestApplication(); - connection = app.get(getConnectionToken()); - await app.init(); - await clearExampleTable(); - }); - - afterEach(async () => { - await clearExampleTable(); - }); - - const clearExampleTable = async () => { - await connection.query('delete from example_model;'); - await connection.query('alter sequence example_model_id_seq restart 1'); - }; - - it(`should commit transaction`, async () => { - const response = await request(app.getHttpServer()) - .post('/transactions') - .send({ value: 'test value' }); - - const lastRow: ExampleModel = await connection - .getRepository(ExampleModel) - .findOne({ order: { id: 'DESC' } }); - - expect(response.status).toBe(201); - expect(lastRow).toMatchObject({ id: 1, value: 'test value' }); - }); - - it(`should rollback transaction`, async () => { - const result = await request(app.getHttpServer()) - .delete('/transactions') - .send({ value: 'test value' }); - - const lastRow: ExampleModel[] = await connection - .getRepository(ExampleModel) - .find(); - - expect(result.status).toBe(500); - expect(lastRow.length).toBe(1); - expect(lastRow[0]).toMatchObject({ id: 2, value: 'rollback transaction' }); - expect(lastRow[1]).toBeUndefined(); - }); -}); diff --git a/libs/database-session/test/database-session/module/database-session-test.module.ts b/libs/database-session/test/database-session/module/database-session-test.module.ts deleted file mode 100644 index c6f4812..0000000 --- a/libs/database-session/test/database-session/module/database-session-test.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TransactionController } from './transaction.controller'; -import { ExampleRepository } from './example.repository'; -import { ExampleModel } from './example.model'; -import { Connection } from 'typeorm'; -import { getConnectionToken, TypeOrmModule } from '@nestjs/typeorm'; -import { DatabaseSessionModule } from '../../../src'; - -@Module({ - providers: [ExampleRepository], - imports: [ - TypeOrmModule.forRoot({ - type: 'postgres', - host: 'localhost', - port: 5432, - password: 'postgres', - username: 'postgres', - synchronize: true, - entities: [ExampleModel], - }), - DatabaseSessionModule.forRootAsync({ - useFactory: async (connection: Connection) => { - return connection; - }, - inject: [getConnectionToken()], - }), - ], - controllers: [TransactionController], -}) -export class DatabaseSessionTestModule {} diff --git a/libs/database-session/test/database-session/module/example.repository.ts b/libs/database-session/test/database-session/module/example.repository.ts deleted file mode 100644 index 075fac9..0000000 --- a/libs/database-session/test/database-session/module/example.repository.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ExampleModel } from './example.model'; -import { QueryRunner } from 'typeorm'; -import { - DatabaseSession, - InjectDatabaseSession, - InjectSessionQueryRunner, -} from '../../../src'; - -@Injectable() -export class ExampleRepository { - constructor( - @InjectSessionQueryRunner() private readonly queryRunner: QueryRunner, - @InjectDatabaseSession() private readonly databaseSession: DatabaseSession, - ) {} - - async save(exampleModel: Partial): Promise { - const repository = this.databaseSession.getRepository(ExampleModel); - return await repository.save(exampleModel); - } -} diff --git a/libs/database-session/test/database-session/module/transaction.controller.ts b/libs/database-session/test/database-session/module/transaction.controller.ts deleted file mode 100644 index 5807f88..0000000 --- a/libs/database-session/test/database-session/module/transaction.controller.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Body, Controller, Delete, Post } from '@nestjs/common'; -import { ExampleRepository } from './example.repository'; -import { ExampleModel } from './example.model'; -import { DatabaseSession, InjectDatabaseSession } from '../../../src'; - -@Controller('transactions') -export class TransactionController { - constructor( - private readonly exampleRepository: ExampleRepository, - @InjectDatabaseSession() - private readonly databaseSession: DatabaseSession, - ) {} - - @Post() - async commitTransaction( - @Body() data: { value: string }, - ): Promise { - try { - await this.databaseSession.transactionStart(); - const result = await this.exampleRepository.save(data); - await this.databaseSession.transactionCommit(); - return result; - } catch (e) { - await this.databaseSession.transactionRollback(); - throw e; - } - } - - @Delete() - async rollbackTransaction(@Body() data: { value: string }): Promise { - try { - await this.databaseSession.transactionStart(); - await this.exampleRepository.save(data); - throw new Error('Transaction will be rollback!'); - await this.databaseSession.transactionCommit(); - } catch (e) { - await this.databaseSession.transactionRollback(); - await this.exampleRepository.save({ value: 'rollback transaction' }); - throw e; - } - } -} diff --git a/libs/database-session/test/module/database-session-test.module.ts b/libs/database-session/test/module/database-session-test.module.ts new file mode 100644 index 0000000..49a66ed --- /dev/null +++ b/libs/database-session/test/module/database-session-test.module.ts @@ -0,0 +1,37 @@ +import { Module } from '@nestjs/common'; +import { TransactionController } from './transaction.controller'; +import { ExampleRepository } from './example.repository'; +import { ExampleModel } from './example.model'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { DatabaseSessionModule } from '../../src'; +import { ExampleSecondRepository } from './example-second.repository'; + +export const SECOND_DATABASE_CONNECTION = 'second-database'; + +@Module({ + providers: [ExampleRepository, ExampleSecondRepository], + imports: [ + TypeOrmModule.forRoot({ + type: 'postgres', + host: 'localhost', + port: 5432, + password: 'postgres', + username: 'postgres', + synchronize: true, + entities: [ExampleModel], + }), + TypeOrmModule.forRoot({ + name: SECOND_DATABASE_CONNECTION, + type: 'postgres', + host: 'localhost', + port: 5433, + password: 'postgres', + username: 'postgres', + synchronize: true, + entities: [ExampleModel], + }), + DatabaseSessionModule.forRoot(), + ], + controllers: [TransactionController], +}) +export class DatabaseSessionTestModule {} diff --git a/libs/database-session/test/module/example-second.repository.ts b/libs/database-session/test/module/example-second.repository.ts new file mode 100644 index 0000000..3918795 --- /dev/null +++ b/libs/database-session/test/module/example-second.repository.ts @@ -0,0 +1,23 @@ +import { DatabaseSession, InjectDatabaseSessionManager } from '../../src'; +import { DatabaseSessionManager } from '../../src/database-session.manager'; +import { ExampleModel } from './example.model'; +import { SECOND_DATABASE_CONNECTION } from './database-session-test.module'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class ExampleSecondRepository { + private databaseSession: DatabaseSession; + constructor( + @InjectDatabaseSessionManager() + private readonly databaseSessionManager: DatabaseSessionManager, + ) { + this.databaseSession = this.databaseSessionManager.getDatabaseSession( + SECOND_DATABASE_CONNECTION, + ); + } + + async save(exampleModel: Partial): Promise { + const repository = this.databaseSession.getRepository(ExampleModel); + return await repository.save(exampleModel); + } +} diff --git a/libs/database-session/test/database-session/module/example.model.ts b/libs/database-session/test/module/example.model.ts similarity index 100% rename from libs/database-session/test/database-session/module/example.model.ts rename to libs/database-session/test/module/example.model.ts diff --git a/libs/database-session/test/module/example.repository.ts b/libs/database-session/test/module/example.repository.ts new file mode 100644 index 0000000..ca77d3c --- /dev/null +++ b/libs/database-session/test/module/example.repository.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@nestjs/common'; +import { ExampleModel } from './example.model'; +import { DatabaseSession, InjectDatabaseSessionManager } from '../../src'; +import { DatabaseSessionManager } from '../../src/database-session.manager'; + +@Injectable() +export class ExampleRepository { + private databaseSession: DatabaseSession; + constructor( + @InjectDatabaseSessionManager() + private readonly databaseSessionManager: DatabaseSessionManager, + ) { + this.databaseSession = this.databaseSessionManager.getDatabaseSession(); + } + + async save(exampleModel: Partial): Promise { + const repository = this.databaseSession.getRepository(ExampleModel); + return await repository.save(exampleModel); + } +} diff --git a/libs/database-session/test/module/transaction.controller.ts b/libs/database-session/test/module/transaction.controller.ts new file mode 100644 index 0000000..7729cb9 --- /dev/null +++ b/libs/database-session/test/module/transaction.controller.ts @@ -0,0 +1,108 @@ +import { Body, Controller, Delete, Post } from '@nestjs/common'; +import { ExampleRepository } from './example.repository'; +import { ExampleModel } from './example.model'; +import { DatabaseSession, InjectDatabaseSessionManager } from '../../src'; +import { DatabaseSessionManager } from '../../src/database-session.manager'; +import { SECOND_DATABASE_CONNECTION } from './database-session-test.module'; +import { ExampleSecondRepository } from './example-second.repository'; + +@Controller('transactions') +export class TransactionController { + private databaseSession: DatabaseSession; + private databaseSessionSecondDatabase: DatabaseSession; + constructor( + private readonly exampleRepository: ExampleRepository, + private readonly exampleSecondRepository: ExampleSecondRepository, + @InjectDatabaseSessionManager() + private readonly databaseSessionManager: DatabaseSessionManager, + ) { + this.databaseSession = this.databaseSessionManager.getDatabaseSession(); + this.databaseSessionSecondDatabase = this.databaseSessionManager.getDatabaseSession( + SECOND_DATABASE_CONNECTION, + ); + } + + @Post() + async commitTransaction( + @Body() data: { value: string }, + ): Promise { + try { + await this.databaseSession.transactionStart(); + const result = await this.exampleRepository.save(data); + await this.databaseSession.transactionCommit(); + return result; + } catch (e) { + await this.databaseSession.transactionRollback(); + throw e; + } + } + + @Delete() + async rollbackTransaction(@Body() data: { value: string }): Promise { + try { + await this.databaseSession.transactionStart(); + await this.exampleRepository.save(data); + throw new Error('Transaction will be rollback!'); + await this.databaseSession.transactionCommit(); + } catch (e) { + await this.databaseSession.transactionRollback(); + await this.exampleRepository.save({ value: 'rollback transaction' }); + throw e; + } + } + + @Post('second-database') + async commitTransactionInSecondDatabase( + @Body() data: { value: string }, + ): Promise { + try { + await this.databaseSessionSecondDatabase.transactionStart(); + const result = await this.exampleSecondRepository.save(data); + await this.databaseSessionSecondDatabase.transactionCommit(); + return result; + } catch (e) { + await this.databaseSessionSecondDatabase.transactionRollback(); + throw e; + } + } + + @Delete('second-database') + async rollbackTransactionInSecondDatabase( + @Body() data: { value: string }, + ): Promise { + try { + await this.databaseSessionSecondDatabase.transactionStart(); + await this.exampleSecondRepository.save(data); + throw new Error('Transaction will be rollback!'); + await this.databaseSession.transactionCommit(); + } catch (e) { + await this.databaseSessionSecondDatabase.transactionRollback(); + await this.exampleSecondRepository.save({ + value: 'rollback transaction in second database', + }); + throw e; + } + } + + @Post('combine') + async combineOfTwoDatabaseTransactions( + @Body() data: { value: string }, + ): Promise { + try { + await this.databaseSession.transactionStart(); + await this.databaseSessionSecondDatabase.transactionStart(); + + await this.exampleRepository.save(data); + await this.exampleSecondRepository.save(data); + + await this.databaseSession.transactionCommit(); + throw new Error('Transaction will be rollback!'); + } catch (e) { + await this.databaseSessionSecondDatabase.transactionRollback(); + await this.exampleSecondRepository.save({ + value: 'rollback transaction in second database', + }); + throw e; + } + } +} diff --git a/ormconfig.json b/ormconfig.json deleted file mode 100644 index 1195d03..0000000 --- a/ormconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "type": "postgres", - "host": "localhost", - "port": 5432, - "username": "postgres", - "password": "postgres", - "database": "postgres", - "entities": [ - "test/**/*.model.ts" - ], - "synchronize": true -} diff --git a/package-lock.json b/package-lock.json index 902ebdb..dc5c862 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "@antyper/database-session", - "version": "0.5.3", + "version": "0.6.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@antyper/database-session", - "version": "0.5.3", + "version": "0.6.1", "license": "ISC", "devDependencies": { "@nestjs/common": "^7.6.12", - "@nestjs/core": "^7.6.13", + "@nestjs/core": "^7.6.12", "@nestjs/platform-express": "^7.6.12", "@nestjs/testing": "^7.6.12", "@nestjs/typeorm": "^7.1.5", diff --git a/package.json b/package.json index 1ca446c..1f1f329 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "name": "@antyper/database-session", - "version": "0.6.1", + "version": "0.7.0", "main": "./dist/index.js", "types": "./dist/index.d.ts", + "prepublish": "npm run build", "scripts": { "test": "jest --runInBand --forceExit", "build": "./node_modules/.bin/tsc -p ." @@ -14,7 +15,7 @@ }, "devDependencies": { "@nestjs/common": "^7.6.12", - "@nestjs/core": "^7.6.13", + "@nestjs/core": "^7.6.12", "@nestjs/platform-express": "^7.6.12", "@nestjs/testing": "^7.6.12", "@nestjs/typeorm": "^7.1.5", diff --git a/tsconfig.json b/tsconfig.json index 3453520..7e0228e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,5 @@ "exclude": [ "libs/database-session/node_modules", "dist", - "libs/database-session/test" ] }