@autofleet/node-common 2.0.1 → 3.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/README.md +0 -29
- package/index.js +0 -10
- package/package.json +21 -20
- package/router/index.js +17 -2
- package/settings/index.js +8 -5
- package/settings/index.test.js +1 -3
- package/delorean-client/index.js +0 -52
- package/delorean-client/index.test.js +0 -105
- package/logger/example.js +0 -5
- package/logger/index.js +0 -114
- package/logger/index.test.js +0 -54
- package/network/index.js +0 -105
- package/network/index.test.js +0 -69
- package/queue/index.js +0 -107
package/README.md
CHANGED
|
@@ -18,35 +18,6 @@ Currently we suppurt:
|
|
|
18
18
|
}
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
## Network
|
|
22
|
-
Server 2 Servers communication.
|
|
23
|
-
|
|
24
|
-
Implemented:
|
|
25
|
-
* Retriving service urls from environment
|
|
26
|
-
* Retry - Using https://github.com/softonic/axios-retry
|
|
27
|
-
* Caching - TBD
|
|
28
|
-
* Syntatic response for fail - TBD
|
|
29
|
-
* Circuit Breaking - TBD
|
|
30
|
-
|
|
31
|
-
The API is just like [axios](https://github.com/axios/axios) api but the creation of new instance **must** have either `serviceName` or `serviceUrl` in options.
|
|
32
|
-
|
|
33
|
-
In case `serviceName` used the constractor will look for an environment varible with the the name `<SERVICE_NAME>_SERVICE_HOST`.
|
|
34
|
-
|
|
35
|
-
For Example:
|
|
36
|
-
```
|
|
37
|
-
const { Network } = require('@autofleet/node-common');
|
|
38
|
-
|
|
39
|
-
n = new Network({ serviceName: 'TEST' });
|
|
40
|
-
|
|
41
|
-
n.get('/posts/1');
|
|
42
|
-
```
|
|
43
|
-
.env file:
|
|
44
|
-
```
|
|
45
|
-
RIDE_SERVICE_HOST=jsonplaceholder.typicode.com
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
To learn more [click here](https://blog.risingstack.com/designing-microservices-architecture-for-failure/).
|
|
49
|
-
|
|
50
21
|
## Settings
|
|
51
22
|
|
|
52
23
|
### Adding settings
|
package/index.js
CHANGED
|
@@ -1,17 +1,7 @@
|
|
|
1
|
-
const Network = require('./network');
|
|
2
|
-
const Logger = require('./logger');
|
|
3
1
|
const Settings = require('./settings');
|
|
4
|
-
const client = require('./delorean-client');
|
|
5
2
|
const Router = require('./router');
|
|
6
|
-
const Queue = require('./queue');
|
|
7
3
|
|
|
8
4
|
module.exports = {
|
|
9
|
-
Network,
|
|
10
|
-
Logger,
|
|
11
5
|
Settings,
|
|
12
|
-
Delorean: {
|
|
13
|
-
client,
|
|
14
|
-
},
|
|
15
6
|
Router,
|
|
16
|
-
Queue,
|
|
17
7
|
};
|
package/package.json
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autofleet/node-common",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"coverage": "jest --coverage --forceExit --runInBand",
|
|
7
7
|
"test": "jest --forceExit --runInBand",
|
|
8
8
|
"test-auto": "jest --watch --runInBand",
|
|
9
|
-
"linter": "
|
|
9
|
+
"linter": "eslint ."
|
|
10
10
|
},
|
|
11
11
|
"jest": {
|
|
12
|
-
"
|
|
13
|
-
|
|
12
|
+
"setupFilesAfterEnv": [
|
|
13
|
+
"jest-extended"
|
|
14
|
+
],
|
|
15
|
+
"testEnvironmentOptions": {
|
|
16
|
+
"url": "http://localhost:8085/"
|
|
17
|
+
}
|
|
14
18
|
},
|
|
15
19
|
"repository": {
|
|
16
20
|
"type": "git",
|
|
@@ -23,27 +27,24 @@
|
|
|
23
27
|
},
|
|
24
28
|
"homepage": "https://github.com/Autofleet/node-common",
|
|
25
29
|
"dependencies": {
|
|
26
|
-
"
|
|
27
|
-
"axios": "^0.18.0",
|
|
30
|
+
"axios": "^0.28.1",
|
|
28
31
|
"axios-retry": "^3.1.0",
|
|
29
32
|
"dotenv": "^5.0.1",
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"jest": "^22.4.4",
|
|
33
|
-
"mock-socket": "^7.1.0",
|
|
34
|
-
"node-cache": "^4.2.0",
|
|
35
|
-
"node-resque": "^5.4.1",
|
|
36
|
-
"portfinder": "^1.0.13",
|
|
37
|
-
"qs": "^6.5.2",
|
|
38
|
-
"timekeeper": "^2.1.2",
|
|
39
|
-
"winston": "^3.0.0",
|
|
40
|
-
"ws": "^5.2.1"
|
|
33
|
+
"express": "^4.19.2",
|
|
34
|
+
"node-cache": "^4.2.0"
|
|
41
35
|
},
|
|
42
36
|
"devDependencies": {
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"eslint
|
|
37
|
+
"@autofleet/logger": "^4.0.6",
|
|
38
|
+
"@autofleet/network": "^1.5.1",
|
|
39
|
+
"eslint": "^8.57.0",
|
|
40
|
+
"eslint-config-airbnb-base": "^15.0.0",
|
|
41
|
+
"eslint-plugin-import": "^2.29.1",
|
|
42
|
+
"jest": "^29.7.0",
|
|
46
43
|
"jest-extended": "^0.7.1",
|
|
47
44
|
"nock": "^10.0.2"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@autofleet/logger": ">=4.0.6",
|
|
48
|
+
"@autofleet/network": ">=1.0.0"
|
|
48
49
|
}
|
|
49
50
|
}
|
package/router/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const { Router } = require('express');
|
|
2
|
-
const
|
|
2
|
+
const { default: Logger } = require('@autofleet/logger');
|
|
3
|
+
|
|
4
|
+
const logger = Logger();
|
|
3
5
|
|
|
4
6
|
const METHODS = [
|
|
5
7
|
'all',
|
|
@@ -12,8 +14,16 @@ const METHODS = [
|
|
|
12
14
|
'head',
|
|
13
15
|
];
|
|
14
16
|
|
|
17
|
+
/**
|
|
18
|
+
* @param {(
|
|
19
|
+
* req: import('express').Request,
|
|
20
|
+
* res: import('express').Response,
|
|
21
|
+
* nextFn: import('express').NextFunction
|
|
22
|
+
* ) => void | PromiseLike<void>} func
|
|
23
|
+
* @returns {import('express').Handler}
|
|
24
|
+
*/
|
|
15
25
|
// eslint-disable-next-line consistent-return
|
|
16
|
-
const AfEntryPoint = func => async (req, res, next) => {
|
|
26
|
+
const AfEntryPoint = (func) => async (req, res, next) => {
|
|
17
27
|
try {
|
|
18
28
|
await func(req, res, next);
|
|
19
29
|
} catch (e) {
|
|
@@ -25,6 +35,11 @@ const AfEntryPoint = func => async (req, res, next) => {
|
|
|
25
35
|
}
|
|
26
36
|
};
|
|
27
37
|
|
|
38
|
+
/**
|
|
39
|
+
* @param {Parameters<typeof Router>[0]} [options]
|
|
40
|
+
* @returns {Router} a monkey-patched express router that
|
|
41
|
+
* will handle async routes (and force a 400 status for codes <500)
|
|
42
|
+
*/
|
|
28
43
|
const AfRouter = (options) => {
|
|
29
44
|
const myRouter = Router({ mergeParams: true, ...options });
|
|
30
45
|
METHODS.map((method) => {
|
package/settings/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const EventEmitter = require('events');
|
|
2
2
|
const NodeCache = require('node-cache');
|
|
3
|
-
const Network = require('
|
|
4
|
-
const Logger = require('
|
|
3
|
+
const Network = require('@autofleet/network');
|
|
4
|
+
const { default: Logger } = require('@autofleet/logger');
|
|
5
5
|
// const keysMap = require('./map');
|
|
6
6
|
|
|
7
7
|
const util = require('util');
|
|
@@ -14,9 +14,9 @@ require('dotenv').config();
|
|
|
14
14
|
const fiveMinutes = 60 * 5;
|
|
15
15
|
const waitingToNetwork = 'waitingToNetwork';
|
|
16
16
|
|
|
17
|
-
const findUrl = serviceUrl => serviceUrl
|
|
18
|
-
(process.env.SETTING_MS_SERVICE_HOST
|
|
19
|
-
(process.env.NODE_ENV !== 'test' ? 'http://setting-ms.autofleet.io/' : 'http://localhost:9999/');
|
|
17
|
+
const findUrl = (serviceUrl) => serviceUrl
|
|
18
|
+
|| (process.env.SETTING_MS_SERVICE_HOST?.length > 0 ? `http://${process.env.SETTING_MS_SERVICE_HOST}/` : undefined)
|
|
19
|
+
|| (process.env.NODE_ENV !== 'test' ? 'http://setting-ms.autofleet.io/' : 'http://localhost:9999/');
|
|
20
20
|
|
|
21
21
|
class SettingsManager {
|
|
22
22
|
static get NEVER_DEFAULT_VALUE() {
|
|
@@ -33,6 +33,9 @@ class SettingsManager {
|
|
|
33
33
|
return returnValue;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* @param {{ serviceUrl: string; ttl?: number }} param0
|
|
38
|
+
*/
|
|
36
39
|
constructor({ serviceUrl, ttl = fiveMinutes } = {}) {
|
|
37
40
|
const localServiceUrl = findUrl(serviceUrl);
|
|
38
41
|
|
package/settings/index.test.js
CHANGED
|
@@ -72,7 +72,6 @@ describe('Settings', () => {
|
|
|
72
72
|
expect(value2).toEqual('value1');
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
|
|
76
75
|
it('Use only one request for similar keys', async () => {
|
|
77
76
|
const m = mockSetting('key1EM', 'value1EM');
|
|
78
77
|
const settings = new Settings({
|
|
@@ -119,7 +118,7 @@ describe('Settings', () => {
|
|
|
119
118
|
.rejects.toEqual(new Error('Cannot find value from network or cache, default value is set to never'));
|
|
120
119
|
});
|
|
121
120
|
|
|
122
|
-
it('Throws an error if network error and no default value and success the next time',
|
|
121
|
+
it('Throws an error if network error and no default value and success the next time', (done) => {
|
|
123
122
|
const settings = new Settings({
|
|
124
123
|
serviceUrl: `http://${serviceUrl}/`,
|
|
125
124
|
});
|
|
@@ -128,7 +127,6 @@ describe('Settings', () => {
|
|
|
128
127
|
expect(f())
|
|
129
128
|
.rejects.toEqual(new Error('Cannot find value from network or cache, default value is set to never'));
|
|
130
129
|
|
|
131
|
-
|
|
132
130
|
setTimeout(async () => {
|
|
133
131
|
const m = mockSetting('key1', 'value1');
|
|
134
132
|
expect(await settings.get('key1', settings.NEVER_DEFAULT_VALUE)).toEqual('value1');
|
package/delorean-client/index.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
const tk = require('timekeeper');
|
|
2
|
-
const WebSocket = require('ws');
|
|
3
|
-
const Logger = require('../logger');
|
|
4
|
-
|
|
5
|
-
const logger = Logger();
|
|
6
|
-
|
|
7
|
-
const createWs = (host, resolve) => {
|
|
8
|
-
const ws = new WebSocket(`ws://${host}`);
|
|
9
|
-
|
|
10
|
-
ws.on('open', () => {
|
|
11
|
-
ws.send(JSON.stringify({ type: 'ping' }));
|
|
12
|
-
if (resolve) {
|
|
13
|
-
resolve(true);
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
ws.on('error', (error) => {
|
|
18
|
-
logger.error('ERROR', error);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
ws.on('message', (data) => {
|
|
22
|
-
const jsonMsg = JSON.parse(data);
|
|
23
|
-
|
|
24
|
-
if (jsonMsg.type === 'freeze') {
|
|
25
|
-
logger.info('I know you just sent me back to the future, but I\'m back.I\'m back from the future.', { goingBackTo: jsonMsg.time });
|
|
26
|
-
tk.freeze(new Date(jsonMsg.time));
|
|
27
|
-
} else if (jsonMsg.type === 'travel') {
|
|
28
|
-
tk.travel(jsonMsg.time);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
ws.on('close', () => {
|
|
33
|
-
setTimeout(() => {
|
|
34
|
-
if (process.env.TIME_TRAVEL_MS_SERVICE_HOST === host) {
|
|
35
|
-
createWs(host, null);
|
|
36
|
-
}
|
|
37
|
-
}, 500);
|
|
38
|
-
});
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
module.exports = () => new Promise((resolve) => {
|
|
42
|
-
const { TIME_TRAVEL_MS_SERVICE_HOST, SHOULD_TIME_TRAVEL } = process.env;
|
|
43
|
-
if (TIME_TRAVEL_MS_SERVICE_HOST && SHOULD_TIME_TRAVEL) {
|
|
44
|
-
logger.info('If my calculations are correct, when this baby hits 88 miles per hour... you\'re gonna see some serious shit.', {
|
|
45
|
-
TIME_TRAVEL_MS_SERVICE_HOST,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
createWs(TIME_TRAVEL_MS_SERVICE_HOST, resolve);
|
|
49
|
-
} else {
|
|
50
|
-
resolve(false);
|
|
51
|
-
}
|
|
52
|
-
});
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
const tk = require('timekeeper');
|
|
2
|
-
const deloreanClient = require('./index');
|
|
3
|
-
const portfinder = require('portfinder');
|
|
4
|
-
const WebSocket = require('ws');
|
|
5
|
-
|
|
6
|
-
const { env } = process;
|
|
7
|
-
|
|
8
|
-
const createUrl = async () => {
|
|
9
|
-
const port = await portfinder.getPortPromise();
|
|
10
|
-
return {
|
|
11
|
-
url: `127.0.0.1:${port}`,
|
|
12
|
-
port,
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const mockServer = (port, time, type) => {
|
|
17
|
-
const wss = new WebSocket.Server({ port });
|
|
18
|
-
let gotPing = false;
|
|
19
|
-
|
|
20
|
-
wss.on('connection', (ws) => {
|
|
21
|
-
ws.on('message', (message) => {
|
|
22
|
-
const json = JSON.parse(message);
|
|
23
|
-
if (json.type === 'ping') {
|
|
24
|
-
gotPing = true;
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
ws.send(JSON.stringify({
|
|
29
|
-
type,
|
|
30
|
-
time,
|
|
31
|
-
}));
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
close: () => wss.close(),
|
|
36
|
-
gotPing: () => gotPing,
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
describe('Logger', () => {
|
|
41
|
-
beforeEach(() => {
|
|
42
|
-
tk.reset();
|
|
43
|
-
env.SHOULD_TIME_TRAVEL = true;
|
|
44
|
-
env.TIME_TRAVEL_MS_SERVICE_HOST = '';
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
afterAll(() => {
|
|
48
|
-
env.SHOULD_TIME_TRAVEL = false;
|
|
49
|
-
env.TIME_TRAVEL_MS_SERVICE_HOST = '0';
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test('Conects to server, sends ping an freeze time', async (done) => {
|
|
53
|
-
const { url, port } = await createUrl();
|
|
54
|
-
env.TIME_TRAVEL_MS_SERVICE_HOST = url;
|
|
55
|
-
const mockedDate = new Date('2013-10-08 12:32:50 +0530').getTime();
|
|
56
|
-
const { close, gotPing } = mockServer(port, mockedDate, 'freeze');
|
|
57
|
-
await deloreanClient();
|
|
58
|
-
|
|
59
|
-
setTimeout(() => {
|
|
60
|
-
expect(Date.now()).toEqual(mockedDate);
|
|
61
|
-
expect(gotPing()).toBeTruthy();
|
|
62
|
-
close();
|
|
63
|
-
done();
|
|
64
|
-
}, 100);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('Conects to server, sends ping an freeze time', async (done) => {
|
|
68
|
-
const { url, port } = await createUrl();
|
|
69
|
-
env.TIME_TRAVEL_MS_SERVICE_HOST = url;
|
|
70
|
-
const mockedDate = new Date('2013-10-08 12:32:50 +0530').getTime();
|
|
71
|
-
const { close, gotPing } = mockServer(port, mockedDate, 'travel');
|
|
72
|
-
await deloreanClient();
|
|
73
|
-
|
|
74
|
-
setTimeout(() => {
|
|
75
|
-
expect(Date.now()).toBeGreaterThan(mockedDate);
|
|
76
|
-
expect(Date.now()).toBeLessThan(new Date('2013-10-08 13:32:50 +0530').getTime());
|
|
77
|
-
expect(gotPing()).toBeTruthy();
|
|
78
|
-
close();
|
|
79
|
-
done();
|
|
80
|
-
}, 100);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('Wont start without TIME_TRAVEL_MS_SERVICE_HOST', async () => {
|
|
84
|
-
const { port } = await createUrl();
|
|
85
|
-
delete env.TIME_TRAVEL_MS_SERVICE_HOST;
|
|
86
|
-
const mockedDate = new Date('2013-10-08 12:32:50 +0530').getTime();
|
|
87
|
-
const { close } = mockServer(port, mockedDate, 'travel');
|
|
88
|
-
const result = await deloreanClient();
|
|
89
|
-
|
|
90
|
-
expect(result).toBeFalsy();
|
|
91
|
-
close();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('Wont start without SHOULD_TIME_TRAVEL', async () => {
|
|
95
|
-
const { url, port } = await createUrl();
|
|
96
|
-
env.TIME_TRAVEL_MS_SERVICE_HOST = url;
|
|
97
|
-
delete env.SHOULD_TIME_TRAVEL;
|
|
98
|
-
const mockedDate = new Date('2013-10-08 12:32:50 +0530').getTime();
|
|
99
|
-
const { close } = mockServer(port, mockedDate, 'travel');
|
|
100
|
-
const result = await deloreanClient();
|
|
101
|
-
|
|
102
|
-
expect(result).toBeFalsy();
|
|
103
|
-
close();
|
|
104
|
-
});
|
|
105
|
-
});
|
package/logger/example.js
DELETED
package/logger/index.js
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
const winston = require('winston');
|
|
2
|
-
|
|
3
|
-
const { createLogger } = winston;
|
|
4
|
-
require('dotenv').config();
|
|
5
|
-
|
|
6
|
-
const { env } = process;
|
|
7
|
-
|
|
8
|
-
const infoEnvNmaes = [
|
|
9
|
-
'production',
|
|
10
|
-
'staging',
|
|
11
|
-
'test',
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
const serializeError = (error) => {
|
|
15
|
-
const fields = ['code', 'status', 'statusCode', 'message', 'messageData', 'name', 'stack', 'stackTrace'];
|
|
16
|
-
return fields.reduce((v, field) => ({ ...v, [field]: error[field] }), {});
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const getLevel = (logLevel) => {
|
|
20
|
-
if (logLevel) return logLevel;
|
|
21
|
-
if (env.LOG_LEVEL) return env.LOG_LEVEL;
|
|
22
|
-
if (infoEnvNmaes.includes(env.NODE_ENV)) return 'info';
|
|
23
|
-
if (env.NODE_ENV === 'development') return 'debug';
|
|
24
|
-
return 'debug';
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const enumerateErrorFormat = winston.format((info) => {
|
|
28
|
-
if (info.message instanceof Error) {
|
|
29
|
-
info.message = Object.assign({ // eslint-disable-line
|
|
30
|
-
message: info.message.message,
|
|
31
|
-
stack: info.message.stack,
|
|
32
|
-
}, info.message);
|
|
33
|
-
}
|
|
34
|
-
if (info instanceof Error) {
|
|
35
|
-
return Object.assign({
|
|
36
|
-
message: info.message,
|
|
37
|
-
stack: info.stack,
|
|
38
|
-
}, info);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// eslint-disable-next-line array-callback-return
|
|
42
|
-
Object.keys(info).map((k) => {
|
|
43
|
-
if (info[k] instanceof Error) {
|
|
44
|
-
// eslint-disable-next-line no-param-reassign
|
|
45
|
-
info[k] = serializeError(info[k]);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
return info;
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const getFormat = () => {
|
|
53
|
-
if (env.NODE_ENV === 'production') {
|
|
54
|
-
return winston.format.combine(
|
|
55
|
-
enumerateErrorFormat(),
|
|
56
|
-
winston.format.json(),
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return winston.format.combine(
|
|
61
|
-
enumerateErrorFormat(),
|
|
62
|
-
winston.format.splat(),
|
|
63
|
-
winston.format.colorize(),
|
|
64
|
-
winston.format.simple(),
|
|
65
|
-
);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const createLoggerInstance = (level, exceptionHandlers) => {
|
|
69
|
-
const transports = [new winston.transports.Console()];
|
|
70
|
-
|
|
71
|
-
const logger = createLogger({
|
|
72
|
-
level,
|
|
73
|
-
format: getFormat(),
|
|
74
|
-
transports,
|
|
75
|
-
exceptionHandlers,
|
|
76
|
-
exitOnError: false,
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
logger.httpMorganInfo = (options) => {
|
|
80
|
-
logger.info(options.url, {
|
|
81
|
-
httpRequest: {
|
|
82
|
-
status: options.status,
|
|
83
|
-
requestUrl: options.url,
|
|
84
|
-
requestMethod: options.method,
|
|
85
|
-
responseSize: options['content-length'],
|
|
86
|
-
latency: {
|
|
87
|
-
seconds: parseInt(options['response-time'], 10) / 1000,
|
|
88
|
-
nanos: parseInt(options['response-time'], 10) * 1000000,
|
|
89
|
-
},
|
|
90
|
-
userAgent: options['user-agent'],
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
};
|
|
94
|
-
if (env.NODE_ENV !== 'production') {
|
|
95
|
-
logger.httpMorganInfo = undefined;
|
|
96
|
-
}
|
|
97
|
-
return logger;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
class LoggerInstanceManager {
|
|
101
|
-
static getLoggerInstance(logLevel) {
|
|
102
|
-
if (!LoggerInstanceManager.instances) {
|
|
103
|
-
LoggerInstanceManager.instances = {};
|
|
104
|
-
}
|
|
105
|
-
const level = getLevel(logLevel);
|
|
106
|
-
if (!LoggerInstanceManager.instances[level]) {
|
|
107
|
-
LoggerInstanceManager.instances[level] = createLoggerInstance(level);
|
|
108
|
-
}
|
|
109
|
-
return LoggerInstanceManager.instances[level];
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
module.exports = LoggerInstanceManager.getLoggerInstance;
|
package/logger/index.test.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
const Logger = require('./index');
|
|
2
|
-
|
|
3
|
-
const { env } = process;
|
|
4
|
-
|
|
5
|
-
describe('Logger', () => {
|
|
6
|
-
it('it`s default level is info', () => {
|
|
7
|
-
env.NODE_ENV = '';
|
|
8
|
-
const logger = Logger();
|
|
9
|
-
expect(logger).toBeDefined();
|
|
10
|
-
expect(logger.level).toBe('debug');
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('its default develpment level is debug', () => {
|
|
14
|
-
env.NODE_ENV = 'development';
|
|
15
|
-
const logger = Logger();
|
|
16
|
-
expect(logger.level).toBe('debug');
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('its default test level is info', () => {
|
|
20
|
-
env.NODE_ENV = 'test';
|
|
21
|
-
const logger = Logger();
|
|
22
|
-
expect(logger.level).toBe('info');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('its default production level is info', () => {
|
|
26
|
-
env.NODE_ENV = 'production';
|
|
27
|
-
const logger = Logger();
|
|
28
|
-
expect(logger.level).toBe('info');
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('its level can be override by LOG_LEVEL env variable', () => {
|
|
32
|
-
env.NODE_ENV = 'development';
|
|
33
|
-
env.LOG_LEVEL = 'warn';
|
|
34
|
-
const logger = Logger();
|
|
35
|
-
expect(logger.level).toBe('warn');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('its level can be override by function variable', () => {
|
|
39
|
-
env.NODE_ENV = 'production';
|
|
40
|
-
env.LOG_LEVEL = 'warn';
|
|
41
|
-
const logger = Logger('silly');
|
|
42
|
-
expect(logger.level).toBe('silly');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('its has functions for each level', () => {
|
|
46
|
-
const logger = Logger();
|
|
47
|
-
expect(logger.error).toBeFunction();
|
|
48
|
-
expect(logger.warn).toBeFunction();
|
|
49
|
-
expect(logger.info).toBeFunction();
|
|
50
|
-
expect(logger.verbose).toBeFunction();
|
|
51
|
-
expect(logger.debug).toBeFunction();
|
|
52
|
-
expect(logger.silly).toBeFunction();
|
|
53
|
-
});
|
|
54
|
-
});
|
package/network/index.js
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
const axios = require('axios');
|
|
2
|
-
const axiosRetry = require('axios-retry');
|
|
3
|
-
const Logger = require('../logger');
|
|
4
|
-
const qs = require('qs');
|
|
5
|
-
const httpAdapter = require('axios/lib/adapters/http');
|
|
6
|
-
require('dotenv').config();
|
|
7
|
-
/**
|
|
8
|
-
* Add support for nock testing
|
|
9
|
-
* see s://github.com/axios/axios/issues/305
|
|
10
|
-
*/
|
|
11
|
-
if (process.env.NODE_ENV === 'test') {
|
|
12
|
-
axios.defaults.adapter = httpAdapter;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const HTTPMethods = [
|
|
16
|
-
'get',
|
|
17
|
-
'post',
|
|
18
|
-
'delete',
|
|
19
|
-
'head',
|
|
20
|
-
'put',
|
|
21
|
-
'patch',
|
|
22
|
-
'options',
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
const defaultSettings = {
|
|
26
|
-
timeout: 10000,
|
|
27
|
-
headers: { 'X-AF-AUTH': 'ANYONE' },
|
|
28
|
-
paramsSerializer: params => qs.stringify(params, { arrayFormat: 'brackets' }),
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const getHttpRequestObject = response => ({
|
|
32
|
-
status: response.status ? response.status : undefined,
|
|
33
|
-
requestUrl: response.config ? response.config.url : undefined,
|
|
34
|
-
query: response.config ? response.config.params : undefined,
|
|
35
|
-
body: response.config ? response.config.data : undefined,
|
|
36
|
-
requestMethod: response.config ? response.config.method.toUpperCase() : undefined,
|
|
37
|
-
message: response.message ? response.message.toString() : undefined,
|
|
38
|
-
stack: response.stack ? response.stack.toString() : undefined,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
module.exports = class Network {
|
|
42
|
-
constructor(settings = {}) {
|
|
43
|
-
this.settings = Object.assign({}, defaultSettings, settings);
|
|
44
|
-
this.createBaseUrl();
|
|
45
|
-
|
|
46
|
-
this.axios = axios.create(this.settings);
|
|
47
|
-
this.addRetry(settings);
|
|
48
|
-
this.buildClassHttpMethods();
|
|
49
|
-
this.addLogs();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
addRetry(settings) {
|
|
53
|
-
axiosRetry(this.axios, {
|
|
54
|
-
retries: 0,
|
|
55
|
-
retryDelay: axiosRetry.exponentialDelay,
|
|
56
|
-
...settings,
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
addLogs() {
|
|
61
|
-
const logger = Logger();
|
|
62
|
-
this.axios.interceptors.request.use((request) => {
|
|
63
|
-
logger.info(`Start Request: [${request.method.toUpperCase()}] ${request.baseURL} ${request.url}`, {
|
|
64
|
-
headers: request.headers,
|
|
65
|
-
query: request.params,
|
|
66
|
-
body: request.data,
|
|
67
|
-
});
|
|
68
|
-
return request;
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
this.axios.interceptors.response.use((response) => {
|
|
72
|
-
logger.info(`Finish Request: ${response.config.method.toUpperCase()} ${response.config.url}`, getHttpRequestObject(response));
|
|
73
|
-
return response;
|
|
74
|
-
}, (error) => {
|
|
75
|
-
logger.error('Finish Request with error', { data: error.response ? error.response.data : undefined, ...getHttpRequestObject(error.response || error) });
|
|
76
|
-
return error;
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
createBaseUrl() {
|
|
81
|
-
if (!this.settings.serviceUrl && !this.settings.serviceName) {
|
|
82
|
-
throw new Error('At least one of the settings Missing serviceUrl or serviceName');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const { settings } = this;
|
|
86
|
-
if (settings.serviceUrl) {
|
|
87
|
-
settings.baseURL = settings.serviceUrl;
|
|
88
|
-
} else if (settings.serviceName) {
|
|
89
|
-
const envServiceHostName = `${settings.serviceName}_SERVICE_HOST`;
|
|
90
|
-
settings.baseURL = `http://${process.env[envServiceHostName]}`;
|
|
91
|
-
if (!settings.baseURL) {
|
|
92
|
-
throw new Error(`Missing environment variable: ${envServiceHostName}`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Build class methods that wrap axios methods
|
|
99
|
-
*/
|
|
100
|
-
buildClassHttpMethods() {
|
|
101
|
-
HTTPMethods.forEach((method) => {
|
|
102
|
-
this[method] = (...args) => this.axios[method](...args);
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
};
|
package/network/index.test.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
const nock = require('nock');
|
|
2
|
-
const Network = require('./index');
|
|
3
|
-
|
|
4
|
-
nock('http://www.google.com')
|
|
5
|
-
.get('/resource')
|
|
6
|
-
.reply(200, { one: 1 });
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
nock('https://www.apple.com')
|
|
10
|
-
.get('/resource')
|
|
11
|
-
.reply(200, { two: 2 });
|
|
12
|
-
|
|
13
|
-
describe('Network', () => {
|
|
14
|
-
it('Making requests by service name from ENV', async () => {
|
|
15
|
-
process.env.TEST_SERVICE_HOST = 'www.google.com';
|
|
16
|
-
const n = new Network({ serviceName: 'TEST' });
|
|
17
|
-
const response = await n.get('/resource');
|
|
18
|
-
expect(response).toBeDefined();
|
|
19
|
-
expect(response.data).toBeDefined();
|
|
20
|
-
expect(response.data.one).toBe(1);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('Making requests by service name from url', async () => {
|
|
24
|
-
const n = new Network({ serviceUrl: 'https://www.apple.com' });
|
|
25
|
-
const response = await n.get('/resource');
|
|
26
|
-
expect(response).toBeDefined();
|
|
27
|
-
expect(response.data).toBeDefined();
|
|
28
|
-
expect(response.data.two).toBe(2);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
// it('Getting 404', async () => {
|
|
32
|
-
// const n = new Network({ serviceUrl: 'https://github.com/' });
|
|
33
|
-
|
|
34
|
-
// const action = async () => n.get('/404', {
|
|
35
|
-
// query: {
|
|
36
|
-
// a: 'b',
|
|
37
|
-
// },
|
|
38
|
-
// });
|
|
39
|
-
|
|
40
|
-
// expect(action()).rejects.toThrow(Error);
|
|
41
|
-
// });
|
|
42
|
-
|
|
43
|
-
test('Retry 1 time', async () => {
|
|
44
|
-
nock('https://www.apple2.com')
|
|
45
|
-
.get('/resource')
|
|
46
|
-
.once()
|
|
47
|
-
.reply(500);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
nock('https://www.apple2.com')
|
|
51
|
-
.get('/resource')
|
|
52
|
-
.once()
|
|
53
|
-
.reply(200, { two: 2 });
|
|
54
|
-
|
|
55
|
-
const n = new Network({ serviceUrl: 'https://www.apple2.com' });
|
|
56
|
-
const response = await n.get('/resource', {
|
|
57
|
-
query: {
|
|
58
|
-
a: 'b',
|
|
59
|
-
},
|
|
60
|
-
'axios-retry': {
|
|
61
|
-
retries: 1,
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
expect(response).toBeDefined();
|
|
66
|
-
expect(response.data).toBeDefined();
|
|
67
|
-
expect(response.data.two).toBe(2);
|
|
68
|
-
});
|
|
69
|
-
});
|
package/queue/index.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
const NodeResque = require('node-resque');
|
|
2
|
-
const logger = require('../logger')();
|
|
3
|
-
|
|
4
|
-
const redisBaseSettings = {
|
|
5
|
-
pkg: 'ioredis',
|
|
6
|
-
host: '127.0.0.1',
|
|
7
|
-
password: '',
|
|
8
|
-
port: 6379,
|
|
9
|
-
database: 0,
|
|
10
|
-
namespace: 'resque',
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
class AfQueue {
|
|
14
|
-
static redisParams(params) {
|
|
15
|
-
return Object.assign({}, redisBaseSettings, params);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
constructor() {
|
|
19
|
-
this.inited = false;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async exitHandler() {
|
|
23
|
-
process.stdin.resume();
|
|
24
|
-
|
|
25
|
-
if (this.existing) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
this.existing = true;
|
|
30
|
-
logger.info('Will remove AfQueue');
|
|
31
|
-
|
|
32
|
-
if (this.worker) {
|
|
33
|
-
await this.worker.end();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (this.queue) {
|
|
37
|
-
await this.queue.end();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (this.scheduler) {
|
|
41
|
-
await this.scheduler.end();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
process.exit();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async init(connectionDetails, workerSettings, shouldCreateQueue = true) {
|
|
48
|
-
if (this.inited) {
|
|
49
|
-
throw new Error('AfQueue: Already inited');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const redisConf = Object.assign({}, redisBaseSettings, connectionDetails);
|
|
53
|
-
this.inited = true;
|
|
54
|
-
const promiseArr = [];
|
|
55
|
-
if (shouldCreateQueue) {
|
|
56
|
-
await this.startQueue(redisConf);
|
|
57
|
-
promiseArr.push(this.startScheduler(redisConf));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (workerSettings) {
|
|
61
|
-
promiseArr.push(this.startWorker(redisConf, workerSettings));
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
process.on('SIGINT', this.exitHandler.bind(this));
|
|
65
|
-
process.on('SIGTERM', this.exitHandler.bind(this));
|
|
66
|
-
|
|
67
|
-
return Promise.all(promiseArr);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async getQueue() {
|
|
71
|
-
if (!this.queue) {
|
|
72
|
-
await this.startQueue();
|
|
73
|
-
}
|
|
74
|
-
return this.queue;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async startScheduler(connectionDetails) {
|
|
78
|
-
this.scheduler = new NodeResque.Scheduler({
|
|
79
|
-
connection: AfQueue.redisParams(connectionDetails),
|
|
80
|
-
});
|
|
81
|
-
await this.scheduler.connect();
|
|
82
|
-
this.scheduler.start();
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async startQueue(connectionDetails) {
|
|
86
|
-
this.queue = new NodeResque.Queue({
|
|
87
|
-
connection: AfQueue.redisParams(connectionDetails),
|
|
88
|
-
});
|
|
89
|
-
await this.queue.connect();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async startWorker(connectionDetails, { queues, jobs }) {
|
|
93
|
-
this.worker = new NodeResque.MultiWorker({
|
|
94
|
-
connection: AfQueue.redisParams(connectionDetails),
|
|
95
|
-
queues,
|
|
96
|
-
minTaskProcessors: 1,
|
|
97
|
-
maxTaskProcessors: 10,
|
|
98
|
-
checkTimeout: 5000,
|
|
99
|
-
maxEventLoopDelay: 10,
|
|
100
|
-
}, jobs);
|
|
101
|
-
|
|
102
|
-
await this.worker.start();
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
module.exports = new AfQueue();
|