Skip to content

Commit

Permalink
Enable rollback of all scripts from same migration
Browse files Browse the repository at this point in the history
  • Loading branch information
ftreguer committed Mar 24, 2021
1 parent 4906ff2 commit bc4bbcb
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 4,829 deletions.
6 changes: 6 additions & 0 deletions README.md
Expand Up @@ -233,6 +233,12 @@ $ migrate-mongo status
└─────────────────────────────────────────┴────────────┘
````

#### Migrate down all scripts from a same migration
With the flag -b (--block), migrate-mongo will revert all scripts of the last migration.
````bash
$ migrate-mongo down -b
````

## Advanced Features

### Using a custom config file
Expand Down
3 changes: 2 additions & 1 deletion bin/migrate-mongo.js
Expand Up @@ -20,7 +20,7 @@ function handleError(err) {
function printStatusTable(statusItems) {
return migrateMongo.config.read().then(config => {
const useFileHash = config.useFileHash === true;
const table = new Table({ head: useFileHash ? ["Filename", "Hash", "Applied At"] : ["Filename", "Applied At"]});
const table = new Table({ head: useFileHash ? ["Filename", "Hash", "Applied At", "Migration block"] : ["Filename", "Applied At", "Migration block"]});
statusItems.forEach(item => table.push(_.values(item)));
console.log(table.toString());
})
Expand Down Expand Up @@ -82,6 +82,7 @@ program
.command("down")
.description("undo the last applied database migration")
.option("-f --file <file>", "use a custom config file")
.option("-b --block", "rollback all scripts from the same migration block")
.action(options => {
global.options = options;
migrateMongo.database
Expand Down
57 changes: 35 additions & 22 deletions lib/actions/down.js
@@ -1,4 +1,5 @@
const _ = require("lodash");
const pEachSeries = require("p-each-series");
const { promisify } = require("util");
const fnArgs = require('fn-args');

Expand All @@ -8,37 +9,49 @@ const migrationsDir = require("../env/migrationsDir");
const hasCallback = require('../utils/has-callback');

module.exports = async (db, client) => {
const isBlockRollback = _.get(global.options, "block");
const downgraded = [];
const statusItems = await status(db);
const appliedItems = statusItems.filter(item => item.appliedAt !== "PENDING");
const lastAppliedItem = _.last(appliedItems);

let itemsToRollback = [];

if (lastAppliedItem) {
try {
const migration = await migrationsDir.loadMigration(lastAppliedItem.fileName);
const down = hasCallback(migration.down) ? promisify(migration.down) : migration.down;
if (isBlockRollback && lastAppliedItem.migrationBlock) {
itemsToRollback = appliedItems.filter(item => item.migrationBlock === lastAppliedItem.migrationBlock).reverse();
} else {
itemsToRollback = [lastAppliedItem];
}

if (hasCallback(migration.down) && fnArgs(migration.down).length < 3) {
// support old callback-based migrations prior to migrate-mongo 7.x.x
await down(db);
} else {
await down(db, client);
const rollbackItem = async item => {
if (item) {
try {
const migration = await migrationsDir.loadMigration(item.fileName);
const down = hasCallback(migration.down) ? promisify(migration.down) : migration.down;

if (hasCallback(migration.down) && fnArgs(migration.down).length < 3) {
// support old callback-based migrations prior to migrate-mongo 7.x.x
await down(db);
} else {
await down(db, client);
}

} catch (err) {
throw new Error(
`Could not migrate down ${item.fileName}: ${err.message}`
);
}
const { changelogCollectionName } = await config.read();
const changelogCollection = db.collection(changelogCollectionName);
try {
await changelogCollection.deleteOne({ fileName: item.fileName });
downgraded.push(item.fileName);
} catch (err) {
throw new Error(`Could not update changelog: ${err.message}`);
}

} catch (err) {
throw new Error(
`Could not migrate down ${lastAppliedItem.fileName}: ${err.message}`
);
}
const { changelogCollectionName } = await config.read();
const changelogCollection = db.collection(changelogCollectionName);
try {
await changelogCollection.deleteOne({ fileName: lastAppliedItem.fileName });
downgraded.push(lastAppliedItem.fileName);
} catch (err) {
throw new Error(`Could not update changelog: ${err.message}`);
}
}

await pEachSeries(itemsToRollback, rollbackItem);
return downgraded;
};
3 changes: 2 additions & 1 deletion lib/actions/status.js
Expand Up @@ -22,7 +22,8 @@ module.exports = async db => {
}
const itemInLog = find(changelog, findTest);
const appliedAt = itemInLog ? itemInLog.appliedAt.toJSON() : "PENDING";
return useFileHash ? { fileName, fileHash, appliedAt } : { fileName, appliedAt };
const migrationBlock = itemInLog ? itemInLog.migrationBlock : undefined;
return useFileHash ? { fileName, fileHash, appliedAt, migrationBlock } : { fileName, appliedAt, migrationBlock };
}));

return statusTable;
Expand Down
3 changes: 2 additions & 1 deletion lib/actions/up.js
Expand Up @@ -12,6 +12,7 @@ module.exports = async (db, client) => {
const statusItems = await status(db);
const pendingItems = _.filter(statusItems, { appliedAt: "PENDING" });
const migrated = [];
const migrationBlock = Date.now();

const migrateItem = async item => {
try {
Expand Down Expand Up @@ -41,7 +42,7 @@ module.exports = async (db, client) => {
const appliedAt = new Date();

try {
await changelogCollection.insertOne(useFileHash === true ? { fileName, fileHash, appliedAt } : { fileName, appliedAt });
await changelogCollection.insertOne(useFileHash === true ? { fileName, fileHash, appliedAt, migrationBlock } : { fileName, appliedAt, migrationBlock });
} catch (err) {
throw new Error(`Could not update changelog: ${err.message}`);
}
Expand Down

0 comments on commit bc4bbcb

Please sign in to comment.