switch to eslint-typescript from deprecated tslint

(part 1)
This commit is contained in:
Kononnable 2019-08-11 20:59:41 +02:00
parent acfb717d89
commit 0d0ceeb1da
30 changed files with 2134 additions and 1018 deletions

View File

@ -1,8 +1,15 @@
root = true
[*]
[*.ts]
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
trim_trailing_whitespace = true
[*.{json,yml}]
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

43
.eslintrc.js Normal file
View File

@ -0,0 +1,43 @@
module.exports = {
env: {
node: true
},
extends: [
"airbnb-base",
"plugin:@typescript-eslint/recommended",
"prettier",
"prettier/@typescript-eslint"
],
parser: "@typescript-eslint/parser",
parserOptions: {
project: "./tsconfig.json"
},
plugins: ["@typescript-eslint"],
rules: {
"@typescript-eslint/explicit-function-return-type": ["off"],
"@typescript-eslint/no-use-before-define": ["error", "nofunc"],
"@typescript-eslint/prefer-interface": ["off"],
"import/no-extraneous-dependencies": [
"error",
{ devDependencies: ["**/*.test.ts"] }
],
"@typescript-eslint/no-floating-promises": ["error"],
"no-use-before-define": ["error", "nofunc"],
"no-console": ["off"],
"no-plusplus": ["error", { allowForLoopAfterthoughts: true }],
"@typescript-eslint/no-non-null-assertion": ["off"],
"@typescript-eslint/no-object-literal-type-assertion": ["off"],
"no-param-reassign": ["off"],
"@typescript-eslint/no-explicit-any": ["off"],
"no-loop-func": ["off"]
},
settings: {
"import/resolver": {
node: {
extensions: [".js", ".jsx", ".ts", ".tsx"]
}
}
}
};

3
.gitignore vendored
View File

@ -4,13 +4,10 @@ ormconfig.json
.vscode
.idea
typings/
**/*.js
**/*.js.map
output/**/*.*
.nyc_output/
coverage/
.env
dist
*.tgz
!gulpfile.js
.tomg-config

View File

@ -23,4 +23,4 @@ codecov.yml
tsconfig.json
typings.json
dist/test/
src/tslint.json
.eslintrc.js

842
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@
"start": "ts-node ./src/index.ts",
"pretest": "tsc --noEmit",
"test": "nyc --reporter=lcov ts-node ./node_modules/mocha/bin/_mocha test/**/*.test.ts -- -R spec --bail",
"posttest": "eslint ./src/*.ts ./src/**/*.ts",
"clean": "rimraf coverage output",
"prettier": "prettier --write ./src/*.ts ./src/**/*.ts"
},
@ -50,12 +51,20 @@
"@types/oracledb": "^3.1.3",
"@types/pg": "^7.4.14",
"@types/sinon": "^7.0.13",
"@types/sqlite3": "^3.1.5",
"@types/yargs": "^12.0.1",
"@typescript-eslint/eslint-plugin": "^1.13.0",
"@typescript-eslint/parser": "^1.13.0",
"@typescript-eslint/typescript-estree": "^1.13.0",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"chai-subset": "^1.6.0",
"codecov": "^3.5.0",
"dotenv": "^8.0.0",
"eslint": "^6.1.0",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-config-prettier": "^6.0.0",
"eslint-plugin-import": "^2.18.2",
"husky": "^3.0.2",
"lint-staged": "^9.2.1",
"mocha": "^6.2.0",
@ -63,9 +72,7 @@
"prettier": "^1.18.2",
"rimraf": "^2.6.3",
"sinon": "^7.3.2",
"sinon-chai": "^3.3.0",
"tslint": "^5.18.0",
"tslint-config-prettier": "^1.18.0"
"sinon-chai": "^3.3.0"
},
"husky": {
"hooks": {

View File

@ -1,7 +1,7 @@
import { EntityInfo } from "./models/EntityInfo";
import { RelationInfo } from "./models/RelationInfo";
import RelationInfo from "./models/RelationInfo";
import EntityInfo from "./models/EntityInfo";
export abstract class AbstractNamingStrategy {
export default abstract class AbstractNamingStrategy {
public abstract relationName(
columnName: string,
relation: RelationInfo,

View File

@ -1,20 +1,21 @@
import * as Handlebars from "handlebars";
import { DataTypeDefaults } from "typeorm/driver/types/DataTypeDefaults";
import * as TomgUtils from "./Utils";
import AbstractDriver from "./drivers/AbstractDriver";
import MssqlDriver from "./drivers/MssqlDriver";
import MariaDbDriver from "./drivers/MariaDbDriver";
import IConnectionOptions from "./IConnectionOptions";
import IGenerationOptions from "./IGenerationOptions";
import EntityInfo from "./models/EntityInfo";
import PostgresDriver from "./drivers/PostgresDriver";
import MysqlDriver from "./drivers/MysqlDriver";
import OracleDriver from "./drivers/OracleDriver";
import SqliteDriver from "./drivers/SqliteDriver";
import NamingStrategy from "./NamingStrategy";
import changeCase = require("change-case");
import fs = require("fs");
import * as Handlebars from "handlebars";
import path = require("path");
import { DataTypeDefaults } from "typeorm/driver/types/DataTypeDefaults";
import { AbstractDriver } from "./drivers/AbstractDriver";
import { MariaDbDriver } from "./drivers/MariaDbDriver";
import { MssqlDriver } from "./drivers/MssqlDriver";
import { MysqlDriver } from "./drivers/MysqlDriver";
import { OracleDriver } from "./drivers/OracleDriver";
import { PostgresDriver } from "./drivers/PostgresDriver";
import { SqliteDriver } from "./drivers/SqliteDriver";
import { IConnectionOptions } from "./IConnectionOptions";
import { IGenerationOptions } from "./IGenerationOptions";
import { EntityInfo } from "./models/EntityInfo";
import { NamingStrategy } from "./NamingStrategy";
import * as TomgUtils from "./Utils";
export function createDriver(driverName: string): AbstractDriver {
switch (driverName) {
@ -60,7 +61,7 @@ export async function dataCollectionPhase(
driver: AbstractDriver,
connectionOptions: IConnectionOptions
) {
return await driver.GetDataFromServer(connectionOptions);
return driver.GetDataFromServer(connectionOptions);
}
export function modelCustomizationPhase(
@ -73,17 +74,17 @@ export function modelCustomizationPhase(
generationOptions.customNamingStrategyPath &&
generationOptions.customNamingStrategyPath !== ""
) {
// tslint:disable-next-line:no-var-requires
// eslint-disable-next-line global-require, import/no-dynamic-require, @typescript-eslint/no-var-requires
const req = require(generationOptions.customNamingStrategyPath);
namingStrategy = new req.NamingStrategy();
} else {
namingStrategy = new NamingStrategy();
}
dbModel = setRelationId(generationOptions, dbModel);
dbModel = applyNamingStrategy(namingStrategy, dbModel);
dbModel = addImportsAndGenerationOptions(dbModel, generationOptions);
dbModel = removeColumnDefaultProperties(dbModel, defaultValues);
return dbModel;
let retVal = setRelationId(generationOptions, dbModel);
retVal = applyNamingStrategy(namingStrategy, retVal);
retVal = addImportsAndGenerationOptions(retVal, generationOptions);
retVal = removeColumnDefaultProperties(retVal, defaultValues);
return retVal;
}
function removeColumnDefaultProperties(
dbModel: EntityInfo[],
@ -155,7 +156,7 @@ function setRelationId(
if (generationOptions.relationIds) {
model.forEach(ent => {
ent.Columns.forEach(col => {
col.relations.map(rel => {
col.relations.forEach(rel => {
rel.relationIdField = rel.isOwner;
});
});
@ -202,8 +203,10 @@ export function modelGenerationPhase(
case "none":
casedFileName = element.tsEntityName;
break;
default:
throw new Error("Unknown case style");
}
const resultFilePath = path.resolve(entitesPath, casedFileName + ".ts");
const resultFilePath = path.resolve(entitesPath, `${casedFileName}.ts`);
const rendered = compliedTemplate(element);
fs.writeFileSync(resultFilePath, rendered, {
encoding: "UTF-8",
@ -226,6 +229,8 @@ function createHandlebarsHelpers(generationOptions: IGenerationOptions) {
case "none":
retStr = str;
break;
default:
throw new Error("Unknown case style");
}
return retStr;
});
@ -247,12 +252,14 @@ function createHandlebarsHelpers(generationOptions: IGenerationOptions) {
case "none":
retStr = str;
break;
default:
throw new Error("Unknown case style");
}
return retStr;
});
Handlebars.registerHelper("printPropertyVisibility", () =>
generationOptions.propertyVisibility !== "none"
? generationOptions.propertyVisibility + " "
? `${generationOptions.propertyVisibility} `
: ""
);
Handlebars.registerHelper("toPropertyName", str => {
@ -267,6 +274,8 @@ function createHandlebarsHelpers(generationOptions: IGenerationOptions) {
case "none":
retStr = str;
break;
default:
throw new Error("Unknown case style");
}
return retStr;
});
@ -277,9 +286,8 @@ function createHandlebarsHelpers(generationOptions: IGenerationOptions) {
Handlebars.registerHelper("toLazy", str => {
if (generationOptions.lazy) {
return `Promise<${str}>`;
} else {
return str;
}
return str;
});
Handlebars.registerHelper({
and: (v1, v2) => v1 && v2,
@ -360,10 +368,10 @@ function applyNamingStrategy(
namingStrategy: NamingStrategy,
dbModel: EntityInfo[]
) {
dbModel = changeRelationNames(dbModel);
dbModel = changeEntityNames(dbModel);
dbModel = changeColumnNames(dbModel);
return dbModel;
let retval = changeRelationNames(dbModel);
retval = changeEntityNames(retval);
retval = changeColumnNames(retval);
return retval;
function changeRelationNames(model: EntityInfo[]) {
model.forEach(entity => {
@ -398,9 +406,9 @@ function applyNamingStrategy(
col =>
col.name === column.tsName
)
.forEach(
col => (col.name = newName)
);
.forEach(col => {
col.name = newName;
});
});
}
});
@ -420,7 +428,9 @@ function applyNamingStrategy(
entity.Indexes.forEach(index => {
index.columns
.filter(column2 => column2.name === column.tsName)
.forEach(column2 => (column2.name = newName));
.forEach(column2 => {
column2.name = newName;
});
});
model.forEach(entity2 => {
entity2.Columns.forEach(column2 => {
@ -431,7 +441,9 @@ function applyNamingStrategy(
entity.tsEntityName &&
relation.relatedColumn === column.tsName
)
.map(v => (v.relatedColumn = newName));
.forEach(v => {
v.relatedColumn = newName;
});
column2.relations
.filter(
relation =>
@ -439,7 +451,9 @@ function applyNamingStrategy(
entity.tsEntityName &&
relation.ownerColumn === column.tsName
)
.map(v => (v.ownerColumn = newName));
.forEach(v => {
v.ownerColumn = newName;
});
});
});

View File

@ -1,11 +1,19 @@
export class IConnectionOptions {
export default class IConnectionOptions {
public host: string = "";
public port: number = 0;
public databaseName: string = "";
public user: string = "";
public password: string = "";
public databaseType: string = "";
public schemaName: string = "";
public ssl: boolean = false;
public timeout?: number;
}

View File

@ -1,14 +1,24 @@
export class IGenerationOptions {
export default class IGenerationOptions {
public resultsPath: string = "";
public noConfigs: boolean = false;
public convertCaseFile: "pascal" | "param" | "camel" | "none" = "none";
public convertCaseEntity: "pascal" | "camel" | "none" = "none";
public convertCaseProperty: "pascal" | "camel" | "none" = "none";
public propertyVisibility: "public" | "protected" | "private" | "none" =
"none";
public lazy: boolean = false;
public activeRecord: boolean = false;
public generateConstructor: boolean = false;
public customNamingStrategyPath: string = "";
public relationIds: boolean = false;
}

View File

@ -1,9 +1,11 @@
import changeCase = require("change-case");
import { AbstractNamingStrategy } from "./AbstractNamingStrategy";
import { EntityInfo } from "./models/EntityInfo";
import { RelationInfo } from "./models/RelationInfo";
import AbstractNamingStrategy from "./AbstractNamingStrategy";
import RelationInfo from "./models/RelationInfo";
import EntityInfo from "./models/EntityInfo";
export class NamingStrategy extends AbstractNamingStrategy {
import changeCase = require("change-case");
/* eslint-disable class-methods-use-this */
export default class NamingStrategy extends AbstractNamingStrategy {
public relationName(
columnOldName: string,
relation: RelationInfo,
@ -24,10 +26,10 @@ export class NamingStrategy extends AbstractNamingStrategy {
columnName.toLowerCase().lastIndexOf("id")
);
}
if (!isNaN(parseInt(columnName[columnName.length - 1], 10))) {
if (!Number.isNaN(parseInt(columnName[columnName.length - 1], 10))) {
columnName = columnName.substring(0, columnName.length - 1);
}
if (!isNaN(parseInt(columnName[columnName.length - 1], 10))) {
if (!Number.isNaN(parseInt(columnName[columnName.length - 1], 10))) {
columnName = columnName.substring(0, columnName.length - 1);
}
columnName += isRelationToMany ? "s" : "";
@ -37,7 +39,7 @@ export class NamingStrategy extends AbstractNamingStrategy {
columnOldName !== columnName
) {
if (ownerEntity.Columns.some(v => v.tsName === columnName)) {
columnName = columnName + "_";
columnName += "_";
for (let i = 2; i <= ownerEntity.Columns.length; i++) {
columnName =
columnName.substring(
@ -68,3 +70,5 @@ export class NamingStrategy extends AbstractNamingStrategy {
return columnName;
}
}
/* eslint-enable class-methods-use-this */

View File

@ -1,9 +1,11 @@
import * as packagejson from "../package.json";
export function LogError(
errText: string,
isABug: boolean = true,
errObject?: any
passedError?: any
) {
let errObject = passedError;
console.error(errText);
console.error(`Error occured in typeorm-model-generator.`);
console.error(`${packageVersion()} node@${process.version}`);
@ -12,10 +14,10 @@ export function LogError(
(packagejson as any).bugs.url
}`
);
if (isABug && !errObject) {
if (isABug && !passedError) {
errObject = new Error().stack;
}
if (!!errObject) {
if (errObject) {
console.error(errObject);
}
}

View File

@ -4,18 +4,21 @@ import {
WithWidthColumnType
} from "typeorm/driver/types/ColumnTypes";
import { DataTypeDefaults } from "typeorm/driver/types/DataTypeDefaults";
import { IConnectionOptions } from "../IConnectionOptions";
import { ColumnInfo } from "../models/ColumnInfo";
import { EntityInfo } from "../models/EntityInfo";
import { IndexInfo } from "../models/IndexInfo";
import { RelationInfo } from "../models/RelationInfo";
import { IRelationTempInfo } from "../models/RelationTempInfo";
import * as TomgUtils from "../Utils";
import EntityInfo from "../models/EntityInfo";
import RelationInfo from "../models/RelationInfo";
import ColumnInfo from "../models/ColumnInfo";
import IConnectionOptions from "../IConnectionOptions";
import IndexInfo from "../models/IndexInfo";
import RelationTempInfo from "../models/RelationTempInfo";
export abstract class AbstractDriver {
export default abstract class AbstractDriver {
public abstract standardPort: number;
public abstract standardSchema: string;
public abstract standardUser: string;
public abstract defaultValues: DataTypeDefaults;
public ColumnTypesWithWidth: WithWidthColumnType[] = [
@ -25,6 +28,7 @@ export abstract class AbstractDriver {
"int",
"bigint"
];
public ColumnTypesWithPrecision: WithPrecisionColumnType[] = [
"float",
"double",
@ -45,6 +49,7 @@ export abstract class AbstractDriver {
"timestamp with time zone",
"timestamp with local time zone"
];
public ColumnTypesWithLength: WithLengthColumnType[] = [
"character varying",
"varying character",
@ -65,15 +70,16 @@ export abstract class AbstractDriver {
schema: string,
dbNames: string
) => Promise<
Array<{
{
TABLE_SCHEMA: string;
TABLE_NAME: string;
DB_NAME: string;
}>
}[]
>;
public FindManyToManyRelations(dbModel: EntityInfo[]) {
const manyToManyEntities = dbModel.filter(
public static FindManyToManyRelations(dbModel: EntityInfo[]) {
let retval = dbModel;
const manyToManyEntities = retval.filter(
entity =>
entity.Columns.filter(column => {
return (
@ -83,7 +89,7 @@ export abstract class AbstractDriver {
);
}).length === entity.Columns.length
);
manyToManyEntities.map(entity => {
manyToManyEntities.forEach(entity => {
let relations: RelationInfo[] = [];
relations = entity.Columns.reduce(
(prev: RelationInfo[], curr) => prev.concat(curr.relations),
@ -92,9 +98,14 @@ export abstract class AbstractDriver {
const namesOfRelatedTables = relations
.map(v => v.relatedTable)
.filter((v, i, s) => s.indexOf(v) === i);
const [
firstRelatedTable,
secondRelatedTable
] = namesOfRelatedTables;
if (namesOfRelatedTables.length === 2) {
const relatedTable1 = dbModel.find(
v => v.tsEntityName === namesOfRelatedTables[0]
const relatedTable1 = retval.find(
v => v.tsEntityName === firstRelatedTable
)!;
relatedTable1.Columns = relatedTable1.Columns.filter(
v =>
@ -102,7 +113,7 @@ export abstract class AbstractDriver {
.toLowerCase()
.startsWith(entity.tsEntityName.toLowerCase())
);
const relatedTable2 = dbModel.find(
const relatedTable2 = retval.find(
v => v.tsEntityName === namesOfRelatedTables[1]
)!;
relatedTable2.Columns = relatedTable2.Columns.filter(
@ -111,31 +122,31 @@ export abstract class AbstractDriver {
.toLowerCase()
.startsWith(entity.tsEntityName.toLowerCase())
);
dbModel = dbModel.filter(ent => {
retval = retval.filter(ent => {
return ent.tsEntityName !== entity.tsEntityName;
});
const column1 = new ColumnInfo();
column1.tsName = namesOfRelatedTables[1];
column1.tsName = secondRelatedTable;
column1.options.name = entity.sqlEntityName;
const col1Rel = new RelationInfo();
col1Rel.relatedTable = namesOfRelatedTables[1];
col1Rel.relatedColumn = namesOfRelatedTables[1];
col1Rel.relatedTable = secondRelatedTable;
col1Rel.relatedColumn = secondRelatedTable;
col1Rel.relationType = "ManyToMany";
col1Rel.isOwner = true;
col1Rel.ownerColumn = namesOfRelatedTables[0];
col1Rel.ownerColumn = firstRelatedTable;
column1.relations.push(col1Rel);
relatedTable1.Columns.push(column1);
const column2 = new ColumnInfo();
column2.tsName = namesOfRelatedTables[0];
column2.tsName = firstRelatedTable;
const col2Rel = new RelationInfo();
col2Rel.relatedTable = namesOfRelatedTables[0];
col2Rel.relatedColumn = namesOfRelatedTables[1];
col2Rel.relatedTable = firstRelatedTable;
col2Rel.relatedColumn = secondRelatedTable;
col2Rel.relationType = "ManyToMany";
col2Rel.isOwner = false;
@ -143,14 +154,15 @@ export abstract class AbstractDriver {
relatedTable2.Columns.push(column2);
}
});
return dbModel;
return retval;
}
public async GetDataFromServer(
connectionOptons: IConnectionOptions
): Promise<EntityInfo[]> {
let dbModel = [] as EntityInfo[];
await this.ConnectToServer(connectionOptons);
const sqlEscapedSchema = this.escapeCommaSeparatedList(
const sqlEscapedSchema = AbstractDriver.escapeCommaSeparatedList(
connectionOptons.schemaName
);
dbModel = await this.GetAllTables(
@ -173,8 +185,8 @@ export abstract class AbstractDriver {
connectionOptons.databaseName
);
await this.DisconnectFromServer();
dbModel = this.FindManyToManyRelations(dbModel);
this.FindPrimaryColumnsFromIndexes(dbModel);
dbModel = AbstractDriver.FindManyToManyRelations(dbModel);
AbstractDriver.FindPrimaryColumnsFromIndexes(dbModel);
return dbModel;
}
@ -199,8 +211,8 @@ export abstract class AbstractDriver {
return ret;
}
public GetRelationsFromRelationTempInfo(
relationsTemp: IRelationTempInfo[],
public static GetRelationsFromRelationTempInfo(
relationsTemp: RelationTempInfo[],
entities: EntityInfo[]
) {
relationsTemp.forEach(relationTmp => {
@ -275,7 +287,7 @@ export abstract class AbstractDriver {
if (
referencedEntity.Columns.some(v => v.tsName === columnName)
) {
columnName = columnName + "_";
columnName += "_";
for (let i = 2; i <= referencedEntity.Columns.length; i++) {
columnName =
columnName.substring(
@ -331,23 +343,26 @@ export abstract class AbstractDriver {
});
return entities;
}
public abstract async GetCoulmnsFromEntity(
entities: EntityInfo[],
schema: string,
dbNames: string
): Promise<EntityInfo[]>;
public abstract async GetIndexesFromEntity(
entities: EntityInfo[],
schema: string,
dbNames: string
): Promise<EntityInfo[]>;
public abstract async GetRelations(
entities: EntityInfo[],
schema: string,
dbNames: string
): Promise<EntityInfo[]>;
public FindPrimaryColumnsFromIndexes(dbModel: EntityInfo[]) {
public static FindPrimaryColumnsFromIndexes(dbModel: EntityInfo[]) {
dbModel.forEach(entity => {
const primaryIndex = entity.Indexes.find(v => v.isPrimaryKey);
entity.Columns.filter(
@ -356,7 +371,10 @@ export abstract class AbstractDriver {
primaryIndex.columns.some(
cIndex => cIndex.name === col.tsName
)
).forEach(col => (col.options.primary = true));
).forEach(col => {
// eslint-disable-next-line no-param-reassign
col.options.primary = true;
});
if (
!entity.Columns.some(v => {
return !!v.options.primary;
@ -366,18 +384,22 @@ export abstract class AbstractDriver {
`Table ${entity.tsEntityName} has no PK.`,
false
);
return;
}
});
}
public abstract async DisconnectFromServer();
public abstract async CreateDB(dbName: string);
public abstract async DropDB(dbName: string);
public abstract async UseDB(dbName: string);
public abstract async CheckIfDBExists(dbName: string): Promise<boolean>;
// TODO: change name
protected escapeCommaSeparatedList(commaSeparatedList: string) {
return "'" + commaSeparatedList.split(",").join("','") + "'";
protected static escapeCommaSeparatedList(commaSeparatedList: string) {
return `'${commaSeparatedList.split(",").join("','")}'`;
}
}

View File

@ -1,5 +1,5 @@
import { MysqlDriver } from "./MysqlDriver";
import MysqlDriver from "./MysqlDriver";
export class MariaDbDriver extends MysqlDriver {
export default class MariaDbDriver extends MysqlDriver {
public readonly EngineName: string = "MariaDb";
}

View File

@ -2,33 +2,37 @@ import * as MSSQL from "mssql";
import { ConnectionOptions } from "typeorm";
import * as TypeormDriver from "typeorm/driver/sqlserver/SqlServerDriver";
import { DataTypeDefaults } from "typeorm/driver/types/DataTypeDefaults";
import { IConnectionOptions } from "../IConnectionOptions";
import { ColumnInfo } from "../models/ColumnInfo";
import { EntityInfo } from "../models/EntityInfo";
import { IndexColumnInfo } from "../models/IndexColumnInfo";
import { IndexInfo } from "../models/IndexInfo";
import { IRelationTempInfo } from "../models/RelationTempInfo";
import * as TomgUtils from "../Utils";
import { AbstractDriver } from "./AbstractDriver";
import AbstractDriver from "./AbstractDriver";
import EntityInfo from "../models/EntityInfo";
import ColumnInfo from "../models/ColumnInfo";
import IndexInfo from "../models/IndexInfo";
import IndexColumnInfo from "../models/IndexColumnInfo";
import RelationTempInfo from "../models/RelationTempInfo";
import IConnectionOptions from "../IConnectionOptions";
export class MssqlDriver extends AbstractDriver {
export default class MssqlDriver extends AbstractDriver {
public defaultValues: DataTypeDefaults = new TypeormDriver.SqlServerDriver({
options: { replication: undefined } as ConnectionOptions
} as any).dataTypeDefaults;
public readonly standardPort = 1433;
public readonly standardSchema = "dbo";
public readonly standardUser = "sa";
private Connection: MSSQL.ConnectionPool;
public GetAllTablesQuery = async (schema: string, dbNames: string) => {
const request = new MSSQL.Request(this.Connection);
const response: Array<{
const response: {
TABLE_SCHEMA: string;
TABLE_NAME: string;
DB_NAME: string;
}> = (await request.query(
}[] = (await request.query(
`SELECT TABLE_SCHEMA,TABLE_NAME, table_catalog as "DB_NAME" FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE='BASE TABLE' and TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG in (${this.escapeCommaSeparatedList(
WHERE TABLE_TYPE='BASE TABLE' and TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG in (${MssqlDriver.escapeCommaSeparatedList(
dbNames
)})`
)).recordset;
@ -41,7 +45,7 @@ WHERE TABLE_TYPE='BASE TABLE' and TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG
dbNames: string
): Promise<EntityInfo[]> {
const request = new MSSQL.Request(this.Connection);
const response: Array<{
const response: {
TABLE_NAME: string;
COLUMN_NAME: string;
COLUMN_DEFAULT: string;
@ -52,7 +56,7 @@ WHERE TABLE_TYPE='BASE TABLE' and TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG
NUMERIC_SCALE: number;
IsIdentity: number;
IsUnique: number;
}> = (await request.query(`SELECT TABLE_NAME,COLUMN_NAME,COLUMN_DEFAULT,IS_NULLABLE,
}[] = (await request.query(`SELECT TABLE_NAME,COLUMN_NAME,COLUMN_DEFAULT,IS_NULLABLE,
DATA_TYPE,CHARACTER_MAXIMUM_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE,
COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') IsIdentity,
(SELECT count(*)
@ -65,7 +69,7 @@ WHERE TABLE_TYPE='BASE TABLE' and TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG
and cu.COLUMN_NAME = c.COLUMN_NAME
and tc.TABLE_SCHEMA=c.TABLE_SCHEMA) IsUnique
FROM INFORMATION_SCHEMA.COLUMNS c
where TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG in (${this.escapeCommaSeparatedList(
where TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG in (${MssqlDriver.escapeCommaSeparatedList(
dbNames
)})
order by ordinal_position`)).recordset;
@ -81,7 +85,7 @@ WHERE TABLE_TYPE='BASE TABLE' and TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG
colInfo.options.nullable = resp.IS_NULLABLE === "YES";
colInfo.options.generated = resp.IsIdentity === 1;
colInfo.options.unique = resp.IsUnique === 1;
colInfo.options.default = this.ReturnDefaultValueFunction(
colInfo.options.default = MssqlDriver.ReturnDefaultValueFunction(
resp.COLUMN_DEFAULT
);
colInfo.options.type = resp.DATA_TYPE as any;
@ -218,28 +222,30 @@ WHERE TABLE_TYPE='BASE TABLE' and TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG
});
return entities;
}
public async GetIndexesFromEntity(
entities: EntityInfo[],
schema: string,
dbNames: string
): Promise<EntityInfo[]> {
const request = new MSSQL.Request(this.Connection);
const response: Array<{
const response: {
TableName: string;
IndexName: string;
ColumnName: string;
is_unique: boolean;
is_primary_key: boolean;
}> = [];
for (const dbName of dbNames.split(",")) {
await this.UseDB(dbName);
const resp: Array<{
TableName: string;
IndexName: string;
ColumnName: string;
is_unique: boolean;
is_primary_key: boolean;
}> = (await request.query(`SELECT
}[] = [];
await Promise.all(
dbNames.split(",").map(async dbName => {
await this.UseDB(dbName);
const resp: {
TableName: string;
IndexName: string;
ColumnName: string;
is_unique: boolean;
is_primary_key: boolean;
}[] = (await request.query(`SELECT
TableName = t.name,
IndexName = ind.name,
ColumnName = col.name,
@ -259,8 +265,9 @@ WHERE TABLE_TYPE='BASE TABLE' and TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG
t.is_ms_shipped = 0 and s.name in (${schema})
ORDER BY
t.name, ind.name, ind.index_id, ic.key_ordinal;`)).recordset;
response.push(...resp);
}
response.push(...resp);
})
);
entities.forEach(ent => {
response
.filter(filterVal => filterVal.TableName === ent.tsEntityName)
@ -272,9 +279,9 @@ WHERE TABLE_TYPE='BASE TABLE' and TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG
return filterVal.name === resp.IndexName;
}).length > 0
) {
indexInfo = ent.Indexes.filter(filterVal => {
[indexInfo] = ent.Indexes.filter(filterVal => {
return filterVal.name === resp.IndexName;
})[0];
});
} else {
indexInfo.columns = [] as IndexColumnInfo[];
indexInfo.name = resp.IndexName;
@ -289,13 +296,14 @@ WHERE TABLE_TYPE='BASE TABLE' and TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG
return entities;
}
public async GetRelations(
entities: EntityInfo[],
schema: string,
dbNames: string
): Promise<EntityInfo[]> {
const request = new MSSQL.Request(this.Connection);
const response: Array<{
const response: {
TableWithForeignKey: string;
FK_PartNo: number;
ForeignKeyColumn: string;
@ -303,20 +311,21 @@ WHERE TABLE_TYPE='BASE TABLE' and TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG
ForeignKeyColumnReferenced: string;
onDelete: "RESTRICT" | "CASCADE" | "SET_NULL" | "NO_ACTION";
onUpdate: "RESTRICT" | "CASCADE" | "SET_NULL" | "NO_ACTION";
object_id: number;
}> = [];
for (const dbName of dbNames.split(",")) {
await this.UseDB(dbName);
const resp: Array<{
TableWithForeignKey: string;
FK_PartNo: number;
ForeignKeyColumn: string;
TableReferenced: string;
ForeignKeyColumnReferenced: string;
onDelete: "RESTRICT" | "CASCADE" | "SET_NULL" | "NO_ACTION";
onUpdate: "RESTRICT" | "CASCADE" | "SET_NULL" | "NO_ACTION";
object_id: number;
}> = (await request.query(`select
objectId: number;
}[] = [];
await Promise.all(
dbNames.split(",").map(async dbName => {
await this.UseDB(dbName);
const resp: {
TableWithForeignKey: string;
FK_PartNo: number;
ForeignKeyColumn: string;
TableReferenced: string;
ForeignKeyColumnReferenced: string;
onDelete: "RESTRICT" | "CASCADE" | "SET_NULL" | "NO_ACTION";
onUpdate: "RESTRICT" | "CASCADE" | "SET_NULL" | "NO_ACTION";
objectId: number;
}[] = (await request.query(`select
parentTable.name as TableWithForeignKey,
fkc.constraint_column_id as FK_PartNo,
parentColumn.name as ForeignKeyColumn,
@ -324,7 +333,7 @@ WHERE TABLE_TYPE='BASE TABLE' and TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG
referencedColumn.name as ForeignKeyColumnReferenced,
fk.delete_referential_action_desc as onDelete,
fk.update_referential_action_desc as onUpdate,
fk.object_id
fk.object_id as objectId
from
sys.foreign_keys fk
inner join
@ -343,15 +352,16 @@ where
fk.is_disabled=0 and fk.is_ms_shipped=0 and parentSchema.name in (${schema})
order by
TableWithForeignKey, FK_PartNo`)).recordset;
response.push(...resp);
}
const relationsTemp: IRelationTempInfo[] = [] as IRelationTempInfo[];
response.push(...resp);
})
);
const relationsTemp: RelationTempInfo[] = [] as RelationTempInfo[];
response.forEach(resp => {
let rels = relationsTemp.find(
val => val.object_id === resp.object_id
val => val.objectId === resp.objectId
);
if (rels === undefined) {
rels = {} as IRelationTempInfo;
rels = {} as RelationTempInfo;
rels.ownerColumnsNames = [];
rels.referencedColumnsNames = [];
switch (resp.onDelete) {
@ -376,7 +386,7 @@ order by
rels.actionOnUpdate = resp.onUpdate;
break;
}
rels.object_id = resp.object_id;
rels.objectId = resp.objectId;
rels.ownerTable = resp.TableWithForeignKey;
rels.referencedTable = resp.TableReferenced;
relationsTemp.push(rels);
@ -384,17 +394,19 @@ order by
rels.ownerColumnsNames.push(resp.ForeignKeyColumn);
rels.referencedColumnsNames.push(resp.ForeignKeyColumnReferenced);
});
entities = this.GetRelationsFromRelationTempInfo(
const retVal = MssqlDriver.GetRelationsFromRelationTempInfo(
relationsTemp,
entities
);
return entities;
return retVal;
}
public async DisconnectFromServer() {
if (this.Connection) {
await this.Connection.close();
}
}
public async ConnectToServer(connectionOptons: IConnectionOptions) {
const databaseName = connectionOptons.databaseName.split(",")[0];
const config: MSSQL.config = {
@ -427,18 +439,22 @@ order by
await promise;
}
public async CreateDB(dbName: string) {
const request = new MSSQL.Request(this.Connection);
await request.query(`CREATE DATABASE ${dbName}; `);
}
public async UseDB(dbName: string) {
const request = new MSSQL.Request(this.Connection);
await request.query(`USE ${dbName}; `);
}
public async DropDB(dbName: string) {
const request = new MSSQL.Request(this.Connection);
await request.query(`DROP DATABASE ${dbName}; `);
}
public async CheckIfDBExists(dbName: string): Promise<boolean> {
const request = new MSSQL.Request(this.Connection);
const resp = await request.query(
@ -446,16 +462,20 @@ order by
);
return resp.recordset.length > 0;
}
private ReturnDefaultValueFunction(defVal: string | null): string | null {
if (!defVal) {
private static ReturnDefaultValueFunction(
defVal: string | null
): string | null {
let defaultValue = defVal;
if (!defaultValue) {
return null;
}
if (defVal.startsWith("(") && defVal.endsWith(")")) {
defVal = defVal.slice(1, -1);
if (defaultValue.startsWith("(") && defaultValue.endsWith(")")) {
defaultValue = defaultValue.slice(1, -1);
}
if (defVal.startsWith(`'`)) {
return `() => "${defVal}"`;
if (defaultValue.startsWith(`'`)) {
return `() => "${defaultValue}"`;
}
return `() => "${defVal}"`;
return `() => "${defaultValue}"`;
}
}

View File

@ -2,22 +2,26 @@ import * as MYSQL from "mysql";
import { ConnectionOptions } from "typeorm";
import * as TypeormDriver from "typeorm/driver/mysql/MysqlDriver";
import { DataTypeDefaults } from "typeorm/driver/types/DataTypeDefaults";
import { IConnectionOptions } from "../IConnectionOptions";
import { ColumnInfo } from "../models/ColumnInfo";
import { EntityInfo } from "../models/EntityInfo";
import { IndexColumnInfo } from "../models/IndexColumnInfo";
import { IndexInfo } from "../models/IndexInfo";
import { IRelationTempInfo } from "../models/RelationTempInfo";
import * as TomgUtils from "../Utils";
import { AbstractDriver } from "./AbstractDriver";
import AbstractDriver from "./AbstractDriver";
import EntityInfo from "../models/EntityInfo";
import ColumnInfo from "../models/ColumnInfo";
import IndexInfo from "../models/IndexInfo";
import IndexColumnInfo from "../models/IndexColumnInfo";
import RelationTempInfo from "../models/RelationTempInfo";
import IConnectionOptions from "../IConnectionOptions";
export class MysqlDriver extends AbstractDriver {
export default class MysqlDriver extends AbstractDriver {
public defaultValues: DataTypeDefaults = new TypeormDriver.MysqlDriver({
options: { replication: undefined } as ConnectionOptions
} as any).dataTypeDefaults;
public readonly EngineName: string = "MySQL";
public readonly standardPort = 3306;
public readonly standardUser = "root";
public readonly standardSchema = "";
private Connection: MYSQL.Connection;
@ -30,7 +34,9 @@ export class MysqlDriver extends AbstractDriver {
}>(`SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_SCHEMA as DB_NAME
FROM information_schema.tables
WHERE table_type='BASE TABLE'
AND table_schema IN (${this.escapeCommaSeparatedList(dbNames)})`);
AND table_schema IN (${MysqlDriver.escapeCommaSeparatedList(
dbNames
)})`);
return response;
};
@ -54,7 +60,7 @@ export class MysqlDriver extends AbstractDriver {
}>(`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, COLUMN_TYPE, COLUMN_KEY
FROM INFORMATION_SCHEMA.COLUMNS where TABLE_SCHEMA IN (${this.escapeCommaSeparatedList(
FROM INFORMATION_SCHEMA.COLUMNS where TABLE_SCHEMA IN (${MysqlDriver.escapeCommaSeparatedList(
dbNames
)})
order by ordinal_position`);
@ -68,7 +74,7 @@ export class MysqlDriver extends AbstractDriver {
colInfo.options.nullable = resp.IS_NULLABLE === "YES";
colInfo.options.generated = resp.IsIdentity === 1;
colInfo.options.unique = resp.COLUMN_KEY === "UNI";
colInfo.options.default = this.ReturnDefaultValueFunction(
colInfo.options.default = MysqlDriver.ReturnDefaultValueFunction(
resp.COLUMN_DEFAULT
);
colInfo.options.type = resp.DATA_TYPE as any;
@ -163,7 +169,7 @@ export class MysqlDriver extends AbstractDriver {
colInfo.options.enum = resp.COLUMN_TYPE.substring(
5,
resp.COLUMN_TYPE.length - 1
).replace(/\'/gi, '"');
).replace(/'/gi, '"');
break;
case "json":
colInfo.tsType = "object";
@ -244,6 +250,7 @@ export class MysqlDriver extends AbstractDriver {
});
return entities;
}
public async GetIndexesFromEntity(
entities: EntityInfo[],
schema: string,
@ -258,7 +265,9 @@ export class MysqlDriver extends AbstractDriver {
}>(`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 IN (${this.escapeCommaSeparatedList(dbNames)})`);
WHERE table_schema IN (${MysqlDriver.escapeCommaSeparatedList(
dbNames
)})`);
entities.forEach(ent => {
response
.filter(filterVal => filterVal.TableName === ent.tsEntityName)
@ -287,6 +296,7 @@ export class MysqlDriver extends AbstractDriver {
return entities;
}
public async GetRelations(
entities: EntityInfo[],
schema: string,
@ -316,23 +326,23 @@ export class MysqlDriver extends AbstractDriver {
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
ON CU.CONSTRAINT_NAME=RC.CONSTRAINT_NAME AND CU.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
WHERE
TABLE_SCHEMA IN (${this.escapeCommaSeparatedList(dbNames)})
TABLE_SCHEMA IN (${MysqlDriver.escapeCommaSeparatedList(dbNames)})
AND CU.REFERENCED_TABLE_NAME IS NOT NULL;
`);
const relationsTemp: IRelationTempInfo[] = [] as IRelationTempInfo[];
const relationsTemp: RelationTempInfo[] = [] as RelationTempInfo[];
response.forEach(resp => {
let rels = relationsTemp.find(
val => val.object_id === resp.object_id
val => val.objectId === resp.object_id
);
if (rels === undefined) {
rels = {} as IRelationTempInfo;
rels = {} as RelationTempInfo;
rels.ownerColumnsNames = [];
rels.referencedColumnsNames = [];
rels.actionOnDelete =
resp.onDelete === "NO_ACTION" ? null : resp.onDelete;
rels.actionOnUpdate =
resp.onUpdate === "NO_ACTION" ? null : resp.onUpdate;
rels.object_id = resp.object_id;
rels.objectId = resp.object_id;
rels.ownerTable = resp.TableWithForeignKey;
rels.referencedTable = resp.TableReferenced;
relationsTemp.push(rels);
@ -340,12 +350,13 @@ export class MysqlDriver extends AbstractDriver {
rels.ownerColumnsNames.push(resp.ForeignKeyColumn);
rels.referencedColumnsNames.push(resp.ForeignKeyColumnReferenced);
});
entities = this.GetRelationsFromRelationTempInfo(
const retVal = MysqlDriver.GetRelationsFromRelationTempInfo(
relationsTemp,
entities
);
return entities;
return retVal;
}
public async DisconnectFromServer() {
const promise = new Promise<boolean>((resolve, reject) => {
this.Connection.end(err => {
@ -365,6 +376,7 @@ export class MysqlDriver extends AbstractDriver {
await promise;
}
}
public async ConnectToServer(connectionOptons: IConnectionOptions) {
const databaseName = connectionOptons.databaseName.split(",")[0];
let config: MYSQL.ConnectionConfig;
@ -410,21 +422,26 @@ export class MysqlDriver extends AbstractDriver {
await promise;
}
public async CreateDB(dbName: string) {
await this.ExecQuery<any>(`CREATE DATABASE ${dbName}; `);
}
public async UseDB(dbName: string) {
await this.ExecQuery<any>(`USE ${dbName}; `);
}
public async DropDB(dbName: string) {
await this.ExecQuery<any>(`DROP DATABASE ${dbName}; `);
}
public async CheckIfDBExists(dbName: string): Promise<boolean> {
const resp = await this.ExecQuery<any>(
`SHOW DATABASES LIKE '${dbName}' `
);
return resp.length > 0;
}
public async ExecQuery<T>(sql: string): Promise<T[]> {
const ret: T[] = [];
const query = this.Connection.query(sql);
@ -439,16 +456,23 @@ export class MysqlDriver extends AbstractDriver {
await promise;
return ret;
}
private ReturnDefaultValueFunction(defVal: string | null): string | null {
if (!defVal || defVal === "NULL") {
private static ReturnDefaultValueFunction(
defVal: string | null
): string | null {
let defaultValue = defVal;
if (!defaultValue || defaultValue === "NULL") {
return null;
}
if (defVal.toLowerCase() === "current_timestamp()") {
defVal = "CURRENT_TIMESTAMP";
if (defaultValue.toLowerCase() === "current_timestamp()") {
defaultValue = "CURRENT_TIMESTAMP";
}
if (defVal === "CURRENT_TIMESTAMP" || defVal.startsWith(`'`)) {
return `() => "${defVal}"`;
if (
defaultValue === "CURRENT_TIMESTAMP" ||
defaultValue.startsWith(`'`)
) {
return `() => "${defaultValue}"`;
}
return `() => "'${defVal}'"`;
return `() => "'${defaultValue}'"`;
}
}

View File

@ -1,28 +1,33 @@
import * as TypeormDriver from "typeorm/driver/oracle/OracleDriver";
import { DataTypeDefaults } from "typeorm/driver/types/DataTypeDefaults";
import { IConnectionOptions } from "../IConnectionOptions";
import { ColumnInfo } from "../models/ColumnInfo";
import { EntityInfo } from "../models/EntityInfo";
import { IndexColumnInfo } from "../models/IndexColumnInfo";
import { IndexInfo } from "../models/IndexInfo";
import { IRelationTempInfo } from "../models/RelationTempInfo";
import * as TomgUtils from "../Utils";
import { AbstractDriver } from "./AbstractDriver";
import AbstractDriver from "./AbstractDriver";
import EntityInfo from "../models/EntityInfo";
import ColumnInfo from "../models/ColumnInfo";
import IndexInfo from "../models/IndexInfo";
import IndexColumnInfo from "../models/IndexColumnInfo";
import RelationTempInfo from "../models/RelationTempInfo";
import IConnectionOptions from "../IConnectionOptions";
export class OracleDriver extends AbstractDriver {
export default class OracleDriver extends AbstractDriver {
public defaultValues: DataTypeDefaults = new TypeormDriver.OracleDriver({
options: undefined
} as any).dataTypeDefaults;
public readonly standardPort = 1521;
public readonly standardUser = "SYS";
public readonly standardSchema = "";
public Oracle: any;
private Connection: any /*Oracle.IConnection*/;
constructor() {
private Connection: any /* Oracle.IConnection */;
public constructor() {
super();
try {
// eslint-disable-next-line import/no-extraneous-dependencies, global-require
this.Oracle = require("oracledb");
this.Oracle.outFormat = this.Oracle.OBJECT;
} catch (error) {
@ -31,22 +36,21 @@ export class OracleDriver extends AbstractDriver {
}
}
public GetAllTablesQuery = async (schema: string) => {
const response: Array<{
public GetAllTablesQuery = async () => {
const response: {
TABLE_SCHEMA: string;
TABLE_NAME: string;
DB_NAME: string;
}> = (await this.Connection.execute(
}[] = (await this.Connection.execute(
` SELECT NULL AS TABLE_SCHEMA, TABLE_NAME, NULL AS DB_NAME FROM all_tables WHERE owner = (select user from dual)`
)).rows!;
return response;
};
public async GetCoulmnsFromEntity(
entities: EntityInfo[],
schema: string
entities: EntityInfo[]
): Promise<EntityInfo[]> {
const response: Array<{
const response: {
TABLE_NAME: string;
COLUMN_NAME: string;
DATA_DEFAULT: string;
@ -57,7 +61,7 @@ export class OracleDriver extends AbstractDriver {
DATA_SCALE: number;
IDENTITY_COLUMN: string;
IS_UNIQUE: number;
}> = (await this.Connection
}[] = (await this.Connection
.execute(`SELECT utc.TABLE_NAME, utc.COLUMN_NAME, DATA_DEFAULT, NULLABLE, DATA_TYPE, DATA_LENGTH,
DATA_PRECISION, DATA_SCALE, IDENTITY_COLUMN,
(select count(*) from USER_CONS_COLUMNS ucc
@ -77,13 +81,13 @@ export class OracleDriver extends AbstractDriver {
colInfo.options.default =
!resp.DATA_DEFAULT || resp.DATA_DEFAULT.includes('"')
? null
: this.ReturnDefaultValueFunction(
: OracleDriver.ReturnDefaultValueFunction(
resp.DATA_DEFAULT
);
colInfo.options.unique = resp.IS_UNIQUE > 0;
resp.DATA_TYPE = resp.DATA_TYPE.replace(/\([0-9]+\)/g, "");
colInfo.options.type = resp.DATA_TYPE.toLowerCase() as any;
switch (resp.DATA_TYPE.toLowerCase()) {
const DATA_TYPE = resp.DATA_TYPE.replace(/\([0-9]+\)/g, "");
colInfo.options.type = DATA_TYPE.toLowerCase() as any;
switch (DATA_TYPE.toLowerCase()) {
case "char":
colInfo.tsType = "string";
break;
@ -173,7 +177,7 @@ export class OracleDriver extends AbstractDriver {
break;
default:
TomgUtils.LogError(
"Unknown column type:" + resp.DATA_TYPE
`Unknown column type:${DATA_TYPE}`
);
break;
}
@ -201,17 +205,17 @@ export class OracleDriver extends AbstractDriver {
});
return entities;
}
public async GetIndexesFromEntity(
entities: EntityInfo[],
schema: string
entities: EntityInfo[]
): Promise<EntityInfo[]> {
const response: Array<{
const response: {
COLUMN_NAME: string;
TABLE_NAME: string;
INDEX_NAME: string;
UNIQUENESS: string;
ISPRIMARYKEY: number;
}> = (await this.Connection
}[] = (await this.Connection
.execute(`SELECT ind.TABLE_NAME, ind.INDEX_NAME, col.COLUMN_NAME,ind.UNIQUENESS, CASE WHEN uc.CONSTRAINT_NAME IS NULL THEN 0 ELSE 1 END ISPRIMARYKEY
FROM USER_INDEXES ind
JOIN USER_IND_COLUMNS col ON ind.INDEX_NAME=col.INDEX_NAME
@ -246,11 +250,9 @@ export class OracleDriver extends AbstractDriver {
return entities;
}
public async GetRelations(
entities: EntityInfo[],
schema: string
): Promise<EntityInfo[]> {
const response: Array<{
public async GetRelations(entities: EntityInfo[]): Promise<EntityInfo[]> {
const response: {
OWNER_TABLE_NAME: string;
OWNER_POSITION: string;
OWNER_COLUMN_NAME: string;
@ -258,7 +260,7 @@ export class OracleDriver extends AbstractDriver {
CHILD_COLUMN_NAME: string;
DELETE_RULE: "RESTRICT" | "CASCADE" | "SET NULL" | "NO ACTION";
CONSTRAINT_NAME: string;
}> = (await this.Connection
}[] = (await this.Connection
.execute(`select owner.TABLE_NAME OWNER_TABLE_NAME,ownCol.POSITION OWNER_POSITION,ownCol.COLUMN_NAME OWNER_COLUMN_NAME,
child.TABLE_NAME CHILD_TABLE_NAME ,childCol.COLUMN_NAME CHILD_COLUMN_NAME,
owner.DELETE_RULE,
@ -270,19 +272,19 @@ export class OracleDriver extends AbstractDriver {
ORDER BY OWNER_TABLE_NAME ASC, owner.CONSTRAINT_NAME ASC, OWNER_POSITION ASC`))
.rows!;
const relationsTemp: IRelationTempInfo[] = [] as IRelationTempInfo[];
const relationsTemp: RelationTempInfo[] = [] as RelationTempInfo[];
response.forEach(resp => {
let rels = relationsTemp.find(
val => val.object_id === resp.CONSTRAINT_NAME
val => val.objectId === resp.CONSTRAINT_NAME
);
if (rels === undefined) {
rels = {} as IRelationTempInfo;
rels = {} as RelationTempInfo;
rels.ownerColumnsNames = [];
rels.referencedColumnsNames = [];
rels.actionOnDelete =
resp.DELETE_RULE === "NO ACTION" ? null : resp.DELETE_RULE;
rels.actionOnUpdate = null;
rels.object_id = resp.CONSTRAINT_NAME;
rels.objectId = resp.CONSTRAINT_NAME;
rels.ownerTable = resp.OWNER_TABLE_NAME;
rels.referencedTable = resp.CHILD_TABLE_NAME;
relationsTemp.push(rels);
@ -290,21 +292,23 @@ export class OracleDriver extends AbstractDriver {
rels.ownerColumnsNames.push(resp.OWNER_COLUMN_NAME);
rels.referencedColumnsNames.push(resp.CHILD_COLUMN_NAME);
});
entities = this.GetRelationsFromRelationTempInfo(
const retVal = OracleDriver.GetRelationsFromRelationTempInfo(
relationsTemp,
entities
);
return entities;
return retVal;
}
public async DisconnectFromServer() {
if (this.Connection) {
await this.Connection.close();
}
}
public async ConnectToServer(connectionOptons: IConnectionOptions) {
let config: any;
if (connectionOptons.user === String(process.env.ORACLE_UsernameSys)) {
config /*Oracle.IConnectionAttributes*/ = {
config /* Oracle.IConnectionAttributes */ = {
connectString: `${connectionOptons.host}:${connectionOptons.port}/${connectionOptons.databaseName}`,
externalAuth: connectionOptons.ssl,
password: connectionOptons.password,
@ -312,7 +316,7 @@ export class OracleDriver extends AbstractDriver {
user: connectionOptons.user
};
} else {
config /*Oracle.IConnectionAttributes*/ = {
config /* Oracle.IConnectionAttributes */ = {
connectString: `${connectionOptons.host}:${connectionOptons.port}/${connectionOptons.databaseName}`,
externalAuth: connectionOptons.ssl,
password: connectionOptons.password,
@ -347,28 +351,36 @@ export class OracleDriver extends AbstractDriver {
);
await this.Connection.execute(`GRANT CONNECT TO ${dbName}`);
}
public async UseDB(dbName: string) {
// eslint-disable-next-line class-methods-use-this
public async UseDB() {
// not supported
}
public async DropDB(dbName: string) {
await this.Connection.execute(`DROP USER ${dbName} CASCADE`);
}
public async CheckIfDBExists(dbName: string): Promise<boolean> {
const x = await this.Connection.execute(
`select count(*) as CNT from dba_users where username='${dbName.toUpperCase()}'`
);
return x.rows[0][0] > 0 || x.rows[0].CNT;
}
private ReturnDefaultValueFunction(defVal: string | null): string | null {
if (!defVal) {
private static ReturnDefaultValueFunction(
defVal: string | null
): string | null {
let defaultVal = defVal;
if (!defaultVal) {
return null;
}
if (defVal.endsWith(" ")) {
defVal = defVal.slice(0, -1);
if (defaultVal.endsWith(" ")) {
defaultVal = defaultVal.slice(0, -1);
}
if (defVal.startsWith(`'`)) {
return `() => "${defVal}"`;
if (defaultVal.startsWith(`'`)) {
return `() => "${defaultVal}"`;
}
return `() => "${defVal}"`;
return `() => "${defaultVal}"`;
}
}

View File

@ -2,31 +2,34 @@ import * as PG from "pg";
import { ConnectionOptions } from "typeorm";
import * as TypeormDriver from "typeorm/driver/postgres/PostgresDriver";
import { DataTypeDefaults } from "typeorm/driver/types/DataTypeDefaults";
import { IConnectionOptions } from "../IConnectionOptions";
import { ColumnInfo } from "../models/ColumnInfo";
import { EntityInfo } from "../models/EntityInfo";
import { IndexColumnInfo } from "../models/IndexColumnInfo";
import { IndexInfo } from "../models/IndexInfo";
import { IRelationTempInfo } from "../models/RelationTempInfo";
import * as TomgUtils from "../Utils";
import { AbstractDriver } from "./AbstractDriver";
import AbstractDriver from "./AbstractDriver";
import EntityInfo from "../models/EntityInfo";
import ColumnInfo from "../models/ColumnInfo";
import IndexInfo from "../models/IndexInfo";
import IndexColumnInfo from "../models/IndexColumnInfo";
import RelationTempInfo from "../models/RelationTempInfo";
import IConnectionOptions from "../IConnectionOptions";
export class PostgresDriver extends AbstractDriver {
export default class PostgresDriver extends AbstractDriver {
public defaultValues: DataTypeDefaults = new TypeormDriver.PostgresDriver({
options: { replication: undefined } as ConnectionOptions
} as any).dataTypeDefaults;
public readonly standardPort = 5432;
public readonly standardUser = "postgres";
public readonly standardSchema = "public";
private Connection: PG.Client;
public GetAllTablesQuery = async (schema: string) => {
const response: Array<{
const response: {
TABLE_SCHEMA: string;
TABLE_NAME: string;
DB_NAME: string;
}> = (await this.Connection.query(
}[] = (await this.Connection.query(
`SELECT table_schema as "TABLE_SCHEMA",table_name as "TABLE_NAME", table_catalog as "DB_NAME" FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' AND table_schema in (${schema}) `
)).rows;
return response;
@ -36,7 +39,7 @@ export class PostgresDriver extends AbstractDriver {
entities: EntityInfo[],
schema: string
): Promise<EntityInfo[]> {
const response: Array<{
const response: {
table_name: string;
column_name: string;
udt_name: string;
@ -49,7 +52,7 @@ export class PostgresDriver extends AbstractDriver {
isidentity: string;
isunique: string;
enumvalues: string | null;
}> = (await this.Connection
}[] = (await this.Connection
.query(`SELECT table_name,column_name,udt_name,column_default,is_nullable,
data_type,character_maximum_length,numeric_precision,numeric_scale,
case when column_default LIKE 'nextval%' then 'YES' else 'NO' end isidentity,
@ -84,14 +87,16 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name
colInfo.options.unique = resp.isunique === "1";
colInfo.options.default = colInfo.options.generated
? null
: this.ReturnDefaultValueFunction(resp.column_default);
: PostgresDriver.ReturnDefaultValueFunction(
resp.column_default
);
const columnTypes = this.MatchColumnTypes(
resp.data_type,
resp.udt_name,
resp.enumvalues
);
if (!columnTypes.sql_type || !columnTypes.ts_type) {
if (!columnTypes.sqlType || !columnTypes.tsType) {
if (
resp.data_type === "USER-DEFINED" ||
resp.data_type === "ARRAY"
@ -106,14 +111,14 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name
}
return;
}
colInfo.options.type = columnTypes.sql_type as any;
colInfo.tsType = columnTypes.ts_type;
colInfo.options.array = columnTypes.is_array;
colInfo.options.type = columnTypes.sqlType as any;
colInfo.tsType = columnTypes.tsType;
colInfo.options.array = columnTypes.isArray;
colInfo.options.enum = columnTypes.enumValues;
if (colInfo.options.array) {
colInfo.tsType = colInfo.tsType
.split("|")
.map(x => x.replace("|", "").trim() + "[]")
.map(x => `${x.replace("|", "").trim()}[]`)
.join(" | ") as any;
}
@ -158,8 +163,8 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name
udtName: string,
enumValues: string | null
) {
const ret: {
ts_type:
let ret: {
tsType:
| "number"
| "string"
| "boolean"
@ -170,243 +175,241 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name
| "string | string[]"
| "any"
| null;
sql_type: string | null;
is_array: boolean;
sqlType: string | null;
isArray: boolean;
enumValues: string[];
} = { ts_type: null, sql_type: null, is_array: false, enumValues: [] };
ret.sql_type = dataType;
} = { tsType: null, sqlType: null, isArray: false, enumValues: [] };
ret.sqlType = dataType;
switch (dataType) {
case "int2":
ret.ts_type = "number";
ret.tsType = "number";
break;
case "int4":
ret.ts_type = "number";
ret.tsType = "number";
break;
case "int8":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "smallint":
ret.ts_type = "number";
ret.tsType = "number";
break;
case "integer":
ret.ts_type = "number";
ret.tsType = "number";
break;
case "bigint":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "decimal":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "numeric":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "real":
ret.ts_type = "number";
ret.tsType = "number";
break;
case "float":
ret.ts_type = "number";
ret.tsType = "number";
break;
case "float4":
ret.ts_type = "number";
ret.tsType = "number";
break;
case "float8":
ret.ts_type = "number";
ret.tsType = "number";
break;
case "double precision":
ret.ts_type = "number";
ret.tsType = "number";
break;
case "money":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "character varying":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "varchar":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "character":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "char":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "bpchar":
ret.sql_type = "char";
ret.ts_type = "string";
ret.sqlType = "char";
ret.tsType = "string";
break;
case "text":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "citext":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "hstore":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "bytea":
ret.ts_type = "Buffer";
ret.tsType = "Buffer";
break;
case "bit":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "varbit":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "bit varying":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "timetz":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "timestamptz":
ret.ts_type = "Date";
ret.tsType = "Date";
break;
case "timestamp":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "timestamp without time zone":
ret.ts_type = "Date";
ret.tsType = "Date";
break;
case "timestamp with time zone":
ret.ts_type = "Date";
ret.tsType = "Date";
break;
case "date":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "time":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "time without time zone":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "time with time zone":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "interval":
ret.ts_type = "any";
ret.tsType = "any";
break;
case "bool":
ret.ts_type = "boolean";
ret.tsType = "boolean";
break;
case "boolean":
ret.ts_type = "boolean";
ret.tsType = "boolean";
break;
case "point":
ret.ts_type = "string | object";
ret.tsType = "string | object";
break;
case "line":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "lseg":
ret.ts_type = "string | string[]";
ret.tsType = "string | string[]";
break;
case "box":
ret.ts_type = "string | object";
ret.tsType = "string | object";
break;
case "path":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "polygon":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "circle":
ret.ts_type = "string | object";
ret.tsType = "string | object";
break;
case "cidr":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "inet":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "macaddr":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "tsvector":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "tsquery":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "uuid":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "xml":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "json":
ret.ts_type = "object";
ret.tsType = "object";
break;
case "jsonb":
ret.ts_type = "object";
ret.tsType = "object";
break;
case "int4range":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "int8range":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "numrange":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "tsrange":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "tstzrange":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "daterange":
ret.ts_type = "string";
ret.tsType = "string";
break;
case "ARRAY":
const z = this.MatchColumnTypes(
ret = this.MatchColumnTypes(
udtName.substring(1),
udtName,
enumValues
);
ret.ts_type = z.ts_type;
ret.sql_type = z.sql_type;
ret.is_array = true;
ret.enumValues = z.enumValues;
ret.isArray = true;
break;
case "USER-DEFINED":
ret.ts_type = "string";
ret.tsType = "string";
switch (udtName) {
case "citext":
case "hstore":
case "geometry":
ret.sql_type = udtName;
ret.sqlType = udtName;
break;
default:
if (enumValues) {
ret.sql_type = "enum";
ret.enumValues = (('"' +
enumValues.split(",").join('","') +
'"') as never) as string[];
ret.sqlType = "enum";
ret.enumValues = (`"${enumValues
.split(",")
.join('","')}"` as never) as string[];
} else {
ret.ts_type = null;
ret.sql_type = null;
ret.tsType = null;
ret.sqlType = null;
}
break;
}
break;
default:
ret.ts_type = null;
ret.sql_type = null;
ret.tsType = null;
ret.sqlType = null;
break;
}
return ret;
}
public async GetIndexesFromEntity(
entities: EntityInfo[],
schema: string
): Promise<EntityInfo[]> {
const response: Array<{
const response: {
tablename: string;
indexname: string;
columnname: string;
is_unique: number;
is_primary_key: number;
}> = (await this.Connection.query(`SELECT
}[] = (await this.Connection.query(`SELECT
c.relname AS tablename,
i.relname as indexname,
f.attname AS columnname,
@ -461,11 +464,12 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name
return entities;
}
public async GetRelations(
entities: EntityInfo[],
schema: string
): Promise<EntityInfo[]> {
const response: Array<{
const response: {
tablewithforeignkey: string;
fk_partno: number;
foreignkeycolumn: string;
@ -475,7 +479,7 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name
onupdate: "RESTRICT" | "CASCADE" | "SET NULL" | "NO ACTION";
object_id: string;
// Distinct because of note in https://www.postgresql.org/docs/9.1/information-schema.html
}> = (await this.Connection.query(`SELECT DISTINCT
}[] = (await this.Connection.query(`SELECT DISTINCT
con.relname AS tablewithforeignkey,
att.attnum as fk_partno,
att2.attname AS foreignkeycolumn,
@ -515,20 +519,20 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name
AND att2.attnum = con.parent
AND rc.constraint_name= con.conname AND constraint_catalog=current_database() AND rc.constraint_schema=nspname
`)).rows;
const relationsTemp: IRelationTempInfo[] = [] as IRelationTempInfo[];
const relationsTemp: RelationTempInfo[] = [] as RelationTempInfo[];
response.forEach(resp => {
let rels = relationsTemp.find(
val => val.object_id === resp.object_id
val => val.objectId === resp.object_id
);
if (rels === undefined) {
rels = {} as IRelationTempInfo;
rels = {} as RelationTempInfo;
rels.ownerColumnsNames = [];
rels.referencedColumnsNames = [];
rels.actionOnDelete =
resp.ondelete === "NO ACTION" ? null : resp.ondelete;
rels.actionOnUpdate =
resp.onupdate === "NO ACTION" ? null : resp.onupdate;
rels.object_id = resp.object_id;
rels.objectId = resp.object_id;
rels.ownerTable = resp.tablewithforeignkey;
rels.referencedTable = resp.tablereferenced;
relationsTemp.push(rels);
@ -536,12 +540,13 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name
rels.ownerColumnsNames.push(resp.foreignkeycolumn);
rels.referencedColumnsNames.push(resp.foreignkeycolumnreferenced);
});
entities = this.GetRelationsFromRelationTempInfo(
const retVal = PostgresDriver.GetRelationsFromRelationTempInfo(
relationsTemp,
entities
);
return entities;
return retVal;
}
public async DisconnectFromServer() {
if (this.Connection) {
const promise = new Promise<boolean>((resolve, reject) => {
@ -569,6 +574,7 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name
password: connectionOptons.password,
port: connectionOptons.port,
ssl: connectionOptons.ssl,
// eslint-disable-next-line @typescript-eslint/camelcase
statement_timeout: connectionOptons.timeout,
user: connectionOptons.user
});
@ -594,26 +600,33 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name
public async CreateDB(dbName: string) {
await this.Connection.query(`CREATE DATABASE ${dbName}; `);
}
public async UseDB(dbName: string) {
await this.Connection.query(`USE ${dbName}; `);
}
public async DropDB(dbName: string) {
await this.Connection.query(`DROP DATABASE ${dbName}; `);
}
public async CheckIfDBExists(dbName: string): Promise<boolean> {
const resp = await this.Connection.query(
`SELECT datname FROM pg_database WHERE datname ='${dbName}' `
);
return resp.rowCount > 0;
}
private ReturnDefaultValueFunction(defVal: string | null): string | null {
if (!defVal) {
private static ReturnDefaultValueFunction(
defVal: string | null
): string | null {
let defaultValue = defVal;
if (!defaultValue) {
return null;
}
defVal = defVal.replace(/'::[\w ]*/, "'");
if (defVal.startsWith(`'`)) {
return `() => "${defVal}"`;
defaultValue = defaultValue.replace(/'::[\w ]*/, "'");
if (defaultValue.startsWith(`'`)) {
return `() => "${defaultValue}"`;
}
return `() => "${defVal}"`;
return `() => "${defaultValue}"`;
}
}

View File

@ -1,29 +1,36 @@
import { ConnectionOptions } from "typeorm";
import * as TypeormDriver from "typeorm/driver/sqlite/SqliteDriver";
import { DataTypeDefaults } from "typeorm/driver/types/DataTypeDefaults";
import { IConnectionOptions } from "../IConnectionOptions";
import { ColumnInfo } from "../models/ColumnInfo";
import { EntityInfo } from "../models/EntityInfo";
import { IndexColumnInfo } from "../models/IndexColumnInfo";
import { IndexInfo } from "../models/IndexInfo";
import { IRelationTempInfo } from "../models/RelationTempInfo";
import * as sqliteLib from "sqlite3";
import * as TomgUtils from "../Utils";
import { AbstractDriver } from "./AbstractDriver";
import AbstractDriver from "./AbstractDriver";
import EntityInfo from "../models/EntityInfo";
import ColumnInfo from "../models/ColumnInfo";
import IndexInfo from "../models/IndexInfo";
import IndexColumnInfo from "../models/IndexColumnInfo";
import RelationTempInfo from "../models/RelationTempInfo";
import IConnectionOptions from "../IConnectionOptions";
export class SqliteDriver extends AbstractDriver {
export default class SqliteDriver extends AbstractDriver {
public defaultValues: DataTypeDefaults = new TypeormDriver.SqliteDriver({
options: { database: "true" } as ConnectionOptions
} as any).dataTypeDefaults;
public readonly standardPort = 0;
public readonly standardUser = "";
public readonly standardSchema = "";
public sqlite = require("sqlite3").verbose();
public sqlite = sqliteLib.verbose();
public db: any;
public tablesWithGeneratedPrimaryKey: string[] = new Array<string>();
public GetAllTablesQuery: any;
public async GetAllTables(schema: string): Promise<EntityInfo[]> {
public async GetAllTables(): Promise<EntityInfo[]> {
const ret: EntityInfo[] = [] as EntityInfo[];
const rows = await this.ExecQuery<{ tbl_name: string; sql: string }>(
`SELECT tbl_name, sql FROM "sqlite_master" WHERE "type" = 'table' AND name NOT LIKE 'sqlite_%'`
@ -40,260 +47,279 @@ export class SqliteDriver extends AbstractDriver {
});
return ret;
}
public async GetCoulmnsFromEntity(
entities: EntityInfo[],
schema: string
entities: EntityInfo[]
): Promise<EntityInfo[]> {
for (const ent of entities) {
const response = await this.ExecQuery<{
cid: number;
name: string;
type: string;
notnull: number;
dflt_value: string;
pk: number;
}>(`PRAGMA table_info('${ent.tsEntityName}');`);
response.forEach(resp => {
const colInfo: ColumnInfo = new ColumnInfo();
colInfo.tsName = resp.name;
colInfo.options.name = resp.name;
colInfo.options.nullable = resp.notnull === 0;
colInfo.options.primary = resp.pk > 0;
colInfo.options.default = this.ReturnDefaultValueFunction(
resp.dflt_value
);
colInfo.options.type = resp.type
.replace(/\([0-9 ,]+\)/g, "")
.toLowerCase()
.trim() as any;
colInfo.options.generated =
colInfo.options.primary &&
this.tablesWithGeneratedPrimaryKey.includes(
ent.tsEntityName
);
switch (colInfo.options.type) {
case "int":
colInfo.tsType = "number";
break;
case "integer":
colInfo.tsType = "number";
break;
case "int2":
colInfo.tsType = "number";
break;
case "int8":
colInfo.tsType = "number";
break;
case "tinyint":
colInfo.tsType = "number";
break;
case "smallint":
colInfo.tsType = "number";
break;
case "mediumint":
colInfo.tsType = "number";
break;
case "bigint":
colInfo.tsType = "string";
break;
case "unsigned big int":
colInfo.tsType = "string";
break;
case "character":
colInfo.tsType = "string";
break;
case "varchar":
colInfo.tsType = "string";
break;
case "varying character":
colInfo.tsType = "string";
break;
case "nchar":
colInfo.tsType = "string";
break;
case "native character":
colInfo.tsType = "string";
break;
case "nvarchar":
colInfo.tsType = "string";
break;
case "text":
colInfo.tsType = "string";
break;
case "blob":
colInfo.tsType = "Buffer";
break;
case "clob":
colInfo.tsType = "string";
break;
case "real":
colInfo.tsType = "number";
break;
case "double":
colInfo.tsType = "number";
break;
case "double precision":
colInfo.tsType = "number";
break;
case "float":
colInfo.tsType = "number";
break;
case "numeric":
colInfo.tsType = "number";
break;
case "decimal":
colInfo.tsType = "number";
break;
case "boolean":
colInfo.tsType = "boolean";
break;
case "date":
colInfo.tsType = "string";
break;
case "datetime":
colInfo.tsType = "Date";
break;
default:
TomgUtils.LogError(
`Unknown column type: ${colInfo.options.type} table name: ${ent.tsEntityName} column name: ${resp.name}`
);
break;
}
const options = resp.type.match(/\([0-9 ,]+\)/g);
if (
this.ColumnTypesWithPrecision.some(
v => v === colInfo.options.type
) &&
options
) {
colInfo.options.precision = options[0]
.substring(1, options[0].length - 1)
.split(",")[0] as any;
colInfo.options.scale = options[0]
.substring(1, options[0].length - 1)
.split(",")[1] as any;
}
if (
this.ColumnTypesWithLength.some(
v => v === colInfo.options.type
) &&
options
) {
colInfo.options.length = options[0].substring(
1,
options[0].length - 1
) as any;
}
if (
this.ColumnTypesWithWidth.some(
v =>
v === colInfo.options.type &&
colInfo.tsType !== "boolean"
) &&
options
) {
colInfo.options.width = options[0].substring(
1,
options[0].length - 1
) as any;
}
if (colInfo.options.type) {
ent.Columns.push(colInfo);
}
});
}
return entities;
}
public async GetIndexesFromEntity(
entities: EntityInfo[],
schema: string
): Promise<EntityInfo[]> {
for (const ent of entities) {
const response = await this.ExecQuery<{
seq: number;
name: string;
unique: number;
origin: string;
partial: number;
}>(`PRAGMA index_list('${ent.tsEntityName}');`);
for (const resp of response) {
const indexColumnsResponse = await this.ExecQuery<{
seqno: number;
await Promise.all(
entities.map(async ent => {
const response = await this.ExecQuery<{
cid: number;
name: string;
}>(`PRAGMA index_info('${resp.name}');`);
indexColumnsResponse.forEach(element => {
let indexInfo: IndexInfo = {} as IndexInfo;
const indexColumnInfo: IndexColumnInfo = {} as IndexColumnInfo;
if (
ent.Indexes.filter(filterVal => {
return filterVal.name === resp.name;
}).length > 0
) {
indexInfo = ent.Indexes.find(
filterVal => filterVal.name === resp.name
)!;
} else {
indexInfo.columns = [] as IndexColumnInfo[];
indexInfo.name = resp.name;
indexInfo.isUnique = resp.unique === 1;
ent.Indexes.push(indexInfo);
type: string;
notnull: number;
dflt_value: string;
pk: number;
}>(`PRAGMA table_info('${ent.tsEntityName}');`);
response.forEach(resp => {
const colInfo: ColumnInfo = new ColumnInfo();
colInfo.tsName = resp.name;
colInfo.options.name = resp.name;
colInfo.options.nullable = resp.notnull === 0;
colInfo.options.primary = resp.pk > 0;
colInfo.options.default = SqliteDriver.ReturnDefaultValueFunction(
resp.dflt_value
);
colInfo.options.type = resp.type
.replace(/\([0-9 ,]+\)/g, "")
.toLowerCase()
.trim() as any;
colInfo.options.generated =
colInfo.options.primary &&
this.tablesWithGeneratedPrimaryKey.includes(
ent.tsEntityName
);
switch (colInfo.options.type) {
case "int":
colInfo.tsType = "number";
break;
case "integer":
colInfo.tsType = "number";
break;
case "int2":
colInfo.tsType = "number";
break;
case "int8":
colInfo.tsType = "number";
break;
case "tinyint":
colInfo.tsType = "number";
break;
case "smallint":
colInfo.tsType = "number";
break;
case "mediumint":
colInfo.tsType = "number";
break;
case "bigint":
colInfo.tsType = "string";
break;
case "unsigned big int":
colInfo.tsType = "string";
break;
case "character":
colInfo.tsType = "string";
break;
case "varchar":
colInfo.tsType = "string";
break;
case "varying character":
colInfo.tsType = "string";
break;
case "nchar":
colInfo.tsType = "string";
break;
case "native character":
colInfo.tsType = "string";
break;
case "nvarchar":
colInfo.tsType = "string";
break;
case "text":
colInfo.tsType = "string";
break;
case "blob":
colInfo.tsType = "Buffer";
break;
case "clob":
colInfo.tsType = "string";
break;
case "real":
colInfo.tsType = "number";
break;
case "double":
colInfo.tsType = "number";
break;
case "double precision":
colInfo.tsType = "number";
break;
case "float":
colInfo.tsType = "number";
break;
case "numeric":
colInfo.tsType = "number";
break;
case "decimal":
colInfo.tsType = "number";
break;
case "boolean":
colInfo.tsType = "boolean";
break;
case "date":
colInfo.tsType = "string";
break;
case "datetime":
colInfo.tsType = "Date";
break;
default:
TomgUtils.LogError(
`Unknown column type: ${colInfo.options.type} table name: ${ent.tsEntityName} column name: ${resp.name}`
);
break;
}
indexColumnInfo.name = element.name;
const options = resp.type.match(/\([0-9 ,]+\)/g);
if (
indexColumnsResponse.length === 1 &&
indexInfo.isUnique
this.ColumnTypesWithPrecision.some(
v => v === colInfo.options.type
) &&
options
) {
ent.Columns.filter(
v => v.tsName === indexColumnInfo.name
).map(v => (v.options.unique = true));
colInfo.options.precision = options[0]
.substring(1, options[0].length - 1)
.split(",")[0] as any;
colInfo.options.scale = options[0]
.substring(1, options[0].length - 1)
.split(",")[1] as any;
}
if (
this.ColumnTypesWithLength.some(
v => v === colInfo.options.type
) &&
options
) {
colInfo.options.length = options[0].substring(
1,
options[0].length - 1
) as any;
}
if (
this.ColumnTypesWithWidth.some(
v =>
v === colInfo.options.type &&
colInfo.tsType !== "boolean"
) &&
options
) {
colInfo.options.width = options[0].substring(
1,
options[0].length - 1
) as any;
}
if (colInfo.options.type) {
ent.Columns.push(colInfo);
}
indexInfo.columns.push(indexColumnInfo);
});
}
}
})
);
return entities;
}
public async GetRelations(
entities: EntityInfo[],
schema: string
public async GetIndexesFromEntity(
entities: EntityInfo[]
): Promise<EntityInfo[]> {
for (const entity of entities) {
const response = await this.ExecQuery<{
id: number;
seq: number;
table: string;
from: string;
to: string;
on_update: "RESTRICT" | "CASCADE" | "SET NULL" | "NO ACTION";
on_delete: "RESTRICT" | "CASCADE" | "SET NULL" | "NO ACTION";
match: string;
}>(`PRAGMA foreign_key_list('${entity.tsEntityName}');`);
const relationsTemp: IRelationTempInfo[] = [] as IRelationTempInfo[];
response.forEach(resp => {
const rels = {} as IRelationTempInfo;
rels.ownerColumnsNames = [];
rels.referencedColumnsNames = [];
rels.actionOnDelete =
resp.on_delete === "NO ACTION" ? null : resp.on_delete;
rels.actionOnUpdate =
resp.on_update === "NO ACTION" ? null : resp.on_update;
rels.ownerTable = entity.tsEntityName;
rels.referencedTable = resp.table;
relationsTemp.push(rels);
rels.ownerColumnsNames.push(resp.from);
rels.referencedColumnsNames.push(resp.to);
});
entities = this.GetRelationsFromRelationTempInfo(
relationsTemp,
entities
);
}
await Promise.all(
entities.map(async ent => {
const response = await this.ExecQuery<{
seq: number;
name: string;
unique: number;
origin: string;
partial: number;
}>(`PRAGMA index_list('${ent.tsEntityName}');`);
await Promise.all(
response.map(async resp => {
const indexColumnsResponse = await this.ExecQuery<{
seqno: number;
cid: number;
name: string;
}>(`PRAGMA index_info('${resp.name}');`);
indexColumnsResponse.forEach(element => {
let indexInfo: IndexInfo = {} as IndexInfo;
const indexColumnInfo: IndexColumnInfo = {} as IndexColumnInfo;
if (
ent.Indexes.filter(filterVal => {
return filterVal.name === resp.name;
}).length > 0
) {
indexInfo = ent.Indexes.find(
filterVal => filterVal.name === resp.name
)!;
} else {
indexInfo.columns = [] as IndexColumnInfo[];
indexInfo.name = resp.name;
indexInfo.isUnique = resp.unique === 1;
ent.Indexes.push(indexInfo);
}
indexColumnInfo.name = element.name;
if (
indexColumnsResponse.length === 1 &&
indexInfo.isUnique
) {
ent.Columns.filter(
v => v.tsName === indexColumnInfo.name
).forEach(v => {
// eslint-disable-next-line no-param-reassign
v.options.unique = true;
});
}
indexInfo.columns.push(indexColumnInfo);
});
})
);
})
);
return entities;
}
public async GetRelations(entities: EntityInfo[]): Promise<EntityInfo[]> {
let retVal = entities;
await Promise.all(
retVal.map(async entity => {
const response = await this.ExecQuery<{
id: number;
seq: number;
table: string;
from: string;
to: string;
on_update:
| "RESTRICT"
| "CASCADE"
| "SET NULL"
| "NO ACTION";
on_delete:
| "RESTRICT"
| "CASCADE"
| "SET NULL"
| "NO ACTION";
match: string;
}>(`PRAGMA foreign_key_list('${entity.tsEntityName}');`);
const relationsTemp: RelationTempInfo[] = [] as RelationTempInfo[];
response.forEach(resp => {
const rels = {} as RelationTempInfo;
rels.ownerColumnsNames = [];
rels.referencedColumnsNames = [];
rels.actionOnDelete =
resp.on_delete === "NO ACTION" ? null : resp.on_delete;
rels.actionOnUpdate =
resp.on_update === "NO ACTION" ? null : resp.on_update;
rels.ownerTable = entity.tsEntityName;
rels.referencedTable = resp.table;
relationsTemp.push(rels);
rels.ownerColumnsNames.push(resp.from);
rels.referencedColumnsNames.push(resp.to);
});
retVal = SqliteDriver.GetRelationsFromRelationTempInfo(
relationsTemp,
retVal
);
})
);
return retVal;
}
public async DisconnectFromServer() {
this.db.close();
}
@ -302,9 +328,11 @@ export class SqliteDriver extends AbstractDriver {
await this.UseDB(connectionOptons.databaseName);
}
public async CreateDB(dbName: string) {
// eslint-disable-next-line class-methods-use-this
public async CreateDB() {
// not supported
}
public async UseDB(dbName: string) {
const promise = new Promise<boolean>((resolve, reject) => {
this.db = new this.sqlite.Database(dbName, err => {
@ -322,10 +350,14 @@ export class SqliteDriver extends AbstractDriver {
});
return promise;
}
public async DropDB(dbName: string) {
// eslint-disable-next-line class-methods-use-this
public async DropDB() {
// not supported
}
public async CheckIfDBExists(dbName: string): Promise<boolean> {
// eslint-disable-next-line class-methods-use-this
public async CheckIfDBExists(): Promise<boolean> {
return true;
}
@ -351,7 +383,10 @@ export class SqliteDriver extends AbstractDriver {
await promise;
return ret;
}
private ReturnDefaultValueFunction(defVal: string | null): string | null {
private static ReturnDefaultValueFunction(
defVal: string | null
): string | null {
if (!defVal) {
return null;
}

View File

@ -1,13 +1,15 @@
import * as Yargs from "yargs";
import { createDriver, createModelFromDatabase } from "./Engine";
import * as TomgUtils from "./Utils";
import AbstractDriver from "./drivers/AbstractDriver";
import IConnectionOptions from "./IConnectionOptions";
import IGenerationOptions from "./IGenerationOptions";
import fs = require("fs-extra");
import inquirer = require("inquirer");
import path = require("path");
import * as Yargs from "yargs";
import { AbstractDriver } from "./drivers/AbstractDriver";
import { createDriver, createModelFromDatabase } from "./Engine";
import { IConnectionOptions } from "./IConnectionOptions";
import { IGenerationOptions } from "./IGenerationOptions";
import * as TomgUtils from "./Utils";
// eslint-disable-next-line @typescript-eslint/no-floating-promises
CliLogic();
async function CliLogic() {
@ -20,41 +22,35 @@ async function CliLogic() {
connectionOptions = retVal.connectionOptions;
generationOptions = retVal.generationOptions;
driver = retVal.driver;
} else if (fs.existsSync(path.resolve(process.cwd(), ".tomg-config"))) {
console.log(
`[${new Date().toLocaleTimeString()}] Using configuration file. [${path.resolve(
process.cwd(),
".tomg-config"
)}]`
);
const retVal = await fs.readJson(
path.resolve(process.cwd(), ".tomg-config")
);
[connectionOptions, generationOptions] = retVal;
driver = createDriver(connectionOptions.databaseType);
} else {
if (fs.existsSync(path.resolve(process.cwd(), ".tomg-config"))) {
console.log(
`[${new Date().toLocaleTimeString()}] Using configuration file. [${path.resolve(
process.cwd(),
".tomg-config"
)}]`
);
const retVal = await fs.readJson(
path.resolve(process.cwd(), ".tomg-config")
);
connectionOptions = retVal[0];
generationOptions = retVal[1];
driver = createDriver(connectionOptions.databaseType);
} else {
const retVal = await GetUtilParametersByInquirer();
driver = retVal.driver;
connectionOptions = retVal.connectionOptions;
generationOptions = retVal.generationOptions;
}
const retVal = await GetUtilParametersByInquirer();
driver = retVal.driver;
connectionOptions = retVal.connectionOptions;
generationOptions = retVal.generationOptions;
}
console.log(
`[${new Date().toLocaleTimeString()}] Starting creation of model classes.`
);
createModelFromDatabase(driver, connectionOptions, generationOptions).then(
() => {
console.info(
`[${new Date().toLocaleTimeString()}] Typeorm model classes created.`
);
}
await createModelFromDatabase(driver, connectionOptions, generationOptions);
console.info(
`[${new Date().toLocaleTimeString()}] Typeorm model classes created.`
);
}
function GetUtilParametersByArgs() {
const argv = Yargs.usage(
const { argv } = Yargs.usage(
"Usage: typeorm-model-generator -h <host> -d <database> -p [port] -u <user> -x [password] -e [engine]\nYou can also run program without specyfiying any parameters."
)
.option("h", {
@ -165,43 +161,40 @@ function GetUtilParametersByArgs() {
.option("timeout", {
describe: "SQL Query timeout(ms)",
number: true
}).argv;
});
const driver = createDriver(argv.e);
const standardPort = driver.standardPort;
const standardSchema = driver.standardSchema;
const { standardPort } = driver;
const { standardSchema } = driver;
const standardUser = driver.standardPort;
let namingStrategyPath: string;
if (argv.namingStrategy && argv.namingStrategy !== "") {
// tslint:disable-next-line:no-var-requires
namingStrategyPath = argv.namingStrategy;
} else {
namingStrategyPath = "";
}
const connectionOptions: IConnectionOptions = new IConnectionOptions();
(connectionOptions.databaseName = argv.d ? argv.d.toString() : null),
(connectionOptions.databaseType = argv.e),
(connectionOptions.host = argv.h),
(connectionOptions.password = argv.x ? argv.x.toString() : null),
(connectionOptions.port = parseInt(argv.p, 10) || standardPort),
(connectionOptions.schemaName = argv.s
? argv.s.toString()
: standardSchema),
(connectionOptions.ssl = argv.ssl),
(connectionOptions.timeout = argv.timeout),
(connectionOptions.user = argv.u ? argv.u.toString() : standardUser);
connectionOptions.databaseName = argv.d ? argv.d.toString() : null;
connectionOptions.databaseType = argv.e;
connectionOptions.host = argv.h;
connectionOptions.password = argv.x ? argv.x.toString() : null;
connectionOptions.port = parseInt(argv.p, 10) || standardPort;
connectionOptions.schemaName = argv.s ? argv.s.toString() : standardSchema;
connectionOptions.ssl = argv.ssl;
connectionOptions.timeout = argv.timeout;
connectionOptions.user = argv.u ? argv.u.toString() : standardUser;
const generationOptions: IGenerationOptions = new IGenerationOptions();
(generationOptions.activeRecord = argv.a),
(generationOptions.generateConstructor = argv.generateConstructor),
(generationOptions.convertCaseEntity = argv.ce),
(generationOptions.convertCaseFile = argv.cf),
(generationOptions.convertCaseProperty = argv.cp),
(generationOptions.lazy = argv.lazy),
(generationOptions.customNamingStrategyPath = namingStrategyPath),
(generationOptions.noConfigs = argv.noConfig),
(generationOptions.propertyVisibility = argv.pv),
(generationOptions.relationIds = argv.relationIds),
(generationOptions.resultsPath = argv.o ? argv.o.toString() : null);
generationOptions.activeRecord = argv.a;
generationOptions.generateConstructor = argv.generateConstructor;
generationOptions.convertCaseEntity = argv.ce;
generationOptions.convertCaseFile = argv.cf;
generationOptions.convertCaseProperty = argv.cp;
generationOptions.lazy = argv.lazy;
generationOptions.customNamingStrategyPath = namingStrategyPath;
generationOptions.noConfigs = argv.noConfig;
generationOptions.propertyVisibility = argv.pv;
generationOptions.relationIds = argv.relationIds;
generationOptions.resultsPath = argv.o ? argv.o.toString() : null;
return { driver, connectionOptions, generationOptions };
}
@ -238,11 +231,11 @@ async function GetUtilParametersByInquirer() {
message: "Database port:",
name: "port",
type: "input",
default(answers: any) {
default() {
return driver.standardPort;
},
validate(value) {
const valid = !isNaN(parseInt(value, 10));
const valid = !Number.isNaN(parseInt(value, 10));
return valid || "Please enter a valid port number";
}
},
@ -256,7 +249,7 @@ async function GetUtilParametersByInquirer() {
message: "Database user name:",
name: "login",
type: "input",
default(answers: any) {
default() {
return driver.standardUser;
}
},
@ -316,35 +309,35 @@ async function GetUtilParametersByInquirer() {
connectionOptions.databaseType === "mssql" ||
connectionOptions.databaseType === "postgres"
) {
const changeRequestTimeout = ((await inquirer.prompt([
const { changeRequestTimeout } = (await inquirer.prompt([
{
default: false,
message: "Do you want to change default sql query timeout?",
name: "changeRequestTimeout",
type: "confirm"
}
])) as any).changeRequestTimeout;
])) as any;
if (changeRequestTimeout) {
const timeout: any = ((await inquirer.prompt({
const { timeout } = (await inquirer.prompt({
message: "Query timeout(ms):",
name: "timeout",
type: "input",
validate(value) {
const valid = !isNaN(parseInt(value, 10));
const valid = !Number.isNaN(parseInt(value, 10));
return valid || "Please enter a valid number";
}
})) as any).timeout;
})) as any;
connectionOptions.timeout = timeout;
}
}
const customizeGeneration = ((await inquirer.prompt([
const { customizeGeneration } = (await inquirer.prompt([
{
default: false,
message: "Do you want to customize generated model?",
name: "customizeGeneration",
type: "confirm"
}
])) as any).customizeGeneration;
])) as any;
if (customizeGeneration) {
const customizations: string[] = ((await inquirer.prompt([
{
@ -413,7 +406,6 @@ async function GetUtilParametersByInquirer() {
])) as any).namingStrategy;
if (namingStrategyPath && namingStrategyPath !== "") {
// tslint:disable-next-line:no-var-requires
generationOptions.customNamingStrategyPath = namingStrategyPath;
} else {
generationOptions.customNamingStrategyPath = "";
@ -449,14 +441,14 @@ async function GetUtilParametersByInquirer() {
generationOptions.convertCaseEntity = namingConventions.entityCase;
}
}
const saveConfig = ((await inquirer.prompt([
const { saveConfig } = (await inquirer.prompt([
{
default: false,
message: "Save configuration to config file?",
name: "saveConfig",
type: "confirm"
}
])) as any).saveConfig;
])) as any;
if (saveConfig) {
await fs.writeJson(
path.resolve(process.cwd(), ".tomg-config"),

View File

@ -1,9 +1,11 @@
import { ColumnOptions } from "typeorm";
import { RelationInfo } from "./RelationInfo";
import RelationInfo from "./RelationInfo";
export class ColumnInfo {
export default class ColumnInfo {
public options: ColumnOptions = {};
public tsName: string = "";
public tsType:
| "number"
| "string"
@ -14,5 +16,6 @@ export class ColumnInfo {
| "string | object"
| "string | string[]"
| "any";
public relations: RelationInfo[] = [];
}

View File

@ -1,16 +1,25 @@
import { ColumnInfo } from "./ColumnInfo";
import { IndexInfo } from "./IndexInfo";
import ColumnInfo from "./ColumnInfo";
import IndexInfo from "./IndexInfo";
export class EntityInfo {
export default class EntityInfo {
public tsEntityName: string;
public sqlEntityName: string;
public Columns: ColumnInfo[];
public Imports: string[];
public UniqueImports: string[];
public Indexes: IndexInfo[];
public Schema: string;
public GenerateConstructor: boolean;
public IsActiveRecord: boolean;
public Database: string;
public relationImports() {

View File

@ -1,3 +1,3 @@
export interface IndexColumnInfo {
export default interface IndexColumnInfo {
name: string;
}

View File

@ -1,6 +1,6 @@
import { IndexColumnInfo } from "./IndexColumnInfo";
import IndexColumnInfo from "./IndexColumnInfo";
export interface IndexInfo {
export default interface IndexInfo {
name: string;
columns: IndexColumnInfo[];
isUnique: boolean;

View File

@ -1,10 +1,16 @@
export class RelationInfo {
export default class RelationInfo {
public isOwner: boolean;
public relationType: "OneToOne" | "OneToMany" | "ManyToOne" | "ManyToMany";
public relatedTable: string;
public relatedColumn: string;
public ownerTable: string;
public ownerColumn: string;
public actionOnDelete:
| "RESTRICT"
| "CASCADE"
@ -12,24 +18,29 @@ export class RelationInfo {
| "DEFAULT"
| "NO ACTION"
| null;
public actionOnUpdate:
| "RESTRICT"
| "CASCADE"
| "SET NULL"
| "DEFAULT"
| null;
public relationIdField: boolean = false;
get isOneToMany(): boolean {
public get isOneToMany(): boolean {
return this.relationType === "OneToMany";
}
get isManyToMany(): boolean {
public get isManyToMany(): boolean {
return this.relationType === "ManyToMany";
}
get isOneToOne(): boolean {
public get isOneToOne(): boolean {
return this.relationType === "OneToOne";
}
get isManyToOne(): boolean {
public get isManyToOne(): boolean {
return this.relationType === "ManyToOne";
}
}

View File

@ -1,4 +1,4 @@
export interface IRelationTempInfo {
export default interface RelationTempInfo {
ownerTable: string;
ownerColumnsNames: string[];
referencedTable: string;
@ -11,5 +11,5 @@ export interface IRelationTempInfo {
| "NO ACTION"
| null;
actionOnUpdate: "RESTRICT" | "CASCADE" | "SET NULL" | "DEFAULT" | null;
object_id: number | string;
objectId: number | string;
}

View File

@ -1,11 +0,0 @@
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended", "tslint-config-prettier"
],
"jsRules": {},
"rules": {
"no-console":false
},
"rulesDirectory": []
}

View File

@ -1,105 +1,112 @@
import { expect } from "chai";
import * as MSSQL from 'mssql'
import * as MSSQL from "mssql";
import { IColumnMetadata, Table } from "mssql";
import * as Sinon from 'sinon'
import { MssqlDriver } from '../../src/drivers/MssqlDriver'
import { ColumnInfo } from '../../src/models/ColumnInfo'
import { EntityInfo } from '../../src/models/EntityInfo'
import { RelationInfo } from '../../src/models/RelationInfo'
import { NamingStrategy } from "../../src/NamingStrategy";
import { IndexInfo } from "../../src/models/IndexInfo";
import * as Sinon from "sinon";
import MssqlDriver from "../../src/drivers/MssqlDriver";
import EntityInfo from "../../src/models/EntityInfo";
import ColumnInfo from "../../src/models/ColumnInfo";
import IndexInfo from "../../src/models/IndexInfo";
import RelationInfo from "../../src/models/RelationInfo";
class fakeResponse implements MSSQL.IResult<any> {
class fakeResponse implements MSSQL.IResult<any> {
public recordsets: Array<MSSQL.IRecordSet<any>>;
public recordset: MSSQL.IRecordSet<any>;
public rowsAffected: number[];
public output: { [key: string]: any; };
public output: { [key: string]: any };
}
class fakeRecordset extends Array<any> implements MSSQL.IRecordSet<any>{
class fakeRecordset extends Array<any> implements MSSQL.IRecordSet<any> {
public columns: IColumnMetadata;
public toTable(): Table {
return new Table();
}
}
describe('MssqlDriver', function () {
let driver: MssqlDriver
const sandbox = Sinon.sandbox.create()
describe("MssqlDriver", function() {
let driver: MssqlDriver;
const sandbox = Sinon.sandbox.create();
beforeEach(() => {
driver = new MssqlDriver();
})
});
afterEach(() => {
sandbox.restore()
})
sandbox.restore();
});
it('should get tables info', async () => {
sandbox.stub(MSSQL, 'Request')
.returns(
{
query: (q) => {
const response = new fakeResponse();
response.recordset = new fakeRecordset();
response.recordset.push({ TABLE_SCHEMA: 'schema', TABLE_NAME: 'name' })
return response;
}
})
const result = await driver.GetAllTables('schema', 'db')
it("should get tables info", async () => {
sandbox.stub(MSSQL, "Request").returns({
query: q => {
const response = new fakeResponse();
response.recordset = new fakeRecordset();
response.recordset.push({
TABLE_SCHEMA: "schema",
TABLE_NAME: "name"
});
return response;
}
});
const result = await driver.GetAllTables("schema", "db");
const expectedResult = [] as EntityInfo[];
const y = new EntityInfo();
y.tsEntityName = 'name'
y.sqlEntityName = 'name'
y.Schema='schema'
y.tsEntityName = "name";
y.sqlEntityName = "name";
y.Schema = "schema";
y.Columns = [] as ColumnInfo[];
y.Indexes = [] as IndexInfo[];
y.Database = "";
expectedResult.push(y)
expect(result).to.be.deep.equal(expectedResult)
})
it('should get columns info', async () => {
sandbox.stub(MSSQL, 'Request')
.returns(
{
query: (q) => {
const response = new fakeResponse();
response.recordset = new fakeRecordset();
response.recordset.push({
TABLE_NAME: 'name', CHARACTER_MAXIMUM_LENGTH: 0,
COLUMN_DEFAULT: "'a'", COLUMN_NAME: 'name', DATA_TYPE: 'int',
IS_NULLABLE: 'YES', NUMERIC_PRECISION: 0, NUMERIC_SCALE: 0,
IsIdentity: 1
})
return response;
}
})
expectedResult.push(y);
expect(result).to.be.deep.equal(expectedResult);
});
it("should get columns info", async () => {
sandbox.stub(MSSQL, "Request").returns({
query: q => {
const response = new fakeResponse();
response.recordset = new fakeRecordset();
response.recordset.push({
TABLE_NAME: "name",
CHARACTER_MAXIMUM_LENGTH: 0,
COLUMN_DEFAULT: "'a'",
COLUMN_NAME: "name",
DATA_TYPE: "int",
IS_NULLABLE: "YES",
NUMERIC_PRECISION: 0,
NUMERIC_SCALE: 0,
IsIdentity: 1
});
return response;
}
});
const entities = [] as EntityInfo[];
const y = new EntityInfo();
y.tsEntityName = 'name'
y.tsEntityName = "name";
y.Columns = [] as ColumnInfo[];
y.Indexes = [] as IndexInfo[];
y.Database = "";
entities.push(y)
entities.push(y);
const expected: EntityInfo[] = JSON.parse(JSON.stringify(entities));
expected[0].Columns.push({
options: {
default: `() => "'a'"`,
nullable: true,
generated: true,
name: 'name',
unique:false,
type: 'int',
name: "name",
unique: false,
type: "int"
},
tsName: 'name',
tsType: 'number',
relations: [] as RelationInfo[],
})
const result = await driver.GetCoulmnsFromEntity(entities, 'schema','db');
expect(result).to.be.deep.equal(expected)
})
it('should find primary indexes')
it('should get indexes info')
it('should get relations info')
})
tsName: "name",
tsType: "number",
relations: [] as RelationInfo[]
});
const result = await driver.GetCoulmnsFromEntity(
entities,
"schema",
"db"
);
expect(result).to.be.deep.equal(expected);
});
it("should find primary indexes");
it("should get indexes info");
it("should get relations info");
});

View File

@ -1,53 +1,62 @@
require('dotenv').config()
require("dotenv").config();
import "reflect-metadata";
import { expect } from "chai";
import fs = require('fs-extra');
import path = require('path');
import fs = require("fs-extra");
import path = require("path");
import { EntityFileToJson } from "../utils/EntityFileToJson";
import { createDriver, createModelFromDatabase, dataCollectionPhase, modelCustomizationPhase, modelGenerationPhase } from "../../src/Engine";
import {
createDriver,
createModelFromDatabase,
dataCollectionPhase,
modelCustomizationPhase,
modelGenerationPhase
} from "../../src/Engine";
import * as ts from "typescript";
import * as GTU from "../utils/GeneralTestUtils"
import chaiSubset = require('chai-subset');
import chai = require('chai');
import { IConnectionOptions } from "../../src/IConnectionOptions";
import * as GTU from "../utils/GeneralTestUtils";
import chaiSubset = require("chai-subset");
import chai = require("chai");
import yn = require("yn");
import { EntityInfo } from "../../src/models/EntityInfo";
import EntityInfo from "../../src/models/EntityInfo";
import IConnectionOptions from "../../src/IConnectionOptions";
chai.use(chaiSubset);
it("Column default values", async function () {
const testPartialPath = 'test/integration/defaultValues'
this.timeout(60000)
this.slow(10000)// compiling created models takes time
it("Column default values", async function() {
const testPartialPath = "test/integration/defaultValues";
this.timeout(60000);
this.slow(10000); // compiling created models takes time
await runTestsFromPath(testPartialPath, true);
})
it("Platform specyfic types", async function () {
this.timeout(60000)
this.slow(10000)// compiling created models takes time
const testPartialPath = 'test/integration/entityTypes'
});
it("Platform specyfic types", async function() {
this.timeout(60000);
this.slow(10000); // compiling created models takes time
const testPartialPath = "test/integration/entityTypes";
await runTestsFromPath(testPartialPath, true);
})
describe("GitHub issues", async function () {
this.timeout(60000)
this.slow(10000)// compiling created models takes time
const testPartialPath = 'test/integration/github-issues'
});
describe("GitHub issues", async function() {
this.timeout(60000);
this.slow(10000); // compiling created models takes time
const testPartialPath = "test/integration/github-issues";
runTestsFromPath(testPartialPath, false);
})
describe("TypeOrm examples", async function () {
this.timeout(60000)
this.slow(10000)// compiling created models takes time
const testPartialPath = 'test/integration/examples'
});
describe("TypeOrm examples", async function() {
this.timeout(60000);
this.slow(10000); // compiling created models takes time
const testPartialPath = "test/integration/examples";
runTestsFromPath(testPartialPath, false);
})
});
export async function runTestsFromPath(testPartialPath: string, isDbSpecific: boolean) {
const resultsPath = path.resolve(process.cwd(), `output`)
export async function runTestsFromPath(
testPartialPath: string,
isDbSpecific: boolean
) {
const resultsPath = path.resolve(process.cwd(), `output`);
if (!fs.existsSync(resultsPath)) {
fs.mkdirSync(resultsPath);
}
const dbDrivers: string[] = GTU.getEnabledDbDrivers();
for (const dbDriver of dbDrivers) {
const newDirPath = path.resolve(resultsPath, dbDriver)
const newDirPath = path.resolve(resultsPath, dbDriver);
if (!fs.existsSync(newDirPath)) {
fs.mkdirSync(newDirPath);
}
@ -61,69 +70,138 @@ export async function runTestsFromPath(testPartialPath: string, isDbSpecific: bo
}
}
}
function runTestForMultipleDrivers(testName: string, dbDrivers: string[], testPartialPath: string) {
it(testName, async function () {
function runTestForMultipleDrivers(
testName: string,
dbDrivers: string[],
testPartialPath: string
) {
it(testName, async function() {
const driversToRun = selectDriversForSpecyficTest();
const modelGenerationPromises = driversToRun.map(async (dbDriver) => {
const { generationOptions, driver, connectionOptions, resultsPath, filesOrgPathTS } = await prepareTestRuns(testPartialPath, testName, dbDriver);
const modelGenerationPromises = driversToRun.map(async dbDriver => {
const {
generationOptions,
driver,
connectionOptions,
resultsPath,
filesOrgPathTS
} = await prepareTestRuns(testPartialPath, testName, dbDriver);
let dbModel: EntityInfo[] = [];
switch (testName) {
case '144':
dbModel = await dataCollectionPhase(driver, Object.assign(connectionOptions, { databaseName: 'db1,db2' }));
case "144":
dbModel = await dataCollectionPhase(
driver,
Object.assign(connectionOptions, {
databaseName: "db1,db2"
})
);
break;
default:
dbModel = await dataCollectionPhase(driver, connectionOptions);
dbModel = await dataCollectionPhase(
driver,
connectionOptions
);
break;
}
dbModel = modelCustomizationPhase(dbModel, generationOptions, driver.defaultValues);
dbModel = modelCustomizationPhase(
dbModel,
generationOptions,
driver.defaultValues
);
modelGenerationPhase(connectionOptions, generationOptions, dbModel);
const filesGenPath = path.resolve(resultsPath, 'entities');
const filesGenPath = path.resolve(resultsPath, "entities");
compareGeneratedFiles(filesOrgPathTS, filesGenPath);
return { dbModel, generationOptions, connectionOptions, resultsPath, filesOrgPathTS, dbDriver };
})
await Promise.all(modelGenerationPromises)
return {
dbModel,
generationOptions,
connectionOptions,
resultsPath,
filesOrgPathTS,
dbDriver
};
});
await Promise.all(modelGenerationPromises);
compileGeneratedModel(path.resolve(process.cwd(), `output`), dbDrivers);
});
function selectDriversForSpecyficTest() {
switch (testName) {
case '39':
return dbDrivers.filter(dbDriver => !['mysql', 'mariadb', 'oracle', 'sqlite'].includes(dbDriver))
case '144':
return dbDrivers.filter(dbDriver => ['mysql', 'mariadb'].includes(dbDriver))
case "39":
return dbDrivers.filter(
dbDriver =>
!["mysql", "mariadb", "oracle", "sqlite"].includes(
dbDriver
)
);
case "144":
return dbDrivers.filter(dbDriver =>
["mysql", "mariadb"].includes(dbDriver)
);
default:
return dbDrivers;
}
}
}
async function runTest(dbDrivers: string[], testPartialPath: string, files: string[]) {
const modelGenerationPromises = dbDrivers.filter(driver => files.includes(driver))
async function runTest(
dbDrivers: string[],
testPartialPath: string,
files: string[]
) {
const modelGenerationPromises = dbDrivers
.filter(driver => files.includes(driver))
.map(async dbDriver => {
const { generationOptions, driver, connectionOptions, resultsPath, filesOrgPathTS } = await prepareTestRuns(testPartialPath, dbDriver, dbDriver);
const {
generationOptions,
driver,
connectionOptions,
resultsPath,
filesOrgPathTS
} = await prepareTestRuns(testPartialPath, dbDriver, dbDriver);
let dbModel = await dataCollectionPhase(driver, connectionOptions);
dbModel = modelCustomizationPhase(dbModel, generationOptions, driver.defaultValues);
dbModel = modelCustomizationPhase(
dbModel,
generationOptions,
driver.defaultValues
);
modelGenerationPhase(connectionOptions, generationOptions, dbModel);
const filesGenPath = path.resolve(resultsPath, 'entities');
const filesGenPath = path.resolve(resultsPath, "entities");
compareGeneratedFiles(filesOrgPathTS, filesGenPath);
return { dbModel, generationOptions, connectionOptions, resultsPath, filesOrgPathTS, dbDriver };
})
await Promise.all(modelGenerationPromises)
return {
dbModel,
generationOptions,
connectionOptions,
resultsPath,
filesOrgPathTS,
dbDriver
};
});
await Promise.all(modelGenerationPromises);
compileGeneratedModel(path.resolve(process.cwd(), `output`), dbDrivers);
}
function compareGeneratedFiles(filesOrgPathTS: string, filesGenPath: string) {
const filesOrg = fs.readdirSync(filesOrgPathTS).filter((val) => val.toString().endsWith('.ts'));
const filesGen = fs.readdirSync(filesGenPath).filter((val) => val.toString().endsWith('.ts'));
expect(filesOrg, 'Errors detected in model comparision').to.be.deep.equal(filesGen);
const filesOrg = fs
.readdirSync(filesOrgPathTS)
.filter(val => val.toString().endsWith(".ts"));
const filesGen = fs
.readdirSync(filesGenPath)
.filter(val => val.toString().endsWith(".ts"));
expect(filesOrg, "Errors detected in model comparision").to.be.deep.equal(
filesGen
);
for (const file of filesOrg) {
const entftj = new EntityFileToJson();
const jsonEntityOrg = entftj.convert(fs.readFileSync(path.resolve(filesOrgPathTS, file)));
const jsonEntityGen = entftj.convert(fs.readFileSync(path.resolve(filesGenPath, file)));
expect(jsonEntityGen, `Error in file ${file}`).to.containSubset(jsonEntityOrg);
const jsonEntityOrg = entftj.convert(
fs.readFileSync(path.resolve(filesOrgPathTS, file))
);
const jsonEntityGen = entftj.convert(
fs.readFileSync(path.resolve(filesGenPath, file))
);
expect(jsonEntityGen, `Error in file ${file}`).to.containSubset(
jsonEntityOrg
);
}
}
@ -131,9 +209,17 @@ function compileGeneratedModel(filesGenPath: string, drivers: string[]) {
let currentDirectoryFiles: string[] = [];
drivers.forEach(driver => {
const entitiesPath = path.resolve(filesGenPath, driver, "entities");
if (fs.existsSync(entitiesPath)){
currentDirectoryFiles.push(...fs.readdirSync(entitiesPath).
filter(fileName => fileName.length >= 3 && fileName.substr(fileName.length - 3, 3) === ".ts").map(v => path.resolve(filesGenPath, driver, "entities", v)));
if (fs.existsSync(entitiesPath)) {
currentDirectoryFiles.push(
...fs
.readdirSync(entitiesPath)
.filter(
fileName =>
fileName.length >= 3 &&
fileName.substr(fileName.length - 3, 3) === ".ts"
)
.map(v => path.resolve(filesGenPath, driver, "entities", v))
);
}
});
const compileErrors = GTU.compileTsFiles(currentDirectoryFiles, {
@ -144,50 +230,64 @@ function compileGeneratedModel(filesGenPath: string, drivers: string[]) {
moduleResolution: ts.ModuleResolutionKind.NodeJs,
module: ts.ModuleKind.CommonJS
});
expect(compileErrors, 'Errors detected while compiling generated model').to.be.false;
expect(compileErrors, "Errors detected while compiling generated model").to
.be.false;
}
async function prepareTestRuns(testPartialPath: string, testName: string, dbDriver: string) {
const filesOrgPathJS = path.resolve(process.cwd(), testPartialPath, testName, 'entity');
const filesOrgPathTS = path.resolve(process.cwd(), testPartialPath, testName, 'entity');
async function prepareTestRuns(
testPartialPath: string,
testName: string,
dbDriver: string
) {
const filesOrgPathJS = path.resolve(
process.cwd(),
testPartialPath,
testName,
"entity"
);
const filesOrgPathTS = path.resolve(
process.cwd(),
testPartialPath,
testName,
"entity"
);
const resultsPath = path.resolve(process.cwd(), `output`, dbDriver);
fs.removeSync(resultsPath);
const driver = createDriver(dbDriver);
const generationOptions = GTU.getGenerationOptions(resultsPath);
switch (testName) {
case '65':
case "65":
generationOptions.relationIds = true;
break;
case 'sample18-lazy-relations':
case "sample18-lazy-relations":
generationOptions.lazy = true;
break;
case '144':
case "144":
let connectionOptions: IConnectionOptions;
switch (dbDriver) {
case 'mysql':
case "mysql":
connectionOptions = {
host: String(process.env.MYSQL_Host),
port: Number(process.env.MYSQL_Port),
databaseName: String(process.env.MYSQL_Database),
user: String(process.env.MYSQL_Username),
password: String(process.env.MYSQL_Password),
databaseType: 'mysql',
schemaName: 'ignored',
ssl: yn(process.env.MYSQL_SSL),
}
databaseType: "mysql",
schemaName: "ignored",
ssl: yn(process.env.MYSQL_SSL)
};
break;
case 'mariadb':
case "mariadb":
connectionOptions = {
host: String(process.env.MARIADB_Host),
port: Number(process.env.MARIADB_Port),
databaseName: String(process.env.MARIADB_Database),
user: String(process.env.MARIADB_Username),
password: String(process.env.MARIADB_Password),
databaseType: 'mariadb',
schemaName: 'ignored',
ssl: yn(process.env.MARIADB_SSL),
}
databaseType: "mariadb",
schemaName: "ignored",
ssl: yn(process.env.MARIADB_SSL)
};
break;
default:
@ -195,18 +295,26 @@ async function prepareTestRuns(testPartialPath: string, testName: string, dbDriv
}
await driver.ConnectToServer(connectionOptions!);
if (! await driver.CheckIfDBExists('db1')) {
var x = await driver.CreateDB('db1')
if (!(await driver.CheckIfDBExists("db1"))) {
var x = await driver.CreateDB("db1");
}
if (! await driver.CheckIfDBExists('db2')) {
var t = await driver.CreateDB('db2')
if (!(await driver.CheckIfDBExists("db2"))) {
var t = await driver.CreateDB("db2");
}
await driver.DisconnectFromServer();
break;
default:
break;
}
const connectionOptions = await GTU.createModelsInDb(dbDriver, filesOrgPathJS);
return { generationOptions, driver, connectionOptions, resultsPath, filesOrgPathTS };
const connectionOptions = await GTU.createModelsInDb(
dbDriver,
filesOrgPathJS
);
return {
generationOptions,
driver,
connectionOptions,
resultsPath,
filesOrgPathTS
};
}

View File

@ -1,35 +1,36 @@
import path = require('path')
import path = require("path");
import { ConnectionOptions, createConnection } from "typeorm";
import * as ts from "typescript";
import * as yn from "yn"
import { AbstractDriver } from "../../src/drivers/AbstractDriver";
import { MariaDbDriver } from "../../src/drivers/MariaDbDriver";
import { MssqlDriver } from "../../src/drivers/MssqlDriver";
import { MysqlDriver } from "../../src/drivers/MysqlDriver";
import { OracleDriver } from "../../src/drivers/OracleDriver";
import { PostgresDriver } from "../../src/drivers/PostgresDriver";
import { SqliteDriver } from "../../src/drivers/SqliteDriver";
import { IConnectionOptions } from "../../src/IConnectionOptions";
import { IGenerationOptions } from "../../src/IGenerationOptions";
import * as yn from "yn";
import IGenerationOptions from "../../src/IGenerationOptions";
import IConnectionOptions from "../../src/IConnectionOptions";
import AbstractDriver from "../../src/drivers/AbstractDriver";
import MssqlDriver from "../../src/drivers/MssqlDriver";
import MariaDbDriver from "../../src/drivers/MariaDbDriver";
import PostgresDriver from "../../src/drivers/PostgresDriver";
import SqliteDriver from "../../src/drivers/SqliteDriver";
import OracleDriver from "../../src/drivers/OracleDriver";
import MysqlDriver from "../../src/drivers/MysqlDriver";
export function getGenerationOptions(resultsPath: string): IGenerationOptions {
return {
resultsPath: resultsPath,
noConfigs: false,
convertCaseEntity: 'none',
convertCaseFile: 'none',
convertCaseProperty: 'none',
propertyVisibility: 'none',
convertCaseEntity: "none",
convertCaseFile: "none",
convertCaseProperty: "none",
propertyVisibility: "none",
lazy: false,
generateConstructor: false,
customNamingStrategyPath: "",
relationIds: false,
activeRecord: false
}
};
}
export async function createMSSQLModels(filesOrgPath: string): Promise<IConnectionOptions> {
export async function createMSSQLModels(
filesOrgPath: string
): Promise<IConnectionOptions> {
let driver: AbstractDriver;
driver = new MssqlDriver();
const connectionOptions: IConnectionOptions = {
@ -38,10 +39,10 @@ export async function createMSSQLModels(filesOrgPath: string): Promise<IConnecti
databaseName: `master`,
user: String(process.env.MSSQL_Username),
password: String(process.env.MSSQL_Password),
databaseType: 'mssql',
schemaName: 'dbo,sch1,sch2',
ssl: yn(process.env.MSSQL_SSL),
}
databaseType: "mssql",
schemaName: "dbo,sch1,sch2",
ssl: yn(process.env.MSSQL_SSL)
};
await driver.ConnectToServer(connectionOptions);
connectionOptions.databaseName = String(process.env.MSSQL_Database);
@ -55,31 +56,33 @@ export async function createMSSQLModels(filesOrgPath: string): Promise<IConnecti
database: String(process.env.MSSQL_Database),
host: String(process.env.MSSQL_Host),
password: String(process.env.MSSQL_Password),
type: 'mssql',
type: "mssql",
username: String(process.env.MSSQL_Username),
port: Number(process.env.MSSQL_Port),
dropSchema: true,
synchronize: false,
entities: [path.resolve(filesOrgPath, '*.ts')],
name: 'mssql'
}
entities: [path.resolve(filesOrgPath, "*.ts")],
name: "mssql"
};
const schemas = 'dbo,sch1,sch2'
let conn = await createConnection(connOpt)
let queryRunner = conn.createQueryRunner()
for (const sch of schemas.split(',')) {
const schemas = "dbo,sch1,sch2";
let conn = await createConnection(connOpt);
let queryRunner = conn.createQueryRunner();
for (const sch of schemas.split(",")) {
await queryRunner.createSchema(sch, true);
}
await conn.synchronize();
if (conn.isConnected) {
await conn.close()
await conn.close();
}
return connectionOptions;
}
export async function createPostgresModels(filesOrgPath: string): Promise<IConnectionOptions> {
export async function createPostgresModels(
filesOrgPath: string
): Promise<IConnectionOptions> {
let driver: AbstractDriver;
driver = new PostgresDriver();
const connectionOptions: IConnectionOptions = {
@ -88,10 +91,10 @@ export async function createPostgresModels(filesOrgPath: string): Promise<IConne
databaseName: `postgres`,
user: String(process.env.POSTGRES_Username),
password: String(process.env.POSTGRES_Password),
databaseType: 'postgres',
schemaName: 'public,sch1,sch2',
ssl: yn(process.env.POSTGRES_SSL),
}
databaseType: "postgres",
schemaName: "public,sch1,sch2",
ssl: yn(process.env.POSTGRES_SSL)
};
await driver.ConnectToServer(connectionOptions);
connectionOptions.databaseName = String(process.env.POSTGRES_Database);
@ -105,43 +108,45 @@ export async function createPostgresModels(filesOrgPath: string): Promise<IConne
database: String(process.env.POSTGRES_Database),
host: String(process.env.POSTGRES_Host),
password: String(process.env.POSTGRES_Password),
type: 'postgres',
type: "postgres",
username: String(process.env.POSTGRES_Username),
port: Number(process.env.POSTGRES_Port),
dropSchema: true,
synchronize: false,
entities: [path.resolve(filesOrgPath, '*.ts')],
name: 'postgres'
}
entities: [path.resolve(filesOrgPath, "*.ts")],
name: "postgres"
};
const schemas = 'public,sch1,sch2'
let conn = await createConnection(connOpt)
let queryRunner = conn.createQueryRunner()
for (const sch of schemas.split(',')) {
const schemas = "public,sch1,sch2";
let conn = await createConnection(connOpt);
let queryRunner = conn.createQueryRunner();
for (const sch of schemas.split(",")) {
await queryRunner.createSchema(sch, true);
}
await conn.synchronize();
if (conn.isConnected) {
await conn.close()
await conn.close();
}
return connectionOptions;
}
export async function createSQLiteModels(filesOrgPath: string): Promise<IConnectionOptions> {
export async function createSQLiteModels(
filesOrgPath: string
): Promise<IConnectionOptions> {
let driver: AbstractDriver;
driver = new SqliteDriver();
const connectionOptions: IConnectionOptions = {
host: '',
host: "",
port: 0,
databaseName: String(process.env.SQLITE_Database),
user: '',
password: '',
databaseType: 'sqlite',
schemaName: '',
ssl: false,
}
user: "",
password: "",
databaseType: "sqlite",
schemaName: "",
ssl: false
};
await driver.ConnectToServer(connectionOptions);
if (await driver.CheckIfDBExists(String(process.env.SQLITE_Database))) {
@ -152,24 +157,26 @@ export async function createSQLiteModels(filesOrgPath: string): Promise<IConnect
const connOpt: ConnectionOptions = {
database: String(process.env.SQLITE_Database),
type: 'sqlite',
type: "sqlite",
dropSchema: true,
synchronize: false,
entities: [path.resolve(filesOrgPath, '*.ts')],
name: 'sqlite'
}
entities: [path.resolve(filesOrgPath, "*.ts")],
name: "sqlite"
};
let conn = await createConnection(connOpt)
let conn = await createConnection(connOpt);
await conn.synchronize();
if (conn.isConnected) {
await conn.close()
await conn.close();
}
return connectionOptions;
}
export async function createMysqlModels(filesOrgPath: string): Promise<IConnectionOptions> {
export async function createMysqlModels(
filesOrgPath: string
): Promise<IConnectionOptions> {
let driver: AbstractDriver;
driver = new MysqlDriver();
const connectionOptions: IConnectionOptions = {
@ -178,10 +185,10 @@ export async function createMysqlModels(filesOrgPath: string): Promise<IConnecti
databaseName: String(process.env.MYSQL_Database),
user: String(process.env.MYSQL_Username),
password: String(process.env.MYSQL_Password),
databaseType: 'mysql',
schemaName: 'ignored',
ssl: yn(process.env.MYSQL_SSL),
}
databaseType: "mysql",
schemaName: "ignored",
ssl: yn(process.env.MYSQL_SSL)
};
await driver.ConnectToServer(connectionOptions);
if (await driver.CheckIfDBExists(String(process.env.MYSQL_Database))) {
@ -194,23 +201,25 @@ export async function createMysqlModels(filesOrgPath: string): Promise<IConnecti
database: String(process.env.MYSQL_Database),
host: String(process.env.MYSQL_Host),
password: String(process.env.MYSQL_Password),
type: 'mysql',
type: "mysql",
username: String(process.env.MYSQL_Username),
port: Number(process.env.MYSQL_Port),
dropSchema: true,
synchronize: true,
entities: [path.resolve(filesOrgPath, '*.ts')],
name: 'mysql'
}
const conn = await createConnection(connOpt)
entities: [path.resolve(filesOrgPath, "*.ts")],
name: "mysql"
};
const conn = await createConnection(connOpt);
if (conn.isConnected) {
await conn.close()
await conn.close();
}
return connectionOptions;
}
export async function createMariaDBModels(filesOrgPath: string): Promise<IConnectionOptions> {
export async function createMariaDBModels(
filesOrgPath: string
): Promise<IConnectionOptions> {
let driver: AbstractDriver;
driver = new MariaDbDriver();
const connectionOptions: IConnectionOptions = {
@ -219,10 +228,10 @@ export async function createMariaDBModels(filesOrgPath: string): Promise<IConnec
databaseName: String(process.env.MARIADB_Database),
user: String(process.env.MARIADB_Username),
password: String(process.env.MARIADB_Password),
databaseType: 'mariadb',
schemaName: 'ignored',
ssl: yn(process.env.MARIADB_SSL),
}
databaseType: "mariadb",
schemaName: "ignored",
ssl: yn(process.env.MARIADB_SSL)
};
await driver.ConnectToServer(connectionOptions);
if (await driver.CheckIfDBExists(String(process.env.MARIADB_Database))) {
@ -235,24 +244,26 @@ export async function createMariaDBModels(filesOrgPath: string): Promise<IConnec
database: String(process.env.MARIADB_Database),
host: String(process.env.MARIADB_Host),
password: String(process.env.MARIADB_Password),
type: 'mariadb',
type: "mariadb",
username: String(process.env.MARIADB_Username),
port: Number(process.env.MARIADB_Port),
dropSchema: true,
synchronize: true,
entities: [path.resolve(filesOrgPath, '*.ts')],
name: 'mariadb'
}
const conn = await createConnection(connOpt)
entities: [path.resolve(filesOrgPath, "*.ts")],
name: "mariadb"
};
const conn = await createConnection(connOpt);
if (conn.isConnected) {
await conn.close()
await conn.close();
}
return connectionOptions;
}
export async function createOracleDBModels(filesOrgPath: string): Promise<IConnectionOptions> {
export async function createOracleDBModels(
filesOrgPath: string
): Promise<IConnectionOptions> {
let driver: AbstractDriver;
driver = new OracleDriver();
@ -262,13 +273,13 @@ export async function createOracleDBModels(filesOrgPath: string): Promise<IConne
databaseName: String(process.env.ORACLE_Database),
user: String(process.env.ORACLE_UsernameSys),
password: String(process.env.ORACLE_PasswordSys),
databaseType: 'oracle',
databaseType: "oracle",
schemaName: String(process.env.ORACLE_Username),
ssl: yn(process.env.ORACLE_SSL),
}
ssl: yn(process.env.ORACLE_SSL)
};
await driver.ConnectToServer(connectionOptions);
connectionOptions.user = String(process.env.ORACLE_Username)
connectionOptions.password = String(process.env.ORACLE_Password)
connectionOptions.user = String(process.env.ORACLE_Username);
connectionOptions.password = String(process.env.ORACLE_Password);
if (await driver.CheckIfDBExists(String(process.env.ORACLE_Username))) {
await driver.DropDB(String(process.env.ORACLE_Username));
@ -281,23 +292,26 @@ export async function createOracleDBModels(filesOrgPath: string): Promise<IConne
sid: String(process.env.ORACLE_Database),
host: String(process.env.ORACLE_Host),
password: String(process.env.ORACLE_Password),
type: 'oracle',
type: "oracle",
username: String(process.env.ORACLE_Username),
port: Number(process.env.ORACLE_Port),
synchronize: true,
entities: [path.resolve(filesOrgPath, '*.ts')],
name: 'oracle',
}
const conn = await createConnection(connOpt)
entities: [path.resolve(filesOrgPath, "*.ts")],
name: "oracle"
};
const conn = await createConnection(connOpt);
if (conn.isConnected) {
await conn.close()
await conn.close();
}
return connectionOptions;
}
export function compileTsFiles(fileNames: string[], options: ts.CompilerOptions): boolean {
export function compileTsFiles(
fileNames: string[],
options: ts.CompilerOptions
): boolean {
const program = ts.createProgram(fileNames, options);
const emitResult = program.emit();
let compileErrors = false;
@ -306,9 +320,17 @@ export function compileTsFiles(fileNames: string[], options: ts.CompilerOptions)
const allDiagnostics = [...preDiagnostics, ...emitResult.diagnostics];
allDiagnostics.forEach(diagnostic => {
const lineAndCharacter = diagnostic.file!.getLineAndCharacterOfPosition(diagnostic.start!);
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
console.log(`${diagnostic.file!.fileName} (${lineAndCharacter.line + 1},${lineAndCharacter.character + 1}): ${message}`);
const lineAndCharacter = diagnostic.file!.getLineAndCharacterOfPosition(
diagnostic.start!
);
const message = ts.flattenDiagnosticMessageText(
diagnostic.messageText,
"\n"
);
console.log(
`${diagnostic.file!.fileName} (${lineAndCharacter.line +
1},${lineAndCharacter.character + 1}): ${message}`
);
compileErrors = true;
});
@ -317,40 +339,43 @@ export function compileTsFiles(fileNames: string[], options: ts.CompilerOptions)
export function getEnabledDbDrivers() {
const dbDrivers: string[] = [];
if (process.env.SQLITE_Skip == '0') {
dbDrivers.push('sqlite');
if (process.env.SQLITE_Skip == "0") {
dbDrivers.push("sqlite");
}
if (process.env.POSTGRES_Skip == '0') {
dbDrivers.push('postgres');
if (process.env.POSTGRES_Skip == "0") {
dbDrivers.push("postgres");
}
if (process.env.MYSQL_Skip == '0') {
dbDrivers.push('mysql');
if (process.env.MYSQL_Skip == "0") {
dbDrivers.push("mysql");
}
if (process.env.MARIADB_Skip == '0') {
dbDrivers.push('mariadb');
if (process.env.MARIADB_Skip == "0") {
dbDrivers.push("mariadb");
}
if (process.env.MSSQL_Skip == '0') {
dbDrivers.push('mssql');
if (process.env.MSSQL_Skip == "0") {
dbDrivers.push("mssql");
}
if (process.env.ORACLE_Skip == '0') {
dbDrivers.push('oracle');
if (process.env.ORACLE_Skip == "0") {
dbDrivers.push("oracle");
}
return dbDrivers;
}
export function createModelsInDb(dbDriver: string, filesOrgPathJS: string): Promise<IConnectionOptions> {
export function createModelsInDb(
dbDriver: string,
filesOrgPathJS: string
): Promise<IConnectionOptions> {
switch (dbDriver) {
case 'sqlite':
case "sqlite":
return createSQLiteModels(filesOrgPathJS);
case 'postgres':
case "postgres":
return createPostgresModels(filesOrgPathJS);
case 'mysql':
case "mysql":
return createMysqlModels(filesOrgPathJS);
case 'mariadb':
case "mariadb":
return createMariaDBModels(filesOrgPathJS);
case 'mssql':
case "mssql":
return createMSSQLModels(filesOrgPathJS);
case 'oracle':
case "oracle":
return createOracleDBModels(filesOrgPathJS);
default:
console.log(`Unknown engine type`);