@elsikora/nestjs-crud-automator
Version:
A library for automating the creation of CRUD operations in NestJS.
115 lines (112 loc) • 5.69 kB
JavaScript
import { randomUUID } from 'node:crypto';
import { ApiFunctionContextStorage } from '../context-storage.class.js';
import { ApiFunctionTransactionLifecycle } from './lifecycle.class.js';
import { ApiFunctionTransactionRegistry } from './registry.class.js';
import '../../../../enum/decorator/api/action.enum.js';
import '../../../../enum/decorator/api/authentication-type.enum.js';
import '../../../../enum/decorator/api/controller/get-list/query/filter/missing-behavior.enum.js';
import '../../../../enum/decorator/api/controller/get-list/query/unlisted-fields.enum.js';
import '../../../../enum/decorator/api/controller/relation-reference-shape.enum.js';
import '../../../../enum/decorator/api/controller/request/target.enum.js';
import '../../../../enum/decorator/api/controller/request/transformer-type.enum.js';
import '../../../../enum/decorator/api/controller/response-target.enum.js';
import '../../../../enum/decorator/api/dto-type.enum.js';
import '../../../../enum/decorator/api/function/context-storage-kind.enum.js';
import '../../../../enum/decorator/api/function/subscriber-transaction-expectation.enum.js';
import '../../../../enum/decorator/api/function/transaction/event-status.enum.js';
import { EApiFunctionTransactionFailureStage } from '../../../../enum/decorator/api/function/transaction/failure-stage.enum.js';
import '../../../../enum/decorator/api/function/transaction/mode.enum.js';
import '../../../../enum/decorator/api/function/transaction/outcome.enum.js';
import '../../../../enum/decorator/api/function/transaction/owner-kind.enum.js';
import '../../../../enum/decorator/api/function/transaction/trace-type.enum.js';
import '../../../../enum/decorator/api/function/type.enum.js';
import '../../../../enum/decorator/api/on-type.enum.js';
import '../../../../enum/decorator/api/property/data-type.enum.js';
import '../../../../enum/decorator/api/property/date/identifier.enum.js';
import '../../../../enum/decorator/api/property/date/type.enum.js';
import '../../../../enum/decorator/api/property/desribe-type.enum.js';
import '../../../../enum/decorator/api/property/number-type.enum.js';
import '../../../../enum/decorator/api/property/string-type.enum.js';
import '../../../../enum/decorator/api/route/subscriber-authorization-expectation.enum.js';
import '../../../../enum/decorator/api/route/type.enum.js';
import { ErrorException } from '../../../../utility/error/exception.utility.js';
import { LoggerUtility } from '../../../../utility/logger.utility.js';
const transactionLogger = LoggerUtility.getLogger("ApiFunctionTransactionRuntime");
class ApiFunctionTransactionRuntime {
static async execute(options) {
if (ApiFunctionContextStorage.getTransactionRegistry()) {
throw ErrorException("Cannot open an owning transaction inside an active Automator transaction");
}
const queryRunner = options.dataSource.createQueryRunner();
try {
await queryRunner.connect();
await queryRunner.startTransaction();
}
catch (error) {
await ApiFunctionTransactionRuntime.releaseBestEffort(queryRunner);
throw error;
}
const registry = new ApiFunctionTransactionRegistry(randomUUID(), options.owner);
const result = {};
let commitCleanupFailure;
let commitError;
let hasCommitError = false;
let hasOperationError = false;
let operationError;
let rollbackFailure;
await ApiFunctionContextStorage.runTransaction(queryRunner.manager, queryRunner, registry, async () => {
try {
result.value = await options.callback(queryRunner.manager);
}
catch (error) {
hasOperationError = true;
operationError = error;
rollbackFailure = await ApiFunctionTransactionRuntime.rollbackBestEffort(queryRunner);
await ApiFunctionTransactionRuntime.releaseBestEffort(queryRunner);
return;
}
try {
await queryRunner.commitTransaction();
}
catch (error) {
commitError = error;
hasCommitError = true;
commitCleanupFailure = await ApiFunctionTransactionRuntime.rollbackBestEffort(queryRunner);
}
await ApiFunctionTransactionRuntime.releaseBestEffort(queryRunner);
});
if (hasOperationError) {
return await ApiFunctionTransactionLifecycle.executeAfterRollback(registry, operationError, rollbackFailure);
}
if (hasCommitError) {
return await ApiFunctionTransactionLifecycle.executeCommitUnknown(registry, commitError, commitCleanupFailure);
}
if (!("value" in result)) {
throw ErrorException("Automator transaction completed without a committed result");
}
await ApiFunctionTransactionLifecycle.executeAfterCommit(registry);
return result.value;
}
static async releaseBestEffort(queryRunner) {
try {
await queryRunner.release();
}
catch (error) {
transactionLogger.error("Failed to release transaction query runner", error);
}
}
static async rollbackBestEffort(queryRunner) {
try {
await queryRunner.rollbackTransaction();
return undefined;
}
catch (error) {
return Object.freeze({
error,
stage: EApiFunctionTransactionFailureStage.ROLLBACK,
});
}
}
}
export { ApiFunctionTransactionRuntime };
//# sourceMappingURL=runtime.class.js.map