UNPKG

@tvkitchen/countertop

Version:

The entry point for developers who want to set up a TV Kitchen.

175 lines (142 loc) 5.38 kB
"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;