UNPKG

actionhero

Version:

The reusable, scalable, and quick node.js API server for stateless and stateful applications

189 lines (162 loc) 5.41 kB
import * as path from "path"; import { Plugin } from "node-resque"; import * as TaskModule from "./../modules/task"; import { api, config, log, utils, task, Initializer } from "../index"; import { Task } from "../classes/task"; import { safeGlobSync } from "../modules/utils/safeGlob"; const taskModule = task; export interface TaskApi { tasks: { [key: string]: Task }; jobs: { [key: string]: any }; middleware: { [key: string]: TaskModule.task.TaskMiddleware }; globalMiddleware: Array<string>; loadFile: TasksInitializer["loadFile"]; jobWrapper: TasksInitializer["jobWrapper"]; loadTasks: TasksInitializer["loadTasks"]; } /** * Tools for enqueuing and inspecting the task system (delayed jobs). */ export class TasksInitializer extends Initializer { constructor() { super(); this.name = "tasks"; this.loadPriority = 699; this.startPriority = 975; } loadFile = async (fullFilePath: string, reload = false) => { let task; let collection = await import(fullFilePath); for (const i in collection) { const TaskClass = collection[i]; task = new TaskClass(); task.validate(); if (api.tasks.tasks[task.name] && !reload) { log( `an existing task with the same name \`${task.name}\` will be overridden by the file ${fullFilePath}`, "crit", ); } api.tasks.tasks[task.name] = task; api.tasks.jobs[task.name] = api.tasks.jobWrapper(task.name); log( `task ${reload ? "(re)" : ""} loaded: ${task.name}, ${fullFilePath}`, "debug", ); } }; jobWrapper = (taskName: string) => { const task = api.tasks.tasks[taskName]; const middleware = task.middleware || []; const plugins = task.plugins || []; const pluginOptions = task.pluginOptions || {}; if (task.frequency > 0) { if (plugins.indexOf("JobLock") < 0) { plugins.push("JobLock"); pluginOptions.JobLock = { reEnqueue: false }; } if (plugins.indexOf("QueueLock") < 0) { plugins.push("QueueLock"); } if (plugins.indexOf("DelayQueueLock") < 0) { plugins.push("DelayQueueLock"); } } // load middleware into plugins const processMiddleware = (m: string) => { if (api.tasks.middleware[m]) { class NodeResquePlugin extends Plugin { constructor(...args: ConstructorParameters<typeof Plugin>) { super(...args); } async beforeEnqueue() { return api.tasks.middleware[m].preEnqueue ? api.tasks.middleware[m].preEnqueue.call(this) : true; } async afterEnqueue() { return api.tasks.middleware[m].postEnqueue ? api.tasks.middleware[m].postEnqueue.call(this) : true; } async beforePerform() { return api.tasks.middleware[m].preProcessor ? api.tasks.middleware[m].preProcessor.call(this) : true; } async afterPerform() { return api.tasks.middleware[m].postProcessor ? api.tasks.middleware[m].postProcessor.call(this) : true; } } //@ts-ignore plugins.push(NodeResquePlugin); } }; api.tasks.globalMiddleware.forEach(processMiddleware); middleware.forEach(processMiddleware); return { plugins, pluginOptions, perform: async function () { const combinedArgs = [].concat(Array.prototype.slice.call(arguments)); combinedArgs.push(this); let response = null; try { response = await task.run.apply(task, combinedArgs); await taskModule.enqueueRecurrentTask(taskName); } catch (error) { if (task.frequency > 0 && task.reEnqueuePeriodicTaskIfException) { await taskModule.enqueueRecurrentTask(taskName); } throw error; } return response; }, }; }; loadTasks = async (reload: boolean) => { for (const p of config.general.paths.task) { await Promise.all( utils .ensureNoTsHeaderOrSpecFiles( safeGlobSync(path.join(p, "**", "**/*(*.js|*.ts)")), ) .map((f) => api.tasks.loadFile(f, reload)), ); } for (const plugin of Object.values(config.plugins)) { if (plugin.tasks !== false) { const pluginPath = path.normalize(plugin.path); // old style at the root of the project let files = safeGlobSync(path.join(pluginPath, "tasks", "**", "*.js")); files = files.concat( safeGlobSync(path.join(pluginPath, "dist", "tasks", "**", "*.js")), ); utils.ensureNoTsHeaderOrSpecFiles(files).forEach((f) => { api.tasks.loadFile(f, reload); }); } } }; async initialize() { api.tasks = { tasks: {}, jobs: {}, middleware: {}, globalMiddleware: [], loadFile: this.loadFile, jobWrapper: this.jobWrapper, loadTasks: this.loadTasks, }; await api.tasks.loadTasks(false); // we want to start the queue now, so that it's available for other initializers and CLI commands await api.resque.startQueue(); } async start() { if (config.tasks.scheduler === true) { await taskModule.enqueueAllRecurrentTasks(); } } }