@ocap/statedb-qldb
Version:
OCAP statedb adapter that uses amazon qldb as backend statedb
103 lines (83 loc) • 3.27 kB
JavaScript
/* eslint-disable no-underscore-dangle */
const { StateDBTable } = require('@ocap/statedb');
const omit = require('lodash/omit');
const debug = require('debug')(require('../../package.json').name);
class QLDBTable extends StateDBTable {
constructor(name, driver, uniqIndex) {
super();
this.name = name;
this.driver = driver;
this.uniqIndex = uniqIndex;
this.indexes = [];
}
// TODO: we should support get record history:
// - https://docs.aws.amazon.com/qldb/latest/developerguide/working.metadata.html
// - https://docs.aws.amazon.com/qldb/latest/developerguide/working.history.html
initialize() {
return this.driver.executeLambda(async (txn) => {
await txn.execute(`CREATE TABLE ${this.name}`);
await txn.execute(`CREATE INDEX ON ${this.name} (${this.uniqIndex})`);
for (let i = 0; i < this.indexes.length; i++) {
// eslint-disable-next-line no-await-in-loop
await txn.execute(`CREATE INDEX ON ${this.name} ("${this.indexes[i]}")`);
}
debug('create table and index', { name: this.name, index: this.uniqIndex });
this.markReady();
});
}
async _create(key, attrs, { txn }) {
const data = { [this.uniqIndex]: key, ...attrs };
debug(`insert ${this.name}`, data);
const result = await txn.execute(`INSERT INTO ${this.name} ?`, data);
debug(`insert ${this.name}`, key, result.getResultList());
return data;
}
async _get(key, { txn }) {
if (!key) {
return null;
}
const result = await txn.execute(`SELECT * FROM ${this.name} WHERE ${this.uniqIndex} = ?`, key);
const item = result.getResultList().shift();
return item ? JSON.parse(JSON.stringify(item)) : null;
}
async _history(key, { txn }) {
if (!key) {
return [];
}
const metaResult = await txn.execute(
`SELECT metadata FROM _ql_committed_${this.name} WHERE data.${this.uniqIndex} = ?`,
key
);
const item = metaResult.getResultList().shift();
if (!item) {
return [];
}
const { metadata } = JSON.parse(JSON.stringify(item));
// TODO: since qldb does not support pagination, we may hit timeout here before retrieved all data
const historyResult = await txn.execute(`SELECT * FROM history(${this.name}) WHERE metadata.id = ?`, metadata.id);
return historyResult.getResultList().map((x) => JSON.parse(JSON.stringify(x)));
}
async _update(key, updates, { txn }) {
// Each time we are doing an override
const data = { [this.uniqIndex]: key, ...updates };
debug(`update ${this.name}`, data);
const result = await txn.execute(`UPDATE ${this.name} AS t SET t = ? WHERE ${this.uniqIndex} = ?`, data, key);
debug(`update ${this.name}`, key, result.getResultList());
return data;
}
async _reset({ txn }) {
if (process.env.CI) {
await txn.execute(`DELETE FROM ${this.name}`);
}
}
updateOrCreate(exist, state, ctx) {
if (!state[this.uniqIndex]) {
throw new Error('Cannot update or create without uniq index');
}
if (exist) {
return this.update(state[this.uniqIndex], omit(state, [this.uniqIndex]), ctx);
}
return this.create(state[this.uniqIndex], omit(state, [this.uniqIndex]), ctx);
}
}
module.exports = QLDBTable;