@contentstack/datasync-manager 2.1.0 → 2.1.2
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 +17 -0
- package/dist/api.js +40 -9
- package/dist/config.js +2 -0
- package/dist/core/index.js +7 -1
- package/dist/core/inet.js +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -154,6 +154,23 @@ And here's an example to get you started:
|
|
|
154
154
|
}
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
+
### Environment Variables
|
|
158
|
+
|
|
159
|
+
The following environment variables can be used to customize the behavior of Contentstack DataSync Manager:
|
|
160
|
+
|
|
161
|
+
| Variable | Description | Default |
|
|
162
|
+
|--------------|-----------------------------------------------------------------------------|------------------------|
|
|
163
|
+
| `TOKEN_PATH` | Path to the directory where token/checkpoint/ledger files are stored. | Project root directory |
|
|
164
|
+
| `PLUGIN_PATH`| Path to the directory where plugins are loaded from. | Project root directory |
|
|
165
|
+
| `NODE_ENV` | Node.js environment (affects config/environment selection). | `development` |
|
|
166
|
+
| `SYNC_ENV` | Overrides the environment used for sync operations. | Value of `NODE_ENV` |
|
|
167
|
+
| `KILLDURATION`| Time (in ms) before the process is forcefully killed (overrides config). | Value from config |
|
|
168
|
+
|
|
169
|
+
**Note:**
|
|
170
|
+
- `TOKEN_PATH` is especially useful for storing token data in a custom directory (e.g., for selective re-syncing based on timestamps).
|
|
171
|
+
- If a relative path is provided, it is resolved from the project root.
|
|
172
|
+
- These variables can be set in your shell or in your process manager configuration.
|
|
173
|
+
|
|
157
174
|
### Further Reading
|
|
158
175
|
|
|
159
176
|
- [Getting started with Contentstack DataSync](https://www.contentstack.com/docs/guide/synchronization/contentstack-datasync)
|
package/dist/api.js
CHANGED
|
@@ -17,11 +17,13 @@ const sanitize_url_1 = require("@braintree/sanitize-url");
|
|
|
17
17
|
const fs_1 = require("./util/fs");
|
|
18
18
|
const debug = (0, debug_1.default)('api');
|
|
19
19
|
let MAX_RETRY_LIMIT;
|
|
20
|
+
let RETRY_DELAY_BASE = 200; // Default base delay in milliseconds
|
|
21
|
+
let TIMEOUT = 30000; // Default timeout in milliseconds
|
|
20
22
|
let Contentstack;
|
|
21
23
|
/**
|
|
22
24
|
* @description Initialize sync utilities API requests
|
|
23
25
|
* @param {Object} contentstack - Contentstack configuration details
|
|
24
|
-
|
|
26
|
+
*/
|
|
25
27
|
const init = (contentstack) => {
|
|
26
28
|
const packageInfo = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '..', 'package.json')));
|
|
27
29
|
Contentstack = contentstack;
|
|
@@ -36,6 +38,12 @@ const init = (contentstack) => {
|
|
|
36
38
|
if (Contentstack.MAX_RETRY_LIMIT) {
|
|
37
39
|
MAX_RETRY_LIMIT = Contentstack.MAX_RETRY_LIMIT;
|
|
38
40
|
}
|
|
41
|
+
if (Contentstack.RETRY_DELAY_BASE) {
|
|
42
|
+
RETRY_DELAY_BASE = Contentstack.RETRY_DELAY_BASE;
|
|
43
|
+
}
|
|
44
|
+
if (Contentstack.TIMEOUT) {
|
|
45
|
+
TIMEOUT = Contentstack.TIMEOUT;
|
|
46
|
+
}
|
|
39
47
|
};
|
|
40
48
|
exports.init = init;
|
|
41
49
|
/**
|
|
@@ -60,12 +68,13 @@ const get = (req, RETRY = 1) => {
|
|
|
60
68
|
path: (0, sanitize_url_1.sanitizeUrl)(encodeURI(req.path)),
|
|
61
69
|
port: Contentstack.port,
|
|
62
70
|
protocol: Contentstack.protocol,
|
|
71
|
+
timeout: TIMEOUT, // Configurable timeout to prevent socket hang ups
|
|
63
72
|
};
|
|
64
73
|
try {
|
|
65
74
|
debug(`${options.method.toUpperCase()}: ${options.path}`);
|
|
66
75
|
let timeDelay;
|
|
67
76
|
let body = '';
|
|
68
|
-
(0, https_1.request)(options, (response) => {
|
|
77
|
+
const httpRequest = (0, https_1.request)(options, (response) => {
|
|
69
78
|
response
|
|
70
79
|
.setEncoding('utf-8')
|
|
71
80
|
.on('data', (chunk) => body += chunk)
|
|
@@ -75,8 +84,8 @@ const get = (req, RETRY = 1) => {
|
|
|
75
84
|
return resolve(JSON.parse(body));
|
|
76
85
|
}
|
|
77
86
|
else if (response.statusCode === 429) {
|
|
78
|
-
timeDelay = Math.pow(Math.SQRT2, RETRY) *
|
|
79
|
-
debug(`API rate limit exceeded. Retrying ${options.path} with ${timeDelay}
|
|
87
|
+
timeDelay = Math.pow(Math.SQRT2, RETRY) * RETRY_DELAY_BASE;
|
|
88
|
+
debug(`API rate limit exceeded. Retrying ${options.path} with ${timeDelay} ms delay`);
|
|
80
89
|
return setTimeout(() => {
|
|
81
90
|
return (0, exports.get)(req, RETRY)
|
|
82
91
|
.then(resolve)
|
|
@@ -85,8 +94,8 @@ const get = (req, RETRY = 1) => {
|
|
|
85
94
|
}
|
|
86
95
|
else if (response.statusCode >= 500) {
|
|
87
96
|
// retry, with delay
|
|
88
|
-
timeDelay = Math.pow(Math.SQRT2, RETRY) *
|
|
89
|
-
debug(`Retrying ${options.path} with ${timeDelay}
|
|
97
|
+
timeDelay = Math.pow(Math.SQRT2, RETRY) * RETRY_DELAY_BASE;
|
|
98
|
+
debug(`Retrying ${options.path} with ${timeDelay} ms delay`);
|
|
90
99
|
RETRY++;
|
|
91
100
|
return setTimeout(() => {
|
|
92
101
|
return (0, exports.get)(req, RETRY)
|
|
@@ -99,9 +108,31 @@ const get = (req, RETRY = 1) => {
|
|
|
99
108
|
return reject(body);
|
|
100
109
|
}
|
|
101
110
|
});
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
|
|
111
|
+
});
|
|
112
|
+
// Set socket timeout to handle socket hang ups
|
|
113
|
+
httpRequest.setTimeout(options.timeout, () => {
|
|
114
|
+
debug(`Request timeout for ${options.path || 'unknown'}`);
|
|
115
|
+
httpRequest.destroy();
|
|
116
|
+
reject(new Error('Request timeout'));
|
|
117
|
+
});
|
|
118
|
+
// Enhanced error handling for socket hang ups and connection resets
|
|
119
|
+
httpRequest.on('error', (error) => {
|
|
120
|
+
var _a;
|
|
121
|
+
debug(`Request error for ${options.path || 'unknown'}: ${(error === null || error === void 0 ? void 0 : error.message) || 'Unknown error'} (${(error === null || error === void 0 ? void 0 : error.code) || 'NO_CODE'})`);
|
|
122
|
+
// Handle socket hang up and connection reset errors with retry
|
|
123
|
+
if (((error === null || error === void 0 ? void 0 : error.code) === 'ECONNRESET' || ((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('socket hang up'))) && RETRY <= MAX_RETRY_LIMIT) {
|
|
124
|
+
timeDelay = Math.pow(Math.SQRT2, RETRY) * RETRY_DELAY_BASE;
|
|
125
|
+
debug(`Socket hang up detected. Retrying ${options.path || 'unknown'} with ${timeDelay} ms delay (attempt ${RETRY}/${MAX_RETRY_LIMIT})`);
|
|
126
|
+
RETRY++;
|
|
127
|
+
return setTimeout(() => {
|
|
128
|
+
return (0, exports.get)(req, RETRY)
|
|
129
|
+
.then(resolve)
|
|
130
|
+
.catch(reject);
|
|
131
|
+
}, timeDelay);
|
|
132
|
+
}
|
|
133
|
+
return reject(error);
|
|
134
|
+
});
|
|
135
|
+
httpRequest.end();
|
|
105
136
|
}
|
|
106
137
|
catch (error) {
|
|
107
138
|
return reject(error);
|
package/dist/config.js
CHANGED
package/dist/core/index.js
CHANGED
|
@@ -310,6 +310,7 @@ const fire = (req) => {
|
|
|
310
310
|
});
|
|
311
311
|
return (0, promise_map_1.map)(contentTypeUids, (uid) => {
|
|
312
312
|
return new Promise((mapResolve, mapReject) => {
|
|
313
|
+
debug(`API called with for content type: ${uid}`);
|
|
313
314
|
return (0, api_1.get)({
|
|
314
315
|
path: `${Contentstack.apis.content_types}${uid}`,
|
|
315
316
|
qs: {
|
|
@@ -330,6 +331,7 @@ const fire = (req) => {
|
|
|
330
331
|
err.code = 'ICTC';
|
|
331
332
|
return mapReject(err);
|
|
332
333
|
}).catch((error) => {
|
|
334
|
+
debug('Error [map] fetching content type schema:', error);
|
|
333
335
|
if ((0, inet_1.netConnectivityIssues)(error)) {
|
|
334
336
|
flag.SQ = false;
|
|
335
337
|
}
|
|
@@ -345,14 +347,17 @@ const fire = (req) => {
|
|
|
345
347
|
flag.SQ = false;
|
|
346
348
|
}
|
|
347
349
|
// Errorred while fetching content type schema
|
|
350
|
+
debug('Error [mapResolve]:', error);
|
|
348
351
|
return reject(error);
|
|
349
352
|
});
|
|
350
353
|
}).catch((processError) => {
|
|
354
|
+
debug('Error [filterItems]:', processError);
|
|
351
355
|
return reject(processError);
|
|
352
356
|
});
|
|
353
357
|
}
|
|
354
358
|
return postProcess(req, syncResponse)
|
|
355
|
-
.then(resolve)
|
|
359
|
+
.then(resolve)
|
|
360
|
+
.catch(reject);
|
|
356
361
|
}).catch((error) => {
|
|
357
362
|
debug('Error [fire]', error);
|
|
358
363
|
if ((0, inet_1.netConnectivityIssues)(error)) {
|
|
@@ -394,6 +399,7 @@ const postProcess = (req, resp) => {
|
|
|
394
399
|
flag.SQ = false;
|
|
395
400
|
return resolve('');
|
|
396
401
|
}
|
|
402
|
+
debug(`Re-Fire called with: ${JSON.stringify(req)}`);
|
|
397
403
|
return fire(req)
|
|
398
404
|
.then(resolve)
|
|
399
405
|
.catch(reject);
|
package/dist/core/inet.js
CHANGED
|
@@ -68,7 +68,10 @@ const checkNetConnectivity = () => {
|
|
|
68
68
|
};
|
|
69
69
|
exports.checkNetConnectivity = checkNetConnectivity;
|
|
70
70
|
const netConnectivityIssues = (error) => {
|
|
71
|
-
|
|
71
|
+
var _a;
|
|
72
|
+
// Include socket hang up and connection reset errors as network connectivity issues
|
|
73
|
+
const networkErrorCodes = ['ENOTFOUND', 'ETIMEDOUT', 'ECONNRESET', 'EPIPE', 'EHOSTUNREACH'];
|
|
74
|
+
if (networkErrorCodes.includes(error.code) || ((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('socket hang up'))) {
|
|
72
75
|
return true;
|
|
73
76
|
}
|
|
74
77
|
return false;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentstack/datasync-manager",
|
|
3
3
|
"author": "Contentstack LLC <support@contentstack.com>",
|
|
4
|
-
"version": "2.1.
|
|
4
|
+
"version": "2.1.2",
|
|
5
5
|
"description": "The primary module of Contentstack DataSync. Syncs Contentstack data with your server using Contentstack Sync API",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"dependencies": {
|