Skip to content

Commit

Permalink
feat(migrations): allow using migrations with ES modules
Browse files Browse the repository at this point in the history
Closes #2631
  • Loading branch information
B4nan committed Jan 12, 2022
1 parent eedace0 commit 072f23f
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 12 deletions.
2 changes: 2 additions & 0 deletions docs/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@ MIKRO_ORM_ENTITIES = ./dist/foo/*.entity.js, ./dist/bar/*.entity.js
MIKRO_ORM_ENTITIES_TS = ./src/foo/*.entity.ts, ./src/bar/*.entity.ts
MIKRO_ORM_DB_NAME = test.db
MIKRO_ORM_MIGRATIONS_PATH = ./dist/migrations
MIKRO_ORM_MIGRATIONS_PATH_TS = ./src/migrations
MIKRO_ORM_MIGRATIONS_PATTERN = ^[\w-]+\d+\.js$
MIKRO_ORM_POPULATE_AFTER_FLUSH = true
MIKRO_ORM_FORCE_ENTITY_CONSTRUCTOR = true
Expand Down Expand Up @@ -501,6 +502,7 @@ Full list of supported options:
| `MIKRO_ORM_DISCOVERY_DISABLE_DYNAMIC_FILE_ACCESS` | `discovery.disableDynamicFileAccess` |
| `MIKRO_ORM_MIGRATIONS_TABLE_NAME` | `migrations.tableName` |
| `MIKRO_ORM_MIGRATIONS_PATH` | `migrations.path` |
| `MIKRO_ORM_MIGRATIONS_PATH_TS` | `migrations.pathTs` |
| `MIKRO_ORM_MIGRATIONS_PATTERN` | `migrations.pattern` |
| `MIKRO_ORM_MIGRATIONS_TRANSACTIONAL` | `migrations.transactional` |
| `MIKRO_ORM_MIGRATIONS_DISABLE_FOREIGN_KEYS` | `migrations.disableForeignKeys` |
Expand Down
11 changes: 10 additions & 1 deletion docs/docs/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,16 @@ Snapshotting can be disabled via `migrations.snapshot: false` in the ORM config.

> Since v5, `umzug` 3.0 is used, and `pattern` option has been replaced with `glob`.
> `migrations.path` and `migrations.pathTs` works the same way as `entities` and
> `entitiesTs` in entity discovery.
```typescript
await MikroORM.init({
// default values:
migrations: {
tableName: 'mikro_orm_migrations', // name of database table with log of executed transactions
path: './migrations', // path to the folder with migrations
pathTs: undefined, // path to the folder with TS migrations (if used, you should put path to compiled files in `path`)
glob: '!(*.d).{js,ts}', // how to match migration files (all .js and .ts files, but not .d.ts)
transactional: true, // wrap each migration in a transaction
disableForeignKeys: true, // wrap statements with `set foreign_key_checks = 0` or equivalent
Expand All @@ -100,8 +104,13 @@ import { MikroORM, Utils } from '@mikro-orm/core';

await MikroORM.init({
migrations: {
path: Utils.detectTsNode() ? 'src/migrations' : 'dist/migrations',
path: 'dist/migrations',
pathTs: 'src/migrations',
},
// or alternatively
// migrations: {
// path: Utils.detectTsNode() ? 'src/migrations' : 'dist/migrations',
// },
// ...
});
```
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/utils/Configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ export interface ConnectionOptions {
export type MigrationsOptions = {
tableName?: string;
path?: string;
pathTs?: string;
glob?: string;
transactional?: boolean;
disableForeignKeys?: boolean;
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/utils/ConfigurationLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ export class ConfigurationLoader {
ret.migrations = {};
read(ret.migrations, 'MIKRO_ORM_MIGRATIONS_TABLE_NAME', 'tableName');
read(ret.migrations, 'MIKRO_ORM_MIGRATIONS_PATH', 'path');
read(ret.migrations, 'MIKRO_ORM_MIGRATIONS_PATH_TS', 'pathTs');
read(ret.migrations, 'MIKRO_ORM_MIGRATIONS_GLOB', 'glob');
read(ret.migrations, 'MIKRO_ORM_MIGRATIONS_TRANSACTIONAL', 'transactional', bool);
read(ret.migrations, 'MIKRO_ORM_MIGRATIONS_DISABLE_FOREIGN_KEYS', 'disableForeignKeys', bool);
Expand Down
2 changes: 1 addition & 1 deletion packages/migrations/src/JSMigrationGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class JSMigrationGenerator extends MigrationGenerator {
generateMigrationFile(className: string, diff: { up: string[]; down: string[] }): string {
let ret = `'use strict';\n`;
ret += `Object.defineProperty(exports, '__esModule', { value: true });\n`;
ret += `const Migration = require('@mikro-orm/migrations').Migration;\n\n`;
ret += `const { Migration } = require('@mikro-orm/migrations');\n\n`;
ret += `class ${className} extends Migration {\n\n`;
ret += ` async up() {\n`;
diff.up.forEach(sql => ret += this.createStatement(sql, 4));
Expand Down
26 changes: 19 additions & 7 deletions packages/migrations/src/Migrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ export class Migrator implements IMigrator {
private readonly schemaGenerator = new SchemaGenerator(this.em);
private readonly config = this.em.config;
private readonly options = this.config.get('migrations');
private readonly absolutePath = Utils.absolutePath(this.options.path!, this.config.get('baseDir'));
private readonly snapshotPath = join(this.absolutePath, `.snapshot-${this.config.get('dbName')}.json`);
private readonly absolutePath: string;
private readonly snapshotPath: string;

constructor(private readonly em: EntityManager) {
/* istanbul ignore next */
const key = (this.config.get('tsNode', Utils.detectTsNode()) && this.options.pathTs) ? 'pathTs' : 'path';
this.absolutePath = Utils.absolutePath(this.options[key]!, this.config.get('baseDir'));
this.snapshotPath = join(this.absolutePath, `.snapshot-${this.config.get('dbName')}.json`);
this.createUmzug();
}

Expand Down Expand Up @@ -200,11 +204,19 @@ export class Migrator implements IMigrator {
}

protected resolve(params: MigrationParams<any>): RunnableMigration<any> {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const migration = require(params.path!);
const MigrationClass = Object.values(migration)[0] as Constructor<Migration>;
const createMigrationHandler = async (method: 'up' | 'down') => {
const migration = await Utils.dynamicImport(params.path!);
const MigrationClass = Object.values(migration)[0] as Constructor<Migration>;
const instance = new MigrationClass(this.driver, this.config);

await this.runner.run(instance, method);
};

return this.initialize(MigrationClass, params.name);
return {
name: this.storage.getMigrationName(params.name),
up: () => createMigrationHandler('up'),
down: () => createMigrationHandler('down'),
};
}

protected async getCurrentSchema(): Promise<DatabaseSchema> {
Expand Down Expand Up @@ -340,7 +352,7 @@ export class Migrator implements IMigrator {

private async ensureMigrationsDirExists() {
if (!this.options.migrationsList) {
await ensureDir(Utils.normalizePath(this.options.path!));
await ensureDir(this.absolutePath);
}
}

Expand Down
16 changes: 15 additions & 1 deletion tests/features/migrations/Migrator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ class MigrationTest1 extends Migration {
this.addSql('select 1 + 1');
}

async down(): Promise<void> {
this.addSql('select 1 - 1');
}

}

class MigrationTest2 extends Migration {
Expand All @@ -27,6 +31,15 @@ class MigrationTest2 extends Migration {
expect(res).toEqual([{ count1: 2 }]);
}

async down(): Promise<void> {
this.addSql('select 1 - 1');
const knex = this.getKnex();
this.addSql(knex.raw('select 1 - 1'));
this.addSql(knex.select(knex.raw('2 - 2 as count2')));
const res = await this.execute('select 1 - 1 as count1');
expect(res).toEqual([{ count1: 2 }]);
}

isTransactional(): boolean {
return false;
}
Expand Down Expand Up @@ -252,7 +265,6 @@ describe('Migrator', () => {
expect(mock.mock.calls[5][0]).toMatch('commit');
mock.mock.calls.length = 0;

await expect(runner.run(migration1, 'down')).rejects.toThrowError('This migration cannot be reverted');
const executed = await migrator.getExecutedMigrations();
expect(executed).toEqual([]);

Expand Down Expand Up @@ -403,6 +415,8 @@ describe('Migrator - with explicit migrations', () => {
const spy1 = jest.spyOn(Migration.prototype, 'addSql');
await migrator.up();
expect(spy1).toBeCalledWith('select 1 + 1');
await migrator.down();
expect(spy1).toBeCalledWith('select 1 - 1');
const calls = mock.mock.calls.map(call => {
return call[0]
.replace(/ \[took \d+ ms]/, '')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ exports[`Migrator (postgres) generate js schema migration: migration-js-dump 1`]
Object {
"code": "'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const Migration = require('@mikro-orm/migrations').Migration;
const { Migration } = require('@mikro-orm/migrations');
class Migration20191013214813 extends Migration {
Expand Down
14 changes: 13 additions & 1 deletion tests/features/migrations/__snapshots__/Migrator.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ Array [
"release savepointtrx_xx (via write connection '127.0.0.1')",
"insert into \`mikro_orm_migrations\` (\`name\`) values (?) (via write connection '127.0.0.1')",
"commit (via write connection '127.0.0.1')",
"select 1 from information_schema.schemata where schema_name = 'mikro_orm_test' (via write connection '127.0.0.1')",
"select table_name as table_name, nullif(table_schema, schema()) as schema_name, table_comment as table_comment from information_schema.tables where table_type = 'BASE TABLE' and table_schema = schema() (via write connection '127.0.0.1')",
"begin (via write connection '127.0.0.1')",
"select * from \`mikro_orm_migrations\` order by \`id\` asc (via write connection '127.0.0.1')",
"savepointtrx_xx (via write connection '127.0.0.1')",
"set names utf8mb4; (via write connection '127.0.0.1')",
"set foreign_key_checks = 0; (via write connection '127.0.0.1')",
"select 1 - 1 (via write connection '127.0.0.1')",
"set foreign_key_checks = 1; (via write connection '127.0.0.1')",
"release savepointtrx_xx (via write connection '127.0.0.1')",
"delete from \`mikro_orm_migrations\` where \`name\` in (?, ?) (via write connection '127.0.0.1')",
"commit (via write connection '127.0.0.1')",
]
`;

Expand Down Expand Up @@ -355,7 +367,7 @@ exports[`Migrator generate js schema migration: migration-js-dump 1`] = `
Object {
"code": "'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const Migration = require('@mikro-orm/migrations').Migration;
const { Migration } = require('@mikro-orm/migrations');
class Migration20191013214813 extends Migration {
Expand Down

0 comments on commit 072f23f

Please sign in to comment.