merge master branch

This commit is contained in:
Kononnable 2019-08-14 12:42:23 +02:00
commit 6021be0041
41 changed files with 3272 additions and 2920 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

5
.eslintignore Normal file
View File

@ -0,0 +1,5 @@
test/integration/defaultValues/**/*.ts
test/integration/entityTypes/**/*.ts
test/integration/examples/**/*.ts
test/integration/github-issues/**/*.ts

53
.eslintrc.js Normal file
View File

@ -0,0 +1,53 @@
module.exports = {
env: {
node: true,
mocha: 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"]
}
}
},
overrides: [
{
files: ["**/*.test.ts"],
rules: {
"no-unused-expressions": "off",
"func-names": "off"
}
}
]
};

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

View File

@ -1,6 +1,6 @@
language: node_js
node_js:
- 11
- 12
- 10
- 8
cache: npm

View File

@ -1,6 +1,5 @@
# typeorm-model-generator
[![Greenkeeper badge](https://badges.greenkeeper.io/Kononnable/typeorm-model-generator.svg)](https://greenkeeper.io/)
[![Build Status](https://travis-ci.org/Kononnable/typeorm-model-generator.svg?branch=master)](https://travis-ci.org/Kononnable/typeorm-model-generator)
[![npm version](https://badge.fury.io/js/typeorm-model-generator.svg)](https://badge.fury.io/js/typeorm-model-generator)
[![codecov](https://codecov.io/gh/Kononnable/typeorm-model-generator/branch/master/graph/badge.svg)](https://codecov.io/gh/Kononnable/typeorm-model-generator)
@ -44,8 +43,9 @@ Options:
[default: "mssql"]
-o, --output Where to place generated models
[default: "Z:\Repos\typeorm-model-generator\output"]
-s, --schema Schema name to create model from. Only for mssql and
postgres
-s, --schema Schema name to create model from. Only for mssql
and postgres. You can pass multiple values
separted by comma eg. -s scheme1,scheme2,scheme3
--ssl [boolean] [default: false]
--noConfig Doesn't create tsconfig.json and ormconfig.json
[boolean] [default: false]

View File

@ -8,3 +8,4 @@ coverage:
default:
threshold: 50
target: 50
patch: off

2791
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,9 +4,10 @@
"description": "Generates models for TypeORM from existing databases.",
"bin": "bin/typeorm-model-generator",
"scripts": {
"setup": "npm install",
"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 ./**/*.ts ./src/**/*.ts ./test/**/*.ts",
"clean": "rimraf coverage output",
"prettier": "prettier --write ./src/*.ts ./src/**/*.ts"
},
@ -22,50 +23,57 @@
"homepage": "https://github.com/Kononnable/typeorm-model-generator#readme",
"dependencies": {
"change-case": "^3.1.0",
"fs-extra": "^8.0.1",
"fs-extra": "^8.1.0",
"handlebars": "^4.1.2",
"inquirer": "^6.3.1",
"inquirer": "^6.5.0",
"mssql": "^5.1.0",
"mysql": "^2.17.1",
"pg": "^7.11.0",
"pg": "^7.12.0",
"reflect-metadata": "^0.1.13",
"sqlite3": "^4.0.8",
"ts-node": "^8.2.0",
"sqlite3": "^4.0.9",
"ts-node": "^8.3.0",
"typeorm": "^0.2.18",
"typescript": "^3.5.1",
"yargs": "^13.2.4",
"typescript": "^3.5.3",
"yargs": "^13.3.0",
"yn": "^2.0.0"
},
"devDependencies": {
"@types/chai": "^4.1.7",
"@types/chai-as-promised": "7.1.0",
"@types/chai-as-promised": "^7.1.1",
"@types/chai-subset": "^1.3.2",
"@types/fs-extra": "^7.0.0",
"@types/fs-extra": "^8.0.0",
"@types/handlebars": "^4.1.0",
"@types/inquirer": "^6.0.3",
"@types/inquirer": "^6.5.0",
"@types/mocha": "^5.2.7",
"@types/mssql": "^4.0.14",
"@types/mssql": "^4.0.16",
"@types/mysql": "^2.15.6",
"@types/node": "^12.0.4",
"@types/oracledb": "^3.1.1",
"@types/node": "^12.6.9",
"@types/oracledb": "^3.1.3",
"@types/pg": "^7.4.14",
"@types/sinon": "^7.0.12",
"@types/sinon": "^7.0.13",
"@types/sqlite3": "^3.1.5",
"@types/yargs": "^12.0.1",
"@types/yn": "^3.1.0",
"@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",
"husky": "^2.3.0",
"lint-staged": "^8.1.7",
"mocha": "^6.1.4",
"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",
"nyc": "^14.1.1",
"prettier": "^1.17.1",
"prettier": "^1.18.2",
"rimraf": "^2.6.3",
"sinon": "^7.3.2",
"sinon-chai": "^3.3.0",
"tslint": "^5.17.0",
"tslint-config-prettier": "^1.18.0"
"sinon-chai": "^3.3.0"
},
"husky": {
"hooks": {

View File

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

View File

@ -1,20 +1,22 @@
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 AbstractNamingStrategy from "./AbstractNamingStrategy";
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 +62,7 @@ export async function dataCollectionPhase(
driver: AbstractDriver,
connectionOptions: IConnectionOptions
) {
return await driver.GetDataFromServer(connectionOptions);
return driver.GetDataFromServer(connectionOptions);
}
export function modelCustomizationPhase(
@ -68,22 +70,22 @@ export function modelCustomizationPhase(
generationOptions: IGenerationOptions,
defaultValues: DataTypeDefaults
) {
let namingStrategy: NamingStrategy;
let namingStrategy: AbstractNamingStrategy;
if (
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[],
@ -106,15 +108,12 @@ function removeColumnDefaultProperties(
if (
column.options.precision &&
defVal.precision &&
column.options.precision === defVal.precision
) {
column.options.precision = undefined;
}
if (
column.options.precision === defVal.precision &&
column.options.scale &&
defVal.scale &&
column.options.scale === defVal.scale
) {
column.options.precision = undefined;
column.options.scale = undefined;
}
if (
@ -158,7 +157,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;
});
});
@ -205,8 +204,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",
@ -229,6 +230,8 @@ function createHandlebarsHelpers(generationOptions: IGenerationOptions) {
case "none":
retStr = str;
break;
default:
throw new Error("Unknown case style");
}
return retStr;
});
@ -250,12 +253,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 => {
@ -270,6 +275,8 @@ function createHandlebarsHelpers(generationOptions: IGenerationOptions) {
case "none":
retStr = str;
break;
default:
throw new Error("Unknown case style");
}
return retStr;
});
@ -280,9 +287,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,13 +366,13 @@ function createTypeOrmConfig(
}
}
function applyNamingStrategy(
namingStrategy: NamingStrategy,
namingStrategy: AbstractNamingStrategy,
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 => {
@ -401,9 +407,9 @@ function applyNamingStrategy(
col =>
col.name === column.tsName
)
.forEach(
col => (col.name = newName)
);
.forEach(col => {
col.name = newName;
});
});
}
});
@ -426,7 +432,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 => {
@ -437,7 +445,9 @@ function applyNamingStrategy(
entity.tsEntityName &&
relation.relatedColumn === column.tsName
)
.map(v => (v.relatedColumn = newName));
.forEach(v => {
v.relatedColumn = newName;
});
column2.relations
.filter(
relation =>
@ -445,7 +455,9 @@ function applyNamingStrategy(
entity.tsEntityName &&
relation.ownerColumn === column.tsName
)
.map(v => (v.ownerColumn = newName));
.forEach(v => {
v.ownerColumn = newName;
});
});
});

View File

@ -1,10 +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,10 +1,11 @@
import changeCase = require("change-case");
import { AbstractNamingStrategy } from "./AbstractNamingStrategy";
import { EntityInfo } from "./models/EntityInfo";
import { ColumnInfo } from "./models/ColumnInfo";
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,
@ -25,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" : "";
@ -38,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(
@ -61,11 +62,13 @@ export class NamingStrategy extends AbstractNamingStrategy {
return columnName;
}
public entityName(entityName: string, entity?: EntityInfo): string {
public entityName(entityName: string): string {
return entityName;
}
public columnName(columnName: string, column?: ColumnInfo): string {
public columnName(columnName: string): string {
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 => {
@ -209,9 +221,7 @@ export abstract class AbstractDriver {
);
if (!ownerEntity) {
TomgUtils.LogError(
`Relation between tables ${relationTmp.ownerTable} and ${
relationTmp.referencedTable
} didn't found entity model ${relationTmp.ownerTable}.`
`Relation between tables ${relationTmp.ownerTable} and ${relationTmp.referencedTable} didn't found entity model ${relationTmp.ownerTable}.`
);
return;
}
@ -221,9 +231,7 @@ export abstract class AbstractDriver {
);
if (!referencedEntity) {
TomgUtils.LogError(
`Relation between tables ${relationTmp.ownerTable} and ${
relationTmp.referencedTable
} didn't found entity model ${relationTmp.referencedTable}.`
`Relation between tables ${relationTmp.ownerTable} and ${relationTmp.referencedTable} didn't found entity model ${relationTmp.referencedTable}.`
);
return;
}
@ -239,13 +247,7 @@ export abstract class AbstractDriver {
);
if (!ownerColumn) {
TomgUtils.LogError(
`Relation between tables ${
relationTmp.ownerTable
} and ${
relationTmp.referencedTable
} didn't found entity column ${
relationTmp.ownerTable
}.${ownerColumn}.`
`Relation between tables ${relationTmp.ownerTable} and ${relationTmp.referencedTable} didn't found entity column ${relationTmp.ownerTable}.${ownerColumn}.`
);
return;
}
@ -256,13 +258,7 @@ export abstract class AbstractDriver {
);
if (!relatedColumn) {
TomgUtils.LogError(
`Relation between tables ${
relationTmp.ownerTable
} and ${
relationTmp.referencedTable
} didn't found entity column ${
relationTmp.referencedTable
}.${relatedColumn}.`
`Relation between tables ${relationTmp.ownerTable} and ${relationTmp.referencedTable} didn't found entity column ${relationTmp.referencedTable}.${relatedColumn}.`
);
return;
}
@ -291,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(
@ -347,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(
@ -372,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;
@ -382,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;
@ -187,11 +191,7 @@ WHERE TABLE_TYPE='BASE TABLE' and TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG
break;
default:
TomgUtils.LogError(
`Unknown column type: ${
resp.DATA_TYPE
} table name: ${
resp.TABLE_NAME
} column name: ${resp.COLUMN_NAME}`
`Unknown column type: ${resp.DATA_TYPE} table name: ${resp.TABLE_NAME} column name: ${resp.COLUMN_NAME}`
);
break;
}
@ -222,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,
@ -263,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)
@ -276,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;
@ -293,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;
@ -307,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,
@ -328,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
@ -347,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) {
@ -380,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);
@ -388,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 = {
@ -409,6 +417,7 @@ order by
},
password: connectionOptons.password,
port: connectionOptons.port,
requestTimeout: connectionOptons.timeout,
server: connectionOptons.host,
user: connectionOptons.user
};
@ -430,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(
@ -449,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,10 +74,13 @@ 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;
colInfo.options.unsigned = resp.COLUMN_TYPE.endsWith(
" unsigned"
);
switch (resp.DATA_TYPE) {
case "int":
colInfo.tsType = "number";
@ -160,10 +169,10 @@ 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";
colInfo.tsType = "object";
break;
case "binary":
colInfo.tsType = "Buffer";
@ -199,11 +208,7 @@ export class MysqlDriver extends AbstractDriver {
break;
default:
TomgUtils.LogError(
`Unknown column type: ${
resp.DATA_TYPE
} table name: ${
resp.TABLE_NAME
} column name: ${resp.COLUMN_NAME}`
`Unknown column type: ${resp.DATA_TYPE} table name: ${resp.TABLE_NAME} column name: ${resp.COLUMN_NAME}`
);
break;
}
@ -245,6 +250,7 @@ export class MysqlDriver extends AbstractDriver {
});
return entities;
}
public async GetIndexesFromEntity(
entities: EntityInfo[],
schema: string,
@ -259,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)
@ -288,6 +296,7 @@ export class MysqlDriver extends AbstractDriver {
return entities;
}
public async GetRelations(
entities: EntityInfo[],
schema: string,
@ -317,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);
@ -341,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 => {
@ -366,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;
@ -378,6 +389,7 @@ export class MysqlDriver extends AbstractDriver {
ssl: {
rejectUnauthorized: false
},
timeout: connectionOptons.timeout,
user: connectionOptons.user
};
} else {
@ -386,6 +398,7 @@ export class MysqlDriver extends AbstractDriver {
host: connectionOptons.host,
password: connectionOptons.password,
port: connectionOptons.port,
timeout: connectionOptons.timeout,
user: connectionOptons.user
};
}
@ -409,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);
@ -438,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,34 +292,32 @@ 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*/ = {
connectString: `${connectionOptons.host}:${
connectionOptons.port
}/${connectionOptons.databaseName}`,
config /* Oracle.IConnectionAttributes */ = {
connectString: `${connectionOptons.host}:${connectionOptons.port}/${connectionOptons.databaseName}`,
externalAuth: connectionOptons.ssl,
password: connectionOptons.password,
privilege: this.Oracle.SYSDBA,
user: connectionOptons.user
};
} else {
config /*Oracle.IConnectionAttributes*/ = {
connectString: `${connectionOptons.host}:${
connectionOptons.port
}/${connectionOptons.databaseName}`,
config /* Oracle.IConnectionAttributes */ = {
connectString: `${connectionOptons.host}:${connectionOptons.port}/${connectionOptons.databaseName}`,
externalAuth: connectionOptons.ssl,
password: connectionOptons.password,
user: connectionOptons.user
@ -351,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;
@ -48,7 +51,8 @@ export class PostgresDriver extends AbstractDriver {
numeric_scale: number;
isidentity: string;
isunique: string;
}> = (await this.Connection
enumvalues: string | null;
}[] = (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,
@ -60,7 +64,14 @@ export class PostgresDriver extends AbstractDriver {
tc.CONSTRAINT_TYPE = 'UNIQUE'
and tc.TABLE_NAME = c.TABLE_NAME
and cu.COLUMN_NAME = c.COLUMN_NAME
and tc.TABLE_SCHEMA=c.TABLE_SCHEMA) IsUnique
and tc.TABLE_SCHEMA=c.TABLE_SCHEMA) IsUnique,
(SELECT
string_agg(enumlabel, ',')
FROM "pg_enum" "e"
INNER JOIN "pg_type" "t" ON "t"."oid" = "e"."enumtypid"
INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace"
WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name
) enumValues
FROM INFORMATION_SCHEMA.COLUMNS c
where table_schema in (${schema})
order by ordinal_position`)).rows;
@ -76,42 +87,38 @@ export class PostgresDriver extends AbstractDriver {
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.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"
) {
TomgUtils.LogError(
`Unknown ${resp.data_type} column type: ${
resp.udt_name
} table name: ${
resp.table_name
} column name: ${resp.column_name}`
`Unknown ${resp.data_type} column type: ${resp.udt_name} table name: ${resp.table_name} column name: ${resp.column_name}`
);
} else {
TomgUtils.LogError(
`Unknown column type: ${
resp.data_type
} table name: ${
resp.table_name
} column name: ${resp.column_name}`
`Unknown column type: ${resp.data_type} table name: ${resp.table_name} column name: ${resp.column_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;
}
@ -151,246 +158,258 @@ export class PostgresDriver extends AbstractDriver {
return entities;
}
public MatchColumnTypes(dataType: string, udtName: string) {
const ret: {
ts_type:
public MatchColumnTypes(
dataType: string,
udtName: string,
enumValues: string | null
) {
let ret: {
tsType:
| "number"
| "string"
| "boolean"
| "Date"
| "Buffer"
| "Object"
| "string | Object"
| "object"
| "string | object"
| "string | string[]"
| "any"
| null;
sql_type: string | null;
is_array: boolean;
} = { ts_type: null, sql_type: null, is_array: false };
ret.sql_type = dataType;
sqlType: string | null;
isArray: boolean;
enumValues: string[];
} = { 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";
break;
case "enum":
ret.ts_type = "string";
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(udtName.substring(1), udtName);
ret.ts_type = z.ts_type;
ret.sql_type = z.sql_type;
ret.is_array = true;
ret = this.MatchColumnTypes(
udtName.substring(1),
udtName,
enumValues
);
ret.isArray = true;
break;
case "USER-DEFINED":
ret.sql_type = udtName;
ret.ts_type = "string";
ret.tsType = "string";
switch (udtName) {
case "citext":
case "hstore":
case "geometry":
ret.sqlType = udtName;
break;
default:
ret.ts_type = null;
ret.sql_type = null;
if (enumValues) {
ret.sqlType = "enum";
ret.enumValues = (`"${enumValues
.split(",")
.join('","')}"` as never) as string[];
} else {
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,
@ -445,11 +464,12 @@ export class PostgresDriver extends AbstractDriver {
return entities;
}
public async GetRelations(
entities: EntityInfo[],
schema: string
): Promise<EntityInfo[]> {
const response: Array<{
const response: {
tablewithforeignkey: string;
fk_partno: number;
foreignkeycolumn: string;
@ -459,7 +479,7 @@ export class PostgresDriver extends AbstractDriver {
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,
@ -499,20 +519,20 @@ export class PostgresDriver extends AbstractDriver {
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);
@ -520,12 +540,13 @@ export class PostgresDriver extends AbstractDriver {
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) => {
@ -553,6 +574,8 @@ export class PostgresDriver extends AbstractDriver {
password: connectionOptons.password,
port: connectionOptons.port,
ssl: connectionOptons.ssl,
// eslint-disable-next-line @typescript-eslint/camelcase
statement_timeout: connectionOptons.timeout,
user: connectionOptons.user
});
@ -577,26 +600,33 @@ export class PostgresDriver extends AbstractDriver {
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,264 +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();
}
@ -306,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 => {
@ -326,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;
}
@ -355,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

@ -15,7 +15,8 @@ import {BaseEntity,Column,Entity,Index,JoinColumn,JoinTable,ManyToMany,ManyToOne
primary:{{primary}},{{/primary}}{{/generated}}{{#unique}}
unique: true,{{/unique}}{{#length}}
length:{{.}},{{/length}}{{#width}}
width:{{.}},{{/width}}{{#default}}
width:{{.}},{{/width}}{{#unsigned}}
unsigned: true,{{/unsigned}}{{#default}}
default: {{.}},{{/default}}{{#precision}}
precision:{{.}},{{/precision}}{{#scale}}
scale:{{.}},{{/scale}}{{#enum}}
@ -25,18 +26,18 @@ import {BaseEntity,Column,Entity,Index,JoinColumn,JoinTable,ManyToMany,ManyToOne
}){{/options}}
{{printPropertyVisibility}}{{toPropertyName tsName}}:{{tsType}}{{#options/nullable}} | null{{/options/nullable}};
{{/relations}}{{#relations}}
@{{relationType}}(type=>{{toEntityName relatedTable}}, {{tolowerCaseFirst relatedTable}}=>{{tolowerCaseFirst relatedTable}}.{{#if isOwner}}{{toPropertyName ownerColumn}},{ {{#../options/primary}}primary:true,{{/../options/primary}}{{^../options/nullable}} nullable:false,{{/../options/nullable}}{{#actionOnDelete}}onDelete: '{{.}}',{{/actionOnDelete}}{{#actionOnUpdate}}onUpdate: '{{.}}'{{/actionOnUpdate}} }{{else}}{{toPropertyName relatedColumn}}{{#if (or actionOnDelete actionOnUpdate ) }},{ {{#actionOnDelete}}onDelete: '{{.}}' ,{{/actionOnDelete}}{{#actionOnUpdate}}onUpdate: '{{.}}'{{/actionOnUpdate}} }{{/if}}{{/if}}){{#isOwner}}
@{{relationType}}(()=>{{toEntityName relatedTable}}, {{tolowerCaseFirst relatedTable}}=>{{tolowerCaseFirst relatedTable}}.{{#if isOwner}}{{toPropertyName ownerColumn}},{ {{#../options/primary}}primary:true,{{/../options/primary}}{{^../options/nullable}} nullable:false,{{/../options/nullable}}{{#actionOnDelete}}onDelete: '{{.}}',{{/actionOnDelete}}{{#actionOnUpdate}}onUpdate: '{{.}}'{{/actionOnUpdate}} }{{else}}{{toPropertyName relatedColumn}}{{#if (or actionOnDelete actionOnUpdate ) }},{ {{#actionOnDelete}}onDelete: '{{.}}' ,{{/actionOnDelete}}{{#actionOnUpdate}}onUpdate: '{{.}}'{{/actionOnUpdate}} }{{/if}}{{/if}}){{#isOwner}}
{{#if isManyToMany}}@JoinTable({ name:'{{ ../options/name}}'}){{else}}@JoinColumn({ name:'{{ ../options/name}}'}){{/if}}{{/isOwner}}
{{#if (or isOneToMany isManyToMany)}}{{printPropertyVisibility}}{{toPropertyName ../tsName}}:{{toLazy (concat (toEntityName relatedTable) "[]")}};
{{else}}{{printPropertyVisibility}}{{toPropertyName ../tsName}}:{{toLazy (concat (toEntityName relatedTable) ' | null')}};
{{/if}}
{{#if relationIdField }}
@RelationId(({{../../tsEntityName}}: {{toEntityName ../../tsEntityName}}) => {{../../tsEntityName}}.{{toPropertyName ../tsName}})
{{toPropertyName ../tsName}}Id: {{#if isOneToOne}}{{toLazy ../tsType}}{{else}}{{toLazy (concat ../tsType "[]")}}{{/if}};{{/if}}{{/relations}}
@RelationId(({{toPropertyName ../../tsEntityName}}: {{toEntityName ../../tsEntityName}}) => {{toEntityName ../../tsEntityName}}.{{toPropertyName ../tsName}})
{{printPropertyVisibility}}{{toPropertyName ../tsName}}Id: {{#if isOneToOne}}{{toLazy ../tsType}}{{else}}{{toLazy (concat ../tsType "[]")}}{{/if}};{{/if}}{{/relations}}
{{/Columns}}
{{#if GenerateConstructor}}
constructor(init?: Partial<{{toEntityName tsEntityName}}>) {
{{printPropertyVisibility}}constructor(init?: Partial<{{toEntityName tsEntityName}}>) {
Object.assign(this, init);
}
{{/if}}

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", {
@ -102,7 +98,7 @@ function GetUtilParametersByArgs() {
.option("s", {
alias: "schema",
describe:
"Schema name to create model from. Only for mssql and postgres. You can pass multiple values separted by comma."
"Schema name to create model from. Only for mssql and postgres. You can pass multiple values separted by comma eg. -s scheme1,scheme2,scheme3"
})
.option("ssl", {
boolean: true,
@ -161,42 +157,44 @@ function GetUtilParametersByArgs() {
boolean: true,
default: false,
describe: "Generate constructor allowing partial initialization"
}).argv;
})
.option("timeout", {
describe: "SQL Query timeout(ms)",
number: true
});
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.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 };
}
@ -233,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";
}
},
@ -251,7 +249,7 @@ async function GetUtilParametersByInquirer() {
message: "Database user name:",
name: "login",
type: "input",
default(answers: any) {
default() {
return driver.standardUser;
}
},
@ -306,15 +304,41 @@ async function GetUtilParametersByInquirer() {
type: "input"
}
])) as any).output;
const customize = ((await inquirer.prompt([
if (
connectionOptions.databaseType === "mssql" ||
connectionOptions.databaseType === "postgres"
) {
const { changeRequestTimeout } = (await inquirer.prompt([
{
default: false,
message: "Do you want to change default sql query timeout?",
name: "changeRequestTimeout",
type: "confirm"
}
])) as any;
if (changeRequestTimeout) {
const { timeout } = (await inquirer.prompt({
message: "Query timeout(ms):",
name: "timeout",
type: "input",
validate(value) {
const valid = !Number.isNaN(parseInt(value, 10));
return valid || "Please enter a valid number";
}
})) as any;
connectionOptions.timeout = timeout;
}
}
const { customizeGeneration } = (await inquirer.prompt([
{
default: false,
message: "Do you want to customize generated model?",
name: "customize",
name: "customizeGeneration",
type: "confirm"
}
])) as any).customize;
if (customize) {
])) as any;
if (customizeGeneration) {
const customizations: string[] = ((await inquirer.prompt([
{
choices: [
@ -354,6 +378,18 @@ async function GetUtilParametersByInquirer() {
type: "checkbox"
}
])) as any).selected;
generationOptions.propertyVisibility = ((await inquirer.prompt([
{
choices: ["public", "protected", "private", "none"],
message:
"Defines which visibility should have the generated property",
name: "propertyVisibility",
default: "none",
type: "list"
}
])) as any).propertyVisibility;
generationOptions.noConfigs = !customizations.includes("config");
generationOptions.lazy = customizations.includes("lazy");
generationOptions.activeRecord = customizations.includes(
@ -382,7 +418,6 @@ async function GetUtilParametersByInquirer() {
])) as any).namingStrategy;
if (namingStrategyPath && namingStrategyPath !== "") {
// tslint:disable-next-line:no-var-requires
generationOptions.customNamingStrategyPath = namingStrategyPath;
} else {
generationOptions.customNamingStrategyPath = "";
@ -418,20 +453,19 @@ 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"),
[connectionOptions, generationOptions],
{ spaces: "\t" }
);
await fs.writeJson(path.resolve(process.cwd(), ".tomg-config"), [
connectionOptions,
generationOptions
]);
console.log(`[${new Date().toLocaleTimeString()}] Config file saved.`);
console.warn(
`\x1b[33m[${new Date().toLocaleTimeString()}] WARNING: Password was saved as plain text.\x1b[0m`

View File

@ -1,18 +1,21 @@
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"
| "boolean"
| "Date"
| "Buffer"
| "Object"
| "string | Object"
| "object"
| "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": []
}

4
src/typings.d.ts vendored
View File

@ -1,4 +0,0 @@
declare module "*.json" {
const value: any;
export default value;
}

View File

@ -1,105 +1,116 @@
import { expect } from "chai";
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 MSSQL from "mssql";
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> {
public recordsets: Array<MSSQL.IRecordSet<any>>;
public recordset: MSSQL.IRecordSet<any>;
public rowsAffected: number[];
public output: { [key: string]: any; };
interface FakeResponse extends MSSQL.IResult<any> {
recordsets: MSSQL.IRecordSet<any>[];
recordset: MSSQL.IRecordSet<any>;
rowsAffected: number[];
output: { [key: string]: any };
}
class fakeRecordset extends Array<any> implements MSSQL.IRecordSet<any>{
public columns: IColumnMetadata;
public toTable(): Table {
return new Table();
class FakeRecordset extends Array<any> implements MSSQL.IRecordSet<any> {
public columns: MSSQL.IColumnMetadata;
// eslint-disable-next-line class-methods-use-this
public toTable(): MSSQL.Table {
return new MSSQL.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: () => {
const response = {} as 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: () => {
const response = {} as 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

@ -89,7 +89,7 @@ export class Post {
// In mariaDb Json is recognized as longtext
// @Column("json")
// json: Object;
// json: object;
@Column("binary")
binary: Buffer;

View File

@ -15,10 +15,13 @@ export class Post {
@Column("int")
int: number;
@Column("int", { unsigned: true })
uint: number;
@Column("tinyint")
tinyint: number;
@Column("tinyint",{width:1})
@Column("tinyint", { width: 1 })
boolean: boolean;
@Column("smallint")
@ -88,7 +91,7 @@ export class Post {
enum: string;
@Column("json")
json: Object;
json: object;
@Column("binary")
binary: Buffer;

View File

@ -119,11 +119,11 @@ export class Post {
@Column("boolean")
boolean: boolean;
// @Column("enum")
// enum: string;
@Column("enum", { enum: ["A", "B", "C"] })
enum: string;
@Column("point")
point: string | Object;
point: string | object;
@Column("line")
line: string;
@ -132,7 +132,7 @@ export class Post {
lseg: string | string[];
@Column("box")
box: string | Object;
box: string | object;
@Column("path")
path: string;
@ -141,7 +141,7 @@ export class Post {
polygon: string;
@Column("circle")
circle: string | Object;
circle: string | object;
@Column("cidr")
cidr: string;
@ -165,10 +165,10 @@ export class Post {
xml: string;
@Column("json")
json: Object;
json: object;
@Column("jsonb")
jsonb: Object;
jsonb: object;
@Column("int4range")
int4range: string;

View File

@ -123,7 +123,7 @@ export class PostArrays {
// enum: string[];
@Column("point", { array: true })
point: string[] | Object[];
point: string[] | object[];
@Column("line", { array: true })
line: string[];
@ -132,7 +132,7 @@ export class PostArrays {
lseg: string[] | string[][];
@Column("box", { array: true })
box: string[] | Object[];
box: string[] | object[];
@Column("path", { array: true })
path: string[];
@ -141,7 +141,7 @@ export class PostArrays {
polygon: string[];
@Column("circle", { array: true })
circle: string[] | Object[];
circle: string[] | object[];
@Column("cidr", { array: true })
cidr: string[];
@ -165,10 +165,10 @@ export class PostArrays {
xml: string[];
@Column("json", { array: true })
json: Object[];
json: object[];
@Column("jsonb", { array: true })
jsonb: Object[];
jsonb: object[];
@Column("int4range", { array: true })
int4range: string[];

View File

@ -1,139 +1,226 @@
require('dotenv').config()
import "reflect-metadata";
import { expect } from "chai";
import fs = require('fs-extra');
import path = require('path');
import { EntityFileToJson } from "../utils/EntityFileToJson";
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 EntityFileToJson from "../utils/EntityFileToJson";
import {
createDriver,
dataCollectionPhase,
modelCustomizationPhase,
modelGenerationPhase
} from "../../src/Engine";
import * as GTU from "../utils/GeneralTestUtils";
import EntityInfo from "../../src/models/EntityInfo";
import IConnectionOptions from "../../src/IConnectionOptions";
import fs = require("fs-extra");
import path = require("path");
import chaiSubset = require("chai-subset");
import chai = require("chai");
import yn = require("yn");
import { EntityInfo } from "../../src/models/EntityInfo";
require("dotenv").config();
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'
runTestsFromPath(testPartialPath, false);
})
describe("TypeOrm examples", async function () {
this.timeout(60000)
this.slow(10000)// compiling created models takes time
const testPartialPath = 'test/integration/examples'
runTestsFromPath(testPartialPath, false);
})
});
describe("GitHub issues", async function() {
this.timeout(60000);
this.slow(10000); // compiling created models takes time
const testPartialPath = "test/integration/github-issues";
await runTestsFromPath(testPartialPath, false);
});
describe("TypeOrm examples", async function() {
this.timeout(60000);
this.slow(10000); // compiling created models takes time
const testPartialPath = "test/integration/examples";
await runTestsFromPath(testPartialPath, false);
});
export async function runTestsFromPath(testPartialPath: string, isDbSpecific: boolean) {
const resultsPath = path.resolve(process.cwd(), `output`)
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)
dbDrivers.forEach(dbDriver => {
const newDirPath = path.resolve(resultsPath, dbDriver);
if (!fs.existsSync(newDirPath)) {
fs.mkdirSync(newDirPath);
}
}
});
const files = fs.readdirSync(path.resolve(process.cwd(), testPartialPath));
if (isDbSpecific) {
await runTest(dbDrivers, testPartialPath, files);
} else {
for (const folder of files) {
files.forEach(folder => {
runTestForMultipleDrivers(folder, dbDrivers, testPartialPath);
}
});
}
}
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);
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 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
);
filesOrg.forEach(file => {
const jsonEntityOrg = EntityFileToJson.convert(
fs.readFileSync(path.resolve(filesOrgPathTS, file))
);
const jsonEntityGen = EntityFileToJson.convert(
fs.readFileSync(path.resolve(filesGenPath, file))
);
expect(jsonEntityGen, `Error in file ${file}`).to.containSubset(
jsonEntityOrg
);
});
}
function compileGeneratedModel(filesGenPath: string, drivers: string[]) {
let currentDirectoryFiles: string[] = [];
const 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 +231,65 @@ 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":
// eslint-disable-next-line no-case-declarations
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 +297,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"))) {
await driver.CreateDB("db1");
}
if (! await driver.CheckIfDBExists('db2')) {
var t = await driver.CreateDB('db2')
if (!(await driver.CheckIfDBExists("db2"))) {
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,361 +1,515 @@
export class EntityFileToJson {
public getEntityOptions(trimmedLine: string, ent: EntityJson) {
const decoratorParameters = trimmedLine.slice(trimmedLine.indexOf('(') + 1, trimmedLine.lastIndexOf(')'))
/* eslint-disable max-classes-per-file */
class EntityJson {
public entityName: string;
public entityOptions: any = {};
public columns: EntityColumn[] = [] as EntityColumn[];
public indicies: EntityIndex[] = [] as EntityIndex[];
}
class EntityColumn {
public columnName: string;
public columnTypes: string[] = [];
public columnOptions: any = {};
public relationType:
| "OneToOne"
| "OneToMany"
| "ManyToOne"
| "ManyToMany"
| "None" = "None";
public isOwnerOfRelation: boolean = false;
}
class EntityIndex {
public indexName: string;
public columnNames: string[] = [];
public isUnique: boolean = false;
}
export default class EntityFileToJson {
public static getEntityOptions(trimmedLine: string, ent: EntityJson) {
const decoratorParameters = trimmedLine.slice(
trimmedLine.indexOf("(") + 1,
trimmedLine.lastIndexOf(")")
);
if (decoratorParameters.length > 0) {
if (decoratorParameters[0] != '"' || !decoratorParameters.endsWith('"')) {
let badJSON = decoratorParameters.substring(decoratorParameters.indexOf(',') + 1).trim()
if (badJSON.lastIndexOf(',') == badJSON.length - 3) {
badJSON = badJSON.slice(0, badJSON.length - 3) + badJSON[badJSON.length - 2] + badJSON[badJSON.length - 1]
if (
decoratorParameters[0] !== '"' ||
!decoratorParameters.endsWith('"')
) {
let badJSON = decoratorParameters
.substring(decoratorParameters.indexOf(",") + 1)
.trim();
if (badJSON.lastIndexOf(",") === badJSON.length - 3) {
badJSON =
badJSON.slice(0, badJSON.length - 3) +
badJSON[badJSON.length - 2] +
badJSON[badJSON.length - 1];
}
ent.entityOptions = JSON.parse(badJSON.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?:/g, '"$2": '))
ent.entityOptions = JSON.parse(
badJSON.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?:/g, '"$2": ')
);
}
}
}
public getColumnOptionsAndType(trimmedLine: string, col: EntityColumn) {
const decoratorParameters = trimmedLine.slice(trimmedLine.indexOf('(') + 1, trimmedLine.lastIndexOf(')'))
const primaryGeneratedColumn = trimmedLine.substring(0, trimmedLine.indexOf('('))=='@PrimaryGeneratedColumn'
public static getColumnOptionsAndType(
trimmedLine: string,
col: EntityColumn
) {
const decoratorParameters = trimmedLine.slice(
trimmedLine.indexOf("(") + 1,
trimmedLine.lastIndexOf(")")
);
const primaryGeneratedColumn =
trimmedLine.substring(0, trimmedLine.indexOf("(")) ===
"@PrimaryGeneratedColumn";
if (decoratorParameters.length > 0) {
if (decoratorParameters.search(',') > 0 && !primaryGeneratedColumn) {
col.columnTypes = decoratorParameters.substring(0, decoratorParameters.indexOf(',')).trim().split('|');
let badJSON = decoratorParameters.substring(decoratorParameters.indexOf(',') + 1).trim()
if (badJSON.lastIndexOf(',') == badJSON.length - 3) {
badJSON = badJSON.slice(0, badJSON.length - 3) + badJSON[badJSON.length - 2] + badJSON[badJSON.length - 1]
if (
decoratorParameters.search(",") > 0 &&
!primaryGeneratedColumn
) {
col.columnTypes = decoratorParameters
.substring(0, decoratorParameters.indexOf(","))
.trim()
.split("|");
let badJSON = decoratorParameters
.substring(decoratorParameters.indexOf(",") + 1)
.trim();
if (badJSON.lastIndexOf(",") === badJSON.length - 3) {
badJSON =
badJSON.slice(0, badJSON.length - 3) +
badJSON[badJSON.length - 2] +
badJSON[badJSON.length - 1];
}
badJSON = badJSON.replace(/default: \(\) => (.*)/, `default: $1`)
col.columnOptions = JSON.parse(badJSON.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?:/g, '"$2": '))
badJSON = badJSON.replace(
/default: \(\) => (.*)/,
`default: $1`
);
col.columnOptions = JSON.parse(
badJSON.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?:/g, '"$2": ')
);
} else if (
decoratorParameters[0] === '"' &&
decoratorParameters.endsWith('"')
) {
col.columnTypes = decoratorParameters
.split("|")
.map(x => x.trim());
} else {
if (decoratorParameters[0] == '"' && decoratorParameters.endsWith('"')) {
col.columnTypes = decoratorParameters.split('|').map( x=>x.trim())
} else {
let badJSON = !primaryGeneratedColumn ? decoratorParameters.substring(decoratorParameters.indexOf(',') + 1) : decoratorParameters
badJSON = badJSON.trim()
if (badJSON.lastIndexOf(',') == badJSON.length - 3) {
badJSON = badJSON.slice(0, badJSON.length - 3) + badJSON[badJSON.length - 2] + badJSON[badJSON.length - 1]
}
col.columnOptions = JSON.parse(badJSON.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?:/g, '"$2": '))
let badJSON = !primaryGeneratedColumn
? decoratorParameters.substring(
decoratorParameters.indexOf(",") + 1
)
: decoratorParameters;
badJSON = badJSON.trim();
if (badJSON.lastIndexOf(",") === badJSON.length - 3) {
badJSON =
badJSON.slice(0, badJSON.length - 3) +
badJSON[badJSON.length - 2] +
badJSON[badJSON.length - 1];
}
col.columnOptions = JSON.parse(
badJSON.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?:/g, '"$2": ')
);
}
}
}
public getRelationOptions(trimmedLine:string, col:EntityColumn){
const decoratorParameters = trimmedLine.slice(trimmedLine.indexOf('(') + 1, trimmedLine.lastIndexOf(')'))
if (decoratorParameters.length > 0) {
const params = decoratorParameters.match(/(,)(?!([^{]*}))/g)
if ( params && params.length == 2) {
let badJSON = decoratorParameters.substring( decoratorParameters.lastIndexOf('{'),decoratorParameters.lastIndexOf('}')+1).trim()
if (badJSON.lastIndexOf(',') == badJSON.length - 3) {
badJSON = badJSON.slice(0, badJSON.length - 3) + badJSON[badJSON.length - 2] + badJSON[badJSON.length - 1]
public static getRelationOptions(trimmedLine: string, col: EntityColumn) {
const decoratorParameters = trimmedLine.slice(
trimmedLine.indexOf("(") + 1,
trimmedLine.lastIndexOf(")")
);
if (decoratorParameters.length > 0) {
const params = decoratorParameters.match(/(,)(?!([^{]*}))/g);
if (params && params.length === 2) {
let badJSON = decoratorParameters
.substring(
decoratorParameters.lastIndexOf("{"),
decoratorParameters.lastIndexOf("}") + 1
)
.trim();
if (badJSON.lastIndexOf(",") === badJSON.length - 3) {
badJSON =
badJSON.slice(0, badJSON.length - 3) +
badJSON[badJSON.length - 2] +
badJSON[badJSON.length - 1];
}
col.columnOptions = JSON.parse(badJSON.replace(/(')/g,`"`).replace(/(['"])?([a-z0-9A-Z_]+)(['"])?:/g, '"$2": '))
col.columnOptions = JSON.parse(
badJSON
.replace(/(')/g, `"`)
.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?:/g, '"$2": ')
);
}
}
}
public getIndexOptions(trimmedLine: string, ind: EntityIndex) {
const decoratorParameters = trimmedLine.slice(trimmedLine.indexOf('(') + 1, trimmedLine.lastIndexOf(')'))
public static getIndexOptions(trimmedLine: string, ind: EntityIndex) {
const decoratorParameters = trimmedLine.slice(
trimmedLine.indexOf("(") + 1,
trimmedLine.lastIndexOf(")")
);
if (decoratorParameters.length > 0) {
const containsTables = decoratorParameters.search('\\[') > -1
const containsOptions = decoratorParameters.search('{') > -1
const containsName = decoratorParameters.search('"') > -1
const containsTables = decoratorParameters.search("\\[") > -1;
const containsOptions = decoratorParameters.search("{") > -1;
const containsName = decoratorParameters.search('"') > -1;
if (containsName) {
ind.indexName = decoratorParameters.slice(decoratorParameters.indexOf('"') + 1, decoratorParameters.substr(decoratorParameters.indexOf('"') + 1).indexOf('"'))
ind.indexName = decoratorParameters.slice(
decoratorParameters.indexOf('"') + 1,
decoratorParameters
.substr(decoratorParameters.indexOf('"') + 1)
.indexOf('"')
);
}
if (containsTables) {
const columnsStr = decoratorParameters.slice(decoratorParameters.indexOf('[') + 1, decoratorParameters.indexOf(']'))
ind.columnNames.push(...columnsStr.split(',').map((val) => {
let colName = ''
if (val.search('\\.') > -1) {
colName = val.split('.')[1]
} else {
colName = val.slice(val.indexOf('"') + 1, val.lastIndexOf('"'))
}
return colName
}).filter(v => v.length > 0))
const columnsStr = decoratorParameters.slice(
decoratorParameters.indexOf("[") + 1,
decoratorParameters.indexOf("]")
);
ind.columnNames.push(
...columnsStr
.split(",")
.map(val => {
let colName = "";
if (val.search("\\.") > -1) {
[, colName] = val.split(".");
} else {
colName = val.slice(
val.indexOf('"') + 1,
val.lastIndexOf('"')
);
}
return colName;
})
.filter(v => v.length > 0)
);
}
if (containsOptions) {
const optionsStr = decoratorParameters.slice(decoratorParameters.indexOf('{') + 1, decoratorParameters.indexOf('}'))
optionsStr.split(',').forEach((v) => {
if (v.split(':').length - 1 > 0) {
switch (optionsStr.split(':')[0].trim()) {
const optionsStr = decoratorParameters.slice(
decoratorParameters.indexOf("{") + 1,
decoratorParameters.indexOf("}")
);
optionsStr.split(",").forEach(v => {
if (v.split(":").length - 1 > 0) {
switch (optionsStr.split(":")[0].trim()) {
case "unique":
ind.isUnique = optionsStr.split(':')[1].trim() == 'true' ? true : false;
ind.isUnique =
optionsStr.split(":")[1].trim() === "true";
break;
default:
console.log(`[EntityFileToJson:convert] Index option not recognized ${ind.indexName}:`)
console.log(`${optionsStr}`)
console.log(
`[EntityFileToJson:convert] Index option not recognized ${ind.indexName}:`
);
console.log(`${optionsStr}`);
break;
}
}
})
});
}
}
}
public convert(entityFile: Buffer): EntityJson {
public static convert(entityFile: Buffer): EntityJson {
const retVal = new EntityJson();
let isInClassBody = false;
let isMultilineStatement = false;
let priorPartOfMultilineStatement = '';
let priorPartOfMultilineStatement = "";
const lines = entityFile.toString().replace('\r', '').split('\n');
for (const line of lines) {
const lines = entityFile
.toString()
.replace("\r", "")
.split("\n");
lines.forEach(line => {
let trimmedLine = line.trim();
if (trimmedLine.startsWith('//')) {
continue;
if (trimmedLine.startsWith("//")) {
return;
}
if (isMultilineStatement) {
trimmedLine = priorPartOfMultilineStatement + ' ' + trimmedLine
trimmedLine = `${priorPartOfMultilineStatement} ${trimmedLine}`;
}
if (trimmedLine.length == 0) {
continue;
if (trimmedLine.length === 0) {
return;
}
else if (!isInClassBody) {
if (trimmedLine.startsWith('import')) {
continue;
} else if (trimmedLine.startsWith('@Entity')) {
if (this.isPartOfMultilineStatement(trimmedLine)) {
if (!isInClassBody) {
if (trimmedLine.startsWith("import")) {
return;
}
if (trimmedLine.startsWith("@Entity")) {
if (
EntityFileToJson.isPartOfMultilineStatement(trimmedLine)
) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
continue;
} else {
this.getEntityOptions(trimmedLine, retVal);
continue;
EntityFileToJson.getEntityOptions(trimmedLine, retVal);
}
} else if (trimmedLine.startsWith('export class')) {
retVal.entityName = trimmedLine.substring(trimmedLine.indexOf('class') + 5, trimmedLine.lastIndexOf('{')).trim().toLowerCase()
return;
}
if (trimmedLine.startsWith("export class")) {
retVal.entityName = trimmedLine
.substring(
trimmedLine.indexOf("class") + 5,
trimmedLine.lastIndexOf("{")
)
.trim()
.toLowerCase();
isInClassBody = true;
continue;
} else if (trimmedLine.startsWith('@Index')) {
if (this.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
continue;
} else {
isMultilineStatement = false;
const ind = new EntityIndex()
this.getIndexOptions(trimmedLine, ind)
retVal.indicies.push(ind);
continue;
}
return;
}
} else {
if (trimmedLine.startsWith('@Column')) {
if (this.isPartOfMultilineStatement(trimmedLine)) {
if (trimmedLine.startsWith("@Index")) {
if (
EntityFileToJson.isPartOfMultilineStatement(trimmedLine)
) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
continue;
} else {
isMultilineStatement = false;
const col = new EntityColumn()
this.getColumnOptionsAndType(trimmedLine, col)
retVal.columns.push(col);
continue;
}
} else if (trimmedLine.startsWith('@PrimaryColumn')) {
if (this.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
continue;
} else {
isMultilineStatement = false;
const col = new EntityColumn()
this.getColumnOptionsAndType(trimmedLine, col)
col.columnOptions.primary = true
retVal.columns.push(col);
continue;
}
} else if (trimmedLine.startsWith('@VersionColumn')) {
if (this.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
continue;
} else {
isMultilineStatement = false;
const col = new EntityColumn()
this.getColumnOptionsAndType(trimmedLine, col)
retVal.columns.push(col);
continue;
}
} else if (trimmedLine.startsWith('@PrimaryGeneratedColumn')) {
if (this.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
continue;
} else {
isMultilineStatement = false;
const col = new EntityColumn()
this.getColumnOptionsAndType(trimmedLine, col)
col.columnOptions.primary = true
col.columnOptions.generated = true
retVal.columns.push(col);
continue;
}
} else if (trimmedLine.startsWith('@ManyToOne')) {
if (this.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
continue;
} else {
isMultilineStatement = false;
const column = new EntityColumn()
retVal.columns.push(column)
column.relationType = "ManyToOne"
column.isOwnerOfRelation = true;
continue;
}
} else if (trimmedLine.startsWith('@OneToMany')) {
if (this.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
continue;
} else {
isMultilineStatement = false;
const column = new EntityColumn()
retVal.columns.push(column)
column.relationType = "OneToMany"
continue;
}
} else if (trimmedLine.startsWith('@ManyToMany')) {
if (this.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
continue;
} else {
isMultilineStatement = false;
const column = new EntityColumn()
retVal.columns.push(column)
column.relationType = "ManyToMany"
continue;
}
} else if (trimmedLine.startsWith('@OneToOne')) {
if (this.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
continue;
} else {
isMultilineStatement = false;
const column = new EntityColumn()
retVal.columns.push(column)
column.relationType = "OneToOne"
this.getRelationOptions(trimmedLine,column);
continue;
}
} else if (trimmedLine.startsWith('@JoinColumn')) {
if (this.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
continue;
} else {
isMultilineStatement = false;
retVal.columns[retVal.columns.length - 1].isOwnerOfRelation = true;
continue;
}
} else if (trimmedLine.startsWith('@JoinTable')) {
if (this.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
continue;
} else {
isMultilineStatement = false;
retVal.columns[retVal.columns.length - 1].isOwnerOfRelation = true;
continue;
}
} else if (trimmedLine.startsWith('@Index')) {
if (this.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
continue;
} else {
isMultilineStatement = false;
const ind = new EntityIndex()
this.getIndexOptions(trimmedLine, ind)
const ind = new EntityIndex();
EntityFileToJson.getIndexOptions(trimmedLine, ind);
retVal.indicies.push(ind);
continue;
}
} else if (trimmedLine.startsWith('constructor')) {
if (this.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
continue;
} else {
isMultilineStatement = false;
continue;
}
} else if (trimmedLine.split(':').length - 1 > 0) {
retVal.columns[retVal.columns.length - 1].columnName = trimmedLine.split(':')[0].trim();
// TODO:Should check if null only column is nullable?
let colTypes=trimmedLine.split(':')[1].split(';')[0].trim();
if (colTypes.startsWith('Promise<')) {
colTypes=colTypes.substring(8,colTypes.length-1)
retVal.columns[retVal.columns.length - 1].columnOptions.isLazy=true;
}
retVal.columns[retVal.columns.length - 1].columnTypes = colTypes.split('|').map(function (x) {
if (x == 'any') {
x = 'string' // for json columns
}
x = x.trim();
return x;
});
if (!retVal.columns[retVal.columns.length - 1].columnTypes.some( (val) => val == "null" ? true : false)) {
retVal.columns[retVal.columns.length - 1].columnTypes.push('null')
}
if (retVal.indicies.length > 0 && retVal.indicies[retVal.indicies.length - 1].columnNames.length == 0) {
retVal.indicies[retVal.indicies.length - 1].columnNames.push(retVal.columns[retVal.columns.length - 1].columnName)
}
continue
} else if (trimmedLine == '}') {
isInClassBody = false;
continue;
}
else {
console.log(`[EntityFileToJson:convert] Line not recognized in entity ${retVal.entityName}:`)
console.log(`${trimmedLine}`)
return;
}
}
console.log(`[EntityFileToJson:convert] Line not recognized in entity ${retVal.entityName}:`)
console.log(`${trimmedLine}`)
}
if (trimmedLine.startsWith("@Column")) {
if (EntityFileToJson.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
} else {
isMultilineStatement = false;
const col = new EntityColumn();
EntityFileToJson.getColumnOptionsAndType(trimmedLine, col);
retVal.columns.push(col);
}
return;
}
if (trimmedLine.startsWith("@PrimaryColumn")) {
if (EntityFileToJson.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
} else {
isMultilineStatement = false;
const col = new EntityColumn();
EntityFileToJson.getColumnOptionsAndType(trimmedLine, col);
col.columnOptions.primary = true;
retVal.columns.push(col);
}
return;
}
if (trimmedLine.startsWith("@VersionColumn")) {
if (EntityFileToJson.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
} else {
isMultilineStatement = false;
const col = new EntityColumn();
EntityFileToJson.getColumnOptionsAndType(trimmedLine, col);
retVal.columns.push(col);
}
return;
}
if (trimmedLine.startsWith("@PrimaryGeneratedColumn")) {
if (EntityFileToJson.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
} else {
isMultilineStatement = false;
const col = new EntityColumn();
EntityFileToJson.getColumnOptionsAndType(trimmedLine, col);
col.columnOptions.primary = true;
col.columnOptions.generated = true;
retVal.columns.push(col);
}
return;
}
if (trimmedLine.startsWith("@ManyToOne")) {
if (EntityFileToJson.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
} else {
isMultilineStatement = false;
const column = new EntityColumn();
retVal.columns.push(column);
column.relationType = "ManyToOne";
column.isOwnerOfRelation = true;
}
return;
}
if (trimmedLine.startsWith("@OneToMany")) {
if (EntityFileToJson.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
} else {
isMultilineStatement = false;
const column = new EntityColumn();
retVal.columns.push(column);
column.relationType = "OneToMany";
}
return;
}
if (trimmedLine.startsWith("@ManyToMany")) {
if (EntityFileToJson.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
} else {
isMultilineStatement = false;
const column = new EntityColumn();
retVal.columns.push(column);
column.relationType = "ManyToMany";
}
return;
}
if (trimmedLine.startsWith("@OneToOne")) {
if (EntityFileToJson.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
} else {
isMultilineStatement = false;
const column = new EntityColumn();
retVal.columns.push(column);
column.relationType = "OneToOne";
EntityFileToJson.getRelationOptions(trimmedLine, column);
}
return;
}
if (trimmedLine.startsWith("@JoinColumn")) {
if (EntityFileToJson.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
} else {
isMultilineStatement = false;
retVal.columns[
retVal.columns.length - 1
].isOwnerOfRelation = true;
}
return;
}
if (trimmedLine.startsWith("@JoinTable")) {
if (EntityFileToJson.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
} else {
isMultilineStatement = false;
retVal.columns[
retVal.columns.length - 1
].isOwnerOfRelation = true;
}
return;
}
if (trimmedLine.startsWith("@Index")) {
if (EntityFileToJson.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
} else {
isMultilineStatement = false;
const ind = new EntityIndex();
EntityFileToJson.getIndexOptions(trimmedLine, ind);
retVal.indicies.push(ind);
}
return;
}
if (trimmedLine.startsWith("constructor")) {
if (EntityFileToJson.isPartOfMultilineStatement(trimmedLine)) {
isMultilineStatement = true;
priorPartOfMultilineStatement = trimmedLine;
} else {
isMultilineStatement = false;
}
return;
}
if (trimmedLine.split(":").length - 1 > 0) {
retVal.columns[
retVal.columns.length - 1
].columnName = trimmedLine.split(":")[0].trim();
// TODO:Should check if null only column is nullable?
let colTypes = trimmedLine
.split(":")[1]
.split(";")[0]
.trim();
if (colTypes.startsWith("Promise<")) {
colTypes = colTypes.substring(8, colTypes.length - 1);
retVal.columns[
retVal.columns.length - 1
].columnOptions.isLazy = true;
}
retVal.columns[
retVal.columns.length - 1
].columnTypes = colTypes.split("|").map(x => {
if (x === "any") {
x = "string"; // for json columns
}
x = x.trim();
return x;
});
if (
!retVal.columns[retVal.columns.length - 1].columnTypes.some(
val => val === "null"
)
) {
retVal.columns[retVal.columns.length - 1].columnTypes.push(
"null"
);
}
if (
retVal.indicies.length > 0 &&
retVal.indicies[retVal.indicies.length - 1].columnNames
.length === 0
) {
retVal.indicies[
retVal.indicies.length - 1
].columnNames.push(
retVal.columns[retVal.columns.length - 1].columnName
);
}
return;
}
if (trimmedLine === "}") {
isInClassBody = false;
return;
}
console.log(
`[EntityFileToJson:convert] Line not recognized in entity ${retVal.entityName}:`
);
console.log(`${trimmedLine}`);
});
retVal.columns = retVal.columns.map(col => {
if (col.columnName.endsWith('Id')) {
col.columnName = col.columnName.substr(0, col.columnName.length - 2)
if (col.columnName.endsWith("Id")) {
col.columnName = col.columnName.substr(
0,
col.columnName.length - 2
);
}
return col;
})
});
retVal.indicies = retVal.indicies.map(ind => {
ind.columnNames = ind.columnNames.map(colName => {
if (colName.endsWith('Id')) {
colName = colName.substr(0, colName.length - 2)
if (colName.endsWith("Id")) {
colName = colName.substr(0, colName.length - 2);
}
return colName;
})
});
return ind;
})
});
return retVal;
}
public isPartOfMultilineStatement(statement: string) {
const matchStarting = statement.split('(').length+statement.split('{').length
const matchEnding = statement.split(')').length+statement.split('}').length
return !(matchStarting == matchEnding)
public static isPartOfMultilineStatement(statement: string) {
const matchStarting =
statement.split("(").length + statement.split("{").length;
const matchEnding =
statement.split(")").length + statement.split("}").length;
return !(matchStarting === matchEnding);
}
}
class EntityJson {
public entityName: string
public entityOptions: any = {}
public columns: EntityColumn[] = [] as EntityColumn[];
public indicies: EntityIndex[] = [] as EntityIndex[];
}
class EntityColumn {
public columnName: string
public columnTypes: string[] = []
public columnOptions: any = {}
public relationType: "OneToOne" | "OneToMany" | "ManyToOne" | "ManyToMany" | "None" = "None"
public isOwnerOfRelation: boolean = false;
}
class EntityIndex {
public indexName: string
public columnNames: string[] = []
public isUnique: boolean = false
}
/* eslint-enable max-classes-per-file */

View File

@ -1,47 +1,46 @@
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 MssqlDriver from "../../src/drivers/MssqlDriver";
import MariaDbDriver from "../../src/drivers/MariaDbDriver";
import PostgresDriver from "../../src/drivers/PostgresDriver";
import OracleDriver from "../../src/drivers/OracleDriver";
import MysqlDriver from "../../src/drivers/MysqlDriver";
import path = require("path");
export function getGenerationOptions(resultsPath: string): IGenerationOptions {
return {
resultsPath: 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> {
let driver: AbstractDriver;
driver = new MssqlDriver();
export async function createMSSQLModels(
filesOrgPath: string
): Promise<IConnectionOptions> {
const driver = new MssqlDriver();
const connectionOptions: IConnectionOptions = {
host: String(process.env.MSSQL_Host),
port: Number(process.env.MSSQL_Port),
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,43 +54,44 @@ 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(',')) {
await queryRunner.createSchema(sch, true);
}
const schemas = "dbo,sch1,sch2";
const conn = await createConnection(connOpt);
const queryRunner = conn.createQueryRunner();
await Promise.all(
schemas.split(",").map(sch => 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> {
let driver: AbstractDriver;
driver = new PostgresDriver();
export async function createPostgresModels(
filesOrgPath: string
): Promise<IConnectionOptions> {
const driver = new PostgresDriver();
const connectionOptions: IConnectionOptions = {
host: String(process.env.POSTGRES_Host),
port: Number(process.env.POSTGRES_Port),
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,83 +105,77 @@ 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(',')) {
await queryRunner.createSchema(sch, true);
}
const schemas = "public,sch1,sch2";
const conn = await createConnection(connOpt);
const queryRunner = conn.createQueryRunner();
await Promise.all(
schemas.split(",").map(sch => 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> {
let driver: AbstractDriver;
driver = new SqliteDriver();
export async function createSQLiteModels(
filesOrgPath: string
): Promise<IConnectionOptions> {
const connectionOptions: IConnectionOptions = {
host: '',
host: "",
port: 0,
databaseName: String(process.env.SQLITE_Database),
user: '',
password: '',
databaseType: 'sqlite',
schemaName: '',
ssl: false,
}
await driver.ConnectToServer(connectionOptions);
if (await driver.CheckIfDBExists(String(process.env.SQLITE_Database))) {
await driver.DropDB(String(process.env.SQLITE_Database));
}
await driver.CreateDB(String(process.env.SQLITE_Database));
await driver.DisconnectFromServer();
user: "",
password: "",
databaseType: "sqlite",
schemaName: "",
ssl: false
};
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)
const 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> {
let driver: AbstractDriver;
driver = new MysqlDriver();
export async function createMysqlModels(
filesOrgPath: string
): Promise<IConnectionOptions> {
const driver = new MysqlDriver();
const connectionOptions: IConnectionOptions = {
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)
};
await driver.ConnectToServer(connectionOptions);
if (await driver.CheckIfDBExists(String(process.env.MYSQL_Database))) {
@ -194,35 +188,36 @@ 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> {
let driver: AbstractDriver;
driver = new MariaDbDriver();
export async function createMariaDBModels(
filesOrgPath: string
): Promise<IConnectionOptions> {
const driver = new MariaDbDriver();
const connectionOptions: IConnectionOptions = {
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)
};
await driver.ConnectToServer(connectionOptions);
if (await driver.CheckIfDBExists(String(process.env.MARIADB_Database))) {
@ -235,26 +230,27 @@ 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> {
let driver: AbstractDriver;
driver = new OracleDriver();
export async function createOracleDBModels(
filesOrgPath: string
): Promise<IConnectionOptions> {
const driver = new OracleDriver();
const connectionOptions: IConnectionOptions = {
host: String(process.env.ORACLE_Host),
@ -262,13 +258,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 +277,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 +305,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 +324,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`);