import { InternalService } from "./InternalService"; import assert from "assert"; /** * Application InternalService map */ interface InternalServiceMap { [name: string]: InternalService } /** * The InternalService constructor type */ type InternalServiceConstructor = new (app: T) => InternalService; /** * Microservice states */ export enum MicroserviceState { Idling, Booting, Starting, Running, Quitting, Finished } /** * The main application class */ export class Microservice { /** * Maintain a static reference to the application instance */ private static __instance: Microservice; /** * A handler function to quit the microservice application */ private __quitHandler!: (value: number | PromiseLike) => void; /** * All available services */ protected services: InternalServiceMap = {}; /** * The current state of the microservice */ protected state: MicroserviceState; /** * Return the current application instance */ public static instance() { return this.__instance; } /** * Create a new application instance */ public constructor() { Microservice.__instance = this; this.state = MicroserviceState.Idling; } // Overridable -------------------------------------------------------------------------------- /** * Invoked when the application has finished booting */ protected onStart(): void|Promise {} // Application Management ---------------------------------------------------------------------- /** * Boot the application and all of the services */ protected async boot() { let InternalServices = Object.values(this.services); return Promise.all(InternalServices.map(InternalService => InternalService.boot())); } /** * Shutdown the application */ protected shutdown() { let InternalServices = Object.values(this.services); return Promise.all(InternalServices.map(InternalService => InternalService.shutdown())); } /** * Start the application */ public async exec() { // Exit if not in an idling state if (this.state !== MicroserviceState.Idling) { return -1; } try { // Boot the microservice this.state = MicroserviceState.Booting; await this.boot(); // Start the microservice this.state = MicroserviceState.Starting await this.onStart(); for (let InternalService of Object.values(this.services)) { InternalService.start(); } } catch(e) { console.error("Failed to start the microservice:", e); return 1; } // Run the microservice this.state = MicroserviceState.Running; process.on("SIGINT", this.quit.bind(this)); let exitCode = await new Promise((resolve) => this.__quitHandler = resolve); // Shutdown the microservice await this.shutdown().catch(() => { console.log("Error ocurred during shutdown..."); exitCode = 1; }); // Return the exit code return exitCode; } /** * Quit the application */ public async quit(code: number = 0) { if (this.state !== MicroserviceState.Running) { return; } this.__quitHandler(code); } // InternalService Management -------------------------------------------------------------------------- /** * Install InternalServices into the application */ public installServices(this: T, InternalServices: InternalServiceConstructor[]) { for (let InternalServiceClass of InternalServices) { this.installService(InternalServiceClass); } } /** * Install a InternalService into the application */ public installService(this: T, InternalServiceClass: InternalServiceConstructor) { let InternalService = new InternalServiceClass(this); this.services[InternalService.NAME] = InternalService; } /** * Get all available services */ public serviceList() { return Object.keys(this.services); } /** * Get an application services instance */ public service>(InternalServiceName: string) { assert(InternalServiceName in this.services); return this.services[InternalServiceName]; } }