UNPKG

n8n-nodes-base

Version:

Base nodes of n8n

858 lines 47 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpRequestV3 = void 0; const set_1 = __importDefault(require("lodash/set")); const n8n_workflow_1 = require("n8n-workflow"); const utilities_1 = require("../../../utils/utilities"); const Description_1 = require("./Description"); const GenericFunctions_1 = require("../GenericFunctions"); const binaryData_1 = require("./utils/binaryData"); const parse_1 = require("./utils/parse"); const optimizeResponse_1 = require("../shared/optimizeResponse"); const buffer_decoding_1 = require("./utils/buffer-decoding"); function toText(data) { if (typeof data === 'object' && data !== null) { return JSON.stringify(data); } return data; } class HttpRequestV3 { description; constructor(baseDescription) { this.description = { ...baseDescription, subtitle: '={{$parameter["method"] + ": " + $parameter["url"]}}', version: [3, 4, 4.1, 4.2, 4.3, 4.4], defaults: { name: 'HTTP Request', color: '#0004F5', }, inputs: [n8n_workflow_1.NodeConnectionTypes.Main], outputs: [n8n_workflow_1.NodeConnectionTypes.Main], credentials: [ { name: 'httpSslAuth', required: true, displayOptions: { show: { provideSslCertificates: [true], }, }, }, ], usableAsTool: { replacements: { codex: { subcategories: { Tools: ['Recommended Tools'], }, }, }, }, properties: Description_1.mainProperties, }; } async execute() { const items = this.getInputData(); const nodeVersion = this.getNode().typeVersion; const fullResponseProperties = ['body', 'headers', 'statusCode', 'statusMessage']; let authentication; try { authentication = this.getNodeParameter('authentication', 0); } catch { } let httpBasicAuth; let httpBearerAuth; let httpDigestAuth; let httpHeaderAuth; let httpQueryAuth; let httpCustomAuth; let oAuth1Api; let oAuth2Api; let sslCertificates; let nodeCredentialType; let genericCredentialType; let requestOptions = { uri: '', }; let returnItems = []; const errorItems = {}; const requestPromises = []; let fullResponse = false; let autoDetectResponseFormat = false; let responseFileName; // Can not be defined on a per item level const pagination = this.getNodeParameter('options.pagination.pagination', 0, null, { rawExpressions: true, }); const requests = []; const updadeQueryParameter = (0, GenericFunctions_1.updadeQueryParameterConfig)(nodeVersion); for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { try { if (authentication === 'genericCredentialType') { genericCredentialType = this.getNodeParameter('genericAuthType', 0); if (genericCredentialType === 'httpBasicAuth') { httpBasicAuth = await this.getCredentials('httpBasicAuth', itemIndex); } else if (genericCredentialType === 'httpBearerAuth') { httpBearerAuth = await this.getCredentials('httpBearerAuth', itemIndex); } else if (genericCredentialType === 'httpDigestAuth') { httpDigestAuth = await this.getCredentials('httpDigestAuth', itemIndex); } else if (genericCredentialType === 'httpHeaderAuth') { httpHeaderAuth = await this.getCredentials('httpHeaderAuth', itemIndex); } else if (genericCredentialType === 'httpQueryAuth') { httpQueryAuth = await this.getCredentials('httpQueryAuth', itemIndex); } else if (genericCredentialType === 'httpCustomAuth') { httpCustomAuth = await this.getCredentials('httpCustomAuth', itemIndex); } else if (genericCredentialType === 'oAuth1Api') { oAuth1Api = await this.getCredentials('oAuth1Api', itemIndex); } else if (genericCredentialType === 'oAuth2Api') { oAuth2Api = await this.getCredentials('oAuth2Api', itemIndex); } } else if (authentication === 'predefinedCredentialType') { nodeCredentialType = this.getNodeParameter('nodeCredentialType', itemIndex); } const url = this.getNodeParameter('url', itemIndex); if (typeof url !== 'string') { const actualType = url === null ? 'null' : typeof url; throw new n8n_workflow_1.NodeOperationError(this.getNode(), `URL parameter must be a string, got ${actualType}`); } if (!url.startsWith('http://') && !url.startsWith('https://')) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid URL: ${url}. URL must start with "http" or "https".`); } const checkDomainRestrictions = async (credentialData, url, credentialType) => { if (credentialData.allowedHttpRequestDomains === 'domains') { const allowedDomains = credentialData.allowedDomains; if (!allowedDomains || allowedDomains.trim() === '') { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No allowed domains specified. Configure allowed domains or change restriction setting.'); } if (!(0, n8n_workflow_1.isDomainAllowed)(url, { allowedDomains })) { const credentialInfo = credentialType ? ` (${credentialType})` : ''; throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Domain not allowed: This credential${credentialInfo} is restricted from accessing ${url}. ` + `Only the following domains are allowed: ${allowedDomains}`); } } else if (credentialData.allowedHttpRequestDomains === 'none') { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'This credential is configured to prevent use within an HTTP Request node'); } }; if (httpBasicAuth) await checkDomainRestrictions(httpBasicAuth, url); if (httpBearerAuth) await checkDomainRestrictions(httpBearerAuth, url); if (httpDigestAuth) await checkDomainRestrictions(httpDigestAuth, url); if (httpHeaderAuth) await checkDomainRestrictions(httpHeaderAuth, url); if (httpQueryAuth) await checkDomainRestrictions(httpQueryAuth, url); if (httpCustomAuth) await checkDomainRestrictions(httpCustomAuth, url); if (oAuth1Api) await checkDomainRestrictions(oAuth1Api, url); if (oAuth2Api) await checkDomainRestrictions(oAuth2Api, url); if (nodeCredentialType) { try { const credentialData = await this.getCredentials(nodeCredentialType, itemIndex); await checkDomainRestrictions(credentialData, url, nodeCredentialType); } catch (error) { if (error.message?.includes('Domain not allowed') || error.message?.includes('configured to prevent') || error.message?.includes('No allowed domains specified')) { throw error; } } } const provideSslCertificates = this.getNodeParameter('provideSslCertificates', itemIndex, false); if (provideSslCertificates) { sslCertificates = await this.getCredentials('httpSslAuth', itemIndex); } const requestMethod = this.getNodeParameter('method', itemIndex); const sendQuery = this.getNodeParameter('sendQuery', itemIndex, false); const queryParameters = this.getNodeParameter('queryParameters.parameters', itemIndex, []); const specifyQuery = this.getNodeParameter('specifyQuery', itemIndex, 'keypair'); const jsonQueryParameter = this.getNodeParameter('jsonQuery', itemIndex, ''); const sendBody = this.getNodeParameter('sendBody', itemIndex, false); const bodyContentType = this.getNodeParameter('contentType', itemIndex, ''); const specifyBody = this.getNodeParameter('specifyBody', itemIndex, ''); const bodyParameters = this.getNodeParameter('bodyParameters.parameters', itemIndex, []); const jsonBodyParameter = this.getNodeParameter('jsonBody', itemIndex, ''); const body = this.getNodeParameter('body', itemIndex, ''); const sendHeaders = this.getNodeParameter('sendHeaders', itemIndex, false); const headerParameters = this.getNodeParameter('headerParameters.parameters', itemIndex, []); const specifyHeaders = this.getNodeParameter('specifyHeaders', itemIndex, 'keypair'); const jsonHeadersParameter = this.getNodeParameter('jsonHeaders', itemIndex, ''); const { redirect, batching, proxy, timeout, allowUnauthorizedCerts, queryParameterArrays, response, lowercaseHeaders, sendCredentialsOnCrossOriginRedirect, } = this.getNodeParameter('options', itemIndex, {}); responseFileName = response?.response?.outputPropertyName; const responseFormat = response?.response?.responseFormat || 'autodetect'; fullResponse = response?.response?.fullResponse || false; autoDetectResponseFormat = responseFormat === 'autodetect'; // defaults batch size to 1 of it's set to 0 const batchSize = batching?.batch?.batchSize > 0 ? batching?.batch?.batchSize : 1; const batchInterval = batching?.batch.batchInterval; if (itemIndex > 0 && batchSize >= 0 && batchInterval > 0) { if (itemIndex % batchSize === 0) { await (0, n8n_workflow_1.sleep)(batchInterval); } } const defaultSendCredentialsOnCrossOriginRedirect = nodeVersion < 4.4; requestOptions = { headers: {}, method: requestMethod, uri: url, gzip: true, rejectUnauthorized: !allowUnauthorizedCerts || false, followRedirect: false, resolveWithFullResponse: true, sendCredentialsOnCrossOriginRedirect: sendCredentialsOnCrossOriginRedirect ?? defaultSendCredentialsOnCrossOriginRedirect, }; if (requestOptions.method !== 'GET' && nodeVersion >= 4.1) { requestOptions = { ...requestOptions, followAllRedirects: false }; } const defaultRedirect = nodeVersion >= 4 && redirect === undefined; if (redirect?.redirect?.followRedirects || defaultRedirect) { requestOptions.followRedirect = true; requestOptions.followAllRedirects = true; } if (redirect?.redirect?.maxRedirects || defaultRedirect) { requestOptions.maxRedirects = redirect?.redirect?.maxRedirects; } if (response?.response?.neverError) { requestOptions.simple = false; } if (proxy) { requestOptions.proxy = proxy; } if (timeout) { requestOptions.timeout = timeout; } else { // set default timeout to 5 minutes requestOptions.timeout = 300_000; } if (sendQuery && queryParameterArrays) { Object.assign(requestOptions, { qsStringifyOptions: { arrayFormat: queryParameterArrays }, }); } const parametersToKeyValue = async (accumulator, cur) => { if (cur.parameterType === 'formBinaryData') { if (!cur.inputDataFieldName) return accumulator; const binaryData = this.helpers.assertBinaryData(itemIndex, cur.inputDataFieldName); let uploadData; if (binaryData.id) { uploadData = await this.helpers.getBinaryStream(binaryData.id); } else { uploadData = Buffer.from(binaryData.data, n8n_workflow_1.BINARY_ENCODING); } accumulator[cur.name] = { value: uploadData, options: { filename: binaryData.fileName, contentType: binaryData.mimeType, }, }; return accumulator; } updadeQueryParameter(accumulator, cur.name, cur.value); return accumulator; }; // Get parameters defined in the UI if (sendBody && bodyParameters) { if (specifyBody === 'keypair' || bodyContentType === 'multipart-form-data') { requestOptions.body = await (0, GenericFunctions_1.prepareRequestBody)(bodyParameters, bodyContentType, nodeVersion, parametersToKeyValue); } else if (specifyBody === 'json') { // body is specified using JSON if (typeof jsonBodyParameter !== 'object' && jsonBodyParameter !== null) { try { JSON.parse(jsonBodyParameter); } catch { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'JSON parameter needs to be valid JSON', { itemIndex, }); } requestOptions.body = (0, n8n_workflow_1.jsonParse)(jsonBodyParameter); } else { requestOptions.body = jsonBodyParameter; } } else if (specifyBody === 'string') { //form urlencoded requestOptions.body = Object.fromEntries(new URLSearchParams(body)); } } // Change the way data get send in case a different content-type than JSON got selected if (sendBody && ['PATCH', 'POST', 'PUT', 'GET'].includes(requestMethod)) { if (bodyContentType === 'multipart-form-data') { requestOptions.formData = requestOptions.body; delete requestOptions.body; } else if (bodyContentType === 'form-urlencoded') { requestOptions.form = requestOptions.body; delete requestOptions.body; } else if (bodyContentType === 'binaryData') { const inputDataFieldName = this.getNodeParameter('inputDataFieldName', itemIndex); let uploadData; let contentLength; const itemBinaryData = this.helpers.assertBinaryData(itemIndex, inputDataFieldName); if (itemBinaryData.id) { uploadData = await this.helpers.getBinaryStream(itemBinaryData.id); const metadata = await this.helpers.getBinaryMetadata(itemBinaryData.id); contentLength = metadata.fileSize; } else { uploadData = Buffer.from(itemBinaryData.data, n8n_workflow_1.BINARY_ENCODING); contentLength = uploadData.length; } requestOptions.body = uploadData; requestOptions.headers = { ...requestOptions.headers, 'content-length': contentLength, 'content-type': itemBinaryData.mimeType ?? 'application/octet-stream', }; } else if (bodyContentType === 'raw') { requestOptions.body = body; } } // Get parameters defined in the UI if (sendQuery && queryParameters) { if (specifyQuery === 'keypair') { requestOptions.qs = await (0, GenericFunctions_1.reduceAsync)(queryParameters, parametersToKeyValue); } else if (specifyQuery === 'json') { // query is specified using JSON try { JSON.parse(jsonQueryParameter); } catch { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'JSON parameter needs to be valid JSON', { itemIndex, }); } requestOptions.qs = (0, n8n_workflow_1.jsonParse)(jsonQueryParameter); } } // Get parameters defined in the UI if (sendHeaders && headerParameters) { let additionalHeaders = {}; if (specifyHeaders === 'keypair') { additionalHeaders = await (0, GenericFunctions_1.reduceAsync)(headerParameters.filter((header) => header.name), parametersToKeyValue); } else if (specifyHeaders === 'json') { // body is specified using JSON try { JSON.parse(jsonHeadersParameter); } catch { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'JSON parameter needs to be valid JSON', { itemIndex, }); } additionalHeaders = (0, n8n_workflow_1.jsonParse)(jsonHeadersParameter); } requestOptions.headers = { ...requestOptions.headers, ...(lowercaseHeaders === undefined || lowercaseHeaders ? (0, utilities_1.keysToLowercase)(additionalHeaders) : additionalHeaders), }; } if (autoDetectResponseFormat || responseFormat === 'file') { requestOptions.encoding = null; requestOptions.json = false; requestOptions.useStream = true; } else if (bodyContentType === 'raw') { requestOptions.json = false; requestOptions.useStream = true; } else { requestOptions.json = true; } // Add Content Type if any are set if (bodyContentType === 'raw') { if (requestOptions.headers === undefined) { requestOptions.headers = {}; } const rawContentType = this.getNodeParameter('rawContentType', itemIndex); requestOptions.headers['content-type'] = rawContentType; } const authDataKeys = {}; // Add SSL certificates if any are set (0, GenericFunctions_1.setAgentOptions)(requestOptions, sslCertificates); if (requestOptions.agentOptions) { authDataKeys.agentOptions = Object.keys(requestOptions.agentOptions); } // Add credentials if any are set if (httpBasicAuth !== undefined) { requestOptions.auth = { user: httpBasicAuth.user, pass: httpBasicAuth.password, }; authDataKeys.auth = ['pass']; } if (httpBearerAuth !== undefined) { requestOptions.headers = requestOptions.headers ?? {}; requestOptions.headers.Authorization = `Bearer ${String(httpBearerAuth.token)}`; authDataKeys.headers = ['Authorization']; } if (httpHeaderAuth !== undefined) { requestOptions.headers[httpHeaderAuth.name] = httpHeaderAuth.value; authDataKeys.headers = [httpHeaderAuth.name]; } if (httpQueryAuth !== undefined) { if (!requestOptions.qs) { requestOptions.qs = {}; } requestOptions.qs[httpQueryAuth.name] = httpQueryAuth.value; authDataKeys.qs = [httpQueryAuth.name]; } if (httpDigestAuth !== undefined) { requestOptions.auth = { user: httpDigestAuth.user, pass: httpDigestAuth.password, sendImmediately: false, }; authDataKeys.auth = ['pass']; } if (httpCustomAuth !== undefined) { const customAuth = (0, n8n_workflow_1.jsonParse)(httpCustomAuth.json || '{}', { errorMessage: 'Invalid Custom Auth JSON' }); if (customAuth.headers) { requestOptions.headers = { ...requestOptions.headers, ...customAuth.headers }; authDataKeys.headers = Object.keys(customAuth.headers); } if (customAuth.body) { requestOptions.body = { ...requestOptions.body, ...customAuth.body }; authDataKeys.body = Object.keys(customAuth.body); } if (customAuth.qs) { requestOptions.qs = { ...requestOptions.qs, ...customAuth.qs }; authDataKeys.qs = Object.keys(customAuth.qs); } } if (requestOptions.headers.accept === undefined) { if (responseFormat === 'json') { requestOptions.headers.accept = 'application/json,text/*;q=0.99'; } else if (responseFormat === 'text') { requestOptions.headers.accept = 'application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, */*;q=0.1'; } else { requestOptions.headers.accept = 'application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, image/*;q=0.8, */*;q=0.7'; } } requests.push({ options: requestOptions, authKeys: authDataKeys, credentialType: nodeCredentialType, }); if (pagination && pagination.paginationMode !== 'off') { let continueExpression = '={{false}}'; if (pagination.paginationCompleteWhen === 'receiveSpecificStatusCodes') { // Split out comma separated list of status codes into array const statusCodesWhenCompleted = pagination.statusCodesWhenComplete .split(',') .map((item) => parseInt(item.trim())); continueExpression = `={{ !${JSON.stringify(statusCodesWhenCompleted)}.includes($response.statusCode) }}`; } else if (pagination.paginationCompleteWhen === 'responseIsEmpty') { continueExpression = '={{ Array.isArray($response.body) ? $response.body.length : !!$response.body }}'; } else { // Other if (!pagination.completeExpression.length || pagination.completeExpression[0] !== '=') { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid or empty Complete Expression'); } continueExpression = `={{ !(${pagination.completeExpression.trim().slice(3, -2)}) }}`; } const paginationData = { continue: continueExpression, request: {}, requestInterval: pagination.requestInterval, }; if (pagination.paginationMode === 'updateAParameterInEachRequest') { // Iterate over all parameters and add them to the request paginationData.request = {}; const { parameters } = pagination.parameters; if (parameters.length === 1 && parameters[0].name === '' && parameters[0].value === '') { 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 "); } pagination.parameters.parameters.forEach((parameter, index) => { if (!paginationData.request[parameter.type]) { paginationData.request[parameter.type] = {}; } const parameterName = parameter.name; if (parameterName === '') { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Parameter name must be set for parameter [${index + 1}] in pagination settings`); } const parameterValue = parameter.value; if (parameterValue === '') { 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`); } paginationData.request[parameter.type][parameterName] = parameterValue; }); } else if (pagination.paginationMode === 'responseContainsNextURL') { paginationData.request.url = pagination.nextURL; } if (pagination.limitPagesFetched) { paginationData.maxRequests = pagination.maxRequests; } if (responseFormat === 'file') { paginationData.binaryResult = true; } const requestPromise = this.helpers.requestWithAuthenticationPaginated .call(this, requestOptions, itemIndex, paginationData, nodeCredentialType ?? genericCredentialType) .catch((error) => { if (error instanceof n8n_workflow_1.NodeOperationError && error.type === 'invalid_url') { const urlParameterName = pagination.paginationMode === 'responseContainsNextURL' ? 'Next URL' : 'URL'; throw new n8n_workflow_1.NodeOperationError(this.getNode(), error.message, { description: `Make sure the "${urlParameterName}" parameter evaluates to a valid URL.`, }); } throw error; }); requestPromises.push(requestPromise); } else if (authentication === 'genericCredentialType' || authentication === 'none') { if (oAuth1Api) { const requestOAuth1 = this.helpers.requestOAuth1.call(this, 'oAuth1Api', requestOptions); requestOAuth1.catch(() => { }); requestPromises.push(requestOAuth1); } else if (oAuth2Api) { const requestOAuth2 = this.helpers.requestOAuth2.call(this, 'oAuth2Api', requestOptions, { tokenType: 'Bearer', }); requestOAuth2.catch(() => { }); requestPromises.push(requestOAuth2); } else { // bearerAuth, queryAuth, headerAuth, digestAuth, none const request = this.helpers.request(requestOptions); request.catch(() => { }); requestPromises.push(request); } } else if (authentication === 'predefinedCredentialType' && nodeCredentialType) { const additionalOAuth2Options = (0, GenericFunctions_1.getOAuth2AdditionalParameters)(nodeCredentialType); // service-specific cred: OAuth1, OAuth2, plain const requestWithAuthentication = this.helpers.requestWithAuthentication.call(this, nodeCredentialType, requestOptions, additionalOAuth2Options && { oauth2: additionalOAuth2Options }, itemIndex); requestWithAuthentication.catch(() => { }); requestPromises.push(requestWithAuthentication); } } catch (error) { if (!this.continueOnFail()) throw error; requestPromises.push(Promise.reject(error).catch(() => { })); errorItems[itemIndex] = error.message; continue; } } const sanitizedRequests = []; const promisesResponses = await Promise.allSettled(requestPromises.map(async (requestPromise, itemIndex) => await requestPromise .then((response) => response) .finally(async () => { if (errorItems[itemIndex]) return; try { // Secrets need to be read after the request because secrets could have changed // For example: OAuth token refresh, preAuthentication const { options, authKeys, credentialType } = requests[itemIndex]; let secrets = []; if (credentialType) { const properties = this.getCredentialsProperties(credentialType); const credentials = await this.getCredentials(credentialType, itemIndex); secrets = (0, GenericFunctions_1.getSecrets)(properties, credentials); } const sanitizedRequestOptions = (0, GenericFunctions_1.sanitizeUiMessage)(options, authKeys, secrets); sanitizedRequests.push(sanitizedRequestOptions); this.sendMessageToUI(sanitizedRequestOptions); } catch (e) { } }))); let responseData; for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { try { responseData = promisesResponses.shift(); if (errorItems[itemIndex]) { returnItems.push({ json: { error: errorItems[itemIndex] }, pairedItem: { item: itemIndex }, }); continue; } if (responseData.status !== 'fulfilled') { if (responseData.reason.statusCode === 429) { responseData.reason.message = "Try spacing your requests out using the batching settings under 'Options'"; } if (!this.continueOnFail()) { if (autoDetectResponseFormat && responseData.reason.error instanceof Buffer) { responseData.reason.error = Buffer.from(responseData.reason.error).toString(); } let error; if (responseData?.reason instanceof n8n_workflow_1.NodeApiError) { error = responseData.reason; (0, set_1.default)(error, 'context.itemIndex', itemIndex); } else { const errorData = (responseData.reason ? responseData.reason : responseData); error = new n8n_workflow_1.NodeApiError(this.getNode(), errorData, { itemIndex }); } (0, set_1.default)(error, 'context.request', sanitizedRequests[itemIndex]); throw error; } else { (0, n8n_workflow_1.removeCircularRefs)(responseData.reason); // Return the actual reason as error returnItems.push({ json: { error: responseData.reason, }, pairedItem: { item: itemIndex, }, }); continue; } } let responses; if (Array.isArray(responseData.value)) { responses = responseData.value; } else { responses = [responseData.value]; } let responseFormat = this.getNodeParameter('options.response.response.responseFormat', 0, 'autodetect'); fullResponse = this.getNodeParameter('options.response.response.fullResponse', 0, false); // eslint-disable-next-line prefer-const for (let [index, response] of Object.entries(responses)) { if (response?.request?.constructor.name === 'ClientRequest') delete response.request; if (this.getMode() === 'manual' && index === '0') { // For manual executions save the first response in the context // so that we can use it in the frontend and so make it easier for // the users to create the required pagination expressions const nodeContext = this.getContext('node'); if (pagination && pagination.paginationMode !== 'off') { nodeContext.response = responseData.value[0]; } else { nodeContext.response = responseData.value; } } const responseContentType = response.headers['content-type'] ?? ''; if (autoDetectResponseFormat) { if (responseContentType.includes('application/json')) { responseFormat = 'json'; if (!response.__bodyResolved) { const neverError = this.getNodeParameter('options.response.response.neverError', 0, false); const data = await (0, buffer_decoding_1.binaryToStringWithEncodingDetection)(response.body, responseContentType, this.helpers); response.body = (0, n8n_workflow_1.jsonParse)(data, { ...(neverError ? { fallbackValue: {} } : { errorMessage: 'Invalid JSON in response body' }), }); } } else if (GenericFunctions_1.binaryContentTypes.some((e) => responseContentType.includes(e))) { responseFormat = 'file'; } else { responseFormat = 'text'; if (!response.__bodyResolved) { const data = await (0, buffer_decoding_1.binaryToStringWithEncodingDetection)(response.body, responseContentType, this.helpers); response.body = !data ? undefined : data; } } } // This is a no-op outside of tool usage const optimizeResponse = (0, optimizeResponse_1.configureResponseOptimizer)(this, itemIndex); if (autoDetectResponseFormat && !fullResponse) { delete response.headers; delete response.statusCode; delete response.statusMessage; } if (!fullResponse) { response = optimizeResponse(response.body); } else { response.body = optimizeResponse(response.body); } if (responseFormat === 'file') { const outputPropertyName = this.getNodeParameter('options.response.response.outputPropertyName', 0, 'data'); const newItem = { json: {}, binary: {}, pairedItem: { item: itemIndex, }, }; if (items[itemIndex].binary !== undefined) { // Create a shallow copy of the binary data so that the old // data references which do not get changed still stay behind // but the incoming data does not get changed. Object.assign(newItem.binary, items[itemIndex].binary); } let binaryData; if (fullResponse) { const returnItem = {}; for (const property of fullResponseProperties) { if (property === 'body') { continue; } returnItem[property] = response[property]; } newItem.json = returnItem; binaryData = response?.body; } else { newItem.json = items[itemIndex].json; binaryData = response; } const preparedBinaryData = await this.helpers.prepareBinaryData(binaryData, undefined, (0, parse_1.mimeTypeFromResponse)(responseContentType)); preparedBinaryData.fileName = (0, binaryData_1.setFilename)(preparedBinaryData, requestOptions, responseFileName); newItem.binary[outputPropertyName] = preparedBinaryData; returnItems.push(newItem); } else if (responseFormat === 'text') { const outputPropertyName = this.getNodeParameter('options.response.response.outputPropertyName', 0, 'data'); if (fullResponse) { const returnItem = {}; for (const property of fullResponseProperties) { if (property === 'body') { returnItem[outputPropertyName] = toText(response[property]); continue; } returnItem[property] = response[property]; } returnItems.push({ json: returnItem, pairedItem: { item: itemIndex, }, }); } else { returnItems.push({ json: { [outputPropertyName]: toText(response), }, pairedItem: { item: itemIndex, }, }); } } else { // responseFormat: 'json' if (fullResponse) { const returnItem = {}; for (const property of fullResponseProperties) { returnItem[property] = response[property]; } if (responseFormat === 'json' && typeof returnItem.body === 'string') { try { returnItem.body = JSON.parse(returnItem.body); } catch (error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Response body is not valid JSON. Change "Response Format" to "Text"', { itemIndex }); } } returnItems.push({ json: returnItem, pairedItem: { item: itemIndex, }, }); } else { if (responseFormat === 'json' && typeof response === 'string') { try { if (typeof response !== 'object') { response = JSON.parse(response); } } catch (error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Response body is not valid JSON. Change "Response Format" to "Text"', { itemIndex }); } } if (Array.isArray(response)) { response.forEach((item) => returnItems.push({ json: item, pairedItem: { item: itemIndex, }, })); } else { returnItems.push({ json: response, pairedItem: { item: itemIndex, }, }); } } } } } catch (error) { if (!this.continueOnFail()) throw error; returnItems.push({ json: { error: error.message }, pairedItem: { item: itemIndex }, }); continue; } } returnItems = returnItems.map(GenericFunctions_1.replaceNullValues); // Only show the Split Out hint in regular workflow context, not when running as an AI Agent tool // (users cannot add nodes after tools in an AI Agent context) if (returnItems.length === 1 && returnItems[0].json.data && Array.isArray(returnItems[0].json.data) && !this.isToolExecution()) { const message = 'To split the contents of ‘data’ into separate items for easier processing, add a ‘Split Out’ node after this one'; if (this.addExecutionHints) { this.addExecutionHints({ message, location: 'outputPane', }); } else { this.logger.info(message); } } return [returnItems]; } } exports.HttpRequestV3 = HttpRequestV3; //# sourceMappingURL=HttpRequestV3.node.js.map