@aiteza/n8n-nodes-aiteza 0.3.0 → 0.4.0
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/dist/nodes/Aiteza/Aiteza.node.js +404 -313
- package/dist/nodes/Aiteza/AitezaProgress.node.js +31 -41
- package/dist/nodes/Aiteza/AitezaTrigger.node.js +6 -22
- package/dist/nodes/Aiteza/AitezaWorkflowResult.node.js +30 -41
- package/dist/nodes/Aiteza/GenericFunctions.d.ts +0 -5
- package/dist/nodes/Aiteza/GenericFunctions.js +7 -39
- package/package.json +1 -1
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AitezaProgress = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
// Helpers for reading the AITEZA Trigger's webhook body
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
5
|
function isAitezaTriggerType(type) {
|
|
9
6
|
return typeof type === 'string' && /(^|\.)aitezaTrigger$/i.test(type);
|
|
10
7
|
}
|
|
@@ -17,11 +14,6 @@ function findAitezaTriggerParentName(ef) {
|
|
|
17
14
|
return undefined;
|
|
18
15
|
}
|
|
19
16
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Look up a value first on the current input item, then on the original
|
|
22
|
-
* AITEZA Trigger output (so intermediate Set/IF/SplitOut nodes that strip
|
|
23
|
-
* fields don't break callbacks).
|
|
24
|
-
*/
|
|
25
17
|
function readFromTrigger(ef, itemIndex, picker) {
|
|
26
18
|
try {
|
|
27
19
|
const items = ef.getInputData();
|
|
@@ -65,9 +57,6 @@ function readTriggerBodyField(ef, itemIndex, field) {
|
|
|
65
57
|
return undefined;
|
|
66
58
|
});
|
|
67
59
|
}
|
|
68
|
-
// ---------------------------------------------------------------------------
|
|
69
|
-
// Node
|
|
70
|
-
// ---------------------------------------------------------------------------
|
|
71
60
|
class AitezaProgress {
|
|
72
61
|
description = {
|
|
73
62
|
displayName: 'AITEZA Progress',
|
|
@@ -76,10 +65,7 @@ class AitezaProgress {
|
|
|
76
65
|
group: ['transform'],
|
|
77
66
|
version: 1,
|
|
78
67
|
subtitle: '={{$parameter["step"]}}',
|
|
79
|
-
description: 'Reports workflow progress back to the
|
|
80
|
-
'this workflow. Reads callbackUrl / executionId / callbackToken from ' +
|
|
81
|
-
'the upstream AITEZA Trigger webhook body and POSTs to ' +
|
|
82
|
-
'{callbackUrl}/api/internal/workflow/{executionId}/progress.',
|
|
68
|
+
description: 'Reports workflow progress back to Aiteza while the workflow is running. Reads the callback details from the upstream Aiteza Trigger node automatically.',
|
|
83
69
|
defaults: { name: 'AITEZA Progress' },
|
|
84
70
|
inputs: ['main'],
|
|
85
71
|
outputs: ['main'],
|
|
@@ -91,8 +77,8 @@ class AitezaProgress {
|
|
|
91
77
|
type: 'string',
|
|
92
78
|
default: '',
|
|
93
79
|
required: true,
|
|
94
|
-
placeholder: 'Fetching files',
|
|
95
|
-
description: 'Short
|
|
80
|
+
placeholder: 'e.g. Fetching files',
|
|
81
|
+
description: 'Short label describing the current step shown to the user',
|
|
96
82
|
},
|
|
97
83
|
{
|
|
98
84
|
displayName: 'Step Index',
|
|
@@ -100,7 +86,7 @@ class AitezaProgress {
|
|
|
100
86
|
type: 'number',
|
|
101
87
|
default: 1,
|
|
102
88
|
typeOptions: { minValue: 0 },
|
|
103
|
-
description: '
|
|
89
|
+
description: 'Position of the current step (1-based)',
|
|
104
90
|
},
|
|
105
91
|
{
|
|
106
92
|
displayName: 'Total Steps',
|
|
@@ -116,7 +102,8 @@ class AitezaProgress {
|
|
|
116
102
|
type: 'string',
|
|
117
103
|
default: '',
|
|
118
104
|
typeOptions: { rows: 2 },
|
|
119
|
-
|
|
105
|
+
placeholder: 'e.g. Processing 5 of 20 documents',
|
|
106
|
+
description: 'Optional longer description of what is currently happening',
|
|
120
107
|
},
|
|
121
108
|
{
|
|
122
109
|
displayName: 'Additional Fields (JSON)',
|
|
@@ -124,7 +111,7 @@ class AitezaProgress {
|
|
|
124
111
|
type: 'json',
|
|
125
112
|
default: '',
|
|
126
113
|
placeholder: '{ "percent": 42 }',
|
|
127
|
-
description: 'Optional JSON object
|
|
114
|
+
description: 'Optional JSON object merged into the progress payload (e.g. percent complete, items processed)',
|
|
128
115
|
},
|
|
129
116
|
{
|
|
130
117
|
displayName: 'Options',
|
|
@@ -133,27 +120,36 @@ class AitezaProgress {
|
|
|
133
120
|
placeholder: 'Add Option',
|
|
134
121
|
default: {},
|
|
135
122
|
options: [
|
|
123
|
+
{
|
|
124
|
+
displayName: 'Callback Token Override',
|
|
125
|
+
name: 'callbackTokenOverride',
|
|
126
|
+
type: 'string',
|
|
127
|
+
typeOptions: { password: true },
|
|
128
|
+
default: '',
|
|
129
|
+
description: 'Override the callback token read from the Aiteza Trigger',
|
|
130
|
+
},
|
|
136
131
|
{
|
|
137
132
|
displayName: 'Callback URL Override',
|
|
138
133
|
name: 'callbackUrlOverride',
|
|
139
134
|
type: 'string',
|
|
140
135
|
default: '',
|
|
141
|
-
|
|
136
|
+
placeholder: 'e.g. https://aiteza.example.com',
|
|
137
|
+
description: 'Override the callback URL read from the Aiteza Trigger (without trailing slash)',
|
|
142
138
|
},
|
|
143
139
|
{
|
|
144
140
|
displayName: 'Execution ID Override',
|
|
145
141
|
name: 'executionIdOverride',
|
|
146
142
|
type: 'string',
|
|
147
143
|
default: '',
|
|
148
|
-
|
|
144
|
+
placeholder: 'e.g. exec-abc123',
|
|
145
|
+
description: 'Override the execution ID read from the Aiteza Trigger',
|
|
149
146
|
},
|
|
150
147
|
{
|
|
151
|
-
displayName: 'Callback
|
|
152
|
-
name: '
|
|
153
|
-
type: '
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
description: 'Override the callback token from the AITEZA Trigger',
|
|
148
|
+
displayName: 'Fail Workflow on Callback Failure',
|
|
149
|
+
name: 'failOnError',
|
|
150
|
+
type: 'boolean',
|
|
151
|
+
default: false,
|
|
152
|
+
description: 'Whether to stop the workflow if the progress callback cannot be delivered. Defaults to false so a failed report never blocks the main flow.',
|
|
157
153
|
},
|
|
158
154
|
{
|
|
159
155
|
displayName: 'Timeout (ms)',
|
|
@@ -161,13 +157,7 @@ class AitezaProgress {
|
|
|
161
157
|
type: 'number',
|
|
162
158
|
default: 5000,
|
|
163
159
|
typeOptions: { minValue: 100 },
|
|
164
|
-
|
|
165
|
-
{
|
|
166
|
-
displayName: 'Fail Workflow on Error',
|
|
167
|
-
name: 'failOnError',
|
|
168
|
-
type: 'boolean',
|
|
169
|
-
default: false,
|
|
170
|
-
description: 'Whether to fail the workflow if the progress callback fails. Defaults to false so progress reports never break the main flow.',
|
|
160
|
+
description: 'Maximum time in milliseconds to wait for the callback to complete',
|
|
171
161
|
},
|
|
172
162
|
],
|
|
173
163
|
},
|
|
@@ -207,7 +197,7 @@ class AitezaProgress {
|
|
|
207
197
|
}
|
|
208
198
|
catch (err) {
|
|
209
199
|
if (failOnError) {
|
|
210
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `
|
|
200
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The 'Additional Fields' value is not valid JSON: ${err.message}`, { itemIndex: i, description: 'Please provide a valid JSON object in the \'Additional Fields\' parameter, e.g. { "percent": 42 }.' });
|
|
211
201
|
}
|
|
212
202
|
}
|
|
213
203
|
}
|
|
@@ -226,10 +216,10 @@ class AitezaProgress {
|
|
|
226
216
|
if (!callbackUrl || !executionId) {
|
|
227
217
|
reportMeta.skipped = true;
|
|
228
218
|
reportMeta.reason = !callbackUrl
|
|
229
|
-
? 'No callbackUrl on
|
|
230
|
-
: 'No executionId on
|
|
219
|
+
? 'No callbackUrl on Aiteza Trigger body'
|
|
220
|
+
: 'No executionId on Aiteza Trigger body';
|
|
231
221
|
if (failOnError) {
|
|
232
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Cannot send progress: ${reportMeta.reason}`, { itemIndex: i });
|
|
222
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Cannot send progress report: ${reportMeta.reason}`, { itemIndex: i, description: 'Make sure this node is downstream of an Aiteza Trigger node that provides \'callbackUrl\' and \'executionId\' in its body.' });
|
|
233
223
|
}
|
|
234
224
|
returnData.push({
|
|
235
225
|
json: { ...items[i].json, _progressReport: reportMeta },
|
|
@@ -255,9 +245,9 @@ class AitezaProgress {
|
|
|
255
245
|
catch (error) {
|
|
256
246
|
reportMeta.sent = false;
|
|
257
247
|
reportMeta.error =
|
|
258
|
-
error?.message ?? error?.response?.statusText ?? '
|
|
248
|
+
error?.message ?? error?.response?.statusText ?? 'The callback could not be delivered';
|
|
259
249
|
if (failOnError) {
|
|
260
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Progress callback
|
|
250
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Progress callback could not be delivered: ${reportMeta.error}`, { itemIndex: i, description: 'Check that the \'callbackUrl\' is reachable and the callback token is valid.' });
|
|
261
251
|
}
|
|
262
252
|
}
|
|
263
253
|
returnData.push({
|
|
@@ -8,16 +8,10 @@ class AitezaTrigger {
|
|
|
8
8
|
icon: 'file:aiteza.svg',
|
|
9
9
|
group: ['trigger'],
|
|
10
10
|
version: 1,
|
|
11
|
-
description: 'Starts the workflow when an
|
|
12
|
-
'Forwards the calling user\'s bearer token (from body.user.bearerToken) to downstream AITEZA nodes ' +
|
|
13
|
-
'so they act on behalf of the triggering user. Falls back to the request\'s Authorization header if no user token is present.',
|
|
11
|
+
description: 'Starts the workflow when an Aiteza event is received via webhook. Forwards the calling user\'s token to downstream Aiteza nodes so they act on behalf of the triggering user.',
|
|
14
12
|
defaults: { name: 'AITEZA Trigger' },
|
|
15
13
|
inputs: [],
|
|
16
14
|
outputs: ['main'],
|
|
17
|
-
// No credentials required – the trigger just receives webhooks.
|
|
18
|
-
// Downstream AITEZA nodes still need a base URL (taken from this trigger
|
|
19
|
-
// or from configured credentials) and will use the token from this
|
|
20
|
-
// trigger for actual authentication.
|
|
21
15
|
credentials: [],
|
|
22
16
|
webhooks: [
|
|
23
17
|
{
|
|
@@ -35,7 +29,7 @@ class AitezaTrigger {
|
|
|
35
29
|
name: 'path',
|
|
36
30
|
type: 'string',
|
|
37
31
|
default: 'aiteza',
|
|
38
|
-
placeholder: 'aiteza',
|
|
32
|
+
placeholder: 'e.g. aiteza',
|
|
39
33
|
required: true,
|
|
40
34
|
description: 'The webhook path to listen on. The full URL will be /webhook/{path}.',
|
|
41
35
|
},
|
|
@@ -48,8 +42,7 @@ class AitezaTrigger {
|
|
|
48
42
|
{
|
|
49
43
|
name: 'Immediately',
|
|
50
44
|
value: 'onReceived',
|
|
51
|
-
description: 'Respond immediately with 200 OK as soon as the webhook is received. '
|
|
52
|
-
'Use the "AITEZA Workflow Result" node at the end of your workflow to send results back.',
|
|
45
|
+
description: 'Respond immediately with 200 OK as soon as the webhook is received. Use the Aiteza Workflow Result node at the end of your workflow to send results back.',
|
|
53
46
|
},
|
|
54
47
|
{
|
|
55
48
|
name: 'When Last Node Finishes',
|
|
@@ -58,16 +51,15 @@ class AitezaTrigger {
|
|
|
58
51
|
},
|
|
59
52
|
],
|
|
60
53
|
default: 'onReceived',
|
|
61
|
-
description: 'When and how to respond to the webhook. Use "Immediately" together with the '
|
|
62
|
-
'"AITEZA Workflow Result" node to send results asynchronously.',
|
|
54
|
+
description: 'When and how to respond to the webhook. Use "Immediately" together with the Aiteza Workflow Result node to send results asynchronously.',
|
|
63
55
|
},
|
|
64
56
|
{
|
|
65
57
|
displayName: 'AITEZA Base URL',
|
|
66
58
|
name: 'baseUrl',
|
|
67
59
|
type: 'string',
|
|
68
60
|
default: '',
|
|
69
|
-
placeholder: 'https://aiteza.example.com',
|
|
70
|
-
description: 'Optional. If set, this URL is forwarded to downstream
|
|
61
|
+
placeholder: 'e.g. https://aiteza.example.com',
|
|
62
|
+
description: 'Optional. If set, this URL is forwarded to downstream Aiteza nodes as \'_baseUrl\' so they don\'t need to repeat it. Without trailing slash.',
|
|
71
63
|
},
|
|
72
64
|
],
|
|
73
65
|
};
|
|
@@ -75,16 +67,10 @@ class AitezaTrigger {
|
|
|
75
67
|
const body = this.getBodyData();
|
|
76
68
|
const headers = this.getHeaderData();
|
|
77
69
|
const query = this.getQueryData();
|
|
78
|
-
// AITEZA places the calling user's bearer token inside the body (body.user.bearerToken)
|
|
79
|
-
// so that downstream nodes can act on behalf of the user. The request's own
|
|
80
|
-
// Authorization header is typically the service-account token used by AITEZA to
|
|
81
|
-
// call the webhook itself – not what we want for delegated calls.
|
|
82
70
|
const user = body?.user ?? {};
|
|
83
71
|
const userBearer = user.bearerToken ?? '';
|
|
84
72
|
const authHeader = headers.authorization || headers.Authorization || '';
|
|
85
73
|
const headerToken = authHeader.replace(/^Bearer\s+/i, '');
|
|
86
|
-
// Prefer the user-context token. Fall back to the header token (e.g. when the
|
|
87
|
-
// webhook is called by something other than AITEZA).
|
|
88
74
|
const authToken = userBearer || headerToken;
|
|
89
75
|
const baseUrl = this.getNodeParameter('baseUrl', '').replace(/\/+$/, '');
|
|
90
76
|
const outputData = {
|
|
@@ -94,8 +80,6 @@ class AitezaTrigger {
|
|
|
94
80
|
};
|
|
95
81
|
if (authToken)
|
|
96
82
|
outputData._authToken = authToken;
|
|
97
|
-
// Expose the service-account token separately in case a workflow explicitly
|
|
98
|
-
// needs to call AITEZA with elevated/service privileges.
|
|
99
83
|
if (headerToken && headerToken !== authToken)
|
|
100
84
|
outputData._serviceAuthToken = headerToken;
|
|
101
85
|
if (baseUrl)
|
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AitezaWorkflowResult = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
// Helpers for reading the AITEZA Trigger's webhook body
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
5
|
function isAitezaTriggerType(type) {
|
|
9
6
|
return typeof type === 'string' && /(^|\.)aitezaTrigger$/i.test(type);
|
|
10
7
|
}
|
|
@@ -17,11 +14,6 @@ function findAitezaTriggerParentName(ef) {
|
|
|
17
14
|
return undefined;
|
|
18
15
|
}
|
|
19
16
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Look up a value first on the current input item, then on the original
|
|
22
|
-
* AITEZA Trigger output (so intermediate Set/IF/SplitOut nodes that strip
|
|
23
|
-
* fields don't break callbacks).
|
|
24
|
-
*/
|
|
25
17
|
function readFromTrigger(ef, itemIndex, picker) {
|
|
26
18
|
try {
|
|
27
19
|
const items = ef.getInputData();
|
|
@@ -63,9 +55,6 @@ function readTriggerBodyField(ef, itemIndex, field) {
|
|
|
63
55
|
return undefined;
|
|
64
56
|
});
|
|
65
57
|
}
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
|
-
// Node
|
|
68
|
-
// ---------------------------------------------------------------------------
|
|
69
58
|
class AitezaWorkflowResult {
|
|
70
59
|
description = {
|
|
71
60
|
displayName: 'AITEZA Workflow Result',
|
|
@@ -74,12 +63,7 @@ class AitezaWorkflowResult {
|
|
|
74
63
|
group: ['output'],
|
|
75
64
|
version: 1,
|
|
76
65
|
subtitle: 'Send result to AITEZA',
|
|
77
|
-
description: 'Sends the final workflow result back to
|
|
78
|
-
'Reads callbackUrl / executionId / callbackToken from the upstream ' +
|
|
79
|
-
'AITEZA Trigger webhook body and POSTs to ' +
|
|
80
|
-
'{callbackUrl}/api/internal/workflow/{executionId}/result. ' +
|
|
81
|
-
'Use this instead of "Respond to Webhook" which is not compatible ' +
|
|
82
|
-
'with custom trigger nodes.',
|
|
66
|
+
description: 'Sends the final workflow result back to Aiteza. Use this node instead of "Respond to Webhook" when your workflow is triggered by an Aiteza Trigger node.',
|
|
83
67
|
defaults: { name: 'AITEZA Workflow Result' },
|
|
84
68
|
inputs: ['main'],
|
|
85
69
|
outputs: ['main'],
|
|
@@ -91,7 +75,7 @@ class AitezaWorkflowResult {
|
|
|
91
75
|
type: 'json',
|
|
92
76
|
default: '',
|
|
93
77
|
placeholder: '={{ $json }}',
|
|
94
|
-
description: 'The result payload to send back to
|
|
78
|
+
description: 'The result payload to send back to Aiteza. Can be a JSON object or an expression. If left empty, the entire input item is sent.',
|
|
95
79
|
},
|
|
96
80
|
{
|
|
97
81
|
displayName: 'Status',
|
|
@@ -101,21 +85,23 @@ class AitezaWorkflowResult {
|
|
|
101
85
|
{
|
|
102
86
|
name: 'Success',
|
|
103
87
|
value: 'success',
|
|
88
|
+
description: 'The workflow completed successfully',
|
|
104
89
|
},
|
|
105
90
|
{
|
|
106
91
|
name: 'Error',
|
|
107
92
|
value: 'error',
|
|
93
|
+
description: 'The workflow encountered an issue',
|
|
108
94
|
},
|
|
109
95
|
],
|
|
110
96
|
default: 'success',
|
|
111
|
-
description: 'The result status to report back to
|
|
97
|
+
description: 'The result status to report back to Aiteza',
|
|
112
98
|
},
|
|
113
99
|
{
|
|
114
100
|
displayName: 'Message',
|
|
115
101
|
name: 'message',
|
|
116
102
|
type: 'string',
|
|
117
103
|
default: '',
|
|
118
|
-
placeholder: 'Workflow completed successfully',
|
|
104
|
+
placeholder: 'e.g. Workflow completed successfully',
|
|
119
105
|
description: 'Optional human-readable message to include with the result',
|
|
120
106
|
},
|
|
121
107
|
{
|
|
@@ -125,27 +111,36 @@ class AitezaWorkflowResult {
|
|
|
125
111
|
placeholder: 'Add Option',
|
|
126
112
|
default: {},
|
|
127
113
|
options: [
|
|
114
|
+
{
|
|
115
|
+
displayName: 'Callback Token Override',
|
|
116
|
+
name: 'callbackTokenOverride',
|
|
117
|
+
type: 'string',
|
|
118
|
+
typeOptions: { password: true },
|
|
119
|
+
default: '',
|
|
120
|
+
description: 'Override the callback token read from the Aiteza Trigger',
|
|
121
|
+
},
|
|
128
122
|
{
|
|
129
123
|
displayName: 'Callback URL Override',
|
|
130
124
|
name: 'callbackUrlOverride',
|
|
131
125
|
type: 'string',
|
|
132
126
|
default: '',
|
|
133
|
-
|
|
127
|
+
placeholder: 'e.g. https://aiteza.example.com',
|
|
128
|
+
description: 'Override the callback URL read from the Aiteza Trigger (without trailing slash)',
|
|
134
129
|
},
|
|
135
130
|
{
|
|
136
131
|
displayName: 'Execution ID Override',
|
|
137
132
|
name: 'executionIdOverride',
|
|
138
133
|
type: 'string',
|
|
139
134
|
default: '',
|
|
140
|
-
|
|
135
|
+
placeholder: 'e.g. exec-abc123',
|
|
136
|
+
description: 'Override the execution ID read from the Aiteza Trigger',
|
|
141
137
|
},
|
|
142
138
|
{
|
|
143
|
-
displayName: 'Callback
|
|
144
|
-
name: '
|
|
145
|
-
type: '
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
description: 'Override the callback token from the AITEZA Trigger',
|
|
139
|
+
displayName: 'Fail Workflow on Callback Failure',
|
|
140
|
+
name: 'failOnError',
|
|
141
|
+
type: 'boolean',
|
|
142
|
+
default: true,
|
|
143
|
+
description: 'Whether to stop the workflow if the result callback cannot be delivered. Defaults to true since the result is typically critical.',
|
|
149
144
|
},
|
|
150
145
|
{
|
|
151
146
|
displayName: 'Timeout (ms)',
|
|
@@ -153,13 +148,7 @@ class AitezaWorkflowResult {
|
|
|
153
148
|
type: 'number',
|
|
154
149
|
default: 10000,
|
|
155
150
|
typeOptions: { minValue: 100 },
|
|
156
|
-
|
|
157
|
-
{
|
|
158
|
-
displayName: 'Fail Workflow on Error',
|
|
159
|
-
name: 'failOnError',
|
|
160
|
-
type: 'boolean',
|
|
161
|
-
default: true,
|
|
162
|
-
description: 'Whether to fail the workflow if the result callback fails. Defaults to true since the result is typically critical.',
|
|
151
|
+
description: 'Maximum time in milliseconds to wait for the callback to complete',
|
|
163
152
|
},
|
|
164
153
|
],
|
|
165
154
|
},
|
|
@@ -193,7 +182,7 @@ class AitezaWorkflowResult {
|
|
|
193
182
|
}
|
|
194
183
|
catch (err) {
|
|
195
184
|
if (failOnError) {
|
|
196
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `
|
|
185
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The 'Result Data' value is not valid JSON: ${err.message}`, { itemIndex: i, description: 'Please provide a valid JSON object in the \'Result Data\' field, or use an expression like ={{ $json }}.' });
|
|
197
186
|
}
|
|
198
187
|
resultPayload = { raw: resultDataRaw };
|
|
199
188
|
}
|
|
@@ -216,10 +205,10 @@ class AitezaWorkflowResult {
|
|
|
216
205
|
if (!callbackUrl || !executionId) {
|
|
217
206
|
reportMeta.skipped = true;
|
|
218
207
|
reportMeta.reason = !callbackUrl
|
|
219
|
-
? 'No callbackUrl on
|
|
220
|
-
: 'No executionId on
|
|
208
|
+
? 'No callbackUrl on Aiteza Trigger body'
|
|
209
|
+
: 'No executionId on Aiteza Trigger body';
|
|
221
210
|
if (failOnError) {
|
|
222
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Cannot send workflow result: ${reportMeta.reason}`, { itemIndex: i });
|
|
211
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Cannot send workflow result: ${reportMeta.reason}`, { itemIndex: i, description: 'Make sure this node is downstream of an Aiteza Trigger node that provides \'callbackUrl\' and \'executionId\' in its body.' });
|
|
223
212
|
}
|
|
224
213
|
returnData.push({
|
|
225
214
|
json: { ...items[i].json, _workflowResult: reportMeta },
|
|
@@ -245,9 +234,9 @@ class AitezaWorkflowResult {
|
|
|
245
234
|
catch (error) {
|
|
246
235
|
reportMeta.sent = false;
|
|
247
236
|
reportMeta.error =
|
|
248
|
-
error?.message ?? error?.response?.statusText ?? '
|
|
237
|
+
error?.message ?? error?.response?.statusText ?? 'The callback could not be delivered';
|
|
249
238
|
if (failOnError) {
|
|
250
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Workflow result
|
|
239
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Workflow result could not be delivered: ${reportMeta.error}`, { itemIndex: i, description: 'Check that the \'callbackUrl\' is reachable and the callback token is valid.' });
|
|
251
240
|
}
|
|
252
241
|
}
|
|
253
242
|
returnData.push({
|
|
@@ -5,11 +5,6 @@ export interface AitezaAuthContext {
|
|
|
5
5
|
}
|
|
6
6
|
export declare function getUpstreamAuthToken(ef: IExecuteFunctions): string | undefined;
|
|
7
7
|
export declare function getUpstreamBaseUrl(ef: IExecuteFunctions): string | undefined;
|
|
8
|
-
/**
|
|
9
|
-
* Returns true when the token is a JWT with an `exp` claim that is in the past
|
|
10
|
-
* (with a small skew). Returns false when the token is not a parsable JWT or
|
|
11
|
-
* has no `exp` (we assume valid in that case – the server is the source of truth).
|
|
12
|
-
*/
|
|
13
8
|
export declare function isJwtExpired(token: string | undefined, skewSeconds?: number): boolean;
|
|
14
9
|
export declare function aitezaApiRequest(this: IExecuteFunctions | ILoadOptionsFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject | IDataObject[], qs?: IDataObject, extraOpts?: Partial<IHttpRequestOptions>, authCtx?: AitezaAuthContext): Promise<any>;
|
|
15
10
|
export declare function aitezaApiRequestFullResponse(this: IExecuteFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject, qs?: IDataObject, extraOpts?: Partial<IHttpRequestOptions>, authCtx?: AitezaAuthContext): Promise<{
|
|
@@ -16,9 +16,6 @@ exports.loadStandaloneFiles = loadStandaloneFiles;
|
|
|
16
16
|
exports.loadStandaloneImages = loadStandaloneImages;
|
|
17
17
|
exports.validateRequiredField = validateRequiredField;
|
|
18
18
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
19
|
-
// Match the AITEZA Trigger node by its type name. The full node-type string
|
|
20
|
-
// includes the package namespace (e.g. "@aiteza/n8n-nodes-aiteza.aitezaTrigger"),
|
|
21
|
-
// so we match on the suffix.
|
|
22
19
|
function isAitezaTriggerType(type) {
|
|
23
20
|
return typeof type === 'string' && /(^|\.)aitezaTrigger$/i.test(type);
|
|
24
21
|
}
|
|
@@ -47,13 +44,10 @@ function readFromTriggerItems(ef, picker) {
|
|
|
47
44
|
}
|
|
48
45
|
}
|
|
49
46
|
catch {
|
|
50
|
-
// proxy/items unavailable – ignore
|
|
51
47
|
}
|
|
52
48
|
return undefined;
|
|
53
49
|
}
|
|
54
50
|
function getUpstreamAuthToken(ef) {
|
|
55
|
-
// 1. Prefer the token on the immediate input items (cheap and matches the
|
|
56
|
-
// documented contract of forwarding `_authToken`).
|
|
57
51
|
try {
|
|
58
52
|
const items = ef.getInputData();
|
|
59
53
|
for (const item of items) {
|
|
@@ -63,11 +57,7 @@ function getUpstreamAuthToken(ef) {
|
|
|
63
57
|
}
|
|
64
58
|
}
|
|
65
59
|
catch {
|
|
66
|
-
// getInputData may not be available in every context
|
|
67
60
|
}
|
|
68
|
-
// 2. Fall back to the AITEZA Trigger node's original output. This makes the
|
|
69
|
-
// node resilient to intermediate transformations (Set/Edit Fields, Split
|
|
70
|
-
// Out, IF, etc.) that strip the `_authToken` field from items.
|
|
71
61
|
return readFromTriggerItems(ef, (json) => json?._authToken);
|
|
72
62
|
}
|
|
73
63
|
function getUpstreamBaseUrl(ef) {
|
|
@@ -80,14 +70,11 @@ function getUpstreamBaseUrl(ef) {
|
|
|
80
70
|
}
|
|
81
71
|
}
|
|
82
72
|
catch {
|
|
83
|
-
// ignore
|
|
84
73
|
}
|
|
85
74
|
const fromTrigger = readFromTriggerItems(ef, (json) => json?._baseUrl);
|
|
86
75
|
return fromTrigger ? fromTrigger.replace(/\/+$/, '') : undefined;
|
|
87
76
|
}
|
|
88
|
-
// ---------------------------------------------------------------------------
|
|
89
77
|
// JWT helpers
|
|
90
|
-
// ---------------------------------------------------------------------------
|
|
91
78
|
function decodeJwtPayload(token) {
|
|
92
79
|
try {
|
|
93
80
|
const payload = token.split('.')[1];
|
|
@@ -95,9 +82,6 @@ function decodeJwtPayload(token) {
|
|
|
95
82
|
return undefined;
|
|
96
83
|
const normalized = payload.replace(/-/g, '+').replace(/_/g, '/');
|
|
97
84
|
const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4);
|
|
98
|
-
// atob is available in Node 16+ and the browser; decodes base64 to a
|
|
99
|
-
// binary string. JWT payloads are UTF-8 JSON; for the `exp` claim a
|
|
100
|
-
// binary-string decode is sufficient.
|
|
101
85
|
const decoded = globalThis.atob
|
|
102
86
|
? globalThis.atob(padded)
|
|
103
87
|
: '';
|
|
@@ -109,11 +93,6 @@ function decodeJwtPayload(token) {
|
|
|
109
93
|
return undefined;
|
|
110
94
|
}
|
|
111
95
|
}
|
|
112
|
-
/**
|
|
113
|
-
* Returns true when the token is a JWT with an `exp` claim that is in the past
|
|
114
|
-
* (with a small skew). Returns false when the token is not a parsable JWT or
|
|
115
|
-
* has no `exp` (we assume valid in that case – the server is the source of truth).
|
|
116
|
-
*/
|
|
117
96
|
function isJwtExpired(token, skewSeconds = 30) {
|
|
118
97
|
if (!token)
|
|
119
98
|
return true;
|
|
@@ -123,9 +102,7 @@ function isJwtExpired(token, skewSeconds = 30) {
|
|
|
123
102
|
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
124
103
|
return payload.exp <= nowSeconds + skewSeconds;
|
|
125
104
|
}
|
|
126
|
-
// ---------------------------------------------------------------------------
|
|
127
105
|
// Internal helpers
|
|
128
|
-
// ---------------------------------------------------------------------------
|
|
129
106
|
async function resolveBaseUrl(ctx, authCtx) {
|
|
130
107
|
if (authCtx?.baseUrl)
|
|
131
108
|
return authCtx.baseUrl;
|
|
@@ -142,20 +119,16 @@ async function executeRequest(ctx, options, authCtx) {
|
|
|
142
119
|
}
|
|
143
120
|
return ctx.helpers.httpRequestWithAuthentication.call(ctx, 'aitezaOAuth2Api', options);
|
|
144
121
|
}
|
|
145
|
-
/** Convert API response items to n8n dropdown options. */
|
|
146
122
|
function toOptions(items, nameField = 'name') {
|
|
147
123
|
return items.map((item) => ({
|
|
148
124
|
name: item[nameField] ?? item.id,
|
|
149
125
|
value: item.id,
|
|
150
126
|
}));
|
|
151
127
|
}
|
|
152
|
-
/** Extract array from API response (handles both raw arrays and paginated `{ content: [...] }`). */
|
|
153
128
|
function extractItems(data) {
|
|
154
129
|
return Array.isArray(data) ? data : data?.content ?? [];
|
|
155
130
|
}
|
|
156
|
-
// ---------------------------------------------------------------------------
|
|
157
131
|
// Generic authenticated request helper
|
|
158
|
-
// ---------------------------------------------------------------------------
|
|
159
132
|
async function aitezaApiRequest(method, endpoint, body = {}, qs = {}, extraOpts = {}, authCtx) {
|
|
160
133
|
const baseUrl = await resolveBaseUrl(this, authCtx);
|
|
161
134
|
const options = {
|
|
@@ -164,7 +137,6 @@ async function aitezaApiRequest(method, endpoint, body = {}, qs = {}, extraOpts
|
|
|
164
137
|
json: true,
|
|
165
138
|
...extraOpts,
|
|
166
139
|
};
|
|
167
|
-
// Only set qs when there are actual values – empty qs can strip URL params.
|
|
168
140
|
if (Object.keys(qs).length > 0) {
|
|
169
141
|
options.qs = qs;
|
|
170
142
|
}
|
|
@@ -180,18 +152,16 @@ async function aitezaApiRequest(method, endpoint, body = {}, qs = {}, extraOpts
|
|
|
180
152
|
catch (error) {
|
|
181
153
|
const statusCode = error?.statusCode ?? error?.httpCode ?? error?.response?.status;
|
|
182
154
|
const messages = {
|
|
183
|
-
401: 'OAuth2 token expired or invalid – check your
|
|
184
|
-
403: '
|
|
185
|
-
404: '
|
|
155
|
+
401: 'The OAuth2 token is expired or invalid – check your Aiteza credentials',
|
|
156
|
+
403: 'Your account does not have permission to access this resource',
|
|
157
|
+
404: 'The requested resource could not be found',
|
|
186
158
|
};
|
|
187
159
|
throw new n8n_workflow_1.NodeApiError(this.getNode(), error, {
|
|
188
|
-
message: messages[statusCode] ?? error.message ?? '
|
|
160
|
+
message: messages[statusCode] ?? error.message ?? 'The request could not be completed',
|
|
189
161
|
});
|
|
190
162
|
}
|
|
191
163
|
}
|
|
192
|
-
// ---------------------------------------------------------------------------
|
|
193
164
|
// Full-response variant (gives access to response headers, e.g. X-Chat-Id)
|
|
194
|
-
// ---------------------------------------------------------------------------
|
|
195
165
|
async function aitezaApiRequestFullResponse(method, endpoint, body = {}, qs = {}, extraOpts = {}, authCtx) {
|
|
196
166
|
const baseUrl = await resolveBaseUrl(this, authCtx);
|
|
197
167
|
const options = {
|
|
@@ -212,9 +182,7 @@ async function aitezaApiRequestFullResponse(method, endpoint, body = {}, qs = {}
|
|
|
212
182
|
throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
|
|
213
183
|
}
|
|
214
184
|
}
|
|
215
|
-
// ---------------------------------------------------------------------------
|
|
216
185
|
// loadOptions helpers
|
|
217
|
-
// ---------------------------------------------------------------------------
|
|
218
186
|
async function loadDatarooms() {
|
|
219
187
|
try {
|
|
220
188
|
const data = await aitezaApiRequest.call(this, 'GET', '/api/dataroom/search?q=&sortBy=recentlyUsed');
|
|
@@ -271,11 +239,11 @@ async function loadStandaloneImages() {
|
|
|
271
239
|
const data = await aitezaApiRequest.call(this, 'GET', '/api/images', {}, { size: 100 });
|
|
272
240
|
return toOptions(extractItems(data));
|
|
273
241
|
}
|
|
274
|
-
// ---------------------------------------------------------------------------
|
|
275
242
|
// Validation helper
|
|
276
|
-
// ---------------------------------------------------------------------------
|
|
277
243
|
function validateRequiredField(ef, value, fieldName) {
|
|
278
244
|
if (value === undefined || value === null || value === '') {
|
|
279
|
-
throw new n8n_workflow_1.NodeOperationError(ef.getNode(), `
|
|
245
|
+
throw new n8n_workflow_1.NodeOperationError(ef.getNode(), `The '${fieldName}' field is required but was not provided.`, {
|
|
246
|
+
description: `Please fill in the '${fieldName}' field before running this node.`,
|
|
247
|
+
});
|
|
280
248
|
}
|
|
281
249
|
}
|