@cryptexlabs/codex-nodejs-common 0.2.3 → 0.3.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.
Files changed (121) hide show
  1. package/lib/package.json +2 -2
  2. package/lib/src/auth/index.js +5 -1
  3. package/lib/src/auth/index.js.map +1 -1
  4. package/lib/src/client/index.js +5 -1
  5. package/lib/src/client/index.js.map +1 -1
  6. package/lib/src/config/default-config.js +5 -5
  7. package/lib/src/config/default-config.js.map +1 -1
  8. package/lib/src/config/index.js +5 -1
  9. package/lib/src/config/index.js.map +1 -1
  10. package/lib/src/config/kafka-config.d.ts +2 -1
  11. package/lib/src/config/kafka-config.interface.d.ts +1 -0
  12. package/lib/src/config/kafka-config.js +2 -1
  13. package/lib/src/config/kafka-config.js.map +1 -1
  14. package/lib/src/context/context.builder.js +2 -2
  15. package/lib/src/context/context.builder.js.map +1 -1
  16. package/lib/src/context/index.js +5 -1
  17. package/lib/src/context/index.js.map +1 -1
  18. package/lib/src/decorator/api-meta-headers.js +12 -12
  19. package/lib/src/decorator/api-meta-headers.js.map +1 -1
  20. package/lib/src/decorator/api-pagination.js +3 -3
  21. package/lib/src/decorator/api-pagination.js.map +1 -1
  22. package/lib/src/decorator/index.js +5 -1
  23. package/lib/src/decorator/index.js.map +1 -1
  24. package/lib/src/event/event-handler.d.ts +1 -1
  25. package/lib/src/event/event-handler.js +4 -1
  26. package/lib/src/event/event-handler.js.map +1 -1
  27. package/lib/src/event/index.js +5 -1
  28. package/lib/src/event/index.js.map +1 -1
  29. package/lib/src/event/websocket-event.handler.js +2 -2
  30. package/lib/src/event/websocket-event.handler.js.map +1 -1
  31. package/lib/src/exception/index.js +5 -1
  32. package/lib/src/exception/index.js.map +1 -1
  33. package/lib/src/filter/app-http-exception-filter.js +2 -2
  34. package/lib/src/filter/app-http-exception-filter.js.map +1 -1
  35. package/lib/src/filter/http-status.interceptor.js +2 -2
  36. package/lib/src/filter/http-status.interceptor.js.map +1 -1
  37. package/lib/src/filter/index.js +5 -1
  38. package/lib/src/filter/index.js.map +1 -1
  39. package/lib/src/index.js +5 -1
  40. package/lib/src/index.js.map +1 -1
  41. package/lib/src/logger/context.logger.js +1 -1
  42. package/lib/src/logger/context.logger.js.map +1 -1
  43. package/lib/src/logger/custom.logger.js +1 -1
  44. package/lib/src/logger/custom.logger.js.map +1 -1
  45. package/lib/src/logger/index.js +5 -1
  46. package/lib/src/logger/index.js.map +1 -1
  47. package/lib/src/message/index.js +5 -1
  48. package/lib/src/message/index.js.map +1 -1
  49. package/lib/src/message/message-meta.js +1 -1
  50. package/lib/src/message/message-meta.js.map +1 -1
  51. package/lib/src/pipe/country-code.pipe.js +1 -1
  52. package/lib/src/pipe/country-code.pipe.js.map +1 -1
  53. package/lib/src/pipe/csv.pipe.js +1 -1
  54. package/lib/src/pipe/csv.pipe.js.map +1 -1
  55. package/lib/src/pipe/index.d.ts +2 -0
  56. package/lib/src/pipe/index.js +7 -1
  57. package/lib/src/pipe/index.js.map +1 -1
  58. package/lib/src/pipe/joi-http-validation.pipe.js +1 -1
  59. package/lib/src/pipe/joi-http-validation.pipe.js.map +1 -1
  60. package/lib/src/pipe/language-code.pipe.js +1 -1
  61. package/lib/src/pipe/language-code.pipe.js.map +1 -1
  62. package/lib/src/pipe/not-empty.pipe.js +1 -1
  63. package/lib/src/pipe/not-empty.pipe.js.map +1 -1
  64. package/lib/src/pipe/uuidv4.array.validation.pipe.js +1 -1
  65. package/lib/src/pipe/uuidv4.array.validation.pipe.js.map +1 -1
  66. package/lib/src/pipe/uuidv4.validation.pipe.d.ts +6 -0
  67. package/lib/src/pipe/uuidv4.validation.pipe.js +32 -0
  68. package/lib/src/pipe/uuidv4.validation.pipe.js.map +1 -0
  69. package/lib/src/response/healthz-response.js +1 -1
  70. package/lib/src/response/healthz-response.js.map +1 -1
  71. package/lib/src/response/index.js +5 -1
  72. package/lib/src/response/index.js.map +1 -1
  73. package/lib/src/result/index.js +5 -1
  74. package/lib/src/result/index.js.map +1 -1
  75. package/lib/src/service/consumer/consumer-service-delegate.interface.d.ts +2 -0
  76. package/lib/src/service/consumer/consumer.service.d.ts +1 -0
  77. package/lib/src/service/consumer/consumer.service.js +23 -10
  78. package/lib/src/service/consumer/consumer.service.js.map +1 -1
  79. package/lib/src/service/consumer/index.js +5 -1
  80. package/lib/src/service/consumer/index.js.map +1 -1
  81. package/lib/src/service/consumer/websocket-consumer.service.js +5 -5
  82. package/lib/src/service/consumer/websocket-consumer.service.js.map +1 -1
  83. package/lib/src/service/elasticsearch/elasticsearch-healthz.service.js +5 -5
  84. package/lib/src/service/elasticsearch/elasticsearch-healthz.service.js.map +1 -1
  85. package/lib/src/service/elasticsearch/index.js +5 -1
  86. package/lib/src/service/elasticsearch/index.js.map +1 -1
  87. package/lib/src/service/healthz/healthz.service.js +3 -3
  88. package/lib/src/service/healthz/healthz.service.js.map +1 -1
  89. package/lib/src/service/healthz/index.js +5 -1
  90. package/lib/src/service/healthz/index.js.map +1 -1
  91. package/lib/src/service/index.js +5 -1
  92. package/lib/src/service/index.js.map +1 -1
  93. package/lib/src/service/kafka/index.d.ts +1 -0
  94. package/lib/src/service/kafka/index.js +6 -1
  95. package/lib/src/service/kafka/index.js.map +1 -1
  96. package/lib/src/service/kafka/kafka.replay.messages.d.ts +10 -0
  97. package/lib/src/service/kafka/kafka.replay.messages.js +42 -0
  98. package/lib/src/service/kafka/kafka.replay.messages.js.map +1 -0
  99. package/lib/src/service/kafka/kafka.service.d.ts +2 -0
  100. package/lib/src/service/kafka/kafka.service.js +83 -3
  101. package/lib/src/service/kafka/kafka.service.js.map +1 -1
  102. package/lib/src/service/kafka/kafka.stub.service.js +1 -1
  103. package/lib/src/service/kafka/kafka.stub.service.js.map +1 -1
  104. package/lib/src/service/socket/index.js +5 -1
  105. package/lib/src/service/socket/index.js.map +1 -1
  106. package/lib/src/service/socket/websocket.gateway.js +8 -8
  107. package/lib/src/service/socket/websocket.gateway.js.map +1 -1
  108. package/lib/src/util/index.js +5 -1
  109. package/lib/src/util/index.js.map +1 -1
  110. package/package.json +2 -2
  111. package/src/config/default-config.ts +2 -1
  112. package/src/config/kafka-config.interface.ts +1 -0
  113. package/src/config/kafka-config.ts +4 -1
  114. package/src/event/event-handler.ts +5 -2
  115. package/src/pipe/index.ts +2 -0
  116. package/src/pipe/uuidv4.validation.pipe.ts +23 -0
  117. package/src/service/consumer/consumer-service-delegate.interface.ts +2 -0
  118. package/src/service/consumer/consumer.service.ts +26 -3
  119. package/src/service/kafka/index.ts +1 -0
  120. package/src/service/kafka/kafka.replay.messages.ts +33 -0
  121. package/src/service/kafka/kafka.service.ts +145 -0
@@ -2,6 +2,7 @@ import { Inject, Injectable, LoggerService } from "@nestjs/common";
2
2
  import {
3
3
  Admin,
4
4
  Consumer,
5
+ EachMessagePayload,
5
6
  ITopicConfig,
6
7
  Kafka,
7
8
  Message,
@@ -115,6 +116,39 @@ export class KafkaService implements ConsumerServiceDelegateInterface {
115
116
  });
116
117
  }
117
118
 
119
+ public async startManualConsumer(
120
+ topics: (string | RegExp)[],
121
+ callback: (topic: string, message: Message) => Promise<void>
122
+ ): Promise<void> {
123
+ await this._setManualConsume();
124
+
125
+ const promises = [];
126
+ for (const topic of topics) {
127
+ promises.push(
128
+ this._kafkaConsumer.subscribe({ topic, fromBeginning: true })
129
+ );
130
+ }
131
+ try {
132
+ await Promise.all(promises);
133
+
134
+ await this._ensureTopicsExist(topics);
135
+
136
+ const results = [
137
+ new Promise((resolve) => {
138
+ this._kafkaConsumer.run({
139
+ eachMessage: async (messagePayload: EachMessagePayload) => {
140
+ const { topic, partition, message } = messagePayload;
141
+ await callback(topic, message);
142
+ },
143
+ });
144
+ }),
145
+ ];
146
+ await Promise.all(results);
147
+ } catch (e) {
148
+ this.logger.error(e.message, e.trace);
149
+ }
150
+ }
151
+
118
152
  private async _ensureTopicsExist(topics: (string | RegExp)[]): Promise<void> {
119
153
  const stringTopics = [];
120
154
  for (const topic of topics) {
@@ -165,4 +199,115 @@ export class KafkaService implements ConsumerServiceDelegateInterface {
165
199
  messages,
166
200
  });
167
201
  }
202
+
203
+ private async _setManualConsume(): Promise<void> {
204
+ /* Based on solution provided here:
205
+ * https://github.com/tulios/kafkajs/issues/825#issuecomment-674106799
206
+ *
207
+ * 1. We need to know which partitions we are assigned.
208
+ * 2. Which partitions have we consumed the last offset for
209
+ * 3. If all partitions have 0 lag, we exit.
210
+ */
211
+
212
+ /*
213
+ * `consumedTopicPartitions` will be an object of all topic-partitions
214
+ * and a boolean indicating whether or not we have consumed all
215
+ * messages in that topic-partition. For example:
216
+ *
217
+ * {
218
+ * "topic-test-0": false,
219
+ * "topic-test-1": false,
220
+ * "topic-test-2": false
221
+ * }
222
+ */
223
+ let consumedTopicPartitions = {};
224
+ this._kafkaConsumer.on(
225
+ this._kafkaConsumer.events.GROUP_JOIN,
226
+ async ({ payload }) => {
227
+ const { memberAssignment } = payload;
228
+
229
+ consumedTopicPartitions = Object.entries(memberAssignment).reduce(
230
+ (topics, [topic, partitions]) => {
231
+ for (const partition in partitions) {
232
+ this.logger.debug("topic-partition: ", `${topic}-${partition}`);
233
+ topics[`${topic}-${partition}`] = false;
234
+ }
235
+ return topics;
236
+ },
237
+ {}
238
+ );
239
+ }
240
+ );
241
+
242
+ /*
243
+ * This is extremely unergonomic, but if we are currently caught up to the head
244
+ * of all topic-partitions, we won't actually get any batches, which means we'll
245
+ * never find out that we are actually caught up. So as a workaround, what we can do
246
+ * is to check in `FETCH_START` if we have previously made a fetch without
247
+ * processing any batches in between. If so, it means that we received empty
248
+ * fetch responses, meaning there was no more data to fetch.
249
+ *
250
+ * We need to initially set this to true, or we would immediately exit.
251
+ */
252
+ let processedBatch = true;
253
+ this._kafkaConsumer.on(this._kafkaConsumer.events.FETCH_START, async () => {
254
+ if (processedBatch === false) {
255
+ await this._kafkaConsumer.disconnect();
256
+ process.exit(0);
257
+ }
258
+
259
+ processedBatch = false;
260
+ });
261
+
262
+ /*
263
+ * Now whenever we have finished processing a batch, we'll update `consumedTopicPartitions`
264
+ * and exit if all topic-partitions have been consumed,
265
+ */
266
+ this._kafkaConsumer.on(
267
+ this._kafkaConsumer.events.END_BATCH_PROCESS,
268
+ async ({ payload }) => {
269
+ const { topic, partition, offsetLag } = payload;
270
+
271
+ consumedTopicPartitions[`${topic}-${partition}`] = offsetLag === "0";
272
+
273
+ if (
274
+ Object.values(consumedTopicPartitions).every((consumed) =>
275
+ Boolean(consumed)
276
+ )
277
+ ) {
278
+ await this._kafkaConsumer.disconnect();
279
+ process.exit(0);
280
+ }
281
+
282
+ processedBatch = true;
283
+ }
284
+ );
285
+
286
+ // Graceful shutdown
287
+ const errorTypes = ["unhandledRejection", "uncaughtException"];
288
+ const signalTraps: NodeJS.Signals[] = ["SIGTERM", "SIGINT", "SIGUSR2"];
289
+
290
+ errorTypes.map((type) => {
291
+ process.on(type, async (e) => {
292
+ try {
293
+ this.logger.debug(`process.on ${type}`);
294
+ this.logger.error(e);
295
+ await this.disconnect();
296
+ process.exit(0);
297
+ } catch (_) {
298
+ process.exit(1);
299
+ }
300
+ });
301
+ });
302
+
303
+ signalTraps.map((type) => {
304
+ process.once(type, async () => {
305
+ try {
306
+ await this.disconnect();
307
+ } finally {
308
+ process.kill(process.pid, type);
309
+ }
310
+ });
311
+ });
312
+ }
168
313
  }