From e1a4fde207e392c372e3876946d6a5740721e253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Mon, 19 Feb 2024 11:50:20 +0100 Subject: [PATCH 1/2] fix(core): Define `SHELL` env variable on docker images (#8670) --- docker/images/n8n-custom/Dockerfile | 1 + docker/images/n8n/Dockerfile | 1 + 2 files changed, 2 insertions(+) diff --git a/docker/images/n8n-custom/Dockerfile b/docker/images/n8n-custom/Dockerfile index 36c4c2302f51d..ed98f852fa1a4 100644 --- a/docker/images/n8n-custom/Dockerfile +++ b/docker/images/n8n-custom/Dockerfile @@ -37,5 +37,6 @@ RUN \ mkdir .n8n && \ chown node:node .n8n +ENV SHELL /bin/sh USER node ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"] diff --git a/docker/images/n8n/Dockerfile b/docker/images/n8n/Dockerfile index 970ec7105b1e8..af7803820c0bb 100644 --- a/docker/images/n8n/Dockerfile +++ b/docker/images/n8n/Dockerfile @@ -21,5 +21,6 @@ COPY docker-entrypoint.sh / RUN \ mkdir .n8n && \ chown node:node .n8n +ENV SHELL /bin/sh USER node ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"] From 40c7f77a35ef4e9bd4dbd9f28886b1b9e6af416f Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Mon, 19 Feb 2024 13:02:20 +0100 Subject: [PATCH 2/2] fix(editor): Fix node runData and pinned data check on manual run (#8669) --- cypress/e2e/19-execution.cy.ts | 55 ++- ...anual_node_run_for_pinned_and_rundata.json | 318 ++++++++++++++++++ packages/editor-ui/src/mixins/workflowRun.ts | 10 +- packages/editor-ui/src/utils/typesUtils.ts | 5 +- 4 files changed, 378 insertions(+), 10 deletions(-) create mode 100644 cypress/fixtures/Check_manual_node_run_for_pinned_and_rundata.json diff --git a/cypress/e2e/19-execution.cy.ts b/cypress/e2e/19-execution.cy.ts index a24d8894bf44d..cc8a55e89ceb8 100644 --- a/cypress/e2e/19-execution.cy.ts +++ b/cypress/e2e/19-execution.cy.ts @@ -490,7 +490,7 @@ describe('Execution', () => { }); }); - it.only('should send proper payload for node rerun', () => { + it('should send proper payload for node rerun', () => { cy.createFixtureWorkflow( 'Multiple_trigger_node_rerun.json', `Multiple trigger node rerun ${uuid()}`, @@ -516,4 +516,57 @@ describe('Execution', () => { expect(interception.request.body.runData).to.include.all.keys(expectedKeys); }); }); + + it('should send proper payload for manual node run', () => { + cy.createFixtureWorkflow( + 'Check_manual_node_run_for_pinned_and_rundata.json', + `Check manual node run for pinned and rundata ${uuid()}`, + ); + + workflowPage.getters.zoomToFitButton().click(); + + cy.intercept('POST', '/rest/workflows/run').as('workflowRun'); + + workflowPage.getters + .canvasNodeByName('If') + .findChildByTestId('execute-node-button') + .click({ force: true }); + + cy.wait('@workflowRun').then((interception) => { + expect(interception.request.body).not.to.have.property('runData').that.is.an('object'); + expect(interception.request.body).to.have.property('pinData').that.is.an('object'); + const expectedPinnedDataKeys = ['Webhook']; + + expect(Object.keys(interception.request.body.pinData)).to.have.lengthOf( + expectedPinnedDataKeys.length, + ); + expect(interception.request.body.pinData).to.include.all.keys(expectedPinnedDataKeys); + }); + + workflowPage.getters.clearExecutionDataButton().should('be.visible'); + + cy.intercept('POST', '/rest/workflows/run').as('workflowRun'); + + workflowPage.getters + .canvasNodeByName('NoOp2') + .findChildByTestId('execute-node-button') + .click({ force: true }); + + cy.wait('@workflowRun').then((interception) => { + expect(interception.request.body).to.have.property('runData').that.is.an('object'); + expect(interception.request.body).to.have.property('pinData').that.is.an('object'); + const expectedPinnedDataKeys = ['Webhook']; + const expectedRunDataKeys = ['If', 'Webhook']; + + expect(Object.keys(interception.request.body.pinData)).to.have.lengthOf( + expectedPinnedDataKeys.length, + ); + expect(interception.request.body.pinData).to.include.all.keys(expectedPinnedDataKeys); + + expect(Object.keys(interception.request.body.runData)).to.have.lengthOf( + expectedRunDataKeys.length, + ); + expect(interception.request.body.runData).to.include.all.keys(expectedRunDataKeys); + }); + }); }); diff --git a/cypress/fixtures/Check_manual_node_run_for_pinned_and_rundata.json b/cypress/fixtures/Check_manual_node_run_for_pinned_and_rundata.json new file mode 100644 index 0000000000000..0236f38811bd8 --- /dev/null +++ b/cypress/fixtures/Check_manual_node_run_for_pinned_and_rundata.json @@ -0,0 +1,318 @@ +{ + "name": "Webhook PairedItem error test", + "nodes": [ + { + "parameters": { + "path": "86f05bcc-44a4-44f7-b774-7002fc2eddfc", + "options": {} + }, + "id": "143572ab-f85b-4a6f-8ca7-4a5cea00a9fe", + "name": "Webhook", + "type": "n8n-nodes-base.webhook", + "typeVersion": 1.1, + "position": [ + 860, + 140 + ], + "webhookId": "86f05bcc-44a4-44f7-b774-7002fc2eddfc" + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict" + }, + "conditions": [ + { + "id": "e9605092-a127-46ad-9fb3-e671f955f856", + "leftValue": "={{ $json.headers.host }}", + "rightValue": "asdf", + "operator": { + "type": "string", + "operation": "equals", + "name": "filter.operator.equals" + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "id": "50542b55-4237-4c18-9e3a-5d146372c270", + "name": "If", + "type": "n8n-nodes-base.if", + "typeVersion": 2, + "position": [ + 1080, + 140 + ] + }, + { + "parameters": {}, + "id": "c6365289-0383-4d73-bd5f-a52b6a6e1eeb", + "name": "No Operation, do nothing", + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [ + 1420, + 20 + ] + }, + { + "parameters": { + "assignments": { + "assignments": [ + { + "id": "1d65053c-31de-43e8-a870-e7e79d34ca67", + "name": "asdf", + "value": "={{ $('Webhook').item.json.headers['accept-encoding'] }}", + "type": "string" + } + ] + }, + "options": {} + }, + "id": "8fd60b5e-19eb-47f4-a3e2-3822c722a68a", + "name": "Edit Fields1", + "type": "n8n-nodes-base.set", + "typeVersion": 3.3, + "position": [ + 1860, + 220 + ] + }, + { + "parameters": {}, + "id": "96ac4860-81eb-4d47-9a6e-7c717d910fcd", + "name": "NoOp1", + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [ + 1420, + 220 + ] + }, + { + "parameters": {}, + "id": "5b550207-3f4f-4519-b272-ff02d9d28ffc", + "name": "NoOp3", + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [ + 2040, + 220 + ] + }, + { + "parameters": {}, + "id": "9f450e47-902e-413e-99ce-ea93f6bc375e", + "name": "NoOp2", + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [ + 1640, + 220 + ] + }, + { + "parameters": { + "assignments": { + "assignments": [ + { + "id": "1d65053c-31de-43e8-a870-e7e79d34ca67", + "name": "asdf", + "value": "={{ $('Webhook').item.json.headers['accept-encoding'] }}", + "type": "string" + } + ] + }, + "options": {} + }, + "id": "7acd1642-a6ef-4c33-a562-95b19fedbded", + "name": "Edit Fields", + "type": "n8n-nodes-base.set", + "typeVersion": 3.3, + "position": [ + 2220, + 220 + ] + }, + { + "parameters": { + "assignments": { + "assignments": [ + { + "id": "1d65053c-31de-43e8-a870-e7e79d34ca67", + "name": "asdf", + "value": "={{ $('Webhook').item.json.headers['accept-encoding'] }}", + "type": "string" + } + ] + }, + "options": {} + }, + "id": "adc93a3b-2825-4ddf-9a17-fe61b5861d43", + "name": "Edit Fields2", + "type": "n8n-nodes-base.set", + "typeVersion": 3.3, + "position": [ + 1460, + 440 + ] + }, + { + "parameters": {}, + "id": "1cd5d8dc-cd08-4596-9435-d48a6e20996d", + "name": "NoOp", + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [ + 1700, + 480 + ] + }, + { + "parameters": { + "content": "For Error:\n1. Execute \"If\"\n2. Execute \"NoOp2\"\n" + }, + "id": "658c3a31-b640-4338-8b22-6d0a17ab5b80", + "name": "Sticky Note", + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + 940, + 480 + ] + } + ], + "pinData": { + "Webhook": [ + { + "json": { + "headers": { + "host": "localhost:5678", + "user-agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0", + "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "accept-language": "en-US,en;q=0.5", + "accept-encoding": "gzip, deflate, br", + "connection": "keep-alive", + "cookie": "n8n-auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImQ1YWQzODQ1LWFmMTAtNDc2OC04ZDU4LTBmNDE3YTVlNDkxNSIsImVtYWlsIjoiamFuQG44bi5pbyIsInBhc3N3b3JkIjoiMzhjMTFiMDEwMjRlMDhiZTE0ZGExZjhmOWVjNGFmOTQxZmQwOGUyMzJmNWEzYmMyNjBmOTI1ZjIxNTBhNTZlYSIsImlhdCI6MTcwNzA3ODgxMSwiZXhwIjoxNzA3NjgzNjExfQ.bhbh8gCbLYAY87kPqZSGZeMBq7_4d9IgKnsrJ0UV4Iw", + "upgrade-insecure-requests": "1", + "sec-fetch-dest": "document", + "sec-fetch-mode": "navigate", + "sec-fetch-site": "none", + "sec-fetch-user": "?1", + "if-none-match": "W/\"22-6OS7cK0FzqnV2NeDHdOSGS1bVUs\"" + }, + "params": {}, + "query": {}, + "body": {} + } + } + ] + }, + "connections": { + "Webhook": { + "main": [ + [ + { + "node": "If", + "type": "main", + "index": 0 + } + ] + ] + }, + "If": { + "main": [ + [ + { + "node": "No Operation, do nothing", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "NoOp1", + "type": "main", + "index": 0 + }, + { + "node": "Edit Fields2", + "type": "main", + "index": 0 + } + ] + ] + }, + "NoOp1": { + "main": [ + [ + { + "node": "NoOp2", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields1": { + "main": [ + [ + { + "node": "NoOp3", + "type": "main", + "index": 0 + } + ] + ] + }, + "NoOp2": { + "main": [ + [ + { + "node": "Edit Fields1", + "type": "main", + "index": 0 + } + ] + ] + }, + "NoOp3": { + "main": [ + [ + { + "node": "Edit Fields", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields2": { + "main": [ + [ + { + "node": "NoOp", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "versionId": "631547ec-c580-4b5f-9220-7fd3d801029b", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "021d3c82ba2d3bc090cbf4fc81c9312668bcc34297e022bb3438c5c88a43a5ff" + }, + "id": "fjCAcetjbaYM1vy6", + "tags": [] +} \ No newline at end of file diff --git a/packages/editor-ui/src/mixins/workflowRun.ts b/packages/editor-ui/src/mixins/workflowRun.ts index 17015a294e922..b4d8aba54101a 100644 --- a/packages/editor-ui/src/mixins/workflowRun.ts +++ b/packages/editor-ui/src/mixins/workflowRun.ts @@ -30,6 +30,7 @@ import { openPopUpWindow } from '@/utils/executionUtils'; import { useExternalHooks } from '@/composables/useExternalHooks'; import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers'; import { useRouter } from 'vue-router'; +import { isEmpty } from '@/utils/typesUtils'; export const consolidateRunDataAndStartNodes = ( directParentNodes: string[], @@ -54,23 +55,20 @@ export const consolidateRunDataAndStartNodes = ( parentNodes.push(directParentNode); for (const parentNode of parentNodes) { - if ( - (runData[parentNode] === undefined || runData[parentNode].length === 0) && - pinData?.[parentNode].length === 0 - ) { + if (!runData[parentNode]?.length && !pinData?.[parentNode]?.length) { // When we hit a node which has no data we stop and set it // as a start node the execution from and then go on with other // direct input nodes startNodes.push(parentNode); break; } - if (runData[parentNode] !== undefined) { + if (runData[parentNode]) { newRunData[parentNode] = runData[parentNode]?.slice(0, 1); } } } - if (Object.keys(newRunData).length === 0) { + if (isEmpty(newRunData)) { // If there is no data for any of the parent nodes make sure // that run data is empty that it runs regularly newRunData = undefined; diff --git a/packages/editor-ui/src/utils/typesUtils.ts b/packages/editor-ui/src/utils/typesUtils.ts index fca1b7d8be51c..4fdf68b4c6a2c 100644 --- a/packages/editor-ui/src/utils/typesUtils.ts +++ b/packages/editor-ui/src/utils/typesUtils.ts @@ -23,11 +23,10 @@ export function isJsonKeyObject(item: unknown): item is { export const isEmpty = (value?: unknown): boolean => { if (!value && value !== 0) return true; if (Array.isArray(value)) { - if (!value.length) return true; - return value.every(isEmpty); + return !value.length || value.every(isEmpty); } if (typeof value === 'object') { - return Object.values(value).every(isEmpty); + return !Object.keys(value).length || Object.values(value).every(isEmpty); } return false; };