100302: Add support to check if a request has a cached value

This commit is contained in:
Yana De Pauw
2023-03-28 14:38:44 +02:00
parent 153a53f118
commit 9f6616a5ce
6 changed files with 129 additions and 17 deletions

View File

@@ -641,6 +641,62 @@ describe('BaseDataService', () => {
}); });
}); });
describe('hasCachedResponse', () => {
it('should return false when the request will be dispatched', (done) => {
const result = service.hasCachedResponse('test-href');
result.subscribe((hasCachedResponse) => {
expect(hasCachedResponse).toBeFalse();
done();
});
});
it('should return true when the request will not be dispatched', (done) => {
(requestService.shouldDispatchRequest as jasmine.Spy).and.returnValue(false);
const result = service.hasCachedResponse('test-href');
result.subscribe((hasCachedResponse) => {
expect(hasCachedResponse).toBeTrue();
done();
});
});
});
describe('hasCachedErrorResponse', () => {
it('should return false when no response is cached', (done) => {
spyOn(service,'hasCachedResponse').and.returnValue(observableOf(false));
const result = service.hasCachedErrorResponse('test-href');
result.subscribe((hasCachedErrorResponse) => {
expect(hasCachedErrorResponse).toBeFalse();
done();
});
});
it('should return false when no error response is cached', (done) => {
spyOn(service,'hasCachedResponse').and.returnValue(observableOf(true));
spyOn(rdbService,'buildSingle').and.returnValue(createSuccessfulRemoteDataObject$({}));
const result = service.hasCachedErrorResponse('test-href');
result.subscribe((hasCachedErrorResponse) => {
expect(hasCachedErrorResponse).toBeFalse();
done();
});
});
it('should return true when an error response is cached', (done) => {
spyOn(service,'hasCachedResponse').and.returnValue(observableOf(true));
spyOn(rdbService,'buildSingle').and.returnValue(createFailedRemoteDataObject$());
const result = service.hasCachedErrorResponse('test-href');
result.subscribe((hasCachedErrorResponse) => {
expect(hasCachedErrorResponse).toBeTrue();
done();
});
});
});
describe('addDependency', () => { describe('addDependency', () => {
let addDependencySpy; let addDependencySpy;

View File

@@ -341,6 +341,37 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
} }
} }
hasCachedResponse(href$: string | Observable<string>): Observable<boolean> {
if (isNotEmpty(href$)) {
if (typeof href$ === 'string') {
href$ = observableOf(href$);
}
return href$.pipe(
isNotEmptyOperator(),
take(1),
map((href: string) => {
const requestId = this.requestService.generateRequestId();
const request = new GetRequest(requestId, href);
return !this.requestService.shouldDispatchRequest(request, true);
}),
);
}
}
hasCachedErrorResponse(href$: string | Observable<string>): Observable<boolean> {
return this.hasCachedResponse(href$).pipe(
switchMap((hasCachedResponse) => {
if (hasCachedResponse) {
return this.rdbService.buildSingle(href$).pipe(
getFirstCompletedRemoteData(),
map((rd => rd.isError || rd.isErrorStale))
);
}
return observableOf(false);
})
);
}
/** /**
* Return the links to traverse from the root of the api to the * Return the links to traverse from the root of the api to the
* endpoint this DataService represents * endpoint this DataService represents

View File

@@ -5,6 +5,7 @@ import { ExternalSourceEntry } from '../shared/external-source-entry.model';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { GetRequest } from './request.models'; import { GetRequest } from './request.models';
import { testSearchDataImplementation } from './base/search-data.spec'; import { testSearchDataImplementation } from './base/search-data.spec';
import { take } from 'rxjs/operators';
describe('ExternalSourceService', () => { describe('ExternalSourceService', () => {
let service: ExternalSourceDataService; let service: ExternalSourceDataService;
@@ -64,19 +65,42 @@ describe('ExternalSourceService', () => {
}); });
describe('getExternalSourceEntries', () => { describe('getExternalSourceEntries', () => {
let result;
beforeEach(() => { describe('when no error response is cached', () => {
result = service.getExternalSourceEntries('test'); let result;
beforeEach(() => {
spyOn(service, 'hasCachedErrorResponse').and.returnValue(observableOf(false));
result = service.getExternalSourceEntries('test');
});
it('should send a GetRequest', () => {
result.pipe(take(1)).subscribe();
expect(requestService.send).toHaveBeenCalledWith(jasmine.any(GetRequest), true);
});
it('should return the entries', () => {
result.subscribe((resultRD) => {
expect(resultRD.payload.page).toBe(entries);
});
});
}); });
it('should send a GetRequest', () => { describe('when an error response is cached', () => {
expect(requestService.send).toHaveBeenCalledWith(jasmine.any(GetRequest), true); let result;
}); beforeEach(() => {
spyOn(service, 'hasCachedErrorResponse').and.returnValue(observableOf(true));
result = service.getExternalSourceEntries('test');
});
it('should return the entries', () => { it('should send a GetRequest', () => {
result.subscribe((resultRD) => { result.pipe(take(1)).subscribe();
expect(resultRD.payload.page).toBe(entries); expect(requestService.send).toHaveBeenCalledWith(jasmine.any(GetRequest), false);
});
it('should return the entries', () => {
result.subscribe((resultRD) => {
expect(resultRD.payload.page).toBe(entries);
});
}); });
}); });
}); });

View File

@@ -74,7 +74,12 @@ export class ExternalSourceDataService extends IdentifiableDataService<ExternalS
); );
// TODO create a dedicated ExternalSourceEntryDataService and move this entire method to it. Then the "as any"s won't be necessary // TODO create a dedicated ExternalSourceEntryDataService and move this entire method to it. Then the "as any"s won't be necessary
return this.findListByHref(href$, undefined, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow as any) as any;
return this.hasCachedErrorResponse(href$).pipe(
switchMap((hasCachedErrorResponse) => {
return this.findListByHref(href$, undefined, !hasCachedErrorResponse, reRequestOnStale, ...linksToFollow as any);
})
) as any;
} }
/** /**

View File

@@ -14,6 +14,7 @@ export function getMockRequestService(requestEntry$: Observable<RequestEntry> =
removeByHrefSubstring: observableOf(true), removeByHrefSubstring: observableOf(true),
setStaleByHrefSubstring: observableOf(true), setStaleByHrefSubstring: observableOf(true),
setStaleByUUID: observableOf(true), setStaleByUUID: observableOf(true),
hasByHref$: observableOf(false) hasByHref$: observableOf(false),
shouldDispatchRequest: true
}); });
} }

View File

@@ -188,17 +188,12 @@ export class SubmissionImportExternalComponent implements OnInit, OnDestroy {
this.retrieveExternalSourcesSub = this.reload$.pipe( this.retrieveExternalSourcesSub = this.reload$.pipe(
filter((sourceQueryObject: ExternalSourceData) => isNotEmpty(sourceQueryObject.sourceId) && isNotEmpty(sourceQueryObject.query)), filter((sourceQueryObject: ExternalSourceData) => isNotEmpty(sourceQueryObject.sourceId) && isNotEmpty(sourceQueryObject.query)),
switchMap((sourceQueryObject: ExternalSourceData) => { switchMap((sourceQueryObject: ExternalSourceData) => {
const currentEntry = this.entriesRD$.getValue();
let useCache = true;
if (hasValue(currentEntry) && currentEntry.isError) {
useCache = false;
}
const query = sourceQueryObject.query; const query = sourceQueryObject.query;
this.routeData = sourceQueryObject; this.routeData = sourceQueryObject;
return this.searchConfigService.paginatedSearchOptions.pipe( return this.searchConfigService.paginatedSearchOptions.pipe(
tap(() => this.isLoading$.next(true)), tap(() => this.isLoading$.next(true)),
filter((searchOptions) => searchOptions.query === query), filter((searchOptions) => searchOptions.query === query),
mergeMap((searchOptions) => this.externalService.getExternalSourceEntries(this.routeData.sourceId, searchOptions, useCache).pipe( mergeMap((searchOptions) => this.externalService.getExternalSourceEntries(this.routeData.sourceId, searchOptions).pipe(
getFinishedRemoteData(), getFinishedRemoteData(),
)) ))
); );