basic naming strategy #65 #75

This commit is contained in:
Kononnable 2018-06-11 01:07:54 +02:00
parent 87502eab19
commit d88ca1975d
14 changed files with 153 additions and 78 deletions

View File

@ -0,0 +1,47 @@
import { NamingStrategy } from "./NamingStrategy";
import { EntityInfo } from "./models/EntityInfo";
export class DefaultNamingStrategy extends NamingStrategy {
relationName(
ownerEntity: EntityInfo,
referencedEntity: EntityInfo,
isOneToMany: boolean
): string {
let columnName = ownerEntity.EntityName.toLowerCase();
if (columnName.endsWith("Id")) {
columnName = columnName.substring(0, columnName.lastIndexOf("Id"));
}
columnName += isOneToMany ? "s" : "";
if (
referencedEntity.Columns.filter(filterVal => {
return filterVal.tsName == columnName;
}).length > 0
) {
for (let i = 2; i <= ownerEntity.Columns.length; i++) {
columnName = ownerEntity.EntityName.toLowerCase();
if (columnName.endsWith("Id")) {
columnName = columnName.substring(
0,
columnName.lastIndexOf("Id")
);
}
columnName += (isOneToMany ? "s" : "") + i.toString();
if (
referencedEntity.Columns.filter(filterVal => {
return filterVal.tsName == columnName;
}).length == 0
)
break;
}
}
return columnName;
}
entityName(entityName: string): string {
return entityName;
}
columnName(columnName: string): string {
return columnName;
}
}

View File

@ -5,6 +5,7 @@ import fs = require("fs");
import path = require("path");
import * as TomgUtils from "./Utils";
import changeCase = require("change-case");
import { NamingStrategy } from "./NamingStrategy";
export class Engine {
constructor(
@ -20,7 +21,8 @@ export class Engine {
this.Options.user,
this.Options.password,
this.Options.schemaName,
this.Options.ssl
this.Options.ssl,
this.Options.namingStrategy
);
if (dbModel.entities.length > 0) {
this.createModelFromMetadata(dbModel);
@ -39,7 +41,8 @@ export class Engine {
user: string,
password: string,
schemaName: string,
ssl: boolean
ssl: boolean,
namingStrategy: NamingStrategy
): Promise<DatabaseModel> {
return await this.driver.GetDataFromServer(
database,
@ -48,7 +51,8 @@ export class Engine {
user,
password,
schemaName,
ssl
ssl,
namingStrategy
);
}
private createModelFromMetadata(databaseModel: DatabaseModel) {
@ -271,4 +275,5 @@ export interface EngineOptions {
convertCaseProperty: "pascal" | "camel" | "none";
lazy: boolean;
constructor: boolean;
namingStrategy: NamingStrategy;
}

9
src/NamingStrategy.ts Normal file
View File

@ -0,0 +1,9 @@
import { EntityInfo } from "./models/EntityInfo";
export abstract class NamingStrategy {
abstract relationName(ownerEntity: EntityInfo, referencedEntity: EntityInfo, isOneToMany: boolean): string;
abstract entityName(entityName: string): string;
abstract columnName(columnName: string): string;
}

View File

@ -8,6 +8,7 @@ import {
WithPrecisionColumnType,
WithLengthColumnType
} from "./../../node_modules/typeorm/driver/types/ColumnTypes";
import { NamingStrategy } from "../NamingStrategy";
export abstract class AbstractDriver {
ColumnTypesWithWidth: WithWidthColumnType[] = [
@ -52,6 +53,7 @@ export abstract class AbstractDriver {
"binary",
"varbinary"
];
namingStrategy: NamingStrategy;
FindManyToManyRelations(dbModel: DatabaseModel) {
let manyToManyEntities = dbModel.entities.filter(entity => {
@ -79,7 +81,7 @@ export abstract class AbstractDriver {
)[0];
relatedTable1.Columns = relatedTable1.Columns.filter(
v =>
!v.name
!v.tsName
.toLowerCase()
.startsWith(entity.EntityName.toLowerCase())
);
@ -88,7 +90,7 @@ export abstract class AbstractDriver {
)[0];
relatedTable2.Columns = relatedTable2.Columns.filter(
v =>
!v.name
!v.tsName
.toLowerCase()
.startsWith(entity.EntityName.toLowerCase())
);
@ -97,21 +99,21 @@ export abstract class AbstractDriver {
});
let column1 = new ColumnInfo();
column1.name = namesOfRelatedTables[1];
column1.tsName = this.namingStrategy.entityName(namesOfRelatedTables[1]);
let col1Rel = new RelationInfo();
col1Rel.relatedTable = namesOfRelatedTables[1];
col1Rel.relatedColumn = namesOfRelatedTables[1];
col1Rel.relatedColumn = this.namingStrategy.entityName(namesOfRelatedTables[1]);
col1Rel.relationType = "ManyToMany";
col1Rel.isOwner = true;
col1Rel.ownerColumn = namesOfRelatedTables[0];
col1Rel.ownerColumn = this.namingStrategy.entityName(namesOfRelatedTables[0]);
column1.relations.push(col1Rel);
relatedTable1.Columns.push(column1);
let column2 = new ColumnInfo();
column2.name = namesOfRelatedTables[0];
column2.tsName = this.namingStrategy.entityName(namesOfRelatedTables[0]);
let col2Rel = new RelationInfo();
col2Rel.relatedTable = namesOfRelatedTables[0];
col2Rel.relatedColumn = namesOfRelatedTables[1];
col2Rel.relatedColumn = this.namingStrategy.entityName(namesOfRelatedTables[1]);
col2Rel.relationType = "ManyToMany";
col2Rel.isOwner = false;
column2.relations.push(col2Rel);
@ -126,9 +128,11 @@ export abstract class AbstractDriver {
user: string,
password: string,
schema: string,
ssl: boolean
ssl: boolean,
namingStrategy:NamingStrategy
): Promise<DatabaseModel> {
let dbModel = <DatabaseModel>{};
this.namingStrategy = namingStrategy;
await this.ConnectToServer(database, server, port, user, password, ssl);
let sqlEscapedSchema = "'" + schema.split(",").join("','") + "'";
dbModel.entities = await this.GetAllTables(sqlEscapedSchema);
@ -209,7 +213,7 @@ export abstract class AbstractDriver {
) {
let ownerColumn = ownerEntity.Columns.find(column => {
return (
column.name ==
column.tsName ==
relationTmp.ownerColumnsNames[relationColumnIndex]
);
});
@ -227,7 +231,7 @@ export abstract class AbstractDriver {
}
let relatedColumn = referencedEntity.Columns.find(column => {
return (
column.name ==
column.tsName ==
relationTmp.referencedColumnsNames[relationColumnIndex]
);
});
@ -249,48 +253,29 @@ export abstract class AbstractDriver {
return (
index.isUnique &&
index.columns.some(col => {
return col.name == ownerColumn!.name;
return col.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;
}
}
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"
? "ManyToOne"
: "OneToOne";
let columnName = this.namingStrategy.relationName(ownerEntity,referencedEntity,isOneToMany);
ownerRelation.ownerColumn = columnName;
ownerColumn.relations.push(ownerRelation);
if (isOneToMany) {
let col = new ColumnInfo();
col.name = columnName;
col.tsName = columnName;
let referencedRelation = new RelationInfo();
col.relations.push(referencedRelation);
referencedRelation.actionOnDelete =
@ -298,15 +283,15 @@ export abstract class AbstractDriver {
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.toLowerCase();
referencedRelation.relationType = "OneToMany";
referencedEntity.Columns.push(col);
} else {
let col = new ColumnInfo();
col.name = columnName;
col.tsName = columnName;
let referencedRelation = new RelationInfo();
col.relations.push(referencedRelation);
referencedRelation.actionOnDelete =
@ -314,10 +299,10 @@ export abstract class AbstractDriver {
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.toLowerCase();
referencedRelation.relationType = "OneToOne";
referencedEntity.Columns.push(col);
}
@ -344,7 +329,7 @@ export abstract class AbstractDriver {
entity.Columns.forEach(col => {
if (
primaryIndex &&
primaryIndex.columns.some(cIndex => cIndex.name == col.name)
primaryIndex.columns.some(cIndex => cIndex.name == col.tsName)
)
col.isPrimary = true;
});

View File

@ -53,7 +53,8 @@ export class MssqlDriver extends AbstractDriver {
})
.forEach(resp => {
let colInfo: ColumnInfo = new ColumnInfo();
colInfo.name = resp.COLUMN_NAME;
colInfo.tsName = this.namingStrategy.entityName(resp.COLUMN_NAME);
colInfo.sqlName = resp.COLUMN_NAME;
colInfo.is_nullable = resp.IS_NULLABLE == "YES";
colInfo.is_generated = resp.IsIdentity == 1;
colInfo.is_unique = resp.IsUnique == 1;

View File

@ -45,7 +45,8 @@ export class MysqlDriver extends AbstractDriver {
})
.forEach(resp => {
let colInfo: ColumnInfo = new ColumnInfo();
colInfo.name = resp.COLUMN_NAME;
colInfo.tsName = this.namingStrategy.entityName(resp.COLUMN_NAME);
colInfo.sqlName = resp.COLUMN_NAME;
colInfo.is_nullable = resp.IS_NULLABLE == "YES";
colInfo.is_generated = resp.IsIdentity == 1;
colInfo.is_unique = resp.column_key == "UNI";

View File

@ -55,7 +55,8 @@ export class OracleDriver extends AbstractDriver {
})
.forEach(resp => {
let colInfo: ColumnInfo = new ColumnInfo();
colInfo.name = resp.COLUMN_NAME;
colInfo.tsName = this.namingStrategy.entityName(resp.COLUMN_NAME);
colInfo.sqlName = resp.COLUMN_NAME;
colInfo.is_nullable = resp.NULLABLE == "Y";
colInfo.is_generated = resp.IDENTITY_COLUMN == "YES";
colInfo.default =

View File

@ -54,7 +54,8 @@ export class PostgresDriver extends AbstractDriver {
})
.forEach(resp => {
let colInfo: ColumnInfo = new ColumnInfo();
colInfo.name = resp.column_name;
colInfo.tsName = this.namingStrategy.entityName(resp.column_name);
colInfo.sqlName = resp.column_name;
colInfo.is_nullable = resp.is_nullable == "YES";
colInfo.is_generated = resp.isidentity == "YES";
colInfo.is_unique = resp.isunique == 1;

View File

@ -41,7 +41,8 @@ export class SqliteDriver extends AbstractDriver {
}>(`PRAGMA table_info('${ent.EntityName}');`);
response.forEach(resp => {
let colInfo: ColumnInfo = new ColumnInfo();
colInfo.name = resp.name;
colInfo.tsName = this.namingStrategy.entityName(resp.name);
colInfo.sqlName = resp.name;
colInfo.is_nullable = resp.notnull == 0;
colInfo.isPrimary = resp.pk > 0;
colInfo.default = resp.dflt_value ? resp.dflt_value : null;
@ -152,12 +153,16 @@ export class SqliteDriver extends AbstractDriver {
) &&
options
) {
colInfo.numericPrecision = <any>options[0]
.substring(1, options[0].length - 1)
.split(",")[0];
colInfo.numericScale = <any>options[0]
.substring(1, options[0].length - 1)
.split(",")[1];
colInfo.numericPrecision = <any>(
options[0]
.substring(1, options[0].length - 1)
.split(",")[0]
);
colInfo.numericScale = <any>(
options[0]
.substring(1, options[0].length - 1)
.split(",")[1]
);
}
if (
this.ColumnTypesWithLength.some(
@ -165,9 +170,8 @@ export class SqliteDriver extends AbstractDriver {
) &&
options
) {
colInfo.lenght = <any>options[0].substring(
1,
options[0].length - 1
colInfo.lenght = <any>(
options[0].substring(1, options[0].length - 1)
);
}
if (
@ -178,9 +182,8 @@ export class SqliteDriver extends AbstractDriver {
) &&
options
) {
colInfo.width = <any>options[0].substring(
1,
options[0].length - 1
colInfo.width = <any>(
options[0].substring(1, options[0].length - 1)
);
}
@ -231,7 +234,7 @@ export class SqliteDriver extends AbstractDriver {
indexInfo.isUnique
) {
ent.Columns.filter(
v => v.name == indexColumnInfo.name
v => v.tsName == indexColumnInfo.name
).map(v => (v.is_unique = true));
}
indexInfo.columns.push(indexColumnInfo);

View File

@ -20,14 +20,14 @@ import {Index,Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, Man
scale:{{.}},{{/numericScale}}{{#isPrimary}}
primary:{{isPrimary}},{{/isPrimary}}{{#enumOptions}}
enum:[{{.}}],{{/enumOptions}}
name:"{{name}}"
name:"{{sqlName}}"
})
{{toPropertyName name}}:{{ts_type}}{{#is_nullable}} | null{{/is_nullable}};
{{toPropertyName tsName}}:{{ts_type}}{{#is_nullable}} | null{{/is_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 (concat (toEntityName relatedTable) ' | null')}};
@{{relationType}}(type=>{{toEntityName relatedTable}}, {{toPropertyName ../tsName}}=>{{toPropertyName ../tsName}}.{{#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:'{{ ../tsName}}'}){{/if}}{{/isOwner}}
{{#if (or isOneToMany isManyToMany)}}{{toPropertyName ../tsName}}:{{toLazy (concat (toEntityName relatedTable) "[]")}};
{{else}}{{toPropertyName ../tsName}}:{{toLazy (concat (toEntityName relatedTable) ' | null')}};
{{/if}}{{/relations}}
{{/Columns}}
{{#if GenerateConstructor}}

View File

@ -9,6 +9,8 @@ import { Engine } from "./Engine";
import * as Yargs from "yargs";
import * as TomgUtils from "./Utils";
import path = require("path");
import { DefaultNamingStrategy } from "./DefaultNamingStrategy";
import { NamingStrategy } from "./NamingStrategy";
var argv = Yargs.usage(
"Usage: typeorm-model-generator -h <host> -d <database> -p [port] -u <user> -x [password] -e [engine]"
@ -130,6 +132,7 @@ switch (argv.e) {
TomgUtils.LogError("Database engine not recognized.", false);
throw new Error("Database engine not recognized.");
}
let namingStrategy: NamingStrategy= new DefaultNamingStrategy();
let engine = new Engine(driver, {
host: argv.h,
@ -146,7 +149,8 @@ let engine = new Engine(driver, {
convertCaseEntity: argv.ce,
convertCaseProperty: argv.cp,
lazy: argv.lazy,
constructor: argv.constructor
constructor: argv.constructor,
namingStrategy: namingStrategy
});
console.log(TomgUtils.packageVersion());

View File

@ -1,7 +1,8 @@
import { RelationInfo } from "./RelationInfo";
export class ColumnInfo {
name: string = "";
tsName: string = "";
sqlName: string = "";
default: string | null = null;
is_nullable: boolean = false;
is_unique: boolean = false;

View File

@ -6,6 +6,7 @@ import { EntityInfo } from './../../src/models/EntityInfo'
import { ColumnInfo } from './../../src/models/ColumnInfo'
import { RelationInfo } from './../../src/models/RelationInfo'
import { Table, IColumnMetadata } from "mssql";
import { DefaultNamingStrategy } from "../../src/DefaultNamingStrategy";
class fakeResponse implements MSSQL.IResult<any> {
recordsets: MSSQL.IRecordSet<any>[];
@ -27,6 +28,7 @@ describe('MssqlDriver', function () {
beforeEach(() => {
driver = new MssqlDriver();
driver.namingStrategy = new DefaultNamingStrategy();
})
afterEach(() => {
@ -84,7 +86,8 @@ describe('MssqlDriver', function () {
is_nullable: true,
isPrimary: false,
is_generated: true,
name: 'name',
tsName: 'name',
sqlName: 'name',
numericPrecision: null,
numericScale: null,
width: null,

View File

@ -10,6 +10,8 @@ import { Engine } from "../../src/Engine";
import { createConnection, ConnectionOptions } from "typeorm";
import * as yn from "yn"
import path = require('path')
import { NamingStrategy } from "../../src/NamingStrategy";
import { DefaultNamingStrategy } from "../../src/DefaultNamingStrategy";
export async function createMSSQLModels(filesOrgPath: string, resultsPath: string): Promise<Engine> {
@ -45,6 +47,7 @@ export async function createMSSQLModels(filesOrgPath: string, resultsPath: strin
if (conn.isConnected)
await conn.close()
let namingStrategy: NamingStrategy = new DefaultNamingStrategy();
driver = new MssqlDriver();
let engine = new Engine(
@ -63,7 +66,8 @@ export async function createMSSQLModels(filesOrgPath: string, resultsPath: strin
convertCaseFile: 'none',
convertCaseProperty: 'none',
lazy: false,
constructor:false
constructor: false,
namingStrategy: namingStrategy
});
conn = await createConnection(connOpt)
@ -110,6 +114,7 @@ export async function createPostgresModels(filesOrgPath: string, resultsPath: st
if (conn.isConnected)
await conn.close()
let namingStrategy: NamingStrategy = new DefaultNamingStrategy();
driver = new PostgresDriver();
let engine = new Engine(
@ -128,7 +133,8 @@ export async function createPostgresModels(filesOrgPath: string, resultsPath: st
convertCaseFile: 'none',
convertCaseProperty: 'none',
lazy: false,
constructor:false
constructor:false,
namingStrategy: namingStrategy
});
conn = await createConnection(connOpt)
@ -167,6 +173,7 @@ export async function createSQLiteModels(filesOrgPath: string, resultsPath: stri
if (conn.isConnected)
await conn.close()
let namingStrategy: NamingStrategy = new DefaultNamingStrategy();
driver = new SqliteDriver();
let engine = new Engine(
@ -185,7 +192,8 @@ export async function createSQLiteModels(filesOrgPath: string, resultsPath: stri
convertCaseFile: 'none',
convertCaseProperty: 'none',
lazy: false,
constructor:false
constructor:false,
namingStrategy: namingStrategy
});
conn = await createConnection(connOpt)
@ -222,6 +230,7 @@ export async function createMysqlModels(filesOrgPath: string, resultsPath: strin
if (conn.isConnected)
await conn.close()
let namingStrategy: NamingStrategy = new DefaultNamingStrategy();
driver = new MysqlDriver();
let engine = new Engine(
@ -240,7 +249,8 @@ export async function createMysqlModels(filesOrgPath: string, resultsPath: strin
convertCaseFile: 'none',
convertCaseProperty: 'none',
lazy: false,
constructor:false
constructor:false,
namingStrategy: namingStrategy
});
return engine;
@ -270,6 +280,7 @@ export async function createMariaDBModels(filesOrgPath: string, resultsPath: str
if (conn.isConnected)
await conn.close()
let namingStrategy: NamingStrategy = new DefaultNamingStrategy();
driver = new MariaDbDriver();
let engine = new Engine(
@ -288,7 +299,8 @@ export async function createMariaDBModels(filesOrgPath: string, resultsPath: str
convertCaseFile: 'none',
convertCaseProperty: 'none',
lazy: false,
constructor:false
constructor:false,
namingStrategy: namingStrategy
});
@ -321,6 +333,7 @@ export async function createOracleDBModels(filesOrgPath: string, resultsPath: st
if (conn.isConnected)
await conn.close()
let namingStrategy: NamingStrategy = new DefaultNamingStrategy();
driver = new OracleDriver();
let engine = new Engine(
@ -339,7 +352,8 @@ export async function createOracleDBModels(filesOrgPath: string, resultsPath: st
convertCaseFile: 'none',
convertCaseProperty: 'none',
lazy: false,
constructor:false
constructor:false,
namingStrategy: namingStrategy
});
return engine;