mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
feat(editor): Supress validation errors for freshly added nodes (#5149)
* feat(editor): Supress validation errors when node is added from node creator * Supress initial errors also for resource locator inputs * Use nodeMetadata prop to store node's `pristine` state * Revert `setNodeParameters` check for `nodeMetadata` * Rename getIsNodePristine to isNodePristine
This commit is contained in:
@@ -53,4 +53,41 @@ describe('NDV', () => {
|
|||||||
ndv.getters.dataContainer().should('contain', 'start');
|
ndv.getters.dataContainer().should('contain', 'start');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should show correct validation state for resource locator params', () => {
|
||||||
|
workflowPage.actions.addNodeToCanvas('Typeform', true);
|
||||||
|
ndv.getters.container().should('be.visible');
|
||||||
|
cy.get('.has-issues').should('have.length', 0);
|
||||||
|
cy.get('[class*=hasIssues]').should('have.length', 0);
|
||||||
|
ndv.getters.backToCanvas().click();
|
||||||
|
// Both credentials and resource locator errors should be visible
|
||||||
|
workflowPage.actions.openNodeNdv('Typeform');
|
||||||
|
cy.get('.has-issues').should('have.length', 1);
|
||||||
|
cy.get('[class*=hasIssues]').should('have.length', 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show validation errors only after blur or re-opening of NDV', () => {
|
||||||
|
workflowPage.actions.addNodeToCanvas('Manual Trigger');
|
||||||
|
workflowPage.actions.addNodeToCanvas('Airtable', true);
|
||||||
|
ndv.getters.container().should('be.visible');
|
||||||
|
cy.get('.has-issues').should('have.length', 0);
|
||||||
|
workflowPage.getters.ndvParameterInput('table').find('input').eq(1).focus().blur()
|
||||||
|
workflowPage.getters.ndvParameterInput('application').find('input').eq(1).focus().blur()
|
||||||
|
cy.get('.has-issues').should('have.length', 2);
|
||||||
|
ndv.getters.backToCanvas().click();
|
||||||
|
workflowPage.actions.openNodeNdv('Airtable');
|
||||||
|
cy.get('.has-issues').should('have.length', 3);
|
||||||
|
cy.get('[class*=hasIssues]').should('have.length', 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show all validation errors when opening pasted node', () => {
|
||||||
|
cy.fixture('Test_workflow_ndv_errors.json').then((data) => {
|
||||||
|
cy.get('body').paste(JSON.stringify(data));
|
||||||
|
workflowPage.getters.canvasNodes().should('have.have.length', 1);
|
||||||
|
workflowPage.actions.openNodeNdv('Airtable');
|
||||||
|
cy.get('.has-issues').should('have.length', 3);
|
||||||
|
cy.get('[class*=hasIssues]').should('have.length', 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
32
cypress/fixtures/Test_workflow_ndv_errors.json
Normal file
32
cypress/fixtures/Test_workflow_ndv_errors.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"instanceId": "3204fc455f5cbeb4e71fdbd3b1dfaf0b088088dea3e639de49e61462b80ffc1d"
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"application": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "url",
|
||||||
|
"value": "",
|
||||||
|
"__regex": "https://airtable.com/([a-zA-Z0-9]{2,})"
|
||||||
|
},
|
||||||
|
"table": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "url",
|
||||||
|
"value": "",
|
||||||
|
"__regex": "https://airtable.com/[a-zA-Z0-9]{2,}/([a-zA-Z0-9]{2,})"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "e0c0cf7e-aa98-4b72-9645-6e64e2902bd1",
|
||||||
|
"name": "Airtable",
|
||||||
|
"type": "n8n-nodes-base.airtable",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
380,
|
||||||
|
180
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {}
|
||||||
|
}
|
||||||
@@ -93,11 +93,12 @@ export class WorkflowPage extends BasePage {
|
|||||||
this.getters.nodeCreatorSearchBar().type('{enter}');
|
this.getters.nodeCreatorSearchBar().type('{enter}');
|
||||||
cy.get('body').type('{esc}');
|
cy.get('body').type('{esc}');
|
||||||
},
|
},
|
||||||
addNodeToCanvas: (nodeDisplayName: string) => {
|
addNodeToCanvas: (nodeDisplayName: string, preventNdvClose?: boolean) => {
|
||||||
this.getters.nodeCreatorPlusButton().click();
|
this.getters.nodeCreatorPlusButton().click();
|
||||||
this.getters.nodeCreatorSearchBar().type(nodeDisplayName);
|
this.getters.nodeCreatorSearchBar().type(nodeDisplayName);
|
||||||
this.getters.nodeCreatorSearchBar().type('{enter}');
|
this.getters.nodeCreatorSearchBar().type('{enter}');
|
||||||
cy.get('body').type('{esc}');
|
|
||||||
|
if (!preventNdvClose) cy.get('body').type('{esc}');
|
||||||
},
|
},
|
||||||
openNodeNdv: (nodeTypeName: string) => {
|
openNodeNdv: (nodeTypeName: string) => {
|
||||||
this.getters.canvasNodeByName(nodeTypeName).dblclick();
|
this.getters.canvasNodeByName(nodeTypeName).dblclick();
|
||||||
|
|||||||
@@ -962,6 +962,7 @@ export interface ITemplatesNode extends IVersionNode {
|
|||||||
|
|
||||||
export interface INodeMetadata {
|
export interface INodeMetadata {
|
||||||
parametersLastUpdatedAt?: number;
|
parametersLastUpdatedAt?: number;
|
||||||
|
pristine: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUsedCredential {
|
export interface IUsedCredential {
|
||||||
|
|||||||
@@ -27,10 +27,11 @@
|
|||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else :class="issues.length ? $style.hasIssues : $style.input">
|
<div v-else :class="issues.length && !hideIssues ? $style.hasIssues : $style.input">
|
||||||
<n8n-select
|
<n8n-select
|
||||||
:value="getSelectedId(credentialTypeDescription.name)"
|
:value="getSelectedId(credentialTypeDescription.name)"
|
||||||
@change="(value) => onCredentialSelected(credentialTypeDescription.name, value)"
|
@change="(value) => onCredentialSelected(credentialTypeDescription.name, value)"
|
||||||
|
@blur="$emit('blur', 'credentials')"
|
||||||
:placeholder="getSelectPlaceholder(credentialTypeDescription.name, issues)"
|
:placeholder="getSelectPlaceholder(credentialTypeDescription.name, issues)"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
@@ -49,7 +50,7 @@
|
|||||||
</n8n-option>
|
</n8n-option>
|
||||||
</n8n-select>
|
</n8n-select>
|
||||||
|
|
||||||
<div :class="$style.warning" v-if="issues.length">
|
<div :class="$style.warning" v-if="issues.length && !hideIssues">
|
||||||
<n8n-tooltip placement="top">
|
<n8n-tooltip placement="top">
|
||||||
<template #content>
|
<template #content>
|
||||||
<titled-list
|
<titled-list
|
||||||
@@ -82,6 +83,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { PropType } from 'vue';
|
||||||
import { restApi } from '@/mixins/restApi';
|
import { restApi } from '@/mixins/restApi';
|
||||||
import {
|
import {
|
||||||
ICredentialsResponse,
|
ICredentialsResponse,
|
||||||
@@ -108,11 +110,23 @@ import { useCredentialsStore } from '@/stores/credentials';
|
|||||||
|
|
||||||
export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend({
|
export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend({
|
||||||
name: 'NodeCredentials',
|
name: 'NodeCredentials',
|
||||||
props: [
|
props: {
|
||||||
'readonly',
|
readonly: {
|
||||||
'node', // INodeUi
|
type: Boolean,
|
||||||
'overrideCredType', // cred type
|
default: false,
|
||||||
],
|
},
|
||||||
|
node: {
|
||||||
|
type: Object as PropType<INodeUi>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
overrideCredType: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
hideIssues: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
TitledList,
|
TitledList,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -93,14 +93,18 @@
|
|||||||
:hideDelete="true"
|
:hideDelete="true"
|
||||||
:nodeValues="nodeValues"
|
:nodeValues="nodeValues"
|
||||||
:isReadOnly="isReadOnly"
|
:isReadOnly="isReadOnly"
|
||||||
|
:hiddenIssuesInputs="hiddenIssuesInputs"
|
||||||
path="parameters"
|
path="parameters"
|
||||||
@valueChanged="valueChanged"
|
@valueChanged="valueChanged"
|
||||||
@activate="onWorkflowActivate"
|
@activate="onWorkflowActivate"
|
||||||
|
@parameterBlur="onParameterBlur"
|
||||||
>
|
>
|
||||||
<node-credentials
|
<node-credentials
|
||||||
:node="node"
|
:node="node"
|
||||||
:readonly="isReadOnly"
|
:readonly="isReadOnly"
|
||||||
@credentialSelected="credentialSelected"
|
@credentialSelected="credentialSelected"
|
||||||
|
@blur="onParameterBlur"
|
||||||
|
:hide-issues="hiddenIssuesInputs.includes('credentials')"
|
||||||
/>
|
/>
|
||||||
</parameter-input-list>
|
</parameter-input-list>
|
||||||
<div v-if="parametersNoneSetting.length === 0" class="no-parameters">
|
<div v-if="parametersNoneSetting.length === 0" class="no-parameters">
|
||||||
@@ -124,16 +128,20 @@
|
|||||||
:parameters="parametersSetting"
|
:parameters="parametersSetting"
|
||||||
:nodeValues="nodeValues"
|
:nodeValues="nodeValues"
|
||||||
:isReadOnly="isReadOnly"
|
:isReadOnly="isReadOnly"
|
||||||
|
:hiddenIssuesInputs="hiddenIssuesInputs"
|
||||||
path="parameters"
|
path="parameters"
|
||||||
@valueChanged="valueChanged"
|
@valueChanged="valueChanged"
|
||||||
|
@parameterBlur="onParameterBlur"
|
||||||
/>
|
/>
|
||||||
<parameter-input-list
|
<parameter-input-list
|
||||||
:parameters="nodeSettings"
|
:parameters="nodeSettings"
|
||||||
:hideDelete="true"
|
:hideDelete="true"
|
||||||
:nodeValues="nodeValues"
|
:nodeValues="nodeValues"
|
||||||
:isReadOnly="isReadOnly"
|
:isReadOnly="isReadOnly"
|
||||||
|
:hiddenIssuesInputs="hiddenIssuesInputs"
|
||||||
path=""
|
path=""
|
||||||
@valueChanged="valueChanged"
|
@valueChanged="valueChanged"
|
||||||
|
@parameterBlur="onParameterBlur"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -414,6 +422,7 @@ export default mixins(externalHooks, nodeHelpers).extend({
|
|||||||
COMMUNITY_NODES_INSTALLATION_DOCS_URL,
|
COMMUNITY_NODES_INSTALLATION_DOCS_URL,
|
||||||
CUSTOM_NODES_DOCS_URL,
|
CUSTOM_NODES_DOCS_URL,
|
||||||
MAIN_NODE_PANEL_WIDTH,
|
MAIN_NODE_PANEL_WIDTH,
|
||||||
|
hiddenIssuesInputs: [] as string[],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -445,10 +454,25 @@ export default mixins(externalHooks, nodeHelpers).extend({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
populateHiddenIssuesSet() {
|
||||||
|
if (!this.node || !this.workflowsStore.isNodePristine(this.node.name)) return;
|
||||||
|
|
||||||
|
this.hiddenIssuesInputs.push('credentials');
|
||||||
|
this.parametersNoneSetting.forEach((parameter) => {
|
||||||
|
this.hiddenIssuesInputs.push(parameter.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.workflowsStore.setNodePristine(this.node.name, false);
|
||||||
|
},
|
||||||
|
onParameterBlur(parameterName: string) {
|
||||||
|
this.hiddenIssuesInputs = this.hiddenIssuesInputs.filter((name) => name !== parameterName);
|
||||||
|
},
|
||||||
onWorkflowActivate() {
|
onWorkflowActivate() {
|
||||||
|
this.hiddenIssuesInputs = [];
|
||||||
this.$emit('activate');
|
this.$emit('activate');
|
||||||
},
|
},
|
||||||
onNodeExecute() {
|
onNodeExecute() {
|
||||||
|
this.hiddenIssuesInputs = [];
|
||||||
this.$emit('execute');
|
this.$emit('execute');
|
||||||
},
|
},
|
||||||
setValue(name: string, value: NodeParameterValue) {
|
setValue(name: string, value: NodeParameterValue) {
|
||||||
@@ -457,7 +481,7 @@ export default mixins(externalHooks, nodeHelpers).extend({
|
|||||||
|
|
||||||
let isArray = false;
|
let isArray = false;
|
||||||
if (lastNamePart !== undefined && lastNamePart.includes('[')) {
|
if (lastNamePart !== undefined && lastNamePart.includes('[')) {
|
||||||
// It incldues an index so we have to extract it
|
// It includes an index so we have to extract it
|
||||||
const lastNameParts = lastNamePart.match(/(.*)\[(\d+)\]$/);
|
const lastNameParts = lastNamePart.match(/(.*)\[(\d+)\]$/);
|
||||||
if (lastNameParts) {
|
if (lastNameParts) {
|
||||||
nameParts.push(lastNameParts[1]);
|
nameParts.push(lastNameParts[1]);
|
||||||
@@ -835,6 +859,7 @@ export default mixins(externalHooks, nodeHelpers).extend({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.populateHiddenIssuesSet();
|
||||||
this.setNodeValues();
|
this.setNodeValues();
|
||||||
if (this.eventBus) {
|
if (this.eventBus) {
|
||||||
(this.eventBus as Vue).$on('openSettings', () => {
|
(this.eventBus as Vue).$on('openSettings', () => {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
:activeDrop="activeDrop"
|
:activeDrop="activeDrop"
|
||||||
:forceShowExpression="forceShowExpression"
|
:forceShowExpression="forceShowExpression"
|
||||||
:hint="hint"
|
:hint="hint"
|
||||||
|
:hide-issues="hideIssues"
|
||||||
@valueChanged="valueChanged"
|
@valueChanged="valueChanged"
|
||||||
@textInput="onTextInput"
|
@textInput="onTextInput"
|
||||||
@focus="onFocus"
|
@focus="onFocus"
|
||||||
@@ -119,6 +120,10 @@ export default mixins(showMessage).extend({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
hideIssues: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
parameter: {
|
parameter: {
|
||||||
type: Object as PropType<INodeProperties>,
|
type: Object as PropType<INodeProperties>,
|
||||||
},
|
},
|
||||||
@@ -197,6 +202,7 @@ export default mixins(showMessage).extend({
|
|||||||
if (!this.parameter.noDataExpression) {
|
if (!this.parameter.noDataExpression) {
|
||||||
this.ndvStore.setMappableNDVInputFocus('');
|
this.ndvStore.setMappableNDVInputFocus('');
|
||||||
}
|
}
|
||||||
|
this.$emit('blur');
|
||||||
},
|
},
|
||||||
onMenuExpanded(expanded: boolean) {
|
onMenuExpanded(expanded: boolean) {
|
||||||
this.menuExpanded = expanded;
|
this.menuExpanded = expanded;
|
||||||
|
|||||||
@@ -93,11 +93,13 @@
|
|||||||
|
|
||||||
<parameter-input-full
|
<parameter-input-full
|
||||||
:parameter="parameter"
|
:parameter="parameter"
|
||||||
|
:hide-issues="hiddenIssuesInputs.includes(parameter.name)"
|
||||||
:value="getParameterValue(nodeValues, parameter.name, path)"
|
:value="getParameterValue(nodeValues, parameter.name, path)"
|
||||||
:displayOptions="true"
|
:displayOptions="true"
|
||||||
:path="getPath(parameter.name)"
|
:path="getPath(parameter.name)"
|
||||||
:isReadOnly="isReadOnly"
|
:isReadOnly="isReadOnly"
|
||||||
@valueChanged="valueChanged"
|
@valueChanged="valueChanged"
|
||||||
|
@blur="onParameterBlur(parameter.name)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -108,13 +110,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import { deepCopy, INodeParameters, INodeProperties, NodeParameterValue } from 'n8n-workflow';
|
||||||
deepCopy,
|
|
||||||
INodeParameters,
|
|
||||||
INodeProperties,
|
|
||||||
INodeTypeDescription,
|
|
||||||
NodeParameterValue,
|
|
||||||
} from 'n8n-workflow';
|
|
||||||
|
|
||||||
import { INodeUi, IUpdateInformation } from '@/Interface';
|
import { INodeUi, IUpdateInformation } from '@/Interface';
|
||||||
|
|
||||||
@@ -126,8 +122,8 @@ import ImportParameter from '@/components/ImportParameter.vue';
|
|||||||
import { get, set } from 'lodash';
|
import { get, set } from 'lodash';
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
import { Component } from 'vue';
|
import { Component, PropType } from 'vue';
|
||||||
import { mapState, mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useNDVStore } from '@/stores/ndv';
|
import { useNDVStore } from '@/stores/ndv';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
|
|
||||||
@@ -140,14 +136,36 @@ export default mixins(workflowHelpers).extend({
|
|||||||
CollectionParameter: () => import('./CollectionParameter.vue') as Promise<Component>,
|
CollectionParameter: () => import('./CollectionParameter.vue') as Promise<Component>,
|
||||||
ImportParameter,
|
ImportParameter,
|
||||||
},
|
},
|
||||||
props: [
|
props: {
|
||||||
'nodeValues', // INodeParameters
|
nodeValues: {
|
||||||
'parameters', // INodeProperties
|
type: Object as PropType<INodeParameters>,
|
||||||
'path', // string
|
required: true,
|
||||||
'hideDelete', // boolean
|
},
|
||||||
'indent',
|
parameters: {
|
||||||
'isReadOnly',
|
type: Array as PropType<INodeProperties[]>,
|
||||||
],
|
required: true,
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
hideDelete: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
indent: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
isReadOnly: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
hiddenIssuesInputs: {
|
||||||
|
type: Array as PropType<string[]>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(useNodeTypesStore, useNDVStore),
|
...mapStores(useNodeTypesStore, useNDVStore),
|
||||||
nodeTypeVersion(): number | null {
|
nodeTypeVersion(): number | null {
|
||||||
@@ -187,6 +205,9 @@ export default mixins(workflowHelpers).extend({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
onParameterBlur(parameterName: string) {
|
||||||
|
this.$emit('parameterBlur', parameterName);
|
||||||
|
},
|
||||||
getCredentialsDependencies() {
|
getCredentialsDependencies() {
|
||||||
const dependencies = new Set();
|
const dependencies = new Set();
|
||||||
const nodeType = this.nodeTypesStore.getNodeType(
|
const nodeType = this.nodeTypesStore.getNodeType(
|
||||||
|
|||||||
@@ -684,6 +684,7 @@ export default mixins(debounceHelper, workflowHelpers, nodeHelpers).extend({
|
|||||||
if (!this.isSearchable || this.currentQueryError) {
|
if (!this.isSearchable || this.currentQueryError) {
|
||||||
this.showResourceDropdown = false;
|
this.showResourceDropdown = false;
|
||||||
}
|
}
|
||||||
|
this.$emit('blur');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -227,6 +227,10 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
this.nodeMetadata[nodeName] && this.nodeMetadata[nodeName].parametersLastUpdatedAt;
|
this.nodeMetadata[nodeName] && this.nodeMetadata[nodeName].parametersLastUpdatedAt;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isNodePristine(): (name: string) => boolean {
|
||||||
|
return (nodeName: string) =>
|
||||||
|
this.nodeMetadata[nodeName] === undefined || this.nodeMetadata[nodeName].pristine === true;
|
||||||
|
},
|
||||||
// Executions getters
|
// Executions getters
|
||||||
getExecutionDataById(): (id: string) => IExecutionsSummary | undefined {
|
getExecutionDataById(): (id: string) => IExecutionsSummary | undefined {
|
||||||
return (id: string): IExecutionsSummary | undefined =>
|
return (id: string): IExecutionsSummary | undefined =>
|
||||||
@@ -731,7 +735,12 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
// TODO: Check if there is an error or whatever that is supposed to be returned
|
// TODO: Check if there is an error or whatever that is supposed to be returned
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.workflow.nodes.push(nodeData);
|
this.workflow.nodes.push(nodeData);
|
||||||
|
// Init node metadata
|
||||||
|
if (!this.nodeMetadata[nodeData.name]) {
|
||||||
|
Vue.set(this.nodeMetadata, nodeData.name, {});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
removeNode(node: INodeUi): void {
|
removeNode(node: INodeUi): void {
|
||||||
@@ -821,6 +830,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
if (!this.nodeMetadata[node.name]) {
|
if (!this.nodeMetadata[node.name]) {
|
||||||
Vue.set(this.nodeMetadata, node.name, {});
|
Vue.set(this.nodeMetadata, node.name, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.set(this.nodeMetadata[node.name], 'parametersLastUpdatedAt', Date.now());
|
Vue.set(this.nodeMetadata[node.name], 'parametersLastUpdatedAt', Date.now());
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -960,5 +970,8 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
setNodePristine(nodeName: string, isPristine: boolean): void {
|
||||||
|
Vue.set(this.nodeMetadata[nodeName], 'pristine', isPristine);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1907,6 +1907,7 @@ export default mixins(
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.addNodes([newNodeData], undefined, trackHistory);
|
await this.addNodes([newNodeData], undefined, trackHistory);
|
||||||
|
this.workflowsStore.setNodePristine(newNodeData.name, true);
|
||||||
|
|
||||||
this.uiStore.stateIsDirty = true;
|
this.uiStore.stateIsDirty = true;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user