src/common/configuration.ts

Summary

Maintainability
A
0 mins
Test Coverage
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars, @typescript-eslint/no-var-requires */
import { readFileSync } from "fs";
import * as path from "path";

require("dotenv").config();

export interface Configuration {
  ci: string;
  env_config_test: string;
  env_not_included_config_test: string;
  default_config_test: string;
  local_config_test: string;
  environment_config_test: string;

  // package config
  ib_host: string;
  ib_port: number;
  ib_test_account: string;
  default_client_id: number;
  client_version: number;
  max_req_per_second: number;

  environment: string;
  isProduction: boolean;
  isStaging: boolean;
  isDevelopment: boolean;
  isTest: boolean;
  isLocal: boolean;
}

let configuration: Configuration = null;

const envsToInclude = [
  "ci",
  "env_config_test",
  "ib_host",
  "ib_port",
  "ib_test_account",
  "client_version",
  "max_req_per_second",
];

function readJson(readPath: string) {
  try {
    const data = readFileSync(readPath, "utf8");
    return JSON.parse(data);
  } catch (error) {
    if (
      error.code !== "ENOENT" ||
      (error.errno !== undefined && error.errno !== -4058 && error.errno !== -2)
    ) {
      throw error;
    }
  }
  return {};
}

function read(file: string): Configuration {
  const filePath = path.resolve(
    __dirname,
    "..",
    "..",
    "config",
    `${file}.json`
  );
  return readJson(filePath);
}

function assignEnvironment(config: any) {
  const newConfig = config;
  envsToInclude.forEach((key) => {
    const lc = key.toLowerCase();
    const uc = key.toUpperCase();
    newConfig[lc] = process.env[uc] || config[lc];
  });
  return newConfig;
}

function loadEnvironmentSpecific(config: Configuration, environment: string) {
  let newConfig = config;
  if (environment) {
    const conf = read(environment);
    if (conf) {
      newConfig = {
        ...newConfig,
        ...conf,
      };
    }
  }
  return newConfig;
}

const ensureInteger = (
  fields: (keyof Configuration)[],
  config: Configuration
) =>
  fields.forEach((field) => {
    const value = config[field];
    if (typeof value === "string") {
      config[field] = parseInt(value, 10) as never;
    }
  });

function load() {
  const nodeEnvironment = process.env.NODE_ENV;

  // load default config
  let config = read("default");

  // load local config
  config = loadEnvironmentSpecific(config, "local");

  // load environment specific config
  config = loadEnvironmentSpecific(config, nodeEnvironment);

  // load config from env variables
  config = assignEnvironment(config);

  config.environment = nodeEnvironment || "local";
  config.isProduction = nodeEnvironment === "production";
  config.isStaging = nodeEnvironment === "staging";
  config.isDevelopment = nodeEnvironment === "development";
  config.isTest = nodeEnvironment === "test";
  config.isLocal = !nodeEnvironment;

  config.ci = config.ci || process.env.CIRCLECI;

  const intFields: (keyof Configuration)[] = [
    "ib_port",
    "default_client_id",
    "client_version",
    "max_req_per_second",
  ];
  ensureInteger(intFields, config);

  return config;
}

export function get() {
  if (!configuration) {
    configuration = load();
  }
  return configuration;
}

configuration = load();
export default configuration;