Skip to content

Commit 9c843b6

Browse files
120109: Fixed BaseDataService not emitting when the request is too fast and the ResponsePending are not emitted
(cherry picked from commit 0f4d71e)
1 parent f47bf46 commit 9c843b6

File tree

2 files changed

+71
-31
lines changed

2 files changed

+71
-31
lines changed

src/app/core/data/base/base-data.service.spec.ts

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ describe('BaseDataService', () => {
6565
let selfLink;
6666
let linksToFollow;
6767
let testScheduler;
68+
let remoteDataTimestamp: number;
6869
let remoteDataMocks: { [responseType: string]: RemoteData<any> };
6970
let remoteDataPageMocks: { [responseType: string]: RemoteData<any> };
7071

@@ -85,7 +86,9 @@ describe('BaseDataService', () => {
8586
expect(actual).toEqual(expected);
8687
});
8788

88-
const timeStamp = new Date().getTime();
89+
// The response's lastUpdated equals the time of 60 seconds after the test started, ensuring they are not perceived
90+
// as cached values.
91+
remoteDataTimestamp = new Date().getTime() + 60 * 1000;
8992
const msToLive = 15 * 60 * 1000;
9093
const payload = {
9194
foo: 'bar',
@@ -112,22 +115,22 @@ describe('BaseDataService', () => {
112115
const statusCodeError = 404;
113116
const errorMessage = 'not found';
114117
remoteDataMocks = {
115-
RequestPending: new RemoteData(undefined, msToLive, timeStamp, RequestEntryState.RequestPending, undefined, undefined, undefined),
116-
ResponsePending: new RemoteData(undefined, msToLive, timeStamp, RequestEntryState.ResponsePending, undefined, undefined, undefined),
117-
ResponsePendingStale: new RemoteData(undefined, msToLive, timeStamp, RequestEntryState.ResponsePendingStale, undefined, undefined, undefined),
118-
Success: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.Success, undefined, payload, statusCodeSuccess),
119-
SuccessStale: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.SuccessStale, undefined, payload, statusCodeSuccess),
120-
Error: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.Error, errorMessage, undefined, statusCodeError),
121-
ErrorStale: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.ErrorStale, errorMessage, undefined, statusCodeError),
118+
RequestPending: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.RequestPending, undefined, undefined, undefined),
119+
ResponsePending: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.ResponsePending, undefined, undefined, undefined),
120+
ResponsePendingStale: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.ResponsePendingStale, undefined, undefined, undefined),
121+
Success: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.Success, undefined, payload, statusCodeSuccess),
122+
SuccessStale: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.SuccessStale, undefined, payload, statusCodeSuccess),
123+
Error: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.Error, errorMessage, undefined, statusCodeError),
124+
ErrorStale: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.ErrorStale, errorMessage, undefined, statusCodeError),
122125
};
123126
remoteDataPageMocks = {
124-
RequestPending: new RemoteData(undefined, msToLive, timeStamp, RequestEntryState.RequestPending, undefined, undefined, undefined),
125-
ResponsePending: new RemoteData(undefined, msToLive, timeStamp, RequestEntryState.ResponsePending, undefined, undefined, undefined),
126-
ResponsePendingStale: new RemoteData(undefined, msToLive, timeStamp, RequestEntryState.ResponsePendingStale, undefined, undefined, undefined),
127-
Success: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.Success, undefined, createPaginatedList([payload]), statusCodeSuccess),
128-
SuccessStale: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.SuccessStale, undefined, createPaginatedList([payload]), statusCodeSuccess),
129-
Error: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.Error, errorMessage, undefined, statusCodeError),
130-
ErrorStale: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.ErrorStale, errorMessage, undefined, statusCodeError),
127+
RequestPending: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.RequestPending, undefined, undefined, undefined),
128+
ResponsePending: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.ResponsePending, undefined, undefined, undefined),
129+
ResponsePendingStale: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.ResponsePendingStale, undefined, undefined, undefined),
130+
Success: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.Success, undefined, createPaginatedList([payload]), statusCodeSuccess),
131+
SuccessStale: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.SuccessStale, undefined, createPaginatedList([payload]), statusCodeSuccess),
132+
Error: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.Error, errorMessage, undefined, statusCodeError),
133+
ErrorStale: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.ErrorStale, errorMessage, undefined, statusCodeError),
131134
};
132135

133136
return new TestService(
@@ -361,11 +364,15 @@ describe('BaseDataService', () => {
361364
spyOn(service as any, 'reRequestStaleRemoteData').and.callFake(() => (source) => source);
362365
});
363366

364-
365-
it(`should not emit a cached completed RemoteData, but only start emitting after the state first changes to RequestPending`, () => {
367+
it('should not emit a cached completed RemoteData', () => {
368+
// Old cached value from 1 minute before the test started
369+
const oldCachedSucceededData: RemoteData<any> = Object.assign({}, remoteDataPageMocks.Success, {
370+
timeCompleted: remoteDataTimestamp - 2 * 60 * 1000,
371+
lastUpdated: remoteDataTimestamp - 2 * 60 * 1000,
372+
} as RemoteData<any>);
366373
testScheduler.run(({ cold, expectObservable }) => {
367374
spyOn(rdbService, 'buildSingle').and.returnValue(cold('a-b-c-d-e', {
368-
a: remoteDataMocks.Success,
375+
a: oldCachedSucceededData,
369376
b: remoteDataMocks.RequestPending,
370377
c: remoteDataMocks.ResponsePending,
371378
d: remoteDataMocks.Success,
@@ -383,6 +390,22 @@ describe('BaseDataService', () => {
383390
});
384391
});
385392

393+
it('should emit the first completed RemoteData since the request was made', () => {
394+
testScheduler.run(({ cold, expectObservable }) => {
395+
spyOn(rdbService, 'buildSingle').and.returnValue(cold('a-b', {
396+
a: remoteDataMocks.Success,
397+
b: remoteDataMocks.SuccessStale,
398+
}));
399+
const expected = 'a-b';
400+
const values = {
401+
a: remoteDataMocks.Success,
402+
b: remoteDataMocks.SuccessStale,
403+
};
404+
405+
expectObservable(service.findByHref(selfLink, false, true, ...linksToFollow)).toBe(expected, values);
406+
});
407+
});
408+
386409
it(`should not emit a cached stale RemoteData, but only start emitting after the state first changes to RequestPending`, () => {
387410
testScheduler.run(({ cold, expectObservable }) => {
388411
spyOn(rdbService, 'buildSingle').and.returnValue(cold('a-b-c-d-e-f-g', {
@@ -411,17 +434,12 @@ describe('BaseDataService', () => {
411434
it('should link all the followLinks of a cached object by calling addDependency', () => {
412435
spyOn(objectCache, 'addDependency').and.callThrough();
413436
testScheduler.run(({ cold, expectObservable, flush }) => {
414-
spyOn(rdbService, 'buildSingle').and.returnValue(cold('a-b-c-d', {
437+
spyOn(rdbService, 'buildSingle').and.returnValue(cold('a', {
415438
a: remoteDataMocks.Success,
416-
b: remoteDataMocks.RequestPending,
417-
c: remoteDataMocks.ResponsePending,
418-
d: remoteDataMocks.Success,
419439
}));
420-
const expected = '--b-c-d';
440+
const expected = 'a';
421441
const values = {
422-
b: remoteDataMocks.RequestPending,
423-
c: remoteDataMocks.ResponsePending,
424-
d: remoteDataMocks.Success,
442+
a: remoteDataMocks.Success,
425443
};
426444

427445
expectObservable(service.findByHref(selfLink, false, false, ...linksToFollow)).toBe(expected, values);
@@ -570,11 +588,15 @@ describe('BaseDataService', () => {
570588
spyOn(service as any, 'reRequestStaleRemoteData').and.callFake(() => (source) => source);
571589
});
572590

573-
574-
it(`should not emit a cached completed RemoteData, but only start emitting after the state first changes to RequestPending`, () => {
591+
it('should not emit a cached completed RemoteData', () => {
575592
testScheduler.run(({ cold, expectObservable }) => {
593+
// Old cached value from 1 minute before the test started
594+
const oldCachedSucceededData: RemoteData<any> = Object.assign({}, remoteDataPageMocks.Success, {
595+
timeCompleted: remoteDataTimestamp - 2 * 60 * 1000,
596+
lastUpdated: remoteDataTimestamp - 2 * 60 * 1000,
597+
} as RemoteData<any>);
576598
spyOn(rdbService, 'buildList').and.returnValue(cold('a-b-c-d-e', {
577-
a: remoteDataPageMocks.Success,
599+
a: oldCachedSucceededData,
578600
b: remoteDataPageMocks.RequestPending,
579601
c: remoteDataPageMocks.ResponsePending,
580602
d: remoteDataPageMocks.Success,
@@ -592,6 +614,22 @@ describe('BaseDataService', () => {
592614
});
593615
});
594616

617+
it('should emit the first completed RemoteData since the request was made', () => {
618+
testScheduler.run(({ cold, expectObservable }) => {
619+
spyOn(rdbService, 'buildList').and.returnValue(cold('a-b', {
620+
a: remoteDataPageMocks.Success,
621+
b: remoteDataPageMocks.SuccessStale,
622+
}));
623+
const expected = 'a-b';
624+
const values = {
625+
a: remoteDataPageMocks.Success,
626+
b: remoteDataPageMocks.SuccessStale,
627+
};
628+
629+
expectObservable(service.findListByHref(selfLink, findListOptions, false, true, ...linksToFollow)).toBe(expected, values);
630+
});
631+
});
632+
595633
it(`should not emit a cached stale RemoteData, but only start emitting after the state first changes to RequestPending`, () => {
596634
testScheduler.run(({ cold, expectObservable }) => {
597635
spyOn(rdbService, 'buildList').and.returnValue(cold('a-b-c-d-e-f-g', {

src/app/core/data/base/base-data.service.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,14 +285,15 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
285285
map((href: string) => this.buildHrefFromFindOptions(href, {}, [], ...linksToFollow)),
286286
);
287287

288+
const startTime: number = new Date().getTime();
288289
this.createAndSendGetRequest(requestHref$, useCachedVersionIfAvailable);
289290

290291
const response$: Observable<RemoteData<T>> = this.rdbService.buildSingle<T>(requestHref$, ...linksToFollow).pipe(
291292
// This skip ensures that if a stale object is present in the cache when you do a
292293
// call it isn't immediately returned, but we wait until the remote data for the new request
293294
// is created. If useCachedVersionIfAvailable is false it also ensures you don't get a
294295
// cached completed object
295-
skipWhile((rd: RemoteData<T>) => rd.isStale || (!useCachedVersionIfAvailable && rd.hasCompleted)),
296+
skipWhile((rd: RemoteData<T>) => rd.isStale || (!useCachedVersionIfAvailable && rd.lastUpdated < startTime)),
296297
this.reRequestStaleRemoteData(reRequestOnStale, () =>
297298
this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)),
298299
);
@@ -338,14 +339,15 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
338339
map((href: string) => this.buildHrefFromFindOptions(href, options, [], ...linksToFollow)),
339340
);
340341

342+
const startTime: number = new Date().getTime();
341343
this.createAndSendGetRequest(requestHref$, useCachedVersionIfAvailable);
342344

343345
const response$: Observable<RemoteData<PaginatedList<T>>> = this.rdbService.buildList<T>(requestHref$, ...linksToFollow).pipe(
344346
// This skip ensures that if a stale object is present in the cache when you do a
345347
// call it isn't immediately returned, but we wait until the remote data for the new request
346348
// is created. If useCachedVersionIfAvailable is false it also ensures you don't get a
347349
// cached completed object
348-
skipWhile((rd: RemoteData<PaginatedList<T>>) => rd.isStale || (!useCachedVersionIfAvailable && rd.hasCompleted)),
350+
skipWhile((rd: RemoteData<PaginatedList<T>>) => rd.isStale || (!useCachedVersionIfAvailable && rd.lastUpdated < startTime)),
349351
this.reRequestStaleRemoteData(reRequestOnStale, () =>
350352
this.findListByHref(href$, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)),
351353
);

0 commit comments

Comments
 (0)