From e24d9d0ab39c52c7742673818f3a13d367ebb97b Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Sun, 26 Jan 2025 19:42:13 +0100 Subject: [PATCH] feat: support transactions mongodb --- .../transaction-manager/TransactionManager.ts | 62 +++++++++++++++++++ .../TransactionManagerFactory.ts | 37 +++++++++++ 2 files changed, 99 insertions(+) create mode 100644 api/src/transaction-manager/TransactionManager.ts create mode 100644 api/src/transaction-manager/TransactionManagerFactory.ts diff --git a/api/src/transaction-manager/TransactionManager.ts b/api/src/transaction-manager/TransactionManager.ts new file mode 100644 index 000000000..9304345f1 --- /dev/null +++ b/api/src/transaction-manager/TransactionManager.ts @@ -0,0 +1,62 @@ +/* + * Copyright © 2025 Hexastack. All rights reserved. + * + * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: + * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. + * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). + */ + +import { ClientSession } from 'mongoose'; + +// TODO: logging +export class TransactionManager { + private hasCommited = false; + + private hasAborted = false; + + private hasEnded = false; + + private callbackHasErrored = false; + + constructor(private session: ClientSession) { + this.session.startTransaction(); + } + + async withTransaction( + callback: (session: ClientSession) => Promise, + ): Promise { + try { + const result = await callback(this.session).catch((err) => { + this.callbackHasErrored = true; + throw err; + }); + + await this.commitTransaction(); + return result; + } catch (error) { + await this.abortTransaction(); + throw error; + } finally { + await this.endTransaction(); + } + } + + private async commitTransaction() { + if (this.session.inTransaction()) { + await this.session.commitTransaction(); + this.hasCommited = true; + } + } + + private async abortTransaction() { + if (this.session.inTransaction()) { + await this.session.abortTransaction(); + this.hasAborted = true; + } + } + + private async endTransaction() { + await this.session.endSession(); + this.hasEnded = true; + } +} diff --git a/api/src/transaction-manager/TransactionManagerFactory.ts b/api/src/transaction-manager/TransactionManagerFactory.ts new file mode 100644 index 000000000..0878635d4 --- /dev/null +++ b/api/src/transaction-manager/TransactionManagerFactory.ts @@ -0,0 +1,37 @@ +/* + * Copyright © 2025 Hexastack. All rights reserved. + * + * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: + * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. + * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). + */ + +import { + HttpException, + HttpStatus, + Injectable, + LoggerService, +} from '@nestjs/common'; +import { InjectConnection } from '@nestjs/mongoose'; +import { Connection } from 'mongoose'; + +import { TransactionManager } from './TransactionManager'; + +// TODO: logging / update mongodb docker file replicas +@Injectable() +export class TransactionManagerFactory { + constructor( + @InjectConnection() private readonly connection: Connection, + private loggerService: LoggerService, + ) {} + + async create(): Promise { + const session = await this.connection.startSession().catch((_err) => { + throw new HttpException( + `Something went wrong while start transaction session`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + }); + return new TransactionManager(session); + } +}