agenda/AgendaUnit.js

/**
 * @module flitter-agenda/AgendaUnit 
 */


const Unit = require('libflitter/Unit')
const Agenda = require('agenda')
const path = require('path')
const rra = require('recursive-readdir-async')

/**
 * Unit to start the Agenda module. 
 * Registers & starts the scheduler and loads jobs from the specified directory.
 * @extends module:libflitter/Unit~Unit
 */
class AgendaUnit extends Unit {

    /**
     * Initialize the class.
     * Resolves and stores the directory containing the job definition files.
     * @param {string} directory - Directory containing the job definition files.
     */
    constructor(directory = './app/jobs'){
        super()

        /**
         * Fully-qualified path to the job files directory.
         * 
         * @type {string}
         * @name AgendaUnit#directory
         */
        this.directory = path.resolve(directory)
    }
    
    /**
     * Initialize the unit. 
     * Creates a new instance of the Agenda scheduler and registers the job files with it.
     * The scheduler is then started.
     * @param {module:libflitter/app/FlitterApp~FlitterApp} app
     * @param {module:libflitter/Context~Context} context
     * @returns {Promise<void>}
     */
    async go(app, context){

        /*
         * Create a new instance of the Agenda scheduler.
         */
        const sched = new Agenda({}).mongo(app.d.database.connection)
        
        context.bind('scheduler', sched)
        app.global.bind('scheduler', sched)
        
        
        /*
         * Load the jobs from the definition files.
         */
        await this.load_jobs(sched, app, context)
        
        await sched.start()
    }
    
    
    /**
     * Loads the job definition classes from files in this.directory 
     * and registers them with the scheduler.
     * @param {Agenda} sched - the Agenda scheduler
     * @param {module:libflitter/app/FlitterApp~FlitterApp} app
     * @param {module:libflitter/Context~Context} context
     * @returns {Promise<void>}
     */
    async load_jobs(sched, app, context){
        
        /*
         * Recursively iterate over the files in this.directory
         * and load the job definition classes from files that end
         * with '.job.js'.
         */
        const files = await rra.list(this.directory)
        let jobs = {}
        
        app.d.utility.log("Job definitions directory: "+this.directory, 2)
            
        /*
         * Iterate over the files by name.
         */
        for (let key in files){
            
            if ( files[key].fullname ){
                /*
                 * Only attempt to load a job from files ending with '.job.js'.
                 */
                if ( files[key].fullname.endsWith('.job.js') ){

                    /*
                     * Parse the name of the job from the file name,
                     * taking into account sub-directories (delineated with ':')
                     */
                    let name = files[key].fullname
                        .replace(this.directory, '')
                        .replace(/.job.js/g, '')
                        .replace(/\//g, ':')
                        .substr(1)

                    /*
                     * Create an instance of the job class,
                     * store it so it can be accessed globally,
                     * and register it with the scheduler.
                     */
                    let job_class = require(files[key].fullname)
                    jobs[name] = new job_class()
                    sched.define(name, jobs[name].exec)

                    context.bind('jobs', jobs)
                }
            }
        }
        
    }

    /**
     * Returns the name of the AgendaUnit.
     * 
     * @returns {string} "sched"
     */
    name(){
        return "sched"
    }

    /**
     * Returns the directories managed by the AgendaUnit.
     * @returns {{jobs: AgendaUnit#directory}}
     */
    directories() {
        return {
            jobs: this.directory,
        }
    }

    /**
     * Returns the templates managed by the AgendaUnit.
     * @returns {{job: {template: ((function(*): string)|*), extension: '.job.js', directory: AgendaUnit#directory}}}
     */
    templates(){
        return {
            job: {
                template: require('./templates/Job'),
                directory: this.directory,
                extension: '.job.js',
            },
        }
    }
    
}

module.exports = exports = AgendaUnit