mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
test(editor): Add tests for node settings parameters composable (no-changelog) (#17232)
This commit is contained in:
@@ -24,7 +24,7 @@ export default defineConfig(frontendConfig, {
|
||||
'@typescript-eslint/dot-notation': 'warn',
|
||||
'@stylistic/lines-between-class-members': 'warn',
|
||||
'@stylistic/member-delimiter-style': 'warn',
|
||||
'@typescript-eslint/naming-convention': 'warn',
|
||||
'@typescript-eslint/naming-convention': 'off',
|
||||
'@typescript-eslint/no-empty-interface': 'warn',
|
||||
'@typescript-eslint/no-for-in-array': 'warn',
|
||||
'@typescript-eslint/no-loop-func': 'warn',
|
||||
|
||||
@@ -132,7 +132,6 @@ declare global {
|
||||
disallowReturnToOpener?: boolean;
|
||||
}) => Promise<Window>;
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
Cypress: unknown;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +303,6 @@ describe('useActionsGenerator', () => {
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
'@version': [1],
|
||||
resource: ['user'],
|
||||
},
|
||||
@@ -324,7 +323,6 @@ describe('useActionsGenerator', () => {
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
'@version': [2],
|
||||
resource: ['user'],
|
||||
},
|
||||
@@ -369,7 +367,6 @@ describe('useActionsGenerator', () => {
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
'@version': [1, 2],
|
||||
resource: ['user'],
|
||||
},
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
isResourceLocatorParameterType,
|
||||
isValidParameterOption,
|
||||
parseFromExpression,
|
||||
shouldSkipParamValidation,
|
||||
} from '@/utils/nodeSettingsUtils';
|
||||
import { hasExpressionMapping, isValueExpression } from '@/utils/nodeTypesUtils';
|
||||
|
||||
@@ -426,7 +427,7 @@ const getIssues = computed<string[]>(() => {
|
||||
|
||||
let checkValues: string[] = [];
|
||||
|
||||
if (!nodeSettingsParameters.shouldSkipParamValidation(displayValue.value)) {
|
||||
if (!shouldSkipParamValidation(displayValue.value)) {
|
||||
if (Array.isArray(displayValue.value)) {
|
||||
checkValues = checkValues.concat(displayValue.value);
|
||||
} else {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { computed } from 'vue';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { usePostHog } from '@/stores/posthog.store';
|
||||
import { AI_TRANSFORM_NODE_TYPE, FOCUS_PANEL_EXPERIMENT } from '@/constants';
|
||||
import { getParameterTypeOption } from '@/utils/nodeSettingsUtils';
|
||||
|
||||
interface Props {
|
||||
parameter: INodeProperties;
|
||||
@@ -41,12 +42,28 @@ const i18n = useI18n();
|
||||
const ndvStore = useNDVStore();
|
||||
const posthogStore = usePostHog();
|
||||
|
||||
const activeNode = computed(() => ndvStore.activeNode);
|
||||
const isDefault = computed(() => props.parameter.default === props.value);
|
||||
const isValueAnExpression = computed(() => isValueExpression(props.parameter, props.value));
|
||||
const isHtmlEditor = computed(() => getArgument('editor') === 'htmlEditor');
|
||||
const isHtmlEditor = computed(
|
||||
() => getParameterTypeOption(props.parameter, 'editor') === 'htmlEditor',
|
||||
);
|
||||
const shouldShowExpressionSelector = computed(
|
||||
() => !props.parameter.noDataExpression && props.showExpressionSelector && !props.isReadOnly,
|
||||
);
|
||||
|
||||
const isFocusPanelFeatureEnabled = computed(() => {
|
||||
return posthogStore.getVariant(FOCUS_PANEL_EXPERIMENT.name) === FOCUS_PANEL_EXPERIMENT.variant;
|
||||
});
|
||||
const hasFocusAction = computed(
|
||||
() =>
|
||||
isFocusPanelFeatureEnabled.value &&
|
||||
!props.parameter.isNodeSetting &&
|
||||
!props.isReadOnly &&
|
||||
activeNode.value && // checking that it's inside ndv
|
||||
(props.parameter.type === 'string' || props.parameter.type === 'json'),
|
||||
);
|
||||
|
||||
const shouldShowOptions = computed(() => {
|
||||
if (props.isReadOnly) {
|
||||
return false;
|
||||
@@ -71,7 +88,6 @@ const shouldShowOptions = computed(() => {
|
||||
return false;
|
||||
});
|
||||
const selectedView = computed(() => (isValueAnExpression.value ? 'expression' : 'fixed'));
|
||||
const activeNode = computed(() => ndvStore.activeNode);
|
||||
const hasRemoteMethod = computed(
|
||||
() =>
|
||||
!!props.parameter.typeOptions?.loadOptionsMethod || !!props.parameter.typeOptions?.loadOptions,
|
||||
@@ -84,18 +100,6 @@ const resetValueLabel = computed(() => {
|
||||
return i18n.baseText('parameterInput.resetValue');
|
||||
});
|
||||
|
||||
const isFocusPanelFeatureEnabled = computed(() => {
|
||||
return posthogStore.getVariant(FOCUS_PANEL_EXPERIMENT.name) === FOCUS_PANEL_EXPERIMENT.variant;
|
||||
});
|
||||
const hasFocusAction = computed(
|
||||
() =>
|
||||
isFocusPanelFeatureEnabled.value &&
|
||||
!props.parameter.isNodeSetting &&
|
||||
!props.isReadOnly &&
|
||||
activeNode.value && // checking that it's inside ndv
|
||||
(props.parameter.type === 'string' || props.parameter.type === 'json'),
|
||||
);
|
||||
|
||||
const actions = computed(() => {
|
||||
if (Array.isArray(props.customActions) && props.customActions.length > 0) {
|
||||
return props.customActions;
|
||||
@@ -161,17 +165,6 @@ const onViewSelected = (selected: string) => {
|
||||
emit('update:modelValue', 'removeExpression');
|
||||
}
|
||||
};
|
||||
const getArgument = (argumentName: string) => {
|
||||
if (props.parameter.typeOptions === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (props.parameter.typeOptions[argumentName] === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return props.parameter.typeOptions[argumentName];
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -45,10 +45,11 @@ import { STORES } from '@n8n/stores';
|
||||
import type { Connection } from '@vue-flow/core';
|
||||
import { useClipboard } from '@/composables/useClipboard';
|
||||
import { createCanvasConnectionHandleString } from '@/utils/canvasUtils';
|
||||
import { nextTick } from 'vue';
|
||||
import { nextTick, ref } from 'vue';
|
||||
import type { CanvasLayoutEvent } from './useCanvasLayout';
|
||||
import { useTelemetry } from './useTelemetry';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import * as nodeHelpers from '@/composables/useNodeHelpers';
|
||||
|
||||
vi.mock('n8n-workflow', async (importOriginal) => {
|
||||
const actual = await importOriginal<{}>();
|
||||
@@ -2823,7 +2824,15 @@ describe('useCanvasOperations', () => {
|
||||
const uiStore = mockedStore(useUIStore);
|
||||
const executionsStore = mockedStore(useExecutionsStore);
|
||||
|
||||
const nodeHelpers = { credentialsUpdated: { value: true } };
|
||||
const credentialsUpdatedRef = ref(true);
|
||||
const credentialsSpy = vi.spyOn(credentialsUpdatedRef, 'value', 'set');
|
||||
const nodeHelpersOriginal = nodeHelpers.useNodeHelpers();
|
||||
vi.spyOn(nodeHelpers, 'useNodeHelpers').mockImplementation(() => {
|
||||
return {
|
||||
...nodeHelpersOriginal,
|
||||
credentialsUpdated: credentialsUpdatedRef,
|
||||
};
|
||||
});
|
||||
|
||||
nodeCreatorStore.setNodeCreatorState = vi.fn();
|
||||
nodeCreatorStore.setShowScrim = vi.fn();
|
||||
@@ -2854,7 +2863,6 @@ describe('useCanvasOperations', () => {
|
||||
startedAt: new Date(),
|
||||
},
|
||||
];
|
||||
nodeHelpers.credentialsUpdated.value = true;
|
||||
|
||||
const { resetWorkspace } = useCanvasOperations();
|
||||
|
||||
@@ -2872,6 +2880,8 @@ describe('useCanvasOperations', () => {
|
||||
expect(uiStore.resetLastInteractedWith).toHaveBeenCalled();
|
||||
expect(uiStore.stateIsDirty).toBe(false);
|
||||
expect(executionsStore.activeExecution).toBeNull();
|
||||
expect(credentialsSpy).toHaveBeenCalledWith(false);
|
||||
expect(credentialsUpdatedRef.value).toBe(false);
|
||||
});
|
||||
|
||||
it('should not call removeTestWebhook if executionWaitingForWebhook is false', () => {
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { setActivePinia } from 'pinia';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { useFocusPanelStore } from '@/stores/focusPanel.store';
|
||||
import { useNodeSettingsParameters } from './useNodeSettingsParameters';
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import * as nodeHelpers from '@/composables/useNodeHelpers';
|
||||
import * as workflowHelpers from '@/composables/useWorkflowHelpers';
|
||||
import * as nodeSettingsUtils from '@/utils/nodeSettingsUtils';
|
||||
import * as nodeTypesUtils from '@/utils/nodeTypesUtils';
|
||||
import type { INodeProperties, INodeTypeDescription } from 'n8n-workflow';
|
||||
import type { MockedStore } from '@/__tests__/utils';
|
||||
import { mockedStore } from '@/__tests__/utils';
|
||||
import type { INodeUi } from '@/Interface';
|
||||
@@ -55,7 +60,7 @@ describe('useNodeSettingsParameters', () => {
|
||||
let focusPanelStore: MockedStore<typeof useFocusPanelStore>;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
setActivePinia(createTestingPinia());
|
||||
|
||||
ndvStore = mockedStore(useNDVStore);
|
||||
focusPanelStore = mockedStore(useFocusPanelStore);
|
||||
@@ -75,6 +80,10 @@ describe('useNodeSettingsParameters', () => {
|
||||
focusPanelStore.focusPanelActive = false;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('sets focused node parameter', () => {
|
||||
const { handleFocus } = useNodeSettingsParameters();
|
||||
const node: INodeUi = {
|
||||
@@ -120,4 +129,204 @@ describe('useNodeSettingsParameters', () => {
|
||||
expect(focusPanelStore.openWithFocusedNodeParameter).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldDisplayNodeParameter', () => {
|
||||
const displayParameterSpy = vi.fn();
|
||||
function mockNodeHelpers({ isCustomApiCallSelected = false } = {}) {
|
||||
const originalNodeHelpers = nodeHelpers.useNodeHelpers();
|
||||
|
||||
vi.spyOn(nodeHelpers, 'useNodeHelpers').mockImplementation(() => {
|
||||
return {
|
||||
...originalNodeHelpers,
|
||||
isCustomApiCallSelected: vi.fn(() => isCustomApiCallSelected),
|
||||
displayParameter: displayParameterSpy,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
let nodeTypesStore: MockedStore<typeof useNodeTypesStore>;
|
||||
|
||||
const mockParameter: INodeProperties = {
|
||||
name: 'foo',
|
||||
type: 'string',
|
||||
displayName: 'Foo',
|
||||
displayOptions: {},
|
||||
default: '',
|
||||
};
|
||||
|
||||
const mockNodeType: INodeTypeDescription = {
|
||||
version: 1,
|
||||
name: 'testNode',
|
||||
displayName: 'Test Node',
|
||||
description: 'A test node',
|
||||
group: ['input'],
|
||||
defaults: {
|
||||
name: 'Test Node',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: [],
|
||||
properties: [mockParameter],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(createTestingPinia());
|
||||
|
||||
nodeTypesStore = mockedStore(useNodeTypesStore);
|
||||
nodeTypesStore.getNodeType = vi.fn().mockReturnValueOnce(mockNodeType);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('returns false for hidden parameter type', () => {
|
||||
mockNodeHelpers();
|
||||
|
||||
const { shouldDisplayNodeParameter } = useNodeSettingsParameters();
|
||||
|
||||
const result = shouldDisplayNodeParameter({}, null, { ...mockParameter, type: 'hidden' });
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for custom API call with mustHideDuringCustomApiCall', () => {
|
||||
vi.spyOn(nodeSettingsUtils, 'mustHideDuringCustomApiCall').mockReturnValueOnce(true);
|
||||
mockNodeHelpers({ isCustomApiCallSelected: true });
|
||||
|
||||
const { shouldDisplayNodeParameter } = useNodeSettingsParameters();
|
||||
|
||||
const result = shouldDisplayNodeParameter({}, null, mockParameter);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false if parameter is auth-related', () => {
|
||||
vi.spyOn(nodeTypesUtils, 'isAuthRelatedParameter').mockReturnValueOnce(true);
|
||||
vi.spyOn(nodeTypesUtils, 'getMainAuthField').mockReturnValueOnce(mockParameter);
|
||||
mockNodeHelpers();
|
||||
|
||||
const { shouldDisplayNodeParameter } = useNodeSettingsParameters();
|
||||
|
||||
const result = shouldDisplayNodeParameter({}, null, mockParameter);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true if displayOptions is undefined', () => {
|
||||
vi.spyOn(nodeTypesUtils, 'isAuthRelatedParameter').mockReturnValueOnce(false);
|
||||
mockNodeHelpers();
|
||||
|
||||
const { shouldDisplayNodeParameter } = useNodeSettingsParameters();
|
||||
|
||||
const result = shouldDisplayNodeParameter({}, null, {
|
||||
...mockParameter,
|
||||
displayOptions: undefined,
|
||||
});
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('calls displayParameter with correct arguments', () => {
|
||||
vi.spyOn(nodeTypesUtils, 'isAuthRelatedParameter').mockReturnValueOnce(false);
|
||||
mockNodeHelpers();
|
||||
displayParameterSpy.mockReturnValueOnce(false);
|
||||
|
||||
const { shouldDisplayNodeParameter } = useNodeSettingsParameters();
|
||||
|
||||
const parameter: INodeProperties = {
|
||||
name: 'foo',
|
||||
type: 'string',
|
||||
displayName: 'Foo',
|
||||
disabledOptions: {},
|
||||
default: '',
|
||||
};
|
||||
const nodeParameters = { foo: 'bar' };
|
||||
const node: INodeUi = {
|
||||
id: '1',
|
||||
name: 'Node1',
|
||||
position: [0, 0],
|
||||
typeVersion: 1,
|
||||
type: 'n8n-nodes-base.set',
|
||||
parameters: nodeParameters,
|
||||
};
|
||||
|
||||
const result = shouldDisplayNodeParameter(
|
||||
nodeParameters,
|
||||
node,
|
||||
parameter,
|
||||
'',
|
||||
'disabledOptions',
|
||||
);
|
||||
|
||||
expect(displayParameterSpy).toHaveBeenCalledWith(
|
||||
nodeParameters,
|
||||
parameter,
|
||||
'',
|
||||
node,
|
||||
'disabledOptions',
|
||||
);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('calls displayParameter with default displayOptions', () => {
|
||||
vi.spyOn(nodeTypesUtils, 'isAuthRelatedParameter').mockReturnValueOnce(false);
|
||||
mockNodeHelpers();
|
||||
displayParameterSpy.mockReturnValueOnce(true);
|
||||
|
||||
const { shouldDisplayNodeParameter } = useNodeSettingsParameters();
|
||||
|
||||
const nodeParameters = { foo: 'bar' };
|
||||
const node: INodeUi = {
|
||||
id: '1',
|
||||
name: 'Node1',
|
||||
position: [0, 0],
|
||||
typeVersion: 1,
|
||||
type: 'n8n-nodes-base.set',
|
||||
parameters: nodeParameters,
|
||||
};
|
||||
|
||||
const result = shouldDisplayNodeParameter(nodeParameters, node, mockParameter);
|
||||
|
||||
expect(displayParameterSpy).toHaveBeenCalledWith(
|
||||
nodeParameters,
|
||||
mockParameter,
|
||||
'',
|
||||
node,
|
||||
'displayOptions',
|
||||
);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('resolves expressions and calls displayParameter with resolved parameters', () => {
|
||||
vi.spyOn(nodeTypesUtils, 'isAuthRelatedParameter').mockReturnValueOnce(false);
|
||||
mockNodeHelpers();
|
||||
displayParameterSpy.mockReturnValueOnce(true);
|
||||
const originalWorkflowHelpers = workflowHelpers.useWorkflowHelpers();
|
||||
vi.spyOn(workflowHelpers, 'useWorkflowHelpers').mockImplementation(() => ({
|
||||
...originalWorkflowHelpers,
|
||||
resolveExpression: (expr: string) => (expr === '=1+1' ? 2 : expr),
|
||||
}));
|
||||
|
||||
const { shouldDisplayNodeParameter } = useNodeSettingsParameters();
|
||||
|
||||
const nodeParameters = { foo: '=1+1' };
|
||||
const node: INodeUi = {
|
||||
id: '1',
|
||||
name: 'Node1',
|
||||
position: [0, 0],
|
||||
typeVersion: 1,
|
||||
type: 'n8n-nodes-base.set',
|
||||
parameters: nodeParameters,
|
||||
};
|
||||
|
||||
const result = shouldDisplayNodeParameter(nodeParameters, node, mockParameter);
|
||||
|
||||
expect(displayParameterSpy).toHaveBeenCalledWith(
|
||||
{ foo: 2 },
|
||||
mockParameter,
|
||||
'',
|
||||
node,
|
||||
'displayOptions',
|
||||
);
|
||||
|
||||
expect(displayParameterSpy).toHaveBeenCalled();
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ 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, KEEP_AUTH_IN_NDV_FOR_NODES } from '@/constants';
|
||||
import { KEEP_AUTH_IN_NDV_FOR_NODES } from '@/constants';
|
||||
import { omitKey } from '@/utils/objectUtils';
|
||||
import {
|
||||
getMainAuthField,
|
||||
@@ -322,7 +322,7 @@ export function useNodeSettingsParameters() {
|
||||
value,
|
||||
nodeParams,
|
||||
) as NodeParameterValue;
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// If expression is invalid ignore
|
||||
nodeParams[key] = '';
|
||||
}
|
||||
@@ -352,10 +352,6 @@ export function useNodeSettingsParameters() {
|
||||
return nodeHelpers.displayParameter(nodeParameters, parameter, path, node, displayKey);
|
||||
}
|
||||
|
||||
function shouldSkipParamValidation(value: string | number | boolean | null) {
|
||||
return typeof value === 'string' && value.includes(CUSTOM_API_CALL_KEY);
|
||||
}
|
||||
|
||||
return {
|
||||
nodeValues,
|
||||
setValue,
|
||||
@@ -363,6 +359,5 @@ export function useNodeSettingsParameters() {
|
||||
updateParameterByPath,
|
||||
updateNodeParameter,
|
||||
handleFocus,
|
||||
shouldSkipParamValidation,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ export const useFocusPanelStore = defineStore(STORES.FOCUS_PANEL, () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const _setOptions = ({
|
||||
function _setOptions({
|
||||
parameters,
|
||||
isActive,
|
||||
wid = workflowsStore.workflowId,
|
||||
@@ -80,7 +80,7 @@ export const useFocusPanelStore = defineStore(STORES.FOCUS_PANEL, () => {
|
||||
parameters?: FocusedNodeParameter[];
|
||||
wid?: string;
|
||||
removeEmpty?: boolean;
|
||||
}) => {
|
||||
}) {
|
||||
const focusPanelDataCurrent = focusPanelData.value;
|
||||
|
||||
if (removeEmpty && PLACEHOLDER_EMPTY_WORKFLOW_ID in focusPanelDataCurrent) {
|
||||
@@ -94,10 +94,10 @@ export const useFocusPanelStore = defineStore(STORES.FOCUS_PANEL, () => {
|
||||
parameters: parameters ?? _focusedNodeParameters.value,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// When a new workflow is saved, we should update the focus panel data with the new workflow ID
|
||||
const onNewWorkflowSave = (wid: string) => {
|
||||
function onNewWorkflowSave(wid: string) {
|
||||
if (!currentFocusPanelData.value || !(PLACEHOLDER_EMPTY_WORKFLOW_ID in focusPanelData.value)) {
|
||||
return;
|
||||
}
|
||||
@@ -109,29 +109,29 @@ export const useFocusPanelStore = defineStore(STORES.FOCUS_PANEL, () => {
|
||||
isActive: latestWorkflowData.isActive,
|
||||
removeEmpty: true,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const openWithFocusedNodeParameter = (nodeParameter: FocusedNodeParameter) => {
|
||||
function openWithFocusedNodeParameter(nodeParameter: FocusedNodeParameter) {
|
||||
const parameters = [nodeParameter];
|
||||
// TODO: uncomment when tabs are implemented
|
||||
// ...focusedNodeParameters.value.filter((p) => p.parameterPath !== nodeParameter.parameterPath),
|
||||
|
||||
_setOptions({ parameters, isActive: true });
|
||||
};
|
||||
}
|
||||
|
||||
const closeFocusPanel = () => {
|
||||
function closeFocusPanel() {
|
||||
_setOptions({ isActive: false });
|
||||
};
|
||||
}
|
||||
|
||||
const toggleFocusPanel = () => {
|
||||
function toggleFocusPanel() {
|
||||
_setOptions({ isActive: !focusPanelActive.value });
|
||||
};
|
||||
}
|
||||
|
||||
const isRichParameter = (
|
||||
function isRichParameter(
|
||||
p: RichFocusedNodeParameter | FocusedNodeParameter,
|
||||
): p is RichFocusedNodeParameter => {
|
||||
): p is RichFocusedNodeParameter {
|
||||
return 'value' in p && 'node' in p;
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
focusPanelActive,
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
isResourceLocatorValue,
|
||||
} from 'n8n-workflow';
|
||||
import type { INodeUi, IUpdateInformation } from '@/Interface';
|
||||
import { SWITCH_NODE_TYPE } from '@/constants';
|
||||
import { CUSTOM_API_CALL_KEY, SWITCH_NODE_TYPE } from '@/constants';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import get from 'lodash/get';
|
||||
import set from 'lodash/set';
|
||||
@@ -166,7 +166,12 @@ export function removeMismatchedOptionValues(
|
||||
nodeType.properties.forEach((prop) => {
|
||||
const displayOptions = prop.displayOptions;
|
||||
// Not processing parameters that are not set or don't have options
|
||||
if (!nodeParameterValues?.hasOwnProperty(prop.name) || !displayOptions || !prop.options) {
|
||||
if (
|
||||
!nodeParameterValues ||
|
||||
!Object.prototype.hasOwnProperty.call(nodeParameterValues, prop.name) ||
|
||||
!displayOptions ||
|
||||
!prop.options
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// Only process the parameters that depend on the updated parameter
|
||||
@@ -353,3 +358,7 @@ export function parseFromExpression(
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function shouldSkipParamValidation(value: string | number | boolean | null) {
|
||||
return typeof value === 'string' && value.includes(CUSTOM_API_CALL_KEY);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user