UNPKG

@angstone/monostone

Version:

monolitic event-sourced framework

165 lines 6.33 kB
"use strict"; /** * Controlls the events toolchain */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const ast = require("@angstone/node-util"); const error_1 = require("../error"); const _1 = require("./"); const events_1 = require("events"); const event_store_client_1 = require("event-store-client"); const MAX_LIVE_QUEUE_SIZE = 10000; const READ_BATCH_SIZE = 500; const state = { isStreamInLive: false, connection: undefined, credentials: undefined, eventStreamSubscription: undefined, firstEventNumberToReduce: 0, moduleClosed: false, eventRead$: new events_1.EventEmitter().setMaxListeners(Infinity), }; const config = () => { ast.log("creating event controller"); state.isStreamInLive = false; state.eventStreamSubscription = undefined; state.firstEventNumberToReduce = 0; state.moduleClosed = false; state.eventRead$ = new events_1.EventEmitter().setMaxListeners(Infinity); state.credentials = { password: process.env.EVENT_SOURCE_PASSWORD || "changeit", username: process.env.EVENT_SOURCE_USERNAME || "admin", }; ast.log("creating connection to event store"); state.connection = createConnection(); ast.log("event store connected"); }; /** * Creates connection with Event Store */ const createConnection = () => { const options = { debug: false, host: process.env.EVENT_SOURCE_HOST, onClose: () => { if (!state.moduleClosed) error_1.error.fatal("connection to eventstore was closed"); }, onError: (err) => { if (!state.moduleClosed) error_1.error.fatal(err, "EventController could not connect to eventstore"); }, port: process.env.EVENT_SOURCE_PORT, }; return new event_store_client_1.Connection(options); }; const stop = () => __awaiter(this, void 0, void 0, function* () { ast.log("stoping event controller"); yield unsubscribeStream(); ast.log("usubscribed from stream"); yield closeConnection(); ast.log("connection closed"); }); const unsubscribeStream = () => { return new Promise((resolve) => { if (state.eventStreamSubscription) { state.eventStreamSubscription.stop(); state.eventStreamSubscription = undefined; resolve(); } else { resolve(); } }); }; const closeConnection = () => { return new Promise((resolve) => { if (!state.moduleClosed) { state.moduleClosed = true; if (state.connection) { state.connection.close(); state.connection = undefined; resolve(); } else { resolve(); } } else { resolve(); } }); }; const start = () => __awaiter(this, void 0, void 0, function* () { ast.log("starting event controller"); yield calculateFirstEventNumberToReduce(); ast.log("reading past events from eventsource"); readAllPastEvents(); ast.log("event controller ready"); }); const calculateFirstEventNumberToReduce = () => __awaiter(this, void 0, void 0, function* () { state.firstEventNumberToReduce = Math.min(...[ yield _1.ReducerModule.getFirstEventNumberToReduce(), yield _1.EffectModule.getFirstEventNumberToEffect(), ]); }); const readAllPastEvents = () => { state.eventStreamSubscription = listenToFrom(state.firstEventNumberToReduce, (event) => { const eventRead = { eventNumber: event.eventNumber, request: event.data, }; state.eventRead$.emit(event.eventType, eventRead); }, (eventStoreStreamCatchUpSubscription, reason, errorFound) => { if (reason !== "UserInitiated") { error_1.error.op("eventstore subscription dropped due to " + reason, errorFound, eventStoreStreamCatchUpSubscription); } }, () => { state.isStreamInLive = true; }); }; /* Executes a catch-up subscription on the given stream, reading events from a given event number, and continuing with a live subscription when all historical events have been read. */ const listenToFrom = (fromEventNumber, onEventAppeared, onDropped, onLiveProcessingStarted) => { const streamId = process.env.EVENT_STREAM_NAME || "mono"; const settings = { debug: false, maxLiveQueueSize: MAX_LIVE_QUEUE_SIZE, readBatchSize: READ_BATCH_SIZE, resolveLinkTos: false, }; if (state.connection && state.credentials) { return state.connection.subscribeToStreamFrom(streamId, // streamId - The name of the stream in the Event Store (string) fromEventNumber, // fromEventNumber - Which event number to start after state.credentials, // credentials - The user name and password needed for permission to subscribe to the stream. onEventAppeared, // onEventAppeared - Callback for each event received (historical or live) onLiveProcessingStarted, /* onLiveProcessingStarted - Callback when historical events have been read and live events are about to be read.*/ onDropped, // onDropped - Callback when subscription drops or is dropped. settings); } }; const isStreamInLive = () => state.isStreamInLive; const awaitStreamInLive = () => __awaiter(this, void 0, void 0, function* () { while (!state.isStreamInLive) { yield ast.delay(10); } }); const getEventReadStream = () => state.eventRead$; exports.EventModule = Object.assign({}, _1.BasicModule, { config, start, stop, isStreamInLive, awaitStreamInLive, getEventReadStream }); //# sourceMappingURL=event.module.js.map