Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make MigrationExecutor API more controllable and public #4880

Closed
leonardofalk opened this issue Oct 9, 2019 · 0 comments
Closed

Make MigrationExecutor API more controllable and public #4880

leonardofalk opened this issue Oct 9, 2019 · 0 comments

Comments

@leonardofalk
Copy link
Contributor

Issue type:

[ ] question
[ ] bug report
[x] feature request
[ ] documentation issue

Database system/driver:

Doesn't apply to a specific driver.

TypeORM version:

[x] latest
[ ] @next
[ ] 0.x.x (or put your version here)

I'd like to use MigrationExecutor to deal with migrations myself, today all that can be done is calling runMigrations, and a few other methods.

In my scenario, if by some reason a duplicated table/column error is thrown I'd like to ignore this migration, add it to done migrations and call the next one, this is impossible without calling MigrationExecutor private API or reimplementing it.

This is a piece of code that show how I'm doing it today:

import { Connection, QueryRunner } from 'typeorm/browser'
import { MigrationExecutor } from 'typeorm/browser/migration/MigrationExecutor'
import _ from 'lodash'

const IGNORED_ERRORS = [
  /table .+ already exists/i,
  /index .+ already exists/i,
  /duplicate column name/i,
  /no such table: .+/i,
  /no such index: .+/i,
].map(r => new RegExp(r))

export class DatabaseMigrationService {
  executor: any
  queryRunner: QueryRunner

  constructor(connection: Connection) {
    this.executor = new MigrationExecutor(connection)
    this.queryRunner = this.executor.queryRunner || connection.createQueryRunner('master')
  }

  call() {
    const queryRunner = this.queryRunner

    return new Promise(async (resolve, reject) => {
      try {
        const migrations: Migration[] = this.executor.getMigrations()
        const doneMigrations = await this.executor.loadExecutedMigrations(queryRunner)
        const newMigrations: any = _.reject(migrations, migration =>
          _.find(doneMigrations, ['timestamp', migration.timestamp])
        )

        _.each(newMigrations, async migration => await this.executeMigration(queryRunner, migration))

        resolve()
      } catch (e) {
        reject(e)
      } finally {
        if (this.queryRunner) {
          this.queryRunner.release()
        }
      }
    })
  }

  executeMigration(queryRunner: QueryRunner, migration: Migration): Promise<boolean> {
    this.executor.transaction = false

    return new Promise(async (resolve, reject) => {
      try {
        this.registerLog(`Migration ${migration.name} started`)

        await (migration.instance as any).up(queryRunner)
        await this.insertExecutedMigration(migration)

        resolve(true)
      } catch (e) {
        this.registerLog(e.message, { level: 'ERROR' })

        if (this.shouldIgnoreError(e)) {
          this.insertExecutedMigration(migration)
          resolve(true)
        } else {
          reject(e)
        }
      } finally {
        this.registerLog(`Migration ${migration.name} finished`)
      }
    })
  }

  shouldIgnoreError(error: Error): boolean {
    return _.some(IGNORED_ERRORS, r => r.test(error.message))
  }

  registerLog(content: string, props = {}) {
    // register this with external apis ...
  }

  insertExecutedMigration(migration: Migration) {
    return this.executor.insertExecutedMigration(this.queryRunner, migration)
  }
}

I'd like to propose this changes:

  • Make getMigrations, loadExecutedMigrations, insertExecutedMigration, deleteExecutedMigration, methods public
  • Make import { MigrationExecutor } from 'typeorm' available
  • Maybe add/rename some methods (ie.: showMigrations could be hasMigrationsPending, while showMigrations could be preserved as a way to log migration status), add getPendingMigrations, rename/add loadExecutedMigrations to getDoneMigrations, and maybe other methods

If this is approved I could put a work on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant