@angstone/monostone
Version:
monolitic event-sourced framework
165 lines • 6.33 kB
JavaScript
;
/**
* 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