@autofleet/node-common 1.1.2 → 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,6 +1,5 @@
1
- const logger = require('./index')('debug')
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 { createLogger, transports, format } = require('winston')
1
+ const winston = require('winston');
2
2
 
3
- const { combine, timestamp, printf } = format
3
+ const { createLogger } = winston;
4
+ require('dotenv').config();
4
5
 
5
- require('dotenv').config()
6
+ const { env } = process;
6
7
 
7
- const { env } = process
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 === 'development') return 'debug'
13
- // test and producion will return info
14
- return 'info'
15
- }
16
-
17
- const getTransporters = () => {
18
- const consoleTrasporter = new transports.Console()
19
- let transporters = [consoleTrasporter]
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
- const newLogger = (level, transporters) => {
30
- const customFormat = printf((info) => {
31
- const level = info.level
32
- const message = info.message
33
- const timestamp = info.timestamp
34
- delete info.level
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
- transports: transporters,
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
- const transporters = getTransporters()
60
- return newLogger(level, transporters)
61
- }
62
-
63
- module.exports = getLogger
47
+ const level = getLevel(logLevel);
48
+ return createLoggerInstance(level);
49
+ };
64
50
 
51
+ module.exports = getLogger;
@@ -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('info')
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,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.2",
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",