From 872f015bdc842cceb2aab113f85123d147a20c7c Mon Sep 17 00:00:00 2001 From: "David J. Hamilton" Date: Tue, 13 Mar 2018 17:39:31 -0700 Subject: [PATCH] [bugfix beta] invalidate link promise on inverse unload (#5377) * unload should invalidate async hasMany * observing a hasMany and reading it immediately triggers an error * [bugfix beta] Invalidate link on inverse unload Invalidates link promises when inverse records are unloaded. Subsequent fetches of a `hasMany` unloaded in this way will load from the link again, rather than loading whatever ids were returned from the prior link load. [fix #5354] --- .../relationships/state/relationship.js | 1 + tests/integration/records/unload-test.js | 81 +++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/addon/-private/system/relationships/state/relationship.js b/addon/-private/system/relationships/state/relationship.js index 5891d8ed8b3..d6c97f5e7b9 100644 --- a/addon/-private/system/relationships/state/relationship.js +++ b/addon/-private/system/relationships/state/relationship.js @@ -130,6 +130,7 @@ export default class Relationship { } inverseDidDematerialize(inverseInternalModel) { + this.linkPromise = null; if (!this.isAsync) { // unloading inverse of a sync relationship is treated as a client-side // delete, so actually remove the models don't merely invalidate the cp diff --git a/tests/integration/records/unload-test.js b/tests/integration/records/unload-test.js index 3afc287f901..34e98b168cc 100644 --- a/tests/integration/records/unload-test.js +++ b/tests/integration/records/unload-test.js @@ -2008,3 +2008,84 @@ test('1 sync : many async unload sync side', function(assert) { }) ); }); + +test('unload invalidates link promises', function(assert) { + let isUnloaded = false; + env.adapter.coalesceFindRequests = false; + + env.adapter.findRecord = (/* store, type, id */) => { + assert.notOk('Records only expected to be loaded via link'); + }; + + env.adapter.findHasMany = (store, snapshot, link) => { + assert.equal(snapshot.modelName, 'person', 'findHasMany(_, snapshot) is correct'); + assert.equal(link, 'boats', 'findHasMany(_, _, link) is correct'); + + let relationships = { + person: { + data: { + type: 'person', + id: 1 + } + } + }; + + let data = [ + { + id: 3, + type: 'boat', + relationships + } + ]; + + if (!isUnloaded) { + data.unshift({ + id: 2, + type: 'boat', + relationships + }); + } + + return { + data + }; + }; + + let person = run(() => + env.store.push({ + data: { + id: 1, + type: 'person', + relationships: { + boats: { + links: { related: 'boats' } + } + } + } + }) + ); + let boats, boat2, boat3; + + return run(() => + person.get('boats').then((asyncRecords) => { + boats = asyncRecords; + [boat2, boat3] = boats.toArray(); + }).then(() => { + assert.deepEqual(person.hasMany('boats').ids(), ['2', '3'], 'initially relationship established rhs'); + assert.equal(boat2.belongsTo('person').id(), '1', 'initially relationship established rhs'); + assert.equal(boat3.belongsTo('person').id(), '1', 'initially relationship established rhs'); + + isUnloaded = true; + run(() => { + boat2.unloadRecord(); + person.get('boats'); + }); + + assert.deepEqual(boats.mapBy('id'), ['3'], 'unloaded boat is removed from ManyArray'); + }).then(() => { + return run(() => person.get('boats')); + }).then(newBoats => { + assert.equal(newBoats.length, 1, 'new ManyArray has only 1 boat after unload'); + }) + ); +});