Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Usage: migrate-mongo [options] [command]

Options:

-c, --custom specify a custom migration file (only for up)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bdcorps having a down custom migration also will be super useful. Specially for some quick hot fixes. See if you can consider a down migration support for custom migration.

-h, --help output usage information
-V, --version output the version number
````
Expand Down
1 change: 1 addition & 0 deletions bin/migrate-mongo.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ program
program
.command("up")
.description("run all pending database migrations")
.option("-c --custom <custom>", "run a custom migration file")
.option("-f --file <file>", "use a custom config file")
.action(options => {
global.options = options;
Expand Down
45 changes: 31 additions & 14 deletions lib/actions/up.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
const _ = require("lodash");
const pEachSeries = require("p-each-series");
const { promisify } = require("util");
const fnArgs = require('fn-args');
const fnArgs = require("fn-args");

const status = require("./status");
const config = require("../env/config");
const migrationsDir = require("../env/migrationsDir");
const hasCallback = require('../utils/has-callback');
const hasCallback = require("../utils/has-callback");

module.exports = async (db, client) => {
const statusItems = await status(db);
const pendingItems = _.filter(statusItems, { appliedAt: "PENDING" });
const migrated = [];

const migrateItem = async item => {
// console.log(pendingItems);
const migrateItem = async (item) => {
try {
const migration = await migrationsDir.loadMigration(item.fileName);
const up = hasCallback(migration.up) ? promisify(migration.up) : migration.up;
const up = hasCallback(migration.up)
? promisify(migration.up)
: migration.up;

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

} catch (err) {
const error = new Error(
`Could not migrate up ${item.fileName}: ${err.message}`
Expand All @@ -33,20 +35,35 @@ module.exports = async (db, client) => {
throw error;
}

const { changelogCollectionName } = await config.read();
const changelogCollection = db.collection(changelogCollectionName);
if (item.appliedAt != "CUSTOM")
{
// console.log("writing", item.appliedAt);
const { changelogCollectionName } = await config.read();
const changelogCollection = db.collection(changelogCollectionName);

const { fileName } = item;
const appliedAt = new Date();
const { fileName } = item;
const appliedAt = new Date();

try {
await changelogCollection.insertOne({ fileName, appliedAt });
} catch (err) {
throw new Error(`Could not update changelog: ${err.message}`);
try {
await changelogCollection.insertOne({ fileName, appliedAt });
} catch (err) {
throw new Error(`Could not update changelog: ${err.message}`);
}
}

migrated.push(item.fileName);
};

await pEachSeries(pendingItems, migrateItem);
// console.log("some", global.options);
if (global.options.custom) {
await migrateItem(
{ fileName: global.options.custom, appliedAt: "CUSTOM" },
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bdcorps IMO it would be better if we stored the date in appliedAt field. It will be useful to check when this particular migration was applied. Perhaps a boolean field will be better to differentiate between the regular and custom migration.

false
);
} else {
// console.log("all");
await pEachSeries(pendingItems, migrateItem);
}

return migrated;
};
3 changes: 2 additions & 1 deletion lib/env/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = {
const url = _.get(configContent, "mongodb.url");
const databaseName = _.get(configContent, "mongodb.databaseName");
const options = _.get(configContent, "mongodb.options");

console.log("config", configContent);
if (!url) {
throw new Error("No `url` defined in config file!");
}
Expand All @@ -25,4 +25,5 @@ module.exports = {
db,
};
}

};
14 changes: 14 additions & 0 deletions samples/migrations/20200911163740-desc2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
async up(db, client) {
// TODO write your migration here.
// See https://github.com/seppevs/migrate-mongo/#creating-a-new-migration-script
// Example:
// await db.collection('albums').updateOne({artist: 'The Beatles'}, {$set: {blacklisted: true}});
},

async down(db, client) {
// TODO write the statements to rollback your migration (if possible)
// Example:
// await db.collection('albums').updateOne({artist: 'The Beatles'}, {$set: {blacklisted: false}});
}
};
14 changes: 14 additions & 0 deletions samples/migrations/dev2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
async up(db, client) {
// TODO write your migration here.
// See https://github.com/seppevs/migrate-mongo/#creating-a-new-migration-script
// Example:
// await db.collection('albums').updateOne({artist: 'The Beatles'}, {$set: {blacklisted: true}});
},

async down(db, client) {
// TODO write the statements to rollback your migration (if possible)
// Example:
// await db.collection('albums').updateOne({artist: 'The Beatles'}, {$set: {blacklisted: false}});
}
};
18 changes: 18 additions & 0 deletions test/actions/up.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe("up", () => {

let firstPendingMigration;
let secondPendingMigration;
let firstCustomMigration;
let changelogCollection;

function mockStatus() {
Expand All @@ -26,6 +27,10 @@ describe("up", () => {
fileName: "20160606093207-second_applied_migration.js",
appliedAt: new Date()
},
{
fileName: "20160608060210-first_custom_migration.js",
appliedAt: "CUSTOM"
},
{
fileName: "20160607173840-first_pending_migration.js",
appliedAt: "PENDING"
Expand Down Expand Up @@ -56,6 +61,9 @@ describe("up", () => {
mock.loadMigration
.withArgs("20160608060209-second_pending_migration.js")
.returns(Promise.resolve(secondPendingMigration));
mock.loadMigration
.withArgs("20160608060210-first_custom_migration.js")
.returns(Promise.resolve(firstCustomMigration));
return mock;
}

Expand Down Expand Up @@ -95,6 +103,7 @@ describe("up", () => {
beforeEach(() => {
firstPendingMigration = mockMigration();
secondPendingMigration = mockMigration();
firstCustomMigration = mockMigration();
changelogCollection = mockChangelogCollection();

status = mockStatus();
Expand All @@ -104,6 +113,8 @@ describe("up", () => {
client = mockClient();

up = loadUpWithInjectedMocks();

global.options={};
});

it("should fetch the status", async () => {
Expand All @@ -130,6 +141,13 @@ describe("up", () => {
sinon.assert.callOrder(firstPendingMigration.up, secondPendingMigration.up);
});

it("should be able to run a custom migration", async () => {
global.options = { custom: "20160608060210-first_custom_migration.js" };
await up(db);
expect(firstCustomMigration.up.called).to.equal(true);
expect(changelogCollection.insertOne.called).to.equal(false);
});

it("should be able to upgrade callback based migration that has both the `db` and `client` args", async () => {
firstPendingMigration = {
up(theDb, theClient, callback) {
Expand Down