UNPKG

pg-transactional-outbox

Version:

A PostgreSQL based transactional outbox and inbox pattern implementation to support exactly once message processing (with at least once message delivery).

174 lines (162 loc) 5.16 kB
/** The node.js environment variable interface */ export interface Env { [key: string]: string | undefined; } /** * Get a string from the environment variable. * @throws Error if the variable is not found or empty and no default value was provided. */ export const getEnvVariableString = ( env: Env, field: string, fallbackField: string, defaultValue?: string, ): string => { const value = env[field] ?? env[fallbackField]; if (typeof value !== 'string' || value === '') { if (defaultValue) { return defaultValue; } throw new Error( `The environment variable ${field} must be a non-empty string.`, ); } return value; }; /** * Get a string from the environment variable. * @throws Error if the variable is not found or empty and no default value was provided. */ export const getEnvVariableNumber = ( env: Env, field: string, fallbackField: string, defaultValue?: number, ): number => { const value = Number( getEnvVariableString(env, field, fallbackField, `${defaultValue}`), ); if (Number.isNaN(value)) { if (defaultValue) { return defaultValue; } throw new Error(`The environment variable ${field} must be a number.`); } return value; }; /** * Get a boolean from the environment variable. The true/1 value return true, false/0 return false. Everything else throws an error. * @throws Error if the variable is not found or empty and no default value was provided. */ export const getEnvVariableBoolean = ( env: Env, field: string, fallbackField: string, defaultValue?: boolean, ): boolean => { const value = getEnvVariableString( env, field, fallbackField, `${defaultValue}`, ).toLowerCase(); if (value === 'true' || value === '1') { return true; } if (value === 'false' || value === '0') { return false; } if (defaultValue) { return defaultValue; } throw new Error(`The environment variable ${field} must be a number.`); }; /** * Transforms a constant formatted value to snake case formatted one * @param constantStr The CONSTANT_VALUE to convert to constantValue * @returns the formatted value */ export const constantToCamel = (constantStr: string): string => { return constantStr .toLowerCase() .replace(/_([a-z0-9])/g, (_, char) => char.toUpperCase()); }; export interface StringSetting { constantName: string; default: string; func: typeof getEnvVariableString; skipFallback?: boolean; description: string; } export interface NumberSetting { constantName: string; default: number; func: typeof getEnvVariableNumber; skipFallback?: boolean; description: string; } export interface BooleanSetting { constantName: string; default: boolean; func: typeof getEnvVariableBoolean; skipFallback?: boolean; description: string; } /** * Loads the configuration settings from the ENV variables into the settings object. * @param map A mapping of all the env variables to config settings. * @param envPrefix The prefix for the env variables to check first (e.g. "TRX_OUTBOX_" or "TRX_INBOX_"). * @param envPrefixFallback The fallback prefix if the other is not found. Useful for defining settings that should be used for both outbox and inbox. * @param env The process.env variable or a custom object * @returns The parsed configuration object. */ export const getConfigSettings = ( map: (StringSetting | NumberSetting | BooleanSetting)[], envPrefix: string, envPrefixFallback: string, env: Env = process.env, ): Record<string, string | number | boolean> => { const settings: Record<string, string | number | boolean> = {}; for (const s of map) { const key = constantToCamel(s.constantName); settings[key] = s.func( env, `${envPrefix}${s.constantName}`, `${envPrefixFallback}${s.constantName}`, s.default as any, ); } return settings; }; /** * Shows the available env variables and their default values. * @param map A mapping of all the env variables to config settings. * @param envPrefix The prefix for the env variables to check first (e.g. "TRX_OUTBOX_" or "TRX_INBOX_"). * @param envPrefixFallback The fallback prefix if the other is not found. Useful for defining settings that should be used for both outbox and inbox. * @param defaultOverrides Default values for the overrides. * @returns A string with all the ENV config keys and their default values. */ export const getConfigSettingsEnvTemplate = ( map: (StringSetting | NumberSetting | BooleanSetting)[], envPrefix: string, envPrefixFallback: string, defaultOverrides?: Record<string, string>, ): string => { let result = ''; for (const s of map) { const val = defaultOverrides?.[`${envPrefix}${s.constantName}`] ?? defaultOverrides?.[s.constantName] ?? s.default; const commentKey = s.skipFallback ? `${envPrefix}${s.constantName}` : `${envPrefixFallback}${s.constantName}`; const quotedVal = typeof val === 'string' ? `"${val}"` : val; result += `# | ${commentKey} | ${typeof val} | ${quotedVal} | ${ s.description } | ${commentKey}=${val} `; } return result; };