ensure HALEndpointService doesn't use stale requests

This commit is contained in:
Art Lowel
2023-11-29 14:25:36 +01:00
parent 6a99185214
commit 38752d9d71
3 changed files with 16 additions and 12 deletions

View File

@@ -9,7 +9,7 @@ import SpyObj = jasmine.SpyObj;
describe('ServerCheckGuard', () => { describe('ServerCheckGuard', () => {
let guard: ServerCheckGuard; let guard: ServerCheckGuard;
let router: Router; let router: Router;
const eventSubject = new ReplaySubject<RouterEvent>(1); let eventSubject: ReplaySubject<RouterEvent>;
let rootDataServiceStub: SpyObj<RootDataService>; let rootDataServiceStub: SpyObj<RootDataService>;
let testScheduler: TestScheduler; let testScheduler: TestScheduler;
let redirectUrlTree: UrlTree; let redirectUrlTree: UrlTree;
@@ -24,6 +24,7 @@ describe('ServerCheckGuard', () => {
findRoot: jasmine.createSpy('findRoot') findRoot: jasmine.createSpy('findRoot')
}); });
redirectUrlTree = new UrlTree(); redirectUrlTree = new UrlTree();
eventSubject = new ReplaySubject<RouterEvent>(1);
router = { router = {
events: eventSubject.asObservable(), events: eventSubject.asObservable(),
navigateByUrl: jasmine.createSpy('navigateByUrl'), navigateByUrl: jasmine.createSpy('navigateByUrl'),
@@ -64,10 +65,10 @@ describe('ServerCheckGuard', () => {
}); });
describe(`listenForRouteChanges`, () => { describe(`listenForRouteChanges`, () => {
it(`should retrieve the root endpoint, without using the cache, when the method is first called`, () => { it(`should invalidate the root cache, when the method is first called`, () => {
testScheduler.run(() => { testScheduler.run(() => {
guard.listenForRouteChanges(); guard.listenForRouteChanges();
expect(rootDataServiceStub.findRoot).toHaveBeenCalledWith(false); expect(rootDataServiceStub.invalidateRootCache).toHaveBeenCalledTimes(1);
}); });
}); });
@@ -80,7 +81,8 @@ describe('ServerCheckGuard', () => {
eventSubject.next(new NavigationEnd(2,'', '')); eventSubject.next(new NavigationEnd(2,'', ''));
eventSubject.next(new NavigationStart(3,'')); eventSubject.next(new NavigationStart(3,''));
}); });
expect(rootDataServiceStub.invalidateRootCache).toHaveBeenCalledTimes(3); // once when the method is first called, and then 3 times for NavigationStart events
expect(rootDataServiceStub.invalidateRootCache).toHaveBeenCalledTimes(1 + 3);
}); });
}); });
}); });

View File

@@ -53,10 +53,8 @@ export class ServerCheckGuard implements CanActivateChild {
*/ */
listenForRouteChanges(): void { listenForRouteChanges(): void {
// we'll always be too late for the first NavigationStart event with the router subscribe below, // we'll always be too late for the first NavigationStart event with the router subscribe below,
// so this statement is for the very first route operation. A `find` without using the cache, // so this statement is for the very first route operation.
// rather than an invalidateRootCache, because invalidating as the app is bootstrapping can this.rootDataService.invalidateRootCache();
// break other features
this.rootDataService.findRoot(false);
this.router.events.pipe( this.router.events.pipe(
filter(event => event instanceof NavigationStart), filter(event => event instanceof NavigationStart),

View File

@@ -1,5 +1,5 @@
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { distinctUntilChanged, map, startWith, switchMap, take } from 'rxjs/operators'; import { distinctUntilChanged, map, startWith, switchMap, take, skipWhile } from 'rxjs/operators';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { EndpointMapRequest } from '../data/request.models'; import { EndpointMapRequest } from '../data/request.models';
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
@@ -9,7 +9,7 @@ import { EndpointMap } from '../cache/response.models';
import { getFirstCompletedRemoteData } from './operators'; import { getFirstCompletedRemoteData } from './operators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { UnCacheableObject } from './uncacheable-object.model'; import { CacheableObject } from '../cache/cacheable-object.model';
@Injectable() @Injectable()
export class HALEndpointService { export class HALEndpointService {
@@ -33,9 +33,13 @@ export class HALEndpointService {
this.requestService.send(request, true); this.requestService.send(request, true);
return this.rdbService.buildFromHref<UnCacheableObject>(href).pipe( return this.rdbService.buildFromHref<CacheableObject>(href).pipe(
// This skip ensures that if a stale object is present in the cache when you do a
// call it isn't immediately returned, but we wait until the remote data for the new request
// is created.
skipWhile((rd: RemoteData<CacheableObject>) => rd.isStale),
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
map((response: RemoteData<UnCacheableObject>) => { map((response: RemoteData<CacheableObject>) => {
if (hasValue(response.payload)) { if (hasValue(response.payload)) {
return response.payload._links; return response.payload._links;
} else { } else {