All files / src/Http HttpService.ts

91.48% Statements 43/47
71.42% Branches 5/7
100% Functions 10/10
90.9% Lines 40/44

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 10739x 39x                 39x   39x 39x 39x   39x   39x           39x             72x 72x 72x 72x 72x   72x   72x 23x   23x 23x           23x   72x 72x       28x   28x 28x 12x         54x       53x       78x       50x       6x 18x   18x         70x 286x 286x   19x   19x 19x   19x                
import * as OpenApiValidator from 'express-openapi-validator';
import {
  CardanoNodeUtil,
  HealthCheckResponse,
  HttpProviderConfigPaths,
  Provider,
  ProviderError,
  ProviderFailure,
  providerFailureToStatusCodeMap
} from '@cardano-sdk/core';
import { HttpServer } from './HttpServer';
import { Logger } from 'ts-log';
import { ProviderHandler, providerHandler } from '../util';
import { RunnableModule } from '@cardano-sdk/util';
import { versionPathFromSpec } from '../util/openApi';
import express, { Router } from 'express';
import path from 'path';
 
const openApiOption = {
  ignoreUndocumented: true,
  validateRequests: true,
  validateResponses: process.env.NODE_ENV !== 'production'
};
 
export abstract class HttpService extends RunnableModule {
  public router: express.Router;
  public slug: string;
  public provider: Provider;
  public openApiPath: string;
 
  constructor(slug: string, provider: Provider, router: express.Router, openApiPath: string, logger: Logger) {
    super(slug, logger);
    this.router = router;
    this.slug = slug;
    this.provider = provider;
    this.openApiPath = path.join(openApiPath, 'openApi.json');
 
    router.use(OpenApiValidator.middleware({ apiSpec: this.openApiPath, ...openApiOption }));
 
    const healthHandler = async (_: express.Request, res: express.Response) => {
      logger.debug('/health');
      let body: HealthCheckResponse | Error['message'];
      try {
        body = await this.healthCheck();
      } catch (error) {
        logger.error(error);
        body = CardanoNodeUtil.isProviderError(error) ? error.message : 'Unknown error';
        res.statusCode = 500;
      }
      res.send(body);
    };
    this.router.get('/health', healthHandler);
    this.router.post('/health', healthHandler);
  }
 
  protected async initializeImpl(): Promise<void> {
    if (this.provider instanceof RunnableModule) await this.provider.initialize();
 
    const health = await this.healthCheck();
    if (!health.ok) {
      this.logger.warn('Service started in unhealthy state');
    }
  }
 
  protected async startImpl(): Promise<void> {
    if (this.provider instanceof RunnableModule) await this.provider.start();
  }
 
  protected async shutdownImpl(): Promise<void> {
    if (this.provider instanceof RunnableModule) await this.provider.shutdown();
  }
 
  async healthCheck(): Promise<HealthCheckResponse> {
    return await this.provider.healthCheck();
  }
 
  apiVersionPath(): string {
    return versionPathFromSpec(this.openApiPath);
  }
 
  attachProviderRoutes<T extends Provider>(provider: T, router: Router, paths: HttpProviderConfigPaths<T>) {
    for (const methodName in paths) {
      const handler = providerHandler((provider[methodName] as Function).bind(provider));
 
      router.post(paths[methodName], handler(HttpService.routeHandler(this.logger), this.logger));
    }
  }
 
  static routeHandler(logger: Logger): ProviderHandler {
    return async (args, _r, res, _n, handler) => {
      try {
        return HttpServer.sendJSON(res, await handler(args));
      } catch (error) {
        logger.error(error);
 
        if (CardanoNodeUtil.isProviderError(error)) {
          const code = providerFailureToStatusCodeMap[error.reason];
 
          return HttpServer.sendJSON(res, error, code);
        }
 
        return HttpServer.sendJSON(res, new ProviderError(ProviderFailure.Unhealthy, error), 500);
      }
    };
  }
}