import * as express from "express"; import { URL } from "url"; import { IErrorExtra_Response_DTO, IError_Response_DTO, } from "@shared/contexts"; import { UseCaseError } from "../../application"; import { IServerError } from "../../domain/errors"; import { IController } from "../Controller.interface"; import { InfrastructureError } from "../InfrastructureError"; import { ProblemDocument, ProblemDocumentExtension } from "./ProblemDocument"; export abstract class ExpressController implements IController { protected req: express.Request; protected res: express.Response; protected next: express.NextFunction; protected serverURL: string = ""; protected file: any; protected abstract executeImpl(): Promise; public execute( req: express.Request, res: express.Response, next: express.NextFunction, ): void { this.req = req; this.res = res; this.next = next; this.serverURL = `${ new URL( `${this.req.protocol}://${this.req.get("host")}${this.req.originalUrl}`, ).origin }/api/v1`; this.file = this.req && this.req["file"]; // <-- ???? this.executeImpl(); } public ok(dto?: T) { if (dto) { return this._jsonResponse(200, dto); } return this.res.status(200).send(); } public fail(error: IServerError) { console.group("ExpressController FAIL RESPONSE ===================="); console.log(error); console.trace("Show me"); console.groupEnd(); return this._errorResponse(500, error ? error.toString() : "Fail"); } public created(dto?: T) { if (dto) { return this.res.status(201).json(dto).send(); } return this.res.status(201).send(); } public noContent() { return this.res.status(204).send(); } public download(filepath: string, filename: string, done?: any) { return this.res.download(filepath, filename, done); } public clientError(message?: string) { return this._errorResponse(400, message); } public unauthorizedError(message?: string) { return this._errorResponse(401, message); } public paymentRequiredError(message?: string) { return this._errorResponse(402, message); } public forbiddenError(message?: string) { return this._errorResponse(403, message); } public notFoundError(message: string, error?: IServerError) { return this._errorResponse(404, message, error); } public conflictError(message: string, error?: IServerError) { return this._errorResponse(409, message, error); } public invalidInputError(message?: string, error?: InfrastructureError) { return this._errorResponse(422, message, error); } public tooManyError(message: string, error?: Error) { return this._errorResponse(429, message, error); } public internalServerError(message?: string, error?: IServerError) { return this._errorResponse(500, message, error); } public todoError(message?: string) { return this._errorResponse(501, message); } public unavailableError(message?: string) { return this._errorResponse(503, message); } private _jsonResponse( statusCode: number, jsonPayload: any, ): express.Response { return this.res.status(statusCode).json(jsonPayload).send(); } private _errorResponse( statusCode: number, message?: string, error?: Error | InfrastructureError, ): express.Response { const context = {}; if (Object.keys(this.res.locals).length) { if ("user" in this.res.locals) { context["user"] = this.res.locals.user; } } if (Object.keys(this.req.params).length) { context["params"] = this.req.params; } if (Object.keys(this.req.query).length) { context["query"] = this.req.query; } if (Object.keys(this.req.body).length) { context["body"] = this.req.body; } const extension = new ProblemDocumentExtension({ context, extra: error ? { ...this._processError(error) } : {}, }); return this._jsonResponse( statusCode, new ProblemDocument( { status: statusCode, detail: message, instance: this.req.baseUrl, }, extension, ), ); } private _processError( error: Error | InfrastructureError, ): IErrorExtra_Response_DTO { /** * * * { code: "INVALID_INPUT_DATA", payload: { label: "tin", path: "tin", // [{path: "first_name"}, {path: "last_name"}] }, name: "UseCaseError", } { code: "INVALID_INPUT_DATA", payload: [ { tin: "{tin} is not allowed to be empty", }, { first_name: "{first_name} is not allowed to be empty", }, { last_name: "{last_name} is not allowed to be empty", }, { company_name: "{company_name} is not allowed to be empty", }, ], name: "InfrastructureError", } */ const useCaseError = error; const payload = !Array.isArray(useCaseError.payload) ? Array(useCaseError.payload) : useCaseError.payload; const errors = payload.map((item) => { if (item.path) { return item.path ? { [String(item.path)]: useCaseError.message, } : {}; } else { return item; } }); return { errors, }; } }