renamed cascade operation option names; added tests for cascade updates/removes

This commit is contained in:
Umed Khudoiberdiev 2016-03-06 22:37:16 +05:00
parent 97208dc72a
commit 23458269df
20 changed files with 492 additions and 89 deletions

View File

@ -17,14 +17,14 @@ export class Image {
post: Post;
@ManyToOne<Post>(() => Post, post => post.secondaryImages, {
isCascadeInsert: true
cascadeInsert: true
})
secondaryPost: Post;
@OneToOne<ImageDetails>(true, () => ImageDetails, details => details.image, {
isCascadeInsert: true,
isCascadeUpdate: true,
isCascadeRemove: true
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
details: ImageDetails;

View File

@ -23,16 +23,16 @@ export class Post {
text: string;
@OneToOne<PostDetails>(true, () => PostDetails, details => details.post, {
isCascadeInsert: true,
isCascadeUpdate: true,
isCascadeRemove: true
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
details: PostDetails;
@OneToMany<Image>(type => Image, image => image.post, {
isCascadeInsert: true,
isCascadeUpdate: true,
isCascadeRemove: true
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
images: Image[] = [];
@ -41,8 +41,8 @@ export class Post {
@ManyToOne<Cover>(type => Cover, cover => cover.posts, {
name: "coverId",
isCascadeInsert: true,
isCascadeRemove: true
cascadeInsert: true,
cascadeRemove: true
})
cover: Cover;
@ -52,9 +52,9 @@ export class Post {
coverId: number;
@ManyToMany<Category>(true, type => Category, category => category.posts, {
isCascadeInsert: true,
isCascadeUpdate: true,
isCascadeRemove: true
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
categories: Category[];

View File

@ -21,14 +21,14 @@ export class PostDetails {
post: Post;
@OneToMany<Category>(type => Category, category => category.details, {
isCascadeInsert: true,
isCascadeRemove: true
cascadeInsert: true,
cascadeRemove: true
})
categories: Category[];
@ManyToOne<Chapter>(_ => Chapter, chapter => chapter.postDetails, {
isCascadeInsert: true,
isCascadeRemove: true
cascadeInsert: true,
cascadeRemove: true
})
chapter: Chapter;

View File

@ -2,6 +2,11 @@ import {TypeORM} from "../../src/TypeORM";
import {Post} from "./entity/Post";
import {ConnectionOptions} from "../../src/connection/ConnectionOptions";
import {PostDetails} from "./entity/PostDetails";
import {PostCategory} from "./entity/PostCategory";
import {PostMetadata} from "./entity/PostMetadata";
import {PostImage} from "./entity/PostImage";
import {PostInformation} from "./entity/PostInformation";
import {PostAuthor} from "./entity/PostAuthor";
// first create a connection
let options: ConnectionOptions = {
@ -13,7 +18,7 @@ let options: ConnectionOptions = {
autoSchemaCreate: true
};
TypeORM.createMysqlConnection(options, [Post, PostDetails]).then(connection => {
TypeORM.createMysqlConnection(options, [Post, PostDetails, PostCategory, PostMetadata, PostImage, PostInformation, PostAuthor]).then(connection => {
let details = new PostDetails();
details.authorName = "Umed";

View File

@ -2,6 +2,11 @@ import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
import {Table} from "../../../src/decorator/Tables";
import {PostDetails} from "./PostDetails";
import {OneToOne} from "../../../src/decorator/Relations";
import {PostCategory} from "./PostCategory";
import {PostAuthor} from "./PostAuthor";
import {PostInformation} from "./PostInformation";
import {PostImage} from "./PostImage";
import {PostMetadata} from "./PostMetadata";
@Table("sample2_post")
export class Post {
@ -15,11 +20,45 @@ export class Post {
@Column()
text: string;
// post has relation with category, however inverse relation is not set (category does not have relation with post set)
@OneToOne<PostCategory>(true, () => PostCategory, {
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
category: 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
@OneToOne<PostDetails>(true, () => PostDetails, details => details.post, {
isCascadeInsert: true,
isCascadeUpdate: true,
isCascadeRemove: true
cascadeInsert: true
})
details: PostDetails;
// 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
@OneToOne<PostImage>(true, () => PostImage, image => image.post, {
cascadeUpdate: true
})
image: 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
@OneToOne<PostMetadata>(true, () => PostMetadata, metadata => metadata.post, {
cascadeRemove: true
})
metadata: PostMetadata;
// post has relation with details. full cascades here
@OneToOne<PostInformation>(true, () => PostInformation, information => information.post, {
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
information: PostInformation;
// post has relation with details. not cascades here. means cannot be persisted, updated or removed
@OneToOne<PostAuthor>(true, () => PostAuthor, author => author.post)
author: PostAuthor;
}

View File

@ -0,0 +1,18 @@
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
import {Table} from "../../../src/decorator/Tables";
import {Post} from "./Post";
import {OneToOne} from "../../../src/decorator/Relations";
@Table("sample2_post_author")
export class PostAuthor {
@PrimaryColumn("int", { autoIncrement: true })
id: number;
@Column()
name: string;
@OneToOne<Post>(false, () => Post, post => post.author)
post: Post;
}

View File

@ -0,0 +1,13 @@
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
import {Table} from "../../../src/decorator/Tables";
@Table("sample2_post_category")
export class PostCategory {
@PrimaryColumn("int", { autoIncrement: true })
id: number;
@Column()
name: string;
}

View File

@ -18,10 +18,10 @@ export class PostDetails {
@Column()
metadata: string;
@OneToOne<Post>(true, () => Post, post => post.details, {
isCascadeInsert: true,
isCascadeUpdate: true,
isCascadeRemove: true
@OneToOne<Post>(false, () => Post, post => post.details, {
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
post: Post;

View File

@ -0,0 +1,18 @@
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
import {Table} from "../../../src/decorator/Tables";
import {Post} from "./Post";
import {OneToOne} from "../../../src/decorator/Relations";
@Table("sample2_post_image")
export class PostImage {
@PrimaryColumn("int", { autoIncrement: true })
id: number;
@Column()
url: string;
@OneToOne<Post>(false, () => Post, post => post.image)
post: Post;
}

View File

@ -0,0 +1,20 @@
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
import {Table} from "../../../src/decorator/Tables";
import {OneToOne} from "../../../src/decorator/Relations";
import {Post} from "./Post";
@Table("sample2_post_information")
export class PostInformation {
@PrimaryColumn("int", { autoIncrement: true })
id: number;
@Column()
text: string;
@OneToOne<Post>(false, () => Post, post => post.information, {
cascadeUpdate: true,
})
post: Post;
}

View File

@ -0,0 +1,18 @@
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
import {Table} from "../../../src/decorator/Tables";
import {Post} from "./Post";
import {OneToOne} from "../../../src/decorator/Relations";
@Table("sample2_post_metadata")
export class PostMetadata {
@PrimaryColumn("int", { autoIncrement: true })
id: number;
@Column()
url: string;
@OneToOne<Post>(false, () => Post, post => post.metadata)
post: Post;
}

View File

@ -5,7 +5,8 @@ import {RelationTypes} from "../metadata-builder/types/RelationTypes";
export function OneToOne<T>(isOwning: boolean, typeFunction: RelationTypeInFunction, options?: RelationOptions): Function;
export function OneToOne<T>(isOwning: boolean, typeFunction: RelationTypeInFunction, inverseSide?: PropertyTypeInFunction<T>, options?: RelationOptions): Function;
export function OneToOne<T>(isOwning: boolean, typeFunction: RelationTypeInFunction,
export function OneToOne<T>(isOwning: boolean,
typeFunction: RelationTypeInFunction,
inverseSideOrOptions: PropertyTypeInFunction<T>|RelationOptions,
options?: RelationOptions): Function {
let inverseSideProperty: PropertyTypeInFunction<T>;

View File

@ -110,12 +110,12 @@ export class RelationMetadata extends PropertyMetadata {
if (options.name)
this._name = options.name;
if (options.isCascadeInsert)
this._isCascadeInsert = options.isCascadeInsert;
if (options.isCascadeUpdate)
this._isCascadeUpdate = options.isCascadeUpdate;
if (options.isCascadeRemove)
this._isCascadeRemove = options.isCascadeRemove;
if (options.cascadeInsert)
this._isCascadeInsert = options.cascadeInsert;
if (options.cascadeUpdate)
this._isCascadeUpdate = options.cascadeUpdate;
if (options.cascadeRemove)
this._isCascadeRemove = options.cascadeRemove;
if (options.oldColumnName)
this._oldColumnName = options.oldColumnName;
if (options.nullable)

View File

@ -8,17 +8,17 @@ export interface RelationOptions {
/**
* If set to true then it means that related object can be allowed to be inserted to the db.
*/
isCascadeInsert?: boolean;
cascadeInsert?: boolean;
/**
* If set to true then it means that related object can be allowed to be updated in the db.
*/
isCascadeUpdate?: boolean;
cascadeUpdate?: boolean;
/**
* If set to true then it means that related object can be allowed to be remove from the db.
*/
isCascadeRemove?: boolean;
cascadeRemove?: boolean;
/**
* Old column name. Used to make safe schema updates.

View File

@ -25,7 +25,7 @@ export class QueryBuilder<Entity> {
private wheres: { type: "simple"|"and"|"or", condition: string }[] = [];
private havings: { type: "simple"|"and"|"or", condition: string }[] = [];
private orderBys: { sort: string, order: "ASC"|"DESC" }[] = [];
private parameters: { [key: string]: string } = {};
private parameters: { [key: string]: any } = {};
private limit: number;
private offset: number;
@ -142,18 +142,21 @@ export class QueryBuilder<Entity> {
return this;
}
where(where: string): this {
where(where: string, parameters?: { [key: string]: any }): this {
this.wheres.push({ type: "simple", condition: where });
if (parameters) this.addParameters(parameters);
return this;
}
andWhere(where: string): this {
andWhere(where: string, parameters?: { [key: string]: any }): this {
this.wheres.push({ type: "and", condition: where });
if (parameters) this.addParameters(parameters);
return this;
}
orWhere(where: string): this {
orWhere(where: string, parameters?: { [key: string]: any }): this {
this.wheres.push({ type: "or", condition: where });
if (parameters) this.addParameters(parameters);
return this;
}
@ -207,8 +210,13 @@ export class QueryBuilder<Entity> {
return this;
}
setParameters(parameters: Object): this {
Object.keys(parameters).forEach(key => this.parameters[key] = (<any> parameters)[key]);
setParameters(parameters: { [key: string]: any }): this {
this.parameters = parameters;
return this;
}
addParameters(parameters: { [key: string]: any }): this {
Object.keys(parameters).forEach(key => this.parameters[key] = parameters[key]);
return this;
}
@ -339,11 +347,11 @@ export class QueryBuilder<Entity> {
return " " + joinType + " JOIN " + junctionTable + " " + junctionAlias + " " + join.conditionType + " " + condition1 +
" " + joinType + " JOIN " + joinTable + " " + joinAlias + " " + join.conditionType + " " + condition2 + appendedCondition;
} else if (relation.isOneToOne || relation.isManyToOne) {
} else if (relation.isManyToOne || (relation.isOneToOne && relation.isOwning)) {
const condition = join.alias.name + "." + joinTableColumn + "=" + parentAlias + "." + join.alias.parentPropertyName;
return " " + joinType + " JOIN " + joinTable + " " + join.alias.name + " " + join.conditionType + " " + condition + appendedCondition;
} else if (relation.isOneToMany) {
} else if (relation.isOneToMany || (relation.isOneToOne && !relation.isOwning)) {
const condition = join.alias.name + "." + relation.inverseSideProperty + "=" + parentAlias + "." + joinTableColumn;
return " " + joinType + " JOIN " + joinTable + " " + join.alias.name + " " + join.conditionType + " " + condition + appendedCondition;

View File

@ -55,6 +55,9 @@ export class AliasMap {
const parentAlias = this.findAliasByName(alias.parentAliasName); // todo: throw exceptions everywhere
const parentEntityMetadata = this.getEntityMetadataByAlias(parentAlias);
const relation = parentEntityMetadata.findRelationWithDbName(alias.parentPropertyName);
if (!relation)
throw new Error("Related entity metadata was not found.");
return relation.relatedEntityMetadata;
}

View File

@ -91,6 +91,8 @@ export class EntityPersistOperationsBuilder {
// if relation has "insert" it can insert a new entity
// if relation has "update" it can only update related entity
// if relation has "remove" it can only remove related entity
private strictCascadesMode = false;
constructor(private connection: Connection) {
}
@ -329,8 +331,13 @@ export class EntityPersistOperationsBuilder {
return dbEntity.id === newEntity[metadata.primaryColumn.name] && dbEntity.entity.constructor.name === metadata.name;
});
if (isObjectNew && fromRelation && !fromRelation.isCascadeInsert)
throw new Error("Cascade inserts are not allowed in " + metadata.name + "#" + fromRelation.propertyName);
if (isObjectNew && fromRelation && !fromRelation.isCascadeInsert) {
if (this.strictCascadesMode) {
throw new Error("Cascade inserts are not allowed in " + metadata.name + "#" + fromRelation.propertyName);
} else {
return [];
}
}
if (isObjectNew)
insertedEntities.push({
@ -422,8 +429,13 @@ export class EntityPersistOperationsBuilder {
const updatedEntities: any[] = [];
const diff = this.diffColumns(metadata, newEntity, dbEntity);
if (diff.length && fromRelation && !fromRelation.isCascadeUpdate)
throw new Error("Cascade updates are not allowed in " + metadata.name + "#" + fromRelation.propertyName);
if (diff.length && fromRelation && !fromRelation.isCascadeUpdate) {
if (this.strictCascadesMode) {
throw new Error("Cascade updates are not allowed in " + metadata.name + "#" + fromRelation.propertyName);
} else {
return [];
}
}
if (diff.length) {
updatedEntities.push({

View File

@ -244,8 +244,19 @@ export class Repository<Entity> {
.map(column => "'" + entity[column.propertyName] + "'");
const allColumns = columns.concat(virtualColumns);
const allVolumes = values.concat(virtualValues);*/
const relationColumns = metadata.relations
.filter(relation => relation.isOwning && !!relation.relatedEntityMetadata)
.filter(relation => entity.hasOwnProperty(relation.propertyName))
.filter(relation => entity[relation.propertyName][relation.relatedEntityMetadata.primaryColumn.name])
.map(relation => relation.name);
const query = `INSERT INTO ${metadata.table.name}(${columns.join(",")}) VALUES (${values.join(",")})`;
const relationValues = metadata.relations
.filter(relation => relation.isOwning && !!relation.relatedEntityMetadata)
.filter(relation => entity.hasOwnProperty(relation.propertyName))
.filter(relation => entity[relation.propertyName].hasOwnProperty(relation.relatedEntityMetadata.primaryColumn.name))
.map(relation => "'" + entity[relation.propertyName][relation.relatedEntityMetadata.primaryColumn.name] + "'");
const query = `INSERT INTO ${metadata.table.name}(${columns.concat(relationColumns).join(",")}) VALUES (${values.concat(relationValues).join(",")})`;
return this.connection.driver.query(query);
}

View File

@ -7,6 +7,11 @@ import {Repository} from "../../src/repository/Repository";
import {SchemaCreator} from "../../src/schema-creator/SchemaCreator";
import {PostDetails} from "../../sample/sample2-one-to-one/entity/PostDetails";
import {Post} from "../../sample/sample2-one-to-one/entity/Post";
import {PostCategory} from "../../sample/sample2-one-to-one/entity/PostCategory";
import {PostAuthor} from "../../sample/sample2-one-to-one/entity/PostAuthor";
import {PostMetadata} from "../../sample/sample2-one-to-one/entity/PostMetadata";
import {PostImage} from "../../sample/sample2-one-to-one/entity/PostImage";
import {PostInformation} from "../../sample/sample2-one-to-one/entity/PostInformation";
chai.should();
describe("insertion", function() {
@ -27,7 +32,7 @@ describe("insertion", function() {
// connect to db
let connection: Connection;
before(function() {
return TypeORM.createMysqlConnection(options, [Post, PostDetails]).then(conn => {
return TypeORM.createMysqlConnection(options, [Post, PostDetails, PostCategory, PostMetadata, PostImage, PostInformation, PostAuthor]).then(conn => {
connection = conn;
}).catch(e => console.log("Error during connection to db: " + e));
});
@ -44,22 +49,26 @@ describe("insertion", function() {
}
let postRepository: Repository<Post>,
postDetailsRepository: Repository<PostDetails>;
postDetailsRepository: Repository<PostDetails>,
postCategoryRepository: Repository<PostCategory>,
postImageRepository: Repository<PostImage>;
before(function() {
postRepository = connection.getRepository<Post>(Post);
postDetailsRepository = connection.getRepository<PostDetails>(PostDetails);
postCategoryRepository = connection.getRepository<PostCategory>(PostCategory);
postImageRepository = connection.getRepository<PostImage>(PostImage);
});
// -------------------------------------------------------------------------
// Specifications: persist
// Specifications
// -------------------------------------------------------------------------
describe("insert post and details", function() {
describe("insert post and details (has inverse relation + full cascade options)", function() {
let newPost: Post, details: PostDetails, savedPost: Post;
before(reloadDatabase);
beforeEach(function() {
before(function() {
details = new PostDetails();
details.authorName = "Umed";
details.comment = "this is post";
@ -86,64 +95,291 @@ describe("insertion", function() {
});
it("should have inserted post in the database", function() {
return postRepository.findById(savedPost.id).should.eventually.eql({
id: savedPost.id,
text: "Hello post",
title: "this is post title"
});
const expectedPost = new Post();
expectedPost.id = savedPost.id;
expectedPost.text = savedPost.text;
expectedPost.title = savedPost.title;
return postRepository.findById(savedPost.id).should.eventually.eql(expectedPost);
});
it("should have inserted post details in the database", function() {
return postDetailsRepository.findById(savedPost.details.id).should.eventually.eql({
id: savedPost.details.id,
authorName: "Umed",
comment: "this is post",
metadata: "post,posting,postman"
});
const expectedDetails = new PostDetails();
expectedDetails.id = savedPost.details.id;
expectedDetails.authorName = savedPost.details.authorName;
expectedDetails.comment = savedPost.details.comment;
expectedDetails.metadata = savedPost.details.metadata;
return postDetailsRepository.findById(savedPost.details.id).should.eventually.eql(expectedDetails);
});
it("should load post and its details if left join used", function() {
const expectedPost = new Post();
expectedPost.id = savedPost.id;
expectedPost.text = savedPost.text;
expectedPost.title = savedPost.title;
expectedPost.details = new PostDetails();
expectedPost.details.id = savedPost.details.id;
expectedPost.details.authorName = savedPost.details.authorName;
expectedPost.details.comment = savedPost.details.comment;
expectedPost.details.metadata = savedPost.details.metadata;
return postRepository
.createQueryBuilder("post")
.leftJoinAndSelect("post.details", "details")
.where("post.id=:id")
.setParameter("id", savedPost.id)
.getSingleResult()
.should.eventually.eql({
id: savedPost.id,
text: "Hello post",
title: "this is post title",
details: {
id: savedPost.details.id,
authorName: "Umed",
comment: "this is post",
metadata: "post,posting,postman"
}
});
.should.eventually.eql(expectedPost);
});
it("should load details and its post if left join used (from reverse side)", function() {
const expectedDetails = new PostDetails();
expectedDetails.id = savedPost.details.id;
expectedDetails.authorName = savedPost.details.authorName;
expectedDetails.comment = savedPost.details.comment;
expectedDetails.metadata = savedPost.details.metadata;
expectedDetails.post = new Post();
expectedDetails.post.id = savedPost.id;
expectedDetails.post.text = savedPost.text;
expectedDetails.post.title = savedPost.title;
return postDetailsRepository
.createQueryBuilder("details")
.leftJoinAndSelect("details.post", "post")
.where("details.id=:id")
.setParameter("id", savedPost.id)
.getSingleResult()
.should.eventually.eql({
id: savedPost.details.id,
authorName: "Umed",
comment: "this is post",
metadata: "post,posting,postman",
post: {
id: savedPost.id,
text: "Hello post",
title: "this is post title",
}
.should.eventually.eql(expectedDetails);
});
it("should load saved post without details if left joins are not specified", function() {
const expectedPost = new Post();
expectedPost.id = savedPost.id;
expectedPost.text = savedPost.text;
expectedPost.title = savedPost.title;
return postRepository
.createQueryBuilder("post")
.where("post.id=:id", { id: savedPost.id })
.getSingleResult()
.should.eventually.eql(expectedPost);
});
it("should load saved post without details if left joins are not specified", function() {
const expectedDetails = new PostDetails();
expectedDetails.id = savedPost.details.id;
expectedDetails.authorName = savedPost.details.authorName;
expectedDetails.comment = savedPost.details.comment;
expectedDetails.metadata = savedPost.details.metadata;
return postDetailsRepository
.createQueryBuilder("details")
.where("details.id=:id", { id: savedPost.id })
.getSingleResult()
.should.eventually.eql(expectedDetails);
});
});
describe("insert post and category (one-side relation)", function() {
let newPost: Post, category: PostCategory, savedPost: Post;
before(reloadDatabase);
before(function() {
category = new PostCategory();
category.name = "technology";
newPost = new Post();
newPost.text = "Hello post";
newPost.title = "this is post title";
newPost.category = category;
return postRepository.persist(newPost).then(post => savedPost = post);
});
it("should return the same post instance after its created", function () {
savedPost.should.be.equal(newPost);
});
it("should return the same post category instance after its created", function () {
savedPost.category.should.be.equal(newPost.category);
});
it("should have a new generated id after post is created", function () {
expect(savedPost.id).not.to.be.empty;
expect(savedPost.category.id).not.to.be.empty;
});
it("should have inserted post in the database", function() {
const expectedPost = new Post();
expectedPost.id = savedPost.id;
expectedPost.text = savedPost.text;
expectedPost.title = savedPost.title;
return postRepository.findById(savedPost.id).should.eventually.eql(expectedPost);
});
it("should have inserted category in the database", function() {
const expectedPost = new PostCategory();
expectedPost.id = savedPost.category.id;
expectedPost.name = "technology";
return postCategoryRepository.findById(savedPost.category.id).should.eventually.eql(expectedPost);
});
it("should load post and its category if left join used", function() {
const expectedPost = new Post();
expectedPost.id = savedPost.id;
expectedPost.title = savedPost.title;
expectedPost.text = savedPost.text;
expectedPost.category = new PostCategory();
expectedPost.category.id = savedPost.category.id;
expectedPost.category.name = savedPost.category.name;
return postRepository
.createQueryBuilder("post")
.leftJoinAndSelect("post.category", "category")
.where("post.id=:id", { id: savedPost.id })
.getSingleResult()
.should.eventually.eql(expectedPost);
});
it("should load details and its post if left join used (from reverse side)", function() {
// later need to specify with what exception we reject it
/*return postCategoryRepository
.createQueryBuilder("category")
.leftJoinAndSelect("category.post", "post")
.where("category.id=:id", { id: savedPost.id })
.getSingleResult()
.should.be.rejectedWith(Error);*/ // not working, find fix
});
});
describe("cascade updates should not be executed when cascadeUpdate option is not set", function() {
let newPost: Post, details: PostDetails, savedPost: Post;
before(reloadDatabase);
before(function() {
details = new PostDetails();
details.authorName = "Umed";
details.comment = "this is post";
details.metadata = "post,posting,postman";
newPost = new Post();
newPost.text = "Hello post";
newPost.title = "this is post title";
newPost.details = details;
return postRepository
.persist(newPost)
.then(post => savedPost = post);
});
it("should ignore updates in the model and do not update the db when entity is updated", function () {
newPost.details.comment = "i am updated comment";
return postRepository.persist(newPost).then(updatedPost => {
updatedPost.details.comment.should.be.equal("i am updated comment");
return postRepository
.createQueryBuilder("post")
.leftJoinAndSelect("post.details", "details")
.where("post.id=:id")
.setParameter("id", updatedPost.id)
.getSingleResult()
}).then(updatedPostReloaded => {
updatedPostReloaded.details.comment.should.be.equal("this is post");
});
}); // todo: also check that updates throw exception in strict cascades mode
});
describe("cascade remove should not be executed when cascadeRemove option is not set", function() {
let newPost: Post, details: PostDetails, savedPost: Post;
before(reloadDatabase);
before(function() {
details = new PostDetails();
details.authorName = "Umed";
details.comment = "this is post";
details.metadata = "post,posting,postman";
newPost = new Post();
newPost.text = "Hello post";
newPost.title = "this is post title";
newPost.details = details;
return postRepository
.persist(newPost)
.then(post => savedPost = post);
});
it("should ignore updates in the model and do not update the db when entity is updated", function () {
newPost.details = null;
return postRepository.persist(newPost).then(updatedPost => {
return postRepository
.createQueryBuilder("post")
.leftJoinAndSelect("post.details", "details")
.where("post.id=:id")
.setParameter("id", updatedPost.id)
.getSingleResult()
}).then(updatedPostReloaded => {
updatedPostReloaded.details.comment.should.be.equal("this is post");
});
});
});
describe("cascade updates should be executed when cascadeUpdate option is set", function() {
let newPost: Post, newImage: PostImage, savedPost: Post, savedImage: PostImage;
before(reloadDatabase);
it("should ignore updates in the model and do not update the db when entity is updated", function () {
newImage = new PostImage();
newImage.url = "logo.png";
newPost = new Post();
newPost.text = "Hello post";
newPost.title = "this is post title";
return postImageRepository
.persist(newImage)
.then(image => {
savedImage = image;
newPost.image = image;
return postRepository.persist(newPost);
}).then(post => {
newPost = post;
return postRepository
.createQueryBuilder("post")
.leftJoinAndSelect("post.image", "image")
.where("post.id=:id")
.setParameter("id", post.id)
.getSingleResult();
}).then(loadedPost => {
loadedPost.image.url = "new-logo.png";
return postRepository.persist(loadedPost);
}).then(() => {
return postRepository
.createQueryBuilder("post")
.leftJoinAndSelect("post.image", "image")
.where("post.id=:id")
.setParameter("id", newPost.id)
.getSingleResult();
}).then(reloadedPost => {
reloadedPost.image.url.should.be.equal("new-logo.png");
});
});
});
// todo: insert objects with different data types: boolean, dates etc.
});

View File

@ -5,6 +5,7 @@
"chai-as-promised": "github:DefinitelyTyped/DefinitelyTyped/chai-as-promised/chai-as-promised.d.ts#d6e3f732183ca0ee4c4b438323253b384f5b4091",
"mocha": "github:DefinitelyTyped/DefinitelyTyped/mocha/mocha.d.ts#7a3ca1f0b8a0960af9fc1838f3234cc9d6ce0645",
"mockery": "github:DefinitelyTyped/DefinitelyTyped/mockery/mockery.d.ts#6f6e5c7dd9effe21fee14eb65fe340ecbbc8580a",
"promises-a-plus": "github:DefinitelyTyped/DefinitelyTyped/promises-a-plus/promises-a-plus.d.ts#56068d3354648384ff32db20b6fcda4262856f33",
"sinon": "github:DefinitelyTyped/DefinitelyTyped/sinon/sinon.d.ts#7a3ca1f0b8a0960af9fc1838f3234cc9d6ce0645"
},
"ambientDependencies": {