1 |
|
2 | const retrieveStatusMeta = (code) =>
|
3 | Object.entries({
|
4 | BadRequest: 400,
|
5 | Authentication: 401,
|
6 | Authorization: 403,
|
7 | ResourceNotFound: 404,
|
8 | Conflict: 409,
|
9 | Gone: 410,
|
10 | Preprocessing: 412,
|
11 | Validation: 422,
|
12 | }).find(([k, v]) => k === code || v === code) || [
|
13 | 'InternalServer',
|
14 | 500,
|
15 | ];
|
16 |
|
17 | class Exception {
|
18 | constructor(code) {
|
19 | const [name, statusCode] = retrieveStatusMeta(code);
|
20 | this.code = code;
|
21 | this.name = name;
|
22 | this.statusCode = statusCode;
|
23 | this.errors = {};
|
24 | }
|
25 |
|
26 | get $error() {
|
27 | const e = new Error();
|
28 | Object.assign(e, this);
|
29 | return e;
|
30 | }
|
31 |
|
32 | field(fieldName) {
|
33 | const formatAPIError = (name, msg, value) => {
|
34 | this.errors[name] = {
|
35 | msg: msg || name,
|
36 | in: 'application',
|
37 | value,
|
38 | };
|
39 | };
|
40 |
|
41 | const getFieldNameProps = (name) => {
|
42 | if (typeof fieldName === 'object')
|
43 | return [
|
44 | name || fieldName.name,
|
45 | fieldName.msg,
|
46 | fieldName.value,
|
47 | ];
|
48 |
|
49 | return [fieldName];
|
50 | };
|
51 |
|
52 | if (
|
53 | typeof fieldName === 'string' ||
|
54 | !Array.isArray(fieldName.name)
|
55 | )
|
56 | formatAPIError(...getFieldNameProps());
|
57 |
|
58 | if (Array.isArray(fieldName.name))
|
59 | fieldName.name.forEach((i) => {
|
60 | formatAPIError(...getFieldNameProps(i));
|
61 | });
|
62 |
|
63 | return this;
|
64 | }
|
65 |
|
66 | msg(msg) {
|
67 | this.message = msg;
|
68 | this.code = msg;
|
69 | return this;
|
70 | }
|
71 |
|
72 | boomerang() {
|
73 | return this.$error;
|
74 | }
|
75 |
|
76 | log() {
|
77 |
|
78 | console.log(this.$error)
|
79 | }
|
80 |
|
81 | throw() {
|
82 | throw this.$error;
|
83 | }
|
84 | }
|
85 |
|
86 | const handleUncaughtExceptions = (err, req, res, next) => {
|
87 |
|
88 | if (process.env.DEBUG_CONTROLLER) console.log(err);
|
89 |
|
90 | const status = err.statusCode || 500;
|
91 | const setHeader = (code) =>
|
92 | !res._headerSent && !res.headersSent
|
93 | ? res.status(code)
|
94 | : undefined;
|
95 |
|
96 | const translateMessage = (m) =>
|
97 | req.t ? req.t(`messages:${m}`) : m;
|
98 |
|
99 | const translateErrors = (e = {}) =>
|
100 | Object.entries(e).reduce(
|
101 | (a, [key, value]) =>
|
102 | req.t
|
103 | ? Object.assign(a, {
|
104 | [key]: {
|
105 | ...value,
|
106 | msg: req.t(`validations:${value.msg}`),
|
107 | },
|
108 | })
|
109 | : a,
|
110 | {},
|
111 | );
|
112 |
|
113 | if (
|
114 | err.errors &&
|
115 | Object.keys(err.errors).length &&
|
116 | status === 500
|
117 | ) {
|
118 | setHeader(422);
|
119 | res.json({
|
120 | message: translateMessage('validation'),
|
121 | errors: translateErrors(err.errors),
|
122 | });
|
123 | } else {
|
124 | setHeader(status);
|
125 | res.json({
|
126 | errors: translateErrors(err.errors),
|
127 | message: translateMessage(err.message),
|
128 | stack: err.stack,
|
129 | name: err.name,
|
130 | });
|
131 | }
|
132 | };
|
133 |
|
134 | module.exports = {
|
135 | exception: (v) => new Exception(v),
|
136 | handleUncaughtExceptions,
|
137 | };
|