fixed issues when update operations were not working correctly when new entity is attached and its inside things were changed + fixed issues with custom column name for relations + added tests for it

This commit is contained in:
Umed Khudoiberdiev 2016-05-23 17:04:56 +05:00
parent f513fc1ba7
commit cf6ebe5c2e
14 changed files with 423 additions and 64 deletions

View File

@ -1,7 +1,7 @@
{
"name": "typeorm",
"private": true,
"version": "0.0.2-alpha.24",
"version": "0.0.2-alpha.25",
"description": "Data-mapper ORM for Typescript",
"license": "Apache-2.0",
"readmeFilename": "README.md",

View File

@ -85,7 +85,7 @@ export class ConnectionManager {
if (existConnection)
this.connections.splice(this.connections.indexOf(existConnection), 1);
const connection = new Connection(name, <Driver> driver, options);
const connection = new Connection(name, driver, options);
this.connections.push(connection);
return connection;
}

View File

@ -130,7 +130,7 @@ export class EntityMetadataBuilder {
entityMetadata.relations.forEach(relation => {
const inverseEntityMetadata = entityMetadatas.find(m => m.target === relation.type);
if (!inverseEntityMetadata)
throw new Error("Entity metadata for " + entityMetadata.name + "#" + relation.name + " was not found.");
throw new Error("Entity metadata for " + entityMetadata.name + "#" + relation.propertyName + " was not found.");
relation.inverseEntityMetadata = inverseEntityMetadata;
});

View File

@ -10,11 +10,11 @@ export class MissingJoinColumnError extends Error {
constructor(entityMetadata: EntityMetadata, relation: RelationMetadata) {
super();
if (relation.hasInverseSide) {
this.message = `JoinColumn is missing on both sides of ${entityMetadata.name}#${relation.name} and ` +
this.message = `JoinColumn is missing on both sides of ${entityMetadata.name}#${relation.propertyName} and ` +
`${relation.inverseEntityMetadata.name}#${relation.inverseRelation.name} one-to-one relationship. ` +
`You need to put JoinColumn decorator on one of the sides.`;
} else {
this.message = `JoinColumn is missing on ${entityMetadata.name}#${relation.name} one-to-one relationship. ` +
this.message = `JoinColumn is missing on ${entityMetadata.name}#${relation.propertyName} one-to-one relationship. ` +
`You need to put JoinColumn decorator on it.`;
}
}

View File

@ -189,8 +189,8 @@ export class EntityMetadata {
createPropertiesMap(): any {
const entity: any = {};
this.columns.forEach(column => entity[column.name] = column.name);
this.relations.forEach(relation => entity[relation.name] = relation.name);
this.columns.forEach(column => entity[column.propertyName] = column.propertyName);
this.relations.forEach(relation => entity[relation.propertyName] = relation.propertyName);
return entity;
}

View File

@ -177,6 +177,13 @@ export class RelationMetadata extends PropertyMetadata {
return this.namingStrategy ? this.namingStrategy.relationName(this._name) : this._name;
}
get referencedColumnName(): string {
if (this.joinColumn && this.joinColumn.referencedColumn && this.joinColumn.referencedColumn.name)
return this.joinColumn.referencedColumn.name;
return this.inverseEntityMetadata.primaryColumn.propertyName;
}
/**
* Indicates if this side is an owner of this relation.

View File

@ -73,11 +73,13 @@ export class EntityPersistOperationBuilder {
persistOperation.inserts = this.findCascadeInsertedEntities(persistedEntity, dbEntities);
persistOperation.updatesByRelations = this.updateRelations(persistOperation.inserts, persistedEntity);
persistOperation.updatesByInverseRelations = this.updateInverseRelations(metadata, dbEntity, persistedEntity);
persistOperation.updates = this.findCascadeUpdateEntities(persistOperation.updatesByRelations, metadata, dbEntity, persistedEntity);
persistOperation.updates = this.findCascadeUpdateEntities(persistOperation.updatesByRelations, metadata, dbEntity, persistedEntity, dbEntities);
persistOperation.junctionInserts = this.findJunctionInsertOperations(metadata, persistedEntity, dbEntities);
persistOperation.removes = this.findCascadeRemovedEntities(metadata, dbEntity, allPersistedEntities, undefined, undefined, undefined);
persistOperation.junctionRemoves = this.findJunctionRemoveOperations(metadata, dbEntity, allPersistedEntities);
// persistOperation.log();
return persistOperation;
}
@ -113,7 +115,7 @@ export class EntityPersistOperationBuilder {
operations: InsertOperation[] = []): InsertOperation[] {
// const metadata = this.connection.getEntityMetadata(newEntity.constructor);
const metadata = this.entityMetadatas.findByTarget(newEntity.constructor);
const isObjectNew = !this.findEntityWithId(dbEntities, metadata.target, newEntity[metadata.primaryColumn.name]);
const isObjectNew = !this.findEntityWithId(dbEntities, metadata.target, newEntity[metadata.primaryColumn.propertyName]);
// if object is new and should be inserted, we check if cascades are allowed before add it to operations list
if (isObjectNew && fromRelation && !this.checkCascadesAllowed("insert", metadata, fromRelation)) {
@ -141,6 +143,7 @@ export class EntityPersistOperationBuilder {
metadata: EntityMetadata,
dbEntity: any,
newEntity: any,
dbEntities: EntityWithId[],
fromRelation?: RelationMetadata,
operations: UpdateOperation[] = []): UpdateOperation[] {
if (!dbEntity)
@ -152,30 +155,39 @@ export class EntityPersistOperationBuilder {
return operations;
} else if (diffColumns.length || diffRelations.length) {
const entityId = newEntity[metadata.primaryColumn.name];
const entityId = newEntity[metadata.primaryColumn.propertyName];
if (entityId)
operations.push(new UpdateOperation(newEntity, entityId, diffColumns, diffRelations));
}
metadata.relations.forEach(relation => {
const relMetadata = relation.inverseEntityMetadata;
const relationIdColumnName = relMetadata.primaryColumn.name;
const relationIdColumnName = relMetadata.primaryColumn.propertyName;
const value = this.getEntityRelationValue(relation, newEntity);
const dbValue = this.getEntityRelationValue(relation, dbEntity);
const referencedColumnName = relation.isOwning ? relation.referencedColumnName : relation.inverseRelation.referencedColumnName;
// const dbValue = this.getEntityRelationValue(relation, dbEntity);
if (!value || !dbValue)
if (!value/* || !dbValue*/)
return;
if (value instanceof Array) {
value.forEach((subEntity: any) => {
const subDbEntity = dbValue.find((subDbEntity: any) => {
/*const subDbEntity = dbValue.find((subDbEntity: any) => {
return subDbEntity[relationIdColumnName] === subEntity[relationIdColumnName];
});*/
const dbValue = dbEntities.find(dbValue => {
return dbValue.entity.constructor === subEntity.constructor && dbValue.entity[referencedColumnName] === subEntity[relationIdColumnName];
});
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, subDbEntity, subEntity, relation, operations);
if (dbValue)
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValue.entity, subEntity, dbEntities, relation, operations);
});
} else {
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValue, value, relation, operations);
const dbValue = dbEntities.find(dbValue => {
return dbValue.entity.constructor === value.constructor && dbValue.entity[referencedColumnName] === value[relationIdColumnName];
});
if (dbValue)
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValue.entity, value, dbEntities, relation, operations);
}
});
@ -193,7 +205,7 @@ export class EntityPersistOperationBuilder {
if (!dbEntity)
return operations;
const entityId = dbEntity[metadata.primaryColumn.name];
const entityId = dbEntity[metadata.primaryColumn.propertyName];
const isObjectRemoved = parentAlreadyRemoved || !this.findEntityWithId(allPersistedEntities, metadata.target, entityId);
// if object is removed and should be removed, we check if cascades are allowed before add it to operations list
@ -211,11 +223,11 @@ export class EntityPersistOperationBuilder {
if (dbValue instanceof Array) {
dbValue.forEach((subDbEntity: any) => {
const relationOperations = this.findCascadeRemovedEntities(relMetadata, subDbEntity, allPersistedEntities, relation, metadata, dbEntity[metadata.primaryColumn.name], isObjectRemoved);
const relationOperations = this.findCascadeRemovedEntities(relMetadata, subDbEntity, allPersistedEntities, relation, metadata, dbEntity[metadata.primaryColumn.propertyName], isObjectRemoved);
relationOperations.forEach(o => operations.push(o));
});
} else {
const relationOperations = this.findCascadeRemovedEntities(relMetadata, dbValue, allPersistedEntities, relation, metadata, dbEntity[metadata.primaryColumn.name], isObjectRemoved);
const relationOperations = this.findCascadeRemovedEntities(relMetadata, dbValue, allPersistedEntities, relation, metadata, dbEntity[metadata.primaryColumn.propertyName], isObjectRemoved);
relationOperations.forEach(o => operations.push(o));
}
}, []);
@ -314,13 +326,13 @@ export class EntityPersistOperationBuilder {
private findJunctionInsertOperations(metadata: EntityMetadata, newEntity: any, dbEntities: EntityWithId[], isRoot = true): JunctionInsertOperation[] {
const dbEntity = dbEntities.find(dbEntity => {
return dbEntity.id === newEntity[metadata.primaryColumn.name] && dbEntity.entity.constructor === metadata.target;
return dbEntity.id === newEntity[metadata.primaryColumn.propertyName] && dbEntity.entity.constructor === metadata.target;
});
return metadata.relations
.filter(relation => newEntity[relation.propertyName] !== null && newEntity[relation.propertyName] !== undefined)
.reduce((operations, relation) => {
const relationMetadata = relation.inverseEntityMetadata;
const relationIdProperty = relationMetadata.primaryColumn.name;
const relationIdProperty = relationMetadata.primaryColumn.propertyName;
const value = this.getEntityRelationValue(relation, newEntity);
const dbValue = dbEntity ? this.getEntityRelationValue(relation, dbEntity.entity) : null;
@ -360,13 +372,13 @@ export class EntityPersistOperationBuilder {
return [];
const newEntity = newEntities.find(newEntity => {
return newEntity.id === dbEntity[metadata.primaryColumn.name] && newEntity.entity.constructor === metadata.target;
return newEntity.id === dbEntity[metadata.primaryColumn.propertyName] && newEntity.entity.constructor === metadata.target;
});
return metadata.relations
.filter(relation => dbEntity[relation.propertyName] !== null && dbEntity[relation.propertyName] !== undefined)
.reduce((operations, relation) => {
const relationMetadata = relation.inverseEntityMetadata;
const relationIdProperty = relationMetadata.primaryColumn.name;
const relationIdProperty = relationMetadata.primaryColumn.propertyName;
const value = newEntity ? this.getEntityRelationValue(relation, newEntity.entity) : null;
const dbValue = this.getEntityRelationValue(relation, dbEntity);
@ -404,7 +416,16 @@ export class EntityPersistOperationBuilder {
private diffColumns(metadata: EntityMetadata, newEntity: any, dbEntity: any) {
return metadata.columns
.filter(column => !column.isVirtual && !column.isUpdateDate && !column.isVersion && !column.isCreateDate)
.filter(column => newEntity[column.propertyName] !== dbEntity[column.propertyName]);
.filter(column => newEntity[column.propertyName] !== dbEntity[column.propertyName])
.filter(column => {
// filter out "relational columns" only in the case if there is a relation object in entity
if (metadata.hasRelationWithDbName(column.propertyName)) {
const relation = metadata.findRelationWithDbName(column.propertyName);
if (newEntity[relation.propertyName] !== null && newEntity[relation.propertyName] !== undefined)
return false;
}
return true;
});
}
private diffRelations(updatesByRelations: UpdateByRelationOperation[], metadata: EntityMetadata, newEntity: any, dbEntity: any) {

View File

@ -248,7 +248,7 @@ export class PersistOperationExecutor {
insertOperation.entity[metadata.versionColumn.propertyName]++;
if (metadata.hasTreeLevelColumn) {
// const parentEntity = insertOperation.entity[metadata.treeParentMetadata.propertyName];
// const parentLevel = parentEntity ? (parentEntity[metadata.treeLevelColumn.name] || 0) : 0;
// const parentLevel = parentEntity ? (parentEntity[metadata.treeLevelColumn.propertyName] || 0) : 0;
insertOperation.entity[metadata.treeLevelColumn.propertyName] = insertOperation.treeLevel;
}
if (metadata.hasTreeChildrenCountColumn) {
@ -273,7 +273,7 @@ export class PersistOperationExecutor {
persistOperation.removes.forEach(removeOperation => {
const metadata = this.entityMetadatas.findByTarget(removeOperation.entity.constructor);
const removedEntity = persistOperation.allPersistedEntities.find(allNewEntity => {
return allNewEntity.entity.constructor === removeOperation.entity.constructor && allNewEntity.id === removeOperation.entity[metadata.primaryColumn.name];
return allNewEntity.entity.constructor === removeOperation.entity.constructor && allNewEntity.id === removeOperation.entity[metadata.primaryColumn.propertyName];
});
if (removedEntity)
removedEntity.entity[metadata.primaryColumn.propertyName] = undefined;
@ -290,11 +290,11 @@ export class PersistOperationExecutor {
if (operation.updatedRelation.isOneToMany) {
const metadata = this.entityMetadatas.findByTarget(operation.insertOperation.entity.constructor);
if (operation.insertOperation.entity === target)
updateMap[operation.updatedRelation.inverseRelation.name] = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
updateMap[operation.updatedRelation.inverseRelation.propertyName] = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
} else {
if (operation.targetEntity === target)
updateMap[operation.updatedRelation.name] = operation.insertOperation.entityId;
updateMap[operation.updatedRelation.propertyName] = operation.insertOperation.entityId;
}
});
@ -325,26 +325,6 @@ export class PersistOperationExecutor {
}
private updateInverseRelation(operation: UpdateByInverseSideOperation, insertOperations: InsertOperation[]) {
/*let tableName: string, relationName: string, relationId: any, idColumn: string, id: any;
const relatedInsertOperation = insertOperations.find(o => o.entity === operation.targetEntity);
const idInInserts = relatedInsertOperation ? relatedInsertOperation.entityId : null;
if (operation.updatedRelation.isOneToMany) {
const metadata = this.entityMetadatas.findByTarget(operation.insertOperation.entity.constructor);
tableName = metadata.table.name;
relationName = operation.updatedRelation.inverseRelation.name;
relationId = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
idColumn = metadata.primaryColumn.name;
id = operation.insertOperation.entityId;
} else {
const metadata = this.entityMetadatas.findByTarget(operation.targetEntity.constructor);
tableName = metadata.table.name;
relationName = operation.updatedRelation.name;
relationId = operation.insertOperation.entityId;
idColumn = metadata.primaryColumn.name;
id = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
}*/
const targetEntityMetadata = this.entityMetadatas.findByTarget(operation.targetEntity.constructor);
const fromEntityMetadata = this.entityMetadatas.findByTarget(operation.fromEntity.constructor);
const tableName = targetEntityMetadata.table.name;
@ -424,14 +404,14 @@ export class PersistOperationExecutor {
const relationColumns = metadata.relations
.filter(relation => relation.isOwning && !!relation.inverseEntityMetadata)
.filter(relation => entity.hasOwnProperty(relation.propertyName))
.filter(relation => entity[relation.propertyName][relation.inverseEntityMetadata.primaryColumn.name])
.filter(relation => entity[relation.propertyName][relation.inverseEntityMetadata.primaryColumn.propertyName])
.map(relation => relation.name);
const relationValues = metadata.relations
.filter(relation => relation.isOwning && !!relation.inverseEntityMetadata)
.filter(relation => entity.hasOwnProperty(relation.propertyName))
.filter(relation => entity[relation.propertyName].hasOwnProperty(relation.inverseEntityMetadata.primaryColumn.name))
.map(relation => entity[relation.propertyName][relation.inverseEntityMetadata.primaryColumn.name]);
.filter(relation => entity[relation.propertyName].hasOwnProperty(relation.inverseEntityMetadata.primaryColumn.propertyName))
.map(relation => entity[relation.propertyName][relation.inverseEntityMetadata.primaryColumn.propertyName]);
const allColumns = columns.concat(relationColumns);
const allValues = values.concat(relationValues);
@ -452,8 +432,8 @@ export class PersistOperationExecutor {
}
if (metadata.hasTreeLevelColumn && metadata.hasTreeParentRelation) {
const parentEntity = entity[metadata.treeParentRelation.propertyName];
const parentLevel = parentEntity ? (parentEntity[metadata.treeLevelColumn.name] || 0) : 0;
const parentEntity = entity[metadata.treeParentRelation.name]; // todo: are you sure here we should use name and not propertyName ?
const parentLevel = parentEntity ? (parentEntity[metadata.treeLevelColumn.propertyName] || 0) : 0;
allColumns.push(metadata.treeLevelColumn.name);
allValues.push(parentLevel + 1);
@ -473,8 +453,9 @@ export class PersistOperationExecutor {
const parentEntity = entity[metadata.treeParentRelation.propertyName];
let parentEntityId: any = 0;
if (parentEntity && parentEntity[metadata.primaryColumn.name]) {
parentEntityId = parentEntity[metadata.primaryColumn.name];
if (parentEntity && parentEntity[metadata.primaryColumn.propertyName]) {
parentEntityId = parentEntity[metadata.primaryColumn.propertyName];
} else if (updateMap && updateMap[metadata.treeParentRelation.propertyName]) { // todo: name or propertyName: depend how update will be implemented. or even find relation of this treeParent and use its name?
parentEntityId = updateMap[metadata.treeParentRelation.propertyName];
}
@ -510,8 +491,8 @@ export class PersistOperationExecutor {
const insertOperation1 = insertOperations.find(o => o.entity === junctionOperation.entity1);
const insertOperation2 = insertOperations.find(o => o.entity === junctionOperation.entity2);
let id1 = junctionOperation.entity1[metadata1.primaryColumn.name];
let id2 = junctionOperation.entity2[metadata2.primaryColumn.name];
let id1 = junctionOperation.entity1[metadata1.primaryColumn.propertyName];
let id2 = junctionOperation.entity2[metadata2.primaryColumn.propertyName];
if (!id1) {
if (insertOperation1) {
@ -545,8 +526,8 @@ export class PersistOperationExecutor {
const metadata1 = this.entityMetadatas.findByTarget(junctionOperation.entity1.constructor);
const metadata2 = this.entityMetadatas.findByTarget(junctionOperation.entity2.constructor);
const columns = junctionMetadata.columns.map(column => column.name);
const id1 = junctionOperation.entity1[metadata1.primaryColumn.name];
const id2 = junctionOperation.entity2[metadata2.primaryColumn.name];
const id1 = junctionOperation.entity1[metadata1.primaryColumn.propertyName];
const id2 = junctionOperation.entity2[metadata2.primaryColumn.propertyName];
return this.driver.delete(junctionMetadata.table.name, { [columns[0]]: id1, [columns[1]]: id2 });
}

View File

@ -632,7 +632,7 @@ export class QueryBuilder<Entity> {
const foundAlias = this.aliasMap.findAliasByName(parentAlias);
if (!foundAlias)
throw new Error(`Alias "${parentAlias}" was not found`);
const parentMetadata = this.aliasMap.getEntityMetadataByAlias(foundAlias);
const relation = parentMetadata.findRelationWithDbName(join.alias.parentPropertyName);
const junctionMetadata = relation.junctionEntityMetadata;
@ -735,9 +735,6 @@ export class QueryBuilder<Entity> {
protected join(joinType: "INNER"|"LEFT", entityOrProperty: Function|string, alias: string, conditionType: "ON"|"WITH", condition: string, parameters?: { [key: string]: any }, mapToProperty?: string, isMappingMany?: boolean): this;
protected join(joinType: "INNER"|"LEFT", entityOrProperty: Function|string, alias: string, conditionType: "ON"|"WITH" = "ON", condition: string = "", parameters?: { [key: string]: any }, mapToProperty?: string, isMappingMany: boolean = false): this {
if (!mapToProperty && typeof entityOrProperty === "string")
mapToProperty = entityOrProperty;
let tableName = "";
const aliasObj = new Alias(alias);
this.aliasMap.addAlias(aliasObj);
@ -747,8 +744,11 @@ export class QueryBuilder<Entity> {
} else if (typeof entityOrProperty === "string" && entityOrProperty.indexOf(".") !== -1) {
aliasObj.parentAliasName = entityOrProperty.split(".")[0];
aliasObj.parentPropertyName = entityOrProperty.split(".")[1];
} else if (typeof entityOrProperty === "string") {
tableName = entityOrProperty;
if (!mapToProperty)
mapToProperty = entityOrProperty;
}
const join: Join = { type: joinType, alias: aliasObj, tableName: tableName, conditionType: conditionType, condition: condition, mapToProperty: mapToProperty, isMappingMany: isMappingMany };

View File

@ -91,7 +91,7 @@ export class RawSqlResultsToEntityTransformer {
if (joinMapping) {
propertyName = joinMapping.propertyName;
}
if (relation.isLazy) {
entity["__" + propertyName + "__"] = result;
} else {

View File

@ -0,0 +1,31 @@
import {Table} from "../../../../../src/decorator/tables/Table";
import {PrimaryColumn} from "../../../../../src/decorator/columns/PrimaryColumn";
import {Post} from "./Post";
import {Column} from "../../../../../src/decorator/columns/Column";
import {OneToMany} from "../../../../../src/decorator/relations/OneToMany";
import {OneToOne} from "../../../../../src/decorator/relations/OneToOne";
import {JoinColumn} from "../../../../../src/decorator/relations/JoinColumn";
import {CategoryMetadata} from "./CategoryMetadata";
@Table()
export class Category {
@PrimaryColumn("int", { generated: true })
id: number;
@OneToMany(type => Post, post => post.category)
posts: Post[];
@Column({ type: "int", nullable: true })
metadataId: number;
@OneToOne(type => CategoryMetadata, metadata => metadata.category, {
cascadeInsert: true
})
@JoinColumn({ name: "metadataId" })
metadata: CategoryMetadata;
@Column()
name: string;
}

View File

@ -0,0 +1,19 @@
import {Table} from "../../../../../src/decorator/tables/Table";
import {PrimaryColumn} from "../../../../../src/decorator/columns/PrimaryColumn";
import {Column} from "../../../../../src/decorator/columns/Column";
import {OneToOne} from "../../../../../src/decorator/relations/OneToOne";
import {Category} from "./Category";
@Table()
export class CategoryMetadata {
@PrimaryColumn("int", { generated: true })
id: number;
@Column()
keyword: string;
@OneToOne(type => Category, category => category.metadata)
category: Category;
}

View File

@ -0,0 +1,27 @@
import {Category} from "./Category";
import {Table} from "../../../../../src/decorator/tables/Table";
import {PrimaryColumn} from "../../../../../src/decorator/columns/PrimaryColumn";
import {Column} from "../../../../../src/decorator/columns/Column";
import {ManyToOne} from "../../../../../src/decorator/relations/ManyToOne";
import {JoinColumn} from "../../../../../src/decorator/relations/JoinColumn";
@Table()
export class Post {
@PrimaryColumn("int", { generated: true })
id: number;
@Column()
title: string;
@Column("int", { nullable: true })
categoryId: number;
@ManyToOne(type => Category, category => category.posts, {
cascadeInsert: true,
cascadeUpdate: true
})
@JoinColumn({ name: "categoryId" })
category: Category;
}

View File

@ -0,0 +1,273 @@
import "reflect-metadata";
import * as chai from "chai";
import {expect} from "chai";
import {Connection} from "../../../../src/connection/Connection";
import {Repository} from "../../../../src/repository/Repository";
import {Post} from "./entity/Post";
import {Category} from "./entity/Category";
import {CreateConnectionOptions} from "../../../../src/connection-manager/CreateConnectionOptions";
import {createConnection} from "../../../../src/index";
import {CategoryMetadata} from "./entity/CategoryMetadata";
chai.should();
chai.use(require("sinon-chai"));
chai.use(require("chai-as-promised"));
describe("persistence > custom-column-names", function() {
// -------------------------------------------------------------------------
// Configuration
// -------------------------------------------------------------------------
const parameters: CreateConnectionOptions = {
driver: "mysql",
connection: {
host: "192.168.99.100",
port: 3306,
username: "root",
password: "admin",
database: "test",
autoSchemaCreate: true,
logging: {
logFailedQueryError: true
}
},
entities: [Post, Category, CategoryMetadata]
};
// connect to db
let connection: Connection;
before(function() {
return createConnection(parameters)
.then(con => connection = con)
.catch(e => {
console.log("Error during connection to db: " + e);
throw e;
});
});
after(function() {
connection.close();
});
// clean up database before each test
function reloadDatabase() {
return connection.driver
.clearDatabase()
.then(() => connection.syncSchema())
.catch(e => console.log("Error during schema re-creation: ", e));
}
let postRepository: Repository<Post>;
let categoryRepository: Repository<Category>;
let metadataRepository: Repository<CategoryMetadata>;
before(function() {
postRepository = connection.getRepository(Post);
categoryRepository = connection.getRepository(Category);
metadataRepository = connection.getRepository(CategoryMetadata);
});
// -------------------------------------------------------------------------
// Specifications
// -------------------------------------------------------------------------
describe("attach exist entity to exist entity with many-to-one relation", function() {
let newPost: Post, newCategory: Category, loadedPost: Post;
before(reloadDatabase);
// save a new category
before(function () {
newCategory = categoryRepository.create();
newCategory.name = "Animals";
return categoryRepository.persist(newCategory);
});
// save a new post
before(function() {
newPost = postRepository.create();
newPost.title = "All about animals";
return postRepository.persist(newPost);
});
// attach category to post and save it
before(function() {
newPost.category = newCategory;
return postRepository.persist(newPost);
});
// load a post
before(function() {
return postRepository
.findOneById(1, { alias: "post", leftJoinAndSelect: { category: "post.categoryId" } })
.then(post => loadedPost = post);
});
it("should contain attached category", function () {
expect(loadedPost).not.to.be.empty;
expect(loadedPost.category).not.to.be.empty;
expect(loadedPost.categoryId).not.to.be.empty;
});
});
describe("attach new entity to exist entity with many-to-one relation", function() {
let newPost: Post, newCategory: Category, loadedPost: Post;
before(reloadDatabase);
// save a new category
before(function () {
newCategory = categoryRepository.create();
newCategory.name = "Animals";
return categoryRepository.persist(newCategory);
});
// save a new post and attach category
before(function() {
newPost = postRepository.create();
newPost.title = "All about animals";
newPost.category = newCategory;
return postRepository.persist(newPost);
});
// load a post
before(function() {
return postRepository
.findOneById(1, { alias: "post", leftJoinAndSelect: { category: "post.categoryId" } })
.then(post => loadedPost = post);
});
it("should contain attached category", function () {
expect(loadedPost).not.to.be.empty;
expect(loadedPost.category).not.to.be.empty;
expect(loadedPost.categoryId).not.to.be.empty;
});
});
describe("attach new entity to new entity with many-to-one relation", function() {
let newPost: Post, newCategory: Category, loadedPost: Post;
before(reloadDatabase);
// save a new category, post and attach category to post
before(function () {
newCategory = categoryRepository.create();
newCategory.name = "Animals";
newPost = postRepository.create();
newPost.title = "All about animals";
newPost.category = newCategory;
return postRepository.persist(newPost);
});
// load a post
before(function() {
return postRepository
.findOneById(1, { alias: "post", leftJoinAndSelect: { category: "post.categoryId" } })
.then(post => loadedPost = post);
});
it("should contain attached category", function () {
expect(loadedPost).not.to.be.empty;
expect(loadedPost.category).not.to.be.empty;
expect(loadedPost.categoryId).not.to.be.empty;
});
});
describe("attach exist entity to exist entity with one-to-one relation", function() {
let newPost: Post, newCategory: Category, newMetadata: CategoryMetadata, loadedPost: Post;
before(reloadDatabase);
// save a new post
before(function() {
newPost = postRepository.create();
newPost.title = "All about animals";
return postRepository.persist(newPost);
});
// save a new category
before(function () {
newCategory = categoryRepository.create();
newCategory.name = "Animals";
return categoryRepository.persist(newCategory);
});
// save a new metadata
before(function() {
newMetadata = metadataRepository.create();
newMetadata.keyword = "animals";
return metadataRepository.persist(newMetadata);
});
// attach metadata to category and category to post and save it
before(function() {
newCategory.metadata = newMetadata;
newPost.category = newCategory;
return postRepository.persist(newPost);
});
// load a post
before(function() {
return postRepository
.findOneById(1, { alias: "post", leftJoinAndSelect: { category: "post.categoryId", metadata: "category.metadataId" } })
.then(post => loadedPost = post);
});
it("should contain attached category and metadata in the category", function () {
expect(loadedPost).not.to.be.empty;
expect(loadedPost.category).not.to.be.empty;
expect(loadedPost.categoryId).not.to.be.empty;
expect(loadedPost.category.metadata).not.to.be.empty;
expect(loadedPost.category.metadataId).not.to.be.empty;
});
});
describe("attach new entity to exist entity with one-to-one relation", function() {
let newPost: Post, newCategory: Category, newMetadata: CategoryMetadata, loadedPost: Post;
before(reloadDatabase);
// save a new post
before(function() {
newPost = postRepository.create();
newPost.title = "All about animals";
return postRepository.persist(newPost);
});
// save a new category and new metadata
before(function () {
newMetadata = metadataRepository.create();
newMetadata.keyword = "animals";
newCategory = categoryRepository.create();
newCategory.name = "Animals";
newCategory.metadata = newMetadata;
return categoryRepository.persist(newCategory);
});
// attach metadata to category and category to post and save it
before(function() {
newPost.category = newCategory;
return postRepository.persist(newPost);
});
// load a post
before(function() {
return postRepository
.findOneById(1, { alias: "post", leftJoinAndSelect: { category: "post.categoryId", metadata: "category.metadataId" } })
.then(post => loadedPost = post);
});
it("should contain attached category and metadata in the category", function () {
expect(loadedPost).not.to.be.empty;
expect(loadedPost.category).not.to.be.empty;
expect(loadedPost.categoryId).not.to.be.empty;
expect(loadedPost.category.metadata).not.to.be.empty;
expect(loadedPost.category.metadataId).not.to.be.empty;
});
});
});