UNPKG

@coko/server

Version:

Reusable server for use by Coko's projects

163 lines 7.83 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.migrationManager = exports.executed = exports.pending = exports.rollback = exports.migrate = void 0; const internals_1 = __importDefault(require("../logger/internals")); const config_1 = __importDefault(require("../configManager/config")); const logger_1 = __importDefault(require("../logger")); const migrateDbHelpers_1 = require("./migrateDbHelpers"); const MigrateOptionIntegrityError_1 = __importDefault(require("./errors/MigrateOptionIntegrityError")); const MigrateSkipLimitError_1 = __importDefault(require("./errors/MigrateSkipLimitError")); const RollbackLimitError_1 = __importDefault(require("./errors/RollbackLimitError")); const RollbackUnavailableError_1 = __importDefault(require("./errors/RollbackUnavailableError")); const migrationRunner_1 = __importDefault(require("./migrationRunner")); const META_ID = '1715865523-create-coko-server-meta'; // #region helpers const updateCheckpoint = async () => { const baseMsg = 'Last successful migrate checkpoint:'; if (!(await migrateDbHelpers_1.migrationsMeta.exists())) { internals_1.default.error(`Coko server meta table does not exist! Not updating last successful migrate checkpoint`); return; } const lastMigration = await migrateDbHelpers_1.migrations.getLastMigration(); const currentCheckpoint = await migrateDbHelpers_1.migrationsMeta.getCheckpoint(); if (lastMigration === currentCheckpoint) { internals_1.default.point(`${baseMsg} Checkpoint already at latest migration. Performing no operation.`); return; } internals_1.default.point(`${baseMsg} updating`); await migrateDbHelpers_1.migrationsMeta.setCheckpoint(lastMigration); internals_1.default.success(`${baseMsg} updated`); }; // #endregion helpers // #region commands const migrate = async (passedConfig, options = {}) => { internals_1.default.section(`Run migrations`); const migrationRunner = new migrationRunner_1.default(passedConfig.get('components')); await migrationRunner.init(); const { skipLast, ...otherOptions } = options; const isSkipLastDefined = typeof skipLast !== 'undefined' && !Number.isNaN(skipLast); if (isSkipLastDefined) { if (!Number.isInteger(skipLast) || skipLast <= 0) { throw new MigrateOptionIntegrityError_1.default('Skip value must be a positive integer.'); } const pending = await migrationRunner.pending(); if (pending.length === 0) { throw new MigrateSkipLimitError_1.default('There are no pending migrations.'); } if (skipLast === pending.length) { throw new MigrateSkipLimitError_1.default('Skip value equals number of pending migrations. There is nothing to migrate.'); } if (skipLast > pending.length) { throw new MigrateSkipLimitError_1.default('Skip value exceeds number of pending migrations.', pending.length - 1); } const runTo = pending[pending.length - 1 - skipLast].name; await migrationRunner.up({ to: runTo }); } else { await migrationRunner.up(otherOptions); } internals_1.default.success('All migrations ran successfully!'); await updateCheckpoint(); }; exports.migrate = migrate; const rollback = async (passedConfig, options = {}) => { if (!(await migrateDbHelpers_1.migrationsMeta.exists())) throw new RollbackUnavailableError_1.default(); const migrationRows = await migrateDbHelpers_1.migrations.getRows(); const metaPosition = migrationRows.findIndex(item => migrationRunner_1.default.stripMigrationExtensionName(item.id) === META_ID); const metaIsLast = metaPosition === migrationRows.length - 1; if (metaIsLast) { throw new RollbackLimitError_1.default('No migrations have run after the upgrade.', { metaLimit: true, }); } const downOptions = { ...options }; const checkpoint = await migrateDbHelpers_1.migrationsMeta.getCheckpoint(); if (!options.lastSuccessfulRun) { const maximum = migrationRows.length - 1 - metaPosition; const stepTooFar = (options.step || 1) > maximum; if (stepTooFar) { throw new RollbackLimitError_1.default(`Maximum steps value for the current state of the migration table is ${maximum}.`, { metaLimit: true }); } } else { const checkpointPosition = migrationRows.findIndex(item => item.id === checkpoint); const checkpointTooFar = checkpointPosition <= metaPosition; if (checkpointTooFar) { throw new RollbackLimitError_1.default(`Check that the checkpoint in the coko_server_meta table in your database is a migration that ran after ${META_ID}`, { metaLimit: true }); } /** * The 'to' option is inclusive, ie. it will revert all migrations, * INCLUDING the one specified. We want to roll back up to, but not * including the specified migration. So we find the one right after. */ if (migrationRows.length - 1 === checkpointPosition) { throw new RollbackLimitError_1.default('No migrations have completed successfully since the last checkpoint. There is nothing to revert.'); } const revertTo = migrationRows[checkpointPosition + 1].id; downOptions.to = revertTo; } /** * If we don't clear the checkpoint, we get a reference error, as the * checkpoint is a foreign key to the migrations id column. */ await migrateDbHelpers_1.migrationsMeta.clearCheckpoint(); try { const migrationRunner = new migrationRunner_1.default(passedConfig.get('components')); await migrationRunner.init(); await migrationRunner.down(downOptions); await updateCheckpoint(); logger_1.default.info('Migrate: Migration rollback successful!'); } catch (e) { // Restore original cleared checkpoint if (checkpoint) await migrateDbHelpers_1.migrationsMeta.setCheckpoint(checkpoint); logger_1.default.error(e); throw e; } }; exports.rollback = rollback; const pending = async (passedConfig) => { const migrationRunner = new migrationRunner_1.default(passedConfig.get('components')); await migrationRunner.init(); const pendingMigrations = await migrationRunner.pending(); if (pendingMigrations.length === 0) { logger_1.default.info('Migrate: There are no pending migrations.'); } else { logger_1.default.info(`Migrate: Pending migrations:`); logger_1.default.info(pendingMigrations); } return pendingMigrations; }; exports.pending = pending; const executed = async (passedConfig) => { const migrationRunner = new migrationRunner_1.default(passedConfig.get('components')); await migrationRunner.init(); const executedMigrations = await migrationRunner.executed(); if (executedMigrations.length === 0) { logger_1.default.info('Migrate: There are no executed migrations.'); } else { logger_1.default.info(`Migrate: Executed migrations:`); logger_1.default.info(executedMigrations); } return executedMigrations; }; exports.executed = executed; // #endregion commmands exports.migrationManager = { migrate: async (options) => { await (0, exports.migrate)(config_1.default, options); }, rollback: async (options) => { await (0, exports.rollback)(config_1.default, options); }, pending: async () => (0, exports.pending)(config_1.default), executed: async () => (0, exports.executed)(config_1.default), }; //# sourceMappingURL=migrate.js.map