@coko/server
Version:
Reusable server for use by Coko's projects
163 lines • 7.83 kB
JavaScript
;
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