123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- import BaseModule from "./BaseModule";
- import JobContext from "./JobContext";
- import JobStatistics from "./JobStatistics";
- import LogBook, { Log } from "./LogBook";
- import ModuleManager from "./ModuleManager";
- import { JobOptions } from "./types/JobOptions";
- import { Modules } from "./types/Modules";
- export enum JobStatus {
- QUEUED = "QUEUED",
- ACTIVE = "ACTIVE",
- PAUSED = "PAUSED",
- COMPLETED = "COMPLETED"
- }
- export default class Job {
- protected name: string;
- protected module: Modules[keyof Modules];
- protected jobFunction: any;
- protected payload: any;
- protected context: JobContext;
- protected priority: number;
- protected longJob?: {
- title: string;
- progress?: {
- data: unknown;
- time: Date;
- timeout?: NodeJS.Timeout;
- };
- };
- protected uuid: string;
- protected status: JobStatus;
- protected createdAt: number;
- protected startedAt?: number;
- protected completedAt?: number;
- protected logBook: LogBook;
- protected jobStatistics: JobStatistics;
- /**
- * Job
- *
- * @param name - Job name
- * @param module - Job module
- * @param callback - Job callback
- * @param options - Job options
- */
- public constructor(
- name: string,
- moduleName: keyof Modules,
- payload: any,
- options?: Omit<JobOptions, "runDirectly">
- ) {
- this.name = name;
- this.priority = 1;
- const module = ModuleManager.getPrimaryInstance().getModule(moduleName);
- if (!module) throw new Error("Module not found.");
- this.module = module;
- // eslint-disable-next-line
- // @ts-ignore
- const jobFunction = this.module[this.name];
- if (!jobFunction || typeof jobFunction !== "function")
- throw new Error("Job not found.");
- if (Object.prototype.hasOwnProperty.call(BaseModule, this.name))
- throw new Error("Illegal job function.");
- this.jobFunction = jobFunction;
- this.payload = payload;
- this.context = new JobContext(this);
- this.logBook = LogBook.getPrimaryInstance();
- this.jobStatistics = JobStatistics.getPrimaryInstance();
- this.jobStatistics.updateStats(this.getName(), "added");
- if (options) {
- const { priority, longJob } = options;
- if (priority) this.priority = priority;
- if (longJob)
- this.longJob = {
- title: longJob
- };
- }
- /* eslint-disable no-bitwise, eqeqeq */
- this.uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
- /[xy]/g,
- c => {
- const r = (Math.random() * 16) | 0;
- const v = c == "x" ? r : (r & 0x3) | 0x8;
- return v.toString(16);
- }
- );
- this.status = JobStatus.QUEUED;
- this.createdAt = Date.now();
- }
- /**
- * getName - Get module and job name in a dot format, e.g. module.jobName
- *
- * @returns module.name
- */
- public getName() {
- return `${this.module.getName()}.${this.name}`;
- }
- /**
- * getPriority - Get job priority
- *
- * @returns priority
- */
- public getPriority() {
- return this.priority;
- }
- /**
- * getUuid - Get job UUID
- *
- * @returns UUID
- */
- public getUuid() {
- return this.uuid;
- }
- /**
- * getStatus - Get job status
- *
- * @returns status
- */
- public getStatus() {
- return this.status;
- }
- /**
- * setStatus - Set job status
- *
- * @param status - Job status
- */
- protected setStatus(status: JobStatus) {
- this.status = status;
- }
- /**
- * getModule - Get module
- *
- * @returns module
- */
- public getModule() {
- return this.module;
- }
- /**
- * execute - Execute job
- *
- * @returns Promise
- */
- public async execute() {
- if (this.startedAt) throw new Error("Job has already been executed.");
- this.setStatus(JobStatus.ACTIVE);
- this.startedAt = Date.now();
- return (
- this.jobFunction
- .apply(this.module, [this.context, this.payload])
- // eslint-disable-next-line
- // @ts-ignore
- .then(response => {
- this.log({
- message: "Job completed successfully",
- type: "success"
- });
- this.jobStatistics.updateStats(
- this.getName(),
- "successful"
- );
- return response;
- })
- .catch((err: any) => {
- this.log({
- message: `Job failed with error "${err}"`,
- type: "error"
- });
- this.jobStatistics.updateStats(this.getName(), "failed");
- throw err;
- })
- .finally(() => {
- this.completedAt = Date.now();
- this.jobStatistics.updateStats(this.getName(), "total");
- if (this.startedAt)
- this.jobStatistics.updateStats(
- this.getName(),
- "averageTime",
- this.completedAt - this.startedAt
- );
- this.setStatus(JobStatus.COMPLETED);
- })
- );
- }
- /**
- * Log a message in the context of the current job, which automatically sets the category and data
- *
- * @param log - Log message or object
- */
- public log(log: string | Omit<Log, "timestamp" | "category">) {
- const {
- message,
- type = undefined,
- data = {}
- } = {
- ...(typeof log === "string" ? { message: log } : log)
- };
- this.logBook.log({
- message,
- type,
- category: this.getName(),
- data: {
- ...this.toJSON(),
- ...data
- }
- });
- }
- /**
- * Serialize job info
- *
- * @returns json
- */
- public toJSON() {
- return {
- uuid: this.getUuid(),
- priority: this.getPriority(),
- name: this.getName(),
- status: this.getStatus(),
- moduleStatus: this.module.getStatus(),
- createdAt: this.createdAt,
- startedAt: this.startedAt,
- completedAt: this.completedAt
- };
- }
- }
|