mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
* introduce analytics * add user survey backend * add user survey backend * set answers on survey submit Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com> * change name to personalization * lint Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com> * N8n 2495 add personalization modal (#2280) * update modals * add onboarding modal * implement questions * introduce analytics * simplify impl * implement survey handling * add personalized cateogry * update modal behavior * add thank you view * handle empty cases * rename modal * standarize modal names * update image, add tags to headings * remove unused file * remove unused interfaces * clean up footer spacing * introduce analytics * refactor to fix bug * update endpoint * set min height * update stories * update naming from questions to survey * remove spacing after core categories * fix bug in logic * sort nodes * rename types * merge with be * rename userSurvey * clean up rest api * use constants for keys * use survey keys * clean up types * move personalization to its own file Co-authored-by: ahsan-virani <ahsan.virani@gmail.com> * Survey new options (#2300) * split up options * fix quotes * remove unused import * add user created workflow event (#2301) * simplify env vars * fix versionCli on FE * update personalization env * fix event User opened Credentials panel * fix select modal spacing * fix nodes panel event * fix workflow id in workflow execute event * improve telemetry error logging * fix config and stop process events * add flush call on n8n stop * ready for release * improve telemetry process exit * fix merge * improve n8n stop events Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com> Co-authored-by: Mutasem <mutdmour@gmail.com> Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
208 lines
6.4 KiB
TypeScript
208 lines
6.4 KiB
TypeScript
import {
|
|
IExecutionPushResponse,
|
|
IExecutionResponse,
|
|
IStartRunData,
|
|
} from '@/Interface';
|
|
|
|
import {
|
|
IRunData,
|
|
IRunExecutionData,
|
|
NodeHelpers,
|
|
} from 'n8n-workflow';
|
|
|
|
import { externalHooks } from '@/components/mixins/externalHooks';
|
|
import { restApi } from '@/components/mixins/restApi';
|
|
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
|
import { showMessage } from '@/components/mixins/showMessage';
|
|
|
|
import mixins from 'vue-typed-mixins';
|
|
import { titleChange } from './titleChange';
|
|
|
|
export const workflowRun = mixins(
|
|
externalHooks,
|
|
restApi,
|
|
workflowHelpers,
|
|
showMessage,
|
|
titleChange,
|
|
).extend({
|
|
methods: {
|
|
// Starts to executes a workflow on server.
|
|
async runWorkflowApi (runData: IStartRunData): Promise<IExecutionPushResponse> {
|
|
if (this.$store.getters.pushConnectionActive === false) {
|
|
// Do not start if the connection to server is not active
|
|
// because then it can not receive the data as it executes.
|
|
throw new Error('No active connection to server. It is maybe down.');
|
|
}
|
|
|
|
this.$store.commit('addActiveAction', 'workflowRunning');
|
|
|
|
let response: IExecutionPushResponse;
|
|
|
|
try {
|
|
response = await this.restApi().runWorkflow(runData);
|
|
} catch (error) {
|
|
this.$store.commit('removeActiveAction', 'workflowRunning');
|
|
throw error;
|
|
}
|
|
|
|
if (response.executionId !== undefined) {
|
|
this.$store.commit('setActiveExecutionId', response.executionId);
|
|
}
|
|
|
|
if (response.waitingForWebhook === true) {
|
|
this.$store.commit('setExecutionWaitingForWebhook', true);
|
|
}
|
|
|
|
return response;
|
|
},
|
|
async runWorkflow (nodeName?: string, source?: string): Promise<IExecutionPushResponse | undefined> {
|
|
const workflow = this.getWorkflow();
|
|
|
|
if(nodeName) {
|
|
this.$telemetry.track('User clicked execute node button', { node_type: nodeName, workflow_id: this.$store.getters.workflowId });
|
|
} else {
|
|
this.$telemetry.track('User clicked execute workflow button', { workflow_id: this.$store.getters.workflowId });
|
|
}
|
|
|
|
if (this.$store.getters.isActionActive('workflowRunning') === true) {
|
|
return;
|
|
}
|
|
|
|
this.$titleSet(workflow.name as string, 'EXECUTING');
|
|
|
|
this.clearAllStickyNotifications();
|
|
|
|
try {
|
|
// Check first if the workflow has any issues before execute it
|
|
const issuesExist = this.$store.getters.nodesIssuesExist;
|
|
if (issuesExist === true) {
|
|
// If issues exist get all of the issues of all nodes
|
|
const workflowIssues = this.checkReadyForExecution(workflow, nodeName);
|
|
if (workflowIssues !== null) {
|
|
const errorMessages = [];
|
|
let nodeIssues: string[];
|
|
for (const nodeName of Object.keys(workflowIssues)) {
|
|
nodeIssues = NodeHelpers.nodeIssuesToString(workflowIssues[nodeName]);
|
|
for (const nodeIssue of nodeIssues) {
|
|
errorMessages.push(`${nodeName}: ${nodeIssue}`);
|
|
}
|
|
}
|
|
|
|
this.$showMessage({
|
|
title: 'Workflow can not be executed',
|
|
message: 'The workflow has issues. Please fix them first:<br /> - ' + errorMessages.join('<br /> - '),
|
|
type: 'error',
|
|
duration: 0,
|
|
});
|
|
this.$titleSet(workflow.name as string, 'ERROR');
|
|
this.$externalHooks().run('workflowRun.runError', { errorMessages, nodeName });
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Get the direct parents of the node
|
|
let directParentNodes: string[] = [];
|
|
if (nodeName !== undefined) {
|
|
directParentNodes = workflow.getParentNodes(nodeName, 'main', 1);
|
|
}
|
|
|
|
const runData = this.$store.getters.getWorkflowRunData;
|
|
|
|
let newRunData: IRunData | undefined;
|
|
|
|
const startNodes: string[] = [];
|
|
|
|
if (runData !== null && Object.keys(runData).length !== 0) {
|
|
newRunData = {};
|
|
|
|
// Go over the direct parents of the node
|
|
for (const directParentNode of directParentNodes) {
|
|
// Go over the parents of that node so that we can get a start
|
|
// node for each of the branches
|
|
const parentNodes = workflow.getParentNodes(directParentNode, 'main');
|
|
|
|
// Add also the direct parent to be checked
|
|
parentNodes.push(directParentNode);
|
|
|
|
for (const parentNode of parentNodes) {
|
|
if (runData[parentNode] === undefined || runData[parentNode].length === 0) {
|
|
// When we hit a node which has no data we stop and set it
|
|
// as a start node the execution from and then go on with other
|
|
// direct input nodes
|
|
startNodes.push(parentNode);
|
|
break;
|
|
}
|
|
newRunData[parentNode] = runData[parentNode].slice(0, 1);
|
|
}
|
|
}
|
|
|
|
if (Object.keys(newRunData).length === 0) {
|
|
// If there is no data for any of the parent nodes make sure
|
|
// that run data is empty that it runs regularly
|
|
newRunData = undefined;
|
|
}
|
|
}
|
|
|
|
if (startNodes.length === 0 && nodeName !== undefined) {
|
|
startNodes.push(nodeName);
|
|
}
|
|
|
|
const isNewWorkflow = this.$store.getters.isNewWorkflow;
|
|
const hasWebhookNode = this.$store.getters.currentWorkflowHasWebhookNode;
|
|
if (isNewWorkflow && hasWebhookNode) {
|
|
await this.saveCurrentWorkflow();
|
|
}
|
|
|
|
const workflowData = await this.getWorkflowDataToSave();
|
|
|
|
const startRunData: IStartRunData = {
|
|
workflowData,
|
|
runData: newRunData,
|
|
startNodes,
|
|
};
|
|
if (nodeName) {
|
|
startRunData.destinationNode = nodeName;
|
|
}
|
|
|
|
// Init the execution data to represent the start of the execution
|
|
// that data which gets reused is already set and data of newly executed
|
|
// nodes can be added as it gets pushed in
|
|
const executionData: IExecutionResponse = {
|
|
id: '__IN_PROGRESS__',
|
|
finished: false,
|
|
mode: 'manual',
|
|
startedAt: new Date(),
|
|
stoppedAt: undefined,
|
|
workflowId: workflow.id,
|
|
data: {
|
|
resultData: {
|
|
runData: newRunData || {},
|
|
startNodes,
|
|
workflowData,
|
|
},
|
|
} as IRunExecutionData,
|
|
workflowData: {
|
|
id: this.$store.getters.workflowId,
|
|
name: workflowData.name!,
|
|
active: workflowData.active!,
|
|
createdAt: 0,
|
|
updatedAt: 0,
|
|
...workflowData,
|
|
},
|
|
};
|
|
this.$store.commit('setWorkflowExecutionData', executionData);
|
|
|
|
const runWorkflowApiResponse = await this.runWorkflowApi(startRunData);
|
|
|
|
this.$externalHooks().run('workflowRun.runWorkflow', { nodeName, source });
|
|
|
|
return runWorkflowApiResponse;
|
|
} catch (error) {
|
|
this.$titleSet(workflow.name as string, 'ERROR');
|
|
this.$showError(error, 'Problem running workflow', 'There was a problem running the workflow:');
|
|
return undefined;
|
|
}
|
|
},
|
|
},
|
|
});
|