From 4ffa63aae4b7b96871cf7019f19ef4f5b6400d98 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: Thu, 21 Nov 2024 12:09:59 +0100 Subject: [PATCH] temporarily revert back to returning additional payload on executionFinished --- cypress/utils/executions.ts | 19 +++++-- packages/@n8n/api-types/src/push/execution.ts | 6 +- .../src/workflow-execute-additional-data.ts | 15 ++++- .../src/composables/usePushConnection.test.ts | 55 +++++++------------ .../src/composables/usePushConnection.ts | 32 ++++++----- 5 files changed, 71 insertions(+), 56 deletions(-) diff --git a/cypress/utils/executions.ts b/cypress/utils/executions.ts index 12f4d2454a4587..cd14f00757a995 100644 --- a/cypress/utils/executions.ts +++ b/cypress/utils/executions.ts @@ -1,5 +1,6 @@ import { stringify } from 'flatted'; import type { IDataObject, IPinData, ITaskData, ITaskDataConnections } from 'n8n-workflow'; +import { nanoid } from 'nanoid'; import { clickExecuteWorkflowButton } from '../composables/workflow'; @@ -80,6 +81,7 @@ export function runMockWorkflowExecution({ lastNodeExecuted: string; runData: Array>; }) { + const workflowId = nanoid(); const executionId = Math.floor(Math.random() * 1_000_000).toString(); cy.intercept('POST', '/rest/workflows/**/run?**', { @@ -117,17 +119,24 @@ export function runMockWorkflowExecution({ resolvedRunData[nodeName] = nodeExecution[nodeName]; }); + const executionData = createMockWorkflowExecutionData({ + lastNodeExecuted, + runData: resolvedRunData, + }); + cy.intercept('GET', `/rest/executions/${executionId}`, { statusCode: 200, body: { - data: createMockWorkflowExecutionData({ - lastNodeExecuted, - runData: resolvedRunData, - }), + data: executionData, }, }).as('getExecution'); - cy.push('executionFinished', { executionId }); + cy.push('executionFinished', { + executionId, + workflowId, + status: 'success', + rawData: executionData.data, + }); cy.wait('@getExecution'); } diff --git a/packages/@n8n/api-types/src/push/execution.ts b/packages/@n8n/api-types/src/push/execution.ts index 9c723e2817f8aa..7a1c1377d9bb49 100644 --- a/packages/@n8n/api-types/src/push/execution.ts +++ b/packages/@n8n/api-types/src/push/execution.ts @@ -1,4 +1,4 @@ -import type { ITaskData, WorkflowExecuteMode } from 'n8n-workflow'; +import type { ExecutionStatus, ITaskData, WorkflowExecuteMode } from 'n8n-workflow'; type ExecutionStarted = { type: 'executionStarted'; @@ -23,6 +23,10 @@ type ExecutionFinished = { type: 'executionFinished'; data: { executionId: string; + workflowId: string; + status: ExecutionStatus; + /** @deprecated: Please construct execution data in the frontend from the data pushed in previous messages, instead of depending on this additional payload serialization */ + rawData?: string; }; }; diff --git a/packages/cli/src/workflow-execute-additional-data.ts b/packages/cli/src/workflow-execute-additional-data.ts index 97322f4fe02302..57253a9d761676 100644 --- a/packages/cli/src/workflow-execute-additional-data.ts +++ b/packages/cli/src/workflow-execute-additional-data.ts @@ -3,6 +3,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { stringify } from 'flatted'; import type { PushType } from '@n8n/api-types'; import { GlobalConfig } from '@n8n/config'; import { WorkflowExecute } from 'n8n-core'; @@ -318,9 +319,17 @@ function hookFunctionsPush(): IWorkflowExecuteHooks { workflowId, }); - const pushType = - fullRunData.status === 'waiting' ? 'executionWaiting' : 'executionFinished'; - pushInstance.send(pushType, { executionId }, pushRef); + const { status } = fullRunData; + if (status === 'waiting') { + pushInstance.send('executionWaiting', { executionId }, pushRef); + } else { + const rawData = stringify(fullRunData.data); + pushInstance.send( + 'executionFinished', + { executionId, workflowId, status, rawData }, + pushRef, + ); + } }, ], }; diff --git a/packages/editor-ui/src/composables/usePushConnection.test.ts b/packages/editor-ui/src/composables/usePushConnection.test.ts index baccb40887b31c..888c36a45df6e9 100644 --- a/packages/editor-ui/src/composables/usePushConnection.test.ts +++ b/packages/editor-ui/src/composables/usePushConnection.test.ts @@ -2,7 +2,6 @@ import { stringify } from 'flatted'; import { useRouter } from 'vue-router'; import { createPinia, setActivePinia } from 'pinia'; import type { PushMessage, PushPayload } from '@n8n/api-types'; -import { mock } from 'vitest-mock-extended'; import type { ITaskData, WorkflowOperationError } from 'n8n-workflow'; import { usePushConnection } from '@/composables/usePushConnection'; @@ -11,7 +10,6 @@ import { useOrchestrationStore } from '@/stores/orchestration.store'; import { useUIStore } from '@/stores/ui.store'; import { useWorkflowsStore } from '@/stores/workflows.store'; import { useToast } from '@/composables/useToast'; -import type { IExecutionResponse } from '@/Interface'; vi.mock('vue-router', () => { return { @@ -140,10 +138,7 @@ describe('usePushConnection()', () => { describe('executionFinished', () => { const executionId = '1'; - const event: PushMessage = { - type: 'executionFinished', - data: { executionId: '1' }, - }; + const workflowId = 'abc'; beforeEach(() => { workflowsStore.activeExecutionId = executionId; @@ -151,28 +146,23 @@ describe('usePushConnection()', () => { }); it('should handle executionFinished event correctly', async () => { - const spy = vi.spyOn(workflowsStore, 'fetchExecutionDataById').mockResolvedValue( - mock({ - id: executionId, - data: stringify({ + const result = await pushConnection.pushMessageReceived({ + type: 'executionFinished', + data: { + executionId, + workflowId, + status: 'success', + rawData: stringify({ resultData: { runData: {}, }, - }) as unknown as IExecutionResponse['data'], - finished: true, - mode: 'manual', - startedAt: new Date(), - stoppedAt: new Date(), - status: 'success', - }), - ); - - const result = await pushConnection.pushMessageReceived(event); + }), + }, + }); expect(result).toBeTruthy(); expect(workflowsStore.workflowExecutionData).toBeDefined(); expect(uiStore.isActionActive['workflowRunning']).toBeTruthy(); - expect(spy).toHaveBeenCalledWith(executionId); expect(toast.showMessage).toHaveBeenCalledWith({ title: 'Workflow executed successfully', @@ -181,10 +171,13 @@ describe('usePushConnection()', () => { }); it('should handle isManualExecutionCancelled correctly', async () => { - const spy = vi.spyOn(workflowsStore, 'fetchExecutionDataById').mockResolvedValue( - mock({ - id: executionId, - data: stringify({ + const result = await pushConnection.pushMessageReceived({ + type: 'executionFinished', + data: { + executionId, + workflowId, + status: 'error', + rawData: stringify({ startData: {}, resultData: { runData: { @@ -198,14 +191,9 @@ describe('usePushConnection()', () => { node: 'Last Node', } as unknown as WorkflowOperationError, }, - }) as unknown as IExecutionResponse['data'], - mode: 'manual', - startedAt: new Date(), - status: 'running', - }), - ); - - const result = await pushConnection.pushMessageReceived(event); + }), + }, + }); expect(useToast().showMessage).toHaveBeenCalledWith({ message: @@ -219,7 +207,6 @@ describe('usePushConnection()', () => { expect(result).toBeTruthy(); expect(workflowsStore.workflowExecutionData).toBeDefined(); expect(uiStore.isActionActive.workflowRunning).toBeTruthy(); - expect(spy).toHaveBeenCalledWith(executionId); }); }); diff --git a/packages/editor-ui/src/composables/usePushConnection.ts b/packages/editor-ui/src/composables/usePushConnection.ts index 6e9808c36a4365..d5ecdefc71365c 100644 --- a/packages/editor-ui/src/composables/usePushConnection.ts +++ b/packages/editor-ui/src/composables/usePushConnection.ts @@ -35,6 +35,7 @@ import { useTelemetry } from '@/composables/useTelemetry'; import type { PushMessageQueueItem } from '@/types'; import { useAssistantStore } from '@/stores/assistant.store'; import NodeExecutionErrorMessage from '@/components/NodeExecutionErrorMessage.vue'; +import type { IExecutionResponse } from '@/Interface'; export function usePushConnection({ router }: { router: ReturnType }) { const workflowHelpers = useWorkflowHelpers({ router }); @@ -205,11 +206,19 @@ export function usePushConnection({ router }: { router: ReturnType; + if (receivedData.type === 'executionFinished' && receivedData.data.rawData) { + const { workflowId, status, rawData } = receivedData.data; + executionData = { workflowId, data: parse(rawData), status }; + } else { + const execution = await workflowsStore.fetchExecutionDataById(executionId); + if (!execution?.data) return false; + executionData = { + workflowId: execution.workflowId, + data: parse(execution.data as unknown as string), + status: execution.status, + }; + } const iRunExecutionData: IRunExecutionData = { startData: executionData.data?.startData, @@ -265,7 +274,7 @@ export function usePushConnection({ router }: { router: ReturnType