Skip to content

Commit 334935f

Browse files
authored
feat(CLI): Adds command to the order of migrations (#1524 - LLC-11)
#1524
1 parent d34bafa commit 334935f

File tree

5 files changed

+126
-11
lines changed

5 files changed

+126
-11
lines changed

cli/src/migrateMongo/index.js

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import logger from 'lib/logger';
22
import migration from 'cli/migrateMongo/models/migration';
3-
import { isBoolean, isString, map } from 'lodash';
3+
import { isBoolean, isString, map, find } from 'lodash';
44
import v2Migrations from 'cli/commands/v2-migrations';
55
import { OrderedMap, List } from 'immutable';
6+
import { map as bmap } from 'bluebird';
67
import migrate from './migrate';
78

89
const getOutstandingMigrations = async ({ down, up, migrations }) => {
9-
const lastMigration = await migration.findOne({}).sort({ updatedAt: -1 });
10+
const lastMigration = await migration.findOne({}).sort({ order: -1, updatedAt: -1 });
1011

1112
const lastKey = lastMigration ? lastMigration.key : null;
1213
if (down) {
@@ -80,8 +81,35 @@ const getOutstandingMigrations = async ({ down, up, migrations }) => {
8081
return outstandingMigrations;
8182
};
8283

84+
export const fixDbMigrationOrder = async ({ migrations }) => {
85+
const dbMigrations = await migration.find({}).sort({ order: 1 });
86+
let toSave = [];
87+
88+
let inc = 1;
89+
for (const migrationItem of migrations.keySeq().toJS()) {
90+
// eslint-disable-next-line no-loop-func
91+
const dbMigration = find(
92+
dbMigrations,
93+
dbMigrationItem => migrationItem === dbMigrationItem.key
94+
);
95+
if (!dbMigration) {
96+
break;
97+
}
98+
99+
dbMigration.order = inc;
100+
toSave = [dbMigration, ...toSave];
101+
102+
inc += 1;
103+
}
104+
105+
await bmap(toSave, (toSav) => {
106+
const out = toSav.save();
107+
return out;
108+
});
109+
};
110+
83111
const checkRunMigrations = async ({ migrations }) => {
84-
const dbMigrations = new List(await migration.find({}));
112+
const dbMigrations = new List(await migration.find({}).sort({ order: 1 }));
85113
const actualMigrations = new List(migrations);
86114
const allMigrations = dbMigrations.zip(actualMigrations);
87115

@@ -138,15 +166,15 @@ const displayInfo = async ({ info, migrations }) => {
138166
const verbose = info === 'v' || info === 'verbose';
139167

140168
if (!verbose) {
141-
const lastRunMigration = await migration.findOne().sort({ updatedAt: -1 });
169+
const lastRunMigration = await migration.findOne().sort({ order: -1, updatedAt: -1 });
142170

143171
if (!lastRunMigration) {
144172
logger.info('No migrations have been run');
145173
} else {
146174
logger.info('Last run migration: ', lastRunMigration.key);
147175
}
148176
} else {
149-
const runMigrations = await migration.find().sort({ updatedAt: 1 });
177+
const runMigrations = await migration.find().sort({ order: 1, updatedAt: 1 });
150178

151179
const output = map(runMigrations, item => item.key);
152180

@@ -170,10 +198,12 @@ const displayInfo = async ({ info, migrations }) => {
170198
}
171199
};
172200

173-
export default async function ({ down, up, info, migrations = v2Migrations, dontExit = false }, next = null) {
201+
export default async function ({ down, up, info, migrations = v2Migrations, dontExit = false, fixOrder = false }, next = null) {
174202
try {
175203
if (info) {
176204
await displayInfo({ info, migrations });
205+
} else if (fixOrder) {
206+
await fixDbMigrationOrder({ migrations });
177207
} else {
178208
await doMigrations({ down, up, migrations });
179209
}

cli/src/migrateMongo/migrate.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const downFn = async ({ key, down }) => {
1414
logger.info(`Finished down migration of ${key}`);
1515
};
1616

17-
const migrateOne = async ({ migrationKey, migration, down: doDown }) => {
17+
const migrateOne = async ({ migrationKey, migration, down: doDown, order }) => {
1818
const { up, down } = migration;
1919

2020
if (doDown) {
@@ -29,11 +29,13 @@ const migrateOne = async ({ migrationKey, migration, down: doDown }) => {
2929
logger.info(`Starting up migration of ${migrationKey}`);
3030
await up();
3131

32-
await new Migration({
32+
const out = await new Migration({
3333
key: migrationKey,
34-
upFn: up.toString()
34+
upFn: up.toString(),
35+
order
3536
}).save();
3637
logger.info(`Finished up migration of ${migrationKey}`);
38+
return out;
3739
} catch (err) {
3840
logger.error(`Error migrating up ${migrationKey}, Reverting ${migrationKey}`);
3941
await downFn({
@@ -54,6 +56,7 @@ const migrate = async ({ migrations, down }) => {
5456
return migrateOne({
5557
migrationKey: key,
5658
migration: value,
59+
order: (acc || { order: 0 }).order + 1,
5760
down
5861
});
5962
},

cli/src/migrateMongo/migrateMongo-test.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,84 @@ describe('migrateMongo', () => {
232232
await expect(result).to.eventually.be.rejectedWith(Error);
233233
});
234234
});
235+
describe('fix order', () => {
236+
it('should set the order', async () => {
237+
await promisify(migrator)({
238+
migrations,
239+
dontExit: true
240+
});
241+
242+
const dbRunMigrations = await migration.find({
243+
key: { $in: ['001-test', '002-test'] }
244+
});
245+
expect(dbRunMigrations.length).to.equal(2);
246+
expect(dbRunMigrations[0].order).to.equal(1);
247+
expect(dbRunMigrations[1].order).to.equal(2);
248+
});
249+
250+
it('should reorder', async () => {
251+
await promisify(migrator)({
252+
migrations,
253+
dontExit: true
254+
});
255+
256+
const dbRunMigrations = await migration.find({
257+
key: { $in: ['001-test', '002-test'] }
258+
});
259+
dbRunMigrations[0].order = 2;
260+
await dbRunMigrations[0].save();
261+
262+
dbRunMigrations[1].order = 1;
263+
await dbRunMigrations[1].save();
264+
265+
await promisify(migrator)({
266+
migrations,
267+
dontExit: true,
268+
fixOrder: true
269+
});
270+
271+
const dbRunMigrations2 = await migration.find({
272+
key: { $in: ['001-test', '002-test'] }
273+
});
274+
275+
expect(dbRunMigrations2[0].order).to.equal(1);
276+
expect(dbRunMigrations2[1].order).to.equal(2);
277+
278+
let errored = false;
279+
try {
280+
await promisify(migrator)({
281+
migrations,
282+
dontExit: true,
283+
});
284+
} catch (err) {
285+
errored = true;
286+
}
287+
expect(errored).to.equal(false);
288+
});
289+
290+
it('should migrate if first items have no order', async () => {
291+
await promisify(migrator)({
292+
migrations,
293+
dontExit: true
294+
});
295+
296+
const dbRunMigrations = await migration.find({
297+
key: { $in: ['001-test', '002-test'] }
298+
});
299+
dbRunMigrations[0].order = null;
300+
await dbRunMigrations[0].save();
301+
302+
await promisify(migrator)({
303+
migrations,
304+
dontExit: true
305+
});
306+
307+
const dbRunMigrations2 = await migration.find({
308+
key: { $in: ['001-test', '002-test'] }
309+
});
310+
311+
expect(dbRunMigrations2[0].order).to.equal(null);
312+
expect(dbRunMigrations2[1].order).to.equal(2);
313+
});
314+
});
235315
});

cli/src/migrateMongo/models/migration.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { getConnection } from 'lib/connections/mongoose';
44

55
const schema = new mongoose.Schema({
66
key: String,
7-
upFn: String
7+
upFn: String,
8+
order: Number
89
});
910

1011
schema.plugin(timestamps);

cli/src/server.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ program
164164
'-d, --down <down>',
165165
"Optional, runs down migration down to [down file name], also accepts ['last']"
166166
)
167-
.option('-i, --info [info]', "Display the state of the migrations, optional ['v'|'verbose']");
167+
.option('-i, --info [info]', "Display the state of the migrations, optional ['v'|'verbose']")
168+
.option('-f, --fixOrder [fixOrder]', 'Resolves the migration order conflicts');
168169
// node cli/dist/server migrateMongo
169170

170171
program

0 commit comments

Comments
 (0)