Skip to content

Commit

Permalink
Fix deleting belongs to relations
Browse files Browse the repository at this point in the history
  • Loading branch information
Ricardo Gama committed Jul 13, 2017
1 parent 0a2efa4 commit 52f8ad6
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 28 deletions.
9 changes: 6 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,21 @@ export default Bookshelf => {
* Dependency map.
*/

function dependencyMap(skipDependents = false) {
if (skipDependents || !this.dependents) {
function dependencyMap() {
if (!this.dependents) {
return;
}

return reduce(this.dependents, (result, dependent) => {
const { relatedData } = this.prototype[dependent]();
const skipDependents = relatedData.type === 'belongsToMany';

if (relatedData.type === 'belongsTo') {
return result;
}

return [
...result, {
dependents: dependencyMap.call(relatedData.target, skipDependents),
key: relatedData.key('foreignKey'),
model: relatedData.target,
skipDependents,
Expand Down
43 changes: 35 additions & 8 deletions test/mysql/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe('with MySQL client', () => {

repository.plugin(cascadeDelete);

const { Account, Author, Comment, Commenter, Post, Tag, TagPost } = fixtures(repository);
const { Account, Author, Comment, CommentMetadata, Commenter, Post, Tag, TagPost } = fixtures(repository);

before(async () => {
await recreateTables(repository);
Expand Down Expand Up @@ -66,10 +66,13 @@ describe('with MySQL client', () => {
it('should not delete model and its dependents if an error is thrown on destroy', async () => {
const author = await Author.forge().save();
const post = await Post.forge().save({ authorId: author.get('author_id') });
const comment = await Comment.forge().save({ postId: post.get('post_id') });
const commentMetadata = await CommentMetadata.forge().save();
const comment = await Comment.forge().save({ metadata: commentMetadata.id, postId: post.get('post_id') });
const tag = await Tag.forge().save();

await Account.forge().save({ authorId: author.get('author_id') });
await Commenter.forge().save({ commentId: comment.get('comment_id') });
await TagPost.forge().save({ postId: post.get('post_id'), tagId: tag.get('tag_id') });

sinon.stub(Model, 'destroy').throws(new Error('foobar'));

Expand All @@ -85,13 +88,19 @@ describe('with MySQL client', () => {
const authors = await Author.fetchAll();
const commenters = await Commenter.fetchAll();
const comments = await Comment.fetchAll();
const commentsMetadata = await CommentMetadata.fetchAll();
const posts = await Post.fetchAll();
const tagPosts = await TagPost.fetchAll();
const tags = await Tag.fetchAll();

accounts.length.should.equal(1);
authors.length.should.equal(1);
commenters.length.should.equal(1);
comments.length.should.equal(1);
commentsMetadata.length.should.equal(1);
posts.length.should.equal(1);
tagPosts.length.should.equal(1);
tags.length.should.equal(1);

sinon.restore(Model);
});
Expand Down Expand Up @@ -121,8 +130,10 @@ describe('with MySQL client', () => {
const author = await Author.forge().save();
const post1 = await Post.forge().save({ authorId: author.get('author_id') });
const post2 = await Post.forge().save({ authorId: author.get('author_id') });
const comment1 = await Comment.forge().save({ postId: post1.get('post_id') });
const comment2 = await Comment.forge().save({ postId: post2.get('post_id') });
const commentMetadata1 = await CommentMetadata.forge().save();
const commentMetadata2 = await CommentMetadata.forge().save();
const comment1 = await Comment.forge().save({ metadata: commentMetadata1.get('id'), postId: post1.get('post_id'), });
const comment2 = await Comment.forge().save({ metadata: commentMetadata2.get('id'), postId: post2.get('post_id') });
const tag1 = await Tag.forge().save();
const tag2 = await Tag.forge().save();

Expand All @@ -138,23 +149,29 @@ describe('with MySQL client', () => {
const authors = await Author.fetchAll();
const commenters = await Commenter.fetchAll();
const comments = await Comment.fetchAll();
const commentsMetadata = await CommentMetadata.fetchAll();
const posts = await Post.fetchAll();
const tagPosts = await TagPost.fetchAll();
const tags = await Tag.fetchAll();

accounts.length.should.equal(0);
authors.length.should.equal(0);
commenters.length.should.equal(0);
comments.length.should.equal(0);
commentsMetadata.length.should.equal(2);
posts.length.should.equal(0);
tagPosts.length.should.equal(0);
tags.length.should.equal(2);
});

it('should delete queried model and all its dependents', async () => {
const author = await Author.forge().save({ name: 'foobar' });
const post1 = await Post.forge().save({ authorId: author.get('author_id') });
const post2 = await Post.forge().save({ authorId: author.get('author_id') });
const comment1 = await Comment.forge().save({ postId: post1.get('post_id') });
const comment2 = await Comment.forge().save({ postId: post2.get('post_id') });
const commentMetadata1 = await CommentMetadata.forge().save();
const commentMetadata2 = await CommentMetadata.forge().save();
const comment1 = await Comment.forge().save({ metadata: commentMetadata1.get('id'), postId: post1.get('post_id'), });
const comment2 = await Comment.forge().save({ metadata: commentMetadata2.get('id'), postId: post2.get('post_id') });
const tag1 = await Tag.forge().save();
const tag2 = await Tag.forge().save();

Expand All @@ -170,24 +187,30 @@ describe('with MySQL client', () => {
const authors = await Author.fetchAll();
const commenters = await Commenter.fetchAll();
const comments = await Comment.fetchAll();
const commentsMetadata = await CommentMetadata.fetchAll();
const posts = await Post.fetchAll();
const tagPosts = await TagPost.fetchAll();
const tags = await Tag.fetchAll();

accounts.length.should.equal(0);
authors.length.should.equal(0);
commenters.length.should.equal(0);
comments.length.should.equal(0);
commentsMetadata.length.should.equal(2);
posts.length.should.equal(0);
tagPosts.length.should.equal(0);
tags.length.should.equal(2);
});

it('should not delete models which are not dependent', async () => {
const author1 = await Author.forge().save();
const author2 = await Author.forge().save();
const post1 = await Post.forge().save({ authorId: author1.get('author_id') });
const post2 = await Post.forge().save({ authorId: author2.get('author_id') });
const comment1 = await Comment.forge().save({ postId: post1.get('post_id') });
const comment2 = await Comment.forge().save({ postId: post2.get('post_id') });
const commentMetadata1 = await CommentMetadata.forge().save();
const commentMetadata2 = await CommentMetadata.forge().save();
const comment1 = await Comment.forge().save({ metadata: commentMetadata1.id, postId: post1.get('post_id') });
const comment2 = await Comment.forge().save({ metadata: commentMetadata2.id, postId: post2.get('post_id') });
const tag1 = await Tag.forge().save();
const tag2 = await Tag.forge().save();

Expand All @@ -204,15 +227,19 @@ describe('with MySQL client', () => {
const authors = await Author.fetchAll();
const commenters = await Commenter.fetchAll();
const comments = await Comment.fetchAll();
const commentsMetadata = await CommentMetadata.fetchAll();
const posts = await Post.fetchAll();
const tagPosts = await TagPost.fetchAll();
const tags = await Tag.fetchAll();

accounts.length.should.equal(1);
authors.length.should.equal(1);
commenters.length.should.equal(1);
comments.length.should.equal(1);
commentsMetadata.length.should.equal(2);
posts.length.should.equal(1);
tagPosts.length.should.equal(1);
tags.length.should.equal(2);
});

it('should call prototype method `destroy` with given `options`', async () => {
Expand Down
57 changes: 42 additions & 15 deletions test/postgres/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
* Module dependencies.
*/

import Bookshelf from 'bookshelf';
import cascadeDelete from '../../src';
import knex from 'knex';
import knexfile from './knexfile';
import should from 'should';
import sinon from 'sinon';
import { clearTables, dropTables, fixtures, recreateTables } from '../utils';
import Bookshelf from 'bookshelf';
import cascadeDelete from '../../src';
import knex from 'knex';
import knexfile from './knexfile';
import should from 'should';
import sinon from 'sinon';
import { clearTables, dropTables, fixtures, recreateTables } from '../utils';

/**
* Test `bookshelf-cascade-delete` plugin with PostgreSQL client.
Expand All @@ -21,7 +21,7 @@ describe('with PostgreSQL client', () => {

repository.plugin(cascadeDelete);

const { Account, Author, Comment, Commenter, Post, Tag, TagPost } = fixtures(repository);
const { Account, Author, Comment, CommentMetadata, Commenter, Post, Tag, TagPost } = fixtures(repository);

before(async () => {
await recreateTables(repository);
Expand Down Expand Up @@ -66,10 +66,13 @@ describe('with PostgreSQL client', () => {
it('should not delete model and its dependents if an error is thrown on destroy', async () => {
const author = await Author.forge().save();
const post = await Post.forge().save({ authorId: author.get('author_id') });
const comment = await Comment.forge().save({ postId: post.get('post_id') });
const commentMetadata = await CommentMetadata.forge().save();
const comment = await Comment.forge().save({ metadata: commentMetadata.id, postId: post.get('post_id') });
const tag = await Tag.forge().save();

await Account.forge().save({ authorId: author.get('author_id') });
await Commenter.forge().save({ commentId: comment.get('comment_id') });
await TagPost.forge().save({ postId: post.get('post_id'), tagId: tag.get('tag_id') });

sinon.stub(Model, 'destroy').throws(new Error('foobar'));

Expand All @@ -85,13 +88,19 @@ describe('with PostgreSQL client', () => {
const authors = await Author.fetchAll();
const commenters = await Commenter.fetchAll();
const comments = await Comment.fetchAll();
const commentsMetadata = await CommentMetadata.fetchAll();
const posts = await Post.fetchAll();
const tagPosts = await TagPost.fetchAll();
const tags = await Tag.fetchAll();

accounts.length.should.equal(1);
authors.length.should.equal(1);
commenters.length.should.equal(1);
comments.length.should.equal(1);
commentsMetadata.length.should.equal(1);
posts.length.should.equal(1);
tagPosts.length.should.equal(1);
tags.length.should.equal(1);

sinon.restore(Model);
});
Expand Down Expand Up @@ -121,8 +130,10 @@ describe('with PostgreSQL client', () => {
const author = await Author.forge().save();
const post1 = await Post.forge().save({ authorId: author.get('author_id') });
const post2 = await Post.forge().save({ authorId: author.get('author_id') });
const comment1 = await Comment.forge().save({ postId: post1.get('post_id') });
const comment2 = await Comment.forge().save({ postId: post2.get('post_id') });
const commentMetadata1 = await CommentMetadata.forge().save();
const commentMetadata2 = await CommentMetadata.forge().save();
const comment1 = await Comment.forge().save({ metadata: commentMetadata1.get('id'), postId: post1.get('post_id'), });
const comment2 = await Comment.forge().save({ metadata: commentMetadata2.get('id'), postId: post2.get('post_id') });
const tag1 = await Tag.forge().save();
const tag2 = await Tag.forge().save();

Expand All @@ -138,23 +149,29 @@ describe('with PostgreSQL client', () => {
const authors = await Author.fetchAll();
const commenters = await Commenter.fetchAll();
const comments = await Comment.fetchAll();
const commentsMetadata = await CommentMetadata.fetchAll();
const posts = await Post.fetchAll();
const tagPosts = await TagPost.fetchAll();
const tags = await Tag.fetchAll();

accounts.length.should.equal(0);
authors.length.should.equal(0);
commenters.length.should.equal(0);
comments.length.should.equal(0);
commentsMetadata.length.should.equal(2);
posts.length.should.equal(0);
tagPosts.length.should.equal(0);
tags.length.should.equal(2);
});

it('should delete queried model and all its dependents', async () => {
const author = await Author.forge().save({ name: 'foobar' });
const post1 = await Post.forge().save({ authorId: author.get('author_id') });
const post2 = await Post.forge().save({ authorId: author.get('author_id') });
const comment1 = await Comment.forge().save({ postId: post1.get('post_id') });
const comment2 = await Comment.forge().save({ postId: post2.get('post_id') });
const commentMetadata1 = await CommentMetadata.forge().save();
const commentMetadata2 = await CommentMetadata.forge().save();
const comment1 = await Comment.forge().save({ metadata: commentMetadata1.get('id'), postId: post1.get('post_id'), });
const comment2 = await Comment.forge().save({ metadata: commentMetadata2.get('id'), postId: post2.get('post_id') });
const tag1 = await Tag.forge().save();
const tag2 = await Tag.forge().save();

Expand All @@ -170,24 +187,30 @@ describe('with PostgreSQL client', () => {
const authors = await Author.fetchAll();
const commenters = await Commenter.fetchAll();
const comments = await Comment.fetchAll();
const commentsMetadata = await CommentMetadata.fetchAll();
const posts = await Post.fetchAll();
const tagPosts = await TagPost.fetchAll();
const tags = await Tag.fetchAll();

accounts.length.should.equal(0);
authors.length.should.equal(0);
commenters.length.should.equal(0);
comments.length.should.equal(0);
commentsMetadata.length.should.equal(2);
posts.length.should.equal(0);
tagPosts.length.should.equal(0);
tags.length.should.equal(2);
});

it('should not delete models which are not dependent', async () => {
const author1 = await Author.forge().save();
const author2 = await Author.forge().save();
const post1 = await Post.forge().save({ authorId: author1.get('author_id') });
const post2 = await Post.forge().save({ authorId: author2.get('author_id') });
const comment1 = await Comment.forge().save({ postId: post1.get('post_id') });
const comment2 = await Comment.forge().save({ postId: post2.get('post_id') });
const commentMetadata1 = await CommentMetadata.forge().save();
const commentMetadata2 = await CommentMetadata.forge().save();
const comment1 = await Comment.forge().save({ metadata: commentMetadata1.id, postId: post1.get('post_id') });
const comment2 = await Comment.forge().save({ metadata: commentMetadata2.id, postId: post2.get('post_id') });
const tag1 = await Tag.forge().save();
const tag2 = await Tag.forge().save();

Expand All @@ -204,15 +227,19 @@ describe('with PostgreSQL client', () => {
const authors = await Author.fetchAll();
const commenters = await Commenter.fetchAll();
const comments = await Comment.fetchAll();
const commentsMetadata = await CommentMetadata.fetchAll();
const posts = await Post.fetchAll();
const tagPosts = await TagPost.fetchAll();
const tags = await Tag.fetchAll();

accounts.length.should.equal(1);
authors.length.should.equal(1);
commenters.length.should.equal(1);
comments.length.should.equal(1);
commentsMetadata.length.should.equal(2);
posts.length.should.equal(1);
tagPosts.length.should.equal(1);
tags.length.should.equal(2);
});

it('should call prototype method `destroy` with given `options`', async () => {
Expand Down
Loading

0 comments on commit 52f8ad6

Please sign in to comment.