Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into NODE-2174-add-redis…
Browse files Browse the repository at this point in the history
…-user-auth
  • Loading branch information
netroy committed Dec 19, 2024
2 parents 278cd2b + bbc2fc9 commit d069fd9
Show file tree
Hide file tree
Showing 64 changed files with 5,091 additions and 361 deletions.
35 changes: 15 additions & 20 deletions cypress/e2e/16-form-trigger-node.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ describe('n8n Form Trigger', () => {
':nth-child(3) > .border-top-dashed > .parameter-input-list-wrapper > :nth-child(1) > .parameter-item',
)
.find('input[placeholder*="e.g. What is your name?"]')
.type('Test Field 3')
.blur();
.type('Test Field 3');
cy.get(
':nth-child(3) > .border-top-dashed > .parameter-input-list-wrapper > :nth-child(2) > .parameter-item',
).click();
Expand All @@ -56,27 +55,24 @@ describe('n8n Form Trigger', () => {
':nth-child(4) > .border-top-dashed > .parameter-input-list-wrapper > :nth-child(1) > .parameter-item',
)
.find('input[placeholder*="e.g. What is your name?"]')
.type('Test Field 4')
.blur();
.type('Test Field 4');
cy.get(
':nth-child(4) > .border-top-dashed > .parameter-input-list-wrapper > :nth-child(2) > .parameter-item',
).click();
getVisibleSelect().contains('Dropdown').click();
cy.get(
'.border-top-dashed > :nth-child(2) > :nth-child(3) > .multi-parameter > .fixed-collection-parameter > :nth-child(2) > .button',
).click();
cy.get(
':nth-child(4) > :nth-child(1) > :nth-child(2) > :nth-child(3) > .multi-parameter > .fixed-collection-parameter > .fixed-collection-parameter-property > :nth-child(1) > :nth-child(1)',
)
.find('input')
.type('Option 1')
.blur();
cy.get(
':nth-child(4) > :nth-child(1) > :nth-child(2) > :nth-child(3) > .multi-parameter > .fixed-collection-parameter > .fixed-collection-parameter-property > :nth-child(1) > :nth-child(2)',
)
.find('input')
.type('Option 2')
.blur();
cy.contains('button', 'Add Field Option').click();
cy.contains('label', 'Field Options')
.parent()
.nextAll()
.find('[data-test-id="parameter-input-field"]')
.eq(0)
.type('Option 1');
cy.contains('label', 'Field Options')
.parent()
.nextAll()
.find('[data-test-id="parameter-input-field"]')
.eq(1)
.type('Option 2');

//add optional submitted message
cy.get('.param-options').click();
Expand All @@ -94,7 +90,6 @@ describe('n8n Form Trigger', () => {
.children()
.children()
.first()
.clear()
.type('Your test form was successfully submitted');

ndv.getters.backToCanvas().click();
Expand Down
20 changes: 0 additions & 20 deletions cypress/e2e/5-ndv.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,26 +65,6 @@ describe('NDV', () => {
cy.shouldNotHaveConsoleErrors();
});

it('should disconect Switch outputs if rules order was changed', () => {
cy.createFixtureWorkflow('NDV-test-switch_reorder.json', 'NDV test switch reorder');
workflowPage.actions.zoomToFit();

workflowPage.actions.executeWorkflow();
workflowPage.actions.openNode('Merge');
ndv.getters.outputPanel().contains('2 items').should('exist');
cy.contains('span', 'first').should('exist');
ndv.getters.backToCanvas().click();

workflowPage.actions.openNode('Switch');
cy.get('.cm-line').realMouseMove(100, 100);
cy.get('.fa-angle-down').first().click();
ndv.getters.backToCanvas().click();
workflowPage.actions.executeWorkflow();
workflowPage.actions.openNode('Merge');
ndv.getters.outputPanel().contains('2 items').should('exist');
cy.contains('span', 'zero').should('exist');
});

it('should show correct validation state for resource locator params', () => {
workflowPage.actions.addNodeToCanvas('Typeform', true, true);
ndv.getters.container().should('be.visible');
Expand Down
2 changes: 1 addition & 1 deletion packages/@n8n/config/src/configs/executions.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class PruningIntervalsConfig {
@Env('EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL')
hardDelete: number = 15;

/** How often (minutes) execution data should be soft-deleted */
/** How often (minutes) execution data should be soft-deleted. */
@Env('EXECUTIONS_DATA_PRUNE_SOFT_DELETE_INTERVAL')
softDelete: number = 60;
}
Expand Down
2 changes: 2 additions & 0 deletions packages/@n8n/task-runner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@
"@sentry/node": "catalog:",
"acorn": "8.14.0",
"acorn-walk": "8.3.4",
"lodash": "catalog:",
"n8n-core": "workspace:*",
"n8n-workflow": "workspace:*",
"nanoid": "catalog:",
"typedi": "catalog:",
"ws": "^8.18.0"
},
"devDependencies": {
"@types/lodash": "catalog:",
"luxon": "catalog:"
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { mock } from 'jest-mock-extended';
import { DateTime } from 'luxon';
import type { IBinaryData } from 'n8n-workflow';
import { setGlobalState, type CodeExecutionMode, type IDataObject } from 'n8n-workflow';
import fs from 'node:fs';
import { builtinModules } from 'node:module';
Expand All @@ -8,10 +9,15 @@ import type { BaseRunnerConfig } from '@/config/base-runner-config';
import type { JsRunnerConfig } from '@/config/js-runner-config';
import { MainConfig } from '@/config/main-config';
import { ExecutionError } from '@/js-task-runner/errors/execution-error';
import { UnsupportedFunctionError } from '@/js-task-runner/errors/unsupported-function.error';
import { ValidationError } from '@/js-task-runner/errors/validation-error';
import type { JSExecSettings } from '@/js-task-runner/js-task-runner';
import { JsTaskRunner } from '@/js-task-runner/js-task-runner';
import type { DataRequestResponse, InputDataChunkDefinition } from '@/runner-types';
import {
UNSUPPORTED_HELPER_FUNCTIONS,
type DataRequestResponse,
type InputDataChunkDefinition,
} from '@/runner-types';
import type { Task } from '@/task-runner';

import {
Expand Down Expand Up @@ -567,6 +573,120 @@ describe('JsTaskRunner', () => {
);
});

describe('helpers', () => {
const binaryDataFile: IBinaryData = {
data: 'data',
fileName: 'file.txt',
mimeType: 'text/plain',
};

const groups = [
{
method: 'helpers.assertBinaryData',
invocation: "helpers.assertBinaryData(0, 'binaryFile')",
expectedParams: [0, 'binaryFile'],
},
{
method: 'helpers.getBinaryDataBuffer',
invocation: "helpers.getBinaryDataBuffer(0, 'binaryFile')",
expectedParams: [0, 'binaryFile'],
},
{
method: 'helpers.prepareBinaryData',
invocation: "helpers.prepareBinaryData(Buffer.from('123'), 'file.txt', 'text/plain')",
expectedParams: [Buffer.from('123'), 'file.txt', 'text/plain'],
},
{
method: 'helpers.setBinaryDataBuffer',
invocation:
"helpers.setBinaryDataBuffer({ data: '123', mimeType: 'text/plain' }, Buffer.from('321'))",
expectedParams: [{ data: '123', mimeType: 'text/plain' }, Buffer.from('321')],
},
{
method: 'helpers.binaryToString',
invocation: "helpers.binaryToString(Buffer.from('123'), 'utf8')",
expectedParams: [Buffer.from('123'), 'utf8'],
},
{
method: 'helpers.httpRequest',
invocation: "helpers.httpRequest({ method: 'GET', url: 'http://localhost' })",
expectedParams: [{ method: 'GET', url: 'http://localhost' }],
},
];

for (const group of groups) {
it(`${group.method} for runOnceForAllItems`, async () => {
// Arrange
const rpcCallSpy = jest
.spyOn(defaultTaskRunner, 'makeRpcCall')
.mockResolvedValue(undefined);

// Act
await execTaskWithParams({
task: newTaskWithSettings({
code: `await ${group.invocation}; return []`,
nodeMode: 'runOnceForAllItems',
}),
taskData: newDataRequestResponse(
[{ json: {}, binary: { binaryFile: binaryDataFile } }],
{},
),
});

expect(rpcCallSpy).toHaveBeenCalledWith('1', group.method, group.expectedParams);
});

it(`${group.method} for runOnceForEachItem`, async () => {
// Arrange
const rpcCallSpy = jest
.spyOn(defaultTaskRunner, 'makeRpcCall')
.mockResolvedValue(undefined);

// Act
await execTaskWithParams({
task: newTaskWithSettings({
code: `await ${group.invocation}; return {}`,
nodeMode: 'runOnceForEachItem',
}),
taskData: newDataRequestResponse(
[{ json: {}, binary: { binaryFile: binaryDataFile } }],
{},
),
});

expect(rpcCallSpy).toHaveBeenCalledWith('1', group.method, group.expectedParams);
});
}

describe('unsupported methods', () => {
for (const unsupportedFunction of UNSUPPORTED_HELPER_FUNCTIONS) {
it(`should throw an error if ${unsupportedFunction} is used in runOnceForAllItems`, async () => {
// Act

await expect(
async () =>
await executeForAllItems({
code: `${unsupportedFunction}()`,
inputItems,
}),
).rejects.toThrow(UnsupportedFunctionError);
});

it(`should throw an error if ${unsupportedFunction} is used in runOnceForEachItem`, async () => {
// Act

await expect(
async () =>
await executeForEachItem({
code: `${unsupportedFunction}()`,
inputItems,
}),
).rejects.toThrow(UnsupportedFunctionError);
});
}
});
});

it('should allow access to Node.js Buffers', async () => {
const outcomeAll = await execTaskWithParams({
task: newTaskWithSettings({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ApplicationError } from 'n8n-workflow';

/**
* Error that indicates that a specific function is not available in the
* Code Node.
*/
export class UnsupportedFunctionError extends ApplicationError {
constructor(functionName: string) {
super(`The function "${functionName}" is not supported in the Code Node`, {
level: 'info',
});
}
}
38 changes: 33 additions & 5 deletions packages/@n8n/task-runner/src/js-task-runner/js-task-runner.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import set from 'lodash/set';
import { getAdditionalKeys } from 'n8n-core';
import { WorkflowDataProxy, Workflow, ObservableObject } from 'n8n-workflow';
import type {
Expand All @@ -19,11 +20,14 @@ import * as a from 'node:assert';
import { runInNewContext, type Context } from 'node:vm';

import type { MainConfig } from '@/config/main-config';
import type {
DataRequestResponse,
InputDataChunkDefinition,
PartialAdditionalData,
TaskResultData,
import { UnsupportedFunctionError } from '@/js-task-runner/errors/unsupported-function.error';
import {
EXPOSED_RPC_METHODS,
UNSUPPORTED_HELPER_FUNCTIONS,
type DataRequestResponse,
type InputDataChunkDefinition,
type PartialAdditionalData,
type TaskResultData,
} from '@/runner-types';
import { type Task, TaskRunner } from '@/task-runner';

Expand All @@ -38,6 +42,10 @@ import { createRequireResolver } from './require-resolver';
import { validateRunForAllItemsOutput, validateRunForEachItemOutput } from './result-validation';
import { DataRequestResponseReconstruct } from '../data-request/data-request-response-reconstruct';

export interface RPCCallObject {
[name: string]: ((...args: unknown[]) => Promise<unknown>) | RPCCallObject;
}

export interface JSExecSettings {
code: string;
nodeMode: CodeExecutionMode;
Expand Down Expand Up @@ -439,4 +447,24 @@ export class JsTaskRunner extends TaskRunner {
this.nodeTypes.addNodeTypeDescriptions(nodeTypes);
}
}

private buildRpcCallObject(taskId: string) {
const rpcObject: RPCCallObject = {};

for (const rpcMethod of EXPOSED_RPC_METHODS) {
set(
rpcObject,
rpcMethod.split('.'),
async (...args: unknown[]) => await this.makeRpcCall(taskId, rpcMethod, args),
);
}

for (const rpcMethod of UNSUPPORTED_HELPER_FUNCTIONS) {
set(rpcObject, rpcMethod.split('.'), () => {
throw new UnsupportedFunctionError(rpcMethod);
});
}

return rpcObject;
}
}
6 changes: 3 additions & 3 deletions packages/@n8n/task-runner/src/message-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { INodeTypeBaseDescription } from 'n8n-workflow';

import type {
NeededNodeType,
RPC_ALLOW_LIST,
AVAILABLE_RPC_METHODS,
TaskDataRequestParams,
TaskResultData,
} from './runner-types';
Expand Down Expand Up @@ -105,7 +105,7 @@ export namespace BrokerMessage {
type: 'broker:rpc';
callId: string;
taskId: string;
name: (typeof RPC_ALLOW_LIST)[number];
name: (typeof AVAILABLE_RPC_METHODS)[number];
params: unknown[];
}

Expand Down Expand Up @@ -239,7 +239,7 @@ export namespace RunnerMessage {
type: 'runner:rpc';
callId: string;
taskId: string;
name: (typeof RPC_ALLOW_LIST)[number];
name: (typeof AVAILABLE_RPC_METHODS)[number];
params: unknown[];
}

Expand Down
Loading

0 comments on commit d069fd9

Please sign in to comment.