Skip to content

Commit

Permalink
fix: Stop listening button not working in NDV (#9023)
Browse files Browse the repository at this point in the history
  • Loading branch information
MiloradFilipovic committed Apr 3, 2024
1 parent 1d40204 commit 2c3658f
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 67 deletions.
14 changes: 14 additions & 0 deletions cypress/e2e/5-ndv.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -658,4 +658,18 @@ describe('NDV', () => {
cy.realPress('Escape');
});
});

it('Stop listening for trigger event from NDV', () => {
workflowPage.actions.addInitialNodeToCanvas('Local File Trigger', {
keepNdvOpen: true,
action: 'On Changes To A Specific File',
isTrigger: true,
});
ndv.getters.triggerPanelExecuteButton().should('exist');
ndv.getters.triggerPanelExecuteButton().click();
ndv.getters.triggerPanelExecuteButton().should('contain', 'Stop Listening');
ndv.getters.triggerPanelExecuteButton().click();
ndv.getters.triggerPanelExecuteButton().should('contain', 'Test step');
workflowPage.getters.successToast().should('exist');
});
});
7 changes: 4 additions & 3 deletions cypress/pages/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,19 +145,20 @@ export class WorkflowPage extends BasePage {
},
addInitialNodeToCanvas: (
nodeDisplayName: string,
opts?: { keepNdvOpen?: boolean; action?: string },
opts?: { keepNdvOpen?: boolean; action?: string, isTrigger?: boolean},
) => {
this.getters.canvasPlusButton().click();
this.getters.nodeCreatorSearchBar().type(nodeDisplayName);
this.getters.nodeCreatorSearchBar().type('{enter}');
if (opts?.action) {
const itemId = opts.isTrigger ? 'Triggers' : 'Actions';
// Expand actions category if it's collapsed
nodeCreator.getters
.getCategoryItem('Actions')
.getCategoryItem(itemId)
.parent()
.then(($el) => {
if ($el.attr('data-category-collapsed') === 'true') {
nodeCreator.getters.getCategoryItem('Actions').click();
nodeCreator.getters.getCategoryItem(itemId).click();
}
});
nodeCreator.getters.getCreatorItem(opts.action).click();
Expand Down
5 changes: 4 additions & 1 deletion packages/editor-ui/src/components/NodeExecuteButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,20 @@ export default defineComponent({
type: Boolean,
},
},
emits: ['stopExecution', 'execute'],
setup(props) {
const router = useRouter();
const workflowsStore = useWorkflowsStore();
const node = workflowsStore.getNodeByName(props.nodeName);
const pinnedData = usePinnedData(node);
const externalHooks = useExternalHooks();
const { runWorkflow } = useRunWorkflow({ router });
const { runWorkflow, stopCurrentExecution } = useRunWorkflow({ router });
return {
externalHooks,
pinnedData,
runWorkflow,
stopCurrentExecution,
...useToast(),
...useMessage(),
};
Expand Down Expand Up @@ -236,6 +238,7 @@ export default defineComponent({
} else if (this.isListeningForEvents) {
await this.stopWaitingForWebhook();
} else if (this.isListeningForWorkflowEvents) {
await this.stopCurrentExecution();
this.$emit('stopExecution');
} else {
let shouldUnpinAndExecute = false;
Expand Down
60 changes: 60 additions & 0 deletions packages/editor-ui/src/composables/useRunWorkflow.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {
IExecutionPushResponse,
IExecutionResponse,
IPushDataExecutionFinished,
IStartRunData,
IWorkflowDb,
} from '@/Interface';
Expand All @@ -13,6 +14,7 @@ import type {
IWorkflowBase,
Workflow,
StartNodeData,
IRun,
} from 'n8n-workflow';
import {
NodeHelpers,
Expand Down Expand Up @@ -449,9 +451,67 @@ export function useRunWorkflow(options: { router: ReturnType<typeof useRouter> }
return { runData: newRunData, startNodeNames };
}

async function stopCurrentExecution() {
const executionId = workflowsStore.activeExecutionId;
if (executionId === null) {
return;
}

try {
await workflowsStore.stopCurrentExecution(executionId);
} catch (error) {
// Execution stop might fail when the execution has already finished. Let's treat this here.
const execution = await this.workflowsStore.getExecution(executionId);

if (execution === undefined) {
// execution finished but was not saved (e.g. due to low connectivity)
workflowsStore.finishActiveExecution({
executionId,
data: { finished: true, stoppedAt: new Date() },
});
workflowsStore.executingNode.length = 0;
uiStore.removeActiveAction('workflowRunning');

titleSet(workflowsStore.workflowName, 'IDLE');
toast.showMessage({
title: i18n.baseText('nodeView.showMessage.stopExecutionCatch.unsaved.title'),
message: i18n.baseText('nodeView.showMessage.stopExecutionCatch.unsaved.message'),
type: 'success',
});
} else if (execution?.finished) {
// execution finished before it could be stopped
const executedData = {
data: execution.data,
finished: execution.finished,
mode: execution.mode,
startedAt: execution.startedAt,
stoppedAt: execution.stoppedAt,
} as IRun;
const pushData = {
data: executedData,
executionId,
retryOf: execution.retryOf,
} as IPushDataExecutionFinished;
workflowsStore.finishActiveExecution(pushData);
titleSet(execution.workflowData.name, 'IDLE');
workflowsStore.executingNode.length = 0;
workflowsStore.setWorkflowExecutionData(executedData as IExecutionResponse);
uiStore.removeActiveAction('workflowRunning');
toast.showMessage({
title: i18n.baseText('nodeView.showMessage.stopExecutionCatch.title'),
message: i18n.baseText('nodeView.showMessage.stopExecutionCatch.message'),
type: 'success',
});
} else {
toast.showError(error, i18n.baseText('nodeView.showError.stopExecution.title'));
}
}
}

return {
consolidateRunDataAndStartNodes,
runWorkflow,
runWorkflowApi,
stopCurrentExecution,
};
}
66 changes: 3 additions & 63 deletions packages/editor-ui/src/views/NodeView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,6 @@ import type {
INodeTypeDescription,
INodeTypeNameVersion,
IPinData,
IRun,
ITaskData,
ITelemetryTrackProperties,
IWorkflowBase,
Expand All @@ -303,7 +302,6 @@ import type {
IUpdateInformation,
IWorkflowDataUpdate,
XYPosition,
IPushDataExecutionFinished,
ITag,
INewWorkflowData,
IWorkflowTemplate,
Expand Down Expand Up @@ -492,7 +490,7 @@ export default defineComponent({
const { callDebounced } = useDebounce();
const canvasPanning = useCanvasPanning(nodeViewRootRef, { onMouseMoveEnd });
const workflowHelpers = useWorkflowHelpers({ router });
const { runWorkflow } = useRunWorkflow({ router });
const { runWorkflow, stopCurrentExecution } = useRunWorkflow({ router });
return {
locale,
Expand All @@ -509,6 +507,7 @@ export default defineComponent({
onMouseMoveEnd,
workflowHelpers,
runWorkflow,
stopCurrentExecution,
callDebounced,
...useCanvasMouseSelect(),
...useGlobalLinkActions(),
Expand Down Expand Up @@ -1930,67 +1929,8 @@ export default defineComponent({
});
},
async stopExecution() {
const executionId = this.workflowsStore.activeExecutionId;
if (executionId === null) {
return;
}
try {
this.stopExecutionInProgress = true;
await this.workflowsStore.stopCurrentExecution(executionId);
} catch (error) {
// Execution stop might fail when the execution has already finished. Let's treat this here.
const execution = await this.workflowsStore.getExecution(executionId);
if (execution === undefined) {
// execution finished but was not saved (e.g. due to low connectivity)
this.workflowsStore.finishActiveExecution({
executionId,
data: { finished: true, stoppedAt: new Date() },
});
this.workflowsStore.executingNode.length = 0;
this.uiStore.removeActiveAction('workflowRunning');
this.titleSet(this.workflowsStore.workflowName, 'IDLE');
this.showMessage({
title: this.$locale.baseText('nodeView.showMessage.stopExecutionCatch.unsaved.title'),
message: this.$locale.baseText(
'nodeView.showMessage.stopExecutionCatch.unsaved.message',
),
type: 'success',
});
} else if (execution?.finished) {
// execution finished before it could be stopped
const executedData = {
data: execution.data,
finished: execution.finished,
mode: execution.mode,
startedAt: execution.startedAt,
stoppedAt: execution.stoppedAt,
} as IRun;
const pushData = {
data: executedData,
executionId,
retryOf: execution.retryOf,
} as IPushDataExecutionFinished;
this.workflowsStore.finishActiveExecution(pushData);
this.titleSet(execution.workflowData.name, 'IDLE');
this.workflowsStore.executingNode.length = 0;
this.workflowsStore.setWorkflowExecutionData(executedData as IExecutionResponse);
this.uiStore.removeActiveAction('workflowRunning');
this.showMessage({
title: this.$locale.baseText('nodeView.showMessage.stopExecutionCatch.title'),
message: this.$locale.baseText('nodeView.showMessage.stopExecutionCatch.message'),
type: 'success',
});
} else {
this.showError(error, this.$locale.baseText('nodeView.showError.stopExecution.title'));
}
}
await this.stopCurrentExecution();
this.stopExecutionInProgress = false;
void this.workflowHelpers.getWorkflowDataToSave().then((workflowData) => {
const trackProps = {
workflow_id: this.workflowsStore.workflowId,
Expand Down

0 comments on commit 2c3658f

Please sign in to comment.