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

@@ -0,0 +1,656 @@
{
"name": "My workflow 22",
"nodes": [
{
"parameters": {},
"id": "fbb0f637-5a91-4227-af0a-cde04cd6059d",
"name": "When clicking \"Execute Workflow\"",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
-460,
980
]
},
{
"parameters": {
"jsCode": "return [\n {\n \"string\": {\n test2: \"hello\",\n test3: \" \",\n test4: \"\",\n test5: \"3\",\n test6: \"3,14\",\n test7: \"3.14\",\n test8: \"false\",\n test8: \"TRUE\",\n test9: \"false\",\n test10: \"1\",\n test11: '[\"apples\", \"oranges\"]',\n test12: '\"apples\", \"oranges\"',\n test13: '[1, 2]',\n test14: '{\"a\": 1, \"b\": { \"c\": 10, \"d\": \"test\"}}',\n test15: '{\"a\": 1}',\n test16: \"null\",\n test17: \"undefined\",\n test18: \"0\",\n },\n \"number\": {\n test1: 52472,\n test2: -1,\n test3: 0,\n test4: 1.334535,\n test5: null,\n test6: undefined,\n test7: 1,\n },\n \"boolean\": {\n // test1: 1,\n // test2: 0,\n test3: true,\n test4: false,\n },\n \"date\": {\n test1: $now,\n test2: \"2023-08-01T12:34:56Z\",\n test3: \"2016-05-25T09:24:15.123\",\n test4: \"Tue, 01 Nov 2016 13:23:12 +0630\",\n test5: \"2017-05-15 09:24:15\",\n test6: \"1542674993\",\n test7: 1542674993,\n },\n \"array\": {\n test13: [1,2,3,4],\n },\n \"object\": {\n obj: {\n objKey: 2,\n objArray: [1,2,3,4],\n objBool: true\n }\n },\n }\n];"
},
"id": "15a372ee-5243-409f-b28e-3eb3ec211e38",
"name": "Code1",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
-200,
980
]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "numberToString1",
"stringValue": "={{ $json.number.test1 }}"
},
{
"name": "numberToString2",
"stringValue": "={{ $json.number.test2 }}"
},
{
"name": "numberToString3",
"stringValue": "={{ $json.number.test4 }}"
},
{
"name": "boolToString1",
"stringValue": "={{ $json.boolean.test3 }}"
},
{
"name": "boolToString2",
"stringValue": "={{ $json.boolean.test4 }}"
},
{
"name": "arrayToString1",
"stringValue": "={{ $json.array.test13 }}"
},
{
"name": "objectToString1",
"stringValue": "={{ $json.object.obj }}"
}
]
},
"include": "none",
"options": {}
},
"id": "570b8f0e-1153-40a5-984f-5c1ae370fc0b",
"name": "To String",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [
160,
600
]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "stringToNumber1",
"type": "numberValue",
"numberValue": "={{ $json.string.test5 }}"
},
{
"name": "stringToNumber2",
"type": "numberValue",
"numberValue": "={{ $json.string.test7 }}"
},
{
"name": "boolToNumber1",
"type": "numberValue",
"numberValue": "={{ $json.boolean.test3 }}"
},
{
"name": "boolToNumber2",
"type": "numberValue",
"numberValue": "={{ $json.boolean.test4 }}"
}
]
},
"include": "none",
"options": {}
},
"id": "660214cb-d38f-4566-b91e-98f3407f7348",
"name": "To Number",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [
160,
800
]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "stringToBoolean1",
"type": "booleanValue",
"booleanValue": "={{ $json.string.test8 }}"
},
{
"name": "stringToBoolean3",
"type": "booleanValue",
"booleanValue": "={{ $json.string.test9 }}"
},
{
"name": "stringToBoolean4",
"type": "booleanValue",
"booleanValue": "={{ $json.string.test10 }}"
},
{
"name": "stringToBoolean5",
"type": "booleanValue",
"booleanValue": "={{ $json.string.test18 }}"
},
{
"name": "numberToBoolean1",
"type": "booleanValue",
"booleanValue": "={{ $json.number.test3 }}"
},
{
"name": "numberToBoolean2",
"type": "booleanValue",
"booleanValue": "={{ $json.number.test7 }}"
}
]
},
"include": "none",
"options": {}
},
"id": "f3e5b73a-6b55-4822-8864-25e9a73a3fe7",
"name": "To Boolean",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [
160,
980
]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "stringToArray1",
"type": "arrayValue",
"arrayValue": "={{ $json.string.test11 }}"
},
{
"name": "stringToArray2",
"type": "arrayValue",
"arrayValue": "={{ $json.string.test13 }}"
},
{
"name": "arrayToArray1",
"type": "arrayValue",
"arrayValue": "={{ $json.array.test13 }}"
}
]
},
"include": "none",
"options": {}
},
"id": "1385e092-ad14-4be7-8e3b-3645a56e0e22",
"name": "To Array",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [
160,
1180
]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "stringToObject1",
"type": "objectValue",
"objectValue": "={{ $json.string.test14 }}"
},
{
"name": "stringToObject2",
"type": "objectValue",
"objectValue": "={{ $json.string.test15 }}"
}
]
},
"include": "none",
"options": {}
},
"id": "df394ded-3519-4604-859d-a50cbc788a56",
"name": "To Object",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [
160,
1360
]
},
{
"parameters": {
"content": "### Strict type checking",
"height": 1063.125,
"width": 369.6875
},
"id": "442560f9-6e05-467a-bdb4-02c8c8313e77",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
80,
540
]
},
{
"parameters": {
"content": "### Loose type checking",
"height": 1058.046875,
"width": 310.703125
},
"id": "b2ff8103-5d4c-46ec-9ca7-d7db8e4b3789",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
560,
544.375
]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "stringToNumber1",
"type": "numberValue",
"numberValue": "={{ $json.string.test2 }}"
},
{
"name": "stringToNumber2",
"type": "numberValue",
"numberValue": "={{ $json.string.test3 }}"
},
{
"name": "stringToNumber3",
"type": "numberValue",
"numberValue": "={{ $json.string.test9 }}"
},
{
"name": "arrayToNumber1",
"type": "numberValue",
"numberValue": "={{ $json.array.test13 }}"
}
]
},
"include": "none",
"options": {
"ignoreConversionErrors": true
}
},
"id": "ff5d7e99-4c8d-48e6-bfbf-70b32e3e19d9",
"name": "To Number1",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [
600,
600
]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "stringToBoolean1",
"type": "booleanValue",
"booleanValue": "={{ $json.string.test5 }}"
},
{
"name": "stringToBoolean2",
"type": "booleanValue",
"booleanValue": "=3,14"
},
{
"name": "stringToBoolean3",
"type": "booleanValue",
"booleanValue": "={{ $json.string.test7 }}"
},
{
"name": "stringToBoolean4",
"type": "booleanValue",
"booleanValue": "={{ $json.string.test11 }}"
},
{
"name": "stringToBoolean5",
"type": "booleanValue",
"booleanValue": "={{ $json.string.test12 }}"
},
{
"name": "stringToBoolean6",
"type": "booleanValue",
"booleanValue": "={{ $json.string.test17 }}"
},
{
"name": "numberToBoolean1",
"type": "booleanValue",
"booleanValue": "={{ $json.number.test1 }}"
},
{
"name": "numberToBoolean2",
"type": "booleanValue",
"booleanValue": "={{ $json.number.test4 }}"
}
]
},
"include": "none",
"options": {
"ignoreConversionErrors": true
}
},
"id": "7ec129f4-ac9d-4cff-b1a8-c957a8119a07",
"name": "To Boolean1",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [
600,
800
]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "stringToArray1",
"type": "arrayValue",
"arrayValue": "={{ $json.string.test2 }}"
},
{
"name": "stringToArray2",
"type": "arrayValue",
"arrayValue": "={{ $json.string.test5 }}"
}
]
},
"include": "none",
"options": {
"ignoreConversionErrors": true
}
},
"id": "7b33fa7e-1cc8-44af-b251-f2521df25618",
"name": "To Array1",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [
600,
980
]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "stringToObject1",
"type": "objectValue",
"objectValue": "={{ $json.string.test14 }}"
},
{
"name": "stringToObject2",
"type": "objectValue",
"objectValue": "={{ $json.string.test15 }}"
}
]
},
"include": "none",
"options": {
"ignoreConversionErrors": true
}
},
"id": "030066a3-7c27-45fc-9173-22866f977fea",
"name": "To Object1",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [
600,
1180
]
},
{
"parameters": {},
"id": "b19c8f55-836f-43c8-9eed-30f51e99d150",
"name": "No Operation, do nothing",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
100,
200
]
},
{
"parameters": {},
"id": "8bacf8f8-314f-4be1-a090-667792000cb4",
"name": "No Operation, do nothing1",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
360,
200
]
}
],
"pinData": {
"To String": [
{
"json": {
"numberToString1": "52472",
"numberToString2": "-1",
"numberToString3": "1.334535",
"boolToString1": "true",
"boolToString2": "false",
"arrayToString1": "[1,2,3,4]",
"objectToString1": "{\"objKey\":2,\"objArray\":[1,2,3,4],\"objBool\":true}"
}
}
],
"To Number": [
{
"json": {
"stringToNumber1": 3,
"stringToNumber2": 3.14,
"boolToNumber1": 1,
"boolToNumber2": 0
}
}
],
"To Boolean": [
{
"json": {
"stringToBoolean1": true,
"stringToBoolean3": false,
"stringToBoolean4": true,
"stringToBoolean5": false,
"numberToBoolean1": false,
"numberToBoolean2": true
}
}
],
"To Array": [
{
"json": {
"stringToArray1": [
"apples",
"oranges"
],
"stringToArray2": [
1,
2
],
"arrayToArray1": [
1,
2,
3,
4
]
}
}
],
"To Object": [
{
"json": {
"stringToObject1": {
"a": 1,
"b": {
"c": 10,
"d": "test"
}
},
"stringToObject2": {
"a": 1
}
}
}
],
"To Number1": [
{
"json": {
"stringToNumber1": "hello",
"stringToNumber2": 0,
"stringToNumber3": "false",
"arrayToNumber1": [
1,
2,
3,
4
]
}
}
],
"To Boolean1": [
{
"json": {
"stringToBoolean1": "3",
"stringToBoolean2": "3,14",
"stringToBoolean3": "3.14",
"stringToBoolean4": "[\"apples\", \"oranges\"]",
"stringToBoolean5": "\"apples\", \"oranges\"",
"stringToBoolean6": "undefined",
"numberToBoolean1": 52472,
"numberToBoolean2": 1.334535
}
}
],
"To Object1": [
{
"json": {
"stringToObject1": {
"a": 1,
"b": {
"c": 10,
"d": "test"
}
},
"stringToObject2": {
"a": 1
}
}
}
],
"To Array1": [
{
"json": {
"stringToArray1": "hello",
"stringToArray2": "3"
}
}
]
},
"connections": {
"When clicking \"Execute Workflow\"": {
"main": [
[
{
"node": "Code1",
"type": "main",
"index": 0
}
]
]
},
"Code1": {
"main": [
[
{
"node": "To String",
"type": "main",
"index": 0
},
{
"node": "To Number",
"type": "main",
"index": 0
},
{
"node": "To Boolean",
"type": "main",
"index": 0
},
{
"node": "To Array",
"type": "main",
"index": 0
},
{
"node": "To Object",
"type": "main",
"index": 0
},
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
]
]
},
"No Operation, do nothing": {
"main": [
[
{
"node": "No Operation, do nothing1",
"type": "main",
"index": 0
}
]
]
},
"No Operation, do nothing1": {
"main": [
[
{
"node": "To Number1",
"type": "main",
"index": 0
},
{
"node": "To Boolean1",
"type": "main",
"index": 0
},
{
"node": "To Array1",
"type": "main",
"index": 0
},
{
"node": "To Object1",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "b54f2d8f-158d-4540-839f-37c0bda20d9b",
"id": "yVUBwSyuyegX6JIL",
"meta": {
"instanceId": "b888bd11cd1ddbb95450babf3e199556799d999b896f650de768b8370ee50363"
},
"tags": []
}

View File

@@ -0,0 +1,247 @@
import type { IDataObject, IExecuteFunctions, IGetNodeParameterOptions, INode } from 'n8n-workflow';
import { constructExecutionMetaData } from 'n8n-core';
import get from 'lodash/get';
import { composeReturnItem, parseJsonParameter, validateEntry } from '../../v2/helpers/utils';
import type { SetNodeOptions } from '../../v2/helpers/interfaces';
export const node: INode = {
id: '11',
name: 'Edit Fields',
type: 'n8n-nodes-base.set',
typeVersion: 3,
position: [42, 42],
parameters: {
mode: 'manual',
fields: {
values: [],
},
include: 'none',
options: {},
},
};
export const createMockExecuteFunction = (nodeParameters: IDataObject) => {
const fakeExecuteFunction = {
getNodeParameter(
parameterName: string,
_itemIndex: number,
fallbackValue?: IDataObject | undefined,
options?: IGetNodeParameterOptions | undefined,
) {
const parameter = options?.extractValue ? `${parameterName}.value` : parameterName;
return get(nodeParameters, parameter, fallbackValue);
},
getNode() {
return node;
},
helpers: { constructExecutionMetaData },
continueOnFail: () => false,
} as unknown as IExecuteFunctions;
return fakeExecuteFunction;
};
describe('test Set2, composeReturnItem', () => {
it('should compose return item including no other fields', () => {
const fakeExecuteFunction = createMockExecuteFunction({});
const inputItem = {
json: {
input1: 'value1',
input2: 2,
input3: [1, 2, 3],
},
pairedItem: {
item: 0,
input: undefined,
},
};
const newData = {
num1: 55,
str1: '42',
arr1: ['foo', 'bar'],
obj: {
key: 'value',
},
};
const options: SetNodeOptions = {
include: 'none',
};
const result = composeReturnItem.call(fakeExecuteFunction, 0, inputItem, newData, options);
expect(result).toEqual({
json: {
num1: 55,
str1: '42',
arr1: ['foo', 'bar'],
obj: {
key: 'value',
},
},
pairedItem: {
item: 0,
},
});
});
it('should compose return item including selected fields', () => {
const fakeExecuteFunction = createMockExecuteFunction({ includeFields: 'input1, input2' });
const inputItem = {
json: {
input1: 'value1',
input2: 2,
input3: [1, 2, 3],
},
pairedItem: {
item: 0,
input: undefined,
},
};
const newData = {
num1: 55,
str1: '42',
arr1: ['foo', 'bar'],
obj: {
key: 'value',
},
};
const options: SetNodeOptions = {
include: 'selected',
};
const result = composeReturnItem.call(fakeExecuteFunction, 0, inputItem, newData, options);
expect(result).toEqual({
json: {
num1: 55,
str1: '42',
arr1: ['foo', 'bar'],
input1: 'value1',
input2: 2,
obj: {
key: 'value',
},
},
pairedItem: {
item: 0,
},
});
});
});
describe('test Set2, parseJsonParameter', () => {
it('should parse valid JSON string', () => {
const result = parseJsonParameter('{"foo": "bar"}', node, 0, 'test');
expect(result).toEqual({
foo: 'bar',
});
});
it('should tolerate single quotes in string', () => {
const result = parseJsonParameter("{'foo': 'bar'}", node, 0, 'test');
expect(result).toEqual({
foo: 'bar',
});
});
it('should tolerate unquoted keys', () => {
const result = parseJsonParameter("{foo: 'bar'}", node, 0, 'test');
expect(result).toEqual({
foo: 'bar',
});
});
it('should tolerate trailing comma', () => {
const result = parseJsonParameter('{"foo": "bar"},', node, 0, 'test');
expect(result).toEqual({
foo: 'bar',
});
});
it('should tolerate trailing commas in objects', () => {
const result = parseJsonParameter("{foo: 'bar', baz: {'foo': 'bar',}, }", node, 0, 'test');
expect(result).toEqual({
foo: 'bar',
baz: {
foo: 'bar',
},
});
});
});
describe('test Set2, validateEntry', () => {
it('should convert number to string', () => {
const result = validateEntry(
{ name: 'foo', type: 'stringValue', stringValue: 42 as unknown as string },
node,
0,
);
expect(result).toEqual({
name: 'foo',
value: '42',
});
});
it('should convert array to string', () => {
const result = validateEntry(
{ name: 'foo', type: 'stringValue', stringValue: [1, 2, 3] as unknown as string },
node,
0,
);
expect(result).toEqual({
name: 'foo',
value: '[1,2,3]',
});
});
it('should convert object to string', () => {
const result = validateEntry(
{ name: 'foo', type: 'stringValue', stringValue: { foo: 'bar' } as unknown as string },
node,
0,
);
expect(result).toEqual({
name: 'foo',
value: '{"foo":"bar"}',
});
});
it('should convert boolean to string', () => {
const result = validateEntry(
{ name: 'foo', type: 'stringValue', stringValue: true as unknown as string },
node,
0,
);
expect(result).toEqual({
name: 'foo',
value: 'true',
});
});
it('should convert undefined to string', () => {
const result = validateEntry(
{ name: 'foo', type: 'stringValue', stringValue: undefined as unknown as string },
node,
0,
);
expect(result).toEqual({
name: 'foo',
value: 'undefined',
});
});
});