libflitter/database/DatabaseModelsUnit.js

/**
 * @module libflitter/database/DatabaseModelsUnit
 */

const CanonicalUnit = require('../canon/CanonicalUnit')
const Model = require('flitter-orm/src/model/Model')
const Setting = require('./model/Setting.model')
const ScaffoldService = require('flitter-orm/src/services/Connection')
const SoftError = require('../errors/SoftError')

/**
 * Unit to load and manage database models.
 * @extends module:libflitter/canon/CanonicalUnit~CanonicalUnit
 */
class DatabaseModelsUnit extends CanonicalUnit {
    /**
     * Defines the services required by this unit.
     * @returns {Array<string>}
     */
    static get services() {
        return [...super.services, 'app', 'output', 'database']
    }

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

    /**
     * Instantiate the unit.
     * @param {string} [base_directory = './app/models'] - base directory to search
     */
    constructor(base_directory = './app/models') {
        super(base_directory)

        /**
         * The canonical name of the item.
         * @type {string} = 'model'
         */
        this.canonical_item = 'model'

        /**
         * The file extension of the canonical item files.
         * @type {string} - '.model.js'
         */
        this.suffix = '.model.js'
    }

    /**
     * Prepare a single canonical model and return the value that should be given by the resolver.
     * @param {object} info
     * @param {module:libflitter/app/FlitterApp} info.app
     * @param {string} info.name - the unqualified canonical name
     * @param {*} info.instance - the static model CLASS from the file
     * @returns {Promise<*>}
     */
    async init_canonical_file({app, name, instance}) {
        // return this.register_model(name, instance)
        if ( instance.prototype instanceof Model ) {
            instance.collection = name.replace(/:/g, '_')
            return instance
        } else {
            this.output.warn(`Invalid or malformed model ${name}. Models should extend from flitter-orm/src/model/Model.`)
        }
    }

    /**
     * Initializes the unit. Ensures that the Flitter-ORM scaffold service exists.
     * @param {module:libflitter/app/FlitterApp~FlitterApp} app - the Flitter app
     * @returns {Promise<void>}
     */
    async go(app) {
        if ( !app.di().has(ScaffoldService.name) ) {
            throw (new SoftError('Models are not supported unless the flitter-orm scaffold system is provided.')).unit(this.constructor.name)
        }

        await super.go(app)
        this.external_model(this, 'setting', Setting)
    }

    /**
     * Register an external model provided by a unit.
     * @param {libflitter/Unit~Unit} unit - the parent unit of the model
     * @param {string} name - the name of the model
     * @param {libflitter/database/Model~Model|object} instance - model class
     * @returns {string} - the flitter canonical name of the registered model
     */
    external_model(unit, name, instance) {
        name = `${unit.name()}::${name}`
        this.output.info(`Registering external model: ${name}`)
        if ( instance.prototype instanceof Model ) {
            instance.collection = name.replace(/:/g, '_')
            this.canonical_items[name] = this.app.di().inject(instance)
        } else {
            this.output.warn(`Invalid or malformed model ${name}. Models should extend from flitter-orm/src/model/Model.`)
        }

        return name
    }

    /**
     * Helper function to get the Mongoose model instance by its Flitter canonical name.
     * This is usually bound to the relevant {@link module:libflitter/app/FlitterApp~FlitterApp} instance.
     * @param {string} name - the Flitter canonical name of the model
     * @returns {Mongoose/Model}
     */
    model(name) {
        return this.canonical_items[name]
    }
}

module.exports = exports = DatabaseModelsUnit