File

src/revertOperation/revertOperation.service.ts

Index

Properties
Methods

Constructor

constructor()

Methods

Async asyncExecuteRevertFunctions
asyncExecuteRevertFunctions(revertFunctions: IRevertFunction[])
Parameters :
Name Type Optional
revertFunctions IRevertFunction[] No
Returns : Promise<void>
deleteAnOperation
deleteAnOperation(operationId: string)
Parameters :
Name Type Optional
operationId string No
Returns : void
Async executeOutstandingRevertOperations
executeOutstandingRevertOperations()
Returns : Promise<void>
Async executeRevertFunctions
executeRevertFunctions(revertFunctions: IRevertFunction[])
Parameters :
Name Type Optional
revertFunctions IRevertFunction[] No
Returns : Promise<void>
Private getAsyncKey
getAsyncKey(operationId: string)
Parameters :
Name Type Optional
operationId string No
Returns : string
Static getOperationId
getOperationId()
Returns : string
Static getRevertFunctionId
getRevertFunctionId()
Returns : string
getRevertOperations
getRevertOperations(operationId: string)
Parameters :
Name Type Optional
operationId string No
Returns : IRevertFunction[]
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>
pushRevertOperation
pushRevertOperation(operationId: string, revertFunctions: IRevertFunction[])
Parameters :
Name Type Optional
operationId string No
revertFunctions IRevertFunction[] No
Returns : 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>

Properties

Private Readonly store
Type : Map<string | IRevertFunction[]>
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);
      }),
    );
  }
}

results matching ""

    No results matching ""