libflitter/database/DatabaseUnit.js

/**
 * @module libflitter/database/DatabaseUnit
 */

const path = require('path')
const Unit = require('../Unit')
const ScaffoldService = require('flitter-orm/src/services/Connection')

/**
 * The database unit loads the model schemas from the specified directory
 * and creates Mongoose models from them. Then, using the specified values
 * it connects to the MongoDB database and continues initializing the stack
 * from within the context of a guaranteed connection to the database.
 *
 * @extends module:libflitter/Unit~Unit
 */
class DatabaseUnit extends Unit {
    /**
     * Defines the services required by this unit.
     * @returns {Array<string>}
     */
    static get services() {
        return [...super.services, 'output', 'configs']
    }

    /**
     * Gets the name of the service provided by this unit: 'database'
     * @returns {string} - 'database'
     */
    static get name() {
        return 'database'
    }

    /**
     * Return the services provided by this unit. In this case, the ORM
     * scaffold service is registered.
     * @return {Array<string>}
     */
    static get provides() {
        return ['scaffold']
    }

    /**
     * Instantiate the unit. Resolve the path to the directory with the model definition schemata.
     * @param {string} [models_directory = './app/models'] - path to the directory with the model definition schemata
     */
    constructor(models_directory = './app/models'){
        super()

        /**
         * The fully-qualified path to the directory with the model definition schemata.
         * @type {string}
         * @name DatabaseUnit#directory
         */
        this.directory = path.resolve(models_directory)
    }

    /**
     * Get the standard formatted MongoDB connect string using the configured information.
     * e.g. 'mongodb://user:pass@host:port/db' &c.
     * @returns {string}
     */
    get connect_string() {
        /*
         * Get the database information from the config.
         */
        const db_host = this.configs.get('database.host')
        const db_port = this.configs.get('database.port')
        const db_name = this.configs.get('database.name')

        const db_proto = this.configs.get('database.protocol') || 'mongodb'
        const db_flags = this.configs.get('database.connect_flags') || ''

        /*
         * Form the connect string, which is the address of
         * the MongoDB server including the name of the
         * database and authentication, if it has been specified.
         */
        let connect_string = ""
        if ( this.configs.get('database.auth.require') === 'true' ){
            const db_user = this.configs.get('database.auth.username')
            const db_pass = this.configs.get('database.auth.password')
            connect_string = `${db_proto}://${db_user}:${db_pass}@${db_host}${db_port ? ':' + db_port : ''}/${db_name}${db_flags}`
        }
        else {
            connect_string = `${db_proto}://${db_host}${db_port ? ':' + db_port : ''}/${db_name}${db_flags}`
        }

        return connect_string
    }

    /**
     * Loads the unit. Loads the model definition schemata from {@link module:libflitter/database/DatabaseUnit~DatabaseUnit#directory}
     * and creates Mongoose models from them. Then, it binds the models, schemata, and helper functions to the appropriate
     * contexts. Opens the database connection using the values configured in the 'database' configuration.
     * @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){
        const connect_string = this.connect_string
        this.output.debug('Database connection string: '+connect_string)

        // Register the ORM service into the DI container
        app.di().container.register_service(ScaffoldService.name, ScaffoldService)

        // Get an instance of the service
        const scaffold = app.di().get(ScaffoldService.name)
        await scaffold.open(connect_string)
    }

    /**
     * Cleans up the unit's resources before Flitter exits. Closes the MongoDB connection cleanly.
     * @param {module:libflitter/app/FlitterApp~FlitterApp} app - the Flitter app
     * @returns {Promise<void>}
     */
    async cleanup(app){
        const scaffold = app.di().get(ScaffoldService.name)
        await scaffold.close()
    }

    /**
     * Get the directories provided by this unit.
     * Currently, {@link module:libflitter/database/DatabaseUnit~DatabaseUnit#directory}
     * @returns {{models: string}}
     */
    directories() {
        return {
            models: this.directory,
        }
    }

    /**
     * Get the templates provided by the unit.
     * Currently, "model" provided by {@link module:libflitter/templates/model}
     * @returns {{model: {template: (model|(function(string): string)|*), extension: string, directory: string}}}
     */
    templates(){
        return {
            model: {
                template: require('../templates/model'),
                directory: this.directory,
                extension: '.model.js'
            }
        }
    }


    /**
     * Get the fully-qualified path to the migrations provided by this unit.
     * @returns {string}
     */
    migrations(){
        return path.resolve(__dirname, 'migrations')
    }
}

module.exports = DatabaseUnit