added remove tests

This commit is contained in:
Umed Khudoiberdiev 2016-03-07 19:20:12 +05:00
parent 23458269df
commit 3c7ff278f6
4 changed files with 114 additions and 80 deletions

View File

@ -10,7 +10,7 @@ export class PostMetadata {
id: number;
@Column()
url: string;
description: string;
@OneToOne<Post>(false, () => Post, post => post.metadata)
post: Post;

View File

@ -14,6 +14,13 @@ export interface InsertOperation {
entityId: number;
}
export interface RemoveOperation {
entity: any;
fromEntityId: any;
metadata: EntityMetadata;
relation: RelationMetadata;
}
export interface UpdateOperation {
entity: any;
columns: ColumnMetadata[];
@ -40,14 +47,14 @@ export class UpdateByRelationOperation {
export class PersistOperation {
inserts: InsertOperation[];
removes: any[];
removes: RemoveOperation[];
updates: UpdateOperation[];
junctionInserts: JunctionInsertOperation[];
junctionRemoves: JunctionRemoveOperation[];
updatesByRelations: UpdateByRelationOperation[];
constructor(inserts: any[],
removes: any[],
constructor(inserts: InsertOperation[],
removes: RemoveOperation[],
updates: UpdateOperation[],
junctionInserts: JunctionInsertOperation[],
junctionRemoves: JunctionRemoveOperation[],
@ -372,13 +379,18 @@ export class EntityPersistOperationsBuilder {
.reduce((removedEntities, relation) => {
const relationIdColumnName = relation.relatedEntityMetadata.primaryColumn.name;
const relMetadata = relation.relatedEntityMetadata;
if (dbEntity[relation.propertyName] instanceof Array) {
if (dbEntity[relation.propertyName] instanceof Array) { // todo: propertyName or name here?
dbEntity[relation.propertyName].forEach((subEntity: any) => {
const isObjectRemoved = !newEntities.find(newEntity => {
return newEntity.id === subEntity[relationIdColumnName] && newEntity.entity.constructor.name === relMetadata.name;
});
if (isObjectRemoved && relation.isCascadeRemove)
removedEntities.push(subEntity);
removedEntities.push({
entity: subEntity,
fromEntityId: dbEntity[metadata.primaryColumn.name],
metadata: metadata,
relation: relation
});
removedEntities = removedEntities.concat(this.findCascadeRemovedEntities(relMetadata, subEntity, newEntities));
});
@ -388,7 +400,12 @@ export class EntityPersistOperationsBuilder {
return newEntity.id === relationId && newEntity.entity.constructor.name === relMetadata.name;
});
if (isObjectRemoved && relation.isCascadeRemove)
removedEntities.push(dbEntity[relation.propertyName]);
removedEntities.push({
entity: dbEntity[relation.propertyName],
fromEntityId: dbEntity[metadata.primaryColumn.name],
metadata: metadata,
relation: relation
});
removedEntities = removedEntities.concat(this.findCascadeRemovedEntities(relMetadata, dbEntity[relation.propertyName], newEntities));
}

View File

@ -6,7 +6,7 @@ import {PlainObjectToNewEntityTransformer} from "../query-builder/transformer/Pl
import {PlainObjectToDatabaseEntityTransformer} from "../query-builder/transformer/PlainObjectToDatabaseEntityTransformer";
import {
EntityPersistOperationsBuilder, PersistOperation, JunctionInsertOperation,
InsertOperation, JunctionRemoveOperation, UpdateOperation, UpdateByRelationOperation
InsertOperation, JunctionRemoveOperation, UpdateOperation, UpdateByRelationOperation, RemoveOperation
} from "./EntityPersistOperationsBuilder";
// todo: think how we can implement queryCount, queryManyAndCount
@ -88,11 +88,17 @@ export class Repository<Entity> {
return promise.then(dbEntity => {
const persistOperations = this.difference(dbEntity, entity);
// create update queries based on diff map
return Promise.all(persistOperations.inserts.map(operation => {
return this.insert(operation.entity).then((result: any) => {
operation.entityId = result.insertId;
});
})).then(() => { // insert junction table insertions
// return Promise.resolve()
return Promise.resolve()
.then(() => { // insert new relations
return Promise.all(persistOperations.inserts.map(operation => {
return this.insert(operation.entity).then((result: any) => {
operation.entityId = result.insertId;
});
}));
}).then(() => { // insert junction table insertions
return Promise.all(persistOperations.junctionInserts.map(junctionOperation => {
return this.insertJunctions(junctionOperation, persistOperations.inserts);
@ -108,63 +114,21 @@ export class Repository<Entity> {
return Promise.all(persistOperations.updatesByRelations.map(updateByRelation => {
this.updateByRelation(updateByRelation, persistOperations.inserts);
}));
/*return Promise.all(persistOperations.inserts.map(operation => {
const meta = this.connection.getMetadata(operation.entity.constructor);
const oneToOneManyToOneUpdates = Promise.all(meta.relations.map(relation => {
let insertOperationUpdates: Promise<any>, updateOperationUpdates: Promise<any>;
if (operation.entity[relation.propertyName] instanceof Array && relation.isOneToMany) {
insertOperationUpdates = Promise.all(persistOperations.inserts.filter(o => {
return operation.entity[relation.propertyName].indexOf(o.entity) !== -1;
}).map(o => {
const oMetadata = this.connection.getMetadata(o.entity.constructor);
const inverseRelation = relation.inverseRelation;
const query = `UPDATE ${oMetadata.table.name} SET ${inverseRelation.name}='${operation.entityId}' WHERE ${oMetadata.primaryColumn.name}='${o.entityId}'`;
return this.connection.driver.query(query);
}));
updateOperationUpdates = Promise.all(persistOperations.updates.filter(o => {
return operation.entity[relation.propertyName].indexOf(o.entity) !== -1;
}).map(o => {
const oMetadata = this.connection.getMetadata(o.entity.constructor);
const inverseRelation = relation.inverseRelation;
const id = operation.entity[meta.primaryColumn.name];
const query = `UPDATE ${oMetadata.table.name} SET ${inverseRelation.name}='${operation.entityId}' WHERE ${oMetadata.primaryColumn.name}='${id}'`;
return this.connection.driver.query(query);
}));
} else {
insertOperationUpdates = Promise.all(persistOperations.inserts.filter(o => {
return operation.entity[relation.propertyName] === o.entity; // only one-to-one and many-to-one
}).map(o => {
const query = `UPDATE ${meta.table.name} SET ${relation.name}='${o.entityId}' WHERE ${meta.primaryColumn.name}='${operation.entityId}'`;
return this.connection.driver.query(query);
}));
updateOperationUpdates = Promise.all(persistOperations.updates.filter(o => {
return operation.entity[relation.propertyName] === o.entity; // only one-to-one and many-to-one
}).map(o => {
const reverseMeta = this.connection.getMetadata(o.entity.constructor);
const id = o.entity[reverseMeta.primaryColumn.name];
const query = `UPDATE ${meta.table.name} SET ${relation.name}='${id}' WHERE ${meta.primaryColumn.name}='${operation.entityId}'`;
return this.connection.driver.query(query);
}));
}
return Promise.all([insertOperationUpdates, updateOperationUpdates]);
}));
return Promise.all([oneToOneManyToOneUpdates]);
}));*/
}).then(() => { // perform updates
return Promise.all(persistOperations.updates.map(updateOperation => {
return this.update(updateOperation);
return Promise.all(persistOperations.updates.map(updateOperation => {
return this.update(updateOperation);
}));
}).then(() => { // remove removed relations
return Promise.all(persistOperations.removes.map(operation => {
return this.updateDeletedRelations(operation);
}));
}).then(() => { // remove removed entities
return Promise.all(persistOperations.removes.map(operation => {
return this.delete(operation.entity);
}));
}).then(() => { // update ids
@ -177,16 +141,6 @@ export class Repository<Entity> {
return entity;
});
//} else {
// do update
/*return this.initialize(entity).then(dbEntity => {
const persistOperations = this.difference(dbEntity, entity);
// create update queries based on diff map
return Promise.all(persistOperations.inserts.map(operation => {
return this.insert(operation.entity);
}));
});*/
//}
});
}
@ -223,6 +177,18 @@ export class Repository<Entity> {
const query = `UPDATE ${metadata.table.name} SET ${values} WHERE ${metadata.primaryColumn.name}='${metadata.getEntityId(entity)}'` ;
return this.connection.driver.query(query);
}
private updateDeletedRelations(removeOperation: RemoveOperation) { // todo: check if both many-to-one deletions work too
const value = removeOperation.relation.name + "=NULL";
const query = `UPDATE ${removeOperation.metadata.table.name} SET ${value} WHERE ${removeOperation.metadata.primaryColumn.name}='${removeOperation.fromEntityId}'` ;
return this.connection.driver.query(query);
}
private delete(entity: any) {
const metadata = this.connection.getMetadata(entity.constructor);
const query = `DELETE FROM ${metadata.table.name} WHERE ${metadata.primaryColumn.name}='${entity[metadata.primaryColumn.propertyName]}'`;
return this.connection.driver.query(query);
}
private insert(entity: any) {
const metadata = this.connection.getMetadata(entity.constructor);

View File

@ -51,12 +51,14 @@ describe("insertion", function() {
let postRepository: Repository<Post>,
postDetailsRepository: Repository<PostDetails>,
postCategoryRepository: Repository<PostCategory>,
postImageRepository: Repository<PostImage>;
postImageRepository: Repository<PostImage>,
postMetadataRepository: Repository<PostMetadata>;
before(function() {
postRepository = connection.getRepository<Post>(Post);
postDetailsRepository = connection.getRepository<PostDetails>(PostDetails);
postCategoryRepository = connection.getRepository<PostCategory>(PostCategory);
postImageRepository = connection.getRepository<PostImage>(PostImage);
postMetadataRepository = connection.getRepository<PostMetadata>(PostMetadata);
});
// -------------------------------------------------------------------------
@ -334,11 +336,11 @@ describe("insertion", function() {
});
describe("cascade updates should be executed when cascadeUpdate option is set", function() {
let newPost: Post, newImage: PostImage, savedPost: Post, savedImage: PostImage;
let newPost: Post, newImage: PostImage, savedImage: PostImage;
before(reloadDatabase);
it("should ignore updates in the model and do not update the db when entity is updated", function () {
it("should update a relation successfully when updated", function () {
newImage = new PostImage();
newImage.url = "logo.png";
@ -382,4 +384,53 @@ describe("insertion", function() {
});
describe("cascade remove should be executed when cascadeRemove option is set", function() {
let newPost: Post, newMetadata: PostMetadata, savedMetadata: PostMetadata;
before(reloadDatabase);
it("should remove a relation entity successfully when removed", function () {
newMetadata = new PostMetadata();
newMetadata.description = "this is post metadata";
newPost = new Post();
newPost.text = "Hello post";
newPost.title = "this is post title";
return postMetadataRepository
.persist(newMetadata)
.then(metadata => {
savedMetadata = metadata;
newPost.metadata = metadata;
return postRepository.persist(newPost);
}).then(post => {
newPost = post;
return postRepository
.createQueryBuilder("post")
.leftJoinAndSelect("post.metadata", "metadata")
.where("post.id=:id")
.setParameter("id", post.id)
.getSingleResult();
}).then(loadedPost => {
loadedPost.metadata = null;
return postRepository.persist(loadedPost);
}).then(() => {
return postRepository
.createQueryBuilder("post")
.leftJoinAndSelect("post.metadata", "metadata")
.where("post.id=:id")
.setParameter("id", newPost.id)
.getSingleResult();
}).then(reloadedPost => {
expect(reloadedPost.metadata).to.be.empty;
});
});
});
});