@blucca/n8n-check 0.0.0-stage → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +10 -0
- package/LICENSE +21 -0
- package/README.md +323 -2
- package/bin/n8n-check.mjs +62 -0
- package/examples/agregado-enrichment/README.md +68 -0
- package/examples/agregado-enrichment/n8n-synthetic +11 -0
- package/examples/agregado-enrichment/observed-results.json +806 -0
- package/examples/agregado-enrichment/prepare.mjs +39 -0
- package/examples/identity-projection/README.md +39 -0
- package/examples/identity-projection/case.json +33 -0
- package/examples/identity-projection/observed-results.json +117 -0
- package/examples/identity-projection/workflow.json +71 -0
- package/examples/identity-projection/wrong-id.json +71 -0
- package/examples/morsof-invoice-history/README.md +77 -0
- package/examples/morsof-invoice-history/UPSTREAM-LICENSE +21 -0
- package/examples/morsof-invoice-history/n8n-datatable.cjs +28 -0
- package/examples/morsof-invoice-history/observed-results.json +113 -0
- package/examples/morsof-invoice-history/prepare.mjs +57 -0
- package/examples/morsof-invoice-history/replay.mjs +35 -0
- package/examples/morsof-invoice-history/run.sh +19 -0
- package/examples/object-body/broken.json +74 -0
- package/examples/object-body/case.json +81 -0
- package/examples/object-body/fixed.json +74 -0
- package/examples/object-body/observed-results.json +449 -0
- package/examples/object-body/retry-case.json +112 -0
- package/examples/object-body/retry.json +77 -0
- package/examples/propfind-body/README.md +75 -0
- package/examples/propfind-body/case.json +51 -0
- package/examples/propfind-body/no-body-control.json +96 -0
- package/examples/propfind-body/observed-results.json +307 -0
- package/examples/propfind-body/workflow.json +96 -0
- package/examples/raw-response/README.md +71 -0
- package/examples/raw-response/file-case.json +54 -0
- package/examples/raw-response/json-body.json +58 -0
- package/examples/raw-response/json-case.json +52 -0
- package/examples/raw-response/observed-results.json +383 -0
- package/examples/raw-response/raw-file.json +88 -0
- package/examples/raw-response/raw-json.json +58 -0
- package/examples/raw-response/raw-text.json +58 -0
- package/examples/raw-response/text-case.json +50 -0
- package/examples/retry-isolation/README.md +71 -0
- package/examples/retry-isolation/direct-case.json +112 -0
- package/examples/retry-isolation/direct.json +101 -0
- package/examples/retry-isolation/editor-template.json +168 -0
- package/examples/retry-isolation/http-batching-case.json +112 -0
- package/examples/retry-isolation/http-batching.json +107 -0
- package/examples/retry-isolation/loop-case.json +117 -0
- package/examples/retry-isolation/loop.json +133 -0
- package/examples/retry-isolation/observed-results.json +155 -0
- package/examples/silent-filter/README.md +57 -0
- package/examples/silent-filter/broken.json +96 -0
- package/examples/silent-filter/case.json +120 -0
- package/examples/silent-filter/fixed.json +96 -0
- package/examples/silent-filter/observed-results.json +273 -0
- package/examples/stop-after/README.md +66 -0
- package/examples/stop-after/broken.json +134 -0
- package/examples/stop-after/case.json +34 -0
- package/examples/stop-after/fixed.json +134 -0
- package/examples/stop-after/observed-results.json +162 -0
- package/examples/vapi-tool-contract/README.md +67 -0
- package/examples/vapi-tool-contract/backend-503.case.json +113 -0
- package/examples/vapi-tool-contract/empty-orders.case.json +113 -0
- package/examples/vapi-tool-contract/observed-results.json +234 -0
- package/examples/vapi-tool-contract/success.case.json +118 -0
- package/examples/vapi-tool-contract/workflow.json +143 -0
- package/examples/vapi-tool-contract/wrong-id.json +143 -0
- package/examples/version-matrix/README.md +65 -0
- package/examples/version-matrix/observed-results.json +307 -0
- package/examples/version-matrix/upgrade-check.yml +32 -0
- package/package.json +41 -4
- package/src/action.mjs +108 -0
- package/src/case-draft.mjs +163 -0
- package/src/local-bundle.mjs +154 -0
- package/src/mock.mjs +72 -0
- package/src/report.mjs +73 -0
- package/src/runner.mjs +111 -0
- package/src/version.mjs +3 -0
- package/src/workflow.mjs +140 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Raw body + File response + UTF-8 JSON extraction",
|
|
3
|
+
"nodes": [
|
|
4
|
+
{
|
|
5
|
+
"parameters": {},
|
|
6
|
+
"id": "raw-response-node-0",
|
|
7
|
+
"name": "When clicking Test workflow",
|
|
8
|
+
"type": "n8n-nodes-base.manualTrigger",
|
|
9
|
+
"typeVersion": 1,
|
|
10
|
+
"position": [
|
|
11
|
+
0,
|
|
12
|
+
0
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"parameters": {
|
|
17
|
+
"method": "POST",
|
|
18
|
+
"url": "https://postman-echo.com/post",
|
|
19
|
+
"sendBody": true,
|
|
20
|
+
"contentType": "raw",
|
|
21
|
+
"rawContentType": "application/json",
|
|
22
|
+
"body": "{\"message\": \"hello\"}",
|
|
23
|
+
"options": {
|
|
24
|
+
"response": {
|
|
25
|
+
"response": {
|
|
26
|
+
"responseFormat": "file",
|
|
27
|
+
"outputPropertyName": "data"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"id": "raw-response-node-1",
|
|
33
|
+
"name": "Send raw request",
|
|
34
|
+
"type": "n8n-nodes-base.httpRequest",
|
|
35
|
+
"typeVersion": 4.4,
|
|
36
|
+
"position": [
|
|
37
|
+
220,
|
|
38
|
+
0
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"parameters": {
|
|
43
|
+
"operation": "fromJson",
|
|
44
|
+
"binaryPropertyName": "data",
|
|
45
|
+
"destinationKey": "receipt",
|
|
46
|
+
"options": {
|
|
47
|
+
"encoding": "utf8"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"id": "raw-response-node-2",
|
|
51
|
+
"name": "Read receipt",
|
|
52
|
+
"type": "n8n-nodes-base.extractFromFile",
|
|
53
|
+
"typeVersion": 1.1,
|
|
54
|
+
"position": [
|
|
55
|
+
440,
|
|
56
|
+
0
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"connections": {
|
|
61
|
+
"When clicking Test workflow": {
|
|
62
|
+
"main": [
|
|
63
|
+
[
|
|
64
|
+
{
|
|
65
|
+
"node": "Send raw request",
|
|
66
|
+
"type": "main",
|
|
67
|
+
"index": 0
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
"Send raw request": {
|
|
73
|
+
"main": [
|
|
74
|
+
[
|
|
75
|
+
{
|
|
76
|
+
"node": "Read receipt",
|
|
77
|
+
"type": "main",
|
|
78
|
+
"index": 0
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"pinData": {},
|
|
85
|
+
"settings": {
|
|
86
|
+
"executionOrder": "v1"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Raw body + explicit JSON response",
|
|
3
|
+
"nodes": [
|
|
4
|
+
{
|
|
5
|
+
"parameters": {},
|
|
6
|
+
"id": "raw-response-node-0",
|
|
7
|
+
"name": "When clicking Test workflow",
|
|
8
|
+
"type": "n8n-nodes-base.manualTrigger",
|
|
9
|
+
"typeVersion": 1,
|
|
10
|
+
"position": [
|
|
11
|
+
0,
|
|
12
|
+
0
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"parameters": {
|
|
17
|
+
"method": "POST",
|
|
18
|
+
"url": "https://postman-echo.com/post",
|
|
19
|
+
"sendBody": true,
|
|
20
|
+
"contentType": "raw",
|
|
21
|
+
"rawContentType": "application/json",
|
|
22
|
+
"body": "{\"message\": \"hello\"}",
|
|
23
|
+
"options": {
|
|
24
|
+
"response": {
|
|
25
|
+
"response": {
|
|
26
|
+
"responseFormat": "json"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"id": "raw-response-node-1",
|
|
32
|
+
"name": "Send raw request",
|
|
33
|
+
"type": "n8n-nodes-base.httpRequest",
|
|
34
|
+
"typeVersion": 4.4,
|
|
35
|
+
"position": [
|
|
36
|
+
220,
|
|
37
|
+
0
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"connections": {
|
|
42
|
+
"When clicking Test workflow": {
|
|
43
|
+
"main": [
|
|
44
|
+
[
|
|
45
|
+
{
|
|
46
|
+
"node": "Send raw request",
|
|
47
|
+
"type": "main",
|
|
48
|
+
"index": 0
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"pinData": {},
|
|
55
|
+
"settings": {
|
|
56
|
+
"executionOrder": "v1"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Raw body + explicit Text response",
|
|
3
|
+
"nodes": [
|
|
4
|
+
{
|
|
5
|
+
"parameters": {},
|
|
6
|
+
"id": "raw-response-node-0",
|
|
7
|
+
"name": "When clicking Test workflow",
|
|
8
|
+
"type": "n8n-nodes-base.manualTrigger",
|
|
9
|
+
"typeVersion": 1,
|
|
10
|
+
"position": [
|
|
11
|
+
0,
|
|
12
|
+
0
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"parameters": {
|
|
17
|
+
"method": "POST",
|
|
18
|
+
"url": "https://postman-echo.com/post",
|
|
19
|
+
"sendBody": true,
|
|
20
|
+
"contentType": "raw",
|
|
21
|
+
"rawContentType": "application/json",
|
|
22
|
+
"body": "{\"message\": \"hello\"}",
|
|
23
|
+
"options": {
|
|
24
|
+
"response": {
|
|
25
|
+
"response": {
|
|
26
|
+
"responseFormat": "text"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"id": "raw-response-node-1",
|
|
32
|
+
"name": "Send raw request",
|
|
33
|
+
"type": "n8n-nodes-base.httpRequest",
|
|
34
|
+
"typeVersion": 4.4,
|
|
35
|
+
"position": [
|
|
36
|
+
220,
|
|
37
|
+
0
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"connections": {
|
|
42
|
+
"When clicking Test workflow": {
|
|
43
|
+
"main": [
|
|
44
|
+
[
|
|
45
|
+
{
|
|
46
|
+
"node": "Send raw request",
|
|
47
|
+
"type": "main",
|
|
48
|
+
"index": 0
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"pinData": {},
|
|
55
|
+
"settings": {
|
|
56
|
+
"executionOrder": "v1"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"name": "Raw body with an explicit text response",
|
|
4
|
+
"input": {
|
|
5
|
+
"node": "When clicking Test workflow",
|
|
6
|
+
"items": [
|
|
7
|
+
{}
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
"mocks": [
|
|
11
|
+
{
|
|
12
|
+
"node": "Send raw request",
|
|
13
|
+
"url": "/deliver",
|
|
14
|
+
"routes": [
|
|
15
|
+
{
|
|
16
|
+
"method": "POST",
|
|
17
|
+
"path": "/deliver",
|
|
18
|
+
"responses": [
|
|
19
|
+
{
|
|
20
|
+
"status": 200,
|
|
21
|
+
"json": {
|
|
22
|
+
"id": "delivery-42",
|
|
23
|
+
"status": "accepted",
|
|
24
|
+
"message": "Livré · 已送达"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"expect": {
|
|
29
|
+
"count": 1,
|
|
30
|
+
"bodies": [
|
|
31
|
+
{
|
|
32
|
+
"message": "hello"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"assertions": [
|
|
41
|
+
{
|
|
42
|
+
"node": "Send raw request",
|
|
43
|
+
"equals": [
|
|
44
|
+
{
|
|
45
|
+
"data": "{\"id\":\"delivery-42\",\"status\":\"accepted\",\"message\":\"Livré · 已送达\"}"
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Isolate HTTP retries to one input item
|
|
2
|
+
|
|
3
|
+
**Two input items. One temporary failure. Three ways to batch.** These fixtures run in real **n8n 2.41.7**, using **HTTP Request v4.2** and **Loop Over Items v3**.
|
|
4
|
+
|
|
5
|
+
Both inputs carry a nested `styling` object. The mocked service responds:
|
|
6
|
+
|
|
7
|
+
- Item **42**: first request `503`, subsequent requests `200` with `{"id":"render-42"}`.
|
|
8
|
+
- Item **43**: every request `200` with `{"id":"render-43"}`.
|
|
9
|
+
|
|
10
|
+
All HTTP nodes have **Retry On Fail** enabled, **Max Tries = 2**, and **Wait Between Tries = 1000 ms**.
|
|
11
|
+
|
|
12
|
+
## Observed results
|
|
13
|
+
|
|
14
|
+
| Workflow | Item 42 requests | Item 43 requests | Total | Final `Done` items |
|
|
15
|
+
|---|---:|---:|---:|---|
|
|
16
|
+
| [Direct multi-item retry](direct.json) | 2 | 2 | **4** | `render-42`, `render-43` |
|
|
17
|
+
| [HTTP Request Batching = 1](http-batching.json) | 2 | 2 | **4** | `render-42`, `render-43` |
|
|
18
|
+
| [Loop Over Items Batch Size = 1](loop.json) | 2 | 1 | **3** | `render-42`, `render-43` |
|
|
19
|
+
|
|
20
|
+
The first two runs observed `42→503, 43→200, 42→200, 43→200`. The loop run observed `42→503, 42→200, 43→200`.
|
|
21
|
+
|
|
22
|
+
HTTP Request's **Options → Batching → Items per Batch = 1**, with **Batch Interval = 1000 ms**, spaces requests within one node execution. Its retry still replays both input items in this test. Loop Over Items supplies one item per HTTP node execution, so retry operates on that one-item input. The completed `43` request is replayed in the first two workflows; the loop requests `43` once.
|
|
23
|
+
|
|
24
|
+
Every case checks exact request counts, full JSON request bodies, and both final output items. The loop case also checks the Loop node's **done output (index 0)**. [Recorded results](observed-results.json) include source hashes, request order and actual output. This is a synthetic HTTP contract; request counts measure transport calls. The fixture's `503` response models a temporary service failure.
|
|
25
|
+
|
|
26
|
+
## Run all three
|
|
27
|
+
|
|
28
|
+
From the repository root, build the pinned runtime and run:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
docker build -t n8n-check .
|
|
32
|
+
for mode in direct http-batching loop; do
|
|
33
|
+
docker run --rm --network none -v "$PWD:/work" n8n-check \
|
|
34
|
+
"examples/retry-isolation/$mode.json" \
|
|
35
|
+
"examples/retry-isolation/$mode-case.json" \
|
|
36
|
+
--out "/work/results/retry-isolation/$mode" || exit "$?"
|
|
37
|
+
done
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
On Linux, add `--user "$(id -u):$(id -g)"` when your user ID differs from the image default. The runner starts its own loopback mock, injects the two case inputs and redirects `Render` to that mock. Every run writes `report.json` and `junit.xml`; the expected exit code is `0`. The repository's [CI](../../.github/workflows/ci.yml) runs these three cases alongside the object-body failure and fix.
|
|
41
|
+
|
|
42
|
+
## Import the workflow into n8n
|
|
43
|
+
|
|
44
|
+
**[Download the annotated editor template](editor-template.json)** for the one-item loop. Its two canvas notes include setup, connections, the exact response sequence and the adaptation contract. It uses built-in n8n nodes and your configured test endpoint.
|
|
45
|
+
|
|
46
|
+
Import `direct.json`, `http-batching.json` or `loop.json` using **Import from File**. Each is a complete workflow with a Manual Trigger, a `Fixture` Code node containing the two synthetic items, and a terminal `Done` node. The `*-case.json` files are inputs to n8n-check.
|
|
47
|
+
|
|
48
|
+
For an editor run, replace `Render`'s placeholder URL with your test service. The service must implement the response sequence above to reproduce the table; n8n-check provides it automatically during CLI runs. The exported URL expression is `https://api.example.com/renders/{{ $json.shortId }}`. Configure any credentials on your test service node as needed.
|
|
49
|
+
|
|
50
|
+
The loop's connections are:
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
Start → Fixture → Loop Over Items (Batch Size = 1)
|
|
54
|
+
├─ loop [output 1] → Render (Retry On Fail)
|
|
55
|
+
│ └─ back to Loop Over Items input
|
|
56
|
+
└─ done [output 0] → Done
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Keep **Reset** off for this fixed input list. Connect the last per-item node back to Loop Over Items, and put work that consumes all results after the **done** output. In this fixture, `Done` runs once with both HTTP response items.
|
|
60
|
+
|
|
61
|
+
## Apply the pattern
|
|
62
|
+
|
|
63
|
+
Use **Loop Over Items with Batch Size = 1** when each item should have its own HTTP-node retry scope. This protects other completed items from replay during a later item's node retry. In the observed input order, item `43` waits until `42` succeeds, then receives one request. Node retry still sends the failing item again: item `42` receives two requests in every case.
|
|
64
|
+
|
|
65
|
+
For writes, pair this scope with **server-side durable idempotency**. Choose a stable operation key, reuse it across retries, and have the receiving service persist the key and outcome consistently with the side effect. If the service accepts a write and its response is lost, the retry can recover the stored result for that same operation. Follow the target API's key scope, retention and concurrency contract. Request pacing, per-item retry scope and server-side duplicate handling each solve a distinct part of delivery.
|
|
66
|
+
|
|
67
|
+
### Upstream references
|
|
68
|
+
|
|
69
|
+
- [HTTP Request batching parameters](https://github.com/n8n-io/n8n-docs/blob/main/docs/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/README.md): items per batch and interval.
|
|
70
|
+
- [Loop Over Items parameters and outputs](https://github.com/n8n-io/n8n-docs/blob/main/docs/integrations/builtin/core-nodes/n8n-nodes-base.splitinbatches.md): saved inputs, per-iteration batches and combined done output.
|
|
71
|
+
- [Pinned HTTP Request implementation](https://github.com/n8n-io/n8n/blob/n8n%402.41.7/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts) and [workflow retry engine](https://github.com/n8n-io/n8n/blob/n8n%402.41.7/packages/core/src/execution-engine/workflow-execute.ts): implementation context for the measured behavior.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"name": "Direct multi-item HTTP retry",
|
|
4
|
+
"input": {
|
|
5
|
+
"node": "Fixture",
|
|
6
|
+
"items": [
|
|
7
|
+
{
|
|
8
|
+
"shortId": 42,
|
|
9
|
+
"styling": {
|
|
10
|
+
"color": "#112233",
|
|
11
|
+
"fontSize": 31
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"shortId": 43,
|
|
16
|
+
"styling": {
|
|
17
|
+
"color": "#aabbcc",
|
|
18
|
+
"fontSize": 47
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"mocks": [
|
|
24
|
+
{
|
|
25
|
+
"node": "Render",
|
|
26
|
+
"url": "/renders/{{ $json.shortId }}",
|
|
27
|
+
"routes": [
|
|
28
|
+
{
|
|
29
|
+
"method": "POST",
|
|
30
|
+
"path": "/renders/42",
|
|
31
|
+
"responses": [
|
|
32
|
+
{
|
|
33
|
+
"status": 503,
|
|
34
|
+
"json": {
|
|
35
|
+
"error": "temporary fixture failure"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"status": 200,
|
|
40
|
+
"json": {
|
|
41
|
+
"id": "render-42"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"expect": {
|
|
46
|
+
"count": 2,
|
|
47
|
+
"bodies": [
|
|
48
|
+
{
|
|
49
|
+
"shortId": 42,
|
|
50
|
+
"renderOptions": {
|
|
51
|
+
"color": "#112233",
|
|
52
|
+
"fontSize": 31
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"shortId": 42,
|
|
57
|
+
"renderOptions": {
|
|
58
|
+
"color": "#112233",
|
|
59
|
+
"fontSize": 31
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"method": "POST",
|
|
67
|
+
"path": "/renders/43",
|
|
68
|
+
"responses": [
|
|
69
|
+
{
|
|
70
|
+
"status": 200,
|
|
71
|
+
"json": {
|
|
72
|
+
"id": "render-43"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"expect": {
|
|
77
|
+
"count": 2,
|
|
78
|
+
"bodies": [
|
|
79
|
+
{
|
|
80
|
+
"shortId": 43,
|
|
81
|
+
"renderOptions": {
|
|
82
|
+
"color": "#aabbcc",
|
|
83
|
+
"fontSize": 47
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"shortId": 43,
|
|
88
|
+
"renderOptions": {
|
|
89
|
+
"color": "#aabbcc",
|
|
90
|
+
"fontSize": 47
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
"assertions": [
|
|
100
|
+
{
|
|
101
|
+
"node": "Done",
|
|
102
|
+
"equals": [
|
|
103
|
+
{
|
|
104
|
+
"id": "render-42"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"id": "render-43"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Direct multi-item HTTP retry",
|
|
3
|
+
"nodes": [
|
|
4
|
+
{
|
|
5
|
+
"id": "start",
|
|
6
|
+
"name": "Start",
|
|
7
|
+
"type": "n8n-nodes-base.manualTrigger",
|
|
8
|
+
"typeVersion": 1,
|
|
9
|
+
"position": [
|
|
10
|
+
-400,
|
|
11
|
+
0
|
|
12
|
+
],
|
|
13
|
+
"parameters": {}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "fixture",
|
|
17
|
+
"name": "Fixture",
|
|
18
|
+
"type": "n8n-nodes-base.code",
|
|
19
|
+
"typeVersion": 2,
|
|
20
|
+
"position": [
|
|
21
|
+
-200,
|
|
22
|
+
0
|
|
23
|
+
],
|
|
24
|
+
"parameters": {
|
|
25
|
+
"jsCode": "return [\n {\n \"shortId\": 42,\n \"styling\": {\n \"color\": \"#112233\",\n \"fontSize\": 31\n }\n },\n {\n \"shortId\": 43,\n \"styling\": {\n \"color\": \"#aabbcc\",\n \"fontSize\": 47\n }\n }\n].map(json => ({ json }));"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "render",
|
|
30
|
+
"name": "Render",
|
|
31
|
+
"type": "n8n-nodes-base.httpRequest",
|
|
32
|
+
"typeVersion": 4.2,
|
|
33
|
+
"position": [
|
|
34
|
+
0,
|
|
35
|
+
0
|
|
36
|
+
],
|
|
37
|
+
"parameters": {
|
|
38
|
+
"method": "POST",
|
|
39
|
+
"url": "=https://api.example.com/renders/{{ $json.shortId }}",
|
|
40
|
+
"sendBody": true,
|
|
41
|
+
"specifyBody": "json",
|
|
42
|
+
"jsonBody": "={{ { shortId: $json.shortId, renderOptions: $json.styling } }}",
|
|
43
|
+
"options": {
|
|
44
|
+
"timeout": 10000
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"retryOnFail": true,
|
|
48
|
+
"maxTries": 2,
|
|
49
|
+
"waitBetweenTries": 1000
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "done",
|
|
53
|
+
"name": "Done",
|
|
54
|
+
"type": "n8n-nodes-base.noOp",
|
|
55
|
+
"typeVersion": 1,
|
|
56
|
+
"position": [
|
|
57
|
+
420,
|
|
58
|
+
0
|
|
59
|
+
],
|
|
60
|
+
"parameters": {}
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
"connections": {
|
|
64
|
+
"Start": {
|
|
65
|
+
"main": [
|
|
66
|
+
[
|
|
67
|
+
{
|
|
68
|
+
"node": "Fixture",
|
|
69
|
+
"type": "main",
|
|
70
|
+
"index": 0
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
"Fixture": {
|
|
76
|
+
"main": [
|
|
77
|
+
[
|
|
78
|
+
{
|
|
79
|
+
"node": "Render",
|
|
80
|
+
"type": "main",
|
|
81
|
+
"index": 0
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
"Render": {
|
|
87
|
+
"main": [
|
|
88
|
+
[
|
|
89
|
+
{
|
|
90
|
+
"node": "Done",
|
|
91
|
+
"type": "main",
|
|
92
|
+
"index": 0
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"settings": {
|
|
99
|
+
"executionOrder": "v1"
|
|
100
|
+
}
|
|
101
|
+
}
|