mongodb-changelog
Version:
Liquibase inspired mongodb changelog tool for node.
87 lines (77 loc) • 2.8 kB
JavaScript
;
const crypto = require('crypto');
const MongoClient = require('mongodb').MongoClient;
const IllegalTaskFormat = require('./error').IllegalTaskFormat;
const HashError = require('./error').HashError;
const Statuses = {
ALREADY_APPLIED: 'ALREADY_APPLIED',
SUCCESSFULLY_APPLIED: 'SUCCESSFULLY_APPLIED'
};
/**
* Run migrations/tasks against database, specified by config.
* @param {Object} config
* @param {Object[]} tasks
* @param {string} tasks[].name - unique name of the task
* @param {function} tasks[].operation - function, returning yieldable value (https://github.com/tj/co#yieldables)
* @returns {Promise} resolved with hash (taskName: Status), or rejected with en error occurred
*/
async function runMigrations(config, tasks) {
const client = await MongoClient.connect(config.mongoUrl, config.mongoConnectionConfig);
const result = {};
try {
const db = client.db();
const changelogCollection = db.collection('databasechangelog');
await changelogCollection.createIndex({name: 1}, {unique: true});
for (let i = 0; i < tasks.length; i++) {
const task = tasks[i];
result[task.name] = await processTask(task, changelogCollection, db);
}
} catch (err) {
await client.close();
throw err;
}
await client.close();
return result;
}
/**
* Process new task. Check hash of applied tasks.
* @param {Object} task
* @param {Collection} changelogCollection - mongodb collection to store changelog in
* @param {Db} db - database instance
* @throws {IllegalTaskFormat} task should have "name" and "operation"
* @throws {HashError} Already applied tasks should not be modified.
* @returns Status
*/
async function processTask(task, changelogCollection, db) {
if (!isTaskValid(task)) {
throw new IllegalTaskFormat();
}
const storedTask = await changelogCollection.findOne({name: task.name});
const md5sum = getMD5Sum(task);
let status;
if (storedTask) {
if (storedTask.md5sum !== md5sum) {
throw new HashError(task, md5sum);
} else {
status = Statuses.ALREADY_APPLIED;
}
} else {
await task.operation(db);
const appliedChange = {
name: task.name,
dateExecuted: new Date(),
md5sum: md5sum
};
await changelogCollection.insertOne(appliedChange);
status = Statuses.SUCCESSFULLY_APPLIED;
}
return status;
}
function isTaskValid(task) {
return task.name && task.operation && task.operation instanceof Function;
}
function getMD5Sum(task) {
return crypto.createHash('md5').update(task.operation.toString()).digest('hex');
}
module.exports = runMigrations;
module.exports.Statuses = Statuses;