Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(Migrator): Added migration support
  • Loading branch information
RWOverdijk committed Oct 19, 2016
1 parent 8d87b73 commit cd5d63d
Show file tree
Hide file tree
Showing 8 changed files with 800 additions and 0 deletions.
135 changes: 135 additions & 0 deletions src/Migrator/Migration.ts
@@ -0,0 +1,135 @@
import {Scope} from '../Scope';
import {Store} from '../Store';
import {Run} from './Run';
import * as Knex from 'knex';
import * as Bluebird from 'bluebird';

export class Migration {

/**
* @type {Scope}
*/
private entityManager: Scope;

/**
* @type {{}[]}
*/
private builders: Array<{store: string, schemaBuilder: Knex.SchemaBuilder, knex: Knex}> = [];

/**
* @type {Function}
*/
private migration: Function;

/**
* @type {Run}
*/
private migrationRun: Run;

/**
* Holds whether or not this Migration is a promise.
*
* @type {boolean}
*/
private promise: boolean = false;

/**
* Construct a new Migration.
*
* @param {Function} migration
* @param {Run} run
*/
public constructor(migration: Function, run: Run) {
this.migration = migration;
this.entityManager = run.getEntityManager();
this.migrationRun = run;

this.prepare();
}

/**
* Prepare the migration by running it.
*/
private prepare(): void {
let prepared = this.migration(this);

if (prepared && 'then' in prepared) {
this.promise = true;
}
}

/**
* Get a schemabuilder to work with.
*
* @param {string} store
*
* @returns {Knex.SchemaBuilder}
*/
public getSchemaBuilder(store?: string): Knex.SchemaBuilder {
return this.getBuilder(store).schema;
}

/**
* Get a (reusable) transaction for `storeName`
*
* @param {string} storeName
*
* @returns {Bluebird<Knex.Transaction>}
*/
public getTransaction(storeName?: string): Bluebird<Knex.Transaction> {
return this.migrationRun.getTransaction(storeName);
}

/**
* Get a builder. This includes the knex instance.
*
* @param {string} store
*
* @returns {{schema: Knex.SchemaBuilder, knex: Knex}}
*/
public getBuilder(store?: string): {schema: Knex.SchemaBuilder, knex: Knex} {
let connection = this.getConnection(store);
let schemaBuilder = connection.schema;

this.builders.push({store, schemaBuilder, knex: connection});

return {schema: schemaBuilder, knex: connection};
}

/**
* Get the SQL for current builders.
*
* @returns {string}
*/
public getSQL(): string {
if (this.promise) {
throw new Error("It's not possible to get SQL for a promise based migration.");
}

return this.builders.map(builder => builder.schemaBuilder.toString()).join('\n');
}

/**
* Run the migration.
*
* @returns {Bluebird<any>}
*/
public run(): Bluebird<any> {
return Bluebird.each(this.builders, builder => {
return this.getTransaction(builder.store).then(transaction => {
return builder.schemaBuilder['transacting'](transaction).then();
});
});
}

/**
* Get connection for store.
*
* @param {string} store
*
* @returns {knex}
*/
private getConnection(store?: string): Knex {
return this.entityManager.getStore(store).getConnection(Store.ROLE_MASTER);
}
}
87 changes: 87 additions & 0 deletions src/Migrator/MigrationFile.ts
@@ -0,0 +1,87 @@
import * as fs from 'fs';
import * as path from 'path';
import * as Promise from 'bluebird';
import {MigratorConfigInterface} from './MigratorConfigInterface';

export class MigrationFile {
/**
* @type {MigratorConfigInterface}
*/
private config: MigratorConfigInterface;

/**
* @param {MigratorConfigInterface} config
*/
public constructor(config: MigratorConfigInterface) {
this.config = config;
}

/**
* Get the config.
*
* @returns {MigratorConfigInterface}
*/
public getConfig(): MigratorConfigInterface {
return this.config;
}

/**
* Create a new migration file.
*
* @param {string} name
*
* @returns {Bluebird}
*/
public create(name: string): Promise<any> {
let sourceFile = `${__dirname}/templates/migration.${this.config.extension}.js`;
let targetFile = path.join(this.config.directory, `${this.makeMigrationName(name)}.${this.config.extension}`);
let readStream = fs.createReadStream(sourceFile);
let writeStream = fs.createWriteStream(targetFile);

readStream.pipe(writeStream);

return new Promise((resolve, reject) => {
readStream.on('error', reject);
writeStream.on('error', reject);
writeStream.on('close', () => resolve());
});
}

/**
* Get all migrations from the directory.
*
* @returns {Bluebird<string[]>}
*/
public getMigrations(): Promise<Array<string>> {
return Promise.promisify(fs.readdir)(this.config.directory).then(contents => {
let regexp = new RegExp(`\.${this.config.extension}$`);

return contents
.filter(migration => migration.search(regexp) > -1)
.map(migration => migration.replace(regexp, ''));
});
}

/**
* Make migration name.
*
* @param {string} name
*
* @returns {string}
*/
private makeMigrationName(name): string {
let date = new Date();
let pad = (source) => {
source = source.toString();

return source[1] ? source : `0${source}`;
};

return date.getFullYear().toString() +
pad(date.getMonth() + 1) +
pad(date.getDate()) +
pad(date.getHours()) +
pad(date.getMinutes()) +
pad(date.getSeconds()) + `_${name}`;
}
}

0 comments on commit cd5d63d

Please sign in to comment.