UNPKG

@opra/sqb

Version:

Opra SQB adapter package

87 lines (86 loc) 2.86 kB
import { ServiceBase } from '@opra/core'; import { SqbClient, SqbConnection } from '@sqb/connect'; const transactionKey = Symbol.for('transaction'); /** * Base class for services using SQB (SQL Builder) for database operations. */ export class SqbServiceBase extends ServiceBase { /** * The SQB client or connection used by this service. */ db; /** * Constructs a new instance. * * @param options - Options for the service. */ constructor(options) { super(options); this.db = options?.db; } /** * Executes the provided callback within a database transaction. * If a transaction is already active in the current context, the callback * joins it rather than starting a new one. * * @param callback - The function to execute within the transaction. */ async withTransaction(callback) { const ctx = this.context; let closeSessionOnFinish = false; let connection = ctx[transactionKey]; if (!connection) { /* Determine the SqbClient or SqbConnection instance */ const db = await this.getConnection(); if (db instanceof SqbConnection) { connection = db; } else { /* Acquire a connection. New connection should be at the end */ connection = await db.acquire({ autoCommit: false }); closeSessionOnFinish = true; } /* Store transaction connection in current context */ ctx[transactionKey] = connection; } const oldInTransaction = connection.inTransaction; connection.retain(); try { if (!oldInTransaction) await connection.startTransaction(); const out = await callback(connection, this); if (!oldInTransaction && connection.inTransaction) await connection.commit(); return out; } catch (e) { if (!oldInTransaction && connection.inTransaction) await connection.rollback(); throw e; } finally { delete ctx[transactionKey]; /* Release connection */ if (closeSessionOnFinish) { await connection.close(); } else connection.release(); } } /** * Returns the active database connection for the current context. * * @throws If no database connection is configured. */ getConnection() { const ctx = this.context; let db = ctx[transactionKey]; if (db) return db; db = typeof this.db === 'function' ? this.db(this) : this.db; if (db) return db; throw new Error(`Database not set!`); } }