From 804a92eeecf6f8d787b30bbba7a92ce85bf1b738 Mon Sep 17 00:00:00 2001 From: Igor Terzic Date: Thu, 5 Aug 2021 13:13:10 -0700 Subject: [PATCH] Return already inflight requests for findRecord when CUSTOM_MODEL_CLASS is on --- .../tests/integration/store-test.js | 50 +++++++++++++++++++ .../store/addon/-private/system/core-store.ts | 15 +++--- .../addon/-private/system/fetch-manager.ts | 14 +++++- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/packages/-ember-data/tests/integration/store-test.js b/packages/-ember-data/tests/integration/store-test.js index 8bb14d4ed09..fed16fa8e50 100644 --- a/packages/-ember-data/tests/integration/store-test.js +++ b/packages/-ember-data/tests/integration/store-test.js @@ -443,6 +443,56 @@ module('integration/store - findRecord', function (hooks) { assert.strictEqual(car.get('model'), 'Princess', 'cached record ignored, record reloaded via server'); }); + test('store#findRecord caches the inflight requests', async function (assert) { + assert.expect(2); + + let calls = 0; + let resolveHandler; + let result = { + data: { + type: 'car', + id: '1', + attributes: { + make: 'BMC', + model: 'Mini', + }, + }, + }; + + const testAdapter = DS.JSONAPIAdapter.extend({ + shouldReloadRecord(store, type, id, snapshot) { + assert.ok(false, 'shouldReloadRecord should not be called when { reload: true }'); + }, + async findRecord() { + calls++; + + return new Promise((resolve) => { + resolveHandler = resolve; + }); + }, + }); + + this.owner.register('adapter:application', testAdapter); + this.owner.register('serializer:application', JSONAPISerializer.extend()); + let firstPromise, secondPromise; + + run(() => { + firstPromise = store.findRecord('car', '1'); + }); + + run(() => { + secondPromise = store.findRecord('car', '1'); + }); + + assert.strictEqual(calls, 1, 'We made one call to findRecord'); + + resolveHandler(result); + let car1 = await firstPromise; + let car2 = await secondPromise; + + assert.strictEqual(car1, car2, 'we receive the same car back'); + }); + test('store#findRecord { backgroundReload: false } returns cached record and does not reload in the background', async function (assert) { assert.expect(2); diff --git a/packages/store/addon/-private/system/core-store.ts b/packages/store/addon/-private/system/core-store.ts index 39b29f50c10..80fc4fea8a6 100644 --- a/packages/store/addon/-private/system/core-store.ts +++ b/packages/store/addon/-private/system/core-store.ts @@ -54,7 +54,6 @@ import RecordArrayManager from './record-array-manager'; import { setRecordDataFor } from './record-data-for'; import NotificationManager from './record-notification-manager'; import { RecordReference } from './references'; -import { RequestPromise } from './request-cache'; import { _bind, _guard, _objectIsAlive, guardDestroyedStore } from './store/common'; import { _find, _findAll, _findBelongsTo, _findHasMany, _findMany, _query, _queryRecord } from './store/finders'; import { internalModelFactoryFor, recordIdentifierFor, setRecordIdentifier } from './store/internal-model-factory'; @@ -1268,6 +1267,10 @@ abstract class CoreStore extends Service { } } else { if (internalModel.currentState.isLoading) { + let pending = this._fetchManager.getPendingFetch(internalModel.identifier); + if (pending) { + return pending.then(() => Promise.resolve(internalModel)); + } return this._scheduleFetch(internalModel, options); } } @@ -2012,13 +2015,9 @@ abstract class CoreStore extends Service { if (internalModel) { // short circuit if we are already loading if (REQUEST_SERVICE) { - // Temporary fix for requests already loading until we move this inside the fetch manager - let pendingRequests = this.getRequestStateService() - .getPendingRequestsForRecord(internalModel.identifier) - .filter((req) => req.type === 'query'); - - if (pendingRequests.length > 0) { - return pendingRequests[0][RequestPromise].then(() => internalModel.getRecord()); + let pendingRequest = this._fetchManager.getPendingFetch(internalModel.identifier); + if (pendingRequest) { + return pendingRequest.then(() => internalModel.getRecord()); } } else { if (internalModel.currentState.isLoading) { diff --git a/packages/store/addon/-private/system/fetch-manager.ts b/packages/store/addon/-private/system/fetch-manager.ts index ca0e5746371..566fcea9884 100644 --- a/packages/store/addon/-private/system/fetch-manager.ts +++ b/packages/store/addon/-private/system/fetch-manager.ts @@ -11,11 +11,13 @@ import { default as RSVP, Promise } from 'rsvp'; import { symbol } from '../utils/symbol'; import coerceId from './coerce-id'; import { errorsArrayToHash } from './errors-utils'; -import RequestCache from './request-cache'; +import RequestCache, { RequestPromise } from './request-cache'; import Snapshot from './snapshot'; import { _bind, _guard, _objectIsAlive, guardDestroyedStore } from './store/common'; import { normalizeResponseHelper } from './store/serializer-response'; +type StableRecordIdentifier = import('../ts-interfaces/identifier').StableRecordIdentifier; + type CoreStore = import('./core-store').default; type FindRecordQuery = import('../ts-interfaces/fetch-manager').FindRecordQuery; type SaveRecordMutation = import('../ts-interfaces/fetch-manager').SaveRecordMutation; @@ -507,6 +509,16 @@ export default class FetchManager { } } + getPendingFetch(identifier: StableRecordIdentifier) { + let pendingRequests = this.requestCache + .getPendingRequestsForRecord(identifier) + .filter((req) => req.type === 'query'); + + if (pendingRequests.length > 0) { + return pendingRequests[0][RequestPromise]; + } + } + flushAllPendingFetches() { if (this.isDestroyed) { return;