/* Imports: Internal */ import { Logger } from '../common/logger' import { LegacyMetrics } from '../common/metrics' type OptionSettings = { [P in keyof TOptions]?: { default?: TOptions[P] validate?: (val: any) => boolean } } type BaseServiceOptions = T & { logger?: Logger metrics?: LegacyMetrics } /** * Base for other "Service" objects. Handles your standard initialization process, can dynamically * start and stop. */ export class BaseService { protected name: string protected options: T protected logger: Logger protected metrics: LegacyMetrics protected initialized = false protected running = false constructor( name: string, options: BaseServiceOptions, optionSettings: OptionSettings ) { validateOptions(options, optionSettings) this.name = name this.options = mergeDefaultOptions(options, optionSettings) this.logger = options.logger || new Logger({ name }) if (options.metrics) { this.metrics = options.metrics } } /** * Initializes the service. */ public async init(): Promise { if (this.initialized) { return } this.logger.info('Service is initializing...') await this._init() this.logger.info('Service has initialized.') this.initialized = true } /** * Starts the service (initializes it if needed). */ public async start(): Promise { if (this.running) { return } this.logger.info('Service is starting...') await this.init() // set the service to running this.running = true await this._start() this.logger.info('Service has started') } /** * Stops the service. */ public async stop(): Promise { if (!this.running) { return } this.logger.info('Service is stopping...') await this._stop() this.logger.info('Service has stopped') this.running = false } /** * Internal init function. Parent should implement. */ protected async _init(): Promise { return } /** * Internal start function. Parent should implement. */ protected async _start(): Promise { return } /** * Internal stop function. Parent should implement. */ protected async _stop(): Promise { return } } /** * Combines user provided and default options. */ const mergeDefaultOptions = ( options: T, optionSettings: OptionSettings ): T => { for (const optionName of Object.keys(optionSettings)) { const optionDefault = optionSettings[optionName].default if (optionDefault === undefined) { continue } if (options[optionName] !== undefined && options[optionName] !== null) { continue } options[optionName] = optionDefault } return options } /** * Performs option validation against the option settings */ const validateOptions = (options: T, optionSettings: OptionSettings) => { for (const optionName of Object.keys(optionSettings)) { const optionValidationFunction = optionSettings[optionName].validate if (optionValidationFunction === undefined) { continue } const optionValue = options[optionName] if (optionValidationFunction(optionValue) === false) { throw new Error( `Provided input for option "${optionName}" is invalid: ${optionValue}` ) } } }