n8n-nodes-prestashop8 2.5.0 → 2.6.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.
|
@@ -670,6 +670,38 @@ exports.PrestaShop8Description = {
|
|
|
670
670
|
},
|
|
671
671
|
],
|
|
672
672
|
},
|
|
673
|
+
{
|
|
674
|
+
displayName: 'Retry On Error',
|
|
675
|
+
name: 'retry',
|
|
676
|
+
type: 'collection',
|
|
677
|
+
placeholder: 'Add Retry Option',
|
|
678
|
+
default: {},
|
|
679
|
+
options: [
|
|
680
|
+
{
|
|
681
|
+
displayName: 'Enabled',
|
|
682
|
+
name: 'retryEnabled',
|
|
683
|
+
type: 'boolean',
|
|
684
|
+
default: false,
|
|
685
|
+
description: 'Whether to retry a call that fails on a transient error (network timeout, connection drop, 5xx server error or 429 rate-limit). Never retries client errors (4xx).',
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
displayName: 'Max Retries',
|
|
689
|
+
name: 'maxRetries',
|
|
690
|
+
type: 'number',
|
|
691
|
+
typeOptions: { minValue: 0 },
|
|
692
|
+
default: 3,
|
|
693
|
+
description: 'Maximum number of retry attempts per failing call. This budget is reset for each call, it is not a global limit for the whole node execution.',
|
|
694
|
+
},
|
|
695
|
+
{
|
|
696
|
+
displayName: 'Retry Delay (ms)',
|
|
697
|
+
name: 'retryDelay',
|
|
698
|
+
type: 'number',
|
|
699
|
+
typeOptions: { minValue: 0 },
|
|
700
|
+
default: 1000,
|
|
701
|
+
description: 'Pause in milliseconds between two successive retry attempts of the same call',
|
|
702
|
+
},
|
|
703
|
+
],
|
|
704
|
+
},
|
|
673
705
|
{
|
|
674
706
|
displayName: 'Timeout (ms)',
|
|
675
707
|
name: 'timeout',
|
|
@@ -124,13 +124,13 @@ class PrestaShop8 {
|
|
|
124
124
|
}
|
|
125
125
|
requestUrl = (0, utils_1.buildUrlWithFilters)(`${credentials.baseUrl}/${resource}`, urlParams, rawMode);
|
|
126
126
|
if (rawMode) {
|
|
127
|
-
const rawResult = await (0, http_1.executeRawModeRequest)(requestUrl, credentials, timeout, neverError, includeResponseHeaders, operation, resource);
|
|
127
|
+
const rawResult = await (0, http_1.executeRawModeRequest)(requestUrl, credentials, timeout, neverError, includeResponseHeaders, operation, resource, opts.retry);
|
|
128
128
|
responseData = rawResult.responseData;
|
|
129
129
|
requestDebugInfo = rawResult.requestDebugInfo;
|
|
130
130
|
}
|
|
131
131
|
else {
|
|
132
132
|
const options = (0, http_1.buildHttpOptions)('GET', requestUrl, credentials, rawMode, timeout);
|
|
133
|
-
const { response, debugInfo, url, responseHeaders, statusCode } = await (0, http_1.executeHttpRequest)(this.helpers, options, credentials, rawMode, operation, resource, neverError);
|
|
133
|
+
const { response, debugInfo, url, responseHeaders, statusCode } = await (0, http_1.executeHttpRequest)(this.helpers, options, credentials, rawMode, operation, resource, neverError, undefined, opts.retry);
|
|
134
134
|
requestUrl = url;
|
|
135
135
|
requestDebugInfo = debugInfo;
|
|
136
136
|
const processedResponse = (0, utils_1.processResponseForMode)(response, resource);
|
|
@@ -153,7 +153,7 @@ class PrestaShop8 {
|
|
|
153
153
|
}
|
|
154
154
|
requestUrl = (0, utils_1.buildUrlWithFilters)(`${credentials.baseUrl}/${resource}/${id}`, urlParams, rawMode);
|
|
155
155
|
const options = (0, http_1.buildHttpOptions)('GET', requestUrl, credentials, rawMode, timeout);
|
|
156
|
-
const { response, debugInfo, url, responseHeaders, statusCode } = await (0, http_1.executeHttpRequest)(this.helpers, options, credentials, rawMode, operation, resource, neverError);
|
|
156
|
+
const { response, debugInfo, url, responseHeaders, statusCode } = await (0, http_1.executeHttpRequest)(this.helpers, options, credentials, rawMode, operation, resource, neverError, undefined, opts.retry);
|
|
157
157
|
requestUrl = url;
|
|
158
158
|
requestDebugInfo = debugInfo;
|
|
159
159
|
const processedResponse = rawMode ? { raw: response } : (0, utils_1.processResponseForMode)(response, resource);
|
|
@@ -190,7 +190,7 @@ class PrestaShop8 {
|
|
|
190
190
|
// Build XML using new format
|
|
191
191
|
body = (0, utils_1.buildCreateXml)(resource, fieldsToCreate);
|
|
192
192
|
const options = (0, http_1.buildHttpOptions)('POST', `${credentials.baseUrl}/${resource}`, credentials, rawMode, timeout, body);
|
|
193
|
-
const { response, debugInfo, url, responseHeaders, statusCode } = await (0, http_1.executeHttpRequest)(this.helpers, options, credentials, rawMode, operation, resource, neverError, body);
|
|
193
|
+
const { response, debugInfo, url, responseHeaders, statusCode } = await (0, http_1.executeHttpRequest)(this.helpers, options, credentials, rawMode, operation, resource, neverError, body, opts.retry);
|
|
194
194
|
requestUrl = url;
|
|
195
195
|
requestDebugInfo = debugInfo;
|
|
196
196
|
const processedResponse = rawMode ? { raw: response } : (0, utils_1.processResponseForMode)(response, resource);
|
|
@@ -223,7 +223,7 @@ class PrestaShop8 {
|
|
|
223
223
|
// Build XML using new format
|
|
224
224
|
body = (0, utils_1.buildUpdateXml)(resource, id, fieldsToUpdate);
|
|
225
225
|
const options = (0, http_1.buildHttpOptions)('PATCH', `${credentials.baseUrl}/${resource}/${id}`, credentials, rawMode, timeout, body);
|
|
226
|
-
const { response, debugInfo, url, responseHeaders, statusCode } = await (0, http_1.executeHttpRequest)(this.helpers, options, credentials, rawMode, operation, resource, neverError, body);
|
|
226
|
+
const { response, debugInfo, url, responseHeaders, statusCode } = await (0, http_1.executeHttpRequest)(this.helpers, options, credentials, rawMode, operation, resource, neverError, body, opts.retry);
|
|
227
227
|
requestUrl = url;
|
|
228
228
|
requestDebugInfo = debugInfo;
|
|
229
229
|
const processedResponse = rawMode ? { raw: response } : (0, utils_1.processResponseForMode)(response, resource);
|
|
@@ -295,13 +295,13 @@ class PrestaShop8 {
|
|
|
295
295
|
}
|
|
296
296
|
requestUrl = (0, utils_1.buildUrlWithFilters)(`${credentials.baseUrl}/${resource}`, urlParams, rawMode);
|
|
297
297
|
if (rawMode) {
|
|
298
|
-
const rawResult = await (0, http_1.executeRawModeRequest)(requestUrl, credentials, timeout, neverError, includeResponseHeaders, operation, resource);
|
|
298
|
+
const rawResult = await (0, http_1.executeRawModeRequest)(requestUrl, credentials, timeout, neverError, includeResponseHeaders, operation, resource, opts.retry);
|
|
299
299
|
responseData = rawResult.responseData;
|
|
300
300
|
requestDebugInfo = rawResult.requestDebugInfo;
|
|
301
301
|
}
|
|
302
302
|
else {
|
|
303
303
|
const options = (0, http_1.buildHttpOptions)('GET', requestUrl, credentials, rawMode, timeout);
|
|
304
|
-
const { response, debugInfo, url, responseHeaders, statusCode } = await (0, http_1.executeHttpRequest)(this.helpers, options, credentials, rawMode, operation, resource, neverError);
|
|
304
|
+
const { response, debugInfo, url, responseHeaders, statusCode } = await (0, http_1.executeHttpRequest)(this.helpers, options, credentials, rawMode, operation, resource, neverError, undefined, opts.retry);
|
|
305
305
|
requestUrl = url;
|
|
306
306
|
requestDebugInfo = debugInfo;
|
|
307
307
|
const processedResponse = (0, utils_1.processResponseForMode)(response, resource);
|
|
@@ -315,7 +315,7 @@ class PrestaShop8 {
|
|
|
315
315
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'ID required for this operation');
|
|
316
316
|
}
|
|
317
317
|
const options = (0, http_1.buildHttpOptions)('DELETE', `${credentials.baseUrl}/${resource}/${id}`, credentials, rawMode, timeout);
|
|
318
|
-
const { debugInfo, url, responseHeaders, statusCode } = await (0, http_1.executeHttpRequest)(this.helpers, options, credentials, rawMode, operation, resource, neverError);
|
|
318
|
+
const { debugInfo, url, responseHeaders, statusCode } = await (0, http_1.executeHttpRequest)(this.helpers, options, credentials, rawMode, operation, resource, neverError, undefined, opts.retry);
|
|
319
319
|
requestUrl = url;
|
|
320
320
|
requestDebugInfo = debugInfo;
|
|
321
321
|
const deleteResponse = {
|
|
@@ -19,11 +19,33 @@ export interface OperationOptions {
|
|
|
19
19
|
showRequestInfo: boolean;
|
|
20
20
|
showRequestUrl: boolean;
|
|
21
21
|
delayBetweenCalls: number;
|
|
22
|
+
retry: RetryOptions;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Retry configuration applied to each individual HTTP call.
|
|
26
|
+
* The retry budget is per call: every failing call gets a fresh set of attempts.
|
|
27
|
+
*/
|
|
28
|
+
export interface RetryOptions {
|
|
29
|
+
enabled: boolean;
|
|
30
|
+
maxRetries: number;
|
|
31
|
+
retryDelay: number;
|
|
22
32
|
}
|
|
23
33
|
/**
|
|
24
34
|
* Pause execution for the given number of milliseconds (used to throttle calls).
|
|
25
35
|
*/
|
|
26
36
|
export declare function sleep(ms: number): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Determine whether an error is transient and worth retrying.
|
|
39
|
+
* Retries on network/timeout errors, 5xx server errors and 429 rate-limit.
|
|
40
|
+
* Never retries on 4xx (invalid key, not found, invalid XML, etc.).
|
|
41
|
+
*/
|
|
42
|
+
export declare function isRetryableError(error: any): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Run an async HTTP operation with per-call retry on transient errors.
|
|
45
|
+
* Each call gets its own retry budget; on the last attempt the error is rethrown
|
|
46
|
+
* so existing neverError / continueOnFail handling applies unchanged.
|
|
47
|
+
*/
|
|
48
|
+
export declare function withRetry<T>(retry: RetryOptions, fn: () => Promise<T>): Promise<T>;
|
|
27
49
|
/**
|
|
28
50
|
* Extract common operation options from node parameters
|
|
29
51
|
*/
|
|
@@ -43,7 +65,7 @@ export declare function wrapResponse(processedResponse: any, includeHeaders: boo
|
|
|
43
65
|
/**
|
|
44
66
|
* Execute HTTP request with debug capture and response metadata
|
|
45
67
|
*/
|
|
46
|
-
export declare function executeHttpRequest(helpers: any, options: IHttpRequestOptions, credentials: any, rawMode: boolean, operation: string, resource: string, neverError?: boolean, body?: string): Promise<{
|
|
68
|
+
export declare function executeHttpRequest(helpers: any, options: IHttpRequestOptions, credentials: any, rawMode: boolean, operation: string, resource: string, neverError?: boolean, body?: string, retry?: RetryOptions): Promise<{
|
|
47
69
|
response: any;
|
|
48
70
|
debugInfo: any;
|
|
49
71
|
url: string;
|
|
@@ -53,7 +75,7 @@ export declare function executeHttpRequest(helpers: any, options: IHttpRequestOp
|
|
|
53
75
|
/**
|
|
54
76
|
* Execute a raw mode request using axios directly (bypasses n8n parsing)
|
|
55
77
|
*/
|
|
56
|
-
export declare function executeRawModeRequest(requestUrl: string, credentials: IPrestaShopCredentials, timeout: number, neverError: boolean, includeResponseHeaders: boolean, operation: string, resource: string): Promise<{
|
|
78
|
+
export declare function executeRawModeRequest(requestUrl: string, credentials: IPrestaShopCredentials, timeout: number, neverError: boolean, includeResponseHeaders: boolean, operation: string, resource: string, retry?: RetryOptions): Promise<{
|
|
57
79
|
responseData: any;
|
|
58
80
|
requestDebugInfo: any;
|
|
59
81
|
requestHeaders: any;
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FILTER_OPERATOR_FORMATS = void 0;
|
|
4
4
|
exports.sleep = sleep;
|
|
5
|
+
exports.isRetryableError = isRetryableError;
|
|
6
|
+
exports.withRetry = withRetry;
|
|
5
7
|
exports.getOperationOptions = getOperationOptions;
|
|
6
8
|
exports.buildHttpOptions = buildHttpOptions;
|
|
7
9
|
exports.captureRequestDebugInfo = captureRequestDebugInfo;
|
|
@@ -34,6 +36,60 @@ exports.FILTER_OPERATOR_FORMATS = {
|
|
|
34
36
|
function sleep(ms) {
|
|
35
37
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
36
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Determine whether an error is transient and worth retrying.
|
|
41
|
+
* Retries on network/timeout errors, 5xx server errors and 429 rate-limit.
|
|
42
|
+
* Never retries on 4xx (invalid key, not found, invalid XML, etc.).
|
|
43
|
+
*/
|
|
44
|
+
function isRetryableError(error) {
|
|
45
|
+
var _a, _b;
|
|
46
|
+
// Network / timeout errors expose a code on the error or its cause
|
|
47
|
+
const code = (error === null || error === void 0 ? void 0 : error.code) || ((_a = error === null || error === void 0 ? void 0 : error.cause) === null || _a === void 0 ? void 0 : _a.code);
|
|
48
|
+
const retryableCodes = [
|
|
49
|
+
'ETIMEDOUT',
|
|
50
|
+
'ECONNRESET',
|
|
51
|
+
'ECONNREFUSED',
|
|
52
|
+
'ECONNABORTED',
|
|
53
|
+
'ENOTFOUND',
|
|
54
|
+
'EAI_AGAIN',
|
|
55
|
+
'EPIPE',
|
|
56
|
+
];
|
|
57
|
+
if (code && retryableCodes.includes(code)) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
// "socket hang up" and timeout messages have no stable code
|
|
61
|
+
const message = String((error === null || error === void 0 ? void 0 : error.message) || '').toLowerCase();
|
|
62
|
+
if (message.includes('socket hang up') || message.includes('timeout')) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
// HTTP status: retry on 429 (rate-limit) and 5xx (server errors) only
|
|
66
|
+
const status = (error === null || error === void 0 ? void 0 : error.httpCode) || ((_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.status);
|
|
67
|
+
if (status === 429 || (status >= 500 && status <= 599)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Run an async HTTP operation with per-call retry on transient errors.
|
|
74
|
+
* Each call gets its own retry budget; on the last attempt the error is rethrown
|
|
75
|
+
* so existing neverError / continueOnFail handling applies unchanged.
|
|
76
|
+
*/
|
|
77
|
+
async function withRetry(retry, fn) {
|
|
78
|
+
const maxRetries = retry.enabled ? Math.max(0, retry.maxRetries) : 0;
|
|
79
|
+
for (let attempt = 0;; attempt++) {
|
|
80
|
+
try {
|
|
81
|
+
return await fn();
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (attempt >= maxRetries || !isRetryableError(error)) {
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
if (retry.retryDelay > 0) {
|
|
88
|
+
await sleep(retry.retryDelay);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
37
93
|
/**
|
|
38
94
|
* Extract common operation options from node parameters
|
|
39
95
|
*/
|
|
@@ -46,6 +102,11 @@ function getOperationOptions(executeFunctions, itemIndex) {
|
|
|
46
102
|
showRequestInfo: executeFunctions.getNodeParameter('options.request.showRequestInfo', itemIndex, false),
|
|
47
103
|
showRequestUrl: executeFunctions.getNodeParameter('options.request.showRequestUrl', itemIndex, false),
|
|
48
104
|
delayBetweenCalls: executeFunctions.getNodeParameter('options.delayBetweenCalls', itemIndex, 0),
|
|
105
|
+
retry: {
|
|
106
|
+
enabled: executeFunctions.getNodeParameter('options.retry.retryEnabled', itemIndex, false),
|
|
107
|
+
maxRetries: executeFunctions.getNodeParameter('options.retry.maxRetries', itemIndex, 3),
|
|
108
|
+
retryDelay: executeFunctions.getNodeParameter('options.retry.retryDelay', itemIndex, 1000),
|
|
109
|
+
},
|
|
49
110
|
};
|
|
50
111
|
}
|
|
51
112
|
/**
|
|
@@ -115,12 +176,12 @@ function wrapResponse(processedResponse, includeHeaders, headers, statusCode) {
|
|
|
115
176
|
/**
|
|
116
177
|
* Execute HTTP request with debug capture and response metadata
|
|
117
178
|
*/
|
|
118
|
-
async function executeHttpRequest(helpers, options, credentials, rawMode, operation, resource, neverError = false, body) {
|
|
179
|
+
async function executeHttpRequest(helpers, options, credentials, rawMode, operation, resource, neverError = false, body, retry = { enabled: false, maxRetries: 0, retryDelay: 0 }) {
|
|
119
180
|
var _a, _b, _c, _d;
|
|
120
181
|
const requestUrl = options.url;
|
|
121
182
|
const debugInfo = captureRequestDebugInfo(options, credentials, rawMode, operation, resource, body);
|
|
122
183
|
try {
|
|
123
|
-
const response = await helpers.httpRequest(options);
|
|
184
|
+
const response = await withRetry(retry, () => helpers.httpRequest(options));
|
|
124
185
|
return {
|
|
125
186
|
response,
|
|
126
187
|
debugInfo,
|
|
@@ -149,11 +210,11 @@ async function executeHttpRequest(helpers, options, credentials, rawMode, operat
|
|
|
149
210
|
/**
|
|
150
211
|
* Execute a raw mode request using axios directly (bypasses n8n parsing)
|
|
151
212
|
*/
|
|
152
|
-
async function executeRawModeRequest(requestUrl, credentials, timeout, neverError, includeResponseHeaders, operation, resource) {
|
|
213
|
+
async function executeRawModeRequest(requestUrl, credentials, timeout, neverError, includeResponseHeaders, operation, resource, retry = { enabled: false, maxRetries: 0, retryDelay: 0 }) {
|
|
153
214
|
var _a, _b;
|
|
154
215
|
let responseData;
|
|
155
216
|
try {
|
|
156
|
-
const axiosResponse = await axios({
|
|
217
|
+
const axiosResponse = await withRetry(retry, () => axios({
|
|
157
218
|
method: 'GET',
|
|
158
219
|
url: requestUrl,
|
|
159
220
|
auth: {
|
|
@@ -164,7 +225,7 @@ async function executeRawModeRequest(requestUrl, credentials, timeout, neverErro
|
|
|
164
225
|
timeout: timeout || 30000,
|
|
165
226
|
transformResponse: [(data) => data],
|
|
166
227
|
validateStatus: neverError ? () => true : undefined,
|
|
167
|
-
});
|
|
228
|
+
}));
|
|
168
229
|
const requestDebugInfo = captureRequestDebugInfo({
|
|
169
230
|
method: 'GET',
|
|
170
231
|
url: requestUrl,
|
package/package.json
CHANGED