@nestjs-cqrs-eventsourcing/core
Version:
Event sourcing for nestjs CQRS
341 lines (340 loc) • 12.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Eventstore = void 0;
const debug_1 = __importDefault(require("debug"));
const events_1 = require("events");
const lodash_1 = __importDefault(require("lodash"));
const process = __importStar(require("process"));
const tolerance_1 = __importDefault(require("tolerance"));
const uuid_1 = require("uuid");
const core_1 = require("@nestjs-cqrs-eventsourcing/core");
const debug = (0, debug_1.default)('eventstore:wrapper');
class Eventstore extends events_1.EventEmitter {
constructor(options) {
super();
this.options = options;
this.eventMappings = {};
this.dispatcher = undefined;
this.store = options.store;
events_1.EventEmitter.call(this);
}
useEventPublisher(fn) {
this.publisher = fn;
return this;
}
defineEventMappings(mappings) {
if (!mappings || !lodash_1.default.isObject(mappings)) {
const err = new Error('Please pass a valid mapping values!');
debug(err.message);
throw err;
}
this.eventMappings = mappings;
return this;
}
async init() {
return new Promise((resolve, reject) => {
const initDispatcher = async () => {
debug('init event dispatcher');
if (!this.publisher) {
throw new Error('No publisher, shutting down');
}
this.dispatcher = new core_1.EventDispatcher(this.publisher, this);
await this.dispatcher.start();
};
this.store.on('connect', () => {
this.emit('connect');
});
this.store.on('disconnect', () => {
this.emit('disconnect');
});
process.nextTick(() => {
(0, tolerance_1.default)(async (callback) => {
await this.store.connect();
callback();
}, this.options.timeout || 0, (err) => {
if (err) {
debug(err.message);
reject(err);
}
if (!this.publisher) {
debug('no publisher defined');
resolve();
return;
}
initDispatcher()
.then(() => {
resolve();
})
.catch((err2) => reject(err2));
});
});
});
}
async streamEvents(query, skip, limit) {
if (!this.store.streamEvents) {
throw new Error(`Streaming API is not suppoted by ${this.options.type || 'inmemory'} db implementation.`);
}
if (typeof query === 'number') {
limit = skip;
skip = query;
query = {};
}
if (typeof query === 'string') {
query = { aggregateId: query };
}
return this.store.streamEvents?.(query, skip, limit);
}
async streamEventsSince(commitStamp, skip, limit) {
if (!this.store.streamEvents) {
throw new Error(`Streaming API is not suppoted by ${this.options.type || 'inmemory'} db implementation.`);
}
if (!this.store.streamEventsSince) {
throw new Error('StreamEventsSince - not supported by the store');
}
if (!commitStamp) {
const err = new Error('Please pass in a date object!');
debug(err.message);
throw err;
}
commitStamp = new Date(commitStamp);
return this.store.streamEventsSince(commitStamp, skip, limit);
}
async streamEventsByRevision(query, revMin, revMax) {
if (!this.store.streamEventsByRevision) {
throw new Error('streamEventsByRevision - Not supported by the store');
}
if (typeof query === 'string') {
query = { aggregateId: query };
}
if (!query.aggregateId) {
const err = new Error('An aggregateId should be passed!');
debug(err.message);
throw err;
}
return this.store.streamEventsByRevision(query, revMin, revMax);
}
async getEvents(query, skip = 0, limit = -1) {
if (typeof query === 'string') {
query = { aggregateId: query };
}
return this.store.getEvents(query, skip, limit);
}
async countEvents(query) {
if (typeof query === 'string') {
query = { aggregateId: query };
}
return this.store.countEvents(query);
}
async getEvent(index) {
return this.store.getEvent(index);
}
async getEventsSince(commitStamp, skip = 0, limit = -1) {
if (!commitStamp) {
const err = new Error('Please pass in a date object!');
debug(err.message);
throw err;
}
return this.store.getEventsSince(commitStamp, skip, limit);
}
async getEventsByRevision(query, revMin = 0, revMax = -1) {
if (typeof query === 'string') {
query = { aggregateId: query };
}
if (!query.aggregateId) {
const err = new Error('An aggregateId should be passed!');
debug(err.message);
throw err;
}
return this.store.getEventsByRevision(query, revMin, revMax);
}
async getEventStream(query, revMin = 0, revMax = -1) {
if (typeof query === 'string') {
query = { aggregateId: query };
}
if (!query.aggregateId) {
const err = new Error('An aggregateId should be passed!');
debug(err.message);
throw err;
}
const events = await this.getEventsByRevision(query, revMin, revMax);
return new core_1.EventStream(this, query, events);
}
async getFromSnapshot(query, revMax) {
if (!revMax) {
revMax = -1;
}
if (typeof query === 'string') {
query = { aggregateId: query };
}
if (!query.aggregateId) {
const err = new Error('An aggregateId should be passed!');
debug(err.message);
throw err;
}
const snap = await this.store.getSnapshot(query, revMax);
let rev = 0;
if (snap?.revision !== undefined && snap.revision !== null) {
rev = snap.revision + 1;
}
const stream = await this.getEventStream(query, rev, revMax);
if (snap && rev > 0 && stream.lastRevision === -1) {
stream.lastRevision = snap.revision;
}
return [snap, stream];
}
async createSnapshot(obj) {
if (obj.streamId && !obj.aggregateId) {
obj.aggregateId = obj.streamId;
}
if (!obj.aggregateId) {
const err = new Error('An aggregateId should be passed!');
debug(err.message);
throw err;
}
obj.streamId = obj.aggregateId;
if (obj.revision && typeof obj.revision === 'string') {
const castedRevision = parseFloat(obj.revision);
if (castedRevision && castedRevision.toString() === obj.revision) {
obj.revision = castedRevision;
}
}
const id = this.getNewId();
try {
const snap = new core_1.Snapshot(id, obj);
snap.timestamp = new Date();
await this.store.addSnapshot(snap);
}
catch (err) {
debug(err);
throw err;
}
}
async commit(eventStream) {
const newId = this.getNewId();
let currentRevision = eventStream.currentRevision();
debug((0, core_1.nowIso)(), '+++ Commit stream', eventStream.nonce, 'currentRevision', currentRevision);
const uncommittedEvents = [...eventStream.uncommittedEvents];
eventStream.uncommittedEvents = [];
const positions = await this.store.getNextPositions?.(uncommittedEvents.length);
for (let i = 0, len = uncommittedEvents.length; i < len; i += 1) {
const event = uncommittedEvents[i];
event.id = newId + i.toString();
event.commitId = newId;
event.commitSequence = i;
event.restInCommitStream = len - 1 - i;
event.timestamp = new Date();
currentRevision += 1;
event.streamRevision = currentRevision;
if (positions)
event.position = positions[i];
debug((0, core_1.nowIso)(), eventStream.nonce, event.aggregateId, event.commitSequence, event.streamRevision);
event.applyMappings?.();
}
try {
await this.store.addEvents(uncommittedEvents);
if (this.publisher && this.dispatcher) {
await this.dispatcher.addUndispatchedEvents(uncommittedEvents);
}
else {
eventStream.eventsToDispatch = [...uncommittedEvents];
}
eventStream.events = eventStream.events.concat(uncommittedEvents);
eventStream.currentRevision();
}
catch (e) {
debug('UUUUUUUPPPPPSSSSS, error while committing events', eventStream.uncommittedEvents.map(evt => evt.aggregateId));
eventStream.uncommittedEvents = uncommittedEvents.concat(eventStream.uncommittedEvents);
}
debug((0, core_1.nowIso)(), '--- Done commit stream', eventStream.nonce, 'currentRevision', currentRevision);
await this.store.commitTransaction?.();
}
async getUndispatchedEvents(query) {
if (typeof query === 'string') {
query = { aggregateId: query };
}
return this.store.getUndispatchedEvents(query);
}
async getLastEvent(query) {
if (!this.store.getLastEvent) {
throw new Error('Not supported by the store');
}
if (typeof query === 'string') {
query = { aggregateId: query };
}
return this.store.getLastEvent(query);
}
async getLastEventAsStream(query) {
if (!this.store.getLastEvent) {
throw new Error('Not supported by the store');
}
let request;
if (typeof query === 'string') {
request = { aggregateId: query };
}
else {
request = query;
}
const event = await this.store.getLastEvent(request);
return new core_1.EventStream(this, request, event ? [event] : []);
}
async setEventToDispatched(evtOrId) {
if (typeof evtOrId === 'object') {
evtOrId = evtOrId.id;
}
if (!this.store.setEventToDispatched) {
return;
}
await this.store.setEventToDispatched(evtOrId);
}
getNewId() {
return (0, uuid_1.v4)().toString();
}
createSequence(name, startValue) {
return new Promise((resolve, reject) => {
this.on('connect', async () => {
await this.store.createSequence(name, startValue);
resolve();
});
setTimeout(() => {
reject(`Unable to create sequence ${name} after 3000ms after initialization of eventstore`);
}, 3000);
});
}
}
exports.Eventstore = Eventstore;