mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat: Option to skip validation in getNodeParameter (#14726)
This commit is contained in:
@@ -18,6 +18,7 @@ import { ApplicationError, ExpressionError, NodeConnectionTypes } from 'n8n-work
|
|||||||
|
|
||||||
import { describeCommonTests } from './shared-tests';
|
import { describeCommonTests } from './shared-tests';
|
||||||
import { ExecuteContext } from '../execute-context';
|
import { ExecuteContext } from '../execute-context';
|
||||||
|
import * as validateUtil from '../utils/validate-value-against-schema';
|
||||||
|
|
||||||
describe('ExecuteContext', () => {
|
describe('ExecuteContext', () => {
|
||||||
const testCredentialType = 'testCredential';
|
const testCredentialType = 'testCredential';
|
||||||
@@ -177,6 +178,18 @@ describe('ExecuteContext', () => {
|
|||||||
const parameter = executeContext.getNodeParameter('testParameter', 0);
|
const parameter = executeContext.getNodeParameter('testParameter', 0);
|
||||||
expect(parameter).toEqual([{ name: undefined, value: undefined }]);
|
expect(parameter).toEqual([{ name: undefined, value: undefined }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not validate parameter if skipValidation in options', () => {
|
||||||
|
const validateSpy = jest.spyOn(validateUtil, 'validateValueAgainstSchema');
|
||||||
|
|
||||||
|
executeContext.getNodeParameter('testParameter', 0, '', {
|
||||||
|
skipValidation: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(validateSpy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
validateSpy.mockRestore();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getCredentials', () => {
|
describe('getCredentials', () => {
|
||||||
|
|||||||
@@ -406,6 +406,8 @@ export abstract class NodeExecutionContext implements Omit<FunctionsBase, 'getCr
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options?.skipValidation) return returnData;
|
||||||
|
|
||||||
// Validate parameter value if it has a schema defined(RMC) or validateType defined
|
// Validate parameter value if it has a schema defined(RMC) or validateType defined
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
returnData = validateValueAgainstSchema(
|
returnData = validateValueAgainstSchema(
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
import type { MockProxy } from 'jest-mock-extended';
|
||||||
|
import { mock } from 'jest-mock-extended';
|
||||||
|
import type { IExecuteFunctions } from 'n8n-workflow';
|
||||||
|
|
||||||
import * as update from '../../../../v2/actions/record/update.operation';
|
import * as update from '../../../../v2/actions/record/update.operation';
|
||||||
import * as transport from '../../../../v2/transport';
|
import * as transport from '../../../../v2/transport';
|
||||||
import { createMockExecuteFunction } from '../helpers';
|
import { createMockExecuteFunction } from '../helpers';
|
||||||
@@ -38,6 +42,44 @@ jest.mock('../../../../v2/transport', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Test AirtableV2, update operation', () => {
|
describe('Test AirtableV2, update operation', () => {
|
||||||
|
let mockExecuteFunctions: MockProxy<IExecuteFunctions>;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip validation if typecast option is true', async () => {
|
||||||
|
mockExecuteFunctions = mock<IExecuteFunctions>();
|
||||||
|
mockExecuteFunctions.helpers.constructExecutionMetaData = jest.fn(() => []);
|
||||||
|
mockExecuteFunctions.getNodeParameter.mockImplementation((key: string) => {
|
||||||
|
if (key === 'columns.mappingMode') {
|
||||||
|
return 'defineBelow';
|
||||||
|
}
|
||||||
|
if (key === 'columns.matchingColumns') {
|
||||||
|
return ['id'];
|
||||||
|
}
|
||||||
|
if (key === 'options') {
|
||||||
|
return {
|
||||||
|
typecast: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (key === 'columns.value') {
|
||||||
|
return {
|
||||||
|
id: 'recXXX',
|
||||||
|
field1: 'foo 1',
|
||||||
|
field2: 'bar 1',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
await update.execute.call(mockExecuteFunctions, [{ json: {} }], 'base', 'table');
|
||||||
|
|
||||||
|
expect(mockExecuteFunctions.getNodeParameter).toHaveBeenCalledWith('columns.value', 0, [], {
|
||||||
|
skipValidation: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should update a record by id, autoMapInputData', async () => {
|
it('should update a record by id, autoMapInputData', async () => {
|
||||||
const nodeParameters = {
|
const nodeParameters = {
|
||||||
operation: 'update',
|
operation: 'update',
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export async function execute(
|
|||||||
try {
|
try {
|
||||||
const records: UpdateRecord[] = [];
|
const records: UpdateRecord[] = [];
|
||||||
const options = this.getNodeParameter('options', i, {});
|
const options = this.getNodeParameter('options', i, {});
|
||||||
|
const typecast = options.typecast ? true : false;
|
||||||
|
|
||||||
if (dataMode === 'autoMapInputData') {
|
if (dataMode === 'autoMapInputData') {
|
||||||
if (columnsToMatchOn.includes('id')) {
|
if (columnsToMatchOn.includes('id')) {
|
||||||
@@ -107,11 +108,22 @@ export async function execute(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dataMode === 'defineBelow') {
|
if (dataMode === 'defineBelow') {
|
||||||
|
const getNodeParameterOptions = typecast ? { skipValidation: true } : undefined;
|
||||||
if (columnsToMatchOn.includes('id')) {
|
if (columnsToMatchOn.includes('id')) {
|
||||||
const { id, ...fields } = this.getNodeParameter('columns.value', i, []) as IDataObject;
|
const { id, ...fields } = this.getNodeParameter(
|
||||||
|
'columns.value',
|
||||||
|
i,
|
||||||
|
[],
|
||||||
|
getNodeParameterOptions,
|
||||||
|
) as IDataObject;
|
||||||
records.push({ id: id as string, fields });
|
records.push({ id: id as string, fields });
|
||||||
} else {
|
} else {
|
||||||
const fields = this.getNodeParameter('columns.value', i, []) as IDataObject;
|
const fields = this.getNodeParameter(
|
||||||
|
'columns.value',
|
||||||
|
i,
|
||||||
|
[],
|
||||||
|
getNodeParameterOptions,
|
||||||
|
) as IDataObject;
|
||||||
|
|
||||||
const matches = findMatches(
|
const matches = findMatches(
|
||||||
tableData,
|
tableData,
|
||||||
@@ -127,7 +139,7 @@ export async function execute(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const body: IDataObject = { typecast: options.typecast ? true : false };
|
const body: IDataObject = { typecast };
|
||||||
|
|
||||||
const responseData = await batchUpdate.call(this, endpoint, body, records);
|
const responseData = await batchUpdate.call(this, endpoint, body, records);
|
||||||
|
|
||||||
|
|||||||
@@ -571,6 +571,8 @@ export interface IGetNodeParameterOptions {
|
|||||||
extractValue?: boolean;
|
extractValue?: boolean;
|
||||||
// get raw value of parameter with unresolved expressions
|
// get raw value of parameter with unresolved expressions
|
||||||
rawExpressions?: boolean;
|
rawExpressions?: boolean;
|
||||||
|
// skip validation of parameter
|
||||||
|
skipValidation?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ExecuteFunctions {
|
namespace ExecuteFunctions {
|
||||||
|
|||||||
Reference in New Issue
Block a user