mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
fix(AWS Bedrock Chat Model Node): Do not show issues for arbitrary model names (#17079)
This commit is contained in:
@@ -60,6 +60,7 @@ export class LmChatAwsBedrock implements INodeType {
|
|||||||
displayName: 'Model',
|
displayName: 'Model',
|
||||||
name: 'model',
|
name: 'model',
|
||||||
type: 'options',
|
type: 'options',
|
||||||
|
allowArbitraryValues: true, // Hide issues when model name is specified in the expression and does not match any of the options
|
||||||
description:
|
description:
|
||||||
'The model which will generate the completion. <a href="https://docs.aws.amazon.com/bedrock/latest/userguide/foundation-models.html">Learn more</a>.',
|
'The model which will generate the completion. <a href="https://docs.aws.amazon.com/bedrock/latest/userguide/foundation-models.html">Learn more</a>.',
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
|
|||||||
@@ -417,17 +417,19 @@ const getIssues = computed<string[]>(() => {
|
|||||||
['options', 'multiOptions'].includes(props.parameter.type) &&
|
['options', 'multiOptions'].includes(props.parameter.type) &&
|
||||||
!remoteParameterOptionsLoading.value &&
|
!remoteParameterOptionsLoading.value &&
|
||||||
remoteParameterOptionsLoadingIssues.value === null &&
|
remoteParameterOptionsLoadingIssues.value === null &&
|
||||||
parameterOptions.value
|
parameterOptions.value &&
|
||||||
|
(!isModelValueExpression.value || props.expressionEvaluated !== null)
|
||||||
) {
|
) {
|
||||||
// Check if the value resolves to a valid option
|
// Check if the value resolves to a valid option.
|
||||||
// Currently it only displays an error in the node itself in
|
// For expressions do not validate if there is no evaluated value.
|
||||||
|
// Currently, it only displays an error in the node itself in
|
||||||
// case the value is not valid. The workflow can still be executed
|
// case the value is not valid. The workflow can still be executed
|
||||||
// and the error is not displayed on the node in the workflow
|
// and the error is not displayed on the node in the workflow
|
||||||
const validOptions = parameterOptions.value.map((options) => options.value);
|
const validOptions = parameterOptions.value.map((options) => options.value);
|
||||||
|
|
||||||
let checkValues: string[] = [];
|
let checkValues: string[] = [];
|
||||||
|
|
||||||
if (!shouldSkipParamValidation(displayValue.value)) {
|
if (!shouldSkipParamValidation(props.parameter, displayValue.value)) {
|
||||||
if (Array.isArray(displayValue.value)) {
|
if (Array.isArray(displayValue.value)) {
|
||||||
checkValues = checkValues.concat(displayValue.value);
|
checkValues = checkValues.concat(displayValue.value);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ import { mockedStore } from '@/__tests__/utils';
|
|||||||
import type { INodeUi } from '@/Interface';
|
import type { INodeUi } from '@/Interface';
|
||||||
|
|
||||||
describe('useNodeSettingsParameters', () => {
|
describe('useNodeSettingsParameters', () => {
|
||||||
describe('setValue', () => {
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
setActivePinia(createTestingPinia());
|
setActivePinia(createTestingPinia());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('setValue', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type {
|
|||||||
IDataObject,
|
IDataObject,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
INodePropertyOptions,
|
INodePropertyOptions,
|
||||||
|
INodeProperties,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import {
|
import {
|
||||||
updateDynamicConnections,
|
updateDynamicConnections,
|
||||||
@@ -13,8 +14,9 @@ import {
|
|||||||
nameIsParameter,
|
nameIsParameter,
|
||||||
formatAsExpression,
|
formatAsExpression,
|
||||||
parseFromExpression,
|
parseFromExpression,
|
||||||
|
shouldSkipParamValidation,
|
||||||
} from './nodeSettingsUtils';
|
} from './nodeSettingsUtils';
|
||||||
import { SWITCH_NODE_TYPE } from '@/constants';
|
import { CUSTOM_API_CALL_KEY, SWITCH_NODE_TYPE } from '@/constants';
|
||||||
import type { INodeUi, IUpdateInformation } from '@/Interface';
|
import type { INodeUi, IUpdateInformation } from '@/Interface';
|
||||||
|
|
||||||
describe('updateDynamicConnections', () => {
|
describe('updateDynamicConnections', () => {
|
||||||
@@ -396,3 +398,158 @@ describe('parseFromExpression', () => {
|
|||||||
expect(parseFromExpression({}, undefined, 'json', null, [])).toBeNull();
|
expect(parseFromExpression({}, undefined, 'json', null, [])).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('shouldSkipParamValidation', () => {
|
||||||
|
describe('CUSTOM_API_CALL_KEY detection', () => {
|
||||||
|
it('should skip validation when value is CUSTOM_API_CALL_KEY', () => {
|
||||||
|
const parameter: INodeProperties = {
|
||||||
|
name: 'testParam',
|
||||||
|
displayName: 'Test Parameter',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = shouldSkipParamValidation(parameter, CUSTOM_API_CALL_KEY);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip validation when value is a string containing CUSTOM_API_CALL_KEY', () => {
|
||||||
|
const parameter: INodeProperties = {
|
||||||
|
name: 'testParam',
|
||||||
|
displayName: 'Test Parameter',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const valueWithKey = `some prefix ${CUSTOM_API_CALL_KEY} some suffix`;
|
||||||
|
const result = shouldSkipParamValidation(parameter, valueWithKey);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not skip validation when value is a string not containing CUSTOM_API_CALL_KEY', () => {
|
||||||
|
const parameter: INodeProperties = {
|
||||||
|
name: 'testParam',
|
||||||
|
displayName: 'Test Parameter',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = shouldSkipParamValidation(parameter, 'regular string value');
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('options parameter type with allowArbitraryValues', () => {
|
||||||
|
it('should skip validation for options parameter with allowArbitraryValues=true', () => {
|
||||||
|
const parameter: INodeProperties = {
|
||||||
|
name: 'optionsParam',
|
||||||
|
displayName: 'Options Parameter',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{ name: 'Option 1', value: 'option1' },
|
||||||
|
{ name: 'Option 2', value: 'option2' },
|
||||||
|
],
|
||||||
|
allowArbitraryValues: true,
|
||||||
|
default: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = shouldSkipParamValidation(parameter, 'arbitrary_value');
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not skip validation for options parameter with allowArbitraryValues=false', () => {
|
||||||
|
const parameter: INodeProperties = {
|
||||||
|
name: 'optionsParam',
|
||||||
|
displayName: 'Options Parameter',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{ name: 'Option 1', value: 'option1' },
|
||||||
|
{ name: 'Option 2', value: 'option2' },
|
||||||
|
],
|
||||||
|
allowArbitraryValues: false,
|
||||||
|
default: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = shouldSkipParamValidation(parameter, 'arbitrary_value');
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not skip validation for options parameter with allowArbitraryValues=undefined', () => {
|
||||||
|
const parameter: INodeProperties = {
|
||||||
|
name: 'optionsParam',
|
||||||
|
displayName: 'Options Parameter',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{ name: 'Option 1', value: 'option1' },
|
||||||
|
{ name: 'Option 2', value: 'option2' },
|
||||||
|
],
|
||||||
|
default: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = shouldSkipParamValidation(parameter, 'arbitrary_value');
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('multiOptions parameter type with allowArbitraryValues', () => {
|
||||||
|
it('should skip validation for multiOptions parameter with allowArbitraryValues=true', () => {
|
||||||
|
const parameter: INodeProperties = {
|
||||||
|
name: 'multiOptionsParam',
|
||||||
|
displayName: 'Multi Options Parameter',
|
||||||
|
type: 'multiOptions',
|
||||||
|
options: [
|
||||||
|
{ name: 'Option 1', value: 'option1' },
|
||||||
|
{ name: 'Option 2', value: 'option2' },
|
||||||
|
],
|
||||||
|
allowArbitraryValues: true,
|
||||||
|
default: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = shouldSkipParamValidation(parameter, ['arbitrary_value']);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not skip validation for multiOptions parameter with allowArbitraryValues=false', () => {
|
||||||
|
const parameter: INodeProperties = {
|
||||||
|
name: 'multiOptionsParam',
|
||||||
|
displayName: 'Multi Options Parameter',
|
||||||
|
type: 'multiOptions',
|
||||||
|
options: [
|
||||||
|
{ name: 'Option 1', value: 'option1' },
|
||||||
|
{ name: 'Option 2', value: 'option2' },
|
||||||
|
],
|
||||||
|
allowArbitraryValues: false,
|
||||||
|
default: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = shouldSkipParamValidation(parameter, ['arbitrary_value']);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('non-options parameter types', () => {
|
||||||
|
const nonOptionsParameterTypes = [
|
||||||
|
'string',
|
||||||
|
'number',
|
||||||
|
'boolean',
|
||||||
|
'json',
|
||||||
|
'dateTime',
|
||||||
|
'color',
|
||||||
|
] as Array<INodeProperties['type']>;
|
||||||
|
|
||||||
|
nonOptionsParameterTypes.forEach((type) => {
|
||||||
|
it(`should not skip validation for ${type} parameter type regardless of allowArbitraryValues`, () => {
|
||||||
|
const parameter: INodeProperties = {
|
||||||
|
name: 'testParam',
|
||||||
|
displayName: 'Test Parameter',
|
||||||
|
type,
|
||||||
|
allowArbitraryValues: true,
|
||||||
|
default: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = shouldSkipParamValidation(parameter, 'test_value');
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -359,6 +359,13 @@ export function parseFromExpression(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shouldSkipParamValidation(value: string | number | boolean | null) {
|
export function shouldSkipParamValidation(
|
||||||
return typeof value === 'string' && value.includes(CUSTOM_API_CALL_KEY);
|
parameter: INodeProperties,
|
||||||
|
value: NodeParameterValueType,
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
(typeof value === 'string' && value.includes(CUSTOM_API_CALL_KEY)) ||
|
||||||
|
(['options', 'multiOptions'].includes(parameter.type) &&
|
||||||
|
Boolean(parameter.allowArbitraryValues))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1466,6 +1466,8 @@ export interface INodeProperties {
|
|||||||
// allows to skip validation during execution or set custom validation/casting logic inside node
|
// allows to skip validation during execution or set custom validation/casting logic inside node
|
||||||
// inline error messages would still be shown in UI
|
// inline error messages would still be shown in UI
|
||||||
ignoreValidationDuringExecution?: boolean;
|
ignoreValidationDuringExecution?: boolean;
|
||||||
|
// for type: options | multiOptions – skip validation of the value (e.g. when value is not in the list and specified via expression)
|
||||||
|
allowArbitraryValues?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INodePropertyModeTypeOptions {
|
export interface INodePropertyModeTypeOptions {
|
||||||
|
|||||||
Reference in New Issue
Block a user