core.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. const async = require("async");
  2. class DeferredPromise {
  3. constructor() {
  4. this.promise = new Promise((resolve, reject) => {
  5. this.reject = reject;
  6. this.resolve = resolve;
  7. });
  8. }
  9. }
  10. class MovingAverageCalculator {
  11. constructor() {
  12. this.count = 0;
  13. this._mean = 0;
  14. }
  15. update(newValue) {
  16. this.count++;
  17. const differential = (newValue - this._mean) / this.count;
  18. this._mean += differential;
  19. }
  20. get mean() {
  21. this.validate();
  22. return this._mean;
  23. }
  24. validate() {
  25. if (this.count === 0) throw new Error("Mean is undefined");
  26. }
  27. }
  28. class CoreClass {
  29. constructor(name) {
  30. this.name = name;
  31. this.status = "UNINITIALIZED";
  32. // this.log("Core constructor");
  33. this.jobQueue = async.priorityQueue(
  34. (job, callback) => this._runJob(job, callback),
  35. 10 // How many jobs can run concurrently
  36. );
  37. this.jobQueue.pause();
  38. this.runningJobs = [];
  39. this.priorities = {};
  40. this.stage = 0;
  41. this.jobStatistics = {};
  42. this.registerJobs();
  43. }
  44. setStatus(status) {
  45. this.status = status;
  46. this.log("INFO", `Status changed to: ${status}`);
  47. if (this.status === "READY") this.jobQueue.resume();
  48. else if (this.status === "FAIL" || this.status === "LOCKDOWN")
  49. this.jobQueue.pause();
  50. }
  51. getStatus() {
  52. return this.status;
  53. }
  54. setStage(stage) {
  55. this.stage = stage;
  56. }
  57. getStage() {
  58. return this.stage;
  59. }
  60. _initialize() {
  61. this.setStatus("INITIALIZING");
  62. this.initialize()
  63. .then(() => {
  64. this.setStatus("READY");
  65. this.moduleManager.onInitialize(this);
  66. })
  67. .catch((err) => {
  68. console.error(err);
  69. this.setStatus("FAILED");
  70. this.moduleManager.onFail(this);
  71. });
  72. }
  73. log() {
  74. let _arguments = Array.from(arguments);
  75. const type = _arguments[0];
  76. _arguments.splice(0, 1);
  77. const start = `|${this.name.toUpperCase()}|`;
  78. const numberOfTabsNeeded = 4 - Math.ceil(start.length / 8);
  79. _arguments.unshift(`${start}${Array(numberOfTabsNeeded).join("\t")}`);
  80. if (type === "INFO") {
  81. _arguments[0] = _arguments[0] + "\x1b[36m";
  82. _arguments.push("\x1b[0m");
  83. console.log.apply(null, _arguments);
  84. } else if (type === "ERROR") {
  85. _arguments[0] = _arguments[0] + "\x1b[31m";
  86. _arguments.push("\x1b[0m");
  87. console.error.apply(null, _arguments);
  88. }
  89. }
  90. registerJobs() {
  91. let props = [];
  92. let obj = this;
  93. do {
  94. props = props.concat(Object.getOwnPropertyNames(obj));
  95. } while ((obj = Object.getPrototypeOf(obj)));
  96. const jobNames = props
  97. .sort()
  98. .filter(
  99. (prop) =>
  100. typeof this[prop] == "function" &&
  101. prop === prop.toUpperCase()
  102. );
  103. jobNames.forEach((jobName) => {
  104. this.jobStatistics[jobName] = {
  105. successful: 0,
  106. failed: 0,
  107. total: 0,
  108. averageTiming: new MovingAverageCalculator(),
  109. };
  110. });
  111. }
  112. runJob(name, payload, options = {}) {
  113. let deferredPromise = new DeferredPromise();
  114. const job = { name, payload, onFinish: deferredPromise };
  115. if (options.bypassQueue) {
  116. this._runJob(job, () => {});
  117. } else {
  118. const priority = this.priorities[name] ? this.priorities[name] : 10;
  119. this.jobQueue.push(job, priority);
  120. }
  121. return deferredPromise.promise;
  122. }
  123. setModuleManager(moduleManager) {
  124. this.moduleManager = moduleManager;
  125. }
  126. _runJob(job, cb) {
  127. this.log("INFO", `Running job ${job.name}`);
  128. const startTime = Date.now();
  129. this.runningJobs.push(job);
  130. const newThis = Object.assign(
  131. Object.create(Object.getPrototypeOf(this)),
  132. this
  133. );
  134. newThis.runJob = (...args) => {
  135. if (args.length === 2) args.push({});
  136. args[2].bypassQueue = true;
  137. return this.runJob.apply(this, args);
  138. };
  139. this[job.name]
  140. .apply(newThis, [job.payload])
  141. .then((response) => {
  142. this.log("INFO", `Ran job ${job.name} successfully`);
  143. this.jobStatistics[job.name].successful++;
  144. job.onFinish.resolve(response);
  145. })
  146. .catch((error) => {
  147. this.log("INFO", `Running job ${job.name} failed`);
  148. this.jobStatistics[job.name].failed++;
  149. job.onFinish.reject(error);
  150. })
  151. .finally(() => {
  152. const endTime = Date.now();
  153. const executionTime = endTime - startTime;
  154. this.jobStatistics[job.name].total++;
  155. this.jobStatistics[job.name].averageTiming.update(
  156. executionTime
  157. );
  158. this.runningJobs.splice(this.runningJobs.indexOf(job), 1);
  159. cb();
  160. });
  161. }
  162. }
  163. module.exports = CoreClass;