fix(core): Do not enable strict type validation by default for resource mapper (#13037)

This commit is contained in:
Elias Meire
2025-02-04 12:31:59 +01:00
committed by GitHub
parent 3a908aca17
commit fdcff9082b
3 changed files with 144 additions and 54 deletions

View File

@@ -247,65 +247,155 @@ describe('validateValueAgainstSchema', () => {
expect(typeof result).toEqual('number'); expect(typeof result).toEqual('number');
}); });
describe('when the mode is in Fixed mode, and the node is a resource mapper', () => { describe('when validating a resource mapper value', () => {
const nodeType = { describe('when attemptToConvertTypes === true', () => {
description: { const nodeType = {
properties: [ description: {
{ properties: [
name: 'operation', {
type: 'resourceMapper', name: 'operation',
typeOptions: { type: 'resourceMapper',
resourceMapper: { typeOptions: {
mode: 'add', resourceMapper: {
mode: 'add',
},
}, },
}, },
},
],
},
} as unknown as INodeType;
const node = {
parameters: {
operation: {
schema: [
{ id: 'num', type: 'number', required: true },
{ id: 'str', type: 'string', required: true },
{ id: 'obj', type: 'object', required: true },
{ id: 'arr', type: 'array', required: true },
], ],
attemptToConvertTypes: true,
mappingMode: '',
value: '',
}, },
}, } as unknown as INodeType;
} as unknown as INode;
const parameterName = 'operation.value'; const node = {
parameters: {
operation: {
schema: [
{ id: 'num', type: 'number', required: true },
{ id: 'str', type: 'string', required: true },
{ id: 'obj', type: 'object', required: true },
{ id: 'arr', type: 'array', required: true },
],
attemptToConvertTypes: true,
mappingMode: '',
value: '',
},
},
} as unknown as INode;
describe('should correctly validate values for', () => { const parameterName = 'operation.value';
test.each([
{ num: 0 }, describe('should correctly validate values for', () => {
{ num: 23 }, test.each([
{ num: -0 }, { num: 0 },
{ num: -Infinity }, { num: 23 },
{ num: Infinity }, { num: -0 },
{ str: '' }, { num: -Infinity },
{ str: ' ' }, { num: Infinity },
{ str: 'hello' }, { str: '' },
{ arr: [] }, { str: ' ' },
{ obj: {} }, { str: 'hello' },
])('%s', (value) => { { arr: [] },
expect(() => { obj: {} },
validateValueAgainstSchema(node, nodeType, value, parameterName, 0, 0), ])('%s', (value) => {
).not.toThrow(); expect(() =>
validateValueAgainstSchema(node, nodeType, value, parameterName, 0, 0),
).not.toThrow();
});
});
describe('should throw an error for', () => {
test.each([{ num: NaN }, { num: undefined }, { num: null }])('%s', (value) => {
expect(() =>
validateValueAgainstSchema(node, nodeType, value, parameterName, 0, 0),
).toThrow();
});
}); });
}); });
describe('should throw an error for', () => { describe('when attemptToConvertTypes is not set (=default)', () => {
test.each([{ num: NaN }, { num: undefined }, { num: null }])('%s', (value) => { test('should correctly validate and convert types', () => {
expect(() => const nodeType = {
validateValueAgainstSchema(node, nodeType, value, parameterName, 0, 0), description: {
).toThrow(); properties: [
{
displayName: 'Columns',
name: 'columns',
type: 'resourceMapper',
noDataExpression: true,
default: {
mappingMode: 'defineBelow',
value: null,
},
required: true,
typeOptions: {
loadOptionsDependsOn: ['table.value', 'operation'],
resourceMapper: {
resourceMapperMethod: 'getMappingColumns',
mode: 'upsert',
fieldWords: {
singular: 'column',
plural: 'columns',
},
addAllFields: true,
multiKeyMatch: true,
},
},
},
],
},
} as unknown as INodeType;
const node: INode = {
parameters: {
columns: {
mappingMode: 'defineBelow',
value: {
id: 2,
count: '={{ $json.count }}',
},
matchingColumns: ['id'],
schema: [
{
id: 'id',
displayName: 'id',
required: false,
defaultMatch: true,
display: true,
type: 'number',
canBeUsedToMatch: true,
},
{
id: 'count',
displayName: 'count',
required: false,
defaultMatch: false,
display: true,
type: 'number',
canBeUsedToMatch: false,
},
],
},
options: {},
},
id: '8d6cec63-8db1-440c-8966-4d6311ee69a9',
name: 'add products to DB',
type: 'n8n-nodes-base.postgres',
typeVersion: 2.3,
position: [420, 0],
};
const value = {
id: 2,
count: '23',
};
const parameterName = 'columns.value';
const result = validateValueAgainstSchema(node, nodeType, value, parameterName, 0, 0);
expect(result).toEqual({
id: 2,
count: 23,
});
}); });
}); });
}); });

View File

@@ -56,8 +56,8 @@ const validateResourceMapperValue = (
if (schemaEntry?.type) { if (schemaEntry?.type) {
const validationResult = validateFieldType(key, resolvedValue, schemaEntry.type, { const validationResult = validateFieldType(key, resolvedValue, schemaEntry.type, {
valueOptions: schemaEntry.options, valueOptions: schemaEntry.options,
strict: !resourceMapperField.attemptToConvertTypes, strict: resourceMapperField.attemptToConvertTypes === false,
parseStrings: !!resourceMapperField.convertFieldsToString, parseStrings: Boolean(resourceMapperField.convertFieldsToString),
}); });
if (!validationResult.valid) { if (!validationResult.valid) {

View File

@@ -2722,8 +2722,8 @@ export type ResourceMapperValue = {
value: { [key: string]: string | number | boolean | null } | null; value: { [key: string]: string | number | boolean | null } | null;
matchingColumns: string[]; matchingColumns: string[];
schema: ResourceMapperField[]; schema: ResourceMapperField[];
attemptToConvertTypes: boolean; attemptToConvertTypes?: boolean;
convertFieldsToString: boolean; convertFieldsToString?: boolean;
}; };
export type FilterOperatorType = export type FilterOperatorType =