const ApplicationError = require('../utils/application-error'); const validMethod = { get: 'get', post: 'post', delete: 'delete', patch: 'patch', put: 'put' }; /** * A class that represents a controller for processing HTTP routes. */ class Controller { /** * Creates a new instance of the Controller class. * * @param {Object} options Options for the controller. * @param {string} options.method HTTP method (for example, 'get', 'post', 'delete', 'patch', 'put'). * @param {string} options.path The route path (URL). * @param {function} options.handler The request handler that will be called when the route is accessed. * @param {Joi.Schema} options.validationSchema Validation scheme for validating input data. * @param {function[]} options.middlewares Middleware to be called before the handler. */ constructor({ method, path, handler, validationSchema, middlewares }) { if (!validMethod[method] || !path || !handler) { console.error(`Controller misconfiguration. Method: ${method}, path: ${path}, handler exists: ${!!handler}`); process.exit(-1); } this.method = method; this.path = path; this.handler = handler; this.validationSchema = validationSchema; this.middlewares = middlewares; } /** * Validates input data according to the validation scheme. * * @param {Express.Request} req The Express request object. * @param {Express.Response} res The Express response object. * @throws {ApplicationError.JsonValidation} If the data does not match the validation scheme. */ async validate(req, res) { if (!this.validationSchema) { return; } const objectToValidate = this.method === 'get' ? req.query : req.body; try { const value = await this.validationSchema.validateAsync(objectToValidate, {stripUnknown: true}); if (this.method === 'get') { req.query = value; } else { req.body = value; } } catch (e) { const message = e.details.map(i => i.message).join(','); throw ApplicationError.JsonValidation(message); } } /** * Performs HTTP request processing. * * @param {Express.Request} req The Express request object. * @param {Express.Response} res The Express response object. * @param {function} next Function for further processing of the request. */ async run(req, res, next) { try { const handlerResult = await this.handler(req, res, next); if (!res.headersSent) { return res.status(200).json({ success: true, statusCode: 200, data: handlerResult }); } } catch (e) { next(e); } } /** * Registers the controller with the Express router. * @param {Express.Router} router The Express router in which the controller will be registered. */ register(router) { const self = this; const handle = async function (req, res, next) { return await self.run(req, res, next); }; this.middlewares.forEach(middleware => { router[this.method](this.path, middleware) ; }); router[this.method](this.path, handle); } } module.exports = Controller;