From 83308b1716715a4f2140a79f40b94a501666ed47 Mon Sep 17 00:00:00 2001 From: Kononnable Date: Mon, 14 Aug 2017 16:38:18 +0200 Subject: [PATCH] added MySQL support --- .travis.yml | 10 +- package-lock.json | 70 ++++- package.json | 6 +- src/drivers/MysqlDriver.ts | 407 +++++++++++++++++++++++++++ src/index.ts | 15 +- test/integration/integration.test.ts | 71 ++++- 6 files changed, 552 insertions(+), 27 deletions(-) create mode 100644 src/drivers/MysqlDriver.ts diff --git a/.travis.yml b/.travis.yml index e9b23ac..a6c4f02 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,15 +13,19 @@ services: - docker env: - - MSSQL_Skip=0 MSSQL_Host=localhost MSSQL_Port=1433 MSSQL_Username=sa MSSQL_Password=!Passw0rd MSSQL_Database=tempdb - - POSTGRES_Skip=0 POSTGRES_Host=localhost POSTGRES_Port=5432 POSTGRES_Username=postgres POSTGRES_Password=!Passw0rd POSTGRES_Database=postgres - + - MSSQL_Skip=0 MSSQL_Host=localhost MSSQL_Port=1433 MSSQL_Username=sa MSSQL_Password=!Passw0rd MSSQL_Database=typeorm_mg + - POSTGRES_Skip=0 POSTGRES_Host=localhost POSTGRES_Port=5432 POSTGRES_Username=postgres POSTGRES_Password=!Passw0rd POSTGRES_Database=typeorm_mg + - MYSQL_Skip=0 MYSQL_Host=localhost MYSQL_Port=5432 MYSQL_Username=root MYSQL_Password=!Passw0rd MYSQL_Database=typeorm_mg + before_install: + - sudo service mysql stop - sudo service postgresql stop - docker pull microsoft/mssql-server-linux - docker pull postgres + - docker pull mysql - docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=!Passw0rd' -p 1433:1433 --name mssql -d microsoft/mssql-server-linux - docker run -e 'POSTGRES_PASSWORD=!Passw0rd' -p 5432:5432 --name postgres -d postgres + - docker run -e MYSQL_ROOT_PASSWORD=!Passw0rd -p 3306:3306 --name mysql -d mysql before_script: diff --git a/package-lock.json b/package-lock.json index 68dd65a..a166aec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "integrity": "sha512-o2qkg/J2LWK+sr007+KFBBOrxzxpr9kiP0gMFC75gQJXhUn/E3pQA0kSVdxrQ3lf+rOwsRnuH0wnR5MNTotEKg==", "dev": true, "requires": { - "@types/node": "7.0.31" + "@types/node": "7.0.39" } }, "@types/handlebars": { @@ -55,20 +55,28 @@ "integrity": "sha1-DAlm4jnCOP5Rzldt7unqGBFmU/Y=", "dev": true, "requires": { - "@types/node": "7.0.31" + "@types/node": "7.0.39" + } + }, + "@types/mysql": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-0.0.34.tgz", + "integrity": "sha512-IocdMpYtCBSLJqSg0BGgZTbYf3K9iAq81a1sNw6FlgVYh3/2cOXR3LpuQS6o5avWAmNTJqC6u5gS+IRrY56+9Q==", + "requires": { + "@types/node": "7.0.39" } }, "@types/node": { - "version": "7.0.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.31.tgz", - "integrity": "sha512-+KrE1LDddn97ip+gXZAnzNQ0pupKH/6tcKwTpo96BDVNpzmhIKGHug0Wd3H0dN4WEqYB1tXYI5m2mZuIZNI8tg==" + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.39.tgz", + "integrity": "sha512-KQHAZeVsk4UIT9XaR6cn4WpHZzimK6UBD1UomQKfQQFmTlUHaNBzeuov+TM4+kigLO0IJt4I5OOsshcCyA9gSA==" }, "@types/pg": { "version": "6.1.41", "resolved": "https://registry.npmjs.org/@types/pg/-/pg-6.1.41.tgz", "integrity": "sha512-iTzD3R2CY/aSybbZieXGXi3Wxrk7XV/jRU1lofH/hyQNCbTTskXB/fomFDv+XTVXKjqrpCB/AXVLnUwbdFqthQ==", "requires": { - "@types/node": "7.0.31", + "@types/node": "7.0.39", "@types/pg-types": "1.11.2" } }, @@ -303,6 +311,11 @@ "resolved": "https://registry.npmjs.org/big-number/-/big-number-0.3.1.tgz", "integrity": "sha1-rHMCDApZu3nrF8LOLbd/d9l04BM=" }, + "bignumber.js": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.0.2.tgz", + "integrity": "sha1-LR3DfuWWiGfs6pC22k0W5oYI0h0=" + }, "bl": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz", @@ -2125,6 +2138,46 @@ "duplexer2": "0.0.2" } }, + "mysql": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.14.1.tgz", + "integrity": "sha512-ZPXqQeYH7L1QPDyC77Rcp32cNCQnNjz8Y4BbF17tOjm5yhSfjFa3xS4PvuxWJtEEmwVc4ccI7sSntj4eyYRq0A==", + "requires": { + "bignumber.js": "4.0.2", + "readable-stream": "2.3.3", + "safe-buffer": "5.1.1", + "sqlstring": "2.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, "native-promise-only": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", @@ -2864,6 +2917,11 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "sqlstring": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.2.0.tgz", + "integrity": "sha1-wxNcTqirzX5+50GklmqJHYak8ZE=" + }, "sshpk": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", diff --git a/package.json b/package.json index aa61cf7..83b00c7 100644 --- a/package.json +++ b/package.json @@ -24,9 +24,11 @@ }, "homepage": "https://github.com/Kononnable/typeorm-model-generator#readme", "dependencies": { + "@types/mysql": "0.0.34", "@types/pg": "^6.1.41", "handlebars": "^4.0.10", "mssql": "^3.3.0", + "mysql": "^2.14.1", "pg": "^6.4.0", "reflect-metadata": "^0.1.10", "typeorm": "0.0.10", @@ -34,14 +36,14 @@ "yargs": "^7.0.2" }, "devDependencies": { - "@types/handlebars": "^4.0.32", "@types/chai": "^3.5.2", "@types/chai-as-promised": "0.0.30", "@types/chai-subset": "^1.3.0", "@types/fs-extra": "^3.0.0", + "@types/handlebars": "^4.0.32", "@types/mocha": "^2.2.41", "@types/mssql": "^3.3.0", - "@types/node": "^7.0.10", + "@types/node": "^7.0.39", "@types/sinon": "^2.1.3", "chai": "^3.5.0", "chai-as-promised": "^6.0.0", diff --git a/src/drivers/MysqlDriver.ts b/src/drivers/MysqlDriver.ts new file mode 100644 index 0000000..6d51a95 --- /dev/null +++ b/src/drivers/MysqlDriver.ts @@ -0,0 +1,407 @@ +import { AbstractDriver } from './AbstractDriver' +import * as MYSQL from 'mysql' +import { ColumnInfo } from './../models/ColumnInfo' +import { EntityInfo } from './../models/EntityInfo' +import { RelationInfo } from './../models/RelationInfo' +import { DatabaseModel } from './../models/DatabaseModel' +/** + * MysqlDriver + */ +export class MysqlDriver extends AbstractDriver { + FindPrimaryColumnsFromIndexes(dbModel: DatabaseModel) { + dbModel.entities.forEach(entity => { + let primaryIndex = entity.Indexes.find(v => v.isPrimaryKey); + if (!primaryIndex) { + console.error(`Table ${entity.EntityName} has no PK.`) + return; + } + entity.Columns.forEach(col => { + if (primaryIndex!.columns.some(cIndex => cIndex.name == col.name)) col.isPrimary = true + }) + }); + } + + async GetAllTables(): Promise { + + let response = await this.ExecQuery<{ TABLE_SCHEMA: string, TABLE_NAME: string }>(`SELECT TABLE_SCHEMA, TABLE_NAME + FROM information_schema.tables + WHERE table_type='BASE TABLE' + AND table_schema like DATABASE()`); + let ret: EntityInfo[] = []; + response.forEach((val) => { + let ent: EntityInfo = new EntityInfo(); + ent.EntityName = val.TABLE_NAME; + ent.Columns = []; + ent.Indexes = []; + ret.push(ent); + }) + return ret; + } + async GetCoulmnsFromEntity(entities: EntityInfo[]): Promise { + let response = await this.ExecQuery<{ + TABLE_NAME: string, COLUMN_NAME: string, COLUMN_DEFAULT: string, + IS_NULLABLE: string, DATA_TYPE: string, CHARACTER_MAXIMUM_LENGTH: number, + NUMERIC_PRECISION: number, NUMERIC_SCALE: number, IsIdentity: number + }>(`SELECT TABLE_NAME,COLUMN_NAME,COLUMN_DEFAULT,IS_NULLABLE, + DATA_TYPE,CHARACTER_MAXIMUM_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE, + CASE WHEN EXTRA like '%auto_increment%' THEN 1 ELSE 0 END IsIdentity FROM INFORMATION_SCHEMA.COLUMNS + where TABLE_SCHEMA like DATABASE()`); + entities.forEach((ent) => { + response.filter((filterVal) => { + return filterVal.TABLE_NAME == ent.EntityName; + }).forEach((resp) => { + let colInfo: ColumnInfo = new ColumnInfo(); + colInfo.name = resp.COLUMN_NAME; + colInfo.is_nullable = resp.IS_NULLABLE == 'YES' ? true : false; + colInfo.is_generated = resp.IsIdentity == 1 ? true : false; + colInfo.default = resp.COLUMN_DEFAULT; + switch (resp.DATA_TYPE) { + case "int": + colInfo.ts_type = "number" + colInfo.sql_type = "int" + break; + case "tinyint": + if (resp.NUMERIC_PRECISION == 3) { + colInfo.ts_type = "boolean" + colInfo.sql_type = "boolean" + } else { + colInfo.ts_type = "number" + colInfo.sql_type = "smallint" + } + break; + case "smallint": + colInfo.ts_type = "number" + colInfo.sql_type = "smallint" + break; + case "bit": + colInfo.ts_type = "boolean" + colInfo.sql_type = "boolean" + break; + case "float": + colInfo.ts_type = "number" + colInfo.sql_type = "float" + break; + case "bigint": + colInfo.ts_type = "number" + colInfo.sql_type = "bigint" + break; + case "date": + colInfo.ts_type = "Date" + colInfo.sql_type = "date" + break; + case "time": + colInfo.ts_type = "Date" + colInfo.sql_type = "time" + break; + case "datetime": + colInfo.ts_type = "Date"; + colInfo.sql_type = "datetime" + break; + case "char": + colInfo.ts_type = "string" + colInfo.sql_type = "text" + break; + case "nchar": + colInfo.ts_type = "string" + colInfo.sql_type = "text" + break; + case "text": + colInfo.ts_type = "string" + colInfo.sql_type = "text" + break; + case "ntext": + colInfo.ts_type = "string" + colInfo.sql_type = "text" + break; + case "varchar": + colInfo.ts_type = "string" + colInfo.sql_type = "string" + break; + case "nvarchar": + colInfo.ts_type = "string" + colInfo.sql_type = "string" + break; + case "money": + colInfo.ts_type = "number" + colInfo.sql_type = "decimal" + break; + case "real": + colInfo.ts_type = "number" + colInfo.sql_type = "double" + break; + case "double": + colInfo.ts_type = "number" + colInfo.sql_type = "double" + break; + case "decimal": + colInfo.ts_type = "number" + colInfo.sql_type = "decimal" + colInfo.numericPrecision = resp.NUMERIC_PRECISION + colInfo.numericScale = resp.NUMERIC_SCALE + break; + case "xml": + colInfo.ts_type = "string" + colInfo.sql_type = "text" + break; + default: + console.error("Unknown column type:" + resp.DATA_TYPE); + break; + } + colInfo.char_max_lenght = resp.CHARACTER_MAXIMUM_LENGTH > 0 ? resp.CHARACTER_MAXIMUM_LENGTH : null; + if (colInfo.sql_type) ent.Columns.push(colInfo); + }) + }) + return entities; + } + async GetIndexesFromEntity(entities: EntityInfo[]): Promise { + let response = await this.ExecQuery<{ + TableName: string, IndexName: string, ColumnName: string, is_unique: number, + is_primary_key: number//, is_descending_key: number//, is_included_column: number + }>(`SELECT TABLE_NAME TableName,INDEX_NAME IndexName,COLUMN_NAME ColumnName,CASE WHEN NON_UNIQUE=0 THEN 1 ELSE 0 END is_unique, + CASE WHEN INDEX_NAME='PRIMARY' THEN 1 ELSE 0 END is_primary_key + FROM information_schema.statistics sta + WHERE table_schema like DATABASE(); + `); + entities.forEach((ent) => { + response.filter((filterVal) => { + return filterVal.TableName == ent.EntityName; + }).forEach((resp) => { + let indexInfo: IndexInfo = {}; + let indexColumnInfo: IndexColumnInfo = {}; + if (ent.Indexes.filter((filterVal) => { + return filterVal.name == resp.IndexName + }).length > 0) { + indexInfo = ent.Indexes.filter((filterVal) => { + return filterVal.name == resp.IndexName + })[0]; + } else { + indexInfo.columns = []; + indexInfo.name = resp.IndexName; + indexInfo.isUnique = resp.is_unique == 1 ? true : false; + indexInfo.isPrimaryKey = resp.is_primary_key == 1 ? true : false; + ent.Indexes.push(indexInfo); + } + indexColumnInfo.name = resp.ColumnName; + // indexColumnInfo.isIncludedColumn = resp.is_included_column == 1 ? true : false; + // indexColumnInfo.isDescending = resp.is_descending_key == 1 ? true : false; + indexInfo.columns.push(indexColumnInfo); + + }) + }) + + return entities; + } + async GetRelations(entities: EntityInfo[]): Promise { + let response = await this.ExecQuery<{ + TableWithForeignKey: string, FK_PartNo: number, ForeignKeyColumn: string, + TableReferenced: string, ForeignKeyColumnReferenced: string, + onDelete: "RESTRICT" | "CASCADE" | "SET NULL", + onUpdate: "RESTRICT" | "CASCADE" | "SET NULL", object_id: string + }>(`SELECT + CU.TABLE_NAME TableWithForeignKey, + CU.ORDINAL_POSITION FK_PartNo, + CU.COLUMN_NAME ForeignKeyColumn, + CU.REFERENCED_TABLE_NAME TableReferenced, + CU.REFERENCED_COLUMN_NAME ForeignKeyColumnReferenced, + RC.DELETE_RULE onDelete, + RC.UPDATE_RULE onUpdate, + CU.CONSTRAINT_NAME object_id + FROM + INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU + JOIN + INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC ON CU.CONSTRAINT_NAME=RC.CONSTRAINT_NAME + WHERE + TABLE_SCHEMA = SCHEMA() + AND CU.REFERENCED_TABLE_NAME IS NOT NULL; + `); + let relationsTemp: RelationTempInfo[] = []; + response.forEach((resp) => { + let rels = relationsTemp.find((val) => { + return val.object_id == resp.object_id; + }) + if (rels == undefined) { + rels = {}; + rels.ownerColumnsNames = []; + rels.referencedColumnsNames = []; + rels.actionOnDelete = resp.onDelete; + rels.actionOnUpdate = resp.onUpdate; + rels.object_id = resp.object_id; + rels.ownerTable = resp.TableWithForeignKey; + rels.referencedTable = resp.TableReferenced; + relationsTemp.push(rels); + } + rels.ownerColumnsNames.push(resp.ForeignKeyColumn); + rels.referencedColumnsNames.push(resp.ForeignKeyColumnReferenced); + }) + relationsTemp.forEach((relationTmp) => { + let ownerEntity = entities.find((entitity) => { + return entitity.EntityName == relationTmp.ownerTable; + }) + if (!ownerEntity) { + console.error(`Relation between tables ${relationTmp.ownerTable} and ${relationTmp.referencedTable} didn't found entity model ${relationTmp.ownerTable}.`) + return; + } + let referencedEntity = entities.find((entitity) => { + return entitity.EntityName == relationTmp.referencedTable; + }) + if (!referencedEntity) { + console.error(`Relation between tables ${relationTmp.ownerTable} and ${relationTmp.referencedTable} didn't found entity model ${relationTmp.referencedTable}.`) + return; + } + let ownerColumn = ownerEntity.Columns.find((column) => { + return column.name == relationTmp.ownerColumnsNames[0]; + }) + if (!ownerColumn) { + console.error(`Relation between tables ${relationTmp.ownerTable} and ${relationTmp.referencedTable} didn't found entity column ${relationTmp.ownerTable}.${ownerColumn}.`) + return; + } + let relatedColumn = referencedEntity.Columns.find((column) => { + return column.name == relationTmp.referencedColumnsNames[0]; + }) + if (!relatedColumn) { + console.error(`Relation between tables ${relationTmp.ownerTable} and ${relationTmp.referencedTable} didn't found entity column ${relationTmp.referencedTable}.${relatedColumn}.`) + return; + } + let ownColumn: ColumnInfo = ownerColumn; + let isOneToMany: boolean; + isOneToMany = false; + let index = ownerEntity.Indexes.find( + (index) => { + return index.isUnique && index.columns.some(col => { + return col.name == ownerColumn!.name + }) + } + ) + if (!index) { + isOneToMany = true; + } else { + isOneToMany = false; + } + let ownerRelation = new RelationInfo() + ownerRelation.actionOnDelete = relationTmp.actionOnDelete + ownerRelation.actionOnUpdate = relationTmp.actionOnUpdate + ownerRelation.isOwner = true + ownerRelation.relatedColumn = relatedColumn.name.toLowerCase() + ownerRelation.relatedTable = relationTmp.referencedTable + ownerRelation.ownerTable = relationTmp.ownerTable + ownerRelation.ownerColumn = ownerEntity.EntityName.toLowerCase() + (isOneToMany ? 's' : '') + ownerRelation.relationType = isOneToMany ? "ManyToOne" : "OneToOne" + ownerColumn.relations.push(ownerRelation) + if (isOneToMany) { + let col = new ColumnInfo() + col.name = ownerEntity.EntityName.toLowerCase() + 's' + let referencedRelation = new RelationInfo(); + col.relations.push(referencedRelation) + referencedRelation.actionOnDelete = relationTmp.actionOnDelete + referencedRelation.actionOnUpdate = relationTmp.actionOnUpdate + referencedRelation.isOwner = false + referencedRelation.relatedColumn = ownerColumn.name + referencedRelation.relatedTable = relationTmp.ownerTable + referencedRelation.ownerTable = relationTmp.referencedTable + referencedRelation.ownerColumn = relatedColumn.name.toLowerCase() + referencedRelation.relationType = "OneToMany" + referencedEntity.Columns.push(col) + } else { + let col = new ColumnInfo() + col.name = ownerEntity.EntityName.toLowerCase() + let referencedRelation = new RelationInfo(); + col.relations.push(referencedRelation) + referencedRelation.actionOnDelete = relationTmp.actionOnDelete + referencedRelation.actionOnUpdate = relationTmp.actionOnUpdate + referencedRelation.isOwner = false + referencedRelation.relatedColumn = ownerColumn.name + referencedRelation.relatedTable = relationTmp.ownerTable + referencedRelation.ownerTable = relationTmp.referencedTable + referencedRelation.ownerColumn = relatedColumn.name.toLowerCase() + referencedRelation.relationType = "OneToOne" + + referencedEntity.Columns.push(col) + } + }) + return entities; + } + async DisconnectFromServer() { + let promise = new Promise( + (resolve, reject) => { + this.Connection.end((err) => { + if (!err) { + //Connection successfull + resolve(true) + } + else { + console.error('Error connecting to MYSQL Server.') + console.error(err.message) + process.abort() + reject(err) + } + }); + } + ) + + if (this.Connection) + await promise; + + } + + private Connection: MYSQL.IConnection; + async ConnectToServer(database: string, server: string, port: number, user: string, password: string) { + let config: MYSQL.IConnectionConfig = { + database: database, + host: server, + port: port, + user: user, + password: password, + } + + + let promise = new Promise( + (resolve, reject) => { + this.Connection = MYSQL.createConnection(config) + + this.Connection.connect((err) => { + if (!err) { + //Connection successfull + resolve(true) + } + else { + console.error('Error connecting to MYSQL Server.') + console.error(err.message) + process.abort() + reject(err) + } + }); + } + ) + + await promise; + } + async CreateDB(dbName: string) { + let resp = await this.ExecQuery(`CREATE DATABASE ${dbName}; `) + } + async UseDB(dbName: string) { + let resp = await this.ExecQuery(`USE ${dbName}; `) + } + async DropDB(dbName: string) { + let resp = await this.ExecQuery(`DROP DATABASE ${dbName}; `) + } + async CheckIfDBExists(dbName: string): Promise { + let resp = await this.ExecQuery(`SHOW DATABASES LIKE '${dbName}' `) + return resp.length > 0; + } + async ExecQuery(sql: string): Promise> { + let ret: Array = []; + let that = this; + let query = this.Connection.query(sql) + let stream = query.stream({}); + let promise = new Promise( + (resolve, reject) => { + stream.on('data', + chunk => { + ret.push(chunk) + }); + stream.on('end', () => resolve(true)); + }) + await promise; + return ret; + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index b57cbf3..bc7aecc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,10 @@ +import { AbstractDriver } from "./drivers/AbstractDriver"; import { MssqlDriver } from './drivers/MssqlDriver'; +import { PostgresDriver } from "./drivers/PostgresDriver"; +import { MysqlDriver } from "./drivers/MysqlDriver"; import { Engine } from './Engine' import * as Yargs from 'yargs' -import { AbstractDriver } from "./drivers/AbstractDriver"; import path = require('path') -import { PostgresDriver } from "./drivers/PostgresDriver"; @@ -36,7 +37,7 @@ var argv = Yargs .option('e', { alias: 'engine', describe: 'Database engine.', - choices: ['mssql','postgres'], + choices: ['mssql', 'postgres', 'mysql'], default: 'mssql' }) .option('o', { @@ -53,11 +54,15 @@ switch (argv.e) { case 'mssql': driver = new MssqlDriver(); standardPort = 1433; - break; - case 'postgres': + break; + case 'postgres': driver = new PostgresDriver(); standardPort = 5432; break; + case 'mysql': + driver = new MysqlDriver(); + standardPort = 3306; + break; default: console.error('Database engine not recognized.') process.abort(); diff --git a/test/integration/integration.test.ts b/test/integration/integration.test.ts index 372d1c1..04f6462 100644 --- a/test/integration/integration.test.ts +++ b/test/integration/integration.test.ts @@ -14,6 +14,7 @@ var chai = require('chai'); var chaiSubset = require('chai-subset'); import * as ts from "typescript"; import { PostgresDriver } from "../../src/drivers/PostgresDriver"; +import { MysqlDriver } from "../../src/drivers/MysqlDriver"; chai.use(chaiSubset); @@ -29,6 +30,7 @@ describe("integration tests", async function () { let dbDrivers: DriverType[] = [] if (process.env.MSSQL_Skip == '0') dbDrivers.push('mssql') if (process.env.POSTGRES_Skip == '0') dbDrivers.push('postgres') + if (process.env.MYSQL_Skip == '0') dbDrivers.push('mysql') for (let folder of files) { @@ -49,9 +51,12 @@ describe("integration tests", async function () { case 'postgres': engine = await createPostgresModels(filesOrgPathJS, resultsPath) break; + case 'mysql': + engine = await createMysqlModels(filesOrgPathJS, resultsPath) + break; default: console.log(`Unknown engine type`); - engine={} + engine = {} break; } @@ -93,13 +98,13 @@ describe("integration tests", async function () { }) async function createMSSQLModels(filesOrgPath: string, resultsPath: string): Promise { - + let driver: AbstractDriver; driver = new MssqlDriver(); - await driver.ConnectToServer(`master`,process.env.MSSQL_Host,process.env.MSSQL_Port,process.env.MSSQL_Username, process.env.MSSQL_Password); - + await driver.ConnectToServer(`master`, process.env.MSSQL_Host, process.env.MSSQL_Port, process.env.MSSQL_Username, process.env.MSSQL_Password); + if (! await driver.CheckIfDBExists(process.env.MSSQL_Database)) - await driver.CreateDB(process.env.MSSQL_Database); + await driver.CreateDB(process.env.MSSQL_Database); await driver.DisconnectFromServer(); let connOpt: ConnectionOptions = { @@ -120,7 +125,7 @@ async function createMSSQLModels(filesOrgPath: string, resultsPath: string): Pro if (conn.isConnected) await conn.close() - + driver = new MssqlDriver(); let engine = new Engine( driver, { @@ -133,17 +138,17 @@ async function createMSSQLModels(filesOrgPath: string, resultsPath: string): Pro resultsPath: resultsPath }); - + return engine; } async function createPostgresModels(filesOrgPath: string, resultsPath: string): Promise { let driver: AbstractDriver; driver = new PostgresDriver(); - await driver.ConnectToServer(`postgres`,process.env.POSTGRES_Host,process.env.POSTGRES_Port,process.env.POSTGRES_Username, process.env.POSTGRES_Password); - + await driver.ConnectToServer(`postgres`, process.env.POSTGRES_Host, process.env.POSTGRES_Port, process.env.POSTGRES_Username, process.env.POSTGRES_Password); + if (! await driver.CheckIfDBExists(process.env.POSTGRES_Database)) - await driver.CreateDB(process.env.POSTGRES_Database); + await driver.CreateDB(process.env.POSTGRES_Database); await driver.DisconnectFromServer(); let connOpt: ConnectionOptions = { @@ -176,7 +181,51 @@ async function createPostgresModels(filesOrgPath: string, resultsPath: string): resultsPath: resultsPath }); - + + + return engine; +} + +async function createMysqlModels(filesOrgPath: string, resultsPath: string): Promise { + let driver: AbstractDriver; + driver = new MysqlDriver(); + await driver.ConnectToServer(`mysql`, process.env.MYSQL_Host, process.env.MYSQL_Port, process.env.MYSQL_Username, process.env.MYSQL_Password); + + if (! await driver.CheckIfDBExists(process.env.MYSQL_Database)) + await driver.CreateDB(process.env.MYSQL_Database); + await driver.DisconnectFromServer(); + + let connOpt: ConnectionOptions = { + driver: { + database: process.env.MYSQL_Database, + host: process.env.MYSQL_Host, + password: process.env.MYSQL_Password, + type: 'mysql', + username: process.env.MYSQL_Username, + port: process.env.MYSQL_Port + }, + dropSchemaOnConnection: true, + autoSchemaSync: true, + entities: [path.resolve(filesOrgPath, '*.js')], + } + let conn = await createConnection(connOpt) + + if (conn.isConnected) + await conn.close() + + driver = new MysqlDriver(); + let engine = new Engine( + driver, { + host: process.env.MYSQL_Host, + port: process.env.MYSQL_Port, + databaseName: process.env.MYSQL_Database, + user: process.env.MYSQL_Username, + password: process.env.MYSQL_Password, + databaseType: 'mysql', + resultsPath: resultsPath + }); + + return engine; }