BaseModule.ts 4.3 KB

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