mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +00:00
224 lines
6.3 KiB
TypeScript
224 lines
6.3 KiB
TypeScript
// @ts-nocheck
|
|
import { Knex } from 'knex';
|
|
import Bluebird from 'bluebird';
|
|
import { getTable, getTableName, getLockTableName } from './TableUtils';
|
|
import getMergedConfig from './SeederConfig';
|
|
import {
|
|
listAllAndCompleted,
|
|
getNewMigrations,
|
|
listCompleted,
|
|
ensureMigrationTables,
|
|
} from './MigrateUtils';
|
|
import { MigrateItem, SeedMigrationContext, ISeederConfig } from './interfaces';
|
|
import { FsMigrations } from './FsMigrations';
|
|
|
|
export class SeedMigration {
|
|
knex: Knex;
|
|
config: ISeederConfig;
|
|
migrationSource: FsMigrations;
|
|
context: SeedMigrationContext;
|
|
|
|
/**
|
|
* Constructor method.
|
|
* @param {Knex} knex - Knex instance.
|
|
* @param {SeedMigrationContext} context -
|
|
*/
|
|
constructor(knex: Knex, context: SeedMigrationContext) {
|
|
this.knex = knex;
|
|
this.config = getMergedConfig(this.knex.client.config.seeds, undefined);
|
|
this.migrationSource = this.config.migrationSource;
|
|
this.context = context;
|
|
}
|
|
|
|
/**
|
|
* Latest migration.
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async latest(config = null): Promise<void> {
|
|
// Merges the configuration.
|
|
this.config = getMergedConfig(config, this.config);
|
|
|
|
// Ensure migration tables.
|
|
await ensureMigrationTables(this.config.tableName, null, this.knex);
|
|
|
|
// Retrieve all and completed migrations.
|
|
const [all, completed] = await listAllAndCompleted(this.config, this.knex);
|
|
|
|
// Retrieve the new migrations.
|
|
const migrations = getNewMigrations(this.migrationSource, all, completed);
|
|
|
|
// Run the latest migration on one batch.
|
|
return this.knex.transaction((trx: Knex.Transaction) => {
|
|
return this.runBatch(migrations, 'up', trx);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Add migration lock flag.
|
|
* @param {Knex.Transaction} trx
|
|
* @returns
|
|
*/
|
|
private migrateLockTable(trx: Knex.Transaction) {
|
|
const tableName = getLockTableName(this.config.tableName);
|
|
return getTable(this.knex, tableName, this.config.schemaName)
|
|
.transacting(trx)
|
|
.where('is_locked', '=', 0)
|
|
.update({ is_locked: 1 })
|
|
.then((rowCount) => {
|
|
if (rowCount != 1) {
|
|
throw new Error('Migration table is already locked');
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Add migration lock flag.
|
|
* @param {Knex.Transaction} trx
|
|
* @returns
|
|
*/
|
|
private migrationLock(trx: Knex.Transaction) {
|
|
return this.migrateLockTable(trx);
|
|
}
|
|
|
|
/**
|
|
* Free the migration lock flag.
|
|
* @param {Knex.Transaction} trx
|
|
* @returns
|
|
*/
|
|
private freeLock(trx = this.knex): Promise<void> {
|
|
const tableName = getLockTableName(this.config.tableName);
|
|
|
|
return getTable(trx, tableName, this.config.schemaName).update({
|
|
is_locked: 0,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Returns the latest batch number.
|
|
* @param trx
|
|
* @returns
|
|
*/
|
|
private latestBatchNumber(trx = this.knex): number {
|
|
return trx
|
|
.from(getTableName(this.config.tableName, this.config.schemaName))
|
|
.max('batch as max_batch')
|
|
.then((obj) => obj[0].max_batch || 0);
|
|
}
|
|
|
|
/**
|
|
* Runs a batch of `migrations` in a specified `direction`, saving the
|
|
* appropriate database information as the migrations are run.
|
|
* @param {number} batchNo
|
|
* @param {MigrateItem[]} migrations
|
|
* @param {string} direction
|
|
* @param {Knex.Transaction} trx
|
|
* @returns {Promise<void>}
|
|
*/
|
|
private waterfallBatch(
|
|
batchNo: number,
|
|
migrations: MigrateItem[],
|
|
direction: string,
|
|
trx: Knex.Transaction,
|
|
): Promise<void> {
|
|
const { tableName } = this.config;
|
|
|
|
return Bluebird.each(migrations, (migration) => {
|
|
const name = this.migrationSource.getMigrationName(migration);
|
|
|
|
return this.migrationSource
|
|
.getMigration(migration)
|
|
.then((migrationContent) =>
|
|
this.runMigrationContent(migrationContent.default, direction, trx),
|
|
)
|
|
.then(() => {
|
|
if (direction === 'up') {
|
|
return trx.into(getTableName(tableName)).insert({
|
|
name,
|
|
batch: batchNo,
|
|
migration_time: new Date(),
|
|
});
|
|
}
|
|
if (direction === 'down') {
|
|
return trx.from(getTableName(tableName)).where({ name }).del();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Runs and builds the given migration class.
|
|
*/
|
|
private runMigrationContent(Migration, direction, trx) {
|
|
const instance = new Migration(trx);
|
|
|
|
if (this.context.i18n) {
|
|
instance.setI18n(this.context.i18n);
|
|
}
|
|
instance.setTenant(this.context.tenant);
|
|
|
|
return instance[direction](trx);
|
|
}
|
|
|
|
/**
|
|
* Validates some migrations by requiring and checking for an `up` and `down`function.
|
|
* @param {MigrateItem} migration
|
|
* @returns {MigrateItem}
|
|
*/
|
|
async validateMigrationStructure(migration: MigrateItem): MigrateItem {
|
|
const migrationName = this.migrationSource.getMigrationName(migration);
|
|
|
|
// maybe promise
|
|
const migrationContent = await this.migrationSource.getMigration(migration);
|
|
if (
|
|
typeof migrationContent.up !== 'function' ||
|
|
typeof migrationContent.down !== 'function'
|
|
) {
|
|
throw new Error(
|
|
`Invalid migration: ${migrationName} must have both an up and down function`,
|
|
);
|
|
}
|
|
return migration;
|
|
}
|
|
|
|
/**
|
|
* Run a batch of current migrations, in sequence.
|
|
* @param {MigrateItem[]} migrations
|
|
* @param {string} direction
|
|
* @param {Knex.Transaction} trx
|
|
* @returns {Promise<void>}
|
|
*/
|
|
private async runBatch(
|
|
migrations: MigrateItem[],
|
|
direction: string,
|
|
trx: Knex.Transaction,
|
|
): Promise<void> {
|
|
// Adds flag to migration lock.
|
|
await this.migrationLock(trx);
|
|
|
|
// When there is a wrapping transaction, some migrations
|
|
// could have been done while waiting for the lock:
|
|
const completed = await listCompleted(
|
|
this.config.tableName,
|
|
this.config.schemaName,
|
|
trx,
|
|
);
|
|
// Differentiate between all and completed to get new migrations.
|
|
const newMigrations = getNewMigrations(
|
|
this.config.migrationSource,
|
|
migrations,
|
|
completed,
|
|
);
|
|
// Retrieve the latest batch number.
|
|
const batchNo = await this.latestBatchNumber(trx);
|
|
|
|
// Increment the next batch number.
|
|
const newBatchNo = direction === 'up' ? batchNo + 1 : batchNo;
|
|
|
|
// Run all migration files in waterfall.
|
|
await this.waterfallBatch(newBatchNo, newMigrations, direction, trx);
|
|
|
|
// Free the migration lock flag.
|
|
await this.freeLock(trx);
|
|
}
|
|
}
|