mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
refactor(editor): Extract large chunks from NodeSettings (#17665)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
@@ -49,7 +49,6 @@ const emit = defineEmits<{
|
|||||||
connectionType: NodeConnectionType,
|
connectionType: NodeConnectionType,
|
||||||
connectionIndex?: number,
|
connectionIndex?: number,
|
||||||
];
|
];
|
||||||
redrawNode: [nodeName: string];
|
|
||||||
stopExecution: [];
|
stopExecution: [];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
@@ -81,7 +80,6 @@ const message = useMessage();
|
|||||||
const { APP_Z_INDEXES } = useStyles();
|
const { APP_Z_INDEXES } = useStyles();
|
||||||
|
|
||||||
const settingsEventBus = createEventBus();
|
const settingsEventBus = createEventBus();
|
||||||
const redrawRequired = ref(false);
|
|
||||||
const runInputIndex = ref(-1);
|
const runInputIndex = ref(-1);
|
||||||
const runOutputIndex = computed(() => ndvStore.output.run ?? -1);
|
const runOutputIndex = computed(() => ndvStore.output.run ?? -1);
|
||||||
const selectedInput = ref<string | undefined>();
|
const selectedInput = ref<string | undefined>();
|
||||||
@@ -498,18 +496,6 @@ const close = async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
activeNode.value &&
|
|
||||||
(typeof activeNodeType.value?.outputs === 'string' ||
|
|
||||||
typeof activeNodeType.value?.inputs === 'string' ||
|
|
||||||
redrawRequired.value)
|
|
||||||
) {
|
|
||||||
const nodeName = activeNode.value.name;
|
|
||||||
setTimeout(() => {
|
|
||||||
emit('redrawNode', nodeName);
|
|
||||||
}, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outputPanelEditMode.value.enabled && activeNode.value) {
|
if (outputPanelEditMode.value.enabled && activeNode.value) {
|
||||||
const shouldPinDataBeforeClosing = await message.confirm(
|
const shouldPinDataBeforeClosing = await message.confirm(
|
||||||
'',
|
'',
|
||||||
@@ -842,7 +828,6 @@ onBeforeUnmount(() => {
|
|||||||
@value-changed="valueChanged"
|
@value-changed="valueChanged"
|
||||||
@execute="onNodeExecute"
|
@execute="onNodeExecute"
|
||||||
@stop-execution="onStopExecution"
|
@stop-execution="onStopExecution"
|
||||||
@redraw-required="redrawRequired = true"
|
|
||||||
@activate="onWorkflowActivate"
|
@activate="onWorkflowActivate"
|
||||||
@switch-selected-node="onSwitchSelectedNode"
|
@switch-selected-node="onSwitchSelectedNode"
|
||||||
@open-connection-node-creator="onOpenConnectionNodeCreator"
|
@open-connection-node-creator="onOpenConnectionNodeCreator"
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ const message = useMessage();
|
|||||||
const { APP_Z_INDEXES } = useStyles();
|
const { APP_Z_INDEXES } = useStyles();
|
||||||
|
|
||||||
const settingsEventBus = createEventBus();
|
const settingsEventBus = createEventBus();
|
||||||
const redrawRequired = ref(false);
|
|
||||||
const runInputIndex = ref(-1);
|
const runInputIndex = ref(-1);
|
||||||
const runOutputIndex = ref(-1);
|
const runOutputIndex = ref(-1);
|
||||||
const isLinkingEnabled = ref(true);
|
const isLinkingEnabled = ref(true);
|
||||||
@@ -821,7 +820,6 @@ onBeforeUnmount(() => {
|
|||||||
:class="$style.settings"
|
:class="$style.settings"
|
||||||
@execute="onNodeExecute"
|
@execute="onNodeExecute"
|
||||||
@stop-execution="onStopExecution"
|
@stop-execution="onStopExecution"
|
||||||
@redraw-required="redrawRequired = true"
|
|
||||||
@activate="onWorkflowActivate"
|
@activate="onWorkflowActivate"
|
||||||
@switch-selected-node="onSwitchSelectedNode"
|
@switch-selected-node="onSwitchSelectedNode"
|
||||||
@open-connection-node-creator="onOpenConnectionNodeCreator"
|
@open-connection-node-creator="onOpenConnectionNodeCreator"
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import { useTemplateRef, computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
import { useTemplateRef, computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||||
import type {
|
import type {
|
||||||
INodeParameters,
|
INodeParameters,
|
||||||
INodeProperties,
|
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
NodeParameterValue,
|
NodeParameterValue,
|
||||||
INodeCredentialDescription,
|
INodeCredentialDescription,
|
||||||
@@ -16,12 +15,7 @@ import type {
|
|||||||
IUpdateInformation,
|
IUpdateInformation,
|
||||||
} from '@/Interface';
|
} from '@/Interface';
|
||||||
|
|
||||||
import {
|
import { BASE_NODE_SURVEY_URL, NDV_UI_OVERHAUL_EXPERIMENT } from '@/constants';
|
||||||
BASE_NODE_SURVEY_URL,
|
|
||||||
COMMUNITY_NODES_INSTALLATION_DOCS_URL,
|
|
||||||
CUSTOM_NODES_DOCS_URL,
|
|
||||||
NDV_UI_OVERHAUL_EXPERIMENT,
|
|
||||||
} from '@/constants';
|
|
||||||
|
|
||||||
import ParameterInputList from '@/components/ParameterInputList.vue';
|
import ParameterInputList from '@/components/ParameterInputList.vue';
|
||||||
import NodeCredentials from '@/components/NodeCredentials.vue';
|
import NodeCredentials from '@/components/NodeCredentials.vue';
|
||||||
@@ -32,7 +26,12 @@ import NodeSettingsHeader from '@/components/NodeSettingsHeader.vue';
|
|||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
|
|
||||||
import NodeExecuteButton from './NodeExecuteButton.vue';
|
import NodeExecuteButton from './NodeExecuteButton.vue';
|
||||||
import { nameIsParameter } from '@/utils/nodeSettingsUtils';
|
import {
|
||||||
|
collectSettings,
|
||||||
|
createCommonNodeSettings,
|
||||||
|
nameIsParameter,
|
||||||
|
getNodeSettingsInitialValues,
|
||||||
|
} from '@/utils/nodeSettingsUtils';
|
||||||
import { isCommunityPackageName } from '@/utils/nodeTypesUtils';
|
import { isCommunityPackageName } from '@/utils/nodeTypesUtils';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import { useNDVStore } from '@/stores/ndv.store';
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
@@ -54,9 +53,9 @@ import { usePostHog } from '@/stores/posthog.store';
|
|||||||
import { shouldShowParameter } from './canvas/experimental/experimentalNdv.utils';
|
import { shouldShowParameter } from './canvas/experimental/experimentalNdv.utils';
|
||||||
import { useResizeObserver } from '@vueuse/core';
|
import { useResizeObserver } from '@vueuse/core';
|
||||||
import { useNodeSettingsParameters } from '@/composables/useNodeSettingsParameters';
|
import { useNodeSettingsParameters } from '@/composables/useNodeSettingsParameters';
|
||||||
import { I18nT } from 'vue-i18n';
|
import { N8nBlockUi, N8nIcon, N8nNotice, N8nText } from '@n8n/design-system';
|
||||||
import { N8nBlockUi, N8nIcon, N8nLink, N8nNotice, N8nText } from '@n8n/design-system';
|
|
||||||
import ExperimentalEmbeddedNdvHeader from '@/components/canvas/experimental/components/ExperimentalEmbeddedNdvHeader.vue';
|
import ExperimentalEmbeddedNdvHeader from '@/components/canvas/experimental/components/ExperimentalEmbeddedNdvHeader.vue';
|
||||||
|
import NodeSettingsInvalidNodeWarning from '@/components/NodeSettingsInvalidNodeWarning.vue';
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@@ -86,7 +85,6 @@ const props = withDefaults(
|
|||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
stopExecution: [];
|
stopExecution: [];
|
||||||
redrawRequired: [];
|
|
||||||
valueChanged: [value: IUpdateInformation];
|
valueChanged: [value: IUpdateInformation];
|
||||||
switchSelectedNode: [nodeName: string];
|
switchSelectedNode: [nodeName: string];
|
||||||
openConnectionNodeCreator: [
|
openConnectionNodeCreator: [
|
||||||
@@ -101,18 +99,7 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const slots = defineSlots<{ actions?: {} }>();
|
const slots = defineSlots<{ actions?: {} }>();
|
||||||
|
|
||||||
const nodeValues = ref<INodeParameters>({
|
const nodeValues = ref<INodeParameters>(getNodeSettingsInitialValues());
|
||||||
color: '#ff0000',
|
|
||||||
alwaysOutputData: false,
|
|
||||||
executeOnce: false,
|
|
||||||
notesInFlow: false,
|
|
||||||
onError: 'stopWorkflow',
|
|
||||||
retryOnFail: false,
|
|
||||||
maxTries: 3,
|
|
||||||
waitBetweenTries: 1000,
|
|
||||||
notes: '',
|
|
||||||
parameters: {},
|
|
||||||
});
|
|
||||||
|
|
||||||
const nodeTypesStore = useNodeTypesStore();
|
const nodeTypesStore = useNodeTypesStore();
|
||||||
const ndvStore = useNDVStore();
|
const ndvStore = useNDVStore();
|
||||||
@@ -145,7 +132,6 @@ const openPanel = ref<Tab>('params');
|
|||||||
const nodeValuesInitialized = ref(false);
|
const nodeValuesInitialized = ref(false);
|
||||||
|
|
||||||
const hiddenIssuesInputs = ref<string[]>([]);
|
const hiddenIssuesInputs = ref<string[]>([]);
|
||||||
const nodeSettings = ref<INodeProperties[]>([]);
|
|
||||||
const subConnections = ref<InstanceType<typeof NDVSubConnections> | null>(null);
|
const subConnections = ref<InstanceType<typeof NDVSubConnections> | null>(null);
|
||||||
|
|
||||||
const installedPackage = ref<PublicInstalledPackage | undefined>(undefined);
|
const installedPackage = ref<PublicInstalledPackage | undefined>(undefined);
|
||||||
@@ -306,11 +292,6 @@ const valueChanged = (parameterData: IUpdateInformation) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parameterData.name === 'onError') {
|
|
||||||
// If that parameter changes, we need to redraw the connections, as the error output may need to be added or removed
|
|
||||||
emit('redrawRequired');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parameterData.name === 'name') {
|
if (parameterData.name === 'name') {
|
||||||
// Name of node changed so we have to set also the new node name as active
|
// Name of node changed so we have to set also the new node name as active
|
||||||
|
|
||||||
@@ -456,132 +437,9 @@ const populateHiddenIssuesSet = () => {
|
|||||||
workflowsStore.setNodePristine(node.value.name, false);
|
workflowsStore.setNodePristine(node.value.name, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const populateSettings = () => {
|
const nodeSettings = computed(() =>
|
||||||
if (isExecutable.value && !isTriggerNode.value) {
|
createCommonNodeSettings(isExecutable.value, isToolNode.value, i18n.baseText.bind(i18n)),
|
||||||
nodeSettings.value.push(
|
);
|
||||||
...([
|
|
||||||
{
|
|
||||||
displayName: i18n.baseText('nodeSettings.alwaysOutputData.displayName'),
|
|
||||||
name: 'alwaysOutputData',
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
noDataExpression: true,
|
|
||||||
description: i18n.baseText('nodeSettings.alwaysOutputData.description'),
|
|
||||||
isNodeSetting: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: i18n.baseText('nodeSettings.executeOnce.displayName'),
|
|
||||||
name: 'executeOnce',
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
noDataExpression: true,
|
|
||||||
description: i18n.baseText('nodeSettings.executeOnce.description'),
|
|
||||||
isNodeSetting: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: i18n.baseText('nodeSettings.retryOnFail.displayName'),
|
|
||||||
name: 'retryOnFail',
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
noDataExpression: true,
|
|
||||||
description: i18n.baseText('nodeSettings.retryOnFail.description'),
|
|
||||||
isNodeSetting: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: i18n.baseText('nodeSettings.maxTries.displayName'),
|
|
||||||
name: 'maxTries',
|
|
||||||
type: 'number',
|
|
||||||
typeOptions: {
|
|
||||||
minValue: 2,
|
|
||||||
maxValue: 5,
|
|
||||||
},
|
|
||||||
default: 3,
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
retryOnFail: [true],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
noDataExpression: true,
|
|
||||||
description: i18n.baseText('nodeSettings.maxTries.description'),
|
|
||||||
isNodeSetting: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: i18n.baseText('nodeSettings.waitBetweenTries.displayName'),
|
|
||||||
name: 'waitBetweenTries',
|
|
||||||
type: 'number',
|
|
||||||
typeOptions: {
|
|
||||||
minValue: 0,
|
|
||||||
maxValue: 5000,
|
|
||||||
},
|
|
||||||
default: 1000,
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
retryOnFail: [true],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
noDataExpression: true,
|
|
||||||
description: i18n.baseText('nodeSettings.waitBetweenTries.description'),
|
|
||||||
isNodeSetting: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: i18n.baseText('nodeSettings.onError.displayName'),
|
|
||||||
name: 'onError',
|
|
||||||
type: 'options',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: i18n.baseText('nodeSettings.onError.options.stopWorkflow.displayName'),
|
|
||||||
value: 'stopWorkflow',
|
|
||||||
description: i18n.baseText('nodeSettings.onError.options.stopWorkflow.description'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n.baseText('nodeSettings.onError.options.continueRegularOutput.displayName'),
|
|
||||||
value: 'continueRegularOutput',
|
|
||||||
description: i18n.baseText(
|
|
||||||
'nodeSettings.onError.options.continueRegularOutput.description',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n.baseText('nodeSettings.onError.options.continueErrorOutput.displayName'),
|
|
||||||
value: 'continueErrorOutput',
|
|
||||||
description: i18n.baseText(
|
|
||||||
'nodeSettings.onError.options.continueErrorOutput.description',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
default: 'stopWorkflow',
|
|
||||||
description: i18n.baseText('nodeSettings.onError.description'),
|
|
||||||
noDataExpression: true,
|
|
||||||
isNodeSetting: true,
|
|
||||||
},
|
|
||||||
] as INodeProperties[]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
nodeSettings.value.push(
|
|
||||||
...([
|
|
||||||
{
|
|
||||||
displayName: i18n.baseText('nodeSettings.notes.displayName'),
|
|
||||||
name: 'notes',
|
|
||||||
type: 'string',
|
|
||||||
typeOptions: {
|
|
||||||
rows: 5,
|
|
||||||
},
|
|
||||||
default: '',
|
|
||||||
noDataExpression: true,
|
|
||||||
description: i18n.baseText('nodeSettings.notes.description'),
|
|
||||||
isNodeSetting: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: i18n.baseText('nodeSettings.notesInFlow.displayName'),
|
|
||||||
name: 'notesInFlow',
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
noDataExpression: true,
|
|
||||||
description: i18n.baseText('nodeSettings.notesInFlow.description'),
|
|
||||||
isNodeSetting: true,
|
|
||||||
},
|
|
||||||
] as INodeProperties[]),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onParameterBlur = (parameterName: string) => {
|
const onParameterBlur = (parameterName: string) => {
|
||||||
hiddenIssuesInputs.value = hiddenIssuesInputs.value.filter((name) => name !== parameterName);
|
hiddenIssuesInputs.value = hiddenIssuesInputs.value.filter((name) => name !== parameterName);
|
||||||
@@ -631,103 +489,7 @@ const setNodeValues = () => {
|
|||||||
|
|
||||||
if (nodeType.value !== null) {
|
if (nodeType.value !== null) {
|
||||||
nodeValid.value = true;
|
nodeValid.value = true;
|
||||||
|
nodeValues.value = collectSettings(node.value, nodeSettings.value);
|
||||||
const foundNodeSettings = [];
|
|
||||||
if (node.value.color) {
|
|
||||||
foundNodeSettings.push('color');
|
|
||||||
nodeValues.value = {
|
|
||||||
...nodeValues.value,
|
|
||||||
color: node.value.color,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.value.notes) {
|
|
||||||
foundNodeSettings.push('notes');
|
|
||||||
nodeValues.value = {
|
|
||||||
...nodeValues.value,
|
|
||||||
notes: node.value.notes,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.value.alwaysOutputData) {
|
|
||||||
foundNodeSettings.push('alwaysOutputData');
|
|
||||||
nodeValues.value = {
|
|
||||||
...nodeValues.value,
|
|
||||||
alwaysOutputData: node.value.alwaysOutputData,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.value.executeOnce) {
|
|
||||||
foundNodeSettings.push('executeOnce');
|
|
||||||
nodeValues.value = {
|
|
||||||
...nodeValues.value,
|
|
||||||
executeOnce: node.value.executeOnce,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.value.continueOnFail) {
|
|
||||||
foundNodeSettings.push('onError');
|
|
||||||
nodeValues.value = {
|
|
||||||
...nodeValues.value,
|
|
||||||
onError: 'continueRegularOutput',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.value.onError) {
|
|
||||||
foundNodeSettings.push('onError');
|
|
||||||
nodeValues.value = {
|
|
||||||
...nodeValues.value,
|
|
||||||
onError: node.value.onError,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.value.notesInFlow) {
|
|
||||||
foundNodeSettings.push('notesInFlow');
|
|
||||||
nodeValues.value = {
|
|
||||||
...nodeValues.value,
|
|
||||||
notesInFlow: node.value.notesInFlow,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.value.retryOnFail) {
|
|
||||||
foundNodeSettings.push('retryOnFail');
|
|
||||||
nodeValues.value = {
|
|
||||||
...nodeValues.value,
|
|
||||||
retryOnFail: node.value.retryOnFail,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.value.maxTries) {
|
|
||||||
foundNodeSettings.push('maxTries');
|
|
||||||
nodeValues.value = {
|
|
||||||
...nodeValues.value,
|
|
||||||
maxTries: node.value.maxTries,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.value.waitBetweenTries) {
|
|
||||||
foundNodeSettings.push('waitBetweenTries');
|
|
||||||
nodeValues.value = {
|
|
||||||
...nodeValues.value,
|
|
||||||
waitBetweenTries: node.value.waitBetweenTries,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set default node settings
|
|
||||||
for (const nodeSetting of nodeSettings.value) {
|
|
||||||
if (!foundNodeSettings.includes(nodeSetting.name)) {
|
|
||||||
// Set default value
|
|
||||||
nodeValues.value = {
|
|
||||||
...nodeValues.value,
|
|
||||||
[nodeSetting.name]: nodeSetting.default,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeValues.value = {
|
|
||||||
...nodeValues.value,
|
|
||||||
parameters: deepCopy(node.value.parameters),
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
nodeValid.value = false;
|
nodeValid.value = false;
|
||||||
}
|
}
|
||||||
@@ -735,22 +497,6 @@ const setNodeValues = () => {
|
|||||||
nodeValuesInitialized.value = true;
|
nodeValuesInitialized.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMissingNodeTextClick = (event: MouseEvent) => {
|
|
||||||
if ((event.target as Element).localName === 'a') {
|
|
||||||
telemetry.track('user clicked cnr browse button', {
|
|
||||||
source: 'cnr missing node modal',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onMissingNodeLearnMoreLinkClick = () => {
|
|
||||||
telemetry.track('user clicked cnr docs link', {
|
|
||||||
source: 'missing node modal source',
|
|
||||||
package_name: node.value?.type.split('.')[0],
|
|
||||||
node_type: node.value?.type,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onStopExecution = () => {
|
const onStopExecution = () => {
|
||||||
emit('stopExecution');
|
emit('stopExecution');
|
||||||
};
|
};
|
||||||
@@ -782,7 +528,6 @@ watch(node, () => {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
populateHiddenIssuesSet();
|
populateHiddenIssuesSet();
|
||||||
populateSettings();
|
|
||||||
setNodeValues();
|
setNodeValues();
|
||||||
props.eventBus?.on('openSettings', openSettings);
|
props.eventBus?.on('openSettings', openSettings);
|
||||||
if (node.value !== null) {
|
if (node.value !== null) {
|
||||||
@@ -885,49 +630,9 @@ function displayCredentials(credentialTypeDescription: INodeCredentialDescriptio
|
|||||||
@value-changed="valueChanged"
|
@value-changed="valueChanged"
|
||||||
@tab-changed="onTabSelect"
|
@tab-changed="onTabSelect"
|
||||||
/>
|
/>
|
||||||
<div v-if="node && !nodeValid" class="node-is-not-valid">
|
|
||||||
<p :class="$style.warningIcon">
|
<NodeSettingsInvalidNodeWarning v-if="node && !nodeValid" :node="node" />
|
||||||
<N8nIcon icon="triangle-alert" />
|
|
||||||
</p>
|
|
||||||
<div class="missingNodeTitleContainer mt-s mb-xs">
|
|
||||||
<N8nText size="large" color="text-dark" bold>
|
|
||||||
{{ i18n.baseText('nodeSettings.communityNodeUnknown.title') }}
|
|
||||||
</N8nText>
|
|
||||||
</div>
|
|
||||||
<div v-if="isCommunityNode" :class="$style.descriptionContainer">
|
|
||||||
<div class="mb-l">
|
|
||||||
<I18nT
|
|
||||||
keypath="nodeSettings.communityNodeUnknown.description"
|
|
||||||
tag="span"
|
|
||||||
scope="global"
|
|
||||||
@click="onMissingNodeTextClick"
|
|
||||||
>
|
|
||||||
<template #action>
|
|
||||||
<a
|
|
||||||
:href="`https://www.npmjs.com/package/${node.type.split('.')[0]}`"
|
|
||||||
target="_blank"
|
|
||||||
>{{ node.type.split('.')[0] }}</a
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
</I18nT>
|
|
||||||
</div>
|
|
||||||
<N8nLink
|
|
||||||
:to="COMMUNITY_NODES_INSTALLATION_DOCS_URL"
|
|
||||||
@click="onMissingNodeLearnMoreLinkClick"
|
|
||||||
>
|
|
||||||
{{ i18n.baseText('nodeSettings.communityNodeUnknown.installLink.text') }}
|
|
||||||
</N8nLink>
|
|
||||||
</div>
|
|
||||||
<I18nT v-else keypath="nodeSettings.nodeTypeUnknown.description" tag="span" scope="global">
|
|
||||||
<template #action>
|
|
||||||
<a
|
|
||||||
:href="CUSTOM_NODES_DOCS_URL"
|
|
||||||
target="_blank"
|
|
||||||
v-text="i18n.baseText('nodeSettings.nodeTypeUnknown.description.customNode')"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</I18nT>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
v-if="node && nodeValid"
|
v-if="node && nodeValid"
|
||||||
ref="nodeParameterWrapper"
|
ref="nodeParameterWrapper"
|
||||||
@@ -1060,16 +765,6 @@ function displayCredentials(credentialTypeDescription: INodeCredentialDescriptio
|
|||||||
background-color: var(--color-background-base);
|
background-color: var(--color-background-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
.warningIcon {
|
|
||||||
color: var(--color-text-lighter);
|
|
||||||
font-size: var(--font-size-2xl);
|
|
||||||
}
|
|
||||||
|
|
||||||
.descriptionContainer {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.featureRequest {
|
.featureRequest {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
@@ -1112,17 +807,6 @@ function displayCredentials(credentialTypeDescription: INodeCredentialDescriptio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.node-is-not-valid {
|
|
||||||
height: 75%;
|
|
||||||
padding: 10px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
line-height: var(--font-line-height-regular);
|
|
||||||
}
|
|
||||||
|
|
||||||
.node-parameters-wrapper {
|
.node-parameters-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useTelemetry } from '@/composables/useTelemetry';
|
||||||
|
import { COMMUNITY_NODES_INSTALLATION_DOCS_URL, CUSTOM_NODES_DOCS_URL } from '@/constants';
|
||||||
|
import type { INodeUi } from '@/Interface';
|
||||||
|
import { isCommunityPackageName } from '@/utils/nodeTypesUtils';
|
||||||
|
import { N8nIcon, N8nLink, N8nText } from '@n8n/design-system';
|
||||||
|
import { useI18n } from '@n8n/i18n';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { I18nT } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { node } = defineProps<{ node: INodeUi }>();
|
||||||
|
|
||||||
|
const i18n = useI18n();
|
||||||
|
const telemetry = useTelemetry();
|
||||||
|
|
||||||
|
const isCommunityNode = computed(() => isCommunityPackageName(node.type));
|
||||||
|
const npmPackage = computed(() => node.type.split('.')[0]);
|
||||||
|
|
||||||
|
function onMissingNodeTextClick(event: MouseEvent) {
|
||||||
|
if (event.target instanceof Element && event.target.localName === 'a') {
|
||||||
|
telemetry.track('user clicked cnr browse button', {
|
||||||
|
source: 'cnr missing node modal',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMissingNodeLearnMoreLinkClick() {
|
||||||
|
telemetry.track('user clicked cnr docs link', {
|
||||||
|
source: 'missing node modal source',
|
||||||
|
package_name: node.type.split('.')[0],
|
||||||
|
node_type: node.type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="$style.nodeIsNotValid">
|
||||||
|
<p :class="$style.warningIcon">
|
||||||
|
<N8nIcon icon="triangle-alert" />
|
||||||
|
</p>
|
||||||
|
<div class="mt-s mb-xs">
|
||||||
|
<N8nText size="large" color="text-dark" bold>
|
||||||
|
{{ i18n.baseText('nodeSettings.communityNodeUnknown.title') }}
|
||||||
|
</N8nText>
|
||||||
|
</div>
|
||||||
|
<div v-if="isCommunityNode" :class="$style.descriptionContainer">
|
||||||
|
<div class="mb-l">
|
||||||
|
<I18nT
|
||||||
|
keypath="nodeSettings.communityNodeUnknown.description"
|
||||||
|
tag="span"
|
||||||
|
scope="global"
|
||||||
|
@click="onMissingNodeTextClick"
|
||||||
|
>
|
||||||
|
<template #action>
|
||||||
|
<a
|
||||||
|
:href="`https://www.npmjs.com/package/${npmPackage}`"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{{ npmPackage }}
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
</I18nT>
|
||||||
|
</div>
|
||||||
|
<N8nLink :to="COMMUNITY_NODES_INSTALLATION_DOCS_URL" @click="onMissingNodeLearnMoreLinkClick">
|
||||||
|
{{ i18n.baseText('nodeSettings.communityNodeUnknown.installLink.text') }}
|
||||||
|
</N8nLink>
|
||||||
|
</div>
|
||||||
|
<I18nT v-else keypath="nodeSettings.nodeTypeUnknown.description" tag="span" scope="global">
|
||||||
|
<template #action>
|
||||||
|
<a
|
||||||
|
:href="CUSTOM_NODES_DOCS_URL"
|
||||||
|
target="_blank"
|
||||||
|
v-text="i18n.baseText('nodeSettings.nodeTypeUnknown.description.customNode')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</I18nT>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.nodeIsNotValid {
|
||||||
|
height: 75%;
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
line-height: var(--font-line-height-regular);
|
||||||
|
}
|
||||||
|
|
||||||
|
.warningIcon {
|
||||||
|
color: var(--color-text-lighter);
|
||||||
|
font-size: var(--font-size-2xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.descriptionContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -30,6 +30,22 @@ import { captureException } from '@sentry/vue';
|
|||||||
import { isPresent } from './typesUtils';
|
import { isPresent } from './typesUtils';
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
import { omitKey } from './objectUtils';
|
import { omitKey } from './objectUtils';
|
||||||
|
import type { BaseTextKey } from '@n8n/i18n';
|
||||||
|
|
||||||
|
export function getNodeSettingsInitialValues(): INodeParameters {
|
||||||
|
return {
|
||||||
|
color: '#ff0000',
|
||||||
|
alwaysOutputData: false,
|
||||||
|
executeOnce: false,
|
||||||
|
notesInFlow: false,
|
||||||
|
onError: 'stopWorkflow',
|
||||||
|
retryOnFail: false,
|
||||||
|
maxTries: 3,
|
||||||
|
waitBetweenTries: 1000,
|
||||||
|
notes: '',
|
||||||
|
parameters: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function setValue(
|
export function setValue(
|
||||||
nodeValues: Ref<INodeParameters>,
|
nodeValues: Ref<INodeParameters>,
|
||||||
@@ -448,3 +464,235 @@ export function shouldSkipParamValidation(
|
|||||||
Boolean(parameter.allowArbitraryValues))
|
Boolean(parameter.allowArbitraryValues))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createCommonNodeSettings(
|
||||||
|
isExecutable: boolean,
|
||||||
|
isTriggerNode: boolean,
|
||||||
|
t: (key: BaseTextKey) => string,
|
||||||
|
) {
|
||||||
|
const ret: INodeProperties[] = [];
|
||||||
|
|
||||||
|
if (isExecutable && !isTriggerNode) {
|
||||||
|
ret.push(
|
||||||
|
{
|
||||||
|
displayName: t('nodeSettings.alwaysOutputData.displayName'),
|
||||||
|
name: 'alwaysOutputData',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
noDataExpression: true,
|
||||||
|
description: t('nodeSettings.alwaysOutputData.description'),
|
||||||
|
isNodeSetting: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: t('nodeSettings.executeOnce.displayName'),
|
||||||
|
name: 'executeOnce',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
noDataExpression: true,
|
||||||
|
description: t('nodeSettings.executeOnce.description'),
|
||||||
|
isNodeSetting: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: t('nodeSettings.retryOnFail.displayName'),
|
||||||
|
name: 'retryOnFail',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
noDataExpression: true,
|
||||||
|
description: t('nodeSettings.retryOnFail.description'),
|
||||||
|
isNodeSetting: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: t('nodeSettings.maxTries.displayName'),
|
||||||
|
name: 'maxTries',
|
||||||
|
type: 'number',
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 2,
|
||||||
|
maxValue: 5,
|
||||||
|
},
|
||||||
|
default: 3,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
retryOnFail: [true],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
noDataExpression: true,
|
||||||
|
description: t('nodeSettings.maxTries.description'),
|
||||||
|
isNodeSetting: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: t('nodeSettings.waitBetweenTries.displayName'),
|
||||||
|
name: 'waitBetweenTries',
|
||||||
|
type: 'number',
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 0,
|
||||||
|
maxValue: 5000,
|
||||||
|
},
|
||||||
|
default: 1000,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
retryOnFail: [true],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
noDataExpression: true,
|
||||||
|
description: t('nodeSettings.waitBetweenTries.description'),
|
||||||
|
isNodeSetting: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: t('nodeSettings.onError.displayName'),
|
||||||
|
name: 'onError',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: t('nodeSettings.onError.options.stopWorkflow.displayName'),
|
||||||
|
value: 'stopWorkflow',
|
||||||
|
description: t('nodeSettings.onError.options.stopWorkflow.description'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t('nodeSettings.onError.options.continueRegularOutput.displayName'),
|
||||||
|
value: 'continueRegularOutput',
|
||||||
|
description: t('nodeSettings.onError.options.continueRegularOutput.description'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t('nodeSettings.onError.options.continueErrorOutput.displayName'),
|
||||||
|
value: 'continueErrorOutput',
|
||||||
|
description: t('nodeSettings.onError.options.continueErrorOutput.description'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'stopWorkflow',
|
||||||
|
description: t('nodeSettings.onError.description'),
|
||||||
|
noDataExpression: true,
|
||||||
|
isNodeSetting: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.push(
|
||||||
|
{
|
||||||
|
displayName: t('nodeSettings.notes.displayName'),
|
||||||
|
name: 'notes',
|
||||||
|
type: 'string',
|
||||||
|
typeOptions: {
|
||||||
|
rows: 5,
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
noDataExpression: true,
|
||||||
|
description: t('nodeSettings.notes.description'),
|
||||||
|
isNodeSetting: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: t('nodeSettings.notesInFlow.displayName'),
|
||||||
|
name: 'notesInFlow',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
noDataExpression: true,
|
||||||
|
description: t('nodeSettings.notesInFlow.description'),
|
||||||
|
isNodeSetting: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function collectSettings(node: INodeUi, nodeSettings: INodeProperties[]): INodeParameters {
|
||||||
|
let ret = getNodeSettingsInitialValues();
|
||||||
|
|
||||||
|
const foundNodeSettings = [];
|
||||||
|
|
||||||
|
if (node.color) {
|
||||||
|
foundNodeSettings.push('color');
|
||||||
|
ret = {
|
||||||
|
...ret,
|
||||||
|
color: node.color,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.notes) {
|
||||||
|
foundNodeSettings.push('notes');
|
||||||
|
ret = {
|
||||||
|
...ret,
|
||||||
|
notes: node.notes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.alwaysOutputData) {
|
||||||
|
foundNodeSettings.push('alwaysOutputData');
|
||||||
|
ret = {
|
||||||
|
...ret,
|
||||||
|
alwaysOutputData: node.alwaysOutputData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.executeOnce) {
|
||||||
|
foundNodeSettings.push('executeOnce');
|
||||||
|
ret = {
|
||||||
|
...ret,
|
||||||
|
executeOnce: node.executeOnce,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.continueOnFail) {
|
||||||
|
foundNodeSettings.push('onError');
|
||||||
|
ret = {
|
||||||
|
...ret,
|
||||||
|
onError: 'continueRegularOutput',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.onError) {
|
||||||
|
foundNodeSettings.push('onError');
|
||||||
|
ret = {
|
||||||
|
...ret,
|
||||||
|
onError: node.onError,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.notesInFlow) {
|
||||||
|
foundNodeSettings.push('notesInFlow');
|
||||||
|
ret = {
|
||||||
|
...ret,
|
||||||
|
notesInFlow: node.notesInFlow,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.retryOnFail) {
|
||||||
|
foundNodeSettings.push('retryOnFail');
|
||||||
|
ret = {
|
||||||
|
...ret,
|
||||||
|
retryOnFail: node.retryOnFail,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.maxTries) {
|
||||||
|
foundNodeSettings.push('maxTries');
|
||||||
|
ret = {
|
||||||
|
...ret,
|
||||||
|
maxTries: node.maxTries,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.waitBetweenTries) {
|
||||||
|
foundNodeSettings.push('waitBetweenTries');
|
||||||
|
ret = {
|
||||||
|
...ret,
|
||||||
|
waitBetweenTries: node.waitBetweenTries,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set default node settings
|
||||||
|
for (const nodeSetting of nodeSettings) {
|
||||||
|
if (!foundNodeSettings.includes(nodeSetting.name)) {
|
||||||
|
// Set default value
|
||||||
|
ret = {
|
||||||
|
...ret,
|
||||||
|
[nodeSetting.name]: nodeSetting.default,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = {
|
||||||
|
...ret,
|
||||||
|
parameters: deepCopy(node.parameters),
|
||||||
|
};
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user