mirror of
https://github.com/n8n-io/n8n.git
synced 2026-09-21 04:37:50 +08:00
refactor(editor): Introduce setter facades for workflow execution state (no-changelog) (#29675)
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
+9
-9
@@ -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);
|
||||
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
+1
-1
@@ -175,7 +175,7 @@ export const useExecutionDebugging = (providedWorkflowState?: WorkflowState) =>
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
workflowsStore.isInDebugMode = false;
|
||||
workflowsStore.setIsInDebugMode(false);
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
+2
-2
@@ -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();
|
||||
|
||||
@@ -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');
|
||||
|
||||
+1
-1
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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' },
|
||||
|
||||
+2
-2
@@ -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');
|
||||
|
||||
|
||||
+1
-1
@@ -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 () => {
|
||||
|
||||
Reference in New Issue
Block a user