@tvkitchen/countertop
Version:
The entry point for developers who want to set up a TV Kitchen.
175 lines (142 loc) • 5.38 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _stream = require("stream");
var _uuid = require("uuid");
var _baseInterfaces = require("@tvkitchen/base-interfaces");
var _baseClasses = require("@tvkitchen/base-classes");
var _loggers = require("../tools/loggers");
var _countertop = require("../tools/utils/countertop");
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
/**
* A CountertopWorker (aka Line Cook) is responsible for monitoring specific data streams
* and processing those streams using TV Kitchen Appliances to create new data.
*
* CountertopWorker instances consume Payloads from Kafka and pass them to their Appliances.
* CountertopWorker instances listen for emitted Payloads from appliances and process them.
*/
class CountertopWorker {
/**
* Create a new CountertopWorker.
*
* @param {Class} Appliance The appliance class this worker will use.
* @param {Object} applianceSettings The settings to be passed to the appliance.
* @param {CountertopStream} options.stream The stream this worker is tracking.
* @param {Logger} options.logger The logger the worker should use.
* @param {KafkaJS} options.kafka The kafkajs object the worker will use.
*/
constructor(Appliance, applianceSettings = {}, {
stream,
logger = _loggers.consoleLogger,
kafka
} = {}) {
_defineProperty(this, "logger", null);
_defineProperty(this, "id", null);
_defineProperty(this, "kafka", null);
_defineProperty(this, "consumer", null);
_defineProperty(this, "producer", null);
_defineProperty(this, "admin", null);
_defineProperty(this, "appliance", null);
_defineProperty(this, "stream", null);
_defineProperty(this, "start", async () => {
this.logger.trace(`CountertopWorker<${this.id}>: start()`);
if (!(await this.appliance.audit())) {
return false;
}
await this.admin.connect();
await this.producer.connect();
await this.consumer.connect();
const inputTopics = this.appliance.constructor.getInputTypes(this.appliance.settings).map(inputType => {
const inputStream = this.stream.getTributaryMap().get(inputType);
if (inputStream) {
return (0, _countertop.getStreamTopic)(inputType, inputStream);
}
return null;
}).filter(topic => topic !== null);
const outputTopics = this.appliance.constructor.getOutputTypes(this.appliance.settings).map(outputType => (0, _countertop.getStreamTopic)(outputType, this.stream));
const outputTopicConfigs = outputTopics.map(topic => ({
topic,
configEntries: [{
name: 'retention.ms',
value: '30000'
}]
}));
await this.admin.createTopics({
waitForLeaders: true,
topics: outputTopicConfigs
});
await Promise.all(inputTopics.map(topic => this.consumer.subscribe({
topic
})));
if (!this.appliance.start()) {
return false;
}
await this.consumer.run({
eachMessage: async ({
message
}) => {
this.logger.trace(`CountertopWorker<${this.stream.id}>.consumer: eachMessage()`);
const payload = _baseClasses.AvroPayload.deserialize(message.value);
return new Promise((resolve, reject) => {
try {
this.appliance.write(payload, resolve);
} catch (err) {
reject(err);
}
});
}
});
return true;
});
_defineProperty(this, "stop", async () => {
this.logger.trace(`CountertopWorker<${this.id}>: stop()`);
await this.appliance.stop();
await this.consumer.disconnect();
await this.producer.disconnect();
await this.admin.disconnect();
return true;
});
_defineProperty(this, "on", (eventType, listener) => {
this.appliance.on(eventType, listener);
return this;
});
this.id = `CountertopWorker::${Appliance.name}::${(0, _uuid.v4)()}`;
this.stream = stream;
this.logger = logger;
this.kafka = kafka;
this.consumer = this.kafka.consumer({
groupId: this.id
});
this.producer = this.kafka.producer();
this.admin = this.kafka.admin();
this.appliance = new Appliance({
logger,
...applianceSettings
});
if (!_baseInterfaces.IAppliance.isIAppliance(this.appliance)) {
throw new Error('TVKitchen can only use Appliances that extend IAppliance.');
}
const kafkaWriteStream = new _stream.Writable({
objectMode: true,
write: async (payload, enc, done) => {
await this.producer.send({
topic: (0, _countertop.getStreamTopic)(payload.type, this.stream),
messages: [{
value: _baseClasses.AvroPayload.serialize(payload)
}]
});
done();
}
});
this.appliance.pipe(kafkaWriteStream);
}
/**
* Start the worker and begin data processing.
*
* @return {Boolean} Whether the worker successfully started.
*/
}
var _default = CountertopWorker;
exports.default = _default;