diff --git a/packages/editor-ui/src/components/Error/NodeErrorView.vue b/packages/editor-ui/src/components/Error/NodeErrorView.vue
index 2aff0307af5d7..7114323acf5c6 100644
--- a/packages/editor-ui/src/components/Error/NodeErrorView.vue
+++ b/packages/editor-ui/src/components/Error/NodeErrorView.vue
@@ -216,17 +216,13 @@ function getErrorDescription(): string {
function addItemIndexSuffix(message: string): string {
let itemIndexSuffix = '';
- const ITEM_INDEX_SUFFIX_TEXT = '[item ';
-
- if (
- hasManyInputItems.value &&
- !message.includes(ITEM_INDEX_SUFFIX_TEXT) &&
- props.error?.context?.itemIndex !== undefined
- ) {
- itemIndexSuffix = ` [item ${props.error.context.itemIndex}]`;
+ if (hasManyInputItems.value && props.error?.context?.itemIndex !== undefined) {
+ itemIndexSuffix = `item ${props.error.context.itemIndex}`;
}
- return message + itemIndexSuffix;
+ if (message.includes(itemIndexSuffix)) return message;
+
+ return `${message} [${itemIndexSuffix}]`;
}
function getErrorMessage(): string {
diff --git a/packages/nodes-base/nodes/Filter/Filter.node.ts b/packages/nodes-base/nodes/Filter/Filter.node.ts
index 69a46d4a4f807..7eb36ca4f0565 100644
--- a/packages/nodes-base/nodes/Filter/Filter.node.ts
+++ b/packages/nodes-base/nodes/Filter/Filter.node.ts
@@ -13,11 +13,13 @@ export class Filter extends VersionedNodeType {
iconColor: 'light-blue',
group: ['transform'],
description: 'Remove items matching a condition',
+ defaultVersion: 2.1,
};
const nodeVersions: IVersionedNodeType['nodeVersions'] = {
1: new FilterV1(baseDescription),
2: new FilterV2(baseDescription),
+ 2.1: new FilterV2(baseDescription),
};
super(nodeVersions, baseDescription);
diff --git a/packages/nodes-base/nodes/Filter/V2/FilterV2.node.ts b/packages/nodes-base/nodes/Filter/V2/FilterV2.node.ts
index 16e95ef634d7c..06df074cd539b 100644
--- a/packages/nodes-base/nodes/Filter/V2/FilterV2.node.ts
+++ b/packages/nodes-base/nodes/Filter/V2/FilterV2.node.ts
@@ -9,6 +9,8 @@ import {
type INodeTypeDescription,
} from 'n8n-workflow';
import { ENABLE_LESS_STRICT_TYPE_VALIDATION } from '../../../utils/constants';
+import { getTypeValidationParameter, getTypeValidationStrictness } from '../../If/V2/utils';
+import { looseTypeValidationProperty } from '../../../utils/descriptions';
export class FilterV2 implements INodeType {
description: INodeTypeDescription;
@@ -16,7 +18,7 @@ export class FilterV2 implements INodeType {
constructor(baseDescription: INodeTypeBaseDescription) {
this.description = {
...baseDescription,
- version: 2,
+ version: [2, 2.1],
defaults: {
name: 'Filter',
color: '#229eff',
@@ -35,7 +37,16 @@ export class FilterV2 implements INodeType {
typeOptions: {
filter: {
caseSensitive: '={{!$parameter.options.ignoreCase}}',
- typeValidation: '={{$parameter.options.looseTypeValidation ? "loose" : "strict"}}',
+ typeValidation: getTypeValidationStrictness(2.1),
+ },
+ },
+ },
+ {
+ ...looseTypeValidationProperty,
+ default: false,
+ displayOptions: {
+ show: {
+ '@version': [{ _cnd: { gte: 2.1 } }],
},
},
},
@@ -54,11 +65,12 @@ export class FilterV2 implements INodeType {
default: true,
},
{
- displayName: 'Less Strict Type Validation',
- description: 'Whether to try casting value types based on the selected operator',
- name: 'looseTypeValidation',
- type: 'boolean',
- default: true,
+ ...looseTypeValidationProperty,
+ displayOptions: {
+ show: {
+ '@version': [{ _cnd: { lt: 2.1 } }],
+ },
+ },
},
],
},
@@ -82,7 +94,10 @@ export class FilterV2 implements INodeType {
extractValue: true,
}) as boolean;
} catch (error) {
- if (!options.looseTypeValidation && !error.description) {
+ if (
+ !getTypeValidationParameter(2.1)(this, itemIndex, options.looseTypeValidation) &&
+ !error.description
+ ) {
set(error, 'description', ENABLE_LESS_STRICT_TYPE_VALIDATION);
}
set(error, 'context.itemIndex', itemIndex);
diff --git a/packages/nodes-base/nodes/If/If.node.ts b/packages/nodes-base/nodes/If/If.node.ts
index 7bc1d459ca8e4..4942fdebf278a 100644
--- a/packages/nodes-base/nodes/If/If.node.ts
+++ b/packages/nodes-base/nodes/If/If.node.ts
@@ -13,12 +13,13 @@ export class If extends VersionedNodeType {
iconColor: 'green',
group: ['transform'],
description: 'Route items to different branches (true/false)',
- defaultVersion: 2,
+ defaultVersion: 2.1,
};
const nodeVersions: IVersionedNodeType['nodeVersions'] = {
1: new IfV1(baseDescription),
2: new IfV2(baseDescription),
+ 2.1: new IfV2(baseDescription),
};
super(nodeVersions, baseDescription);
diff --git a/packages/nodes-base/nodes/If/V2/IfV2.node.ts b/packages/nodes-base/nodes/If/V2/IfV2.node.ts
index 31fb258e76ebe..691fc2e0b9016 100644
--- a/packages/nodes-base/nodes/If/V2/IfV2.node.ts
+++ b/packages/nodes-base/nodes/If/V2/IfV2.node.ts
@@ -9,6 +9,8 @@ import {
type INodeTypeDescription,
} from 'n8n-workflow';
import { ENABLE_LESS_STRICT_TYPE_VALIDATION } from '../../../utils/constants';
+import { looseTypeValidationProperty } from '../../../utils/descriptions';
+import { getTypeValidationParameter, getTypeValidationStrictness } from './utils';
export class IfV2 implements INodeType {
description: INodeTypeDescription;
@@ -16,7 +18,7 @@ export class IfV2 implements INodeType {
constructor(baseDescription: INodeTypeBaseDescription) {
this.description = {
...baseDescription,
- version: 2,
+ version: [2, 2.1],
defaults: {
name: 'If',
color: '#408000',
@@ -35,7 +37,16 @@ export class IfV2 implements INodeType {
typeOptions: {
filter: {
caseSensitive: '={{!$parameter.options.ignoreCase}}',
- typeValidation: '={{$parameter.options.looseTypeValidation ? "loose" : "strict"}}',
+ typeValidation: getTypeValidationStrictness(2.1),
+ },
+ },
+ },
+ {
+ ...looseTypeValidationProperty,
+ default: false,
+ displayOptions: {
+ show: {
+ '@version': [{ _cnd: { gte: 2.1 } }],
},
},
},
@@ -54,11 +65,12 @@ export class IfV2 implements INodeType {
default: true,
},
{
- displayName: 'Less Strict Type Validation',
- description: 'Whether to try casting value types based on the selected operator',
- name: 'looseTypeValidation',
- type: 'boolean',
- default: true,
+ ...looseTypeValidationProperty,
+ displayOptions: {
+ show: {
+ '@version': [{ _cnd: { lt: 2.1 } }],
+ },
+ },
},
],
},
@@ -82,7 +94,10 @@ export class IfV2 implements INodeType {
extractValue: true,
}) as boolean;
} catch (error) {
- if (!options.looseTypeValidation && !error.description) {
+ if (
+ !getTypeValidationParameter(2.1)(this, itemIndex, options.looseTypeValidation) &&
+ !error.description
+ ) {
set(error, 'description', ENABLE_LESS_STRICT_TYPE_VALIDATION);
}
set(error, 'context.itemIndex', itemIndex);
diff --git a/packages/nodes-base/nodes/If/V2/utils.ts b/packages/nodes-base/nodes/If/V2/utils.ts
new file mode 100644
index 0000000000000..21af3724218c6
--- /dev/null
+++ b/packages/nodes-base/nodes/If/V2/utils.ts
@@ -0,0 +1,15 @@
+import type { IExecuteFunctions } from 'n8n-workflow';
+
+export const getTypeValidationStrictness = (version: number) => {
+ return `={{ ($nodeVersion < ${version} ? $parameter.options.looseTypeValidation : $parameter.looseTypeValidation) ? "loose" : "strict" }}`;
+};
+
+export const getTypeValidationParameter = (version: number) => {
+ return (context: IExecuteFunctions, itemIndex: number, option: boolean | undefined) => {
+ if (context.getNode().typeVersion < version) {
+ return option;
+ } else {
+ return context.getNodeParameter('looseTypeValidation', itemIndex, false) as boolean;
+ }
+ };
+};
diff --git a/packages/nodes-base/nodes/Switch/Switch.node.ts b/packages/nodes-base/nodes/Switch/Switch.node.ts
index c54f5b55da624..fb7262f742fa7 100644
--- a/packages/nodes-base/nodes/Switch/Switch.node.ts
+++ b/packages/nodes-base/nodes/Switch/Switch.node.ts
@@ -14,13 +14,14 @@ export class Switch extends VersionedNodeType {
iconColor: 'light-blue',
group: ['transform'],
description: 'Route items depending on defined expression or rules',
- defaultVersion: 3,
+ defaultVersion: 3.1,
};
const nodeVersions: IVersionedNodeType['nodeVersions'] = {
1: new SwitchV1(baseDescription),
2: new SwitchV2(baseDescription),
3: new SwitchV3(baseDescription),
+ 3.1: new SwitchV3(baseDescription),
};
super(nodeVersions, baseDescription);
diff --git a/packages/nodes-base/nodes/Switch/V3/SwitchV3.node.ts b/packages/nodes-base/nodes/Switch/V3/SwitchV3.node.ts
index 2c54fb860efe3..c4ac82c990948 100644
--- a/packages/nodes-base/nodes/Switch/V3/SwitchV3.node.ts
+++ b/packages/nodes-base/nodes/Switch/V3/SwitchV3.node.ts
@@ -13,6 +13,8 @@ import { ApplicationError, NodeConnectionType, NodeOperationError } from 'n8n-wo
import set from 'lodash/set';
import { capitalize } from '@utils/utilities';
import { ENABLE_LESS_STRICT_TYPE_VALIDATION } from '../../../utils/constants';
+import { looseTypeValidationProperty } from '../../../utils/descriptions';
+import { getTypeValidationParameter, getTypeValidationStrictness } from '../../If/V2/utils';
const configuredOutputs = (parameters: INodeParameters) => {
const mode = parameters.mode as string;
@@ -48,7 +50,7 @@ export class SwitchV3 implements INodeType {
this.description = {
...baseDescription,
subtitle: `=mode: {{(${capitalize})($parameter["mode"])}}`,
- version: [3],
+ version: [3, 3.1],
defaults: {
name: 'Switch',
color: '#506000',
@@ -157,8 +159,7 @@ export class SwitchV3 implements INodeType {
multipleValues: false,
filter: {
caseSensitive: '={{!$parameter.options.ignoreCase}}',
- typeValidation:
- '={{$parameter.options.looseTypeValidation ? "loose" : "strict"}}',
+ typeValidation: getTypeValidationStrictness(3.1),
},
},
},
@@ -184,6 +185,15 @@ export class SwitchV3 implements INodeType {
},
],
},
+ {
+ ...looseTypeValidationProperty,
+ default: false,
+ displayOptions: {
+ show: {
+ '@version': [{ _cnd: { gte: 3.1 } }],
+ },
+ },
+ },
{
displayName: 'Options',
name: 'options',
@@ -218,11 +228,12 @@ export class SwitchV3 implements INodeType {
default: true,
},
{
- displayName: 'Less Strict Type Validation',
- description: 'Whether to try casting value types based on the selected operator',
- name: 'looseTypeValidation',
- type: 'boolean',
- default: true,
+ ...looseTypeValidationProperty,
+ displayOptions: {
+ show: {
+ '@version': [{ _cnd: { lt: 3.1 } }],
+ },
+ },
},
{
displayName: 'Rename Fallback Output',
@@ -349,7 +360,14 @@ export class SwitchV3 implements INodeType {
},
) as boolean;
} catch (error) {
- if (!options.looseTypeValidation && !error.description) {
+ if (
+ !getTypeValidationParameter(3.1)(
+ this,
+ itemIndex,
+ options.looseTypeValidation as boolean,
+ ) &&
+ !error.description
+ ) {
error.description = ENABLE_LESS_STRICT_TYPE_VALIDATION;
}
set(error, 'context.itemIndex', itemIndex);
diff --git a/packages/nodes-base/utils/descriptions.ts b/packages/nodes-base/utils/descriptions.ts
index 1465d3e31476c..fb6772a12bebf 100644
--- a/packages/nodes-base/utils/descriptions.ts
+++ b/packages/nodes-base/utils/descriptions.ts
@@ -33,6 +33,14 @@ export const returnAllOrLimit: INodeProperties[] = [
},
];
+export const looseTypeValidationProperty: INodeProperties = {
+ displayName: 'Less Strict Type Validation',
+ description: 'Whether to try casting value types based on the selected operator',
+ name: 'looseTypeValidation',
+ type: 'boolean',
+ default: true,
+};
+
export const encodeDecodeOptions: INodePropertyOptions[] = [
{
name: 'armscii8',
diff --git a/packages/workflow/src/NodeParameters/FilterParameter.ts b/packages/workflow/src/NodeParameters/FilterParameter.ts
index 1d8c1a9a25832..2c2fabf96ba4f 100644
--- a/packages/workflow/src/NodeParameters/FilterParameter.ts
+++ b/packages/workflow/src/NodeParameters/FilterParameter.ts
@@ -94,14 +94,61 @@ function parseFilterConditionValues(
)} ${suffix}`;
};
- const invalidTypeDescription = 'Try changing the type of comparison.';
+ const getTypeDescription = (isStrict: boolean) => {
+ if (isStrict)
+ return 'Try changing the type of the comparison, or enabling less strict type validation.';
+ return 'Try changing the type of the comparison.';
+ };
+
+ const composeInvalidTypeDescription = (
+ type: string,
+ fromType: string,
+ valuePosition: 'first' | 'second',
+ ) => {
+ fromType = fromType.toLocaleLowerCase();
+ const expectedType = withIndefiniteArticle(type);
+
+ let convertionFunction = '';
+ if (type === 'string') {
+ convertionFunction = '.toString()';
+ } else if (type === 'number') {
+ convertionFunction = '.toNumber()';
+ } else if (type === 'boolean') {
+ convertionFunction = '.toBoolean()';
+ }
+
+ if (strict && convertionFunction) {
+ const suggestFunction = ` by adding ${convertionFunction}
`;
+ return `
+
Try either:
+