UNPKG

@ocap/statedb-fs

Version:
152 lines (119 loc) 4.14 kB
/* eslint-disable no-underscore-dangle */ const path = require('path'); const omit = require('lodash/omit'); const { StateDBTable } = require('@ocap/statedb'); const Lokijs = require('lokijs'); const FsAdapter = require('lokijs/src/loki-fs-structured-adapter'); const debug = require('debug')(require('../../package.json').name); class FsTable extends StateDBTable { /** * @param {Object} param * @param {string} param.name db name * @param {string|string[]} param.uniqIndex primary keys * @param {string} param.dataDir data directory for db * @param {boolean} param.syncBalance sync balance table when create / update / get * @param {BalanceTable} param.balanceTable balance table */ constructor({ name, dataDir, uniqIndex, balanceTable, syncBalance = false }) { super(uniqIndex); this.name = name; this.dataDir = dataDir; this.uniqIndex = uniqIndex; this.collection = null; this.balanceTable = balanceTable; this.syncBalance = syncBalance; if (this.syncBalance && !this.balanceTable) { throw new Error('balanceTable is required when syncBalance is true'); } const adapter = new FsAdapter(); const db = new Lokijs(path.join(dataDir, `${name}.db`), { adapter, autoload: true, autosave: true, autosaveInterval: 1000, autoloadCallback: () => { this.collection = db.getCollection(name); if (this.collection === null) { this.collection = db.addCollection(name, { unique: [this.primaryKey], clone: true }); } this.markReady(); }, }); } async _create(key, attrs) { const id = this.generatePrimaryKey(key); // Don't call this._get here cause _get may be overwritten const doc = await FsTable.prototype._get.call(this, id); if (doc) { throw new Error(`${this.name} already exists: ${key}`); } debug(`insert ${this.name}`, attrs); const insertAttrs = { ...attrs }; // insert without tokens cause we've migrate token to balance table if (this.syncBalance) { delete insertAttrs.tokens; } const result = await this.collection.insert({ [this.primaryKey]: id, ...insertAttrs }); if (this.syncBalance && attrs.tokens) { debug(`update balance for ${this.name}`, attrs); result.tokens = await this.balanceTable.updateBalance({ address: attrs.address, tokens: attrs.tokens, context: attrs.context, }); } return result; } async _get(key) { if (!key) return null; const id = this.generatePrimaryKey(key); const result = await this.collection.by(this.primaryKey, id); if (result && this.syncBalance) { const balance = await this.balanceTable.getBalance(result.address); result.tokens = { ...(result.tokens || {}), ...balance }; } return result; } _history() { return []; } async _update(key, updates) { const id = this.generatePrimaryKey(key); // Don't call this._get here cause _get may be overwritten const doc = await FsTable.prototype._get.call(this, id); if (!doc) { throw new Error(`${this.name} does not exists: ${key}`); } Object.assign(doc, updates); const updateAttrs = { ...doc }; // insert without tokens cause we've migrate token to balance table if (this.syncBalance) { delete updateAttrs.tokens; } await this.collection.update(updateAttrs); if (this.syncBalance && doc.tokens) { debug(`update balance for ${this.name}`, doc); doc.tokens = await this.balanceTable.updateBalance({ address: doc.address, tokens: doc.tokens, context: doc.context, }); } return doc; } updateOrCreate(exist, state, ctx) { const id = this.generatePrimaryKey(state); const attrs = omit(state, this.primaryKey); if (!id) { throw new Error('Cannot update or create without uniq index'); } if (exist) { return this.update(id, attrs, ctx); } return this.create(id, attrs, ctx); } _reset() { this.collection.removeWhere({}); } } module.exports = FsTable;