merge master branch
This commit is contained in:
commit
6021be0041
@ -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
5
.eslintignore
Normal 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
53
.eslintrc.js
Normal 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
3
.gitignore
vendored
@ -4,13 +4,10 @@ ormconfig.json
|
||||
.vscode
|
||||
.idea
|
||||
typings/
|
||||
**/*.js
|
||||
**/*.js.map
|
||||
output/**/*.*
|
||||
.nyc_output/
|
||||
coverage/
|
||||
.env
|
||||
dist
|
||||
*.tgz
|
||||
!gulpfile.js
|
||||
.tomg-config
|
||||
|
@ -23,4 +23,4 @@ codecov.yml
|
||||
tsconfig.json
|
||||
typings.json
|
||||
dist/test/
|
||||
src/tslint.json
|
||||
.eslintrc.js
|
||||
|
@ -1,6 +1,6 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 11
|
||||
- 12
|
||||
- 10
|
||||
- 8
|
||||
cache: npm
|
||||
|
@ -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]
|
||||
|
@ -8,3 +8,4 @@ coverage:
|
||||
default:
|
||||
threshold: 50
|
||||
target: 50
|
||||
patch: off
|
||||
|
2791
package-lock.json
generated
2791
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
52
package.json
52
package.json
@ -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": {
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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("','")}'`;
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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}"`;
|
||||
}
|
||||
}
|
||||
|
@ -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}'"`;
|
||||
}
|
||||
}
|
||||
|
@ -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}"`;
|
||||
}
|
||||
}
|
||||
|
@ -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}"`;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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}}
|
||||
|
180
src/index.ts
180
src/index.ts
@ -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`
|
||||
|
@ -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[] = [];
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -1,3 +1,3 @@
|
||||
export interface IndexColumnInfo {
|
||||
export default interface IndexColumnInfo {
|
||||
name: string;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": [
|
||||
"tslint:recommended", "tslint-config-prettier"
|
||||
],
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"no-console":false
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
4
src/typings.d.ts
vendored
4
src/typings.d.ts
vendored
@ -1,4 +0,0 @@
|
||||
declare module "*.json" {
|
||||
const value: any;
|
||||
export default value;
|
||||
}
|
@ -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");
|
||||
});
|
||||
|
@ -89,7 +89,7 @@ export class Post {
|
||||
|
||||
// In mariaDb Json is recognized as longtext
|
||||
// @Column("json")
|
||||
// json: Object;
|
||||
// json: object;
|
||||
|
||||
@Column("binary")
|
||||
binary: Buffer;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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[];
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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`);
|
||||
|
Loading…
Reference in New Issue
Block a user