import { Response, Request, NextFunction } from 'express'; import { matchedData, validationResult } from 'express-validator'; import accepts from 'accepts'; import { isArray, drop, first, camelCase, snakeCase, omit, set, get } from 'lodash'; import { mapKeysDeep } from 'utils'; import asyncMiddleware from '@/api/middleware/asyncMiddleware'; export default class BaseController { /** * Converts plain object keys to cameCase style. * @param {Object} data */ protected dataToCamelCase(data) { return mapKeysDeep(data, (v, k) => camelCase(k)); } /** * Matches the body data from validation schema. * @param {Request} req * @param options */ protected matchedBodyData(req: Request, options: any = {}) { const data = matchedData(req, { locations: ['body'], includeOptionals: true, ...omit(options, ['locations']), // override any propery except locations. }); return this.dataToCamelCase(data); } /** * Matches the query data from validation schema. * @param {Request} req */ protected matchedQueryData(req: Request) { const data = matchedData(req, { locations: ['query'], }); return this.dataToCamelCase(data); } /** * Validate validation schema middleware. * @param {Request} req * @param {Response} res * @param {NextFunction} next */ protected validationResult(req: Request, res: Response, next: NextFunction) { const validationErrors = validationResult(req); if (!validationErrors.isEmpty()) { return res.boom.badData(null, { code: 'validation_error', ...validationErrors, }); } next(); } /** * Sets localization to response object by the given path. * @param {Response} response - * @param {string} path - * @param {Request} req - */ private setLocalizationByPath( response: any, path: string, req: Request, ) { const DOT = '.'; if (isArray(response)) { response.forEach((va) => { const currentPath = first(path.split(DOT)); const value = get(va, currentPath); if (isArray(value)) { const nextPath = drop(path.split(DOT)).join(DOT); this.setLocalizationByPath(value, nextPath, req); } else { set(va, path, req.__(value)); } }) } else { const value = get(response, path); set(response, path, req.__(value)); } } /** * Transform the given data to response. * @param {any} data */ protected transfromToResponse( data: any, translatable?: string | string[], req?: Request ) { const response = mapKeysDeep(data, (v, k) => snakeCase(k)); if (translatable) { const translatables = Array.isArray(translatable) ? translatable : [translatable]; translatables.forEach((path) => { this.setLocalizationByPath(response, path, req); }); } return response; } /** * Async middleware. * @param {function} callback */ protected asyncMiddleware(callback) { return asyncMiddleware(callback); } /** * * @param {Request} req * @returns */ protected accepts(req) { return accepts(req); } /** * * @param {Request} req * @param {string[]} types * @returns {string} */ protected acceptTypes(req: Request, types: string[]) { return this.accepts(req).types(types); } }