120109: Fixed BaseDataService not emitting when the request is too fast and the ResponsePending are not emitted

(cherry picked from commit 0f4d71eb58)
This commit is contained in:
Alexandre Vryghem
2024-11-19 16:35:45 +01:00
parent 404ccd9b0e
commit 987ea5104a
2 changed files with 60 additions and 15 deletions

View File

@@ -50,6 +50,7 @@ describe('BaseDataService', () => {
let selfLink;
let linksToFollow;
let testScheduler;
let remoteDataTimestamp: number;
let remoteDataMocks;
function initTestService(): TestService {
@@ -86,19 +87,21 @@ describe('BaseDataService', () => {
expect(actual).toEqual(expected);
});
const timeStamp = new Date().getTime();
// The response's lastUpdated equals the time of 60 seconds after the test started, ensuring they are not perceived
// as cached values.
remoteDataTimestamp = new Date().getTime() + 60 * 1000;
const msToLive = 15 * 60 * 1000;
const payload = { foo: 'bar' };
const statusCodeSuccess = 200;
const statusCodeError = 404;
const errorMessage = 'not found';
remoteDataMocks = {
RequestPending: new RemoteData(undefined, msToLive, timeStamp, RequestEntryState.RequestPending, undefined, undefined, undefined),
ResponsePending: new RemoteData(undefined, msToLive, timeStamp, RequestEntryState.ResponsePending, undefined, undefined, undefined),
Success: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.Success, undefined, payload, statusCodeSuccess),
SuccessStale: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.SuccessStale, undefined, payload, statusCodeSuccess),
Error: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.Error, errorMessage, undefined, statusCodeError),
ErrorStale: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.ErrorStale, errorMessage, undefined, statusCodeError),
RequestPending: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.RequestPending, undefined, undefined, undefined),
ResponsePending: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.ResponsePending, undefined, undefined, undefined),
Success: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.Success, undefined, payload, statusCodeSuccess),
SuccessStale: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.SuccessStale, undefined, payload, statusCodeSuccess),
Error: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.Error, errorMessage, undefined, statusCodeError),
ErrorStale: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.ErrorStale, errorMessage, undefined, statusCodeError),
};
return new TestService(
@@ -330,11 +333,15 @@ describe('BaseDataService', () => {
spyOn(service as any, 'reRequestStaleRemoteData').and.callFake(() => (source) => source);
});
it(`should not emit a cached completed RemoteData, but only start emitting after the state first changes to RequestPending`, () => {
it('should not emit a cached completed RemoteData', () => {
// Old cached value from 1 minute before the test started
const oldCachedSucceededData: RemoteData<any> = Object.assign({}, remoteDataMocks.Success, {
timeCompleted: remoteDataTimestamp - 2 * 60 * 1000,
lastUpdated: remoteDataTimestamp - 2 * 60 * 1000,
} as RemoteData<any>);
testScheduler.run(({ cold, expectObservable }) => {
spyOn(rdbService, 'buildSingle').and.returnValue(cold('a-b-c-d-e', {
a: remoteDataMocks.Success,
a: oldCachedSucceededData,
b: remoteDataMocks.RequestPending,
c: remoteDataMocks.ResponsePending,
d: remoteDataMocks.Success,
@@ -352,6 +359,22 @@ describe('BaseDataService', () => {
});
});
it('should emit the first completed RemoteData since the request was made', () => {
testScheduler.run(({ cold, expectObservable }) => {
spyOn(rdbService, 'buildSingle').and.returnValue(cold('a-b', {
a: remoteDataMocks.Success,
b: remoteDataMocks.SuccessStale,
}));
const expected = 'a-b';
const values = {
a: remoteDataMocks.Success,
b: remoteDataMocks.SuccessStale,
};
expectObservable(service.findByHref(selfLink, false, true, ...linksToFollow)).toBe(expected, values);
});
});
it(`should not emit a cached stale RemoteData, but only start emitting after the state first changes to RequestPending`, () => {
testScheduler.run(({ cold, expectObservable }) => {
spyOn(rdbService, 'buildSingle').and.returnValue(cold('a-b-c-d-e', {
@@ -514,11 +537,15 @@ describe('BaseDataService', () => {
spyOn(service as any, 'reRequestStaleRemoteData').and.callFake(() => (source) => source);
});
it(`should not emit a cached completed RemoteData, but only start emitting after the state first changes to RequestPending`, () => {
it('should not emit a cached completed RemoteData', () => {
testScheduler.run(({ cold, expectObservable }) => {
// Old cached value from 1 minute before the test started
const oldCachedSucceededData: RemoteData<any> = Object.assign({}, remoteDataMocks.Success, {
timeCompleted: remoteDataTimestamp - 2 * 60 * 1000,
lastUpdated: remoteDataTimestamp - 2 * 60 * 1000,
} as RemoteData<any>);
spyOn(rdbService, 'buildList').and.returnValue(cold('a-b-c-d-e', {
a: remoteDataMocks.Success,
a: oldCachedSucceededData,
b: remoteDataMocks.RequestPending,
c: remoteDataMocks.ResponsePending,
d: remoteDataMocks.Success,
@@ -536,6 +563,22 @@ describe('BaseDataService', () => {
});
});
it('should emit the first completed RemoteData since the request was made', () => {
testScheduler.run(({ cold, expectObservable }) => {
spyOn(rdbService, 'buildList').and.returnValue(cold('a-b', {
a: remoteDataMocks.Success,
b: remoteDataMocks.SuccessStale,
}));
const expected = 'a-b';
const values = {
a: remoteDataMocks.Success,
b: remoteDataMocks.SuccessStale,
};
expectObservable(service.findListByHref(selfLink, findListOptions, false, true, ...linksToFollow)).toBe(expected, values);
});
});
it(`should not emit a cached stale RemoteData, but only start emitting after the state first changes to RequestPending`, () => {
testScheduler.run(({ cold, expectObservable }) => {
spyOn(rdbService, 'buildList').and.returnValue(cold('a-b-c-d-e', {

View File

@@ -266,6 +266,7 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
map((href: string) => this.buildHrefFromFindOptions(href, {}, [], ...linksToFollow)),
);
const startTime: number = new Date().getTime();
this.createAndSendGetRequest(requestHref$, useCachedVersionIfAvailable);
return this.rdbService.buildSingle<T>(requestHref$, ...linksToFollow).pipe(
@@ -273,7 +274,7 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
// call it isn't immediately returned, but we wait until the remote data for the new request
// is created. If useCachedVersionIfAvailable is false it also ensures you don't get a
// cached completed object
skipWhile((rd: RemoteData<T>) => useCachedVersionIfAvailable ? rd.isStale : rd.hasCompleted),
skipWhile((rd: RemoteData<T>) => rd.isStale || (!useCachedVersionIfAvailable && rd.lastUpdated < startTime)),
this.reRequestStaleRemoteData(reRequestOnStale, () =>
this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)),
);
@@ -300,6 +301,7 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
map((href: string) => this.buildHrefFromFindOptions(href, options, [], ...linksToFollow)),
);
const startTime: number = new Date().getTime();
this.createAndSendGetRequest(requestHref$, useCachedVersionIfAvailable);
return this.rdbService.buildList<T>(requestHref$, ...linksToFollow).pipe(
@@ -307,7 +309,7 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
// call it isn't immediately returned, but we wait until the remote data for the new request
// is created. If useCachedVersionIfAvailable is false it also ensures you don't get a
// cached completed object
skipWhile((rd: RemoteData<PaginatedList<T>>) => useCachedVersionIfAvailable ? rd.isStale : rd.hasCompleted),
skipWhile((rd: RemoteData<PaginatedList<T>>) => rd.isStale || (!useCachedVersionIfAvailable && rd.lastUpdated < startTime)),
this.reRequestStaleRemoteData(reRequestOnStale, () =>
this.findListByHref(href$, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)),
);