feat(Set Node): Overhaul (#6348)

Github issue / Community forum post (link here to close automatically):
https://github.com/n8n-io/n8n/pull/6348

---------

Co-authored-by: Giulio Andreini <g.andreini@gmail.com>
Co-authored-by: Marcus <marcus@n8n.io>
This commit is contained in:
Michael Kret
2023-09-19 13:16:35 +03:00
committed by GitHub
parent 050ba706d3
commit 3a474552b2
42 changed files with 2626 additions and 410 deletions

View File

@@ -1,217 +1,26 @@
import type {
IExecuteFunctions,
INodeExecutionData,
INodeParameters,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
import { deepCopy } from 'n8n-workflow';
import type { INodeTypeBaseDescription, IVersionedNodeType } from 'n8n-workflow';
import { VersionedNodeType } from 'n8n-workflow';
import set from 'lodash/set';
import { SetV1 } from './v1/SetV1.node';
import { SetV2 } from './v2/SetV2.node';
export class Set implements INodeType {
description: INodeTypeDescription = {
displayName: 'Set',
name: 'set',
icon: 'fa:pen',
group: ['input'],
version: [1, 2],
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:
'Whether 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',
requiresDataPath: 'single',
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,
// eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether
description: 'The boolean value to write in the property',
},
],
},
{
name: 'number',
displayName: 'Number',
values: [
{
displayName: 'Name',
name: 'name',
type: 'string',
default: 'propertyName',
requiresDataPath: 'single',
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',
requiresDataPath: 'single',
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',
},
],
},
],
},
export class Set extends VersionedNodeType {
constructor() {
const baseDescription: INodeTypeBaseDescription = {
displayName: 'Set',
name: 'set',
icon: 'fa:pen',
group: ['input'],
description: 'Add or edit fields on an input item and optionally remove other fields',
defaultVersion: 3,
};
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
options: [
{
displayName: 'Dot Notation',
name: 'dotNotation',
type: 'boolean',
default: true,
// eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether
description:
'<p>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} }.<p></p>If that is not intended this can be deactivated, it will then set { "a.b": value } instead.</p>.',
},
],
},
],
};
const nodeVersions: IVersionedNodeType['nodeVersions'] = {
1: new SetV1(baseDescription),
2: new SetV1(baseDescription),
3: new SetV2(baseDescription),
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const nodeVersion = this.getNode().typeVersion;
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: {},
pairedItem: item.pairedItem,
};
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 (
nodeVersion >= 2 &&
typeof setItem.value === 'string' &&
!Number.isNaN(Number(setItem.value))
) {
setItem.value = Number(setItem.value);
}
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];
super(nodeVersions, baseDescription);
}
}