fix(HTTP Request Node): Handle special characters in pagination expressions + improve hint text (#8576)

Co-authored-by: Michael Kret <michael.k@radency.com>
This commit is contained in:
Elias Meire 2024-02-12 17:32:27 +01:00 committed by GitHub
parent d38a822b95
commit 3b2078c3ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 218 additions and 153 deletions

View file

@ -352,10 +352,10 @@ export class ChatTrigger implements INodeType {
await validateAuth(this); await validateAuth(this);
} catch (error) { } catch (error) {
if (error) { if (error) {
res.writeHead(error.responseCode as number, { res.writeHead((error as IDataObject).responseCode as number, {
'www-authenticate': 'Basic realm="Webhook"', 'www-authenticate': 'Basic realm="Webhook"',
}); });
res.end(error.message as string); res.end((error as IDataObject).message as string);
return { noWebhookResponse: true }; return { noWebhookResponse: true };
} }
throw error; throw error;

View file

@ -197,6 +197,17 @@ const createFormDataObject = (data: Record<string, unknown>) => {
return formData; return formData;
}; };
const validateUrl = (url?: string): boolean => {
if (!url) return false;
try {
new URL(url);
return true;
} catch (error) {
return false;
}
};
function searchForHeader(config: AxiosRequestConfig, headerName: string) { function searchForHeader(config: AxiosRequestConfig, headerName: string) {
if (config.headers === undefined) { if (config.headers === undefined) {
return undefined; return undefined;
@ -1240,7 +1251,10 @@ function applyPaginationRequestData(
requestData: OptionsWithUri, requestData: OptionsWithUri,
paginationRequestData: PaginationOptions['request'], paginationRequestData: PaginationOptions['request'],
): OptionsWithUri { ): OptionsWithUri {
const preparedPaginationData: Partial<OptionsWithUri> = { ...paginationRequestData }; const preparedPaginationData: Partial<OptionsWithUri> = {
...paginationRequestData,
uri: paginationRequestData.url,
};
if ('formData' in requestData) { if ('formData' in requestData) {
preparedPaginationData.formData = paginationRequestData.body; preparedPaginationData.formData = paginationRequestData.body;
@ -2885,6 +2899,14 @@ const getRequestHelperFunctions = (
const tempRequestOptions = applyPaginationRequestData(requestOptions, paginateRequestData); const tempRequestOptions = applyPaginationRequestData(requestOptions, paginateRequestData);
if (!validateUrl(tempRequestOptions.uri as string)) {
throw new NodeOperationError(node, `'${paginateRequestData.url}' is not a valid URL.`, {
itemIndex,
runIndex,
type: 'invalid_url',
});
}
if (credentialsType) { if (credentialsType) {
tempResponseData = await this.helpers.requestWithAuthentication.call( tempResponseData = await this.helpers.requestWithAuthentication.call(
this, this,

View file

@ -3,22 +3,21 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue';
import type { PropType } from 'vue';
import { mapStores } from 'pinia';
import { EditorView, keymap } from '@codemirror/view';
import { Compartment, EditorState, Prec } from '@codemirror/state';
import { history, redo, undo } from '@codemirror/commands';
import { acceptCompletion, autocompletion, completionStatus } from '@codemirror/autocomplete'; import { acceptCompletion, autocompletion, completionStatus } from '@codemirror/autocomplete';
import { history, redo, undo } from '@codemirror/commands';
import { Compartment, EditorState, Prec } from '@codemirror/state';
import { EditorView, keymap } from '@codemirror/view';
import type { PropType } from 'vue';
import { defineComponent } from 'vue';
import { useNDVStore } from '@/stores/ndv.store';
import { expressionManager } from '@/mixins/expressionManager';
import { highlighter } from '@/plugins/codemirror/resolvableHighlighter';
import { expressionInputHandler } from '@/plugins/codemirror/inputHandlers/expression.inputHandler';
import { inputTheme } from './theme';
import { n8nLang } from '@/plugins/codemirror/n8nLang';
import { completionManager } from '@/mixins/completionManager'; import { completionManager } from '@/mixins/completionManager';
import { expressionManager } from '@/mixins/expressionManager';
import { expressionInputHandler } from '@/plugins/codemirror/inputHandlers/expression.inputHandler';
import { n8nLang } from '@/plugins/codemirror/n8nLang';
import { highlighter } from '@/plugins/codemirror/resolvableHighlighter';
import { isEqual } from 'lodash-es';
import type { IDataObject } from 'n8n-workflow'; import type { IDataObject } from 'n8n-workflow';
import { inputTheme } from './theme';
const editableConf = new Compartment(); const editableConf = new Compartment();
@ -68,26 +67,18 @@ export default defineComponent({
}, },
}); });
}, },
ndvInputData() { displayableSegments(segments, newSegments) {
this.editor?.dispatch({ if (isEqual(segments, newSegments)) return;
changes: {
from: 0,
to: this.editor.state.doc.length,
insert: this.modelValue,
},
});
setTimeout(() => { highlighter.removeColor(this.editor, this.plaintextSegments);
this.editor?.contentDOM.blur(); highlighter.addColor(this.editor, this.resolvableSegments);
this.$emit('change', {
value: this.unresolvedExpression,
segments: this.displayableSegments,
}); });
}, },
}, },
computed: {
...mapStores(useNDVStore),
ndvInputData(): object {
return this.ndvStore.ndvInputData;
},
},
mounted() { mounted() {
const extensions = [ const extensions = [
n8nLang(), n8nLang(),
@ -125,19 +116,11 @@ export default defineComponent({
// Force segments value update by keeping track of editor state // Force segments value update by keeping track of editor state
this.editorState = this.editor.state; this.editorState = this.editor.state;
highlighter.removeColor(this.editor, this.plaintextSegments);
highlighter.addColor(this.editor, this.resolvableSegments);
setTimeout(() => { setTimeout(() => {
try { try {
this.trackCompletion(viewUpdate, this.path); this.trackCompletion(viewUpdate, this.path);
} catch {} } catch {}
}); });
this.$emit('change', {
value: this.unresolvedExpression,
segments: this.displayableSegments,
});
}), }),
]; ];
@ -148,14 +131,10 @@ export default defineComponent({
extensions, extensions,
}), }),
}); });
this.editorState = this.editor.state; this.editorState = this.editor.state;
highlighter.addColor(this.editor, this.resolvableSegments); highlighter.addColor(this.editor, this.resolvableSegments);
this.$emit('change', {
value: this.unresolvedExpression,
segments: this.displayableSegments,
});
}, },
beforeUnmount() { beforeUnmount() {
this.editor?.destroy(); this.editor?.destroy();

View file

@ -727,7 +727,8 @@ export default defineComponent({
this.$emit('removeNode', this.data.name); this.$emit('removeNode', this.data.name);
}, },
toggleDisableNode() { toggleDisableNode(event: MouseEvent) {
(event.currentTarget as HTMLButtonElement).blur();
this.$telemetry.track('User clicked node hover button', { this.$telemetry.track('User clicked node hover button', {
node_type: this.data.type, node_type: this.data.type,
button_name: 'disable', button_name: 'disable',

View file

@ -214,7 +214,7 @@ export function resolveParameter(
// in pagination expressions // in pagination expressions
additionalKeys.$response = get( additionalKeys.$response = get(
executionData, executionData,
`data.executionData.contextData['node:${activeNode.name}'].response`, ['data', 'executionData', 'contextData', `node:${activeNode.name}`, 'response'],
{}, {},
); );
} }

View file

@ -988,7 +988,7 @@ export class HttpRequestV3 implements INodeType {
}, },
default: '', default: '',
description: description:
'Should evaluate to true when pagination is complete. More info.', 'Should evaluate to the URL of the next page. <a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/#pagination" target="_blank">More info</a>.',
}, },
{ {
displayName: 'Parameters', displayName: 'Parameters',
@ -1112,7 +1112,7 @@ export class HttpRequestV3 implements INodeType {
}, },
default: '', default: '',
description: description:
'Should evaluate to true when pagination is complete. More info.', 'Should evaluate to true when pagination is complete. <a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/#pagination" target="_blank">More info</a>.',
}, },
{ {
displayName: 'Limit Pages Fetched', displayName: 'Limit Pages Fetched',
@ -1704,13 +1704,25 @@ export class HttpRequestV3 implements INodeType {
paginationData.binaryResult = true; paginationData.binaryResult = true;
} }
const requestPromise = this.helpers.requestWithAuthenticationPaginated.call( const requestPromise = this.helpers.requestWithAuthenticationPaginated
this, .call(
requestOptions, this,
itemIndex, requestOptions,
paginationData, itemIndex,
nodeCredentialType ?? genericCredentialType, paginationData,
); nodeCredentialType ?? genericCredentialType,
)
.catch((error) => {
if (error instanceof NodeOperationError && error.type === 'invalid_url') {
const urlParameterName =
pagination.paginationMode === 'responseContainsNextURL' ? 'Next URL' : 'URL';
throw new NodeOperationError(this.getNode(), error.message, {
description: `Make sure the "${urlParameterName}" parameter evaluates to a valid URL.`,
});
}
throw error;
});
requestPromises.push(requestPromise); requestPromises.push(requestPromise);
} else if (authentication === 'genericCredentialType' || authentication === 'none') { } else if (authentication === 'genericCredentialType' || authentication === 'none') {
if (oAuth1Api) { if (oAuth1Api) {

View file

@ -35,13 +35,13 @@
} }
} }
}, },
"id": "37742e68-f44e-457a-beb2-f698f4c0dbb5", "id": "c42631bf-5122-4b84-86c7-00ad9dcdcdfb",
"name": "Page Limit", "name": "Page Limit",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2220, 980,
1800 1980
] ]
}, },
{ {
@ -77,13 +77,13 @@
} }
} }
}, },
"id": "07d9001f-5384-4b74-beca-e623467790a8", "id": "337101b7-9815-466b-8920-b69529c90c73",
"name": "Response Empty", "name": "Response Empty",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2220, 980,
1980 2160
] ]
}, },
{ {
@ -121,13 +121,13 @@
} }
} }
}, },
"id": "c918b2be-575e-48df-b6bf-36eee64f12c0", "id": "a2953ca3-e17c-4e83-8bf0-149587c14088",
"name": "Receive Status Code", "name": "Receive Status Code",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2220, 980,
2140 2320
] ]
}, },
{ {
@ -165,13 +165,13 @@
} }
} }
}, },
"id": "3ac6da0f-b931-4c0f-9f75-9818e03cb65b", "id": "aad6795d-4156-445b-8b6c-968692ed3620",
"name": "Complete Expression", "name": "Complete Expression",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2220, 980,
2320 2500
] ]
}, },
{ {
@ -180,13 +180,13 @@
"height": 223.6542431762359, "height": 223.6542431762359,
"width": 365.5274479049966 "width": 365.5274479049966
}, },
"id": "29478700-ec0e-4b88-b771-8c12cc17f6e5", "id": "036cf1e4-0534-4422-9aea-36a9bb308e79",
"name": "Sticky Note", "name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote", "type": "n8n-nodes-base.stickyNote",
"typeVersion": 1, "typeVersion": 1,
"position": [ "position": [
2160, 920,
2920 3100
] ]
}, },
{ {
@ -195,13 +195,13 @@
"height": 1140.0832129820226, "height": 1140.0832129820226,
"width": 354.2110090941684 "width": 354.2110090941684
}, },
"id": "cf67a13d-6204-4d1d-99c9-dea6eb882a17", "id": "e36564a0-d1d9-4f15-9c78-b4713f75b13f",
"name": "Sticky Note1", "name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote", "type": "n8n-nodes-base.stickyNote",
"typeVersion": 1, "typeVersion": 1,
"position": [ "position": [
2167, 927,
1721 1901
] ]
}, },
{ {
@ -233,13 +233,13 @@
} }
} }
}, },
"id": "9c7e0300-e94c-4957-abac-0e6f3f98df94", "id": "83ca19dd-f110-4b36-8c52-836c0b501a7e",
"name": "Response Empty - Text", "name": "Response Empty - Text",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2220, 980,
3240 3420
] ]
}, },
{ {
@ -270,13 +270,13 @@
} }
} }
}, },
"id": "26d8b9ca-68ca-48c2-b603-93b0a2a9369c", "id": "2b770b1b-0c66-4668-a037-f7b2986fd793",
"name": "Response Empty Next with Max Pages", "name": "Response Empty Next with Max Pages",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2220, 980,
2980 3160
] ]
}, },
{ {
@ -285,13 +285,13 @@
"height": 388.6542431762359, "height": 388.6542431762359,
"width": 363.5274479049966 "width": 363.5274479049966
}, },
"id": "7e71e5db-372b-49f4-a309-fafa8cfa6c42", "id": "fee3b5a3-9af9-44d6-8814-a8334e29ed0e",
"name": "Sticky Note2", "name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote", "type": "n8n-nodes-base.stickyNote",
"typeVersion": 1, "typeVersion": 1,
"position": [ "position": [
2160, 920,
3180 3360
] ]
}, },
{ {
@ -329,13 +329,13 @@
} }
} }
}, },
"id": "63515259-3498-49f6-86b4-fd0386b61244", "id": "d91beb91-8716-4817-a729-91114a8d4a63",
"name": "Complete Expression - JSON", "name": "Complete Expression - JSON",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2220, 980,
1520 1700
] ]
}, },
{ {
@ -344,13 +344,13 @@
"height": 232.15942469988397, "height": 232.15942469988397,
"width": 323.21100909416833 "width": 323.21100909416833
}, },
"id": "8dbf7c42-1cbc-4b13-ae60-031f4d690cbd", "id": "693b1adb-1be8-4e87-bad8-23936c685155",
"name": "Sticky Note3", "name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote", "type": "n8n-nodes-base.stickyNote",
"typeVersion": 1, "typeVersion": 1,
"position": [ "position": [
2160, 920,
1452.366284408126 1632.366284408126
] ]
}, },
{ {
@ -383,13 +383,13 @@
} }
} }
}, },
"id": "ca3ce84d-b32b-4f94-a51c-634b36d2553d", "id": "72a3ce0d-f428-41cc-a93e-8692f5ed97f9",
"name": "Response Empty - Include Full Response", "name": "Response Empty - Include Full Response",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2220, 980,
3420 3600
] ]
}, },
{ {
@ -418,13 +418,13 @@
} }
} }
}, },
"id": "de6858d8-8677-4e02-9617-24fa13005278", "id": "b579b38a-ac67-40ab-ab82-b465dc6387dc",
"name": "Pagination Off", "name": "Pagination Off",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2220, 980,
3640 3820
] ]
}, },
{ {
@ -433,13 +433,13 @@
"height": 373, "height": 373,
"width": 363 "width": 363
}, },
"id": "dfee3f26-25e8-4db8-82d7-c3d93a565a35", "id": "601cef96-0ac0-4890-b1c3-8ca15fbc2571",
"name": "Sticky Note4", "name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote", "type": "n8n-nodes-base.stickyNote",
"typeVersion": 1, "typeVersion": 1,
"position": [ "position": [
2160, 920,
3600 3780
] ]
}, },
{ {
@ -463,13 +463,13 @@
} }
} }
}, },
"id": "1ef00909-5412-49c8-9de4-920ebb29999c", "id": "bdd6cf22-746e-4fda-84b2-df178f5d3dab",
"name": "Pagination Not Set", "name": "Pagination Not Set",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2220, 980,
3820 4000
] ]
}, },
{ {
@ -478,13 +478,13 @@
"height": 232.15942469988397, "height": 232.15942469988397,
"width": 394.89100909416834 "width": 394.89100909416834
}, },
"id": "f7b8a43e-c176-4b35-8d9f-1ce027a9a6eb", "id": "1e7bfcfb-93d0-4ee2-8c0f-380ee2dd7fcb",
"name": "Sticky Note5", "name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote", "type": "n8n-nodes-base.stickyNote",
"typeVersion": 1, "typeVersion": 1,
"position": [ "position": [
2160, 920,
4040 4220
] ]
}, },
{ {
@ -500,13 +500,13 @@
"include": "none", "include": "none",
"options": {} "options": {}
}, },
"id": "046aa51f-ad74-4213-ad52-16b7f6bf0ad6", "id": "35a1f1f3-d701-4252-9dea-5b2b0ef7f32f",
"name": "Edit Fields", "name": "Edit Fields",
"type": "n8n-nodes-base.set", "type": "n8n-nodes-base.set",
"typeVersion": 3.2, "typeVersion": 3.2,
"position": [ "position": [
2400, 1160,
4120 4300
] ]
}, },
{ {
@ -541,29 +541,29 @@
} }
} }
}, },
"id": "a6eed8b6-0cd4-449f-a062-d2a6387fc327", "id": "12f6310f-e88e-490f-9dd1-2012586bf9c9",
"name": "Loop", "name": "Loop",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2220, 980,
4120 4300
], ],
"continueOnFail": true "continueOnFail": true
}, },
{ {
"parameters": { "parameters": {
"content": "### Next URL\nResponse Format: Autodetect\nActual Response Format: JSON", "content": "### Next URL\nResponse Format: Autodetect\nActual Response Format: JSON",
"height": 458.3224664750446, "height": 650.4724697091658,
"width": 323.21100909416833 "width": 323.21100909416833
}, },
"id": "1ece8875-2a64-4a5c-9bb4-ea2aec1d3740", "id": "0074b955-3217-4fc9-b7c4-58f11fc323c1",
"name": "Sticky Note6", "name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote", "type": "n8n-nodes-base.stickyNote",
"typeVersion": 1, "typeVersion": 1,
"position": [ "position": [
2140, 900,
520 507.8499967658788
] ]
}, },
{ {
@ -571,13 +571,13 @@
"content": "# Response Format: Autodetect\n", "content": "# Response Format: Autodetect\n",
"width": 545.8929725020898 "width": 545.8929725020898
}, },
"id": "96087f8a-f670-439f-a84d-24139fb82425", "id": "e7b7440f-aad2-49df-9c10-695497aec7c5",
"name": "Sticky Note7", "name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote", "type": "n8n-nodes-base.stickyNote",
"typeVersion": 1, "typeVersion": 1,
"position": [ "position": [
1520, 280,
528 708
] ]
}, },
{ {
@ -585,13 +585,13 @@
"content": "# Response Format: set", "content": "# Response Format: set",
"width": 545.8929725020898 "width": 545.8929725020898
}, },
"id": "8c2c8849-bf67-480e-9d60-333d35b731af", "id": "1ad4fad8-4c66-4afe-8893-2727c852ba44",
"name": "Sticky Note8", "name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote", "type": "n8n-nodes-base.stickyNote",
"typeVersion": 1, "typeVersion": 1,
"position": [ "position": [
1540, 300,
1460 1640
] ]
}, },
{ {
@ -620,13 +620,13 @@
} }
} }
}, },
"id": "dffe62af-48de-4f81-9dff-9449b8eaed47", "id": "415e52cd-bdb4-42a8-af18-fec7eb9e6a1f",
"name": "Complete Expression - JSON Autodetect set", "name": "Complete Expression - JSON Autodetect set",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2200, 960,
608 600
] ]
}, },
{ {
@ -652,46 +652,46 @@
} }
} }
}, },
"id": "029a965f-ffbb-463e-805a-9d799c1652ba", "id": "a111a0a4-ae9c-4b27-af04-5ab9584798ae",
"name": "Complete Expression - JSON unset", "name": "Complete Expression - JSON unset",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2200, 960,
788 780
] ]
}, },
{ {
"parameters": {}, "parameters": {},
"id": "1d695252-948a-40de-bb8c-52c0a05e024a", "id": "aaa4d56b-5d72-4ecb-bcc4-9ccdf0c7c139",
"name": "No Operation, do nothing1", "name": "No Operation, do nothing1",
"type": "n8n-nodes-base.noOp", "type": "n8n-nodes-base.noOp",
"typeVersion": 1, "typeVersion": 1,
"position": [ "position": [
1740, 500,
2320 2500
] ]
}, },
{ {
"parameters": {}, "parameters": {},
"id": "91eaf5ae-87dd-4eac-b144-e165ff55569d", "id": "6fbde1fb-8f7d-4947-84f1-2695b919cc43",
"name": "Data 2", "name": "Data 2",
"type": "n8n-nodes-base.manualTrigger", "type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1, "typeVersion": 1,
"position": [ "position": [
1440, 200,
1800 1980
] ]
}, },
{ {
"parameters": {}, "parameters": {},
"id": "4279a32b-787d-4360-aee6-bbb95c3b0afa", "id": "f13379f2-5867-46d9-9af1-48b9ff9524d0",
"name": "Data 1", "name": "Data 1",
"type": "n8n-nodes-base.noOp", "type": "n8n-nodes-base.noOp",
"typeVersion": 1, "typeVersion": 1,
"position": [ "position": [
1760, 520,
780 960
] ]
}, },
{ {
@ -731,13 +731,13 @@
} }
} }
}, },
"id": "10a5e5bd-b0e0-4490-bb7f-0de29f717b77", "id": "bcbb8fe5-1613-4dc6-a92c-89f1415c1821",
"name": "Response Empty - Text1", "name": "Response Empty - Text1",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2200, 960,
1080 1260
] ]
}, },
{ {
@ -746,13 +746,13 @@
"height": 437.60980047313967, "height": 437.60980047313967,
"width": 323.31395441111135 "width": 323.31395441111135
}, },
"id": "45e77316-f3cb-4515-a777-f921e10edce6", "id": "4e73a489-5c9f-4b8f-a32f-ad6d91fc187a",
"name": "Sticky Note9", "name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote", "type": "n8n-nodes-base.stickyNote",
"typeVersion": 1, "typeVersion": 1,
"position": [ "position": [
2140, 900,
993.7738085909444 1173.7738085909446
] ]
}, },
{ {
@ -793,13 +793,13 @@
} }
} }
}, },
"id": "7f0491f2-cd1d-4dc8-9cb7-26fce638c9b8", "id": "c02661c0-b6e7-45f8-8049-ef1a97f4b590",
"name": "Response Empty - Include Full Response1", "name": "Response Empty - Include Full Response1",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2200, 960,
1260 1440
] ]
}, },
{ {
@ -843,13 +843,13 @@
} }
} }
}, },
"id": "2d4217e3-4c0c-48d1-87e9-fd46b370be4e", "id": "69569627-0e1b-45e0-a279-bf53c3f99c8f",
"name": "POST Form Data", "name": "POST Form Data",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2220, 980,
2680 2860
] ]
}, },
{ {
@ -892,14 +892,37 @@
} }
} }
}, },
"id": "a428f414-d13e-40d5-b1cf-beaf53df5884", "id": "c68c8649-298e-42fe-bc61-df4a4b9d5c39",
"name": "POST JSON", "name": "POST JSON",
"type": "n8n-nodes-base.httpRequest", "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1, "typeVersion": 4.1,
"position": [ "position": [
2220, 980,
2500 2680
] ]
},
{
"parameters": {
"url": "https://dummyjson.com/users",
"options": {
"pagination": {
"pagination": {
"paginationMode": "responseContainsNextURL",
"limitPagesFetched": true,
"maxRequests": 2
}
}
}
},
"id": "c004d7eb-d755-4d65-b359-6e0ddab0406d",
"name": "Complete Expression - JSON unset1",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
960,
980
],
"onError": "continueRegularOutput"
} }
], ],
"pinData": { "pinData": {
@ -1686,6 +1709,18 @@
"id": 8 "id": 8
} }
} }
],
"Complete Expression - JSON unset1": [
{
"json": {
"error": {
"message": "'' is not a valid URL.",
"name": "NodeOperationError",
"description": "Make sure the \"Next URL\" parameter evaluates to a valid URL.",
"context": {}
}
}
}
] ]
}, },
"connections": { "connections": {
@ -1809,6 +1844,11 @@
"node": "Response Empty - Include Full Response1", "node": "Response Empty - Include Full Response1",
"type": "main", "type": "main",
"index": 0 "index": 0
},
{
"node": "Complete Expression - JSON unset1",
"type": "main",
"index": 0
} }
] ]
] ]
@ -1818,11 +1858,11 @@
"settings": { "settings": {
"executionOrder": "v1" "executionOrder": "v1"
}, },
"versionId": "107ed80e-d232-412e-ad52-9e902b46b2a4", "versionId": "5189ec73-b659-4740-83f5-d5bf3995f5df",
"meta": { "meta": {
"templateCredsSetupCompleted": true, "templateCredsSetupCompleted": true,
"instanceId": "27cc9b56542ad45b38725555722c50a1c3fee1670bbb67980558314ee08517c4" "instanceId": "27cc9b56542ad45b38725555722c50a1c3fee1670bbb67980558314ee08517c4"
}, },
"id": "PZHNXWq2FzZp0F9A", "id": "4WORX7JyBFP94dXM",
"tags": [] "tags": []
} }

View file

@ -324,12 +324,19 @@ export const equalityTest = async (testData: WorkflowTestData, types: INodeTypes
resultNodeData.forEach(({ nodeName, resultData }) => { resultNodeData.forEach(({ nodeName, resultData }) => {
const msg = `Equality failed for "${testData.description}" at node "${nodeName}"`; const msg = `Equality failed for "${testData.description}" at node "${nodeName}"`;
resultData.forEach((item) => { resultData.forEach((item) => {
item?.forEach(({ binary }) => { item?.forEach(({ binary, json }) => {
if (binary) { if (binary) {
// @ts-ignore // @ts-ignore
delete binary.data.data; delete binary.data.data;
delete binary.data.directory; delete binary.data.directory;
} }
// Convert errors to JSON so tests can compare
if (json.error instanceof Error) {
json.error = JSON.parse(
JSON.stringify(json.error, ['message', 'name', 'description', 'context']),
);
}
}); });
}); });
return expect(resultData, msg).toEqual(testData.output.nodeData[nodeName]); return expect(resultData, msg).toEqual(testData.output.nodeData[nodeName]);

View file

@ -223,7 +223,7 @@ export class RoutingNode {
returnData.push(...responseData); returnData.push(...responseData);
} catch (error) { } catch (error) {
if (thisArgs !== undefined && thisArgs.continueOnFail()) { if (thisArgs !== undefined && thisArgs.continueOnFail()) {
returnData.push({ json: {}, error: error as NodeError }); returnData.push({ json: {}, error: error as NodeApiError });
continue; continue;
} }

View file

@ -24,6 +24,7 @@ export interface NodeOperationErrorOptions {
level?: ReportingOptions['level']; level?: ReportingOptions['level'];
messageMapping?: { [key: string]: string }; // allows to pass custom mapping for error messages scoped to a node messageMapping?: { [key: string]: string }; // allows to pass custom mapping for error messages scoped to a node
functionality?: Functionality; functionality?: Functionality;
type?: string;
} }
interface NodeApiErrorOptions extends NodeOperationErrorOptions { interface NodeApiErrorOptions extends NodeOperationErrorOptions {

View file

@ -8,6 +8,8 @@ import { NodeError } from './abstract/node.error';
export class NodeOperationError extends NodeError { export class NodeOperationError extends NodeError {
lineNumber: number | undefined; lineNumber: number | undefined;
type: string | undefined;
constructor( constructor(
node: INode, node: INode,
error: Error | string | JsonObject, error: Error | string | JsonObject,
@ -21,6 +23,7 @@ export class NodeOperationError extends NodeError {
if (options.message) this.message = options.message; if (options.message) this.message = options.message;
if (options.level) this.level = options.level; if (options.level) this.level = options.level;
if (options.functionality) this.functionality = options.functionality; if (options.functionality) this.functionality = options.functionality;
if (options.type) this.type = options.type;
this.description = options.description; this.description = options.description;
this.context.runIndex = options.runIndex; this.context.runIndex = options.runIndex;
this.context.itemIndex = options.itemIndex; this.context.itemIndex = options.itemIndex;