All files / src/util/DbSyncProvider DbSyncProvider.ts

100% Statements 27/27
100% Branches 5/5
100% Functions 5/5
100% Lines 26/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 11251x   51x                                                     51x   51x                 59x         59x 59x 59x   59x 59x 59x 59x         29x 29x 29x 17x   29x 29x   17x     27x 27x         27x   27x           2x   29x                                         244x            
import { Cardano, CardanoNode, HealthCheckResponse, Provider, ProviderDependencies } from '@cardano-sdk/core';
import { InMemoryCache } from '../../InMemoryCache';
import { LedgerTipModel, findLedgerTip } from './util';
import { Logger } from 'ts-log';
import { Pool } from 'pg';
 
/** Dedicated DB pools */
export interface DbPools {
  /** Main operational db pool */
  main: Pool;
  /** Secondary health check db pool */
  healthCheck: Pool;
}
 
/** Properties that are need to create DbSyncProvider */
export interface DbSyncProviderDependencies extends ProviderDependencies {
  /** Cache engines. Default: InMemoryCache with HealthCheck.cacheTTL as default TTL */
  cache: {
    healthCheck: InMemoryCache;
  };
  /** DB pools */
  dbPools: DbPools;
  /** Ogmios Cardano Node provider */
  cardanoNode: CardanoNode;
}
 
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type AnyArgs = any[];
 
export const findLedgerTipOptions = { name: 'find_ledger_tip', text: findLedgerTip } as const;
 
export const DbSyncProvider = <
  T extends (abstract new (...args: AnyArgs) => {}) | (new (...args: AnyArgs) => {}) = { new (): {} }
>(
  BaseClass?: T
) => {
  abstract class Mixin extends (BaseClass || Object) implements Provider {
    public dbPools: DbPools;
    public cardanoNode: CardanoNode;
    public logger: Logger;
    #cache: {
      healthCheck: InMemoryCache;
    };
 
    constructor(...args: AnyArgs) {
      const [dependencies, ...baseArgs] = [...args] as [DbSyncProviderDependencies, ...AnyArgs];
      const { cache, dbPools, cardanoNode, logger } = dependencies;
      super(...baseArgs);
 
      this.dbPools = dbPools;
      this.cardanoNode = cardanoNode;
      this.logger = logger;
      this.#cache = cache;
    }
 
    /** Healthy if the tip of both the node and database can be accessed. */
    public async healthCheck(): Promise<HealthCheckResponse> {
      const response: HealthCheckResponse = { ok: false };
      try {
        const cardanoNode = await this.#cache.healthCheck.get('node_health', async () =>
          this.cardanoNode.healthCheck()
        );
        response.localNode = cardanoNode.localNode;
        const tip = await this.#cache.healthCheck.get(
          'db_tip',
          async () => (await this.dbPools.healthCheck.query<LedgerTipModel>(findLedgerTipOptions)).rows[0]
        );
 
        if (tip) {
          response.projectedTip = {
            blockNo: Cardano.BlockNo(tip.block_no),
            hash: tip.hash.toString('hex') as unknown as Cardano.BlockId,
            slot: Cardano.Slot(Number(tip.slot_no))
          };
          response.ok = cardanoNode.ok && !!tip?.block_no;
 
          this.logger.debug(
            `Service /health: projected block tip: ${tip.block_no},local node block tip: ${cardanoNode.localNode?.ledgerTip?.blockNo}.`
          );
        }
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
      } catch (error: any) {
        this.logger.error(error.message);
      }
      return response;
    }
  }
 
  type BaseArgs = T extends abstract new (...baseArgs: infer A) => {}
    ? A
    : T extends new (...baseArgs: infer A) => {}
    ? A
    : never;
  type BaseInstance = T extends abstract new (...baseArgs: AnyArgs) => infer I
    ? I
    : T extends new (...baseArgs: AnyArgs) => infer I
    ? I
    : never;
  type ReturnedType = BaseInstance & {
    dbPools: DbPools;
    cardanoNode: CardanoNode;
    logger: Logger;
    healthCheck: () => Promise<HealthCheckResponse>;
  };
 
  return Mixin as unknown as (T extends new (...baseArgs: AnyArgs) => {}
    ? new (dependencies: DbSyncProviderDependencies, ...args: BaseArgs) => ReturnedType
    : abstract new (dependencies: DbSyncProviderDependencies, ...args: BaseArgs) => ReturnedType) & {
    prototype: { healthCheck: () => Promise<HealthCheckResponse> };
  };
};