2024-04-23 15:29:38 +00:00
|
|
|
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";
|
2024-05-15 19:56:22 +00:00
|
|
|
import { IController } from "../Controller.interface";
|
2024-04-23 15:29:38 +00:00
|
|
|
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<void | any>;
|
|
|
|
|
|
|
|
|
|
public execute(
|
|
|
|
|
req: express.Request,
|
|
|
|
|
res: express.Response,
|
2024-05-15 19:56:22 +00:00
|
|
|
next: express.NextFunction,
|
2024-04-23 15:29:38 +00:00
|
|
|
): void {
|
|
|
|
|
this.req = req;
|
|
|
|
|
this.res = res;
|
|
|
|
|
this.next = next;
|
|
|
|
|
|
|
|
|
|
this.serverURL = `${
|
|
|
|
|
new URL(
|
2024-05-15 19:56:22 +00:00
|
|
|
`${this.req.protocol}://${this.req.get("host")}${this.req.originalUrl}`,
|
2024-04-23 15:29:38 +00:00
|
|
|
).origin
|
|
|
|
|
}/api/v1`;
|
|
|
|
|
|
|
|
|
|
this.file = this.req && this.req["file"]; // <-- ????
|
|
|
|
|
|
|
|
|
|
this.executeImpl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ok<T>(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<T>(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,
|
2024-05-15 19:56:22 +00:00
|
|
|
jsonPayload: any,
|
2024-04-23 15:29:38 +00:00
|
|
|
): express.Response<any> {
|
|
|
|
|
return this.res.status(statusCode).json(jsonPayload).send();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _errorResponse(
|
|
|
|
|
statusCode: number,
|
|
|
|
|
message?: string,
|
2024-05-15 19:56:22 +00:00
|
|
|
error?: Error | InfrastructureError,
|
2024-04-23 15:29:38 +00:00
|
|
|
): express.Response<IError_Response_DTO> {
|
|
|
|
|
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,
|
|
|
|
|
},
|
2024-05-15 19:56:22 +00:00
|
|
|
extension,
|
|
|
|
|
),
|
2024-04-23 15:29:38 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _processError(
|
2024-05-15 19:56:22 +00:00
|
|
|
error: Error | InfrastructureError,
|
2024-04-23 15:29:38 +00:00
|
|
|
): 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 = <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,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|