BaseModule.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. import JobContext from "./JobContext";
  2. import JobQueue from "./JobQueue";
  3. import LogBook, { Log } from "./LogBook";
  4. import ModuleManager from "./ModuleManager";
  5. import { Modules } from "./types/Modules";
  6. export enum ModuleStatus {
  7. LOADED = "LOADED",
  8. STARTING = "STARTING",
  9. STARTED = "STARTED",
  10. STOPPED = "STOPPED",
  11. STOPPING = "STOPPING",
  12. ERROR = "ERROR",
  13. DISABLED = "DISABLED"
  14. }
  15. export default abstract class BaseModule {
  16. protected _moduleManager: ModuleManager;
  17. protected _logBook: LogBook;
  18. protected _jobQueue: JobQueue;
  19. protected _name: string;
  20. protected _status: ModuleStatus;
  21. protected _dependentModules: (keyof Modules)[];
  22. protected _jobApiDefault: boolean;
  23. protected _jobConfig: Record<
  24. string,
  25. | "disabled"
  26. | boolean
  27. | ((context: JobContext, payload?: any) => Promise<any>)
  28. | {
  29. api?: boolean;
  30. method?: (context: JobContext, payload?: any) => Promise<any>;
  31. }
  32. >;
  33. protected _jobs: Record<
  34. string,
  35. {
  36. api: boolean;
  37. method: (context: JobContext, payload?: any) => Promise<any>;
  38. }
  39. >;
  40. /**
  41. * Base Module
  42. *
  43. * @param name - Module name
  44. */
  45. public constructor(name: string) {
  46. this._moduleManager = ModuleManager.getPrimaryInstance();
  47. this._logBook = LogBook.getPrimaryInstance();
  48. this._jobQueue = JobQueue.getPrimaryInstance();
  49. this._name = name;
  50. this._status = ModuleStatus.LOADED;
  51. this._dependentModules = [];
  52. this._jobApiDefault = true;
  53. this._jobConfig = {};
  54. this._jobs = {};
  55. this.log(`Module (${this._name}) loaded`);
  56. }
  57. /**
  58. * getName - Get module name
  59. *
  60. * @returns name
  61. */
  62. public getName() {
  63. return this._name;
  64. }
  65. /**
  66. * getStatus - Get module status
  67. *
  68. * @returns status
  69. */
  70. public getStatus() {
  71. return this._status;
  72. }
  73. /**
  74. * setStatus - Set module status
  75. *
  76. * @param status - Module status
  77. */
  78. public setStatus(status: ModuleStatus) {
  79. this._status = status;
  80. }
  81. /**
  82. * getDependentModules - Get module dependencies
  83. */
  84. public getDependentModules() {
  85. return this._dependentModules;
  86. }
  87. /**
  88. * _loadJobs - Load jobs available via api module
  89. */
  90. private async _loadJobs() {
  91. this._jobs = {};
  92. const module = Object.getPrototypeOf(this);
  93. await Promise.all(
  94. Object.getOwnPropertyNames(module).map(async property => {
  95. if (
  96. typeof module[property] !== "function" ||
  97. Object.prototype.hasOwnProperty.call(
  98. BaseModule.prototype,
  99. property
  100. ) ||
  101. property.startsWith("_")
  102. )
  103. return;
  104. const options = this._jobConfig[property];
  105. let api = this._jobApiDefault;
  106. if (
  107. typeof options === "object" &&
  108. typeof options.api === "boolean"
  109. )
  110. api = options.api;
  111. else if (typeof options === "boolean") api = options;
  112. this._jobs[property] = {
  113. api,
  114. method: module[property]
  115. };
  116. })
  117. );
  118. await Promise.all(
  119. Object.entries(this._jobConfig).map(async ([name, options]) => {
  120. if (options === "disabled") {
  121. if (this._jobs[name]) delete this._jobs[name];
  122. return;
  123. }
  124. if (
  125. typeof options === "boolean" ||
  126. (typeof options === "object" &&
  127. typeof options.method !== "function")
  128. )
  129. return;
  130. if (this._jobs[name])
  131. throw new Error(`Job "${name}" is already defined`);
  132. let api = this._jobApiDefault;
  133. if (
  134. typeof options === "object" &&
  135. typeof options.api === "boolean"
  136. )
  137. api = options.api;
  138. let method = options;
  139. if (
  140. typeof method === "object" &&
  141. typeof method.method === "function"
  142. )
  143. method = method.method;
  144. if (typeof method !== "function")
  145. throw new Error(
  146. `Job "${name}" has no function method defined`
  147. );
  148. this._jobs[name] = {
  149. api,
  150. method
  151. };
  152. })
  153. );
  154. }
  155. /**
  156. * getJob - Get module job
  157. */
  158. public getJob(name: string) {
  159. if (!this._jobs[name]) throw new Error(`Job "${name}" not found.`);
  160. return this._jobs[name];
  161. }
  162. /**
  163. * getJobs - Get module jobs
  164. */
  165. public getJobs() {
  166. return this._jobs;
  167. }
  168. /**
  169. * startup - Startup module
  170. */
  171. public async startup() {
  172. this.log(`Module (${this._name}) starting`);
  173. this.setStatus(ModuleStatus.STARTING);
  174. }
  175. /**
  176. * started - called with the module has started
  177. */
  178. protected async _started() {
  179. await this._loadJobs();
  180. this.log(`Module (${this._name}) started`);
  181. this.setStatus(ModuleStatus.STARTED);
  182. }
  183. /**
  184. * shutdown - Shutdown module
  185. */
  186. public async shutdown() {
  187. this.log(`Module (${this._name}) stopping`);
  188. this.setStatus(ModuleStatus.STOPPING);
  189. }
  190. /**
  191. * stopped - called when the module has stopped
  192. */
  193. protected async _stopped() {
  194. this.log(`Module (${this._name}) stopped`);
  195. this.setStatus(ModuleStatus.STOPPED);
  196. }
  197. /**
  198. * log - Add log to logbook
  199. *
  200. * @param log - Log message or object
  201. */
  202. public log(log: string | Omit<Log, "timestamp" | "category">) {
  203. const {
  204. message,
  205. type = undefined,
  206. data = {}
  207. } = {
  208. ...(typeof log === "string" ? { message: log } : log)
  209. };
  210. this._logBook.log({
  211. message,
  212. type,
  213. category: `modules.${this.getName()}`,
  214. data: {
  215. moduleName: this._name,
  216. ...data
  217. }
  218. });
  219. }
  220. }