mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-22 04:10:01 +00:00
fix(editor): Properly update workflow info in main header (#9789)
This commit is contained in:
@@ -235,8 +235,6 @@ import {
|
||||
START_NODE_TYPE,
|
||||
STICKY_NODE_TYPE,
|
||||
VIEWS,
|
||||
WEBHOOK_NODE_TYPE,
|
||||
FORM_TRIGGER_NODE_TYPE,
|
||||
TRIGGER_NODE_CREATOR_VIEW,
|
||||
EnterpriseEditionFeature,
|
||||
REGULAR_NODE_CREATOR_VIEW,
|
||||
@@ -279,11 +277,8 @@ import type {
|
||||
ExecutionSummary,
|
||||
INode,
|
||||
INodeConnections,
|
||||
INodeCredentialsDetails,
|
||||
INodeInputConfiguration,
|
||||
INodeTypeDescription,
|
||||
INodeTypeNameVersion,
|
||||
IPinData,
|
||||
ITaskData,
|
||||
ITelemetryTrackProperties,
|
||||
IWorkflowBase,
|
||||
@@ -302,7 +297,6 @@ import {
|
||||
} from 'n8n-workflow';
|
||||
import type {
|
||||
NewConnectionInfo,
|
||||
ICredentialsResponse,
|
||||
IExecutionResponse,
|
||||
IWorkflowDb,
|
||||
IWorkflowData,
|
||||
@@ -350,7 +344,6 @@ import { getAccountAge } from '@/utils/userUtils';
|
||||
import { getConnectionInfo, getNodeViewTab } from '@/utils/canvasUtils';
|
||||
import {
|
||||
AddConnectionCommand,
|
||||
AddNodeCommand,
|
||||
MoveNodeCommand,
|
||||
RemoveConnectionCommand,
|
||||
RemoveNodeCommand,
|
||||
@@ -550,8 +543,6 @@ export default defineComponent({
|
||||
moveCanvasKeyPressed: false,
|
||||
stopExecutionInProgress: false,
|
||||
blankRedirect: false,
|
||||
credentialsUpdated: false,
|
||||
pullConnActiveNodeName: null as string | null,
|
||||
pullConnActive: false,
|
||||
dropPrevented: false,
|
||||
connectionDragScope: {
|
||||
@@ -564,8 +555,6 @@ export default defineComponent({
|
||||
showTriggerMissingTooltip: false,
|
||||
workflowData: null as INewWorkflowData | null,
|
||||
activeConnection: null as null | Connection,
|
||||
isInsertingNodes: false,
|
||||
isProductionExecutionPreview: false,
|
||||
enterTimer: undefined as undefined | ReturnType<typeof setTimeout>,
|
||||
exitTimer: undefined as undefined | ReturnType<typeof setTimeout>,
|
||||
readOnlyNotification: null as null | NotificationHandle,
|
||||
@@ -779,6 +768,9 @@ export default defineComponent({
|
||||
const isCloudDeployment = this.settingsStore.isCloudDeployment;
|
||||
return isCloudDeployment && experimentEnabled && !userHasSeenAIAssistantExperiment;
|
||||
},
|
||||
isProductionExecutionPreview(): boolean {
|
||||
return this.nodeHelpers.isProductionExecutionPreview.value;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// Listen to route changes and load the workflow accordingly
|
||||
@@ -916,7 +908,7 @@ export default defineComponent({
|
||||
|
||||
setTimeout(() => {
|
||||
void this.usersStore.showPersonalizationSurvey();
|
||||
this.addPinDataConnections(this.workflowsStore.pinnedWorkflowData);
|
||||
this.nodeHelpers.addPinDataConnections(this.workflowsStore.pinnedWorkflowData);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
@@ -1007,8 +999,8 @@ export default defineComponent({
|
||||
historyBus.on('revertRenameNode', this.onRevertNameChange);
|
||||
historyBus.on('enableNodeToggle', this.onRevertEnableToggle);
|
||||
|
||||
dataPinningEventBus.on('pin-data', this.addPinDataConnections);
|
||||
dataPinningEventBus.on('unpin-data', this.removePinDataConnections);
|
||||
dataPinningEventBus.on('pin-data', this.nodeHelpers.addPinDataConnections);
|
||||
dataPinningEventBus.on('unpin-data', this.nodeHelpers.removePinDataConnections);
|
||||
nodeViewEventBus.on('saveWorkflow', this.saveCurrentWorkflowExternal);
|
||||
|
||||
this.canvasStore.isDemo = this.isDemo;
|
||||
@@ -1033,8 +1025,8 @@ export default defineComponent({
|
||||
historyBus.off('revertRenameNode', this.onRevertNameChange);
|
||||
historyBus.off('enableNodeToggle', this.onRevertEnableToggle);
|
||||
|
||||
dataPinningEventBus.off('pin-data', this.addPinDataConnections);
|
||||
dataPinningEventBus.off('unpin-data', this.removePinDataConnections);
|
||||
dataPinningEventBus.off('pin-data', this.nodeHelpers.addPinDataConnections);
|
||||
dataPinningEventBus.off('unpin-data', this.nodeHelpers.removePinDataConnections);
|
||||
nodeViewEventBus.off('saveWorkflow', this.saveCurrentWorkflowExternal);
|
||||
},
|
||||
beforeMount() {
|
||||
@@ -1334,7 +1326,7 @@ export default defineComponent({
|
||||
this.workflowsStore.setUsedCredentials(data.workflowData.usedCredentials);
|
||||
}
|
||||
|
||||
await this.addNodes(
|
||||
await this.nodeHelpers.addNodes(
|
||||
deepCopy(data.workflowData.nodes),
|
||||
deepCopy(data.workflowData.connections),
|
||||
);
|
||||
@@ -1405,7 +1397,7 @@ export default defineComponent({
|
||||
this.resetWorkspace();
|
||||
data.workflow.nodes = NodeViewUtils.getFixedNodesList(data.workflow.nodes);
|
||||
|
||||
await this.addNodes(data.workflow.nodes as INodeUi[], data.workflow.connections);
|
||||
await this.nodeHelpers.addNodes(data.workflow.nodes as INodeUi[], data.workflow.connections);
|
||||
|
||||
if (data.workflow.pinData) {
|
||||
this.workflowsStore.setWorkflowPinData(data.workflow.pinData);
|
||||
@@ -1457,7 +1449,7 @@ export default defineComponent({
|
||||
const convertedNodes = data.workflow.nodes.map(
|
||||
this.workflowsStore.convertTemplateNodeToNodeUi,
|
||||
);
|
||||
await this.addNodes(convertedNodes, data.workflow.connections);
|
||||
await this.nodeHelpers.addNodes(convertedNodes, data.workflow.connections);
|
||||
this.workflowData =
|
||||
(await this.workflowsStore.getNewWorkflowData(
|
||||
data.name,
|
||||
@@ -1482,14 +1474,7 @@ export default defineComponent({
|
||||
|
||||
this.resetWorkspace();
|
||||
|
||||
this.workflowsStore.addWorkflow(workflow);
|
||||
this.workflowsStore.setActive(workflow.active || false);
|
||||
this.workflowsStore.setWorkflowId(workflow.id);
|
||||
this.workflowsStore.setWorkflowName({ newName: workflow.name, setStateDirty: false });
|
||||
this.workflowsStore.setWorkflowSettings(workflow.settings ?? {});
|
||||
this.workflowsStore.setWorkflowPinData(workflow.pinData ?? {});
|
||||
this.workflowsStore.setWorkflowVersionId(workflow.versionId);
|
||||
this.workflowsStore.setWorkflowMetadata(workflow.meta);
|
||||
await this.workflowHelpers.initState(workflow);
|
||||
|
||||
if (workflow.sharedWithProjects) {
|
||||
this.workflowsEEStore.setWorkflowSharedWith({
|
||||
@@ -1502,14 +1487,9 @@ export default defineComponent({
|
||||
this.workflowsStore.setUsedCredentials(workflow.usedCredentials);
|
||||
}
|
||||
|
||||
const tags = (workflow.tags ?? []) as ITag[];
|
||||
const tagIds = tags.map((tag) => tag.id);
|
||||
this.workflowsStore.setWorkflowTagIds(tagIds || []);
|
||||
this.tagsStore.upsertTags(tags);
|
||||
await this.nodeHelpers.addNodes(workflow.nodes, workflow.connections);
|
||||
|
||||
await this.addNodes(workflow.nodes, workflow.connections);
|
||||
|
||||
if (!this.credentialsUpdated) {
|
||||
if (!this.nodeHelpers.credentialsUpdated.value) {
|
||||
this.uiStore.stateIsDirty = false;
|
||||
}
|
||||
this.canvasStore.zoomToFit();
|
||||
@@ -2340,7 +2320,7 @@ export default defineComponent({
|
||||
|
||||
this.workflowsStore.addWorkflowTagIds(tagIds);
|
||||
setTimeout(() => {
|
||||
this.addPinDataConnections(this.workflowsStore.pinnedWorkflowData);
|
||||
this.nodeHelpers.addPinDataConnections(this.workflowsStore.pinnedWorkflowData);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -2461,7 +2441,7 @@ export default defineComponent({
|
||||
[defaultCredential.type]: selected,
|
||||
};
|
||||
|
||||
await this.loadNodesProperties(
|
||||
await this.nodeHelpers.loadNodesProperties(
|
||||
[newNodeData].map((node) => ({ name: node.type, version: node.typeVersion })),
|
||||
);
|
||||
const nodeType = this.nodeTypesStore.getNodeType(newNodeData.type, newNodeData.typeVersion);
|
||||
@@ -2703,7 +2683,7 @@ export default defineComponent({
|
||||
newNodeData.webhookId = uuid();
|
||||
}
|
||||
|
||||
await this.addNodes([newNodeData], undefined, trackHistory);
|
||||
await this.nodeHelpers.addNodes([newNodeData], undefined, trackHistory);
|
||||
this.workflowsStore.setNodePristine(newNodeData.name, true);
|
||||
|
||||
this.uiStore.stateIsDirty = true;
|
||||
@@ -2811,7 +2791,7 @@ export default defineComponent({
|
||||
},
|
||||
] as [IConnection, IConnection];
|
||||
|
||||
this.__addConnection(connectionData);
|
||||
this.nodeHelpers.addConnection(connectionData);
|
||||
},
|
||||
async addNode(
|
||||
nodeTypeName: string,
|
||||
@@ -2879,7 +2859,7 @@ export default defineComponent({
|
||||
await this.$nextTick();
|
||||
|
||||
if (lastSelectedConnection?.__meta) {
|
||||
this.__deleteJSPlumbConnection(lastSelectedConnection, trackHistory);
|
||||
this.nodeHelpers.deleteJSPlumbConnection(lastSelectedConnection, trackHistory);
|
||||
|
||||
const targetNodeName = lastSelectedConnection.__meta.targetNodeName;
|
||||
const targetOutputIndex = lastSelectedConnection.__meta.targetOutputIndex;
|
||||
@@ -2980,7 +2960,7 @@ export default defineComponent({
|
||||
this.dropPrevented = false;
|
||||
return;
|
||||
}
|
||||
if (this.pullConnActiveNodeName) {
|
||||
if (this.nodeHelpers.pullConnActiveNodeName.value) {
|
||||
const sourceNode = this.workflowsStore.getNodeById(connection.parameters.nodeId);
|
||||
const connectionType = connection.parameters.type ?? NodeConnectionType.Main;
|
||||
const overrideTargetEndpoint = connection?.connector
|
||||
@@ -2988,8 +2968,12 @@ export default defineComponent({
|
||||
|
||||
if (sourceNode) {
|
||||
const isTarget = connection.parameters.connection === 'target';
|
||||
const sourceNodeName = isTarget ? this.pullConnActiveNodeName : sourceNode.name;
|
||||
const targetNodeName = isTarget ? sourceNode.name : this.pullConnActiveNodeName;
|
||||
const sourceNodeName = isTarget
|
||||
? this.nodeHelpers.pullConnActiveNodeName.value
|
||||
: sourceNode.name;
|
||||
const targetNodeName = isTarget
|
||||
? sourceNode.name
|
||||
: this.nodeHelpers.pullConnActiveNodeName.value;
|
||||
const outputIndex = connection.parameters.index;
|
||||
NodeViewUtils.resetConnectionAfterPull(connection);
|
||||
await this.$nextTick();
|
||||
@@ -3001,7 +2985,7 @@ export default defineComponent({
|
||||
overrideTargetEndpoint?.parameters?.index ?? 0,
|
||||
connectionType,
|
||||
);
|
||||
this.pullConnActiveNodeName = null;
|
||||
this.nodeHelpers.pullConnActiveNodeName.value = null;
|
||||
this.dropPrevented = false;
|
||||
}
|
||||
return;
|
||||
@@ -3149,7 +3133,7 @@ export default defineComponent({
|
||||
)
|
||||
) {
|
||||
this.dropPrevented = true;
|
||||
this.pullConnActiveNodeName = null;
|
||||
this.nodeHelpers.pullConnActiveNodeName.value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3213,7 +3197,7 @@ export default defineComponent({
|
||||
info.connection,
|
||||
() => {
|
||||
this.activeConnection = null;
|
||||
this.__deleteJSPlumbConnection(info.connection);
|
||||
this.nodeHelpers.deleteJSPlumbConnection(info.connection);
|
||||
},
|
||||
() => {
|
||||
this.insertNodeAfterSelected({
|
||||
@@ -3245,7 +3229,7 @@ export default defineComponent({
|
||||
}
|
||||
// When we add multiple nodes, this event could be fired hundreds of times for large workflows.
|
||||
// And because the updateNodesInputIssues() method is quite expensive, we only call it if not in insert mode
|
||||
if (!this.isInsertingNodes) {
|
||||
if (!this.nodeHelpers.isInsertingNodes.value) {
|
||||
this.nodeHelpers.updateNodesInputIssues();
|
||||
this.resetEndpointsErrors();
|
||||
setTimeout(() => {
|
||||
@@ -3263,17 +3247,6 @@ export default defineComponent({
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
addConectionsTestData() {
|
||||
this.instance.connections.forEach((connection) => {
|
||||
NodeViewUtils.addConnectionTestData(
|
||||
connection.source,
|
||||
connection.target,
|
||||
connection?.connector?.hasOwnProperty('canvas')
|
||||
? connection?.connector.canvas
|
||||
: undefined,
|
||||
);
|
||||
});
|
||||
},
|
||||
onDragMove() {
|
||||
const totalNodes = this.nodes.length;
|
||||
void this.callDebounced(this.updateConnectionsOverlays, {
|
||||
@@ -3390,7 +3363,7 @@ export default defineComponent({
|
||||
},
|
||||
] as [IConnection, IConnection];
|
||||
|
||||
this.__removeConnection(connectionInfo, false);
|
||||
this.nodeHelpers.removeConnection(connectionInfo, false);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -3420,9 +3393,9 @@ export default defineComponent({
|
||||
NodeViewUtils.showOutputNameLabel(info.sourceEndpoint, info.connection);
|
||||
|
||||
info.connection.removeOverlays();
|
||||
this.__removeConnectionByConnectionInfo(info, false, false);
|
||||
this.nodeHelpers.removeConnectionByConnectionInfo(info, false, false);
|
||||
|
||||
if (this.pullConnActiveNodeName) {
|
||||
if (this.nodeHelpers.pullConnActiveNodeName.value) {
|
||||
// establish new connection when dragging connection from one node to another
|
||||
this.historyStore.startRecordingUndo();
|
||||
const sourceNode = this.workflowsStore.getNodeById(info.connection.parameters.nodeId);
|
||||
@@ -3442,11 +3415,11 @@ export default defineComponent({
|
||||
this.connectTwoNodes(
|
||||
sourceNodeName,
|
||||
outputIndex,
|
||||
this.pullConnActiveNodeName,
|
||||
this.nodeHelpers.pullConnActiveNodeName.value,
|
||||
overrideTargetEndpoint?.parameters?.index ?? 0,
|
||||
NodeConnectionType.Main,
|
||||
);
|
||||
this.pullConnActiveNodeName = null;
|
||||
this.nodeHelpers.pullConnActiveNodeName.value = null;
|
||||
await this.$nextTick();
|
||||
this.historyStore.stopRecordingUndo();
|
||||
} else if (
|
||||
@@ -3470,7 +3443,7 @@ export default defineComponent({
|
||||
// manually
|
||||
connection.overlays['midpoint-arrow']?.setVisible(false);
|
||||
try {
|
||||
this.pullConnActiveNodeName = null;
|
||||
this.nodeHelpers.pullConnActiveNodeName.value = null;
|
||||
this.pullConnActive = true;
|
||||
this.canvasStore.newNodeInsertPosition = null;
|
||||
NodeViewUtils.hideConnectionActions(connection);
|
||||
@@ -3555,12 +3528,12 @@ export default defineComponent({
|
||||
const endpoint = intersectingEndpoint.jtk.endpoint as Endpoint;
|
||||
const node = this.workflowsStore.getNodeById(endpoint.parameters.nodeId);
|
||||
|
||||
this.pullConnActiveNodeName = node?.name ?? null;
|
||||
this.nodeHelpers.pullConnActiveNodeName.value = node?.name ?? null;
|
||||
|
||||
NodeViewUtils.showDropConnectionState(connection, endpoint);
|
||||
} else {
|
||||
NodeViewUtils.showPullConnectionState(connection);
|
||||
this.pullConnActiveNodeName = null;
|
||||
this.nodeHelpers.pullConnActiveNodeName.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3762,7 +3735,7 @@ export default defineComponent({
|
||||
) {
|
||||
const manualTriggerNode = this.canvasStore.getAutoAddManualTriggerNode();
|
||||
if (manualTriggerNode) {
|
||||
await this.addNodes([manualTriggerNode]);
|
||||
await this.nodeHelpers.addNodes([manualTriggerNode]);
|
||||
this.uiStore.lastSelectedNode = manualTriggerNode.name;
|
||||
}
|
||||
}
|
||||
@@ -3857,130 +3830,6 @@ export default defineComponent({
|
||||
// waiting in the store and display them
|
||||
this.showNotificationForViews([VIEWS.WORKFLOW, VIEWS.NEW_WORKFLOW]);
|
||||
},
|
||||
getOutputEndpointUUID(
|
||||
nodeName: string,
|
||||
connectionType: NodeConnectionType,
|
||||
index: number,
|
||||
): string | null {
|
||||
const node = this.workflowsStore.getNodeByName(nodeName);
|
||||
if (!node) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return NodeViewUtils.getOutputEndpointUUID(node.id, connectionType, index);
|
||||
},
|
||||
getInputEndpointUUID(nodeName: string, connectionType: NodeConnectionType, index: number) {
|
||||
const node = this.workflowsStore.getNodeByName(nodeName);
|
||||
if (!node) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return NodeViewUtils.getInputEndpointUUID(node.id, connectionType, index);
|
||||
},
|
||||
__addConnection(connection: [IConnection, IConnection]) {
|
||||
const outputUuid = this.getOutputEndpointUUID(
|
||||
connection[0].node,
|
||||
connection[0].type,
|
||||
connection[0].index,
|
||||
);
|
||||
const inputUuid = this.getInputEndpointUUID(
|
||||
connection[1].node,
|
||||
connection[1].type,
|
||||
connection[1].index,
|
||||
);
|
||||
if (!outputUuid || !inputUuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uuid: [string, string] = [outputUuid, inputUuid];
|
||||
// Create connections in DOM
|
||||
this.instance?.connect({
|
||||
uuids: uuid,
|
||||
detachable: !this.isReadOnlyRoute && !this.readOnlyEnv,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.addPinDataConnections(this.workflowsStore.pinnedWorkflowData);
|
||||
});
|
||||
},
|
||||
__removeConnection(connection: [IConnection, IConnection], removeVisualConnection = false) {
|
||||
if (removeVisualConnection) {
|
||||
const sourceNode = this.workflowsStore.getNodeByName(connection[0].node);
|
||||
const targetNode = this.workflowsStore.getNodeByName(connection[1].node);
|
||||
|
||||
if (!sourceNode || !targetNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sourceElement = document.getElementById(sourceNode.id);
|
||||
const targetElement = document.getElementById(targetNode.id);
|
||||
|
||||
if (sourceElement && targetElement) {
|
||||
const connections = this.instance?.getConnections({
|
||||
source: sourceElement,
|
||||
target: targetElement,
|
||||
});
|
||||
|
||||
if (Array.isArray(connections)) {
|
||||
connections.forEach((connectionInstance: Connection) => {
|
||||
if (connectionInstance.__meta) {
|
||||
// Only delete connections from specific indexes (if it can be determined by meta)
|
||||
if (
|
||||
connectionInstance.__meta.sourceOutputIndex === connection[0].index &&
|
||||
connectionInstance.__meta.targetOutputIndex === connection[1].index
|
||||
) {
|
||||
this.__deleteJSPlumbConnection(connectionInstance);
|
||||
}
|
||||
} else {
|
||||
this.__deleteJSPlumbConnection(connectionInstance);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.workflowsStore.removeConnection({ connection });
|
||||
},
|
||||
__deleteJSPlumbConnection(connection: Connection, trackHistory = false) {
|
||||
// Make sure to remove the overlay else after the second move
|
||||
// it visibly stays behind free floating without a connection.
|
||||
connection.removeOverlays();
|
||||
|
||||
this.pullConnActiveNodeName = null; // prevent new connections when connectionDetached is triggered
|
||||
this.instance?.deleteConnection(connection); // on delete, triggers connectionDetached event which applies mutation to store
|
||||
if (trackHistory && connection.__meta) {
|
||||
const connectionData: [IConnection, IConnection] = [
|
||||
{
|
||||
index: connection.__meta?.sourceOutputIndex,
|
||||
node: connection.__meta.sourceNodeName,
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
{
|
||||
index: connection.__meta?.targetOutputIndex,
|
||||
node: connection.__meta.targetNodeName,
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
];
|
||||
const removeCommand = new RemoveConnectionCommand(connectionData);
|
||||
this.historyStore.pushCommandToUndo(removeCommand);
|
||||
}
|
||||
},
|
||||
__removeConnectionByConnectionInfo(
|
||||
info: ConnectionDetachedParams,
|
||||
removeVisualConnection = false,
|
||||
trackHistory = false,
|
||||
) {
|
||||
const connectionInfo: [IConnection, IConnection] | null = getConnectionInfo(info);
|
||||
|
||||
if (connectionInfo) {
|
||||
if (removeVisualConnection) {
|
||||
this.__deleteJSPlumbConnection(info.connection, trackHistory);
|
||||
} else if (trackHistory) {
|
||||
this.historyStore.pushCommandToUndo(new RemoveConnectionCommand(connectionInfo));
|
||||
}
|
||||
this.workflowsStore.removeConnection({ connection: connectionInfo });
|
||||
}
|
||||
},
|
||||
async duplicateNodes(nodes: INode[]): Promise<void> {
|
||||
if (!this.editAllowedCheck()) {
|
||||
return;
|
||||
@@ -4310,7 +4159,11 @@ export default defineComponent({
|
||||
await this.$nextTick();
|
||||
|
||||
// Add the new updated nodes
|
||||
await this.addNodes(Object.values(workflow.nodes), workflow.connectionsBySourceNode, false);
|
||||
await this.nodeHelpers.addNodes(
|
||||
Object.values(workflow.nodes),
|
||||
workflow.connectionsBySourceNode,
|
||||
false,
|
||||
);
|
||||
|
||||
// Make sure that the node is selected again
|
||||
this.deselectAllNodes();
|
||||
@@ -4337,188 +4190,6 @@ export default defineComponent({
|
||||
this.instance.deleteEveryConnection({ fireEvent: true });
|
||||
}
|
||||
},
|
||||
matchCredentials(node: INodeUi) {
|
||||
if (!node.credentials) {
|
||||
return;
|
||||
}
|
||||
Object.entries(node.credentials).forEach(
|
||||
([nodeCredentialType, nodeCredentials]: [string, INodeCredentialsDetails]) => {
|
||||
const credentialOptions = this.credentialsStore.getCredentialsByType(nodeCredentialType);
|
||||
|
||||
// Check if workflows applies old credentials style
|
||||
if (typeof nodeCredentials === 'string') {
|
||||
nodeCredentials = {
|
||||
id: null,
|
||||
name: nodeCredentials,
|
||||
};
|
||||
this.credentialsUpdated = true;
|
||||
}
|
||||
|
||||
if (nodeCredentials.id) {
|
||||
// Check whether the id is matching with a credential
|
||||
const credentialsId = nodeCredentials.id.toString(); // due to a fixed bug in the migration UpdateWorkflowCredentials (just sqlite) we have to cast to string and check later if it has been a number
|
||||
const credentialsForId = credentialOptions.find(
|
||||
(optionData: ICredentialsResponse) => optionData.id === credentialsId,
|
||||
);
|
||||
if (credentialsForId) {
|
||||
if (
|
||||
credentialsForId.name !== nodeCredentials.name ||
|
||||
typeof nodeCredentials.id === 'number'
|
||||
) {
|
||||
node.credentials![nodeCredentialType] = {
|
||||
id: credentialsForId.id,
|
||||
name: credentialsForId.name,
|
||||
};
|
||||
this.credentialsUpdated = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No match for id found or old credentials type used
|
||||
node.credentials![nodeCredentialType] = nodeCredentials;
|
||||
|
||||
// check if only one option with the name would exist
|
||||
const credentialsForName = credentialOptions.filter(
|
||||
(optionData: ICredentialsResponse) => optionData.name === nodeCredentials.name,
|
||||
);
|
||||
|
||||
// only one option exists for the name, take it
|
||||
if (credentialsForName.length === 1) {
|
||||
node.credentials![nodeCredentialType].id = credentialsForName[0].id;
|
||||
this.credentialsUpdated = true;
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
async addNodes(nodes: INodeUi[], connections?: IConnections, trackHistory = false) {
|
||||
if (!nodes?.length) {
|
||||
return;
|
||||
}
|
||||
this.isInsertingNodes = true;
|
||||
// Before proceeding we must check if all nodes contain the `properties` attribute.
|
||||
// Nodes are loaded without this information so we must make sure that all nodes
|
||||
// being added have this information.
|
||||
await this.loadNodesProperties(
|
||||
nodes.map((node) => ({ name: node.type, version: node.typeVersion })),
|
||||
);
|
||||
|
||||
// Add the node to the node-list
|
||||
let nodeType: INodeTypeDescription | null;
|
||||
nodes.forEach((node) => {
|
||||
const newNode: INodeUi = {
|
||||
...node,
|
||||
};
|
||||
|
||||
if (!newNode.id) {
|
||||
newNode.id = uuid();
|
||||
}
|
||||
|
||||
nodeType = this.nodeTypesStore.getNodeType(newNode.type, newNode.typeVersion);
|
||||
|
||||
// Make sure that some properties always exist
|
||||
if (!newNode.hasOwnProperty('disabled')) {
|
||||
newNode.disabled = false;
|
||||
}
|
||||
|
||||
if (!newNode.hasOwnProperty('parameters')) {
|
||||
newNode.parameters = {};
|
||||
}
|
||||
|
||||
// Load the default parameter values because only values which differ
|
||||
// from the defaults get saved
|
||||
if (nodeType !== null) {
|
||||
let nodeParameters = null;
|
||||
try {
|
||||
nodeParameters = NodeHelpers.getNodeParameters(
|
||||
nodeType.properties,
|
||||
newNode.parameters,
|
||||
true,
|
||||
false,
|
||||
node,
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(
|
||||
this.$locale.baseText('nodeView.thereWasAProblemLoadingTheNodeParametersOfNode') +
|
||||
`: "${newNode.name}"`,
|
||||
);
|
||||
console.error(e);
|
||||
}
|
||||
newNode.parameters = nodeParameters ?? {};
|
||||
|
||||
// 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(newNode.type) &&
|
||||
newNode.parameters.path === ''
|
||||
) {
|
||||
newNode.parameters.path = newNode.webhookId as string;
|
||||
}
|
||||
}
|
||||
|
||||
// check and match credentials, apply new format if old is used
|
||||
this.matchCredentials(newNode);
|
||||
this.workflowsStore.addNode(newNode);
|
||||
if (trackHistory) {
|
||||
this.historyStore.pushCommandToUndo(new AddNodeCommand(newNode));
|
||||
}
|
||||
});
|
||||
|
||||
// Wait for the nodes to be rendered
|
||||
await this.$nextTick();
|
||||
|
||||
this.instance?.setSuspendDrawing(true);
|
||||
|
||||
if (connections) {
|
||||
await this.addConnections(connections);
|
||||
}
|
||||
// Add the node issues at the end as the node-connections are required
|
||||
this.nodeHelpers.refreshNodeIssues();
|
||||
this.nodeHelpers.updateNodesInputIssues();
|
||||
this.resetEndpointsErrors();
|
||||
this.isInsertingNodes = false;
|
||||
|
||||
// Now it can draw again
|
||||
this.instance?.setSuspendDrawing(false, true);
|
||||
},
|
||||
async addConnections(connections: IConnections) {
|
||||
const batchedConnectionData: Array<[IConnection, IConnection]> = [];
|
||||
|
||||
for (const sourceNode in connections) {
|
||||
for (const type in connections[sourceNode]) {
|
||||
connections[sourceNode][type].forEach((outwardConnections, sourceIndex) => {
|
||||
if (outwardConnections) {
|
||||
outwardConnections.forEach((targetData) => {
|
||||
batchedConnectionData.push([
|
||||
{
|
||||
node: sourceNode,
|
||||
type: getEndpointScope(type) ?? NodeConnectionType.Main,
|
||||
index: sourceIndex,
|
||||
},
|
||||
{ node: targetData.node, type: targetData.type, index: targetData.index },
|
||||
]);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Process the connections in batches
|
||||
await this.processConnectionBatch(batchedConnectionData);
|
||||
setTimeout(this.addConectionsTestData, 0);
|
||||
},
|
||||
|
||||
async processConnectionBatch(batchedConnectionData: Array<[IConnection, IConnection]>) {
|
||||
const batchSize = 100;
|
||||
|
||||
for (let i = 0; i < batchedConnectionData.length; i += batchSize) {
|
||||
const batch = batchedConnectionData.slice(i, i + batchSize);
|
||||
|
||||
batch.forEach((connectionData) => {
|
||||
this.__addConnection(connectionData);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
async addNodesToWorkflow(data: IWorkflowDataUpdate): Promise<IWorkflowDataUpdate> {
|
||||
// Because nodes with the same name maybe already exist, it could
|
||||
// be needed that they have to be renamed. Also could it be possible
|
||||
@@ -4544,7 +4215,7 @@ export default defineComponent({
|
||||
let newName: string;
|
||||
const createNodes: INode[] = [];
|
||||
|
||||
await this.loadNodesProperties(
|
||||
await this.nodeHelpers.loadNodesProperties(
|
||||
data.nodes.map((node) => ({ name: node.type, version: node.typeVersion })),
|
||||
);
|
||||
|
||||
@@ -4660,7 +4331,7 @@ export default defineComponent({
|
||||
|
||||
// Add the nodes with the changed node names, expressions and connections
|
||||
this.historyStore.startRecordingUndo();
|
||||
await this.addNodes(
|
||||
await this.nodeHelpers.addNodes(
|
||||
Object.values(tempWorkflow.nodes),
|
||||
tempWorkflow.connectionsBySourceNode,
|
||||
true,
|
||||
@@ -4784,7 +4455,7 @@ export default defineComponent({
|
||||
this.uiStore.resetSelectedNodes();
|
||||
this.uiStore.nodeViewOffsetPosition = [0, 0];
|
||||
|
||||
this.credentialsUpdated = false;
|
||||
this.nodeHelpers.credentialsUpdated.value = false;
|
||||
},
|
||||
async loadActiveWorkflows(): Promise<void> {
|
||||
await this.workflowsStore.fetchActiveWorkflows();
|
||||
@@ -4824,30 +4495,6 @@ export default defineComponent({
|
||||
async loadSecrets(): Promise<void> {
|
||||
await this.externalSecretsStore.fetchAllSecrets();
|
||||
},
|
||||
async loadNodesProperties(nodeInfos: INodeTypeNameVersion[]): Promise<void> {
|
||||
const allNodes: INodeTypeDescription[] = this.nodeTypesStore.allNodeTypes;
|
||||
|
||||
const nodesToBeFetched: INodeTypeNameVersion[] = [];
|
||||
allNodes.forEach((node) => {
|
||||
const nodeVersions = Array.isArray(node.version) ? node.version : [node.version];
|
||||
if (
|
||||
!!nodeInfos.find((n) => n.name === node.name && nodeVersions.includes(n.version)) &&
|
||||
!node.hasOwnProperty('properties')
|
||||
) {
|
||||
nodesToBeFetched.push({
|
||||
name: node.name,
|
||||
version: Array.isArray(node.version) ? node.version.slice(-1)[0] : node.version,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (nodesToBeFetched.length > 0) {
|
||||
// Only call API if node information is actually missing
|
||||
this.canvasStore.startLoading();
|
||||
await this.nodeTypesStore.getNodesInformation(nodesToBeFetched);
|
||||
this.canvasStore.stopLoading();
|
||||
}
|
||||
},
|
||||
async onPostMessageReceived(message: MessageEvent) {
|
||||
if (!message || typeof message.data !== 'string' || !message.data?.includes?.('"command"')) {
|
||||
return;
|
||||
@@ -4880,7 +4527,7 @@ export default defineComponent({
|
||||
try {
|
||||
// If this NodeView is used in preview mode (in iframe) it will not have access to the main app store
|
||||
// so everything it needs has to be sent using post messages and passed down to child components
|
||||
this.isProductionExecutionPreview = json.executionMode !== 'manual';
|
||||
this.nodeHelpers.isProductionExecutionPreview.value = json.executionMode !== 'manual';
|
||||
|
||||
await this.openExecution(json.executionId);
|
||||
this.canOpenNDV = json.canOpenNDV ?? true;
|
||||
@@ -4918,73 +4565,6 @@ export default defineComponent({
|
||||
await this.importWorkflowData(workflowData, 'url');
|
||||
}
|
||||
},
|
||||
addPinDataConnections(pinData?: IPinData) {
|
||||
if (!pinData) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(pinData).forEach((nodeName) => {
|
||||
const node = this.workflowsStore.getNodeByName(nodeName);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nodeElement = document.getElementById(node.id);
|
||||
if (!nodeElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasRun = this.workflowsStore.getWorkflowResultDataByNodeName(nodeName) !== null;
|
||||
// In case we are showing a production execution preview we want
|
||||
// to show pinned data connections as they wouldn't have been pinned
|
||||
const classNames = this.isProductionExecutionPreview ? [] : ['pinned'];
|
||||
|
||||
if (hasRun) {
|
||||
classNames.push('has-run');
|
||||
}
|
||||
|
||||
const connections = this.instance?.getConnections({
|
||||
source: nodeElement,
|
||||
});
|
||||
|
||||
const connectionsArray = Array.isArray(connections)
|
||||
? connections
|
||||
: Object.values(connections);
|
||||
|
||||
connectionsArray.forEach((connection) => {
|
||||
NodeViewUtils.addConnectionOutputSuccess(connection, {
|
||||
total: pinData[nodeName].length,
|
||||
iterations: 0,
|
||||
classNames,
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
removePinDataConnections(pinData: IPinData) {
|
||||
Object.keys(pinData).forEach((nodeName) => {
|
||||
const node = this.workflowsStore.getNodeByName(nodeName);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nodeElement = document.getElementById(node.id);
|
||||
if (!nodeElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const connections = this.instance?.getConnections({
|
||||
source: nodeElement,
|
||||
});
|
||||
|
||||
const connectionsArray = Array.isArray(connections)
|
||||
? connections
|
||||
: Object.values(connections);
|
||||
|
||||
this.instance.setSuspendDrawing(true);
|
||||
connectionsArray.forEach(NodeViewUtils.resetConnection);
|
||||
this.instance.setSuspendDrawing(false, true);
|
||||
});
|
||||
},
|
||||
onToggleNodeCreator({ source, createNodeActive, nodeCreatorView }: ToggleNodeCreatorOptions) {
|
||||
if (createNodeActive === this.createNodeActive) return;
|
||||
|
||||
@@ -5077,7 +4657,7 @@ export default defineComponent({
|
||||
});
|
||||
}
|
||||
|
||||
this.addPinDataConnections(this.workflowsStore.pinnedWorkflowData);
|
||||
this.nodeHelpers.addPinDataConnections(this.workflowsStore.pinnedWorkflowData);
|
||||
},
|
||||
|
||||
async saveCurrentWorkflowExternal(callback: () => void) {
|
||||
@@ -5108,16 +4688,16 @@ export default defineComponent({
|
||||
// For some reason, returning node to canvas with old id
|
||||
// makes it's endpoint to render at wrong position
|
||||
node.id = uuid();
|
||||
await this.addNodes([node]);
|
||||
await this.nodeHelpers.addNodes([node]);
|
||||
},
|
||||
onRevertAddConnection({ connection }: { connection: [IConnection, IConnection] }) {
|
||||
this.suspendRecordingDetachedConnections = true;
|
||||
this.__removeConnection(connection, true);
|
||||
this.nodeHelpers.removeConnection(connection, true);
|
||||
this.suspendRecordingDetachedConnections = false;
|
||||
},
|
||||
async onRevertRemoveConnection({ connection }: { connection: [IConnection, IConnection] }) {
|
||||
this.suspendRecordingDetachedConnections = true;
|
||||
this.__addConnection(connection);
|
||||
this.nodeHelpers.addConnection(connection);
|
||||
this.suspendRecordingDetachedConnections = false;
|
||||
},
|
||||
async onRevertNameChange({ currentName, newName }: { currentName: string; newName: string }) {
|
||||
|
||||
Reference in New Issue
Block a user