@vida-global/core 1.3.2 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/server/server.js
CHANGED
|
@@ -106,40 +106,17 @@ class VidaServer {
|
|
|
106
106
|
|
|
107
107
|
get loggingMiddleware() {
|
|
108
108
|
return httpLogger({
|
|
109
|
-
logger:
|
|
110
|
-
customLogLevel:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
// Format the request log as a string
|
|
117
|
-
req: (req) => `Request: ${req.method} ${req.url}`,
|
|
118
|
-
// Format the response log as a string
|
|
119
|
-
res: (res) => `statusCode=${res.statusCode}, responseTime=${res.get('X-Response-Time')}`,
|
|
120
|
-
},
|
|
121
|
-
wrapSerializers: false,
|
|
109
|
+
logger: this.middlewareLogger,
|
|
110
|
+
customLogLevel: this.requestLogLevel.bind(this),
|
|
111
|
+
customSuccessMessage: this.requestLogMessage.bind(this),
|
|
112
|
+
customErrorMessage: this.requestLogMessage.bind(this),
|
|
113
|
+
customErrorObject: this.requestLogDetails.bind(this),
|
|
114
|
+
customSuccessObject: this.requestLogDetails.bind(this),
|
|
115
|
+
wrapSerializers: false,
|
|
122
116
|
})
|
|
123
117
|
}
|
|
124
118
|
|
|
125
119
|
|
|
126
|
-
get middlewareLogger() {
|
|
127
|
-
return pino({
|
|
128
|
-
level: process.env.HTTP_LOG_LEVEL || 'info',
|
|
129
|
-
transport: {
|
|
130
|
-
level: process.env.HTTP_LOG_LEVEL || 'info',
|
|
131
|
-
target: 'pino-pretty',
|
|
132
|
-
options: {
|
|
133
|
-
colorize: true,
|
|
134
|
-
ignore: 'pid,hostname',
|
|
135
|
-
translateTime: 'SYS:standard',
|
|
136
|
-
messageFormat: '{msg}',
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
120
|
get staticFilesDirectory() {
|
|
144
121
|
return `${process.cwd()}/static`;
|
|
145
122
|
}
|
|
@@ -158,6 +135,53 @@ class VidaServer {
|
|
|
158
135
|
use() { this.#expressServer.use(...arguments); }
|
|
159
136
|
|
|
160
137
|
|
|
138
|
+
/***********************************************************************************************
|
|
139
|
+
* LOGGING
|
|
140
|
+
***********************************************************************************************/
|
|
141
|
+
requestLogDetails(req, res, err) {
|
|
142
|
+
const details = {
|
|
143
|
+
req: `${req.method} ${req.url}`,
|
|
144
|
+
res: `statusCode=${res.statusCode}, responseTime=${res.get('X-Response-Time')}`,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
if (res.statusCode >= 500 && res.error) {
|
|
148
|
+
details.error = this.requestLogErrorDetails(req, res, res.error);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return details;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
requestLogErrorDetails(req, res, err) {
|
|
156
|
+
if (typeof err == 'string') return {message: err};
|
|
157
|
+
return {
|
|
158
|
+
type: err.constructor.name,
|
|
159
|
+
message: err.message,
|
|
160
|
+
stack: err.stack
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
requestLogMessage(req, res) {
|
|
166
|
+
const prefix = '[VidaServer]';
|
|
167
|
+
if (req.controller) {
|
|
168
|
+
return `${prefix} ${req.controller.constructor.name}#${req.action}`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return `${prefix} ${res.status >= 500 ? 'request errored' : 'request completed'}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
requestLogLevel(req, res) {
|
|
176
|
+
return 'debug';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
get middlewareLogger() {
|
|
181
|
+
return logger;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
|
|
161
185
|
/***********************************************************************************************
|
|
162
186
|
* SETTINGS
|
|
163
187
|
***********************************************************************************************/
|
|
@@ -226,8 +250,9 @@ class VidaServer {
|
|
|
226
250
|
|
|
227
251
|
requestHandler(action, controllerCls) {
|
|
228
252
|
return async function(request, response) {
|
|
229
|
-
if (process.env.NODE_ENV != 'test') this.logger.info(`${controllerCls.name}#${action}`);
|
|
230
253
|
const controllerInstance = this.buildController(controllerCls, request, response);
|
|
254
|
+
request.controller = controllerInstance;
|
|
255
|
+
request.action = action;
|
|
231
256
|
await controllerInstance.performRequest(action);
|
|
232
257
|
}.bind(this);
|
|
233
258
|
}
|
|
@@ -133,9 +133,14 @@ class VidaServerController {
|
|
|
133
133
|
await this.renderErrors(err.message, err.fields);
|
|
134
134
|
|
|
135
135
|
} else {
|
|
136
|
-
this.
|
|
137
|
-
|
|
138
|
-
if (
|
|
136
|
+
this._response.error = err;
|
|
137
|
+
let message;
|
|
138
|
+
if (typeof err == 'string') {
|
|
139
|
+
message = err;
|
|
140
|
+
} else {
|
|
141
|
+
message = err.message;
|
|
142
|
+
}
|
|
143
|
+
await this.renderServerErrorResponse(message);
|
|
139
144
|
}
|
|
140
145
|
}
|
|
141
146
|
|
|
@@ -148,7 +153,11 @@ class VidaServerController {
|
|
|
148
153
|
this._response.send(body);
|
|
149
154
|
} else {
|
|
150
155
|
body = await this.processJSONBody(body, options);
|
|
151
|
-
|
|
156
|
+
|
|
157
|
+
const errors = body.errors || null;
|
|
158
|
+
delete body.errors;
|
|
159
|
+
|
|
160
|
+
body = this.formatJSONBody(body, errors, options);
|
|
152
161
|
this._response.json(body);
|
|
153
162
|
}
|
|
154
163
|
this.#rendered = true;
|
|
@@ -177,27 +186,40 @@ class VidaServerController {
|
|
|
177
186
|
}
|
|
178
187
|
|
|
179
188
|
|
|
189
|
+
async renderUnauthorizedResponse(message=null) {
|
|
190
|
+
return await this.#renderError(401, message);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
async renderForbiddenResponse(message=null) {
|
|
195
|
+
return await this.#renderError(403, message);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
180
199
|
async renderNotFoundResponse(message=null) {
|
|
181
|
-
|
|
182
|
-
await this.render({ message });
|
|
200
|
+
return await this.#renderError(404, message);
|
|
183
201
|
}
|
|
184
202
|
|
|
185
203
|
|
|
186
|
-
async
|
|
187
|
-
this
|
|
188
|
-
await this.render({ message });
|
|
204
|
+
async renderServerErrorResponse(message=null) {
|
|
205
|
+
return await this.#renderError(500, message);
|
|
189
206
|
}
|
|
190
207
|
|
|
191
208
|
|
|
192
|
-
async
|
|
193
|
-
this.statusCode =
|
|
194
|
-
|
|
209
|
+
async #renderError(statusCode, message) {
|
|
210
|
+
this.statusCode = statusCode;
|
|
211
|
+
const body = {errors: {message: message || null}};
|
|
212
|
+
return await this.render(body);
|
|
195
213
|
}
|
|
196
214
|
|
|
197
215
|
|
|
198
|
-
formatJSONBody(body, options) {
|
|
216
|
+
formatJSONBody(body, errors, options) {
|
|
199
217
|
if (options.standardize === false) return body;
|
|
200
|
-
|
|
218
|
+
|
|
219
|
+
const response = {data: body, status: this.statusText};
|
|
220
|
+
if (errors) response.errors = errors;
|
|
221
|
+
|
|
222
|
+
return response;
|
|
201
223
|
}
|
|
202
224
|
|
|
203
225
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vida-global/core",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"description": "Core libraries for supporting Vida development",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "ISC",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@vida-global/release": "^1.0.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"jest": "^
|
|
36
|
+
"jest": "^29.0.0",
|
|
37
37
|
"jest-express": "^1.12.0",
|
|
38
38
|
"jest-extended": "^7.0.0",
|
|
39
39
|
"@vida-global/test-helpers": "^1.0.1"
|
|
@@ -8,7 +8,7 @@ const { VidaServer } = require('../../lib/server');
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
jest.mock('express', () => {
|
|
11
|
-
const
|
|
11
|
+
const express = require('jest-express');
|
|
12
12
|
const { Response } = require('jest-express/lib/response');
|
|
13
13
|
|
|
14
14
|
// missing functions in mock express
|
|
@@ -8,7 +8,7 @@ const TestHelpers = require('@vida-global/test-helpers');
|
|
|
8
8
|
const { VidaServerController } = require('../../lib/server');
|
|
9
9
|
|
|
10
10
|
describe('VidaServerController', () => {
|
|
11
|
-
describe('
|
|
11
|
+
describe('.actionNames', () => {
|
|
12
12
|
it ('returns actions based on method names', () => {
|
|
13
13
|
const names = FooController.actionNames;
|
|
14
14
|
const expected = ['postIndex',
|
|
@@ -23,7 +23,7 @@ describe('VidaServerController', () => {
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
describe('
|
|
26
|
+
describe('.autoLoadHelpers', () => {
|
|
27
27
|
class AutoLoadTestController extends VidaServerController {
|
|
28
28
|
static get autoLoadHelperPath() {
|
|
29
29
|
return `${process.cwd()}/test/server/helpers/autoload/${this.name}.js`;
|
|
@@ -60,7 +60,7 @@ describe('VidaServerController', () => {
|
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
describe('
|
|
63
|
+
describe('._constructAction', () => {
|
|
64
64
|
class UsersController extends VidaServerController {
|
|
65
65
|
static get routes() {
|
|
66
66
|
return {getBazBan: '/baz/:id/ban'};
|
|
@@ -123,7 +123,7 @@ describe('VidaServerController', () => {
|
|
|
123
123
|
});
|
|
124
124
|
|
|
125
125
|
|
|
126
|
-
describe('
|
|
126
|
+
describe('.validateParameters', () => {
|
|
127
127
|
const paramName1 = TestHelpers.Faker.Text.randomString();
|
|
128
128
|
const paramName2 = TestHelpers.Faker.Text.randomString();
|
|
129
129
|
const value1 = TestHelpers.Faker.Text.randomString();
|
|
@@ -217,7 +217,7 @@ describe('VidaServerController', () => {
|
|
|
217
217
|
});
|
|
218
218
|
|
|
219
219
|
|
|
220
|
-
describe('
|
|
220
|
+
describe('.validatePresence', () => {
|
|
221
221
|
const controller = new FooController({}, {});
|
|
222
222
|
it ('returns undefined when there is a value', () => {
|
|
223
223
|
const result = controller.validatePresence(TestHelpers.Faker.Text.randomString());
|
|
@@ -231,7 +231,7 @@ describe('VidaServerController', () => {
|
|
|
231
231
|
});
|
|
232
232
|
|
|
233
233
|
|
|
234
|
-
describe('
|
|
234
|
+
describe('.validateIsInteger', () => {
|
|
235
235
|
const controller = new FooController({}, {});
|
|
236
236
|
it ('returns undefined when there is an integer', () => {
|
|
237
237
|
const val = Math.ceil(Math.random() * 100);
|
|
@@ -316,7 +316,7 @@ describe('VidaServerController', () => {
|
|
|
316
316
|
});
|
|
317
317
|
|
|
318
318
|
|
|
319
|
-
describe('
|
|
319
|
+
describe('.validateIsString', () => {
|
|
320
320
|
const controller = new FooController({}, {});
|
|
321
321
|
it ('returns undefined when there is an string', () => {
|
|
322
322
|
const result = controller.validateIsString('')
|
|
@@ -396,7 +396,7 @@ describe('VidaServerController', () => {
|
|
|
396
396
|
});
|
|
397
397
|
|
|
398
398
|
|
|
399
|
-
describe('
|
|
399
|
+
describe('.validateIsDateTime', () => {
|
|
400
400
|
const controller = new FooController({}, {});
|
|
401
401
|
it ('returns undefined when passed a string of format YYYY-MM-DD', () => {
|
|
402
402
|
const result = controller.validateIsDateTime('2025-12-17');
|
|
@@ -435,7 +435,7 @@ describe('VidaServerController', () => {
|
|
|
435
435
|
});
|
|
436
436
|
|
|
437
437
|
|
|
438
|
-
describe('
|
|
438
|
+
describe('.validateIsEnum', () => {
|
|
439
439
|
const controller = new FooController({}, {});
|
|
440
440
|
const enums = [Math.random(), Math.random(), Math.random()];
|
|
441
441
|
const error = TestHelpers.Faker.Text.randomString();
|
|
@@ -452,7 +452,7 @@ describe('VidaServerController', () => {
|
|
|
452
452
|
});
|
|
453
453
|
|
|
454
454
|
|
|
455
|
-
describe('
|
|
455
|
+
describe('.validateFunction', () => {
|
|
456
456
|
const controller = new FooController({}, {});
|
|
457
457
|
const correctValue = Math.random();
|
|
458
458
|
const error = TestHelpers.Faker.Text.randomString();
|
|
@@ -470,7 +470,7 @@ describe('VidaServerController', () => {
|
|
|
470
470
|
});
|
|
471
471
|
|
|
472
472
|
|
|
473
|
-
describe('
|
|
473
|
+
describe('#performRequest', () => {
|
|
474
474
|
const errorHandlers = [
|
|
475
475
|
['renderUnauthorizedResponse', 'AuthorizationError'],
|
|
476
476
|
['renderForbiddenResponse', 'ForbiddenError'],
|
|
@@ -505,7 +505,7 @@ describe('VidaServerController', () => {
|
|
|
505
505
|
await controller.performRequest('testAction');
|
|
506
506
|
|
|
507
507
|
expect(controller.statusCode).toEqual(500);
|
|
508
|
-
expect(controller.render).toHaveBeenCalledWith({
|
|
508
|
+
expect(controller.render).toHaveBeenCalledWith({errors: { message }});
|
|
509
509
|
});
|
|
510
510
|
});
|
|
511
511
|
|
|
@@ -520,7 +520,7 @@ describe('VidaServerController', () => {
|
|
|
520
520
|
});
|
|
521
521
|
|
|
522
522
|
|
|
523
|
-
describe('
|
|
523
|
+
describe('#renderErrors', () => {
|
|
524
524
|
it ('returns a 400 response', async () => {
|
|
525
525
|
await controller.renderErrors();
|
|
526
526
|
expect(response.statusCode).toBe(400);
|
|
@@ -577,7 +577,7 @@ describe('VidaServerController', () => {
|
|
|
577
577
|
});
|
|
578
578
|
|
|
579
579
|
|
|
580
|
-
describe('
|
|
580
|
+
describe('#renderUnauthorizedResponse', () => {
|
|
581
581
|
it ('returns a 401 response', async () => {
|
|
582
582
|
await controller.renderUnauthorizedResponse();
|
|
583
583
|
expect(response.statusCode).toBe(401);
|
|
@@ -586,19 +586,19 @@ describe('VidaServerController', () => {
|
|
|
586
586
|
it ('includes a blank message by default', async () => {
|
|
587
587
|
await controller.renderUnauthorizedResponse();
|
|
588
588
|
expect(controller.render).toHaveBeenCalledTimes(1);
|
|
589
|
-
expect(controller.render).toHaveBeenCalledWith({message: null});
|
|
589
|
+
expect(controller.render).toHaveBeenCalledWith({errors: {message: null}});
|
|
590
590
|
})
|
|
591
591
|
|
|
592
592
|
it ('includes a provided message', async () => {
|
|
593
593
|
const msg = TestHelpers.Faker.Text.randomString();
|
|
594
594
|
await controller.renderUnauthorizedResponse(msg);
|
|
595
595
|
expect(controller.render).toHaveBeenCalledTimes(1);
|
|
596
|
-
expect(controller.render).toHaveBeenCalledWith({message: msg});
|
|
596
|
+
expect(controller.render).toHaveBeenCalledWith({errors: {message: msg}});
|
|
597
597
|
});
|
|
598
598
|
});
|
|
599
599
|
|
|
600
600
|
|
|
601
|
-
describe('
|
|
601
|
+
describe('#renderForbiddenResponse', () => {
|
|
602
602
|
it ('returns a 403 response', async () => {
|
|
603
603
|
await controller.renderForbiddenResponse();
|
|
604
604
|
expect(response.statusCode).toBe(403);
|
|
@@ -607,19 +607,19 @@ describe('VidaServerController', () => {
|
|
|
607
607
|
it ('includes a blank message by default', async () => {
|
|
608
608
|
await controller.renderForbiddenResponse();
|
|
609
609
|
expect(controller.render).toHaveBeenCalledTimes(1);
|
|
610
|
-
expect(controller.render).toHaveBeenCalledWith({message: null});
|
|
610
|
+
expect(controller.render).toHaveBeenCalledWith({errors: {message: null}});
|
|
611
611
|
})
|
|
612
612
|
|
|
613
613
|
it ('includes a provided message', async () => {
|
|
614
614
|
const msg = TestHelpers.Faker.Text.randomString();
|
|
615
615
|
await controller.renderForbiddenResponse(msg);
|
|
616
616
|
expect(controller.render).toHaveBeenCalledTimes(1);
|
|
617
|
-
expect(controller.render).toHaveBeenCalledWith({message: msg});
|
|
617
|
+
expect(controller.render).toHaveBeenCalledWith({errors: {message: msg}});
|
|
618
618
|
});
|
|
619
619
|
});
|
|
620
620
|
|
|
621
621
|
|
|
622
|
-
describe('
|
|
622
|
+
describe('#renderNotFoundResponse', () => {
|
|
623
623
|
it ('returns a 404 response', async () => {
|
|
624
624
|
await controller.renderNotFoundResponse();
|
|
625
625
|
expect(response.statusCode).toBe(404);
|
|
@@ -628,14 +628,14 @@ describe('VidaServerController', () => {
|
|
|
628
628
|
it ('includes a blank message by default', async () => {
|
|
629
629
|
await controller.renderNotFoundResponse();
|
|
630
630
|
expect(controller.render).toHaveBeenCalledTimes(1);
|
|
631
|
-
expect(controller.render).toHaveBeenCalledWith({message: null});
|
|
631
|
+
expect(controller.render).toHaveBeenCalledWith({errors: {message: null}});
|
|
632
632
|
})
|
|
633
633
|
|
|
634
634
|
it ('includes a provided message', async () => {
|
|
635
635
|
const msg = TestHelpers.Faker.Text.randomString();
|
|
636
636
|
await controller.renderNotFoundResponse(msg);
|
|
637
637
|
expect(controller.render).toHaveBeenCalledTimes(1);
|
|
638
|
-
expect(controller.render).toHaveBeenCalledWith({message: msg});
|
|
638
|
+
expect(controller.render).toHaveBeenCalledWith({errors: {message: msg}});
|
|
639
639
|
});
|
|
640
640
|
});
|
|
641
641
|
});
|
|
@@ -653,7 +653,7 @@ describe('VidaServerController', () => {
|
|
|
653
653
|
controller = new FooController(request, response);
|
|
654
654
|
});
|
|
655
655
|
|
|
656
|
-
describe('
|
|
656
|
+
describe('#requestHeaders', () => {
|
|
657
657
|
it ('returns a copy of the request headers', () => {
|
|
658
658
|
expect(controller.requestHeaders).not.toBe(request.headers);
|
|
659
659
|
expect(JSON.stringify(controller.requestHeaders)).toEqual(JSON.stringify(request.headers));
|
|
@@ -661,28 +661,28 @@ describe('VidaServerController', () => {
|
|
|
661
661
|
});
|
|
662
662
|
|
|
663
663
|
|
|
664
|
-
describe('
|
|
664
|
+
describe('#responseHeaders', () => {
|
|
665
665
|
it ('returns the request headers', () => {
|
|
666
666
|
expect(controller.responseHeaders).toBe(response.headers);
|
|
667
667
|
});
|
|
668
668
|
});
|
|
669
669
|
|
|
670
670
|
|
|
671
|
-
describe('
|
|
671
|
+
describe('#contentType', () => {
|
|
672
672
|
it ('returns the content-type request header', () => {
|
|
673
673
|
expect(controller.contentType).toEqual(request.headers['content-type']);
|
|
674
674
|
});
|
|
675
675
|
});
|
|
676
676
|
|
|
677
677
|
|
|
678
|
-
describe('
|
|
678
|
+
describe('#logger', () => {
|
|
679
679
|
it ('uses the standard logger', () => {
|
|
680
680
|
expect(controller.logger).toBe(logger);
|
|
681
681
|
});
|
|
682
682
|
});
|
|
683
683
|
|
|
684
684
|
|
|
685
|
-
describe('
|
|
685
|
+
describe('#rendered', () => {
|
|
686
686
|
it ('returns false if nothing has rendered', () => {
|
|
687
687
|
expect(controller.rendered).toBeFalsy();
|
|
688
688
|
});
|
|
@@ -701,7 +701,7 @@ describe('VidaServerController', () => {
|
|
|
701
701
|
});
|
|
702
702
|
|
|
703
703
|
|
|
704
|
-
describe('
|
|
704
|
+
describe('#statusCode', () => {
|
|
705
705
|
it ('returns the response status code', () => {
|
|
706
706
|
expect(controller.statusCode).toEqual(response.statusCode);
|
|
707
707
|
});
|
|
@@ -715,7 +715,7 @@ describe('VidaServerController', () => {
|
|
|
715
715
|
});
|
|
716
716
|
|
|
717
717
|
|
|
718
|
-
describe('
|
|
718
|
+
describe('#setupCallbacks', () => {
|
|
719
719
|
it ('is only called once', () => {
|
|
720
720
|
class CallbacksTestController extends VidaServerController {}
|
|
721
721
|
CallbacksTestController.prototype.setupCallbacks = jest.fn();
|