mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
252 lines
7.0 KiB
TypeScript
252 lines
7.0 KiB
TypeScript
import get from 'lodash/get';
|
|
import set from 'lodash/set';
|
|
import { ref } from 'vue';
|
|
import {
|
|
type INode,
|
|
type INodeParameters,
|
|
type INodeProperties,
|
|
type NodeParameterValue,
|
|
NodeHelpers,
|
|
deepCopy,
|
|
} from 'n8n-workflow';
|
|
import { useTelemetry } from './useTelemetry';
|
|
import { useNodeHelpers } from './useNodeHelpers';
|
|
import { useCanvasOperations } from './useCanvasOperations';
|
|
import { useExternalHooks } from './useExternalHooks';
|
|
import type { INodeUi, IUpdateInformation } from '@/Interface';
|
|
import { updateDynamicConnections, updateParameterByPath } from '@/utils/nodeSettingsUtils';
|
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
|
import { useFocusPanelStore } from '@/stores/focusPanel.store';
|
|
import { useNDVStore } from '@/stores/ndv.store';
|
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
|
import { CUSTOM_API_CALL_KEY } from '@/constants';
|
|
import { omitKey } from '@/utils/objectUtils';
|
|
|
|
export function useNodeSettingsParameters() {
|
|
const workflowsStore = useWorkflowsStore();
|
|
const nodeTypesStore = useNodeTypesStore();
|
|
const telemetry = useTelemetry();
|
|
const nodeHelpers = useNodeHelpers();
|
|
const canvasOperations = useCanvasOperations();
|
|
const externalHooks = useExternalHooks();
|
|
|
|
const nodeValues = ref<INodeParameters>({
|
|
color: '#ff0000',
|
|
alwaysOutputData: false,
|
|
executeOnce: false,
|
|
notesInFlow: false,
|
|
onError: 'stopWorkflow',
|
|
retryOnFail: false,
|
|
maxTries: 3,
|
|
waitBetweenTries: 1000,
|
|
notes: '',
|
|
parameters: {},
|
|
});
|
|
|
|
function setValue(name: string, value: NodeParameterValue) {
|
|
const nameParts = name.split('.');
|
|
let lastNamePart: string | undefined = nameParts.pop();
|
|
|
|
let isArray = false;
|
|
if (lastNamePart?.includes('[')) {
|
|
// It includes an index so we have to extract it
|
|
const lastNameParts = lastNamePart.match(/(.*)\[(\d+)\]$/);
|
|
if (lastNameParts) {
|
|
nameParts.push(lastNameParts[1]);
|
|
lastNamePart = lastNameParts[2];
|
|
isArray = true;
|
|
}
|
|
}
|
|
|
|
// Set the value so that everything updates correctly in the UI
|
|
if (nameParts.length === 0) {
|
|
// Data is on top level
|
|
if (value === null) {
|
|
// Property should be deleted
|
|
if (lastNamePart) {
|
|
nodeValues.value = omitKey(nodeValues.value, lastNamePart);
|
|
}
|
|
} else {
|
|
// Value should be set
|
|
nodeValues.value = {
|
|
...nodeValues.value,
|
|
[lastNamePart as string]: value,
|
|
};
|
|
}
|
|
} else {
|
|
// Data is on lower level
|
|
if (value === null) {
|
|
// Property should be deleted
|
|
let tempValue = get(nodeValues.value, nameParts.join('.')) as
|
|
| INodeParameters
|
|
| INodeParameters[];
|
|
|
|
if (lastNamePart && !Array.isArray(tempValue)) {
|
|
tempValue = omitKey(tempValue, lastNamePart);
|
|
}
|
|
|
|
if (isArray && Array.isArray(tempValue) && tempValue.length === 0) {
|
|
// If a value from an array got delete and no values are left
|
|
// delete also the parent
|
|
lastNamePart = nameParts.pop();
|
|
tempValue = get(nodeValues.value, nameParts.join('.')) as INodeParameters;
|
|
if (lastNamePart) {
|
|
tempValue = omitKey(tempValue, lastNamePart);
|
|
}
|
|
}
|
|
} else {
|
|
// Value should be set
|
|
if (typeof value === 'object') {
|
|
set(
|
|
get(nodeValues.value, nameParts.join('.')) as Record<string, unknown>,
|
|
lastNamePart as string,
|
|
deepCopy(value),
|
|
);
|
|
} else {
|
|
set(
|
|
get(nodeValues.value, nameParts.join('.')) as Record<string, unknown>,
|
|
lastNamePart as string,
|
|
value,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
nodeValues.value = { ...nodeValues.value };
|
|
}
|
|
|
|
function updateNodeParameter(
|
|
parameterData: IUpdateInformation & { name: `parameters.${string}` },
|
|
newValue: NodeParameterValue,
|
|
node: INode,
|
|
isToolNode: boolean,
|
|
) {
|
|
const nodeTypeDescription = nodeTypesStore.getNodeType(node.type, node.typeVersion);
|
|
if (!nodeTypeDescription) {
|
|
return;
|
|
}
|
|
|
|
// Get only the parameters which are different to the defaults
|
|
let nodeParameters = NodeHelpers.getNodeParameters(
|
|
nodeTypeDescription.properties,
|
|
node.parameters,
|
|
false,
|
|
false,
|
|
node,
|
|
nodeTypeDescription,
|
|
);
|
|
|
|
const oldNodeParameters = Object.assign({}, nodeParameters);
|
|
|
|
// Copy the data because it is the data of vuex so make sure that
|
|
// we do not edit it directly
|
|
nodeParameters = deepCopy(nodeParameters);
|
|
|
|
const parameterPath = updateParameterByPath(
|
|
parameterData.name,
|
|
newValue,
|
|
nodeParameters,
|
|
nodeTypeDescription,
|
|
node.typeVersion,
|
|
);
|
|
|
|
// Get the parameters with the now new defaults according to the
|
|
// from the user actually defined parameters
|
|
nodeParameters = NodeHelpers.getNodeParameters(
|
|
nodeTypeDescription.properties,
|
|
nodeParameters as INodeParameters,
|
|
true,
|
|
false,
|
|
node,
|
|
nodeTypeDescription,
|
|
);
|
|
|
|
if (isToolNode) {
|
|
const updatedDescription = NodeHelpers.getUpdatedToolDescription(
|
|
nodeTypeDescription,
|
|
nodeParameters,
|
|
node.parameters,
|
|
);
|
|
|
|
if (updatedDescription && nodeParameters) {
|
|
nodeParameters.toolDescription = updatedDescription;
|
|
}
|
|
}
|
|
|
|
if (NodeHelpers.isDefaultNodeName(node.name, nodeTypeDescription, node.parameters ?? {})) {
|
|
const newName = NodeHelpers.makeNodeName(nodeParameters ?? {}, nodeTypeDescription);
|
|
// Account for unique-ified nodes with `<name><digit>`
|
|
if (!node.name.startsWith(newName)) {
|
|
// We need a timeout here to support events reacting to the valueChange based on node names
|
|
setTimeout(async () => await canvasOperations.renameNode(node.name, newName));
|
|
}
|
|
}
|
|
|
|
for (const [key, value] of Object.entries(nodeParameters as object)) {
|
|
if (value !== null && value !== undefined) {
|
|
setValue(`parameters.${key}`, value as string);
|
|
}
|
|
}
|
|
|
|
// Update the data in vuex
|
|
const updateInformation: IUpdateInformation = {
|
|
name: node.name,
|
|
value: nodeParameters,
|
|
};
|
|
|
|
const connections = workflowsStore.allConnections;
|
|
|
|
const updatedConnections = updateDynamicConnections(node, connections, parameterData);
|
|
|
|
if (updatedConnections) {
|
|
workflowsStore.setConnections(updatedConnections, true);
|
|
}
|
|
|
|
workflowsStore.setNodeParameters(updateInformation);
|
|
|
|
void externalHooks.run('nodeSettings.valueChanged', {
|
|
parameterPath,
|
|
newValue,
|
|
parameters: nodeTypeDescription.properties,
|
|
oldNodeParameters,
|
|
});
|
|
|
|
nodeHelpers.updateNodeParameterIssuesByName(node.name);
|
|
nodeHelpers.updateNodeCredentialIssuesByName(node.name);
|
|
telemetry.trackNodeParametersValuesChange(nodeTypeDescription.name, parameterData);
|
|
}
|
|
|
|
function handleFocus(node: INodeUi | undefined, path: string, parameter: INodeProperties) {
|
|
if (!node) return;
|
|
|
|
const ndvStore = useNDVStore();
|
|
const focusPanelStore = useFocusPanelStore();
|
|
|
|
focusPanelStore.setFocusedNodeParameter({
|
|
nodeId: node.id,
|
|
parameterPath: path,
|
|
parameter,
|
|
});
|
|
|
|
if (ndvStore.activeNode) {
|
|
ndvStore.setActiveNodeName(null);
|
|
ndvStore.resetNDVPushRef();
|
|
}
|
|
|
|
focusPanelStore.focusPanelActive = true;
|
|
}
|
|
|
|
function shouldSkipParamValidation(value: string | number | boolean | null) {
|
|
return typeof value === 'string' && value.includes(CUSTOM_API_CALL_KEY);
|
|
}
|
|
|
|
return {
|
|
nodeValues,
|
|
setValue,
|
|
updateParameterByPath,
|
|
updateNodeParameter,
|
|
handleFocus,
|
|
shouldSkipParamValidation,
|
|
};
|
|
}
|