forms/FormsUnit.js

/**
 * @module flitter-forms/FormsUnit
 */

const Unit = require('libflitter/Unit')
const recursive = require('recursive-readdir')
const rra = require('recursive-readdir-async')
const path = require('path')
const Validator = require('./Validator')
const ncp = require('ncp')

/**
 * Unit to register functionality provided by flitter-forms.
 * 
 * @extends module:libflitter/Unit~Unit
 */
class FormsUnit extends Unit {
    
    /**
     * Instantiates the class. Resolves the path to the schemata folder.
     * @param {string} [directory = './app/validators'] - path to the directory where validator definition schemata are located.
     */
    constructor(directory = "./app/validators"){
        super()

        /**
         * Fully-qualified path to the directory where validator definition schemata are located.
         * @name FormsUnit#directory
         * @type {string}
         */
        this.directory = path.resolve(directory)
    }

    /**
     * Loads the unit. Imports the validators in {@link module:flitter-forms/FormsUnit~FormsUnit#directory} and registers them by name.
     * Also binds validators, schemata and helper functions to the appropriate contexts.
     * @param {module:libflitter/app/FlitterApp~FlitterApp} app - the Flitter app
     * @param {module:libflitter/Context~Context} context - the Unit's context
     * @returns {Promise<void>}
     */
    async go(app, context){
        
        /*
         * Get, recursively, files in the specified directory.
         * That is, get all the files that should contain
         * validation schemas from a directory.
         */
        const files = await rra.list(this.directory)
    
        app.d.utility.log("Validators directory: "+this.directory, 3)
        const validators = {}
        
        /*
         * Iterate over the files.
         */
        for (let key in files){
            
            /*
             * Parse the name of the validator from the file name.
             * e.g.
             *      auth/Login.validator.js -> "auth:Login"
             */
            if ( files[key].fullname ){
                let name = files[key].fullname
                    .replace(this.directory, '')
                    .replace(/.validator.js/g, '')
                    .replace(/\//g, ':')
                    .substr(1)
                

                /*
                 * Create a new validator from the schema in the file.
                 */
                validators[name] = new Validator(name, require(files[key].fullname))
            }
        }
        
        context.bind('validators', validators)
        
        app.d.utility.log("Created validators from schema definitions.", 3)
        app.d.utility.log(validators, 4)
        
        context.bind('getvalidator', this.validator)
        app.global.bind('validator', this.validator)
        
        context.bind('init', this.init)
        app.global.bind('init_form', this.init)
    }

    /**
     * Get a registered Validator instance by name. Only works when bound to the FlitterApp.
     * @param {string} name - name of the validator to retrieve. should be a Flitter canonical name.
     * @returns {module:flitter-forms/Validator~Validator}
     */
    validator(name){
        return this.d.forms.validators[name]
    }

    /**
     * Initialize a validator by creating a new error session with the validator's name in the request's session data.
     * @param {module:flitter-forms/Validator~Validator} validator - the Validator instance to be initialized
     * @param {Express/Request} request - the Express request
     */
    init(validator, request){

        /*
         * Create the forms object in the session,
         * if it doesn't already exist.
         */
        if ( ! ('forms' in request.session) ){
            request.session.forms = {}
        }

        if ( ! ( validator.name in request.session.forms ) ) {
            request.session.forms[validator.name] = {
                data: {},
                errors: {}
            }
        }
        else {
            if ( !( 'data' in request.session.forms[validator.name] ) ){
                request.session.forms[validator.name].data = {}
            }

            if ( !( 'errors' in request.session.forms[validator.name] ) ){
                request.session.forms[validator.name].errors = {}
            }
        }
        
    }
    
    /**
     * Deploy the sample files provided by this unit. Creates the "app/validators" directory and an example validator
     * schema.
     * @returns {Promise<void>}
     */
    async deploy(){
        
        /*
         * Root directory of the flitter-forms package.
         */
        const package_dir = __dirname
        
        /*
         * Root directory of the Flitter app.
         */
        
        _flitter.log("Forms deploy from: "+package_dir, 2)
        _flitter.log("To: "+this.directory, 2)

        function do_copy(from, to){
            return new Promise(
                (resolve, reject) => {
                    ncp(from, to, (error) => {
                        if ( error ) reject(error)

                        resolve()
                    })
                }
            )
        }
        
        /*
         * Copy the template validators form over to the validators/ directory.
         */
        await do_copy(path.resolve(package_dir+"/deploy/validators"), this.directory)
    }

    /**
     * Get the name of this unit.
     * @returns {string} "forms"
     */
    name(){
        return "forms"
    }

    /**
     * Get the directories managed by this unit.
     * @returns {{validators: FormsUnit#directory}}
     */
    directories() {
        return {
            validators: this.directory
        }
    }

    /**
     * Get the templates provided by this unit.
     * @returns {Object}
     */
    templates(){
        return {
            validator: {
                template: require('flitter-forms/templates/validator'),
                directory: this.directory,
                extension: '.validator.js'
            }
        }
    }
}

module.exports = exports = FormsUnit