@tragedy-labs/sprite
Version:
A TypeScript driver for ArcadeDB
134 lines • 5.73 kB
JavaScript
import { Rest } from '../rest/Rest.js';
import { Routes } from '../database/routes.js';
import { SpriteTransaction } from './SpriteTransaction.js';
import { HeaderKeys } from '../rest/SpriteHeaders.js';
/**
* Static class for performing operations on or in a transaction.
*/
export class Transaction {
/**
* Static method to begin a transaction.
* @param session The {@link DatabaseSession `DatabaseSession`} to begin the transaction on.
* @param isolationLevel The isolation level of the transaction.
* @returns A {@link SpriteTransaction `SpriteTransaction`} instance for the target session.
*/
static begin = async (session, isolationLevel) => {
try {
// 'READ_COMMITTED' is default in ARCADEDB,
// so we don't bother sending that
const response = await Rest.post(Routes.BEGIN, isolationLevel === 'REPEATABLE_READ'
? JSON.stringify({ isolationLevel })
: null, session);
if (response.status !== 204) {
throw new Error(`Server returned an unexpected response. Status: ${response.status} / ${response.statusText}.`);
}
const transactionId = response.headers?.get(HeaderKeys.ArcadeSessionId);
// because the headers could be null
// TODO: this isn't a good way to check for the transactionId
if (!transactionId || typeof transactionId !== 'string') {
throw new Error('Invalid transaction key received from server.');
}
else {
return new SpriteTransaction(session, transactionId);
}
}
catch (error) {
throw new Error(`Unable to begin transaction in database "${session.databaseName}".`, { cause: error });
}
};
/**
* Static method to commit a transaction.
* @param session The {@link DatabaseSession `DatabaseSession`} to commit the transaction on.
* @param transaction The id of the transaction to commit.
* @returns `true` if the transaction was committed.
*/
static commit = async (session, transaction) => {
try {
const result = await Rest.post(Routes.COMMIT, null, session, transaction);
if (result.status === 204) {
return true;
}
else {
throw new Error(`Unexpected response from the server when attemping to commit transaction ${transaction.id}`);
}
}
catch (error) {
throw new Error(`Unable to commit transaction ${transaction.id}`, {
cause: error
});
}
};
/**
* Static method to rollback a transaction.
* @param session The {@link DatabaseSession `DatabaseSession`} to rollback the transaction on.
* @param transaction The id of the transaction to rollback.
* @returns `true` if the transaction was rolled back.
*/
static rollback = async (session, transaction) => {
try {
const result = await Rest.post(Routes.ROLLBACK, null, session, transaction);
if (result.status === 204) {
return true;
}
else {
throw new Error(`Unexpected response from the server when attemping to rollback transaction ${transaction.id}`);
}
}
catch (error) {
throw new Error(`Unable to rollback transaction ${transaction.id}`, {
cause: error
});
}
};
/**
* Static method to manage a transaction within the scope.
* @param session The {@link DatabaseSession `DatabaseSession`} to execute the transaction on.
* @param callback The callback to execute within transaction context.
* @param isolationLevel The {@link ArcadeTransactionIsolationLevel isolationLevel} of the transaction.
* @returns A touple containing result of the transaction, and a boolean indicating if the transaction was committed or rolled-back.
*/
static manage = async (session, callback, isolationLevel) => {
const trx = await this.begin(session, isolationLevel);
try {
const result = await callback(trx);
if (trx.rolledBack) {
return [false, result];
}
else {
await trx.commit();
return [true, result];
}
}
catch (error) {
// Attempt to rollback the transaction
try {
await trx.rollback();
throw new Error(`Transaction ${trx.id} failed and was rolled back.`, {
cause: error
});
}
catch (rollbackError) {
throw new Error(`Transaction ${trx.id} failed and rollback also failed.`, { cause: rollbackError });
}
}
};
/**
* Static method to execute CRUD as part of a transaction.
* @param language The {@link ArcadeSupportedQueryLanguages `ArcadeSupportedQueryLanguages`} to use for the command.
* @param command The CRUD command to execute.
* @param params The parameters to pass to the command.
* @returns The result of the CRUD operation.
* @throws Error if the CRUD operation fails.
*/
static crud = async (session, transaction, language, command, params) => {
try {
return await Rest.postJson(Routes.COMMAND, { language, command, params }, session, transaction);
}
catch (error) {
throw new Error(`Could not perform CRUD operation ${command}`, {
cause: error
});
}
};
}
//# sourceMappingURL=Transaction.js.map