feat(core): Add batching and other options to declarative nodes (#8885)

Co-authored-by: Michael Kret <michael.k@radency.com>
This commit is contained in:
Jan Oberhauser
2024-06-06 22:39:31 -07:00
committed by GitHub
parent e520f8a98f
commit 4e568631be
4 changed files with 514 additions and 78 deletions

View File

@@ -19,7 +19,10 @@ import type {
import { RoutingNode } from '@/RoutingNode';
import { Workflow } from '@/Workflow';
import * as utilsModule from '@/utils';
import * as Helpers from './Helpers';
import { applyDeclarativeNodeOptionParameters } from '@/NodeHelpers';
import { mock } from 'jest-mock-extended';
const postReceiveFunction1 = async function (
@@ -42,6 +45,23 @@ const preSendFunction1 = async function (
describe('RoutingNode', () => {
const additionalData = mock<IWorkflowExecuteAdditionalData>();
test('applyDeclarativeNodeOptionParameters', () => {
const nodeTypes = Helpers.NodeTypes();
const nodeType = nodeTypes.getByNameAndVersion('test.setMulti');
applyDeclarativeNodeOptionParameters(nodeType);
const options = nodeType.description.properties.find(
(property) => property.name === 'requestOptions',
);
expect(options?.options).toBeDefined;
const optionNames = options!.options!.map((option) => option.name);
expect(optionNames).toEqual(['batching', 'allowUnauthorizedCerts', 'proxy', 'timeout']);
});
describe('getRequestOptionsFromParameters', () => {
const tests: Array<{
description: string;
@@ -717,6 +737,11 @@ describe('RoutingNode', () => {
const tests: Array<{
description: string;
input: {
specialTestOptions?: {
applyDeclarativeNodeOptionParameters?: boolean;
numberOfItems?: number;
sleepCalls?: number[][];
};
nodeType: {
properties?: INodeProperties[];
credentials?: INodeCredentialDescription[];
@@ -772,6 +797,7 @@ describe('RoutingNode', () => {
},
baseURL: 'http://127.0.0.1:5678',
returnFullResponse: true,
timeout: 300000,
},
},
},
@@ -821,6 +847,7 @@ describe('RoutingNode', () => {
},
baseURL: 'http://127.0.0.1:5678',
returnFullResponse: true,
timeout: 300000,
},
},
},
@@ -876,6 +903,7 @@ describe('RoutingNode', () => {
},
baseURL: 'http://127.0.0.1:5678',
returnFullResponse: true,
timeout: 300000,
},
},
},
@@ -931,6 +959,7 @@ describe('RoutingNode', () => {
},
baseURL: 'http://127.0.0.1:5678',
returnFullResponse: true,
timeout: 300000,
},
},
},
@@ -988,6 +1017,154 @@ describe('RoutingNode', () => {
offset: 10,
},
returnFullResponse: true,
timeout: 300000,
},
},
},
],
],
},
{
description: 'multiple parameters, from applyDeclarativeNodeOptionParameters',
input: {
specialTestOptions: {
applyDeclarativeNodeOptionParameters: true,
numberOfItems: 5,
sleepCalls: [[500], [500]],
},
node: {
parameters: {
requestOptions: {
allowUnauthorizedCerts: true,
batching: {
batch: {
batchSize: 2,
batchInterval: 500,
},
},
proxy: 'http://user:password@127.0.0.1:8080',
timeout: 123,
},
},
},
nodeType: {
properties: [],
},
},
output: [
[
{
json: {
headers: {},
statusCode: 200,
requestOptions: {
qs: {},
headers: {},
proxy: {
auth: {
username: 'user',
password: 'password',
},
host: '127.0.0.1',
protocol: 'http',
port: 8080,
},
body: {},
returnFullResponse: true,
skipSslCertificateValidation: true,
timeout: 123,
},
},
},
{
json: {
headers: {},
statusCode: 200,
requestOptions: {
qs: {},
headers: {},
proxy: {
auth: {
username: 'user',
password: 'password',
},
host: '127.0.0.1',
protocol: 'http',
port: 8080,
},
body: {},
returnFullResponse: true,
skipSslCertificateValidation: true,
timeout: 123,
},
},
},
{
json: {
headers: {},
statusCode: 200,
requestOptions: {
qs: {},
headers: {},
proxy: {
auth: {
username: 'user',
password: 'password',
},
host: '127.0.0.1',
protocol: 'http',
port: 8080,
},
body: {},
returnFullResponse: true,
skipSslCertificateValidation: true,
timeout: 123,
},
},
},
{
json: {
headers: {},
statusCode: 200,
requestOptions: {
qs: {},
headers: {},
proxy: {
auth: {
username: 'user',
password: 'password',
},
host: '127.0.0.1',
protocol: 'http',
port: 8080,
},
body: {},
returnFullResponse: true,
skipSslCertificateValidation: true,
timeout: 123,
},
},
},
{
json: {
headers: {},
statusCode: 200,
requestOptions: {
qs: {},
headers: {},
proxy: {
auth: {
username: 'user',
password: 'password',
},
host: '127.0.0.1',
protocol: 'http',
port: 8080,
},
body: {},
returnFullResponse: true,
skipSslCertificateValidation: true,
timeout: 123,
},
},
},
@@ -1424,6 +1601,7 @@ describe('RoutingNode', () => {
addedIn: 'preSendFunction1',
},
returnFullResponse: true,
timeout: 300000,
},
},
],
@@ -1519,6 +1697,7 @@ describe('RoutingNode', () => {
baseURL: 'http://127.0.0.1:5678',
url: '/test-url',
returnFullResponse: true,
timeout: 300000,
},
},
},
@@ -1698,15 +1877,12 @@ describe('RoutingNode', () => {
const connectionInputData: INodeExecutionData[] = [];
const runExecutionData: IRunExecutionData = { resultData: { runData: {} } };
const nodeType = nodeTypes.getByNameAndVersion(baseNode.type);
applyDeclarativeNodeOptionParameters(nodeType);
const propertiesOriginal = nodeType.description.properties;
const inputData: ITaskDataConnections = {
main: [
[
{
json: {},
},
],
],
main: [[]],
};
for (const testData of tests) {
@@ -1719,6 +1895,9 @@ describe('RoutingNode', () => {
};
nodeType.description = { ...testData.input.nodeType } as INodeTypeDescription;
if (testData.input.specialTestOptions?.applyDeclarativeNodeOptionParameters) {
nodeType.description.properties = propertiesOriginal;
}
const workflow = new Workflow({
nodes: workflowData.nodes,
@@ -1742,18 +1921,46 @@ describe('RoutingNode', () => {
source: null,
} as IExecuteData;
const executeFunctions = mock<IExecuteFunctions>();
const executeSingleFunctions = Helpers.getExecuteSingleFunctions(
workflow,
runExecutionData,
runIndex,
node,
itemIndex,
);
const nodeExecuteFunctions: Partial<INodeExecuteFunctions> = {
getExecuteFunctions: () => mock<IExecuteFunctions>(),
getExecuteSingleFunctions: () =>
Helpers.getExecuteSingleFunctions(
workflow,
runExecutionData,
runIndex,
node,
itemIndex,
),
getExecuteFunctions: () => executeFunctions,
getExecuteSingleFunctions: () => executeSingleFunctions,
};
const numberOfItems = testData.input.specialTestOptions?.numberOfItems ?? 1;
if (!inputData.main[0] || inputData.main[0].length !== numberOfItems) {
inputData.main[0] = [];
for (let i = 0; i < numberOfItems; i++) {
inputData.main[0].push({ json: {} });
}
}
const spy = jest.spyOn(utilsModule, 'sleep').mockReturnValue(
new Promise((resolve) => {
resolve();
}),
);
spy.mockClear();
executeFunctions.getNodeParameter.mockImplementation(
(parameterName: string) => testData.input.node.parameters[parameterName] || {},
);
const getNodeParameter = executeSingleFunctions.getNodeParameter;
executeSingleFunctions.getNodeParameter = (parameterName: string) =>
parameterName in testData.input.node.parameters
? testData.input.node.parameters[parameterName]
: getNodeParameter(parameterName) ?? {};
const result = await routingNode.runNode(
inputData,
runIndex,
@@ -1762,6 +1969,12 @@ describe('RoutingNode', () => {
nodeExecuteFunctions as INodeExecuteFunctions,
);
if (testData.input.specialTestOptions?.sleepCalls) {
expect(spy.mock.calls).toEqual(testData.input.specialTestOptions?.sleepCalls);
} else {
expect(spy).toHaveBeenCalledTimes(0);
}
expect(result).toEqual(testData.output);
});
}
@@ -1820,6 +2033,7 @@ describe('RoutingNode', () => {
},
baseURL: 'http://127.0.0.1:5678',
returnFullResponse: true,
timeout: 300000,
},
},
},