@citrineos/util
Version:
The OCPP util module which supplies helpful utilities like cache and queue connectors, etc.
136 lines • 6.02 kB
JavaScript
;
// Copyright (c) 2023 S44, LLC
// Copyright Contributors to the CitrineOS Project
//
// SPDX-License-Identifier: Apache 2.0
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.KafkaReceiver = void 0;
const base_1 = require("@citrineos/base");
const class_transformer_1 = require("class-transformer");
const kafkajs_1 = require("kafkajs");
/**
* Implementation of a {@link IMessageHandler} using Kafka as the underlying transport.
*/
class KafkaReceiver extends base_1.AbstractMessageHandler {
constructor(config, logger, module) {
var _a, _b, _c, _d, _e;
super(config, logger, module);
this._consumerMap = new Map();
this._client = new kafkajs_1.Kafka({
brokers: ((_a = this._config.util.messageBroker.kafka) === null || _a === void 0 ? void 0 : _a.brokers) || [],
ssl: true,
sasl: {
mechanism: 'plain',
username: ((_b = this._config.util.messageBroker.kafka) === null || _b === void 0 ? void 0 : _b.sasl.username) || '',
password: ((_c = this._config.util.messageBroker.kafka) === null || _c === void 0 ? void 0 : _c.sasl.password) || '',
},
});
this._topicName = `${(_d = this._config.util.messageBroker.kafka) === null || _d === void 0 ? void 0 : _d.topicPrefix}-${(_e = this._config.util.messageBroker.kafka) === null || _e === void 0 ? void 0 : _e.topicName}`;
const admin = this._client.admin();
admin
.connect()
.then(() => admin.listTopics())
.then((topics) => {
this._logger.debug('Topics:', topics);
if (!topics || topics.filter((topic) => topic === this._topicName).length === 0) {
this._client
.admin()
.createTopics({ topics: [{ topic: this._topicName }] })
.then(() => {
this._logger.debug(`Topic ${this._topicName} created.`);
})
.catch((err) => {
this._logger.error('Error creating topic', err);
});
}
else {
this._logger.debug(`Topic ${this._topicName} already exists.`);
}
})
.then(() => admin.disconnect())
.catch((err) => {
this._logger.error(err);
});
}
subscribe(identifier, actions, filter) {
this._logger.debug(`Subscribing to ${this._topicName}...`, identifier, actions, filter);
const consumer = this._client.consumer({ groupId: 'test-group' });
return consumer
.connect()
.then(() => consumer.subscribe({ topic: this._topicName, fromBeginning: false }))
.then(() => consumer.run({
autoCommit: false,
eachMessage: (payload) => this._onMessage(payload, consumer),
})) // TODO: Add filter
.then(() => this._consumerMap.set(identifier, consumer))
.then(() => true)
.catch((err) => {
this._logger.error(err);
return false;
});
}
unsubscribe(identifier) {
const consumer = this._consumerMap.get(identifier);
if (!consumer) {
this._logger.error('Consumer not found', identifier);
return Promise.resolve(false);
}
return consumer
.disconnect()
.then(() => this._consumerMap.delete(identifier))
.catch((err) => {
this._logger.error(err);
return false;
});
}
shutdown() {
return __awaiter(this, void 0, void 0, function* () {
for (const consumer of this._consumerMap.values()) {
yield consumer.disconnect();
}
});
}
/**
* Private Methods
*/
/**
* Underlying Kafka message handler.
*
* @param message The kafka message to process
*/
_onMessage(_a, consumer_1) {
return __awaiter(this, arguments, void 0, function* ({ topic, partition, message }, consumer) {
var _b, _c;
this._logger.debug(`Received message ${(_b = message.value) === null || _b === void 0 ? void 0 : _b.toString()} on topic ${topic} partition ${partition}`);
try {
const messageValue = message.value;
if (messageValue) {
const parsed = (0, class_transformer_1.plainToInstance)((base_1.Message), messageValue.toString());
yield this.handle(parsed, (_c = message.key) === null || _c === void 0 ? void 0 : _c.toString());
}
}
catch (error) {
if (error instanceof base_1.RetryMessageError) {
this._logger.warn('Retrying message: ', error.message);
// Retryable error, usually ongoing call with station when trying to send new call
return;
}
else {
this._logger.error('Error while processing message:', error, message);
}
}
yield consumer.commitOffsets([{ topic, partition, offset: message.offset }]);
});
}
}
exports.KafkaReceiver = KafkaReceiver;
//# sourceMappingURL=receiver.js.map