diff --git a/packages/cli/test/integration/publicApi/credentials.test.ts b/packages/cli/test/integration/publicApi/credentials.test.ts
index aa611d0d78..bc5c414142 100644
--- a/packages/cli/test/integration/publicApi/credentials.test.ts
+++ b/packages/cli/test/integration/publicApi/credentials.test.ts
@@ -252,19 +252,17 @@ describe('GET /credentials/schema/:credentialType', () => {
});
test('should retrieve credential type', async () => {
- const response = await authOwnerAgent.get('/credentials/schema/githubApi');
+ const response = await authOwnerAgent.get('/credentials/schema/ftp');
const { additionalProperties, type, properties, required } = response.body;
expect(additionalProperties).toBe(false);
expect(type).toBe('object');
- expect(properties.server).toBeDefined();
- expect(properties.server.type).toBe('string');
- expect(properties.user.type).toBeDefined();
- expect(properties.user.type).toBe('string');
- expect(properties.accessToken.type).toBeDefined();
- expect(properties.accessToken.type).toBe('string');
- expect(required).toEqual(expect.arrayContaining(['server', 'user', 'accessToken']));
+ expect(properties.host.type).toBe('string');
+ expect(properties.port.type).toBe('number');
+ expect(properties.username.type).toBe('string');
+ expect(properties.password.type).toBe('string');
+ expect(required).toEqual(expect.arrayContaining(['host', 'port']));
expect(response.statusCode).toBe(200);
});
});
@@ -301,9 +299,9 @@ const INVALID_PAYLOADS = [
},
{
name: randomName(),
- type: 'githubApi',
+ type: 'ftp',
data: {
- server: randomName(),
+ username: randomName(),
},
},
{},
diff --git a/packages/cli/test/integration/shared/utils/index.ts b/packages/cli/test/integration/shared/utils/index.ts
index 6033422db2..660b4ada5c 100644
--- a/packages/cli/test/integration/shared/utils/index.ts
+++ b/packages/cli/test/integration/shared/utils/index.ts
@@ -1,20 +1,13 @@
import { Container } from 'typedi';
import { randomBytes } from 'crypto';
import { existsSync } from 'fs';
-import { CronJob } from 'cron';
-import set from 'lodash/set';
import { BinaryDataManager, UserSettings } from 'n8n-core';
-import type {
- ICredentialType,
- IExecuteFunctions,
- INode,
- INodeExecutionData,
- INodeParameters,
- ITriggerFunctions,
- TriggerTime,
-} from 'n8n-workflow';
-import { deepCopy } from 'n8n-workflow';
-import { NodeHelpers, toCronExpression } from 'n8n-workflow';
+import type { INode } from 'n8n-workflow';
+import { GithubApi } from 'n8n-nodes-base/credentials/GithubApi.credentials';
+import { Ftp } from 'n8n-nodes-base/credentials/Ftp.credentials';
+import { Cron } from 'n8n-nodes-base/nodes/Cron/Cron.node';
+import { Set } from 'n8n-nodes-base/nodes/Set/Set.node';
+import { Start } from 'n8n-nodes-base/nodes/Start/Start.node';
import type request from 'supertest';
import { v4 as uuid } from 'uuid';
@@ -42,45 +35,17 @@ export async function initActiveWorkflowRunner(): Promise
return workflowRunner;
}
-export function gitHubCredentialType(): ICredentialType {
- return {
- name: 'githubApi',
- displayName: 'Github API',
- documentationUrl: 'github',
- properties: [
- {
- displayName: 'Github Server',
- name: 'server',
- type: 'string',
- default: 'https://api.github.com',
- required: true,
- description: 'The server to connect to. Only has to be set if Github Enterprise is used.',
- },
- {
- displayName: 'User',
- name: 'user',
- type: 'string',
- required: true,
- default: '',
- },
- {
- displayName: 'Access Token',
- name: 'accessToken',
- type: 'string',
- required: true,
- default: '',
- },
- ],
- };
-}
-
/**
* Initialize node types.
*/
export async function initCredentialsTypes(): Promise {
Container.get(LoadNodesAndCredentials).loaded.credentials = {
githubApi: {
- type: gitHubCredentialType(),
+ type: new GithubApi(),
+ sourcePath: '',
+ },
+ ftp: {
+ type: new Ftp(),
sourcePath: '',
},
};
@@ -92,294 +57,16 @@ export async function initCredentialsTypes(): Promise {
export async function initNodeTypes() {
Container.get(LoadNodesAndCredentials).loaded.nodes = {
'n8n-nodes-base.start': {
+ type: new Start(),
sourcePath: '',
- type: {
- description: {
- displayName: 'Start',
- name: 'start',
- group: ['input'],
- version: 1,
- description: 'Starts the workflow execution from this node',
- defaults: {
- name: 'Start',
- color: '#553399',
- },
- inputs: [],
- outputs: ['main'],
- properties: [],
- },
- async execute(this: IExecuteFunctions) {
- return [this.getInputData()];
- },
- },
},
'n8n-nodes-base.cron': {
+ type: new Cron(),
sourcePath: '',
- type: {
- description: {
- displayName: 'Cron',
- name: 'cron',
- icon: 'fa:calendar',
- group: ['trigger', 'schedule'],
- version: 1,
- description: 'Triggers the workflow at a specific time',
- eventTriggerDescription: '',
- activationMessage:
- 'Your cron trigger will now trigger executions on the schedule you have defined.',
- defaults: {
- name: 'Cron',
- color: '#00FF00',
- },
- inputs: [],
- outputs: ['main'],
- properties: [
- {
- displayName: 'Trigger Times',
- name: 'triggerTimes',
- type: 'fixedCollection',
- typeOptions: {
- multipleValues: true,
- multipleValueButtonText: 'Add Time',
- },
- default: {},
- description: 'Triggers for the workflow',
- placeholder: 'Add Cron Time',
- options: NodeHelpers.cronNodeOptions,
- },
- ],
- },
- async trigger(this: ITriggerFunctions) {
- const triggerTimes = this.getNodeParameter('triggerTimes') as unknown as {
- item: TriggerTime[];
- };
-
- // Get all the trigger times
- const cronTimes = (triggerTimes.item || []).map(toCronExpression);
-
- // The trigger function to execute when the cron-time got reached
- // or when manually triggered
- const executeTrigger = () => {
- this.emit([this.helpers.returnJsonArray([{}])]);
- };
-
- const timezone = this.getTimezone();
-
- // Start the cron-jobs
- const cronJobs = cronTimes.map(
- (cronTime) => new CronJob(cronTime, executeTrigger, undefined, true, timezone),
- );
-
- // Stop the cron-jobs
- async function closeFunction() {
- for (const cronJob of cronJobs) {
- cronJob.stop();
- }
- }
-
- async function manualTriggerFunction() {
- executeTrigger();
- }
-
- return {
- closeFunction,
- manualTriggerFunction,
- };
- },
- },
},
'n8n-nodes-base.set': {
+ type: new Set(),
sourcePath: '',
- type: {
- description: {
- displayName: 'Set',
- name: 'set',
- icon: 'fa:pen',
- group: ['input'],
- version: 1,
- description: 'Sets values on items and optionally remove other values',
- defaults: {
- name: 'Set',
- color: '#0000FF',
- },
- inputs: ['main'],
- outputs: ['main'],
- properties: [
- {
- displayName: 'Keep Only Set',
- name: 'keepOnlySet',
- type: 'boolean',
- default: false,
- description:
- 'If only the values set on this node should be kept and all others removed.',
- },
- {
- displayName: 'Values to Set',
- name: 'values',
- placeholder: 'Add Value',
- type: 'fixedCollection',
- typeOptions: {
- multipleValues: true,
- sortable: true,
- },
- description: 'The value to set.',
- default: {},
- options: [
- {
- name: 'boolean',
- displayName: 'Boolean',
- values: [
- {
- displayName: 'Name',
- name: 'name',
- type: 'string',
- default: 'propertyName',
- description:
- 'Name of the property to write data to. Supports dot-notation. Example: "data.person[0].name"',
- },
- {
- displayName: 'Value',
- name: 'value',
- type: 'boolean',
- default: false,
- description: 'The boolean value to write in the property.',
- },
- ],
- },
- {
- name: 'number',
- displayName: 'Number',
- values: [
- {
- displayName: 'Name',
- name: 'name',
- type: 'string',
- default: 'propertyName',
- description:
- 'Name of the property to write data to. Supports dot-notation. Example: "data.person[0].name"',
- },
- {
- displayName: 'Value',
- name: 'value',
- type: 'number',
- default: 0,
- description: 'The number value to write in the property.',
- },
- ],
- },
- {
- name: 'string',
- displayName: 'String',
- values: [
- {
- displayName: 'Name',
- name: 'name',
- type: 'string',
- default: 'propertyName',
- description:
- 'Name of the property to write data to. Supports dot-notation. Example: "data.person[0].name"',
- },
- {
- displayName: 'Value',
- name: 'value',
- type: 'string',
- default: '',
- description: 'The string value to write in the property.',
- },
- ],
- },
- ],
- },
-
- {
- displayName: 'Options',
- name: 'options',
- type: 'collection',
- placeholder: 'Add Option',
- default: {},
- options: [
- {
- displayName: 'Dot Notation',
- name: 'dotNotation',
- type: 'boolean',
- default: true,
- description: `By default, dot-notation is used in property names. This means that "a.b" will set the property "b" underneath "a" so { "a": { "b": value} }.
If that is not intended this can be deactivated, it will then set { "a.b": value } instead.
- `,
- },
- ],
- },
- ],
- },
- async execute(this: IExecuteFunctions) {
- const items = this.getInputData();
-
- if (items.length === 0) {
- items.push({ json: {} });
- }
-
- const returnData: INodeExecutionData[] = [];
-
- let item: INodeExecutionData;
- let keepOnlySet: boolean;
- for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
- keepOnlySet = this.getNodeParameter('keepOnlySet', itemIndex, false) as boolean;
- item = items[itemIndex];
- const options = this.getNodeParameter('options', itemIndex, {});
-
- const newItem: INodeExecutionData = {
- json: {},
- };
-
- if (!keepOnlySet) {
- if (item.binary !== undefined) {
- // Create a shallow copy of the binary data so that the old
- // data references which do not get changed still stay behind
- // but the incoming data does not get changed.
- newItem.binary = {};
- Object.assign(newItem.binary, item.binary);
- }
-
- newItem.json = deepCopy(item.json);
- }
-
- // Add boolean values
- (this.getNodeParameter('values.boolean', itemIndex, []) as INodeParameters[]).forEach(
- (setItem) => {
- if (options.dotNotation === false) {
- newItem.json[setItem.name as string] = !!setItem.value;
- } else {
- set(newItem.json, setItem.name as string, !!setItem.value);
- }
- },
- );
-
- // Add number values
- (this.getNodeParameter('values.number', itemIndex, []) as INodeParameters[]).forEach(
- (setItem) => {
- if (options.dotNotation === false) {
- newItem.json[setItem.name as string] = setItem.value;
- } else {
- set(newItem.json, setItem.name as string, setItem.value);
- }
- },
- );
-
- // Add string values
- (this.getNodeParameter('values.string', itemIndex, []) as INodeParameters[]).forEach(
- (setItem) => {
- if (options.dotNotation === false) {
- newItem.json[setItem.name as string] = setItem.value;
- } else {
- set(newItem.json, setItem.name as string, setItem.value);
- }
- },
- );
-
- returnData.push(newItem);
- }
-
- return [returnData];
- },
- },
},
};
}
diff --git a/packages/core/package.json b/packages/core/package.json
index a0886df901..bf5664ea8a 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -43,6 +43,9 @@
"@types/request-promise-native": "~1.0.15",
"@types/uuid": "^8.3.2"
},
+ "peerDependencies": {
+ "n8n-nodes-base": "workspace:*"
+ },
"dependencies": {
"axios": "^0.21.1",
"@n8n/client-oauth2": "workspace:*",
diff --git a/packages/core/test/helpers/constants.ts b/packages/core/test/helpers/constants.ts
index a566ba8a72..a917fd00d2 100644
--- a/packages/core/test/helpers/constants.ts
+++ b/packages/core/test/helpers/constants.ts
@@ -1,467 +1,35 @@
-import set from 'lodash/set';
-
import type {
IExecuteFunctions,
INodeExecutionData,
- INodeParameters,
INodeTypeData,
- NodeParameterValue,
WorkflowTestData,
} from 'n8n-workflow';
-import { deepCopy } from 'n8n-workflow';
+import { If } from 'n8n-nodes-base/nodes/If/If.node';
+import { Merge } from 'n8n-nodes-base/dist/nodes/Merge/Merge.node';
+import { NoOp } from 'n8n-nodes-base/nodes/NoOp/NoOp.node';
+import { Set } from 'n8n-nodes-base/nodes/Set/Set.node';
+import { Start } from 'n8n-nodes-base/nodes/Start/Start.node';
export const predefinedNodesTypes: INodeTypeData = {
'n8n-nodes-base.if': {
+ type: new If(),
sourcePath: '',
- type: {
- description: {
- displayName: 'If',
- name: 'if',
- group: ['transform'],
- version: 1,
- description: 'Splits a stream depending on defined compare operations.',
- defaults: {
- name: 'IF',
- color: '#408000',
- },
- inputs: ['main'],
- outputs: ['main', 'main'],
- properties: [
- {
- displayName: 'Conditions',
- name: 'conditions',
- placeholder: 'Add Condition',
- type: 'fixedCollection',
- typeOptions: {
- multipleValues: true,
- },
- description: 'The type of values to compare.',
- default: {},
- options: [
- {
- name: 'boolean',
- displayName: 'Boolean',
- values: [
- {
- displayName: 'Value 1',
- name: 'value1',
- type: 'boolean',
- default: false,
- description: 'The value to compare with the second one.',
- },
- {
- displayName: 'Operation',
- name: 'operation',
- type: 'options',
- options: [
- {
- name: 'Equal',
- value: 'equal',
- },
- {
- name: 'Not Equal',
- value: 'notEqual',
- },
- ],
- default: 'equal',
- description: 'Operation to decide where the the data should be mapped to.',
- },
- {
- displayName: 'Value 2',
- name: 'value2',
- type: 'boolean',
- default: false,
- description: 'The value to compare with the first one.',
- },
- ],
- },
- {
- name: 'number',
- displayName: 'Number',
- values: [
- {
- displayName: 'Value 1',
- name: 'value1',
- type: 'number',
- default: 0,
- description: 'The value to compare with the second one.',
- },
- {
- displayName: 'Operation',
- name: 'operation',
- type: 'options',
- options: [
- {
- name: 'Smaller',
- value: 'smaller',
- },
- {
- name: 'Smaller Equal',
- value: 'smallerEqual',
- },
- {
- name: 'Equal',
- value: 'equal',
- },
- {
- name: 'Not Equal',
- value: 'notEqual',
- },
- {
- name: 'Larger',
- value: 'larger',
- },
- {
- name: 'Larger Equal',
- value: 'largerEqual',
- },
- {
- name: 'Is Empty',
- value: 'isEmpty',
- },
- ],
- default: 'smaller',
- description: 'Operation to decide where the the data should be mapped to.',
- },
- {
- displayName: 'Value 2',
- name: 'value2',
- type: 'number',
- displayOptions: {
- hide: {
- operation: ['isEmpty'],
- },
- },
- default: 0,
- description: 'The value to compare with the first one.',
- },
- ],
- },
- {
- name: 'string',
- displayName: 'String',
- values: [
- {
- displayName: 'Value 1',
- name: 'value1',
- type: 'string',
- default: '',
- description: 'The value to compare with the second one.',
- },
- {
- displayName: 'Operation',
- name: 'operation',
- type: 'options',
- options: [
- {
- name: 'Contains',
- value: 'contains',
- },
- {
- name: 'Ends With',
- value: 'endsWith',
- },
- {
- name: 'Equal',
- value: 'equal',
- },
- {
- name: 'Not Contains',
- value: 'notContains',
- },
- {
- name: 'Not Equal',
- value: 'notEqual',
- },
- {
- name: 'Regex',
- value: 'regex',
- },
- {
- name: 'Starts With',
- value: 'startsWith',
- },
- {
- name: 'Is Empty',
- value: 'isEmpty',
- },
- ],
- default: 'equal',
- description: 'Operation to decide where the the data should be mapped to.',
- },
- {
- displayName: 'Value 2',
- name: 'value2',
- type: 'string',
- displayOptions: {
- hide: {
- operation: ['isEmpty', 'regex'],
- },
- },
- default: '',
- description: 'The value to compare with the first one.',
- },
- {
- displayName: 'Regex',
- name: 'value2',
- type: 'string',
- displayOptions: {
- show: {
- operation: ['regex'],
- },
- },
- default: '',
- placeholder: '/text/i',
- description: 'The regex which has to match.',
- },
- ],
- },
- ],
- },
- {
- displayName: 'Combine',
- name: 'combineOperation',
- type: 'options',
- options: [
- {
- name: 'ALL',
- description: 'Only if all conditions are met it goes into "true" branch.',
- value: 'all',
- },
- {
- name: 'ANY',
- description: 'If any of the conditions is met it goes into "true" branch.',
- value: 'any',
- },
- ],
- default: 'all',
- description:
- 'If multiple rules got set this settings decides if it is true as soon as ANY condition matches or only if ALL get meet.',
- },
- ],
- },
- async execute(this: IExecuteFunctions) {
- const returnDataTrue: INodeExecutionData[] = [];
- const returnDataFalse: INodeExecutionData[] = [];
-
- const items = this.getInputData();
-
- let item: INodeExecutionData;
- let combineOperation: string;
-
- // The compare operations
- const compareOperationFunctions: {
- [key: string]: (value1: NodeParameterValue, value2: NodeParameterValue) => boolean;
- } = {
- contains: (value1: NodeParameterValue, value2: NodeParameterValue) =>
- (value1 || '').toString().includes((value2 || '').toString()),
- notContains: (value1: NodeParameterValue, value2: NodeParameterValue) =>
- !(value1 || '').toString().includes((value2 || '').toString()),
- endsWith: (value1: NodeParameterValue, value2: NodeParameterValue) =>
- (value1 as string).endsWith(value2 as string),
- equal: (value1: NodeParameterValue, value2: NodeParameterValue) => value1 === value2,
- notEqual: (value1: NodeParameterValue, value2: NodeParameterValue) => value1 !== value2,
- larger: (value1: NodeParameterValue, value2: NodeParameterValue) =>
- (value1 || 0) > (value2 || 0),
- largerEqual: (value1: NodeParameterValue, value2: NodeParameterValue) =>
- (value1 || 0) >= (value2 || 0),
- smaller: (value1: NodeParameterValue, value2: NodeParameterValue) =>
- (value1 || 0) < (value2 || 0),
- smallerEqual: (value1: NodeParameterValue, value2: NodeParameterValue) =>
- (value1 || 0) <= (value2 || 0),
- startsWith: (value1: NodeParameterValue, value2: NodeParameterValue) =>
- (value1 as string).startsWith(value2 as string),
- isEmpty: (value1: NodeParameterValue) => [undefined, null, ''].includes(value1 as string),
- regex: (value1: NodeParameterValue, value2: NodeParameterValue) => {
- const regexMatch = (value2 || '').toString().match(new RegExp('^/(.*?)/([gimusy]*)$'));
-
- let regex: RegExp;
- if (!regexMatch) {
- regex = new RegExp((value2 || '').toString());
- } else if (regexMatch.length === 1) {
- regex = new RegExp(regexMatch[1]);
- } else {
- regex = new RegExp(regexMatch[1], regexMatch[2]);
- }
-
- return !!(value1 || '').toString().match(regex);
- },
- };
-
- // The different dataTypes to check the values in
- const dataTypes = ['boolean', 'number', 'string'];
-
- // Iterate over all items to check which ones should be output as via output "true" and
- // which ones via output "false"
- let dataType: string;
- let compareOperationResult: boolean;
- itemLoop: for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
- item = items[itemIndex];
-
- let compareData: INodeParameters;
-
- combineOperation = this.getNodeParameter('combineOperation', itemIndex) as string;
-
- // Check all the values of the different dataTypes
- for (dataType of dataTypes) {
- // Check all the values of the current dataType
- for (compareData of this.getNodeParameter(
- `conditions.${dataType}`,
- itemIndex,
- [],
- ) as INodeParameters[]) {
- // Check if the values passes
- compareOperationResult = compareOperationFunctions[compareData.operation as string](
- compareData.value1 as NodeParameterValue,
- compareData.value2 as NodeParameterValue,
- );
-
- if (compareOperationResult && combineOperation === 'any') {
- // If it passes and the operation is "any" we do not have to check any
- // other ones as it should pass anyway. So go on with the next item.
- returnDataTrue.push(item);
- continue itemLoop;
- } else if (!compareOperationResult && combineOperation === 'all') {
- // If it fails and the operation is "all" we do not have to check any
- // other ones as it should be not pass anyway. So go on with the next item.
- returnDataFalse.push(item);
- continue itemLoop;
- }
- }
- }
-
- if (combineOperation === 'all') {
- // If the operation is "all" it means the item did match all conditions
- // so it passes.
- returnDataTrue.push(item);
- } else {
- // If the operation is "any" it means the the item did not match any condition.
- returnDataFalse.push(item);
- }
- }
-
- return [returnDataTrue, returnDataFalse];
- },
- },
},
'n8n-nodes-base.merge': {
+ type: new Merge(),
sourcePath: '',
- type: {
- description: {
- displayName: 'Merge',
- name: 'merge',
- icon: 'fa:clone',
- group: ['transform'],
- version: [1, 2],
- requiredInputs: '={{ $version === 2 ? 1 : undefined }}',
- description: 'Merges data of multiple streams once data of both is available',
- defaults: {
- name: 'Merge',
- color: '#00cc22',
- },
- inputs: ['main', 'main'],
- outputs: ['main'],
- properties: [
- {
- displayName: 'Mode',
- name: 'mode',
- type: 'options',
- options: [
- {
- name: 'Append',
- value: 'append',
- description:
- 'Combines data of both inputs. The output will contain items of input 1 and input 2.',
- },
- {
- name: 'Pass-through',
- value: 'passThrough',
- description:
- 'Passes through data of one input. The output will contain only items of the defined input.',
- },
- {
- name: 'Wait',
- value: 'wait',
- description:
- 'Waits till data of both inputs is available and will then output a single empty item.',
- },
- ],
- default: 'append',
- description:
- 'How data should be merged. If it should simply
be appended or merged depending on a property.',
- },
- {
- displayName: 'Output Data',
- name: 'output',
- type: 'options',
- displayOptions: {
- show: {
- mode: ['passThrough'],
- },
- },
- options: [
- {
- name: 'Input 1',
- value: 'input1',
- },
- {
- name: 'Input 2',
- value: 'input2',
- },
- ],
- default: 'input1',
- description: 'Defines of which input the data should be used as output of node.',
- },
- ],
- },
- async execute(this: IExecuteFunctions) {
- const returnData: INodeExecutionData[] = [];
-
- const mode = this.getNodeParameter('mode', 0) as string;
-
- if (mode === 'append') {
- // Simply appends the data
- for (let i = 0; i < 2; i++) {
- returnData.push.apply(returnData, this.getInputData(i));
- }
- } else if (mode === 'passThrough') {
- const output = this.getNodeParameter('output', 0) as string;
-
- if (output === 'input1') {
- returnData.push.apply(returnData, this.getInputData(0));
- } else {
- returnData.push.apply(returnData, this.getInputData(1));
- }
- } else if (mode === 'wait') {
- returnData.push({ json: {} });
- }
-
- return [returnData];
- },
- },
},
'n8n-nodes-base.noOp': {
+ type: new NoOp(),
+ sourcePath: '',
+ },
+ 'n8n-nodes-base.set': {
+ type: new Set(),
+ sourcePath: '',
+ },
+ 'n8n-nodes-base.start': {
+ type: new Start(),
sourcePath: '',
- type: {
- description: {
- displayName: 'No Operation, do nothing',
- name: 'noOp',
- icon: 'fa:arrow-right',
- group: ['organization'],
- version: 1,
- description: 'No Operation',
- defaults: {
- name: 'NoOp',
- color: '#b0b0b0',
- },
- inputs: ['main'],
- outputs: ['main'],
- properties: [],
- },
- async execute(this: IExecuteFunctions) {
- return [this.getInputData()];
- },
- },
},
'n8n-nodes-base.versionTest': {
sourcePath: '',
@@ -522,220 +90,6 @@ export const predefinedNodesTypes: INodeTypeData = {
},
},
},
- 'n8n-nodes-base.set': {
- sourcePath: '',
- type: {
- description: {
- displayName: 'Set',
- name: 'set',
- group: ['input'],
- version: 1,
- description: 'Sets a value',
- defaults: {
- name: 'Set',
- color: '#0000FF',
- },
- inputs: ['main'],
- outputs: ['main'],
- properties: [
- {
- displayName: 'Keep Only Set',
- name: 'keepOnlySet',
- type: 'boolean',
- default: false,
- description:
- 'If only the values set on this node should be
kept and all others removed.',
- },
- {
- displayName: 'Values to Set',
- name: 'values',
- placeholder: 'Add Value',
- type: 'fixedCollection',
- typeOptions: {
- multipleValues: true,
- },
- description: 'The value to set.',
- default: {},
- options: [
- {
- name: 'boolean',
- displayName: 'Boolean',
- values: [
- {
- displayName: 'Name',
- name: 'name',
- type: 'string',
- default: 'propertyName',
- description:
- 'Name of the property to write data to.
Supports dot-notation.
Example: "data.person[0].name"',
- },
- {
- displayName: 'Value',
- name: 'value',
- type: 'boolean',
- default: false,
- description: 'The boolean value to write in the property.',
- },
- ],
- },
- {
- name: 'number',
- displayName: 'Number',
- values: [
- {
- displayName: 'Name',
- name: 'name',
- type: 'string',
- default: 'propertyName',
- description:
- 'Name of the property to write data to.
Supports dot-notation.
Example: "data.person[0].name"',
- },
- {
- displayName: 'Value',
- name: 'value',
- type: 'number',
- default: 0,
- description: 'The number value to write in the property.',
- },
- ],
- },
- {
- name: 'string',
- displayName: 'String',
- values: [
- {
- displayName: 'Name',
- name: 'name',
- type: 'string',
- default: 'propertyName',
- description:
- 'Name of the property to write data to.
Supports dot-notation.
Example: "data.person[0].name"',
- },
- {
- displayName: 'Value',
- name: 'value',
- type: 'string',
- default: '',
- description: 'The string value to write in the property.',
- },
- ],
- },
- ],
- },
-
- {
- displayName: 'Options',
- name: 'options',
- type: 'collection',
- placeholder: 'Add Option',
- default: {},
- options: [
- {
- displayName: 'Dot Notation',
- name: 'dotNotation',
- type: 'boolean',
- default: true,
- description:
- 'By default, dot-notation is used in property names. This means that "a.b" will set the property "b" underneath "a" so { "a": { "b": value} }.
If that is not intended this can be deactivated, it will then set { "a.b": value } instead.
',
- },
- ],
- },
- ],
- },
- async execute(this: IExecuteFunctions) {
- const items = this.getInputData();
-
- if (items.length === 0) {
- items.push({ json: {} });
- }
-
- const returnData: INodeExecutionData[] = [];
-
- let item: INodeExecutionData;
- let keepOnlySet: boolean;
- for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
- keepOnlySet = this.getNodeParameter('keepOnlySet', itemIndex, false) as boolean;
- item = items[itemIndex];
- const options = this.getNodeParameter('options', itemIndex, {});
-
- const newItem: INodeExecutionData = {
- json: {},
- };
-
- if (!keepOnlySet) {
- if (item.binary !== undefined) {
- // Create a shallow copy of the binary data so that the old
- // data references which do not get changed still stay behind
- // but the incoming data does not get changed.
- newItem.binary = {};
- Object.assign(newItem.binary, item.binary);
- }
-
- newItem.json = deepCopy(item.json);
- }
-
- // Add boolean values
- (this.getNodeParameter('values.boolean', itemIndex, []) as INodeParameters[]).forEach(
- (setItem) => {
- if (options.dotNotation === false) {
- newItem.json[setItem.name as string] = !!setItem.value;
- } else {
- set(newItem.json, setItem.name as string, !!setItem.value);
- }
- },
- );
-
- // Add number values
- (this.getNodeParameter('values.number', itemIndex, []) as INodeParameters[]).forEach(
- (setItem) => {
- if (options.dotNotation === false) {
- newItem.json[setItem.name as string] = setItem.value;
- } else {
- set(newItem.json, setItem.name as string, setItem.value);
- }
- },
- );
-
- // Add string values
- (this.getNodeParameter('values.string', itemIndex, []) as INodeParameters[]).forEach(
- (setItem) => {
- if (options.dotNotation === false) {
- newItem.json[setItem.name as string] = setItem.value;
- } else {
- set(newItem.json, setItem.name as string, setItem.value);
- }
- },
- );
-
- returnData.push(newItem);
- }
-
- return [returnData];
- },
- },
- },
- 'n8n-nodes-base.start': {
- sourcePath: '',
- type: {
- description: {
- displayName: 'Start',
- name: 'start',
- group: ['input'],
- version: 1,
- description: 'Starts the workflow execution from this node',
- defaults: {
- name: 'Start',
- color: '#553399',
- },
- inputs: [],
- outputs: ['main'],
- properties: [],
- },
- async execute(this: IExecuteFunctions): Promise {
- return [this.getInputData()];
- },
- },
- },
};
export const legacyWorkflowExecuteTests: WorkflowTestData[] = [
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 22df3e77bb..7b4b32a11d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -615,6 +615,9 @@ importers:
mime-types:
specifier: ^2.1.27
version: 2.1.35
+ n8n-nodes-base:
+ specifier: workspace:*
+ version: link:../nodes-base
n8n-workflow:
specifier: workspace:*
version: link:../workflow