@kothique/moleculer-rethinkdbdash
Version:
Moleculer service ensuring specified dbs, tables, and indices exist, and giving access to the driver.
131 lines (109 loc) • 4.87 kB
JavaScript
;
const Promise = require('bluebird');
const r = require('rethinkdbdash');
const _ = require('lodash');
const { MoleculerClientError } = require('moleculer').Errors;
/**
* Service mixin to access `rethinkdbdash` driver.
*/
module.exports = {
r: null,
rTable: null,
async created() {
this.r = r({
silent: true,
/* All logs are automatically forwarded to the service logger.
This can be easily overrided. */
log: message => this.logger.debug(message),
...this.schema.rOptions
});
const createdIndices = {};
if (this.schema.rInitial) {
if (!_.isFunction(this.schema.rInitial) && !_.isObject(this.schema.rInitial)) {
throw new MoleculerClientError('Expected this.rInitial to be a function or an object.', null, 'ERR_INVALID_R_INITIAL');
}
const rInitial = _.isFunction(this.schema.rInitial) ? this.schema.rInitial(this.r) : this.schema.rInitial;
if (_.isFunction(this.schema.rInitial) && !_.isObject(rInitial)) {
throw new MoleculerClientError('Expected this.rInitial(r) to return an object.', null, 'ERR_INVALID_R_INITIAL_RESULT');
}
const dbNames = await this.r.dbList();
await Promise.all(Object.keys(rInitial).map(async dbName => {
const dbInitial = rInitial[dbName];
createdIndices[dbName] = {};
if (!dbNames.includes(dbName)) {
try {
await this.r.dbCreate(dbName);
this.logger.debug(`Created DB: '${dbName}'.`);
} catch (error) {
if (error.name !== 'ReqlOpFailedError') {
throw error;
}
}
}
if (dbInitial !== true && !_.isObject(dbInitial)) {
throw new MoleculerClientError(`Expected this.rInitial.${dbName} to be true or an object.`, null, 'ERR_INVALID_DB_INITIAL');
}
if (dbInitial === true) { return; } // Skip creating tables and indices
const tableNames = await this.r.db(dbName).tableList();
await Promise.all(Object.keys(dbInitial).map(async tableName => {
if (tableName.startsWith('$')) { return; }
const tableInitial = dbInitial[tableName];
createdIndices[dbName][tableName] = [];
if (tableInitial !== true && !_.isObject(tableInitial)) {
throw new MoleculerClientError(`Expected this.rInitial.${dbName}.${tableName} to be true or an object.`, null, 'ERR_INVALID_TABLE_INITIAL');
}
if (!tableNames.includes(tableName)) {
try {
await this.r.db(dbName).tableCreate(tableName, tableInitial.$options);
this.logger.debug(`Created${tableInitial.$default ? ' default' : ''} table: ${dbName}.${tableName}`, {
options: tableInitial.$options
});
} catch (error) {
if (error.name !== 'ReqlOpFailedError') {
throw error;
}
}
}
if (tableInitial.$default === true) {
this.rTable = this.r.db(dbName).table(tableName);
}
if (tableInitial === true) { return; } // Skip creating indices
const indexNames = await this.r.db(dbName).table(tableName).indexList();
await Promise.all(Object.keys(tableInitial).map(async indexName => {
if (indexName.startsWith('$')) { return; }
const indexInitial = tableInitial[indexName];
if (tableInitial !== true && !_.isObject(tableInitial)) {
throw new MoleculerClientError(`Expected this.rInitial.${dbName}.${tableName}.${indexName} to be true or an object.`, null, 'ERR_INVALID_INDEX_INITIAL');
}
if (!indexNames.includes(indexName)) {
try {
await this.r.db(dbName).table(tableName).indexCreate(
indexName,
...indexInitial.$function ? [indexInitial.$function] : [],
...indexInitial.$options ? [indexInitial.$options] : []
);
createdIndices[dbName][tableName].push(indexName);
this.logger.debug(`Created index: ${dbName}.${tableName}.${indexName}`, {
options: indexInitial.$options
});
} catch (error) {
if (error.name !== 'ReqlOpFailedError') {
throw error;
}
}
}
}));
}));
}));
}
if (_.isFunction(this.schema.rOnReady)) {
/* Wait for indices to be created. */
await Promise.all(Object.keys(createdIndices).map(dbName =>
Promise.all(Object.keys(createdIndices[dbName]).map(tableName =>
this.r.db(dbName).table(tableName).indexWait(...createdIndices[dbName][tableName])
))
));
this.schema.rOnReady.call(this);
}
}
};