github.com/ethereum-optimism/optimism@v1.7.2/packages/common-ts/src/base-service/base-service.ts (about) 1 /* Imports: Internal */ 2 import { Logger } from '../common/logger' 3 import { LegacyMetrics } from '../common/metrics' 4 5 type OptionSettings<TOptions> = { 6 [P in keyof TOptions]?: { 7 default?: TOptions[P] 8 validate?: (val: any) => boolean 9 } 10 } 11 12 type BaseServiceOptions<T> = T & { 13 logger?: Logger 14 metrics?: LegacyMetrics 15 } 16 17 /** 18 * Base for other "Service" objects. Handles your standard initialization process, can dynamically 19 * start and stop. 20 */ 21 export class BaseService<T> { 22 protected name: string 23 protected options: T 24 protected logger: Logger 25 protected metrics: LegacyMetrics 26 protected initialized = false 27 protected running = false 28 29 constructor( 30 name: string, 31 options: BaseServiceOptions<T>, 32 optionSettings: OptionSettings<T> 33 ) { 34 validateOptions(options, optionSettings) 35 this.name = name 36 this.options = mergeDefaultOptions(options, optionSettings) 37 this.logger = options.logger || new Logger({ name }) 38 if (options.metrics) { 39 this.metrics = options.metrics 40 } 41 } 42 43 /** 44 * Initializes the service. 45 */ 46 public async init(): Promise<void> { 47 if (this.initialized) { 48 return 49 } 50 51 this.logger.info('Service is initializing...') 52 await this._init() 53 this.logger.info('Service has initialized.') 54 this.initialized = true 55 } 56 57 /** 58 * Starts the service (initializes it if needed). 59 */ 60 public async start(): Promise<void> { 61 if (this.running) { 62 return 63 } 64 this.logger.info('Service is starting...') 65 await this.init() 66 67 // set the service to running 68 this.running = true 69 await this._start() 70 this.logger.info('Service has started') 71 } 72 73 /** 74 * Stops the service. 75 */ 76 public async stop(): Promise<void> { 77 if (!this.running) { 78 return 79 } 80 81 this.logger.info('Service is stopping...') 82 await this._stop() 83 this.logger.info('Service has stopped') 84 this.running = false 85 } 86 87 /** 88 * Internal init function. Parent should implement. 89 */ 90 protected async _init(): Promise<void> { 91 return 92 } 93 94 /** 95 * Internal start function. Parent should implement. 96 */ 97 protected async _start(): Promise<void> { 98 return 99 } 100 101 /** 102 * Internal stop function. Parent should implement. 103 */ 104 protected async _stop(): Promise<void> { 105 return 106 } 107 } 108 109 /** 110 * Combines user provided and default options. 111 */ 112 const mergeDefaultOptions = <T>( 113 options: T, 114 optionSettings: OptionSettings<T> 115 ): T => { 116 for (const optionName of Object.keys(optionSettings)) { 117 const optionDefault = optionSettings[optionName].default 118 if (optionDefault === undefined) { 119 continue 120 } 121 122 if (options[optionName] !== undefined && options[optionName] !== null) { 123 continue 124 } 125 126 options[optionName] = optionDefault 127 } 128 129 return options 130 } 131 132 /** 133 * Performs option validation against the option settings 134 */ 135 const validateOptions = <T>(options: T, optionSettings: OptionSettings<T>) => { 136 for (const optionName of Object.keys(optionSettings)) { 137 const optionValidationFunction = optionSettings[optionName].validate 138 if (optionValidationFunction === undefined) { 139 continue 140 } 141 142 const optionValue = options[optionName] 143 144 if (optionValidationFunction(optionValue) === false) { 145 throw new Error( 146 `Provided input for option "${optionName}" is invalid: ${optionValue}` 147 ) 148 } 149 } 150 }