Merge pull request #8 from pleerock/async-await

Async await
This commit is contained in:
Umed Khudoiberdiev 2016-05-27 09:05:40 +05:00
commit 0264d1fc10
11 changed files with 263 additions and 167 deletions

View File

@ -0,0 +1,20 @@
import {ColumnMetadata} from "../metadata/ColumnMetadata";
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
import {TableMetadata} from "../metadata/TableMetadata";
import {RelationMetadata} from "../metadata/RelationMetadata";
import {IndexMetadata} from "../metadata/IndexMetadata";
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
/**
* Arguments for EntityMetadata class.
*/
export interface EntityMetadataArgs {
readonly namingStrategy: NamingStrategyInterface;
readonly tableMetadata: TableMetadata;
readonly columnMetadatas?: ColumnMetadata[];
readonly relationMetadatas?: RelationMetadata[];
readonly indexMetadatas?: IndexMetadata[];
readonly foreignKeyMetadatas?: ForeignKeyMetadata[];
}

View File

@ -73,7 +73,10 @@ export class MetadataArgsStorage {
const relationCounts = this.relationCounts.filterByClass(tableMetadata.target);
allTableMetadatas
.filter(metadata => this.isInherited(tableMetadata.target, metadata.target))
.filter(metadata => {
if (!tableMetadata.target || !metadata.target) return false;
return this.isInherited(tableMetadata.target, metadata.target);
})
.forEach(parentMetadata => {
const metadatasFromAbstract = this.mergeWithAbstract(allTableMetadatas, parentMetadata);

View File

@ -8,7 +8,7 @@ export interface TableMetadataArgs {
/**
* Class to which table is applied.
*/
readonly target: Function;
readonly target?: Function;
/**
* Table name.

View File

@ -6,7 +6,12 @@ export class TargetMetadataArgsCollection<T extends { target?: Function }> exten
// Public Methods
// -------------------------------------------------------------------------
filterByClass(cls: Function): this {
filterByClass(cls?: Function): this {
// if no class specified then simply return empty collection
if (!cls)
return new (<any> this.constructor)();
return this.filterByClasses([cls]);
}

View File

@ -0,0 +1,76 @@
import {EntityMetadata} from "../metadata/EntityMetadata";
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
import {ColumnOptions} from "../decorator/options/ColumnOptions";
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
import {TableMetadata} from "../metadata/TableMetadata";
import {ColumnMetadataArgs} from "../metadata-args/ColumnMetadataArgs";
import {ColumnTypes} from "../metadata/types/ColumnTypes";
/**
* Helps to create EntityMetadatas for junction tables.
*
* @internal
*/
export interface ClosureJunctionEntityMetadataBuilderArgs {
namingStrategy: NamingStrategyInterface;
table: TableMetadata;
primaryColumn: ColumnMetadata;
hasTreeLevelColumn: boolean;
}
/**
* Helps to create EntityMetadatas for junction tables for closure tables.
*
* @internal
*/
export class ClosureJunctionEntityMetadataBuilder {
build(args: ClosureJunctionEntityMetadataBuilderArgs) {
const columns = [
new ColumnMetadata(<ColumnMetadataArgs> {
propertyType: args.primaryColumn.type,
mode: "virtual",
options: <ColumnOptions> {
length: args.primaryColumn.length,
type: args.primaryColumn.type,
name: "ancestor"
}
}),
new ColumnMetadata(<ColumnMetadataArgs> {
propertyType: args.primaryColumn.type,
mode: "virtual",
options: <ColumnOptions> {
length: args.primaryColumn.length,
type: args.primaryColumn.type,
name: "descendant"
}
})
];
if (args.hasTreeLevelColumn) {
columns.push(new ColumnMetadata(<ColumnMetadataArgs> {
propertyType: ColumnTypes.INTEGER,
mode: "virtual",
options: {
type: ColumnTypes.INTEGER,
name: "level"
}
}));
}
const closureJunctionTableMetadata = new TableMetadata(undefined, args.table.name, "closureJunction");
return new EntityMetadata({
namingStrategy: args.namingStrategy,
tableMetadata: closureJunctionTableMetadata,
columnMetadatas: columns,
foreignKeyMetadatas: [
new ForeignKeyMetadata([columns[0]], args.table, [args.primaryColumn]),
new ForeignKeyMetadata([columns[1]], args.table, [args.primaryColumn])
]
});
}
}

View File

@ -6,15 +6,12 @@ import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
import {EntityMetadataValidator} from "./EntityMetadataValidator";
import {IndexMetadata} from "../metadata/IndexMetadata";
import {JoinColumnMetadata} from "../metadata/JoinColumnMetadata";
import {JoinColumnOptions} from "../decorator/options/JoinColumnOptions";
import {TableMetadata} from "../metadata/TableMetadata";
import {ColumnTypes} from "../metadata/types/ColumnTypes";
import {getMetadataArgsStorage} from "../index";
import {getMetadataArgsStorage, getFromContainer} from "../index";
import {RelationMetadata} from "../metadata/RelationMetadata";
import {JoinTableMetadata} from "../metadata/JoinTableMetadata";
import {JoinTableMetadataArgs} from "../metadata-args/JoinTableMetadataArgs";
import {PropertyMetadataArgsCollection} from "../metadata-args/collection/PropertyMetadataArgsCollection";
import {ColumnMetadataArgs} from "../metadata-args/ColumnMetadataArgs";
import {JunctionEntityMetadataBuilder} from "./JunctionEntityMetadataBuilder";
import {ClosureJunctionEntityMetadataBuilder} from "./ClosureJunctionEntityMetadataBuilder";
/**
* Aggregates all metadata: table, column, relation into one collection grouped by tables for a given set of classes.
@ -30,8 +27,6 @@ export class EntityMetadataBuilder {
// todo: tree decorators can be used only on closure table (validation)
// todo: throw error if parent tree metadata was not specified in a closure table
private entityValidator = new EntityMetadataValidator();
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
@ -50,13 +45,13 @@ export class EntityMetadataBuilder {
const indices = mergedArgs.indices.map(args => new IndexMetadata(args));
// create a new entity metadata
const entityMetadata = new EntityMetadata(namingStrategy, table, columns, relations, indices);
// set entity metadata everywhere its used
table.entityMetadata = entityMetadata;
columns.forEach(column => column.entityMetadata = entityMetadata);
relations.forEach(relation => relation.entityMetadata = entityMetadata);
indices.forEach(index => index.entityMetadata = entityMetadata);
const entityMetadata = new EntityMetadata({
namingStrategy: namingStrategy,
tableMetadata: table,
columnMetadatas: columns,
relationMetadatas: relations,
indexMetadatas: indices
});
// create entity's relations join tables
entityMetadata.manyToManyRelations.forEach(relation => {
@ -105,159 +100,76 @@ export class EntityMetadataBuilder {
});
// check for errors in a built metadata schema (we need to check after relationEntityMetadata is set)
this.entityValidator.validateMany(entityMetadatas);
getFromContainer(EntityMetadataValidator).validateMany(entityMetadatas);
// generate columns and foreign keys for tables with relations
entityMetadatas.forEach(metadata => {
metadata.relationsWithJoinColumns.forEach(relation => {
// find relational column and if it does not exist - add it
// todo: later add support for propertyInFunction
const inverseSideColumn = relation.joinColumn.referencedColumn;
let relationalColumn = metadata.columns.find(column => column.name === relation.name); // todo?: ColumnCollection.findByName
let relationalColumn = metadata.columns.find(column => column.name === relation.name);
if (!relationalColumn) {
const options: ColumnOptions = {
type: inverseSideColumn.type,
oldColumnName: relation.oldColumnName,
nullable: relation.isNullable
};
relationalColumn = new ColumnMetadata(metadata, {
relationalColumn = new ColumnMetadata({
target: metadata.target,
propertyName: relation.name,
propertyType: inverseSideColumn.propertyType,
mode: "virtual",
options: options
options: <ColumnOptions> {
type: inverseSideColumn.type,
oldColumnName: relation.oldColumnName,
nullable: relation.isNullable
}
});
relationalColumn.entityMetadata = metadata;
metadata.columns.push(relationalColumn);
}
// create and add foreign key
const foreignKey = new ForeignKeyMetadata(
metadata,
metadata.table,
[relationalColumn],
relation.inverseEntityMetadata.table,
[inverseSideColumn],
relation.onDelete
);
foreignKey.entityMetadata = metadata;
metadata.foreignKeys.push(foreignKey);
});
});
// generate closure tables
const closureJunctionEntityMetadatas: EntityMetadata[] = [];
entityMetadatas
.filter(metadata => metadata.table.isClosure)
.forEach(metadata => {
const closureTableName = namingStrategy.closureJunctionTableName(metadata.table.name);
const closureJunctionTableMetadata = new TableMetadata(undefined, closureTableName, "closureJunction");
const column1Args: ColumnMetadataArgs = {
propertyType: metadata.primaryColumn.type,
mode: "virtual",
options: <ColumnOptions> {
length: metadata.primaryColumn.length,
type: metadata.primaryColumn.type,
name: "ancestor"
}
};
const column2Args: ColumnMetadataArgs = {
propertyType: metadata.primaryColumn.type,
mode: "virtual",
options: <ColumnOptions> {
length: metadata.primaryColumn.length,
type: metadata.primaryColumn.type,
name: "descendant"
}
};
const column3Args: ColumnMetadataArgs = {
propertyType: ColumnTypes.INTEGER,
mode: "virtual",
options: {
type: ColumnTypes.INTEGER,
name: "level"
}
};
const columns = [
new ColumnMetadata(metadata, column1Args),
new ColumnMetadata(metadata, column2Args)
];
if (metadata.hasTreeLevelColumn)
columns.push(new ColumnMetadata(metadata, column3Args));
const closureJunctionEntityMetadata = new EntityMetadata(namingStrategy, closureJunctionTableMetadata, columns, [], []);
closureJunctionTableMetadata.entityMetadata = closureJunctionEntityMetadata;
closureJunctionEntityMetadata.foreignKeys.push(
new ForeignKeyMetadata(closureJunctionEntityMetadata, closureJunctionTableMetadata, [columns[0]], metadata.table, [metadata.primaryColumn]),
new ForeignKeyMetadata(closureJunctionEntityMetadata, closureJunctionTableMetadata, [columns[1]], metadata.table, [metadata.primaryColumn])
);
closureJunctionEntityMetadatas.push(closureJunctionEntityMetadata);
metadata.closureJunctionTable = closureJunctionEntityMetadata;
});
// generate junction tables with its columns and foreign keys
const junctionEntityMetadatas: EntityMetadata[] = [];
// generate junction tables for all closure tables
entityMetadatas.forEach(metadata => {
metadata.ownerManyToManyRelations.map(relation => {
const tableMetadata = new TableMetadata({
target: Function,
name: relation.joinTable.name,
type: "junction"
if (!metadata.table.isClosure)
return;
const closureJunctionEntityMetadata = getFromContainer(ClosureJunctionEntityMetadataBuilder).build({
namingStrategy: namingStrategy,
table: metadata.table,
primaryColumn: metadata.primaryColumn,
hasTreeLevelColumn: metadata.hasTreeLevelColumn
});
metadata.closureJunctionTable = closureJunctionEntityMetadata;
entityMetadatas.push(closureJunctionEntityMetadata);
});
// generate junction tables for all many-to-many tables
entityMetadatas.forEach(metadata => {
metadata.ownerManyToManyRelations.forEach(relation => {
const junctionEntityMetadata = getFromContainer(JunctionEntityMetadataBuilder).build({
namingStrategy: namingStrategy,
firstTable: metadata.table,
secondTable: relation.inverseEntityMetadata.table,
joinTable: relation.joinTable
});
const column1 = relation.joinTable.referencedColumn;
const column2 = relation.joinTable.inverseReferencedColumn;
const column1options: ColumnOptions = {
length: column1.length,
type: column1.type,
name: relation.joinTable.joinColumnName // metadata.table.name + "_" + column1.name
};
const column2options: ColumnOptions = {
length: column2.length,
type: column2.type,
name: relation.joinTable.inverseJoinColumnName // inverseSideMetadata.table.name + "_" + column2.name
};
const columns = [
new ColumnMetadata(metadata, {
target: Function, // todo: temp, fix it later
propertyName: "", // todo: temp, fix it later
propertyType: column2.type,
mode: "regular", // or virtual?
options: column1options
}),
new ColumnMetadata(metadata, {
target: Function, // todo: temp, fix it later
propertyName: "", // todo: temp, fix it later
propertyType: column2.type,
mode: "regular", // or virtual?
options: column2options
})
];
const junctionEntityMetadata = new EntityMetadata(namingStrategy, tableMetadata, columns, [], []);
tableMetadata.entityMetadata = junctionEntityMetadata;
junctionEntityMetadata.foreignKeys.push(
new ForeignKeyMetadata(junctionEntityMetadata, tableMetadata, [columns[0]], metadata.table, [column1]),
new ForeignKeyMetadata(junctionEntityMetadata, tableMetadata, [columns[1]], relation.inverseEntityMetadata.table, [column2])
);
junctionEntityMetadatas.push(junctionEntityMetadata);
relation.junctionEntityMetadata = junctionEntityMetadata;
if (relation.hasInverseSide)
relation.inverseRelation.junctionEntityMetadata = junctionEntityMetadata;
entityMetadatas.push(junctionEntityMetadata);
});
});
return entityMetadatas
.concat(junctionEntityMetadatas)
.concat(closureJunctionEntityMetadatas);
return entityMetadatas;
}
// -------------------------------------------------------------------------
// Private Methods
// -------------------------------------------------------------------------
}

View File

@ -0,0 +1,71 @@
import {EntityMetadata} from "../metadata/EntityMetadata";
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
import {ColumnOptions} from "../decorator/options/ColumnOptions";
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
import {TableMetadata} from "../metadata/TableMetadata";
import {JoinTableMetadata} from "../metadata/JoinTableMetadata";
/**
* Helps to create EntityMetadatas for junction tables.
*
* @internal
*/
export interface JunctionEntityMetadataBuilderArgs {
namingStrategy: NamingStrategyInterface;
firstTable: TableMetadata;
secondTable: TableMetadata;
joinTable: JoinTableMetadata;
}
/**
* Helps to create EntityMetadatas for junction tables.
*
* @internal
*/
export class JunctionEntityMetadataBuilder {
build(args: JunctionEntityMetadataBuilderArgs) {
const column1 = args.joinTable.referencedColumn;
const column2 = args.joinTable.inverseReferencedColumn;
const tableMetadata = new TableMetadata({
name: args.joinTable.name,
type: "junction"
});
const junctionColumn1 = new ColumnMetadata({
propertyType: column1.type,
mode: "virtual",
options: <ColumnOptions> {
length: column1.length,
type: column1.type,
name: args.joinTable.joinColumnName
}
});
const junctionColumn2 = new ColumnMetadata({
propertyType: column2.type,
mode: "virtual",
options: <ColumnOptions> {
length: column2.length,
type: column2.type,
name: args.joinTable.inverseJoinColumnName
}
});
return new EntityMetadata({
namingStrategy: args.namingStrategy,
tableMetadata: tableMetadata,
columnMetadatas: [
junctionColumn1,
junctionColumn2
],
foreignKeyMetadatas: [
new ForeignKeyMetadata([junctionColumn1], args.firstTable, [column1]),
new ForeignKeyMetadata([junctionColumn2], args.secondTable, [column2])
]
});
}
}

View File

@ -5,6 +5,7 @@ import {IndexMetadata} from "./IndexMetadata";
import {RelationTypes} from "./types/RelationTypes";
import {ForeignKeyMetadata} from "./ForeignKeyMetadata";
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
import {EntityMetadataArgs} from "../metadata-args/EntityMetadataArgs";
/**
* Contains all entity metadata.
@ -58,16 +59,19 @@ export class EntityMetadata {
// Constructor
// -------------------------------------------------------------------------
constructor(namingStrategy: NamingStrategyInterface,
table: TableMetadata,
columns: ColumnMetadata[],
relations: RelationMetadata[],
indices: IndexMetadata[]) {
this.namingStrategy = namingStrategy;
this.table = table;
this.columns = columns;
this.relations = relations;
this.indices = indices;
constructor(args: EntityMetadataArgs) {
this.namingStrategy = args.namingStrategy;
this.table = args.tableMetadata;
this.columns = args.columnMetadatas || [];
this.relations = args.relationMetadatas || [];
this.indices = args.indexMetadatas || [];
this.foreignKeys = args.foreignKeyMetadatas || [];
this.table.entityMetadata = this;
this.columns.forEach(column => column.entityMetadata = this);
this.relations.forEach(relation => relation.entityMetadata = this);
this.foreignKeys.forEach(foreignKey => foreignKey.entityMetadata = this);
this.indices.forEach(index => index.entityMetadata = this);
}
// -------------------------------------------------------------------------

View File

@ -13,18 +13,17 @@ export type OnDeleteType = "RESTRICT"|"CASCADE"|"SET NULL";
export class ForeignKeyMetadata {
// -------------------------------------------------------------------------
// Public Readonly Properties
// Public Properties
// -------------------------------------------------------------------------
/**
* Entity metadata where this foreign key is.
*/
readonly entityMetadata: EntityMetadata;
entityMetadata: EntityMetadata;
/**
* Table to which this foreign key is applied.
*/
readonly table: TableMetadata;
// -------------------------------------------------------------------------
// Public Readonly Properties
// -------------------------------------------------------------------------
/**
* Array of columns of this foreign key.
@ -50,14 +49,10 @@ export class ForeignKeyMetadata {
// Constructor
// -------------------------------------------------------------------------
constructor(entityMetadata: EntityMetadata,
table: TableMetadata,
columns: ColumnMetadata[],
constructor(columns: ColumnMetadata[],
referencedTable: TableMetadata,
referencedColumns: ColumnMetadata[],
onDelete?: OnDeleteType) {
this.entityMetadata = entityMetadata;
this.table = table;
this.columns = columns;
this.referencedTable = referencedTable;
this.referencedColumns = referencedColumns;
@ -69,6 +64,20 @@ export class ForeignKeyMetadata {
// Accessors
// -------------------------------------------------------------------------
/**
* Gets the table name to which this foreign key is applied.
*/
get tableName() {
return this.entityMetadata.table.name;
}
/**
* Gets foreign key name.
*/
get name() {
return this.entityMetadata.namingStrategy.foreignKeyName(this.tableName, this.columnNames, this.referencedTable.name, this.referencedColumnNames);
}
/**
* Gets array of column names.
*/
@ -83,11 +92,4 @@ export class ForeignKeyMetadata {
return this.referencedColumns.map(column => column.name);
}
/**
* Gets foreign key name.
*/
get name() {
return this.entityMetadata.namingStrategy.foreignKeyName(this.table.name, this.columnNames, this.referencedTable.name, this.referencedColumnNames);
}
}

View File

@ -63,6 +63,9 @@ export class TableMetadata extends TargetMetadata {
*/
get name() {
if (this.isClosure && this._name)
return this.entityMetadata.namingStrategy.closureJunctionTableName(this._name);
// if custom name is given then use it
if (this._name)
return this.entityMetadata.namingStrategy.tableNameCustomized(this._name);

View File

@ -57,7 +57,7 @@ export class MysqlSchemaBuilder extends SchemaBuilder {
}
addForeignKeyQuery(foreignKey: ForeignKeyMetadata): Promise<void> {
let sql = `ALTER TABLE ${foreignKey.table.name} ADD CONSTRAINT \`${foreignKey.name}\` ` +
let sql = `ALTER TABLE ${foreignKey.tableName} ADD CONSTRAINT \`${foreignKey.name}\` ` +
`FOREIGN KEY (${foreignKey.columnNames.join(", ")}) ` +
`REFERENCES ${foreignKey.referencedTable.name}(${foreignKey.referencedColumnNames.join(",")})`;
if (foreignKey.onDelete)
@ -70,7 +70,7 @@ export class MysqlSchemaBuilder extends SchemaBuilder {
dropForeignKeyQuery(tableNameOrForeignKey: string|ForeignKeyMetadata, foreignKeyName?: string): Promise<void> {
let tableName = <string> tableNameOrForeignKey;
if (tableNameOrForeignKey instanceof ForeignKeyMetadata) {
tableName = tableNameOrForeignKey.table.name;
tableName = tableNameOrForeignKey.tableName;
foreignKeyName = tableNameOrForeignKey.name;
}