diff --git a/packages/frontend/editor-ui/src/app/components/AiGatewaySelector.test.ts b/packages/frontend/editor-ui/src/app/components/AiGatewaySelector.test.ts index 1c3a988b3da..4921ef86cd3 100644 --- a/packages/frontend/editor-ui/src/app/components/AiGatewaySelector.test.ts +++ b/packages/frontend/editor-ui/src/app/components/AiGatewaySelector.test.ts @@ -41,7 +41,7 @@ describe('AiGatewaySelector', () => { const pinia = createTestingPinia({ stubActions: false }); setActivePinia(pinia); workflowsStore = mockedStore(useWorkflowsStore); - workflowsStore.workflowExecutionData = null; + workflowsStore.setWorkflowExecutionData(null); }); describe('rendering', () => { @@ -162,7 +162,7 @@ describe('AiGatewaySelector', () => { renderComponent({ props: { aiGatewayEnabled: true, readonly: false } }); mockFetchBalance.mockClear(); - workflowsStore.workflowExecutionData = { finished: true } as never; + workflowsStore.setWorkflowExecutionData({ finished: true } as never); await vi.waitFor(() => expect(mockFetchBalance).toHaveBeenCalledOnce()); }); @@ -171,7 +171,7 @@ describe('AiGatewaySelector', () => { renderComponent({ props: { aiGatewayEnabled: true, readonly: false } }); mockFetchBalance.mockClear(); - workflowsStore.workflowExecutionData = { finished: false, stoppedAt: new Date() } as never; + workflowsStore.setWorkflowExecutionData({ finished: false, stoppedAt: new Date() } as never); await vi.waitFor(() => expect(mockFetchBalance).toHaveBeenCalledOnce()); }); @@ -180,7 +180,7 @@ describe('AiGatewaySelector', () => { renderComponent({ props: { aiGatewayEnabled: true, readonly: false } }); mockFetchBalance.mockClear(); - workflowsStore.workflowExecutionData = { finished: false, stoppedAt: undefined } as never; + workflowsStore.setWorkflowExecutionData({ finished: false, stoppedAt: undefined } as never); await new Promise((r) => setTimeout(r, 10)); expect(mockFetchBalance).not.toHaveBeenCalled(); @@ -190,17 +190,17 @@ describe('AiGatewaySelector', () => { renderComponent({ props: { aiGatewayEnabled: true, readonly: false } }); mockFetchBalance.mockClear(); - workflowsStore.workflowExecutionData = { finished: true } as never; + workflowsStore.setWorkflowExecutionData({ finished: true } as never); await vi.waitFor(() => expect(mockFetchBalance).toHaveBeenCalledTimes(1)); - workflowsStore.workflowExecutionData = { finished: true } as never; + workflowsStore.setWorkflowExecutionData({ finished: true } as never); await vi.waitFor(() => expect(mockFetchBalance).toHaveBeenCalledTimes(2)); }); it('should not call fetchWallet when execution finishes but gateway is disabled', async () => { renderComponent({ props: { aiGatewayEnabled: false, readonly: false } }); - workflowsStore.workflowExecutionData = { finished: true } as never; + workflowsStore.setWorkflowExecutionData({ finished: true } as never); await new Promise((r) => setTimeout(r, 10)); expect(mockFetchBalance).not.toHaveBeenCalled(); diff --git a/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.test.ts b/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.test.ts index deef1b12e6b..2d40a61cc8a 100644 --- a/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.test.ts +++ b/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.test.ts @@ -3923,6 +3923,12 @@ describe('useCanvasOperations', () => { workflowsStore.removeTestWebhook = vi.fn(); workflowsStore.resetWorkflow = vi.fn(); workflowsStore.resetState = vi.fn(); + workflowsStore.clearCurrentWorkflowExecutions = vi.fn(() => { + workflowsStore.currentWorkflowExecutions = []; + }); + workflowsStore.setLastSuccessfulExecution = vi.fn((value) => { + workflowsStore.lastSuccessfulExecution = value; + }); const setActiveExecutionId = vi.spyOn(workflowState, 'setActiveExecutionId'); uiStore.resetLastInteractedWith = vi.fn(); executionsStore.activeExecution = null; diff --git a/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.ts b/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.ts index c7d3d6bfe8b..3272e7a4662 100644 --- a/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.ts +++ b/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.ts @@ -2316,9 +2316,9 @@ export function useCanvasOperations() { // Reset editable workflow state workflowsStore.resetWorkflow(); workflowState.resetState(); - workflowsStore.currentWorkflowExecutions = []; + workflowsStore.clearCurrentWorkflowExecutions(); workflowState.setActiveExecutionId(undefined); - workflowsStore.lastSuccessfulExecution = null; + workflowsStore.setLastSuccessfulExecution(null); // Reset actions uiStore.resetLastInteractedWith(); @@ -3157,7 +3157,7 @@ export function useCanvasOperations() { canvasStore.startLoading(); canvasStore.setLoadingText(i18n.baseText('nodeView.loadingTemplate')); - workflowsStore.currentWorkflowExecutions = []; + workflowsStore.clearCurrentWorkflowExecutions(); executionsStore.activeExecution = null; let data: IWorkflowTemplate | undefined; @@ -3220,7 +3220,7 @@ export function useCanvasOperations() { canvasStore.startLoading(); canvasStore.setLoadingText(i18n.baseText('nodeView.loadingTemplate')); - workflowsStore.currentWorkflowExecutions = []; + workflowsStore.clearCurrentWorkflowExecutions(); executionsStore.activeExecution = null; uiStore.isBlankRedirect = true; diff --git a/packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/executionStarted.test.ts b/packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/executionStarted.test.ts index 725b51fd541..b1b075ce08a 100644 --- a/packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/executionStarted.test.ts +++ b/packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/executionStarted.test.ts @@ -45,7 +45,7 @@ describe('executionStarted', () => { it('should accept execution when activeExecutionId is null and populate workflowData from store', async () => { workflowsStore.activeExecutionId = null; - workflowsStore.workflowExecutionData = null; + workflowsStore.setWorkflowExecutionData(null); workflowsStore.workflow.id = 'wf-123'; const workflowDocumentStore = useWorkflowDocumentStore(createWorkflowDocumentId('wf-123')); workflowDocumentStore.setName('My Workflow'); @@ -64,10 +64,10 @@ describe('executionStarted', () => { it('should not reinitialize when same execution ID arrives', async () => { workflowsStore.activeExecutionId = 'exec-1'; - workflowsStore.workflowExecutionData = { + workflowsStore.setWorkflowExecutionData({ id: 'exec-1', data: { resultData: { runData: {} } }, - } as never; + } as never); await executionStarted(makeEvent('exec-1'), mockOptions); @@ -97,10 +97,10 @@ describe('executionStarted', () => { it('should accept execution when activeExecutionId is undefined in iframe (post-executionFinished)', async () => { workflowsStore.activeExecutionId = undefined; - workflowsStore.workflowExecutionData = { + workflowsStore.setWorkflowExecutionData({ id: 'old-exec', data: { resultData: { runData: { Node1: [{ executionTime: 100 }] } } }, - } as never; + } as never); await executionStarted(makeEvent('exec-2'), mockOptions); @@ -112,10 +112,10 @@ describe('executionStarted', () => { it('should accept new execution and reset state when re-executing in iframe', async () => { workflowsStore.activeExecutionId = 'exec-1'; - workflowsStore.workflowExecutionData = { + workflowsStore.setWorkflowExecutionData({ id: 'exec-1', data: { resultData: { runData: { Node1: [{ executionTime: 100 }] } } }, - } as never; + } as never); await executionStarted(makeEvent('exec-2'), mockOptions); @@ -127,10 +127,10 @@ describe('executionStarted', () => { it('should not reset when same execution ID arrives in iframe', async () => { workflowsStore.activeExecutionId = 'exec-1'; - workflowsStore.workflowExecutionData = { + workflowsStore.setWorkflowExecutionData({ id: 'exec-1', data: { resultData: { runData: {} } }, - } as never; + } as never); await executionStarted(makeEvent('exec-1'), mockOptions); diff --git a/packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/testWebhookDeleted.ts b/packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/testWebhookDeleted.ts index 5241a054434..5da3284dec5 100644 --- a/packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/testWebhookDeleted.ts +++ b/packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/testWebhookDeleted.ts @@ -12,7 +12,7 @@ export async function testWebhookDeleted( const workflowsStore = useWorkflowsStore(); if (data.workflowId === workflowsStore.workflowId) { - workflowsStore.executionWaitingForWebhook = false; + workflowsStore.setExecutionWaitingForWebhook(false); options.workflowState.setActiveExecutionId(undefined); } } diff --git a/packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/testWebhookReceived.ts b/packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/testWebhookReceived.ts index 2ce4664b040..19c538897c3 100644 --- a/packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/testWebhookReceived.ts +++ b/packages/frontend/editor-ui/src/app/composables/usePushConnection/handlers/testWebhookReceived.ts @@ -12,7 +12,7 @@ export async function testWebhookReceived( const workflowsStore = useWorkflowsStore(); if (data.workflowId === workflowsStore.workflowId) { - workflowsStore.executionWaitingForWebhook = false; + workflowsStore.setExecutionWaitingForWebhook(false); options.workflowState.setActiveExecutionId(data.executionId ?? null); } } diff --git a/packages/frontend/editor-ui/src/app/composables/useRunWorkflow.test.ts b/packages/frontend/editor-ui/src/app/composables/useRunWorkflow.test.ts index f8172153271..17ab551a3a3 100644 --- a/packages/frontend/editor-ui/src/app/composables/useRunWorkflow.test.ts +++ b/packages/frontend/editor-ui/src/app/composables/useRunWorkflow.test.ts @@ -100,6 +100,7 @@ vi.mock('@/app/stores/workflows.store', () => { activeExecutionId: undefined, previousExecutionId: undefined, executionWaitingForWebhook: false, + chatPartialExecutionDestinationNode: null, workflow: { nodes: [], id: '', @@ -117,6 +118,16 @@ vi.mock('@/app/stores/workflows.store', () => { '123': true, }, getExecution: vi.fn(), + setWorkflowExecutionData: vi.fn((execution) => { + storeState.workflowExecutionData = execution; + }), + setExecutionWaitingForWebhook: vi.fn((value) => { + storeState.executionWaitingForWebhook = value; + }), + setChatPartialExecutionDestinationNode: vi.fn((value) => { + storeState.chatPartialExecutionDestinationNode = value; + }), + clearExecutionStartedData: vi.fn(), private: { setActiveExecutionId: vi.fn((id: string | null | undefined) => { storeState.activeExecutionId = id; @@ -1231,7 +1242,7 @@ describe('useRunWorkflow({ router })', () => { 'test-wf-id', ); workflowState.setActiveExecutionId('test-exec-id'); - workflowsStore.executionWaitingForWebhook = false; + workflowsStore.setExecutionWaitingForWebhook(false); getExecutionSpy.mockResolvedValue(executionData); diff --git a/packages/frontend/editor-ui/src/app/composables/useRunWorkflow.ts b/packages/frontend/editor-ui/src/app/composables/useRunWorkflow.ts index b5ad667229c..f43673787a9 100644 --- a/packages/frontend/editor-ui/src/app/composables/useRunWorkflow.ts +++ b/packages/frontend/editor-ui/src/app/composables/useRunWorkflow.ts @@ -134,7 +134,7 @@ export function useRunWorkflow(useRunWorkflowOpts: { } if (response.waitingForWebhook === true) { - workflowsStore.executionWaitingForWebhook = true; + workflowsStore.setExecutionWaitingForWebhook(true); } return response; @@ -250,7 +250,7 @@ export function useRunWorkflow(useRunWorkflowOpts: { // If the chat node has no input data or pin data, open the chat modal // and halt the execution if (!chatHasInputData && !chatHasPinData) { - workflowsStore.chatPartialExecutionDestinationNode = options.destinationNode.nodeName; + workflowsStore.setChatPartialExecutionDestinationNode(options.destinationNode.nodeName); startChat(); return; } diff --git a/packages/frontend/editor-ui/src/app/composables/useWorkflowInitialization.ts b/packages/frontend/editor-ui/src/app/composables/useWorkflowInitialization.ts index 072518ebbb5..1710a163a7f 100644 --- a/packages/frontend/editor-ui/src/app/composables/useWorkflowInitialization.ts +++ b/packages/frontend/editor-ui/src/app/composables/useWorkflowInitialization.ts @@ -184,7 +184,7 @@ export function useWorkflowInitialization(workflowState: WorkflowState) { const executionId = route.params.executionId; if (typeof executionId === 'string') { await applyExecutionData(executionId); - workflowsStore.isInDebugMode = true; + workflowsStore.setIsInDebugMode(true); } } } diff --git a/packages/frontend/editor-ui/src/app/composables/useWorkflowState.test.ts b/packages/frontend/editor-ui/src/app/composables/useWorkflowState.test.ts index d3e7fd46b98..fe37961154d 100644 --- a/packages/frontend/editor-ui/src/app/composables/useWorkflowState.test.ts +++ b/packages/frontend/editor-ui/src/app/composables/useWorkflowState.test.ts @@ -16,26 +16,28 @@ describe('useWorkflowState', () => { describe('markExecutionAsStopped', () => { beforeEach(() => { - workflowsStore.workflowExecutionData = createTestWorkflowExecutionResponse({ - status: 'running', - startedAt: new Date('2023-01-01T09:00:00Z'), - stoppedAt: undefined, - data: createRunExecutionData({ - resultData: { - runData: { - node1: [ - createTestTaskData({ executionStatus: 'success' }), - createTestTaskData({ executionStatus: 'error' }), - createTestTaskData({ executionStatus: 'running' }), - ], - node2: [ - createTestTaskData({ executionStatus: 'success' }), - createTestTaskData({ executionStatus: 'waiting' }), - ], + workflowsStore.setWorkflowExecutionData( + createTestWorkflowExecutionResponse({ + status: 'running', + startedAt: new Date('2023-01-01T09:00:00Z'), + stoppedAt: undefined, + data: createRunExecutionData({ + resultData: { + runData: { + node1: [ + createTestTaskData({ executionStatus: 'success' }), + createTestTaskData({ executionStatus: 'error' }), + createTestTaskData({ executionStatus: 'running' }), + ], + node2: [ + createTestTaskData({ executionStatus: 'success' }), + createTestTaskData({ executionStatus: 'waiting' }), + ], + }, }, - }, + }), }), - }); + ); }); it('should remove non successful node runs', () => { diff --git a/packages/frontend/editor-ui/src/app/composables/useWorkflowState.ts b/packages/frontend/editor-ui/src/app/composables/useWorkflowState.ts index 040c466d0d8..1230228f0b5 100644 --- a/packages/frontend/editor-ui/src/app/composables/useWorkflowState.ts +++ b/packages/frontend/editor-ui/src/app/composables/useWorkflowState.ts @@ -7,7 +7,6 @@ import { import { DEFAULT_SETTINGS } from '@/app/stores/workflowDocument/useWorkflowDocumentSettings'; import { useWorkflowsStore } from '@/app/stores/workflows.store'; import { useWorkflowStateStore } from '@/app/stores/workflowState.store'; -import { getPairedItemsMapping } from '@/app/utils/pairedItemUtils'; import { isEmpty } from '@/app/utils/typesUtils'; import { useBuilderStore } from '@/features/ai/assistant/builder.store'; import type { @@ -31,15 +30,7 @@ export function useWorkflowState() { //// function setWorkflowExecutionData(workflowResultData: IExecutionResponse | null) { - if (workflowResultData?.data?.waitTill) { - delete workflowResultData.data.resultData.runData[ - workflowResultData.data.resultData.lastNodeExecuted as string - ]; - } - ws.workflowExecutionData = workflowResultData; - ws.workflowExecutionPairedItemMappings = getPairedItemsMapping(workflowResultData); - ws.workflowExecutionResultDataLastUpdate = Date.now(); - ws.workflowExecutionStartedData = undefined; + ws.setWorkflowExecutionData(workflowResultData); } function setActiveExecutionId(id: string | null | undefined) { @@ -83,10 +74,10 @@ export function useWorkflowState() { function markExecutionAsStopped(stopData?: IExecutionsStopData) { setActiveExecutionId(undefined); workflowStateStore.executingNode.clearNodeExecutionQueue(); - ws.executionWaitingForWebhook = false; + ws.setExecutionWaitingForWebhook(false); const workflowDocumentStore = useWorkflowDocumentStore(createWorkflowDocumentId(ws.workflowId)); documentTitle.setDocumentTitle(workflowDocumentStore.name, 'IDLE'); - ws.workflowExecutionStartedData = undefined; + ws.clearExecutionStartedData(); // TODO(ckolb): confirm this works across files? clearPopupWindowState(); @@ -115,7 +106,7 @@ export function useWorkflowState() { setActiveExecutionId(undefined); workflowStateStore.executingNode.executingNode.length = 0; - ws.executionWaitingForWebhook = false; + ws.setExecutionWaitingForWebhook(false); useBuilderStore().resetManualExecutionStats(); } diff --git a/packages/frontend/editor-ui/src/app/stores/workflows.store.test.ts b/packages/frontend/editor-ui/src/app/stores/workflows.store.test.ts index b3cbc4a168a..4474efd28fe 100644 --- a/packages/frontend/editor-ui/src/app/stores/workflows.store.test.ts +++ b/packages/frontend/editor-ui/src/app/stores/workflows.store.test.ts @@ -219,14 +219,14 @@ describe('useWorkflowsStore', () => { describe('getWorkflowRunData', () => { it('should return null when no execution data is present', () => { - workflowsStore.workflowExecutionData = null; + workflowsStore.setWorkflowExecutionData(null); const runData = workflowsStore.getWorkflowRunData; expect(runData).toBeNull(); }); it('should return null when execution data does not contain resultData', () => { - workflowsStore.workflowExecutionData = { data: {} } as IExecutionResponse; + workflowsStore.setWorkflowExecutionData({ data: {} } as IExecutionResponse); const runData = workflowsStore.getWorkflowRunData; expect(runData).toBeNull(); @@ -234,9 +234,9 @@ describe('useWorkflowsStore', () => { it('should return runData when execution data contains resultData', () => { const expectedRunData = { node1: [{}, {}], node2: [{}] }; - workflowsStore.workflowExecutionData = { + workflowsStore.setWorkflowExecutionData({ data: { resultData: { runData: expectedRunData } }, - } as unknown as IExecutionResponse; + } as unknown as IExecutionResponse); const runData = workflowsStore.getWorkflowRunData; expect(runData).toEqual(expectedRunData); @@ -245,16 +245,16 @@ describe('useWorkflowsStore', () => { describe('getWorkflowResultDataByNodeName()', () => { it('should return null when no workflow run data is present', () => { - workflowsStore.workflowExecutionData = null; + workflowsStore.setWorkflowExecutionData(null); const resultData = workflowsStore.getWorkflowResultDataByNodeName('Node1'); expect(resultData).toBeNull(); }); it('should return null when node name is not present in workflow run data', () => { - workflowsStore.workflowExecutionData = { + workflowsStore.setWorkflowExecutionData({ data: { resultData: { runData: {} } }, - } as unknown as IExecutionResponse; + } as unknown as IExecutionResponse); const resultData = workflowsStore.getWorkflowResultDataByNodeName('Node1'); expect(resultData).toBeNull(); @@ -262,9 +262,9 @@ describe('useWorkflowsStore', () => { it('should return result data when node name is present in workflow run data', () => { const expectedData = [{}, {}]; - workflowsStore.workflowExecutionData = { + workflowsStore.setWorkflowExecutionData({ data: { resultData: { runData: { Node1: expectedData } } }, - } as unknown as IExecutionResponse; + } as unknown as IExecutionResponse); const resultData = workflowsStore.getWorkflowResultDataByNodeName('Node1'); expect(resultData).toEqual(expectedData); @@ -601,32 +601,34 @@ describe('useWorkflowsStore', () => { describe('updateNodeExecutionRunData', () => { beforeEach(() => { - workflowsStore.workflowExecutionData = createTestWorkflowExecutionResponse({ - id: 'test-execution', - data: createRunExecutionData({ - resultData: { - runData: { - n0: [ - createTestTaskData({ - executionIndex: 0, - executionStatus: 'success', - executionTime: 33, - }), - createTestTaskData({ - executionIndex: 1, - executionStatus: 'success', - executionTime: 44, - }), - createTestTaskData({ - executionIndex: 2, - executionStatus: 'running', - executionTime: undefined, - }), - ], + workflowsStore.setWorkflowExecutionData( + createTestWorkflowExecutionResponse({ + id: 'test-execution', + data: createRunExecutionData({ + resultData: { + runData: { + n0: [ + createTestTaskData({ + executionIndex: 0, + executionStatus: 'success', + executionTime: 33, + }), + createTestTaskData({ + executionIndex: 1, + executionStatus: 'success', + executionTime: 44, + }), + createTestTaskData({ + executionIndex: 2, + executionStatus: 'running', + executionTime: undefined, + }), + ], + }, }, - }, + }), }), - }); + ); }); it('should replace run data at the matched index in the execution data', () => { @@ -1176,7 +1178,7 @@ describe('useWorkflowsStore', () => { const nodeName = 'Rename me'; const newName = 'Renamed'; - workflowsStore.workflowExecutionData = { + workflowsStore.setWorkflowExecutionData({ data: { resultData: { runData: { @@ -1255,7 +1257,7 @@ describe('useWorkflowsStore', () => { lastNodeExecuted: 'Edit Fields', }, }, - } as unknown as IExecutionResponse; + } as unknown as IExecutionResponse); workflowsStore.workflow.id = 'test-workflow-id'; @@ -1873,7 +1875,7 @@ describe('useWorkflowsStore', () => { workflowsStore.workflow.nodes = [createTestNode({ name: nodeName, type: WAIT_NODE_TYPE })]; // Initialize execution data directly - workflowsStore.workflowExecutionData = { + workflowsStore.setWorkflowExecutionData({ id: executionId, workflowData: createTestWorkflow(), finished: false, @@ -1882,7 +1884,7 @@ describe('useWorkflowsStore', () => { createdAt: new Date(), status: 'running', data: createEmptyRunExecutionData(), - } as IExecutionResponse; + } as IExecutionResponse); // Call updateNodeExecutionStatus with waiting status and metadata.resumeFormUrl workflowsStore.updateNodeExecutionStatus({ @@ -1914,7 +1916,7 @@ describe('useWorkflowsStore', () => { workflowsStore.workflow.nodes = [createTestNode({ name: nodeName, type: WAIT_NODE_TYPE })]; // Initialize execution data directly - workflowsStore.workflowExecutionData = { + workflowsStore.setWorkflowExecutionData({ id: executionId, workflowData: createTestWorkflow(), finished: false, @@ -1923,7 +1925,7 @@ describe('useWorkflowsStore', () => { createdAt: new Date(), status: 'running', data: createEmptyRunExecutionData(), - } as IExecutionResponse; + } as IExecutionResponse); // Call updateNodeExecutionStatus with waiting status but NO metadata.resumeFormUrl workflowsStore.updateNodeExecutionStatus({ diff --git a/packages/frontend/editor-ui/src/app/stores/workflows.store.ts b/packages/frontend/editor-ui/src/app/stores/workflows.store.ts index f550e541d6e..8f34639ffc1 100644 --- a/packages/frontend/editor-ui/src/app/stores/workflows.store.ts +++ b/packages/frontend/editor-ui/src/app/stores/workflows.store.ts @@ -64,6 +64,7 @@ import { createWorkflowDocumentId, } from '@/app/stores/workflowDocument.store'; import { DEFAULT_SETTINGS } from '@/app/stores/workflowDocument/useWorkflowDocumentSettings'; +import { getPairedItemsMapping } from '@/app/utils/pairedItemUtils'; const createEmptyWorkflow = (): IWorkflowDb => ({ id: '', @@ -524,6 +525,46 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => { } } + function setWorkflowExecutionData(execution: IExecutionResponse | null): void { + if (execution?.data?.waitTill) { + delete execution.data.resultData.runData[ + execution.data.resultData.lastNodeExecuted as string + ]; + } + workflowExecutionData.value = execution; + workflowExecutionPairedItemMappings.value = getPairedItemsMapping(execution); + workflowExecutionResultDataLastUpdate.value = Date.now(); + workflowExecutionStartedData.value = undefined; + } + + function clearExecutionStartedData(): void { + workflowExecutionStartedData.value = undefined; + } + + function setExecutionWaitingForWebhook(value: boolean): void { + executionWaitingForWebhook.value = value; + } + + function setIsInDebugMode(value: boolean): void { + isInDebugMode.value = value; + } + + function setChatPartialExecutionDestinationNode(value: string | null): void { + chatPartialExecutionDestinationNode.value = value; + } + + function setLastSuccessfulExecution(execution: IExecutionResponse | null): void { + lastSuccessfulExecution.value = execution; + } + + function clearCurrentWorkflowExecutions(): void { + currentWorkflowExecutions.value = []; + } + + function setCurrentWorkflowExecutions(executions: ExecutionSummary[]): void { + currentWorkflowExecutions.value = executions; + } + function renameNodeSelectedAndExecution(nameData: { old: string; new: string }): void { uiStore.markStateDirty(); @@ -1090,6 +1131,14 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => { setWorkflowInactive, getDuplicateCurrentWorkflowName, setWorkflowExecutionRunData, + setWorkflowExecutionData, + clearExecutionStartedData, + setExecutionWaitingForWebhook, + setIsInDebugMode, + setChatPartialExecutionDestinationNode, + setLastSuccessfulExecution, + clearCurrentWorkflowExecutions, + setCurrentWorkflowExecutions, renameNodeSelectedAndExecution, updateNodeExecutionRunData, updateNodeExecutionStatus, diff --git a/packages/frontend/editor-ui/src/features/ai/assistant/builder.store.test.ts b/packages/frontend/editor-ui/src/features/ai/assistant/builder.store.test.ts index 018649c0533..5a2ebaceaf5 100644 --- a/packages/frontend/editor-ui/src/features/ai/assistant/builder.store.test.ts +++ b/packages/frontend/editor-ui/src/features/ai/assistant/builder.store.test.ts @@ -177,7 +177,7 @@ describe('AI Builder store', () => { workflowsStore.workflow.nodes = []; workflowsStore.workflow.connections = {}; workflowsStore.nodesByName = {}; - workflowsStore.workflowExecutionData = null; + workflowsStore.setWorkflowExecutionData(null); workflowState = useWorkflowState(); vi.mocked(injectWorkflowState).mockReturnValue(workflowState); diff --git a/packages/frontend/editor-ui/src/features/ai/chatHub/chat.store.ts b/packages/frontend/editor-ui/src/features/ai/chatHub/chat.store.ts index f602ea8d5cf..82b7cb48304 100644 --- a/packages/frontend/editor-ui/src/features/ai/chatHub/chat.store.ts +++ b/packages/frontend/editor-ui/src/features/ai/chatHub/chat.store.ts @@ -567,7 +567,7 @@ export const useChatStore = defineStore(STORES.CHAT_HUB, () => { createWorkflowDocumentId(workflowsStore.workflowId), ); - workflowsStore.workflowExecutionData = { + workflowsStore.setWorkflowExecutionData({ id: IN_PROGRESS_EXECUTION_ID, finished: false, mode: 'manual', @@ -580,7 +580,7 @@ export const useChatStore = defineStore(STORES.CHAT_HUB, () => { resultData: { runData: {} }, }), workflowData: workflowDocumentStore.getSnapshot(), - }; + }); // Signal canvas that an execution is pending (null = waiting for execution ID) workflowsStore.private.setActiveExecutionId(null); diff --git a/packages/frontend/editor-ui/src/features/execution/executions/composables/useExecutionDebugging.ts b/packages/frontend/editor-ui/src/features/execution/executions/composables/useExecutionDebugging.ts index 9d068dee7e6..d1dafcf77a1 100644 --- a/packages/frontend/editor-ui/src/features/execution/executions/composables/useExecutionDebugging.ts +++ b/packages/frontend/editor-ui/src/features/execution/executions/composables/useExecutionDebugging.ts @@ -175,7 +175,7 @@ export const useExecutionDebugging = (providedWorkflowState?: WorkflowState) => event.stopPropagation(); return; } - workflowsStore.isInDebugMode = false; + workflowsStore.setIsInDebugMode(false); }; return { diff --git a/packages/frontend/editor-ui/src/features/execution/logs/composables/useChatState.test.ts b/packages/frontend/editor-ui/src/features/execution/logs/composables/useChatState.test.ts index 4cb3dea47fc..04287b0fb5f 100644 --- a/packages/frontend/editor-ui/src/features/execution/logs/composables/useChatState.test.ts +++ b/packages/frontend/editor-ui/src/features/execution/logs/composables/useChatState.test.ts @@ -396,7 +396,7 @@ describe('useChatState', () => { }); it('should include destinationNode when set in workflowsStore', async () => { - workflowsStore.chatPartialExecutionDestinationNode = 'DestinationNode'; + workflowsStore.setChatPartialExecutionDestinationNode('DestinationNode'); const chatState = useChatState(false); await chatState.registerChatWebhook(); @@ -523,7 +523,7 @@ describe('useChatState', () => { }); it('should clear partial execution destination node', () => { - workflowsStore.chatPartialExecutionDestinationNode = 'SomeNode'; + workflowsStore.setChatPartialExecutionDestinationNode('SomeNode'); const chatState = useChatState(false); chatState.refreshSession(); diff --git a/packages/frontend/editor-ui/src/features/execution/logs/composables/useChatState.ts b/packages/frontend/editor-ui/src/features/execution/logs/composables/useChatState.ts index 404d92de10d..3faf8bbb3f3 100644 --- a/packages/frontend/editor-ui/src/features/execution/logs/composables/useChatState.ts +++ b/packages/frontend/editor-ui/src/features/execution/logs/composables/useChatState.ts @@ -217,7 +217,7 @@ export function useChatState( mode: 'inclusive', }; // Clear after use so subsequent messages run full workflow - workflowsStore.chatPartialExecutionDestinationNode = null; + workflowsStore.setChatPartialExecutionDestinationNode(null); } const response = await runWorkflow(runWorkflowOptions); @@ -337,7 +337,7 @@ export function useChatState( logsStore.resetChatSessionId(); logsStore.resetMessages(); // Clear partial execution destination to allow full workflow execution - workflowsStore.chatPartialExecutionDestinationNode = null; + workflowsStore.setChatPartialExecutionDestinationNode(null); if (logsStore.isOpen) { chatEventBus.emit('focusInput'); diff --git a/packages/frontend/editor-ui/src/features/execution/logs/composables/useLogsExecutionData.ts b/packages/frontend/editor-ui/src/features/execution/logs/composables/useLogsExecutionData.ts index 03008850cd5..548565e2eb8 100644 --- a/packages/frontend/editor-ui/src/features/execution/logs/composables/useLogsExecutionData.ts +++ b/packages/frontend/editor-ui/src/features/execution/logs/composables/useLogsExecutionData.ts @@ -111,7 +111,7 @@ export function useLogsExecutionData({ isEnabled, filter }: UseLogsExecutionData workflowState.setWorkflowExecutionData(null); nodeHelpers.updateNodesExecutionIssues(); // Clear partial execution destination to allow full workflow execution - workflowsStore.chatPartialExecutionDestinationNode = null; + workflowsStore.setChatPartialExecutionDestinationNode(null); void workflowsStore.fetchLastSuccessfulExecution(); } diff --git a/packages/frontend/editor-ui/src/features/ndv/panel/components/TriggerPanel.test.ts b/packages/frontend/editor-ui/src/features/ndv/panel/components/TriggerPanel.test.ts index 3d7d4b6612c..e94b533e081 100644 --- a/packages/frontend/editor-ui/src/features/ndv/panel/components/TriggerPanel.test.ts +++ b/packages/frontend/editor-ui/src/features/ndv/panel/components/TriggerPanel.test.ts @@ -65,7 +65,7 @@ describe('TriggerPanel.vue', () => { }); it('renders listening state for webhook node', () => { - workflowsStore.executionWaitingForWebhook = true; + workflowsStore.setExecutionWaitingForWebhook(true); workflowsStore.executedNode = 'Webhook'; const { getByTestId } = renderComponent(TriggerPanel, { props: { nodeName: 'Webhook' }, @@ -79,7 +79,7 @@ describe('TriggerPanel.vue', () => { }); it('does not render listening state for other nodes', () => { - workflowsStore.executionWaitingForWebhook = true; + workflowsStore.setExecutionWaitingForWebhook(true); workflowsStore.executedNode = 'OtherNode'; const { queryByTestId } = renderComponent(TriggerPanel, { props: { nodeName: 'Webhook' }, @@ -93,7 +93,7 @@ describe('TriggerPanel.vue', () => { }); it('renders listening state when executedNode is a child of the current node', () => { - workflowsStore.executionWaitingForWebhook = true; + workflowsStore.setExecutionWaitingForWebhook(true); workflowsStore.executedNode = 'ChildNode'; vi.spyOn(workflowDocStore, 'getParentNodes').mockReturnValue(['Webhook']); const { getByTestId } = renderComponent(TriggerPanel, { @@ -108,7 +108,7 @@ describe('TriggerPanel.vue', () => { }); it('does not render listening state when executedNode is not a child or current node', () => { - workflowsStore.executionWaitingForWebhook = true; + workflowsStore.setExecutionWaitingForWebhook(true); workflowsStore.executedNode = 'UnrelatedNode'; const { queryByTestId } = renderComponent(TriggerPanel, { props: { nodeName: 'Webhook' }, diff --git a/packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataJsonActions.test.ts b/packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataJsonActions.test.ts index fcbfccf48e9..5cdd58ee4c6 100644 --- a/packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataJsonActions.test.ts +++ b/packages/frontend/editor-ui/src/features/ndv/runData/components/RunDataJsonActions.test.ts @@ -62,7 +62,7 @@ async function createPiniaWithActiveNode() { workflowsStore.workflow = workflow; const workflowDocumentStore = useWorkflowDocumentStore(createWorkflowDocumentId(workflow.id)); workflowDocumentStore.initPristineNodeMetadata(node.name); - workflowsStore.workflowExecutionData = { + workflowsStore.setWorkflowExecutionData({ id: '1', finished: true, mode: 'trigger', @@ -120,7 +120,7 @@ async function createPiniaWithActiveNode() { }, }, }), - }; + }); ndvStore.setActiveNodeName(node.name, 'other'); diff --git a/packages/frontend/editor-ui/src/features/ndv/runData/components/ai/RunDataAi.test.ts b/packages/frontend/editor-ui/src/features/ndv/runData/components/ai/RunDataAi.test.ts index 7189a543e22..8c37c6ef485 100644 --- a/packages/frontend/editor-ui/src/features/ndv/runData/components/ai/RunDataAi.test.ts +++ b/packages/frontend/editor-ui/src/features/ndv/runData/components/ai/RunDataAi.test.ts @@ -91,7 +91,7 @@ describe('RunDataAi', () => { beforeEach(() => { setActivePinia(createTestingPinia({ stubActions: false })); workflowsStore = useWorkflowsStore(); - workflowsStore.workflowExecutionData = executionResponse; + workflowsStore.setWorkflowExecutionData(executionResponse); }); it('should render the log that belong to given run index', async () => {