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  }