first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
|
||||
describe('Test Merge Node', () => {
|
||||
new NodeTestHarness().setupTests();
|
||||
});
|
||||
@@ -0,0 +1,143 @@
|
||||
{
|
||||
"name": "merge tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "9f880c6f-b5e0-4453-b1d7-550beb1febe8",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 380]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"id": 2
|
||||
},
|
||||
{
|
||||
"id": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "263d9c7f-32d4-4112-b271-73d09b73809a",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [1020, 220]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "82ff69e1-596b-48dd-b724-388ef454a822",
|
||||
"name": "Merge",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 1,
|
||||
"position": [1280, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 4
|
||||
},
|
||||
{
|
||||
"id": 5
|
||||
},
|
||||
{
|
||||
"id": 6
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "3defcf5a-ec5e-4d23-a1f6-a99b7362a241",
|
||||
"name": "Code1",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [1040, 540]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Merge": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 6
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Code1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "7082c0c9-60ab-4bcb-adbd-89e56e0bddd6",
|
||||
"id": "107",
|
||||
"meta": {
|
||||
"instanceId": "36203ea1ce3cef713fa25999bd9874ae26b9e4c2c3a90a365f2882a154d031d0"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
{
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "8d731d98fab18fb8e68289d44c50faa5d5c5378aedc3f892abdd7d5d3b7061c5"
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"fields": {
|
||||
"values": [
|
||||
{
|
||||
"name": "one",
|
||||
"stringValue": "={{ $('Code').item.json.id }}"
|
||||
},
|
||||
{
|
||||
"name": "two",
|
||||
"stringValue": "={{ $('Code1').item.json.id }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "853f9e11-e59b-4f21-81ba-08ec2d69c87a",
|
||||
"name": "Edit Fields",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [900, 580]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "8310a9da-b02b-4a77-80d4-7c2de5bcbae8",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [180, 600]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "0f1fb40d-0c6c-455d-bb3b-0c0fa818e063",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [420, 520]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "873fcccc-86c1-40fd-bdae-2bb31e971382",
|
||||
"name": "Code1",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [420, 680]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "chooseBranch",
|
||||
"output": "empty"
|
||||
},
|
||||
"id": "b81a2372-9f8b-4899-b2fd-c581bcf930d8",
|
||||
"name": "Merge3",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [700, 580]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Code1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge3",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Merge3": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {
|
||||
"Edit Fields": [
|
||||
{
|
||||
"json": {
|
||||
"one": "1",
|
||||
"two": "2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
{
|
||||
"name": "merge tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "9f880c6f-b5e0-4453-b1d7-550beb1febe8",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 380]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"id": 2
|
||||
},
|
||||
{
|
||||
"id": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "263d9c7f-32d4-4112-b271-73d09b73809a",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [1020, 220]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 4
|
||||
},
|
||||
{
|
||||
"id": 5
|
||||
},
|
||||
{
|
||||
"id": 6
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "3defcf5a-ec5e-4d23-a1f6-a99b7362a241",
|
||||
"name": "Code1",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [1040, 540]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "chooseBranch"
|
||||
},
|
||||
"id": "7120cd6f-29f3-4f5b-aa25-babebd6335d3",
|
||||
"name": "Merge",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1300, 240]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "chooseBranch",
|
||||
"output": "input2"
|
||||
},
|
||||
"id": "5dbe68d4-bf2f-40ea-a9ae-ee83b4030fa5",
|
||||
"name": "Merge1",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1300, 380]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "chooseBranch",
|
||||
"output": "empty"
|
||||
},
|
||||
"id": "3b2885c3-5c2e-4972-910a-50ff8e173d11",
|
||||
"name": "Merge2",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1300, 520]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Merge": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge1": [
|
||||
{
|
||||
"json": {
|
||||
"id": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 6
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge2": [
|
||||
{
|
||||
"json": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Code1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge1",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge2",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "dc43b494-33dc-4671-9004-e5e53170732c",
|
||||
"id": "107",
|
||||
"meta": {
|
||||
"instanceId": "36203ea1ce3cef713fa25999bd9874ae26b9e4c2c3a90a365f2882a154d031d0"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,518 @@
|
||||
{
|
||||
"name": "merge tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "9f880c6f-b5e0-4453-b1d7-550beb1febe8",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [700, 660]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"data": "a",
|
||||
"input": 1,
|
||||
"text": "foo"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"data": "b",
|
||||
"input": 1,
|
||||
"text": "foo"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "263d9c7f-32d4-4112-b271-73d09b73809a",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [900, 540]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"data": "c",
|
||||
"input": 2,
|
||||
"tag": "second"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"data": "d",
|
||||
"input": 2,
|
||||
"tag": "second"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"data": "e",
|
||||
"input": 2,
|
||||
"tag": "second"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "3defcf5a-ec5e-4d23-a1f6-a99b7362a241",
|
||||
"name": "Code1",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [900, 780]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "7120cd6f-29f3-4f5b-aa25-babebd6335d3",
|
||||
"name": "Merge",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1320, 140]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"joinMode": "keepNonMatches",
|
||||
"options": {}
|
||||
},
|
||||
"id": "1d6973e5-0716-4d47-85ef-298d3f86bb9d",
|
||||
"name": "Merge1",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1320, 280]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"joinMode": "keepEverything",
|
||||
"options": {}
|
||||
},
|
||||
"id": "dd75b8c0-21bf-460f-9920-644a467ed356",
|
||||
"name": "Merge2",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1320, 420]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"joinMode": "enrichInput1",
|
||||
"options": {}
|
||||
},
|
||||
"id": "3545877d-6e4f-437e-b91f-4c792d714e73",
|
||||
"name": "Merge3",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1320, 560]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"joinMode": "enrichInput2",
|
||||
"options": {}
|
||||
},
|
||||
"id": "4b6ac099-6e6f-4e91-bcfe-74d7524fad54",
|
||||
"name": "Merge4",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1320, 740]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"outputDataFrom": "input1",
|
||||
"options": {}
|
||||
},
|
||||
"id": "fb4fef36-26ea-4fc0-b6e9-a4b2781c94fa",
|
||||
"name": "Merge5",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1320, 900]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"outputDataFrom": "input2",
|
||||
"options": {}
|
||||
},
|
||||
"id": "0b4fbaaa-cf0b-4da5-b35f-da3f49409965",
|
||||
"name": "Merge6",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1320, 1040]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"clashHandling": {
|
||||
"values": {
|
||||
"resolveClash": "addSuffix"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "25489a7a-74c1-48d5-a03d-1bbd2aba2abe",
|
||||
"name": "Merge7",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1320, 1200]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Merge": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": "c",
|
||||
"input": 2,
|
||||
"text": "foo",
|
||||
"tag": "second"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": "d",
|
||||
"input": 2,
|
||||
"text": "foo",
|
||||
"tag": "second"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge1": [
|
||||
{
|
||||
"json": {
|
||||
"id": 3,
|
||||
"data": "e",
|
||||
"input": 2,
|
||||
"tag": "second",
|
||||
"_source": "input2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge2": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": "c",
|
||||
"input": 2,
|
||||
"text": "foo",
|
||||
"tag": "second"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": "d",
|
||||
"input": 2,
|
||||
"text": "foo",
|
||||
"tag": "second"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3,
|
||||
"data": "e",
|
||||
"input": 2,
|
||||
"tag": "second"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge3": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": "c",
|
||||
"input": 2,
|
||||
"text": "foo",
|
||||
"tag": "second"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": "d",
|
||||
"input": 2,
|
||||
"text": "foo",
|
||||
"tag": "second"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge4": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": "a",
|
||||
"input": 1,
|
||||
"text": "foo",
|
||||
"tag": "second"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": "b",
|
||||
"input": 1,
|
||||
"text": "foo",
|
||||
"tag": "second"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3,
|
||||
"data": "e",
|
||||
"input": 2,
|
||||
"tag": "second"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge5": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": "a",
|
||||
"input": 1,
|
||||
"text": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": "b",
|
||||
"input": 1,
|
||||
"text": "foo"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge6": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": "c",
|
||||
"input": 2,
|
||||
"tag": "second"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": "d",
|
||||
"input": 2,
|
||||
"tag": "second"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge7": [
|
||||
{
|
||||
"json": {
|
||||
"id_1": 1,
|
||||
"data_1": "a",
|
||||
"input_1": 1,
|
||||
"text_1": "foo",
|
||||
"id_2": 1,
|
||||
"data_2": "c",
|
||||
"input_2": 2,
|
||||
"tag_2": "second"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id_1": 2,
|
||||
"data_1": "b",
|
||||
"input_1": 1,
|
||||
"text_1": "foo",
|
||||
"id_2": 2,
|
||||
"data_2": "d",
|
||||
"input_2": 2,
|
||||
"tag_2": "second"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Code1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge4",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge5",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge6",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge7",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge3",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge2",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge1",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge4",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge5",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge6",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge7",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "8bad6701-bb00-4617-b1d5-bd24145eadaf",
|
||||
"id": "107",
|
||||
"meta": {
|
||||
"instanceId": "36203ea1ce3cef713fa25999bd9874ae26b9e4c2c3a90a365f2882a154d031d0"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
{
|
||||
"name": "merge tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "9f880c6f-b5e0-4453-b1d7-550beb1febe8",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [840, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"data": "a",
|
||||
"input": 1
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"data": "b",
|
||||
"input": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "263d9c7f-32d4-4112-b271-73d09b73809a",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [1040, 200]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"data": "c",
|
||||
"input": 2
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"data": "d",
|
||||
"input": 2
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"data": "e",
|
||||
"input": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "3defcf5a-ec5e-4d23-a1f6-a99b7362a241",
|
||||
"name": "Code1",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [1060, 520]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"combinationMode": "mergeByPosition",
|
||||
"options": {
|
||||
"clashHandling": {
|
||||
"values": {
|
||||
"resolveClash": "addSuffix"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "7120cd6f-29f3-4f5b-aa25-babebd6335d3",
|
||||
"name": "Merge",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1260, 200]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"combinationMode": "mergeByPosition",
|
||||
"options": {
|
||||
"clashHandling": {
|
||||
"values": {
|
||||
"resolveClash": "preferInput1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "b40fdf2e-7527-4954-84fe-464a762f8957",
|
||||
"name": "Merge1",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1260, 380]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"combinationMode": "mergeByPosition",
|
||||
"options": {
|
||||
"clashHandling": {
|
||||
"values": {
|
||||
"resolveClash": "preferInput2"
|
||||
}
|
||||
},
|
||||
"includeUnpaired": true
|
||||
}
|
||||
},
|
||||
"id": "daca2305-691a-45c2-9010-34a9ef5a783f",
|
||||
"name": "Merge2",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1260, 540]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Merge": [
|
||||
{
|
||||
"json": {
|
||||
"id_1": 1,
|
||||
"data_1": "a",
|
||||
"input_1": 1,
|
||||
"id_2": 1,
|
||||
"data_2": "c",
|
||||
"input_2": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id_1": 2,
|
||||
"data_1": "b",
|
||||
"input_1": 1,
|
||||
"id_2": 2,
|
||||
"data_2": "d",
|
||||
"input_2": 2
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge1": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": "a",
|
||||
"input": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": "b",
|
||||
"input": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge2": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": "c",
|
||||
"input": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": "d",
|
||||
"input": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3,
|
||||
"data": "e",
|
||||
"input": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Code1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge1",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge2",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "c61ce62f-d995-46bb-900a-8b3edf95a858",
|
||||
"id": "107",
|
||||
"meta": {
|
||||
"instanceId": "36203ea1ce3cef713fa25999bd9874ae26b9e4c2c3a90a365f2882a154d031d0"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
{
|
||||
"name": "merge tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "9f880c6f-b5e0-4453-b1d7-550beb1febe8",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 380]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"data": "a",
|
||||
"input": 1
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"data": "b",
|
||||
"input": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "263d9c7f-32d4-4112-b271-73d09b73809a",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [1020, 220]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"data": "c",
|
||||
"input": 2
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"data": "d",
|
||||
"input": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "3defcf5a-ec5e-4d23-a1f6-a99b7362a241",
|
||||
"name": "Code1",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [1040, 540]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"combinationMode": "multiplex",
|
||||
"options": {
|
||||
"clashHandling": {
|
||||
"values": {
|
||||
"resolveClash": "addSuffix"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "7120cd6f-29f3-4f5b-aa25-babebd6335d3",
|
||||
"name": "Merge",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1260, 220]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"combinationMode": "multiplex",
|
||||
"options": {
|
||||
"clashHandling": {
|
||||
"values": {
|
||||
"resolveClash": "preferInput1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "1ec4a466-432d-43f8-b6c0-3cc02cf32f6f",
|
||||
"name": "Merge1",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1260, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"combinationMode": "multiplex",
|
||||
"options": {
|
||||
"clashHandling": {
|
||||
"values": {
|
||||
"resolveClash": "preferInput2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "fb671be6-b476-4302-984f-0c973839581b",
|
||||
"name": "Merge2",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1260, 500]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Merge": [
|
||||
{
|
||||
"json": {
|
||||
"id_1": 1,
|
||||
"data_1": "a",
|
||||
"input_1": 1,
|
||||
"id_2": 1,
|
||||
"data_2": "c",
|
||||
"input_2": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id_1": 1,
|
||||
"data_1": "a",
|
||||
"input_1": 1,
|
||||
"id_2": 2,
|
||||
"data_2": "d",
|
||||
"input_2": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id_1": 2,
|
||||
"data_1": "b",
|
||||
"input_1": 1,
|
||||
"id_2": 1,
|
||||
"data_2": "c",
|
||||
"input_2": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id_1": 2,
|
||||
"data_1": "b",
|
||||
"input_1": 1,
|
||||
"id_2": 2,
|
||||
"data_2": "d",
|
||||
"input_2": 2
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge1": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": "a",
|
||||
"input": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": "b",
|
||||
"input": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": "a",
|
||||
"input": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": "b",
|
||||
"input": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge2": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": "c",
|
||||
"input": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": "d",
|
||||
"input": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": "c",
|
||||
"input": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": "d",
|
||||
"input": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Code1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge1",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge2",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "0cab5d94-90b7-4024-ad58-af9cafb46a21",
|
||||
"id": "107",
|
||||
"meta": {
|
||||
"instanceId": "36203ea1ce3cef713fa25999bd9874ae26b9e4c2c3a90a365f2882a154d031d0"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,612 @@
|
||||
{
|
||||
"name": "merge fix",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "94003e55-6c4e-492f-802a-49f4fb5b5f4b",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [120, 940]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"first_name": "John",
|
||||
"last_name": "Doe"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"first_name": "Jane",
|
||||
"last_name": "Smith"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"first_name": "Michael",
|
||||
"last_name": "Johnson"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"first_name": "Emily",
|
||||
"last_name": "Davis"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"first_name": "Robert",
|
||||
"last_name": "Taylor"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"first_name": "Olivia",
|
||||
"last_name": "Martin"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"first_name": "William",
|
||||
"last_name": "Brown"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"first_name": "Sophia",
|
||||
"last_name": "Anderson"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"first_name": "Daniel",
|
||||
"last_name": "Wilson"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"first_name": "Ava",
|
||||
"last_name": "Miller"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "da82fbb0-b689-4c4e-b310-303361e8411c",
|
||||
"name": "Mock data A",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [340, 840]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": []
|
||||
},
|
||||
"id": "493ecb02-52e7-43b7-8c73-d45a3f7e76f6",
|
||||
"name": "Mock data B",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [340, 1040]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"joinMode": "keepNonMatches",
|
||||
"options": {}
|
||||
},
|
||||
"id": "d0c0f123-1461-4b78-9cf8-6410440430d3",
|
||||
"name": "Merge",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2.1,
|
||||
"position": [700, 500]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"joinMode": "keepNonMatches",
|
||||
"options": {}
|
||||
},
|
||||
"id": "5093ec3e-c987-44c7-815f-279ea7842db5",
|
||||
"name": "Merge1",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2.1,
|
||||
"position": [700, 680]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"joinMode": "keepNonMatches",
|
||||
"outputDataFrom": "input1",
|
||||
"options": {}
|
||||
},
|
||||
"id": "0453bb35-3971-4a23-93b7-31853e4634fb",
|
||||
"name": "Merge2",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2.1,
|
||||
"position": [700, 880]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"joinMode": "keepNonMatches",
|
||||
"outputDataFrom": "input1",
|
||||
"options": {}
|
||||
},
|
||||
"id": "f09c9327-4557-406b-8ae0-db851055dd08",
|
||||
"name": "Merge3",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2.1,
|
||||
"position": [700, 1060],
|
||||
"alwaysOutputData": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"joinMode": "keepNonMatches",
|
||||
"outputDataFrom": "input2",
|
||||
"options": {}
|
||||
},
|
||||
"id": "d5bc6ee7-141e-4eba-a9aa-cb866b177b89",
|
||||
"name": "Merge4",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2.1,
|
||||
"position": [700, 1280],
|
||||
"alwaysOutputData": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"joinMode": "keepNonMatches",
|
||||
"outputDataFrom": "input2",
|
||||
"options": {}
|
||||
},
|
||||
"id": "4c881679-3f76-491a-a2c9-e35e4c9e28ff",
|
||||
"name": "Merge5",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2.1,
|
||||
"position": [700, 1460],
|
||||
"alwaysOutputData": true
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Merge": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"first_name": "John",
|
||||
"last_name": "Doe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"first_name": "Jane",
|
||||
"last_name": "Smith"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3,
|
||||
"first_name": "Michael",
|
||||
"last_name": "Johnson"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 4,
|
||||
"first_name": "Emily",
|
||||
"last_name": "Davis"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 5,
|
||||
"first_name": "Robert",
|
||||
"last_name": "Taylor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 6,
|
||||
"first_name": "Olivia",
|
||||
"last_name": "Martin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 7,
|
||||
"first_name": "William",
|
||||
"last_name": "Brown"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 8,
|
||||
"first_name": "Sophia",
|
||||
"last_name": "Anderson"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 9,
|
||||
"first_name": "Daniel",
|
||||
"last_name": "Wilson"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 10,
|
||||
"first_name": "Ava",
|
||||
"last_name": "Miller"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge1": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"first_name": "John",
|
||||
"last_name": "Doe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"first_name": "Jane",
|
||||
"last_name": "Smith"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3,
|
||||
"first_name": "Michael",
|
||||
"last_name": "Johnson"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 4,
|
||||
"first_name": "Emily",
|
||||
"last_name": "Davis"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 5,
|
||||
"first_name": "Robert",
|
||||
"last_name": "Taylor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 6,
|
||||
"first_name": "Olivia",
|
||||
"last_name": "Martin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 7,
|
||||
"first_name": "William",
|
||||
"last_name": "Brown"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 8,
|
||||
"first_name": "Sophia",
|
||||
"last_name": "Anderson"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 9,
|
||||
"first_name": "Daniel",
|
||||
"last_name": "Wilson"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 10,
|
||||
"first_name": "Ava",
|
||||
"last_name": "Miller"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge2": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"first_name": "John",
|
||||
"last_name": "Doe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"first_name": "Jane",
|
||||
"last_name": "Smith"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3,
|
||||
"first_name": "Michael",
|
||||
"last_name": "Johnson"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 4,
|
||||
"first_name": "Emily",
|
||||
"last_name": "Davis"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 5,
|
||||
"first_name": "Robert",
|
||||
"last_name": "Taylor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 6,
|
||||
"first_name": "Olivia",
|
||||
"last_name": "Martin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 7,
|
||||
"first_name": "William",
|
||||
"last_name": "Brown"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 8,
|
||||
"first_name": "Sophia",
|
||||
"last_name": "Anderson"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 9,
|
||||
"first_name": "Daniel",
|
||||
"last_name": "Wilson"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 10,
|
||||
"first_name": "Ava",
|
||||
"last_name": "Miller"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Merge3": [
|
||||
{
|
||||
"json": {}
|
||||
}
|
||||
],
|
||||
"Merge4": [
|
||||
{
|
||||
"json": {}
|
||||
}
|
||||
],
|
||||
"Merge5": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"first_name": "John",
|
||||
"last_name": "Doe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"first_name": "Jane",
|
||||
"last_name": "Smith"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3,
|
||||
"first_name": "Michael",
|
||||
"last_name": "Johnson"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 4,
|
||||
"first_name": "Emily",
|
||||
"last_name": "Davis"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 5,
|
||||
"first_name": "Robert",
|
||||
"last_name": "Taylor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 6,
|
||||
"first_name": "Olivia",
|
||||
"last_name": "Martin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 7,
|
||||
"first_name": "William",
|
||||
"last_name": "Brown"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 8,
|
||||
"first_name": "Sophia",
|
||||
"last_name": "Anderson"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 9,
|
||||
"first_name": "Daniel",
|
||||
"last_name": "Wilson"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 10,
|
||||
"first_name": "Ava",
|
||||
"last_name": "Miller"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Mock data A",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Mock data B",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Mock data A": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge1",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge3",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge4",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge5",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Mock data B": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge2",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge4",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge5",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "54329e46-4d05-491a-919c-e156e77d6d11",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "b888bd11cd1ddbb95450babf3e199556799d999b896f650de768b8370ee50363"
|
||||
},
|
||||
"id": "WbgpQNak2tO6spx2",
|
||||
"tags": []
|
||||
}
|
||||
+2440
File diff suppressed because it is too large
Load Diff
+2378
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,214 @@
|
||||
{
|
||||
"name": "do not error on missing keys",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "c7c0cf66-790a-4da7-81c8-ba9e4bbcec9a",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [720, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"id": 2
|
||||
},
|
||||
{
|
||||
"id": 3
|
||||
},
|
||||
{
|
||||
"id": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "cec18624-ced0-4de1-8987-d4b184b136b9",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [1020, 200]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": []
|
||||
},
|
||||
"id": "754d549c-82ce-4625-ba2b-6f8edcbf715e",
|
||||
"name": "Code1",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [1020, 400]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "551ed574-607a-4d98-9b06-350df92c805e",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1540, 200]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "00e75760-d88e-413c-b276-c759db72411f",
|
||||
"name": "No Operation, do nothing1",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1540, 380]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "idd",
|
||||
"field2": "idd"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "2d412290-4c43-40a6-be78-946602749aa1",
|
||||
"name": "Merge1",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2.1,
|
||||
"position": [1340, 520],
|
||||
"alwaysOutputData": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "idd",
|
||||
"field2": "idd"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "b6f05b31-3ca0-48c4-a5ec-c7f81dc7957f",
|
||||
"name": "Merge2",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 2,
|
||||
"position": [1320, 160],
|
||||
"continueOnFail": true
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing1": [
|
||||
{
|
||||
"json": {}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Code1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge1",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "Merge2",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"No Operation, do nothing1": {
|
||||
"main": [[]]
|
||||
},
|
||||
"Merge1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Merge2": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "d0ebcbdd-b57d-495e-8f84-b9500d25c384",
|
||||
"id": "8",
|
||||
"meta": {
|
||||
"instanceId": "6ebec4953fe56f1c009e7c3b107578b375137523af057073c0b5da17350651bd"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
import {
|
||||
loadAlaSqlSandbox,
|
||||
resetSandboxCache,
|
||||
runAlaSqlInSandbox,
|
||||
} from '../../v3/helpers/sandbox-utils';
|
||||
|
||||
describe('combineBySql sandbox (isolated-vm)', () => {
|
||||
describe('loadAlaSqlSandbox', () => {
|
||||
beforeEach(() => {
|
||||
resetSandboxCache();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetSandboxCache();
|
||||
});
|
||||
|
||||
it('should return a context with alasql available', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
const result = await context.eval('typeof alasql', { copy: true });
|
||||
expect(result).toBe('function');
|
||||
});
|
||||
|
||||
it('should return the same cached context on subsequent calls', async () => {
|
||||
const context1 = await loadAlaSqlSandbox();
|
||||
const context2 = await loadAlaSqlSandbox();
|
||||
expect(context1).toBe(context2);
|
||||
});
|
||||
|
||||
it('should recreate the sandbox after resetSandboxCache', async () => {
|
||||
const context1 = await loadAlaSqlSandbox();
|
||||
resetSandboxCache();
|
||||
const context2 = await loadAlaSqlSandbox();
|
||||
expect(context1).not.toBe(context2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('runAlaSqlInSandbox – isolation', () => {
|
||||
beforeAll(() => {
|
||||
resetSandboxCache();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
resetSandboxCache();
|
||||
});
|
||||
|
||||
// ── Host API access ────────────────────────────────────────────────────
|
||||
|
||||
it('should not expose require inside the sandbox', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
await expect(
|
||||
context.eval('(function() { return typeof require; })()', { copy: true }),
|
||||
).resolves.toBe('undefined');
|
||||
});
|
||||
|
||||
it('should not expose process inside the sandbox', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
await expect(
|
||||
context.eval('(function() { return typeof process; })()', { copy: true }),
|
||||
).resolves.toBe('undefined');
|
||||
});
|
||||
|
||||
it('should not expose __dirname inside the sandbox', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
await expect(
|
||||
context.eval('(function() { return typeof __dirname; })()', { copy: true }),
|
||||
).resolves.toBe('undefined');
|
||||
});
|
||||
|
||||
it('should not expose __filename inside the sandbox', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
await expect(
|
||||
context.eval('(function() { return typeof __filename; })()', { copy: true }),
|
||||
).resolves.toBe('undefined');
|
||||
});
|
||||
|
||||
it('should not expose global inside the sandbox', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
await expect(
|
||||
context.eval('(function() { return typeof global; })()', { copy: true }),
|
||||
).resolves.toBe('undefined');
|
||||
});
|
||||
|
||||
it('should not expose Buffer inside the sandbox', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
await expect(
|
||||
context.eval('(function() { return typeof Buffer; })()', { copy: true }),
|
||||
).resolves.toBe('undefined');
|
||||
});
|
||||
|
||||
// ── File system access ─────────────────────────────────────────────────
|
||||
|
||||
it('should block FROM FILE() SQL clause (no fs in browser bundle)', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
await expect(
|
||||
runAlaSqlInSandbox(
|
||||
context,
|
||||
[[{ id: 1 }]],
|
||||
"SELECT * FROM FILE('/etc/passwd', {headers: false})",
|
||||
),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
it('should block ATTACH CSV FILE attempts', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
await expect(
|
||||
runAlaSqlInSandbox(
|
||||
context,
|
||||
[[{ id: 1 }]],
|
||||
"ATTACH CSV '/etc/passwd' AS secret; SELECT * FROM secret",
|
||||
),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
// ── Code evaluation / function injection ───────────────────────────────
|
||||
|
||||
it('should block CREATE FUNCTION from accessing host APIs', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
// Even if a user crafts a SQL function, require is not in scope inside the isolate
|
||||
await expect(
|
||||
runAlaSqlInSandbox(
|
||||
context,
|
||||
[[{ id: 1 }]],
|
||||
'SELECT id FROM input1 WHERE alasql(\'CREATE FUNCTION danger() RETURNS STRING BEGIN RETURN typeof require END; SELECT danger() FROM [{"x":1}]\')',
|
||||
),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
it('should return "undefined" for require even when called via a SQL function', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
// CREATE FUNCTION compiles a JS body – require must still be absent
|
||||
await expect(
|
||||
runAlaSqlInSandbox(
|
||||
context,
|
||||
[[{ id: 1 }]],
|
||||
"SELECT alasql('CREATE FUNCTION getRequire() RETURNS STRING BEGIN RETURN typeof require END') FROM input1",
|
||||
),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
it('should block eval() inside the sandbox from escaping isolation', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
// eval exists in V8 but must not be able to reach host-side objects
|
||||
const result = (await context.eval(
|
||||
`(function() {
|
||||
try { return eval('typeof require'); }
|
||||
catch(e) { return 'error: ' + e.message; }
|
||||
})()`,
|
||||
{ copy: true },
|
||||
)) as string;
|
||||
// Either eval is blocked or require is still undefined inside eval'd code
|
||||
expect(result === 'undefined' || result.startsWith('error:')).toBe(true);
|
||||
});
|
||||
|
||||
it('should block alasql arrow-operator prototype traversal from reaching host globals', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
// process is unavailable in the isolate, the inner call silently fails, resulting in an empty object
|
||||
const result = await runAlaSqlInSandbox(
|
||||
context,
|
||||
[[{ id: 1 }]],
|
||||
'SELECT {}.constructor->constructor->[call](\'\',\'return {result: process.getBuiltinModule("child_process").execSync("id").toString()}\') AS r FROM input1',
|
||||
);
|
||||
expect(result).toEqual([{}]);
|
||||
});
|
||||
|
||||
it('should block Function constructor from reaching host APIs', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
const result = (await context.eval(
|
||||
`(function() {
|
||||
try { return new Function('return typeof require')(); }
|
||||
catch(e) { return 'error: ' + e.message; }
|
||||
})()`,
|
||||
{ copy: true },
|
||||
)) as string;
|
||||
expect(result === 'undefined' || result.startsWith('error:')).toBe(true);
|
||||
});
|
||||
|
||||
// ── Prototype pollution ────────────────────────────────────────────────
|
||||
|
||||
it('should contain prototype pollution within the isolate heap', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
// Pollute Object.prototype inside the sandbox
|
||||
await context.eval('Object.prototype.__polluted = true', { copy: true });
|
||||
// Host-side Object.prototype must be untouched
|
||||
expect(({} as Record<string, unknown>).__polluted).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should have alasql.fn frozen after sandbox initialisation', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
const isFrozen = (await context.eval('Object.isFrozen(alasql.fn)', {
|
||||
copy: true,
|
||||
})) as boolean;
|
||||
expect(isFrozen).toBe(true);
|
||||
});
|
||||
|
||||
it('should not allow a CREATE FUNCTION', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
|
||||
await expect(
|
||||
runAlaSqlInSandbox(
|
||||
context,
|
||||
[[{ x: 1 }]],
|
||||
'CREATE FUNCTION double(n) RETURNS NUMBER BEGIN RETURN n * 2 END; SELECT double(x) AS v FROM input1',
|
||||
),
|
||||
).rejects.toThrow();
|
||||
|
||||
await expect(
|
||||
runAlaSqlInSandbox(context, [[{ x: 1 }]], 'SELECT double(x) AS v FROM input1'),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
// ── Resource limits ────────────────────────────────────────────────────
|
||||
|
||||
it('should enforce the CPU timeout on an infinite loop', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
await expect(
|
||||
context.eval('(function() { while(true) {} })()', { timeout: 100, copy: true }),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('runAlaSqlInSandbox – SQL execution', () => {
|
||||
beforeAll(() => {
|
||||
resetSandboxCache();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
resetSandboxCache();
|
||||
});
|
||||
|
||||
it('should execute a basic SELECT query', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
const result = await runAlaSqlInSandbox(
|
||||
context,
|
||||
[
|
||||
[
|
||||
{ name: 'Alice', age: 30 },
|
||||
{ name: 'Bob', age: 25 },
|
||||
],
|
||||
],
|
||||
'SELECT * FROM input1',
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{ name: 'Alice', age: 30 },
|
||||
{ name: 'Bob', age: 25 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should execute a JOIN query across two inputs', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
const result = await runAlaSqlInSandbox(
|
||||
context,
|
||||
[
|
||||
[
|
||||
{ id: 1, name: 'Alice' },
|
||||
{ id: 2, name: 'Bob' },
|
||||
],
|
||||
[
|
||||
{ userId: 1, score: 100 },
|
||||
{ userId: 2, score: 200 },
|
||||
],
|
||||
],
|
||||
'SELECT input1.name, input2.score FROM input1 LEFT JOIN input2 ON input1.id = input2.userId',
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{ name: 'Alice', score: 100 },
|
||||
{ name: 'Bob', score: 200 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should execute a WHERE filter', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
const result = await runAlaSqlInSandbox(
|
||||
context,
|
||||
[
|
||||
[
|
||||
{ id: 1, val: 10 },
|
||||
{ id: 2, val: 20 },
|
||||
{ id: 3, val: 30 },
|
||||
],
|
||||
],
|
||||
'SELECT * FROM input1 WHERE val > 15',
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{ id: 2, val: 20 },
|
||||
{ id: 3, val: 30 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array for a query with no results', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
const result = await runAlaSqlInSandbox(
|
||||
context,
|
||||
[[{ id: 1 }]],
|
||||
'SELECT * FROM input1 WHERE id = 999',
|
||||
);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should throw a meaningful error for invalid SQL', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
await expect(runAlaSqlInSandbox(context, [[{ id: 1 }]], 'THIS IS NOT SQL')).rejects.toThrow();
|
||||
});
|
||||
|
||||
it('should clean up its database after each execution', async () => {
|
||||
const context = await loadAlaSqlSandbox();
|
||||
|
||||
const countBefore = (await context.eval('Object.keys(alasql.databases).length', {
|
||||
copy: true,
|
||||
})) as number;
|
||||
|
||||
await runAlaSqlInSandbox(context, [[{ id: 1 }]], 'SELECT * FROM input1');
|
||||
await runAlaSqlInSandbox(context, [[{ id: 2 }]], 'SELECT * FROM input1');
|
||||
|
||||
const countAfter = (await context.eval('Object.keys(alasql.databases).length', {
|
||||
copy: true,
|
||||
})) as number;
|
||||
|
||||
// Database count must not grow – each invocation cleans up after itself
|
||||
expect(countAfter).toBe(countBefore);
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,92 @@
|
||||
import type { IDataObject, INodeExecutionData, IPairedItemData } from 'n8n-workflow';
|
||||
|
||||
import { modifySelectQuery, rowToExecutionData } from '../../v3/helpers/utils';
|
||||
|
||||
describe('rowToExecutionData', () => {
|
||||
test('should return empty json and pairedItem when input is empty', () => {
|
||||
const input: IDataObject = {};
|
||||
const result = rowToExecutionData(input);
|
||||
expect(result).toEqual({ json: {}, pairedItem: [] });
|
||||
});
|
||||
|
||||
test('should separate json properties and pairedItem properties', () => {
|
||||
const input: IDataObject = {
|
||||
key1: 'value1',
|
||||
key2: 42,
|
||||
pairedItem1: { item: 0, input: undefined } as IPairedItemData,
|
||||
pairedItem2: { item: 0, input: 1 } as IPairedItemData,
|
||||
};
|
||||
|
||||
const expectedOutput: INodeExecutionData = {
|
||||
json: { key1: 'value1', key2: 42 },
|
||||
pairedItem: [
|
||||
{ item: 0, input: undefined },
|
||||
{ item: 0, input: 1 },
|
||||
],
|
||||
};
|
||||
|
||||
expect(rowToExecutionData(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
test('should ignore undefined pairedItem values', () => {
|
||||
const input: IDataObject = {
|
||||
key: 'value',
|
||||
pairedItem1: { item: 0, input: undefined } as IPairedItemData,
|
||||
pairedItem2: undefined,
|
||||
};
|
||||
|
||||
const expectedOutput: INodeExecutionData = {
|
||||
json: { key: 'value' },
|
||||
pairedItem: [{ item: 0, input: undefined }],
|
||||
};
|
||||
|
||||
expect(rowToExecutionData(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
test('should handle only json properties without pairedItem', () => {
|
||||
const input: IDataObject = {
|
||||
name: 'Alice',
|
||||
age: 30,
|
||||
};
|
||||
|
||||
const expectedOutput: INodeExecutionData = {
|
||||
json: { name: 'Alice', age: 30 },
|
||||
pairedItem: [],
|
||||
};
|
||||
|
||||
expect(rowToExecutionData(input)).toEqual(expectedOutput);
|
||||
});
|
||||
});
|
||||
|
||||
describe('modifySelectQuery', () => {
|
||||
test('should return the original query if no SELECT match is found', () => {
|
||||
const query = 'UPDATE table SET column = 1';
|
||||
expect(modifySelectQuery(query, 2)).toBe(query);
|
||||
});
|
||||
|
||||
test('should return the original query if SELECT * is used', () => {
|
||||
const query = 'SELECT * FROM input1';
|
||||
expect(modifySelectQuery(query, 2)).toBe(query);
|
||||
});
|
||||
|
||||
test('should append pairedItem columns when input tables exist', () => {
|
||||
const query = 'SELECT column1, column2 FROM input1 WHERE input1.id = table.id';
|
||||
const modifiedQuery = modifySelectQuery(query, 2);
|
||||
expect(modifiedQuery).toBe(
|
||||
'SELECT column1, column2, input1.pairedItem AS pairedItem1 FROM input1 WHERE input1.id = table.id',
|
||||
);
|
||||
});
|
||||
|
||||
test('should handle multiple input tables correctly', () => {
|
||||
const query = 'SELECT column1 FROM input1 LEFT JOIN input2 ON input1.name = input2.name';
|
||||
const modifiedQuery = modifySelectQuery(query, 2);
|
||||
expect(modifiedQuery).toBe(
|
||||
'SELECT column1, input1.pairedItem AS pairedItem1, input2.pairedItem AS pairedItem2 FROM input1 LEFT JOIN input2 ON input1.name = input2.name',
|
||||
);
|
||||
});
|
||||
|
||||
test('should not modify query if no input tables are found', () => {
|
||||
const query = 'SELECT column1 FROM table';
|
||||
expect(modifySelectQuery(query, 2)).toBe(query);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user