/**
* @module libflitter/middleware/MiddlewareUnit
*/
const CanonicalUnit = require('../canon/CanonicalUnit')
const Middleware = require('./Middleware')
const SoftError = require('../errors/SoftError')
const path = require('path')
const error_context = require('../errors/error_context.fn')
/**
* Unit to load and manage middleware class definitions.
* @extends module:libflitter/canon/CanonicalUnit~CanonicalUnit
*/
class MiddlewareUnit extends CanonicalUnit {
/**
* Defines the services required by this unit.
* @returns {Array<string>}
*/
static get services() {
return [...super.services, 'output']
}
/**
* Get the name of the service provided by this unit: 'middlewares'
* @returns {string} - 'middlewares'
*/
static get name() {
return 'middlewares'
}
/**
* Instantiate the unit.
* @param {string} [base_directory = './app/routing/middleware']
* @param {string} [globals_file = './app/routing/Middleware.js']
*/
constructor(base_directory = './app/routing/middleware', globals_file = './app/routing/Middleware.js') {
super(base_directory)
/**
* Fully-qualified path to the file with the definitions for globally-applied middleware.
* @type {Promise<void> | Promise<string>}
*/
this.globals_file = path.resolve(globals_file)
/**
* The canonical name of the item.
* @type {string} = 'middleware'
*/
this.canonical_item = 'middleware'
/**
* The file extension of the canonical item files.
* @type {string} - '.middleware.js'
*/
this.suffix = '.middleware.js'
}
/**
* Initializes the unit. Registers global middleware.
* @param {module:libflitter/app/FlitterApp~FlitterApp} app - the Flitter app
* @returns {Promise<void>}
*/
async go(app) {
await super.go(app)
// Register global middlewares
const globals = require(this.globals_file)
this.globals = globals
for ( const name of globals ) {
if ( !(name in this.canonical_items) ) {
throw (new SoftError(`Unknown middleware name in global Middleware.js file: ${name}`)).unit(this.constructor.name)
}
app.express.use(async (req, res, next, args = {}) => {
return await this.canonical_items[name].test(req, res, next, args)
})
}
}
/**
* Prepare a single canonical middleware definition 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 middleware CLASS from the file
* @returns {Promise<*>}
*/
async init_canonical_file({app, name, instance}) {
if ( instance.prototype instanceof Middleware ) {
this.output.debug('Registering middleware: '+name)
return new instance(app)
} else {
throw (new SoftError(`Invalid middleware class definition for ${name}. Middleware classes must extend libflitter/middleware/Middleware.`)).unit(this.constructor.name)
}
}
/**
* A helper function to return the Express middleware function for a registered middleware, using its Flitter canonical name.
* @param {string} name - the Flitter canonical name of the middleware whose handler should be returned
* @param {*} args - An argument or arguments to be passed to the middleware function as the 4th argument.
* @returns {function} - the Express middleware
*/
get(name, args) {
try {
if (!this.canonical_items[name]) {
throw (new SoftError('Attempted to access middleware that does not exist: ' + name)).unit(this.constructor.name)
}
return (req, res, next, merge_args) => {
let pass_args = typeof args === 'undefined' ? (merge_args ? merge_args : {}) : args
return this.canonical_items[name].test(req, res, next, pass_args)
}
} catch(e) {
throw error_context(e, {
resource_name: name,
})
}
}
/**
* Get the directories provided by this unit.
* {@link module:libflitter/middleware/MiddlewareUnit~MiddlewareUnit#directory} as "middleware".
* @returns {{middleware: string}}
*/
directories() {
return {
...super.directories(),
global_middleware: this.globals_file,
}
}
/**
* Get the templates provided by this unit.
* Currently, "middleware" template, using the generator {@link module:libflitter/templates/middleware}.
* @returns {{middleware: {template: (middleware|(function(string): string)|*), extension: string, directory: string}}}
*/
templates(){
return {
middleware: {
template: require('../templates/middleware'),
directory: this.directory,
extension: '.middleware.js'
}
}
}
/**
* Get the fully-qualified path to the migrations provided by this unit.
* @returns {string}
*/
migrations(){
return path.resolve(__dirname, 'migrations')
}
}
module.exports = exports = MiddlewareUnit