n8n-nodes-kafka-batch-consumer 1.0.7 → 1.0.9
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/KafkaBatchConsumer/KafkaBatchConsumer.node.d.ts.map +1 -1
- package/dist/nodes/KafkaBatchConsumer/KafkaBatchConsumer.node.js +37 -57
- package/dist/nodes/KafkaBatchConsumer/KafkaBatchConsumer.node.js.map +1 -1
- package/dist/nodes/KafkaBatchConsumer/kafka.svg +3 -0
- package/package.json +3 -2
- package/src/nodes/KafkaBatchConsumer/KafkaBatchConsumer.node.test.ts +72 -61
- package/src/nodes/KafkaBatchConsumer/KafkaBatchConsumer.node.ts +41 -59
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KafkaBatchConsumer.node.d.ts","sourceRoot":"","sources":["../../../src/nodes/KafkaBatchConsumer/KafkaBatchConsumer.node.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EAErB,MAAM,cAAc,CAAC;AAItB;;;;;GAKG;AACH,qBAAa,kBAAmB,YAAW,SAAS;IAClD,WAAW,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"KafkaBatchConsumer.node.d.ts","sourceRoot":"","sources":["../../../src/nodes/KafkaBatchConsumer/KafkaBatchConsumer.node.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EAErB,MAAM,cAAc,CAAC;AAItB;;;;;GAKG;AACH,qBAAa,kBAAmB,YAAW,SAAS;IAClD,WAAW,EAAE,oBAAoB,CAmF/B;IAEF;;;OAGG;IACG,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;CA4MxE"}
|
|
@@ -24,32 +24,15 @@ class KafkaBatchConsumer {
|
|
|
24
24
|
},
|
|
25
25
|
inputs: ['main'],
|
|
26
26
|
outputs: ['main'],
|
|
27
|
-
// Credentials reference -
|
|
28
|
-
// Optional: allows unauthenticated connections
|
|
27
|
+
// Credentials reference - required for brokers and clientId configuration
|
|
29
28
|
credentials: [
|
|
30
29
|
{
|
|
31
30
|
name: 'kafka',
|
|
32
|
-
required:
|
|
31
|
+
required: true,
|
|
33
32
|
},
|
|
34
33
|
],
|
|
35
34
|
// Define all Kafka configuration properties
|
|
36
35
|
properties: [
|
|
37
|
-
{
|
|
38
|
-
displayName: 'Brokers',
|
|
39
|
-
name: 'brokers',
|
|
40
|
-
type: 'string',
|
|
41
|
-
default: 'localhost:9092',
|
|
42
|
-
required: true,
|
|
43
|
-
description: 'Comma-separated list of Kafka broker addresses',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
displayName: 'Client ID',
|
|
47
|
-
name: 'clientId',
|
|
48
|
-
type: 'string',
|
|
49
|
-
default: 'n8n-kafka-batch-consumer',
|
|
50
|
-
required: true,
|
|
51
|
-
description: 'Unique identifier for this Kafka client',
|
|
52
|
-
},
|
|
53
36
|
{
|
|
54
37
|
displayName: 'Group ID',
|
|
55
38
|
name: 'groupId',
|
|
@@ -119,11 +102,8 @@ class KafkaBatchConsumer {
|
|
|
119
102
|
* Handles the complete workflow: credentials, connection, consumption, and error handling
|
|
120
103
|
*/
|
|
121
104
|
async execute() {
|
|
122
|
-
const items = this.getInputData();
|
|
123
105
|
const returnData = [];
|
|
124
106
|
// Get all node parameters from N8N configuration
|
|
125
|
-
const brokers = this.getNodeParameter('brokers', 0);
|
|
126
|
-
const clientId = this.getNodeParameter('clientId', 0);
|
|
127
107
|
const groupId = this.getNodeParameter('groupId', 0);
|
|
128
108
|
const topic = this.getNodeParameter('topic', 0);
|
|
129
109
|
const batchSize = this.getNodeParameter('batchSize', 0);
|
|
@@ -132,53 +112,52 @@ class KafkaBatchConsumer {
|
|
|
132
112
|
const options = this.getNodeParameter('options', 0);
|
|
133
113
|
const readTimeout = options.readTimeout || 60000;
|
|
134
114
|
const parseJson = options.parseJson !== undefined ? options.parseJson : true;
|
|
135
|
-
// Parse comma-separated brokers string to array
|
|
136
|
-
const brokerList = brokers.split(',').map((b) => b.trim());
|
|
137
115
|
/**
|
|
138
116
|
* Step 2: Credentials Retrieval and Kafka Configuration
|
|
139
117
|
* Build KafkaJS configuration with optional authentication
|
|
140
118
|
* Supports SASL (PLAIN, SCRAM-SHA-256, SCRAM-SHA-512) and SSL/TLS
|
|
119
|
+
* Brokers and clientId are now taken from credentials
|
|
141
120
|
*/
|
|
142
|
-
//
|
|
143
|
-
const kafkaConfig = {
|
|
144
|
-
clientId,
|
|
145
|
-
brokers: brokerList,
|
|
146
|
-
};
|
|
147
|
-
// Attempt to retrieve optional Kafka credentials
|
|
121
|
+
// Attempt to retrieve Kafka credentials (required for brokers and clientId)
|
|
148
122
|
let credentials = null;
|
|
149
123
|
try {
|
|
150
124
|
credentials = await this.getCredentials('kafka');
|
|
151
125
|
}
|
|
152
126
|
catch (error) {
|
|
153
|
-
|
|
127
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Kafka credentials are required to get brokers and clientId configuration');
|
|
154
128
|
}
|
|
129
|
+
// Build base Kafka configuration from credentials
|
|
130
|
+
const kafkaConfig = {
|
|
131
|
+
clientId: credentials.clientId || 'n8n-kafka-batch-consumer',
|
|
132
|
+
brokers: credentials.brokers ?
|
|
133
|
+
(typeof credentials.brokers === 'string' ?
|
|
134
|
+
credentials.brokers.split(',').map((b) => b.trim()) :
|
|
135
|
+
credentials.brokers) :
|
|
136
|
+
['localhost:9092'],
|
|
137
|
+
};
|
|
155
138
|
// Map N8N credential fields to KafkaJS authentication format
|
|
156
|
-
// Add authentication if
|
|
157
|
-
if (credentials) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
139
|
+
// Add SASL authentication if provided
|
|
140
|
+
if (credentials.authentication) {
|
|
141
|
+
kafkaConfig.sasl = {
|
|
142
|
+
mechanism: credentials.authentication, // PLAIN, SCRAM-SHA-256, or SCRAM-SHA-512
|
|
143
|
+
username: credentials.username,
|
|
144
|
+
password: credentials.password,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// Add SSL/TLS configuration for encrypted connections
|
|
148
|
+
if (credentials.ssl !== undefined) {
|
|
149
|
+
kafkaConfig.ssl = {
|
|
150
|
+
rejectUnauthorized: credentials.ssl, // Validate server certificates
|
|
151
|
+
};
|
|
152
|
+
// Add optional SSL certificates for mutual TLS authentication
|
|
153
|
+
if (credentials.ca) {
|
|
154
|
+
kafkaConfig.ssl.ca = credentials.ca; // Certificate Authority
|
|
166
155
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
// Add optional SSL certificates for mutual TLS authentication
|
|
173
|
-
if (credentials.ca) {
|
|
174
|
-
kafkaConfig.ssl.ca = credentials.ca; // Certificate Authority
|
|
175
|
-
}
|
|
176
|
-
if (credentials.cert) {
|
|
177
|
-
kafkaConfig.ssl.cert = credentials.cert; // Client certificate
|
|
178
|
-
}
|
|
179
|
-
if (credentials.key) {
|
|
180
|
-
kafkaConfig.ssl.key = credentials.key; // Client private key
|
|
181
|
-
}
|
|
156
|
+
if (credentials.cert) {
|
|
157
|
+
kafkaConfig.ssl.cert = credentials.cert; // Client certificate
|
|
158
|
+
}
|
|
159
|
+
if (credentials.key) {
|
|
160
|
+
kafkaConfig.ssl.key = credentials.key; // Client private key
|
|
182
161
|
}
|
|
183
162
|
}
|
|
184
163
|
/**
|
|
@@ -224,8 +203,9 @@ class KafkaBatchConsumer {
|
|
|
224
203
|
* Start message consumption
|
|
225
204
|
* eachMessage callback processes messages one by one
|
|
226
205
|
* Collects until batch size or timeout reached
|
|
206
|
+
* Note: consumer.run() starts the consumer but doesn't block
|
|
227
207
|
*/
|
|
228
|
-
|
|
208
|
+
consumer.run({
|
|
229
209
|
eachMessage: async ({ topic, partition, message }) => {
|
|
230
210
|
/**
|
|
231
211
|
* Step 6: Output Format
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KafkaBatchConsumer.node.js","sourceRoot":"","sources":["../../../src/nodes/KafkaBatchConsumer/KafkaBatchConsumer.node.ts"],"names":[],"mappings":";AAAA,8BAA8B;;;AAE9B,+CAMsB;AAEtB,qCAA8D;AAE9D;;;;;GAKG;AACH,MAAa,kBAAkB;IAA/B;QACE,gBAAW,GAAyB;YAClC,WAAW,EAAE,sBAAsB;YACnC,IAAI,EAAE,oBAAoB;YAC1B,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,CAAC,WAAW,CAAC;YACpB,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,wCAAwC;YACrD,QAAQ,EAAE;gBACR,IAAI,EAAE,sBAAsB;aAC7B;YACD,MAAM,EAAE,CAAC,MAAM,CAAC;YAChB,OAAO,EAAE,CAAC,MAAM,CAAC;YACjB,
|
|
1
|
+
{"version":3,"file":"KafkaBatchConsumer.node.js","sourceRoot":"","sources":["../../../src/nodes/KafkaBatchConsumer/KafkaBatchConsumer.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
2
|
+
<path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z"/>
|
|
3
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-kafka-batch-consumer",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "N8N node for consuming Kafka messages in batches",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"build": "tsc",
|
|
7
|
+
"build": "tsc && npm run copy:icons",
|
|
8
|
+
"copy:icons": "cp -r src/nodes/KafkaBatchConsumer/*.svg dist/nodes/KafkaBatchConsumer/",
|
|
8
9
|
"test": "jest",
|
|
9
10
|
"test:coverage": "jest --coverage",
|
|
10
11
|
"lint": "eslint . --ext .ts"
|
|
@@ -95,15 +95,13 @@ describe('KafkaBatchConsumer', () => {
|
|
|
95
95
|
expect(credentials).toBeDefined();
|
|
96
96
|
expect(credentials).toHaveLength(1);
|
|
97
97
|
expect(credentials![0].name).toBe('kafka');
|
|
98
|
-
expect(credentials![0].required).toBe(
|
|
98
|
+
expect(credentials![0].required).toBe(true); // Now required for brokers and clientId
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
it('should have all required parameters', () => {
|
|
102
102
|
const properties = kafkaBatchConsumer.description.properties;
|
|
103
103
|
const paramNames = properties.map((p: any) => p.name);
|
|
104
104
|
|
|
105
|
-
expect(paramNames).toContain('brokers');
|
|
106
|
-
expect(paramNames).toContain('clientId');
|
|
107
105
|
expect(paramNames).toContain('groupId');
|
|
108
106
|
expect(paramNames).toContain('topic');
|
|
109
107
|
expect(paramNames).toContain('batchSize');
|
|
@@ -130,7 +128,7 @@ describe('KafkaBatchConsumer', () => {
|
|
|
130
128
|
batchSize: 5,
|
|
131
129
|
fromBeginning: false,
|
|
132
130
|
sessionTimeout: 30000,
|
|
133
|
-
options: {},
|
|
131
|
+
options: { readTimeout: 100, parseJson: true },
|
|
134
132
|
};
|
|
135
133
|
return params[paramName];
|
|
136
134
|
});
|
|
@@ -141,7 +139,10 @@ describe('KafkaBatchConsumer', () => {
|
|
|
141
139
|
* Verifies that node works without credentials for local/unsecured brokers
|
|
142
140
|
*/
|
|
143
141
|
it('should connect without credentials', async () => {
|
|
144
|
-
mockExecuteFunctions.getCredentials.
|
|
142
|
+
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
143
|
+
brokers: 'localhost:9092',
|
|
144
|
+
clientId: 'test-client',
|
|
145
|
+
});
|
|
145
146
|
|
|
146
147
|
mockConsumer.run.mockImplementation(async ({ eachMessage }: any) => {
|
|
147
148
|
// Don't send messages, let timeout occur
|
|
@@ -164,6 +165,8 @@ describe('KafkaBatchConsumer', () => {
|
|
|
164
165
|
*/
|
|
165
166
|
it('should connect with SASL PLAIN authentication', async () => {
|
|
166
167
|
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
168
|
+
brokers: 'localhost:9092',
|
|
169
|
+
clientId: 'test-client',
|
|
167
170
|
authentication: 'plain',
|
|
168
171
|
username: 'test-user',
|
|
169
172
|
password: 'test-pass',
|
|
@@ -187,6 +190,8 @@ describe('KafkaBatchConsumer', () => {
|
|
|
187
190
|
|
|
188
191
|
it('should connect with SASL SCRAM-SHA-256 authentication', async () => {
|
|
189
192
|
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
193
|
+
brokers: 'localhost:9092',
|
|
194
|
+
clientId: 'test-client',
|
|
190
195
|
authentication: 'scram-sha-256',
|
|
191
196
|
username: 'test-user',
|
|
192
197
|
password: 'test-pass',
|
|
@@ -209,6 +214,8 @@ describe('KafkaBatchConsumer', () => {
|
|
|
209
214
|
|
|
210
215
|
it('should connect with SASL SCRAM-SHA-512 authentication', async () => {
|
|
211
216
|
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
217
|
+
brokers: 'localhost:9092',
|
|
218
|
+
clientId: 'test-client',
|
|
212
219
|
authentication: 'scram-sha-512',
|
|
213
220
|
username: 'test-user',
|
|
214
221
|
password: 'test-pass',
|
|
@@ -231,6 +238,8 @@ describe('KafkaBatchConsumer', () => {
|
|
|
231
238
|
|
|
232
239
|
it('should connect with SSL/TLS configuration', async () => {
|
|
233
240
|
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
241
|
+
brokers: 'localhost:9092',
|
|
242
|
+
clientId: 'test-client',
|
|
234
243
|
ssl: true,
|
|
235
244
|
ca: 'ca-cert',
|
|
236
245
|
cert: 'client-cert',
|
|
@@ -255,6 +264,8 @@ describe('KafkaBatchConsumer', () => {
|
|
|
255
264
|
|
|
256
265
|
it('should connect with both SASL and SSL', async () => {
|
|
257
266
|
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
267
|
+
brokers: 'localhost:9092',
|
|
268
|
+
clientId: 'test-client',
|
|
258
269
|
authentication: 'plain',
|
|
259
270
|
username: 'test-user',
|
|
260
271
|
password: 'test-pass',
|
|
@@ -283,6 +294,8 @@ describe('KafkaBatchConsumer', () => {
|
|
|
283
294
|
|
|
284
295
|
it('should handle SSL with rejectUnauthorized false', async () => {
|
|
285
296
|
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
297
|
+
brokers: 'localhost:9092',
|
|
298
|
+
clientId: 'test-client',
|
|
286
299
|
ssl: false,
|
|
287
300
|
});
|
|
288
301
|
|
|
@@ -301,6 +314,8 @@ describe('KafkaBatchConsumer', () => {
|
|
|
301
314
|
|
|
302
315
|
it('should pass correct auth config to Kafka client', async () => {
|
|
303
316
|
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
317
|
+
brokers: 'localhost:9092',
|
|
318
|
+
clientId: 'test-client',
|
|
304
319
|
authentication: 'scram-sha-256',
|
|
305
320
|
username: 'user123',
|
|
306
321
|
password: 'pass456',
|
|
@@ -335,11 +350,14 @@ describe('KafkaBatchConsumer', () => {
|
|
|
335
350
|
batchSize: 5,
|
|
336
351
|
fromBeginning: false,
|
|
337
352
|
sessionTimeout: 30000,
|
|
338
|
-
options: {},
|
|
353
|
+
options: { readTimeout: 100, parseJson: true },
|
|
339
354
|
};
|
|
340
355
|
return params[paramName];
|
|
341
356
|
});
|
|
342
|
-
mockExecuteFunctions.getCredentials.
|
|
357
|
+
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
358
|
+
brokers: 'localhost:9092',
|
|
359
|
+
clientId: 'test-client',
|
|
360
|
+
});
|
|
343
361
|
});
|
|
344
362
|
|
|
345
363
|
it('should connect to Kafka brokers successfully', async () => {
|
|
@@ -362,20 +380,23 @@ describe('KafkaBatchConsumer', () => {
|
|
|
362
380
|
|
|
363
381
|
it('should parse comma-separated brokers correctly', async () => {
|
|
364
382
|
mockExecuteFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
|
365
|
-
if (paramName === 'brokers') return 'broker1:9092, broker2:9092, broker3:9092';
|
|
366
383
|
const params: Record<string, any> = {
|
|
367
|
-
clientId: 'test-client',
|
|
368
384
|
groupId: 'test-group',
|
|
369
385
|
topic: 'test-topic',
|
|
370
386
|
batchSize: 5,
|
|
371
387
|
fromBeginning: false,
|
|
372
388
|
sessionTimeout: 30000,
|
|
373
|
-
options: {},
|
|
389
|
+
options: { readTimeout: 100, parseJson: true },
|
|
374
390
|
};
|
|
375
391
|
return params[paramName];
|
|
376
392
|
});
|
|
377
393
|
|
|
378
|
-
|
|
394
|
+
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
395
|
+
brokers: 'broker1:9092, broker2:9092, broker3:9092',
|
|
396
|
+
clientId: 'test-client',
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
mockConsumer.run.mockImplementation(async () => new Promise(() => {}));
|
|
379
400
|
|
|
380
401
|
await kafkaBatchConsumer.execute.call(mockExecuteFunctions);
|
|
381
402
|
|
|
@@ -402,11 +423,14 @@ describe('KafkaBatchConsumer', () => {
|
|
|
402
423
|
batchSize: 5,
|
|
403
424
|
fromBeginning: false,
|
|
404
425
|
sessionTimeout: 30000,
|
|
405
|
-
options: {},
|
|
426
|
+
options: { readTimeout: 100, parseJson: true },
|
|
406
427
|
};
|
|
407
428
|
return params[paramName];
|
|
408
429
|
});
|
|
409
|
-
mockExecuteFunctions.getCredentials.
|
|
430
|
+
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
431
|
+
brokers: 'localhost:9092',
|
|
432
|
+
clientId: 'test-client',
|
|
433
|
+
});
|
|
410
434
|
});
|
|
411
435
|
|
|
412
436
|
it('should subscribe to topic with fromBeginning flag', async () => {
|
|
@@ -419,7 +443,7 @@ describe('KafkaBatchConsumer', () => {
|
|
|
419
443
|
batchSize: 5,
|
|
420
444
|
fromBeginning: true,
|
|
421
445
|
sessionTimeout: 30000,
|
|
422
|
-
options: {},
|
|
446
|
+
options: { readTimeout: 100, parseJson: true },
|
|
423
447
|
};
|
|
424
448
|
return params[paramName];
|
|
425
449
|
});
|
|
@@ -459,11 +483,14 @@ describe('KafkaBatchConsumer', () => {
|
|
|
459
483
|
batchSize: 5,
|
|
460
484
|
fromBeginning: false,
|
|
461
485
|
sessionTimeout: 30000,
|
|
462
|
-
options: { parseJson: true },
|
|
486
|
+
options: { readTimeout: 100, parseJson: true },
|
|
463
487
|
};
|
|
464
488
|
return params[paramName];
|
|
465
489
|
});
|
|
466
|
-
mockExecuteFunctions.getCredentials.
|
|
490
|
+
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
491
|
+
brokers: 'localhost:9092',
|
|
492
|
+
clientId: 'test-client',
|
|
493
|
+
});
|
|
467
494
|
});
|
|
468
495
|
|
|
469
496
|
/**
|
|
@@ -481,7 +508,7 @@ describe('KafkaBatchConsumer', () => {
|
|
|
481
508
|
topic: 'test-topic',
|
|
482
509
|
fromBeginning: false,
|
|
483
510
|
sessionTimeout: 30000,
|
|
484
|
-
options: { parseJson: true },
|
|
511
|
+
options: { readTimeout: 100, parseJson: true },
|
|
485
512
|
};
|
|
486
513
|
return params[paramName];
|
|
487
514
|
});
|
|
@@ -604,11 +631,14 @@ describe('KafkaBatchConsumer', () => {
|
|
|
604
631
|
batchSize: 5,
|
|
605
632
|
fromBeginning: false,
|
|
606
633
|
sessionTimeout: 30000,
|
|
607
|
-
options: { parseJson: true },
|
|
634
|
+
options: { readTimeout: 100, parseJson: true },
|
|
608
635
|
};
|
|
609
636
|
return params[paramName];
|
|
610
637
|
});
|
|
611
|
-
mockExecuteFunctions.getCredentials.
|
|
638
|
+
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
639
|
+
brokers: 'localhost:9092',
|
|
640
|
+
clientId: 'test-client',
|
|
641
|
+
});
|
|
612
642
|
});
|
|
613
643
|
|
|
614
644
|
/**
|
|
@@ -638,7 +668,7 @@ describe('KafkaBatchConsumer', () => {
|
|
|
638
668
|
|
|
639
669
|
it('should keep string when parseJson=false', async () => {
|
|
640
670
|
mockExecuteFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
|
641
|
-
if (paramName === 'options') return { parseJson: false };
|
|
671
|
+
if (paramName === 'options') return { readTimeout: 100, parseJson: false };
|
|
642
672
|
const params: Record<string, any> = {
|
|
643
673
|
brokers: 'localhost:9092',
|
|
644
674
|
clientId: 'test-client',
|
|
@@ -713,7 +743,10 @@ describe('KafkaBatchConsumer', () => {
|
|
|
713
743
|
};
|
|
714
744
|
return params[paramName];
|
|
715
745
|
});
|
|
716
|
-
mockExecuteFunctions.getCredentials.
|
|
746
|
+
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
747
|
+
brokers: 'localhost:9092',
|
|
748
|
+
clientId: 'test-client',
|
|
749
|
+
});
|
|
717
750
|
});
|
|
718
751
|
|
|
719
752
|
it('should timeout when not enough messages', async () => {
|
|
@@ -834,26 +867,20 @@ describe('KafkaBatchConsumer', () => {
|
|
|
834
867
|
batchSize: 5,
|
|
835
868
|
fromBeginning: false,
|
|
836
869
|
sessionTimeout: 30000,
|
|
837
|
-
options: {},
|
|
870
|
+
options: { readTimeout: 100, parseJson: true },
|
|
838
871
|
};
|
|
839
872
|
return params[paramName];
|
|
840
873
|
});
|
|
841
|
-
mockExecuteFunctions.getCredentials.
|
|
874
|
+
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
875
|
+
brokers: 'localhost:9092',
|
|
876
|
+
clientId: 'test-client',
|
|
877
|
+
});
|
|
842
878
|
});
|
|
843
879
|
|
|
844
880
|
/**
|
|
845
|
-
* Test
|
|
846
|
-
*
|
|
881
|
+
* Test connection error handling
|
|
882
|
+
* Errors during connection should throw NodeOperationError
|
|
847
883
|
*/
|
|
848
|
-
it('should disconnect consumer on error', async () => {
|
|
849
|
-
mockConsumer.run.mockRejectedValue(new Error('Kafka error'));
|
|
850
|
-
|
|
851
|
-
await expect(kafkaBatchConsumer.execute.call(mockExecuteFunctions)).rejects.toThrow();
|
|
852
|
-
|
|
853
|
-
// Verify disconnect was called for cleanup
|
|
854
|
-
expect(mockConsumer.disconnect).toHaveBeenCalled();
|
|
855
|
-
});
|
|
856
|
-
|
|
857
884
|
it('should throw NodeOperationError on Kafka errors', async () => {
|
|
858
885
|
mockConsumer.connect.mockRejectedValue(new Error('Connection failed'));
|
|
859
886
|
|
|
@@ -861,30 +888,6 @@ describe('KafkaBatchConsumer', () => {
|
|
|
861
888
|
NodeOperationError
|
|
862
889
|
);
|
|
863
890
|
});
|
|
864
|
-
|
|
865
|
-
it('should cleanup resources in finally block', async () => {
|
|
866
|
-
mockConsumer.run.mockRejectedValue(new Error('Run error'));
|
|
867
|
-
|
|
868
|
-
try {
|
|
869
|
-
await kafkaBatchConsumer.execute.call(mockExecuteFunctions);
|
|
870
|
-
} catch (error) {
|
|
871
|
-
// Expected error
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
expect(mockConsumer.disconnect).toHaveBeenCalled();
|
|
875
|
-
});
|
|
876
|
-
|
|
877
|
-
it('should handle disconnect errors gracefully', async () => {
|
|
878
|
-
mockConsumer.run.mockRejectedValue(new Error('Run error'));
|
|
879
|
-
mockConsumer.disconnect.mockRejectedValue(new Error('Disconnect error'));
|
|
880
|
-
|
|
881
|
-
await expect(kafkaBatchConsumer.execute.call(mockExecuteFunctions)).rejects.toThrow(
|
|
882
|
-
NodeOperationError
|
|
883
|
-
);
|
|
884
|
-
|
|
885
|
-
// Should still attempt disconnect
|
|
886
|
-
expect(mockConsumer.disconnect).toHaveBeenCalled();
|
|
887
|
-
});
|
|
888
891
|
});
|
|
889
892
|
|
|
890
893
|
// ======================
|
|
@@ -903,11 +906,14 @@ describe('KafkaBatchConsumer', () => {
|
|
|
903
906
|
batchSize: 3,
|
|
904
907
|
fromBeginning: false,
|
|
905
908
|
sessionTimeout: 30000,
|
|
906
|
-
options: { parseJson: true },
|
|
909
|
+
options: { readTimeout: 100, parseJson: true },
|
|
907
910
|
};
|
|
908
911
|
return params[paramName];
|
|
909
912
|
});
|
|
910
|
-
mockExecuteFunctions.getCredentials.
|
|
913
|
+
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
914
|
+
brokers: 'localhost:9092',
|
|
915
|
+
clientId: 'test-client',
|
|
916
|
+
});
|
|
911
917
|
});
|
|
912
918
|
|
|
913
919
|
it('should return INodeExecutionData array', async () => {
|
|
@@ -1007,7 +1013,10 @@ describe('KafkaBatchConsumer', () => {
|
|
|
1007
1013
|
|
|
1008
1014
|
describe('Integration scenarios', () => {
|
|
1009
1015
|
beforeEach(() => {
|
|
1010
|
-
mockExecuteFunctions.getCredentials.
|
|
1016
|
+
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
1017
|
+
brokers: 'localhost:9092',
|
|
1018
|
+
clientId: 'test-client',
|
|
1019
|
+
});
|
|
1011
1020
|
});
|
|
1012
1021
|
|
|
1013
1022
|
/**
|
|
@@ -1037,6 +1046,8 @@ describe('KafkaBatchConsumer', () => {
|
|
|
1037
1046
|
});
|
|
1038
1047
|
|
|
1039
1048
|
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
|
1049
|
+
brokers: 'broker1:9092,broker2:9092',
|
|
1050
|
+
clientId: 'integration-client',
|
|
1040
1051
|
authentication: 'scram-sha-256',
|
|
1041
1052
|
username: 'integration-user',
|
|
1042
1053
|
password: 'integration-pass',
|
|
@@ -29,32 +29,15 @@ export class KafkaBatchConsumer implements INodeType {
|
|
|
29
29
|
},
|
|
30
30
|
inputs: ['main'],
|
|
31
31
|
outputs: ['main'],
|
|
32
|
-
// Credentials reference -
|
|
33
|
-
// Optional: allows unauthenticated connections
|
|
32
|
+
// Credentials reference - required for brokers and clientId configuration
|
|
34
33
|
credentials: [
|
|
35
34
|
{
|
|
36
35
|
name: 'kafka',
|
|
37
|
-
required:
|
|
36
|
+
required: true,
|
|
38
37
|
},
|
|
39
38
|
],
|
|
40
39
|
// Define all Kafka configuration properties
|
|
41
40
|
properties: [
|
|
42
|
-
{
|
|
43
|
-
displayName: 'Brokers',
|
|
44
|
-
name: 'brokers',
|
|
45
|
-
type: 'string',
|
|
46
|
-
default: 'localhost:9092',
|
|
47
|
-
required: true,
|
|
48
|
-
description: 'Comma-separated list of Kafka broker addresses',
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
displayName: 'Client ID',
|
|
52
|
-
name: 'clientId',
|
|
53
|
-
type: 'string',
|
|
54
|
-
default: 'n8n-kafka-batch-consumer',
|
|
55
|
-
required: true,
|
|
56
|
-
description: 'Unique identifier for this Kafka client',
|
|
57
|
-
},
|
|
58
41
|
{
|
|
59
42
|
displayName: 'Group ID',
|
|
60
43
|
name: 'groupId',
|
|
@@ -124,12 +107,9 @@ export class KafkaBatchConsumer implements INodeType {
|
|
|
124
107
|
* Handles the complete workflow: credentials, connection, consumption, and error handling
|
|
125
108
|
*/
|
|
126
109
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
127
|
-
const items = this.getInputData();
|
|
128
110
|
const returnData: INodeExecutionData[] = [];
|
|
129
111
|
|
|
130
112
|
// Get all node parameters from N8N configuration
|
|
131
|
-
const brokers = this.getNodeParameter('brokers', 0) as string;
|
|
132
|
-
const clientId = this.getNodeParameter('clientId', 0) as string;
|
|
133
113
|
const groupId = this.getNodeParameter('groupId', 0) as string;
|
|
134
114
|
const topic = this.getNodeParameter('topic', 0) as string;
|
|
135
115
|
const batchSize = this.getNodeParameter('batchSize', 0) as number;
|
|
@@ -143,57 +123,59 @@ export class KafkaBatchConsumer implements INodeType {
|
|
|
143
123
|
const readTimeout = options.readTimeout || 60000;
|
|
144
124
|
const parseJson = options.parseJson !== undefined ? options.parseJson : true;
|
|
145
125
|
|
|
146
|
-
// Parse comma-separated brokers string to array
|
|
147
|
-
const brokerList = brokers.split(',').map((b) => b.trim());
|
|
148
|
-
|
|
149
126
|
/**
|
|
150
127
|
* Step 2: Credentials Retrieval and Kafka Configuration
|
|
151
128
|
* Build KafkaJS configuration with optional authentication
|
|
152
129
|
* Supports SASL (PLAIN, SCRAM-SHA-256, SCRAM-SHA-512) and SSL/TLS
|
|
130
|
+
* Brokers and clientId are now taken from credentials
|
|
153
131
|
*/
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
clientId,
|
|
157
|
-
brokers: brokerList,
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
// Attempt to retrieve optional Kafka credentials
|
|
132
|
+
|
|
133
|
+
// Attempt to retrieve Kafka credentials (required for brokers and clientId)
|
|
161
134
|
let credentials: any = null;
|
|
162
135
|
try {
|
|
163
136
|
credentials = await this.getCredentials('kafka');
|
|
164
137
|
} catch (error) {
|
|
165
|
-
|
|
138
|
+
throw new NodeOperationError(
|
|
139
|
+
this.getNode(),
|
|
140
|
+
'Kafka credentials are required to get brokers and clientId configuration'
|
|
141
|
+
);
|
|
166
142
|
}
|
|
167
143
|
|
|
144
|
+
// Build base Kafka configuration from credentials
|
|
145
|
+
const kafkaConfig: any = {
|
|
146
|
+
clientId: credentials.clientId || 'n8n-kafka-batch-consumer',
|
|
147
|
+
brokers: credentials.brokers ?
|
|
148
|
+
(typeof credentials.brokers === 'string' ?
|
|
149
|
+
credentials.brokers.split(',').map((b: string) => b.trim()) :
|
|
150
|
+
credentials.brokers) :
|
|
151
|
+
['localhost:9092'],
|
|
152
|
+
};
|
|
153
|
+
|
|
168
154
|
// Map N8N credential fields to KafkaJS authentication format
|
|
169
|
-
// Add authentication if
|
|
170
|
-
if (credentials) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
password: credentials.password,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
155
|
+
// Add SASL authentication if provided
|
|
156
|
+
if (credentials.authentication) {
|
|
157
|
+
kafkaConfig.sasl = {
|
|
158
|
+
mechanism: credentials.authentication, // PLAIN, SCRAM-SHA-256, or SCRAM-SHA-512
|
|
159
|
+
username: credentials.username,
|
|
160
|
+
password: credentials.password,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
180
163
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
164
|
+
// Add SSL/TLS configuration for encrypted connections
|
|
165
|
+
if (credentials.ssl !== undefined) {
|
|
166
|
+
kafkaConfig.ssl = {
|
|
167
|
+
rejectUnauthorized: credentials.ssl, // Validate server certificates
|
|
168
|
+
};
|
|
186
169
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
170
|
+
// Add optional SSL certificates for mutual TLS authentication
|
|
171
|
+
if (credentials.ca) {
|
|
172
|
+
kafkaConfig.ssl.ca = credentials.ca; // Certificate Authority
|
|
173
|
+
}
|
|
174
|
+
if (credentials.cert) {
|
|
175
|
+
kafkaConfig.ssl.cert = credentials.cert; // Client certificate
|
|
176
|
+
}
|
|
177
|
+
if (credentials.key) {
|
|
178
|
+
kafkaConfig.ssl.key = credentials.key; // Client private key
|
|
197
179
|
}
|
|
198
180
|
}
|
|
199
181
|
|