n8n-nodes-mat-framework 1.0.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/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ LICENSE
2
+ Copyright © 2026 Iquall Networks.
3
+ All rights reserved.
4
+
5
+ This software and associated documentation files (the “Software”) are proprietary to Iquall Networks. Permission is granted solely to install and use the Software for integration with a valid and properly licensed MAT platform instance. Any use beyond the scope expressly permitted herein is strictly prohibited.
6
+
7
+ The following actions are strictly prohibited:
8
+ Modify, adapt, translate, reverse engineer, decompile, or disassemble the Software.
9
+
10
+ Create derivative works based on the Software.
11
+
12
+ Redistribute, sublicense, rent, lease, sell, or otherwise transfer the Software.
13
+
14
+ Publish the Software or make it publicly available without explicit written authorization from Iquall Networks.
15
+
16
+ Remove, alter, or obscure any proprietary notices.
17
+ The Software is licensed, not sold. All intellectual property rights in and to the Software remain the exclusive property of Iquall Networks. Any unauthorized use automatically terminates this license.
18
+
19
+ The Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to merchantability, fitness for a particular purpose, and non-infringement. In no event shall Iquall Networks be liable for any claim, damages, or other liability arising from or in connection with the Software or its use.
package/README.md ADDED
@@ -0,0 +1,86 @@
1
+
2
+ # MAT Framework n8n Community Node
3
+ This repository provides the official community node that integrates MAT Framework with n8n. The node enables n8n workflows to securely trigger and monitor network-impacting automations executed inside MAT Framework.
4
+
5
+ All execution logic, governance, policy enforcement, and auditing are handled entirely within MAT.
6
+
7
+ ## Documentation
8
+ Full documentation, configuration guides, compatibility notes, and release information are available at:
9
+
10
+ https://iquall.net/mat-n8n/
11
+
12
+ For production deployments, always refer to the official documentation page above. This repository does not duplicate or maintain detailed usage documentation.
13
+
14
+ ## Overview
15
+
16
+ The node acts as a thin client over MAT’s northbound API layer. It does not execute automation logic itself. All execution, governance, policy enforcement, and auditing are handled by MAT.
17
+
18
+ High-level flow:
19
+ n8n Workflow
20
+
21
+ MAT Community Node
22
+
23
+ MAT Framework API
24
+
25
+ Network / OSS / BSS Domains
26
+
27
+ **What this Node does:**
28
+ - Triggers MAT automations or Bricks
29
+ - Pass structured input parameters
30
+ - Retrieves execution results
31
+ - Integrates MAT job outputs into n8n workflows
32
+
33
+ The node acts strictly as a thin client over MAT’s REST API. All authorization and validation are enforced server-side by MAT.
34
+
35
+ ## Prerequisites
36
+ - A running MAT Framework instance (SaaS or On-Prem)
37
+ - A valid API token
38
+ - Network connectivity between n8n and your MAT tenant
39
+ - A valid commercial MAT license
40
+
41
+ ## Security & Governance
42
+
43
+ This node does not execute automation logic locally and does not bypass MAT controls. All RBAC, CI/CD validation, approval gates, maintenance windows, and audit logging are enforced server-side within MAT.
44
+
45
+ When triggering a workflow:
46
+ 1. n8n sends a request to MAT API.
47
+ 2. MAT validates authentication and permissions.
48
+ 3. MAT registers a Job internally.
49
+ 4. Execution is performed within the MAT Engine.
50
+ 5. Job status and output can be polled asynchronously.
51
+
52
+ The node does not bypass:
53
+ - Role-based access control
54
+ - Approval gates
55
+ - CI/CD enforcement
56
+ - Closed-loop policies
57
+ - Audit logging
58
+
59
+ ## Error Handling
60
+ Errors returned by the node fall into two categories:
61
+ - Transport errors (network, timeout, TLS)
62
+ - Execution errors (returned by MAT API)
63
+
64
+ MAT-side execution errors include:
65
+ - Authorization failure
66
+ - Invalid workflow ID
67
+ - Parameter validation error
68
+ - Policy restriction
69
+
70
+ All execution logs remain traceable inside MAT.
71
+
72
+ ## Security Notes
73
+ - The node stores credentials in n8n’s encrypted credential store.
74
+ - API tokens should be scoped according to least privilege.
75
+ - All sensitive execution logic remains server-side in MAT.
76
+ - No network configuration logic resides in this repository.
77
+
78
+ ## Development
79
+ This node is implemented in TypeScript and follows n8n community node structure.
80
+
81
+ ## Scope
82
+ This repository does not include the MAT Engine, automation workflows or SLA/support services, nor does it expose proprietary Bricks. Use of MAT APIs requires a valid commercial license.
83
+
84
+ ## License
85
+ Proprietary License.
86
+ See [LICENSE](./LICENSE) for details.
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MatApi = void 0;
4
+ class MatApi {
5
+ name = 'matApi';
6
+ displayName = 'MAT API';
7
+ documentationUrl = 'https://iquall.net/mat-n8n/';
8
+ properties = [
9
+ {
10
+ displayName: 'API Key',
11
+ name: 'apiKey',
12
+ type: 'string',
13
+ typeOptions: {
14
+ password: true,
15
+ },
16
+ default: '',
17
+ required: true,
18
+ description: 'The API key for authenticating with the MAT platform',
19
+ },
20
+ ];
21
+ authenticate = {
22
+ type: 'generic',
23
+ properties: {
24
+ headers: {
25
+ apikey: '={{$credentials.apiKey}}',
26
+ },
27
+ },
28
+ };
29
+ }
30
+ exports.MatApi = MatApi;
@@ -0,0 +1,357 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MATFrameworkRead = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ class MATFrameworkRead {
6
+ description = {
7
+ displayName: 'MAT Framework Read',
8
+ name: 'matFrameworkRead',
9
+ icon: 'file:mat-framework-icon.png',
10
+ group: ['transform'],
11
+ version: 1,
12
+ subtitle: '={{$parameter["readOperation"]}}',
13
+ description: 'Read job data and reports from MAT platform',
14
+ documentationUrl: 'https://iquall.net/mat-n8n/',
15
+ defaults: {
16
+ name: 'MAT Framework Read',
17
+ },
18
+ inputs: ['main'],
19
+ outputs: ['main'],
20
+ credentials: [
21
+ {
22
+ name: 'matApi',
23
+ required: true,
24
+ },
25
+ ],
26
+ properties: [
27
+ {
28
+ displayName: 'MAT REST API URL',
29
+ name: 'baseUrl',
30
+ type: 'string',
31
+ default: '',
32
+ placeholder: 'https://your-domain.com/namespace/api/v2',
33
+ required: true,
34
+ description: 'The base URL for your MAT platform API (must end with /api/v2). Protocol (https://) will be added automatically if not provided.',
35
+ },
36
+ {
37
+ displayName: 'Environment',
38
+ name: 'environment',
39
+ type: 'options',
40
+ options: [
41
+ {
42
+ name: 'Production',
43
+ value: 'production',
44
+ },
45
+ {
46
+ name: 'Preproduction',
47
+ value: 'preproduction',
48
+ },
49
+ {
50
+ name: 'Sandbox',
51
+ value: 'sandbox',
52
+ },
53
+ ],
54
+ default: 'production',
55
+ required: true,
56
+ description: 'The environment to read data from',
57
+ },
58
+ {
59
+ displayName: 'Automation Type',
60
+ name: 'automationType',
61
+ type: 'options',
62
+ options: [
63
+ {
64
+ name: 'Network Task',
65
+ value: 'networktask',
66
+ },
67
+ {
68
+ name: 'Network Workflow',
69
+ value: 'networkworkflow',
70
+ },
71
+ {
72
+ name: 'Orchestration Workflow',
73
+ value: 'orchestrationworkflow',
74
+ },
75
+ // {
76
+ // name: 'Serverless Function',
77
+ // value: 'serverlessfunctions',
78
+ // },
79
+ ],
80
+ default: 'networktask',
81
+ required: true,
82
+ description: 'The type of automation',
83
+ },
84
+ {
85
+ displayName: 'Operation',
86
+ name: 'readOperation',
87
+ type: 'options',
88
+ options: [
89
+ {
90
+ name: 'Get Job Information',
91
+ value: 'jobDetails',
92
+ },
93
+ {
94
+ name: 'View Job Logs',
95
+ value: 'logs',
96
+ },
97
+ {
98
+ name: 'Get Network Elements',
99
+ value: 'networkelements',
100
+ },
101
+ {
102
+ name: 'Get Job Result',
103
+ value: 'result',
104
+ },
105
+ {
106
+ name: 'Get Job Report',
107
+ value: 'report',
108
+ },
109
+ {
110
+ name: 'Download Report File',
111
+ value: 'reportFile',
112
+ },
113
+ {
114
+ name: 'Get Alarms',
115
+ value: 'alarms',
116
+ },
117
+ ],
118
+ default: 'jobDetails',
119
+ required: true,
120
+ description: 'The type of data to retrieve',
121
+ },
122
+ {
123
+ displayName: 'Automation ID',
124
+ name: 'automationId',
125
+ type: 'string',
126
+ default: '',
127
+ required: true,
128
+ displayOptions: {
129
+ hide: {
130
+ automationType: ['serverlessfunctions'],
131
+ },
132
+ },
133
+ description: 'The ID of the automation',
134
+ },
135
+ // {
136
+ // displayName: 'SLF Namespace ID',
137
+ // name: 'slfNamespace',
138
+ // type: 'string',
139
+ // default: '',
140
+ // required: true,
141
+ // displayOptions: {
142
+ // show: {
143
+ // automationType: ['serverlessfunctions'],
144
+ // readOperation: ['alarms'],
145
+ // },
146
+ // },
147
+ // description: 'The namespace for the serverless function',
148
+ // },
149
+ // {
150
+ // displayName: 'Function Name',
151
+ // name: 'functionName',
152
+ // type: 'string',
153
+ // default: '',
154
+ // required: true,
155
+ // displayOptions: {
156
+ // show: {
157
+ // automationType: ['serverlessfunctions'],
158
+ // readOperation: ['alarms'],
159
+ // },
160
+ // },
161
+ // description: 'The name of the serverless function',
162
+ // },
163
+ {
164
+ displayName: 'Job ID',
165
+ name: 'jobId',
166
+ type: 'string',
167
+ default: '',
168
+ required: true,
169
+ displayOptions: {
170
+ show: {
171
+ readOperation: ['jobDetails', 'logs', 'networkelements', 'result', 'report', 'reportFile'],
172
+ },
173
+ hide: {
174
+ automationType: ['serverlessfunctions'],
175
+ },
176
+ },
177
+ description: 'The ID of the job to retrieve data from',
178
+ },
179
+ {
180
+ displayName: 'Report Name',
181
+ name: 'reportName',
182
+ type: 'string',
183
+ default: '',
184
+ required: true,
185
+ displayOptions: {
186
+ show: {
187
+ readOperation: ['report', 'reportFile'],
188
+ },
189
+ },
190
+ description: 'The name of the report to retrieve',
191
+ },
192
+ {
193
+ displayName: 'File ID',
194
+ name: 'fileId',
195
+ type: 'string',
196
+ default: '',
197
+ required: true,
198
+ displayOptions: {
199
+ show: {
200
+ readOperation: ['reportFile'],
201
+ },
202
+ },
203
+ description: 'The ID of the file to download from the report',
204
+ },
205
+ ],
206
+ };
207
+ async execute() {
208
+ const items = this.getInputData();
209
+ const returnData = [];
210
+ for (let i = 0; i < items.length; i++) {
211
+ try {
212
+ // 1. Get parameters
213
+ let baseUrl = this.getNodeParameter('baseUrl', i);
214
+ const environment = this.getNodeParameter('environment', i);
215
+ const automationType = this.getNodeParameter('automationType', i);
216
+ const readOperation = this.getNodeParameter('readOperation', i);
217
+ // 2. Get credentials
218
+ const credentials = await this.getCredentials('matApi', i);
219
+ const apiKey = credentials.apiKey;
220
+ // 3. Normalize and validate base URL
221
+ // Trim whitespace
222
+ baseUrl = baseUrl.trim();
223
+ // Add https:// if no protocol present
224
+ if (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) {
225
+ baseUrl = 'https://' + baseUrl;
226
+ }
227
+ // Remove trailing slashes
228
+ baseUrl = baseUrl.replace(/\/+$/, '');
229
+ // Validate base URL ends with /api/v2 (case-insensitive)
230
+ if (!baseUrl.toLowerCase().endsWith('/api/v2')) {
231
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `MAT REST API URL must end with /api/v2\nProvided: ${baseUrl}\nExpected format: https://your-domain.com/namespace/api/v2`, { itemIndex: i });
232
+ }
233
+ // Ensure /api/v2 is lowercase for consistency
234
+ if (!baseUrl.endsWith('/api/v2')) {
235
+ baseUrl = baseUrl.substring(0, baseUrl.length - 7) + '/api/v2';
236
+ }
237
+ // 4. Validate operation compatibility
238
+ if (automationType === 'serverlessfunctions' && readOperation !== 'alarms') {
239
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Serverless Functions only support the "Get Alarms" operation', { itemIndex: i });
240
+ }
241
+ // 5. Build URL based on automation type and operation
242
+ let url;
243
+ if (readOperation === 'alarms') {
244
+ if (automationType === 'serverlessfunctions') {
245
+ const slfNamespace = this.getNodeParameter('slfNamespace', i);
246
+ const functionName = this.getNodeParameter('functionName', i);
247
+ url = `${baseUrl}/serverlessfunctions/namespace/${slfNamespace}/function/${functionName}/env/${environment}/alarms`;
248
+ }
249
+ else {
250
+ const automationId = this.getNodeParameter('automationId', i);
251
+ url = `${baseUrl}/${automationType}/${automationId}/env/${environment}/alarms`;
252
+ }
253
+ }
254
+ else {
255
+ // All other operations require automation_id and job_id
256
+ const automationId = this.getNodeParameter('automationId', i);
257
+ const jobId = this.getNodeParameter('jobId', i);
258
+ const baseJobUrl = `${baseUrl}/${automationType}/${automationId}/env/${environment}/jobs/${jobId}`;
259
+ switch (readOperation) {
260
+ case 'jobDetails':
261
+ url = baseJobUrl;
262
+ break;
263
+ case 'logs':
264
+ url = `${baseJobUrl}/logs`;
265
+ break;
266
+ case 'networkelements':
267
+ url = `${baseJobUrl}/networkelements`;
268
+ break;
269
+ case 'result':
270
+ url = `${baseJobUrl}/result`;
271
+ break;
272
+ case 'report':
273
+ const reportName = this.getNodeParameter('reportName', i);
274
+ url = `${baseJobUrl}/report/${reportName}`;
275
+ break;
276
+ case 'reportFile':
277
+ const reportNameForFile = this.getNodeParameter('reportName', i);
278
+ const fileId = this.getNodeParameter('fileId', i);
279
+ url = `${baseJobUrl}/report/${reportNameForFile}/file/${fileId}`;
280
+ break;
281
+ default:
282
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${readOperation}`, { itemIndex: i });
283
+ }
284
+ }
285
+ // 6. Make HTTP GET request
286
+ if (readOperation === 'reportFile') {
287
+ // Handle binary file download
288
+ const fileId = this.getNodeParameter('fileId', i);
289
+ const response = await this.helpers.request({
290
+ method: 'GET',
291
+ url: url,
292
+ headers: {
293
+ 'apikey': apiKey,
294
+ },
295
+ encoding: null, // Important: don't encode binary data
296
+ resolveWithFullResponse: true,
297
+ });
298
+ // Extract filename from fileId or Content-Disposition header
299
+ let fileName = fileId;
300
+ const contentDisposition = response.headers['content-disposition'];
301
+ if (contentDisposition) {
302
+ const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(contentDisposition);
303
+ if (matches != null && matches[1]) {
304
+ fileName = matches[1].replace(/['"]/g, '');
305
+ }
306
+ }
307
+ // Prepare binary data
308
+ const binaryData = await this.helpers.prepareBinaryData(response.body, fileName, response.headers['content-type'] || 'application/octet-stream');
309
+ // 7. Return as binary
310
+ returnData.push({
311
+ json: {
312
+ fileId: fileId,
313
+ fileName: fileName,
314
+ mimeType: response.headers['content-type'] || 'application/octet-stream',
315
+ },
316
+ binary: {
317
+ data: binaryData,
318
+ },
319
+ });
320
+ }
321
+ else {
322
+ // Handle JSON responses (all other operations)
323
+ const options = {
324
+ method: 'GET',
325
+ url: url,
326
+ headers: {
327
+ 'apikey': apiKey,
328
+ 'Accept': 'application/json',
329
+ 'Content-Type': 'application/json',
330
+ },
331
+ json: true,
332
+ };
333
+ const response = await this.helpers.request(options);
334
+ // 7. Return response
335
+ returnData.push({ json: response });
336
+ }
337
+ }
338
+ catch (error) {
339
+ if (this.continueOnFail()) {
340
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
341
+ const statusCode = error.statusCode || 'unknown';
342
+ returnData.push({
343
+ json: {
344
+ error: errorMessage,
345
+ statusCode: statusCode,
346
+ },
347
+ });
348
+ }
349
+ else {
350
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i });
351
+ }
352
+ }
353
+ }
354
+ return [returnData];
355
+ }
356
+ }
357
+ exports.MATFrameworkRead = MATFrameworkRead;
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MATFrameworkTrigger = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ class MATFrameworkTrigger {
6
+ description = {
7
+ displayName: 'MAT Framework Trigger',
8
+ name: 'matFrameworkTrigger',
9
+ icon: 'file:mat-framework-icon.png',
10
+ group: ['transform'],
11
+ version: 1,
12
+ subtitle: '={{$parameter["automationType"]}}',
13
+ description: 'Trigger MAT platform automations via simplified API',
14
+ documentationUrl: 'https://iquall.net/mat-n8n/',
15
+ defaults: {
16
+ name: 'MAT Framework Trigger',
17
+ },
18
+ inputs: ['main'],
19
+ outputs: ['main'],
20
+ credentials: [
21
+ {
22
+ name: 'matApi',
23
+ required: true,
24
+ },
25
+ ],
26
+ properties: [
27
+ {
28
+ displayName: 'MAT REST API URL',
29
+ name: 'baseUrl',
30
+ type: 'string',
31
+ default: '',
32
+ placeholder: 'https://your-domain.com/namespace/api/v2',
33
+ required: true,
34
+ description: 'The base URL for your MAT platform API (must end with /api/v2). Protocol (https://) will be added automatically if not provided.',
35
+ },
36
+ {
37
+ displayName: 'Environment',
38
+ name: 'environment',
39
+ type: 'options',
40
+ options: [
41
+ {
42
+ name: 'Production',
43
+ value: 'production',
44
+ },
45
+ {
46
+ name: 'Preproduction',
47
+ value: 'preproduction',
48
+ },
49
+ {
50
+ name: 'Sandbox',
51
+ value: 'sandbox',
52
+ },
53
+ ],
54
+ default: 'production',
55
+ required: true,
56
+ description: 'The environment to trigger the automation in',
57
+ },
58
+ {
59
+ displayName: 'Automation Type',
60
+ name: 'automationType',
61
+ type: 'options',
62
+ options: [
63
+ {
64
+ name: 'Network Task',
65
+ value: 'networktask',
66
+ },
67
+ {
68
+ name: 'Network Workflow',
69
+ value: 'networkworkflow',
70
+ },
71
+ {
72
+ name: 'Orchestration Workflow',
73
+ value: 'orchestrationworkflow',
74
+ },
75
+ // {
76
+ // name: 'Serverless Function',
77
+ // value: 'serverlessfunctions',
78
+ // },
79
+ ],
80
+ default: 'networktask',
81
+ required: true,
82
+ description: 'The type of automation to trigger',
83
+ },
84
+ {
85
+ displayName: 'Execution Mode',
86
+ name: 'syncMode',
87
+ type: 'options',
88
+ options: [
89
+ {
90
+ name: 'Async',
91
+ value: 'async',
92
+ },
93
+ {
94
+ name: 'Sync',
95
+ value: 'sync',
96
+ },
97
+ ],
98
+ default: 'async',
99
+ displayOptions: {
100
+ show: {
101
+ automationType: ['networktask'],
102
+ },
103
+ },
104
+ description: 'Whether to execute synchronously or asynchronously',
105
+ },
106
+ {
107
+ displayName: 'Automation ID',
108
+ name: 'automationId',
109
+ type: 'string',
110
+ default: '',
111
+ required: true,
112
+ displayOptions: {
113
+ hide: {
114
+ automationType: ['serverlessfunctions'],
115
+ },
116
+ },
117
+ description: 'The ID of the automation to trigger',
118
+ },
119
+ // {
120
+ // displayName: 'SLF Namespace ID',
121
+ // name: 'slfNamespace',
122
+ // type: 'string',
123
+ // default: '',
124
+ // required: true,
125
+ // displayOptions: {
126
+ // show: {
127
+ // automationType: ['serverlessfunctions'],
128
+ // },
129
+ // },
130
+ // description: 'The namespace for the serverless function',
131
+ // },
132
+ // {
133
+ // displayName: 'Function Name',
134
+ // name: 'functionName',
135
+ // type: 'string',
136
+ // default: '',
137
+ // required: true,
138
+ // displayOptions: {
139
+ // show: {
140
+ // automationType: ['serverlessfunctions'],
141
+ // },
142
+ // },
143
+ // description: 'The name of the serverless function to invoke',
144
+ // },
145
+ // {
146
+ // displayName: 'Request Body',
147
+ // name: 'requestBodyServerless',
148
+ // type: 'json',
149
+ // default: '{}',
150
+ // displayOptions: {
151
+ // show: {
152
+ // automationType: ['serverlessfunctions'],
153
+ // },
154
+ // },
155
+ // description: 'JSON body for Serverless Function requests.',
156
+ // },
157
+ {
158
+ displayName: 'Request Body',
159
+ name: 'requestBodyStandard',
160
+ type: 'json',
161
+ default: '{\n "form_data": {}\n}',
162
+ displayOptions: {
163
+ hide: {
164
+ automationType: ['serverlessfunctions'],
165
+ },
166
+ },
167
+ description: 'JSON body for non-serverless requests. Keep payload under form_data.',
168
+ },
169
+ ],
170
+ };
171
+ async execute() {
172
+ const items = this.getInputData();
173
+ const returnData = [];
174
+ for (let i = 0; i < items.length; i++) {
175
+ try {
176
+ // 1. Get all parameters
177
+ let baseUrl = this.getNodeParameter('baseUrl', i);
178
+ const environment = this.getNodeParameter('environment', i);
179
+ const automationType = this.getNodeParameter('automationType', i);
180
+ // 2. Get credentials
181
+ const credentials = await this.getCredentials('matApi', i);
182
+ const apiKey = credentials.apiKey;
183
+ // 3. Normalize and validate base URL
184
+ // Trim whitespace
185
+ baseUrl = baseUrl.trim();
186
+ // Add https:// if no protocol present
187
+ if (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) {
188
+ baseUrl = 'https://' + baseUrl;
189
+ }
190
+ // Remove trailing slashes
191
+ baseUrl = baseUrl.replace(/\/+$/, '');
192
+ // Validate base URL ends with /api/v2 (case-insensitive)
193
+ if (!baseUrl.toLowerCase().endsWith('/api/v2')) {
194
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `MAT REST API URL must end with /api/v2\nProvided: ${baseUrl}\nExpected format: https://your-domain.com/namespace/api/v2`, { itemIndex: i });
195
+ }
196
+ // Ensure /api/v2 is lowercase for consistency
197
+ if (!baseUrl.endsWith('/api/v2')) {
198
+ baseUrl = baseUrl.substring(0, baseUrl.length - 7) + '/api/v2';
199
+ }
200
+ // 4. Build URL based on automation type
201
+ let url;
202
+ if (automationType === 'serverlessfunctions') {
203
+ const slfNamespace = this.getNodeParameter('slfNamespace', i);
204
+ const functionName = this.getNodeParameter('functionName', i);
205
+ url = `${baseUrl}/serverlessfunctions/namespace/${slfNamespace}/function/${functionName}/env/${environment}`;
206
+ }
207
+ else {
208
+ const automationId = this.getNodeParameter('automationId', i);
209
+ url = `${baseUrl}/${automationType}/${automationId}/env/${environment}/jobs`;
210
+ // Add /sync suffix for Network Task + Sync mode
211
+ if (automationType === 'networktask') {
212
+ const syncMode = this.getNodeParameter('syncMode', i);
213
+ if (syncMode === 'sync') {
214
+ url += '/sync';
215
+ }
216
+ }
217
+ }
218
+ // 5. Process request body
219
+ let requestBody;
220
+ const bodyParam = this.getNodeParameter(automationType === 'serverlessfunctions' ? 'requestBodyServerless' : 'requestBodyStandard', i);
221
+ // Parse JSON string if it's a string
222
+ if (typeof bodyParam === 'string') {
223
+ try {
224
+ requestBody = JSON.parse(bodyParam);
225
+ }
226
+ catch (error) {
227
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
228
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid JSON in Request Body: ${errorMessage}`, { itemIndex: i });
229
+ }
230
+ }
231
+ else {
232
+ requestBody = bodyParam;
233
+ }
234
+ // Only wrap in form_data for automation types that require it
235
+ // Serverless Functions expect plain JSON bodies
236
+ if (automationType !== 'serverlessfunctions') {
237
+ if (!Object.prototype.hasOwnProperty.call(requestBody ?? {}, 'form_data')) {
238
+ requestBody = { form_data: requestBody };
239
+ }
240
+ }
241
+ // 6. Make HTTP request
242
+ const options = {
243
+ method: 'POST',
244
+ url: url,
245
+ headers: {
246
+ 'apikey': apiKey,
247
+ 'Accept': 'application/json',
248
+ 'Content-Type': 'application/json',
249
+ },
250
+ body: requestBody,
251
+ json: true,
252
+ };
253
+ const response = await this.helpers.request(options);
254
+ // 7. Return response
255
+ returnData.push({ json: response });
256
+ }
257
+ catch (error) {
258
+ if (this.continueOnFail()) {
259
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
260
+ const statusCode = error.statusCode || 'unknown';
261
+ returnData.push({
262
+ json: {
263
+ error: errorMessage,
264
+ statusCode: statusCode,
265
+ },
266
+ });
267
+ }
268
+ else {
269
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i });
270
+ }
271
+ }
272
+ }
273
+ return [returnData];
274
+ }
275
+ }
276
+ exports.MATFrameworkTrigger = MATFrameworkTrigger;
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "n8n-nodes-mat-framework",
3
+ "version": "1.0.0",
4
+ "description": "Official community nodes that integrate MAT Framework with n8n",
5
+ "keywords": [
6
+ "n8n-community-node-package",
7
+ "mat-framework"
8
+ ],
9
+ "license": "SEE LICENSE IN LICENSE",
10
+ "homepage": "https://iquall.net/",
11
+ "author": {
12
+ "name": "Iquall Networks",
13
+ "email": "marketing@iquall.net"
14
+ },
15
+ "scripts": {
16
+ "build": "tsc && gulp build:icons",
17
+ "prepack": "npm run build",
18
+ "dev": "tsc --watch",
19
+ "start": "npm run build && PKG_NAME=$(node -p \"require('./package.json').name\") && CUSTOM_DIR=\"$HOME/.n8n/custom\" && mkdir -p \"$CUSTOM_DIR\" && ln -sfn \"$(pwd)\" \"$CUSTOM_DIR/$PKG_NAME\" && N8N_CUSTOM_EXTENSIONS=\"$CUSTOM_DIR\" npx n8n",
20
+ "lint": "eslint nodes credentials --ext .ts",
21
+ "lintfix": "eslint nodes credentials --ext .ts --fix",
22
+ "format": "prettier nodes credentials --write"
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "n8n": {
28
+ "n8nNodesApiVersion": 1,
29
+ "credentials": [
30
+ "dist/credentials/MatApi.credentials.js"
31
+ ],
32
+ "nodes": [
33
+ "dist/nodes/MAT_Framework/Trigger/MATFrameworkTrigger.node.js",
34
+ "dist/nodes/MAT_Framework/Read/MATFrameworkRead.node.js"
35
+ ]
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^22.0.0",
39
+ "@typescript-eslint/parser": "^7.0.0",
40
+ "eslint": "^8.56.0",
41
+ "eslint-plugin-n8n-nodes-base": "^1.16.1",
42
+ "gulp": "^4.0.2",
43
+ "n8n-workflow": "^1.0.0",
44
+ "prettier": "^3.0.0",
45
+ "typescript": "^5.3.0"
46
+ },
47
+ "peerDependencies": {
48
+ "n8n-workflow": "*"
49
+ }
50
+ }