UNPKG

conveyor-client

Version:

A client for communicating with the Conveyor event sourcing engine via the HTTP/Webhook interface'

159 lines (128 loc) 5.68 kB
'use strict'; function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } const impl = require('./impl'); const Repository = require('./Repository'); const dbTools = require('./dbTools'); const privateToken_ = Symbol('private-token'); /** * Repository that eagerly builds up it collection of objects in-memory * and keeps them updated as new events are streamed in. * * An instance should only be created via the EagerRepository.create() * static method. */ class EagerRepository extends Repository { constructor(connectionOrClient, name, basePath, ModelClass, privateToken) { super(connectionOrClient, name, basePath); if (privateToken !== privateToken_) { throw new Error('EagerRepository must be created via EagerRepository.create()'); } this.ModelClass = ModelClass; // State to hold messages received before all historical messages have been delivered this.buffer = []; this.hasFetched = false; // Internal storage for all models, sorted by ID this.table = []; // By default, models are not indexed in any way, and searching does a full // scan of the table. Indexing functions may be specified that build // up ordered indexes that allow for efficient retrieval. Yes, this is // essentially a tiny, inefficient in-memory database. this.indexes = {}; this.indexFns = {}; this.handle = this.handle.bind(this); } static create(connectionOrClient, name, basePath, ModelClass) { return _asyncToGenerator(function* () { const inst = new EagerRepository(connectionOrClient, name, basePath, ModelClass, privateToken_); impl.subscribe(inst.connection, inst.basePath, function (event) { if (!inst.hasFetched) { buffered.push(event); return; } inst.handle(event); }); const feed = yield impl.getEvents(inst.connection, inst.basePath); const firstSubscribedEvent = inst.buffer.length > 0 ? inst.buffer : null; feed.events.filter(function (e) { return firstSubscribedEvent === null || e['tx-id'] < firstSubscribedEvent['tx-id']; }).forEach(inst.handle); inst.hasFetched = true; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = inst.buffer[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { const event = _step.value; inst.handle(event); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } return inst; })(); } get indexPairs() { return Object.keys(this.indexes).map(idxName => [this.indexes[idxName], this.indexFns[idxName]]); } createIndex(name, fn) { const idx = []; this.indexFns[name] = fn; this.indexes[name] = idx; // Add existing rows to index this.table.forEach(row => dbTools.idxInsert(idx, row, fn)); } queryIndex(name, value) { const idx = this.indexes[name]; if (!idx) { throw new Error(`Cannot query nonexistent index: ${name}`); } return dbTools.indexedTblLookup(this.table, idx, value); } handle(evt) { const path = evt.feed.split('/'); const id = path[path.length - 1]; let model = dbTools.tblLookup(this.table, id); const idxNames = Object.keys(this.indexFns); const isNew = !model; if (isNew) { model = new this.ModelClass(); } else if (evt.data.type === 'deleted') { dbTools.removeFromAll(this.table, this.indexPairs, model); return; } else { // Remove from existing indexes // TODO: only rewrite in index for indexes that actually change, not every index idxNames.forEach(idxName => dbTools.idxRemove(this.indexes[idxName], model, this.indexFns[idxName])); } model.handle(evt.data); if (isNew) { dbTools.tblInsert(this.table, model); } // (re-)add to indexes with possibly updated values idxNames.forEach(idxName => dbTools.idxInsert(this.indexes[idxName], model, this.indexFns[idxName])); } fetch(id) { var _this = this; return _asyncToGenerator(function* () { return dbTools.tblLookup(_this.table, id); })(); } fetchAll() { var _this2 = this; return _asyncToGenerator(function* () { return _this2.table; })(); } } module.exports = EagerRepository;