@opra/sqb
Version:
Opra SQB adapter package
87 lines (86 loc) • 2.86 kB
JavaScript
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!`);
}
}