refactor: Add node IDs (#3788)

* update type

* add id to new nodes

* update paste/import behavior

* update duplicate/copy

* update duplicate workflow

* update import functions + templates

* add instance id on copy

* on download add instance id

* simplify for testing

* update telemetry events

* add ids to nodegraph

* not if same instance

* update spacing

* fix tests

* update tests

* add uuid

* fix tests

update tests

add uuid

fix ts issue

* fix telemetry event

* update workflow import

* update public api

* add sqlit migration

* on workflow update

* add psql migration

* add mysql migration

* revert to title

* fix telemetry bug

* remove console log

* remove migration logs

* fix copy/paste bug

* replace node index with node id

* remove console log

* address PR feedback

* address comment

* fix type issue

* fix select

* update schema

* fix ts issue

* update tel helpers

* fix eslint issues
This commit is contained in:
Mutasem Aldmour
2022-08-03 13:06:53 +02:00
committed by GitHub
parent b5ea666ecf
commit 679a443a0c
40 changed files with 602 additions and 157 deletions

View File

@@ -117,7 +117,7 @@ export default mixins(showMessage, workflowHelpers).extend({
this.$data.isSaving = true;
const saved = await this.saveAsNewWorkflow({name, tags: this.currentTagIds, resetWebhookUrls: true, openInNewWindow: true});
const saved = await this.saveAsNewWorkflow({name, tags: this.currentTagIds, resetWebhookUrls: true, openInNewWindow: true, resetNodeIds: true});
if (saved) {
this.closeDialog();

View File

@@ -183,7 +183,7 @@ import {
IExecutionResponse,
IWorkflowDataUpdate,
IMenuItem,
IUser,
IWorkflowToShare,
} from '../Interface';
import ExecutionsList from '@/components/ExecutionsList.vue';
@@ -442,7 +442,6 @@ export default mixins(
return;
}
this.$telemetry.track('User imported workflow', { source: 'file', workflow_id: this.$store.getters.workflowId });
this.$root.$emit('importWorkflowData', { data: worflowData });
};
@@ -513,8 +512,11 @@ export default mixins(
data.id = parseInt(data.id, 10);
}
const exportData: IWorkflowDataUpdate = {
const exportData: IWorkflowToShare = {
...data,
meta: {
instanceId: this.$store.getters.instanceId,
},
tags: (tags || []).map(tagId => {
const {usageCount, ...tag} = this.$store.getters["tags/getTagById"](tagId);

View File

@@ -1,5 +1,5 @@
<template>
<div class="node-wrapper" :style="nodePosition">
<div class="node-wrapper" :style="nodePosition" :id="nodeId">
<div class="select-background" v-show="isSelected"></div>
<div :class="{'node-default': true, 'touch-active': isTouchActive, 'is-touch-device': isTouchDevice}" :data-name="data.name" :ref="data.name">
<div :class="nodeClass" :style="nodeStyle" @dblclick="setNodeActive" @click.left="mouseLeftClick" v-touch:start="touchStart" v-touch:end="touchEnd">

View File

@@ -1,5 +1,5 @@
<template>
<div class="sticky-wrapper" :style="stickyPosition">
<div class="sticky-wrapper" :style="stickyPosition" :id="nodeId">
<div
:class="{'sticky-default': true, 'touch-active': isTouchActive, 'is-touch-device': isTouchDevice}"
:style="stickySize"
@@ -18,7 +18,7 @@
:height="node.parameters.height"
:width="node.parameters.width"
:scale="nodeViewScale"
:id="nodeIndex"
:id="node.id"
:readOnly="isReadOnly"
:defaultText="defaultText"
:editMode="isActive && !isReadOnly"
@@ -165,9 +165,9 @@ export default mixins(externalHooks, nodeBase, nodeHelpers, workflowHelpers).ext
if (!this.isSelected && this.node) {
this.$emit('nodeSelected', this.node.name, false, true);
}
const nodeIndex = this.$store.getters.getNodeIndex(this.data.name);
const nodeIdName = `node-${nodeIndex}`;
this.instance.destroyDraggable(nodeIdName); // todo
if (this.node) {
this.instance.destroyDraggable(this.node.id); // todo avoid destroying if possible
}
},
onResize({height, width, dX, dY}: { width: number, height: number, dX: number, dY: number }) {
if (!this.node) {

View File

@@ -3,12 +3,10 @@ import { INodeUi, XYPosition } from '@/Interface';
import mixins from 'vue-typed-mixins';
import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers';
import { nodeIndex } from '@/components/mixins/nodeIndex';
import { getMousePosition, getRelativePosition } from '@/views/canvasHelpers';
export const mouseSelect = mixins(
deviceSupportHelpers,
nodeIndex,
).extend({
data () {
return {
@@ -171,18 +169,15 @@ export const mouseSelect = mixins(
this.updateSelectBox(e);
},
nodeDeselected (node: INodeUi) {
this.$store.commit('removeNodeFromSelection', node);
const nodeElement = `node-${this.getNodeIndex(node.name)}`;
// @ts-ignore
this.instance.removeFromDragSelection(nodeElement);
this.instance.removeFromDragSelection(node.id);
},
nodeSelected (node: INodeUi) {
this.$store.commit('addSelectedNode', node);
const nodeElement = `node-${this.getNodeIndex(node.name)}`;
// @ts-ignore
this.instance.addToDragSelection(nodeElement);
this.instance.addToDragSelection(node.id);
},
deselectAllNodes () {
// @ts-ignore

View File

@@ -2,12 +2,10 @@ import mixins from 'vue-typed-mixins';
// @ts-ignore
import normalizeWheel from 'normalize-wheel';
import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers';
import { nodeIndex } from '@/components/mixins/nodeIndex';
import { getMousePosition } from '@/views/canvasHelpers';
export const moveNodeWorkflow = mixins(
deviceSupportHelpers,
nodeIndex,
).extend({
data () {
return {

View File

@@ -3,8 +3,7 @@ import { IEndpointOptions, INodeUi, XYPosition } from '@/Interface';
import mixins from 'vue-typed-mixins';
import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers';
import { nodeIndex } from '@/components/mixins/nodeIndex';
import { NODE_NAME_PREFIX, NO_OP_NODE_TYPE, STICKY_NODE_TYPE } from '@/constants';
import { NO_OP_NODE_TYPE, STICKY_NODE_TYPE } from '@/constants';
import * as CanvasHelpers from '@/views/canvasHelpers';
import { Endpoint } from 'jsplumb';
@@ -15,7 +14,6 @@ import { getStyleTokenValue } from '../helpers';
export const nodeBase = mixins(
deviceSupportHelpers,
nodeIndex,
).extend({
mounted () {
// Initialize the node
@@ -28,10 +26,7 @@ export const nodeBase = mixins(
return this.$store.getters.getNodeByName(this.name);
},
nodeId (): string {
return NODE_NAME_PREFIX + this.nodeIndex;
},
nodeIndex (): string {
return this.$store.getters.getNodeIndex(this.data.name).toString();
return this.data.id;
},
},
props: [
@@ -62,7 +57,7 @@ export const nodeBase = mixins(
const anchorPosition = CanvasHelpers.ANCHOR_POSITIONS.input[nodeTypeData.inputs.length][index];
const newEndpointData: IEndpointOptions = {
uuid: CanvasHelpers.getInputEndpointUUID(this.nodeIndex, index),
uuid: CanvasHelpers.getInputEndpointUUID(this.nodeId, index),
anchor: anchorPosition,
maxConnections: -1,
endpoint: 'Rectangle',
@@ -71,7 +66,7 @@ export const nodeBase = mixins(
isSource: false,
isTarget: !this.isReadOnly && nodeTypeData.inputs.length > 1, // only enabled for nodes with multiple inputs.. otherwise attachment handled by connectionDrag event in NodeView,
parameters: {
nodeIndex: this.nodeIndex,
nodeId: this.nodeId,
type: inputName,
index,
},
@@ -130,7 +125,7 @@ export const nodeBase = mixins(
const anchorPosition = CanvasHelpers.ANCHOR_POSITIONS.output[nodeTypeData.outputs.length][index];
const newEndpointData: IEndpointOptions = {
uuid: CanvasHelpers.getOutputEndpointUUID(this.nodeIndex, index),
uuid: CanvasHelpers.getOutputEndpointUUID(this.nodeId, index),
anchor: anchorPosition,
maxConnections: -1,
endpoint: 'Dot',
@@ -140,7 +135,7 @@ export const nodeBase = mixins(
isTarget: false,
enabled: !this.isReadOnly,
parameters: {
nodeIndex: this.nodeIndex,
nodeId: this.nodeId,
type: inputName,
index,
},
@@ -166,7 +161,7 @@ export const nodeBase = mixins(
if (!this.isReadOnly) {
const plusEndpointData: IEndpointOptions = {
uuid: CanvasHelpers.getOutputEndpointUUID(this.nodeIndex, index),
uuid: CanvasHelpers.getOutputEndpointUUID(this.nodeId, index),
anchor: anchorPosition,
maxConnections: -1,
endpoint: 'N8nPlus',
@@ -187,7 +182,7 @@ export const nodeBase = mixins(
hover: true, // hack to distinguish hover state
},
parameters: {
nodeIndex: this.nodeIndex,
nodeId: this.nodeId,
type: inputName,
index,
},
@@ -258,8 +253,7 @@ export const nodeBase = mixins(
// create a proper solution
let newNodePositon: XYPosition;
moveNodes.forEach((node: INodeUi) => {
const nodeElement = `node-${this.getNodeIndex(node.name)}`;
const element = document.getElementById(nodeElement);
const element = document.getElementById(node.id);
if (element === null) {
return;
}

View File

@@ -1,18 +0,0 @@
import Vue from 'vue';
export const nodeIndex = Vue.extend({
methods: {
getNodeIndex (nodeName: string): string {
let uniqueId = this.$store.getters.getNodeIndex(nodeName);
if (uniqueId === -1) {
this.$store.commit('addToNodeIndex', nodeName);
uniqueId = this.$store.getters.getNodeIndex(nodeName);
}
// We return as string as draggable and jsplumb seems to make problems
// when numbers are given
return uniqueId.toString();
},
},
});

View File

@@ -53,7 +53,7 @@ import { showMessage } from '@/components/mixins/showMessage';
import { isEqual } from 'lodash';
import mixins from 'vue-typed-mixins';
import { v4 as uuidv4 } from 'uuid';
import { v4 as uuid } from 'uuid';
export const workflowHelpers = mixins(
externalHooks,
@@ -666,7 +666,7 @@ export const workflowHelpers = mixins(
}
},
async saveAsNewWorkflow ({name, tags, resetWebhookUrls, openInNewWindow}: {name?: string, tags?: string[], resetWebhookUrls?: boolean, openInNewWindow?: boolean} = {}, redirect = true): Promise<boolean> {
async saveAsNewWorkflow ({name, tags, resetWebhookUrls, resetNodeIds, openInNewWindow}: {name?: string, tags?: string[], resetWebhookUrls?: boolean, openInNewWindow?: boolean, resetNodeIds?: boolean} = {}, redirect = true): Promise<boolean> {
try {
this.$store.commit('addActiveAction', 'workflowSaving');
@@ -674,10 +674,19 @@ export const workflowHelpers = mixins(
// make sure that the new ones are not active
workflowDataRequest.active = false;
const changedNodes = {} as IDataObject;
if (resetNodeIds) {
workflowDataRequest.nodes = workflowDataRequest.nodes!.map(node => {
node.id = uuid();
return node;
});
}
if (resetWebhookUrls) {
workflowDataRequest.nodes = workflowDataRequest.nodes!.map(node => {
if (node.webhookId) {
node.webhookId = uuidv4();
node.webhookId = uuid();
changedNodes[node.name] = node.webhookId;
}
return node;