diff --git a/packages/legacy-compat/src/legacy-network-handler/snapshot.ts b/packages/legacy-compat/src/legacy-network-handler/snapshot.ts index d8934e36795..5e9a97c09e5 100644 --- a/packages/legacy-compat/src/legacy-network-handler/snapshot.ts +++ b/packages/legacy-compat/src/legacy-network-handler/snapshot.ts @@ -148,8 +148,13 @@ export default class Snapshot implements Snapshot { @type {Model} @public */ - get record(): RecordInstance { - return this._store._instanceCache.getRecord(this.identifier); + get record(): RecordInstance | null { + const record = this._store.peekRecord(this.identifier); + assert( + `Record ${this.identifier.type} ${this.identifier.id} (${this.identifier.lid}) is not yet loaded and thus cannot be accessed from the Snapshot during serialization`, + record !== null + ); + return record; } get _attributes(): Record { diff --git a/tests/main/tests/integration/relationships/has-many-test.js b/tests/main/tests/integration/relationships/has-many-test.js index d4785a1e2ec..d1115eeacf6 100644 --- a/tests/main/tests/integration/relationships/has-many-test.js +++ b/tests/main/tests/integration/relationships/has-many-test.js @@ -191,6 +191,64 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, /Assertion Failed: Encountered a relationship identifier without a type for the hasMany relationship 'comments' on , expected an identifier with type 'comment' but found/); }); + test('A record with an async hasMany relationship can safely be saved and later access the relationship', async function (assert) { + const store = this.owner.lookup('service:store'); + this.owner.register( + 'adapter:application', + class extends JSONAPIAdapter { + updateRecord(store, schema, snapshot) { + assert.step('updateRecord'); + const data = store.serializerFor(schema.modelName).serialize(snapshot, { includeId: true }); + + return Promise.resolve(data); + } + findRecord(store, schema, id, snapshot) { + assert.step('findRecord'); + return Promise.resolve({ + data: { id, type: 'chapter', attributes: { title: `Chapter ${id}` } }, + }); + } + } + ); + this.owner.register( + 'serializer:application', + class extends JSONAPISerializer { + serialize(snapshot, options) { + assert.step('serialize'); + return super.serialize(snapshot, options); + } + } + ); + const book = store.push({ + data: { + type: 'book', + id: '1', + relationships: { + chapters: { + data: [ + { type: 'chapter', id: '1' }, + { type: 'chapter', id: '2' }, + ], + }, + }, + }, + }); + book.title = 'The Book of Foo'; + + await book.save(); + + assert.verifySteps(['updateRecord', 'serialize']); + + const chapters = await book.chapters; + + assert.verifySteps(['findRecord', 'findRecord']); + assert.strictEqual(chapters.length, 2); + assert.deepEqual( + chapters.map((v) => v.id), + ['1', '2'] + ); + }); + test("When a hasMany relationship is accessed, the adapter's findMany method should not be called if all the records in the relationship are already loaded", async function (assert) { assert.expect(1);