@autofleet/node-common 1.1.2 → 1.1.4
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/.circleci/config.yml +26 -0
- package/.eslintrc.json +0 -3
- package/README.md +1 -1
- package/index.js +8 -3
- package/logger/example.js +4 -5
- package/logger/index.js +37 -50
- package/logger/index.test.js +38 -92
- package/network/index.js +55 -23
- package/network/index.test.js +57 -20
- package/package.json +5 -3
- package/settings/example.js +30 -0
- package/settings/index.js +52 -0
- package/settings/index.test.js +74 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
jobs:
|
|
3
|
+
test:
|
|
4
|
+
docker:
|
|
5
|
+
- image: circleci/node:8.9.4
|
|
6
|
+
steps:
|
|
7
|
+
- checkout
|
|
8
|
+
- restore_cache:
|
|
9
|
+
keys:
|
|
10
|
+
- v1-dependencies-{{ checksum "package.json" }}
|
|
11
|
+
# fallback to using the latest cache if no exact match is found
|
|
12
|
+
- v1-dependencies-
|
|
13
|
+
- run: npm i
|
|
14
|
+
- save_cache:
|
|
15
|
+
paths:
|
|
16
|
+
- node_modules
|
|
17
|
+
key: v1-dependencies-{{ checksum "package.json" }}
|
|
18
|
+
- run: npm run coverage
|
|
19
|
+
- run: rm -rf ./coverage
|
|
20
|
+
- run: npm run linter
|
|
21
|
+
|
|
22
|
+
workflows:
|
|
23
|
+
version: 2
|
|
24
|
+
build_and_test:
|
|
25
|
+
jobs:
|
|
26
|
+
- test
|
package/.eslintrc.json
CHANGED
package/README.md
CHANGED
package/index.js
CHANGED
package/logger/example.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
const logger = require('./index')(
|
|
1
|
+
const logger = require('./index')();
|
|
2
2
|
|
|
3
|
-
logger.info('it is working')
|
|
4
|
-
logger.error('errors displayed in logs/error.log')
|
|
5
|
-
logger.debug('debug with object', { a: 5 })
|
|
6
|
-
logger.debug('debug with 2 objects', { 1: { a: 5 }, 2: { b: 2 } })
|
|
3
|
+
logger.info('it is working');
|
|
4
|
+
logger.error('errors displayed in logs/error.log');
|
|
5
|
+
logger.debug('debug with object', { a: 5 });
|
package/logger/index.js
CHANGED
|
@@ -1,64 +1,51 @@
|
|
|
1
|
-
const
|
|
1
|
+
const winston = require('winston');
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { createLogger } = winston;
|
|
4
|
+
require('dotenv').config();
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
const { env } = process;
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
const infoEnvNmaes = [
|
|
9
|
+
'production',
|
|
10
|
+
'staging',
|
|
11
|
+
'test',
|
|
12
|
+
];
|
|
8
13
|
|
|
9
14
|
const getLevel = (logLevel) => {
|
|
10
|
-
if (logLevel) return logLevel
|
|
11
|
-
if (env.LOG_LEVEL) return env.LOG_LEVEL
|
|
12
|
-
if (env.NODE_ENV
|
|
13
|
-
|
|
14
|
-
return '
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (env.USE_LOG_FILES === 'true') {
|
|
21
|
-
transporters = transporters.concat([
|
|
22
|
-
new transports.File({ filename: 'logs/error.log', level: 'error' }),
|
|
23
|
-
new transports.File({ filename: 'logs/all.log' }),
|
|
24
|
-
])
|
|
15
|
+
if (logLevel) return logLevel;
|
|
16
|
+
if (env.LOG_LEVEL) return env.LOG_LEVEL;
|
|
17
|
+
if (infoEnvNmaes.includes(env.NODE_ENV)) return 'info';
|
|
18
|
+
if (env.NODE_ENV === 'development') return 'debug';
|
|
19
|
+
return 'debug';
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const getFormat = () => {
|
|
23
|
+
if (env.NODE_ENV === 'producation') {
|
|
24
|
+
return winston.format.json();
|
|
25
25
|
}
|
|
26
|
-
return transporters
|
|
27
|
-
}
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
delete info.message
|
|
36
|
-
delete info.timestamp
|
|
37
|
-
const params = JSON.stringify(info, null, 2)
|
|
38
|
-
return `${level}: ${message} ${timestamp} \n ${params} \n *****************`
|
|
39
|
-
})
|
|
27
|
+
return winston.format.combine(
|
|
28
|
+
winston.format.splat(),
|
|
29
|
+
winston.format.colorize(),
|
|
30
|
+
winston.format.simple(),
|
|
31
|
+
);
|
|
32
|
+
};
|
|
40
33
|
|
|
34
|
+
const createLoggerInstance = (level, transports) => {
|
|
41
35
|
const logger = createLogger({
|
|
42
|
-
format: combine(
|
|
43
|
-
format.colorize(),
|
|
44
|
-
// format.json(),
|
|
45
|
-
format.simple(),
|
|
46
|
-
// prettyPrint(),
|
|
47
|
-
timestamp(),
|
|
48
|
-
customFormat,
|
|
49
|
-
),
|
|
50
36
|
level,
|
|
51
|
-
|
|
52
|
-
|
|
37
|
+
format: getFormat(),
|
|
38
|
+
transports: [new winston.transports.Console()],
|
|
39
|
+
exceptionHandlers: transports,
|
|
40
|
+
exitOnError: false,
|
|
41
|
+
});
|
|
53
42
|
|
|
54
|
-
return logger
|
|
55
|
-
}
|
|
43
|
+
return logger;
|
|
44
|
+
};
|
|
56
45
|
|
|
57
46
|
const getLogger = (logLevel) => {
|
|
58
|
-
const level = getLevel(logLevel)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
module.exports = getLogger
|
|
47
|
+
const level = getLevel(logLevel);
|
|
48
|
+
return createLoggerInstance(level);
|
|
49
|
+
};
|
|
64
50
|
|
|
51
|
+
module.exports = getLogger;
|
package/logger/index.test.js
CHANGED
|
@@ -1,108 +1,54 @@
|
|
|
1
|
-
const Logger = require('./index')
|
|
2
|
-
const fs = require('fs')
|
|
3
|
-
const wait = require('wait-promise')
|
|
1
|
+
const Logger = require('./index');
|
|
4
2
|
|
|
5
|
-
const { env } = process
|
|
6
|
-
|
|
7
|
-
const deleteLogFile = (filePath) => {
|
|
8
|
-
const dir = 'logs'
|
|
9
|
-
if (!fs.existsSync(dir)) {
|
|
10
|
-
fs.mkdirSync(dir)
|
|
11
|
-
}
|
|
12
|
-
if (fs.existsSync(filePath)) {
|
|
13
|
-
fs.unlinkSync(filePath)
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const waitForFileToHaveContent = (filePath => wait.limit(30).until(() => {
|
|
18
|
-
if (fs.existsSync(filePath)) {
|
|
19
|
-
const content = fs.readFileSync(filePath, 'utf8')
|
|
20
|
-
return content !== ''
|
|
21
|
-
}
|
|
22
|
-
return false
|
|
23
|
-
}))
|
|
3
|
+
const { env } = process;
|
|
24
4
|
|
|
25
5
|
describe('Logger', () => {
|
|
26
6
|
it('it`s default level is info', () => {
|
|
27
|
-
env.NODE_ENV = ''
|
|
28
|
-
const logger = Logger()
|
|
29
|
-
expect(logger).toBeDefined()
|
|
30
|
-
expect(logger.level).toBe('
|
|
31
|
-
})
|
|
7
|
+
env.NODE_ENV = '';
|
|
8
|
+
const logger = Logger();
|
|
9
|
+
expect(logger).toBeDefined();
|
|
10
|
+
expect(logger.level).toBe('debug');
|
|
11
|
+
});
|
|
32
12
|
|
|
33
13
|
it('its default develpment level is debug', () => {
|
|
34
|
-
env.NODE_ENV = 'development'
|
|
35
|
-
const logger = Logger()
|
|
36
|
-
expect(logger.level).toBe('debug')
|
|
37
|
-
})
|
|
14
|
+
env.NODE_ENV = 'development';
|
|
15
|
+
const logger = Logger();
|
|
16
|
+
expect(logger.level).toBe('debug');
|
|
17
|
+
});
|
|
38
18
|
|
|
39
19
|
it('its default test level is info', () => {
|
|
40
|
-
env.NODE_ENV = 'test'
|
|
41
|
-
const logger = Logger()
|
|
42
|
-
expect(logger.level).toBe('info')
|
|
43
|
-
})
|
|
20
|
+
env.NODE_ENV = 'test';
|
|
21
|
+
const logger = Logger();
|
|
22
|
+
expect(logger.level).toBe('info');
|
|
23
|
+
});
|
|
44
24
|
|
|
45
25
|
it('its default production level is info', () => {
|
|
46
|
-
env.NODE_ENV = 'production'
|
|
47
|
-
const logger = Logger()
|
|
48
|
-
expect(logger.level).toBe('info')
|
|
49
|
-
})
|
|
26
|
+
env.NODE_ENV = 'production';
|
|
27
|
+
const logger = Logger();
|
|
28
|
+
expect(logger.level).toBe('info');
|
|
29
|
+
});
|
|
50
30
|
|
|
51
31
|
it('its level can be override by LOG_LEVEL env variable', () => {
|
|
52
|
-
env.NODE_ENV = 'development'
|
|
53
|
-
env.LOG_LEVEL = 'warn'
|
|
54
|
-
const logger = Logger()
|
|
55
|
-
expect(logger.level).toBe('warn')
|
|
56
|
-
})
|
|
32
|
+
env.NODE_ENV = 'development';
|
|
33
|
+
env.LOG_LEVEL = 'warn';
|
|
34
|
+
const logger = Logger();
|
|
35
|
+
expect(logger.level).toBe('warn');
|
|
36
|
+
});
|
|
57
37
|
|
|
58
38
|
it('its level can be override by function variable', () => {
|
|
59
|
-
env.NODE_ENV = 'production'
|
|
60
|
-
env.LOG_LEVEL = 'warn'
|
|
61
|
-
const logger = Logger('silly')
|
|
62
|
-
expect(logger.level).toBe('silly')
|
|
63
|
-
})
|
|
39
|
+
env.NODE_ENV = 'production';
|
|
40
|
+
env.LOG_LEVEL = 'warn';
|
|
41
|
+
const logger = Logger('silly');
|
|
42
|
+
expect(logger.level).toBe('silly');
|
|
43
|
+
});
|
|
64
44
|
|
|
65
45
|
it('its has functions for each level', () => {
|
|
66
|
-
const logger = Logger()
|
|
67
|
-
expect(logger.error).toBeFunction()
|
|
68
|
-
expect(logger.warn).toBeFunction()
|
|
69
|
-
expect(logger.info).toBeFunction()
|
|
70
|
-
expect(logger.verbose).toBeFunction()
|
|
71
|
-
expect(logger.debug).toBeFunction()
|
|
72
|
-
expect(logger.silly).toBeFunction()
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('it can have general file transporters', async () => {
|
|
76
|
-
env.USE_LOG_FILES = 'true'
|
|
77
|
-
const allPath = 'logs/all.log'
|
|
78
|
-
deleteLogFile(allPath)
|
|
79
|
-
|
|
80
|
-
const logger = Logger()
|
|
81
|
-
const errorText = 'this is error'
|
|
82
|
-
logger.error(errorText)
|
|
83
|
-
|
|
84
|
-
await waitForFileToHaveContent(allPath)
|
|
85
|
-
|
|
86
|
-
expect(fs.existsSync(allPath)).toBe(true)
|
|
87
|
-
|
|
88
|
-
const contents = fs.readFileSync(allPath, 'utf8')
|
|
89
|
-
expect(contents).toBe(`error: ${errorText}\n`)
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('it can have general file transporters', async () => {
|
|
93
|
-
env.USE_LOG_FILES = 'true'
|
|
94
|
-
const errorPath = 'logs/error.log'
|
|
95
|
-
deleteLogFile(errorPath)
|
|
96
|
-
|
|
97
|
-
expect(fs.existsSync(errorPath)).toBe(false)
|
|
98
|
-
|
|
99
|
-
const logger = Logger()
|
|
100
|
-
const errorText = 'this is error'
|
|
101
|
-
logger.error(errorText)
|
|
102
|
-
|
|
103
|
-
await waitForFileToHaveContent(errorPath)
|
|
104
|
-
|
|
105
|
-
const contents = fs.readFileSync(errorPath, 'utf8')
|
|
106
|
-
expect(contents).toBe(`error: ${errorText}\n`)
|
|
107
|
-
})
|
|
108
|
-
})
|
|
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
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
const axios = require('axios')
|
|
2
|
-
const
|
|
3
|
-
require('
|
|
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();
|
|
4
7
|
/**
|
|
5
8
|
* Add support for nock testing
|
|
6
9
|
* see s://github.com/axios/axios/issues/305
|
|
7
10
|
*/
|
|
8
11
|
if (process.env.NODE_ENV === 'test') {
|
|
9
|
-
axios.defaults.adapter = httpAdapter
|
|
12
|
+
axios.defaults.adapter = httpAdapter;
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
const HTTPMethods = [
|
|
@@ -17,36 +20,66 @@ const HTTPMethods = [
|
|
|
17
20
|
'put',
|
|
18
21
|
'patch',
|
|
19
22
|
'options',
|
|
20
|
-
]
|
|
23
|
+
];
|
|
21
24
|
|
|
22
25
|
const defaultSettings = {
|
|
23
26
|
timeout: 2500,
|
|
24
27
|
headers: { 'X-AF-AUTH': 'ANYONE' },
|
|
25
|
-
|
|
28
|
+
paramsSerializer: params => qs.stringify(params),
|
|
29
|
+
};
|
|
26
30
|
|
|
27
31
|
module.exports = class Network {
|
|
28
32
|
constructor(settings = {}) {
|
|
29
|
-
this.settings = Object.assign(defaultSettings, settings)
|
|
30
|
-
this.
|
|
31
|
-
|
|
32
|
-
this.
|
|
33
|
+
this.settings = Object.assign({}, defaultSettings, settings);
|
|
34
|
+
this.createBaseUrl();
|
|
35
|
+
|
|
36
|
+
this.axios = axios.create(this.settings);
|
|
37
|
+
this.addRetry(settings);
|
|
38
|
+
this.buildClassHttpMethods();
|
|
39
|
+
this.addLogs();
|
|
33
40
|
}
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
addRetry(settings) {
|
|
43
|
+
axiosRetry(this.axios, {
|
|
44
|
+
retries: 0,
|
|
45
|
+
retryDelay: axiosRetry.exponentialDelay,
|
|
46
|
+
...settings,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
addLogs() {
|
|
51
|
+
const logger = Logger();
|
|
52
|
+
this.axios.interceptors.request.use((request) => {
|
|
53
|
+
logger.info(`Start Request: [${request.method.toUpperCase()}] ${request.baseURL} ${request.url}`, {
|
|
54
|
+
headers: request.headers,
|
|
55
|
+
query: request.query,
|
|
56
|
+
body: request.body,
|
|
57
|
+
});
|
|
58
|
+
return request;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
this.axios.interceptors.response.use((response) => {
|
|
62
|
+
logger.info(`Finish Request: [${response.config.method.toUpperCase()} ${response.config.url}`);
|
|
63
|
+
return response;
|
|
64
|
+
}, (response) => {
|
|
65
|
+
logger.error(`Faild Request: [${response.config.method.toUpperCase()}] ${response.config.url}`);
|
|
66
|
+
return response;
|
|
67
|
+
});
|
|
39
68
|
}
|
|
40
69
|
|
|
41
70
|
createBaseUrl() {
|
|
42
|
-
|
|
71
|
+
if (!this.settings.serviceUrl && !this.settings.serviceName) {
|
|
72
|
+
throw new Error('At least one of the settings Missing serviceUrl or serviceName');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const { settings } = this;
|
|
43
76
|
if (settings.serviceUrl) {
|
|
44
|
-
settings.baseURL = settings.serviceUrl
|
|
77
|
+
settings.baseURL = settings.serviceUrl;
|
|
45
78
|
} else if (settings.serviceName) {
|
|
46
|
-
const envServiceHostName = `${settings.serviceName}_SERVICE_HOST
|
|
47
|
-
settings.baseURL = `http://${process.env[envServiceHostName]}
|
|
79
|
+
const envServiceHostName = `${settings.serviceName}_SERVICE_HOST`;
|
|
80
|
+
settings.baseURL = `http://${process.env[envServiceHostName]}`;
|
|
48
81
|
if (!settings.baseURL) {
|
|
49
|
-
throw new Error(`Missing environment variable: ${envServiceHostName}`)
|
|
82
|
+
throw new Error(`Missing environment variable: ${envServiceHostName}`);
|
|
50
83
|
}
|
|
51
84
|
}
|
|
52
85
|
}
|
|
@@ -55,9 +88,8 @@ module.exports = class Network {
|
|
|
55
88
|
* Build class methods that wrap axios methods
|
|
56
89
|
*/
|
|
57
90
|
buildClassHttpMethods() {
|
|
58
|
-
this.axios = axios.create(this.settings)
|
|
59
91
|
HTTPMethods.forEach((method) => {
|
|
60
|
-
this[method] = (...args) => this.axios[method](...args)
|
|
61
|
-
})
|
|
92
|
+
this[method] = (...args) => this.axios[method](...args);
|
|
93
|
+
});
|
|
62
94
|
}
|
|
63
|
-
}
|
|
95
|
+
};
|
package/network/index.test.js
CHANGED
|
@@ -1,31 +1,68 @@
|
|
|
1
|
-
const nock = require('nock')
|
|
2
|
-
const Network = require('./index')
|
|
1
|
+
const nock = require('nock');
|
|
2
|
+
const Network = require('./index');
|
|
3
3
|
|
|
4
4
|
nock('http://www.google.com')
|
|
5
5
|
.get('/resource')
|
|
6
|
-
.reply(200, { one: 1 })
|
|
6
|
+
.reply(200, { one: 1 });
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
nock('https://www.apple.com')
|
|
10
10
|
.get('/resource')
|
|
11
|
-
.reply(200, { two: 2 })
|
|
11
|
+
.reply(200, { two: 2 });
|
|
12
12
|
|
|
13
13
|
describe('Network', () => {
|
|
14
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
|
-
})
|
|
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
22
|
|
|
23
|
-
it('Making requests by service name from
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
expect(response).toBeDefined()
|
|
28
|
-
expect(response.data).
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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://www.apple.com' });
|
|
33
|
+
const response = await n.get('/resource', {
|
|
34
|
+
query: {
|
|
35
|
+
a: 'b',
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
expect(response).toBeDefined();
|
|
39
|
+
expect(response.data).not.toBeDefined();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('Retry 1 time', async () => {
|
|
43
|
+
nock('https://www.apple2.com')
|
|
44
|
+
.get('/resource')
|
|
45
|
+
.once()
|
|
46
|
+
.reply(500);
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
nock('https://www.apple2.com')
|
|
50
|
+
.get('/resource')
|
|
51
|
+
.once()
|
|
52
|
+
.reply(200, { two: 2 });
|
|
53
|
+
|
|
54
|
+
const n = new Network({ serviceUrl: 'https://www.apple2.com' });
|
|
55
|
+
const response = await n.get('/resource', {
|
|
56
|
+
query: {
|
|
57
|
+
a: 'b',
|
|
58
|
+
},
|
|
59
|
+
'axios-retry': {
|
|
60
|
+
retries: 1,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
expect(response).toBeDefined();
|
|
65
|
+
expect(response.data).toBeDefined();
|
|
66
|
+
expect(response.data.two).toBe(2);
|
|
67
|
+
});
|
|
68
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autofleet/node-common",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"coverage": "jest --coverage --forceExit --runInBand",
|
|
@@ -23,10 +23,12 @@
|
|
|
23
23
|
"homepage": "https://gitlab.com/AutoFleet/node-common#README",
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"axios": "^0.18.0",
|
|
26
|
+
"axios-retry": "^3.1.0",
|
|
26
27
|
"dotenv": "^5.0.1",
|
|
27
28
|
"jest": "^22.4.3",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
29
|
+
"node-cache": "^4.2.0",
|
|
30
|
+
"qs": "^6.5.2",
|
|
31
|
+
"winston": "^3.0.0-rc4"
|
|
30
32
|
},
|
|
31
33
|
"devDependencies": {
|
|
32
34
|
"eslint": "^4.19.1",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* eslint no-console: 0 */
|
|
2
|
+
const Settings = require('./index');
|
|
3
|
+
|
|
4
|
+
const settings = new Settings({
|
|
5
|
+
serviceUrl: 'http://localhost:8085/',
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
const fleetId = 'e7bbfcad-98a8-421d-962f-fd1c7790c789';
|
|
9
|
+
|
|
10
|
+
const testSettings = async () => {
|
|
11
|
+
const generalSetting = await settings.get('ride.matching_retry', 1);
|
|
12
|
+
const fleetSetting = await settings.get('ride.matching_retry', 1, [{ fleetId }]);
|
|
13
|
+
const notFoundKey = await settings.get('not.found.key', 1, [{ fleetId }]);
|
|
14
|
+
await settings.get('not.found.key', 1, [{ fleetId }, { dor: 1 }]);
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
await settings.get('not.found.key');
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.log(error);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
console.log(generalSetting);
|
|
23
|
+
console.log(fleetSetting);
|
|
24
|
+
console.log(notFoundKey);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
testSettings();
|
|
28
|
+
setTimeout(() => {
|
|
29
|
+
testSettings();
|
|
30
|
+
}, 500);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const NodeCache = require('node-cache');
|
|
2
|
+
const Network = require('../network');
|
|
3
|
+
const Logger = require('../logger');
|
|
4
|
+
|
|
5
|
+
const logger = Logger();
|
|
6
|
+
require('dotenv').config();
|
|
7
|
+
|
|
8
|
+
const fiveMinutes = 60 * 5;
|
|
9
|
+
|
|
10
|
+
module.exports = class SettingsManager {
|
|
11
|
+
constructor({ serviceUrl, ttl = fiveMinutes }) {
|
|
12
|
+
const localServiceUrl = serviceUrl || process.env.SETTING_MS_SERVICE_HOST;
|
|
13
|
+
|
|
14
|
+
this.ttl = ttl;
|
|
15
|
+
this.settingsCache = new NodeCache();
|
|
16
|
+
this.network = new Network({
|
|
17
|
+
serviceUrl: localServiceUrl,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async get(key, defaultValue, labels = []) {
|
|
22
|
+
if (!defaultValue) {
|
|
23
|
+
throw new Error('Can\'t get a key without defaultValue');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const cacheKey = `${key}_${JSON.stringify(labels)}`;
|
|
27
|
+
const cacheValue = this.settingsCache.get(cacheKey);
|
|
28
|
+
if (cacheValue !== undefined) {
|
|
29
|
+
return cacheValue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let networkValue;
|
|
33
|
+
try {
|
|
34
|
+
const networkReponse = await this.network.get(`/api/v1/setting/get-setting/${key}`, {
|
|
35
|
+
params: {
|
|
36
|
+
labels,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
networkValue = networkReponse.data.value;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
logger.error('Cant get setting from network', error);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const returnValue = networkValue || defaultValue;
|
|
45
|
+
this.settingsCache.set(cacheKey, returnValue, this.ttl);
|
|
46
|
+
return returnValue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
flush() {
|
|
50
|
+
return this.settingsCache.flushAll();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const nock = require('nock');
|
|
2
|
+
const Settings = require('./index');
|
|
3
|
+
|
|
4
|
+
const serviceUrl = 'http://localhost:8085';
|
|
5
|
+
|
|
6
|
+
const mockSetting = (key, value, labels, response = 200) => nock(serviceUrl)
|
|
7
|
+
.get(`/api/v1/setting/get-setting/${key}`)
|
|
8
|
+
.query(labels ? { labels } : undefined)
|
|
9
|
+
.reply(response, { value });
|
|
10
|
+
|
|
11
|
+
describe('Settings', () => {
|
|
12
|
+
it('Get a key from server', async () => {
|
|
13
|
+
const m = mockSetting('key1', 'value1');
|
|
14
|
+
const settings = new Settings({
|
|
15
|
+
serviceUrl,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const value = await settings.get('key1', 'dv');
|
|
19
|
+
expect(value).toEqual('value1');
|
|
20
|
+
expect(m.isDone()).toBeTruthy();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('Cache from server', async () => {
|
|
24
|
+
const m = mockSetting('key1', 'value1');
|
|
25
|
+
const settings = new Settings({
|
|
26
|
+
serviceUrl,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const value = await settings.get('key1', 'dv');
|
|
30
|
+
expect(value).toEqual('value1');
|
|
31
|
+
expect(m.isDone()).toBeTruthy();
|
|
32
|
+
|
|
33
|
+
const value2 = await settings.get('key1', 'dv');
|
|
34
|
+
expect(value2).toEqual('value1');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('Finds with labels', async () => {
|
|
38
|
+
const m = mockSetting('key1', 'value1', [{ fleetId: 'uuid' }]);
|
|
39
|
+
const settings = new Settings({
|
|
40
|
+
serviceUrl,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const value = await settings.get('key1', 'dv', [{ fleetId: 'uuid' }]);
|
|
44
|
+
expect(value).toEqual('value1');
|
|
45
|
+
expect(m.isDone()).toBeTruthy();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('Returns default value when network error', async () => {
|
|
49
|
+
const settings = new Settings({
|
|
50
|
+
serviceUrl,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const value = await settings.get('key1', 'dv', [{ fleetId: 'uuid' }]);
|
|
54
|
+
expect(value).toEqual('dv');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('Values can be flushed', async () => {
|
|
58
|
+
const m1 = mockSetting('key1', 'value1');
|
|
59
|
+
const settings = new Settings({
|
|
60
|
+
serviceUrl,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const value = await settings.get('key1', 'dv');
|
|
64
|
+
expect(value).toEqual('value1');
|
|
65
|
+
expect(m1.isDone()).toBeTruthy();
|
|
66
|
+
|
|
67
|
+
settings.flush();
|
|
68
|
+
const m2 = mockSetting('key1', 'value2');
|
|
69
|
+
|
|
70
|
+
const value2 = await settings.get('key1', 'dv');
|
|
71
|
+
expect(value2).toEqual('value2');
|
|
72
|
+
expect(m2.isDone()).toBeTruthy();
|
|
73
|
+
});
|
|
74
|
+
});
|