feat(editor): Add stop current execution button in new canvas (no-changelog) (#9968)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { createPinia, setActivePinia } from 'pinia';
|
||||
import type { Connection } from '@vue-flow/core';
|
||||
import type { IConnection } from 'n8n-workflow';
|
||||
import type { IConnection, Workflow } from 'n8n-workflow';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
import { useCanvasOperations } from '@/composables/useCanvasOperations';
|
||||
import type { CanvasElement } from '@/types';
|
||||
@@ -65,7 +65,7 @@ describe('useCanvasOperations', () => {
|
||||
usedCredentials: [],
|
||||
});
|
||||
workflowsStore.workflowsById[workflowId] = workflow;
|
||||
await workflowHelpers.initState(workflow, true);
|
||||
await workflowHelpers.initState(workflow);
|
||||
|
||||
canvasOperations = useCanvasOperations({ router, lastClickPosition });
|
||||
});
|
||||
@@ -248,8 +248,8 @@ describe('useCanvasOperations', () => {
|
||||
it('should add nodes at current position when position is not specified', async () => {
|
||||
const nodeTypeName = 'type';
|
||||
const nodes = [
|
||||
mockNode({ name: 'Node 1', type: nodeTypeName, position: [40, 40] }),
|
||||
mockNode({ name: 'Node 2', type: nodeTypeName, position: [100, 240] }),
|
||||
mockNode({ name: 'Node 1', type: nodeTypeName, position: [120, 120] }),
|
||||
mockNode({ name: 'Node 2', type: nodeTypeName, position: [180, 320] }),
|
||||
];
|
||||
const workflowStoreAddNodeSpy = vi.spyOn(workflowsStore, 'addNode');
|
||||
|
||||
@@ -292,9 +292,16 @@ describe('useCanvasOperations', () => {
|
||||
}),
|
||||
]);
|
||||
|
||||
canvasOperations.editableWorkflowObject.value.getParentNodesByDepth = vi
|
||||
.fn()
|
||||
.mockReturnValue(nodes.map((node) => node.name));
|
||||
vi.spyOn(workflowsStore, 'getCurrentWorkflow').mockImplementation(() =>
|
||||
mock<Workflow>({
|
||||
getParentNodesByDepth: () =>
|
||||
nodes.map((node) => ({
|
||||
name: node.name,
|
||||
depth: 0,
|
||||
indicies: [],
|
||||
})),
|
||||
}),
|
||||
);
|
||||
|
||||
await canvasOperations.addNodes(nodes, {});
|
||||
|
||||
|
||||
@@ -11,13 +11,19 @@ import type {
|
||||
INodeUpdatePropertiesInformation,
|
||||
XYPosition,
|
||||
} from '@/Interface';
|
||||
import { QUICKSTART_NOTE_NAME, STICKY_NODE_TYPE } from '@/constants';
|
||||
import {
|
||||
FORM_TRIGGER_NODE_TYPE,
|
||||
QUICKSTART_NOTE_NAME,
|
||||
STICKY_NODE_TYPE,
|
||||
WEBHOOK_NODE_TYPE,
|
||||
} from '@/constants';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { useHistoryStore } from '@/stores/history.store';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { useExternalHooks } from '@/composables/useExternalHooks';
|
||||
import {
|
||||
AddNodeCommand,
|
||||
MoveNodeCommand,
|
||||
RemoveConnectionCommand,
|
||||
RemoveNodeCommand,
|
||||
@@ -53,10 +59,8 @@ import type { useRouter } from 'vue-router';
|
||||
import { useCanvasStore } from '@/stores/canvas.store';
|
||||
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
||||
|
||||
type AddNodeData = {
|
||||
name?: string;
|
||||
type AddNodeData = Partial<INodeUi> & {
|
||||
type: string;
|
||||
position?: XYPosition;
|
||||
};
|
||||
|
||||
type AddNodeOptions = {
|
||||
@@ -266,13 +270,12 @@ export function useCanvasOperations({
|
||||
) {
|
||||
let currentPosition = position;
|
||||
let lastAddedNode: INodeUi | undefined;
|
||||
for (const { type, name, position: nodePosition, isAutoAdd, openDetail } of nodes) {
|
||||
for (const { isAutoAdd, openDetail, ...nodeData } of nodes) {
|
||||
try {
|
||||
await createNode(
|
||||
{
|
||||
name,
|
||||
type,
|
||||
position: nodePosition ?? currentPosition,
|
||||
...nodeData,
|
||||
position: nodeData.position ?? currentPosition,
|
||||
},
|
||||
{
|
||||
dragAndDrop,
|
||||
@@ -328,14 +331,16 @@ export function useCanvasOperations({
|
||||
|
||||
workflowsStore.addNode(newNodeData);
|
||||
|
||||
// @TODO Figure out why this is needed and if we can do better...
|
||||
// this.matchCredentials(node);
|
||||
nodeHelpers.matchCredentials(newNodeData);
|
||||
|
||||
const lastSelectedNode = uiStore.getLastSelectedNode;
|
||||
const lastSelectedNodeOutputIndex = uiStore.lastSelectedNodeOutputIndex;
|
||||
const lastSelectedNodeEndpointUuid = uiStore.lastSelectedNodeEndpointUuid;
|
||||
|
||||
historyStore.startRecordingUndo();
|
||||
if (options.trackHistory) {
|
||||
historyStore.pushCommandToUndo(new AddNodeCommand(newNodeData));
|
||||
}
|
||||
|
||||
const outputIndex = lastSelectedNodeOutputIndex ?? 0;
|
||||
const targetEndpoint = lastSelectedNodeEndpointUuid ?? '';
|
||||
@@ -399,12 +404,14 @@ export function useCanvasOperations({
|
||||
}
|
||||
|
||||
const newNodeData: INodeUi = {
|
||||
id: uuid(),
|
||||
...node,
|
||||
id: node.id ?? uuid(),
|
||||
name: node.name ?? (nodeTypeDescription.defaults.name as string),
|
||||
type: nodeTypeDescription.name,
|
||||
typeVersion: nodeVersion,
|
||||
position: node.position ?? [0, 0],
|
||||
parameters: {},
|
||||
disabled: node.disabled ?? false,
|
||||
parameters: node.parameters ?? {},
|
||||
};
|
||||
|
||||
await loadNodeTypesProperties([{ name: newNodeData.type, version: newNodeData.typeVersion }]);
|
||||
@@ -664,6 +671,14 @@ export function useCanvasOperations({
|
||||
newNodeData.webhookId = uuid();
|
||||
}
|
||||
|
||||
// if it's a webhook and the path is empty set the UUID as the default path
|
||||
if (
|
||||
[WEBHOOK_NODE_TYPE, FORM_TRIGGER_NODE_TYPE].includes(newNodeData.type) &&
|
||||
newNodeData.parameters.path === ''
|
||||
) {
|
||||
newNodeData.parameters.path = newNodeData.webhookId as string;
|
||||
}
|
||||
|
||||
workflowsStore.setNodePristine(newNodeData.name, true);
|
||||
uiStore.stateIsDirty = true;
|
||||
|
||||
|
||||
@@ -1253,6 +1253,7 @@ export function useNodeHelpers() {
|
||||
updateNodesCredentialsIssues,
|
||||
getNodeInputData,
|
||||
setSuccessOutput,
|
||||
matchCredentials,
|
||||
isInsertingNodes,
|
||||
credentialsUpdated,
|
||||
isProductionExecutionPreview,
|
||||
|
||||
@@ -435,10 +435,20 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
|
||||
}
|
||||
}
|
||||
|
||||
async function stopWaitingForWebhook() {
|
||||
try {
|
||||
await workflowsStore.removeTestWebhook(workflowsStore.workflowId);
|
||||
} catch (error) {
|
||||
toast.showError(error, i18n.baseText('nodeView.showError.stopWaitingForWebhook.title'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
consolidateRunDataAndStartNodes,
|
||||
runWorkflow,
|
||||
runWorkflowApi,
|
||||
stopCurrentExecution,
|
||||
stopWaitingForWebhook,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1050,12 +1050,8 @@ export function useWorkflowHelpers(options: { router: ReturnType<typeof useRoute
|
||||
}
|
||||
}
|
||||
|
||||
async function initState(workflowData: IWorkflowDb, set = false): Promise<void> {
|
||||
async function initState(workflowData: IWorkflowDb): Promise<void> {
|
||||
workflowsStore.addWorkflow(workflowData);
|
||||
if (set) {
|
||||
workflowsStore.setWorkflow(workflowData);
|
||||
}
|
||||
|
||||
workflowsStore.setActive(workflowData.active || false);
|
||||
workflowsStore.setWorkflowId(workflowData.id);
|
||||
workflowsStore.setWorkflowName({
|
||||
|
||||
Reference in New Issue
Block a user