import * as Agenda from 'agenda'; import * as winston from 'winston'; import { Dao, Job, Service } from './entities'; import { MultivestError } from './error'; import { PluginOptions } from './model'; import { Plugin } from './plugin'; import { Constructable, Hashtable } from './structure'; export class PluginManager { // @ts-ignore protected jobExecutor: Agenda = null; private plugins: Hashtable> = {}; private pluginsOrdered: Array> = []; private jobs: Hashtable = {}; private daos: Hashtable> = {}; private services: Hashtable = {}; constructor(private pluginList: Array = []) { const myFormat = winston.format.printf((info) => { return `${info.timestamp} ${info.level}: ${info.message}`; }); winston.configure({ format: winston.format.combine( winston.format.timestamp(), myFormat ), transports: [ new winston.transports.Console() ] }); winston.debug('creating PluginManager'); process.on('unhandledRejection', (err) => { winston.error('unhandledRejection', err); throw err; }); process.on('uncaughtException', (err) => { winston.error('uncaughtException', err); throw err; }); } public setJobExecutor(agenda: Agenda) { this.jobExecutor = agenda; } public getJobExecutor() { return this.jobExecutor; } public getJob(jobId: string) { return this.jobs[jobId]; } public getDao(daoId: string) { return this.daos[daoId]; } public registerService(service: Service) { this.services[service.getServiceId()] = service; } public getService(serviceId: string): T { return this.services[serviceId] as T; } public getServicesByPath(path: string): Array { const ids = Object.keys(this.services); const filteredServices: Array = []; for (const id of ids) { if (id.match(path)) { const service = this.services[id]; filteredServices.push(service); } } return filteredServices; } public getServicesTableByPath(path: string): Hashtable { const ids = Object.keys(this.services); const filteredServices: Hashtable = {}; for (const id of ids) { if (id.match(path)) { const service = this.services[id]; filteredServices[id] = service; } } return filteredServices; } public async enableJob(jobId: string, interval: string) { if (!Object.prototype.hasOwnProperty.call(this.jobs, jobId)) { throw new MultivestError(`PluginManager: Unknown job ${jobId}`); } if (this.jobs[jobId].enabled) { return; } await this.jobs[jobId].init(); this.jobExecutor.every(interval, jobId); this.jobs[jobId].enabled = true; } public get>(pluginId: string): T { if (Object.prototype.hasOwnProperty.call(this.plugins, pluginId)) { return this.plugins[pluginId] as T; } throw new MultivestError(`Unknown plugin ${pluginId}`); } public async init() { winston.debug('PluginManager init called'); const startTime = new Date().getTime(); try { for (const pluginOptions of this.pluginList) { winston.debug(`loading plugin ${pluginOptions.path}`); try { let pluginInstance; if (pluginOptions && pluginOptions.path) { const PluginClass = require(pluginOptions.path).Plugin; const PluginConstructor = PluginClass as Constructable>; pluginInstance = new PluginConstructor(this); } else if (pluginOptions && pluginOptions.pluginClass) { pluginInstance = new pluginOptions.pluginClass(this); } // @ts-ignore this.plugins[pluginInstance.getPluginId()] = pluginInstance; this.pluginsOrdered.push(pluginInstance); } catch (error) { winston.error(`PluginManager: Failed to load plugin ${pluginOptions.path}`, error); process.exit(1); } } for (const plugin of this.pluginsOrdered) { await plugin.init(); // registerService(), servicesClasses.push(ServiceConstructor) } for (const plugin of this.pluginsOrdered) { plugin.invoke(); this.jobs = Object.assign(this.jobs, plugin.getJobs()); this.daos = Object.assign(this.daos, await plugin.getDaos()); this.services = Object.assign(this.services, plugin.getServices()); // authService, userService ... } for (const serviceId in this.services) { if (this.services[serviceId]) { await this.services[serviceId].init(); // userService calls authService } } const endTime = new Date().getTime(); winston.info(`PluginManager: Launched for ${endTime - startTime} ms`); } catch (error) { winston.error('PluginManager: Failed to launch', error); } } }