UNPKG

antfarm

Version:

File automation framework for ants.

256 lines (219 loc) 7.17 kB
import { FolderNest } from "../nest/folderNest"; import { Nest } from "../nest/nest"; import { Job } from "../job/job"; import { Environment } from "../environment/environment"; import {WebhookNest} from "../nest/webhookNest"; import {FtpNest} from "../nest/ftpNest"; const async = require("async"), mm = require("micromatch"); /** * Tunnels are runnable work flow units that can watch nests. */ export class Tunnel { protected _name: string; protected _nests: Nest[]; protected _run_list: any[]; protected _run_sync_list: any[]; protected _run_fail: any; protected e: Environment; protected job_counter: number; protected match_obj = { queue: [], run: null, pattern: null, orphan_minutes: null }; constructor(e: Environment, tunnelName: string) { this.e = e; this._nests = []; this._name = tunnelName; this._run_list = []; this._run_sync_list = []; this.job_counter = 0; } public toString() { return "Tunnel"; } public get name() { return this._name; } public set name(name: string) { this._name = name; } public get nests() { return this._nests; } public get runList() { return this._run_list; } public get runSyncList() { return this._run_sync_list; } public get runFail() { return this._run_fail; } public set runFail(callback: any) { this._run_fail = callback; } /** * Instructs the tunnel to watch a nest for new jobs. * @param nest */ public watch(nest: FolderNest | WebhookNest | FtpNest) { nest.register(this); nest.load(); nest.watch(); this.nests.push(nest); } public arrive(job: Job, nest?: Nest) { this.job_counter++; this.e.log(1, `Job ${this.job_counter} triggered tunnel arrive.`, this, [job, nest]); this.executeRun(job, nest); this.executeRunSync(job, nest); this.executeMatch(job, nest); } /** * Run program logic asynchronously. * @param callback */ public run(callback) { this.runList.push(callback); } /** * Run program logic synchronously. * @param callback */ public runSync(callback) { this.runSyncList.push(callback); } /** * Failed jobs runner. * @param callback */ public fail(callback) { this.runFail = callback; } /** * Asynchronous run event. * @param job * @param nest */ protected executeRun(job: Job, nest: Nest) { let tn = this; if (tn.runList.length > 0) { tn.runList.forEach((callback) => { try { callback(job, nest); } catch (e) { // Fail if an error is thrown tn.executeFail(job, nest, e); } }); } } /** * Synchronous run event. * @param job * @param nest */ protected executeRunSync(job: Job, nest: Nest) { let tn = this; let breakFailure = false; let successfulRuns = 0; if (tn.runSyncList.length > 0) { async.eachSeries(tn.runSyncList, (run, doNextRun) => { if (breakFailure === false) { run(job, nest, () => { successfulRuns++; doNextRun(); }); } }, (err) => { if (err) { breakFailure = true; tn.executeFail(job, nest, err); } tn.e.log(0, `Completed ${successfulRuns}/${tn.runSyncList.length} synchronous run list(s).`, tn, [job, nest]); }); } } /** * Fail run event. * @param job * @param nest * @param reason */ public executeFail(job: Job, nest: Nest, reason: string) { let tn = this; tn.e.log(3, `Failed for reason "${reason}".`, tn, [job, nest]); let failCallback = tn.runFail; failCallback(job, nest, reason); } /** * Interface for matching two or more _files together based on an array of glob filename patterns. * @param pattern * @param orphanMinutes * @param callback */ public match(pattern: string[]|string, orphanMinutes: number, callback: any) { this.match_obj.pattern = pattern; this.match_obj.orphan_minutes = orphanMinutes; this.match_obj.run = callback; } /** * Match execution * @param job * @param nest */ protected executeMatch(job: Job, nest: Nest) { if (this.match_obj.run) { // Try to find match in queue let tn = this; let qjob_pattern_match_result, job_pattern_match_result; let matched_jobs = []; let job_base, qjob_base; tn.e.log(0, "Executing matching process.", tn); tn.match_obj.pattern.forEach(function (pattern, i) { job_pattern_match_result = mm.isMatch(job.name, pattern); if (job_pattern_match_result === true) { job_base = job.name.substr(0, job.name.indexOf(pattern.replace("*", ""))); } }); tn.match_obj.queue.slice().reverse().forEach(function (qJob, qIndex, qObject) { tn.match_obj.pattern.forEach(function (pattern) { qjob_pattern_match_result = mm.isMatch(qJob.name, pattern); if (qjob_pattern_match_result === true) { qjob_base = qJob.name.substr(0, qJob.name.indexOf(pattern.replace("*", ""))); if (job_base === qjob_base) { // Pull out qjob from queue tn.match_obj.queue.splice(qObject.length - 1 - qIndex, 1); matched_jobs.push(qJob); return; } } }); }); // if found if (matched_jobs.length > 0) { matched_jobs.push(job); tn.e.log(0, `Matched ${matched_jobs.length} jobs.`, tn); tn.match_obj.run(matched_jobs); } else { // If not found, add this job to the queue tn.match_obj.queue.push(job); } // Need to set a timeout for the job to fail setTimeout(function () { if (tn.match_obj.queue.length !== 0) { // Fail all jobs in the queue tn.match_obj.queue.forEach(function (fJob) { fJob.fail("Orphan timeout."); }); // Wipe the queue tn.e.log(0, `Orphan timeout executed on ${tn.match_obj.queue.length} jobs.`, tn); tn.match_obj.queue = []; } }, tn.match_obj.orphan_minutes * 60000); } } }