mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +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,
|
||||
connectionIndex?: number,
|
||||
];
|
||||
redrawNode: [nodeName: string];
|
||||
stopExecution: [];
|
||||
}>();
|
||||
|
||||
@@ -81,7 +80,6 @@ const message = useMessage();
|
||||
const { APP_Z_INDEXES } = useStyles();
|
||||
|
||||
const settingsEventBus = createEventBus();
|
||||
const redrawRequired = ref(false);
|
||||
const runInputIndex = ref(-1);
|
||||
const runOutputIndex = computed(() => ndvStore.output.run ?? -1);
|
||||
const selectedInput = ref<string | undefined>();
|
||||
@@ -498,18 +496,6 @@ const close = async () => {
|
||||
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) {
|
||||
const shouldPinDataBeforeClosing = await message.confirm(
|
||||
'',
|
||||
@@ -842,7 +828,6 @@ onBeforeUnmount(() => {
|
||||
@value-changed="valueChanged"
|
||||
@execute="onNodeExecute"
|
||||
@stop-execution="onStopExecution"
|
||||
@redraw-required="redrawRequired = true"
|
||||
@activate="onWorkflowActivate"
|
||||
@switch-selected-node="onSwitchSelectedNode"
|
||||
@open-connection-node-creator="onOpenConnectionNodeCreator"
|
||||
|
||||
@@ -85,7 +85,6 @@ const message = useMessage();
|
||||
const { APP_Z_INDEXES } = useStyles();
|
||||
|
||||
const settingsEventBus = createEventBus();
|
||||
const redrawRequired = ref(false);
|
||||
const runInputIndex = ref(-1);
|
||||
const runOutputIndex = ref(-1);
|
||||
const isLinkingEnabled = ref(true);
|
||||
@@ -821,7 +820,6 @@ onBeforeUnmount(() => {
|
||||
:class="$style.settings"
|
||||
@execute="onNodeExecute"
|
||||
@stop-execution="onStopExecution"
|
||||
@redraw-required="redrawRequired = true"
|
||||
@activate="onWorkflowActivate"
|
||||
@switch-selected-node="onSwitchSelectedNode"
|
||||
@open-connection-node-creator="onOpenConnectionNodeCreator"
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { useTemplateRef, computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import type {
|
||||
INodeParameters,
|
||||
INodeProperties,
|
||||
NodeConnectionType,
|
||||
NodeParameterValue,
|
||||
INodeCredentialDescription,
|
||||
@@ -16,12 +15,7 @@ import type {
|
||||
IUpdateInformation,
|
||||
} from '@/Interface';
|
||||
|
||||
import {
|
||||
BASE_NODE_SURVEY_URL,
|
||||
COMMUNITY_NODES_INSTALLATION_DOCS_URL,
|
||||
CUSTOM_NODES_DOCS_URL,
|
||||
NDV_UI_OVERHAUL_EXPERIMENT,
|
||||
} from '@/constants';
|
||||
import { BASE_NODE_SURVEY_URL, NDV_UI_OVERHAUL_EXPERIMENT } from '@/constants';
|
||||
|
||||
import ParameterInputList from '@/components/ParameterInputList.vue';
|
||||
import NodeCredentials from '@/components/NodeCredentials.vue';
|
||||
@@ -32,7 +26,12 @@ import NodeSettingsHeader from '@/components/NodeSettingsHeader.vue';
|
||||
import get from 'lodash/get';
|
||||
|
||||
import NodeExecuteButton from './NodeExecuteButton.vue';
|
||||
import { nameIsParameter } from '@/utils/nodeSettingsUtils';
|
||||
import {
|
||||
collectSettings,
|
||||
createCommonNodeSettings,
|
||||
nameIsParameter,
|
||||
getNodeSettingsInitialValues,
|
||||
} from '@/utils/nodeSettingsUtils';
|
||||
import { isCommunityPackageName } from '@/utils/nodeTypesUtils';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.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 { useResizeObserver } from '@vueuse/core';
|
||||
import { useNodeSettingsParameters } from '@/composables/useNodeSettingsParameters';
|
||||
import { I18nT } from 'vue-i18n';
|
||||
import { N8nBlockUi, N8nIcon, N8nLink, N8nNotice, N8nText } from '@n8n/design-system';
|
||||
import { N8nBlockUi, N8nIcon, N8nNotice, N8nText } from '@n8n/design-system';
|
||||
import ExperimentalEmbeddedNdvHeader from '@/components/canvas/experimental/components/ExperimentalEmbeddedNdvHeader.vue';
|
||||
import NodeSettingsInvalidNodeWarning from '@/components/NodeSettingsInvalidNodeWarning.vue';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@@ -86,7 +85,6 @@ const props = withDefaults(
|
||||
|
||||
const emit = defineEmits<{
|
||||
stopExecution: [];
|
||||
redrawRequired: [];
|
||||
valueChanged: [value: IUpdateInformation];
|
||||
switchSelectedNode: [nodeName: string];
|
||||
openConnectionNodeCreator: [
|
||||
@@ -101,18 +99,7 @@ const emit = defineEmits<{
|
||||
|
||||
const slots = defineSlots<{ actions?: {} }>();
|
||||
|
||||
const nodeValues = ref<INodeParameters>({
|
||||
color: '#ff0000',
|
||||
alwaysOutputData: false,
|
||||
executeOnce: false,
|
||||
notesInFlow: false,
|
||||
onError: 'stopWorkflow',
|
||||
retryOnFail: false,
|
||||
maxTries: 3,
|
||||
waitBetweenTries: 1000,
|
||||
notes: '',
|
||||
parameters: {},
|
||||
});
|
||||
const nodeValues = ref<INodeParameters>(getNodeSettingsInitialValues());
|
||||
|
||||
const nodeTypesStore = useNodeTypesStore();
|
||||
const ndvStore = useNDVStore();
|
||||
@@ -145,7 +132,6 @@ const openPanel = ref<Tab>('params');
|
||||
const nodeValuesInitialized = ref(false);
|
||||
|
||||
const hiddenIssuesInputs = ref<string[]>([]);
|
||||
const nodeSettings = ref<INodeProperties[]>([]);
|
||||
const subConnections = ref<InstanceType<typeof NDVSubConnections> | null>(null);
|
||||
|
||||
const installedPackage = ref<PublicInstalledPackage | undefined>(undefined);
|
||||
@@ -306,11 +292,6 @@ const valueChanged = (parameterData: IUpdateInformation) => {
|
||||
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') {
|
||||
// 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);
|
||||
};
|
||||
|
||||
const populateSettings = () => {
|
||||
if (isExecutable.value && !isTriggerNode.value) {
|
||||
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 nodeSettings = computed(() =>
|
||||
createCommonNodeSettings(isExecutable.value, isToolNode.value, i18n.baseText.bind(i18n)),
|
||||
);
|
||||
|
||||
const onParameterBlur = (parameterName: string) => {
|
||||
hiddenIssuesInputs.value = hiddenIssuesInputs.value.filter((name) => name !== parameterName);
|
||||
@@ -631,103 +489,7 @@ const setNodeValues = () => {
|
||||
|
||||
if (nodeType.value !== null) {
|
||||
nodeValid.value = true;
|
||||
|
||||
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),
|
||||
};
|
||||
nodeValues.value = collectSettings(node.value, nodeSettings.value);
|
||||
} else {
|
||||
nodeValid.value = false;
|
||||
}
|
||||
@@ -735,22 +497,6 @@ const setNodeValues = () => {
|
||||
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 = () => {
|
||||
emit('stopExecution');
|
||||
};
|
||||
@@ -782,7 +528,6 @@ watch(node, () => {
|
||||
|
||||
onMounted(async () => {
|
||||
populateHiddenIssuesSet();
|
||||
populateSettings();
|
||||
setNodeValues();
|
||||
props.eventBus?.on('openSettings', openSettings);
|
||||
if (node.value !== null) {
|
||||
@@ -885,49 +630,9 @@ function displayCredentials(credentialTypeDescription: INodeCredentialDescriptio
|
||||
@value-changed="valueChanged"
|
||||
@tab-changed="onTabSelect"
|
||||
/>
|
||||
<div v-if="node && !nodeValid" class="node-is-not-valid">
|
||||
<p :class="$style.warningIcon">
|
||||
<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>
|
||||
|
||||
<NodeSettingsInvalidNodeWarning v-if="node && !nodeValid" :node="node" />
|
||||
|
||||
<div
|
||||
v-if="node && nodeValid"
|
||||
ref="nodeParameterWrapper"
|
||||
@@ -1060,16 +765,6 @@ function displayCredentials(credentialTypeDescription: INodeCredentialDescriptio
|
||||
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 {
|
||||
margin-top: auto;
|
||||
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 {
|
||||
display: flex;
|
||||
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 type { Ref } from 'vue';
|
||||
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(
|
||||
nodeValues: Ref<INodeParameters>,
|
||||
@@ -448,3 +464,235 @@ export function shouldSkipParamValidation(
|
||||
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