From 9e39629a4f02525b86c183c24ca11097e14d525a Mon Sep 17 00:00:00 2001 From: "David J. Hamilton" Date: Wed, 1 Nov 2017 19:53:07 -0700 Subject: [PATCH] [BUGFIX beta] Fix client-side delete + resurrection Previously relationships were only cleared for new records. Now they are cleared for destroyed records as well, allowing destroyed records to be pushed back into the store and properly update their inverse relationships. This fixes the public API for client-side delete via an adapter, see #5136. --- addon/-private/system/model/internal-model.js | 16 +--- .../adapter/client-side-delete-test.js | 88 +++++++++++++++++++ 2 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 tests/integration/adapter/client-side-delete-test.js diff --git a/addon/-private/system/model/internal-model.js b/addon/-private/system/model/internal-model.js index 3a1036e3a88..805331e1c97 100644 --- a/addon/-private/system/model/internal-model.js +++ b/addon/-private/system/model/internal-model.js @@ -772,7 +772,7 @@ export default class InternalModel { } if (this.isNew()) { - this.removeFromInverseRelationships(true); + this.removeFromInverseRelationships(); } if (this.isValid()) { @@ -891,19 +891,13 @@ export default class InternalModel { It will remove this record from any associated relationships. - If `isNew` is true (default false), it will also completely reset all - relationships to an empty state as well. - @method removeFromInverseRelationships - @param {Boolean} isNew whether to unload from the `isNew` perspective @private */ - removeFromInverseRelationships(isNew = false) { + removeFromInverseRelationships() { this._relationships.forEach((name, rel) => { rel.removeCompletelyFromInverse(); - if (isNew === true) { - rel.clear(); - } + rel.clear(); }); let implicitRelationships = this._implicitRelationships; @@ -913,9 +907,7 @@ export default class InternalModel { let rel = implicitRelationships[key]; rel.removeCompletelyFromInverse(); - if (isNew === true) { - rel.clear(); - } + rel.clear(); }); } diff --git a/tests/integration/adapter/client-side-delete-test.js b/tests/integration/adapter/client-side-delete-test.js new file mode 100644 index 00000000000..38693df8dd5 --- /dev/null +++ b/tests/integration/adapter/client-side-delete-test.js @@ -0,0 +1,88 @@ +import { resolve } from 'rsvp'; +import { run } from '@ember/runloop'; +import setupStore from 'dummy/tests/helpers/store'; + +import { module, test } from 'qunit'; + +import DS from 'ember-data'; + +module('integration/adapter/store-adapter - client-side delete', { + beforeEach() { + this.Bookstore = DS.Model.extend({ + books: DS.hasMany('book', { async: false, inverse: 'bookstore' }) + }); + this.Book = DS.Model.extend({ + bookstore: DS.belongsTo('bookstore', { inverse: 'books' }) + }); + + this.env = setupStore({ + bookstore: this.Bookstore, + book: this.Book + }); + this.store = this.env.store; + this.adapter = this.env.adapter; + }, + + afterEach() { + run(this.env.container, 'destroy'); + } +}); + +test('client-side deleted records can be added back from an inverse', function(assert) { + this.adapter.deleteRecord = function (store, modelClass, snapshot) { + if (snapshot.adapterOptions.clientSideDelete) { + return resolve(); + } + + assert.ok(false, 'unreachable'); + } + + run(() => this.store.push({ + data: { + id: '1', + type: 'bookstore', + relationships: { + books: { + data: [{ + id: '1', type: 'book' + }, { + id: '2', type: 'book' + }] + } + } + }, included: [{ + id: '1', + type: 'book' + }, { + id: '2', + type: 'book' + }] + })); + + let bookstore = this.store.peekRecord('bookstore', '1'); + assert.deepEqual(bookstore.get('books').mapBy('id'), ['1', '2'], 'initial hasmany loaded'); + + let book2 = this.store.peekRecord('book', '2'); + return run(() => book2.destroyRecord({ adapterOptions: { clientSideDelete: true } })).then(() => { + run(() => book2.unloadRecord()); + assert.equal(this.store.hasRecordForId('book', '2'), false, 'book 2 unloaded'); + assert.deepEqual(bookstore.get('books').mapBy('id'), ['1'], 'one book client-side deleted'); + + run(() => this.store.push({ + data: { + id: '2', + type: 'book', + relationships: { + bookstore: { + data: { + id: '1', + type: 'bookstore' + } + } + } + } + })); + + assert.deepEqual(bookstore.get('books').mapBy('id'), ['1', '2'], 'the deleted book (with same id) is pushed back into the store'); + }); +});