Files
n8n-enterprise-unlocked/packages/editor-ui/src/stores/workflows.ts
Alex Grozav 7d2e2ee0f7 fix: Remove foreign credentials when copying nodes or duplicating workflow (#4880)
* fix: Remove foreign credentials when copying nodes or duplicating workflow

* chore: fix linting issue
2022-12-12 12:59:16 +02:00

855 lines
27 KiB
TypeScript

import {
DEFAULT_NEW_WORKFLOW_NAME,
DUPLICATE_POSTFFIX,
EnterpriseEditionFeature,
MAX_WORKFLOW_NAME_LENGTH,
PLACEHOLDER_EMPTY_WORKFLOW_ID,
STORES,
} from "@/constants";
import {
IExecutionResponse,
IExecutionsCurrentSummaryExtended,
IExecutionsSummary,
INewWorkflowData,
INodeUi,
INodeUpdatePropertiesInformation,
IPushDataExecutionFinished,
IPushDataNodeExecuteAfter,
IUpdateInformation,
IUsedCredential,
IWorkflowDb,
IWorkflowsMap,
WorkflowsState,
} from "@/Interface";
import { defineStore } from "pinia";
import {
deepCopy,
IConnection,
IConnections,
IDataObject,
INode,
INodeConnections,
INodeCredentials,
INodeCredentialsDetails,
INodeExecutionData,
INodeIssueData,
IPinData,
IRunData,
ITaskData,
IWorkflowSettings,
} from 'n8n-workflow';
import Vue from "vue";
import { useRootStore } from "./n8nRootStore";
import {
getActiveWorkflows,
getCurrentExecutions,
getFinishedExecutions,
getNewWorkflow,
getWorkflows,
} from "@/api/workflows";
import {useUIStore} from "./ui";
import {dataPinningEventBus} from "@/event-bus/data-pinning-event-bus";
import {isJsonKeyObject, getPairedItemsMapping, stringSizeInBytes, isObjectLiteral} from "@/utils";
import {useNDVStore} from "./ndv";
import {useNodeTypesStore} from "./nodeTypes";
import {useWorkflowsEEStore} from "@/stores/workflows.ee";
import {useUsersStore} from "@/stores/users";
import {useSettingsStore} from "@/stores/settings";
const createEmptyWorkflow = (): IWorkflowDb => ({
id: PLACEHOLDER_EMPTY_WORKFLOW_ID,
name: '',
active: false,
createdAt: -1,
updatedAt: -1,
connections: {},
nodes: [],
settings: {},
tags: [],
pinData: {},
versionId: '',
usedCredentials: [],
});
export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
state: (): WorkflowsState => ({
workflow: createEmptyWorkflow(),
usedCredentials: {},
activeWorkflows: [],
activeExecutions: [],
currentWorkflowExecutions: [],
activeWorkflowExecution: null,
finishedExecutionsCount: 0,
workflowExecutionData: null,
workflowExecutionPairedItemMappings: {},
workflowsById: {},
subWorkflowExecutionError: null,
activeExecutionId: null,
executingNode: null,
executionWaitingForWebhook: false,
nodeMetadata: {},
}),
getters: {
// Workflow getters
workflowName(): string {
return this.workflow.name;
},
workflowId(): string {
return this.workflow.id;
},
workflowVersionId(): string | undefined {
return this.workflow.versionId;
},
workflowSettings() : IWorkflowSettings {
if (this.workflow.settings === undefined) {
return {};
}
return this.workflow.settings;
},
workflowTags() : string[] {
return this.workflow.tags as string[];
},
allWorkflows() : IWorkflowDb[] {
return Object.values(this.workflowsById)
.sort((a, b) => a.name.localeCompare(b.name));
},
isNewWorkflow() : boolean {
return this.workflow.id === PLACEHOLDER_EMPTY_WORKFLOW_ID;
},
isWorkflowActive(): boolean {
return this.workflow.active;
},
workflowTriggerNodes() : INodeUi[] {
return this.workflow.nodes.filter((node: INodeUi) => {
const nodeTypesStore = useNodeTypesStore();
const nodeType = nodeTypesStore.getNodeType(node.type as string, node.typeVersion as number);
return nodeType && nodeType.group.includes('trigger');
});
},
currentWorkflowHasWebhookNode(): boolean {
return !!this.workflow.nodes.find((node: INodeUi) => !!node.webhookId);
},
getWorkflowRunData(): IRunData | null {
if (!this.workflowExecutionData || !this.workflowExecutionData.data || !this.workflowExecutionData.data.resultData) {
return null;
}
return this.workflowExecutionData.data.resultData.runData;
},
getWorkflowResultDataByNodeName() {
return (nodeName: string): ITaskData[] | null => {
const workflowRunData = this.getWorkflowRunData;
if (workflowRunData === null) {
return null;
}
if (!workflowRunData.hasOwnProperty(nodeName)) {
return null;
}
return workflowRunData[nodeName];
};
},
// Node getters
allConnections() : IConnections {
return this.workflow.connections;
},
outgoingConnectionsByNodeName() {
return (nodeName: string): INodeConnections => {
if (this.workflow.connections.hasOwnProperty(nodeName)) {
return this.workflow.connections[nodeName];
}
return {};
};
},
allNodes() : INodeUi[] {
return this.workflow.nodes;
},
nodesByName() : { [name: string]: INodeUi } {
return this.workflow.nodes.reduce((accu: { [name: string]: INodeUi }, node) => {
accu[node.name] = node;
return accu;
}, {});
},
getNodeByName() {
return (nodeName: string): INodeUi | null => this.nodesByName[nodeName] || null;
},
getNodeById() {
return (nodeId: string): INodeUi | undefined => this.workflow.nodes.find((node: INodeUi) => node.id === nodeId);
},
nodesIssuesExist(): boolean {
for (const node of this.workflow.nodes) {
if (node.issues === undefined || Object.keys(node.issues).length === 0) {
continue;
}
return true;
}
return false;
},
getPinData(): IPinData | undefined {
return this.workflow.pinData;
},
pinDataSize(): number {
const ndvStore = useNDVStore();
const activeNode = ndvStore.activeNodeName;
return this.workflow.nodes
.reduce((acc, node) => {
if (typeof node.pinData !== 'undefined' && node.name !== activeNode) {
acc += stringSizeInBytes(node.pinData);
}
return acc;
}, 0);
},
executedNode(): string | undefined {
return this.workflowExecutionData ? this.workflowExecutionData.executedNode : undefined;
},
getParametersLastUpdate(): ((name: string) => number | undefined) {
return (nodeName: string) => this.nodeMetadata[nodeName] && this.nodeMetadata[nodeName].parametersLastUpdatedAt;
},
// Executions getters
getExecutionDataById(): (id: string) => IExecutionsSummary | undefined {
return (id: string): IExecutionsSummary | undefined => this.currentWorkflowExecutions.find(execution => execution.id === id);
},
getAllLoadedFinishedExecutions(): IExecutionsSummary[] {
return this.currentWorkflowExecutions.filter(ex => ex.finished === true || ex.stoppedAt !== undefined);
},
getWorkflowExecution(): IExecutionResponse | null {
return this.workflowExecutionData;
},
getTotalFinishedExecutionsCount(): number {
return this.finishedExecutionsCount;
},
},
actions: {
// Workflow actions
async fetchAllWorkflows(): Promise<IWorkflowDb[]> {
const rootStore = useRootStore();
const workflows = await getWorkflows(rootStore.getRestApiContext);
this.setWorkflows(workflows);
return workflows;
},
async getNewWorkflowData(name?: string): Promise<INewWorkflowData> {
const workflowsEEStore = useWorkflowsEEStore();
let workflowData = {
name: '',
onboardingFlowEnabled: false,
};
try {
const rootStore = useRootStore();
workflowData = await getNewWorkflow(rootStore.getRestApiContext, name);
}
catch (e) {
// in case of error, default to original name
workflowData.name = name || DEFAULT_NEW_WORKFLOW_NAME;
}
this.setWorkflowName({ newName: workflowData.name, setStateDirty: false });
return workflowData;
},
resetWorkflow() {
const usersStore = useUsersStore();
const settingsStore = useSettingsStore();
this.workflow = createEmptyWorkflow();
if (settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.WorkflowSharing)) {
Vue.set(this.workflow, 'ownedBy', usersStore.currentUser);
}
},
setWorkflowId (id: string): void {
this.workflow.id = id === 'new' ? PLACEHOLDER_EMPTY_WORKFLOW_ID : id;
},
setUsedCredentials(data: IUsedCredential[]) {
this.workflow.usedCredentials = data;
this.usedCredentials = data.reduce<{ [name: string]: IUsedCredential }>((accu, credential) => {
accu[credential.id!] = credential;
return accu;
}, {});
},
setWorkflowName(data: { newName: string, setStateDirty: boolean }): void {
if (data.setStateDirty === true) {
const uiStore = useUIStore();
uiStore.stateIsDirty = true;
}
this.workflow.name = data.newName;
if (this.workflow.id !== PLACEHOLDER_EMPTY_WORKFLOW_ID && this.workflowsById[this.workflow.id]) {
this.workflowsById[this.workflow.id].name = data.newName;
}
},
setWorkflowVersionId(versionId: string): void {
this.workflow.versionId = versionId;
},
// replace invalid credentials in workflow
replaceInvalidWorkflowCredentials(data: {credentials: INodeCredentialsDetails, invalid: INodeCredentialsDetails, type: string}): void {
this.workflow.nodes.forEach((node : INodeUi) => {
const nodeCredentials: INodeCredentials | undefined = (node as unknown as INode).credentials;
if (!nodeCredentials || !nodeCredentials[data.type]) {
return;
}
const nodeCredentialDetails: INodeCredentialsDetails | string = nodeCredentials[data.type];
if (typeof nodeCredentialDetails === 'string' && nodeCredentialDetails === data.invalid.name) {
(node.credentials as INodeCredentials)[data.type] = data.credentials;
return;
}
if (nodeCredentialDetails.id === null) {
if (nodeCredentialDetails.name === data.invalid.name) {
(node.credentials as INodeCredentials)[data.type] = data.credentials;
}
return;
}
if (nodeCredentialDetails.id === data.invalid.id) {
(node.credentials as INodeCredentials)[data.type] = data.credentials;
}
});
},
setWorkflows(workflows: IWorkflowDb[]) : void {
this.workflowsById = workflows.reduce<IWorkflowsMap>((acc, workflow: IWorkflowDb) => {
if (workflow.id) {
acc[workflow.id] = workflow;
}
return acc;
}, {});
},
deleteWorkflow(id: string) : void {
const { [id]: deletedWorkflow, ...workflows } = this.workflowsById;
this.workflowsById = workflows;
},
addWorkflow(workflow: IWorkflowDb) : void {
Vue.set(this.workflowsById, workflow.id, {
...this.workflowsById[workflow.id],
...deepCopy(workflow),
});
},
setWorkflowActive(workflowId: string): void {
const uiStore = useUIStore();
uiStore.stateIsDirty = false;
const index = this.activeWorkflows.indexOf(workflowId);
if (index === -1) {
this.activeWorkflows.push(workflowId);
}
if (this.workflowsById[workflowId]) {
this.workflowsById[workflowId].active = true;
}
},
setWorkflowInactive(workflowId: string): void {
const index = this.activeWorkflows.indexOf(workflowId);
if (index !== -1) {
this.activeWorkflows.splice(index, 1);
}
if (this.workflowsById[workflowId]) {
this.workflowsById[workflowId].active = false;
}
},
async fetchActiveWorkflows(): Promise<string[]> {
const rootStore = useRootStore();
const activeWorkflows = await getActiveWorkflows(rootStore.getRestApiContext);
this.activeWorkflows = activeWorkflows;
return activeWorkflows;
},
setActive(newActive: boolean) : void {
this.workflow.active = newActive;
},
async getDuplicateCurrentWorkflowName(currentWorkflowName: string): Promise<string> {
if (currentWorkflowName && (currentWorkflowName.length + DUPLICATE_POSTFFIX.length) >= MAX_WORKFLOW_NAME_LENGTH) {
return currentWorkflowName;
}
let newName = `${currentWorkflowName}${DUPLICATE_POSTFFIX}`;
try {
const rootStore = useRootStore();
const newWorkflow = await getNewWorkflow(rootStore.getRestApiContext, newName );
newName = newWorkflow.name;
}
catch (e) {
}
return newName;
},
// Node actions
setWorkflowExecutionData(workflowResultData: IExecutionResponse | null): void {
this.workflowExecutionData = workflowResultData;
this.workflowExecutionPairedItemMappings = getPairedItemsMapping(this.workflowExecutionData);
},
setWorkflowSettings(workflowSettings: IWorkflowSettings): void {
Vue.set(this.workflow, 'settings', workflowSettings);
},
setWorkflowPinData(pinData: IPinData): void {
Vue.set(this.workflow, 'pinData', pinData || {});
dataPinningEventBus.$emit('pin-data', pinData || {});
},
setWorkflowTagIds(tags: string[]): void {
Vue.set(this.workflow, 'tags', tags);
},
addWorkflowTagIds(tags: string[]): void {
Vue.set(this.workflow, 'tags', [...new Set([...(this.workflow.tags || []), ...tags])]);
},
removeWorkflowTagId(tagId: string): void {
const tags = this.workflow.tags as string[];
const updated = tags.filter((id: string) => id !== tagId);
Vue.set(this.workflow, 'tags', updated);
},
setWorkflow(workflow: IWorkflowDb): void {
Vue.set(this, 'workflow', workflow);
if (!this.workflow.hasOwnProperty('active')) {
Vue.set(this.workflow, 'active', false);
}
if (!this.workflow.hasOwnProperty('connections')) {
Vue.set(this.workflow, 'connections', {});
}
if (!this.workflow.hasOwnProperty('createdAt')) {
Vue.set(this.workflow, 'createdAt', -1);
}
if (!this.workflow.hasOwnProperty('updatedAt')) {
Vue.set(this.workflow, 'updatedAt', -1);
}
if (!this.workflow.hasOwnProperty('id')) {
Vue.set(this.workflow, 'id', PLACEHOLDER_EMPTY_WORKFLOW_ID);
}
if (!this.workflow.hasOwnProperty('nodes')) {
Vue.set(this.workflow, 'nodes', []);
}
if (!this.workflow.hasOwnProperty('settings')) {
Vue.set(this.workflow, 'settings', {});
}
},
pinData(payload: { node: INodeUi, data: INodeExecutionData[] }): void {
if (!this.workflow.pinData) {
Vue.set(this.workflow, 'pinData', {});
}
if (!Array.isArray(payload.data)) {
payload.data = [payload.data];
}
const storedPinData = payload.data.map(item => isJsonKeyObject(item) ? item : { json: item });
Vue.set(this.workflow.pinData!, payload.node.name, storedPinData);
const uiStore = useUIStore();
uiStore.stateIsDirty = true;
dataPinningEventBus.$emit('pin-data', { [payload.node.name]: storedPinData });
},
unpinData(payload: { node: INodeUi }): void {
if (!this.workflow.pinData) {
Vue.set(this.workflow, 'pinData', {});
}
Vue.set(this.workflow.pinData!, payload.node.name, undefined);
delete this.workflow.pinData![payload.node.name];
const uiStore = useUIStore();
uiStore.stateIsDirty = true;
dataPinningEventBus.$emit('unpin-data', { [payload.node.name]: undefined });
},
addConnection(data: { connection: IConnection[], setStateDirty: boolean }): void {
if (data.connection.length !== 2) {
// All connections need two entries
// TODO: Check if there is an error or whatever that is supposed to be returned
return;
}
const uiStore = useUIStore();
if (data.setStateDirty === true) {
uiStore.stateIsDirty = true;
}
const sourceData: IConnection = data.connection[0];
const destinationData: IConnection = data.connection[1];
// Check if source node and type exist already and if not add them
if (!this.workflow.connections.hasOwnProperty(sourceData.node)) {
Vue.set(this.workflow.connections, sourceData.node, {});
}
if (!this.workflow.connections[sourceData.node].hasOwnProperty(sourceData.type)) {
Vue.set(this.workflow.connections[sourceData.node], sourceData.type, []);
}
if (this.workflow.connections[sourceData.node][sourceData.type].length < (sourceData.index + 1)) {
for (let i = this.workflow.connections[sourceData.node][sourceData.type].length; i <= sourceData.index; i++) {
this.workflow.connections[sourceData.node][sourceData.type].push([]);
}
}
// Check if the same connection exists already
const checkProperties = ['index', 'node', 'type'];
let propertyName: string;
let connectionExists = false;
connectionLoop:
for (const existingConnection of this.workflow.connections[sourceData.node][sourceData.type][sourceData.index]) {
for (propertyName of checkProperties) {
if ((existingConnection as any)[propertyName] !== (destinationData as any)[propertyName]) { // tslint:disable-line:no-any
continue connectionLoop;
}
}
connectionExists = true;
break;
}
// Add the new connection if it does not exist already
if (connectionExists === false) {
this.workflow.connections[sourceData.node][sourceData.type][sourceData.index].push(destinationData);
}
},
removeConnection(data: { connection: IConnection[] }): void {
const sourceData = data.connection[0];
const destinationData = data.connection[1];
if (!this.workflow.connections.hasOwnProperty(sourceData.node)) {
return;
}
if (!this.workflow.connections[sourceData.node].hasOwnProperty(sourceData.type)) {
return;
}
if (this.workflow.connections[sourceData.node][sourceData.type].length < (sourceData.index + 1)) {
return;
}
const uiStore = useUIStore();
uiStore.stateIsDirty = true;
const connections = this.workflow.connections[sourceData.node][sourceData.type][sourceData.index];
for (const index in connections) {
if (connections[index].node === destinationData.node && connections[index].type === destinationData.type && connections[index].index === destinationData.index) {
// Found the connection to remove
connections.splice(parseInt(index, 10), 1);
}
}
},
removeAllConnections(data: { setStateDirty: boolean }): void {
if (data && data.setStateDirty === true) {
const uiStore = useUIStore();
uiStore.stateIsDirty = true;
}
this.workflow.connections = {};
},
removeAllNodeConnection(node: INodeUi): void {
const uiStore = useUIStore();
uiStore.stateIsDirty = true;
// Remove all source connections
if (this.workflow.connections.hasOwnProperty(node.name)) {
delete this.workflow.connections[node.name];
}
// Remove all destination connections
const indexesToRemove = [];
let sourceNode: string, type: string, sourceIndex: string, connectionIndex: string, connectionData: IConnection;
for (sourceNode of Object.keys(this.workflow.connections)) {
for (type of Object.keys(this.workflow.connections[sourceNode])) {
for (sourceIndex of Object.keys(this.workflow.connections[sourceNode][type])) {
indexesToRemove.length = 0;
for (connectionIndex of Object.keys(this.workflow.connections[sourceNode][type][parseInt(sourceIndex, 10)])) {
connectionData = this.workflow.connections[sourceNode][type][parseInt(sourceIndex, 10)][parseInt(connectionIndex, 10)];
if (connectionData.node === node.name) {
indexesToRemove.push(connectionIndex);
}
}
indexesToRemove.forEach((index) => {
this.workflow.connections[sourceNode][type][parseInt(sourceIndex, 10)].splice(parseInt(index, 10), 1);
});
}
}
}
},
renameNodeSelectedAndExecution(nameData: { old: string, new: string }): void {
const uiStore = useUIStore();
uiStore.stateIsDirty = true;
// If node has any WorkflowResultData rename also that one that the data
// does still get displayed also after node got renamed
if (this.workflowExecutionData !== null && this.workflowExecutionData.data && this.workflowExecutionData.data.resultData.runData.hasOwnProperty(nameData.old)) {
this.workflowExecutionData.data.resultData.runData[nameData.new] = this.workflowExecutionData.data.resultData.runData[nameData.old];
delete this.workflowExecutionData.data.resultData.runData[nameData.old];
}
// In case the renamed node was last selected set it also there with the new name
if (uiStore.lastSelectedNode === nameData.old) {
uiStore.lastSelectedNode = nameData.new;
}
Vue.set(this.nodeMetadata, nameData.new, this.nodeMetadata[nameData.old]);
Vue.delete(this.nodeMetadata, nameData.old);
if (this.workflow.pinData && this.workflow.pinData.hasOwnProperty(nameData.old)) {
Vue.set(this.workflow.pinData, nameData.new, this.workflow.pinData[nameData.old]);
Vue.delete(this.workflow.pinData, nameData.old);
}
this.workflowExecutionPairedItemMappings = getPairedItemsMapping(this.workflowExecutionData);
},
resetAllNodesIssues(): boolean {
this.workflow.nodes.forEach((node) => {
node.issues = undefined;
});
return true;
},
setNodeIssue(nodeIssueData: INodeIssueData): boolean {
const node = this.workflow.nodes.find(node => {
return node.name === nodeIssueData.node;
});
if (!node) {
return false;
}
if (nodeIssueData.value === null) {
// Remove the value if one exists
if (node.issues === undefined || node.issues[nodeIssueData.type] === undefined) {
// No values for type exist so nothing has to get removed
return true;
}
// @ts-ignore
Vue.delete(node.issues, nodeIssueData.type);
} else {
if (node.issues === undefined) {
Vue.set(node, 'issues', {});
}
// Set/Overwrite the value
Vue.set(node.issues!, nodeIssueData.type, nodeIssueData.value);
}
return true;
},
addNode(nodeData: INodeUi): void {
if (!nodeData.hasOwnProperty('name')) {
// All nodes have to have a name
// TODO: Check if there is an error or whatever that is supposed to be returned
return;
}
this.workflow.nodes.push(nodeData);
},
removeNode(node: INodeUi): void {
Vue.delete(this.nodeMetadata, node.name);
if (this.workflow.pinData && this.workflow.pinData.hasOwnProperty(node.name)) {
Vue.delete(this.workflow.pinData, node.name);
}
for (let i = 0; i < this.workflow.nodes.length; i++) {
if (this.workflow.nodes[i].name === node.name) {
this.workflow.nodes.splice(i, 1);
const uiStore = useUIStore();
uiStore.stateIsDirty = true;
return;
}
}
},
removeAllNodes(data: { setStateDirty: boolean, removePinData: boolean }): void {
if (data.setStateDirty === true) {
const uiStore = useUIStore();
uiStore.stateIsDirty = true;
}
if (data.removePinData) {
Vue.set(this.workflow, 'pinData', {});
}
this.workflow.nodes.splice(0, this.workflow.nodes.length);
this.nodeMetadata = {};
},
updateNodeProperties(updateInformation: INodeUpdatePropertiesInformation): void {
// Find the node that should be updated
const node = this.workflow.nodes.find(node => {
return node.name === updateInformation.name;
});
if (node) {
for (const key of Object.keys(updateInformation.properties)) {
const uiStore = useUIStore();
uiStore.stateIsDirty = true;
Vue.set(node, key, updateInformation.properties[key]);
}
}
},
setNodeValue(updateInformation: IUpdateInformation): void {
// Find the node that should be updated
const node = this.workflow.nodes.find(node => {
return node.name === updateInformation.name;
});
if (node === undefined || node === null || !updateInformation.key) {
throw new Error(`Node with the name "${updateInformation.name}" could not be found to set parameter.`);
}
const uiStore = useUIStore();
uiStore.stateIsDirty = true;
Vue.set(node, updateInformation.key, updateInformation.value);
},
setNodeParameters(updateInformation: IUpdateInformation, append?: boolean): void {
// Find the node that should be updated
const node = this.workflow.nodes.find(node => {
return node.name === updateInformation.name;
});
if (node === undefined || node === null) {
throw new Error(`Node with the name "${updateInformation.name}" could not be found to set parameter.`);
}
const uiStore = useUIStore();
uiStore.stateIsDirty = true;
const newParameters = !!append && isObjectLiteral(updateInformation.value)
? {...node.parameters, ...updateInformation.value }
: updateInformation.value;
Vue.set(node, 'parameters', newParameters);
if (!this.nodeMetadata[node.name]) {
Vue.set(this.nodeMetadata, node.name, {});
}
Vue.set(this.nodeMetadata[node.name], 'parametersLastUpdatedAt', Date.now());
},
setLastNodeParameters(updateInformation: IUpdateInformation) {
const latestNode = this.workflow.nodes.findLast((node) => node.type === updateInformation.key) as INodeUi;
if(latestNode) this.setNodeParameters({...updateInformation, name: latestNode.name}, true);
},
addNodeExecutionData(pushData: IPushDataNodeExecuteAfter): void {
if (this.workflowExecutionData === null || !this.workflowExecutionData.data) {
throw new Error('The "workflowExecutionData" is not initialized!');
}
if (this.workflowExecutionData.data.resultData.runData[pushData.nodeName] === undefined) {
Vue.set(this.workflowExecutionData.data.resultData.runData, pushData.nodeName, []);
}
this.workflowExecutionData.data.resultData.runData[pushData.nodeName].push(pushData.data);
this.workflowExecutionPairedItemMappings = getPairedItemsMapping(this.workflowExecutionData);
},
clearNodeExecutionData(nodeName: string): void {
if (this.workflowExecutionData === null || !this.workflowExecutionData.data) {
return;
}
Vue.delete(this.workflowExecutionData.data.resultData.runData, nodeName);
},
pinDataByNodeName(nodeName: string): INodeExecutionData[] | undefined {
if (!this.workflow.pinData || !this.workflow.pinData[nodeName]) return undefined;
return this.workflow.pinData[nodeName].map(item => item.json) as INodeExecutionData[];
},
activeNode(): INodeUi | null {
// kept here for FE hooks
const ndvStore = useNDVStore();
return ndvStore.activeNode;
},
// Executions actions
addActiveExecution(newActiveExecution: IExecutionsCurrentSummaryExtended) : void {
// Check if the execution exists already
const activeExecution = this.activeExecutions.find(execution => {
return execution.id === newActiveExecution.id;
});
if (activeExecution !== undefined) {
// Exists already so no need to add it again
if (activeExecution.workflowName === undefined) {
activeExecution.workflowName = newActiveExecution.workflowName;
}
return;
}
this.activeExecutions.unshift(newActiveExecution);
},
finishActiveExecution(finishedActiveExecution: IPushDataExecutionFinished) : void {
// Find the execution to set to finished
const activeExecution = this.activeExecutions.find(execution => {
return execution.id === finishedActiveExecution.executionId;
});
if (activeExecution === undefined) {
// The execution could not be found
return;
}
if (finishedActiveExecution.executionId !== undefined) {
Vue.set(activeExecution, 'id', finishedActiveExecution.executionId);
}
Vue.set(activeExecution, 'finished', finishedActiveExecution.data.finished);
Vue.set(activeExecution, 'stoppedAt', finishedActiveExecution.data.stoppedAt);
},
setActiveExecutions(newActiveExecutions: IExecutionsCurrentSummaryExtended[]) : void {
Vue.set(this, 'activeExecutions', newActiveExecutions);
},
async loadCurrentWorkflowExecutions (filter: { finished: boolean, status: string }): Promise<IExecutionsSummary[]> {
let activeExecutions = [];
let finishedExecutions = [];
const requestFilter: IDataObject = { workflowId: this.workflowId };
if (!this.workflowId) {
return [];
}
try {
const rootStore = useRootStore();
if (filter.status === ''|| !filter.finished) {
activeExecutions = await getCurrentExecutions(rootStore.getRestApiContext, requestFilter);
}
if (filter.status === '' || filter.finished) {
if (filter.status === 'waiting') {
requestFilter.waitTill = true;
} else if (filter.status !== '') {
requestFilter.finished = filter.status === 'success';
}
finishedExecutions = await getFinishedExecutions(rootStore.getRestApiContext, requestFilter);
}
// context.commit('setTotalFinishedExecutionsCount', finishedExecutions.count);
return [...activeExecutions, ...finishedExecutions.results || []];
} catch (error) {
throw(error);
}
},
deleteExecution (execution: IExecutionsSummary): void {
this.currentWorkflowExecutions.splice(this.currentWorkflowExecutions.indexOf(execution), 1);
},
},
});