mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
feat: Better error when calling expression function on input that is undefined or null (#10009)
This commit is contained in:
@@ -6,6 +6,7 @@ import type {
|
|||||||
INodeTypeBaseDescription,
|
INodeTypeBaseDescription,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
import { ENABLE_LESS_STRICT_TYPE_VALIDATION } from '../../../utils/constants';
|
||||||
|
|
||||||
export class FilterV2 implements INodeType {
|
export class FilterV2 implements INodeType {
|
||||||
description: INodeTypeDescription;
|
description: INodeTypeDescription;
|
||||||
@@ -79,12 +80,8 @@ export class FilterV2 implements INodeType {
|
|||||||
extractValue: true,
|
extractValue: true,
|
||||||
}) as boolean;
|
}) as boolean;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!options.looseTypeValidation) {
|
if (!options.looseTypeValidation && !error.description) {
|
||||||
set(
|
set(error, 'description', ENABLE_LESS_STRICT_TYPE_VALIDATION);
|
||||||
error,
|
|
||||||
'description',
|
|
||||||
"Try changing the type of comparison. Alternatively you can enable 'Less Strict Type Validation' in the options.",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
set(error, 'context.itemIndex', itemIndex);
|
set(error, 'context.itemIndex', itemIndex);
|
||||||
set(error, 'node', this.getNode());
|
set(error, 'node', this.getNode());
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type {
|
|||||||
INodeTypeBaseDescription,
|
INodeTypeBaseDescription,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
import { ENABLE_LESS_STRICT_TYPE_VALIDATION } from '../../../utils/constants';
|
||||||
|
|
||||||
export class IfV2 implements INodeType {
|
export class IfV2 implements INodeType {
|
||||||
description: INodeTypeDescription;
|
description: INodeTypeDescription;
|
||||||
@@ -79,12 +80,8 @@ export class IfV2 implements INodeType {
|
|||||||
extractValue: true,
|
extractValue: true,
|
||||||
}) as boolean;
|
}) as boolean;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!options.looseTypeValidation) {
|
if (!options.looseTypeValidation && !error.description) {
|
||||||
set(
|
set(error, 'description', ENABLE_LESS_STRICT_TYPE_VALIDATION);
|
||||||
error,
|
|
||||||
'description',
|
|
||||||
"Try changing the type of comparison. Alternatively you can enable 'Less Strict Type Validation' in the options.",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
set(error, 'context.itemIndex', itemIndex);
|
set(error, 'context.itemIndex', itemIndex);
|
||||||
set(error, 'node', this.getNode());
|
set(error, 'node', this.getNode());
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import type {
|
|||||||
import { NodeConnectionType, NodeOperationError } from 'n8n-workflow';
|
import { NodeConnectionType, NodeOperationError } from 'n8n-workflow';
|
||||||
import set from 'lodash/set';
|
import set from 'lodash/set';
|
||||||
import { capitalize } from '@utils/utilities';
|
import { capitalize } from '@utils/utilities';
|
||||||
|
import { ENABLE_LESS_STRICT_TYPE_VALIDATION } from '../../../utils/constants';
|
||||||
|
|
||||||
const configuredOutputs = (parameters: INodeParameters) => {
|
const configuredOutputs = (parameters: INodeParameters) => {
|
||||||
const mode = parameters.mode as string;
|
const mode = parameters.mode as string;
|
||||||
@@ -348,9 +349,8 @@ export class SwitchV3 implements INodeType {
|
|||||||
},
|
},
|
||||||
) as boolean;
|
) as boolean;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!options.looseTypeValidation) {
|
if (!options.looseTypeValidation && !error.description) {
|
||||||
error.description =
|
error.description = ENABLE_LESS_STRICT_TYPE_VALIDATION;
|
||||||
"Try changing the type of comparison. Alternatively you can enable 'Less Strict Type Validation' in the options.";
|
|
||||||
}
|
}
|
||||||
set(error, 'context.itemIndex', itemIndex);
|
set(error, 'context.itemIndex', itemIndex);
|
||||||
set(error, 'node', this.getNode());
|
set(error, 'node', this.getNode());
|
||||||
|
|||||||
@@ -2,3 +2,6 @@ export const NODE_RAN_MULTIPLE_TIMES_WARNING =
|
|||||||
"This node ran multiple times - once for each input item. You can change this by setting 'execute once' in the node settings. <a href='https://docs.n8n.io/flow-logic/looping/#executing-nodes-once' target='_blank'>More Info</a>";
|
"This node ran multiple times - once for each input item. You can change this by setting 'execute once' in the node settings. <a href='https://docs.n8n.io/flow-logic/looping/#executing-nodes-once' target='_blank'>More Info</a>";
|
||||||
|
|
||||||
export const LOCALHOST = '127.0.0.1';
|
export const LOCALHOST = '127.0.0.1';
|
||||||
|
|
||||||
|
export const ENABLE_LESS_STRICT_TYPE_VALIDATION =
|
||||||
|
"Try changing the type of comparison. Alternatively you can enable 'Less Strict Type Validation' in the options.";
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import type { ExpressionChunk, ExpressionCode } from './ExpressionParser';
|
|||||||
import { joinExpression, splitExpression } from './ExpressionParser';
|
import { joinExpression, splitExpression } from './ExpressionParser';
|
||||||
import { booleanExtensions } from './BooleanExtensions';
|
import { booleanExtensions } from './BooleanExtensions';
|
||||||
import type { ExtensionMap } from './Extensions';
|
import type { ExtensionMap } from './Extensions';
|
||||||
|
import { checkIfValueDefinedOrThrow } from './utils';
|
||||||
|
|
||||||
const EXPRESSION_EXTENDER = 'extend';
|
const EXPRESSION_EXTENDER = 'extend';
|
||||||
const EXPRESSION_EXTENDER_OPTIONAL = 'extendOptional';
|
const EXPRESSION_EXTENDER_OPTIONAL = 'extendOptional';
|
||||||
@@ -514,6 +515,7 @@ export function extend(input: unknown, functionName: string, args: unknown[]) {
|
|||||||
// any types have a function with that name. Then throw an error
|
// any types have a function with that name. Then throw an error
|
||||||
// letting the user know the available types.
|
// letting the user know the available types.
|
||||||
if (!foundFunction) {
|
if (!foundFunction) {
|
||||||
|
checkIfValueDefinedOrThrow(input, functionName);
|
||||||
const haveFunction = EXTENSION_OBJECTS.filter((v) => functionName in v.functions);
|
const haveFunction = EXTENSION_OBJECTS.filter((v) => functionName in v.functions);
|
||||||
if (!haveFunction.length) {
|
if (!haveFunction.length) {
|
||||||
// This shouldn't really be possible but we should cover it anyway
|
// This shouldn't really be possible but we should cover it anyway
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
import { ExpressionExtensionError } from '../errors/expression-extension.error';
|
||||||
|
|
||||||
// Utility functions and type guards for expression extensions
|
// Utility functions and type guards for expression extensions
|
||||||
|
|
||||||
@@ -17,3 +18,15 @@ export const convertToDateTime = (value: string | Date | DateTime): DateTime | u
|
|||||||
}
|
}
|
||||||
return converted;
|
return converted;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function checkIfValueDefinedOrThrow<T>(value: T, functionName: string): void {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
throw new ExpressionExtensionError(
|
||||||
|
`${functionName}() could not be called on "${String(value)}" type`,
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
'You are trying to access a field that does not exist, modify your expression or set a default value',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
|
|
||||||
/* eslint-disable n8n-local-rules/no-interpolation-in-regular-string */
|
/* eslint-disable n8n-local-rules/no-interpolation-in-regular-string */
|
||||||
|
|
||||||
import { extendTransform } from '@/Extensions';
|
import { extendTransform, extend } from '@/Extensions';
|
||||||
import { joinExpression, splitExpression } from '@/Extensions/ExpressionParser';
|
import { joinExpression, splitExpression } from '@/Extensions/ExpressionParser';
|
||||||
import { evaluate } from './Helpers';
|
import { evaluate } from './Helpers';
|
||||||
|
import { ExpressionExtensionError } from '../../src/errors/expression-extension.error';
|
||||||
|
|
||||||
describe('Expression Extension Transforms', () => {
|
describe('Expression Extension Transforms', () => {
|
||||||
describe('extend() transform', () => {
|
describe('extend() transform', () => {
|
||||||
@@ -242,4 +243,31 @@ describe('tmpl Expression Parser', () => {
|
|||||||
expect(evaluate('={{ $ifEmpty({a: 1}, "default") }}')).toEqual({ a: 1 });
|
expect(evaluate('={{ $ifEmpty({a: 1}, "default") }}')).toEqual({ a: 1 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Test extend with undefined', () => {
|
||||||
|
test('input is undefined', () => {
|
||||||
|
try {
|
||||||
|
extend(undefined, 'toDateTime', []);
|
||||||
|
} catch (error) {
|
||||||
|
expect(error).toBeInstanceOf(ExpressionExtensionError);
|
||||||
|
expect(error).toHaveProperty(
|
||||||
|
'message',
|
||||||
|
'toDateTime() could not be called on "undefined" type',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
test('input is null', () => {
|
||||||
|
try {
|
||||||
|
extend(null, 'startsWith', []);
|
||||||
|
} catch (error) {
|
||||||
|
expect(error).toBeInstanceOf(ExpressionExtensionError);
|
||||||
|
expect(error).toHaveProperty('message', 'startsWith() could not be called on "null" type');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
test('input should be converted to upper case', () => {
|
||||||
|
const result = extend('TEST', 'toUpperCase', []);
|
||||||
|
|
||||||
|
expect(result).toEqual('TEST');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user