migrating to typescript
This commit is contained in:
parent
72fab99880
commit
7dc8676b70
|
@ -26,7 +26,7 @@ import DBVendors from './DBVendors';
|
|||
/**
|
||||
* Checks correctness of connection details of both MySQL and PostgreSQL.
|
||||
*/
|
||||
async function checkConnection(conversion: Conversion, dbAccess: DBAccess): Promise<string> {
|
||||
export async function checkConnection(conversion: Conversion, dbAccess: DBAccess): Promise<string> {
|
||||
const logTitle: string = 'BootProcessor::checkConnection';
|
||||
let resultMessage: string = '';
|
||||
const sql: string = 'SELECT 1;';
|
||||
|
@ -42,7 +42,7 @@ async function checkConnection(conversion: Conversion, dbAccess: DBAccess): Prom
|
|||
/**
|
||||
* Returns Nmig's logo.
|
||||
*/
|
||||
function getLogo(): string {
|
||||
export function getLogo(): string {
|
||||
return '\n\t/\\_ |\\ /\\/\\ /\\___'
|
||||
+ '\n\t| \\ | |\\ | | | __'
|
||||
+ '\n\t| |\\\\| || | | | \\_ \\'
|
||||
|
@ -56,7 +56,7 @@ function getLogo(): string {
|
|||
/**
|
||||
* Boots the migration.
|
||||
*/
|
||||
export default(conversion: Conversion): Promise<Conversion> => {
|
||||
export function boot(conversion: Conversion): Promise<Conversion> {
|
||||
return new Promise<Conversion>(async resolve => {
|
||||
const dbAccess: DBAccess = new DBAccess(conversion);
|
||||
const connectionErrorMessage = await checkConnection(conversion, dbAccess);
|
||||
|
|
|
@ -225,7 +225,7 @@ export default class Conversion {
|
|||
? this._mySqlDbName
|
||||
: this._config.schema;
|
||||
|
||||
this._maxDbConnectionPoolSize = this._config.max_db_connection_pool_size !== undefined && this.isIntNumeric(this._config.max_db_connection_pool_size)
|
||||
this._maxDbConnectionPoolSize = this._config.max_db_connection_pool_size !== undefined && Conversion._isIntNumeric(this._config.max_db_connection_pool_size)
|
||||
? +this._config.max_db_connection_pool_size
|
||||
: 10;
|
||||
|
||||
|
@ -235,7 +235,7 @@ export default class Conversion {
|
|||
this._removeTestResources = this._config.remove_test_resources === undefined ? true : this._config.remove_test_resources;
|
||||
this._maxDbConnectionPoolSize = this._maxDbConnectionPoolSize > 0 ? this._maxDbConnectionPoolSize : 10;
|
||||
this._loaderMaxOldSpaceSize = this._config.loader_max_old_space_size;
|
||||
this._loaderMaxOldSpaceSize = this.isIntNumeric(this._loaderMaxOldSpaceSize) ? this._loaderMaxOldSpaceSize : 'DEFAULT';
|
||||
this._loaderMaxOldSpaceSize = Conversion._isIntNumeric(this._loaderMaxOldSpaceSize) ? this._loaderMaxOldSpaceSize : 'DEFAULT';
|
||||
this._migrateOnlyData = this._config.migrate_only_data === undefined ? false : this._config.migrate_only_data;
|
||||
this._delimiter = this._config.delimiter !== undefined && this._config.delimiter.length === 1
|
||||
? this._config.delimiter
|
||||
|
@ -245,7 +245,14 @@ export default class Conversion {
|
|||
/**
|
||||
* Checks if given value is integer number.
|
||||
*/
|
||||
private isIntNumeric(value: any): boolean {
|
||||
private static _isIntNumeric(value: any): boolean {
|
||||
return !isNaN(parseInt(value)) && isFinite(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes Conversion instance.
|
||||
*/
|
||||
public static initializeConversion(config: any): Promise<Conversion> {
|
||||
return Promise.resolve(new Conversion(config));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* This file is a part of "NMIG" - the database migration tool.
|
||||
*
|
||||
* Copyright (C) 2016 - present, Anatoly Khaytovich <anatolyuss@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (please see the "LICENSE.md" file).
|
||||
* If not, see <http://www.gnu.org/licenses/gpl.txt>.
|
||||
*
|
||||
* @author Anatoly Khaytovich <anatolyuss@gmail.com>
|
||||
*/
|
||||
import * as fs from 'fs';
|
||||
import Conversion from './Conversion';
|
||||
|
||||
/**
|
||||
* Reads "./config/data_types_map.json" and converts its json content to js object.
|
||||
*/
|
||||
export default (conversion: Conversion): Promise<Conversion> => {
|
||||
return new Promise<Conversion>(resolve => {
|
||||
fs.readFile(conversion._dataTypesMapAddr, (error: Error, data: Buffer) => {
|
||||
if (error) {
|
||||
console.log(`\t--[readDataTypesMap] Cannot read "DataTypesMap" from ${conversion._dataTypesMapAddr}`);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
conversion._dataTypesMap = JSON.parse(data.toString());
|
||||
console.log('\t--[readDataTypesMap] Data Types Map is loaded...');
|
||||
resolve(conversion);
|
||||
});
|
||||
});
|
||||
}
|
119
src/FsOps.ts
Normal file
119
src/FsOps.ts
Normal file
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* This file is a part of "NMIG" - the database migration tool.
|
||||
*
|
||||
* Copyright (C) 2016 - present, Anatoly Khaytovich <anatolyuss@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (please see the "LICENSE.md" file).
|
||||
* If not, see <http://www.gnu.org/licenses/gpl.txt>.
|
||||
*
|
||||
* @author Anatoly Khaytovich <anatolyuss@gmail.com>
|
||||
*/
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import Conversion from './Conversion';
|
||||
import log from './Logger';
|
||||
|
||||
/**
|
||||
* Reads the configuration file.
|
||||
*/
|
||||
export function readConfig(baseDir: string, configFileName: string = 'config.json'): Promise<any> {
|
||||
return new Promise<any>(resolve => {
|
||||
const strPathToConfig = path.join(baseDir, 'config', configFileName);
|
||||
|
||||
fs.readFile(strPathToConfig, (error: Error, data: Buffer) => {
|
||||
if (error) {
|
||||
console.log(`\n\t--Cannot run migration\nCannot read configuration info from ${ strPathToConfig }`);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
const config: any = JSON.parse(data.toString());
|
||||
config.logsDirPath = path.join(baseDir, 'logs_directory');
|
||||
config.dataTypesMapAddr = path.join(baseDir, 'config', 'data_types_map.json');
|
||||
resolve(config);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the extra configuration file, if necessary.
|
||||
*/
|
||||
export function readExtraConfig(config: any, baseDir: string): Promise<any> {
|
||||
return new Promise<any>(resolve => {
|
||||
if (config.enable_extra_config !== true) {
|
||||
config.extraConfig = null;
|
||||
return resolve(config);
|
||||
}
|
||||
|
||||
const strPathToExtraConfig = path.join(baseDir, 'config', 'extra_config.json');
|
||||
|
||||
fs.readFile(strPathToExtraConfig, (error: Error, data: Buffer) => {
|
||||
if (error) {
|
||||
console.log(`\n\t--Cannot run migration\nCannot read configuration info from ${ strPathToExtraConfig }`);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
config.extraConfig = JSON.parse(data.toString());
|
||||
resolve(config);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates logs directory.
|
||||
*/
|
||||
export function createLogsDirectory(conversion: Conversion): Promise<Conversion> {
|
||||
return new Promise<Conversion>(resolve => {
|
||||
const logTitle: string = 'FsOps::createLogsDirectory';
|
||||
console.log(`\t--[${ logTitle }] Creating logs directory...`);
|
||||
|
||||
fs.stat(conversion._logsDirPath, (directoryDoesNotExist: Error, stat: fs.Stats) => {
|
||||
if (directoryDoesNotExist) {
|
||||
fs.mkdir(conversion._logsDirPath, conversion._0777, e => {
|
||||
if (e) {
|
||||
console.log(`\t--[${ logTitle }] Cannot perform a migration due to impossibility to create "logs_directory": ${ conversion._logsDirPath }`);
|
||||
process.exit();
|
||||
} else {
|
||||
log(conversion, '\t--[logTitle] Logs directory is created...');
|
||||
resolve(conversion);
|
||||
}
|
||||
});
|
||||
} else if (!stat.isDirectory()) {
|
||||
console.log(`\t--[${ logTitle }] Cannot perform a migration due to unexpected error`);
|
||||
process.exit();
|
||||
} else {
|
||||
log(conversion, `\t--[${ logTitle }] Logs directory already exists...`);
|
||||
resolve(conversion);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads "./config/data_types_map.json" and converts its json content to js object.
|
||||
*/
|
||||
export function readDataTypesMap(conversion: Conversion): Promise<Conversion> {
|
||||
return new Promise<Conversion>(resolve => {
|
||||
fs.readFile(conversion._dataTypesMapAddr, (error: Error, data: Buffer) => {
|
||||
const logTitle: string = 'FsOps::readDataTypesMap';
|
||||
|
||||
if (error) {
|
||||
console.log(`\t--[${ logTitle }] Cannot read "DataTypesMap" from ${conversion._dataTypesMapAddr}`);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
conversion._dataTypesMap = JSON.parse(data.toString());
|
||||
console.log(`\t--[${ logTitle }] Data Types Map is loaded...`);
|
||||
resolve(conversion);
|
||||
});
|
||||
});
|
||||
}
|
104
src/Main.ts
104
src/Main.ts
|
@ -18,113 +18,25 @@
|
|||
*
|
||||
* @author Anatoly Khaytovich <anatolyuss@gmail.com>
|
||||
*/
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import readDataTypesMap from './DataTypesMapReader';
|
||||
import Conversion from './Conversion';
|
||||
import SchemaProcessor from './SchemaProcessor';
|
||||
import createSchema from './SchemaProcessor';
|
||||
import loadStructureToMigrate from './StructureLoader';
|
||||
import pipeData from './DataPipeManager';
|
||||
import boot from './BootProcessor';
|
||||
import { boot } from './BootProcessor';
|
||||
import { createStateLogsTable } from './MigrationStateManager';
|
||||
import { createDataPoolTable, readDataPool } from './DataPoolManager';
|
||||
import log from './Logger';
|
||||
import { readConfig, readExtraConfig, createLogsDirectory, readDataTypesMap } from './FsOps';
|
||||
|
||||
export class Main {
|
||||
/**
|
||||
* Read the configuration file.
|
||||
*/
|
||||
public readConfig(baseDir: string, configFileName: string = 'config.json'): Promise<any> {
|
||||
return new Promise(resolve => {
|
||||
const strPathToConfig = path.join(baseDir, 'config', configFileName);
|
||||
|
||||
fs.readFile(strPathToConfig, (error: Error, data: Buffer) => {
|
||||
if (error) {
|
||||
console.log(`\n\t--Cannot run migration\nCannot read configuration info from ${ strPathToConfig }`);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
const config: any = JSON.parse(data.toString());
|
||||
config.logsDirPath = path.join(baseDir, 'logs_directory');
|
||||
config.dataTypesMapAddr = path.join(baseDir, 'config', 'data_types_map.json');
|
||||
resolve(config);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the extra configuration file, if necessary.
|
||||
*/
|
||||
public readExtraConfig(config: any, baseDir: string): Promise<any> {
|
||||
return new Promise(resolve => {
|
||||
if (config.enable_extra_config !== true) {
|
||||
config.extraConfig = null;
|
||||
return resolve(config);
|
||||
}
|
||||
|
||||
const strPathToExtraConfig = path.join(baseDir, 'config', 'extra_config.json');
|
||||
|
||||
fs.readFile(strPathToExtraConfig, (error: Error, data: Buffer) => {
|
||||
if (error) {
|
||||
console.log(`\n\t--Cannot run migration\nCannot read configuration info from ${ strPathToExtraConfig }`);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
config.extraConfig = JSON.parse(data.toString());
|
||||
resolve(config);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Conversion instance.
|
||||
*/
|
||||
public initializeConversion(config: any): Promise<Conversion> {
|
||||
return Promise.resolve(new Conversion(config));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates logs directory.
|
||||
*/
|
||||
public createLogsDirectory(self: Conversion): Promise<Conversion> {
|
||||
return new Promise(resolve => {
|
||||
console.log('\t--[DirectoriesManager.createLogsDirectory] Creating logs directory...');
|
||||
fs.stat(self._logsDirPath, (directoryDoesNotExist: Error, stat: fs.Stats) => {
|
||||
if (directoryDoesNotExist) {
|
||||
fs.mkdir(self._logsDirPath, self._0777, e => {
|
||||
if (e) {
|
||||
const msg = `\t--[DirectoriesManager.createLogsDirectory] Cannot perform a migration due to impossibility to create
|
||||
"logs_directory": ${ self._logsDirPath }`;
|
||||
|
||||
console.log(msg);
|
||||
process.exit();
|
||||
} else {
|
||||
log(self, '\t--[DirectoriesManager.createLogsDirectory] Logs directory is created...');
|
||||
resolve(self);
|
||||
}
|
||||
});
|
||||
} else if (!stat.isDirectory()) {
|
||||
console.log('\t--[DirectoriesManager.createLogsDirectory] Cannot perform a migration due to unexpected error');
|
||||
process.exit();
|
||||
} else {
|
||||
log(self, '\t--[DirectoriesManager.createLogsDirectory] Logs directory already exists...');
|
||||
resolve(self);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const app: Main = new Main();
|
||||
const baseDir: string = path.join(__dirname, '..', '..');
|
||||
|
||||
app.readConfig(baseDir)
|
||||
.then(config => app.readExtraConfig(config, baseDir))
|
||||
.then(app.initializeConversion)
|
||||
readConfig(baseDir)
|
||||
.then(config => readExtraConfig(config, baseDir))
|
||||
.then(Conversion.initializeConversion)
|
||||
.then(boot)
|
||||
.then(readDataTypesMap)
|
||||
.then(app.createLogsDirectory)
|
||||
.then(conversion => (new SchemaProcessor(conversion)).createSchema())
|
||||
.then(createLogsDirectory)
|
||||
.then(createSchema)
|
||||
.then(createStateLogsTable)
|
||||
.then(createDataPoolTable)
|
||||
.then(loadStructureToMigrate)
|
||||
|
|
|
@ -23,38 +23,19 @@ import DBAccess from './DBAccess';
|
|||
import DBAccessQueryResult from './DBAccessQueryResult';
|
||||
import DBVendors from './DBVendors';
|
||||
|
||||
export default class SchemaProcessor {
|
||||
/**
|
||||
* An instance of "Conversion".
|
||||
*/
|
||||
private readonly _conversion: Conversion;
|
||||
/**
|
||||
* Creates a new PostgreSQL schema if it does not exist yet.
|
||||
*/
|
||||
export default async function(conversion: Conversion): Promise<Conversion> {
|
||||
const logTitle: string = 'SchemaProcessor::createSchema';
|
||||
let sql: string = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = '${ conversion._schema }';`;
|
||||
const dbAccess: DBAccess = new DBAccess(conversion);
|
||||
const result: DBAccessQueryResult = await dbAccess.query(logTitle, sql, DBVendors.PG, true, true);
|
||||
|
||||
/**
|
||||
* An instance of "DBAccess".
|
||||
*/
|
||||
private readonly _dbAccess: DBAccess;
|
||||
|
||||
/**
|
||||
* SchemaProcessor constructor.
|
||||
*/
|
||||
public constructor(conversion: Conversion) {
|
||||
this._conversion = conversion;
|
||||
this._dbAccess = new DBAccess(this._conversion);
|
||||
if (result.data.rows.length === 0) {
|
||||
sql = `CREATE SCHEMA "${ conversion._schema }";`;
|
||||
await dbAccess.query(logTitle, sql, DBVendors.PG, true, false, result.client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new database schema if it does not exist yet.
|
||||
*/
|
||||
public async createSchema(): Promise<Conversion> {
|
||||
let sql: string = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = '${ this._conversion._schema }';`;
|
||||
|
||||
const result: DBAccessQueryResult = await this._dbAccess.query('SchemaProcessor::createSchema', sql, DBVendors.PG, true, true);
|
||||
|
||||
if (result.data.rows.length === 0) {
|
||||
sql = `CREATE SCHEMA "${ this._conversion._schema }";`;
|
||||
await this._dbAccess.query('SchemaProcessor::createSchema', sql, DBVendors.PG, true, false, result.client);
|
||||
}
|
||||
|
||||
return this._conversion;
|
||||
}
|
||||
return conversion;
|
||||
}
|
||||
|
|
|
@ -25,22 +25,17 @@ import Conversion from '../../src/Conversion';
|
|||
import DBAccess from '../../src/DBAccess';
|
||||
import DBVendors from '../../src/DBVendors';
|
||||
import DBAccessQueryResult from '../../src/DBAccessQueryResult';
|
||||
import { Main } from '../../src/Main';
|
||||
import SchemaProcessor from '../../src/SchemaProcessor';
|
||||
import readDataTypesMap from '../../src/DataTypesMapReader';
|
||||
import createSchema from '../../src/SchemaProcessor';
|
||||
import loadStructureToMigrate from '../../src/StructureLoader';
|
||||
import pipeData from '../../src/DataPipeManager';
|
||||
import { createStateLogsTable } from '../../src/MigrationStateManager';
|
||||
import { createDataPoolTable, readDataPool } from '../../src/DataPoolManager';
|
||||
import generateError from '../../src/ErrorGenerator';
|
||||
import log from '../../src/Logger';
|
||||
import { readConfig, readExtraConfig, createLogsDirectory, readDataTypesMap } from '../../src/FsOps';
|
||||
import { checkConnection, getLogo } from '../../src/BootProcessor';
|
||||
|
||||
export default class TestSchemaProcessor {
|
||||
/**
|
||||
* Instance of class Main.
|
||||
*/
|
||||
private readonly _app: Main;
|
||||
|
||||
/**
|
||||
* Instance of class Conversion.
|
||||
*/
|
||||
|
@ -55,7 +50,6 @@ export default class TestSchemaProcessor {
|
|||
* TestSchemaProcessor constructor.
|
||||
*/
|
||||
public constructor() {
|
||||
this._app = new Main();
|
||||
this.conversion = undefined;
|
||||
this.dbAccess = undefined;
|
||||
}
|
||||
|
@ -265,12 +259,21 @@ export default class TestSchemaProcessor {
|
|||
*/
|
||||
public async initializeConversion(): Promise<Conversion> {
|
||||
const baseDir: string = path.join(__dirname, '..', '..', '..');
|
||||
const config: any = await this._app.readConfig(baseDir, 'test_config.json');
|
||||
const fullConfig: any = await this._app.readExtraConfig(config, baseDir);
|
||||
this.conversion = await this._app.initializeConversion(fullConfig);
|
||||
const config: any = await readConfig(baseDir, 'test_config.json');
|
||||
const fullConfig: any = await readExtraConfig(config, baseDir);
|
||||
this.conversion = await Conversion.initializeConversion(fullConfig);
|
||||
this.conversion._runsInTestMode = true;
|
||||
this.conversion._eventEmitter = new EventEmitter();
|
||||
this.dbAccess = new DBAccess(this.conversion);
|
||||
const connectionErrorMessage = await checkConnection(this.conversion, this.dbAccess);
|
||||
const logo: string = getLogo();
|
||||
console.log(logo);
|
||||
|
||||
if (connectionErrorMessage) {
|
||||
console.log(connectionErrorMessage);
|
||||
// process.exit();
|
||||
}
|
||||
|
||||
delete this.conversion._sourceConString.database;
|
||||
return this.conversion;
|
||||
}
|
||||
|
@ -287,8 +290,8 @@ export default class TestSchemaProcessor {
|
|||
.then(this._loadTestSchema.bind(this))
|
||||
.then(this._loadTestData.bind(this))
|
||||
.then(readDataTypesMap)
|
||||
.then(this._app.createLogsDirectory)
|
||||
.then(conversion => (new SchemaProcessor(conversion)).createSchema())
|
||||
.then(createLogsDirectory)
|
||||
.then(createSchema)
|
||||
.then(createStateLogsTable)
|
||||
.then(createDataPoolTable)
|
||||
.then(loadStructureToMigrate)
|
||||
|
|
Loading…
Reference in a new issue