@autofleet/node-common 1.1.1 → 1.1.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.
@@ -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
@@ -4,8 +4,5 @@
4
4
  ],
5
5
  "env": {
6
6
  "jest": true
7
- },
8
- "rules": {
9
- "semi": ["error", "never"]
10
7
  }
11
8
  }
package/README.md CHANGED
@@ -12,7 +12,7 @@ Server 2 Servers communication.
12
12
 
13
13
  Implement:
14
14
  * Retriving service urls from environment
15
- * Retry - TBD
15
+ * Retry - Using https://github.com/softonic/axios-retry
16
16
  * Caching - TBD
17
17
  * Syntatic response for fail - TBD
18
18
  * Circuit Breaking - TBD
package/index.js CHANGED
@@ -1,4 +1,7 @@
1
+ const Network = require('./network');
2
+ const Logger = require('./logger');
3
+
1
4
  module.exports = {
2
- Network: require('./network'),
3
- Logger: require('./logger'),
4
- }
5
+ Network,
6
+ Logger,
7
+ };
package/logger/example.js CHANGED
@@ -1,4 +1,5 @@
1
- const logger = require('./index')('debug')
2
- logger.info('it is working')
3
- logger.error('errors displayed in logs/error.log')
4
- logger.debug('debug with object', {a: 5})
1
+ const logger = require('./index')();
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,41 +1,51 @@
1
- const winston = require('winston')
2
- require('dotenv').config()
1
+ const winston = require('winston');
3
2
 
4
- const { env } = process
3
+ const { createLogger } = winston;
4
+ require('dotenv').config();
5
+
6
+ const { env } = process;
7
+
8
+ const infoEnvNmaes = [
9
+ 'production',
10
+ 'staging',
11
+ 'test',
12
+ ];
5
13
 
6
14
  const getLevel = (logLevel) => {
7
- if (logLevel) return logLevel
8
- if (env.LOG_LEVEL) return env.LOG_LEVEL
9
- if (env.NODE_ENV === 'development') return 'debug'
10
- // test and producion will return info
11
- return 'info'
12
- }
13
-
14
- const getTransporters = () => {
15
- let transporters = [new winston.transports.Console()]
16
- if (env.USE_LOG_FILES === 'true') {
17
- transporters = transporters.concat([
18
- new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
19
- new winston.transports.File({ filename: 'logs/all.log' }),
20
- ])
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();
21
25
  }
22
- return transporters
23
- }
24
26
 
25
- const createLogger = (level, transporters) => {
26
- const logger = winston.createLogger({
27
+ return winston.format.combine(
28
+ winston.format.splat(),
29
+ winston.format.colorize(),
30
+ winston.format.simple(),
31
+ );
32
+ };
33
+
34
+ const createLoggerInstance = (level, transports) => {
35
+ const logger = createLogger({
27
36
  level,
28
- format: winston.format.json(),
29
- transports: transporters,
30
- })
37
+ format: getFormat(),
38
+ transports: [new winston.transports.Console()],
39
+ exceptionHandlers: transports,
40
+ exitOnError: false,
41
+ });
31
42
 
32
- return logger
33
- }
43
+ return logger;
44
+ };
34
45
 
35
46
  const getLogger = (logLevel) => {
36
- const level = getLevel(logLevel)
37
- const transporters = getTransporters()
38
- return createLogger(level, transporters)
39
- }
47
+ const level = getLevel(logLevel);
48
+ return createLoggerInstance(level);
49
+ };
40
50
 
41
- module.exports = getLogger
51
+ module.exports = getLogger;
@@ -1,110 +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) => {
18
- return wait.limit(30).until(() => {
19
- if (fs.existsSync(filePath)) {
20
- const content = fs.readFileSync(filePath, 'utf8')
21
- return content !== ''
22
- }
23
- return false
24
- })
25
- })
3
+ const { env } = process;
26
4
 
27
5
  describe('Logger', () => {
28
6
  it('it`s default level is info', () => {
29
- env.NODE_ENV=''
30
- const logger = Logger()
31
- expect(logger).toBeDefined()
32
- expect(logger.level).toBe('info')
33
- })
7
+ env.NODE_ENV = '';
8
+ const logger = Logger();
9
+ expect(logger).toBeDefined();
10
+ expect(logger.level).toBe('debug');
11
+ });
34
12
 
35
13
  it('its default develpment level is debug', () => {
36
- env.NODE_ENV='development'
37
- const logger = Logger()
38
- expect(logger.level).toBe('debug')
39
- })
14
+ env.NODE_ENV = 'development';
15
+ const logger = Logger();
16
+ expect(logger.level).toBe('debug');
17
+ });
40
18
 
41
19
  it('its default test level is info', () => {
42
- env.NODE_ENV='test'
43
- const logger = Logger()
44
- expect(logger.level).toBe('info')
45
- })
20
+ env.NODE_ENV = 'test';
21
+ const logger = Logger();
22
+ expect(logger.level).toBe('info');
23
+ });
46
24
 
47
25
  it('its default production level is info', () => {
48
- env.NODE_ENV='production'
49
- const logger = Logger()
50
- expect(logger.level).toBe('info')
51
- })
26
+ env.NODE_ENV = 'production';
27
+ const logger = Logger();
28
+ expect(logger.level).toBe('info');
29
+ });
52
30
 
53
31
  it('its level can be override by LOG_LEVEL env variable', () => {
54
- env.NODE_ENV='development'
55
- env.LOG_LEVEL='warn'
56
- const logger = Logger()
57
- expect(logger.level).toBe('warn')
58
- })
32
+ env.NODE_ENV = 'development';
33
+ env.LOG_LEVEL = 'warn';
34
+ const logger = Logger();
35
+ expect(logger.level).toBe('warn');
36
+ });
59
37
 
60
38
  it('its level can be override by function variable', () => {
61
- env.NODE_ENV='production'
62
- env.LOG_LEVEL='warn'
63
- const logger = Logger('silly')
64
- expect(logger.level).toBe('silly')
65
- })
39
+ env.NODE_ENV = 'production';
40
+ env.LOG_LEVEL = 'warn';
41
+ const logger = Logger('silly');
42
+ expect(logger.level).toBe('silly');
43
+ });
66
44
 
67
45
  it('its has functions for each level', () => {
68
- const logger = Logger()
69
- expect(logger.error).toBeFunction()
70
- expect(logger.warn).toBeFunction()
71
- expect(logger.info).toBeFunction()
72
- expect(logger.verbose).toBeFunction()
73
- expect(logger.debug).toBeFunction()
74
- expect(logger.silly).toBeFunction()
75
- })
76
-
77
- it('it can have general file transporters', async () => {
78
- env.USE_LOG_FILES='true'
79
- const allPath = 'logs/all.log'
80
- deleteLogFile(allPath)
81
-
82
- const logger = Logger()
83
- const errorText = "this is error"
84
- logger.error(errorText)
85
-
86
- await waitForFileToHaveContent(allPath)
87
-
88
- expect(fs.existsSync(allPath)).toBe(true)
89
-
90
- const contents = fs.readFileSync(allPath, 'utf8')
91
- expect(contents).toBe(`error: ${errorText}\n`)
92
- })
93
-
94
- it('it can have general file transporters', async () => {
95
- env.USE_LOG_FILES='true'
96
- const errorPath = 'logs/error.log'
97
- deleteLogFile(errorPath)
98
-
99
- expect(fs.existsSync(errorPath)).toBe(false)
100
-
101
- const logger = Logger()
102
- const errorText = "this is error"
103
- logger.error(errorText)
104
-
105
- await waitForFileToHaveContent(errorPath)
106
-
107
- const contents = fs.readFileSync(errorPath, 'utf8')
108
- expect(contents).toBe(`error: ${errorText}\n`)
109
- })
110
- })
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,14 @@
1
- const axios = require('axios')
2
- const httpAdapter = require('axios/lib/adapters/http')
3
- require('dotenv').config()
1
+ const axios = require('axios');
2
+ const axiosRetry = require('axios-retry');
3
+ const Logger = require('../logger');
4
+ const httpAdapter = require('axios/lib/adapters/http');
5
+ require('dotenv').config();
4
6
  /**
5
7
  * Add support for nock testing
6
8
  * see s://github.com/axios/axios/issues/305
7
9
  */
8
10
  if (process.env.NODE_ENV === 'test') {
9
- axios.defaults.adapter = httpAdapter
11
+ axios.defaults.adapter = httpAdapter;
10
12
  }
11
13
 
12
14
  const HTTPMethods = [
@@ -17,36 +19,65 @@ const HTTPMethods = [
17
19
  'put',
18
20
  'patch',
19
21
  'options',
20
- ]
22
+ ];
21
23
 
22
24
  const defaultSettings = {
23
25
  timeout: 2500,
24
26
  headers: { 'X-AF-AUTH': 'ANYONE' },
25
- }
27
+ };
26
28
 
27
29
  module.exports = class Network {
28
30
  constructor(settings = {}) {
29
- this.settings = Object.assign(defaultSettings, settings)
30
- this.validate()
31
- this.createBaseUrl()
32
- this.buildClassHttpMethods()
31
+ this.settings = Object.assign({}, defaultSettings, settings);
32
+ this.createBaseUrl();
33
+
34
+ this.axios = axios.create(this.settings);
35
+ this.addRetry(settings);
36
+ this.buildClassHttpMethods();
37
+ this.addLogs();
33
38
  }
34
39
 
35
- validate() {
36
- if (!this.settings.serviceUrl && !this.settings.serviceName) {
37
- throw new Error('At least one of the settings Missing serviceUrl or serviceName')
38
- }
40
+ addRetry(settings) {
41
+ axiosRetry(this.axios, {
42
+ retries: 0,
43
+ retryDelay: axiosRetry.exponentialDelay,
44
+ ...settings,
45
+ });
46
+ }
47
+
48
+ addLogs() {
49
+ const logger = Logger();
50
+ this.axios.interceptors.request.use((request) => {
51
+ logger.info(`Start Request: [${request.method.toUpperCase()}] ${request.baseURL} ${request.url}`, {
52
+ headers: request.headers,
53
+ query: request.query,
54
+ body: request.body,
55
+ });
56
+ return request;
57
+ });
58
+
59
+ this.axios.interceptors.response.use((response) => {
60
+ logger.info(`Finish Request: [${response.config.method.toUpperCase()} ${response.config.url}`);
61
+ return response;
62
+ }, (response) => {
63
+ logger.error(`Faild Request: [${response.config.method.toUpperCase()}] ${response.config.url}`);
64
+ return response;
65
+ });
39
66
  }
40
67
 
41
68
  createBaseUrl() {
42
- const { settings } = this
69
+ if (!this.settings.serviceUrl && !this.settings.serviceName) {
70
+ throw new Error('At least one of the settings Missing serviceUrl or serviceName');
71
+ }
72
+
73
+ const { settings } = this;
43
74
  if (settings.serviceUrl) {
44
- settings.baseURL = settings.serviceUrl
75
+ settings.baseURL = settings.serviceUrl;
45
76
  } else if (settings.serviceName) {
46
- const envServiceHostName = `${settings.serviceName}_SERVICE_HOST`
47
- settings.baseURL = `http://${process.env[envServiceHostName]}`
77
+ const envServiceHostName = `${settings.serviceName}_SERVICE_HOST`;
78
+ settings.baseURL = `http://${process.env[envServiceHostName]}`;
48
79
  if (!settings.baseURL) {
49
- throw new Error(`Missing environment variable: ${envServiceHostName}`)
80
+ throw new Error(`Missing environment variable: ${envServiceHostName}`);
50
81
  }
51
82
  }
52
83
  }
@@ -55,9 +86,8 @@ module.exports = class Network {
55
86
  * Build class methods that wrap axios methods
56
87
  */
57
88
  buildClassHttpMethods() {
58
- this.axios = axios.create(this.settings)
59
89
  HTTPMethods.forEach((method) => {
60
- this[method] = (...args) => this.axios[method](...args)
61
- })
90
+ this[method] = (...args) => this.axios[method](...args);
91
+ });
62
92
  }
63
- }
93
+ };
@@ -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 ENV', async () => {
24
- process.env.TEST_SERVICE_HOST = 'www.google.com'
25
- const n = new Network({ serviceUrl: 'https://www.apple.com' })
26
- const response = await n.get('/resource')
27
- expect(response).toBeDefined()
28
- expect(response.data).toBeDefined()
29
- expect(response.data.two).toBe(2)
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.1",
3
+ "version": "1.1.3",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "coverage": "jest --coverage --forceExit --runInBand",
@@ -23,10 +23,10 @@
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
- "wait-promise": "^0.4.1",
29
- "winston": "^3.0.0-rc5"
29
+ "winston": "^3.0.0-rc4"
30
30
  },
31
31
  "devDependencies": {
32
32
  "eslint": "^4.19.1",