n8n-nodes-better-http-request 0.1.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/README.md +79 -0
- package/dist/nodes/BetterHttpRequest/BetterHttpRequest.node.d.ts +5 -0
- package/dist/nodes/BetterHttpRequest/BetterHttpRequest.node.js +1032 -0
- package/dist/nodes/BetterHttpRequest/BetterHttpRequest.node.js.map +1 -0
- package/dist/nodes/BetterHttpRequest/BetterHttpRequest.node.json +11 -0
- package/dist/nodes/BetterHttpRequest/Description.d.ts +2 -0
- package/dist/nodes/BetterHttpRequest/Description.js +743 -0
- package/dist/nodes/BetterHttpRequest/Description.js.map +1 -0
- package/dist/nodes/BetterHttpRequest/helpers.d.ts +38 -0
- package/dist/nodes/BetterHttpRequest/helpers.js +257 -0
- package/dist/nodes/BetterHttpRequest/helpers.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,1032 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BetterHttpRequest = void 0;
|
|
7
|
+
const set_1 = __importDefault(require("lodash/set"));
|
|
8
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
9
|
+
const Description_1 = require("./Description");
|
|
10
|
+
const helpers_1 = require("./helpers");
|
|
11
|
+
function toText(data) {
|
|
12
|
+
if (typeof data === 'object' && data !== null) {
|
|
13
|
+
return JSON.stringify(data);
|
|
14
|
+
}
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
function parseJsonParameter(node, jsonString, fieldName, itemIndex) {
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(jsonString);
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
const error = (0, n8n_workflow_1.ensureError)(e);
|
|
23
|
+
throw new n8n_workflow_1.NodeOperationError(node, `The value in the "${fieldName}" field is not valid JSON`, {
|
|
24
|
+
itemIndex,
|
|
25
|
+
description: error.message,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function isDomainAllowedLocal(url, opts) {
|
|
30
|
+
try {
|
|
31
|
+
const parsedUrl = new URL(url);
|
|
32
|
+
const hostname = parsedUrl.hostname.toLowerCase();
|
|
33
|
+
const allowedDomains = opts.allowedDomains
|
|
34
|
+
.split(',')
|
|
35
|
+
.map((d) => d.trim().toLowerCase())
|
|
36
|
+
.filter((d) => d.length > 0);
|
|
37
|
+
return allowedDomains.some((domain) => hostname === domain || hostname.endsWith('.' + domain));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
class BetterHttpRequest {
|
|
44
|
+
constructor() {
|
|
45
|
+
this.description = {
|
|
46
|
+
displayName: 'Better HTTP Request',
|
|
47
|
+
name: 'betterHttpRequest',
|
|
48
|
+
icon: 'file:betterhttp.svg',
|
|
49
|
+
group: ['output'],
|
|
50
|
+
subtitle: '={{$parameter["method"] + ": " + $parameter["url"]}}',
|
|
51
|
+
version: 1,
|
|
52
|
+
defaults: {
|
|
53
|
+
name: 'Better HTTP Request',
|
|
54
|
+
color: '#0004F5',
|
|
55
|
+
},
|
|
56
|
+
inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
57
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
58
|
+
credentials: [
|
|
59
|
+
{
|
|
60
|
+
name: 'httpSslAuth',
|
|
61
|
+
required: true,
|
|
62
|
+
displayOptions: {
|
|
63
|
+
show: {
|
|
64
|
+
provideSslCertificates: [true],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
description: 'Enhanced HTTP Request with retry-only-failed-items support',
|
|
70
|
+
properties: Description_1.mainProperties,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async execute() {
|
|
74
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3;
|
|
75
|
+
const items = this.getInputData();
|
|
76
|
+
const nodeVersion = this.getNode().typeVersion;
|
|
77
|
+
const fullResponseProperties = ['body', 'headers', 'statusCode', 'statusMessage'];
|
|
78
|
+
let authentication;
|
|
79
|
+
try {
|
|
80
|
+
authentication = this.getNodeParameter('authentication', 0);
|
|
81
|
+
}
|
|
82
|
+
catch { }
|
|
83
|
+
let httpBasicAuth;
|
|
84
|
+
let httpBearerAuth;
|
|
85
|
+
let httpDigestAuth;
|
|
86
|
+
let httpHeaderAuth;
|
|
87
|
+
let httpQueryAuth;
|
|
88
|
+
let httpCustomAuth;
|
|
89
|
+
let oAuth1Api;
|
|
90
|
+
let oAuth2Api;
|
|
91
|
+
let sslCertificates;
|
|
92
|
+
let nodeCredentialType;
|
|
93
|
+
let genericCredentialType;
|
|
94
|
+
let requestOptions = {
|
|
95
|
+
uri: '',
|
|
96
|
+
};
|
|
97
|
+
let returnItems = [];
|
|
98
|
+
const errorItems = {};
|
|
99
|
+
const requestPromises = [];
|
|
100
|
+
let fullResponse = false;
|
|
101
|
+
let autoDetectResponseFormat = false;
|
|
102
|
+
let responseFileName;
|
|
103
|
+
const pagination = this.getNodeParameter('options.pagination.pagination', 0, null, {
|
|
104
|
+
rawExpressions: true,
|
|
105
|
+
});
|
|
106
|
+
const requests = [];
|
|
107
|
+
const updadeQueryParameter = (0, helpers_1.updadeQueryParameterConfig)(nodeVersion);
|
|
108
|
+
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
109
|
+
try {
|
|
110
|
+
if (authentication === 'genericCredentialType') {
|
|
111
|
+
genericCredentialType = this.getNodeParameter('genericAuthType', 0);
|
|
112
|
+
if (genericCredentialType === 'httpBasicAuth') {
|
|
113
|
+
httpBasicAuth = await this.getCredentials('httpBasicAuth', itemIndex);
|
|
114
|
+
}
|
|
115
|
+
else if (genericCredentialType === 'httpBearerAuth') {
|
|
116
|
+
httpBearerAuth = await this.getCredentials('httpBearerAuth', itemIndex);
|
|
117
|
+
}
|
|
118
|
+
else if (genericCredentialType === 'httpDigestAuth') {
|
|
119
|
+
httpDigestAuth = await this.getCredentials('httpDigestAuth', itemIndex);
|
|
120
|
+
}
|
|
121
|
+
else if (genericCredentialType === 'httpHeaderAuth') {
|
|
122
|
+
httpHeaderAuth = await this.getCredentials('httpHeaderAuth', itemIndex);
|
|
123
|
+
}
|
|
124
|
+
else if (genericCredentialType === 'httpQueryAuth') {
|
|
125
|
+
httpQueryAuth = await this.getCredentials('httpQueryAuth', itemIndex);
|
|
126
|
+
}
|
|
127
|
+
else if (genericCredentialType === 'httpCustomAuth') {
|
|
128
|
+
httpCustomAuth = await this.getCredentials('httpCustomAuth', itemIndex);
|
|
129
|
+
}
|
|
130
|
+
else if (genericCredentialType === 'oAuth1Api') {
|
|
131
|
+
oAuth1Api = await this.getCredentials('oAuth1Api', itemIndex);
|
|
132
|
+
}
|
|
133
|
+
else if (genericCredentialType === 'oAuth2Api') {
|
|
134
|
+
oAuth2Api = await this.getCredentials('oAuth2Api', itemIndex);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else if (authentication === 'predefinedCredentialType') {
|
|
138
|
+
nodeCredentialType = this.getNodeParameter('nodeCredentialType', itemIndex);
|
|
139
|
+
}
|
|
140
|
+
const url = this.getNodeParameter('url', itemIndex);
|
|
141
|
+
if (typeof url !== 'string') {
|
|
142
|
+
const actualType = url === null ? 'null' : typeof url;
|
|
143
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `URL parameter must be a string, got ${actualType}`);
|
|
144
|
+
}
|
|
145
|
+
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|
146
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid URL: ${url}. URL must start with "http" or "https".`);
|
|
147
|
+
}
|
|
148
|
+
const checkDomainRestrictions = async (credentialData, requestUrl, credentialType) => {
|
|
149
|
+
if (credentialData.allowedHttpRequestDomains === 'domains') {
|
|
150
|
+
const allowedDomains = credentialData.allowedDomains;
|
|
151
|
+
if (!allowedDomains || allowedDomains.trim() === '') {
|
|
152
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No allowed domains specified. Configure allowed domains or change restriction setting.');
|
|
153
|
+
}
|
|
154
|
+
if (!isDomainAllowedLocal(requestUrl, { allowedDomains })) {
|
|
155
|
+
const credentialInfo = credentialType ? ` (${credentialType})` : '';
|
|
156
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Domain not allowed: This credential${credentialInfo} is restricted from accessing ${requestUrl}. ` +
|
|
157
|
+
`Only the following domains are allowed: ${allowedDomains}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else if (credentialData.allowedHttpRequestDomains === 'none') {
|
|
161
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'This credential is configured to prevent use within an HTTP Request node');
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
if (httpBasicAuth)
|
|
165
|
+
await checkDomainRestrictions(httpBasicAuth, url);
|
|
166
|
+
if (httpBearerAuth)
|
|
167
|
+
await checkDomainRestrictions(httpBearerAuth, url);
|
|
168
|
+
if (httpDigestAuth)
|
|
169
|
+
await checkDomainRestrictions(httpDigestAuth, url);
|
|
170
|
+
if (httpHeaderAuth)
|
|
171
|
+
await checkDomainRestrictions(httpHeaderAuth, url);
|
|
172
|
+
if (httpQueryAuth)
|
|
173
|
+
await checkDomainRestrictions(httpQueryAuth, url);
|
|
174
|
+
if (httpCustomAuth)
|
|
175
|
+
await checkDomainRestrictions(httpCustomAuth, url);
|
|
176
|
+
if (oAuth1Api)
|
|
177
|
+
await checkDomainRestrictions(oAuth1Api, url);
|
|
178
|
+
if (oAuth2Api)
|
|
179
|
+
await checkDomainRestrictions(oAuth2Api, url);
|
|
180
|
+
if (nodeCredentialType) {
|
|
181
|
+
try {
|
|
182
|
+
const credentialData = await this.getCredentials(nodeCredentialType, itemIndex);
|
|
183
|
+
await checkDomainRestrictions(credentialData, url, nodeCredentialType);
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
if (((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('Domain not allowed')) ||
|
|
187
|
+
((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('configured to prevent')) ||
|
|
188
|
+
((_c = error.message) === null || _c === void 0 ? void 0 : _c.includes('No allowed domains specified'))) {
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const provideSslCertificates = this.getNodeParameter('provideSslCertificates', itemIndex, false);
|
|
194
|
+
if (provideSslCertificates) {
|
|
195
|
+
sslCertificates = (await this.getCredentials('httpSslAuth', itemIndex));
|
|
196
|
+
}
|
|
197
|
+
const requestMethod = this.getNodeParameter('method', itemIndex);
|
|
198
|
+
const sendQuery = this.getNodeParameter('sendQuery', itemIndex, false);
|
|
199
|
+
const queryParameters = this.getNodeParameter('queryParameters.parameters', itemIndex, []);
|
|
200
|
+
const specifyQuery = this.getNodeParameter('specifyQuery', itemIndex, 'keypair');
|
|
201
|
+
const jsonQueryParameter = this.getNodeParameter('jsonQuery', itemIndex, '');
|
|
202
|
+
const sendBody = this.getNodeParameter('sendBody', itemIndex, false);
|
|
203
|
+
const bodyContentType = this.getNodeParameter('contentType', itemIndex, '');
|
|
204
|
+
const specifyBody = this.getNodeParameter('specifyBody', itemIndex, '');
|
|
205
|
+
const bodyParameters = this.getNodeParameter('bodyParameters.parameters', itemIndex, []);
|
|
206
|
+
const jsonBodyParameter = this.getNodeParameter('jsonBody', itemIndex, '');
|
|
207
|
+
const body = this.getNodeParameter('body', itemIndex, '');
|
|
208
|
+
const sendHeaders = this.getNodeParameter('sendHeaders', itemIndex, false);
|
|
209
|
+
const headerParameters = this.getNodeParameter('headerParameters.parameters', itemIndex, []);
|
|
210
|
+
const specifyHeaders = this.getNodeParameter('specifyHeaders', itemIndex, 'keypair');
|
|
211
|
+
const jsonHeadersParameter = this.getNodeParameter('jsonHeaders', itemIndex, '');
|
|
212
|
+
const { redirect, batching, proxy, timeout, allowUnauthorizedCerts, queryParameterArrays, response, lowercaseHeaders, sendCredentialsOnCrossOriginRedirect, } = this.getNodeParameter('options', itemIndex, {});
|
|
213
|
+
responseFileName = (_d = response === null || response === void 0 ? void 0 : response.response) === null || _d === void 0 ? void 0 : _d.outputPropertyName;
|
|
214
|
+
const responseFormat = ((_e = response === null || response === void 0 ? void 0 : response.response) === null || _e === void 0 ? void 0 : _e.responseFormat) || 'autodetect';
|
|
215
|
+
fullResponse = ((_f = response === null || response === void 0 ? void 0 : response.response) === null || _f === void 0 ? void 0 : _f.fullResponse) || false;
|
|
216
|
+
autoDetectResponseFormat = responseFormat === 'autodetect';
|
|
217
|
+
const batchSize = ((_g = batching === null || batching === void 0 ? void 0 : batching.batch) === null || _g === void 0 ? void 0 : _g.batchSize) > 0 ? (_h = batching === null || batching === void 0 ? void 0 : batching.batch) === null || _h === void 0 ? void 0 : _h.batchSize : 1;
|
|
218
|
+
const batchInterval = (_j = batching === null || batching === void 0 ? void 0 : batching.batch) === null || _j === void 0 ? void 0 : _j.batchInterval;
|
|
219
|
+
if (itemIndex > 0 && batchSize >= 0 && batchInterval > 0) {
|
|
220
|
+
if (itemIndex % batchSize === 0) {
|
|
221
|
+
await (0, n8n_workflow_1.sleep)(batchInterval);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
requestOptions = {
|
|
225
|
+
headers: {},
|
|
226
|
+
method: requestMethod,
|
|
227
|
+
uri: url,
|
|
228
|
+
gzip: true,
|
|
229
|
+
rejectUnauthorized: !allowUnauthorizedCerts || false,
|
|
230
|
+
followRedirect: false,
|
|
231
|
+
resolveWithFullResponse: true,
|
|
232
|
+
sendCredentialsOnCrossOriginRedirect: sendCredentialsOnCrossOriginRedirect !== null && sendCredentialsOnCrossOriginRedirect !== void 0 ? sendCredentialsOnCrossOriginRedirect : false,
|
|
233
|
+
};
|
|
234
|
+
if (requestOptions.method !== 'GET' && nodeVersion >= 4.1) {
|
|
235
|
+
requestOptions = { ...requestOptions, followAllRedirects: false };
|
|
236
|
+
}
|
|
237
|
+
const defaultRedirect = redirect === undefined;
|
|
238
|
+
if (((_k = redirect === null || redirect === void 0 ? void 0 : redirect.redirect) === null || _k === void 0 ? void 0 : _k.followRedirects) || defaultRedirect) {
|
|
239
|
+
requestOptions.followRedirect = true;
|
|
240
|
+
requestOptions.followAllRedirects = true;
|
|
241
|
+
}
|
|
242
|
+
if (((_l = redirect === null || redirect === void 0 ? void 0 : redirect.redirect) === null || _l === void 0 ? void 0 : _l.maxRedirects) || defaultRedirect) {
|
|
243
|
+
requestOptions.maxRedirects = (_m = redirect === null || redirect === void 0 ? void 0 : redirect.redirect) === null || _m === void 0 ? void 0 : _m.maxRedirects;
|
|
244
|
+
}
|
|
245
|
+
if ((_o = response === null || response === void 0 ? void 0 : response.response) === null || _o === void 0 ? void 0 : _o.neverError) {
|
|
246
|
+
requestOptions.simple = false;
|
|
247
|
+
}
|
|
248
|
+
if (proxy) {
|
|
249
|
+
requestOptions.proxy = proxy;
|
|
250
|
+
}
|
|
251
|
+
if (timeout) {
|
|
252
|
+
requestOptions.timeout = timeout;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
requestOptions.timeout = 300000;
|
|
256
|
+
}
|
|
257
|
+
if (sendQuery && queryParameterArrays) {
|
|
258
|
+
Object.assign(requestOptions, {
|
|
259
|
+
qsStringifyOptions: { arrayFormat: queryParameterArrays },
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
const parametersToKeyValue = async (accumulator, cur) => {
|
|
263
|
+
if (cur.parameterType === 'formBinaryData') {
|
|
264
|
+
if (!cur.inputDataFieldName)
|
|
265
|
+
return accumulator;
|
|
266
|
+
const binaryData = this.helpers.assertBinaryData(itemIndex, cur.inputDataFieldName);
|
|
267
|
+
let uploadData;
|
|
268
|
+
if (binaryData.id) {
|
|
269
|
+
uploadData = await this.helpers.getBinaryStream(binaryData.id);
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
uploadData = Buffer.from(binaryData.data, n8n_workflow_1.BINARY_ENCODING);
|
|
273
|
+
}
|
|
274
|
+
accumulator[cur.name] = {
|
|
275
|
+
value: uploadData,
|
|
276
|
+
options: {
|
|
277
|
+
filename: binaryData.fileName,
|
|
278
|
+
contentType: binaryData.mimeType,
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
return accumulator;
|
|
282
|
+
}
|
|
283
|
+
updadeQueryParameter(accumulator, cur.name, cur.value);
|
|
284
|
+
return accumulator;
|
|
285
|
+
};
|
|
286
|
+
if (sendBody && bodyParameters) {
|
|
287
|
+
if (specifyBody === 'keypair' || bodyContentType === 'multipart-form-data') {
|
|
288
|
+
requestOptions.body = await (0, helpers_1.prepareRequestBody)(bodyParameters, bodyContentType, nodeVersion, parametersToKeyValue);
|
|
289
|
+
}
|
|
290
|
+
else if (specifyBody === 'json') {
|
|
291
|
+
if (typeof jsonBodyParameter !== 'object' &&
|
|
292
|
+
jsonBodyParameter !== null) {
|
|
293
|
+
requestOptions.body = parseJsonParameter(this.getNode(), jsonBodyParameter, 'JSON Body', itemIndex);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
requestOptions.body = jsonBodyParameter;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else if (specifyBody === 'string') {
|
|
300
|
+
requestOptions.body = Object.fromEntries(new URLSearchParams(body));
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (sendBody && ['PATCH', 'POST', 'PUT', 'GET'].includes(requestMethod)) {
|
|
304
|
+
if (bodyContentType === 'multipart-form-data') {
|
|
305
|
+
requestOptions.formData = requestOptions.body;
|
|
306
|
+
delete requestOptions.body;
|
|
307
|
+
}
|
|
308
|
+
else if (bodyContentType === 'form-urlencoded') {
|
|
309
|
+
requestOptions.form = requestOptions.body;
|
|
310
|
+
delete requestOptions.body;
|
|
311
|
+
}
|
|
312
|
+
else if (bodyContentType === 'binaryData') {
|
|
313
|
+
const inputDataFieldName = this.getNodeParameter('inputDataFieldName', itemIndex);
|
|
314
|
+
let uploadData;
|
|
315
|
+
let contentLength;
|
|
316
|
+
const itemBinaryData = this.helpers.assertBinaryData(itemIndex, inputDataFieldName);
|
|
317
|
+
if (itemBinaryData.id) {
|
|
318
|
+
uploadData = await this.helpers.getBinaryStream(itemBinaryData.id);
|
|
319
|
+
const metadata = await this.helpers.getBinaryMetadata(itemBinaryData.id);
|
|
320
|
+
contentLength = metadata.fileSize;
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
uploadData = Buffer.from(itemBinaryData.data, n8n_workflow_1.BINARY_ENCODING);
|
|
324
|
+
contentLength = uploadData.length;
|
|
325
|
+
}
|
|
326
|
+
requestOptions.body = uploadData;
|
|
327
|
+
requestOptions.headers = {
|
|
328
|
+
...requestOptions.headers,
|
|
329
|
+
'content-length': contentLength,
|
|
330
|
+
'content-type': (_p = itemBinaryData.mimeType) !== null && _p !== void 0 ? _p : 'application/octet-stream',
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
else if (bodyContentType === 'raw') {
|
|
334
|
+
requestOptions.body = body;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (sendQuery && queryParameters) {
|
|
338
|
+
if (specifyQuery === 'keypair') {
|
|
339
|
+
requestOptions.qs = await (0, helpers_1.reduceAsync)(queryParameters, parametersToKeyValue);
|
|
340
|
+
}
|
|
341
|
+
else if (specifyQuery === 'json') {
|
|
342
|
+
requestOptions.qs = parseJsonParameter(this.getNode(), jsonQueryParameter, 'JSON Query Parameters', itemIndex);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (sendHeaders && headerParameters) {
|
|
346
|
+
let additionalHeaders = {};
|
|
347
|
+
if (specifyHeaders === 'keypair') {
|
|
348
|
+
additionalHeaders = await (0, helpers_1.reduceAsync)(headerParameters.filter((header) => header.name), parametersToKeyValue);
|
|
349
|
+
}
|
|
350
|
+
else if (specifyHeaders === 'json') {
|
|
351
|
+
additionalHeaders = parseJsonParameter(this.getNode(), jsonHeadersParameter, 'JSON Headers', itemIndex);
|
|
352
|
+
}
|
|
353
|
+
requestOptions.headers = {
|
|
354
|
+
...requestOptions.headers,
|
|
355
|
+
...(lowercaseHeaders === undefined || lowercaseHeaders
|
|
356
|
+
? (0, helpers_1.keysToLowercase)(additionalHeaders)
|
|
357
|
+
: additionalHeaders),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
if (autoDetectResponseFormat || responseFormat === 'file') {
|
|
361
|
+
requestOptions.encoding = null;
|
|
362
|
+
requestOptions.json = false;
|
|
363
|
+
requestOptions.useStream = true;
|
|
364
|
+
}
|
|
365
|
+
else if (bodyContentType === 'raw') {
|
|
366
|
+
requestOptions.json = false;
|
|
367
|
+
requestOptions.useStream = true;
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
requestOptions.json = true;
|
|
371
|
+
}
|
|
372
|
+
if (bodyContentType === 'raw') {
|
|
373
|
+
if (requestOptions.headers === undefined) {
|
|
374
|
+
requestOptions.headers = {};
|
|
375
|
+
}
|
|
376
|
+
const rawContentType = this.getNodeParameter('rawContentType', itemIndex);
|
|
377
|
+
requestOptions.headers['content-type'] = rawContentType;
|
|
378
|
+
}
|
|
379
|
+
const authDataKeys = {};
|
|
380
|
+
(0, helpers_1.setAgentOptions)(requestOptions, sslCertificates);
|
|
381
|
+
if (requestOptions.agentOptions) {
|
|
382
|
+
authDataKeys.agentOptions = Object.keys(requestOptions.agentOptions);
|
|
383
|
+
}
|
|
384
|
+
if (httpBasicAuth !== undefined) {
|
|
385
|
+
requestOptions.auth = {
|
|
386
|
+
user: httpBasicAuth.user,
|
|
387
|
+
pass: httpBasicAuth.password,
|
|
388
|
+
};
|
|
389
|
+
authDataKeys.auth = ['pass'];
|
|
390
|
+
}
|
|
391
|
+
if (httpBearerAuth !== undefined) {
|
|
392
|
+
requestOptions.headers = (_q = requestOptions.headers) !== null && _q !== void 0 ? _q : {};
|
|
393
|
+
requestOptions.headers.Authorization = `Bearer ${String(httpBearerAuth.token)}`;
|
|
394
|
+
authDataKeys.headers = ['Authorization'];
|
|
395
|
+
}
|
|
396
|
+
if (httpHeaderAuth !== undefined) {
|
|
397
|
+
requestOptions.headers[httpHeaderAuth.name] =
|
|
398
|
+
httpHeaderAuth.value;
|
|
399
|
+
authDataKeys.headers = [httpHeaderAuth.name];
|
|
400
|
+
}
|
|
401
|
+
if (httpQueryAuth !== undefined) {
|
|
402
|
+
if (!requestOptions.qs) {
|
|
403
|
+
requestOptions.qs = {};
|
|
404
|
+
}
|
|
405
|
+
requestOptions.qs[httpQueryAuth.name] = httpQueryAuth.value;
|
|
406
|
+
authDataKeys.qs = [httpQueryAuth.name];
|
|
407
|
+
}
|
|
408
|
+
if (httpDigestAuth !== undefined) {
|
|
409
|
+
requestOptions.auth = {
|
|
410
|
+
user: httpDigestAuth.user,
|
|
411
|
+
pass: httpDigestAuth.password,
|
|
412
|
+
sendImmediately: false,
|
|
413
|
+
};
|
|
414
|
+
authDataKeys.auth = ['pass'];
|
|
415
|
+
}
|
|
416
|
+
if (httpCustomAuth !== undefined) {
|
|
417
|
+
const customAuth = (0, n8n_workflow_1.jsonParse)(httpCustomAuth.json || '{}', { errorMessage: 'Invalid Custom Auth JSON' });
|
|
418
|
+
if (customAuth.headers) {
|
|
419
|
+
requestOptions.headers = {
|
|
420
|
+
...requestOptions.headers,
|
|
421
|
+
...customAuth.headers,
|
|
422
|
+
};
|
|
423
|
+
authDataKeys.headers = Object.keys(customAuth.headers);
|
|
424
|
+
}
|
|
425
|
+
if (customAuth.body) {
|
|
426
|
+
requestOptions.body = {
|
|
427
|
+
...requestOptions.body,
|
|
428
|
+
...customAuth.body,
|
|
429
|
+
};
|
|
430
|
+
authDataKeys.body = Object.keys(customAuth.body);
|
|
431
|
+
}
|
|
432
|
+
if (customAuth.qs) {
|
|
433
|
+
requestOptions.qs = { ...requestOptions.qs, ...customAuth.qs };
|
|
434
|
+
authDataKeys.qs = Object.keys(customAuth.qs);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (requestOptions.headers.accept === undefined) {
|
|
438
|
+
if (responseFormat === 'json') {
|
|
439
|
+
requestOptions.headers.accept = 'application/json,text/*;q=0.99';
|
|
440
|
+
}
|
|
441
|
+
else if (responseFormat === 'text') {
|
|
442
|
+
requestOptions.headers.accept =
|
|
443
|
+
'application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, */*;q=0.1';
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
requestOptions.headers.accept =
|
|
447
|
+
'application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, image/*;q=0.8, */*;q=0.7';
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
requests.push({
|
|
451
|
+
options: requestOptions,
|
|
452
|
+
authKeys: authDataKeys,
|
|
453
|
+
credentialType: nodeCredentialType,
|
|
454
|
+
});
|
|
455
|
+
if (pagination && pagination.paginationMode !== 'off') {
|
|
456
|
+
let continueExpression = '={{false}}';
|
|
457
|
+
if (pagination.paginationCompleteWhen === 'receiveSpecificStatusCodes') {
|
|
458
|
+
const statusCodesWhenCompleted = pagination.statusCodesWhenComplete
|
|
459
|
+
.split(',')
|
|
460
|
+
.map((item) => parseInt(item.trim()));
|
|
461
|
+
continueExpression = `={{ !${JSON.stringify(statusCodesWhenCompleted)}.includes($response.statusCode) }}`;
|
|
462
|
+
}
|
|
463
|
+
else if (pagination.paginationCompleteWhen === 'responseIsEmpty') {
|
|
464
|
+
continueExpression =
|
|
465
|
+
'={{ Array.isArray($response.body) ? $response.body.length : !!$response.body }}';
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
if (!pagination.completeExpression.length ||
|
|
469
|
+
pagination.completeExpression[0] !== '=') {
|
|
470
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid or empty Complete Expression');
|
|
471
|
+
}
|
|
472
|
+
const completionExpression = pagination.completeExpression
|
|
473
|
+
.trim()
|
|
474
|
+
.slice(3, -2);
|
|
475
|
+
if ((_r = response === null || response === void 0 ? void 0 : response.response) === null || _r === void 0 ? void 0 : _r.neverError) {
|
|
476
|
+
continueExpression = `={{ !(${completionExpression}) }}`;
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
continueExpression = `={{ !(${completionExpression}) || ($response.statusCode < 200 || $response.statusCode >= 300) }}`;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
const paginationData = {
|
|
483
|
+
continue: continueExpression,
|
|
484
|
+
request: {},
|
|
485
|
+
requestInterval: pagination.requestInterval,
|
|
486
|
+
};
|
|
487
|
+
if (pagination.paginationMode ===
|
|
488
|
+
'updateAParameterInEachRequest') {
|
|
489
|
+
paginationData.request = {};
|
|
490
|
+
const { parameters } = pagination.parameters;
|
|
491
|
+
if (parameters.length === 1 &&
|
|
492
|
+
parameters[0].name === '' &&
|
|
493
|
+
parameters[0].value === '') {
|
|
494
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), "At least one entry with 'Name' and 'Value' filled must be included in 'Parameters' to use 'Update a Parameter in Each Request' mode ");
|
|
495
|
+
}
|
|
496
|
+
pagination.parameters.parameters.forEach((parameter, index) => {
|
|
497
|
+
if (!paginationData.request[parameter.type]) {
|
|
498
|
+
paginationData.request[parameter.type] = {};
|
|
499
|
+
}
|
|
500
|
+
const parameterName = parameter.name;
|
|
501
|
+
if (parameterName === '') {
|
|
502
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Parameter name must be set for parameter [${index + 1}] in pagination settings`);
|
|
503
|
+
}
|
|
504
|
+
const parameterValue = parameter.value;
|
|
505
|
+
if (parameterValue === '') {
|
|
506
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Some value must be provided for parameter [${index + 1}] in pagination settings, omitting it will result in an infinite loop`);
|
|
507
|
+
}
|
|
508
|
+
paginationData.request[parameter.type][parameterName] = parameterValue;
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
else if (pagination.paginationMode === 'responseContainsNextURL') {
|
|
512
|
+
paginationData.request.url = pagination.nextURL;
|
|
513
|
+
}
|
|
514
|
+
if (pagination.limitPagesFetched) {
|
|
515
|
+
paginationData.maxRequests = pagination.maxRequests;
|
|
516
|
+
}
|
|
517
|
+
if (responseFormat === 'file') {
|
|
518
|
+
paginationData.binaryResult = true;
|
|
519
|
+
}
|
|
520
|
+
const requestPromise = this.helpers.requestWithAuthenticationPaginated
|
|
521
|
+
.call(this, requestOptions, itemIndex, paginationData, nodeCredentialType !== null && nodeCredentialType !== void 0 ? nodeCredentialType : genericCredentialType)
|
|
522
|
+
.catch((error) => {
|
|
523
|
+
if (error instanceof n8n_workflow_1.NodeOperationError &&
|
|
524
|
+
error.type === 'invalid_url') {
|
|
525
|
+
const urlParameterName = pagination.paginationMode ===
|
|
526
|
+
'responseContainsNextURL'
|
|
527
|
+
? 'Next URL'
|
|
528
|
+
: 'URL';
|
|
529
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error.message, {
|
|
530
|
+
description: `Make sure the "${urlParameterName}" parameter evaluates to a valid URL.`,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
throw error;
|
|
534
|
+
});
|
|
535
|
+
requestPromises.push(requestPromise);
|
|
536
|
+
}
|
|
537
|
+
else if (authentication === 'genericCredentialType' ||
|
|
538
|
+
authentication === 'none') {
|
|
539
|
+
if (oAuth1Api) {
|
|
540
|
+
const requestOAuth1 = this.helpers.requestOAuth1.call(this, 'oAuth1Api', requestOptions);
|
|
541
|
+
requestOAuth1.catch(() => { });
|
|
542
|
+
requestPromises.push(requestOAuth1);
|
|
543
|
+
}
|
|
544
|
+
else if (oAuth2Api) {
|
|
545
|
+
const requestOAuth2 = this.helpers.requestOAuth2.call(this, 'oAuth2Api', requestOptions, { tokenType: 'Bearer' });
|
|
546
|
+
requestOAuth2.catch(() => { });
|
|
547
|
+
requestPromises.push(requestOAuth2);
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
const request = this.helpers.request(requestOptions);
|
|
551
|
+
request.catch(() => { });
|
|
552
|
+
requestPromises.push(request);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
else if (authentication === 'predefinedCredentialType' &&
|
|
556
|
+
nodeCredentialType) {
|
|
557
|
+
const additionalOAuth2Options = (0, helpers_1.getOAuth2AdditionalParameters)(nodeCredentialType);
|
|
558
|
+
const requestWithAuthentication = this.helpers.requestWithAuthentication.call(this, nodeCredentialType, requestOptions, additionalOAuth2Options && {
|
|
559
|
+
oauth2: additionalOAuth2Options,
|
|
560
|
+
}, itemIndex);
|
|
561
|
+
requestWithAuthentication.catch(() => { });
|
|
562
|
+
requestPromises.push(requestWithAuthentication);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
catch (error) {
|
|
566
|
+
if (!this.continueOnFail())
|
|
567
|
+
throw error;
|
|
568
|
+
requestPromises.push(Promise.reject(error).catch(() => { }));
|
|
569
|
+
errorItems[itemIndex] = error.message;
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
const sanitizedRequests = [];
|
|
574
|
+
const promisesResponses = await Promise.allSettled(requestPromises.map(async (requestPromise, itemIndex) => await requestPromise
|
|
575
|
+
.then((response) => response)
|
|
576
|
+
.finally(async () => {
|
|
577
|
+
if (errorItems[itemIndex])
|
|
578
|
+
return;
|
|
579
|
+
try {
|
|
580
|
+
const { options, authKeys, credentialType } = requests[itemIndex];
|
|
581
|
+
let secrets = [];
|
|
582
|
+
if (credentialType) {
|
|
583
|
+
const properties = this.getCredentialsProperties(credentialType);
|
|
584
|
+
const credentials = await this.getCredentials(credentialType, itemIndex);
|
|
585
|
+
secrets = (0, helpers_1.getSecrets)(properties, credentials);
|
|
586
|
+
}
|
|
587
|
+
const sanitizedRequestOptions = (0, helpers_1.sanitizeUiMessage)(options, authKeys, secrets);
|
|
588
|
+
sanitizedRequests.push(sanitizedRequestOptions);
|
|
589
|
+
this.sendMessageToUI(sanitizedRequestOptions);
|
|
590
|
+
}
|
|
591
|
+
catch { }
|
|
592
|
+
})));
|
|
593
|
+
let responseData;
|
|
594
|
+
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
595
|
+
try {
|
|
596
|
+
responseData = promisesResponses.shift();
|
|
597
|
+
if (errorItems[itemIndex]) {
|
|
598
|
+
returnItems.push({
|
|
599
|
+
json: { error: errorItems[itemIndex] },
|
|
600
|
+
pairedItem: { item: itemIndex },
|
|
601
|
+
});
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
if (responseData.status !== 'fulfilled') {
|
|
605
|
+
if (responseData.reason.statusCode === 429) {
|
|
606
|
+
responseData.reason.message =
|
|
607
|
+
"Try spacing your requests out using the batching settings under 'Options'";
|
|
608
|
+
}
|
|
609
|
+
if (!this.continueOnFail()) {
|
|
610
|
+
if (autoDetectResponseFormat &&
|
|
611
|
+
responseData.reason.error instanceof Buffer) {
|
|
612
|
+
responseData.reason.error = Buffer.from(responseData.reason.error).toString();
|
|
613
|
+
}
|
|
614
|
+
let error;
|
|
615
|
+
if ((responseData === null || responseData === void 0 ? void 0 : responseData.reason) instanceof n8n_workflow_1.NodeApiError) {
|
|
616
|
+
error = responseData.reason;
|
|
617
|
+
(0, set_1.default)(error, 'context.itemIndex', itemIndex);
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
const errorData = (responseData.reason
|
|
621
|
+
? responseData.reason
|
|
622
|
+
: responseData);
|
|
623
|
+
error = new n8n_workflow_1.NodeApiError(this.getNode(), errorData, {
|
|
624
|
+
itemIndex,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
(0, set_1.default)(error, 'context.request', sanitizedRequests[itemIndex]);
|
|
628
|
+
throw error;
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
const reason = responseData.reason;
|
|
632
|
+
let safeError;
|
|
633
|
+
if (reason instanceof Error) {
|
|
634
|
+
safeError = {
|
|
635
|
+
message: reason.message,
|
|
636
|
+
name: reason.name,
|
|
637
|
+
...(reason.statusCode !== undefined ? { statusCode: reason.statusCode } : {}),
|
|
638
|
+
...(reason.httpCode !== undefined ? { httpCode: reason.httpCode } : {}),
|
|
639
|
+
...(reason.code !== undefined ? { code: reason.code } : {}),
|
|
640
|
+
...(reason.description !== undefined ? { description: reason.description } : {}),
|
|
641
|
+
...(reason.headers ? { headers: reason.headers } : {}),
|
|
642
|
+
...(((_s = reason.response) === null || _s === void 0 ? void 0 : _s.headers) ? { response: { headers: reason.response.headers } } : {}),
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
else if (typeof reason === 'object' && reason !== null) {
|
|
646
|
+
try {
|
|
647
|
+
(0, n8n_workflow_1.removeCircularRefs)(reason);
|
|
648
|
+
safeError = reason;
|
|
649
|
+
}
|
|
650
|
+
catch {
|
|
651
|
+
safeError = { message: String(reason) };
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
safeError = { message: String(reason) };
|
|
656
|
+
}
|
|
657
|
+
returnItems.push({
|
|
658
|
+
json: { error: safeError },
|
|
659
|
+
pairedItem: { item: itemIndex },
|
|
660
|
+
});
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
let responses;
|
|
665
|
+
if (Array.isArray(responseData.value)) {
|
|
666
|
+
responses = responseData.value;
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
responses = [responseData.value];
|
|
670
|
+
}
|
|
671
|
+
let responseFormat = this.getNodeParameter('options.response.response.responseFormat', 0, 'autodetect');
|
|
672
|
+
fullResponse = this.getNodeParameter('options.response.response.fullResponse', 0, false);
|
|
673
|
+
for (let [index, response] of Object.entries(responses)) {
|
|
674
|
+
if (((_t = response === null || response === void 0 ? void 0 : response.request) === null || _t === void 0 ? void 0 : _t.constructor.name) === 'ClientRequest')
|
|
675
|
+
delete response.request;
|
|
676
|
+
if (this.getMode() === 'manual' && index === '0') {
|
|
677
|
+
const nodeContext = this.getContext('node');
|
|
678
|
+
if (pagination && pagination.paginationMode !== 'off') {
|
|
679
|
+
nodeContext.response = responseData.value[0];
|
|
680
|
+
}
|
|
681
|
+
else {
|
|
682
|
+
nodeContext.response = responseData.value;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
const responseContentType = (_v = (_u = response.headers) === null || _u === void 0 ? void 0 : _u['content-type']) !== null && _v !== void 0 ? _v : '';
|
|
686
|
+
if (autoDetectResponseFormat) {
|
|
687
|
+
if (responseContentType.includes('application/json')) {
|
|
688
|
+
responseFormat = 'json';
|
|
689
|
+
if (!response.__bodyResolved) {
|
|
690
|
+
const neverError = this.getNodeParameter('options.response.response.neverError', 0, false);
|
|
691
|
+
const data = await (0, helpers_1.binaryToStringWithEncodingDetection)(response.body, responseContentType, this.helpers);
|
|
692
|
+
response.body = (0, n8n_workflow_1.jsonParse)(data, {
|
|
693
|
+
...(neverError
|
|
694
|
+
? { fallbackValue: {} }
|
|
695
|
+
: {
|
|
696
|
+
errorMessage: 'Invalid JSON in response body',
|
|
697
|
+
}),
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
else if (helpers_1.binaryContentTypes.some((e) => responseContentType.includes(e))) {
|
|
702
|
+
responseFormat = 'file';
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
responseFormat = 'text';
|
|
706
|
+
if (!response.__bodyResolved) {
|
|
707
|
+
const data = await (0, helpers_1.binaryToStringWithEncodingDetection)(response.body, responseContentType, this.helpers);
|
|
708
|
+
response.body = !data ? undefined : data;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
const optimizeResponse = (0, helpers_1.configureResponseOptimizer)(this, itemIndex);
|
|
713
|
+
if (autoDetectResponseFormat && !fullResponse) {
|
|
714
|
+
delete response.headers;
|
|
715
|
+
delete response.statusCode;
|
|
716
|
+
delete response.statusMessage;
|
|
717
|
+
}
|
|
718
|
+
if (!fullResponse) {
|
|
719
|
+
response = optimizeResponse(response.body);
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
response.body = optimizeResponse(response.body);
|
|
723
|
+
}
|
|
724
|
+
if (responseFormat === 'file') {
|
|
725
|
+
const outputPropertyName = this.getNodeParameter('options.response.response.outputPropertyName', 0, 'data');
|
|
726
|
+
const newItem = {
|
|
727
|
+
json: {},
|
|
728
|
+
binary: {},
|
|
729
|
+
pairedItem: { item: itemIndex },
|
|
730
|
+
};
|
|
731
|
+
if (items[itemIndex].binary !== undefined) {
|
|
732
|
+
Object.assign(newItem.binary, items[itemIndex].binary);
|
|
733
|
+
}
|
|
734
|
+
let binaryData;
|
|
735
|
+
if (fullResponse) {
|
|
736
|
+
const returnItem = {};
|
|
737
|
+
for (const property of fullResponseProperties) {
|
|
738
|
+
if (property === 'body')
|
|
739
|
+
continue;
|
|
740
|
+
returnItem[property] = response[property];
|
|
741
|
+
}
|
|
742
|
+
newItem.json = returnItem;
|
|
743
|
+
binaryData = response === null || response === void 0 ? void 0 : response.body;
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
newItem.json = items[itemIndex].json;
|
|
747
|
+
binaryData = response;
|
|
748
|
+
}
|
|
749
|
+
const preparedBinaryData = await this.helpers.prepareBinaryData(binaryData, undefined, (0, helpers_1.mimeTypeFromResponse)(responseContentType));
|
|
750
|
+
preparedBinaryData.fileName = (0, helpers_1.setFilename)(preparedBinaryData, requestOptions, responseFileName);
|
|
751
|
+
newItem.binary[outputPropertyName] =
|
|
752
|
+
preparedBinaryData;
|
|
753
|
+
returnItems.push(newItem);
|
|
754
|
+
}
|
|
755
|
+
else if (responseFormat === 'text') {
|
|
756
|
+
const outputPropertyName = this.getNodeParameter('options.response.response.outputPropertyName', 0, 'data');
|
|
757
|
+
if (fullResponse) {
|
|
758
|
+
const returnItem = {};
|
|
759
|
+
for (const property of fullResponseProperties) {
|
|
760
|
+
if (property === 'body') {
|
|
761
|
+
returnItem[outputPropertyName] = toText(response[property]);
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
returnItem[property] = response[property];
|
|
765
|
+
}
|
|
766
|
+
returnItems.push({
|
|
767
|
+
json: returnItem,
|
|
768
|
+
pairedItem: { item: itemIndex },
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
returnItems.push({
|
|
773
|
+
json: {
|
|
774
|
+
[outputPropertyName]: toText(response),
|
|
775
|
+
},
|
|
776
|
+
pairedItem: { item: itemIndex },
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
if (fullResponse) {
|
|
782
|
+
const returnItem = {};
|
|
783
|
+
for (const property of fullResponseProperties) {
|
|
784
|
+
returnItem[property] = response[property];
|
|
785
|
+
}
|
|
786
|
+
if (responseFormat === 'json' &&
|
|
787
|
+
typeof returnItem.body === 'string') {
|
|
788
|
+
try {
|
|
789
|
+
returnItem.body = JSON.parse(returnItem.body);
|
|
790
|
+
}
|
|
791
|
+
catch {
|
|
792
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Response body is not valid JSON. Change "Response Format" to "Text"', { itemIndex });
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
returnItems.push({
|
|
796
|
+
json: returnItem,
|
|
797
|
+
pairedItem: { item: itemIndex },
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
else {
|
|
801
|
+
if (responseFormat === 'json' &&
|
|
802
|
+
typeof response === 'string') {
|
|
803
|
+
try {
|
|
804
|
+
if (typeof response !== 'object') {
|
|
805
|
+
response = JSON.parse(response);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
catch {
|
|
809
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Response body is not valid JSON. Change "Response Format" to "Text"', { itemIndex });
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
if (Array.isArray(response)) {
|
|
813
|
+
response.forEach((item) => returnItems.push({
|
|
814
|
+
json: item,
|
|
815
|
+
pairedItem: { item: itemIndex },
|
|
816
|
+
}));
|
|
817
|
+
}
|
|
818
|
+
else {
|
|
819
|
+
returnItems.push({
|
|
820
|
+
json: response,
|
|
821
|
+
pairedItem: { item: itemIndex },
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
catch (error) {
|
|
829
|
+
if (!this.continueOnFail())
|
|
830
|
+
throw error;
|
|
831
|
+
returnItems.push({
|
|
832
|
+
json: { error: error.message },
|
|
833
|
+
pairedItem: { item: itemIndex },
|
|
834
|
+
});
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
const retryOnFail = this.getNodeParameter('options.retryOnFail', 0, false);
|
|
839
|
+
if (retryOnFail && this.continueOnFail()) {
|
|
840
|
+
const maxRetries = this.getNodeParameter('options.maxRetries', 0, 3);
|
|
841
|
+
const retryDelay = this.getNodeParameter('options.retryDelay', 0, 1000);
|
|
842
|
+
const retryOnStatusCodesStr = this.getNodeParameter('options.retryOnStatusCodes', 0, '429,500,502,503,504');
|
|
843
|
+
const retryOnStatusCodes = new Set(retryOnStatusCodesStr
|
|
844
|
+
.split(',')
|
|
845
|
+
.map((s) => parseInt(s.trim(), 10))
|
|
846
|
+
.filter((n) => !isNaN(n)));
|
|
847
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
848
|
+
const failedIndices = [];
|
|
849
|
+
for (let i = 0; i < returnItems.length; i++) {
|
|
850
|
+
const item = returnItems[i];
|
|
851
|
+
if (item.json && item.json.error) {
|
|
852
|
+
const errObj = item.json.error;
|
|
853
|
+
let statusCode;
|
|
854
|
+
if (typeof errObj === 'object' && errObj !== null) {
|
|
855
|
+
statusCode =
|
|
856
|
+
(_x = (_w = errObj.statusCode) !== null && _w !== void 0 ? _w : errObj.httpCode) !== null && _x !== void 0 ? _x : errObj.code;
|
|
857
|
+
}
|
|
858
|
+
if (statusCode !== undefined &&
|
|
859
|
+
retryOnStatusCodes.has(statusCode)) {
|
|
860
|
+
failedIndices.push(i);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
if (failedIndices.length === 0)
|
|
865
|
+
break;
|
|
866
|
+
let effectiveDelay = retryDelay;
|
|
867
|
+
for (const idx of failedIndices) {
|
|
868
|
+
const errObj = returnItems[idx].json.error;
|
|
869
|
+
if (typeof errObj === 'object' && errObj !== null) {
|
|
870
|
+
const sc = errObj.statusCode;
|
|
871
|
+
if (sc === 429) {
|
|
872
|
+
const retryAfterHeader = (_z = (_y = errObj.headers) === null || _y === void 0 ? void 0 : _y['retry-after']) !== null && _z !== void 0 ? _z : (_1 = (_0 = errObj.response) === null || _0 === void 0 ? void 0 : _0.headers) === null || _1 === void 0 ? void 0 : _1['retry-after'];
|
|
873
|
+
if (retryAfterHeader) {
|
|
874
|
+
const retryAfterSeconds = parseInt(retryAfterHeader, 10);
|
|
875
|
+
if (!isNaN(retryAfterSeconds)) {
|
|
876
|
+
effectiveDelay = Math.max(effectiveDelay, retryAfterSeconds * 1000);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
if (effectiveDelay > 0) {
|
|
883
|
+
await (0, n8n_workflow_1.sleep)(effectiveDelay);
|
|
884
|
+
}
|
|
885
|
+
const retryPromises = [];
|
|
886
|
+
for (const idx of failedIndices) {
|
|
887
|
+
const originalItemIndex = returnItems[idx].pairedItem &&
|
|
888
|
+
typeof returnItems[idx].pairedItem === 'object' &&
|
|
889
|
+
!Array.isArray(returnItems[idx].pairedItem)
|
|
890
|
+
? returnItems[idx].pairedItem
|
|
891
|
+
.item
|
|
892
|
+
: idx;
|
|
893
|
+
if (requests[originalItemIndex]) {
|
|
894
|
+
const { options } = requests[originalItemIndex];
|
|
895
|
+
const retryRequest = this.helpers
|
|
896
|
+
.request(options)
|
|
897
|
+
.catch(() => { });
|
|
898
|
+
retryPromises.push({
|
|
899
|
+
index: idx,
|
|
900
|
+
promise: retryRequest,
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
const retryResults = await Promise.allSettled(retryPromises.map((r) => r.promise));
|
|
905
|
+
for (let ri = 0; ri < retryResults.length; ri++) {
|
|
906
|
+
const result = retryResults[ri];
|
|
907
|
+
const idx = retryPromises[ri].index;
|
|
908
|
+
const originalItemIndex = returnItems[idx].pairedItem &&
|
|
909
|
+
typeof returnItems[idx].pairedItem === 'object' &&
|
|
910
|
+
!Array.isArray(returnItems[idx].pairedItem)
|
|
911
|
+
? returnItems[idx].pairedItem
|
|
912
|
+
.item
|
|
913
|
+
: idx;
|
|
914
|
+
if (result.status === 'fulfilled' && result.value != null) {
|
|
915
|
+
const response = result.value;
|
|
916
|
+
if (typeof response === 'object' &&
|
|
917
|
+
response !== null &&
|
|
918
|
+
response.body !== undefined) {
|
|
919
|
+
let responseFormat = this.getNodeParameter('options.response.response.responseFormat', 0, 'autodetect');
|
|
920
|
+
const currentFullResponse = this.getNodeParameter('options.response.response.fullResponse', 0, false);
|
|
921
|
+
if (responseFormat === 'autodetect') {
|
|
922
|
+
const ct = (_3 = (_2 = response.headers) === null || _2 === void 0 ? void 0 : _2['content-type']) !== null && _3 !== void 0 ? _3 : '';
|
|
923
|
+
if (ct.includes('application/json')) {
|
|
924
|
+
responseFormat = 'json';
|
|
925
|
+
}
|
|
926
|
+
else {
|
|
927
|
+
responseFormat = 'text';
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
if (currentFullResponse) {
|
|
931
|
+
returnItems[idx] = {
|
|
932
|
+
json: {
|
|
933
|
+
body: typeof response.body === 'string'
|
|
934
|
+
? (0, n8n_workflow_1.jsonParse)(response.body, {
|
|
935
|
+
fallbackValue: response.body,
|
|
936
|
+
})
|
|
937
|
+
: response.body,
|
|
938
|
+
headers: response.headers,
|
|
939
|
+
statusCode: response.statusCode,
|
|
940
|
+
statusMessage: response.statusMessage,
|
|
941
|
+
},
|
|
942
|
+
pairedItem: {
|
|
943
|
+
item: originalItemIndex,
|
|
944
|
+
},
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
else {
|
|
948
|
+
let bodyData = response.body;
|
|
949
|
+
if (Buffer.isBuffer(bodyData)) {
|
|
950
|
+
const bodyStr = bodyData.toString('utf-8');
|
|
951
|
+
try {
|
|
952
|
+
bodyData = JSON.parse(bodyStr);
|
|
953
|
+
}
|
|
954
|
+
catch {
|
|
955
|
+
bodyData = bodyStr;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
else if (typeof bodyData === 'string') {
|
|
959
|
+
try {
|
|
960
|
+
bodyData = JSON.parse(bodyData);
|
|
961
|
+
}
|
|
962
|
+
catch { }
|
|
963
|
+
}
|
|
964
|
+
if (typeof bodyData === 'object' &&
|
|
965
|
+
bodyData !== null) {
|
|
966
|
+
returnItems[idx] = {
|
|
967
|
+
json: bodyData,
|
|
968
|
+
pairedItem: {
|
|
969
|
+
item: originalItemIndex,
|
|
970
|
+
},
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
else {
|
|
974
|
+
returnItems[idx] = {
|
|
975
|
+
json: { data: bodyData },
|
|
976
|
+
pairedItem: {
|
|
977
|
+
item: originalItemIndex,
|
|
978
|
+
},
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
else if (typeof response === 'object' &&
|
|
984
|
+
response !== null) {
|
|
985
|
+
returnItems[idx] = {
|
|
986
|
+
json: response,
|
|
987
|
+
pairedItem: { item: originalItemIndex },
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
else {
|
|
991
|
+
try {
|
|
992
|
+
const parsed = JSON.parse(response);
|
|
993
|
+
returnItems[idx] = {
|
|
994
|
+
json: parsed,
|
|
995
|
+
pairedItem: {
|
|
996
|
+
item: originalItemIndex,
|
|
997
|
+
},
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
catch {
|
|
1001
|
+
returnItems[idx] = {
|
|
1002
|
+
json: { data: response },
|
|
1003
|
+
pairedItem: {
|
|
1004
|
+
item: originalItemIndex,
|
|
1005
|
+
},
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
returnItems = returnItems.map(helpers_1.replaceNullValues);
|
|
1014
|
+
if (returnItems.length === 1 &&
|
|
1015
|
+
returnItems[0].json.data &&
|
|
1016
|
+
Array.isArray(returnItems[0].json.data)) {
|
|
1017
|
+
const message = "To split the contents of 'data' into separate items for easier processing, add a 'Split Out' node after this one";
|
|
1018
|
+
if (this.addExecutionHints) {
|
|
1019
|
+
this.addExecutionHints({
|
|
1020
|
+
message,
|
|
1021
|
+
location: 'outputPane',
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
else {
|
|
1025
|
+
this.logger.info(message);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
return [returnItems];
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
exports.BetterHttpRequest = BetterHttpRequest;
|
|
1032
|
+
//# sourceMappingURL=BetterHttpRequest.node.js.map
|