Merge branch 'master' into pr/rmachado/79
This commit is contained in:
commit
727e8059e6
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,3 +13,4 @@ coverage/
|
||||
dist
|
||||
*.tgz
|
||||
!gulpfile.js
|
||||
.tomg-config
|
||||
|
@ -23,3 +23,4 @@ codecov.yml
|
||||
tsconfig.json
|
||||
typings.json
|
||||
dist/test/
|
||||
src/tslint.json
|
||||
|
43
.travis.yml
43
.travis.yml
@ -1,19 +1,20 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
#- stable
|
||||
- 11
|
||||
- 10
|
||||
- 8
|
||||
- 6
|
||||
cache: npm
|
||||
sudo: required
|
||||
services:
|
||||
- docker
|
||||
env:
|
||||
matrix:
|
||||
- POSTGRES_Skip=0 POSTGRES_Host=localhost POSTGRES_Port=5432 POSTGRES_Username=postgres
|
||||
POSTGRES_Password=!Passw0rd POSTGRES_Database=typeorm_mg POSTGRES_SSL=0 MYSQL_Skip=0
|
||||
MYSQL_Host=localhost MYSQL_Port=3306 MYSQL_Username=root MYSQL_Password=!Passw0rd
|
||||
MYSQL_Database=typeorm_mg MYSQL_SSL=1 MARIADB_Skip=0 MARIADB_Host=localhost MARIADB_Port=3307
|
||||
MARIADB_Username=root MARIADB_Password=!Passw0rd MARIADB_Database=typeorm_mg MARIADB_SSL=0
|
||||
MSSQL_Skip=0 MSSQL_Host=localhost MSSQL_Port=1433 MSSQL_Username=sa MSSQL_Password=!Passw0rd
|
||||
- POSTGRES_Skip=0 POSTGRES_Host=localhost POSTGRES_Port=5432 POSTGRES_Username=test
|
||||
POSTGRES_Password=test POSTGRES_Database=test POSTGRES_SSL=0 MYSQL_Skip=0
|
||||
MYSQL_Host=localhost MYSQL_Port=3306 MYSQL_Username=root MYSQL_Password=admin
|
||||
MYSQL_Database=test MYSQL_SSL=0 MARIADB_Skip=0 MARIADB_Host=localhost MARIADB_Port=3307
|
||||
MARIADB_Username=root MARIADB_Password=admin MARIADB_Database=test MARIADB_SSL=0
|
||||
MSSQL_Skip=0 MSSQL_Host=localhost MSSQL_Port=1433 MSSQL_Username=sa MSSQL_Password=Admin12345
|
||||
MSSQL_Database=typeorm_mg MSSQL_SSL=0
|
||||
ORACLE_Skip=0
|
||||
ORACLE_Host=localhost
|
||||
@ -30,21 +31,21 @@ before_install:
|
||||
- sudo service mysql stop
|
||||
- sudo service postgresql stop
|
||||
- if [ -z "$DOCKER_USERNAME" ]; then echo "DOCKER_USERNAME is unset"; else echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin; fi
|
||||
- if [ -z "$DOCKER_USERNAME" ]; then mv docker-compose-without-login.yml docker-compose.yml; fi
|
||||
- if [ -z "$DOCKER_USERNAME" ]; then export ORACLE_Skip=1; fi
|
||||
- docker-compose up -d
|
||||
- mkdir /opt/oracle
|
||||
- if [ -n "$DOCKER_USERNAME" ]; then docker cp typeorm-mg-oracle-client:/usr/lib/oracle/12.2/client64/lib /opt/oracle/instantclient_12_2; fi
|
||||
- export LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2:$LD_LIBRARY_PATH
|
||||
- npm install -g npm@5
|
||||
- npm install -g greenkeeper-lockfile@1
|
||||
- if [ -z "$DOCKER_USERNAME" ]; then export ORACLE_Skip=1; else images=(${images[@]} oracle oracle_client); fi
|
||||
- if [ -n "$DOCKER_USERNAME" ]; then docker-compose up -d ${images[@]}; fi
|
||||
- images=(mysql mariadb postgres mssql)
|
||||
- echo ${images[@]}
|
||||
- docker-compose pull --parallel --ignore-pull-failures ${images[@]}
|
||||
- docker-compose up -d ${images[@]}
|
||||
before_script:
|
||||
- greenkeeper-lockfile-update
|
||||
- typings install
|
||||
- if [ -n "$DOCKER_USERNAME" ]; then mkdir /opt/oracle; npm i oracledb --no-save; docker cp typeorm-mg-oracle-client:/usr/lib/oracle/12.2/client64/lib /opt/oracle/instantclient_12_2; fi
|
||||
- export LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2:$LD_LIBRARY_PATH
|
||||
- npm link typescript
|
||||
- tsc
|
||||
- sleep 180
|
||||
after_script:
|
||||
- greenkeeper-lockfile-upload
|
||||
- sleep 60
|
||||
script:
|
||||
- travis_retry npm t
|
||||
after_success:
|
||||
- codecov --file=./coverage/coverage.json
|
||||
dd:
|
||||
secure: lONUbDz2a1LWId+Z2tTaxajK7MilX/XbQ875FYD6EE09DQ0xcoPdq2/KW5/pxuN1tz4QzTG7izMwra3XWtkBySxqFwJDUOibsgYVgn+EMMuPWNMNnQgXqTTmHbtbm1L1aSMHu4bCu4cJkJBX6503R0Kv4Hbdr2LFnSUI/9KqrevA1cVyksN71BlNBdRtvnHInwB5wNNvGULSLT+DR6qGytLGjq4ZF+pW7dH3A1LNGfDY4ivGPHt9eAWGHcVuESmudO1ADmf6XTZAJVdKqDy5eJguK48XyAqRmTc1vBxDJmCNDaU/mV6fkUoEkCjn9XfG5nJLOKviycc1j/OCuuWuqojmTlRInPGV8GDT8lNivwqLBMzvKoKgSQQROEVus4xzo64M808dFbUS30et3++O589X/7P9Wjmt+6HawcEsSq5TQfEutyB+tM9OwedTkB5Fwwmymuqx23zCAJ2orP7WoIG/ApmnKu6LmpoM340UxxSGkurztQP1OqFrf4u8kDVp9/xzqnd7qSfEd8iKvvb1bOykWGxx6dhyThCdSNyT5GQL3aub3LV6g0UB37lbhB+BVSrOAhN0r1cIWT2wr2mRxwoepObmrcNQ+AOUUXE/RcONsiEQr+STsEIjJb7bTANljRYMKpiPdsAdhvDaUZRyu8KBArTCDPotanzwQFERcw8=
|
||||
|
59
CHANGELOG.md
Normal file
59
CHANGELOG.md
Normal file
@ -0,0 +1,59 @@
|
||||
# Changelog
|
||||
|
||||
## 0.3.2
|
||||
* added option to generate models based on multiple databases(#144)
|
||||
* fixed generation of ManyToMany relations on junction tables with custom names(#151)
|
||||
* fixed problems with mysql 8
|
||||
* fixed shadowed variables tslint errors(#141)
|
||||
* fixed order of generated columns
|
||||
* mariadb default value comatibility changes(#153)
|
||||
|
||||
## 0.3.1
|
||||
* Fixed npx ussage(#146)
|
||||
|
||||
# 0.3.0
|
||||
* Wizard mode - you can now run model generation without passing any parameters and provide them step by step. It also allows you to save provided informations for future use
|
||||
* generated columns no longer contains options which are set by default in typeorm
|
||||
* added support for VARBINARY type on MySQL, MariaDb
|
||||
* fixed issue with case convertion and @RetlationId fields
|
||||
* a lot of internal work
|
||||
|
||||
## 0.2.25
|
||||
* fixed naming strategy changing entity name in db
|
||||
* fixed proper relation generation when unique index have more columns
|
||||
|
||||
## 0.2.24
|
||||
* fixed generation of default values
|
||||
* fixed generation of duplicate relations for mysql
|
||||
* added option for generating entities for AciveRecord pattern
|
||||
|
||||
## 0.2.23
|
||||
* added column type to generated `@PrimaryGeneratedColumn` decorator
|
||||
* allow to define property visibility, by using --pv
|
||||
* fixed some problems with duplicated relationships on mysql database
|
||||
|
||||
## 0.2.22
|
||||
* fixed naming stategy for guid ended column names
|
||||
* fixed column names case convertion in index declarations
|
||||
|
||||
## 0.2.21
|
||||
* primary keys using identity/sequence are now generated with `@PrimaryGeneratedColumn` decorator [#96](https://github.com/Kononnable/typeorm-model-generator/issues/96)
|
||||
|
||||
## 0.2.20
|
||||
* relation onUpdate fixes
|
||||
* postgres support for citext, hstore, geometry, array column types
|
||||
* upgraded typeorm version
|
||||
|
||||
## 0.2.19
|
||||
* custom naming strategy fiexes
|
||||
* dependencies update
|
||||
|
||||
## 0.2.18
|
||||
* oracle output format fixed
|
||||
|
||||
## 0.2.17
|
||||
|
||||
* added support for relationId fields
|
||||
* added support for custom naming entity fields
|
||||
* removed oracledb from dependencies
|
||||
* generating nullable column types for nullable columns
|
11
README.md
11
README.md
@ -21,6 +21,9 @@ To install module globally simply type `npm i -g typeorm-model-generator` in you
|
||||
### Npx way
|
||||
Thanks to npx you can use npm modules without polluting global installs. So nothing to do here :)
|
||||
>To use `npx` you need to use npm at version at least 5.2.0. Try updating your npm by `npm i -g npm`
|
||||
### Database drivers
|
||||
All database drivers except oracle are installed by default. To use typeorm-model-generator with oracle databese you need to install driver with `npm i oracledb` and configure [oracle install client](http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html) on your machine.
|
||||
|
||||
## Usage
|
||||
|
||||
```shell
|
||||
@ -53,6 +56,10 @@ Options:
|
||||
--cp, --case-property Convert property names to specified case
|
||||
[choices: "pascal", "camel", "none"] [default: "none"]
|
||||
--lazy Generate lazy relations [boolean] [default: false]
|
||||
-a, --active-record Generate models that use the ActiveRecord syntax
|
||||
[boolean] [default: false]
|
||||
--namingStrategy Use custom naming strategy
|
||||
--relationIds Generate RelationId fields [boolean] [default: false]
|
||||
--generateConstructor Generate constructor allowing partial initialization
|
||||
[boolean] [default: false]
|
||||
```
|
||||
@ -85,3 +92,7 @@ Options:
|
||||
```
|
||||
npx typeorm-model-generator -d "Z:\sqlite.db" -e sqlite -o .
|
||||
````
|
||||
## Naming strategy
|
||||
If you want to generate custom names for properties in generated entities you need to use custom naming strategy. You need to create your own version of [NamingStrategy](https://github.com/Kononnable/typeorm-model-generator/blob/master/src/NamingStrategy.ts) and pass it as command parameter.
|
||||
|
||||
```typeorm-model-generator -d typeorm_mg --namingStrategy=./NamingStrategy -e sqlite -db /tmp/sqliteto.db```
|
||||
|
@ -1,6 +1,9 @@
|
||||
parsers:
|
||||
javascript:
|
||||
enable_partials: yes
|
||||
comment:
|
||||
layout: header
|
||||
behavior: default
|
||||
comment: off
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
threshold: 50
|
||||
|
@ -1,54 +0,0 @@
|
||||
version: '2'
|
||||
services:
|
||||
|
||||
# mysql
|
||||
mysql:
|
||||
image: "mysql:5.7.19"
|
||||
container_name: "typeorm-mg-mysql"
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: "!Passw0rd"
|
||||
|
||||
# mariadb
|
||||
mariadb:
|
||||
image: "mariadb:10.2.9"
|
||||
container_name: "typeorm-mg-mariadb"
|
||||
ports:
|
||||
- "3307:3306"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: "!Passw0rd"
|
||||
|
||||
# postgres
|
||||
postgres:
|
||||
image: "postgres:10.0"
|
||||
container_name: "typeorm-mg-postgres"
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_PASSWORD: "!Passw0rd"
|
||||
|
||||
# mssql
|
||||
mssql:
|
||||
image: "microsoft/mssql-server-linux:2017-CU4"
|
||||
container_name: "typeorm-mg-mssql"
|
||||
ports:
|
||||
- "1433:1433"
|
||||
environment:
|
||||
ACCEPT_EULA: "Y"
|
||||
SA_PASSWORD: "!Passw0rd"
|
||||
|
||||
# oracle
|
||||
# oracle:
|
||||
# image: "store/oracle/database-enterprise:12.2.0.1-slim"
|
||||
# container_name: "typeorm-mg-oracle"
|
||||
# ports:
|
||||
# - "1521:1521"
|
||||
# environment:
|
||||
# DB_SID: "sys"
|
||||
# SYS_PASSWORD: "ORCLCDB"
|
||||
|
||||
# oracle_client:
|
||||
# image: "store/oracle/database-instantclient:12.2.0.1"
|
||||
# container_name: "typeorm-mg-oracle-client"
|
||||
|
@ -3,40 +3,48 @@ services:
|
||||
|
||||
# mysql
|
||||
mysql:
|
||||
image: "mysql:5.7.19"
|
||||
image: "mysql:5.7.10"
|
||||
container_name: "typeorm-mg-mysql"
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: "!Passw0rd"
|
||||
MYSQL_ROOT_PASSWORD: "admin"
|
||||
MYSQL_USER: "test"
|
||||
MYSQL_PASSWORD: "test"
|
||||
MYSQL_DATABASE: "test"
|
||||
|
||||
# mariadb
|
||||
mariadb:
|
||||
image: "mariadb:10.2.9"
|
||||
image: "mariadb:10.1.16"
|
||||
container_name: "typeorm-mg-mariadb"
|
||||
ports:
|
||||
- "3307:3306"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: "!Passw0rd"
|
||||
MYSQL_ROOT_PASSWORD: "admin"
|
||||
MYSQL_USER: "test"
|
||||
MYSQL_PASSWORD: "test"
|
||||
MYSQL_DATABASE: "test"
|
||||
|
||||
# postgres
|
||||
postgres:
|
||||
image: "postgres:10.0"
|
||||
image: "mdillon/postgis:9.6"
|
||||
container_name: "typeorm-mg-postgres"
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_PASSWORD: "!Passw0rd"
|
||||
POSTGRES_USER: "test"
|
||||
POSTGRES_PASSWORD: "test"
|
||||
POSTGRES_DB: "test"
|
||||
|
||||
# mssql
|
||||
mssql:
|
||||
image: "microsoft/mssql-server-linux:2017-CU4"
|
||||
image: "mcr.microsoft.com/mssql/server:2017-GA-ubuntu"
|
||||
container_name: "typeorm-mg-mssql"
|
||||
ports:
|
||||
- "1433:1433"
|
||||
environment:
|
||||
SA_PASSWORD: "Admin12345"
|
||||
ACCEPT_EULA: "Y"
|
||||
SA_PASSWORD: "!Passw0rd"
|
||||
|
||||
# oracle
|
||||
oracle:
|
||||
|
78
gulpfile.js
78
gulpfile.js
@ -1,78 +0,0 @@
|
||||
const gulp = require('gulp')
|
||||
const ts = require("gulp-typescript");
|
||||
const sourcemaps = require("gulp-sourcemaps");
|
||||
const clean = require("gulp-clean");
|
||||
const shell = require('gulp-shell');
|
||||
const istanbul = require('gulp-istanbul');
|
||||
const mocha = require('gulp-mocha');
|
||||
const remapIstanbul = require('remap-istanbul/lib/gulpRemapIstanbul');
|
||||
|
||||
gulp.task('compile', ['clean'], function () {
|
||||
var tsProject = ts.createProject('tsconfig.json');
|
||||
return tsProject.src()
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(tsProject())
|
||||
.pipe(sourcemaps.write('.'))
|
||||
.pipe(gulp.dest('dist'))
|
||||
});
|
||||
|
||||
gulp.task('clean', function () {
|
||||
return gulp.src(['dist', 'coverage', 'output'], { read: false })
|
||||
.pipe(clean());
|
||||
});
|
||||
|
||||
gulp.task('prettier', function () {
|
||||
return gulp.src('.prettierrc')
|
||||
.pipe(shell(['prettier ./src/**/*.ts --write']))
|
||||
});
|
||||
|
||||
gulp.task('pre-commit', ['prettier'], function () {
|
||||
return gulp.src('package.json', { read: false })
|
||||
.pipe(shell(['git update-index --again']))
|
||||
})
|
||||
|
||||
gulp.task('watch', function () {
|
||||
gulp.src('tsconfig.json')
|
||||
.pipe(shell(['tsc -w']))
|
||||
|
||||
var watcher = gulp.watch(['src/**/*.ts', 'test/**/*.ts']);
|
||||
|
||||
watcher.on('change', function (changeInfo) {
|
||||
console.log('File ' + changeInfo.path + ' was ' + changeInfo.type + '.');
|
||||
if (changeInfo.type == 'deleted') {
|
||||
let jsFilePath = changeInfo.path
|
||||
.split('.ts').join('.js')
|
||||
.split('\\').join('/')
|
||||
.split('/src/').join('/dist/src/')
|
||||
.split('/test/').join('/dist/test/');
|
||||
return gulp.src([jsFilePath, jsFilePath + '.map'], { read: false })
|
||||
.pipe(clean());
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
gulp.task('pre-test', function () {
|
||||
return gulp.src(['dist/src/**/*.js'])
|
||||
.pipe(istanbul())
|
||||
.pipe(istanbul.hookRequire());
|
||||
});
|
||||
|
||||
gulp.task('test', ['pre-test'], function () {
|
||||
return gulp.src(['dist/test/**/*.test.js'], { read: false })
|
||||
.pipe(mocha())
|
||||
});
|
||||
|
||||
gulp.task('coveragePost', ['test'], function () {
|
||||
return gulp.src(['dist/test/**/*.test.js'], { read: false })
|
||||
.pipe(istanbul.writeReports())
|
||||
});
|
||||
|
||||
gulp.task('coverageRemap', ['coveragePost'], function () {
|
||||
var GulpStream = gulp.src('coverage/coverage-final.json')
|
||||
.pipe(remapIstanbul())
|
||||
.pipe(gulp.dest('coverage/remapped'));
|
||||
if ((process.env.CI == 'true')) {
|
||||
GulpStream = GulpStream.pipe(shell(['codecov --file=./coverage/remapped/coverage-final.json ']));
|
||||
}
|
||||
return GulpStream;
|
||||
})
|
10138
package-lock.json
generated
10138
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
145
package.json
145
package.json
@ -1,73 +1,78 @@
|
||||
{
|
||||
"name": "typeorm-model-generator",
|
||||
"version": "0.2.16",
|
||||
"description": "Generates models for TypeORM from existing databases.",
|
||||
"bin": "bin/typeorm-model-generator",
|
||||
"scripts": {
|
||||
"setup": "npm install && typings install",
|
||||
"start": " node ./dist/src/index.js",
|
||||
"test": "gulp coverageRemap",
|
||||
"compile": "gulp compile",
|
||||
"precommit": "gulp pre-commit"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Kononnable/typeorm-model-generator.git"
|
||||
},
|
||||
"author": "Kononnable",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Kononnable/typeorm-model-generator/issues"
|
||||
},
|
||||
"homepage": "https://github.com/Kononnable/typeorm-model-generator#readme",
|
||||
"dependencies": {
|
||||
"change-case": "^3.0.2",
|
||||
"handlebars": "^4.0.11",
|
||||
"mssql": "^4.0.4",
|
||||
"mysql": "^2.15.0",
|
||||
"oracledb": "^2.2.0",
|
||||
"pg": "^7.4.0",
|
||||
"reflect-metadata": "^0.1.10",
|
||||
"typeorm": "^0.2.4",
|
||||
"typescript": "^2.8.3",
|
||||
"yargs": "^11.1.0",
|
||||
"sqlite3": "^4.0.0",
|
||||
"yn": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.1.3",
|
||||
"@types/chai-as-promised": "7.1.0",
|
||||
"@types/chai-subset": "^1.3.1",
|
||||
"@types/fs-extra": "^5.0.2",
|
||||
"@types/handlebars": "^4.0.37",
|
||||
"@types/mocha": "^5.2.0",
|
||||
"@types/mssql": "^4.0.7",
|
||||
"@types/mysql": "2.15.4",
|
||||
"@types/node": "^10.0.1",
|
||||
"@types/oracledb": "^1.11.34",
|
||||
"@types/pg": "^7.4.8",
|
||||
"@types/sinon": "^5.0.0",
|
||||
"chai": "^4.1.2",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-subset": "^1.6.0",
|
||||
"codecov": "^3.0.1",
|
||||
"dotenv": "^5.0.1",
|
||||
"fs-extra": "^6.0.0",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-clean": "^0.4.0",
|
||||
"gulp-istanbul": "^1.1.3",
|
||||
"gulp-mocha": "^6.0.0",
|
||||
"gulp-shell": "^0.6.5",
|
||||
"gulp-sourcemaps": "^2.6.4",
|
||||
"gulp-typescript": "^4.0.2",
|
||||
"husky": "^0.14.3",
|
||||
"istanbul": "^0.4.5",
|
||||
"lint-staged": "^7.0.5",
|
||||
"mocha": "^5.1.1",
|
||||
"prettier": "^1.12.1",
|
||||
"remap-istanbul": "^0.11.1",
|
||||
"sinon": "^5.0.1",
|
||||
"sinon-chai": "^3.0.0",
|
||||
"typings": "^2.1.1"
|
||||
"name": "typeorm-model-generator",
|
||||
"version": "0.3.2",
|
||||
"description": "Generates models for TypeORM from existing databases.",
|
||||
"bin": "bin/typeorm-model-generator",
|
||||
"scripts": {
|
||||
"setup": "npm install",
|
||||
"start": " node ./dist/src/index.js",
|
||||
"compile": "npm run clean && tsc",
|
||||
"test": "istanbul cover ./node_modules/mocha/bin/_mocha dist/test/**/*.test.js -- -R spec --bail",
|
||||
"posttest": "remap-istanbul -i ./coverage/coverage.json -o ./coverage/lcov.info -t lcovonly && remap-istanbul -i ./coverage/coverage.json -o ./coverage/coverage.json",
|
||||
"clean": "rimraf dist coverage output",
|
||||
"prettier": "prettier --write ./src/*.ts ./src/**/*.ts",
|
||||
"prepack": "npm run compile"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Kononnable/typeorm-model-generator.git"
|
||||
},
|
||||
"author": "Kononnable",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Kononnable/typeorm-model-generator/issues"
|
||||
},
|
||||
"homepage": "https://github.com/Kononnable/typeorm-model-generator#readme",
|
||||
"dependencies": {
|
||||
"change-case": "^3.1.0",
|
||||
"fs-extra": "^7.0.1",
|
||||
"handlebars": "^4.0.12",
|
||||
"inquirer": "^6.2.2",
|
||||
"mssql": "^4.3.2",
|
||||
"mysql": "^2.16.0",
|
||||
"pg": "^7.8.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"sqlite3": "^4.0.6",
|
||||
"typeorm": "^0.2.13",
|
||||
"typescript": "^3.3.3333",
|
||||
"yargs": "^13.2.1",
|
||||
"yn": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.1.7",
|
||||
"@types/chai-as-promised": "7.1.0",
|
||||
"@types/chai-subset": "^1.3.2",
|
||||
"@types/fs-extra": "^5.0.5",
|
||||
"@types/handlebars": "^4.0.40",
|
||||
"@types/inquirer": "0.0.44",
|
||||
"@types/mocha": "^5.2.6",
|
||||
"@types/mssql": "^4.0.12",
|
||||
"@types/mysql": "2.15.5",
|
||||
"@types/node": "^11.10.4",
|
||||
"@types/oracledb": "^1.11.34",
|
||||
"@types/pg": "^7.4.13",
|
||||
"@types/sinon": "^7.0.8",
|
||||
"@types/yargs": "^12.0.1",
|
||||
"chai": "^4.2.0",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-subset": "^1.6.0",
|
||||
"codecov": "^3.2.0",
|
||||
"dotenv": "^6.2.0",
|
||||
"husky": "^1.3.1",
|
||||
"istanbul": "^0.4.5",
|
||||
"lint-staged": "^8.1.5",
|
||||
"mocha": "^6.0.2",
|
||||
"prettier": "^1.16.4",
|
||||
"remap-istanbul": "^0.13.0",
|
||||
"rimraf": "^2.6.3",
|
||||
"sinon": "^7.2.6",
|
||||
"sinon-chai": "^3.3.0",
|
||||
"tslint": "^5.13.1",
|
||||
"tslint-config-prettier": "^1.18.0"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "npm run prettier && git update-index --again"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
14
src/AbstractNamingStrategy.ts
Normal file
14
src/AbstractNamingStrategy.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { EntityInfo } from "./models/EntityInfo";
|
||||
import { RelationInfo } from "./models/RelationInfo";
|
||||
|
||||
export abstract class AbstractNamingStrategy {
|
||||
public abstract relationName(
|
||||
columnName: string,
|
||||
relation: RelationInfo,
|
||||
dbModel: EntityInfo[]
|
||||
): string;
|
||||
|
||||
public abstract entityName(entityName: string): string;
|
||||
|
||||
public abstract columnName(columnName: string): string;
|
||||
}
|
685
src/Engine.ts
685
src/Engine.ts
@ -1,204 +1,306 @@
|
||||
import { AbstractDriver } from "./drivers/AbstractDriver";
|
||||
import { DatabaseModel } from "./models/DatabaseModel";
|
||||
import * as Handlebars from "handlebars";
|
||||
import fs = require("fs");
|
||||
import path = require("path");
|
||||
import * as TomgUtils from "./Utils";
|
||||
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 class Engine {
|
||||
constructor(
|
||||
private driver: AbstractDriver,
|
||||
public Options: EngineOptions
|
||||
) {}
|
||||
export function createDriver(driverName: string): AbstractDriver {
|
||||
switch (driverName) {
|
||||
case "mssql":
|
||||
return new MssqlDriver();
|
||||
case "postgres":
|
||||
return new PostgresDriver();
|
||||
case "mysql":
|
||||
return new MysqlDriver();
|
||||
case "mariadb":
|
||||
return new MariaDbDriver();
|
||||
case "oracle":
|
||||
return new OracleDriver();
|
||||
case "sqlite":
|
||||
return new SqliteDriver();
|
||||
default:
|
||||
TomgUtils.LogError("Database engine not recognized.", false);
|
||||
throw new Error("Database engine not recognized.");
|
||||
}
|
||||
}
|
||||
|
||||
public async createModelFromDatabase(): Promise<boolean> {
|
||||
let dbModel = await this.getEntitiesInfo(
|
||||
this.Options.databaseName,
|
||||
this.Options.host,
|
||||
this.Options.port,
|
||||
this.Options.user,
|
||||
this.Options.password,
|
||||
this.Options.schemaName,
|
||||
this.Options.ssl
|
||||
export async function createModelFromDatabase(
|
||||
driver: AbstractDriver,
|
||||
connectionOptions: IConnectionOptions,
|
||||
generationOptions: IGenerationOptions
|
||||
) {
|
||||
let dbModel = await dataCollectionPhase(driver, connectionOptions);
|
||||
if (dbModel.length === 0) {
|
||||
TomgUtils.LogError(
|
||||
"Tables not found in selected database. Skipping creation of typeorm model.",
|
||||
false
|
||||
);
|
||||
if (dbModel.entities.length > 0) {
|
||||
this.createModelFromMetadata(dbModel);
|
||||
} else {
|
||||
TomgUtils.LogError(
|
||||
"Tables not found in selected database. Skipping creation of typeorm model.",
|
||||
false
|
||||
);
|
||||
}
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
private async getEntitiesInfo(
|
||||
database: string,
|
||||
server: string,
|
||||
port: number,
|
||||
user: string,
|
||||
password: string,
|
||||
schemaName: string,
|
||||
ssl: boolean
|
||||
): Promise<DatabaseModel> {
|
||||
return await this.driver.GetDataFromServer(
|
||||
database,
|
||||
server,
|
||||
port,
|
||||
user,
|
||||
password,
|
||||
schemaName,
|
||||
ssl
|
||||
);
|
||||
dbModel = modelCustomizationPhase(
|
||||
dbModel,
|
||||
generationOptions,
|
||||
driver.defaultValues
|
||||
);
|
||||
modelGenerationPhase(connectionOptions, generationOptions, dbModel);
|
||||
}
|
||||
export async function dataCollectionPhase(
|
||||
driver: AbstractDriver,
|
||||
connectionOptions: IConnectionOptions
|
||||
) {
|
||||
return await driver.GetDataFromServer(connectionOptions);
|
||||
}
|
||||
|
||||
export function modelCustomizationPhase(
|
||||
dbModel: EntityInfo[],
|
||||
generationOptions: IGenerationOptions,
|
||||
defaultValues: DataTypeDefaults
|
||||
) {
|
||||
let namingStrategy: NamingStrategy;
|
||||
if (
|
||||
generationOptions.customNamingStrategyPath &&
|
||||
generationOptions.customNamingStrategyPath !== ""
|
||||
) {
|
||||
// tslint:disable-next-line:no-var-requires
|
||||
const req = require(generationOptions.customNamingStrategyPath);
|
||||
namingStrategy = new req.NamingStrategy();
|
||||
} else {
|
||||
namingStrategy = new NamingStrategy();
|
||||
}
|
||||
private createModelFromMetadata(databaseModel: DatabaseModel) {
|
||||
this.createHandlebarsHelpers();
|
||||
let templatePath = path.resolve(__dirname, "../../src/entity.mst");
|
||||
let template = fs.readFileSync(templatePath, "UTF-8");
|
||||
let resultPath = this.Options.resultsPath;
|
||||
if (!fs.existsSync(resultPath)) fs.mkdirSync(resultPath);
|
||||
let entitesPath = resultPath;
|
||||
if (!this.Options.noConfigs) {
|
||||
this.createTsConfigFile(resultPath);
|
||||
this.createTypeOrmConfig(resultPath);
|
||||
entitesPath = path.resolve(resultPath, "./entities");
|
||||
if (!fs.existsSync(entitesPath)) fs.mkdirSync(entitesPath);
|
||||
}
|
||||
let compliedTemplate = Handlebars.compile(template, { noEscape: true });
|
||||
databaseModel.entities.forEach(element => {
|
||||
element.Imports = [];
|
||||
element.Columns.forEach(column => {
|
||||
column.relations.forEach(relation => {
|
||||
if (element.EntityName !== relation.relatedTable) {
|
||||
element.Imports.push(relation.relatedTable);
|
||||
}
|
||||
dbModel = setRelationId(generationOptions, dbModel);
|
||||
dbModel = applyNamingStrategy(namingStrategy, dbModel);
|
||||
dbModel = addImportsAndGenerationOptions(dbModel, generationOptions);
|
||||
dbModel = removeColumnDefaultProperties(dbModel, defaultValues);
|
||||
return dbModel;
|
||||
}
|
||||
function removeColumnDefaultProperties(
|
||||
dbModel: EntityInfo[],
|
||||
defaultValues: DataTypeDefaults
|
||||
) {
|
||||
if (!defaultValues) {
|
||||
return dbModel;
|
||||
}
|
||||
dbModel.forEach(entity => {
|
||||
entity.Columns.forEach(column => {
|
||||
const defVal = defaultValues[column.options.type as any];
|
||||
if (defVal) {
|
||||
if (
|
||||
column.options.length &&
|
||||
defVal.length &&
|
||||
column.options.length === defVal.length
|
||||
) {
|
||||
column.options.length = undefined;
|
||||
}
|
||||
if (
|
||||
column.options.precision &&
|
||||
defVal.precision &&
|
||||
column.options.precision === defVal.precision
|
||||
) {
|
||||
column.options.precision = undefined;
|
||||
}
|
||||
if (
|
||||
column.options.scale &&
|
||||
defVal.scale &&
|
||||
column.options.scale === defVal.scale
|
||||
) {
|
||||
column.options.scale = undefined;
|
||||
}
|
||||
if (
|
||||
column.options.width &&
|
||||
defVal.width &&
|
||||
column.options.width === defVal.width
|
||||
) {
|
||||
column.options.width = undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return dbModel;
|
||||
}
|
||||
function addImportsAndGenerationOptions(
|
||||
dbModel: EntityInfo[],
|
||||
generationOptions: IGenerationOptions
|
||||
) {
|
||||
dbModel.forEach(element => {
|
||||
element.Imports = [];
|
||||
element.Columns.forEach(column => {
|
||||
column.relations.forEach(relation => {
|
||||
if (element.tsEntityName !== relation.relatedTable) {
|
||||
element.Imports.push(relation.relatedTable);
|
||||
}
|
||||
});
|
||||
});
|
||||
element.GenerateConstructor = generationOptions.generateConstructor;
|
||||
element.IsActiveRecord = generationOptions.activeRecord;
|
||||
element.Imports.filter((elem, index, self) => {
|
||||
return index === self.indexOf(elem);
|
||||
});
|
||||
});
|
||||
return dbModel;
|
||||
}
|
||||
|
||||
function setRelationId(
|
||||
generationOptions: IGenerationOptions,
|
||||
model: EntityInfo[]
|
||||
) {
|
||||
if (generationOptions.relationIds) {
|
||||
model.forEach(ent => {
|
||||
ent.Columns.forEach(col => {
|
||||
col.relations.map(rel => {
|
||||
rel.relationIdField = rel.isOwner;
|
||||
});
|
||||
});
|
||||
element.GenerateConstructor = this.Options.constructor;
|
||||
element.Imports.filter(function(elem, index, self) {
|
||||
return index === self.indexOf(elem);
|
||||
});
|
||||
let casedFileName = "";
|
||||
switch (this.Options.convertCaseFile) {
|
||||
case "camel":
|
||||
casedFileName = changeCase.camelCase(element.EntityName);
|
||||
break;
|
||||
case "param":
|
||||
casedFileName = changeCase.paramCase(element.EntityName);
|
||||
break;
|
||||
case "pascal":
|
||||
casedFileName = changeCase.pascalCase(element.EntityName);
|
||||
break;
|
||||
case "none":
|
||||
casedFileName = element.EntityName;
|
||||
break;
|
||||
}
|
||||
let resultFilePath = path.resolve(
|
||||
entitesPath,
|
||||
casedFileName + ".ts"
|
||||
);
|
||||
let rendered = compliedTemplate(element);
|
||||
fs.writeFileSync(resultFilePath, rendered, {
|
||||
encoding: "UTF-8",
|
||||
flag: "w"
|
||||
});
|
||||
});
|
||||
}
|
||||
private createHandlebarsHelpers() {
|
||||
Handlebars.registerHelper("curly", open => {
|
||||
return open ? "{" : "}";
|
||||
});
|
||||
Handlebars.registerHelper("toEntityName", str => {
|
||||
let retStr = "";
|
||||
switch (this.Options.convertCaseEntity) {
|
||||
case "camel":
|
||||
retStr = changeCase.camelCase(str);
|
||||
break;
|
||||
case "pascal":
|
||||
retStr = changeCase.pascalCase(str);
|
||||
break;
|
||||
case "none":
|
||||
retStr = str;
|
||||
break;
|
||||
}
|
||||
return retStr;
|
||||
});
|
||||
Handlebars.registerHelper("concat", (stra, strb) => {
|
||||
return stra + strb;
|
||||
});
|
||||
Handlebars.registerHelper("toFileName", str => {
|
||||
let retStr = "";
|
||||
switch (this.Options.convertCaseFile) {
|
||||
case "camel":
|
||||
retStr = changeCase.camelCase(str);
|
||||
break;
|
||||
case "param":
|
||||
retStr = changeCase.paramCase(str);
|
||||
break;
|
||||
case "pascal":
|
||||
retStr = changeCase.pascalCase(str);
|
||||
break;
|
||||
case "none":
|
||||
retStr = str;
|
||||
break;
|
||||
}
|
||||
return retStr;
|
||||
});
|
||||
Handlebars.registerHelper("toPropertyName", str => {
|
||||
let retStr = "";
|
||||
switch (this.Options.convertCaseProperty) {
|
||||
case "camel":
|
||||
retStr = changeCase.camelCase(str);
|
||||
break;
|
||||
case "pascal":
|
||||
retStr = changeCase.pascalCase(str);
|
||||
break;
|
||||
case "none":
|
||||
retStr = str;
|
||||
break;
|
||||
}
|
||||
return retStr;
|
||||
});
|
||||
Handlebars.registerHelper("toLowerCase", str => {
|
||||
return str.toLowerCase();
|
||||
});
|
||||
Handlebars.registerHelper("toLazy", str => {
|
||||
if (this.Options.lazy) return `Promise<${str}>`;
|
||||
else return str;
|
||||
});
|
||||
Handlebars.registerHelper({
|
||||
eq: function(v1, v2) {
|
||||
return v1 === v2;
|
||||
},
|
||||
ne: function(v1, v2) {
|
||||
return v1 !== v2;
|
||||
},
|
||||
lt: function(v1, v2) {
|
||||
return v1 < v2;
|
||||
},
|
||||
gt: function(v1, v2) {
|
||||
return v1 > v2;
|
||||
},
|
||||
lte: function(v1, v2) {
|
||||
return v1 <= v2;
|
||||
},
|
||||
gte: function(v1, v2) {
|
||||
return v1 >= v2;
|
||||
},
|
||||
and: function(v1, v2) {
|
||||
return v1 && v2;
|
||||
},
|
||||
or: function(v1, v2) {
|
||||
return v1 || v2;
|
||||
}
|
||||
});
|
||||
return model;
|
||||
}
|
||||
export function modelGenerationPhase(
|
||||
connectionOptions: IConnectionOptions,
|
||||
generationOptions: IGenerationOptions,
|
||||
databaseModel: EntityInfo[]
|
||||
) {
|
||||
createHandlebarsHelpers(generationOptions);
|
||||
const templatePath = path.resolve(__dirname, "../../src/entity.mst");
|
||||
const template = fs.readFileSync(templatePath, "UTF-8");
|
||||
const resultPath = generationOptions.resultsPath;
|
||||
if (!fs.existsSync(resultPath)) {
|
||||
fs.mkdirSync(resultPath);
|
||||
}
|
||||
let entitesPath = resultPath;
|
||||
if (!generationOptions.noConfigs) {
|
||||
createTsConfigFile(resultPath);
|
||||
createTypeOrmConfig(resultPath, connectionOptions);
|
||||
entitesPath = path.resolve(resultPath, "./entities");
|
||||
if (!fs.existsSync(entitesPath)) {
|
||||
fs.mkdirSync(entitesPath);
|
||||
}
|
||||
}
|
||||
const compliedTemplate = Handlebars.compile(template, {
|
||||
noEscape: true
|
||||
});
|
||||
databaseModel.forEach(element => {
|
||||
let casedFileName = "";
|
||||
switch (generationOptions.convertCaseFile) {
|
||||
case "camel":
|
||||
casedFileName = changeCase.camelCase(element.tsEntityName);
|
||||
break;
|
||||
case "param":
|
||||
casedFileName = changeCase.paramCase(element.tsEntityName);
|
||||
break;
|
||||
case "pascal":
|
||||
casedFileName = changeCase.pascalCase(element.tsEntityName);
|
||||
break;
|
||||
case "none":
|
||||
casedFileName = element.tsEntityName;
|
||||
break;
|
||||
}
|
||||
const resultFilePath = path.resolve(entitesPath, casedFileName + ".ts");
|
||||
const rendered = compliedTemplate(element);
|
||||
fs.writeFileSync(resultFilePath, rendered, {
|
||||
encoding: "UTF-8",
|
||||
flag: "w"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//TODO:Move to mustache template file
|
||||
private createTsConfigFile(resultPath) {
|
||||
fs.writeFileSync(
|
||||
path.resolve(resultPath, "tsconfig.json"),
|
||||
`{"compilerOptions": {
|
||||
function createHandlebarsHelpers(generationOptions: IGenerationOptions) {
|
||||
Handlebars.registerHelper("curly", open => (open ? "{" : "}"));
|
||||
Handlebars.registerHelper("toEntityName", str => {
|
||||
let retStr = "";
|
||||
switch (generationOptions.convertCaseEntity) {
|
||||
case "camel":
|
||||
retStr = changeCase.camelCase(str);
|
||||
break;
|
||||
case "pascal":
|
||||
retStr = changeCase.pascalCase(str);
|
||||
break;
|
||||
case "none":
|
||||
retStr = str;
|
||||
break;
|
||||
}
|
||||
return retStr;
|
||||
});
|
||||
Handlebars.registerHelper("concat", (stra, strb) => {
|
||||
return stra + strb;
|
||||
});
|
||||
Handlebars.registerHelper("toFileName", str => {
|
||||
let retStr = "";
|
||||
switch (generationOptions.convertCaseFile) {
|
||||
case "camel":
|
||||
retStr = changeCase.camelCase(str);
|
||||
break;
|
||||
case "param":
|
||||
retStr = changeCase.paramCase(str);
|
||||
break;
|
||||
case "pascal":
|
||||
retStr = changeCase.pascalCase(str);
|
||||
break;
|
||||
case "none":
|
||||
retStr = str;
|
||||
break;
|
||||
}
|
||||
return retStr;
|
||||
});
|
||||
Handlebars.registerHelper("printPropertyVisibility", () =>
|
||||
generationOptions.propertyVisibility !== "none"
|
||||
? generationOptions.propertyVisibility + " "
|
||||
: ""
|
||||
);
|
||||
Handlebars.registerHelper("toPropertyName", str => {
|
||||
let retStr = "";
|
||||
switch (generationOptions.convertCaseProperty) {
|
||||
case "camel":
|
||||
retStr = changeCase.camelCase(str);
|
||||
break;
|
||||
case "pascal":
|
||||
retStr = changeCase.pascalCase(str);
|
||||
break;
|
||||
case "none":
|
||||
retStr = str;
|
||||
break;
|
||||
}
|
||||
return retStr;
|
||||
});
|
||||
Handlebars.registerHelper("toLowerCase", str => str.toLowerCase());
|
||||
Handlebars.registerHelper("tolowerCaseFirst", str =>
|
||||
changeCase.lowerCaseFirst(str)
|
||||
);
|
||||
Handlebars.registerHelper("toLazy", str => {
|
||||
if (generationOptions.lazy) {
|
||||
return `Promise<${str}>`;
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
});
|
||||
Handlebars.registerHelper({
|
||||
and: (v1, v2) => v1 && v2,
|
||||
eq: (v1, v2) => v1 === v2,
|
||||
gt: (v1, v2) => v1 > v2,
|
||||
gte: (v1, v2) => v1 >= v2,
|
||||
lt: (v1, v2) => v1 < v2,
|
||||
lte: (v1, v2) => v1 <= v2,
|
||||
ne: (v1, v2) => v1 !== v2,
|
||||
or: (v1, v2) => v1 || v2
|
||||
});
|
||||
}
|
||||
|
||||
// TODO:Move to mustache template file
|
||||
function createTsConfigFile(resultPath) {
|
||||
fs.writeFileSync(
|
||||
path.resolve(resultPath, "tsconfig.json"),
|
||||
`{"compilerOptions": {
|
||||
"lib": ["es5", "es6"],
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
@ -207,68 +309,165 @@ export class Engine {
|
||||
"experimentalDecorators": true,
|
||||
"sourceMap": true
|
||||
}}`,
|
||||
{ encoding: "UTF-8", flag: "w" }
|
||||
);
|
||||
}
|
||||
private createTypeOrmConfig(resultPath) {
|
||||
if (this.Options.schemaName == "") {
|
||||
fs.writeFileSync(
|
||||
path.resolve(resultPath, "ormconfig.json"),
|
||||
`[
|
||||
{ encoding: "UTF-8", flag: "w" }
|
||||
);
|
||||
}
|
||||
function createTypeOrmConfig(
|
||||
resultPath: string,
|
||||
connectionOptions: IConnectionOptions
|
||||
) {
|
||||
if (connectionOptions.schemaName === "") {
|
||||
fs.writeFileSync(
|
||||
path.resolve(resultPath, "ormconfig.json"),
|
||||
`[
|
||||
{
|
||||
"name": "default",
|
||||
"type": "${this.Options.databaseType}",
|
||||
"host": "${this.Options.host}",
|
||||
"port": ${this.Options.port},
|
||||
"username": "${this.Options.user}",
|
||||
"password": "${this.Options.password}",
|
||||
"database": "${this.Options.databaseName}",
|
||||
"synchronize": false
|
||||
"entities": [
|
||||
"entities/*.js"
|
||||
]
|
||||
}
|
||||
]`,
|
||||
{ encoding: "UTF-8", flag: "w" }
|
||||
);
|
||||
} else {
|
||||
fs.writeFileSync(
|
||||
path.resolve(resultPath, "ormconfig.json"),
|
||||
`[
|
||||
{
|
||||
"name": "default",
|
||||
"type": "${this.Options.databaseType}",
|
||||
"host": "${this.Options.host}",
|
||||
"port": ${this.Options.port},
|
||||
"username": "${this.Options.user}",
|
||||
"password": "${this.Options.password}",
|
||||
"database": "${this.Options.databaseName}",
|
||||
"schema": "${this.Options.schemaName}",
|
||||
"type": "${connectionOptions.databaseType}",
|
||||
"host": "${connectionOptions.host}",
|
||||
"port": ${connectionOptions.port},
|
||||
"username": "${connectionOptions.user}",
|
||||
"password": "${connectionOptions.password}",
|
||||
"database": "${connectionOptions.databaseName}",
|
||||
"synchronize": false,
|
||||
"entities": [
|
||||
"entities/*.js"
|
||||
]
|
||||
}
|
||||
]`,
|
||||
{ encoding: "UTF-8", flag: "w" }
|
||||
);
|
||||
}
|
||||
{ encoding: "UTF-8", flag: "w" }
|
||||
);
|
||||
} else {
|
||||
fs.writeFileSync(
|
||||
path.resolve(resultPath, "ormconfig.json"),
|
||||
`[
|
||||
{
|
||||
"name": "default",
|
||||
"type": "${connectionOptions.databaseType}",
|
||||
"host": "${connectionOptions.host}",
|
||||
"port": ${connectionOptions.port},
|
||||
"username": "${connectionOptions.user}",
|
||||
"password": "${connectionOptions.password}",
|
||||
"database": "${connectionOptions.databaseName}",
|
||||
"schema": "${connectionOptions.schemaName}",
|
||||
"synchronize": false,
|
||||
"entities": [
|
||||
"entities/*.js"
|
||||
]
|
||||
}
|
||||
]`,
|
||||
{ encoding: "UTF-8", flag: "w" }
|
||||
);
|
||||
}
|
||||
}
|
||||
export interface EngineOptions {
|
||||
host: string;
|
||||
port: number;
|
||||
databaseName: string;
|
||||
user: string;
|
||||
password: string;
|
||||
resultsPath: string;
|
||||
databaseType: string;
|
||||
schemaName: string;
|
||||
ssl: boolean;
|
||||
noConfigs: boolean;
|
||||
convertCaseFile: "pascal" | "param" | "camel" | "none";
|
||||
convertCaseEntity: "pascal" | "camel" | "none";
|
||||
convertCaseProperty: "pascal" | "camel" | "none";
|
||||
lazy: boolean;
|
||||
constructor: boolean;
|
||||
function applyNamingStrategy(
|
||||
namingStrategy: NamingStrategy,
|
||||
dbModel: EntityInfo[]
|
||||
) {
|
||||
dbModel = changeRelationNames(dbModel);
|
||||
dbModel = changeEntityNames(dbModel);
|
||||
dbModel = changeColumnNames(dbModel);
|
||||
return dbModel;
|
||||
|
||||
function changeRelationNames(model: EntityInfo[]) {
|
||||
model.forEach(entity => {
|
||||
entity.Columns.forEach(column => {
|
||||
column.relations.forEach(relation => {
|
||||
const newName = namingStrategy.relationName(
|
||||
column.tsName,
|
||||
relation,
|
||||
model
|
||||
);
|
||||
model.forEach(entity2 => {
|
||||
entity2.Columns.forEach(column2 => {
|
||||
column2.relations.forEach(relation2 => {
|
||||
if (
|
||||
relation2.relatedTable ===
|
||||
entity.tsEntityName &&
|
||||
relation2.ownerColumn === column.tsName
|
||||
) {
|
||||
relation2.ownerColumn = newName;
|
||||
}
|
||||
if (
|
||||
relation2.relatedTable ===
|
||||
entity.tsEntityName &&
|
||||
relation2.relatedColumn === column.tsName
|
||||
) {
|
||||
relation2.relatedColumn = newName;
|
||||
}
|
||||
if (relation.isOwner) {
|
||||
entity.Indexes.forEach(ind => {
|
||||
ind.columns
|
||||
.filter(
|
||||
col =>
|
||||
col.name === column.tsName
|
||||
)
|
||||
.forEach(
|
||||
col => (col.name = newName)
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
column.tsName = newName;
|
||||
});
|
||||
});
|
||||
});
|
||||
return dbModel;
|
||||
}
|
||||
|
||||
function changeColumnNames(model: EntityInfo[]) {
|
||||
model.forEach(entity => {
|
||||
entity.Columns.forEach(column => {
|
||||
const newName = namingStrategy.columnName(column.tsName);
|
||||
entity.Indexes.forEach(index => {
|
||||
index.columns
|
||||
.filter(column2 => column2.name === column.tsName)
|
||||
.forEach(column2 => (column2.name = newName));
|
||||
});
|
||||
model.forEach(entity2 => {
|
||||
entity2.Columns.forEach(column2 => {
|
||||
column2.relations
|
||||
.filter(
|
||||
relation =>
|
||||
relation.relatedTable ===
|
||||
entity.tsEntityName &&
|
||||
relation.relatedColumn === column.tsName
|
||||
)
|
||||
.map(v => (v.relatedColumn = newName));
|
||||
column2.relations
|
||||
.filter(
|
||||
relation =>
|
||||
relation.relatedTable ===
|
||||
entity.tsEntityName &&
|
||||
relation.ownerColumn === column.tsName
|
||||
)
|
||||
.map(v => (v.ownerColumn = newName));
|
||||
});
|
||||
});
|
||||
|
||||
column.tsName = newName;
|
||||
});
|
||||
});
|
||||
return model;
|
||||
}
|
||||
function changeEntityNames(entities: EntityInfo[]) {
|
||||
entities.forEach(entity => {
|
||||
const newName = namingStrategy.entityName(entity.tsEntityName);
|
||||
entities.forEach(entity2 => {
|
||||
entity2.Columns.forEach(column => {
|
||||
column.relations.forEach(relation => {
|
||||
if (relation.ownerTable === entity.tsEntityName) {
|
||||
relation.ownerTable = newName;
|
||||
}
|
||||
if (relation.relatedTable === entity.tsEntityName) {
|
||||
relation.relatedTable = newName;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
entity.tsEntityName = newName;
|
||||
});
|
||||
return entities;
|
||||
}
|
||||
}
|
||||
|
10
src/IConnectionOptions.ts
Normal file
10
src/IConnectionOptions.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export 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;
|
||||
}
|
14
src/IGenerationOptions.ts
Normal file
14
src/IGenerationOptions.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export 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;
|
||||
}
|
71
src/NamingStrategy.ts
Normal file
71
src/NamingStrategy.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import { AbstractNamingStrategy } from "./AbstractNamingStrategy";
|
||||
import { EntityInfo } from "./models/EntityInfo";
|
||||
import { RelationInfo } from "./models/RelationInfo";
|
||||
|
||||
export class NamingStrategy extends AbstractNamingStrategy {
|
||||
public relationName(
|
||||
columnOldName: string,
|
||||
relation: RelationInfo,
|
||||
dbModel: EntityInfo[]
|
||||
): string {
|
||||
const isRelationToMany = relation.isOneToMany || relation.isManyToMany;
|
||||
const ownerEntity = dbModel.find(
|
||||
v => v.tsEntityName === relation.ownerTable
|
||||
)!;
|
||||
|
||||
let columnName =
|
||||
columnOldName[0].toLowerCase() +
|
||||
columnOldName.substring(1, columnOldName.length);
|
||||
if (
|
||||
columnName.toLowerCase().endsWith("id") &&
|
||||
!columnName.toLowerCase().endsWith("guid")
|
||||
) {
|
||||
columnName = columnName.substring(
|
||||
0,
|
||||
columnName.toLowerCase().lastIndexOf("id")
|
||||
);
|
||||
}
|
||||
if (!isNaN(parseInt(columnName[columnName.length - 1], 10))) {
|
||||
columnName = columnName.substring(0, columnName.length - 1);
|
||||
}
|
||||
if (!isNaN(parseInt(columnName[columnName.length - 1], 10))) {
|
||||
columnName = columnName.substring(0, columnName.length - 1);
|
||||
}
|
||||
columnName += isRelationToMany ? "s" : "";
|
||||
|
||||
if (
|
||||
relation.relationType !== "ManyToMany" &&
|
||||
columnOldName !== columnName
|
||||
) {
|
||||
if (ownerEntity.Columns.some(v => v.tsName === columnName)) {
|
||||
columnName = columnName + "_";
|
||||
for (let i = 2; i <= ownerEntity.Columns.length; i++) {
|
||||
columnName =
|
||||
columnName.substring(
|
||||
0,
|
||||
columnName.length - i.toString().length
|
||||
) + i.toString();
|
||||
if (
|
||||
ownerEntity.Columns.every(
|
||||
v =>
|
||||
v.tsName !== columnName ||
|
||||
columnName === columnOldName
|
||||
)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return columnName;
|
||||
}
|
||||
|
||||
public entityName(entityName: string): string {
|
||||
return entityName;
|
||||
}
|
||||
|
||||
public columnName(columnName: string): string {
|
||||
return columnName;
|
||||
}
|
||||
}
|
13
src/Utils.ts
13
src/Utils.ts
@ -9,13 +9,16 @@ export function LogError(
|
||||
console.error(`${packageVersion()} node@${process.version}`);
|
||||
console.error(
|
||||
`If you think this is a bug please open an issue including this log on ${
|
||||
(<any>packagejson).bugs.url
|
||||
(packagejson as any).bugs.url
|
||||
}`
|
||||
);
|
||||
if (isABug && !errObject) errObject = new Error().stack;
|
||||
if (!!errObject) console.error(errObject);
|
||||
// process.abort();
|
||||
if (isABug && !errObject) {
|
||||
errObject = new Error().stack;
|
||||
}
|
||||
if (!!errObject) {
|
||||
console.error(errObject);
|
||||
}
|
||||
}
|
||||
export function packageVersion() {
|
||||
return `${(<any>packagejson).name}@${(<any>packagejson).version}`;
|
||||
return `${(packagejson as any).name}@${(packagejson as any).version}`;
|
||||
}
|
||||
|
@ -1,23 +1,29 @@
|
||||
import { EntityInfo } from "./../models/EntityInfo";
|
||||
import { DatabaseModel } from "./../models/DatabaseModel";
|
||||
import * as TomgUtils from "./../Utils";
|
||||
import { RelationInfo } from "../models/RelationInfo";
|
||||
import { ColumnInfo } from "../models/ColumnInfo";
|
||||
import {
|
||||
WithWidthColumnType,
|
||||
WithLengthColumnType,
|
||||
WithPrecisionColumnType,
|
||||
WithLengthColumnType
|
||||
} from "./../../node_modules/typeorm/driver/types/ColumnTypes";
|
||||
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 { RelationInfo } from "../models/RelationInfo";
|
||||
import * as TomgUtils from "../Utils";
|
||||
|
||||
export abstract class AbstractDriver {
|
||||
ColumnTypesWithWidth: WithWidthColumnType[] = [
|
||||
public abstract standardPort: number;
|
||||
public abstract standardSchema: string;
|
||||
public abstract standardUser: string;
|
||||
public abstract defaultValues: DataTypeDefaults;
|
||||
|
||||
public ColumnTypesWithWidth: WithWidthColumnType[] = [
|
||||
"tinyint",
|
||||
"smallint",
|
||||
"mediumint",
|
||||
"int",
|
||||
"bigint"
|
||||
];
|
||||
ColumnTypesWithPrecision: WithPrecisionColumnType[] = [
|
||||
public ColumnTypesWithPrecision: WithPrecisionColumnType[] = [
|
||||
"float",
|
||||
"double",
|
||||
"dec",
|
||||
@ -37,7 +43,7 @@ export abstract class AbstractDriver {
|
||||
"timestamp with time zone",
|
||||
"timestamp with local time zone"
|
||||
];
|
||||
ColumnTypesWithLength: WithLengthColumnType[] = [
|
||||
public ColumnTypesWithLength: WithLengthColumnType[] = [
|
||||
"character varying",
|
||||
"varying character",
|
||||
"nvarchar",
|
||||
@ -53,136 +59,152 @@ export abstract class AbstractDriver {
|
||||
"varbinary"
|
||||
];
|
||||
|
||||
FindManyToManyRelations(dbModel: DatabaseModel) {
|
||||
let manyToManyEntities = dbModel.entities.filter(entity => {
|
||||
return (
|
||||
public abstract GetAllTablesQuery: (
|
||||
schema: string,
|
||||
dbNames: string
|
||||
) => Promise<
|
||||
Array<{
|
||||
TABLE_SCHEMA: string;
|
||||
TABLE_NAME: string;
|
||||
DB_NAME: string;
|
||||
}>
|
||||
>;
|
||||
|
||||
public FindManyToManyRelations(dbModel: EntityInfo[]) {
|
||||
const manyToManyEntities = dbModel.filter(
|
||||
entity =>
|
||||
entity.Columns.filter(column => {
|
||||
return (
|
||||
column.relations.length == 1 &&
|
||||
column.relations.length === 1 &&
|
||||
!column.relations[0].isOneToMany &&
|
||||
column.relations[0].isOwner
|
||||
);
|
||||
}).length == entity.Columns.length
|
||||
);
|
||||
});
|
||||
}).length === entity.Columns.length
|
||||
);
|
||||
manyToManyEntities.map(entity => {
|
||||
let relations: RelationInfo[] = [];
|
||||
relations = entity.Columns.reduce((prev: RelationInfo[], curr) => {
|
||||
return prev.concat(curr.relations);
|
||||
}, relations);
|
||||
let namesOfRelatedTables = relations
|
||||
relations = entity.Columns.reduce(
|
||||
(prev: RelationInfo[], curr) => prev.concat(curr.relations),
|
||||
relations
|
||||
);
|
||||
const namesOfRelatedTables = relations
|
||||
.map(v => v.relatedTable)
|
||||
.filter((v, i, s) => s.indexOf(v) == i);
|
||||
if (namesOfRelatedTables.length == 2) {
|
||||
let relatedTable1 = dbModel.entities.filter(
|
||||
v => v.EntityName == namesOfRelatedTables[0]
|
||||
)[0];
|
||||
.filter((v, i, s) => s.indexOf(v) === i);
|
||||
if (namesOfRelatedTables.length === 2) {
|
||||
const relatedTable1 = dbModel.find(
|
||||
v => v.tsEntityName === namesOfRelatedTables[0]
|
||||
)!;
|
||||
relatedTable1.Columns = relatedTable1.Columns.filter(
|
||||
v =>
|
||||
!v.name
|
||||
!v.tsName
|
||||
.toLowerCase()
|
||||
.startsWith(entity.EntityName.toLowerCase())
|
||||
.startsWith(entity.tsEntityName.toLowerCase())
|
||||
);
|
||||
let relatedTable2 = dbModel.entities.filter(
|
||||
v => v.EntityName == namesOfRelatedTables[1]
|
||||
)[0];
|
||||
const relatedTable2 = dbModel.find(
|
||||
v => v.tsEntityName === namesOfRelatedTables[1]
|
||||
)!;
|
||||
relatedTable2.Columns = relatedTable2.Columns.filter(
|
||||
v =>
|
||||
!v.name
|
||||
!v.tsName
|
||||
.toLowerCase()
|
||||
.startsWith(entity.EntityName.toLowerCase())
|
||||
.startsWith(entity.tsEntityName.toLowerCase())
|
||||
);
|
||||
dbModel.entities = dbModel.entities.filter(ent => {
|
||||
return ent.EntityName != entity.EntityName;
|
||||
dbModel = dbModel.filter(ent => {
|
||||
return ent.tsEntityName !== entity.tsEntityName;
|
||||
});
|
||||
|
||||
let column1 = new ColumnInfo();
|
||||
column1.name = namesOfRelatedTables[1];
|
||||
let col1Rel = new RelationInfo();
|
||||
const column1 = new ColumnInfo();
|
||||
column1.tsName = namesOfRelatedTables[1];
|
||||
column1.options.name = entity.sqlEntityName;
|
||||
|
||||
const col1Rel = new RelationInfo();
|
||||
col1Rel.relatedTable = namesOfRelatedTables[1];
|
||||
col1Rel.relatedColumn = namesOfRelatedTables[1];
|
||||
|
||||
col1Rel.relationType = "ManyToMany";
|
||||
col1Rel.isOwner = true;
|
||||
col1Rel.ownerColumn = namesOfRelatedTables[0];
|
||||
|
||||
column1.relations.push(col1Rel);
|
||||
relatedTable1.Columns.push(column1);
|
||||
|
||||
let column2 = new ColumnInfo();
|
||||
column2.name = namesOfRelatedTables[0];
|
||||
let col2Rel = new RelationInfo();
|
||||
const column2 = new ColumnInfo();
|
||||
column2.tsName = namesOfRelatedTables[0];
|
||||
|
||||
const col2Rel = new RelationInfo();
|
||||
col2Rel.relatedTable = namesOfRelatedTables[0];
|
||||
col2Rel.relatedColumn = namesOfRelatedTables[1];
|
||||
|
||||
col2Rel.relationType = "ManyToMany";
|
||||
col2Rel.isOwner = false;
|
||||
column2.relations.push(col2Rel);
|
||||
relatedTable2.Columns.push(column2);
|
||||
}
|
||||
});
|
||||
return dbModel;
|
||||
}
|
||||
async GetDataFromServer(
|
||||
database: string,
|
||||
server: string,
|
||||
port: number,
|
||||
user: string,
|
||||
password: string,
|
||||
schema: string,
|
||||
ssl: boolean
|
||||
): Promise<DatabaseModel> {
|
||||
let dbModel = <DatabaseModel>{};
|
||||
await this.ConnectToServer(database, server, port, user, password, ssl);
|
||||
let sqlEscapedSchema = "'" + schema.split(",").join("','") + "'";
|
||||
dbModel.entities = await this.GetAllTables(sqlEscapedSchema);
|
||||
await this.GetCoulmnsFromEntity(dbModel.entities, sqlEscapedSchema);
|
||||
await this.GetIndexesFromEntity(dbModel.entities, sqlEscapedSchema);
|
||||
dbModel.entities = await this.GetRelations(
|
||||
dbModel.entities,
|
||||
sqlEscapedSchema
|
||||
public async GetDataFromServer(
|
||||
connectionOptons: IConnectionOptions
|
||||
): Promise<EntityInfo[]> {
|
||||
let dbModel = [] as EntityInfo[];
|
||||
await this.ConnectToServer(connectionOptons);
|
||||
const sqlEscapedSchema = this.escapeCommaSeparatedList(
|
||||
connectionOptons.schemaName
|
||||
);
|
||||
dbModel = await this.GetAllTables(
|
||||
sqlEscapedSchema,
|
||||
connectionOptons.databaseName
|
||||
);
|
||||
await this.GetCoulmnsFromEntity(
|
||||
dbModel,
|
||||
sqlEscapedSchema,
|
||||
connectionOptons.databaseName
|
||||
);
|
||||
await this.GetIndexesFromEntity(
|
||||
dbModel,
|
||||
sqlEscapedSchema,
|
||||
connectionOptons.databaseName
|
||||
);
|
||||
dbModel = await this.GetRelations(
|
||||
dbModel,
|
||||
sqlEscapedSchema,
|
||||
connectionOptons.databaseName
|
||||
);
|
||||
await this.DisconnectFromServer();
|
||||
this.FindManyToManyRelations(dbModel);
|
||||
dbModel = this.FindManyToManyRelations(dbModel);
|
||||
this.FindPrimaryColumnsFromIndexes(dbModel);
|
||||
return dbModel;
|
||||
}
|
||||
abstract async ConnectToServer(
|
||||
database: string,
|
||||
server: string,
|
||||
port: number,
|
||||
user: string,
|
||||
password: string,
|
||||
ssl: boolean
|
||||
);
|
||||
|
||||
abstract GetAllTablesQuery: (
|
||||
schema: string
|
||||
) => Promise<
|
||||
{
|
||||
TABLE_SCHEMA: string;
|
||||
TABLE_NAME: string;
|
||||
}[]
|
||||
>;
|
||||
public abstract async ConnectToServer(connectionOptons: IConnectionOptions);
|
||||
|
||||
async GetAllTables(schema: string): Promise<EntityInfo[]> {
|
||||
let response = await this.GetAllTablesQuery(schema);
|
||||
let ret: EntityInfo[] = <EntityInfo[]>[];
|
||||
public async GetAllTables(
|
||||
schema: string,
|
||||
dbNames: string
|
||||
): Promise<EntityInfo[]> {
|
||||
const response = await this.GetAllTablesQuery(schema, dbNames);
|
||||
const ret: EntityInfo[] = [] as EntityInfo[];
|
||||
response.forEach(val => {
|
||||
let ent: EntityInfo = new EntityInfo();
|
||||
ent.EntityName = val.TABLE_NAME;
|
||||
const ent: EntityInfo = new EntityInfo();
|
||||
ent.tsEntityName = val.TABLE_NAME;
|
||||
ent.sqlEntityName = val.TABLE_NAME;
|
||||
ent.Schema = val.TABLE_SCHEMA;
|
||||
ent.Columns = <ColumnInfo[]>[];
|
||||
ent.Indexes = <IndexInfo[]>[];
|
||||
ent.Columns = [] as ColumnInfo[];
|
||||
ent.Indexes = [] as IndexInfo[];
|
||||
ent.Database = dbNames.includes(",") ? val.DB_NAME : "";
|
||||
ret.push(ent);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
GetRelationsFromRelationTempInfo(
|
||||
relationsTemp: RelationTempInfo[],
|
||||
public GetRelationsFromRelationTempInfo(
|
||||
relationsTemp: IRelationTempInfo[],
|
||||
entities: EntityInfo[]
|
||||
) {
|
||||
relationsTemp.forEach(relationTmp => {
|
||||
let ownerEntity = entities.find(entitity => {
|
||||
return entitity.EntityName == relationTmp.ownerTable;
|
||||
});
|
||||
const ownerEntity = entities.find(
|
||||
entitity => entitity.tsEntityName === relationTmp.ownerTable
|
||||
);
|
||||
if (!ownerEntity) {
|
||||
TomgUtils.LogError(
|
||||
`Relation between tables ${relationTmp.ownerTable} and ${
|
||||
@ -191,9 +213,10 @@ export abstract class AbstractDriver {
|
||||
);
|
||||
return;
|
||||
}
|
||||
let referencedEntity = entities.find(entitity => {
|
||||
return entitity.EntityName == relationTmp.referencedTable;
|
||||
});
|
||||
const referencedEntity = entities.find(
|
||||
entitity =>
|
||||
entitity.tsEntityName === relationTmp.referencedTable
|
||||
);
|
||||
if (!referencedEntity) {
|
||||
TomgUtils.LogError(
|
||||
`Relation between tables ${relationTmp.ownerTable} and ${
|
||||
@ -207,12 +230,11 @@ export abstract class AbstractDriver {
|
||||
relationColumnIndex < relationTmp.ownerColumnsNames.length;
|
||||
relationColumnIndex++
|
||||
) {
|
||||
let ownerColumn = ownerEntity.Columns.find(column => {
|
||||
return (
|
||||
column.name ==
|
||||
const ownerColumn = ownerEntity.Columns.find(
|
||||
column =>
|
||||
column.tsName ===
|
||||
relationTmp.ownerColumnsNames[relationColumnIndex]
|
||||
);
|
||||
});
|
||||
);
|
||||
if (!ownerColumn) {
|
||||
TomgUtils.LogError(
|
||||
`Relation between tables ${
|
||||
@ -225,12 +247,11 @@ export abstract class AbstractDriver {
|
||||
);
|
||||
return;
|
||||
}
|
||||
let relatedColumn = referencedEntity.Columns.find(column => {
|
||||
return (
|
||||
column.name ==
|
||||
const relatedColumn = referencedEntity.Columns.find(
|
||||
column =>
|
||||
column.tsName ===
|
||||
relationTmp.referencedColumnsNames[relationColumnIndex]
|
||||
);
|
||||
});
|
||||
);
|
||||
if (!relatedColumn) {
|
||||
TomgUtils.LogError(
|
||||
`Relation between tables ${
|
||||
@ -245,79 +266,78 @@ export abstract class AbstractDriver {
|
||||
}
|
||||
let isOneToMany: boolean;
|
||||
isOneToMany = false;
|
||||
let index = ownerEntity.Indexes.find(index => {
|
||||
return (
|
||||
index.isUnique &&
|
||||
index.columns.some(col => {
|
||||
return col.name == ownerColumn!.name;
|
||||
})
|
||||
);
|
||||
});
|
||||
const index = ownerEntity.Indexes.find(
|
||||
ind =>
|
||||
ind.isUnique &&
|
||||
ind.columns.length === 1 &&
|
||||
ind.columns[0].name === ownerColumn!.tsName
|
||||
);
|
||||
isOneToMany = !index;
|
||||
|
||||
let ownerRelation = new RelationInfo();
|
||||
let columnName =
|
||||
ownerEntity.EntityName.toLowerCase() +
|
||||
(isOneToMany ? "s" : "");
|
||||
if (
|
||||
referencedEntity.Columns.filter(filterVal => {
|
||||
return filterVal.name == columnName;
|
||||
}).length > 0
|
||||
) {
|
||||
for (let i = 2; i <= ownerEntity.Columns.length; i++) {
|
||||
columnName =
|
||||
ownerEntity.EntityName.toLowerCase() +
|
||||
(isOneToMany ? "s" : "") +
|
||||
i.toString();
|
||||
if (
|
||||
referencedEntity.Columns.filter(filterVal => {
|
||||
return filterVal.name == columnName;
|
||||
}).length == 0
|
||||
)
|
||||
break;
|
||||
}
|
||||
}
|
||||
const ownerRelation = new RelationInfo();
|
||||
ownerRelation.actionOnDelete = relationTmp.actionOnDelete;
|
||||
ownerRelation.actionOnUpdate = relationTmp.actionOnUpdate;
|
||||
ownerRelation.isOwner = true;
|
||||
ownerRelation.relatedColumn = relatedColumn.name.toLowerCase();
|
||||
ownerRelation.relatedColumn = relatedColumn.tsName.toLowerCase();
|
||||
ownerRelation.relatedTable = relationTmp.referencedTable;
|
||||
ownerRelation.ownerTable = relationTmp.ownerTable;
|
||||
ownerRelation.ownerColumn = columnName;
|
||||
ownerRelation.relationType = isOneToMany
|
||||
? "ManyToOne"
|
||||
: "OneToOne";
|
||||
|
||||
let columnName = ownerEntity.tsEntityName;
|
||||
if (
|
||||
referencedEntity.Columns.some(v => v.tsName === columnName)
|
||||
) {
|
||||
columnName = columnName + "_";
|
||||
for (let i = 2; i <= referencedEntity.Columns.length; i++) {
|
||||
columnName =
|
||||
columnName.substring(
|
||||
0,
|
||||
columnName.length - i.toString().length
|
||||
) + i.toString();
|
||||
if (
|
||||
referencedEntity.Columns.every(
|
||||
v => v.tsName !== columnName
|
||||
)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ownerRelation.ownerColumn = columnName;
|
||||
ownerColumn.relations.push(ownerRelation);
|
||||
if (isOneToMany) {
|
||||
let col = new ColumnInfo();
|
||||
col.name = columnName;
|
||||
let referencedRelation = new RelationInfo();
|
||||
const col = new ColumnInfo();
|
||||
col.tsName = columnName;
|
||||
const referencedRelation = new RelationInfo();
|
||||
col.relations.push(referencedRelation);
|
||||
referencedRelation.actionOnDelete =
|
||||
relationTmp.actionOnDelete;
|
||||
referencedRelation.actionOnUpdate =
|
||||
relationTmp.actionOnUpdate;
|
||||
referencedRelation.isOwner = false;
|
||||
referencedRelation.relatedColumn = ownerColumn.name;
|
||||
referencedRelation.relatedColumn = ownerColumn.tsName;
|
||||
referencedRelation.relatedTable = relationTmp.ownerTable;
|
||||
referencedRelation.ownerTable = relationTmp.referencedTable;
|
||||
referencedRelation.ownerColumn = relatedColumn.name.toLowerCase();
|
||||
referencedRelation.ownerColumn = relatedColumn.tsName;
|
||||
referencedRelation.relationType = "OneToMany";
|
||||
referencedEntity.Columns.push(col);
|
||||
} else {
|
||||
let col = new ColumnInfo();
|
||||
col.name = columnName;
|
||||
let referencedRelation = new RelationInfo();
|
||||
const col = new ColumnInfo();
|
||||
col.tsName = columnName;
|
||||
const referencedRelation = new RelationInfo();
|
||||
col.relations.push(referencedRelation);
|
||||
referencedRelation.actionOnDelete =
|
||||
relationTmp.actionOnDelete;
|
||||
referencedRelation.actionOnUpdate =
|
||||
relationTmp.actionOnUpdate;
|
||||
referencedRelation.isOwner = false;
|
||||
referencedRelation.relatedColumn = ownerColumn.name;
|
||||
referencedRelation.relatedColumn = ownerColumn.tsName;
|
||||
referencedRelation.relatedTable = relationTmp.ownerTable;
|
||||
referencedRelation.ownerTable = relationTmp.referencedTable;
|
||||
referencedRelation.ownerColumn = relatedColumn.name.toLowerCase();
|
||||
referencedRelation.ownerColumn = relatedColumn.tsName;
|
||||
referencedRelation.relationType = "OneToOne";
|
||||
referencedEntity.Columns.push(col);
|
||||
}
|
||||
@ -325,45 +345,53 @@ export abstract class AbstractDriver {
|
||||
});
|
||||
return entities;
|
||||
}
|
||||
abstract async GetCoulmnsFromEntity(
|
||||
public abstract async GetCoulmnsFromEntity(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
schema: string,
|
||||
dbNames: string
|
||||
): Promise<EntityInfo[]>;
|
||||
abstract async GetIndexesFromEntity(
|
||||
public abstract async GetIndexesFromEntity(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
schema: string,
|
||||
dbNames: string
|
||||
): Promise<EntityInfo[]>;
|
||||
abstract async GetRelations(
|
||||
public abstract async GetRelations(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
schema: string,
|
||||
dbNames: string
|
||||
): Promise<EntityInfo[]>;
|
||||
|
||||
FindPrimaryColumnsFromIndexes(dbModel: DatabaseModel) {
|
||||
dbModel.entities.forEach(entity => {
|
||||
let primaryIndex = entity.Indexes.find(v => v.isPrimaryKey);
|
||||
entity.Columns.forEach(col => {
|
||||
if (
|
||||
public FindPrimaryColumnsFromIndexes(dbModel: EntityInfo[]) {
|
||||
dbModel.forEach(entity => {
|
||||
const primaryIndex = entity.Indexes.find(v => v.isPrimaryKey);
|
||||
entity.Columns.filter(
|
||||
col =>
|
||||
primaryIndex &&
|
||||
primaryIndex.columns.some(cIndex => cIndex.name == col.name)
|
||||
)
|
||||
col.isPrimary = true;
|
||||
});
|
||||
primaryIndex.columns.some(
|
||||
cIndex => cIndex.name === col.tsName
|
||||
)
|
||||
).forEach(col => (col.options.primary = true));
|
||||
if (
|
||||
!entity.Columns.some(v => {
|
||||
return v.isPrimary;
|
||||
return !!v.options.primary;
|
||||
})
|
||||
) {
|
||||
TomgUtils.LogError(
|
||||
`Table ${entity.EntityName} has no PK.`,
|
||||
`Table ${entity.tsEntityName} has no PK.`,
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
abstract async DisconnectFromServer();
|
||||
abstract async CreateDB(dbName: string);
|
||||
abstract async DropDB(dbName: string);
|
||||
abstract async UseDB(dbName: string);
|
||||
abstract async CheckIfDBExists(dbName: string): Promise<boolean>;
|
||||
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("','") + "'";
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { MysqlDriver } from "./MysqlDriver";
|
||||
|
||||
export class MariaDbDriver extends MysqlDriver {
|
||||
readonly EngineName: string = "MariaDb";
|
||||
public readonly EngineName: string = "MariaDb";
|
||||
}
|
||||
|
@ -1,27 +1,44 @@
|
||||
import { AbstractDriver } from "./AbstractDriver";
|
||||
import * as MSSQL from "mssql";
|
||||
import { ColumnInfo } from "./../models/ColumnInfo";
|
||||
import { EntityInfo } from "./../models/EntityInfo";
|
||||
import * as TomgUtils from "./../Utils";
|
||||
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 * as TomgUtils from "../Utils";
|
||||
import { AbstractDriver } from "./AbstractDriver";
|
||||
|
||||
export class MssqlDriver extends AbstractDriver {
|
||||
GetAllTablesQuery = async (schema: string) => {
|
||||
let request = new MSSQL.Request(this.Connection);
|
||||
let response: {
|
||||
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<{
|
||||
TABLE_SCHEMA: string;
|
||||
TABLE_NAME: string;
|
||||
}[] = (await request.query(
|
||||
`SELECT TABLE_SCHEMA,TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' and TABLE_SCHEMA in (${schema})`
|
||||
DB_NAME: string;
|
||||
}> = (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(
|
||||
dbNames
|
||||
)})`
|
||||
)).recordset;
|
||||
return response;
|
||||
};
|
||||
|
||||
async GetCoulmnsFromEntity(
|
||||
public async GetCoulmnsFromEntity(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
schema: string,
|
||||
dbNames: string
|
||||
): Promise<EntityInfo[]> {
|
||||
let request = new MSSQL.Request(this.Connection);
|
||||
let response: {
|
||||
const request = new MSSQL.Request(this.Connection);
|
||||
const response: Array<{
|
||||
TABLE_NAME: string;
|
||||
COLUMN_NAME: string;
|
||||
COLUMN_DEFAULT: string;
|
||||
@ -32,7 +49,7 @@ export class MssqlDriver extends AbstractDriver {
|
||||
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(*)
|
||||
@ -44,120 +61,126 @@ export class MssqlDriver extends AbstractDriver {
|
||||
and tc.TABLE_NAME = c.TABLE_NAME
|
||||
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})`))
|
||||
.recordset;
|
||||
FROM INFORMATION_SCHEMA.COLUMNS c
|
||||
where TABLE_SCHEMA in (${schema}) AND TABLE_CATALOG in (${this.escapeCommaSeparatedList(
|
||||
dbNames
|
||||
)})
|
||||
order by ordinal_position`)).recordset;
|
||||
entities.forEach(ent => {
|
||||
response
|
||||
.filter(filterVal => {
|
||||
return filterVal.TABLE_NAME == ent.EntityName;
|
||||
return filterVal.TABLE_NAME === ent.tsEntityName;
|
||||
})
|
||||
.forEach(resp => {
|
||||
let colInfo: ColumnInfo = new ColumnInfo();
|
||||
colInfo.name = resp.COLUMN_NAME;
|
||||
colInfo.is_nullable = resp.IS_NULLABLE == "YES";
|
||||
colInfo.is_generated = resp.IsIdentity == 1;
|
||||
colInfo.is_unique = resp.IsUnique == 1;
|
||||
colInfo.default = resp.COLUMN_DEFAULT;
|
||||
colInfo.sql_type = resp.DATA_TYPE;
|
||||
const colInfo: ColumnInfo = new ColumnInfo();
|
||||
colInfo.tsName = resp.COLUMN_NAME;
|
||||
colInfo.options.name = resp.COLUMN_NAME;
|
||||
colInfo.options.nullable = resp.IS_NULLABLE === "YES";
|
||||
colInfo.options.generated = resp.IsIdentity === 1;
|
||||
colInfo.options.unique = resp.IsUnique === 1;
|
||||
colInfo.options.default = this.ReturnDefaultValueFunction(
|
||||
resp.COLUMN_DEFAULT
|
||||
);
|
||||
colInfo.options.type = resp.DATA_TYPE as any;
|
||||
switch (resp.DATA_TYPE) {
|
||||
case "bigint":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "bit":
|
||||
colInfo.ts_type = "boolean";
|
||||
colInfo.tsType = "boolean";
|
||||
break;
|
||||
case "decimal":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "int":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "money":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "numeric":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "smallint":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "smallmoney":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "tinyint":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "float":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "real":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "date":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.tsType = "Date";
|
||||
break;
|
||||
case "datetime2":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.tsType = "Date";
|
||||
break;
|
||||
case "datetime":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.tsType = "Date";
|
||||
break;
|
||||
case "datetimeoffset":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.tsType = "Date";
|
||||
break;
|
||||
case "smalldatetime":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.tsType = "Date";
|
||||
break;
|
||||
case "time":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.tsType = "Date";
|
||||
break;
|
||||
case "char":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "text":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "varchar":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "nchar":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "ntext":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "nvarchar":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "binary":
|
||||
colInfo.ts_type = "Buffer";
|
||||
colInfo.tsType = "Buffer";
|
||||
break;
|
||||
case "image":
|
||||
colInfo.ts_type = "Buffer";
|
||||
colInfo.tsType = "Buffer";
|
||||
break;
|
||||
case "varbinary":
|
||||
colInfo.ts_type = "Buffer";
|
||||
colInfo.tsType = "Buffer";
|
||||
break;
|
||||
case "hierarchyid":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "sql_variant":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "timestamp":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.tsType = "Date";
|
||||
break;
|
||||
case "uniqueidentifier":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "xml":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "geometry":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "geography":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
default:
|
||||
TomgUtils.LogError(
|
||||
@ -172,80 +195,92 @@ export class MssqlDriver extends AbstractDriver {
|
||||
|
||||
if (
|
||||
this.ColumnTypesWithPrecision.some(
|
||||
v => v == colInfo.sql_type
|
||||
v => v === colInfo.options.type
|
||||
)
|
||||
) {
|
||||
colInfo.numericPrecision = resp.NUMERIC_PRECISION;
|
||||
colInfo.numericScale = resp.NUMERIC_SCALE;
|
||||
colInfo.options.precision = resp.NUMERIC_PRECISION;
|
||||
colInfo.options.scale = resp.NUMERIC_SCALE;
|
||||
}
|
||||
if (
|
||||
this.ColumnTypesWithLength.some(
|
||||
v => v == colInfo.sql_type
|
||||
v => v === colInfo.options.type
|
||||
)
|
||||
) {
|
||||
colInfo.lenght =
|
||||
colInfo.options.length =
|
||||
resp.CHARACTER_MAXIMUM_LENGTH > 0
|
||||
? resp.CHARACTER_MAXIMUM_LENGTH
|
||||
: null;
|
||||
: undefined;
|
||||
}
|
||||
|
||||
if (colInfo.sql_type) ent.Columns.push(colInfo);
|
||||
if (colInfo.options.type) {
|
||||
ent.Columns.push(colInfo);
|
||||
}
|
||||
});
|
||||
});
|
||||
return entities;
|
||||
}
|
||||
async GetIndexesFromEntity(
|
||||
public async GetIndexesFromEntity(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
schema: string,
|
||||
dbNames: string
|
||||
): Promise<EntityInfo[]> {
|
||||
let request = new MSSQL.Request(this.Connection);
|
||||
let response: {
|
||||
const request = new MSSQL.Request(this.Connection);
|
||||
const response: Array<{
|
||||
TableName: string;
|
||||
IndexName: string;
|
||||
ColumnName: string;
|
||||
is_unique: number;
|
||||
is_primary_key: number;
|
||||
}[] = (await request.query(`SELECT
|
||||
TableName = t.name,
|
||||
IndexName = ind.name,
|
||||
ColumnName = col.name,
|
||||
ind.is_unique,
|
||||
ind.is_primary_key
|
||||
FROM
|
||||
sys.indexes ind
|
||||
INNER JOIN
|
||||
sys.index_columns ic ON ind.object_id = ic.object_id and ind.index_id = ic.index_id
|
||||
INNER JOIN
|
||||
sys.columns col ON ic.object_id = col.object_id and ic.column_id = col.column_id
|
||||
INNER JOIN
|
||||
sys.tables t ON ind.object_id = t.object_id
|
||||
INNER JOIN
|
||||
sys.schemas s on s.schema_id=t.schema_id
|
||||
WHERE
|
||||
t.is_ms_shipped = 0 and s.name in (${schema})
|
||||
ORDER BY
|
||||
t.name, ind.name, ind.index_id, ic.key_ordinal;`)).recordset;
|
||||
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
|
||||
TableName = t.name,
|
||||
IndexName = ind.name,
|
||||
ColumnName = col.name,
|
||||
ind.is_unique,
|
||||
ind.is_primary_key
|
||||
FROM
|
||||
sys.indexes ind
|
||||
INNER JOIN
|
||||
sys.index_columns ic ON ind.object_id = ic.object_id and ind.index_id = ic.index_id
|
||||
INNER JOIN
|
||||
sys.columns col ON ic.object_id = col.object_id and ic.column_id = col.column_id
|
||||
INNER JOIN
|
||||
sys.tables t ON ind.object_id = t.object_id
|
||||
INNER JOIN
|
||||
sys.schemas s on s.schema_id=t.schema_id
|
||||
WHERE
|
||||
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);
|
||||
}
|
||||
entities.forEach(ent => {
|
||||
response
|
||||
.filter(filterVal => {
|
||||
return filterVal.TableName == ent.EntityName;
|
||||
})
|
||||
.filter(filterVal => filterVal.TableName === ent.tsEntityName)
|
||||
.forEach(resp => {
|
||||
let indexInfo: IndexInfo = <IndexInfo>{};
|
||||
let indexColumnInfo: IndexColumnInfo = <IndexColumnInfo>{};
|
||||
let indexInfo: IndexInfo = {} as IndexInfo;
|
||||
const indexColumnInfo: IndexColumnInfo = {} as IndexColumnInfo;
|
||||
if (
|
||||
ent.Indexes.filter(filterVal => {
|
||||
return filterVal.name == resp.IndexName;
|
||||
return filterVal.name === resp.IndexName;
|
||||
}).length > 0
|
||||
) {
|
||||
indexInfo = ent.Indexes.filter(filterVal => {
|
||||
return filterVal.name == resp.IndexName;
|
||||
return filterVal.name === resp.IndexName;
|
||||
})[0];
|
||||
} else {
|
||||
indexInfo.columns = <IndexColumnInfo[]>[];
|
||||
indexInfo.columns = [] as IndexColumnInfo[];
|
||||
indexInfo.name = resp.IndexName;
|
||||
indexInfo.isUnique = resp.is_unique == 1;
|
||||
indexInfo.isPrimaryKey = resp.is_primary_key == 1;
|
||||
indexInfo.isUnique = resp.is_unique;
|
||||
indexInfo.isPrimaryKey = resp.is_primary_key;
|
||||
ent.Indexes.push(indexInfo);
|
||||
}
|
||||
indexColumnInfo.name = resp.ColumnName;
|
||||
@ -255,12 +290,13 @@ ORDER BY
|
||||
|
||||
return entities;
|
||||
}
|
||||
async GetRelations(
|
||||
public async GetRelations(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
schema: string,
|
||||
dbNames: string
|
||||
): Promise<EntityInfo[]> {
|
||||
let request = new MSSQL.Request(this.Connection);
|
||||
let response: {
|
||||
const request = new MSSQL.Request(this.Connection);
|
||||
const response: Array<{
|
||||
TableWithForeignKey: string;
|
||||
FK_PartNo: number;
|
||||
ForeignKeyColumn: string;
|
||||
@ -269,7 +305,19 @@ ORDER BY
|
||||
onDelete: "RESTRICT" | "CASCADE" | "SET_NULL" | "NO_ACTION";
|
||||
onUpdate: "RESTRICT" | "CASCADE" | "SET_NULL" | "NO_ACTION";
|
||||
object_id: number;
|
||||
}[] = (await request.query(`select
|
||||
}> = [];
|
||||
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
|
||||
parentTable.name as TableWithForeignKey,
|
||||
fkc.constraint_column_id as FK_PartNo,
|
||||
parentColumn.name as ForeignKeyColumn,
|
||||
@ -296,13 +344,15 @@ where
|
||||
fk.is_disabled=0 and fk.is_ms_shipped=0 and parentSchema.name in (${schema})
|
||||
order by
|
||||
TableWithForeignKey, FK_PartNo`)).recordset;
|
||||
let relationsTemp: RelationTempInfo[] = <RelationTempInfo[]>[];
|
||||
response.push(...resp);
|
||||
}
|
||||
const relationsTemp: IRelationTempInfo[] = [] as IRelationTempInfo[];
|
||||
response.forEach(resp => {
|
||||
let rels = relationsTemp.find(val => {
|
||||
return val.object_id == resp.object_id;
|
||||
});
|
||||
if (rels == undefined) {
|
||||
rels = <RelationTempInfo>{};
|
||||
let rels = relationsTemp.find(
|
||||
val => val.object_id === resp.object_id
|
||||
);
|
||||
if (rels === undefined) {
|
||||
rels = {} as IRelationTempInfo;
|
||||
rels.ownerColumnsNames = [];
|
||||
rels.referencedColumnsNames = [];
|
||||
switch (resp.onDelete) {
|
||||
@ -314,7 +364,6 @@ order by
|
||||
break;
|
||||
default:
|
||||
rels.actionOnDelete = resp.onDelete;
|
||||
|
||||
break;
|
||||
}
|
||||
switch (resp.onUpdate) {
|
||||
@ -326,7 +375,6 @@ order by
|
||||
break;
|
||||
default:
|
||||
rels.actionOnUpdate = resp.onUpdate;
|
||||
|
||||
break;
|
||||
}
|
||||
rels.object_id = resp.object_id;
|
||||
@ -343,32 +391,26 @@ order by
|
||||
);
|
||||
return entities;
|
||||
}
|
||||
async DisconnectFromServer() {
|
||||
if (this.Connection) await this.Connection.close();
|
||||
public async DisconnectFromServer() {
|
||||
if (this.Connection) {
|
||||
await this.Connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
private Connection: MSSQL.ConnectionPool;
|
||||
async ConnectToServer(
|
||||
database: string,
|
||||
server: string,
|
||||
port: number,
|
||||
user: string,
|
||||
password: string,
|
||||
ssl: boolean
|
||||
) {
|
||||
let config: MSSQL.config = {
|
||||
database: database,
|
||||
server: server,
|
||||
port: port,
|
||||
user: user,
|
||||
password: password,
|
||||
public async ConnectToServer(connectionOptons: IConnectionOptions) {
|
||||
const databaseName = connectionOptons.databaseName.split(",")[0];
|
||||
const config: MSSQL.config = {
|
||||
database: databaseName,
|
||||
options: {
|
||||
encrypt: ssl,
|
||||
appName: "typeorm-model-generator"
|
||||
}
|
||||
appName: "typeorm-model-generator",
|
||||
encrypt: connectionOptons.ssl
|
||||
},
|
||||
password: connectionOptons.password,
|
||||
port: connectionOptons.port,
|
||||
server: connectionOptons.host,
|
||||
user: connectionOptons.user
|
||||
};
|
||||
|
||||
let promise = new Promise<boolean>((resolve, reject) => {
|
||||
const promise = new Promise<boolean>((resolve, reject) => {
|
||||
this.Connection = new MSSQL.ConnectionPool(config, err => {
|
||||
if (!err) {
|
||||
resolve(true);
|
||||
@ -385,23 +427,35 @@ order by
|
||||
|
||||
await promise;
|
||||
}
|
||||
async CreateDB(dbName: string) {
|
||||
let request = new MSSQL.Request(this.Connection);
|
||||
public async CreateDB(dbName: string) {
|
||||
const request = new MSSQL.Request(this.Connection);
|
||||
await request.query(`CREATE DATABASE ${dbName}; `);
|
||||
}
|
||||
async UseDB(dbName: string) {
|
||||
let request = new MSSQL.Request(this.Connection);
|
||||
public async UseDB(dbName: string) {
|
||||
const request = new MSSQL.Request(this.Connection);
|
||||
await request.query(`USE ${dbName}; `);
|
||||
}
|
||||
async DropDB(dbName: string) {
|
||||
let request = new MSSQL.Request(this.Connection);
|
||||
public async DropDB(dbName: string) {
|
||||
const request = new MSSQL.Request(this.Connection);
|
||||
await request.query(`DROP DATABASE ${dbName}; `);
|
||||
}
|
||||
async CheckIfDBExists(dbName: string): Promise<boolean> {
|
||||
let request = new MSSQL.Request(this.Connection);
|
||||
let resp = await request.query(
|
||||
public async CheckIfDBExists(dbName: string): Promise<boolean> {
|
||||
const request = new MSSQL.Request(this.Connection);
|
||||
const resp = await request.query(
|
||||
`SELECT name FROM master.sys.databases WHERE name = N'${dbName}' `
|
||||
);
|
||||
return resp.recordset.length > 0;
|
||||
}
|
||||
private ReturnDefaultValueFunction(defVal: string | null): string | null {
|
||||
if (!defVal) {
|
||||
return null;
|
||||
}
|
||||
if (defVal.startsWith("(") && defVal.endsWith(")")) {
|
||||
defVal = defVal.slice(1, -1);
|
||||
}
|
||||
if (defVal.startsWith(`'`)) {
|
||||
return `() => "${defVal}"`;
|
||||
}
|
||||
return `() => "${defVal}"`;
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,42 @@
|
||||
import { AbstractDriver } from "./AbstractDriver";
|
||||
import * as MYSQL from "mysql";
|
||||
import { ColumnInfo } from "./../models/ColumnInfo";
|
||||
import { EntityInfo } from "./../models/EntityInfo";
|
||||
import * as TomgUtils from "./../Utils";
|
||||
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 * as TomgUtils from "../Utils";
|
||||
import { AbstractDriver } from "./AbstractDriver";
|
||||
|
||||
export class MysqlDriver extends AbstractDriver {
|
||||
readonly EngineName: string = "MySQL";
|
||||
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 = "";
|
||||
|
||||
GetAllTablesQuery = async (schema: string) => {
|
||||
let response = this.ExecQuery<{
|
||||
private Connection: MYSQL.Connection;
|
||||
|
||||
public GetAllTablesQuery = async (schema: string, dbNames: string) => {
|
||||
const response = this.ExecQuery<{
|
||||
TABLE_SCHEMA: string;
|
||||
TABLE_NAME: string;
|
||||
}>(`SELECT TABLE_SCHEMA, TABLE_NAME
|
||||
DB_NAME: string;
|
||||
}>(`SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_SCHEMA as DB_NAME
|
||||
FROM information_schema.tables
|
||||
WHERE table_type='BASE TABLE'
|
||||
AND table_schema like DATABASE()`);
|
||||
AND table_schema IN (${this.escapeCommaSeparatedList(dbNames)})`);
|
||||
return response;
|
||||
};
|
||||
|
||||
async GetCoulmnsFromEntity(
|
||||
public async GetCoulmnsFromEntity(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
schema: string,
|
||||
dbNames: string
|
||||
): Promise<EntityInfo[]> {
|
||||
let response = await this.ExecQuery<{
|
||||
const response = await this.ExecQuery<{
|
||||
TABLE_NAME: string;
|
||||
COLUMN_NAME: string;
|
||||
COLUMN_DEFAULT: string;
|
||||
@ -32,28 +46,32 @@ export class MysqlDriver extends AbstractDriver {
|
||||
NUMERIC_PRECISION: number;
|
||||
NUMERIC_SCALE: number;
|
||||
IsIdentity: number;
|
||||
column_type: string;
|
||||
column_key: string;
|
||||
COLUMN_TYPE: string;
|
||||
COLUMN_KEY: string;
|
||||
}>(`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 like DATABASE()`);
|
||||
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(
|
||||
dbNames
|
||||
)})
|
||||
order by ordinal_position`);
|
||||
entities.forEach(ent => {
|
||||
response
|
||||
.filter(filterVal => {
|
||||
return filterVal.TABLE_NAME == ent.EntityName;
|
||||
})
|
||||
.filter(filterVal => filterVal.TABLE_NAME === ent.tsEntityName)
|
||||
.forEach(resp => {
|
||||
let colInfo: ColumnInfo = new ColumnInfo();
|
||||
colInfo.name = resp.COLUMN_NAME;
|
||||
colInfo.is_nullable = resp.IS_NULLABLE == "YES";
|
||||
colInfo.is_generated = resp.IsIdentity == 1;
|
||||
colInfo.is_unique = resp.column_key == "UNI";
|
||||
colInfo.default = resp.COLUMN_DEFAULT;
|
||||
colInfo.sql_type = resp.DATA_TYPE;
|
||||
const colInfo: ColumnInfo = new ColumnInfo();
|
||||
colInfo.tsName = resp.COLUMN_NAME;
|
||||
colInfo.options.name = resp.COLUMN_NAME;
|
||||
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(
|
||||
resp.COLUMN_DEFAULT
|
||||
);
|
||||
colInfo.options.type = resp.DATA_TYPE as any;
|
||||
switch (resp.DATA_TYPE) {
|
||||
case "int":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "bit":
|
||||
if (resp.column_type == "bit(1)") {
|
||||
@ -64,111 +82,117 @@ export class MysqlDriver extends AbstractDriver {
|
||||
}
|
||||
break;
|
||||
case "tinyint":
|
||||
if (resp.column_type == "tinyint(1)") {
|
||||
colInfo.width = 1;
|
||||
colInfo.ts_type = "boolean";
|
||||
if (resp.COLUMN_TYPE === "tinyint(1)") {
|
||||
colInfo.options.width = 1;
|
||||
colInfo.tsType = "boolean";
|
||||
} else {
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
}
|
||||
break;
|
||||
case "smallint":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "mediumint":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "bigint":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "float":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "double":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "decimal":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "date":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "datetime":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.tsType = "Date";
|
||||
break;
|
||||
case "timestamp":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.tsType = "Date";
|
||||
break;
|
||||
case "time":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "year":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "char":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "varchar":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "blob":
|
||||
colInfo.ts_type = "Buffer";
|
||||
colInfo.tsType = "Buffer";
|
||||
break;
|
||||
case "text":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "tinyblob":
|
||||
colInfo.ts_type = "Buffer";
|
||||
colInfo.tsType = "Buffer";
|
||||
break;
|
||||
case "tinytext":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "mediumblob":
|
||||
colInfo.ts_type = "Buffer";
|
||||
colInfo.tsType = "Buffer";
|
||||
break;
|
||||
case "mediumtext":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "longblob":
|
||||
colInfo.ts_type = "Buffer";
|
||||
colInfo.tsType = "Buffer";
|
||||
break;
|
||||
case "longtext":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "enum":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.enumOptions = resp.column_type
|
||||
.substring(5, resp.column_type.length - 1)
|
||||
.replace(/\'/gi, '"');
|
||||
colInfo.tsType = "string";
|
||||
colInfo.options.enum = resp.COLUMN_TYPE.substring(
|
||||
5,
|
||||
resp.COLUMN_TYPE.length - 1
|
||||
).replace(/\'/gi, '"');
|
||||
break;
|
||||
case "json":
|
||||
colInfo.ts_type = "Object";
|
||||
colInfo.tsType = "Object";
|
||||
break;
|
||||
case "binary":
|
||||
colInfo.ts_type = "Buffer";
|
||||
colInfo.tsType = "Buffer";
|
||||
break;
|
||||
case "varbinary":
|
||||
colInfo.tsType = "Buffer";
|
||||
break;
|
||||
case "geometry":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "point":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "linestring":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "polygon":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "multipoint":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "multilinestring":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "multipolygon":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "geometrycollection":
|
||||
colInfo.ts_type = "string";
|
||||
case "geomcollection":
|
||||
colInfo.options.type = "geometrycollection";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
default:
|
||||
TomgUtils.LogError(
|
||||
@ -182,45 +206,48 @@ export class MysqlDriver extends AbstractDriver {
|
||||
}
|
||||
if (
|
||||
this.ColumnTypesWithPrecision.some(
|
||||
v => v == colInfo.sql_type
|
||||
v => v === colInfo.options.type
|
||||
)
|
||||
) {
|
||||
colInfo.numericPrecision = resp.NUMERIC_PRECISION;
|
||||
colInfo.numericScale = resp.NUMERIC_SCALE;
|
||||
colInfo.options.precision = resp.NUMERIC_PRECISION;
|
||||
colInfo.options.scale = resp.NUMERIC_SCALE;
|
||||
}
|
||||
if (
|
||||
this.ColumnTypesWithLength.some(
|
||||
v => v == colInfo.sql_type
|
||||
v => v === colInfo.options.type
|
||||
)
|
||||
) {
|
||||
colInfo.lenght =
|
||||
colInfo.options.length =
|
||||
resp.CHARACTER_MAXIMUM_LENGTH > 0
|
||||
? resp.CHARACTER_MAXIMUM_LENGTH
|
||||
: null;
|
||||
: undefined;
|
||||
}
|
||||
if (
|
||||
this.ColumnTypesWithWidth.some(
|
||||
v =>
|
||||
v == colInfo.sql_type &&
|
||||
colInfo.ts_type != "boolean"
|
||||
v === colInfo.options.type &&
|
||||
colInfo.tsType !== "boolean"
|
||||
)
|
||||
) {
|
||||
colInfo.width =
|
||||
colInfo.options.width =
|
||||
resp.CHARACTER_MAXIMUM_LENGTH > 0
|
||||
? resp.CHARACTER_MAXIMUM_LENGTH
|
||||
: null;
|
||||
: undefined;
|
||||
}
|
||||
|
||||
if (colInfo.sql_type) ent.Columns.push(colInfo);
|
||||
if (colInfo.options.type) {
|
||||
ent.Columns.push(colInfo);
|
||||
}
|
||||
});
|
||||
});
|
||||
return entities;
|
||||
}
|
||||
async GetIndexesFromEntity(
|
||||
public async GetIndexesFromEntity(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
schema: string,
|
||||
dbNames: string
|
||||
): Promise<EntityInfo[]> {
|
||||
let response = await this.ExecQuery<{
|
||||
const response = await this.ExecQuery<{
|
||||
TableName: string;
|
||||
IndexName: string;
|
||||
ColumnName: string;
|
||||
@ -229,29 +256,26 @@ 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 like DATABASE();
|
||||
`);
|
||||
WHERE table_schema IN (${this.escapeCommaSeparatedList(dbNames)})`);
|
||||
entities.forEach(ent => {
|
||||
response
|
||||
.filter(filterVal => {
|
||||
return filterVal.TableName == ent.EntityName;
|
||||
})
|
||||
.filter(filterVal => filterVal.TableName === ent.tsEntityName)
|
||||
.forEach(resp => {
|
||||
let indexInfo: IndexInfo = <IndexInfo>{};
|
||||
let indexColumnInfo: IndexColumnInfo = <IndexColumnInfo>{};
|
||||
let indexInfo: IndexInfo = {} as IndexInfo;
|
||||
const indexColumnInfo: IndexColumnInfo = {} as IndexColumnInfo;
|
||||
if (
|
||||
ent.Indexes.filter(filterVal => {
|
||||
return filterVal.name == resp.IndexName;
|
||||
}).length > 0
|
||||
ent.Indexes.filter(
|
||||
filterVal => filterVal.name === resp.IndexName
|
||||
).length > 0
|
||||
) {
|
||||
indexInfo = ent.Indexes.filter(filterVal => {
|
||||
return filterVal.name == resp.IndexName;
|
||||
})[0];
|
||||
indexInfo = ent.Indexes.find(
|
||||
filterVal => filterVal.name === resp.IndexName
|
||||
)!;
|
||||
} else {
|
||||
indexInfo.columns = <IndexColumnInfo[]>[];
|
||||
indexInfo.columns = [] as IndexColumnInfo[];
|
||||
indexInfo.name = resp.IndexName;
|
||||
indexInfo.isUnique = resp.is_unique == 1;
|
||||
indexInfo.isPrimaryKey = resp.is_primary_key == 1;
|
||||
indexInfo.isUnique = resp.is_unique === 1;
|
||||
indexInfo.isPrimaryKey = resp.is_primary_key === 1;
|
||||
ent.Indexes.push(indexInfo);
|
||||
}
|
||||
indexColumnInfo.name = resp.ColumnName;
|
||||
@ -261,11 +285,12 @@ export class MysqlDriver extends AbstractDriver {
|
||||
|
||||
return entities;
|
||||
}
|
||||
async GetRelations(
|
||||
public async GetRelations(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
schema: string,
|
||||
dbNames: string
|
||||
): Promise<EntityInfo[]> {
|
||||
let response = await this.ExecQuery<{
|
||||
const response = await this.ExecQuery<{
|
||||
TableWithForeignKey: string;
|
||||
FK_PartNo: number;
|
||||
ForeignKeyColumn: string;
|
||||
@ -286,24 +311,25 @@ export class MysqlDriver extends AbstractDriver {
|
||||
FROM
|
||||
INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU
|
||||
JOIN
|
||||
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC ON CU.CONSTRAINT_NAME=RC.CONSTRAINT_NAME
|
||||
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
|
||||
ON CU.CONSTRAINT_NAME=RC.CONSTRAINT_NAME AND CU.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
|
||||
WHERE
|
||||
TABLE_SCHEMA = SCHEMA()
|
||||
TABLE_SCHEMA IN (${this.escapeCommaSeparatedList(dbNames)})
|
||||
AND CU.REFERENCED_TABLE_NAME IS NOT NULL;
|
||||
`);
|
||||
let relationsTemp: RelationTempInfo[] = <RelationTempInfo[]>[];
|
||||
const relationsTemp: IRelationTempInfo[] = [] as IRelationTempInfo[];
|
||||
response.forEach(resp => {
|
||||
let rels = relationsTemp.find(val => {
|
||||
return val.object_id == resp.object_id;
|
||||
});
|
||||
if (rels == undefined) {
|
||||
rels = <RelationTempInfo>{};
|
||||
let rels = relationsTemp.find(
|
||||
val => val.object_id === resp.object_id
|
||||
);
|
||||
if (rels === undefined) {
|
||||
rels = {} as IRelationTempInfo;
|
||||
rels.ownerColumnsNames = [];
|
||||
rels.referencedColumnsNames = [];
|
||||
rels.actionOnDelete =
|
||||
resp.onDelete == "NO_ACTION" ? null : resp.onDelete;
|
||||
resp.onDelete === "NO_ACTION" ? null : resp.onDelete;
|
||||
rels.actionOnUpdate =
|
||||
resp.onUpdate == "NO_ACTION" ? null : resp.onUpdate;
|
||||
resp.onUpdate === "NO_ACTION" ? null : resp.onUpdate;
|
||||
rels.object_id = resp.object_id;
|
||||
rels.ownerTable = resp.TableWithForeignKey;
|
||||
rels.referencedTable = resp.TableReferenced;
|
||||
@ -318,8 +344,8 @@ export class MysqlDriver extends AbstractDriver {
|
||||
);
|
||||
return entities;
|
||||
}
|
||||
async DisconnectFromServer() {
|
||||
let promise = new Promise<boolean>((resolve, reject) => {
|
||||
public async DisconnectFromServer() {
|
||||
const promise = new Promise<boolean>((resolve, reject) => {
|
||||
this.Connection.end(err => {
|
||||
if (!err) {
|
||||
resolve(true);
|
||||
@ -333,41 +359,35 @@ export class MysqlDriver extends AbstractDriver {
|
||||
}
|
||||
});
|
||||
});
|
||||
if (this.Connection) await promise;
|
||||
if (this.Connection) {
|
||||
await promise;
|
||||
}
|
||||
}
|
||||
|
||||
private Connection: MYSQL.Connection;
|
||||
async ConnectToServer(
|
||||
database: string,
|
||||
server: string,
|
||||
port: number,
|
||||
user: string,
|
||||
password: string,
|
||||
ssl: boolean
|
||||
) {
|
||||
public async ConnectToServer(connectionOptons: IConnectionOptions) {
|
||||
const databaseName = connectionOptons.databaseName.split(",")[0];
|
||||
let config: MYSQL.ConnectionConfig;
|
||||
if (ssl) {
|
||||
if (connectionOptons.ssl) {
|
||||
config = {
|
||||
database: database,
|
||||
host: server,
|
||||
port: port,
|
||||
user: user,
|
||||
password: password,
|
||||
database: databaseName,
|
||||
host: connectionOptons.host,
|
||||
password: connectionOptons.password,
|
||||
port: connectionOptons.port,
|
||||
ssl: {
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
},
|
||||
user: connectionOptons.user
|
||||
};
|
||||
} else {
|
||||
config = {
|
||||
database: database,
|
||||
host: server,
|
||||
port: port,
|
||||
user: user,
|
||||
password: password
|
||||
database: databaseName,
|
||||
host: connectionOptons.host,
|
||||
password: connectionOptons.password,
|
||||
port: connectionOptons.port,
|
||||
user: connectionOptons.user
|
||||
};
|
||||
}
|
||||
|
||||
let promise = new Promise<boolean>((resolve, reject) => {
|
||||
const promise = new Promise<boolean>((resolve, reject) => {
|
||||
this.Connection = MYSQL.createConnection(config);
|
||||
|
||||
this.Connection.connect(err => {
|
||||
@ -386,28 +406,28 @@ export class MysqlDriver extends AbstractDriver {
|
||||
|
||||
await promise;
|
||||
}
|
||||
async CreateDB(dbName: string) {
|
||||
public async CreateDB(dbName: string) {
|
||||
await this.ExecQuery<any>(`CREATE DATABASE ${dbName}; `);
|
||||
}
|
||||
async UseDB(dbName: string) {
|
||||
public async UseDB(dbName: string) {
|
||||
await this.ExecQuery<any>(`USE ${dbName}; `);
|
||||
}
|
||||
async DropDB(dbName: string) {
|
||||
public async DropDB(dbName: string) {
|
||||
await this.ExecQuery<any>(`DROP DATABASE ${dbName}; `);
|
||||
}
|
||||
async CheckIfDBExists(dbName: string): Promise<boolean> {
|
||||
let resp = await this.ExecQuery<any>(
|
||||
public async CheckIfDBExists(dbName: string): Promise<boolean> {
|
||||
const resp = await this.ExecQuery<any>(
|
||||
`SHOW DATABASES LIKE '${dbName}' `
|
||||
);
|
||||
return resp.length > 0;
|
||||
}
|
||||
async ExecQuery<T>(sql: string): Promise<Array<T>> {
|
||||
let ret: Array<T> = [];
|
||||
let query = this.Connection.query(sql);
|
||||
let stream = query.stream({});
|
||||
let promise = new Promise<boolean>((resolve, reject) => {
|
||||
public async ExecQuery<T>(sql: string): Promise<T[]> {
|
||||
const ret: T[] = [];
|
||||
const query = this.Connection.query(sql);
|
||||
const stream = query.stream({});
|
||||
const promise = new Promise<boolean>((resolve, reject) => {
|
||||
stream.on("data", chunk => {
|
||||
ret.push(<T>(<any>chunk));
|
||||
ret.push((chunk as any) as T);
|
||||
});
|
||||
stream.on("error", err => reject(err));
|
||||
stream.on("end", () => resolve(true));
|
||||
@ -415,4 +435,16 @@ export class MysqlDriver extends AbstractDriver {
|
||||
await promise;
|
||||
return ret;
|
||||
}
|
||||
private ReturnDefaultValueFunction(defVal: string | null): string | null {
|
||||
if (!defVal || defVal === "NULL") {
|
||||
return null;
|
||||
}
|
||||
if (defVal.toLowerCase() === "current_timestamp()") {
|
||||
defVal = "CURRENT_TIMESTAMP";
|
||||
}
|
||||
if (defVal === "CURRENT_TIMESTAMP" || defVal.startsWith(`'`)) {
|
||||
return `() => "${defVal}"`;
|
||||
}
|
||||
return `() => "'${defVal}'"`;
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,49 @@
|
||||
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 * as TomgUtils from "../Utils";
|
||||
import { AbstractDriver } from "./AbstractDriver";
|
||||
import { ColumnInfo } from "./../models/ColumnInfo";
|
||||
import { EntityInfo } from "./../models/EntityInfo";
|
||||
import * as TomgUtils from "./../Utils";
|
||||
|
||||
export class OracleDriver extends AbstractDriver {
|
||||
Oracle: any;
|
||||
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() {
|
||||
super();
|
||||
try {
|
||||
this.Oracle = require("oracledb");
|
||||
this.Oracle.outFormat = this.Oracle.OBJECT;
|
||||
} catch (error) {
|
||||
TomgUtils.LogError("", false, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
GetAllTablesQuery = async (schema: string) => {
|
||||
let response: {
|
||||
public GetAllTablesQuery = async (schema: string) => {
|
||||
const response: Array<{
|
||||
TABLE_SCHEMA: string;
|
||||
TABLE_NAME: string;
|
||||
}[] = (await this.Connection.execute(
|
||||
` SELECT NULL AS TABLE_SCHEMA, TABLE_NAME FROM all_tables WHERE owner = (select user from dual)`
|
||||
DB_NAME: string;
|
||||
}> = (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;
|
||||
};
|
||||
|
||||
async GetCoulmnsFromEntity(
|
||||
public async GetCoulmnsFromEntity(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
): Promise<EntityInfo[]> {
|
||||
let response: {
|
||||
const response: Array<{
|
||||
TABLE_NAME: string;
|
||||
COLUMN_NAME: string;
|
||||
DATA_DEFAULT: string;
|
||||
@ -39,8 +53,8 @@ export class OracleDriver extends AbstractDriver {
|
||||
DATA_PRECISION: number;
|
||||
DATA_SCALE: number;
|
||||
IDENTITY_COLUMN: string;
|
||||
IS_UNIQUE: Number;
|
||||
}[] = (await this.Connection
|
||||
IS_UNIQUE: number;
|
||||
}> = (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
|
||||
@ -50,108 +64,109 @@ export class OracleDriver extends AbstractDriver {
|
||||
|
||||
entities.forEach(ent => {
|
||||
response
|
||||
.filter(filterVal => {
|
||||
return filterVal.TABLE_NAME == ent.EntityName;
|
||||
})
|
||||
.filter(filterVal => filterVal.TABLE_NAME === ent.tsEntityName)
|
||||
.forEach(resp => {
|
||||
let colInfo: ColumnInfo = new ColumnInfo();
|
||||
colInfo.name = resp.COLUMN_NAME;
|
||||
colInfo.is_nullable = resp.NULLABLE == "Y";
|
||||
colInfo.is_generated = resp.IDENTITY_COLUMN == "YES";
|
||||
colInfo.default =
|
||||
const colInfo: ColumnInfo = new ColumnInfo();
|
||||
colInfo.tsName = resp.COLUMN_NAME;
|
||||
colInfo.options.name = resp.COLUMN_NAME;
|
||||
colInfo.options.nullable = resp.NULLABLE === "Y";
|
||||
colInfo.options.generated = resp.IDENTITY_COLUMN === "YES";
|
||||
colInfo.options.default =
|
||||
!resp.DATA_DEFAULT || resp.DATA_DEFAULT.includes('"')
|
||||
? null
|
||||
: resp.DATA_DEFAULT;
|
||||
colInfo.is_unique = resp.IS_UNIQUE > 0;
|
||||
: this.ReturnDefaultValueFunction(
|
||||
resp.DATA_DEFAULT
|
||||
);
|
||||
colInfo.options.unique = resp.IS_UNIQUE > 0;
|
||||
resp.DATA_TYPE = resp.DATA_TYPE.replace(/\([0-9]+\)/g, "");
|
||||
colInfo.sql_type = resp.DATA_TYPE.toLowerCase();
|
||||
colInfo.options.type = resp.DATA_TYPE.toLowerCase() as any;
|
||||
switch (resp.DATA_TYPE.toLowerCase()) {
|
||||
case "char":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "nchar":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "nvarchar2":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "varchar2":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "long":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "raw":
|
||||
colInfo.ts_type = "Buffer";
|
||||
colInfo.tsType = "Buffer";
|
||||
break;
|
||||
case "long raw":
|
||||
colInfo.ts_type = "Buffer";
|
||||
colInfo.tsType = "Buffer";
|
||||
break;
|
||||
case "number":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "numeric":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "float":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "dec":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "decimal":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "integer":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "int":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "smallint":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "real":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "double precision":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "date":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.tsType = "Date";
|
||||
break;
|
||||
case "timestamp":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.tsType = "Date";
|
||||
break;
|
||||
case "timestamp with time zone":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.tsType = "Date";
|
||||
break;
|
||||
case "timestamp with local time zone":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.tsType = "Date";
|
||||
break;
|
||||
case "interval year to month":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "interval day to second":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "bfile":
|
||||
colInfo.ts_type = "Buffer";
|
||||
colInfo.tsType = "Buffer";
|
||||
break;
|
||||
case "blob":
|
||||
colInfo.ts_type = "Buffer";
|
||||
colInfo.tsType = "Buffer";
|
||||
break;
|
||||
case "clob":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "nclob":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "rowid":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "urowid":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
default:
|
||||
TomgUtils.LogError(
|
||||
@ -161,37 +176,39 @@ export class OracleDriver extends AbstractDriver {
|
||||
}
|
||||
if (
|
||||
this.ColumnTypesWithPrecision.some(
|
||||
v => v == colInfo.sql_type
|
||||
v => v === colInfo.options.type
|
||||
)
|
||||
) {
|
||||
colInfo.numericPrecision = resp.DATA_PRECISION;
|
||||
colInfo.numericScale = resp.DATA_SCALE;
|
||||
colInfo.options.precision = resp.DATA_PRECISION;
|
||||
colInfo.options.scale = resp.DATA_SCALE;
|
||||
}
|
||||
if (
|
||||
this.ColumnTypesWithLength.some(
|
||||
v => v == colInfo.sql_type
|
||||
v => v === colInfo.options.type
|
||||
)
|
||||
) {
|
||||
colInfo.lenght =
|
||||
resp.DATA_LENGTH > 0 ? resp.DATA_LENGTH : null;
|
||||
colInfo.options.length =
|
||||
resp.DATA_LENGTH > 0 ? resp.DATA_LENGTH : undefined;
|
||||
}
|
||||
|
||||
if (colInfo.sql_type) ent.Columns.push(colInfo);
|
||||
if (colInfo.options.type) {
|
||||
ent.Columns.push(colInfo);
|
||||
}
|
||||
});
|
||||
});
|
||||
return entities;
|
||||
}
|
||||
async GetIndexesFromEntity(
|
||||
public async GetIndexesFromEntity(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
): Promise<EntityInfo[]> {
|
||||
let response: {
|
||||
const response: Array<{
|
||||
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
|
||||
@ -200,25 +217,23 @@ export class OracleDriver extends AbstractDriver {
|
||||
|
||||
entities.forEach(ent => {
|
||||
response
|
||||
.filter(filterVal => {
|
||||
return filterVal.TABLE_NAME == ent.EntityName;
|
||||
})
|
||||
.filter(filterVal => filterVal.TABLE_NAME === ent.tsEntityName)
|
||||
.forEach(resp => {
|
||||
let indexInfo: IndexInfo = <IndexInfo>{};
|
||||
let indexColumnInfo: IndexColumnInfo = <IndexColumnInfo>{};
|
||||
let indexInfo: IndexInfo = {} as IndexInfo;
|
||||
const indexColumnInfo: IndexColumnInfo = {} as IndexColumnInfo;
|
||||
if (
|
||||
ent.Indexes.filter(filterVal => {
|
||||
return filterVal.name == resp.INDEX_NAME;
|
||||
}).length > 0
|
||||
ent.Indexes.filter(
|
||||
filterVal => filterVal.name === resp.INDEX_NAME
|
||||
).length > 0
|
||||
) {
|
||||
indexInfo = ent.Indexes.filter(filterVal => {
|
||||
return filterVal.name == resp.INDEX_NAME;
|
||||
})[0];
|
||||
indexInfo = ent.Indexes.find(
|
||||
filterVal => filterVal.name === resp.INDEX_NAME
|
||||
)!;
|
||||
} else {
|
||||
indexInfo.columns = <IndexColumnInfo[]>[];
|
||||
indexInfo.columns = [] as IndexColumnInfo[];
|
||||
indexInfo.name = resp.INDEX_NAME;
|
||||
indexInfo.isUnique = resp.UNIQUENESS == "UNIQUE";
|
||||
indexInfo.isPrimaryKey = resp.ISPRIMARYKEY == 1;
|
||||
indexInfo.isUnique = resp.UNIQUENESS === "UNIQUE";
|
||||
indexInfo.isPrimaryKey = resp.ISPRIMARYKEY === 1;
|
||||
ent.Indexes.push(indexInfo);
|
||||
}
|
||||
indexColumnInfo.name = resp.COLUMN_NAME;
|
||||
@ -228,11 +243,11 @@ export class OracleDriver extends AbstractDriver {
|
||||
|
||||
return entities;
|
||||
}
|
||||
async GetRelations(
|
||||
public async GetRelations(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
): Promise<EntityInfo[]> {
|
||||
let response: {
|
||||
const response: Array<{
|
||||
OWNER_TABLE_NAME: string;
|
||||
OWNER_POSITION: string;
|
||||
OWNER_COLUMN_NAME: string;
|
||||
@ -240,7 +255,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,
|
||||
@ -252,17 +267,17 @@ export class OracleDriver extends AbstractDriver {
|
||||
ORDER BY OWNER_TABLE_NAME ASC, owner.CONSTRAINT_NAME ASC, OWNER_POSITION ASC`))
|
||||
.rows!;
|
||||
|
||||
let relationsTemp: RelationTempInfo[] = <RelationTempInfo[]>[];
|
||||
const relationsTemp: IRelationTempInfo[] = [] as IRelationTempInfo[];
|
||||
response.forEach(resp => {
|
||||
let rels = relationsTemp.find(val => {
|
||||
return val.object_id == resp.CONSTRAINT_NAME;
|
||||
});
|
||||
if (rels == undefined) {
|
||||
rels = <RelationTempInfo>{};
|
||||
let rels = relationsTemp.find(
|
||||
val => val.object_id === resp.CONSTRAINT_NAME
|
||||
);
|
||||
if (rels === undefined) {
|
||||
rels = {} as IRelationTempInfo;
|
||||
rels.ownerColumnsNames = [];
|
||||
rels.referencedColumnsNames = [];
|
||||
rels.actionOnDelete =
|
||||
resp.DELETE_RULE == "NO ACTION" ? null : resp.DELETE_RULE;
|
||||
resp.DELETE_RULE === "NO ACTION" ? null : resp.DELETE_RULE;
|
||||
rels.actionOnUpdate = null;
|
||||
rels.object_id = resp.CONSTRAINT_NAME;
|
||||
rels.ownerTable = resp.OWNER_TABLE_NAME;
|
||||
@ -278,39 +293,36 @@ export class OracleDriver extends AbstractDriver {
|
||||
);
|
||||
return entities;
|
||||
}
|
||||
async DisconnectFromServer() {
|
||||
if (this.Connection) await this.Connection.close();
|
||||
public async DisconnectFromServer() {
|
||||
if (this.Connection) {
|
||||
await this.Connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
private Connection: any /*Oracle.IConnection*/;
|
||||
async ConnectToServer(
|
||||
database: string,
|
||||
server: string,
|
||||
port: number,
|
||||
user: string,
|
||||
password: string,
|
||||
ssl: boolean
|
||||
) {
|
||||
public async ConnectToServer(connectionOptons: IConnectionOptions) {
|
||||
let config: any;
|
||||
if (user == String(process.env.ORACLE_UsernameSys)) {
|
||||
if (connectionOptons.user === String(process.env.ORACLE_UsernameSys)) {
|
||||
config /*Oracle.IConnectionAttributes*/ = {
|
||||
user: user,
|
||||
password: password,
|
||||
connectString: `${server}:${port}/${database}`,
|
||||
externalAuth: ssl,
|
||||
privilege: this.Oracle.SYSDBA
|
||||
connectString: `${connectionOptons.host}:${
|
||||
connectionOptons.port
|
||||
}/${connectionOptons.databaseName}`,
|
||||
externalAuth: connectionOptons.ssl,
|
||||
password: connectionOptons.password,
|
||||
privilege: this.Oracle.SYSDBA,
|
||||
user: connectionOptons.user
|
||||
};
|
||||
} else {
|
||||
config /*Oracle.IConnectionAttributes*/ = {
|
||||
user: user,
|
||||
password: password,
|
||||
connectString: `${server}:${port}/${database}`,
|
||||
externalAuth: ssl
|
||||
connectString: `${connectionOptons.host}:${
|
||||
connectionOptons.port
|
||||
}/${connectionOptons.databaseName}`,
|
||||
externalAuth: connectionOptons.ssl,
|
||||
password: connectionOptons.password,
|
||||
user: connectionOptons.user
|
||||
};
|
||||
}
|
||||
let that = this;
|
||||
let promise = new Promise<boolean>((resolve, reject) => {
|
||||
this.Oracle.getConnection(config, function(err, connection) {
|
||||
const that = this;
|
||||
const promise = new Promise<boolean>((resolve, reject) => {
|
||||
this.Oracle.getConnection(config, (err, connection) => {
|
||||
if (!err) {
|
||||
that.Connection = connection;
|
||||
resolve(true);
|
||||
@ -328,7 +340,7 @@ export class OracleDriver extends AbstractDriver {
|
||||
await promise;
|
||||
}
|
||||
|
||||
async CreateDB(dbName: string) {
|
||||
public async CreateDB(dbName: string) {
|
||||
await this.Connection.execute(
|
||||
`CREATE USER ${dbName} IDENTIFIED BY ${String(
|
||||
process.env.ORACLE_Password
|
||||
@ -336,14 +348,28 @@ export class OracleDriver extends AbstractDriver {
|
||||
);
|
||||
await this.Connection.execute(`GRANT CONNECT TO ${dbName}`);
|
||||
}
|
||||
async UseDB(dbName: string) {}
|
||||
async DropDB(dbName: string) {
|
||||
public async UseDB(dbName: string) {
|
||||
// not supported
|
||||
}
|
||||
public async DropDB(dbName: string) {
|
||||
await this.Connection.execute(`DROP USER ${dbName} CASCADE`);
|
||||
}
|
||||
async CheckIfDBExists(dbName: string): Promise<boolean> {
|
||||
var x = await this.Connection.execute(
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
if (defVal.endsWith(" ")) {
|
||||
defVal = defVal.slice(0, -1);
|
||||
}
|
||||
if (defVal.startsWith(`'`)) {
|
||||
return `() => "${defVal}"`;
|
||||
}
|
||||
return `() => "${defVal}"`;
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,42 @@
|
||||
import { AbstractDriver } from "./AbstractDriver";
|
||||
import * as PG from "pg";
|
||||
import { ColumnInfo } from "./../models/ColumnInfo";
|
||||
import { EntityInfo } from "./../models/EntityInfo";
|
||||
import * as TomgUtils from "./../Utils";
|
||||
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 * as TomgUtils from "../Utils";
|
||||
import { AbstractDriver } from "./AbstractDriver";
|
||||
|
||||
export 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;
|
||||
|
||||
GetAllTablesQuery = async (schema: string) => {
|
||||
let response: {
|
||||
public GetAllTablesQuery = async (schema: string) => {
|
||||
const response: Array<{
|
||||
TABLE_SCHEMA: string;
|
||||
TABLE_NAME: string;
|
||||
}[] = (await this.Connection.query(
|
||||
`SELECT table_schema as "TABLE_SCHEMA",table_name as "TABLE_NAME" FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' AND table_schema in (${schema}) `
|
||||
DB_NAME: string;
|
||||
}> = (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;
|
||||
};
|
||||
|
||||
async GetCoulmnsFromEntity(
|
||||
public async GetCoulmnsFromEntity(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
): Promise<EntityInfo[]> {
|
||||
let response: {
|
||||
const response: Array<{
|
||||
table_name: string;
|
||||
column_name: string;
|
||||
udt_name: string;
|
||||
column_default: string;
|
||||
is_nullable: string;
|
||||
data_type: string;
|
||||
@ -31,9 +44,9 @@ export class PostgresDriver extends AbstractDriver {
|
||||
numeric_precision: number;
|
||||
numeric_scale: number;
|
||||
isidentity: string;
|
||||
isunique: number;
|
||||
}[] = (await this.Connection
|
||||
.query(`SELECT table_name,column_name,column_default,is_nullable,
|
||||
isunique: string;
|
||||
}> = (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,
|
||||
(SELECT count(*)
|
||||
@ -45,205 +58,40 @@ export class PostgresDriver extends AbstractDriver {
|
||||
and tc.TABLE_NAME = c.TABLE_NAME
|
||||
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})`))
|
||||
.rows;
|
||||
FROM INFORMATION_SCHEMA.COLUMNS c
|
||||
where table_schema in (${schema})
|
||||
order by ordinal_position`)).rows;
|
||||
entities.forEach(ent => {
|
||||
response
|
||||
.filter(filterVal => {
|
||||
return filterVal.table_name == ent.EntityName;
|
||||
})
|
||||
.filter(filterVal => filterVal.table_name === ent.tsEntityName)
|
||||
.forEach(resp => {
|
||||
let colInfo: ColumnInfo = new ColumnInfo();
|
||||
colInfo.name = resp.column_name;
|
||||
colInfo.is_nullable = resp.is_nullable == "YES";
|
||||
colInfo.is_generated = resp.isidentity == "YES";
|
||||
colInfo.is_unique = resp.isunique == 1;
|
||||
colInfo.default = colInfo.is_generated
|
||||
const colInfo: ColumnInfo = new ColumnInfo();
|
||||
colInfo.tsName = resp.column_name;
|
||||
colInfo.options.name = resp.column_name;
|
||||
colInfo.options.nullable = resp.is_nullable === "YES";
|
||||
colInfo.options.generated = resp.isidentity === "YES";
|
||||
colInfo.options.unique = resp.isunique === "1";
|
||||
colInfo.options.default = colInfo.options.generated
|
||||
? null
|
||||
: resp.column_default;
|
||||
colInfo.sql_type = resp.data_type;
|
||||
switch (resp.data_type) {
|
||||
case "int2":
|
||||
colInfo.ts_type = "number";
|
||||
break;
|
||||
case "int4":
|
||||
colInfo.ts_type = "number";
|
||||
break;
|
||||
case "int8":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "smallint":
|
||||
colInfo.ts_type = "number";
|
||||
break;
|
||||
case "integer":
|
||||
colInfo.ts_type = "number";
|
||||
break;
|
||||
case "bigint":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "decimal":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "numeric":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "real":
|
||||
colInfo.ts_type = "number";
|
||||
break;
|
||||
case "float":
|
||||
colInfo.ts_type = "number";
|
||||
break;
|
||||
case "float4":
|
||||
colInfo.ts_type = "number";
|
||||
break;
|
||||
case "float8":
|
||||
colInfo.ts_type = "number";
|
||||
break;
|
||||
case "double precision":
|
||||
colInfo.ts_type = "number";
|
||||
break;
|
||||
case "money":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "character varying":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "varchar":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "character":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "char":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "text":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "citext":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "hstore":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "bytea":
|
||||
colInfo.ts_type = "Buffer";
|
||||
break;
|
||||
case "bit":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "varbit":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "bit varying":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "timetz":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "timestamptz":
|
||||
colInfo.ts_type = "Date";
|
||||
break;
|
||||
case "timestamp":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "timestamp without time zone":
|
||||
colInfo.ts_type = "Date";
|
||||
break;
|
||||
case "timestamp with time zone":
|
||||
colInfo.ts_type = "Date";
|
||||
break;
|
||||
case "date":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "time":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "time without time zone":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "time with time zone":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "interval":
|
||||
colInfo.ts_type = "any";
|
||||
break;
|
||||
case "bool":
|
||||
colInfo.ts_type = "boolean";
|
||||
break;
|
||||
case "boolean":
|
||||
colInfo.ts_type = "boolean";
|
||||
break;
|
||||
case "enum":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "point":
|
||||
colInfo.ts_type = "string | Object";
|
||||
break;
|
||||
case "line":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "lseg":
|
||||
colInfo.ts_type = "string | string[]";
|
||||
break;
|
||||
case "box":
|
||||
colInfo.ts_type = "string | Object";
|
||||
break;
|
||||
case "path":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "polygon":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "circle":
|
||||
colInfo.ts_type = "string | Object";
|
||||
break;
|
||||
case "cidr":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "inet":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "macaddr":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "tsvector":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "tsquery":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "uuid":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "xml":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "json":
|
||||
colInfo.ts_type = "Object";
|
||||
break;
|
||||
case "jsonb":
|
||||
colInfo.ts_type = "Object";
|
||||
break;
|
||||
case "int4range":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "int8range":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "numrange":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "tsrange":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "tstzrange":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
case "daterange":
|
||||
colInfo.ts_type = "string";
|
||||
break;
|
||||
default:
|
||||
: this.ReturnDefaultValueFunction(resp.column_default);
|
||||
|
||||
const columnTypes = this.MatchColumnTypes(
|
||||
resp.data_type,
|
||||
resp.udt_name
|
||||
);
|
||||
if (!columnTypes.sql_type || !columnTypes.ts_type) {
|
||||
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}`
|
||||
);
|
||||
} else {
|
||||
TomgUtils.LogError(
|
||||
`Unknown column type: ${
|
||||
resp.data_type
|
||||
@ -251,62 +99,305 @@ export class PostgresDriver extends AbstractDriver {
|
||||
resp.table_name
|
||||
} column name: ${resp.column_name}`
|
||||
);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
colInfo.options.type = columnTypes.sql_type as any;
|
||||
colInfo.tsType = columnTypes.ts_type;
|
||||
colInfo.options.array = columnTypes.is_array;
|
||||
if (colInfo.options.array) {
|
||||
colInfo.tsType = colInfo.tsType
|
||||
.split("|")
|
||||
.map(x => x.replace("|", "").trim() + "[]")
|
||||
.join(" | ") as any;
|
||||
}
|
||||
|
||||
if (
|
||||
this.ColumnTypesWithPrecision.some(
|
||||
v => v == colInfo.sql_type
|
||||
v => v === colInfo.options.type
|
||||
)
|
||||
) {
|
||||
colInfo.numericPrecision = resp.numeric_precision;
|
||||
colInfo.numericScale = resp.numeric_scale;
|
||||
colInfo.options.precision = resp.numeric_precision;
|
||||
colInfo.options.scale = resp.numeric_scale;
|
||||
}
|
||||
if (
|
||||
this.ColumnTypesWithLength.some(
|
||||
v => v == colInfo.sql_type
|
||||
v => v === colInfo.options.type
|
||||
)
|
||||
) {
|
||||
colInfo.lenght =
|
||||
colInfo.options.length =
|
||||
resp.character_maximum_length > 0
|
||||
? resp.character_maximum_length
|
||||
: null;
|
||||
: undefined;
|
||||
}
|
||||
if (
|
||||
this.ColumnTypesWithWidth.some(
|
||||
v => v == colInfo.sql_type
|
||||
v => v === colInfo.options.type
|
||||
)
|
||||
) {
|
||||
colInfo.width =
|
||||
colInfo.options.width =
|
||||
resp.character_maximum_length > 0
|
||||
? resp.character_maximum_length
|
||||
: null;
|
||||
: undefined;
|
||||
}
|
||||
if (colInfo.options.type && colInfo.tsType) {
|
||||
ent.Columns.push(colInfo);
|
||||
}
|
||||
if (colInfo.sql_type) ent.Columns.push(colInfo);
|
||||
});
|
||||
});
|
||||
return entities;
|
||||
}
|
||||
async GetIndexesFromEntity(
|
||||
|
||||
public MatchColumnTypes(dataType: string, udtName: string) {
|
||||
const ret: {
|
||||
ts_type:
|
||||
| "number"
|
||||
| "string"
|
||||
| "boolean"
|
||||
| "Date"
|
||||
| "Buffer"
|
||||
| "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;
|
||||
switch (dataType) {
|
||||
case "int2":
|
||||
ret.ts_type = "number";
|
||||
break;
|
||||
case "int4":
|
||||
ret.ts_type = "number";
|
||||
break;
|
||||
case "int8":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "smallint":
|
||||
ret.ts_type = "number";
|
||||
break;
|
||||
case "integer":
|
||||
ret.ts_type = "number";
|
||||
break;
|
||||
case "bigint":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "decimal":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "numeric":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "real":
|
||||
ret.ts_type = "number";
|
||||
break;
|
||||
case "float":
|
||||
ret.ts_type = "number";
|
||||
break;
|
||||
case "float4":
|
||||
ret.ts_type = "number";
|
||||
break;
|
||||
case "float8":
|
||||
ret.ts_type = "number";
|
||||
break;
|
||||
case "double precision":
|
||||
ret.ts_type = "number";
|
||||
break;
|
||||
case "money":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "character varying":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "varchar":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "character":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "char":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "bpchar":
|
||||
ret.sql_type = "char";
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "text":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "citext":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "hstore":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "bytea":
|
||||
ret.ts_type = "Buffer";
|
||||
break;
|
||||
case "bit":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "varbit":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "bit varying":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "timetz":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "timestamptz":
|
||||
ret.ts_type = "Date";
|
||||
break;
|
||||
case "timestamp":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "timestamp without time zone":
|
||||
ret.ts_type = "Date";
|
||||
break;
|
||||
case "timestamp with time zone":
|
||||
ret.ts_type = "Date";
|
||||
break;
|
||||
case "date":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "time":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "time without time zone":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "time with time zone":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "interval":
|
||||
ret.ts_type = "any";
|
||||
break;
|
||||
case "bool":
|
||||
ret.ts_type = "boolean";
|
||||
break;
|
||||
case "boolean":
|
||||
ret.ts_type = "boolean";
|
||||
break;
|
||||
case "enum":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "point":
|
||||
ret.ts_type = "string | Object";
|
||||
break;
|
||||
case "line":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "lseg":
|
||||
ret.ts_type = "string | string[]";
|
||||
break;
|
||||
case "box":
|
||||
ret.ts_type = "string | Object";
|
||||
break;
|
||||
case "path":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "polygon":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "circle":
|
||||
ret.ts_type = "string | Object";
|
||||
break;
|
||||
case "cidr":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "inet":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "macaddr":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "tsvector":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "tsquery":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "uuid":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "xml":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "json":
|
||||
ret.ts_type = "Object";
|
||||
break;
|
||||
case "jsonb":
|
||||
ret.ts_type = "Object";
|
||||
break;
|
||||
case "int4range":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "int8range":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "numrange":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "tsrange":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "tstzrange":
|
||||
ret.ts_type = "string";
|
||||
break;
|
||||
case "daterange":
|
||||
ret.ts_type = "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;
|
||||
break;
|
||||
case "USER-DEFINED":
|
||||
ret.sql_type = udtName;
|
||||
ret.ts_type = "string";
|
||||
switch (udtName) {
|
||||
case "citext":
|
||||
case "hstore":
|
||||
case "geometry":
|
||||
break;
|
||||
default:
|
||||
ret.ts_type = null;
|
||||
ret.sql_type = null;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret.ts_type = null;
|
||||
ret.sql_type = null;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
public async GetIndexesFromEntity(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
): Promise<EntityInfo[]> {
|
||||
let response: {
|
||||
const response: Array<{
|
||||
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,
|
||||
CASE
|
||||
WHEN ix.indisunique = true THEN '1'
|
||||
ELSE '0'
|
||||
WHEN ix.indisunique = true THEN 1
|
||||
ELSE 0
|
||||
END AS is_unique,
|
||||
CASE
|
||||
WHEN ix.indisprimary='true' THEN '1'
|
||||
ELSE '0'
|
||||
WHEN ix.indisprimary='true' THEN 1
|
||||
ELSE 0
|
||||
END AS is_primary_key
|
||||
FROM pg_attribute f
|
||||
JOIN pg_class c ON c.oid = f.attrelid
|
||||
@ -322,29 +413,27 @@ export class PostgresDriver extends AbstractDriver {
|
||||
ORDER BY c.relname,f.attname;`)).rows;
|
||||
entities.forEach(ent => {
|
||||
response
|
||||
.filter(filterVal => {
|
||||
return filterVal.tablename == ent.EntityName;
|
||||
})
|
||||
.filter(filterVal => filterVal.tablename === ent.tsEntityName)
|
||||
.forEach(resp => {
|
||||
let indexInfo: IndexInfo = <IndexInfo>{};
|
||||
let indexColumnInfo: IndexColumnInfo = <IndexColumnInfo>{};
|
||||
let indexInfo: IndexInfo = {} as IndexInfo;
|
||||
const indexColumnInfo: IndexColumnInfo = {} as IndexColumnInfo;
|
||||
if (
|
||||
ent.Indexes.filter(filterVal => {
|
||||
return filterVal.name == resp.indexname;
|
||||
}).length > 0
|
||||
ent.Indexes.filter(
|
||||
filterVal => filterVal.name === resp.indexname
|
||||
).length > 0
|
||||
) {
|
||||
indexInfo = ent.Indexes.filter(filterVal => {
|
||||
return filterVal.name == resp.indexname;
|
||||
})[0];
|
||||
indexInfo = ent.Indexes.find(
|
||||
filterVal => filterVal.name === resp.indexname
|
||||
)!;
|
||||
} else {
|
||||
indexInfo.columns = <IndexColumnInfo[]>[];
|
||||
indexInfo.columns = [] as IndexColumnInfo[];
|
||||
indexInfo.name = resp.indexname;
|
||||
indexInfo.isUnique = resp.is_unique == 1;
|
||||
indexInfo.isPrimaryKey = resp.is_primary_key == 1;
|
||||
indexInfo.isUnique = resp.is_unique === 1;
|
||||
indexInfo.isPrimaryKey = resp.is_primary_key === 1;
|
||||
ent.Indexes.push(indexInfo);
|
||||
}
|
||||
indexColumnInfo.name = resp.columnname;
|
||||
if (resp.is_primary_key == 0) {
|
||||
if (resp.is_primary_key === 0) {
|
||||
indexInfo.isPrimaryKey = false;
|
||||
}
|
||||
indexInfo.columns.push(indexColumnInfo);
|
||||
@ -353,11 +442,11 @@ export class PostgresDriver extends AbstractDriver {
|
||||
|
||||
return entities;
|
||||
}
|
||||
async GetRelations(
|
||||
public async GetRelations(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
): Promise<EntityInfo[]> {
|
||||
let response: {
|
||||
const response: Array<{
|
||||
tablewithforeignkey: string;
|
||||
fk_partno: number;
|
||||
foreignkeycolumn: string;
|
||||
@ -366,7 +455,8 @@ export class PostgresDriver extends AbstractDriver {
|
||||
ondelete: "RESTRICT" | "CASCADE" | "SET NULL" | "NO ACTION";
|
||||
onupdate: "RESTRICT" | "CASCADE" | "SET NULL" | "NO ACTION";
|
||||
object_id: string;
|
||||
}[] = (await this.Connection.query(`SELECT
|
||||
// Distinct because of note in https://www.postgresql.org/docs/9.1/information-schema.html
|
||||
}> = (await this.Connection.query(`SELECT DISTINCT
|
||||
con.relname AS tablewithforeignkey,
|
||||
att.attnum as fk_partno,
|
||||
att2.attname AS foreignkeycolumn,
|
||||
@ -374,7 +464,7 @@ export class PostgresDriver extends AbstractDriver {
|
||||
att.attname AS foreignkeycolumnreferenced,
|
||||
delete_rule as ondelete,
|
||||
update_rule as onupdate,
|
||||
con.conname as object_id
|
||||
concat(con.conname,con.conrelid,con.confrelid) as object_id
|
||||
FROM (
|
||||
SELECT
|
||||
unnest(con1.conkey) AS parent,
|
||||
@ -382,7 +472,8 @@ export class PostgresDriver extends AbstractDriver {
|
||||
con1.confrelid,
|
||||
con1.conrelid,
|
||||
cl_1.relname,
|
||||
con1.conname
|
||||
con1.conname,
|
||||
nspname
|
||||
FROM
|
||||
pg_class cl_1,
|
||||
pg_namespace ns,
|
||||
@ -403,20 +494,21 @@ export class PostgresDriver extends AbstractDriver {
|
||||
AND cl.oid = con.confrelid
|
||||
AND att2.attrelid = con.conrelid
|
||||
AND att2.attnum = con.parent
|
||||
and rc.constraint_name= con.conname`)).rows;
|
||||
let relationsTemp: RelationTempInfo[] = <RelationTempInfo[]>[];
|
||||
AND rc.constraint_name= con.conname AND constraint_catalog=current_database() AND rc.constraint_schema=nspname
|
||||
`)).rows;
|
||||
const relationsTemp: IRelationTempInfo[] = [] as IRelationTempInfo[];
|
||||
response.forEach(resp => {
|
||||
let rels = relationsTemp.find(val => {
|
||||
return val.object_id == resp.object_id;
|
||||
});
|
||||
if (rels == undefined) {
|
||||
rels = <RelationTempInfo>{};
|
||||
let rels = relationsTemp.find(
|
||||
val => val.object_id === resp.object_id
|
||||
);
|
||||
if (rels === undefined) {
|
||||
rels = {} as IRelationTempInfo;
|
||||
rels.ownerColumnsNames = [];
|
||||
rels.referencedColumnsNames = [];
|
||||
rels.actionOnDelete =
|
||||
resp.ondelete == "NO ACTION" ? null : resp.ondelete;
|
||||
resp.ondelete === "NO ACTION" ? null : resp.ondelete;
|
||||
rels.actionOnUpdate =
|
||||
resp.onupdate == "NO ACTION" ? null : resp.onupdate;
|
||||
resp.onupdate === "NO ACTION" ? null : resp.onupdate;
|
||||
rels.object_id = resp.object_id;
|
||||
rels.ownerTable = resp.tablewithforeignkey;
|
||||
rels.referencedTable = resp.tablereferenced;
|
||||
@ -431,9 +523,9 @@ export class PostgresDriver extends AbstractDriver {
|
||||
);
|
||||
return entities;
|
||||
}
|
||||
async DisconnectFromServer() {
|
||||
public async DisconnectFromServer() {
|
||||
if (this.Connection) {
|
||||
let promise = new Promise<boolean>((resolve, reject) => {
|
||||
const promise = new Promise<boolean>((resolve, reject) => {
|
||||
this.Connection.end(err => {
|
||||
if (!err) {
|
||||
resolve(true);
|
||||
@ -451,24 +543,17 @@ export class PostgresDriver extends AbstractDriver {
|
||||
}
|
||||
}
|
||||
|
||||
async ConnectToServer(
|
||||
database: string,
|
||||
server: string,
|
||||
port: number,
|
||||
user: string,
|
||||
password: string,
|
||||
ssl: boolean
|
||||
) {
|
||||
public async ConnectToServer(connectionOptons: IConnectionOptions) {
|
||||
this.Connection = new PG.Client({
|
||||
database: database,
|
||||
host: server,
|
||||
port: port,
|
||||
user: user,
|
||||
password: password,
|
||||
ssl: ssl
|
||||
database: connectionOptons.databaseName,
|
||||
host: connectionOptons.host,
|
||||
password: connectionOptons.password,
|
||||
port: connectionOptons.port,
|
||||
ssl: connectionOptons.ssl,
|
||||
user: connectionOptons.user
|
||||
});
|
||||
|
||||
let promise = new Promise<boolean>((resolve, reject) => {
|
||||
const promise = new Promise<boolean>((resolve, reject) => {
|
||||
this.Connection.connect(err => {
|
||||
if (!err) {
|
||||
resolve(true);
|
||||
@ -486,19 +571,29 @@ export class PostgresDriver extends AbstractDriver {
|
||||
await promise;
|
||||
}
|
||||
|
||||
async CreateDB(dbName: string) {
|
||||
public async CreateDB(dbName: string) {
|
||||
await this.Connection.query(`CREATE DATABASE ${dbName}; `);
|
||||
}
|
||||
async UseDB(dbName: string) {
|
||||
public async UseDB(dbName: string) {
|
||||
await this.Connection.query(`USE ${dbName}; `);
|
||||
}
|
||||
async DropDB(dbName: string) {
|
||||
public async DropDB(dbName: string) {
|
||||
await this.Connection.query(`DROP DATABASE ${dbName}; `);
|
||||
}
|
||||
async CheckIfDBExists(dbName: string): Promise<boolean> {
|
||||
let resp = await this.Connection.query(
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
defVal = defVal.replace(/'::[\w ]*/, "'");
|
||||
if (defVal.startsWith(`'`)) {
|
||||
return `() => "${defVal}"`;
|
||||
}
|
||||
return `() => "${defVal}"`;
|
||||
}
|
||||
}
|
||||
|
@ -1,238 +1,255 @@
|
||||
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 * as TomgUtils from "../Utils";
|
||||
import { AbstractDriver } from "./AbstractDriver";
|
||||
import { ColumnInfo } from "./../models/ColumnInfo";
|
||||
import { EntityInfo } from "./../models/EntityInfo";
|
||||
import * as TomgUtils from "./../Utils";
|
||||
|
||||
export class SqliteDriver extends AbstractDriver {
|
||||
sqlite = require("sqlite3").verbose();
|
||||
db: any;
|
||||
tablesWithGeneratedPrimaryKey: String[] = new Array<String>();
|
||||
GetAllTablesQuery: any;
|
||||
public defaultValues: DataTypeDefaults = new TypeormDriver.SqliteDriver({
|
||||
options: { database: "true" } as ConnectionOptions
|
||||
} as any).dataTypeDefaults;
|
||||
public readonly standardPort = 0;
|
||||
public readonly standardUser = "";
|
||||
public readonly standardSchema = "";
|
||||
|
||||
async GetAllTables(schema: string): Promise<EntityInfo[]> {
|
||||
let ret: EntityInfo[] = <EntityInfo[]>[];
|
||||
let rows = await this.ExecQuery<{ tbl_name: string; sql: string }>(
|
||||
public sqlite = require("sqlite3").verbose();
|
||||
public db: any;
|
||||
public tablesWithGeneratedPrimaryKey: string[] = new Array<string>();
|
||||
public GetAllTablesQuery: any;
|
||||
|
||||
public async GetAllTables(schema: string): 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_%'`
|
||||
);
|
||||
rows.forEach(val => {
|
||||
let ent: EntityInfo = new EntityInfo();
|
||||
ent.EntityName = val.tbl_name;
|
||||
ent.Columns = <ColumnInfo[]>[];
|
||||
ent.Indexes = <IndexInfo[]>[];
|
||||
const ent: EntityInfo = new EntityInfo();
|
||||
ent.tsEntityName = val.tbl_name;
|
||||
ent.Columns = [] as ColumnInfo[];
|
||||
ent.Indexes = [] as IndexInfo[];
|
||||
if (val.sql.includes("AUTOINCREMENT")) {
|
||||
this.tablesWithGeneratedPrimaryKey.push(ent.EntityName);
|
||||
this.tablesWithGeneratedPrimaryKey.push(ent.tsEntityName);
|
||||
}
|
||||
ret.push(ent);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
async GetCoulmnsFromEntity(
|
||||
public async GetCoulmnsFromEntity(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
): Promise<EntityInfo[]> {
|
||||
for (const ent of entities) {
|
||||
let response = await this.ExecQuery<{
|
||||
const response = await this.ExecQuery<{
|
||||
cid: number;
|
||||
name: string;
|
||||
type: string;
|
||||
notnull: number;
|
||||
dflt_value: string;
|
||||
pk: number;
|
||||
}>(`PRAGMA table_info('${ent.EntityName}');`);
|
||||
}>(`PRAGMA table_info('${ent.tsEntityName}');`);
|
||||
response.forEach(resp => {
|
||||
let colInfo: ColumnInfo = new ColumnInfo();
|
||||
colInfo.name = resp.name;
|
||||
colInfo.is_nullable = resp.notnull == 0;
|
||||
colInfo.isPrimary = resp.pk > 0;
|
||||
colInfo.default = resp.dflt_value ? resp.dflt_value : null;
|
||||
colInfo.sql_type = resp.type
|
||||
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();
|
||||
colInfo.is_generated =
|
||||
colInfo.isPrimary &&
|
||||
this.tablesWithGeneratedPrimaryKey.includes(ent.EntityName);
|
||||
switch (colInfo.sql_type) {
|
||||
.trim() as any;
|
||||
colInfo.options.generated =
|
||||
colInfo.options.primary &&
|
||||
this.tablesWithGeneratedPrimaryKey.includes(
|
||||
ent.tsEntityName
|
||||
);
|
||||
switch (colInfo.options.type) {
|
||||
case "int":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "integer":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "int2":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "int8":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "tinyint":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "smallint":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "mediumint":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "bigint":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "unsigned big int":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "character":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "varchar":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "varying character":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "nchar":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "native character":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "nvarchar":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "text":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "blob":
|
||||
colInfo.ts_type = "Buffer";
|
||||
colInfo.tsType = "Buffer";
|
||||
break;
|
||||
case "clob":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "real":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "double":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "double precision":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "float":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "numeric":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "decimal":
|
||||
colInfo.ts_type = "number";
|
||||
colInfo.tsType = "number";
|
||||
break;
|
||||
case "boolean":
|
||||
colInfo.ts_type = "boolean";
|
||||
colInfo.tsType = "boolean";
|
||||
break;
|
||||
case "date":
|
||||
colInfo.ts_type = "string";
|
||||
colInfo.tsType = "string";
|
||||
break;
|
||||
case "datetime":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.tsType = "Date";
|
||||
break;
|
||||
default:
|
||||
console.log(colInfo.sql_type.toLowerCase().trim());
|
||||
TomgUtils.LogError(
|
||||
`Unknown column type: ${
|
||||
colInfo.sql_type
|
||||
} table name: ${ent.EntityName} column name: ${
|
||||
colInfo.options.type
|
||||
} table name: ${ent.tsEntityName} column name: ${
|
||||
resp.name
|
||||
}`
|
||||
);
|
||||
break;
|
||||
}
|
||||
let options = resp.type.match(/\([0-9 ,]+\)/g);
|
||||
const options = resp.type.match(/\([0-9 ,]+\)/g);
|
||||
if (
|
||||
this.ColumnTypesWithPrecision.some(
|
||||
v => v == colInfo.sql_type
|
||||
v => v === colInfo.options.type
|
||||
) &&
|
||||
options
|
||||
) {
|
||||
colInfo.numericPrecision = <any>options[0]
|
||||
colInfo.options.precision = options[0]
|
||||
.substring(1, options[0].length - 1)
|
||||
.split(",")[0];
|
||||
colInfo.numericScale = <any>options[0]
|
||||
.split(",")[0] as any;
|
||||
colInfo.options.scale = options[0]
|
||||
.substring(1, options[0].length - 1)
|
||||
.split(",")[1];
|
||||
.split(",")[1] as any;
|
||||
}
|
||||
if (
|
||||
this.ColumnTypesWithLength.some(
|
||||
v => v == colInfo.sql_type
|
||||
v => v === colInfo.options.type
|
||||
) &&
|
||||
options
|
||||
) {
|
||||
colInfo.lenght = <any>options[0].substring(
|
||||
colInfo.options.length = options[0].substring(
|
||||
1,
|
||||
options[0].length - 1
|
||||
);
|
||||
) as any;
|
||||
}
|
||||
if (
|
||||
this.ColumnTypesWithWidth.some(
|
||||
v =>
|
||||
v == colInfo.sql_type &&
|
||||
colInfo.ts_type != "boolean"
|
||||
v === colInfo.options.type &&
|
||||
colInfo.tsType !== "boolean"
|
||||
) &&
|
||||
options
|
||||
) {
|
||||
colInfo.width = <any>options[0].substring(
|
||||
colInfo.options.width = options[0].substring(
|
||||
1,
|
||||
options[0].length - 1
|
||||
);
|
||||
) as any;
|
||||
}
|
||||
|
||||
if (colInfo.sql_type) ent.Columns.push(colInfo);
|
||||
if (colInfo.options.type) {
|
||||
ent.Columns.push(colInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
async GetIndexesFromEntity(
|
||||
public async GetIndexesFromEntity(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
): Promise<EntityInfo[]> {
|
||||
for (const ent of entities) {
|
||||
let response = await this.ExecQuery<{
|
||||
const response = await this.ExecQuery<{
|
||||
seq: number;
|
||||
name: string;
|
||||
unique: number;
|
||||
origin: string;
|
||||
partial: number;
|
||||
}>(`PRAGMA index_list('${ent.EntityName}');`);
|
||||
}>(`PRAGMA index_list('${ent.tsEntityName}');`);
|
||||
for (const resp of response) {
|
||||
let indexColumnsResponse = await this.ExecQuery<{
|
||||
const indexColumnsResponse = await this.ExecQuery<{
|
||||
seqno: number;
|
||||
cid: number;
|
||||
name: string;
|
||||
}>(`PRAGMA index_info('${resp.name}');`);
|
||||
indexColumnsResponse.forEach(element => {
|
||||
let indexInfo: IndexInfo = <IndexInfo>{};
|
||||
let indexColumnInfo: IndexColumnInfo = <IndexColumnInfo>{};
|
||||
let indexInfo: IndexInfo = {} as IndexInfo;
|
||||
const indexColumnInfo: IndexColumnInfo = {} as IndexColumnInfo;
|
||||
if (
|
||||
ent.Indexes.filter(filterVal => {
|
||||
return filterVal.name == resp.name;
|
||||
return filterVal.name === resp.name;
|
||||
}).length > 0
|
||||
) {
|
||||
indexInfo = ent.Indexes.filter(filterVal => {
|
||||
return filterVal.name == resp.name;
|
||||
})[0];
|
||||
indexInfo = ent.Indexes.find(
|
||||
filterVal => filterVal.name === resp.name
|
||||
)!;
|
||||
} else {
|
||||
indexInfo.columns = <IndexColumnInfo[]>[];
|
||||
indexInfo.columns = [] as IndexColumnInfo[];
|
||||
indexInfo.name = resp.name;
|
||||
indexInfo.isUnique = resp.unique == 1;
|
||||
indexInfo.isUnique = resp.unique === 1;
|
||||
ent.Indexes.push(indexInfo);
|
||||
}
|
||||
indexColumnInfo.name = element.name;
|
||||
if (
|
||||
indexColumnsResponse.length == 1 &&
|
||||
indexColumnsResponse.length === 1 &&
|
||||
indexInfo.isUnique
|
||||
) {
|
||||
ent.Columns.filter(
|
||||
v => v.name == indexColumnInfo.name
|
||||
).map(v => (v.is_unique = true));
|
||||
v => v.tsName === indexColumnInfo.name
|
||||
).map(v => (v.options.unique = true));
|
||||
}
|
||||
indexInfo.columns.push(indexColumnInfo);
|
||||
});
|
||||
@ -241,12 +258,12 @@ export class SqliteDriver extends AbstractDriver {
|
||||
|
||||
return entities;
|
||||
}
|
||||
async GetRelations(
|
||||
public async GetRelations(
|
||||
entities: EntityInfo[],
|
||||
schema: string
|
||||
): Promise<EntityInfo[]> {
|
||||
for (const entity of entities) {
|
||||
let response = await this.ExecQuery<{
|
||||
const response = await this.ExecQuery<{
|
||||
id: number;
|
||||
seq: number;
|
||||
table: string;
|
||||
@ -255,17 +272,17 @@ export class SqliteDriver extends AbstractDriver {
|
||||
on_update: "RESTRICT" | "CASCADE" | "SET NULL" | "NO ACTION";
|
||||
on_delete: "RESTRICT" | "CASCADE" | "SET NULL" | "NO ACTION";
|
||||
match: string;
|
||||
}>(`PRAGMA foreign_key_list('${entity.EntityName}');`);
|
||||
let relationsTemp: RelationTempInfo[] = <RelationTempInfo[]>[];
|
||||
}>(`PRAGMA foreign_key_list('${entity.tsEntityName}');`);
|
||||
const relationsTemp: IRelationTempInfo[] = [] as IRelationTempInfo[];
|
||||
response.forEach(resp => {
|
||||
let rels = <RelationTempInfo>{};
|
||||
const rels = {} as IRelationTempInfo;
|
||||
rels.ownerColumnsNames = [];
|
||||
rels.referencedColumnsNames = [];
|
||||
rels.actionOnDelete =
|
||||
resp.on_delete == "NO ACTION" ? null : resp.on_delete;
|
||||
resp.on_delete === "NO ACTION" ? null : resp.on_delete;
|
||||
rels.actionOnUpdate =
|
||||
resp.on_update == "NO ACTION" ? null : resp.on_update;
|
||||
rels.ownerTable = entity.EntityName;
|
||||
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);
|
||||
@ -278,27 +295,26 @@ export class SqliteDriver extends AbstractDriver {
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
async DisconnectFromServer() {
|
||||
public async DisconnectFromServer() {
|
||||
this.db.close();
|
||||
}
|
||||
|
||||
async ConnectToServer(
|
||||
database: string,
|
||||
server: string,
|
||||
port: number,
|
||||
user: string,
|
||||
password: string,
|
||||
ssl: boolean
|
||||
) {
|
||||
await this.UseDB(database);
|
||||
public async ConnectToServer(connectionOptons: IConnectionOptions) {
|
||||
await this.UseDB(connectionOptons.databaseName);
|
||||
}
|
||||
|
||||
async CreateDB(dbName: string) {}
|
||||
async UseDB(dbName: string) {
|
||||
let promise = new Promise<boolean>((resolve, reject) => {
|
||||
public async CreateDB(dbName: string) {
|
||||
// not supported
|
||||
}
|
||||
public async UseDB(dbName: string) {
|
||||
const promise = new Promise<boolean>((resolve, reject) => {
|
||||
this.db = new this.sqlite.Database(dbName, err => {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
TomgUtils.LogError(
|
||||
"Error connecting to SQLite database.",
|
||||
false,
|
||||
err.message
|
||||
);
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
@ -307,16 +323,18 @@ export class SqliteDriver extends AbstractDriver {
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
async DropDB(dbName: string) {}
|
||||
async CheckIfDBExists(dbName: string): Promise<boolean> {
|
||||
public async DropDB(dbName: string) {
|
||||
// not supported
|
||||
}
|
||||
public async CheckIfDBExists(dbName: string): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
async ExecQuery<T>(sql: string): Promise<Array<T>> {
|
||||
public async ExecQuery<T>(sql: string): Promise<T[]> {
|
||||
let ret: any;
|
||||
let promise = new Promise<boolean>((resolve, reject) => {
|
||||
const promise = new Promise<boolean>((resolve, reject) => {
|
||||
this.db.serialize(() => {
|
||||
this.db.all(sql, [], function(err, row) {
|
||||
this.db.all(sql, [], (err, row) => {
|
||||
if (!err) {
|
||||
ret = row;
|
||||
resolve(true);
|
||||
@ -334,4 +352,13 @@ export class SqliteDriver extends AbstractDriver {
|
||||
await promise;
|
||||
return ret;
|
||||
}
|
||||
private ReturnDefaultValueFunction(defVal: string | null): string | null {
|
||||
if (!defVal) {
|
||||
return null;
|
||||
}
|
||||
if (defVal.startsWith(`'`)) {
|
||||
return `() => "${defVal}"`;
|
||||
}
|
||||
return `() => "${defVal}"`;
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +1,42 @@
|
||||
import {Index,Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable} from "typeorm";
|
||||
import {BaseEntity,Column,Entity,Index,JoinColumn,JoinTable,ManyToMany,ManyToOne,OneToMany,OneToOne,PrimaryColumn,PrimaryGeneratedColumn,RelationId} from "typeorm";
|
||||
{{relationImports}}{{#each UniqueImports}}import {{curly true}}{{toEntityName this}}{{curly false}} from "./{{toFileName this}}";
|
||||
{{/each}}
|
||||
|
||||
|
||||
@Entity("{{EntityName}}"{{#Schema}},{schema:"{{.}}"}{{/Schema}})
|
||||
{{#Indexes}}{{^isPrimaryKey}}@Index("{{name}}",[{{#columns}}"{{name}}",{{/columns}}]{{#isUnique}},{unique:true}{{/isUnique}})
|
||||
{{/isPrimaryKey}}{{/Indexes}}export class {{toEntityName EntityName}} {
|
||||
@Entity("{{sqlEntityName}}"{{#Schema}},{schema:"{{.}}"{{#if ../Database}}, database:"{{../Database}}"{{/if}} } {{/Schema}})
|
||||
{{#Indexes}}{{^isPrimaryKey}}@Index("{{name}}",[{{#columns}}"{{toPropertyName name}}",{{/columns}}]{{#isUnique}},{unique:true}{{/isUnique}})
|
||||
{{/isPrimaryKey}}{{/Indexes}}export class {{toEntityName tsEntityName}}{{#IsActiveRecord}} extends BaseEntity{{/IsActiveRecord}} {
|
||||
{{#Columns}}
|
||||
|
||||
{{^relations}} @Column("{{sql_type}}",{ {{#is_generated}}
|
||||
generated:true,{{/is_generated}}{{#is_nullable}}
|
||||
nullable:true,{{/is_nullable}}{{^is_nullable}}
|
||||
nullable:false,{{/is_nullable}}{{#is_unique}}
|
||||
unique: true,{{/is_unique}}{{#lenght}}
|
||||
length:{{.}},{{/lenght}}{{#width}}
|
||||
{{^relations}}{{#options}}{{#generated}} @PrimaryGeneratedColumn({
|
||||
type:"{{type}}", {{/generated}}{{^generated}} @Column("{{type}}",{ {{#nullable}}
|
||||
nullable:true,{{/nullable}}{{^nullable}}
|
||||
nullable:false,{{/nullable}}{{#primary}}
|
||||
primary:{{primary}},{{/primary}}{{/generated}}{{#unique}}
|
||||
unique: true,{{/unique}}{{#length}}
|
||||
length:{{.}},{{/length}}{{#width}}
|
||||
width:{{.}},{{/width}}{{#default}}
|
||||
default:"{{.}}",{{/default}}{{#numericPrecision}}
|
||||
precision:{{.}},{{/numericPrecision}}{{#numericScale}}
|
||||
scale:{{.}},{{/numericScale}}{{#isPrimary}}
|
||||
primary:{{isPrimary}},{{/isPrimary}}{{#enumOptions}}
|
||||
enum:[{{.}}],{{/enumOptions}}
|
||||
default: {{.}},{{/default}}{{#precision}}
|
||||
precision:{{.}},{{/precision}}{{#scale}}
|
||||
scale:{{.}},{{/scale}}{{#enum}}
|
||||
enum:[{{.}}],{{/enum}}{{#array}}
|
||||
array:{{array}},{{/array}}
|
||||
name:"{{name}}"
|
||||
})
|
||||
{{toPropertyName name}}:{{ts_type}};
|
||||
}){{/options}}
|
||||
{{printPropertyVisibility}}{{toPropertyName tsName}}:{{tsType}}{{#options/nullable}} | null{{/options/nullable}};
|
||||
{{/relations}}{{#relations}}
|
||||
@{{relationType}}(type=>{{toEntityName relatedTable}}, {{toPropertyName ../name}}=>{{toPropertyName ../name}}.{{#if isOwner}}{{toPropertyName ownerColumn}},{ {{#../isPrimary}}primary:true,{{/../isPrimary}}{{^../is_nullable}} nullable:false,{{/../is_nullable}}{{#actionOnDelete}}onDelete: '{{.}}',{{/actionOnDelete}}{{#actionOnUpdate}}onUpdate: '{{.}}'{{/actionOnUpdate}} }{{else}}{{toPropertyName relatedColumn}}{{#actionOnDelete}},{ onDelete: '{{.}}' }{{/actionOnDelete}}{{/if}}){{#isOwner}}
|
||||
{{#if isManyToMany}}@JoinTable(){{else}}@JoinColumn({ name:'{{ ../name}}'}){{/if}}{{/isOwner}}
|
||||
{{#if (or isOneToMany isManyToMany)}}{{toPropertyName ../name}}:{{toLazy (concat (toEntityName relatedTable) "[]")}};
|
||||
{{else}}{{toPropertyName ../name}}:{{toLazy (toEntityName relatedTable)}};
|
||||
{{/if}}{{/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}}
|
||||
{{#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}}
|
||||
{{/Columns}}
|
||||
{{#if GenerateConstructor}}
|
||||
constructor(init?: Partial<{{toEntityName EntityName}}>) {
|
||||
constructor(init?: Partial<{{toEntityName tsEntityName}}>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
{{/if}}
|
||||
|
589
src/index.ts
589
src/index.ts
@ -1,160 +1,441 @@
|
||||
import { AbstractDriver } from "./drivers/AbstractDriver";
|
||||
import { MssqlDriver } from "./drivers/MssqlDriver";
|
||||
import { PostgresDriver } from "./drivers/PostgresDriver";
|
||||
import { SqliteDriver } from "./drivers/SqliteDriver";
|
||||
import { MysqlDriver } from "./drivers/MysqlDriver";
|
||||
import { MariaDbDriver } from "./drivers/MariaDbDriver";
|
||||
import { OracleDriver } from "./drivers/OracleDriver";
|
||||
import { Engine } from "./Engine";
|
||||
import * as Yargs from "yargs";
|
||||
import * as TomgUtils from "./Utils";
|
||||
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";
|
||||
|
||||
var argv = Yargs.usage(
|
||||
"Usage: typeorm-model-generator -h <host> -d <database> -p [port] -u <user> -x [password] -e [engine]"
|
||||
)
|
||||
.option("h", {
|
||||
alias: "host",
|
||||
describe: "IP adress/Hostname for database server",
|
||||
default: "127.0.0.1"
|
||||
})
|
||||
.option("d", {
|
||||
alias: "database",
|
||||
describe: "Database name(or path for sqlite)",
|
||||
demand: true
|
||||
})
|
||||
.option("u", {
|
||||
alias: "user",
|
||||
describe: "Username for database server"
|
||||
})
|
||||
.option("x", {
|
||||
alias: "pass",
|
||||
describe: "Password for database server",
|
||||
default: ""
|
||||
})
|
||||
.option("p", {
|
||||
alias: "port",
|
||||
describe: "Port number for database server"
|
||||
})
|
||||
.option("e", {
|
||||
alias: "engine",
|
||||
describe: "Database engine",
|
||||
choices: ["mssql", "postgres", "mysql", "mariadb", "oracle", "sqlite"],
|
||||
default: "mssql"
|
||||
})
|
||||
.option("o", {
|
||||
alias: "output",
|
||||
describe: "Where to place generated models",
|
||||
default: path.resolve(process.cwd(), "output")
|
||||
})
|
||||
.option("s", {
|
||||
alias: "schema",
|
||||
describe:
|
||||
"Schema name to create model from. Only for mssql and postgres"
|
||||
})
|
||||
.option("ssl", {
|
||||
boolean: true,
|
||||
default: false
|
||||
})
|
||||
.option("noConfig", {
|
||||
boolean: true,
|
||||
describe: `Doesn't create tsconfig.json and ormconfig.json`,
|
||||
default: false
|
||||
})
|
||||
.option("cf", {
|
||||
alias: "case-file",
|
||||
describe: "Convert file names to specified case",
|
||||
choices: ["pascal", "param", "camel", "none"],
|
||||
default: "none"
|
||||
})
|
||||
.option("ce", {
|
||||
alias: "case-entity",
|
||||
describe: "Convert class names to specified case",
|
||||
choices: ["pascal", "camel", "none"],
|
||||
default: "none"
|
||||
})
|
||||
.option("cp", {
|
||||
alias: "case-property",
|
||||
describe: "Convert property names to specified case",
|
||||
choices: ["pascal", "camel", "none"],
|
||||
default: "none"
|
||||
})
|
||||
.option("lazy", {
|
||||
describe: "Generate lazy relations",
|
||||
boolean: true,
|
||||
default: false
|
||||
})
|
||||
.option("generateConstructor", {
|
||||
describe: "Generate constructor allowing partial initialization",
|
||||
boolean: true,
|
||||
default: false
|
||||
}).argv;
|
||||
CliLogic();
|
||||
|
||||
let driver: AbstractDriver;
|
||||
let standardPort: number;
|
||||
let standardSchema: string = "";
|
||||
let standardUser: string = "";
|
||||
switch (argv.e) {
|
||||
case "mssql":
|
||||
driver = new MssqlDriver();
|
||||
standardPort = 1433;
|
||||
standardSchema = "dbo";
|
||||
standardUser = "sa";
|
||||
break;
|
||||
case "postgres":
|
||||
driver = new PostgresDriver();
|
||||
standardPort = 5432;
|
||||
standardSchema = "public";
|
||||
standardUser = "postgres";
|
||||
break;
|
||||
case "mysql":
|
||||
driver = new MysqlDriver();
|
||||
standardPort = 3306;
|
||||
standardUser = "root";
|
||||
break;
|
||||
case "mariadb":
|
||||
driver = new MysqlDriver();
|
||||
standardPort = 3306;
|
||||
standardUser = "root";
|
||||
break;
|
||||
case "oracle":
|
||||
driver = new OracleDriver();
|
||||
standardPort = 1521;
|
||||
standardUser = "SYS";
|
||||
break;
|
||||
case "sqlite":
|
||||
driver = new SqliteDriver();
|
||||
standardPort = 0;
|
||||
break;
|
||||
default:
|
||||
TomgUtils.LogError("Database engine not recognized.", false);
|
||||
throw new Error("Database engine not recognized.");
|
||||
async function CliLogic() {
|
||||
console.log(TomgUtils.packageVersion());
|
||||
let driver: AbstractDriver;
|
||||
let connectionOptions: IConnectionOptions;
|
||||
let generationOptions: IGenerationOptions;
|
||||
if (process.argv.length > 2) {
|
||||
const retVal = GetUtilParametersByArgs();
|
||||
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 = retVal[0];
|
||||
generationOptions = retVal[1];
|
||||
driver = createDriver(connectionOptions.databaseType);
|
||||
} else {
|
||||
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.`
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let engine = new Engine(driver, {
|
||||
host: argv.h,
|
||||
port: parseInt(argv.p) || standardPort,
|
||||
databaseName: argv.d ? argv.d.toString() : null,
|
||||
user: argv.u ? argv.u.toString() : standardUser,
|
||||
password: argv.x ? argv.x.toString() : null,
|
||||
databaseType: argv.e,
|
||||
resultsPath: argv.o ? argv.o.toString() : null,
|
||||
schemaName: argv.s ? argv.s.toString() : standardSchema,
|
||||
ssl: argv.ssl,
|
||||
noConfigs: argv.noConfig,
|
||||
convertCaseFile: argv.cf,
|
||||
convertCaseEntity: argv.ce,
|
||||
convertCaseProperty: argv.cp,
|
||||
lazy: argv.lazy,
|
||||
constructor: argv.constructor
|
||||
});
|
||||
function GetUtilParametersByArgs() {
|
||||
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", {
|
||||
alias: "host",
|
||||
default: "127.0.0.1",
|
||||
describe: "IP adress/Hostname for database server"
|
||||
})
|
||||
.option("d", {
|
||||
alias: "database",
|
||||
demand: true,
|
||||
describe:
|
||||
"Database name(or path for sqlite). You can pass multiple values separted by comma."
|
||||
})
|
||||
.option("u", {
|
||||
alias: "user",
|
||||
describe: "Username for database server"
|
||||
})
|
||||
.option("x", {
|
||||
alias: "pass",
|
||||
default: "",
|
||||
describe: "Password for database server"
|
||||
})
|
||||
.option("p", {
|
||||
alias: "port",
|
||||
describe: "Port number for database server"
|
||||
})
|
||||
.option("e", {
|
||||
alias: "engine",
|
||||
choices: [
|
||||
"mssql",
|
||||
"postgres",
|
||||
"mysql",
|
||||
"mariadb",
|
||||
"oracle",
|
||||
"sqlite"
|
||||
],
|
||||
default: "mssql",
|
||||
describe: "Database engine"
|
||||
})
|
||||
.option("o", {
|
||||
alias: "output",
|
||||
default: path.resolve(process.cwd(), "output"),
|
||||
describe: "Where to place generated models"
|
||||
})
|
||||
.option("s", {
|
||||
alias: "schema",
|
||||
describe:
|
||||
"Schema name to create model from. Only for mssql and postgres. You can pass multiple values separted by comma."
|
||||
})
|
||||
.option("ssl", {
|
||||
boolean: true,
|
||||
default: false
|
||||
})
|
||||
.option("noConfig", {
|
||||
boolean: true,
|
||||
default: false,
|
||||
describe: `Doesn't create tsconfig.json and ormconfig.json`
|
||||
})
|
||||
.option("cf", {
|
||||
alias: "case-file",
|
||||
choices: ["pascal", "param", "camel", "none"],
|
||||
default: "none",
|
||||
describe: "Convert file names to specified case"
|
||||
})
|
||||
.option("ce", {
|
||||
alias: "case-entity",
|
||||
choices: ["pascal", "camel", "none"],
|
||||
default: "none",
|
||||
describe: "Convert class names to specified case"
|
||||
})
|
||||
.option("cp", {
|
||||
alias: "case-property",
|
||||
choices: ["pascal", "camel", "none"],
|
||||
default: "none",
|
||||
describe: "Convert property names to specified case"
|
||||
})
|
||||
.option("pv", {
|
||||
alias: "property-visibility",
|
||||
choices: ["public", "protected", "private", "none"],
|
||||
default: "none",
|
||||
describe:
|
||||
"Defines which visibility should have the generated property"
|
||||
})
|
||||
.option("lazy", {
|
||||
boolean: true,
|
||||
default: false,
|
||||
describe: "Generate lazy relations"
|
||||
})
|
||||
.option("a", {
|
||||
alias: "active-record",
|
||||
boolean: true,
|
||||
default: false,
|
||||
describe: "Use ActiveRecord syntax for generated models"
|
||||
})
|
||||
.option("namingStrategy", {
|
||||
describe: "Use custom naming strategy"
|
||||
})
|
||||
.option("relationIds", {
|
||||
boolean: true,
|
||||
default: false,
|
||||
describe: "Generate RelationId fields"
|
||||
})
|
||||
.option("generateConstructor", {
|
||||
boolean: true,
|
||||
default: false,
|
||||
describe: "Generate constructor allowing partial initialization"
|
||||
}).argv;
|
||||
|
||||
console.log(TomgUtils.packageVersion());
|
||||
console.log(
|
||||
`[${new Date().toLocaleTimeString()}] Starting creation of model classes.`
|
||||
);
|
||||
engine.createModelFromDatabase().then(() => {
|
||||
console.info(
|
||||
`[${new Date().toLocaleTimeString()}] Typeorm model classes created.`
|
||||
);
|
||||
});
|
||||
const driver = createDriver(argv.e);
|
||||
const standardPort = driver.standardPort;
|
||||
const standardSchema = driver.standardSchema;
|
||||
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);
|
||||
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);
|
||||
|
||||
return { driver, connectionOptions, generationOptions };
|
||||
}
|
||||
|
||||
async function GetUtilParametersByInquirer() {
|
||||
const connectionOptions: IConnectionOptions = new IConnectionOptions();
|
||||
const generationOptions: IGenerationOptions = new IGenerationOptions();
|
||||
|
||||
connectionOptions.databaseType = ((await inquirer.prompt([
|
||||
{
|
||||
choices: [
|
||||
"mssql",
|
||||
"postgres",
|
||||
"mysql",
|
||||
"mariadb",
|
||||
"oracle",
|
||||
"sqlite"
|
||||
],
|
||||
message: "Choose database engine",
|
||||
name: "engine",
|
||||
type: "list"
|
||||
}
|
||||
])) as any).engine;
|
||||
const driver = createDriver(connectionOptions.databaseType);
|
||||
if (connectionOptions.databaseType !== "sqlite") {
|
||||
const answ: any = await inquirer.prompt([
|
||||
{
|
||||
default: "localhost",
|
||||
message: "Database adress:",
|
||||
name: "host",
|
||||
type: "input"
|
||||
},
|
||||
{
|
||||
message: "Database port:",
|
||||
name: "port",
|
||||
type: "input",
|
||||
default(answers: any) {
|
||||
return driver.standardPort;
|
||||
},
|
||||
validate(value) {
|
||||
const valid = !isNaN(parseInt(value, 10));
|
||||
return valid || "Please enter a valid port number";
|
||||
}
|
||||
},
|
||||
{
|
||||
default: false,
|
||||
message: "Use SSL:",
|
||||
name: "ssl",
|
||||
type: "confirm"
|
||||
},
|
||||
{
|
||||
message: "Database user name:",
|
||||
name: "login",
|
||||
type: "input",
|
||||
default(answers: any) {
|
||||
return driver.standardUser;
|
||||
}
|
||||
},
|
||||
{
|
||||
message: "Database user pasword:",
|
||||
name: "password",
|
||||
type: "password"
|
||||
},
|
||||
{
|
||||
default: "",
|
||||
message:
|
||||
"Database name: (You can pass multiple values separted by comma)",
|
||||
name: "dbName",
|
||||
type: "input"
|
||||
}
|
||||
]);
|
||||
if (
|
||||
connectionOptions.databaseType === "mssql" ||
|
||||
connectionOptions.databaseType === "postgres"
|
||||
) {
|
||||
connectionOptions.schemaName = ((await inquirer.prompt([
|
||||
{
|
||||
default: driver.standardSchema,
|
||||
message:
|
||||
"Database schema: (You can pass multiple values separted by comma)",
|
||||
name: "schema",
|
||||
type: "input"
|
||||
}
|
||||
])) as any).schema;
|
||||
}
|
||||
connectionOptions.port = answ.port;
|
||||
connectionOptions.host = answ.host;
|
||||
connectionOptions.user = answ.login;
|
||||
connectionOptions.password = answ.password;
|
||||
connectionOptions.databaseName = answ.dbName;
|
||||
connectionOptions.ssl = answ.ssl;
|
||||
} else {
|
||||
connectionOptions.databaseName = ((await inquirer.prompt([
|
||||
{
|
||||
default: "",
|
||||
message: "Path to database file:",
|
||||
name: "dbName",
|
||||
type: "input"
|
||||
}
|
||||
])) as any).dbName;
|
||||
}
|
||||
generationOptions.resultsPath = ((await inquirer.prompt([
|
||||
{
|
||||
default: path.resolve(process.cwd(), "output"),
|
||||
message: "Path where generated models should be stored:",
|
||||
name: "output",
|
||||
type: "input"
|
||||
}
|
||||
])) as any).output;
|
||||
const customize = ((await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Do you want to customize generated model?",
|
||||
name: "customize",
|
||||
type: "confirm"
|
||||
}
|
||||
])) as any).customize;
|
||||
if (customize) {
|
||||
const customizations: string[] = ((await inquirer.prompt([
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
checked: true,
|
||||
name: "Generate config files",
|
||||
value: "config"
|
||||
},
|
||||
{
|
||||
name: "Generate lazy relations",
|
||||
value: "lazy"
|
||||
},
|
||||
{
|
||||
name: "Use ActiveRecord syntax for generated models",
|
||||
value: "activeRecord"
|
||||
},
|
||||
{
|
||||
name: "Use custom naming strategy",
|
||||
value: "namingStrategy"
|
||||
},
|
||||
{
|
||||
name: "Generate RelationId fields",
|
||||
value: "relationId"
|
||||
},
|
||||
{
|
||||
name:
|
||||
"Generate constructor allowing partial initialization",
|
||||
value: "constructor"
|
||||
},
|
||||
{
|
||||
name: "Use specific naming convention",
|
||||
value: "namingConvention"
|
||||
}
|
||||
],
|
||||
message: "Avaliable customizations",
|
||||
name: "selected",
|
||||
type: "checkbox"
|
||||
}
|
||||
])) as any).selected;
|
||||
generationOptions.noConfigs = !customizations.includes("config");
|
||||
generationOptions.lazy = customizations.includes("lazy");
|
||||
generationOptions.activeRecord = customizations.includes(
|
||||
"activeRecord"
|
||||
);
|
||||
generationOptions.relationIds = customizations.includes("relationId");
|
||||
generationOptions.generateConstructor = customizations.includes(
|
||||
"constructor"
|
||||
);
|
||||
|
||||
if (customizations.includes("namingStrategy")) {
|
||||
const namingStrategyPath = ((await inquirer.prompt([
|
||||
{
|
||||
default: path.resolve(process.cwd()),
|
||||
message: "Path to custom naming stategy file:",
|
||||
name: "namingStrategy",
|
||||
type: "input",
|
||||
validate(value) {
|
||||
const valid = value === "" || fs.existsSync(value);
|
||||
return (
|
||||
valid ||
|
||||
"Please enter a a valid path to custom naming strategy file"
|
||||
);
|
||||
}
|
||||
}
|
||||
])) as any).namingStrategy;
|
||||
|
||||
if (namingStrategyPath && namingStrategyPath !== "") {
|
||||
// tslint:disable-next-line:no-var-requires
|
||||
generationOptions.customNamingStrategyPath = namingStrategyPath;
|
||||
} else {
|
||||
generationOptions.customNamingStrategyPath = "";
|
||||
}
|
||||
}
|
||||
if (customizations.includes("namingConvention")) {
|
||||
const namingConventions = (await inquirer.prompt([
|
||||
{
|
||||
choices: ["pascal", "param", "camel", "none"],
|
||||
default: "none",
|
||||
message: "Convert file names to specified case:",
|
||||
name: "fileCase",
|
||||
type: "list"
|
||||
},
|
||||
{
|
||||
choices: ["pascal", "camel", "none"],
|
||||
default: "none",
|
||||
message: "Convert class names to specified case:",
|
||||
name: "entityCase",
|
||||
type: "list"
|
||||
},
|
||||
{
|
||||
choices: ["pascal", "camel", "none"],
|
||||
default: "none",
|
||||
message: "Convert property names to specified case:",
|
||||
name: "propertyCase",
|
||||
type: "list"
|
||||
}
|
||||
])) as any;
|
||||
generationOptions.convertCaseFile = namingConventions.fileCase;
|
||||
generationOptions.convertCaseProperty =
|
||||
namingConventions.propertyCase;
|
||||
generationOptions.convertCaseEntity = namingConventions.entityCase;
|
||||
}
|
||||
}
|
||||
const saveConfig = ((await inquirer.prompt([
|
||||
{
|
||||
default: false,
|
||||
message: "Save configuration to config file?",
|
||||
name: "saveConfig",
|
||||
type: "confirm"
|
||||
}
|
||||
])) as any).saveConfig;
|
||||
if (saveConfig) {
|
||||
await fs.writeJson(
|
||||
path.resolve(process.cwd(), ".tomg-config"),
|
||||
[connectionOptions, generationOptions],
|
||||
{ spaces: "\t" }
|
||||
);
|
||||
console.log(`[${new Date().toLocaleTimeString()}] Config file saved.`);
|
||||
console.warn(
|
||||
`\x1b[33m[${new Date().toLocaleTimeString()}] WARNING: Password was saved as plain text.\x1b[0m`
|
||||
);
|
||||
}
|
||||
return { driver, connectionOptions, generationOptions };
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { ColumnOptions } from "typeorm";
|
||||
import { RelationInfo } from "./RelationInfo";
|
||||
|
||||
export class ColumnInfo {
|
||||
name: string = "";
|
||||
default: string | null = null;
|
||||
is_nullable: boolean = false;
|
||||
is_unique: boolean = false;
|
||||
ts_type:
|
||||
public options: ColumnOptions = {};
|
||||
public tsName: string = "";
|
||||
public tsType:
|
||||
| "number"
|
||||
| "string"
|
||||
| "boolean"
|
||||
@ -15,17 +14,5 @@ export class ColumnInfo {
|
||||
| "string | Object"
|
||||
| "string | string[]"
|
||||
| "any";
|
||||
sql_type: string;
|
||||
lenght: number | null = null;
|
||||
width: number | null = null;
|
||||
isPrimary: boolean = false;
|
||||
is_generated: boolean = false;
|
||||
numericPrecision: number | null = null;
|
||||
numericScale: number | null = null;
|
||||
enumOptions: string | null = null;
|
||||
relations: RelationInfo[];
|
||||
|
||||
constructor() {
|
||||
this.relations = [];
|
||||
}
|
||||
public relations: RelationInfo[] = [];
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
import { EntityInfo } from "./EntityInfo";
|
||||
export class DatabaseModel {
|
||||
entities: EntityInfo[];
|
||||
}
|
@ -1,24 +1,28 @@
|
||||
import { ColumnInfo } from "./ColumnInfo";
|
||||
|
||||
export class EntityInfo {
|
||||
EntityName: string;
|
||||
Columns: ColumnInfo[];
|
||||
Imports: string[];
|
||||
UniqueImports: string[];
|
||||
Indexes: IndexInfo[];
|
||||
Schema: string;
|
||||
GenerateConstructor: boolean;
|
||||
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;
|
||||
|
||||
relationImports(): any {
|
||||
var imports: string[] = [];
|
||||
public relationImports() {
|
||||
const imports: string[] = [];
|
||||
this.Columns.forEach(column => {
|
||||
column.relations.forEach(relation => {
|
||||
if (this.EntityName != relation.relatedTable)
|
||||
if (this.tsEntityName !== relation.relatedTable) {
|
||||
imports.push(relation.relatedTable);
|
||||
}
|
||||
});
|
||||
});
|
||||
this.UniqueImports = imports.filter(function(elem, index, self) {
|
||||
return index == self.indexOf(elem);
|
||||
});
|
||||
this.UniqueImports = imports.filter(
|
||||
(elem, index, self) => index === self.indexOf(elem)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,35 @@
|
||||
export class RelationInfo {
|
||||
isOwner: boolean;
|
||||
relationType: "OneToOne" | "OneToMany" | "ManyToOne" | "ManyToMany";
|
||||
relatedTable: string;
|
||||
relatedColumn: string;
|
||||
ownerTable: string;
|
||||
ownerColumn: string;
|
||||
actionOnDelete: "RESTRICT" | "CASCADE" | "SET NULL" | null;
|
||||
actionOnUpdate: "RESTRICT" | "CASCADE" | "SET NULL" | null;
|
||||
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"
|
||||
| "SET NULL"
|
||||
| "DEFAULT"
|
||||
| "NO ACTION"
|
||||
| null;
|
||||
public actionOnUpdate:
|
||||
| "RESTRICT"
|
||||
| "CASCADE"
|
||||
| "SET NULL"
|
||||
| "DEFAULT"
|
||||
| null;
|
||||
public relationIdField: boolean = false;
|
||||
|
||||
get isOneToMany(): boolean {
|
||||
return this.relationType == "OneToMany";
|
||||
return this.relationType === "OneToMany";
|
||||
}
|
||||
get isManyToMany(): boolean {
|
||||
return this.relationType == "ManyToMany";
|
||||
return this.relationType === "ManyToMany";
|
||||
}
|
||||
get isOneToOne(): boolean {
|
||||
return this.relationType === "OneToOne";
|
||||
}
|
||||
get isManyToOne(): boolean {
|
||||
return this.relationType === "ManyToOne";
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,15 @@
|
||||
interface RelationTempInfo {
|
||||
interface IRelationTempInfo {
|
||||
ownerTable: string;
|
||||
ownerColumnsNames: string[];
|
||||
referencedTable: string;
|
||||
referencedColumnsNames: string[];
|
||||
actionOnDelete: "RESTRICT" | "CASCADE" | "SET NULL" | null;
|
||||
actionOnUpdate: "RESTRICT" | "CASCADE" | "SET NULL" | null;
|
||||
actionOnDelete:
|
||||
| "RESTRICT"
|
||||
| "CASCADE"
|
||||
| "SET NULL"
|
||||
| "DEFAULT"
|
||||
| "NO ACTION"
|
||||
| null;
|
||||
actionOnUpdate: "RESTRICT" | "CASCADE" | "SET NULL" | "DEFAULT" | null;
|
||||
object_id: number | string;
|
||||
}
|
||||
|
11
src/tslint.json
Normal file
11
src/tslint.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": [
|
||||
"tslint:recommended", "tslint-config-prettier"
|
||||
],
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"no-console":false
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
@ -1,29 +1,30 @@
|
||||
import { expect } from "chai";
|
||||
import { MssqlDriver } from './../../src/drivers/MssqlDriver'
|
||||
import * as Sinon from 'sinon'
|
||||
import * as MSSQL from 'mssql'
|
||||
import { EntityInfo } from './../../src/models/EntityInfo'
|
||||
import { ColumnInfo } from './../../src/models/ColumnInfo'
|
||||
import { RelationInfo } from './../../src/models/RelationInfo'
|
||||
import { Table, IColumnMetadata } 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";
|
||||
|
||||
class fakeResponse implements MSSQL.IResult<any> {
|
||||
recordsets: MSSQL.IRecordSet<any>[];
|
||||
recordset: MSSQL.IRecordSet<any>;
|
||||
rowsAffected: number[];
|
||||
output: { [key: string]: any; };
|
||||
public recordsets: Array<MSSQL.IRecordSet<any>>;
|
||||
public recordset: MSSQL.IRecordSet<any>;
|
||||
public rowsAffected: number[];
|
||||
public output: { [key: string]: any; };
|
||||
}
|
||||
|
||||
class fakeRecordset extends Array<any> implements MSSQL.IRecordSet<any>{
|
||||
columns: IColumnMetadata;
|
||||
toTable(): Table {
|
||||
public columns: IColumnMetadata;
|
||||
public toTable(): Table {
|
||||
return new Table();
|
||||
}
|
||||
}
|
||||
|
||||
describe('MssqlDriver', function () {
|
||||
let driver: MssqlDriver
|
||||
let sandbox = Sinon.sandbox.create()
|
||||
const sandbox = Sinon.sandbox.create()
|
||||
|
||||
beforeEach(() => {
|
||||
driver = new MssqlDriver();
|
||||
@ -38,19 +39,21 @@ describe('MssqlDriver', function () {
|
||||
.returns(
|
||||
{
|
||||
query: (q) => {
|
||||
let response = new fakeResponse();
|
||||
const response = new fakeResponse();
|
||||
response.recordset = new fakeRecordset();
|
||||
response.recordset.push({ TABLE_SCHEMA: 'schema', TABLE_NAME: 'name' })
|
||||
return response;
|
||||
}
|
||||
})
|
||||
let result = await driver.GetAllTables('schema')
|
||||
let expectedResult = <EntityInfo[]>[];
|
||||
let y = new EntityInfo();
|
||||
y.EntityName = 'name'
|
||||
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.Columns = <ColumnInfo[]>[];
|
||||
y.Indexes = <IndexInfo[]>[];
|
||||
y.Columns = [] as ColumnInfo[];
|
||||
y.Indexes = [] as IndexInfo[];
|
||||
y.Database = "";
|
||||
expectedResult.push(y)
|
||||
expect(result).to.be.deep.equal(expectedResult)
|
||||
})
|
||||
@ -59,11 +62,11 @@ describe('MssqlDriver', function () {
|
||||
.returns(
|
||||
{
|
||||
query: (q) => {
|
||||
let response = new fakeResponse();
|
||||
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',
|
||||
COLUMN_DEFAULT: "'a'", COLUMN_NAME: 'name', DATA_TYPE: 'int',
|
||||
IS_NULLABLE: 'YES', NUMERIC_PRECISION: 0, NUMERIC_SCALE: 0,
|
||||
IsIdentity: 1
|
||||
})
|
||||
@ -71,30 +74,28 @@ describe('MssqlDriver', function () {
|
||||
}
|
||||
})
|
||||
|
||||
let entities = <EntityInfo[]>[];
|
||||
let y = new EntityInfo();
|
||||
y.EntityName = 'name'
|
||||
y.Columns = <ColumnInfo[]>[];
|
||||
y.Indexes = <IndexInfo[]>[];
|
||||
const entities = [] as EntityInfo[];
|
||||
const y = new EntityInfo();
|
||||
y.tsEntityName = 'name'
|
||||
y.Columns = [] as ColumnInfo[];
|
||||
y.Indexes = [] as IndexInfo[];
|
||||
y.Database = "";
|
||||
entities.push(y)
|
||||
var expected: EntityInfo[] = JSON.parse(JSON.stringify(entities));
|
||||
const expected: EntityInfo[] = JSON.parse(JSON.stringify(entities));
|
||||
expected[0].Columns.push({
|
||||
lenght: null,
|
||||
default: 'a',
|
||||
is_nullable: true,
|
||||
isPrimary: false,
|
||||
is_generated: true,
|
||||
name: 'name',
|
||||
numericPrecision: null,
|
||||
numericScale: null,
|
||||
width: null,
|
||||
sql_type: 'int',
|
||||
ts_type: 'number',
|
||||
enumOptions: null,
|
||||
is_unique:false,
|
||||
relations: <RelationInfo[]>[]
|
||||
options: {
|
||||
default: `() => "'a'"`,
|
||||
nullable: true,
|
||||
generated: true,
|
||||
name: 'name',
|
||||
unique:false,
|
||||
type: 'int',
|
||||
},
|
||||
tsName: 'name',
|
||||
tsType: 'number',
|
||||
relations: [] as RelationInfo[],
|
||||
})
|
||||
let result = await driver.GetCoulmnsFromEntity(entities, 'schema');
|
||||
const result = await driver.GetCoulmnsFromEntity(entities, 'schema','db');
|
||||
expect(result).to.be.deep.equal(expected)
|
||||
})
|
||||
it('should find primary indexes')
|
||||
|
21
test/integration/defaultValues/mariadb/entity/Post.ts
Normal file
21
test/integration/defaultValues/mariadb/entity/Post.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, JoinColumn } from "typeorm";
|
||||
|
||||
@Entity("Post")
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column("timestamp",{
|
||||
default: () => "CURRENT_TIMESTAMP",
|
||||
})
|
||||
createdAt:Date;
|
||||
|
||||
|
||||
@Column("varchar",{
|
||||
length: 30,
|
||||
default: () => "'defVal'",
|
||||
})
|
||||
text:string;
|
||||
|
||||
}
|
21
test/integration/defaultValues/mssql/entity/Post.ts
Normal file
21
test/integration/defaultValues/mssql/entity/Post.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, JoinColumn } from "typeorm";
|
||||
|
||||
@Entity("Post")
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column("datetime",{
|
||||
default: () => "getdate()",
|
||||
})
|
||||
createdAt:Date;
|
||||
|
||||
|
||||
@Column("varchar",{
|
||||
length: 30,
|
||||
default: () => "'defVal'",
|
||||
})
|
||||
text:string;
|
||||
|
||||
}
|
21
test/integration/defaultValues/mysql/entity/Post.ts
Normal file
21
test/integration/defaultValues/mysql/entity/Post.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, JoinColumn } from "typeorm";
|
||||
|
||||
@Entity("Post")
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column("timestamp",{
|
||||
default: () => "CURRENT_TIMESTAMP",
|
||||
})
|
||||
createdAt:Date;
|
||||
|
||||
|
||||
@Column("varchar",{
|
||||
length: 30,
|
||||
default: () => "'defVal'",
|
||||
})
|
||||
text:string;
|
||||
|
||||
}
|
21
test/integration/defaultValues/oracle/entity/Post.ts
Normal file
21
test/integration/defaultValues/oracle/entity/Post.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, JoinColumn } from "typeorm";
|
||||
|
||||
@Entity("Post")
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column("timestamp",{
|
||||
default: () => "CURRENT_TIMESTAMP",
|
||||
})
|
||||
createdAt:Date;
|
||||
|
||||
|
||||
@Column("varchar",{
|
||||
length: 30,
|
||||
default: () => "'defVal'",
|
||||
})
|
||||
text:string;
|
||||
|
||||
}
|
21
test/integration/defaultValues/postgres/entity/Post.ts
Normal file
21
test/integration/defaultValues/postgres/entity/Post.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, JoinColumn } from "typeorm";
|
||||
|
||||
@Entity("Post")
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column("timestamp",{
|
||||
default: () => "now()",
|
||||
})
|
||||
createdAt:Date;
|
||||
|
||||
|
||||
@Column("varchar",{
|
||||
length: 30,
|
||||
default: () => "'defVal'",
|
||||
})
|
||||
text:string;
|
||||
|
||||
}
|
21
test/integration/defaultValues/sqlite/entity/Post.ts
Normal file
21
test/integration/defaultValues/sqlite/entity/Post.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, JoinColumn } from "typeorm";
|
||||
|
||||
@Entity("Post")
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column("datetime",{
|
||||
default: () => "CURRENT_TIMESTAMP",
|
||||
})
|
||||
createdAt:Date;
|
||||
|
||||
|
||||
@Column("varchar",{
|
||||
length: 30,
|
||||
default: () => "'defVal'",
|
||||
})
|
||||
text:string;
|
||||
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
require('dotenv').config()
|
||||
import "reflect-metadata";
|
||||
import fs = require('fs-extra');
|
||||
import path = require('path')
|
||||
import { expect } from "chai";
|
||||
import { EntityFileToJson } from "../utils/EntityFileToJson";
|
||||
var chai = require('chai');
|
||||
var chaiSubset = require('chai-subset');
|
||||
import * as ts from "typescript";
|
||||
import * as GTU from "../utils/GeneralTestUtils"
|
||||
import { Engine } from "./../../src/Engine";
|
||||
|
||||
chai.use(chaiSubset);
|
||||
|
||||
describe("Platform specyfic types", async function () {
|
||||
this.timeout(30000)
|
||||
this.slow(5000)//compiling created models takes time
|
||||
|
||||
let dbDrivers: string[] = []
|
||||
if (process.env.SQLITE_Skip == '0') dbDrivers.push('sqlite')
|
||||
if (process.env.POSTGRES_Skip == '0') dbDrivers.push('postgres')
|
||||
if (process.env.MYSQL_Skip == '0') dbDrivers.push('mysql')
|
||||
if (process.env.MARIADB_Skip == '0') dbDrivers.push('mariadb')
|
||||
if (process.env.MSSQL_Skip == '0') dbDrivers.push('mssql')
|
||||
if (process.env.ORACLE_Skip == '0') dbDrivers.push('oracle')
|
||||
|
||||
let examplesPathJS = path.resolve(process.cwd(), 'dist/test/integration/entityTypes')
|
||||
let examplesPathTS = path.resolve(process.cwd(), 'test/integration/entityTypes')
|
||||
let files = fs.readdirSync(examplesPathTS)
|
||||
|
||||
for (let dbDriver of dbDrivers) {
|
||||
for (let folder of files) {
|
||||
if (dbDriver == folder) {
|
||||
it(dbDriver, async function () {
|
||||
|
||||
let filesOrgPathJS = path.resolve(examplesPathJS, folder, 'entity')
|
||||
let filesOrgPathTS = path.resolve(examplesPathTS, folder, 'entity')
|
||||
let resultsPath = path.resolve(process.cwd(), `output`)
|
||||
fs.removeSync(resultsPath)
|
||||
|
||||
let engine: Engine;
|
||||
switch (dbDriver) {
|
||||
case 'sqlite':
|
||||
engine = await GTU.createSQLiteModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'postgres':
|
||||
engine = await GTU.createPostgresModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'mysql':
|
||||
engine = await GTU.createMysqlModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'mariadb':
|
||||
engine = await GTU.createMariaDBModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'mssql':
|
||||
engine = await GTU.createMSSQLModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'oracle':
|
||||
engine = await GTU.createOracleDBModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
default:
|
||||
console.log(`Unknown engine type`);
|
||||
engine = <Engine>{}
|
||||
break;
|
||||
}
|
||||
|
||||
await engine.createModelFromDatabase()
|
||||
let filesGenPath = path.resolve(resultsPath, 'entities')
|
||||
|
||||
let filesOrg = fs.readdirSync(filesOrgPathTS).filter(function (this, val, ind, arr) { return val.toString().endsWith('.ts') })
|
||||
let filesGen = fs.readdirSync(filesGenPath).filter(function (this, val, ind, arr) { return val.toString().endsWith('.ts') })
|
||||
|
||||
expect(filesOrg, 'Errors detected in model comparision').to.be.deep.equal(filesGen)
|
||||
|
||||
for (let file of filesOrg) {
|
||||
let entftj = new EntityFileToJson();
|
||||
let jsonEntityOrg = entftj.convert(fs.readFileSync(path.resolve(filesOrgPathTS, file)))
|
||||
let jsonEntityGen = entftj.convert(fs.readFileSync(path.resolve(filesGenPath, file)))
|
||||
expect(jsonEntityGen, `Error in file ${file}`).to.containSubset(jsonEntityOrg)
|
||||
}
|
||||
const currentDirectoryFiles = fs.readdirSync(filesGenPath).
|
||||
filter(fileName => fileName.length >= 3 && fileName.substr(fileName.length - 3, 3) === ".ts").map(v => {
|
||||
return path.resolve(filesGenPath, v)
|
||||
})
|
||||
let compileErrors = GTU.compileTsFiles(currentDirectoryFiles, {
|
||||
experimentalDecorators: true,
|
||||
sourceMap: false,
|
||||
emitDecoratorMetadata: true,
|
||||
target: ts.ScriptTarget.ES2016,
|
||||
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
||||
module: ts.ModuleKind.CommonJS
|
||||
});
|
||||
expect(compileErrors, 'Errors detected while compiling generated model').to.be.false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
@ -66,11 +66,11 @@ export class Post {
|
||||
@Column("text")
|
||||
text: string;
|
||||
|
||||
// @Column("citext")
|
||||
// citext: string;
|
||||
@Column("citext")
|
||||
citext: string;
|
||||
|
||||
// @Column("hstore")
|
||||
// hstore: string;
|
||||
@Column("hstore")
|
||||
hstore: string;
|
||||
|
||||
@Column("bytea")
|
||||
bytea: Buffer;
|
||||
|
191
test/integration/entityTypes/postgres/entity/PostArrays.ts
Normal file
191
test/integration/entityTypes/postgres/entity/PostArrays.ts
Normal file
@ -0,0 +1,191 @@
|
||||
import { Entity, PrimaryColumn, Column } from "typeorm";
|
||||
|
||||
@Entity("PostArrays")
|
||||
export class PostArrays {
|
||||
|
||||
@PrimaryColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@Column("int2", { array: true })
|
||||
int2: number[];
|
||||
|
||||
@Column("int4", { array: true })
|
||||
int4: number[];
|
||||
|
||||
@Column("int8", { array: true })
|
||||
int8: string[];
|
||||
|
||||
@Column("smallint", { array: true })
|
||||
smallint: number[];
|
||||
|
||||
@Column("integer", { array: true })
|
||||
integer: number[];
|
||||
|
||||
@Column("bigint", { array: true })
|
||||
bigint: string[];
|
||||
|
||||
@Column("decimal", { array: true })
|
||||
decimal: string[];
|
||||
|
||||
@Column("numeric", { array: true })
|
||||
numeric: string[];
|
||||
|
||||
@Column("real", { array: true })
|
||||
real: number[];
|
||||
|
||||
@Column("float", { array: true })
|
||||
float: number[];
|
||||
|
||||
@Column("float4", { array: true })
|
||||
float4: number[];
|
||||
|
||||
@Column("float8", { array: true })
|
||||
float8: number[];
|
||||
|
||||
@Column("double precision", { array: true })
|
||||
doublePrecision: number[];
|
||||
|
||||
@Column("money", { array: true })
|
||||
money: string[];
|
||||
|
||||
@Column("character varying", { array: true })
|
||||
characterVarying: string[];
|
||||
|
||||
@Column("varchar", { array: true })
|
||||
varchar: string[];
|
||||
|
||||
@Column("character", { array: true })
|
||||
character: string[];
|
||||
|
||||
@Column("char", { array: true })
|
||||
char: string[];
|
||||
|
||||
@Column("text", { array: true })
|
||||
text: string[];
|
||||
|
||||
@Column("citext", { array: true })
|
||||
citext: string[];
|
||||
|
||||
@Column("hstore", { array: true })
|
||||
hstore: string[];
|
||||
|
||||
@Column("bytea", { array: true })
|
||||
bytea: Buffer[];
|
||||
|
||||
@Column("bit", { array: true })
|
||||
bit: string[];
|
||||
|
||||
@Column("varbit", { array: true })
|
||||
varbit: string[];
|
||||
|
||||
@Column("bit varying", { array: true })
|
||||
bit_varying: string[];
|
||||
|
||||
@Column("timetz", { array: true })
|
||||
timetz: string[];
|
||||
|
||||
@Column("timestamptz", { array: true })
|
||||
timestamptz: Date[];
|
||||
|
||||
// @Column("timestamp", { array: true })
|
||||
// timestamp: Date[];
|
||||
|
||||
// @Column("timestamp without time zone", { array: true })
|
||||
// timestamp_without_time_zone: Date[];
|
||||
|
||||
@Column("timestamp with time zone", { array: true })
|
||||
timestamp_with_time_zone: Date[];
|
||||
|
||||
@Column("date", { array: true })
|
||||
date: string[];
|
||||
|
||||
@Column("time", { array: true })
|
||||
time: string[];
|
||||
@Column("time without time zone", { array: true })
|
||||
time_without_time_zone: string[];
|
||||
|
||||
@Column("time with time zone", { array: true })
|
||||
time_with_time_zone: string[];
|
||||
|
||||
@Column("interval", { array: true })
|
||||
interval: any[];
|
||||
|
||||
@Column("bool", { array: true })
|
||||
bool: boolean[];
|
||||
|
||||
@Column("boolean", { array: true })
|
||||
boolean: boolean[];
|
||||
|
||||
// @Column("enum", { array: true })
|
||||
// enum: string[];
|
||||
|
||||
@Column("point", { array: true })
|
||||
point: string[] | Object[];
|
||||
|
||||
@Column("line", { array: true })
|
||||
line: string[];
|
||||
|
||||
@Column("lseg", { array: true })
|
||||
lseg: string[] | string[][];
|
||||
|
||||
@Column("box", { array: true })
|
||||
box: string[] | Object[];
|
||||
|
||||
@Column("path", { array: true })
|
||||
path: string[];
|
||||
|
||||
@Column("polygon", { array: true })
|
||||
polygon: string[];
|
||||
|
||||
@Column("circle", { array: true })
|
||||
circle: string[] | Object[];
|
||||
|
||||
@Column("cidr", { array: true })
|
||||
cidr: string[];
|
||||
|
||||
@Column("inet", { array: true })
|
||||
inet: string[];
|
||||
|
||||
@Column("macaddr", { array: true })
|
||||
macaddr: string[];
|
||||
|
||||
@Column("tsvector", { array: true })
|
||||
tsvector: string[];
|
||||
|
||||
@Column("tsquery", { array: true })
|
||||
tsquery: string[];
|
||||
|
||||
@Column("uuid", { array: true })
|
||||
uuid: string[];
|
||||
|
||||
@Column("xml", { array: true })
|
||||
xml: string[];
|
||||
|
||||
@Column("json", { array: true })
|
||||
json: Object[];
|
||||
|
||||
@Column("jsonb", { array: true })
|
||||
jsonb: Object[];
|
||||
|
||||
@Column("int4range", { array: true })
|
||||
int4range: string[];
|
||||
|
||||
@Column("int8range", { array: true })
|
||||
int8range: string[];
|
||||
|
||||
@Column("numrange", { array: true })
|
||||
numrange: string[];
|
||||
|
||||
@Column("tsrange", { array: true })
|
||||
tsrange: string[];
|
||||
|
||||
@Column("tstzrange", { array: true })
|
||||
tstzrange: string[];
|
||||
|
||||
@Column("daterange", { array: true })
|
||||
daterange: string[];
|
||||
|
||||
}
|
@ -10,7 +10,7 @@ export class Category {
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@ManyToMany(type => Post, post => post.Category)
|
||||
Post: Promise<Post[]>;
|
||||
@ManyToMany(type => Post, post => post.categorys)
|
||||
posts: Promise<Post[]>;
|
||||
|
||||
}
|
||||
|
@ -21,10 +21,10 @@ export class Post {
|
||||
})
|
||||
author: Promise<Author | null>;
|
||||
|
||||
@ManyToMany(type => Category, category => category.Post, {
|
||||
@ManyToMany(type => Category, category => category.posts, {
|
||||
// cascade: true
|
||||
})
|
||||
@JoinTable()
|
||||
Category: Promise<Category[]>;
|
||||
categorys: Promise<Category[]>;
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable } from "typeorm";
|
||||
import {PostDetails} from "./PostDetails";
|
||||
import {PostDetail} from "./PostDetail";
|
||||
import {PostCategory} from "./PostCategory";
|
||||
import {PostAuthor} from "./PostAuthor";
|
||||
import {PostInformation} from "./PostInformation";
|
||||
@ -23,40 +23,40 @@ export class Post {
|
||||
cascade: true
|
||||
})
|
||||
@JoinTable()
|
||||
PostCategory: PostCategory[];
|
||||
postCategorys: PostCategory[];
|
||||
|
||||
// post has relation with details. cascade inserts here means if new PostDetails instance will be set to this
|
||||
// relation it will be inserted automatically to the db when you save this Post entity
|
||||
@ManyToMany(type => PostDetails, details => details.Post, {
|
||||
@ManyToMany(type => PostDetail, details => details.posts, {
|
||||
cascade: true
|
||||
})
|
||||
@JoinTable()
|
||||
PostDetails: PostDetails[];
|
||||
postDetails: PostDetail[];
|
||||
|
||||
// post has relation with details. cascade update here means if new PostDetail instance will be set to this relation
|
||||
// it will be inserted automatically to the db when you save this Post entity
|
||||
@ManyToMany(type => PostImage, image => image.Post, {
|
||||
@ManyToMany(type => PostImage, image => image.posts, {
|
||||
cascade: true
|
||||
})
|
||||
@JoinTable()
|
||||
PostImage: PostImage[];
|
||||
postImages: PostImage[];
|
||||
|
||||
// post has relation with details. cascade update here means if new PostDetail instance will be set to this relation
|
||||
// it will be inserted automatically to the db when you save this Post entity
|
||||
@ManyToMany(type => PostMetadata, metadata => metadata.Post)
|
||||
@ManyToMany(type => PostMetadata, metadata => metadata.posts)
|
||||
@JoinTable()
|
||||
PostMetadata: PostMetadata[];
|
||||
postMetadatas: PostMetadata[];
|
||||
|
||||
// post has relation with details. full cascades here
|
||||
@ManyToMany(type => PostInformation, information => information.Post, {
|
||||
@ManyToMany(type => PostInformation, information => information.posts, {
|
||||
cascade: true
|
||||
})
|
||||
@JoinTable()
|
||||
PostInformation: PostInformation[];
|
||||
postInformations: PostInformation[];
|
||||
|
||||
// post has relation with details. not cascades here. means cannot be persisted, updated or removed
|
||||
@ManyToMany(type => PostAuthor, author => author.Post)
|
||||
@ManyToMany(type => PostAuthor, author => author.posts)
|
||||
@JoinTable()
|
||||
PostAuthor: PostAuthor[];
|
||||
postAuthors: PostAuthor[];
|
||||
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export class PostAuthor {
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@ManyToMany(type => Post, post => post.PostAuthor)
|
||||
Post: Post[];
|
||||
@ManyToMany(type => Post, post => post.postAuthors)
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable } from "typeorm";
|
||||
import {Post} from "./Post";
|
||||
|
||||
@Entity("PostDetails")
|
||||
export class PostDetails {
|
||||
@Entity("PostDetail")
|
||||
export class PostDetail {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@ -16,7 +16,7 @@ export class PostDetails {
|
||||
@Column()
|
||||
metadata: string;
|
||||
|
||||
@ManyToMany(type => Post, post => post.PostDetails)
|
||||
Post: Post[];
|
||||
@ManyToMany(type => Post, post => post.postDetails)
|
||||
posts: Post[];
|
||||
|
||||
}
|
@ -10,7 +10,7 @@ export class PostImage {
|
||||
@Column()
|
||||
url: string;
|
||||
|
||||
@ManyToMany(type => Post, post => post.PostImage)
|
||||
Post: Post[];
|
||||
@ManyToMany(type => Post, post => post.postImages)
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export class PostInformation {
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
@ManyToMany(type => Post, post => post.PostInformation)
|
||||
Post: Post[];
|
||||
@ManyToMany(type => Post, post => post.postInformations)
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export class PostMetadata {
|
||||
@Column()
|
||||
description: string;
|
||||
|
||||
@ManyToMany(type => Post, post => post.PostMetadata)
|
||||
Post: Post[];
|
||||
@ManyToMany(type => Post, post => post.postMetadatas)
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
|
@ -11,8 +11,8 @@ export class Post {
|
||||
type: string;
|
||||
|
||||
// post has relation with details. not cascades here. means cannot be persisted, updated or removed
|
||||
@ManyToMany(type => PostAuthor, author => author.Post)
|
||||
@ManyToMany(type => PostAuthor, author => author.posts)
|
||||
@JoinTable()
|
||||
PostAuthor: PostAuthor[];
|
||||
postAuthors: PostAuthor[];
|
||||
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ export class PostAuthor {
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@ManyToMany(type => Post, post => post.PostAuthor)
|
||||
Post: Post[];
|
||||
@ManyToMany(type => Post, post => post.postAuthors)
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
|
26
test/integration/github-issues/135/entity/Post.ts
Normal file
26
test/integration/github-issues/135/entity/Post.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Index, Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable } from "typeorm";
|
||||
import { PostAuthor } from "./PostAuthor";
|
||||
import { PostCategory } from "./PostCategory";
|
||||
|
||||
|
||||
@Index("travel_travelplanextra_travel_plan_id_extra_id_f825ca51_uniq",["postAuthor","postCategory",],{unique:true})
|
||||
@Entity("Post")
|
||||
export class Post {
|
||||
|
||||
@Column("int", {
|
||||
nullable: false,
|
||||
primary: true,
|
||||
name: "Id"
|
||||
})
|
||||
Id: number;
|
||||
|
||||
@ManyToOne(type => PostAuthor, PostAuthor => PostAuthor.Id)
|
||||
@JoinColumn()
|
||||
postAuthor: PostAuthor;
|
||||
|
||||
|
||||
@ManyToOne(type => PostCategory, PostCategory => PostCategory.Id)
|
||||
@JoinColumn()
|
||||
postCategory: PostCategory;
|
||||
|
||||
}
|
20
test/integration/github-issues/135/entity/PostAuthor.ts
Normal file
20
test/integration/github-issues/135/entity/PostAuthor.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import {Index,Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable, RelationId} from "typeorm";
|
||||
import { Post } from "./Post";
|
||||
|
||||
|
||||
@Entity("PostAuthor")
|
||||
export class PostAuthor {
|
||||
|
||||
@Column("int",{
|
||||
nullable:false,
|
||||
primary:true,
|
||||
name:"Id"
|
||||
})
|
||||
Id:number;
|
||||
|
||||
|
||||
|
||||
@OneToMany(type => Post, Post => Post.Id)
|
||||
posts:Post[];
|
||||
|
||||
}
|
20
test/integration/github-issues/135/entity/PostCategory.ts
Normal file
20
test/integration/github-issues/135/entity/PostCategory.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import {Index,Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable, RelationId} from "typeorm";
|
||||
import { Post } from "./Post";
|
||||
|
||||
|
||||
@Entity("PostCategory")
|
||||
export class PostCategory {
|
||||
|
||||
@Column("int",{
|
||||
nullable:false,
|
||||
primary:true,
|
||||
name:"Id"
|
||||
})
|
||||
Id:number;
|
||||
|
||||
|
||||
|
||||
@OneToMany(type => Post, Post => Post.Id)
|
||||
posts:Post[];
|
||||
|
||||
}
|
22
test/integration/github-issues/144/entity/Post.ts
Normal file
22
test/integration/github-issues/144/entity/Post.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { PrimaryGeneratedColumn, Column, Entity, OneToOne, JoinColumn, Index } from "typeorm";
|
||||
import { PostAuthor } from "./PostAuthor";
|
||||
|
||||
@Entity("Post", {database: "db1"})
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
// post has relation with details. not cascades here. means cannot be persisted, updated or removed
|
||||
@OneToOne(type => PostAuthor, author => author.post)
|
||||
@JoinColumn()
|
||||
// @Index({ unique: true })
|
||||
author: PostAuthor;
|
||||
|
||||
}
|
16
test/integration/github-issues/144/entity/PostAuthor.ts
Normal file
16
test/integration/github-issues/144/entity/PostAuthor.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { PrimaryGeneratedColumn, Column, Entity, OneToOne, JoinColumn } from "typeorm";
|
||||
import { Post } from "./Post";
|
||||
|
||||
@Entity("PostAuthor", {database: "db2"})
|
||||
export class PostAuthor {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@OneToOne(type => Post, post => post.author)
|
||||
post: Post;
|
||||
|
||||
}
|
@ -4,33 +4,33 @@ import {quests} from "./quests";
|
||||
|
||||
|
||||
@Entity("feedextrainfo")
|
||||
@Index("feedExtraInfo_FeedOwnerId_idx",["FeedOwnerId",])
|
||||
@Index("feedExtraInfo_ReaderId_idx",["ReaderId",])
|
||||
@Index("feedExtraInfo_QuestId_idx",["QuestId",])
|
||||
@Index("feedExtraInfo_FeedOwnerId_idx",["feedOwnerId",],{unique:true})
|
||||
@Index("feedExtraInfo_ReaderId_idx",["readerId",],{unique:true})
|
||||
@Index("feedExtraInfo_QuestId_idx",["questId",],{unique:true})
|
||||
export class feedextrainfo {
|
||||
|
||||
|
||||
|
||||
@OneToOne(type=>users, FeedOwnerId=>FeedOwnerId.feedextrainfo,{primary:true, nullable:false, })
|
||||
@JoinColumn({ name:'FeedOwnerId'})
|
||||
FeedOwnerId:users;
|
||||
|
||||
feedOwnerId:users;
|
||||
|
||||
|
||||
|
||||
|
||||
@OneToOne(type=>quests, QuestId=>QuestId.feedextrainfo,{primary:true, nullable:false, })
|
||||
@JoinColumn({ name:'QuestId'})
|
||||
QuestId:quests;
|
||||
|
||||
questId:quests;
|
||||
|
||||
|
||||
|
||||
|
||||
@OneToOne(type=>users, ReaderId=>ReaderId.feedextrainfo2,{primary:true, nullable:false, })
|
||||
@JoinColumn({ name:'ReaderId'})
|
||||
ReaderId:users;
|
||||
|
||||
readerId:users;
|
||||
|
||||
@Column("int",{
|
||||
|
||||
@Column("int",{
|
||||
nullable:false,
|
||||
name:"MostUpdatedFeedEntryIdUserRead"
|
||||
})
|
||||
MostUpdatedFeedEntryIdUserRead:number;
|
||||
|
||||
|
||||
}
|
||||
|
@ -5,16 +5,16 @@ import {feedextrainfo} from "./feedextrainfo";
|
||||
@Entity("quests")
|
||||
export class quests {
|
||||
|
||||
@Column("int",{
|
||||
@Column("int",{
|
||||
nullable:false,
|
||||
primary:true,
|
||||
name:"QuestId"
|
||||
})
|
||||
QuestId:number;
|
||||
|
||||
|
||||
|
||||
@OneToOne(type=>feedextrainfo, feedextrainfo=>feedextrainfo.QuestId)
|
||||
|
||||
|
||||
@OneToOne(type=>feedextrainfo, feedextrainfo=>feedextrainfo.questId)
|
||||
feedextrainfo:feedextrainfo;
|
||||
|
||||
|
||||
}
|
||||
|
@ -5,21 +5,21 @@ import {feedextrainfo} from "./feedextrainfo";
|
||||
@Entity("users")
|
||||
export class users {
|
||||
|
||||
@Column("int",{
|
||||
@Column("int",{
|
||||
nullable:false,
|
||||
primary:true,
|
||||
name:"UserId"
|
||||
})
|
||||
UserId:number;
|
||||
|
||||
|
||||
|
||||
@OneToOne(type=>feedextrainfo, feedextrainfo=>feedextrainfo.FeedOwnerId)
|
||||
|
||||
|
||||
@OneToOne(type=>feedextrainfo, feedextrainfo=>feedextrainfo.feedOwnerId)
|
||||
feedextrainfo:feedextrainfo;
|
||||
|
||||
|
||||
|
||||
@OneToOne(type=>feedextrainfo, feedextrainfo2=>feedextrainfo2.ReaderId)
|
||||
|
||||
|
||||
@OneToOne(type=>feedextrainfo, feedextrainfo2=>feedextrainfo2.readerId)
|
||||
feedextrainfo2:feedextrainfo;
|
||||
|
||||
|
||||
}
|
||||
|
24
test/integration/github-issues/65/entity/Post.ts
Normal file
24
test/integration/github-issues/65/entity/Post.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import {Index,Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable} from "typeorm";
|
||||
import { PostAuthor } from "./PostAuthor";
|
||||
import { PostReader } from "./PostReader";
|
||||
|
||||
|
||||
@Entity("Post")
|
||||
export class Post {
|
||||
|
||||
@Column("int",{
|
||||
nullable:false,
|
||||
primary:true,
|
||||
name:"Id"
|
||||
})
|
||||
Id:number;
|
||||
|
||||
|
||||
|
||||
@OneToOne(type => PostAuthor, PostAuthor => PostAuthor.Id)
|
||||
postAuthor: PostAuthor;
|
||||
|
||||
@OneToMany(type => PostReader, PostReader => PostReader.Id)
|
||||
postReaders: PostReader[];
|
||||
|
||||
}
|
23
test/integration/github-issues/65/entity/PostAuthor.ts
Normal file
23
test/integration/github-issues/65/entity/PostAuthor.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import {Index,Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable, RelationId} from "typeorm";
|
||||
import { Post } from "./Post";
|
||||
|
||||
|
||||
@Entity("PostAuthor")
|
||||
export class PostAuthor {
|
||||
|
||||
@Column("int",{
|
||||
nullable:false,
|
||||
primary:true,
|
||||
name:"Id"
|
||||
})
|
||||
Id:number;
|
||||
|
||||
|
||||
|
||||
@OneToOne(type => Post, Post => Post.Id)
|
||||
@JoinColumn()
|
||||
post:Post;
|
||||
|
||||
@RelationId((postAuthor: PostAuthor) => postAuthor.post)
|
||||
postId: number;
|
||||
}
|
21
test/integration/github-issues/65/entity/PostReader.ts
Normal file
21
test/integration/github-issues/65/entity/PostReader.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import {Index,Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable, RelationId} from "typeorm";
|
||||
import { Post } from "./Post";
|
||||
|
||||
|
||||
@Entity("PostReader")
|
||||
export class PostReader {
|
||||
|
||||
@Column("int",{
|
||||
nullable:false,
|
||||
primary:true,
|
||||
name:"Id"
|
||||
})
|
||||
Id:number;
|
||||
|
||||
@ManyToOne(type => Post, Post => Post.Id)
|
||||
@JoinColumn()
|
||||
post:Post;
|
||||
|
||||
@RelationId((postReader: PostReader) => postReader.post)
|
||||
postId: number[];
|
||||
}
|
41
test/integration/github-issues/71/entity/Post.ts
Normal file
41
test/integration/github-issues/71/entity/Post.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { Index, Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable } from "typeorm";
|
||||
import { PostReader } from "./PostReader";
|
||||
import { PostAuthor } from "./PostAuthor";
|
||||
import { PostCategory } from "./PostCategory";
|
||||
import { PostDetails } from "./PostDetails";
|
||||
|
||||
|
||||
@Entity("Post")
|
||||
export class Post {
|
||||
|
||||
@Column("int", {
|
||||
nullable: false,
|
||||
primary: true,
|
||||
name: "Id"
|
||||
})
|
||||
Id: number;
|
||||
|
||||
@OneToOne(type => PostAuthor, PostAuthor => PostAuthor.Id,
|
||||
{
|
||||
// onDelete: "CASCADE",
|
||||
// onUpdate: "CASCADE"
|
||||
})
|
||||
postAuthor: PostAuthor;
|
||||
|
||||
@OneToOne(type => PostReader, PostReader => PostReader.Id)
|
||||
postReader: PostReader;
|
||||
|
||||
@OneToOne(type => PostCategory, PostCategory => PostCategory.Id,
|
||||
{
|
||||
// onDelete: "RESTRICT",
|
||||
// onUpdate: "RESTRICT"
|
||||
})
|
||||
postCategory: PostCategory;
|
||||
|
||||
@OneToOne(type => PostDetails, PostDetails => PostDetails.Id,
|
||||
{
|
||||
// onDelete: "SET NULL",
|
||||
// onUpdate: "SET NULL"
|
||||
})
|
||||
postDetails: PostDetails;
|
||||
}
|
24
test/integration/github-issues/71/entity/PostAuthor.ts
Normal file
24
test/integration/github-issues/71/entity/PostAuthor.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import {Index,Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable, RelationId} from "typeorm";
|
||||
import { Post } from "./Post";
|
||||
|
||||
|
||||
@Entity("PostAuthor")
|
||||
export class PostAuthor {
|
||||
|
||||
@Column("int",{
|
||||
nullable:false,
|
||||
primary:true,
|
||||
name:"Id"
|
||||
})
|
||||
Id:number;
|
||||
|
||||
|
||||
|
||||
@OneToOne(type => Post, Post => Post.Id,{
|
||||
onDelete: "CASCADE",
|
||||
// onUpdate: "CASCADE"
|
||||
})
|
||||
@JoinColumn()
|
||||
post:Post;
|
||||
|
||||
}
|
25
test/integration/github-issues/71/entity/PostCategory.ts
Normal file
25
test/integration/github-issues/71/entity/PostCategory.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import {Index,Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable, RelationId} from "typeorm";
|
||||
import { Post } from "./Post";
|
||||
|
||||
|
||||
@Entity("PostCategory")
|
||||
export class PostCategory {
|
||||
|
||||
@Column("int",{
|
||||
nullable:false,
|
||||
primary:true,
|
||||
name:"Id"
|
||||
})
|
||||
Id:number;
|
||||
|
||||
|
||||
|
||||
@OneToOne(type => Post, Post => Post.Id,
|
||||
{
|
||||
// onDelete: "RESTRICT",
|
||||
// onUpdate: "RESTRICT"
|
||||
})
|
||||
@JoinColumn()
|
||||
post:Post;
|
||||
|
||||
}
|
23
test/integration/github-issues/71/entity/PostDetails.ts
Normal file
23
test/integration/github-issues/71/entity/PostDetails.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import {Index,Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable, RelationId} from "typeorm";
|
||||
import { Post } from "./Post";
|
||||
|
||||
|
||||
@Entity("PostDetails")
|
||||
export class PostDetails {
|
||||
|
||||
@Column("int",{
|
||||
nullable:false,
|
||||
primary:true,
|
||||
name:"Id"
|
||||
})
|
||||
Id:number;
|
||||
|
||||
@OneToOne(type => Post, Post => Post.Id,
|
||||
{
|
||||
onDelete: "SET NULL",
|
||||
// onUpdate: "SET NULL"
|
||||
})
|
||||
@JoinColumn()
|
||||
post:Post;
|
||||
|
||||
}
|
19
test/integration/github-issues/71/entity/PostReader.ts
Normal file
19
test/integration/github-issues/71/entity/PostReader.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {Index,Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable, RelationId} from "typeorm";
|
||||
import { Post } from "./Post";
|
||||
|
||||
|
||||
@Entity("PostReader")
|
||||
export class PostReader {
|
||||
|
||||
@Column("int",{
|
||||
nullable:false,
|
||||
primary:true,
|
||||
name:"Id"
|
||||
})
|
||||
Id:number;
|
||||
|
||||
@OneToOne(type => Post, Post => Post.Id)
|
||||
@JoinColumn()
|
||||
post:Post;
|
||||
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
require('dotenv').config()
|
||||
import "reflect-metadata";
|
||||
import { createConnection, ConnectionOptions, Connection } from "typeorm";
|
||||
import fs = require('fs-extra');
|
||||
import path = require('path')
|
||||
import { Engine } from "./../../src/Engine";
|
||||
import { expect } from "chai";
|
||||
import * as Sinon from 'sinon'
|
||||
import { EntityFileToJson } from "../utils/EntityFileToJson";
|
||||
var chai = require('chai');
|
||||
var chaiSubset = require('chai-subset');
|
||||
import * as ts from "typescript";
|
||||
import * as GTU from "../utils/GeneralTestUtils"
|
||||
|
||||
chai.use(chaiSubset);
|
||||
|
||||
|
||||
describe("GitHub issues", async function () {
|
||||
this.timeout(30000)
|
||||
this.slow(5000)//compiling created models takes time
|
||||
|
||||
let dbDrivers: string[] = []
|
||||
if (process.env.SQLITE_Skip == '0') dbDrivers.push('sqlite')
|
||||
if (process.env.POSTGRES_Skip == '0') dbDrivers.push('postgres')
|
||||
if (process.env.MYSQL_Skip == '0') dbDrivers.push('mysql')
|
||||
if (process.env.MARIADB_Skip == '0') dbDrivers.push('mariadb')
|
||||
if (process.env.MSSQL_Skip == '0') dbDrivers.push('mssql')
|
||||
if (process.env.ORACLE_Skip == '0') dbDrivers.push('oracle')
|
||||
|
||||
let examplesPathJS = path.resolve(process.cwd(), 'dist/test/integration/github-issues')
|
||||
let examplesPathTS = path.resolve(process.cwd(), 'test/integration/github-issues')
|
||||
let files = fs.readdirSync(examplesPathTS)
|
||||
|
||||
for (let folder of files) {
|
||||
|
||||
describe(`#${folder}`, async function () {
|
||||
for (let dbDriver of dbDrivers) {
|
||||
|
||||
switch (folder) {
|
||||
case '39':
|
||||
if (dbDriver == 'mysql' || dbDriver == 'mariadb' || dbDriver == 'oracle' || dbDriver == 'sqlite')
|
||||
continue;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
it(dbDriver, async function () {
|
||||
|
||||
let filesOrgPathJS = path.resolve(examplesPathJS, folder, 'entity')
|
||||
let filesOrgPathTS = path.resolve(examplesPathTS, folder, 'entity')
|
||||
let resultsPath = path.resolve(process.cwd(), `output`)
|
||||
fs.removeSync(resultsPath)
|
||||
|
||||
let engine: Engine;
|
||||
switch (dbDriver) {
|
||||
case 'sqlite':
|
||||
engine = await GTU.createSQLiteModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'postgres':
|
||||
engine = await GTU.createPostgresModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'mysql':
|
||||
engine = await GTU.createMysqlModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'mariadb':
|
||||
engine = await GTU.createMariaDBModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'mssql':
|
||||
engine = await GTU.createMSSQLModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'oracle':
|
||||
engine = await GTU.createOracleDBModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
default:
|
||||
console.log(`Unknown engine type`);
|
||||
engine = <Engine>{}
|
||||
break;
|
||||
}
|
||||
|
||||
await engine.createModelFromDatabase()
|
||||
let filesGenPath = path.resolve(resultsPath, 'entities')
|
||||
|
||||
let filesOrg = fs.readdirSync(filesOrgPathTS).filter(function (this, val) { return val.toString().endsWith('.ts') })
|
||||
let filesGen = fs.readdirSync(filesGenPath).filter(function (this, val) { return val.toString().endsWith('.ts') })
|
||||
|
||||
expect(filesOrg, 'Errors detected in model comparision').to.be.deep.equal(filesGen)
|
||||
|
||||
for (let file of filesOrg) {
|
||||
let entftj = new EntityFileToJson();
|
||||
let jsonEntityOrg = entftj.convert(fs.readFileSync(path.resolve(filesOrgPathTS, file)))
|
||||
let jsonEntityGen = entftj.convert(fs.readFileSync(path.resolve(filesGenPath, file)))
|
||||
expect(jsonEntityGen, `Error in file ${file}`).to.containSubset(jsonEntityOrg)
|
||||
}
|
||||
const currentDirectoryFiles = fs.readdirSync(filesGenPath).
|
||||
filter(fileName => fileName.length >= 3 && fileName.substr(fileName.length - 3, 3) === ".ts").map(v => {
|
||||
return path.resolve(filesGenPath, v)
|
||||
})
|
||||
let compileErrors = GTU.compileTsFiles(currentDirectoryFiles, {
|
||||
experimentalDecorators: true,
|
||||
sourceMap: false,
|
||||
emitDecoratorMetadata: true,
|
||||
target: ts.ScriptTarget.ES2016,
|
||||
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
||||
module: ts.ModuleKind.CommonJS
|
||||
});
|
||||
expect(compileErrors, 'Errors detected while compiling generated model').to.be.false;
|
||||
});
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
@ -1,101 +0,0 @@
|
||||
require('dotenv').config()
|
||||
import "reflect-metadata";
|
||||
import fs = require('fs-extra');
|
||||
import path = require('path')
|
||||
import { Engine } from "./../../src/Engine";
|
||||
import { expect } from "chai";
|
||||
import { EntityFileToJson } from "../utils/EntityFileToJson";
|
||||
var chai = require('chai');
|
||||
var chaiSubset = require('chai-subset');
|
||||
import * as ts from "typescript";
|
||||
import * as GTU from "../utils/GeneralTestUtils"
|
||||
|
||||
chai.use(chaiSubset);
|
||||
|
||||
describe("TypeOrm examples", async function () {
|
||||
this.timeout(30000)
|
||||
this.slow(5000)//compiling created models takes time
|
||||
|
||||
let dbDrivers: string[] = []
|
||||
if (process.env.SQLITE_Skip == '0') dbDrivers.push('sqlite')
|
||||
if (process.env.POSTGRES_Skip == '0') dbDrivers.push('postgres')
|
||||
if (process.env.MYSQL_Skip == '0') dbDrivers.push('mysql')
|
||||
if (process.env.MARIADB_Skip == '0') dbDrivers.push('mariadb')
|
||||
if (process.env.MSSQL_Skip == '0') dbDrivers.push('mssql')
|
||||
if (process.env.ORACLE_Skip == '0') dbDrivers.push('oracle')
|
||||
|
||||
let examplesPathJS = path.resolve(process.cwd(), 'dist/test/integration/examples')
|
||||
let examplesPathTS = path.resolve(process.cwd(), 'test/integration/examples')
|
||||
let files = fs.readdirSync(examplesPathTS)
|
||||
|
||||
for (let folder of files) {
|
||||
describe(folder, async function () {
|
||||
for (let dbDriver of dbDrivers) {
|
||||
it(dbDriver, async function () {
|
||||
let filesOrgPathJS = path.resolve(examplesPathJS, folder, 'entity')
|
||||
let filesOrgPathTS = path.resolve(examplesPathTS, folder, 'entity')
|
||||
let resultsPath = path.resolve(process.cwd(), `output`)
|
||||
fs.removeSync(resultsPath)
|
||||
|
||||
let engine: Engine;
|
||||
switch (dbDriver) {
|
||||
case 'sqlite':
|
||||
engine = await GTU.createSQLiteModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'postgres':
|
||||
engine = await GTU.createPostgresModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'mysql':
|
||||
engine = await GTU.createMysqlModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'mariadb':
|
||||
engine = await GTU.createMariaDBModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'mssql':
|
||||
engine = await GTU.createMSSQLModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'oracle':
|
||||
engine = await GTU.createOracleDBModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
default:
|
||||
console.log(`Unknown engine type`);
|
||||
engine = <Engine>{}
|
||||
break;
|
||||
}
|
||||
if (folder == 'sample18-lazy-relations') {
|
||||
engine.Options.lazy = true;
|
||||
}
|
||||
|
||||
await engine.createModelFromDatabase()
|
||||
let filesGenPath = path.resolve(resultsPath, 'entities')
|
||||
|
||||
let filesOrg = fs.readdirSync(filesOrgPathTS).filter(function (this, val) { return val.toString().endsWith('.ts') })
|
||||
let filesGen = fs.readdirSync(filesGenPath).filter(function (this, val) { return val.toString().endsWith('.ts') })
|
||||
|
||||
expect(filesOrg, 'Errors detected in model comparision').to.be.deep.equal(filesGen)
|
||||
|
||||
for (let file of filesOrg) {
|
||||
let entftj = new EntityFileToJson();
|
||||
let jsonEntityOrg = entftj.convert(fs.readFileSync(path.resolve(filesOrgPathTS, file)))
|
||||
let jsonEntityGen = entftj.convert(fs.readFileSync(path.resolve(filesGenPath, file)))
|
||||
expect(jsonEntityGen, `Error in file ${file}`).to.containSubset(jsonEntityOrg)
|
||||
}
|
||||
const currentDirectoryFiles = fs.readdirSync(filesGenPath).
|
||||
filter(fileName => fileName.length >= 3 && fileName.substr(fileName.length - 3, 3) === ".ts").map(v => {
|
||||
return path.resolve(filesGenPath, v)
|
||||
})
|
||||
let compileErrors = GTU.compileTsFiles(currentDirectoryFiles, {
|
||||
experimentalDecorators: true,
|
||||
sourceMap: false,
|
||||
emitDecoratorMetadata: true,
|
||||
target: ts.ScriptTarget.ES2016,
|
||||
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
||||
module: ts.ModuleKind.CommonJS
|
||||
});
|
||||
expect(compileErrors, 'Errors detected while compiling generated model').to.be.false;
|
||||
});
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
212
test/integration/runTestsFromPath.test.ts
Normal file
212
test/integration/runTestsFromPath.test.ts
Normal file
@ -0,0 +1,212 @@
|
||||
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 yn = require("yn");
|
||||
import { EntityInfo } from "../../src/models/EntityInfo";
|
||||
|
||||
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
|
||||
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'
|
||||
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);
|
||||
})
|
||||
|
||||
export async function runTestsFromPath(testPartialPath: string, isDbSpecific: boolean) {
|
||||
const resultsPath = path.resolve(process.cwd(), `output`)
|
||||
if (!fs.existsSync(resultsPath)) {
|
||||
fs.mkdirSync(resultsPath);
|
||||
}
|
||||
const dbDrivers: string[] = GTU.getEnabledDbDrivers();
|
||||
for (const dbDriver of dbDrivers) {
|
||||
const newDirPath = path.resolve(resultsPath, dbDriver)
|
||||
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) {
|
||||
runTestForMultipleDrivers(folder, dbDrivers, testPartialPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
let dbModel: EntityInfo[] = [];
|
||||
switch (testName) {
|
||||
case '144':
|
||||
dbModel = await dataCollectionPhase(driver, Object.assign(connectionOptions, { databaseName: 'db1,db2' }));
|
||||
break;
|
||||
|
||||
default:
|
||||
dbModel = await dataCollectionPhase(driver, connectionOptions);
|
||||
break;
|
||||
}
|
||||
|
||||
dbModel = modelCustomizationPhase(dbModel, generationOptions, driver.defaultValues);
|
||||
modelGenerationPhase(connectionOptions, generationOptions, dbModel);
|
||||
const filesGenPath = path.resolve(resultsPath, 'entities');
|
||||
compareGeneratedFiles(filesOrgPathTS, filesGenPath);
|
||||
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))
|
||||
default:
|
||||
return dbDrivers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
let dbModel = await dataCollectionPhase(driver, connectionOptions);
|
||||
dbModel = modelCustomizationPhase(dbModel, generationOptions, driver.defaultValues);
|
||||
modelGenerationPhase(connectionOptions, generationOptions, dbModel);
|
||||
const filesGenPath = path.resolve(resultsPath, 'entities');
|
||||
compareGeneratedFiles(filesOrgPathTS, filesGenPath);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
function compileGeneratedModel(filesGenPath: string, drivers: string[]) {
|
||||
let currentDirectoryFiles: string[] = [];
|
||||
drivers.forEach(driver => {
|
||||
const entitiesPath = path.resolve(filesGenPath, driver, "entities");
|
||||
if (fs.existsSync(entitiesPath)){
|
||||
currentDirectoryFiles.push(...fs.readdirSync(entitiesPath).
|
||||
filter(fileName => fileName.length >= 3 && fileName.substr(fileName.length - 3, 3) === ".ts").map(v => path.resolve(filesGenPath, driver, "entities", v)));
|
||||
}
|
||||
});
|
||||
const compileErrors = GTU.compileTsFiles(currentDirectoryFiles, {
|
||||
experimentalDecorators: true,
|
||||
sourceMap: false,
|
||||
emitDecoratorMetadata: true,
|
||||
target: ts.ScriptTarget.ES2016,
|
||||
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
||||
module: ts.ModuleKind.CommonJS
|
||||
});
|
||||
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(), 'dist', 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':
|
||||
generationOptions.relationIds = true;
|
||||
break;
|
||||
case 'sample18-lazy-relations':
|
||||
generationOptions.lazy = true;
|
||||
break;
|
||||
case '144':
|
||||
|
||||
let connectionOptions: IConnectionOptions;
|
||||
switch (dbDriver) {
|
||||
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),
|
||||
}
|
||||
break;
|
||||
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),
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
await driver.ConnectToServer(connectionOptions!);
|
||||
if (! await driver.CheckIfDBExists('db1')) {
|
||||
var x = await driver.CreateDB('db1')
|
||||
}
|
||||
if (! await driver.CheckIfDBExists('db2')) {
|
||||
var t = await driver.CreateDB('db2')
|
||||
}
|
||||
await driver.DisconnectFromServer();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const connectionOptions = await GTU.createModelsInDb(dbDriver, filesOrgPathJS);
|
||||
return { generationOptions, driver, connectionOptions, resultsPath, filesOrgPathTS };
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
|
||||
export class EntityFileToJson {
|
||||
getEntityOptions(trimmedLine: string, ent: EntityJson) {
|
||||
let decoratorParameters = trimmedLine.slice(trimmedLine.indexOf('(') + 1, trimmedLine.lastIndexOf(')'))
|
||||
public getEntityOptions(trimmedLine: string, ent: EntityJson) {
|
||||
const decoratorParameters = trimmedLine.slice(trimmedLine.indexOf('(') + 1, trimmedLine.lastIndexOf(')'))
|
||||
if (decoratorParameters.length > 0) {
|
||||
if (decoratorParameters[0] == '"' && decoratorParameters.endsWith('"')) {
|
||||
|
||||
} else {
|
||||
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]
|
||||
@ -14,26 +11,24 @@ export class EntityFileToJson {
|
||||
}
|
||||
}
|
||||
}
|
||||
getColumnOptionsAndType(trimmedLine: string, col: EntityColumn) {
|
||||
let decoratorParameters = trimmedLine.slice(trimmedLine.indexOf('(') + 1, trimmedLine.lastIndexOf(')'))
|
||||
public 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) {
|
||||
col.columnTypes = decoratorParameters.substring(0, decoratorParameters.indexOf(',')).trim().split('|').map(function (x) {
|
||||
return x;
|
||||
});
|
||||
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": '))
|
||||
} else {
|
||||
if (decoratorParameters[0] == '"' && decoratorParameters.endsWith('"')) {
|
||||
col.columnTypes = decoratorParameters.split('|').map(function (x) {
|
||||
x = x.trim();
|
||||
return x;
|
||||
});
|
||||
col.columnTypes = decoratorParameters.split('|').map( x=>x.trim())
|
||||
} else {
|
||||
let badJSON = decoratorParameters.substring(decoratorParameters.indexOf(',') + 1).trim()
|
||||
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]
|
||||
}
|
||||
@ -42,33 +37,31 @@ export class EntityFileToJson {
|
||||
}
|
||||
}
|
||||
}
|
||||
getRelationOptions(trimmedLine:string, col:EntityColumn){
|
||||
let decoratorParameters = trimmedLine.slice(trimmedLine.indexOf('(') + 1, trimmedLine.lastIndexOf(')'))
|
||||
public getRelationOptions(trimmedLine:string, col:EntityColumn){
|
||||
const decoratorParameters = trimmedLine.slice(trimmedLine.indexOf('(') + 1, trimmedLine.lastIndexOf(')'))
|
||||
if (decoratorParameters.length > 0) {
|
||||
let params = decoratorParameters.match(/(,)(?!([^{]*}))/g)
|
||||
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": '))
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
getIndexOptions(trimmedLine: string, ind: EntityIndex) {
|
||||
let decoratorParameters = trimmedLine.slice(trimmedLine.indexOf('(') + 1, trimmedLine.lastIndexOf(')'))
|
||||
public getIndexOptions(trimmedLine: string, ind: EntityIndex) {
|
||||
const decoratorParameters = trimmedLine.slice(trimmedLine.indexOf('(') + 1, trimmedLine.lastIndexOf(')'))
|
||||
|
||||
if (decoratorParameters.length > 0) {
|
||||
let containsTables = decoratorParameters.search('\\[') > -1
|
||||
let containsOptions = decoratorParameters.search('{') > -1
|
||||
let containsName = decoratorParameters.search('"') > -1//TODO:no name, but fields as string[]
|
||||
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('"'))
|
||||
}
|
||||
if (containsTables) {
|
||||
let columnsStr = decoratorParameters.slice(decoratorParameters.indexOf('[') + 1, decoratorParameters.indexOf(']'))
|
||||
const columnsStr = decoratorParameters.slice(decoratorParameters.indexOf('[') + 1, decoratorParameters.indexOf(']'))
|
||||
ind.columnNames.push(...columnsStr.split(',').map((val) => {
|
||||
let colName = ''
|
||||
if (val.search('\\.') > -1) {
|
||||
@ -77,12 +70,10 @@ export class EntityFileToJson {
|
||||
colName = val.slice(val.indexOf('"') + 1, val.lastIndexOf('"'))
|
||||
}
|
||||
return colName
|
||||
}).filter(v => {
|
||||
return v.length > 0
|
||||
}))
|
||||
}).filter(v => v.length > 0))
|
||||
}
|
||||
if (containsOptions) {
|
||||
let optionsStr = decoratorParameters.slice(decoratorParameters.indexOf('{') + 1, decoratorParameters.indexOf('}'))
|
||||
const optionsStr = decoratorParameters.slice(decoratorParameters.indexOf('{') + 1, decoratorParameters.indexOf('}'))
|
||||
optionsStr.split(',').forEach((v) => {
|
||||
if (v.split(':').length - 1 > 0) {
|
||||
switch (optionsStr.split(':')[0].trim()) {
|
||||
@ -100,23 +91,25 @@ export class EntityFileToJson {
|
||||
}
|
||||
}
|
||||
|
||||
convert(entityFile: Buffer): EntityJson {
|
||||
let retVal = new EntityJson();
|
||||
public convert(entityFile: Buffer): EntityJson {
|
||||
const retVal = new EntityJson();
|
||||
|
||||
let isInClassBody = false;
|
||||
let isMultilineStatement = false;
|
||||
let priorPartOfMultilineStatement = '';
|
||||
|
||||
let lines = entityFile.toString().replace('\r', '').split('\n');
|
||||
for (let line of lines) {
|
||||
const lines = entityFile.toString().replace('\r', '').split('\n');
|
||||
for (const line of lines) {
|
||||
let trimmedLine = line.trim();
|
||||
if (trimmedLine.startsWith('//')) {
|
||||
continue;
|
||||
}
|
||||
if (isMultilineStatement)
|
||||
if (isMultilineStatement) {
|
||||
trimmedLine = priorPartOfMultilineStatement + ' ' + trimmedLine
|
||||
if (trimmedLine.length == 0)
|
||||
}
|
||||
if (trimmedLine.length == 0) {
|
||||
continue;
|
||||
}
|
||||
else if (!isInClassBody) {
|
||||
if (trimmedLine.startsWith('import')) {
|
||||
continue;
|
||||
@ -140,7 +133,7 @@ export class EntityFileToJson {
|
||||
continue;
|
||||
} else {
|
||||
isMultilineStatement = false;
|
||||
let ind = new EntityIndex()
|
||||
const ind = new EntityIndex()
|
||||
this.getIndexOptions(trimmedLine, ind)
|
||||
retVal.indicies.push(ind);
|
||||
continue;
|
||||
@ -154,7 +147,7 @@ export class EntityFileToJson {
|
||||
continue;
|
||||
} else {
|
||||
isMultilineStatement = false;
|
||||
let col = new EntityColumn()
|
||||
const col = new EntityColumn()
|
||||
this.getColumnOptionsAndType(trimmedLine, col)
|
||||
retVal.columns.push(col);
|
||||
continue;
|
||||
@ -166,9 +159,9 @@ export class EntityFileToJson {
|
||||
continue;
|
||||
} else {
|
||||
isMultilineStatement = false;
|
||||
let col = new EntityColumn()
|
||||
const col = new EntityColumn()
|
||||
this.getColumnOptionsAndType(trimmedLine, col)
|
||||
col.columnOptions['primary'] = true
|
||||
col.columnOptions.primary = true
|
||||
retVal.columns.push(col);
|
||||
continue;
|
||||
}
|
||||
@ -179,7 +172,7 @@ export class EntityFileToJson {
|
||||
continue;
|
||||
} else {
|
||||
isMultilineStatement = false;
|
||||
let col = new EntityColumn()
|
||||
const col = new EntityColumn()
|
||||
this.getColumnOptionsAndType(trimmedLine, col)
|
||||
retVal.columns.push(col);
|
||||
continue;
|
||||
@ -191,10 +184,10 @@ export class EntityFileToJson {
|
||||
continue;
|
||||
} else {
|
||||
isMultilineStatement = false;
|
||||
let col = new EntityColumn()
|
||||
const col = new EntityColumn()
|
||||
this.getColumnOptionsAndType(trimmedLine, col)
|
||||
col.columnOptions['primary'] = true
|
||||
col.columnOptions['generated'] = true
|
||||
col.columnOptions.primary = true
|
||||
col.columnOptions.generated = true
|
||||
retVal.columns.push(col);
|
||||
continue;
|
||||
}
|
||||
@ -205,7 +198,7 @@ export class EntityFileToJson {
|
||||
continue;
|
||||
} else {
|
||||
isMultilineStatement = false;
|
||||
let column = new EntityColumn()
|
||||
const column = new EntityColumn()
|
||||
retVal.columns.push(column)
|
||||
column.relationType = "ManyToOne"
|
||||
column.isOwnerOfRelation = true;
|
||||
@ -218,7 +211,7 @@ export class EntityFileToJson {
|
||||
continue;
|
||||
} else {
|
||||
isMultilineStatement = false;
|
||||
let column = new EntityColumn()
|
||||
const column = new EntityColumn()
|
||||
retVal.columns.push(column)
|
||||
column.relationType = "OneToMany"
|
||||
continue;
|
||||
@ -230,7 +223,7 @@ export class EntityFileToJson {
|
||||
continue;
|
||||
} else {
|
||||
isMultilineStatement = false;
|
||||
let column = new EntityColumn()
|
||||
const column = new EntityColumn()
|
||||
retVal.columns.push(column)
|
||||
column.relationType = "ManyToMany"
|
||||
continue;
|
||||
@ -242,7 +235,7 @@ export class EntityFileToJson {
|
||||
continue;
|
||||
} else {
|
||||
isMultilineStatement = false;
|
||||
let column = new EntityColumn()
|
||||
const column = new EntityColumn()
|
||||
retVal.columns.push(column)
|
||||
column.relationType = "OneToOne"
|
||||
this.getRelationOptions(trimmedLine,column);
|
||||
@ -275,7 +268,7 @@ export class EntityFileToJson {
|
||||
continue;
|
||||
} else {
|
||||
isMultilineStatement = false;
|
||||
let ind = new EntityIndex()
|
||||
const ind = new EntityIndex()
|
||||
this.getIndexOptions(trimmedLine, ind)
|
||||
retVal.indicies.push(ind);
|
||||
continue;
|
||||
@ -291,7 +284,7 @@ export class EntityFileToJson {
|
||||
}
|
||||
} 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?
|
||||
// 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)
|
||||
@ -299,15 +292,15 @@ export class EntityFileToJson {
|
||||
}
|
||||
retVal.columns[retVal.columns.length - 1].columnTypes = colTypes.split('|').map(function (x) {
|
||||
if (x == 'any') {
|
||||
x = 'string' //for json columns
|
||||
x = 'string' // for json columns
|
||||
}
|
||||
x = x.trim();
|
||||
return x;
|
||||
});
|
||||
|
||||
if (!retVal.columns[retVal.columns.length - 1].columnTypes.some(function (this, val, ind, arr) {
|
||||
return val == "null" ? true : false;
|
||||
})) retVal.columns[retVal.columns.length - 1].columnTypes.push('null')
|
||||
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)
|
||||
}
|
||||
@ -326,41 +319,43 @@ export class EntityFileToJson {
|
||||
}
|
||||
|
||||
retVal.columns = retVal.columns.map(col => {
|
||||
if (col.columnName.endsWith('Id'))
|
||||
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'))
|
||||
if (colName.endsWith('Id')) {
|
||||
colName = colName.substr(0, colName.length - 2)
|
||||
}
|
||||
return colName;
|
||||
})
|
||||
return ind;
|
||||
})
|
||||
return retVal;
|
||||
}
|
||||
isPartOfMultilineStatement(statement: string) {
|
||||
let matchStarting = statement.split('(').length+statement.split('{').length
|
||||
let matchEnding = statement.split(')').length+statement.split('}').length
|
||||
public isPartOfMultilineStatement(statement: string) {
|
||||
const matchStarting = statement.split('(').length+statement.split('{').length
|
||||
const matchEnding = statement.split(')').length+statement.split('}').length
|
||||
return !(matchStarting == matchEnding)
|
||||
}
|
||||
}
|
||||
class EntityJson {
|
||||
entityName: string
|
||||
entityOptions: any = {}
|
||||
columns: EntityColumn[] = <EntityColumn[]>[];
|
||||
indicies: EntityIndex[] = <EntityIndex[]>[];
|
||||
public entityName: string
|
||||
public entityOptions: any = {}
|
||||
public columns: EntityColumn[] = [] as EntityColumn[];
|
||||
public indicies: EntityIndex[] = [] as EntityIndex[];
|
||||
}
|
||||
class EntityColumn {
|
||||
columnName: string
|
||||
columnTypes: string[] = []
|
||||
columnOptions: any = {}
|
||||
relationType: "OneToOne" | "OneToMany" | "ManyToOne" | "ManyToMany" | "None" = "None"
|
||||
isOwnerOfRelation: boolean = false;
|
||||
public columnName: string
|
||||
public columnTypes: string[] = []
|
||||
public columnOptions: any = {}
|
||||
public relationType: "OneToOne" | "OneToMany" | "ManyToOne" | "ManyToMany" | "None" = "None"
|
||||
public isOwnerOfRelation: boolean = false;
|
||||
}
|
||||
class EntityIndex {
|
||||
indexName: string
|
||||
columnNames: string[] = []
|
||||
isUnique: boolean = false
|
||||
public indexName: string
|
||||
public columnNames: string[] = []
|
||||
public isUnique: boolean = false
|
||||
}
|
||||
|
@ -1,28 +1,57 @@
|
||||
import * as ts from "typescript";
|
||||
import { AbstractDriver } from "../../src/drivers/AbstractDriver";
|
||||
import { MssqlDriver } from "../../src/drivers/MssqlDriver";
|
||||
import { PostgresDriver } from "./../../src/drivers/PostgresDriver";
|
||||
import { MysqlDriver } from "../../src/drivers/MysqlDriver";
|
||||
import { MariaDbDriver } from "../../src/drivers/MariaDbDriver";
|
||||
import { OracleDriver } from "../../src/drivers/OracleDriver";
|
||||
import { SqliteDriver } from "../../src/drivers/SqliteDriver";
|
||||
import { Engine } from "../../src/Engine";
|
||||
import { createConnection, ConnectionOptions } from "typeorm";
|
||||
import * as yn from "yn"
|
||||
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";
|
||||
|
||||
export async function createMSSQLModels(filesOrgPath: string, resultsPath: string): Promise<Engine> {
|
||||
export function getGenerationOptions(resultsPath: string): IGenerationOptions {
|
||||
return {
|
||||
resultsPath: resultsPath,
|
||||
noConfigs: false,
|
||||
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();
|
||||
await driver.ConnectToServer(`master`, String(process.env.MSSQL_Host), Number(process.env.MSSQL_Port), String(process.env.MSSQL_Username), String(process.env.MSSQL_Password), yn(process.env.MSSQL_SSL));
|
||||
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),
|
||||
}
|
||||
await driver.ConnectToServer(connectionOptions);
|
||||
connectionOptions.databaseName = String(process.env.MSSQL_Database);
|
||||
|
||||
if (await driver.CheckIfDBExists(String(process.env.MSSQL_Database)))
|
||||
if (await driver.CheckIfDBExists(String(process.env.MSSQL_Database))) {
|
||||
await driver.DropDB(String(process.env.MSSQL_Database));
|
||||
}
|
||||
await driver.CreateDB(String(process.env.MSSQL_Database));
|
||||
await driver.DisconnectFromServer();
|
||||
|
||||
let connOpt: ConnectionOptions = {
|
||||
const connOpt: ConnectionOptions = {
|
||||
database: String(process.env.MSSQL_Database),
|
||||
host: String(process.env.MSSQL_Host),
|
||||
password: String(process.env.MSSQL_Password),
|
||||
@ -32,9 +61,10 @@ export async function createMSSQLModels(filesOrgPath: string, resultsPath: strin
|
||||
dropSchema: true,
|
||||
synchronize: false,
|
||||
entities: [path.resolve(filesOrgPath, '*.js')],
|
||||
name: 'mssql'
|
||||
}
|
||||
|
||||
let schemas = 'dbo,sch1,sch2'
|
||||
const schemas = 'dbo,sch1,sch2'
|
||||
let conn = await createConnection(connOpt)
|
||||
let queryRunner = conn.createQueryRunner()
|
||||
for (const sch of schemas.split(',')) {
|
||||
@ -42,53 +72,36 @@ export async function createMSSQLModels(filesOrgPath: string, resultsPath: strin
|
||||
}
|
||||
await conn.synchronize();
|
||||
|
||||
if (conn.isConnected)
|
||||
if (conn.isConnected) {
|
||||
await conn.close()
|
||||
|
||||
|
||||
driver = new MssqlDriver();
|
||||
let engine = new Engine(
|
||||
driver, {
|
||||
host: String(process.env.MSSQL_Host),
|
||||
port: Number(process.env.MSSQL_Port),
|
||||
databaseName: String(process.env.MSSQL_Database),
|
||||
user: String(process.env.MSSQL_Username),
|
||||
password: String(process.env.MSSQL_Password),
|
||||
databaseType: 'mssql',
|
||||
resultsPath: resultsPath,
|
||||
schemaName: 'dbo,sch1,sch2',
|
||||
ssl: yn(process.env.MSSQL_SSL),
|
||||
noConfigs: false,
|
||||
convertCaseEntity: 'none',
|
||||
convertCaseFile: 'none',
|
||||
convertCaseProperty: 'none',
|
||||
lazy: false,
|
||||
constructor:false
|
||||
});
|
||||
|
||||
conn = await createConnection(connOpt)
|
||||
queryRunner = conn.createQueryRunner()
|
||||
for (const sch of schemas.split(',')) {
|
||||
await queryRunner.createSchema(sch, true);
|
||||
}
|
||||
await conn.synchronize();
|
||||
if (conn.isConnected)
|
||||
await conn.close()
|
||||
|
||||
return engine;
|
||||
return connectionOptions;
|
||||
}
|
||||
|
||||
export async function createPostgresModels(filesOrgPath: string, resultsPath: string): Promise<Engine> {
|
||||
export async function createPostgresModels(filesOrgPath: string): Promise<IConnectionOptions> {
|
||||
let driver: AbstractDriver;
|
||||
driver = new PostgresDriver();
|
||||
await driver.ConnectToServer(`postgres`, String(process.env.POSTGRES_Host), Number(process.env.POSTGRES_Port), String(process.env.POSTGRES_Username), String(process.env.POSTGRES_Password), yn(process.env.POSTGRES_SSL));
|
||||
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),
|
||||
}
|
||||
await driver.ConnectToServer(connectionOptions);
|
||||
connectionOptions.databaseName = String(process.env.POSTGRES_Database);
|
||||
|
||||
if (await driver.CheckIfDBExists(String(process.env.POSTGRES_Database)))
|
||||
if (await driver.CheckIfDBExists(String(process.env.POSTGRES_Database))) {
|
||||
await driver.DropDB(String(process.env.POSTGRES_Database));
|
||||
}
|
||||
await driver.CreateDB(String(process.env.POSTGRES_Database));
|
||||
await driver.DisconnectFromServer();
|
||||
|
||||
let connOpt: ConnectionOptions = {
|
||||
const connOpt: ConnectionOptions = {
|
||||
database: String(process.env.POSTGRES_Database),
|
||||
host: String(process.env.POSTGRES_Host),
|
||||
password: String(process.env.POSTGRES_Password),
|
||||
@ -98,9 +111,10 @@ export async function createPostgresModels(filesOrgPath: string, resultsPath: st
|
||||
dropSchema: true,
|
||||
synchronize: false,
|
||||
entities: [path.resolve(filesOrgPath, '*.js')],
|
||||
name: 'postgres'
|
||||
}
|
||||
|
||||
let schemas = 'public,sch1,sch2'
|
||||
const schemas = 'public,sch1,sch2'
|
||||
let conn = await createConnection(connOpt)
|
||||
let queryRunner = conn.createQueryRunner()
|
||||
for (const sch of schemas.split(',')) {
|
||||
@ -108,106 +122,75 @@ export async function createPostgresModels(filesOrgPath: string, resultsPath: st
|
||||
}
|
||||
await conn.synchronize();
|
||||
|
||||
if (conn.isConnected)
|
||||
if (conn.isConnected) {
|
||||
await conn.close()
|
||||
|
||||
driver = new PostgresDriver();
|
||||
let engine = new Engine(
|
||||
driver, {
|
||||
host: String(process.env.POSTGRES_Host),
|
||||
port: Number(process.env.POSTGRES_Port),
|
||||
databaseName: String(process.env.POSTGRES_Database),
|
||||
user: String(process.env.POSTGRES_Username),
|
||||
password: String(process.env.POSTGRES_Password),
|
||||
databaseType: 'postgres',
|
||||
resultsPath: resultsPath,
|
||||
schemaName: 'public,sch1,sch2',
|
||||
ssl: yn(process.env.POSTGRES_SSL),
|
||||
noConfigs: false,
|
||||
convertCaseEntity: 'none',
|
||||
convertCaseFile: 'none',
|
||||
convertCaseProperty: 'none',
|
||||
lazy: false,
|
||||
constructor:false
|
||||
});
|
||||
|
||||
conn = await createConnection(connOpt)
|
||||
queryRunner = conn.createQueryRunner()
|
||||
for (const sch of schemas.split(',')) {
|
||||
await queryRunner.createSchema(sch, true);
|
||||
}
|
||||
await conn.synchronize();
|
||||
if (conn.isConnected)
|
||||
await conn.close()
|
||||
|
||||
return engine;
|
||||
return connectionOptions;
|
||||
}
|
||||
|
||||
export async function createSQLiteModels(filesOrgPath: string, resultsPath: string): Promise<Engine> {
|
||||
export async function createSQLiteModels(filesOrgPath: string): Promise<IConnectionOptions> {
|
||||
let driver: AbstractDriver;
|
||||
driver = new SqliteDriver();
|
||||
await driver.ConnectToServer(String(process.env.SQLITE_Database), '', 0, '', '', false);
|
||||
const connectionOptions: IConnectionOptions = {
|
||||
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)))
|
||||
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();
|
||||
|
||||
let connOpt: ConnectionOptions = {
|
||||
const connOpt: ConnectionOptions = {
|
||||
database: String(process.env.SQLITE_Database),
|
||||
type: 'sqlite',
|
||||
dropSchema: true,
|
||||
synchronize: false,
|
||||
entities: [path.resolve(filesOrgPath, '*.js')],
|
||||
name: 'sqlite'
|
||||
}
|
||||
|
||||
let conn = await createConnection(connOpt)
|
||||
let queryRunner = conn.createQueryRunner()
|
||||
await conn.synchronize();
|
||||
|
||||
if (conn.isConnected)
|
||||
if (conn.isConnected) {
|
||||
await conn.close()
|
||||
}
|
||||
|
||||
driver = new SqliteDriver();
|
||||
let engine = new Engine(
|
||||
driver, {
|
||||
host: '',
|
||||
port: 0,
|
||||
databaseName: String(process.env.SQLITE_Database),
|
||||
user: '',
|
||||
password: '',
|
||||
databaseType: 'sqlite',
|
||||
resultsPath: resultsPath,
|
||||
schemaName: '',
|
||||
ssl: false,
|
||||
noConfigs: false,
|
||||
convertCaseEntity: 'none',
|
||||
convertCaseFile: 'none',
|
||||
convertCaseProperty: 'none',
|
||||
lazy: false,
|
||||
constructor:false
|
||||
});
|
||||
|
||||
conn = await createConnection(connOpt)
|
||||
queryRunner = conn.createQueryRunner()
|
||||
await conn.synchronize();
|
||||
if (conn.isConnected)
|
||||
await conn.close()
|
||||
|
||||
return engine;
|
||||
return connectionOptions;
|
||||
}
|
||||
|
||||
export async function createMysqlModels(filesOrgPath: string, resultsPath: string): Promise<Engine> {
|
||||
export async function createMysqlModels(filesOrgPath: string): Promise<IConnectionOptions> {
|
||||
let driver: AbstractDriver;
|
||||
driver = new MysqlDriver();
|
||||
await driver.ConnectToServer(`mysql`, String(process.env.MYSQL_Host), Number(process.env.MYSQL_Port), String(process.env.MYSQL_Username), String(process.env.MYSQL_Password), yn(process.env.MYSQL_SSL));
|
||||
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),
|
||||
}
|
||||
await driver.ConnectToServer(connectionOptions);
|
||||
|
||||
if (await driver.CheckIfDBExists(String(process.env.MYSQL_Database)))
|
||||
if (await driver.CheckIfDBExists(String(process.env.MYSQL_Database))) {
|
||||
await driver.DropDB(String(process.env.MYSQL_Database));
|
||||
}
|
||||
await driver.CreateDB(String(process.env.MYSQL_Database));
|
||||
await driver.DisconnectFromServer();
|
||||
|
||||
let connOpt: ConnectionOptions = {
|
||||
const connOpt: ConnectionOptions = {
|
||||
database: String(process.env.MYSQL_Database),
|
||||
host: String(process.env.MYSQL_Host),
|
||||
password: String(process.env.MYSQL_Password),
|
||||
@ -217,45 +200,38 @@ export async function createMysqlModels(filesOrgPath: string, resultsPath: strin
|
||||
dropSchema: true,
|
||||
synchronize: true,
|
||||
entities: [path.resolve(filesOrgPath, '*.js')],
|
||||
name: 'mysql'
|
||||
}
|
||||
let conn = await createConnection(connOpt)
|
||||
const conn = await createConnection(connOpt)
|
||||
|
||||
if (conn.isConnected)
|
||||
if (conn.isConnected) {
|
||||
await conn.close()
|
||||
}
|
||||
|
||||
driver = new MysqlDriver();
|
||||
let engine = new Engine(
|
||||
driver, {
|
||||
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',
|
||||
resultsPath: resultsPath,
|
||||
schemaName: 'ignored',
|
||||
ssl: yn(process.env.MYSQL_SSL),
|
||||
noConfigs: false,
|
||||
convertCaseEntity: 'none',
|
||||
convertCaseFile: 'none',
|
||||
convertCaseProperty: 'none',
|
||||
lazy: false,
|
||||
constructor:false
|
||||
});
|
||||
|
||||
return engine;
|
||||
return connectionOptions;
|
||||
}
|
||||
export async function createMariaDBModels(filesOrgPath: string, resultsPath: string): Promise<Engine> {
|
||||
export async function createMariaDBModels(filesOrgPath: string): Promise<IConnectionOptions> {
|
||||
let driver: AbstractDriver;
|
||||
driver = new MariaDbDriver();
|
||||
await driver.ConnectToServer(`mysql`, String(process.env.MARIADB_Host), Number(process.env.MARIADB_Port), String(process.env.MARIADB_Username), String(process.env.MARIADB_Password), yn(process.env.MARIADB_SSL));
|
||||
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),
|
||||
}
|
||||
await driver.ConnectToServer(connectionOptions);
|
||||
|
||||
if (await driver.CheckIfDBExists(String(process.env.MARIADB_Database)))
|
||||
if (await driver.CheckIfDBExists(String(process.env.MARIADB_Database))) {
|
||||
await driver.DropDB(String(process.env.MARIADB_Database));
|
||||
}
|
||||
await driver.CreateDB(String(process.env.MARIADB_Database));
|
||||
await driver.DisconnectFromServer();
|
||||
|
||||
let connOpt: ConnectionOptions = {
|
||||
const connOpt: ConnectionOptions = {
|
||||
database: String(process.env.MARIADB_Database),
|
||||
host: String(process.env.MARIADB_Host),
|
||||
password: String(process.env.MARIADB_Password),
|
||||
@ -265,48 +241,42 @@ export async function createMariaDBModels(filesOrgPath: string, resultsPath: str
|
||||
dropSchema: true,
|
||||
synchronize: true,
|
||||
entities: [path.resolve(filesOrgPath, '*.js')],
|
||||
name: 'mariadb'
|
||||
}
|
||||
let conn = await createConnection(connOpt)
|
||||
const conn = await createConnection(connOpt)
|
||||
|
||||
if (conn.isConnected)
|
||||
if (conn.isConnected) {
|
||||
await conn.close()
|
||||
}
|
||||
|
||||
driver = new MariaDbDriver();
|
||||
let engine = new Engine(
|
||||
driver, {
|
||||
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',
|
||||
resultsPath: resultsPath,
|
||||
schemaName: 'ignored',
|
||||
ssl: yn(process.env.MARIADB_SSL),
|
||||
noConfigs: false,
|
||||
convertCaseEntity: 'none',
|
||||
convertCaseFile: 'none',
|
||||
convertCaseProperty: 'none',
|
||||
lazy: false,
|
||||
constructor:false
|
||||
});
|
||||
|
||||
|
||||
|
||||
return engine;
|
||||
return connectionOptions;
|
||||
}
|
||||
|
||||
export async function createOracleDBModels(filesOrgPath: string, resultsPath: string): Promise<Engine> {
|
||||
export async function createOracleDBModels(filesOrgPath: string): Promise<IConnectionOptions> {
|
||||
let driver: AbstractDriver;
|
||||
driver = new OracleDriver();
|
||||
await driver.ConnectToServer(String(process.env.ORACLE_Database), String(process.env.ORACLE_Host), Number(process.env.ORACLE_Port), String(process.env.ORACLE_UsernameSys), String(process.env.ORACLE_PasswordSys), yn(process.env.ORACLE_SSL));
|
||||
|
||||
if (await driver.CheckIfDBExists(String(process.env.ORACLE_Username)))
|
||||
const connectionOptions: IConnectionOptions = {
|
||||
host: String(process.env.ORACLE_Host),
|
||||
port: Number(process.env.ORACLE_Port),
|
||||
databaseName: String(process.env.ORACLE_Database),
|
||||
user: String(process.env.ORACLE_UsernameSys),
|
||||
password: String(process.env.ORACLE_PasswordSys),
|
||||
databaseType: 'oracle',
|
||||
schemaName: String(process.env.ORACLE_Username),
|
||||
ssl: yn(process.env.ORACLE_SSL),
|
||||
}
|
||||
await driver.ConnectToServer(connectionOptions);
|
||||
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));
|
||||
}
|
||||
await driver.CreateDB(String(process.env.ORACLE_Username));
|
||||
await driver.DisconnectFromServer();
|
||||
|
||||
let connOpt: ConnectionOptions = {
|
||||
const connOpt: ConnectionOptions = {
|
||||
database: String(process.env.ORACLE_Database),
|
||||
sid: String(process.env.ORACLE_Database),
|
||||
host: String(process.env.ORACLE_Host),
|
||||
@ -316,49 +286,74 @@ export async function createOracleDBModels(filesOrgPath: string, resultsPath: st
|
||||
port: Number(process.env.ORACLE_Port),
|
||||
synchronize: true,
|
||||
entities: [path.resolve(filesOrgPath, '*.js')],
|
||||
name: 'oracle',
|
||||
}
|
||||
let conn = await createConnection(connOpt)
|
||||
const conn = await createConnection(connOpt)
|
||||
|
||||
if (conn.isConnected)
|
||||
if (conn.isConnected) {
|
||||
await conn.close()
|
||||
}
|
||||
|
||||
driver = new OracleDriver();
|
||||
let engine = new Engine(
|
||||
driver, {
|
||||
host: String(process.env.ORACLE_Host),
|
||||
port: Number(process.env.ORACLE_Port),
|
||||
databaseName: String(process.env.ORACLE_Database),
|
||||
user: String(process.env.ORACLE_Username),
|
||||
password: String(process.env.ORACLE_Password),
|
||||
databaseType: 'oracle',
|
||||
resultsPath: resultsPath,
|
||||
schemaName: String(process.env.ORACLE_Username),
|
||||
ssl: yn(process.env.ORACLE_SSL),
|
||||
noConfigs: false,
|
||||
convertCaseEntity: 'none',
|
||||
convertCaseFile: 'none',
|
||||
convertCaseProperty: 'none',
|
||||
lazy: false,
|
||||
constructor:false
|
||||
});
|
||||
|
||||
return engine;
|
||||
return connectionOptions;
|
||||
}
|
||||
|
||||
export function compileTsFiles(fileNames: string[], options: ts.CompilerOptions): boolean {
|
||||
let program = ts.createProgram(fileNames, options);
|
||||
let emitResult = program.emit();
|
||||
const program = ts.createProgram(fileNames, options);
|
||||
const emitResult = program.emit();
|
||||
let compileErrors = false;
|
||||
let preDiagnostics = ts.getPreEmitDiagnostics(program);
|
||||
const preDiagnostics = ts.getPreEmitDiagnostics(program);
|
||||
|
||||
let allDiagnostics = [...preDiagnostics, ...emitResult.diagnostics];
|
||||
const allDiagnostics = [...preDiagnostics, ...emitResult.diagnostics];
|
||||
|
||||
allDiagnostics.forEach(diagnostic => {
|
||||
let lineAndCharacter = diagnostic.file!.getLineAndCharacterOfPosition(diagnostic.start!);
|
||||
let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
||||
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;
|
||||
});
|
||||
|
||||
return compileErrors;
|
||||
}
|
||||
|
||||
export function getEnabledDbDrivers() {
|
||||
const dbDrivers: string[] = [];
|
||||
if (process.env.SQLITE_Skip == '0') {
|
||||
dbDrivers.push('sqlite');
|
||||
}
|
||||
if (process.env.POSTGRES_Skip == '0') {
|
||||
dbDrivers.push('postgres');
|
||||
}
|
||||
if (process.env.MYSQL_Skip == '0') {
|
||||
dbDrivers.push('mysql');
|
||||
}
|
||||
if (process.env.MARIADB_Skip == '0') {
|
||||
dbDrivers.push('mariadb');
|
||||
}
|
||||
if (process.env.MSSQL_Skip == '0') {
|
||||
dbDrivers.push('mssql');
|
||||
}
|
||||
if (process.env.ORACLE_Skip == '0') {
|
||||
dbDrivers.push('oracle');
|
||||
}
|
||||
return dbDrivers;
|
||||
}
|
||||
|
||||
export function createModelsInDb(dbDriver: string, filesOrgPathJS: string): Promise<IConnectionOptions> {
|
||||
switch (dbDriver) {
|
||||
case 'sqlite':
|
||||
return createSQLiteModels(filesOrgPathJS);
|
||||
case 'postgres':
|
||||
return createPostgresModels(filesOrgPathJS);
|
||||
case 'mysql':
|
||||
return createMysqlModels(filesOrgPathJS);
|
||||
case 'mariadb':
|
||||
return createMariaDBModels(filesOrgPathJS);
|
||||
case 'mssql':
|
||||
return createMSSQLModels(filesOrgPathJS);
|
||||
case 'oracle':
|
||||
return createOracleDBModels(filesOrgPathJS);
|
||||
default:
|
||||
console.log(`Unknown engine type`);
|
||||
throw new Error("Unknown engine type");
|
||||
}
|
||||
}
|
||||
|
10
typings.json
10
typings.json
@ -1,10 +0,0 @@
|
||||
{
|
||||
"globalDevDependencies": {
|
||||
"mustache": "registry:dt/mustache#0.8.2+20160510002910"
|
||||
},
|
||||
"dependencies": {
|
||||
"mssql": "registry:dt/mssql#3.3.0+20170311011547",
|
||||
"yargs": "registry:npm/yargs#5.0.0+20160907000723",
|
||||
"yn": "registry:npm/yn#1.3.0+20170508185912"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user