@itentialopensource/adapter-kafkav2 0.3.13 → 0.3.15

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/CHANGELOG.md CHANGED
@@ -1,4 +1,20 @@
1
1
 
2
+ ## 0.3.15 [02-17-2023]
3
+
4
+ * Updated readme
5
+
6
+ See merge request itentialopensource/adapters/notification-messaging/adapter-kafkav2!2
7
+
8
+ ---
9
+
10
+ ## 0.3.14 [02-16-2023]
11
+
12
+ * updated to allow multiple subscribers per topic entry and fixed partition logic
13
+
14
+ See merge request itentialopensource/adapters/notification-messaging/adapter-kafkav2!1
15
+
16
+ ---
17
+
2
18
  ## 0.3.13 [02-07-2023]
3
19
 
4
20
  * Bug fixes and performance improvements
package/README.md CHANGED
@@ -148,9 +148,66 @@ Example of adapter's configuration with filtering applied:
148
148
  }
149
149
  ],
150
150
  ```
151
-
152
151
  When operator skips to provide `filters` property or the property contains no actual filters set, then all messages are passed.
153
152
 
153
+ Example of adapter's configuration with multiple paritions and subscribers per topic:
154
+ ```json
155
+ "topics": [
156
+ {
157
+ "name": "topic1",
158
+ "always": true,
159
+ "partition": 0,
160
+ "subscriberInfo": [
161
+ {
162
+ "subname": "default",
163
+ "filters": [
164
+ "PIXI",
165
+ "DRED",
166
+ "F: (\\w+), L: (\\w+)",
167
+ "\\d{3,4}"
168
+ ],
169
+ "rabbit": "topic1",
170
+ "throttle": {}
171
+ },
172
+ {
173
+ "subname": "sub2",
174
+ "filters": [
175
+ "[abc]",
176
+ "F: (\\w+), L: (\\w+)",
177
+ "\\d{3,4}"
178
+ ],
179
+ "rabbit": "topic1-s2",
180
+ "throttle": {}
181
+ }
182
+ ]
183
+ },
184
+ {
185
+ "name": "topic1",
186
+ "always": true,
187
+ "partition": 1,
188
+ "subscriberInfo": [
189
+ {
190
+ "subname": "default",
191
+ "filters": [
192
+ "PIXI",
193
+ "DRED",
194
+ "[abc]",
195
+ "F: (\\w+), L: (\\w+)",
196
+ "\\d{3,4}"
197
+ ],
198
+ "rabbit": "topic1-p1",
199
+ "throttle": {}
200
+ }
201
+ ]
202
+ },
203
+ {
204
+ "name": "test-6",
205
+ "always": true
206
+ }
207
+ ],
208
+ ```
209
+ Note that if no parition is supplied only messages on partition 0 will be consumed. Additionally, if no rabbit topic is supplied, events will be published to a topic with the same name as the Kafka topic. For example, topic `test-6` above will be published to the `test-6` rabbit topic.
210
+
154
211
  ### Connection Properties
155
212
 
156
213
  These base properties are used to connect to Kafka upon the adapter initially coming up. It is important to set these properties appropriately.
@@ -211,6 +268,10 @@ The following properties are used to define the Kafka Consumer. These properties
211
268
  | encoding | If set to 'buffer', values will be returned as raw buffer objects..|
212
269
  | keyEncoding | |
213
270
 
271
+ ## Parsing Properties
272
+
273
+ The `parseMessage` property allows the user to define how they want the Kafka message to be published to IAP's event system. If `parseMessage` is set to true or omitted, the value of the Kafka message will be parsed as either an object or string and wrapped in an outer object. The wrapper object's key can be defined with the property `wrapMessage`, or the default value `payload` can be used if omitted. If `parseMessage` is set to false, the entire kafka payload, including metadata, would be returned and the message itself would need to be transformed at a later point.
274
+
214
275
  ## Testing an Itential Product Adapter
215
276
 
216
277
  Mocha is generally used to test all Itential Product Adapters. There are unit tests as well as integration tests performed. Integration tests can generally be run as standalone using mock data and running the adapter in stub mode, or as integrated. When running integrated, every effort is made to prevent environmental failures, however there is still a possibility.
package/adapter.js CHANGED
@@ -217,7 +217,7 @@ class Kafkav2 extends EventEmitterCl {
217
217
  {
218
218
  subname: 'default',
219
219
  filters: [],
220
- rabbit: 'kafka',
220
+ rabbit: this.topicsEvents[t].topic,
221
221
  throttle: {}
222
222
  }
223
223
  ];
@@ -260,7 +260,7 @@ class Kafkav2 extends EventEmitterCl {
260
260
  {
261
261
  subname: 'default',
262
262
  filters: [],
263
- rabbit: 'kafka',
263
+ rabbit: this.props.topics[t].name,
264
264
  throttle: {}
265
265
  }
266
266
  ];
@@ -293,7 +293,7 @@ class Kafkav2 extends EventEmitterCl {
293
293
  // check to see if we need to add a subscriber
294
294
  for (let sub = 0; sub < subInfo.length; sub += 1) {
295
295
  let subFound = false;
296
- for (let s = 0; s <= this.topicsEvents[te].subInfo.length; s += 1) {
296
+ for (let s = 0; s < this.topicsEvents[te].subInfo.length; s += 1) {
297
297
  // if the subscriber is found, no need to add anything
298
298
  if (subInfo[sub].subname === this.topicsEvents[te].subInfo[s].subname) {
299
299
  subFound = true;
@@ -407,13 +407,13 @@ class Kafkav2 extends EventEmitterCl {
407
407
  try {
408
408
  // Create a kafka client
409
409
  const combinedProps = this.props.client || {};
410
- if (this.props.client.ssl.ca) {
410
+ if (this.props.client.ssl && this.props.client.ssl.ca) {
411
411
  combinedProps.ssl.ca = [fs.readFileSync(this.props.client.ssl.ca, 'utf-8')];
412
412
  }
413
- if (this.props.client.ssl.cert) {
413
+ if (this.props.client.ssl && this.props.client.ssl.cert) {
414
414
  combinedProps.ssl.cert = fs.readFileSync(this.props.client.ssl.cert, 'utf-8');
415
415
  }
416
- if (this.props.client.ssl.key) {
416
+ if (this.props.client.ssl && this.props.client.ssl.key) {
417
417
  combinedProps.ssl.key = fs.readFileSync(this.props.client.ssl.key, 'utf-8');
418
418
  }
419
419
 
@@ -423,22 +423,18 @@ class Kafkav2 extends EventEmitterCl {
423
423
 
424
424
  this.consumer = this.KafkaClient.consumer(this.props.consumer || {});
425
425
 
426
- const isPassingFiltering = (topic, message) => {
426
+ const isPassingFiltering = (topic, message, filters) => {
427
427
  const topicConfig = this.topicsEvents.find((e) => e.topic === topic);
428
428
 
429
429
  if (!topicConfig) return true;
430
430
 
431
431
  if (!topicConfig.subInfo || !Array.isArray(topicConfig.subInfo)) return true;
432
-
433
- const allFilters = topicConfig.subInfo.filter((e) => Array.isArray(e.filters) && e.filters.length > 0);
434
- if (allFilters.length === 0) return true;
435
- const matched = allFilters.find((s) => {
436
- for (let f = 0; f < s.filters.length; f += 1) {
437
- const re = new RegExp(s.filters[f]);
438
- if (message.value.toString().match(re)) {
439
- log.debug('Matched filter: ', re);
440
- return true;
441
- }
432
+ if (filters.length === 0) return true;
433
+ const matched = filters.find((filter) => {
434
+ const re = new RegExp(filter);
435
+ if (message.value.toString().match(re)) {
436
+ log.debug('Matched filter: ', re);
437
+ return true;
442
438
  }
443
439
  return false;
444
440
  });
@@ -463,27 +459,8 @@ class Kafkav2 extends EventEmitterCl {
463
459
  log.info(`PROCESSING NEW MESSAGE ON TOPIC: ${topic} PARTITION: ${partition} WITH OFFSET: ${message.offset}`);
464
460
  const newMsg = message;
465
461
  let avroUsed = false;
466
- let desiredTopic = 'kafka';
467
-
468
- // find the topic here to change the offset
469
- for (let t = 0; t < this.topicsEvents.length; t += 1) {
470
- if (this.topicsEvents[t].topic === topic && this.topicsEvents[t].partition === partition) {
471
- if (this.topicsEvents[t].offset < message.offset) {
472
- this.topicsEvents[t].offset = message.offset;
473
- }
474
- // determine if we are using AVRO and set flag
475
- if (this.topicsEvents[t].avro && this.topicsEvents[t].avro.toUpperCase() === 'YES') {
476
- avroUsed = true;
477
- }
478
- // set desired topic
479
- if (this.topicsEvents[t].subInfo && this.topicsEvents[t].subInfo.rabbit) {
480
- desiredTopic = this.topicsEvents[t].subInfo.rabbit;
481
- } else {
482
- desiredTopic = this.topicsEvents[t].topic;
483
- }
484
- break;
485
- }
486
- }
462
+ let desiredTopic = [];
463
+ let allowedPartition = false;
487
464
 
488
465
  // if using avro
489
466
  if (this.registry && avroUsed) {
@@ -514,18 +491,45 @@ class Kafkav2 extends EventEmitterCl {
514
491
  }
515
492
  }, fastInt);
516
493
  }
494
+ // find the topic here to change the offset
495
+ for (let t = 0; t < this.topicsEvents.length; t += 1) {
496
+ if (this.topicsEvents[t].topic === topic && this.topicsEvents[t].partition === partition) {
497
+ allowedPartition = true;
498
+ if (this.topicsEvents[t].offset < message.offset) {
499
+ this.topicsEvents[t].offset = message.offset;
500
+ }
501
+ // determine if we are using AVRO and set flag
502
+ if (this.topicsEvents[t].avro && this.topicsEvents[t].avro.toUpperCase() === 'YES') {
503
+ avroUsed = true;
504
+ }
505
+ // set desired topic for messages passing filtering
506
+ if (this.topicsEvents[t].subInfo) {
507
+ for (let k = 0; k < this.topicsEvents[t].subInfo.length; k += 1) {
508
+ let passesFiltering = true;
509
+ if (this.topicsEvents[t].subInfo[k].filters) {
510
+ passesFiltering = isPassingFiltering(topic, newMsg, this.topicsEvents[t].subInfo[k].filters);
511
+ }
512
+ if (this.topicsEvents[t].subInfo[k].rabbit && passesFiltering) {
513
+ desiredTopic.push(this.topicsEvents[t].subInfo[k].rabbit);
514
+ } else if (passesFiltering) {
515
+ desiredTopic.push(this.topicsEvents[t].topic);
516
+ }
517
+ }
518
+ }
519
+ break;
520
+ }
521
+ }
517
522
 
518
- const goodMsg = isPassingFiltering(topic, newMsg);
519
- if (goodMsg) {
523
+ if (desiredTopic.length !== 0 && allowedPartition) {
520
524
  log.info(`Message: '${message.value.toString()}' being CONSUMED as it does meet the filter condition`);
521
- if (mytopics.includes(desiredTopic)) {
525
+ if (desiredTopic.every((val) => mytopics.includes(val))) {
522
526
  log.info(`Emitting: ${message.value.toString()} TO ${desiredTopic}`);
523
527
  } else {
524
528
  log.info(`The desired topic ${desiredTopic} does not exist. If it should, make sure it is in pronghorn.json`);
525
529
  log.info(`Emitting: ${newMsg.value.toString()} TO kafka`);
526
530
  desiredTopic = 'kafka';
527
531
  }
528
- if (this.props.parseMessage) {
532
+ if (this.props.parseMessage || this.props.parseMessage === undefined) {
529
533
  let messageObj;
530
534
  const wrapper = this.props.wrapMessage || 'payload';
531
535
  try {
@@ -533,9 +537,13 @@ class Kafkav2 extends EventEmitterCl {
533
537
  } catch (ex) {
534
538
  messageObj = { [wrapper]: newMsg.value.toString() };
535
539
  }
536
- eventSystem.publish(desiredTopic, messageObj);
540
+ for (let j = 0; j < desiredTopic.length; j += 1) {
541
+ eventSystem.publish(desiredTopic[j], messageObj);
542
+ }
537
543
  } else {
538
- eventSystem.publish(desiredTopic, newMsg.value.toString());
544
+ for (let j = 0; j < desiredTopic.length; j += 1) {
545
+ eventSystem.publish(desiredTopic[j], newMsg);
546
+ }
539
547
  }
540
548
  } else {
541
549
  log.info(`Message: '${newMsg.value.toString()}' being DROPPED as it fails to meet any filter condition`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itentialopensource/adapter-kafkav2",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
4
4
  "description": "Itential adapter to connect to kafka",
5
5
  "main": "adapter.js",
6
6
  "wizardVersion": "2.35.0",
package/pronghorn.json CHANGED
@@ -9,9 +9,7 @@
9
9
  "admin"
10
10
  ],
11
11
  "topics": {
12
- "kafka": {},
13
- "first_kafka_topic": {},
14
- "test-topic": {}
12
+ "kafka": {}
15
13
  },
16
14
  "methods": [
17
15
  {
Binary file