All files / src/Program/programs projector.ts

36.11% Statements 13/36
0% Branches 0/12
0% Functions 0/2
34.28% Lines 12/35

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 112 113 114 115 116 117 118 119 120 121 122 123 124 125    40x 40x         40x   40x 40x   40x 40x 40x   40x 40x                                                   40x                                                 40x                                                                                                              
import { Cardano } from '@cardano-sdk/core';
import { CommonProgramOptions, OgmiosProgramOptions, PosgresProgramOptions } from '../options';
import { DnsResolver, createDnsResolver } from '../utils';
import {
  HandlePolicyIdsOptionDescriptions,
  HandlePolicyIdsProgramOptions,
  handlePolicyIdsFromFile
} from '../options/policyIds';
import { HttpServer, HttpServerConfig } from '../../Http';
import { Logger } from 'ts-log';
import { MissingProgramOption, UnknownServiceName } from '../errors';
import { ProjectionHttpService, ProjectionName, createTypeormProjection, storeOperators } from '../../Projection';
import { SrvRecord } from 'dns';
import { createLogger } from 'bunyan';
import { createStorePoolMetricsUpdateJob, createStoreStakePoolMetadataJob } from '@cardano-sdk/projection-typeorm';
import { getConnectionConfig, getOgmiosObservableCardanoNode } from '../services';
 
export const BLOCKS_BUFFER_LENGTH_DEFAULT = 10;
export const PROJECTOR_API_URL_DEFAULT = new URL('http://localhost:3002');
 
export type ProjectorArgs = CommonProgramOptions &
  PosgresProgramOptions<''> &
  HandlePolicyIdsProgramOptions &
  OgmiosProgramOptions & {
    blocksBufferLength: number;
    dropSchema: boolean;
    dryRun: boolean;
    exitAtBlockNo: Cardano.BlockNo;
    metadataJobRetryDelay: number;
    poolsMetricsInterval: number;
    projectionNames: ProjectionName[];
    synchronize: boolean;
  };
export interface LoadProjectorDependencies {
  dnsResolver?: (serviceName: string) => Promise<SrvRecord>;
  logger?: Logger;
}
 
interface ProjectionMapFactoryOptions {
  args: ProjectorArgs;
  dnsResolver: DnsResolver;
  logger: Logger;
}
 
const createProjectionHttpService = async (options: ProjectionMapFactoryOptions) => {
  const { args, dnsResolver, logger } = options;
  storeOperators.storePoolMetricsUpdateJob = createStorePoolMetricsUpdateJob(args.poolsMetricsInterval)();
  storeOperators.storeStakePoolMetadataJob = createStoreStakePoolMetadataJob(args.metadataJobRetryDelay)();
  const cardanoNode = getOgmiosObservableCardanoNode(dnsResolver, logger, {
    ogmiosSrvServiceName: args.ogmiosSrvServiceName,
    ogmiosUrl: args.ogmiosUrl
  });
  const connectionConfig$ = getConnectionConfig(dnsResolver, 'projector', '', args);
  const { blocksBufferLength, dropSchema, dryRun, exitAtBlockNo, handlePolicyIds, projectionNames, synchronize } = args;
  const projection$ = createTypeormProjection({
    blocksBufferLength,
    cardanoNode,
    connectionConfig$,
    devOptions: { dropSchema, synchronize },
    exitAtBlockNo,
    logger,
    projectionOptions: {
      handlePolicyIds
    },
    projections: projectionNames
  });
  return new ProjectionHttpService({ dryRun, projection$, projectionNames }, { logger });
};
 
export const loadProjector = async (args: ProjectorArgs, deps: LoadProjectorDependencies = {}): Promise<HttpServer> => {
  const supportedProjections = Object.values(ProjectionName);
 
  await handlePolicyIdsFromFile(args);
 
  const {
    apiUrl,
    buildInfo,
    enableMetrics,
    handlePolicyIds,
    loggerMinSeverity,
    projectionNames,
    serviceDiscoveryBackoffFactor,
    serviceDiscoveryTimeout
  } = args;
 
  for (const projectionName of projectionNames) {
    Iif (!supportedProjections.includes(projectionName)) {
      throw new UnknownServiceName(projectionName, Object.values(ProjectionName));
    }
    Iif (projectionName === ProjectionName.Handle && !handlePolicyIds) {
      throw new MissingProgramOption(ProjectionName.Handle, [
        HandlePolicyIdsOptionDescriptions.HandlePolicyIds,
        HandlePolicyIdsOptionDescriptions.HandlePolicyIdsFile
      ]);
    }
  }
  const logger =
    deps?.logger ||
    createLogger({
      level: loggerMinSeverity,
      name: 'projector'
    });
  const dnsResolver =
    deps?.dnsResolver ||
    createDnsResolver(
      {
        factor: serviceDiscoveryBackoffFactor,
        maxRetryTime: serviceDiscoveryTimeout
      },
      logger
    );
  const service = await createProjectionHttpService({ args, dnsResolver, logger });
  const config: HttpServerConfig = {
    listen: {
      host: apiUrl.hostname,
      port: apiUrl ? Number.parseInt(apiUrl.port) : undefined
    },
    meta: { ...buildInfo, startupTime: Date.now() }
  };
  Iif (enableMetrics) {
    config.metrics = { enabled: enableMetrics };
  }
  return new HttpServer(config, { logger, services: [service] });
};