n8n-nodes-prestashop8 1.1.3 → 1.2.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.
|
@@ -154,16 +154,23 @@ exports.PrestaShop8Description = {
|
|
|
154
154
|
placeholder: '[id_DESC] ou [name_ASC]',
|
|
155
155
|
description: 'Sort criteria (e.g. [id_DESC], [name_ASC], [date_add_DESC])',
|
|
156
156
|
},
|
|
157
|
+
{
|
|
158
|
+
displayName: 'Date Format',
|
|
159
|
+
name: 'dateFormat',
|
|
160
|
+
type: 'boolean',
|
|
161
|
+
default: false,
|
|
162
|
+
description: 'Enable date processing in filters (adds date=1 parameter)',
|
|
163
|
+
},
|
|
157
164
|
],
|
|
158
165
|
},
|
|
159
|
-
// Display options (always visible for list/search operations)
|
|
166
|
+
// Display options (always visible for list/search/getById operations)
|
|
160
167
|
{
|
|
161
168
|
displayName: 'Display',
|
|
162
169
|
name: 'display',
|
|
163
170
|
type: 'options',
|
|
164
171
|
displayOptions: {
|
|
165
172
|
show: {
|
|
166
|
-
operation: ['list', 'search'],
|
|
173
|
+
operation: ['list', 'search', 'getById'],
|
|
167
174
|
},
|
|
168
175
|
},
|
|
169
176
|
options: [
|
|
@@ -193,7 +200,7 @@ exports.PrestaShop8Description = {
|
|
|
193
200
|
type: 'string',
|
|
194
201
|
displayOptions: {
|
|
195
202
|
show: {
|
|
196
|
-
operation: ['list', 'search'],
|
|
203
|
+
operation: ['list', 'search', 'getById'],
|
|
197
204
|
display: ['custom'],
|
|
198
205
|
},
|
|
199
206
|
},
|
|
@@ -226,9 +233,13 @@ exports.PrestaShop8Description = {
|
|
|
226
233
|
name: 'field',
|
|
227
234
|
type: 'string',
|
|
228
235
|
default: '',
|
|
229
|
-
required: true,
|
|
230
236
|
placeholder: 'name, reference, price, etc.',
|
|
231
237
|
description: 'Name of field to filter. Examples: name, reference, price, active, id_manufacturer, etc.',
|
|
238
|
+
displayOptions: {
|
|
239
|
+
hide: {
|
|
240
|
+
operator: ['CUSTOM'],
|
|
241
|
+
},
|
|
242
|
+
},
|
|
232
243
|
},
|
|
233
244
|
{
|
|
234
245
|
displayName: 'Operator',
|
|
@@ -237,16 +248,33 @@ exports.PrestaShop8Description = {
|
|
|
237
248
|
options: types_1.FILTER_OPERATORS,
|
|
238
249
|
default: '=',
|
|
239
250
|
noDataExpression: true,
|
|
240
|
-
description: 'Comparison operator for filtering.
|
|
251
|
+
description: 'Comparison operator for filtering. Custom allows you to write your own filter expression.',
|
|
241
252
|
},
|
|
242
253
|
{
|
|
243
254
|
displayName: 'Value',
|
|
244
255
|
name: 'value',
|
|
245
256
|
type: 'string',
|
|
246
257
|
default: '',
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
258
|
+
placeholder: 'search value or interval',
|
|
259
|
+
description: 'Value to search for. Examples: "Product Name", "1" (active), "29.99" (price). For Equal/Not Equal: use "10,20" for intervals (becomes [10,20]).',
|
|
260
|
+
displayOptions: {
|
|
261
|
+
hide: {
|
|
262
|
+
operator: ['IS_EMPTY', 'IS_NOT_EMPTY', 'CUSTOM'],
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
displayName: 'Custom Filter Expression',
|
|
268
|
+
name: 'customFilter',
|
|
269
|
+
type: 'string',
|
|
270
|
+
default: '',
|
|
271
|
+
placeholder: 'filter[name]=[Product]%',
|
|
272
|
+
description: 'Complete filter expression in PrestaShop format. Example: filter[name]=[Product]%, filter[price]=>[100], etc.',
|
|
273
|
+
displayOptions: {
|
|
274
|
+
show: {
|
|
275
|
+
operator: ['CUSTOM'],
|
|
276
|
+
},
|
|
277
|
+
},
|
|
250
278
|
},
|
|
251
279
|
],
|
|
252
280
|
},
|
|
@@ -610,34 +638,66 @@ exports.PrestaShop8Description = {
|
|
|
610
638
|
],
|
|
611
639
|
description: 'Fields to update in the resource. Add multiple field/value pairs as needed. For multilingual fields (names, descriptions), use format: fieldname-langid (e.g., name-1, name-2 for different languages).',
|
|
612
640
|
},
|
|
613
|
-
//
|
|
641
|
+
// Options
|
|
614
642
|
{
|
|
615
|
-
displayName: '
|
|
616
|
-
name: '
|
|
643
|
+
displayName: 'Options',
|
|
644
|
+
name: 'options',
|
|
617
645
|
type: 'collection',
|
|
618
|
-
default: {},
|
|
619
646
|
placeholder: 'Add Option',
|
|
647
|
+
default: {},
|
|
620
648
|
options: [
|
|
621
649
|
{
|
|
622
|
-
displayName: '
|
|
623
|
-
name: '
|
|
624
|
-
type: '
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
650
|
+
displayName: 'Request',
|
|
651
|
+
name: 'request',
|
|
652
|
+
type: 'collection',
|
|
653
|
+
placeholder: 'Add Request Option',
|
|
654
|
+
default: {},
|
|
655
|
+
options: [
|
|
656
|
+
{
|
|
657
|
+
displayName: 'Show Request Info',
|
|
658
|
+
name: 'showRequestInfo',
|
|
659
|
+
type: 'boolean',
|
|
660
|
+
default: false,
|
|
661
|
+
description: 'Add complete HTTP request information to the response (method, URL, headers, authentication, body, etc.) - useful for debugging API communication',
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
displayName: 'Show Request URL',
|
|
665
|
+
name: 'showRequestUrl',
|
|
666
|
+
type: 'boolean',
|
|
667
|
+
default: false,
|
|
668
|
+
description: 'Add the complete request URL to the response - useful for debugging API calls',
|
|
669
|
+
},
|
|
670
|
+
],
|
|
634
671
|
},
|
|
635
672
|
{
|
|
636
|
-
displayName: '
|
|
637
|
-
name: '
|
|
638
|
-
type: '
|
|
639
|
-
|
|
640
|
-
|
|
673
|
+
displayName: 'Response',
|
|
674
|
+
name: 'response',
|
|
675
|
+
type: 'collection',
|
|
676
|
+
placeholder: 'Add Response Option',
|
|
677
|
+
default: {},
|
|
678
|
+
options: [
|
|
679
|
+
{
|
|
680
|
+
displayName: 'Include Response Headers and Status',
|
|
681
|
+
name: 'includeResponseHeaders',
|
|
682
|
+
type: 'boolean',
|
|
683
|
+
default: false,
|
|
684
|
+
description: 'Whether to return the full response (headers and response status code) data instead of only the body',
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
displayName: 'Never Error',
|
|
688
|
+
name: 'neverError',
|
|
689
|
+
type: 'boolean',
|
|
690
|
+
default: false,
|
|
691
|
+
description: 'Whether to succeeds also when status code is not 2xx',
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
displayName: 'Raw Mode',
|
|
695
|
+
name: 'rawMode',
|
|
696
|
+
type: 'boolean',
|
|
697
|
+
default: false,
|
|
698
|
+
description: 'Return raw PrestaShop XML/JSON response without n8n processing - useful for debugging or custom XML handling',
|
|
699
|
+
},
|
|
700
|
+
],
|
|
641
701
|
},
|
|
642
702
|
{
|
|
643
703
|
displayName: 'Timeout (ms)',
|
|
@@ -67,17 +67,41 @@ function buildHttpOptions(method, url, credentials, rawMode, timeout, body) {
|
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
|
-
* Execute HTTP request with debug capture
|
|
70
|
+
* Execute HTTP request with debug capture and response metadata
|
|
71
71
|
*/
|
|
72
|
-
async function executeHttpRequest(helpers, options, credentials, rawMode, operation, resource, body) {
|
|
72
|
+
async function executeHttpRequest(helpers, options, credentials, rawMode, operation, resource, neverError = false, body) {
|
|
73
|
+
var _a, _b, _c, _d;
|
|
73
74
|
const requestUrl = options.url;
|
|
74
75
|
const debugInfo = captureRequestDebugInfo(options, credentials, rawMode, operation, resource, body);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
response,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
try {
|
|
77
|
+
const response = await helpers.httpRequest(options);
|
|
78
|
+
// For n8n's httpRequest, the response IS the body, headers are not directly available
|
|
79
|
+
// We return the response as-is since n8n handles the HTTP layer
|
|
80
|
+
return {
|
|
81
|
+
response,
|
|
82
|
+
debugInfo,
|
|
83
|
+
url: requestUrl,
|
|
84
|
+
responseHeaders: {},
|
|
85
|
+
statusCode: 200, // Assume success if no error thrown
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (neverError) {
|
|
90
|
+
// Return structured error response for Never Error mode
|
|
91
|
+
const errorResponse = {
|
|
92
|
+
status: error.httpCode || ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) || 500,
|
|
93
|
+
message: ((_b = error.response) === null || _b === void 0 ? void 0 : _b.data) || ''
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
response: errorResponse,
|
|
97
|
+
debugInfo,
|
|
98
|
+
url: requestUrl,
|
|
99
|
+
responseHeaders: ((_c = error.response) === null || _c === void 0 ? void 0 : _c.headers) || {},
|
|
100
|
+
statusCode: error.httpCode || ((_d = error.response) === null || _d === void 0 ? void 0 : _d.status) || 500,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
81
105
|
}
|
|
82
106
|
/**
|
|
83
107
|
* Collect required fields from individual input parameters
|
|
@@ -266,6 +290,7 @@ class PrestaShop8 {
|
|
|
266
290
|
};
|
|
267
291
|
}
|
|
268
292
|
async execute() {
|
|
293
|
+
var _a, _b, _c, _d;
|
|
269
294
|
const items = this.getInputData();
|
|
270
295
|
const returnData = [];
|
|
271
296
|
const credentials = await this.getCredentials('prestaShop8Api');
|
|
@@ -278,7 +303,7 @@ class PrestaShop8 {
|
|
|
278
303
|
let requestUrl = '';
|
|
279
304
|
let requestHeaders = {};
|
|
280
305
|
let requestDebugInfo = {};
|
|
281
|
-
const rawMode = this.getNodeParameter('
|
|
306
|
+
const rawMode = this.getNodeParameter('options.response.rawMode', i, false);
|
|
282
307
|
// Use resource for response processing
|
|
283
308
|
const currentMode = resource;
|
|
284
309
|
switch (operation) {
|
|
@@ -286,28 +311,23 @@ class PrestaShop8 {
|
|
|
286
311
|
const advancedOptions = this.getNodeParameter('advancedOptions', i, {});
|
|
287
312
|
const display = this.getNodeParameter('display', i, 'full');
|
|
288
313
|
const customFields = this.getNodeParameter('customFields', i, '');
|
|
289
|
-
// Handle display parameter - minimal = no display param (IDs only)
|
|
290
314
|
const displayValue = (0, utils_1.processDisplayParameter)(display, resource, customFields);
|
|
291
|
-
|
|
315
|
+
const urlParams = {
|
|
292
316
|
limit: advancedOptions.limit,
|
|
293
317
|
sort: advancedOptions.sort,
|
|
294
|
-
display: displayValue,
|
|
295
|
-
}, rawMode);
|
|
296
|
-
const options = {
|
|
297
|
-
method: 'GET',
|
|
298
|
-
url: requestUrl,
|
|
299
|
-
auth: {
|
|
300
|
-
username: credentials.apiKey,
|
|
301
|
-
password: '',
|
|
302
|
-
},
|
|
303
|
-
headers: buildHeaders(rawMode),
|
|
304
|
-
timeout: this.getNodeParameter('debugOptions.timeout', i, 30000),
|
|
305
|
-
...(rawMode ? { json: false } : {}),
|
|
306
318
|
};
|
|
307
|
-
//
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
319
|
+
// Add date=1 parameter if Date Format option is enabled
|
|
320
|
+
if (advancedOptions.dateFormat) {
|
|
321
|
+
urlParams.date = 1;
|
|
322
|
+
}
|
|
323
|
+
// Only add display parameter if not null (minimal mode returns null)
|
|
324
|
+
if (displayValue !== null) {
|
|
325
|
+
urlParams.display = displayValue;
|
|
326
|
+
}
|
|
327
|
+
requestUrl = (0, utils_1.buildUrlWithFilters)(`${credentials.baseUrl}/${resource}`, urlParams, rawMode);
|
|
328
|
+
const timeout = this.getNodeParameter('options.timeout', i, 30000);
|
|
329
|
+
const neverError = this.getNodeParameter('options.response.neverError', i, false);
|
|
330
|
+
const includeResponseHeaders = this.getNodeParameter('options.response.includeResponseHeaders', i, false);
|
|
311
331
|
if (rawMode) {
|
|
312
332
|
// In Raw mode, use axios directly to avoid n8n automatic parsing
|
|
313
333
|
const axios = require('axios');
|
|
@@ -319,15 +339,47 @@ class PrestaShop8 {
|
|
|
319
339
|
username: credentials.apiKey,
|
|
320
340
|
password: ''
|
|
321
341
|
},
|
|
322
|
-
headers:
|
|
323
|
-
timeout:
|
|
324
|
-
transformResponse: [(data) => data]
|
|
342
|
+
headers: buildHeaders(rawMode),
|
|
343
|
+
timeout: timeout || 30000,
|
|
344
|
+
transformResponse: [(data) => data],
|
|
345
|
+
validateStatus: neverError ? () => true : undefined // Accept all status codes if neverError is true
|
|
325
346
|
});
|
|
326
|
-
|
|
347
|
+
requestDebugInfo = captureRequestDebugInfo({
|
|
348
|
+
method: 'GET',
|
|
349
|
+
url: requestUrl,
|
|
350
|
+
headers: buildHeaders(rawMode),
|
|
351
|
+
timeout: timeout
|
|
352
|
+
}, credentials, rawMode, operation, resource);
|
|
353
|
+
requestHeaders = requestDebugInfo.headers;
|
|
354
|
+
// Check if this is an error response with Never Error mode
|
|
355
|
+
if (neverError && (axiosResponse.status < 200 || axiosResponse.status >= 300)) {
|
|
356
|
+
responseData = {
|
|
357
|
+
status: axiosResponse.status,
|
|
358
|
+
message: axiosResponse.data || ''
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
let processedResponse = { raw: axiosResponse.data };
|
|
363
|
+
// Add response headers and status if requested
|
|
364
|
+
if (includeResponseHeaders) {
|
|
365
|
+
responseData = {
|
|
366
|
+
body: processedResponse,
|
|
367
|
+
headers: axiosResponse.headers,
|
|
368
|
+
statusCode: axiosResponse.status,
|
|
369
|
+
statusMessage: axiosResponse.status >= 200 && axiosResponse.status < 300 ? 'OK' : 'Error'
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
responseData = processedResponse;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
327
376
|
}
|
|
328
377
|
catch (error) {
|
|
329
|
-
if (
|
|
330
|
-
|
|
378
|
+
if (neverError) {
|
|
379
|
+
responseData = {
|
|
380
|
+
status: ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) || 500,
|
|
381
|
+
message: ((_b = error.response) === null || _b === void 0 ? void 0 : _b.data) || ''
|
|
382
|
+
};
|
|
331
383
|
}
|
|
332
384
|
else {
|
|
333
385
|
throw error;
|
|
@@ -335,56 +387,62 @@ class PrestaShop8 {
|
|
|
335
387
|
}
|
|
336
388
|
}
|
|
337
389
|
else {
|
|
338
|
-
|
|
390
|
+
const options = buildHttpOptions('GET', requestUrl, credentials, rawMode, timeout);
|
|
391
|
+
const { response, debugInfo, url, responseHeaders, statusCode } = await executeHttpRequest(this.helpers, options, credentials, rawMode, operation, resource, neverError);
|
|
392
|
+
requestUrl = url;
|
|
393
|
+
requestDebugInfo = debugInfo;
|
|
394
|
+
requestHeaders = debugInfo.headers;
|
|
395
|
+
let processedResponse = (0, utils_1.processResponseForMode)(response, resource, currentMode);
|
|
396
|
+
// Add response headers and status if requested
|
|
397
|
+
if (includeResponseHeaders) {
|
|
398
|
+
responseData = {
|
|
399
|
+
body: processedResponse,
|
|
400
|
+
headers: responseHeaders,
|
|
401
|
+
statusCode: statusCode,
|
|
402
|
+
statusMessage: statusCode && statusCode >= 200 && statusCode < 300 ? 'OK' : 'Error'
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
responseData = processedResponse;
|
|
407
|
+
}
|
|
339
408
|
}
|
|
340
|
-
responseData = rawMode ? { raw: response } : (0, utils_1.processResponseForMode)(response, resource, currentMode);
|
|
341
409
|
break;
|
|
342
410
|
}
|
|
343
411
|
case 'getById': {
|
|
344
412
|
const id = this.getNodeParameter('id', i);
|
|
345
|
-
const
|
|
413
|
+
const display = this.getNodeParameter('display', i, 'full');
|
|
414
|
+
const customFields = this.getNodeParameter('customFields', i, '');
|
|
346
415
|
if (!id) {
|
|
347
416
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'ID required for this operation');
|
|
348
417
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
418
|
+
const displayValue = (0, utils_1.processDisplayParameter)(display, resource, customFields);
|
|
419
|
+
const urlParams = {};
|
|
420
|
+
// Only add display parameter if not null (minimal mode returns null)
|
|
421
|
+
if (displayValue !== null) {
|
|
422
|
+
urlParams.display = displayValue;
|
|
423
|
+
}
|
|
424
|
+
requestUrl = (0, utils_1.buildUrlWithFilters)(`${credentials.baseUrl}/${resource}/${id}`, urlParams, rawMode);
|
|
425
|
+
const timeout = this.getNodeParameter('options.timeout', i, 30000);
|
|
426
|
+
const neverError = this.getNodeParameter('options.response.neverError', i, false);
|
|
427
|
+
const includeResponseHeaders = this.getNodeParameter('options.response.includeResponseHeaders', i, false);
|
|
353
428
|
const options = buildHttpOptions('GET', requestUrl, credentials, rawMode, timeout);
|
|
354
|
-
const { response, debugInfo, url } = await executeHttpRequest(this.helpers, options, credentials, rawMode, operation, resource);
|
|
429
|
+
const { response, debugInfo, url, responseHeaders, statusCode } = await executeHttpRequest(this.helpers, options, credentials, rawMode, operation, resource, neverError);
|
|
355
430
|
requestUrl = url;
|
|
356
431
|
requestDebugInfo = debugInfo;
|
|
357
432
|
requestHeaders = debugInfo.headers;
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
limit: advancedOptions.limit,
|
|
372
|
-
sort: advancedOptions.sort,
|
|
373
|
-
display: displayValue,
|
|
374
|
-
}, rawMode);
|
|
375
|
-
const options = {
|
|
376
|
-
method: 'GET',
|
|
377
|
-
url: requestUrl,
|
|
378
|
-
auth: {
|
|
379
|
-
username: credentials.apiKey,
|
|
380
|
-
password: '',
|
|
381
|
-
},
|
|
382
|
-
headers: buildHeaders(rawMode),
|
|
383
|
-
timeout: this.getNodeParameter('debugOptions.timeout', i, 30000),
|
|
384
|
-
...(rawMode ? { json: false } : {}),
|
|
385
|
-
};
|
|
386
|
-
const response = await this.helpers.httpRequest(options);
|
|
387
|
-
responseData = rawMode ? { raw: response } : (0, utils_1.processResponseForMode)(response, resource, currentMode);
|
|
433
|
+
let processedResponse = rawMode ? { raw: response } : (0, utils_1.processResponseForMode)(response, resource, currentMode);
|
|
434
|
+
// Add response headers and status if requested
|
|
435
|
+
if (includeResponseHeaders) {
|
|
436
|
+
responseData = {
|
|
437
|
+
body: processedResponse,
|
|
438
|
+
headers: responseHeaders,
|
|
439
|
+
statusCode: statusCode,
|
|
440
|
+
statusMessage: statusCode && statusCode >= 200 && statusCode < 300 ? 'OK' : 'Error'
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
responseData = processedResponse;
|
|
445
|
+
}
|
|
388
446
|
break;
|
|
389
447
|
}
|
|
390
448
|
case 'create': {
|
|
@@ -416,13 +474,27 @@ class PrestaShop8 {
|
|
|
416
474
|
}
|
|
417
475
|
// Build XML using new format
|
|
418
476
|
body = (0, utils_1.buildCreateXml)(resource, fieldsToCreate);
|
|
419
|
-
const timeout = this.getNodeParameter('
|
|
477
|
+
const timeout = this.getNodeParameter('options.timeout', i, 30000);
|
|
478
|
+
const neverError = this.getNodeParameter('options.response.neverError', i, false);
|
|
479
|
+
const includeResponseHeaders = this.getNodeParameter('options.response.includeResponseHeaders', i, false);
|
|
420
480
|
const options = buildHttpOptions('POST', `${credentials.baseUrl}/${resource}`, credentials, rawMode, timeout, body);
|
|
421
|
-
const { response, debugInfo, url } = await executeHttpRequest(this.helpers, options, credentials, rawMode, operation, resource, body);
|
|
481
|
+
const { response, debugInfo, url, responseHeaders, statusCode } = await executeHttpRequest(this.helpers, options, credentials, rawMode, operation, resource, neverError, body);
|
|
422
482
|
requestUrl = url;
|
|
423
483
|
requestDebugInfo = debugInfo;
|
|
424
484
|
requestHeaders = debugInfo.headers;
|
|
425
|
-
|
|
485
|
+
let processedResponse = rawMode ? { raw: response } : (0, utils_1.processResponseForMode)(response, resource, currentMode);
|
|
486
|
+
// Add response headers and status if requested
|
|
487
|
+
if (includeResponseHeaders) {
|
|
488
|
+
responseData = {
|
|
489
|
+
body: processedResponse,
|
|
490
|
+
headers: responseHeaders,
|
|
491
|
+
statusCode: statusCode,
|
|
492
|
+
statusMessage: statusCode && statusCode >= 200 && statusCode < 300 ? 'OK' : 'Error'
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
responseData = processedResponse;
|
|
497
|
+
}
|
|
426
498
|
break;
|
|
427
499
|
}
|
|
428
500
|
case 'update': {
|
|
@@ -450,13 +522,244 @@ class PrestaShop8 {
|
|
|
450
522
|
}
|
|
451
523
|
// Build XML using new format
|
|
452
524
|
body = (0, utils_1.buildUpdateXml)(resource, id, fieldsToUpdate);
|
|
453
|
-
const timeout = this.getNodeParameter('
|
|
525
|
+
const timeout = this.getNodeParameter('options.timeout', i, 30000);
|
|
526
|
+
const neverError = this.getNodeParameter('options.response.neverError', i, false);
|
|
527
|
+
const includeResponseHeaders = this.getNodeParameter('options.response.includeResponseHeaders', i, false);
|
|
454
528
|
const options = buildHttpOptions('PATCH', `${credentials.baseUrl}/${resource}/${id}`, credentials, rawMode, timeout, body);
|
|
455
|
-
const { response, debugInfo, url } = await executeHttpRequest(this.helpers, options, credentials, rawMode, operation, resource, body);
|
|
529
|
+
const { response, debugInfo, url, responseHeaders, statusCode } = await executeHttpRequest(this.helpers, options, credentials, rawMode, operation, resource, neverError, body);
|
|
456
530
|
requestUrl = url;
|
|
457
531
|
requestDebugInfo = debugInfo;
|
|
458
532
|
requestHeaders = debugInfo.headers;
|
|
459
|
-
|
|
533
|
+
let processedResponse = rawMode ? { raw: response } : (0, utils_1.processResponseForMode)(response, resource, currentMode);
|
|
534
|
+
// Add response headers and status if requested
|
|
535
|
+
if (includeResponseHeaders) {
|
|
536
|
+
responseData = {
|
|
537
|
+
body: processedResponse,
|
|
538
|
+
headers: responseHeaders,
|
|
539
|
+
statusCode: statusCode,
|
|
540
|
+
statusMessage: statusCode && statusCode >= 200 && statusCode < 300 ? 'OK' : 'Error'
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
responseData = processedResponse;
|
|
545
|
+
}
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
case 'search': {
|
|
549
|
+
const filtersParam = this.getNodeParameter('filters', i, {});
|
|
550
|
+
const advancedOptions = this.getNodeParameter('advancedOptions', i, {});
|
|
551
|
+
const display = this.getNodeParameter('display', i, 'full');
|
|
552
|
+
const customFields = this.getNodeParameter('customFields', i, '');
|
|
553
|
+
// Debug: Log the filters parameter structure
|
|
554
|
+
const showRequestInfo = this.getNodeParameter('options.request.showRequestInfo', i, false);
|
|
555
|
+
if (showRequestInfo) {
|
|
556
|
+
console.log('DEBUG - filtersParam:', JSON.stringify(filtersParam, null, 2));
|
|
557
|
+
}
|
|
558
|
+
const filters = filtersParam.filter || [];
|
|
559
|
+
if (!filters.length) {
|
|
560
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'At least one filter is required for search');
|
|
561
|
+
}
|
|
562
|
+
// Build filter parameters for URL
|
|
563
|
+
const filterParams = {};
|
|
564
|
+
for (const filter of filters) {
|
|
565
|
+
if (showRequestInfo) {
|
|
566
|
+
console.log('DEBUG - Processing filter:', JSON.stringify(filter, null, 2));
|
|
567
|
+
}
|
|
568
|
+
// Handle CUSTOM filter operator
|
|
569
|
+
if (filter.operator === 'CUSTOM') {
|
|
570
|
+
if (filter.customFilter && filter.customFilter.trim()) {
|
|
571
|
+
// Add custom filter directly to URL without any interpretation
|
|
572
|
+
// User writes exactly what they want: date=1, filter[name]=test, etc.
|
|
573
|
+
const customFilter = filter.customFilter.trim();
|
|
574
|
+
// Parse the custom filter to extract key=value pairs for URL construction
|
|
575
|
+
const parts = customFilter.split('&');
|
|
576
|
+
for (const part of parts) {
|
|
577
|
+
const [key, value] = part.split('=', 2);
|
|
578
|
+
if (key && value !== undefined) {
|
|
579
|
+
filterParams[key.trim()] = value.trim();
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
const key = `filter[${filter.field}]`;
|
|
586
|
+
// Handle different operators with correct PrestaShop formats
|
|
587
|
+
switch (filter.operator) {
|
|
588
|
+
case '=':
|
|
589
|
+
if (filter.value && filter.value.trim()) {
|
|
590
|
+
const value = filter.value.trim();
|
|
591
|
+
// Check if value contains comma for interval (e.g., "10,20" → "[10,20]")
|
|
592
|
+
if (value.includes(',') && !value.startsWith('[')) {
|
|
593
|
+
filterParams[key] = `[${value}]`;
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
filterParams[key] = `[${value}]`;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
break;
|
|
600
|
+
case '!=':
|
|
601
|
+
if (filter.value && filter.value.trim()) {
|
|
602
|
+
const value = filter.value.trim();
|
|
603
|
+
// Check if value contains comma for interval (e.g., "10,20" → "![10,20]")
|
|
604
|
+
if (value.includes(',') && !value.startsWith('[')) {
|
|
605
|
+
filterParams[key] = `![${value}]`;
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
filterParams[key] = `![${value}]`;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
break;
|
|
612
|
+
case '>':
|
|
613
|
+
if (filter.value && filter.value.trim()) {
|
|
614
|
+
filterParams[key] = `>[${filter.value}]`;
|
|
615
|
+
}
|
|
616
|
+
break;
|
|
617
|
+
case '>=':
|
|
618
|
+
if (filter.value && filter.value.trim()) {
|
|
619
|
+
filterParams[key] = `>=[${filter.value}]`;
|
|
620
|
+
}
|
|
621
|
+
break;
|
|
622
|
+
case '<':
|
|
623
|
+
if (filter.value && filter.value.trim()) {
|
|
624
|
+
filterParams[key] = `<[${filter.value}]`;
|
|
625
|
+
}
|
|
626
|
+
break;
|
|
627
|
+
case '<=':
|
|
628
|
+
if (filter.value && filter.value.trim()) {
|
|
629
|
+
filterParams[key] = `<=[${filter.value}]`;
|
|
630
|
+
}
|
|
631
|
+
break;
|
|
632
|
+
case 'CONTAINS':
|
|
633
|
+
if (filter.value && filter.value.trim()) {
|
|
634
|
+
filterParams[key] = `%[${filter.value}]%`;
|
|
635
|
+
}
|
|
636
|
+
break;
|
|
637
|
+
case 'BEGINS':
|
|
638
|
+
if (filter.value && filter.value.trim()) {
|
|
639
|
+
filterParams[key] = `[${filter.value}]%`;
|
|
640
|
+
}
|
|
641
|
+
break;
|
|
642
|
+
case 'ENDS':
|
|
643
|
+
if (filter.value && filter.value.trim()) {
|
|
644
|
+
filterParams[key] = `%[${filter.value}]`;
|
|
645
|
+
}
|
|
646
|
+
break;
|
|
647
|
+
case 'IS_EMPTY':
|
|
648
|
+
filterParams[key] = `[]`;
|
|
649
|
+
break;
|
|
650
|
+
case 'IS_NOT_EMPTY':
|
|
651
|
+
filterParams[key] = `![]`;
|
|
652
|
+
break;
|
|
653
|
+
default:
|
|
654
|
+
if (filter.value && filter.value.trim()) {
|
|
655
|
+
filterParams[key] = `[${filter.value}]`;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
// Handle display parameter
|
|
660
|
+
const displayValue = (0, utils_1.processDisplayParameter)(display, resource, customFields);
|
|
661
|
+
const urlParams = {
|
|
662
|
+
...filterParams,
|
|
663
|
+
limit: advancedOptions.limit,
|
|
664
|
+
sort: advancedOptions.sort,
|
|
665
|
+
};
|
|
666
|
+
// Add date=1 parameter if Date Format option is enabled
|
|
667
|
+
if (advancedOptions.dateFormat) {
|
|
668
|
+
urlParams.date = 1;
|
|
669
|
+
}
|
|
670
|
+
// Only add display parameter if not null (minimal mode returns null)
|
|
671
|
+
if (displayValue !== null) {
|
|
672
|
+
urlParams.display = displayValue;
|
|
673
|
+
}
|
|
674
|
+
if (showRequestInfo) {
|
|
675
|
+
console.log('DEBUG - Final filterParams:', JSON.stringify(filterParams, null, 2));
|
|
676
|
+
console.log('DEBUG - Final urlParams:', JSON.stringify(urlParams, null, 2));
|
|
677
|
+
}
|
|
678
|
+
requestUrl = (0, utils_1.buildUrlWithFilters)(`${credentials.baseUrl}/${resource}`, urlParams, rawMode);
|
|
679
|
+
if (showRequestInfo) {
|
|
680
|
+
console.log('DEBUG - Final URL:', requestUrl);
|
|
681
|
+
}
|
|
682
|
+
const timeout = this.getNodeParameter('options.timeout', i, 30000);
|
|
683
|
+
const neverError = this.getNodeParameter('options.response.neverError', i, false);
|
|
684
|
+
const includeResponseHeaders = this.getNodeParameter('options.response.includeResponseHeaders', i, false);
|
|
685
|
+
if (rawMode) {
|
|
686
|
+
// In Raw mode, use axios directly to avoid n8n automatic parsing
|
|
687
|
+
const axios = require('axios');
|
|
688
|
+
try {
|
|
689
|
+
const axiosResponse = await axios({
|
|
690
|
+
method: 'GET',
|
|
691
|
+
url: requestUrl,
|
|
692
|
+
auth: {
|
|
693
|
+
username: credentials.apiKey,
|
|
694
|
+
password: ''
|
|
695
|
+
},
|
|
696
|
+
headers: buildHeaders(rawMode),
|
|
697
|
+
timeout: timeout || 30000,
|
|
698
|
+
transformResponse: [(data) => data],
|
|
699
|
+
validateStatus: neverError ? () => true : undefined // Accept all status codes if neverError is true
|
|
700
|
+
});
|
|
701
|
+
requestDebugInfo = captureRequestDebugInfo({
|
|
702
|
+
method: 'GET',
|
|
703
|
+
url: requestUrl,
|
|
704
|
+
headers: buildHeaders(rawMode),
|
|
705
|
+
timeout: timeout
|
|
706
|
+
}, credentials, rawMode, operation, resource);
|
|
707
|
+
requestHeaders = requestDebugInfo.headers;
|
|
708
|
+
// Check if this is an error response with Never Error mode
|
|
709
|
+
if (neverError && (axiosResponse.status < 200 || axiosResponse.status >= 300)) {
|
|
710
|
+
responseData = {
|
|
711
|
+
status: axiosResponse.status,
|
|
712
|
+
message: axiosResponse.data || ''
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
else {
|
|
716
|
+
let processedResponse = { raw: axiosResponse.data };
|
|
717
|
+
// Add response headers and status if requested
|
|
718
|
+
if (includeResponseHeaders) {
|
|
719
|
+
responseData = {
|
|
720
|
+
body: processedResponse,
|
|
721
|
+
headers: axiosResponse.headers,
|
|
722
|
+
statusCode: axiosResponse.status,
|
|
723
|
+
statusMessage: axiosResponse.status >= 200 && axiosResponse.status < 300 ? 'OK' : 'Error'
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
else {
|
|
727
|
+
responseData = processedResponse;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
catch (error) {
|
|
732
|
+
if (neverError) {
|
|
733
|
+
responseData = {
|
|
734
|
+
status: ((_c = error.response) === null || _c === void 0 ? void 0 : _c.status) || 500,
|
|
735
|
+
message: ((_d = error.response) === null || _d === void 0 ? void 0 : _d.data) || ''
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
else {
|
|
739
|
+
throw error;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
const options = buildHttpOptions('GET', requestUrl, credentials, rawMode, timeout);
|
|
745
|
+
const { response, debugInfo, url, responseHeaders, statusCode } = await executeHttpRequest(this.helpers, options, credentials, rawMode, operation, resource, neverError);
|
|
746
|
+
requestUrl = url;
|
|
747
|
+
requestDebugInfo = debugInfo;
|
|
748
|
+
requestHeaders = debugInfo.headers;
|
|
749
|
+
let processedResponse = (0, utils_1.processResponseForMode)(response, resource, currentMode);
|
|
750
|
+
// Add response headers and status if requested
|
|
751
|
+
if (includeResponseHeaders) {
|
|
752
|
+
responseData = {
|
|
753
|
+
body: processedResponse,
|
|
754
|
+
headers: responseHeaders,
|
|
755
|
+
statusCode: statusCode,
|
|
756
|
+
statusMessage: statusCode && statusCode >= 200 && statusCode < 300 ? 'OK' : 'Error'
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
responseData = processedResponse;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
460
763
|
break;
|
|
461
764
|
}
|
|
462
765
|
case 'delete': {
|
|
@@ -464,52 +767,60 @@ class PrestaShop8 {
|
|
|
464
767
|
if (!id) {
|
|
465
768
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'ID required for this operation');
|
|
466
769
|
}
|
|
467
|
-
const timeout = this.getNodeParameter('
|
|
770
|
+
const timeout = this.getNodeParameter('options.timeout', i, 30000);
|
|
771
|
+
const neverError = this.getNodeParameter('options.response.neverError', i, false);
|
|
772
|
+
const includeResponseHeaders = this.getNodeParameter('options.response.includeResponseHeaders', i, false);
|
|
468
773
|
const options = buildHttpOptions('DELETE', `${credentials.baseUrl}/${resource}/${id}`, credentials, rawMode, timeout);
|
|
469
|
-
const { debugInfo, url } = await executeHttpRequest(this.helpers, options, credentials, rawMode, operation, resource);
|
|
774
|
+
const { debugInfo, url, responseHeaders, statusCode } = await executeHttpRequest(this.helpers, options, credentials, rawMode, operation, resource, neverError);
|
|
470
775
|
requestUrl = url;
|
|
471
776
|
requestDebugInfo = debugInfo;
|
|
472
777
|
requestHeaders = debugInfo.headers;
|
|
473
|
-
|
|
778
|
+
let deleteResponse = {
|
|
474
779
|
success: true,
|
|
475
780
|
message: `${resource} with ID ${id} deleted successfully`,
|
|
476
781
|
deletedId: id,
|
|
477
782
|
};
|
|
783
|
+
// Add response headers and status if requested
|
|
784
|
+
if (includeResponseHeaders) {
|
|
785
|
+
responseData = {
|
|
786
|
+
body: deleteResponse,
|
|
787
|
+
headers: responseHeaders,
|
|
788
|
+
statusCode: statusCode,
|
|
789
|
+
statusMessage: statusCode && statusCode >= 200 && statusCode < 300 ? 'OK' : 'Error'
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
responseData = deleteResponse;
|
|
794
|
+
}
|
|
478
795
|
break;
|
|
479
796
|
}
|
|
480
797
|
default:
|
|
481
798
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Operation "${operation}" not supported`);
|
|
482
799
|
}
|
|
483
800
|
// Add debug metadata if requested
|
|
484
|
-
const
|
|
801
|
+
const showRequestUrl = this.getNodeParameter('options.request.showRequestUrl', i, false);
|
|
802
|
+
const showRequestInfo = this.getNodeParameter('options.request.showRequestInfo', i, false);
|
|
485
803
|
// Capture request information for debug purposes if needed
|
|
486
|
-
if (
|
|
804
|
+
if (showRequestInfo && Object.keys(requestDebugInfo).length === 0) {
|
|
487
805
|
// Fallback if no request debug info was captured
|
|
488
806
|
requestDebugInfo = {
|
|
489
807
|
method: 'GET',
|
|
490
808
|
url: requestUrl,
|
|
491
|
-
headers: {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
},
|
|
496
|
-
authentication: {
|
|
497
|
-
type: 'Basic Auth',
|
|
498
|
-
username: credentials.apiKey,
|
|
499
|
-
password: '[HIDDEN]',
|
|
500
|
-
baseUrl: credentials.baseUrl,
|
|
501
|
-
},
|
|
502
|
-
operation: operation,
|
|
503
|
-
resource: resource,
|
|
504
|
-
mode: rawMode ? 'Raw XML' : 'JSON',
|
|
809
|
+
headers: {},
|
|
810
|
+
auth: 'Basic (hidden)',
|
|
811
|
+
timeout: 30000,
|
|
812
|
+
timestamp: new Date().toISOString(),
|
|
505
813
|
};
|
|
814
|
+
}
|
|
815
|
+
// Add headers to output if requested
|
|
816
|
+
if (showRequestInfo) {
|
|
506
817
|
requestHeaders = requestDebugInfo.headers;
|
|
507
818
|
}
|
|
508
|
-
if (
|
|
819
|
+
if (showRequestUrl || showRequestInfo) {
|
|
509
820
|
responseData = {
|
|
510
821
|
data: responseData,
|
|
511
|
-
...(
|
|
512
|
-
...(
|
|
822
|
+
...(showRequestUrl && { requestUrl }),
|
|
823
|
+
...(showRequestInfo && { requestInfo: requestDebugInfo }),
|
|
513
824
|
};
|
|
514
825
|
}
|
|
515
826
|
processResponseData(responseData, returnData, i);
|
|
@@ -360,8 +360,10 @@ exports.FILTER_OPERATORS = [
|
|
|
360
360
|
{ name: '≥ Greater than or equal to', value: '>=' },
|
|
361
361
|
{ name: '< Less than', value: '<' },
|
|
362
362
|
{ name: '≤ Less than or equal to', value: '<=' },
|
|
363
|
-
{ name: '∋ Contains', value: '
|
|
364
|
-
{ name: '∌ Does not contain', value: 'NOT LIKE' },
|
|
363
|
+
{ name: '∋ Contains', value: 'CONTAINS' },
|
|
365
364
|
{ name: '↦ Starts with', value: 'BEGINS' },
|
|
366
365
|
{ name: '↤ Ends with', value: 'ENDS' },
|
|
366
|
+
{ name: '∅ Is Empty', value: 'IS_EMPTY' },
|
|
367
|
+
{ name: '∄ Is not Empty', value: 'IS_NOT_EMPTY' },
|
|
368
|
+
{ name: '⚙️ Custom Filter', value: 'CUSTOM' },
|
|
367
369
|
];
|
|
@@ -429,30 +429,21 @@ exports.parseXmlToJson = parseXmlToJson;
|
|
|
429
429
|
function buildUrlWithFilters(baseUrl, options, rawMode) {
|
|
430
430
|
const url = new URL(baseUrl);
|
|
431
431
|
const params = new URLSearchParams();
|
|
432
|
-
//
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
if (
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
432
|
+
// Add all parameters from options object
|
|
433
|
+
for (const [key, value] of Object.entries(options)) {
|
|
434
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
435
|
+
if (key === 'sort') {
|
|
436
|
+
// Special handling for sort parameter
|
|
437
|
+
const normalizedSort = processSortParameter(String(value));
|
|
438
|
+
if (normalizedSort)
|
|
439
|
+
params.append('sort', normalizedSort);
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
// Add all other parameters as-is (including custom user parameters)
|
|
443
|
+
params.append(key, String(value));
|
|
443
444
|
}
|
|
444
445
|
}
|
|
445
446
|
}
|
|
446
|
-
// Add other parameters
|
|
447
|
-
if (options.limit)
|
|
448
|
-
params.append('limit', options.limit);
|
|
449
|
-
if (options.sort) {
|
|
450
|
-
const normalizedSort = processSortParameter(options.sort);
|
|
451
|
-
if (normalizedSort)
|
|
452
|
-
params.append('sort', normalizedSort);
|
|
453
|
-
}
|
|
454
|
-
if (options.display !== null && options.display !== undefined)
|
|
455
|
-
params.append('display', options.display);
|
|
456
447
|
// Ajouter output_format seulement si pas en mode Raw
|
|
457
448
|
if (!rawMode) {
|
|
458
449
|
params.append('output_format', 'JSON');
|
package/package.json
CHANGED