@uber-clone/common 1.0.2 → 1.0.3
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/build/events/event-types/user-created-event.d.ts +9 -0
- package/build/events/event-types/user-created-event.js +2 -0
- package/build/events/kafka-listener.d.ts +34 -0
- package/build/events/kafka-listener.js +89 -0
- package/build/events/kafka-publisher.d.ts +28 -0
- package/build/events/kafka-publisher.js +67 -0
- package/build/events/subjects.d.ts +3 -0
- package/build/events/subjects.js +7 -0
- package/build/index.d.ts +4 -0
- package/build/index.js +4 -0
- package/build/middlewares/current-user.js +0 -1
- package/package.json +2 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Kafka, EachMessagePayload } from "kafkajs";
|
|
2
|
+
import { Subjects } from "./subjects";
|
|
3
|
+
interface Event {
|
|
4
|
+
subject: Subjects;
|
|
5
|
+
data: any;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Abstract class for a Kafka Consumer (Listener).
|
|
9
|
+
* It abstracts away Kafka connection, topic subscription, and message parsing.
|
|
10
|
+
* The equivalent of NATS 'queueGroupName' is the Kafka 'groupId'.
|
|
11
|
+
*/
|
|
12
|
+
export declare abstract class KafkaListener<T extends Event> {
|
|
13
|
+
abstract subject: T["subject"];
|
|
14
|
+
abstract groupId: string;
|
|
15
|
+
abstract onMessage(data: T["data"], messagePayload: EachMessagePayload): void;
|
|
16
|
+
private kafka;
|
|
17
|
+
private consumer;
|
|
18
|
+
constructor(kafka: Kafka);
|
|
19
|
+
/**
|
|
20
|
+
* Connects the consumer and subscribes to the topic.
|
|
21
|
+
* FIX: The consumer is now initialized here, where 'this.groupId' is guaranteed to be set
|
|
22
|
+
* by the subclass constructor before the 'listen()' method is called.
|
|
23
|
+
*/
|
|
24
|
+
listen(): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Converts the message value (Buffer) into a parsed JSON object.
|
|
27
|
+
*/
|
|
28
|
+
private parseMessage;
|
|
29
|
+
/**
|
|
30
|
+
* Graceful shutdown function.
|
|
31
|
+
*/
|
|
32
|
+
close(): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.KafkaListener = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* Abstract class for a Kafka Consumer (Listener).
|
|
15
|
+
* It abstracts away Kafka connection, topic subscription, and message parsing.
|
|
16
|
+
* The equivalent of NATS 'queueGroupName' is the Kafka 'groupId'.
|
|
17
|
+
*/
|
|
18
|
+
class KafkaListener {
|
|
19
|
+
// No changes needed here, as it only stores the Kafka client instance.
|
|
20
|
+
constructor(kafka) {
|
|
21
|
+
this.kafka = kafka;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Connects the consumer and subscribes to the topic.
|
|
25
|
+
* FIX: The consumer is now initialized here, where 'this.groupId' is guaranteed to be set
|
|
26
|
+
* by the subclass constructor before the 'listen()' method is called.
|
|
27
|
+
*/
|
|
28
|
+
listen() {
|
|
29
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
try {
|
|
31
|
+
// Initialize the consumer instance using the abstract 'groupId'
|
|
32
|
+
this.consumer = this.kafka.consumer({ groupId: this.groupId });
|
|
33
|
+
yield this.consumer.connect();
|
|
34
|
+
// Subscribe to the defined topic
|
|
35
|
+
yield this.consumer.subscribe({
|
|
36
|
+
topic: this.subject,
|
|
37
|
+
fromBeginning: true, // Similar to NATS setDeliverAllAvailable
|
|
38
|
+
});
|
|
39
|
+
console.log(`Kafka Consumer connected and listening to topic: ${this.subject} with group: ${this.groupId}`);
|
|
40
|
+
// Run the consumer to process messages
|
|
41
|
+
yield this.consumer.run({
|
|
42
|
+
eachMessage: (payload) => __awaiter(this, void 0, void 0, function* () {
|
|
43
|
+
const { topic, partition, message } = payload;
|
|
44
|
+
console.log(`Message received: ${topic} / partition ${partition}`);
|
|
45
|
+
// Kafka data is always a Buffer or null, so we must parse it.
|
|
46
|
+
const parsedData = this.parseMessage(message.value);
|
|
47
|
+
// Pass the parsed data and the full payload to the implementation
|
|
48
|
+
this.onMessage(parsedData, payload);
|
|
49
|
+
// Note: Offset is automatically committed by kafkajs
|
|
50
|
+
// after onMessage completes successfully.
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
console.error("Kafka Listener Error:", err);
|
|
56
|
+
// Exit or retry connection logic would go here
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Converts the message value (Buffer) into a parsed JSON object.
|
|
62
|
+
*/
|
|
63
|
+
parseMessage(value) {
|
|
64
|
+
if (!value) {
|
|
65
|
+
console.warn("Received null message value. Returning empty object.");
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
return JSON.parse(value.toString("utf8"));
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
console.error("Failed to parse Kafka message value.", e);
|
|
73
|
+
throw new Error("Invalid message format received from Kafka.");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Graceful shutdown function.
|
|
78
|
+
*/
|
|
79
|
+
close() {
|
|
80
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
81
|
+
// Only try to disconnect if the consumer was actually initialized
|
|
82
|
+
if (this.consumer) {
|
|
83
|
+
yield this.consumer.disconnect();
|
|
84
|
+
console.log(`Kafka Consumer for group ${this.groupId} disconnected.`);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.KafkaListener = KafkaListener;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Kafka } from "kafkajs";
|
|
2
|
+
import { Subjects } from "./subjects";
|
|
3
|
+
interface Event {
|
|
4
|
+
subject: Subjects;
|
|
5
|
+
data: any;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Abstract class for a Kafka Producer (Publisher).
|
|
9
|
+
* It abstracts away Kafka connection and message serialization.
|
|
10
|
+
*/
|
|
11
|
+
export declare abstract class KafkaPublisher<T extends Event> {
|
|
12
|
+
abstract subject: T["subject"];
|
|
13
|
+
private producer;
|
|
14
|
+
constructor(kafka: Kafka);
|
|
15
|
+
/**
|
|
16
|
+
* Connects the producer to the Kafka broker. Call this before publishing.
|
|
17
|
+
*/
|
|
18
|
+
connect(): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Publishes the event data to the defined topic.
|
|
21
|
+
*/
|
|
22
|
+
publish(data: T["data"]): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Graceful shutdown function.
|
|
25
|
+
*/
|
|
26
|
+
close(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.KafkaPublisher = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* Abstract class for a Kafka Producer (Publisher).
|
|
15
|
+
* It abstracts away Kafka connection and message serialization.
|
|
16
|
+
*/
|
|
17
|
+
class KafkaPublisher {
|
|
18
|
+
constructor(kafka) {
|
|
19
|
+
// Create a producer instance
|
|
20
|
+
this.producer = kafka.producer();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Connects the producer to the Kafka broker. Call this before publishing.
|
|
24
|
+
*/
|
|
25
|
+
connect() {
|
|
26
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
27
|
+
yield this.producer.connect();
|
|
28
|
+
console.log("Kafka Producer connected.");
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Publishes the event data to the defined topic.
|
|
33
|
+
*/
|
|
34
|
+
publish(data) {
|
|
35
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
// Ensure the producer is connected before sending
|
|
37
|
+
// In a real app, you'd manage connection state better.
|
|
38
|
+
// Assuming connect() was called before this method.
|
|
39
|
+
try {
|
|
40
|
+
const response = yield this.producer.send({
|
|
41
|
+
topic: this.subject,
|
|
42
|
+
messages: [
|
|
43
|
+
{
|
|
44
|
+
value: JSON.stringify(data), // Kafka requires data to be a Buffer or string
|
|
45
|
+
// key: 'some-key-for-partitioning' // Use a key for ordered processing (e.g., ticket ID)
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
});
|
|
49
|
+
console.log("Event published to subject", this.subject, "Response:", response);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
console.error("Kafka Publisher Error:", err);
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Graceful shutdown function.
|
|
59
|
+
*/
|
|
60
|
+
close() {
|
|
61
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
yield this.producer.disconnect();
|
|
63
|
+
console.log("Kafka Producer disconnected.");
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.KafkaPublisher = KafkaPublisher;
|
package/build/index.d.ts
CHANGED
|
@@ -8,3 +8,7 @@ export * from "./middlewares/current-user";
|
|
|
8
8
|
export * from "./middlewares/error-handler";
|
|
9
9
|
export * from "./middlewares/require-auth";
|
|
10
10
|
export * from "./middlewares/validate-request";
|
|
11
|
+
export * from "./events/kafka-listener";
|
|
12
|
+
export * from "./events/kafka-publisher";
|
|
13
|
+
export * from "./events/subjects";
|
|
14
|
+
export * from "./events/event-types/user-created-event";
|
package/build/index.js
CHANGED
|
@@ -24,3 +24,7 @@ __exportStar(require("./middlewares/current-user"), exports);
|
|
|
24
24
|
__exportStar(require("./middlewares/error-handler"), exports);
|
|
25
25
|
__exportStar(require("./middlewares/require-auth"), exports);
|
|
26
26
|
__exportStar(require("./middlewares/validate-request"), exports);
|
|
27
|
+
__exportStar(require("./events/kafka-listener"), exports);
|
|
28
|
+
__exportStar(require("./events/kafka-publisher"), exports);
|
|
29
|
+
__exportStar(require("./events/subjects"), exports);
|
|
30
|
+
__exportStar(require("./events/event-types/user-created-event"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uber-clone/common",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"main": "./build/index.js",
|
|
5
5
|
"types": "./build/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"express": "^5.1.0",
|
|
28
28
|
"express-validator": "^7.2.1",
|
|
29
29
|
"jsonwebtoken": "^9.0.2",
|
|
30
|
+
"kafkajs": "^2.2.4",
|
|
30
31
|
"node-nats-streaming": "^0.3.2"
|
|
31
32
|
}
|
|
32
33
|
}
|