@autofleet/network 1.5.2 → 1.6.1-beta-194901dc.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/index.js +95 -58
- package/package.json +14 -12
package/index.js
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
|
-
const axios = require('axios');
|
|
2
|
-
const axiosRetry = require('axios-retry');
|
|
3
|
-
const Logger = require('@autofleet/logger');
|
|
4
|
-
const qs = require('qs');
|
|
5
|
-
const httpAdapter = require('axios/lib/adapters/http');
|
|
1
|
+
const { default: axios } = require('axios');
|
|
2
|
+
const { default: axiosRetry } = require('axios-retry');
|
|
3
|
+
const { default: Logger } = require('@autofleet/logger');
|
|
6
4
|
const merge = require('deepmerge');
|
|
7
5
|
const { setup } = require('@autofleet/axios-cache-adapter');
|
|
8
|
-
const
|
|
9
|
-
const HttpsAgent = require('agentkeepalive').HttpsAgent;
|
|
6
|
+
const { HttpAgent, HttpsAgent } = require('agentkeepalive');
|
|
10
7
|
|
|
11
|
-
require('dotenv').config();
|
|
12
8
|
/**
|
|
13
9
|
* Add support for nock testing
|
|
14
|
-
* see
|
|
10
|
+
* see https://github.com/axios/axios/issues/305
|
|
15
11
|
*/
|
|
16
12
|
if (process.env.NODE_ENV === 'test') {
|
|
13
|
+
require('dotenv').config();
|
|
14
|
+
const httpAdapter = require('axios/lib/adapters/http');
|
|
17
15
|
axios.defaults.adapter = httpAdapter;
|
|
18
16
|
}
|
|
19
17
|
|
|
18
|
+
/** @type {import('qs').stringify | undefined} */
|
|
19
|
+
let qsStringify;
|
|
20
|
+
const lazilyLoadQsStringify = () => {
|
|
21
|
+
qsStringify ??= require('qs').stringify;
|
|
22
|
+
return qsStringify;
|
|
23
|
+
}
|
|
24
|
+
|
|
20
25
|
const HTTPMethods = [
|
|
21
26
|
'get',
|
|
22
27
|
'post',
|
|
@@ -28,19 +33,29 @@ const HTTPMethods = [
|
|
|
28
33
|
];
|
|
29
34
|
|
|
30
35
|
const defaultSettings = {
|
|
31
|
-
timeout:
|
|
36
|
+
timeout: 10_000,
|
|
32
37
|
headers: {
|
|
33
38
|
'X-AF-AUTH': 'ANYONE',
|
|
34
39
|
'X-IAF-ORIGIN-SERVICE': process.env.AF_SERVICE_NAME || null,
|
|
35
40
|
},
|
|
36
|
-
paramsSerializer: params =>
|
|
41
|
+
paramsSerializer: params => lazilyLoadQsStringify(params, { arrayFormat: 'brackets' }),
|
|
37
42
|
'axios-retry': {
|
|
38
43
|
shouldResetTimeout: true,
|
|
39
44
|
},
|
|
40
45
|
keepAlive: true,
|
|
41
46
|
};
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
/** @param {import('axios').AxiosRequestConfig} request @returns {string} */
|
|
49
|
+
const createRequestString = (request) => `[${(request.method || '').toUpperCase()}] ${request.baseURL ?? 'unknown-base-url'}${request.url ?? '/unknown-url'}`;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @typedef {object} NetworkSettingsBase
|
|
53
|
+
* @property {string} [serviceName]
|
|
54
|
+
* @property {string} [serviceUrl]
|
|
55
|
+
* @property {boolean} [keepAlive]
|
|
56
|
+
* @property {import('@autofleet/logger').LoggerInstanceManager} [logger]
|
|
57
|
+
* @typedef {import('axios').CreateAxiosDefaults & NetworkSettingsBase} NetworkSettings
|
|
58
|
+
*/
|
|
44
59
|
|
|
45
60
|
/*
|
|
46
61
|
The default free socket keepalive timeout, in milliseconds.
|
|
@@ -48,45 +63,59 @@ const createRequestString = (request) => `[${(request.method || '').toUpperCase(
|
|
|
48
63
|
and we want to make sure we don't have any socket issues.
|
|
49
64
|
See https://www.npmjs.com/package/agentkeepalive
|
|
50
65
|
|
|
51
|
-
There is also autoscaling reason for that, if we
|
|
66
|
+
There is also autoscaling reason for that, if we don't timeout at some point,
|
|
52
67
|
new pods that were auto scaled will not be used.
|
|
53
68
|
*/
|
|
54
|
-
const FREE_SOCKET_TIMEOUT = process.env.FREE_SOCKET_TIMEOUT ||
|
|
69
|
+
const FREE_SOCKET_TIMEOUT = process.env.FREE_SOCKET_TIMEOUT || 5_000;
|
|
55
70
|
|
|
56
71
|
module.exports = class Network {
|
|
72
|
+
/** @private @readonly @type {NetworkSettings} */
|
|
73
|
+
settings;
|
|
74
|
+
/** @private @readonly @type {import('axios').AxiosInstance} */
|
|
75
|
+
axios;
|
|
76
|
+
|
|
77
|
+
/** @type {import('axios').AxiosInstance['get']} */
|
|
78
|
+
get;
|
|
79
|
+
/** @type {import('axios').AxiosInstance['post']} */
|
|
80
|
+
post;
|
|
81
|
+
/** @type {import('axios').AxiosInstance['delete']} */
|
|
82
|
+
delete;
|
|
83
|
+
/** @type {import('axios').AxiosInstance['head']} */
|
|
84
|
+
head;
|
|
85
|
+
/** @type {import('axios').AxiosInstance['put']} */
|
|
86
|
+
put;
|
|
87
|
+
/** @type {import('axios').AxiosInstance['patch']} */
|
|
88
|
+
patch;
|
|
89
|
+
/** @type {import('axios').AxiosInstance['options']} */
|
|
90
|
+
options;
|
|
91
|
+
|
|
92
|
+
/** @param {NetworkSettings} [settings] */
|
|
57
93
|
constructor(settings = {}) {
|
|
58
94
|
this.settings = merge(defaultSettings, settings);
|
|
59
95
|
if (this.settings.keepAlive) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
});
|
|
63
|
-
const keepaliveHttpsAgent = new HttpsAgent({
|
|
64
|
-
freeSocketTimeout: FREE_SOCKET_TIMEOUT,
|
|
65
|
-
});
|
|
66
|
-
this.settings.httpAgent = keepaliveAgent;
|
|
67
|
-
this.settings.httpsAgent = keepaliveHttpsAgent;
|
|
96
|
+
this.settings.httpAgent = new HttpAgent({ freeSocketTimeout: FREE_SOCKET_TIMEOUT });
|
|
97
|
+
this.settings.httpsAgent = new HttpsAgent({ freeSocketTimeout: FREE_SOCKET_TIMEOUT });
|
|
68
98
|
delete this.settings.keepAlive;
|
|
69
99
|
}
|
|
70
|
-
this
|
|
100
|
+
this.#createBaseUrl();
|
|
71
101
|
|
|
72
102
|
if(settings.cache) {
|
|
73
103
|
this.settings.cache = {
|
|
74
104
|
maxAge: 15 * 60 * 1000,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
query: false
|
|
78
|
-
},
|
|
105
|
+
// Store responses from requests with query parameters in cache
|
|
106
|
+
exclude: { query: false },
|
|
79
107
|
...settings.cache,
|
|
80
108
|
}
|
|
81
109
|
}
|
|
82
110
|
|
|
83
111
|
this.axios = settings.cache ? setup(this.settings) : axios.create(this.settings);
|
|
84
|
-
this
|
|
85
|
-
this
|
|
86
|
-
this
|
|
112
|
+
this.#addRetry(settings);
|
|
113
|
+
this.#buildClassHttpMethods();
|
|
114
|
+
this.#addLogs();
|
|
87
115
|
}
|
|
88
116
|
|
|
89
|
-
|
|
117
|
+
/** @param {NetworkSettings} settings */
|
|
118
|
+
#addRetry(settings) {
|
|
90
119
|
axiosRetry(this.axios, {
|
|
91
120
|
retries: 0,
|
|
92
121
|
retryDelay: axiosRetry.exponentialDelay,
|
|
@@ -94,8 +123,8 @@ module.exports = class Network {
|
|
|
94
123
|
});
|
|
95
124
|
}
|
|
96
125
|
|
|
97
|
-
addLogs() {
|
|
98
|
-
const logger = Logger();
|
|
126
|
+
#addLogs() {
|
|
127
|
+
const logger = this.settings.logger ?? Logger();
|
|
99
128
|
this.axios.interceptors.request.use((request) => {
|
|
100
129
|
logger.info(`Start Request: ${createRequestString(request)}`);
|
|
101
130
|
return request;
|
|
@@ -105,10 +134,8 @@ module.exports = class Network {
|
|
|
105
134
|
logger.info(`Finish Request: ${createRequestString(response.config)}`);
|
|
106
135
|
return response;
|
|
107
136
|
}, (error) => {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
['ECONNRESET', 'EPIPE'].includes(error.code)
|
|
111
|
-
) {
|
|
137
|
+
debugger;
|
|
138
|
+
if(error.request?._currentRequest?.reusedSocket && ['ECONNRESET', 'EPIPE'].includes(error.code)) {
|
|
112
139
|
// See https://www.npmjs.com/package/agentkeepalive
|
|
113
140
|
// Support req.reusedSocket
|
|
114
141
|
// https://code-examples.net/en/q/28a8069
|
|
@@ -118,16 +145,16 @@ module.exports = class Network {
|
|
|
118
145
|
})
|
|
119
146
|
return this.axios.request(error.config);
|
|
120
147
|
}
|
|
121
|
-
const request = error.request;
|
|
148
|
+
const request = error.config?.baseURL ? error.config : error.request;
|
|
122
149
|
logger.error(`Finish Request with error ${request ? createRequestString(request) : ''}`, {
|
|
123
150
|
status: error.status,
|
|
124
|
-
data: error.response
|
|
151
|
+
data: error.response?.data,
|
|
125
152
|
});
|
|
126
153
|
throw error;
|
|
127
154
|
});
|
|
128
155
|
}
|
|
129
156
|
|
|
130
|
-
createBaseUrl() {
|
|
157
|
+
#createBaseUrl() {
|
|
131
158
|
if (!this.settings.serviceUrl && !this.settings.serviceName) {
|
|
132
159
|
throw new Error('At least one of the settings Missing serviceUrl or serviceName');
|
|
133
160
|
}
|
|
@@ -147,44 +174,54 @@ module.exports = class Network {
|
|
|
147
174
|
/**
|
|
148
175
|
* Build class methods that wrap axios methods
|
|
149
176
|
*/
|
|
150
|
-
buildClassHttpMethods() {
|
|
177
|
+
#buildClassHttpMethods() {
|
|
151
178
|
HTTPMethods.forEach((method) => {
|
|
152
179
|
this[method] = (...args) => this.axios[method](...args);
|
|
153
180
|
});
|
|
154
181
|
}
|
|
155
182
|
|
|
183
|
+
/**
|
|
184
|
+
* @template T
|
|
185
|
+
* @param {string} url - The endpoint URL to send the request to.
|
|
186
|
+
* @param {object} [options={}] - Additional options to include in the request payload.
|
|
187
|
+
* @returns {Promise<T[]>} - A promise that resolves to an array of all results.
|
|
188
|
+
*/
|
|
156
189
|
async getAllPages(url, options = {}) {
|
|
190
|
+
/** @type {T[]} */
|
|
157
191
|
let currentResult = [];
|
|
192
|
+
/** @type {number | null} */
|
|
158
193
|
let resultsInFirstPage = null;
|
|
194
|
+
/** @type {number | null} */
|
|
159
195
|
let lastResultsSize = null;
|
|
160
196
|
|
|
161
|
-
const localOptions = { params: {}, ...options };
|
|
162
|
-
localOptions.params.page
|
|
163
|
-
while((localOptions.params.page === 1 || lastResultsSize === resultsInFirstPage) && lastResultsSize !== 0) {
|
|
197
|
+
const localOptions = { params: { page: 1 }, ...options };
|
|
198
|
+
while ((localOptions.params.page === 1 || lastResultsSize === resultsInFirstPage) && lastResultsSize !== 0) {
|
|
164
199
|
const { data } = await this.get(url, localOptions);
|
|
165
200
|
lastResultsSize = data.length;
|
|
166
|
-
if(localOptions.params.page === 1) {
|
|
201
|
+
if (localOptions.params.page === 1) {
|
|
167
202
|
resultsInFirstPage = data.length;
|
|
168
203
|
}
|
|
169
204
|
|
|
170
205
|
localOptions.params.page += 1;
|
|
171
|
-
currentResult
|
|
206
|
+
Array.prototype.push.apply(currentResult, data);
|
|
172
207
|
}
|
|
173
208
|
|
|
174
209
|
return currentResult;
|
|
175
210
|
}
|
|
176
211
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
212
|
+
/**
|
|
213
|
+
* Fetches all pages from a paginated API endpoint until all results are retrieved
|
|
214
|
+
* or an optional page limit is reached.
|
|
215
|
+
*
|
|
216
|
+
* @template T
|
|
217
|
+
* @param {string} url - The endpoint URL to send the request to.
|
|
218
|
+
* @param {object} [options={}] - Additional options to include in the request payload.
|
|
219
|
+
* @param {number} [pageLimit=100] - The maximum number of pages to fetch.
|
|
220
|
+
* Set to -1 for no limit.
|
|
221
|
+
* @returns {Promise<T[]>} - A promise that resolves to an array of all results.
|
|
222
|
+
*/
|
|
187
223
|
async getAllPagesFromQueryEndpoint(url, options = {}, pageLimit = 100) {
|
|
224
|
+
/** @type {T[]} */
|
|
188
225
|
let currentResult = [];
|
|
189
226
|
|
|
190
227
|
let moreResultsToLoad = true;
|
|
@@ -192,11 +229,11 @@ module.exports = class Network {
|
|
|
192
229
|
while (moreResultsToLoad && (pageLimit === -1 || page < pageLimit)) {
|
|
193
230
|
const { data } = await this.post(url, {
|
|
194
231
|
...options,
|
|
195
|
-
page
|
|
232
|
+
page,
|
|
196
233
|
});
|
|
197
234
|
const { rows, count } = data;
|
|
198
235
|
page += 1;
|
|
199
|
-
currentResult
|
|
236
|
+
Array.prototype.push.apply(currentResult, rows);
|
|
200
237
|
moreResultsToLoad = currentResult.length < count;
|
|
201
238
|
}
|
|
202
239
|
|
package/package.json
CHANGED
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autofleet/network",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1-beta-194901dc.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "
|
|
8
|
-
"test-auto": "jest --watch --runInBand",
|
|
9
|
-
"linter": "./node_modules/.bin/eslint ."
|
|
7
|
+
"test": "node --test"
|
|
10
8
|
},
|
|
11
9
|
"author": "Dor Shay",
|
|
12
10
|
"license": "ISC",
|
|
13
11
|
"dependencies": {
|
|
14
12
|
"@autofleet/axios-cache-adapter": "^2.7.3-hotfix-1",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"axios": "^
|
|
18
|
-
"axios-retry": "^3.2.4",
|
|
13
|
+
"agentkeepalive": "^4.6.0",
|
|
14
|
+
"axios": "^0.29.0",
|
|
15
|
+
"axios-retry": "^4.5.0",
|
|
19
16
|
"deepmerge": "^3.0.0",
|
|
20
|
-
"
|
|
21
|
-
|
|
17
|
+
"qs": "^6.14.0"
|
|
18
|
+
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"@autofleet/logger": ">=4.0.0"
|
|
22
21
|
},
|
|
23
22
|
"devDependencies": {
|
|
24
|
-
"
|
|
25
|
-
"
|
|
23
|
+
"@autofleet/logger": "^4.1.0",
|
|
24
|
+
"@types/axios": "^0.9.36",
|
|
25
|
+
"@types/node": "^18.19.75",
|
|
26
|
+
"dotenv": "^16.4.7",
|
|
27
|
+
"nock": "^14.0.1"
|
|
26
28
|
}
|
|
27
29
|
}
|