UNPKG

ksmf

Version:

Modular Microframework for create minimalistic CLI/Web application or REST API

343 lines (325 loc) 12.1 kB
/** * @author Antonio Membrides Espinosa * @email tonykssa@gmail.com * @date 07/03/2020 * @copyright Copyright (c) 2020-2030 * @license GPL * @version 1.4 **/ class Module { /** * @type {Object|null} */ helper = null; /** * @type {Object|null} */ app = null; /** * @type {Console|null} */ logger = null; /** * @type {String|null} */ type; /** * @description initialize module * @param {Object} payload * @param {Object} [payload.app] * @param {Object} [payload.web] * @param {Object} [payload.opt] */ constructor(payload) { this.configure(payload); } /** * @description initialize module * @param {Object} payload * @param {Object} [payload.app] * @param {Object} [payload.web] * @param {Object} [payload.drv] * @param {Object} [payload.opt] * @param {String} [payload.opt.name] * @param {String} [payload.opt.prefix] * @param {Array} [payload.opt.routes] * @param {Boolean} [payload.opt.rest] * @param {Boolean} [payload.rest] * @param {Object} [payload.server] * @param {Object} [payload.routes] * @param {Object} [payload.prefix] * @param {Object} [payload.middleware] * @returns {Module} self */ configure(payload) { this.app = payload?.app || this.app || null; this.opt = payload?.opt || this.opt || null; this.web = payload?.web || this.web || null; this.drv = payload?.drv || this.drv || null; this.name = this.opt?.name || ""; this.prefix = payload?.prefix || payload?.opt?.prefix || "/" + this.name; this.rest = payload?.rest ?? payload?.opt?.rest ?? this.rest ?? true; this.routes = payload?.routes || payload?.opt?.routes || this.routes || []; this.middleware = this.initMiddlewareList(payload?.middleware || this.middleware); return this; } /** * @description implement template method pattern */ init() { this.initConfig(); this.initApp(); this.initRoutes(); } /** * @description allow customized application initialization by module */ initApp() { // TODO document why this method 'initApp' is empty } /** * @description allow customized config initialization by module */ initConfig() { this.routes.push({ route: this.prefix, controller: 'DefaultController', path: 'controller' }); } /** * @description allow customized routes initialization by module */ initRoutes() { for (const i in this.routes) { const route = this.routes[i]; route.method = route.method || 'rest'; if (this.rest && route.method === 'rest') { this.initRouterREST(route); } this.initRouterWeb(route); } } /** * @description allow customized web routes initialization by module * @param {Object} opt * @param {String} [opt.route] * @param {String} [opt.action] * @param {String} [opt.name] * @param {String} [opt.controller] * @param {String} [opt.method] * @param {String} [opt.path] */ async initRouterWeb(opt) { if (typeof (this.app?.server?.set) !== 'function') return; // ... load controller const _locator = this.getLocator(opt); const _prefix = _locator?.route || opt?.route; const _controller = await this.getController(_locator); // ... define routes this.app.server.set({ method: opt.method, route: _prefix, middlewares: this.getMiddlewareList(_controller, opt) || [], handler: (req, res, next) => { const _action = _controller[opt?.action || opt?.method]; if (_action instanceof Function) { _action.apply(_controller, [req, res, next]); } } }); } /** * @description allow customized REST routes initialization by module * @param {Object} opt * @param {String} [opt.route] * @param {String} [opt.name] * @param {String} [opt.controller] * @param {String} [opt.path] */ async initRouterREST(opt) { if (!this.helper || typeof (this.app?.server?.set) !== 'function') { return null; } // ... load controller const _locator = this.getLocator(opt); const _prefix = _locator?.route || opt?.route; const _controller = await this.getController(_locator); if (!_controller) return null; // ... define route select this.app?.server.set({ method: 'get', route: (_prefix + '/:id').replace(/[\/\/]+/g, '/'), middlewares: this.getMiddlewareList(_controller, opt, 'select') || [], handler: (req, res, next) => _controller.select(req, res, next) }); // ... define route clone this.app?.server.set({ method: 'post', route: (_prefix + '/:id').replace(/[\/\/]+/g, '/'), middlewares: this.getMiddlewareList(_controller, opt, 'clone') || [], handler: (req, res, next) => _controller.clone(req, res, next) }); // ... define route update this.app?.server.set({ method: 'put', route: (_prefix + '/:id').replace(/[\/\/]+/g, '/'), middlewares: this.getMiddlewareList(_controller, opt, 'update') || [], handler: (req, res, next) => _controller.update(req, res, next) }); this.app?.server.set({ method: 'patch', route: (_prefix + '/:id').replace(/[\/\/]+/g, '/'), middlewares: this.getMiddlewareList(_controller, opt, 'update') || [], handler: (req, res, next) => _controller.update(req, res, next) }); // ... define route delete this.app?.server.set({ method: 'delete', route: (_prefix + '/:id').replace(/[\/\/]+/g, '/'), middlewares: this.getMiddlewareList(_controller, opt, 'delete') || [], handler: (req, res, next) => _controller.delete(req, res, next) }); // ... define route option this.app?.server.set({ method: 'options', route: (_prefix + '/:id').replace(/[\/\/]+/g, '/'), middlewares: this.getMiddlewareList(_controller, opt, 'option') || [], handler: (req, res, next) => _controller.option(req, res, next) }); // ... define route list this.app?.server.set({ method: 'get', route: _prefix, middlewares: this.getMiddlewareList(_controller, opt, 'list') || [], handler: (req, res, next) => _controller.list(req, res, next) }); // ... define route insert this.app?.server.set({ method: 'post', route: _prefix, middlewares: this.getMiddlewareList(_controller, opt, 'insert') || [], handler: (req, res, next) => _controller.insert(req, res, next) }); // ... define route options this.app?.server.set({ method: 'options', route: _prefix, middlewares: this.getMiddlewareList(_controller, opt, 'options') || [], handler: (req, res, next) => _controller.options(req, res, next) }); // ... define route clean this.app?.server.set({ method: 'delete', route: _prefix, middlewares: this.getMiddlewareList(_controller, opt, 'clean') || [], handler: (req, res, next) => _controller.clean(req, res, next) }); } /** * @description get IoC locator options * @param {Object} opt * @param {String} [opt.route] * @param {String} [opt.name] * @param {String} [opt.action] * @param {String} [opt.controller] * @param {String} [opt.path] * @param {Object} [opt.params] * @param {Object} [opt.method] * @param {Object} [opt.options] * @param {Object} [opt.delegate] * @param {Function} [opt.handler] * @param {Object} [opt.dependency] * @param {Boolean} [opt.strict] * @returns {Object} locator */ getLocator(opt) { opt = typeof opt === 'string' ? { name: opt } : opt; if (opt.strict) { return opt; } return { name: opt.controller, path: opt.path || 'controller', module: this.name, moduleType: this.type, delegate: opt.delegate || null, handler: opt.handler || null, method: opt.method || null, options: { opt: this.opt, module: this.name, ...opt.options, ...opt.params }, dependency: { 'helper': 'helper', 'app': 'app', ...opt.dependency } } } /** * @description get a controller instance * @param {Object} locator * @returns {Promise<Object>} controller */ async getController(locator) { if (locator?.delegate && typeof (locator?.delegate) === "object") { return locator?.delegate; } if (locator?.handler && typeof (locator?.handler) === "function") { return { [locator.action || locator.method]: locator?.handler } } return this.helper?.get instanceof Function && await this.helper.get(locator); } /** * @description get middleware list by controller * @param {Object} controller * @param {Object} opt * @param {String} action * @returns {Array} middlewares */ getMiddlewareList(controller, opt, action = null) { try { action = action || opt.action; if (opt.middleware && opt.method === 'rest') { controller.middleware = Object.assign( controller.middleware || {}, opt.middleware ); } const middlewareModule = this.initMiddlewareList(this.middleware); const middlewareController = this.initMiddlewareList(controller?.middleware); const middlewareRoute = opt.middleware && opt.middleware instanceof Array ? opt.middleware : []; this.drv?.json instanceof Function && middlewareModule.global.push(this.drv.json()); return [ ...middlewareModule.global, ...middlewareModule[action] instanceof Array ? middlewareModule[action] : [], ...middlewareController.global, ...middlewareController[action] instanceof Array ? middlewareController[action] : [], ...middlewareRoute ]; } catch (error) { console.log('[ERROR]', error); return []; } } /** * @description initialize module middleware list * @param {Object} middleware * @returns {Object} middleware */ initMiddlewareList(middleware, actions = null) { actions = actions || ['global', 'list', 'select', 'insert', 'update', 'delete', 'clone', 'clean', 'option', 'options']; middleware = middleware || {}; for (let action of actions) { middleware[action] = middleware[action] instanceof Array ? middleware[action] : []; } return middleware; } } module.exports = Module;