@syntropylog/adapters
Version:
External adapters for SyntropyLog framework
80 lines • 3.39 kB
JavaScript
import { PayloadSerializer } from '../utils/PayloadSerializer';
/**
* Helper function to normalize Kafka's complex IHeaders object into
* the simple Record<string, string | Buffer> that our framework expects.
* @param headers The headers object from a Kafka message.
* @returns A normalized headers object.
*/
function normalizeKafkaHeaders(headers) {
if (!headers) {
return undefined;
}
const normalized = {};
for (const key in headers) {
if (Object.prototype.hasOwnProperty.call(headers, key)) {
const value = headers[key];
// We only accept string or Buffer, and we discard undefined or arrays for simplicity.
if (typeof value === 'string' || Buffer.isBuffer(value)) {
normalized[key] = value;
}
}
}
return normalized;
}
export class KafkaAdapter {
// The constructor now receives the Kafka instance already created.
// This makes it more flexible and easier to test.
constructor(kafkaInstance, groupId) {
this.producer = kafkaInstance.producer();
this.consumer = kafkaInstance.consumer({ groupId });
}
async connect() {
await this.producer.connect();
await this.consumer.connect();
}
async disconnect() {
await this.producer.disconnect();
await this.consumer.disconnect();
}
async publish(topic, message) {
const serializedPayload = PayloadSerializer.serializeForBroker(message);
await this.producer.send({
topic,
messages: [{ value: serializedPayload, headers: message.headers }],
});
}
async subscribe(topic, handler) {
await this.consumer.subscribe({ topic, fromBeginning: true });
await this.consumer.run({
eachMessage: async ({ topic, partition, message }) => {
try {
const brokerMessage = PayloadSerializer.createBrokerMessage(message.value, normalizeKafkaHeaders(message.headers));
const controls = {
ack: async () => {
await this.consumer.commitOffsets([
{
topic,
partition,
offset: (Number(message.offset) + 1).toString(),
},
]);
},
nack: async () => {
// Nacking in Kafka is complex. For now, we just log.
// A real implementation might move the message to a dead-letter queue.
console.log(`NACK received for message on topic ${topic}.`);
},
};
await handler(brokerMessage, controls);
}
catch (err) {
// If there's an error (e.g., JSON parsing), we can't process the message,
// but we don't want to crash the whole service. We'll log it.
// A more robust implementation might publish to a dead-letter queue.
console.error(`Failed to process message from topic ${topic}`, err);
}
},
});
}
}
//# sourceMappingURL=KafkaAdapter.js.map