Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
command: npm run lint
- run:
name: test
command: ./node_modules/.bin/jest --ci
command: ./node_modules/.bin/jest --ci --forceExit

workflows:
version: 2
Expand Down
34 changes: 32 additions & 2 deletions __tests__/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ describe("ah-sequelize-plugin", function () {
const actionhero = new Process();

beforeAll(async () => {
await actionhero.start();
await truncate([User, Post]);
try {
await actionhero.start();
await truncate([User, Post]);
} catch (error) {
console.error(error);
throw error;
}
});

afterAll(async () => await actionhero.stop());
Expand Down Expand Up @@ -138,4 +143,29 @@ describe("ah-sequelize-plugin", function () {
const count = await User.count();
expect(count).toBe(0);
});

describe("migration name update", () => {
beforeAll(async () => {
await api.sequelize.query(
"INSERT INTO \"SequelizeMeta\" (\"name\") VALUES ('0003-js-test.js'), ('0004-ts-test.ts')"
);
await actionhero.restart();
});

afterAll(async () => {
await api.sequelize.query(
"DELETE FROM \"SequelizeMeta\" WHERE \"name\" IN ('0003-js-test.js', '0004-ts-test.ts', '0003-js-test', '0004-ts-test')"
);
});

test("migration names with file extensions are fixed", async () => {
const [rows] = await api.sequelize.query('SELECT * FROM "SequelizeMeta"');
expect(rows).toEqual([
{ name: "0001-createUsersTable" },
{ name: "0002-createPostsTable" },
{ name: "0003-js-test" },
{ name: "0004-ts-test" },
]);
});
});
});
11 changes: 8 additions & 3 deletions __tests__/utils/truncate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
export async function truncate(models: Array<any>) {
await Promise.all(
models.map((model) => model.destroy({ truncate: true, force: true }))
);
try {
await Promise.all(
models.map((model) => model.destroy({ truncate: true, force: true }))
);
} catch (error) {
console.error(error); // jest has trouble showing the fill DB error
throw error;
}
}
56 changes: 47 additions & 9 deletions src/modules/migrations.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Umzug, SequelizeStorage } from "umzug";
import { Sequelize } from "sequelize";
import { Sequelize, Op } from "sequelize";
import * as path from "path";

export namespace Migrations {
Expand All @@ -17,7 +17,7 @@ export namespace Migrations {
logLevel: string
) {
logger("running sequelize migrations", "debug");
const umzugs = importMigrationsFromDirectory(
const umzugs = await importMigrationsFromDirectory(
sequelizeConfig,
sequelizeInstance,
logger,
Expand Down Expand Up @@ -65,20 +65,22 @@ export namespace Migrations {
}
}

export function importMigrationsFromDirectory(
export async function importMigrationsFromDirectory(
sequelizeConfig: SequelizeConfig,
sequelizeInstance: Sequelize,
logger: MigrationLogger,
logLevel: string
) {
const umzugs: Umzug[] = [];
(Array.isArray(sequelizeConfig.migrations)
const dirs: string[] = Array.isArray(sequelizeConfig.migrations)
? sequelizeConfig.migrations
: [sequelizeConfig.migrations]
).forEach((dir) => {
: [sequelizeConfig.migrations];
const umzugs: Umzug[] = [];
for (const dir of dirs) {
const context = sequelizeInstance.getQueryInterface();

const umzug = new Umzug({
storage: new SequelizeStorage({ sequelize: sequelizeInstance }),
context: sequelizeInstance.getQueryInterface(),
context,
migrations: {
glob: ["*.{js,ts}", { cwd: dir, ignore: "**/*.d.ts" }],
resolve: ({ path: filePath, context }) => {
Expand All @@ -104,8 +106,44 @@ export namespace Migrations {
umzug.on("reverting", (ev) => logUmzugEvent(ev.name, "reverting"));
umzug.on("reverted", (ev) => logUmzugEvent(ev.name, "reverted"));

// Older versions of Umzug would have allowed .ts and .js names in the migration
// and silently matched them. Now, we need to remove the filename prefixes in the
// database to match the migration names.
// We are doing the name update in JS rather than SQL to avoid dialect-specific commands.
const model = context.sequelize.models.SequelizeMeta;
let metaTableExists = false;
try {
await model.findOne();
metaTableExists = true;
} catch (error) {
if (error.name === "SequelizeDatabaseError") {
// it's ok, the table doesn't exist yet
} else {
throw error;
}
}

if (metaTableExists) {
const rows = await model.findAll({
where: {
name: { [Op.or]: [{ [Op.like]: "%.js" }, { [Op.like]: "%.ts" }] },
},
});

for (const row of rows) {
// @ts-ignore
const oldName: string = row.name;
const newName = oldName.replace(/\.ts$/, "").replace(/\.js$/, "");
await model.update({ name: newName }, { where: { name: oldName } });
logger(
`[migration] renamed migration '${oldName}' to '${newName}'`,
logLevel
);
}
}

umzugs.push(umzug);
});
}

return umzugs;
}
Expand Down