Source

operations/Operations.ts

import { Hashing, Model } from "@decaf-ts/decorator-validation";
import { OperationHandler } from "./types";
import { OperationsRegistry } from "./OperationsRegistry";
import { OperationKeys } from "./constants";
import { IRepository } from "../interfaces";
import { Context } from "../repository";
import { RepositoryFlags } from "../repository/types";

/**
 * @description Static utility class for database operation management
 * @summary Provides functionality for registering, retrieving, and managing database operation handlers
 * @class Operations
 * @template M - Model type
 * @template R - Repository type
 * @template V - Metadata type
 * @template F - Repository flags
 * @template C - Context type
 * @example
 * // Register a handler for a create operation
 * Operations.register(myHandler, OperationKeys.CREATE, targetModel, 'propertyName');
 * 
 * // Get handlers for a specific operation
 * const handlers = Operations.get(targetModel.constructor.name, 'propertyName', 'onCreate');
 * 
 * @mermaid
 * classDiagram
 *   class Operations {
 *     -registry: OperationsRegistry
 *     +getHandlerName(handler)
 *     +key(str)
 *     +get(targetName, propKey, operation)
 *     -getOpRegistry()
 *     +register(handler, operation, target, propKey)
 *   }
 *   Operations --> OperationsRegistry : uses
 */
export class Operations {
  private static registry: OperationsRegistry;

  private constructor() {}

  /**
   * @description Gets a unique name for an operation handler
   * @summary Returns the name of the handler function or generates a hash if name is not available
   * @param {OperationHandler<any, any, any, any, any>} handler - The handler function to get the name for
   * @return {string} The name of the handler or a generated hash
   */
  static getHandlerName(handler: OperationHandler<any, any, any, any, any>) {
    if (handler.name) return handler.name;

    console.warn(
      "Handler name not defined. A name will be generated, but this is not desirable. please avoid using anonymous functions"
    );
    return Hashing.hash(handler.toString());
  }

  /**
   * @description Generates a reflection metadata key
   * @summary Creates a fully qualified metadata key by prefixing with the reflection namespace
   * @param {string} str - The operation key string to prefix
   * @return {string} The fully qualified metadata key
   */
  static key(str: string) {
    return OperationKeys.REFLECT + str;
  }

  /**
   * @description Retrieves operation handlers for a specific target and operation
   * @summary Gets registered handlers from the operations registry for a given target, property, and operation
   * @template M - Model type extending Model
   * @template R - Repository type extending IRepository
   * @template V - Metadata type, defaults to object
   * @template F - Repository flags extending RepositoryFlags
   * @template C - Context type extending Context<F>
   * @param {string | Record<string, any>} targetName - The target class name or object
   * @param {string} propKey - The property key to get handlers for
   * @param {string} operation - The operation key to get handlers for
   * @return {any} The registered handlers for the specified target, property, and operation
   */
  static get<
    M extends Model,
    R extends IRepository<M, F, C>,
    V = object,
    F extends RepositoryFlags = RepositoryFlags,
    C extends Context<F> = Context<F>,
  >(
    targetName: string | Record<string, any>,
    propKey: string,
    operation: string
  ) {
    return Operations.registry.get<M, R, V, F, C>(
      targetName,
      propKey,
      operation
    );
  }

  /**
   * @description Gets or initializes the operations registry
   * @summary Returns the existing registry or creates a new one if it doesn't exist
   * @return {OperationsRegistry} The operations registry instance
   * @private
   */
  private static getOpRegistry() {
    if (!Operations.registry) Operations.registry = new OperationsRegistry();
    return Operations.registry;
  }

  /**
   * @description Registers an operation handler for a specific target and operation
   * @summary Adds a handler to the operations registry for a given target, property, and operation
   * @template V - Model type extending Model
   * @param {OperationHandler<V, any, any>} handler - The handler function to register
   * @param {OperationKeys} operation - The operation key to register the handler for
   * @param {V} target - The target model instance
   * @param {string | symbol} propKey - The property key to register the handler for
   * @return {void}
   */
  static register<V extends Model>(
    handler: OperationHandler<V, any, any>,
    operation: OperationKeys,
    target: V,
    propKey: string | symbol
  ) {
    Operations.getOpRegistry().register(
      handler as any,
      operation,
      target,
      propKey
    );
  }
}