UNPKG

@golemio/db-migrate

Version:

Database migration framework for node.js

239 lines (195 loc) 5.92 kB
const MSTATE = '__dbmigrate_state__'; const SSTATE = '__dbmigrate_schema__'; const RUNON = 'run_on'; const log = require('db-migrate-shared').log; const fs = require('fs').promises; const path = require('path'); const crypto = require('crypto'); const ID = crypto.randomBytes(32).toString('base64'); let owner = false; module.exports = { /** * Lock state will add the basic state controller an ID * and a process controlled date. * It is important that all systems are in sync. We will use the * database clock instead if possible. */ lockState: async function (driver, state, internals) { if (!state) { owner = true; return driver._insertKV( internals.migrationState, MSTATE, JSON.stringify({ s: { step: 0, fin: 0, ID, date: new Date() } }) ); } else { let runOn = state.run_on; state = state.value; state = JSON.parse(state); if (!state.s.ID || state.s.ID !== ID) { owner = true; state.s.ID = ID; } const d = new Date(); state.s.date = d; await driver._updateKVC( internals.migrationState, MSTATE, JSON.stringify(state), RUNON, runOn ); state = await driver._getKV(internals.migrationState, MSTATE); runOn = state.run_on; state = state.value; state = JSON.parse(state); if (state.s.ID !== ID) { owner = false; } } }, isOwner: function () { return owner; }, init: async function (driver, internals, { emptyState, backupState }) { await driver._createKV(internals.migrationState); const _schema = await driver._getKV(internals.migrationState, SSTATE); if (_schema && backupState) { const newName = `${internals.migrationState}_b_${Math.floor((new Date() - 0) / 1000)}`; log.info(`[state] Created a backup of ${internals.migrationState} by writing to file ${newName}.dbmigrate`); await fs.writeFile(path.resolve(`${newName}.dbmigrate`), JSON.stringify(_schema), 'utf8'); if (emptyState) { await driver.renameTable(internals.migrationState, newName); log.info(`[state] Created a backup of ${internals.migrationState} by renaming table to ${newName}`); await driver._createKV(internals.migrationState); await driver._insertKV(internals.migrationState, SSTATE, '{}'); } } const schema = emptyState !== true ? _schema : null; if (schema) { internals.schema = JSON.parse(schema.value); } else if (!emptyState) { await driver._insertKV(internals.migrationState, SSTATE, '{}'); } if (!internals.dryRun) { const state = await driver._getKV(internals.migrationState, MSTATE); return module.exports.lockState(driver, state, internals); } return Promise.resolve(); }, startMigration: async function (driver, file, internals) { const state = await driver._getKV(internals.migrationState, MSTATE); const mig = await driver._getKV(internals.migrationState, file.name); if (mig && mig.value !== '{}') { internals.modSchema = JSON.parse(mig.value); } if (internals.dryRun) { return Promise.resolve(); } await module.exports.lockState(driver, state, internals); if (!mig) { return driver._insertKV( internals.migrationState, file.name, JSON.stringify({}) ); } else { return Promise.resolve(); } }, update: async function (driver, file, state, internals) { log.verbose(`[state] update state`); if (internals.dryRun) { return Promise.resolve(); } await driver._updateKV(internals.migrationState, SSTATE, JSON.stringify(internals.schema)); return driver._updateKV( internals.migrationState, file.name, JSON.stringify(state) ); }, get: function (driver, file, internals) { return driver._getKV(internals.migrationState, file.name); }, tick: async function (driver, internals) { if (internals.dryRun) { return Promise.resolve(); } let state = await driver._getKV( internals.migrationState, MSTATE ); let runOn = state.run_on; state = state.value; state = JSON.parse(state); if (!state.s.ID || state.s.ID !== ID) { owner = true; state.s.ID = ID; } const d = new Date(); state.s.date = d; await driver._updateKVC( internals.migrationState, MSTATE, JSON.stringify(state), RUNON, runOn ); state = await driver._getKV(internals.migrationState, MSTATE); runOn = state.run_on; state = state.value; state = JSON.parse(state); if (state.s.ID !== ID) { owner = false; } }, step: async function (driver, step, internals) { log.verbose(`[state] proceeded to step ${step}`); if (internals.dryRun) { return Promise.resolve(); } let { value: state } = await driver._getKV( internals.migrationState, MSTATE ); state = JSON.parse(state); state.s.date = new Date(); state.s.step = step; return driver._updateKV( internals.migrationState, MSTATE, JSON.stringify(state) ); }, deleteState: function (driver, file, internals) { return driver._deleteKV(internals.migrationState, file.name); }, endMigration: async function (driver, file, internals) { if (internals.dryRun) { return Promise.resolve(); } let { value: state } = await driver._getKV( internals.migrationState, MSTATE ); state = JSON.parse(state); state.s.date = new Date(); state.s.fin = 1; // unlock from current process state.s.ID = 0; return driver._updateKV( internals.migrationState, MSTATE, JSON.stringify(state) ); } };