File
Methods
|
Async
asyncExecuteRevertFunctions
|
asyncExecuteRevertFunctions(revertFunctions: IRevertFunction[])
|
|
|
|
Returns : Promise<void>
|
|
deleteAnOperation
|
deleteAnOperation(operationId: string)
|
|
|
Parameters :
| Name |
Type |
Optional |
| operationId |
string
|
No
|
|
|
Async
executeOutstandingRevertOperations
|
executeOutstandingRevertOperations()
|
|
|
|
Returns : Promise<void>
|
|
Async
executeRevertFunctions
|
executeRevertFunctions(revertFunctions: IRevertFunction[])
|
|
|
|
Returns : Promise<void>
|
|
Private
getAsyncKey
|
getAsyncKey(operationId: string)
|
|
|
Parameters :
| Name |
Type |
Optional |
| operationId |
string
|
No
|
|
|
Static
getOperationId
|
getOperationId()
|
|
|
|
|
|
Static
getRevertFunctionId
|
getRevertFunctionId()
|
|
|
|
|
|
getRevertOperations
|
getRevertOperations(operationId: string)
|
|
|
Parameters :
| Name |
Type |
Optional |
| operationId |
string
|
No
|
|
|
Async
isRevertFunctionExecuted
|
isRevertFunctionExecuted(rFunctionId: string)
|
|
|
Parameters :
| Name |
Type |
Optional |
| rFunctionId |
string
|
No
|
Returns : Promise<boolean>
|
|
Async
markRevertFunctionExecuted
|
markRevertFunctionExecuted(rFunctionId: string)
|
|
|
Parameters :
| Name |
Type |
Optional |
| rFunctionId |
string
|
No
|
Returns : Promise<void>
|
|
Async
runRevertOperation
|
runRevertOperation(operationId: string)
|
|
|
Parameters :
| Name |
Type |
Optional |
| operationId |
string
|
No
|
Returns : Promise<void>
|
|
Async
setAsyncExecution
|
setAsyncExecution(operationId: string)
|
|
|
Parameters :
| Name |
Type |
Optional |
| operationId |
string
|
No
|
Returns : Promise<void>
|
|
Private
Readonly
storeProps
|
Type : Map<string | boolean>
|
|
|
import { Injectable } from "@nestjs/common";
import { v4 as uuid } from "uuid";
import { IRevertFunction } from "./interfaces";
@Injectable()
export class RevertOperationService {
private readonly store: Map<string, IRevertFunction[]>; // operationId -> revertFunctions
private readonly storeProps: Map<string, boolean>; // operationId -> isAsync, rFunctionId -> isExecuted
constructor() {
this.store = new Map<string, IRevertFunction[]>();
this.storeProps = new Map<string, boolean>();
this.executeOutstandingRevertOperations(); // run outstanding revert operations on startup
}
static getOperationId(): string {
return "opid" + "__" + uuid();
}
static getRevertFunctionId(): string {
return "rfuncid" + "__" + uuid();
}
pushRevertOperation(
operationId: string,
revertFunctions: IRevertFunction[],
): void {
const storedRevertOperations = this.getRevertOperations(operationId);
const newRevertOperations = [...storedRevertOperations, ...revertFunctions];
this.store.set(operationId, newRevertOperations);
}
getRevertOperations(operationId: string): IRevertFunction[] {
if (!this.store.has(operationId)) {
return [];
}
const revertOperations = this.store.get(operationId);
return revertOperations;
}
deleteAnOperation(operationId: string): void {
this.store.delete(operationId);
}
private getAsyncKey(operationId: string): string {
return operationId + "___" + "async";
}
async setAsyncExecution(operationId: string): Promise<void> {
const key = this.getAsyncKey(operationId);
this.storeProps.set(key, true);
}
async markRevertFunctionExecuted(rFunctionId: string): Promise<void> {
this.storeProps.set(rFunctionId, true);
}
async isRevertFunctionExecuted(rFunctionId: string): Promise<boolean> {
const isExecuted = this.storeProps.get(rFunctionId);
return isExecuted;
}
async runRevertOperation(operationId: string): Promise<void> {
const revertFunctions = this.getRevertOperations(operationId);
const isAsync = this.storeProps.get(this.getAsyncKey(operationId));
if (isAsync) {
await this.asyncExecuteRevertFunctions(revertFunctions);
} else {
await this.executeRevertFunctions(revertFunctions);
}
this.deleteAnOperation(operationId);
}
async asyncExecuteRevertFunctions(
revertFunctions: IRevertFunction[],
): Promise<void> {
await Promise.all(
revertFunctions.map(async (revertItem) => {
const { revertFunction, args, rFunctionId } = revertItem;
if (await this.isRevertFunctionExecuted(rFunctionId)) {
return;
}
await revertFunction(...args);
await this.markRevertFunctionExecuted(rFunctionId);
}),
);
}
async executeRevertFunctions(
revertFunctions: IRevertFunction[],
): Promise<void> {
const reverseFunctionsArray = [...revertFunctions].reverse(); // de-structure and create a new array, so reverse doesn't impact revertFunctions not a good practice to revert parameters
for (const revertItem of reverseFunctionsArray) {
const { revertFunction, args, rFunctionId } = revertItem;
if (await this.isRevertFunctionExecuted(rFunctionId)) {
continue;
}
await revertFunction(...args);
await this.markRevertFunctionExecuted(rFunctionId);
}
}
async executeOutstandingRevertOperations(): Promise<void> {
const operationIds = Array.from(this.store.keys());
await Promise.all(
operationIds.map(async (operationId) => {
await this.runRevertOperation(operationId);
}),
);
}
}