@vida-global/core 1.4.5 → 2.0.0
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/AGENTS.md +13 -0
- package/README.md +1 -0
- package/config/newrelic-config.js +118 -0
- package/index.js +9 -5
- package/lib/activeRecord/baseRecord.js +49 -3
- package/lib/apm/agent.js +22 -0
- package/lib/apm/index.js +54 -0
- package/lib/apm/utils.js +43 -0
- package/lib/http/client.js +16 -2
- package/lib/jobQueue/README.md +63 -0
- package/lib/jobQueue/abstractJob.js +103 -0
- package/lib/jobQueue/abstractJobComponent.js +37 -0
- package/lib/jobQueue/index.js +10 -0
- package/lib/jobQueue/jobImporter.js +19 -0
- package/lib/jobQueue/queue.js +161 -0
- package/lib/jobQueue/worker.js +273 -0
- package/lib/logger/README.md +33 -2
- package/lib/logger/index.js +117 -20
- package/lib/logger/serverMiddleware.js +41 -0
- package/lib/redis/redisClient.js +32 -24
- package/lib/server/controllerImporter.js +9 -47
- package/lib/server/server.js +34 -71
- package/lib/server/serverController.js +215 -23
- package/lib/utils/abstractAutoImporter.js +64 -0
- package/package.json +8 -6
- package/test/activeRecord/baseRecord.test.js +129 -0
- package/test/activeRecord/db/connection.test.js +4 -1
- package/test/activeRecord/db/connectionConfiguration.test.js +9 -3
- package/test/activeRecord/db/schema.test.js +217 -0
- package/test/activeRecord/helpers/baseRecordMocks.js +6 -4
- package/test/activeRecord/helpers/connection.js +0 -3
- package/test/activeRecord/helpers/connectionConfiguration.js +0 -8
- package/test/activeRecord/utils.test.js +72 -0
- package/test/apm/agent.test.js +56 -0
- package/test/apm/utils.test.js +121 -0
- package/test/helpers/env.js +33 -0
- package/test/http/client.test.js +3 -3
- package/test/jobQueue/abstractJob.test.js +307 -0
- package/test/jobQueue/abstractJobComponent.test.js +110 -0
- package/test/jobQueue/helpers/abstractJob.js +26 -0
- package/test/jobQueue/helpers/apmMock.js +20 -0
- package/test/jobQueue/helpers/bullmqMock.js +67 -0
- package/test/jobQueue/helpers/env.js +29 -0
- package/test/jobQueue/helpers/fixtureJobs/notAJob.js +8 -0
- package/test/jobQueue/helpers/fixtureJobs/testJobA.js +14 -0
- package/test/jobQueue/helpers/fixtureJobs/testJobB.js +14 -0
- package/test/jobQueue/helpers/loggerMock.js +43 -0
- package/test/jobQueue/helpers/worker.js +38 -0
- package/test/jobQueue/queue.test.js +320 -0
- package/test/jobQueue/worker.test.js +526 -0
- package/test/logger/index.test.js +61 -0
- package/test/logger/serverMiddleware.test.js +72 -0
- package/test/server/apiDocsGenerator.test.js +38 -0
- package/test/server/server.test.js +186 -4
- package/test/server/serverController.test.js +407 -5
package/lib/redis/redisClient.js
CHANGED
|
@@ -3,40 +3,48 @@ const { logger } = require('../logger');
|
|
|
3
3
|
const redis = require('redis');
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
const CERT_LOCATION = process.env.REDIS_CERT_LOCATION || `${process.cwd()}/redis_ca.pem`;
|
|
7
|
+
const clients = {};
|
|
8
|
+
logger.addScope('redis');
|
|
7
9
|
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
function redisClientFactory({ host, port, password, subscriber=false } = {}) {
|
|
12
|
+
host = host || process.env.REDIS_HOST;
|
|
13
|
+
port = parseInt(port || process.env.REDIS_PORT);
|
|
14
|
+
password = password || process.env.REDIS_PASSWORD
|
|
15
|
+
const key = `${host}:${port}:${Boolean(subscriber)}`;
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
const cert = fs.readFileSync(certLocation)
|
|
17
|
+
if (clients[key]) return clients[key];
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
},
|
|
22
|
-
password: process.env.REDIS_PASSWORD
|
|
23
|
-
});
|
|
19
|
+
const client = createClient(host, port, password);
|
|
20
|
+
clients[key] = client;
|
|
21
|
+
|
|
22
|
+
configureClient(client)
|
|
23
|
+
return client;
|
|
24
|
+
}
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
function createClient(host, port, password) {
|
|
28
|
+
const ca = fs.readFileSync(CERT_LOCATION)
|
|
29
|
+
const socket = { host, port, ca, tls: true, };
|
|
30
|
+
return redis.createClient({ socket, password });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
function configureClient(client) {
|
|
35
|
+
client.on('ready', (err) => {
|
|
36
|
+
logger.redis.info('Connected');
|
|
27
37
|
});
|
|
28
|
-
|
|
38
|
+
client.on('error', (err) => {
|
|
29
39
|
const errno = err.errno ? ` (${err.errno})` : '';
|
|
30
|
-
logger.error(
|
|
40
|
+
logger.redis.error(`${err.message}${errno}`);
|
|
31
41
|
});
|
|
32
42
|
|
|
33
|
-
await redisSingleton.connect();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
43
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
const connect = client.connect.bind(client);
|
|
45
|
+
client.connect = async function() {
|
|
46
|
+
if (!this.isReady) await connect();
|
|
47
|
+
}
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
|
|
@@ -1,61 +1,23 @@
|
|
|
1
|
-
const
|
|
1
|
+
const { AbstractAutoImporter } = require('../utils/abstractAutoImporter');
|
|
2
2
|
const { VidaServerController } = require('./serverController');
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
class ControllerImporter {
|
|
6
|
-
#controllerDirectories;
|
|
7
|
-
#controllerClasses;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
constructor(controllerDirectories) {
|
|
11
|
-
if (!Array.isArray(controllerDirectories)) {
|
|
12
|
-
controllerDirectories = [controllerDirectories];
|
|
13
|
-
}
|
|
14
|
-
this.#controllerDirectories = controllerDirectories;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
class ControllerImporter extends AbstractAutoImporter {
|
|
18
6
|
get controllerClasses() {
|
|
19
|
-
|
|
20
|
-
this.#importControllerClasses();
|
|
21
|
-
}
|
|
22
|
-
return this.#controllerClasses;
|
|
7
|
+
return this.imports;
|
|
23
8
|
}
|
|
24
9
|
|
|
25
10
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
this.#controllerDirectories.forEach(dir => {
|
|
29
|
-
this.#importDirectory(dir, dir);
|
|
30
|
-
});
|
|
11
|
+
shouldImport(obj) {
|
|
12
|
+
return obj.prototype instanceof VidaServerController;
|
|
31
13
|
}
|
|
32
14
|
|
|
33
15
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
this.#importDirectory(filePath, topLevelDirectory);
|
|
39
|
-
} else if (f.endsWith('.js')) {
|
|
40
|
-
this.#importFromFile(filePath, dir, topLevelDirectory);
|
|
41
|
-
}
|
|
42
|
-
}).bind(this));
|
|
16
|
+
processImport(obj, directoryPrefix) {
|
|
17
|
+
obj.directoryPrefix = directoryPrefix;
|
|
18
|
+
obj.autoLoadHelpers();
|
|
19
|
+
obj.autoLoadDocumentation();
|
|
43
20
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
#importFromFile(filePath, dir, topLevelDirectory) {
|
|
47
|
-
const exports = Object.values(require(filePath));
|
|
48
|
-
exports.forEach(_export => {
|
|
49
|
-
if (_export.prototype instanceof VidaServerController) {
|
|
50
|
-
const directoryPrefix = dir.replace(topLevelDirectory, '');
|
|
51
|
-
_export.directoryPrefix = directoryPrefix;
|
|
52
|
-
_export.autoLoadHelpers();
|
|
53
|
-
_export.autoLoadDocumentation();
|
|
54
|
-
this.#controllerClasses.push(_export);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
21
|
}
|
|
60
22
|
|
|
61
23
|
|
package/lib/server/server.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
+
const APM = require('../apm');
|
|
1
2
|
const { ControllerImporter } = require('./controllerImporter');
|
|
2
3
|
const express = require('express');
|
|
3
|
-
const httpLogger = require('pino-http')
|
|
4
4
|
const { logger } = require('../logger');
|
|
5
|
+
const loggingMiddleware = require('../logger/serverMiddleware');
|
|
5
6
|
const mustacheExpress = require('mustache-express');
|
|
6
|
-
const { pino } = require('pino');
|
|
7
7
|
const responseTime = require('response-time');
|
|
8
8
|
const IoServer = require("socket.io")
|
|
9
9
|
const { Server } = require('http');
|
|
10
10
|
const { SystemController } = require('./systemController');
|
|
11
|
+
const requestID = require( 'express-request-id');
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
logger.addScope('server');
|
|
11
15
|
|
|
12
16
|
|
|
13
17
|
class VidaServer {
|
|
@@ -31,7 +35,7 @@ class VidaServer {
|
|
|
31
35
|
await this.registerControllers();
|
|
32
36
|
|
|
33
37
|
this.#httpServer.listen(this.#port, this.#host, () => {
|
|
34
|
-
this.logger.info(
|
|
38
|
+
this.logger.info(`${this.constructor.name} running on port ${this.#port}`);
|
|
35
39
|
if (callback) callback();
|
|
36
40
|
});
|
|
37
41
|
}
|
|
@@ -39,7 +43,7 @@ class VidaServer {
|
|
|
39
43
|
|
|
40
44
|
get host() { return this.#host; }
|
|
41
45
|
get port() { return this.#port; }
|
|
42
|
-
get logger() { return logger; }
|
|
46
|
+
get logger() { return logger.server; }
|
|
43
47
|
|
|
44
48
|
|
|
45
49
|
/***********************************************************************************************
|
|
@@ -72,11 +76,12 @@ class VidaServer {
|
|
|
72
76
|
setupMiddleware() {
|
|
73
77
|
this.use(this.jsonParsingMiddleware);
|
|
74
78
|
this.use(this.octetStreamParsingMiddleware);
|
|
75
|
-
this.use(responseTime())
|
|
79
|
+
this.use(responseTime({suffix: true}))
|
|
76
80
|
this.use(express.static('public'))
|
|
77
81
|
this.use(this.loggingMiddleware);
|
|
78
82
|
this.use('/static', express.static(this.staticFilesDirectory));
|
|
79
83
|
this.use(this.connectionAbortedMiddleware);
|
|
84
|
+
this.use(requestID());
|
|
80
85
|
|
|
81
86
|
this.#expressServer.engine('html', mustacheExpress());
|
|
82
87
|
}
|
|
@@ -105,15 +110,7 @@ class VidaServer {
|
|
|
105
110
|
|
|
106
111
|
|
|
107
112
|
get loggingMiddleware() {
|
|
108
|
-
return
|
|
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,
|
|
116
|
-
})
|
|
113
|
+
return loggingMiddleware.middleware(this.logger);
|
|
117
114
|
}
|
|
118
115
|
|
|
119
116
|
|
|
@@ -123,7 +120,7 @@ class VidaServer {
|
|
|
123
120
|
|
|
124
121
|
|
|
125
122
|
get connectionAbortedMiddleware() {
|
|
126
|
-
return async function (err, req, res, next) {
|
|
123
|
+
return async function connectionAborted(err, req, res, next) {
|
|
127
124
|
if (err && err.code === 'ECONNABORTED') {
|
|
128
125
|
return res.status(400).end(); // Don't process this error any further to avoid its logging
|
|
129
126
|
}
|
|
@@ -135,53 +132,6 @@ class VidaServer {
|
|
|
135
132
|
use() { this.#expressServer.use(...arguments); }
|
|
136
133
|
|
|
137
134
|
|
|
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
|
-
|
|
185
135
|
/***********************************************************************************************
|
|
186
136
|
* SETTINGS
|
|
187
137
|
***********************************************************************************************/
|
|
@@ -234,7 +184,7 @@ class VidaServer {
|
|
|
234
184
|
const method = action.method.toLowerCase();
|
|
235
185
|
const requestHandler = this.requestHandler(action.action, controllerCls)
|
|
236
186
|
if (process.env.NODE_ENV != 'test') {
|
|
237
|
-
logger.
|
|
187
|
+
this.logger.verbose(`ROUTE: ${method.toUpperCase().padEnd(6)} ${action.path}`);
|
|
238
188
|
}
|
|
239
189
|
this['_'+method](action.path, requestHandler);
|
|
240
190
|
}
|
|
@@ -249,24 +199,29 @@ class VidaServer {
|
|
|
249
199
|
|
|
250
200
|
|
|
251
201
|
requestHandler(action, controllerCls) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
202
|
+
// Implemented with this object access for APM segment tracking
|
|
203
|
+
const server = this;
|
|
204
|
+
const handler = {
|
|
205
|
+
async requestHandler(request, response) {
|
|
206
|
+
const controllerInstance = server.buildController(controllerCls, request, response);
|
|
207
|
+
request.controller = controllerInstance;
|
|
208
|
+
request.action = action;
|
|
209
|
+
await controllerInstance.performRequest(action);
|
|
210
|
+
}
|
|
211
|
+
}.requestHandler;
|
|
212
|
+
return handler;
|
|
258
213
|
}
|
|
259
214
|
|
|
260
215
|
|
|
261
216
|
buildController(controllerCls, request, response) {
|
|
262
|
-
return new controllerCls(request, response);
|
|
217
|
+
return new controllerCls(request, response, this.logger);
|
|
263
218
|
}
|
|
264
219
|
|
|
265
220
|
|
|
266
221
|
get controllerClasses() {
|
|
267
222
|
if (!this.#controllerClasses) {
|
|
268
223
|
const controllerImporter = new ControllerImporter(this.controllerDirectories);
|
|
269
|
-
this.#controllerClasses = controllerImporter.controllerClasses;
|
|
224
|
+
this.#controllerClasses = controllerImporter.controllerClasses.filter(cls => !cls.name.startsWith('Abstract'));
|
|
270
225
|
}
|
|
271
226
|
|
|
272
227
|
return this.#controllerClasses;
|
|
@@ -290,6 +245,14 @@ class VidaServer {
|
|
|
290
245
|
}
|
|
291
246
|
|
|
292
247
|
|
|
248
|
+
/***********************************************************************************************
|
|
249
|
+
* APM
|
|
250
|
+
***********************************************************************************************/
|
|
251
|
+
startApmSegment(name, record, handler) {
|
|
252
|
+
APM.startSegment(name, record, handler);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
|
|
293
256
|
/***********************************************************************************************
|
|
294
257
|
* DEPRECATED PASS THROUGH METHODS
|
|
295
258
|
***********************************************************************************************/
|