mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-10 03:23:07 +00:00
108915: Always invalidate all followLinks when invalidating linked cached object
This commit is contained in:
@@ -21,6 +21,8 @@ import { RequestEntryState } from '../request-entry-state.model';
|
||||
import { fakeAsync, tick } from '@angular/core/testing';
|
||||
import { BaseDataService } from './base-data.service';
|
||||
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
||||
import { ObjectCacheServiceStub } from '../../../shared/testing/object-cache-service.stub';
|
||||
import { ObjectCacheEntry } from '../../cache/object-cache.reducer';
|
||||
|
||||
const endpoint = 'https://rest.api/core';
|
||||
|
||||
@@ -46,7 +48,7 @@ describe('BaseDataService', () => {
|
||||
let requestService;
|
||||
let halService;
|
||||
let rdbService;
|
||||
let objectCache;
|
||||
let objectCache: ObjectCacheServiceStub;
|
||||
let selfLink;
|
||||
let linksToFollow;
|
||||
let testScheduler;
|
||||
@@ -56,24 +58,7 @@ describe('BaseDataService', () => {
|
||||
requestService = getMockRequestService();
|
||||
halService = new HALEndpointServiceStub('url') as any;
|
||||
rdbService = getMockRemoteDataBuildService();
|
||||
objectCache = {
|
||||
|
||||
addPatch: () => {
|
||||
/* empty */
|
||||
},
|
||||
getObjectBySelfLink: () => {
|
||||
/* empty */
|
||||
},
|
||||
getByHref: () => {
|
||||
/* empty */
|
||||
},
|
||||
addDependency: () => {
|
||||
/* empty */
|
||||
},
|
||||
removeDependents: () => {
|
||||
/* empty */
|
||||
},
|
||||
} as any;
|
||||
objectCache = new ObjectCacheServiceStub();
|
||||
selfLink = 'https://rest.api/endpoint/1698f1d3-be98-4c51-9fd8-6bfedcbd59b7';
|
||||
linksToFollow = [
|
||||
followLink('a'),
|
||||
@@ -104,7 +89,7 @@ describe('BaseDataService', () => {
|
||||
return new TestService(
|
||||
requestService,
|
||||
rdbService,
|
||||
objectCache,
|
||||
objectCache as ObjectCacheService,
|
||||
halService,
|
||||
);
|
||||
}
|
||||
@@ -567,7 +552,7 @@ describe('BaseDataService', () => {
|
||||
getByHrefSpy = spyOn(objectCache, 'getByHref').and.returnValue(observableOf({
|
||||
requestUUIDs: ['request1', 'request2', 'request3'],
|
||||
dependentRequestUUIDs: ['request4', 'request5']
|
||||
}));
|
||||
} as ObjectCacheEntry));
|
||||
|
||||
});
|
||||
|
||||
|
@@ -24,6 +24,7 @@ import { ObjectCacheEntry } from '../../cache/object-cache.reducer';
|
||||
import { ObjectCacheService } from '../../cache/object-cache.service';
|
||||
import { HALDataService } from './hal-data-service.interface';
|
||||
import { getFirstCompletedRemoteData } from '../../shared/operators';
|
||||
import { HALLink } from '../../shared/hal-link.model';
|
||||
|
||||
export const EMBED_SEPARATOR = '%2F';
|
||||
/**
|
||||
@@ -268,7 +269,7 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
|
||||
|
||||
this.createAndSendGetRequest(requestHref$, useCachedVersionIfAvailable);
|
||||
|
||||
return this.rdbService.buildSingle<T>(requestHref$, ...linksToFollow).pipe(
|
||||
const response$: Observable<RemoteData<T>> = this.rdbService.buildSingle<T>(requestHref$, ...linksToFollow).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. If useCachedVersionIfAvailable is false it also ensures you don't get a
|
||||
@@ -277,6 +278,22 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
|
||||
this.reRequestStaleRemoteData(reRequestOnStale, () =>
|
||||
this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)),
|
||||
);
|
||||
return response$.pipe(
|
||||
// Ensure all followLinks from the cached object are automatically invalidated when invalidating the cached object
|
||||
tap((remoteDataObject: RemoteData<T>) => {
|
||||
if (hasValue(remoteDataObject?.payload?._links)) {
|
||||
for (const followLink of Object.values(remoteDataObject.payload._links)) {
|
||||
// followLink can be either an individual HALLink or a HALLink[]
|
||||
const followLinksList: HALLink[] = [].concat(followLink);
|
||||
for (const individualFollowLink of followLinksList) {
|
||||
if (hasValue(individualFollowLink?.href)) {
|
||||
this.addDependency(response$, individualFollowLink.href);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,7 +319,7 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
|
||||
|
||||
this.createAndSendGetRequest(requestHref$, useCachedVersionIfAvailable);
|
||||
|
||||
return this.rdbService.buildList<T>(requestHref$, ...linksToFollow).pipe(
|
||||
const response$: Observable<RemoteData<PaginatedList<T>>> = this.rdbService.buildList<T>(requestHref$, ...linksToFollow).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. If useCachedVersionIfAvailable is false it also ensures you don't get a
|
||||
@@ -311,6 +328,26 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
|
||||
this.reRequestStaleRemoteData(reRequestOnStale, () =>
|
||||
this.findListByHref(href$, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)),
|
||||
);
|
||||
return response$.pipe(
|
||||
// Ensure all followLinks from the cached object are automatically invalidated when invalidating the cached object
|
||||
tap((remoteDataObject: RemoteData<PaginatedList<T>>) => {
|
||||
if (hasValue(remoteDataObject?.payload?.page)) {
|
||||
for (const object of remoteDataObject.payload.page) {
|
||||
if (hasValue(object?._links)) {
|
||||
for (const followLink of Object.values(object._links)) {
|
||||
// followLink can be either an individual HALLink or a HALLink[]
|
||||
const followLinksList: HALLink[] = [].concat(followLink);
|
||||
for (const individualFollowLink of followLinksList) {
|
||||
if (hasValue(individualFollowLink?.href)) {
|
||||
this.addDependency(response$, individualFollowLink.href);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -24,6 +24,7 @@ import { testFindAllDataImplementation } from './base/find-all-data.spec';
|
||||
import { testSearchDataImplementation } from './base/search-data.spec';
|
||||
import { testPatchDataImplementation } from './base/patch-data.spec';
|
||||
import { testDeleteDataImplementation } from './base/delete-data.spec';
|
||||
import { ObjectCacheServiceStub } from '../../shared/testing/object-cache-service.stub';
|
||||
|
||||
const url = 'fake-url';
|
||||
const collectionId = 'fake-collection-id';
|
||||
@@ -35,7 +36,7 @@ describe('CollectionDataService', () => {
|
||||
let translate: TranslateService;
|
||||
let notificationsService: any;
|
||||
let rdbService: RemoteDataBuildService;
|
||||
let objectCache: ObjectCacheService;
|
||||
let objectCache: ObjectCacheServiceStub;
|
||||
let halService: any;
|
||||
|
||||
const mockCollection1: Collection = Object.assign(new Collection(), {
|
||||
@@ -205,14 +206,12 @@ describe('CollectionDataService', () => {
|
||||
buildFromRequestUUID: buildResponse$,
|
||||
buildSingle: buildResponse$
|
||||
});
|
||||
objectCache = jasmine.createSpyObj('objectCache', {
|
||||
remove: jasmine.createSpy('remove')
|
||||
});
|
||||
objectCache = new ObjectCacheServiceStub();
|
||||
halService = new HALEndpointServiceStub(url);
|
||||
notificationsService = new NotificationsServiceStub();
|
||||
translate = getMockTranslateService();
|
||||
|
||||
service = new CollectionDataService(requestService, rdbService, objectCache, halService, null, notificationsService, null, null, translate);
|
||||
service = new CollectionDataService(requestService, rdbService, objectCache as ObjectCacheService, halService, null, notificationsService, null, null, translate);
|
||||
}
|
||||
|
||||
});
|
||||
|
@@ -23,6 +23,7 @@ import { FindListOptions } from './find-list-options.model';
|
||||
import { testSearchDataImplementation } from './base/search-data.spec';
|
||||
import { MetadataValue } from '../shared/metadata.models';
|
||||
import { MetadataRepresentationType } from '../shared/metadata-representation/metadata-representation.model';
|
||||
import { ObjectCacheServiceStub } from '../../shared/testing/object-cache-service.stub';
|
||||
|
||||
describe('RelationshipDataService', () => {
|
||||
let service: RelationshipDataService;
|
||||
@@ -114,14 +115,7 @@ describe('RelationshipDataService', () => {
|
||||
'href': buildList$,
|
||||
'https://rest.api/core/publication/relationships': relationships$
|
||||
});
|
||||
const objectCache = Object.assign({
|
||||
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
|
||||
remove: () => {
|
||||
},
|
||||
hasBySelfLinkObservable: () => observableOf(false),
|
||||
hasByHref$: () => observableOf(false)
|
||||
/* eslint-enable no-empty, @typescript-eslint/no-empty-function */
|
||||
}) as ObjectCacheService;
|
||||
const objectCache = new ObjectCacheServiceStub();
|
||||
|
||||
const itemService = jasmine.createSpyObj('itemService', {
|
||||
findById: (uuid) => createSuccessfulRemoteDataObject(relatedItems.find((relatedItem) => relatedItem.id === uuid)),
|
||||
@@ -133,7 +127,7 @@ describe('RelationshipDataService', () => {
|
||||
requestService,
|
||||
rdbService,
|
||||
halService,
|
||||
objectCache,
|
||||
objectCache as ObjectCacheService,
|
||||
itemService,
|
||||
null,
|
||||
jasmine.createSpy('paginatedRelationsToItems').and.returnValue((v) => v),
|
||||
|
@@ -10,6 +10,7 @@ import { RequestService } from './request.service';
|
||||
import { createPaginatedList } from '../../shared/testing/utils.test';
|
||||
import { hasValueOperator } from '../../shared/empty.util';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { ObjectCacheServiceStub } from '../../shared/testing/object-cache-service.stub';
|
||||
|
||||
describe('RelationshipTypeDataService', () => {
|
||||
let service: RelationshipTypeDataService;
|
||||
@@ -28,7 +29,7 @@ describe('RelationshipTypeDataService', () => {
|
||||
|
||||
let buildList;
|
||||
let rdbService;
|
||||
let objectCache;
|
||||
let objectCache: ObjectCacheServiceStub;
|
||||
|
||||
function init() {
|
||||
restEndpointURL = 'https://rest.api/relationshiptypes';
|
||||
@@ -60,21 +61,14 @@ describe('RelationshipTypeDataService', () => {
|
||||
|
||||
buildList = createSuccessfulRemoteDataObject(createPaginatedList([relationshipType1, relationshipType2]));
|
||||
rdbService = getMockRemoteDataBuildService(undefined, observableOf(buildList));
|
||||
objectCache = Object.assign({
|
||||
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
|
||||
remove: () => {
|
||||
},
|
||||
hasBySelfLinkObservable: () => observableOf(false)
|
||||
/* eslint-enable no-empty, @typescript-eslint/no-empty-function */
|
||||
}) as ObjectCacheService;
|
||||
|
||||
objectCache = new ObjectCacheServiceStub();
|
||||
}
|
||||
|
||||
function initTestService() {
|
||||
return new RelationshipTypeDataService(
|
||||
requestService,
|
||||
rdbService,
|
||||
objectCache,
|
||||
objectCache as ObjectCacheService,
|
||||
halService,
|
||||
);
|
||||
}
|
||||
|
@@ -20,13 +20,14 @@ import { FindListOptions } from '../data/find-list-options.model';
|
||||
import { EPersonDataService } from '../eperson/eperson-data.service';
|
||||
import { GroupDataService } from '../eperson/group-data.service';
|
||||
import { RestRequestMethod } from '../data/rest-request-method';
|
||||
import { ObjectCacheServiceStub } from '../../shared/testing/object-cache-service.stub';
|
||||
|
||||
describe('ResourcePolicyService', () => {
|
||||
let scheduler: TestScheduler;
|
||||
let service: ResourcePolicyDataService;
|
||||
let requestService: RequestService;
|
||||
let rdbService: RemoteDataBuildService;
|
||||
let objectCache: ObjectCacheService;
|
||||
let objectCache: ObjectCacheServiceStub;
|
||||
let halService: HALEndpointService;
|
||||
let responseCacheEntry: RequestEntry;
|
||||
let ePersonService: EPersonDataService;
|
||||
@@ -139,14 +140,14 @@ describe('ResourcePolicyService', () => {
|
||||
a: 'https://rest.api/rest/api/eperson/groups/' + groupUUID
|
||||
}),
|
||||
});
|
||||
objectCache = {} as ObjectCacheService;
|
||||
objectCache = new ObjectCacheServiceStub();
|
||||
const notificationsService = {} as NotificationsService;
|
||||
const comparator = {} as any;
|
||||
|
||||
service = new ResourcePolicyDataService(
|
||||
requestService,
|
||||
rdbService,
|
||||
objectCache,
|
||||
objectCache as ObjectCacheService,
|
||||
halService,
|
||||
notificationsService,
|
||||
comparator,
|
||||
|
@@ -25,6 +25,7 @@ import { createPaginatedList } from '../../../shared/testing/utils.test';
|
||||
import { RequestEntry } from '../../data/request-entry.model';
|
||||
import { VocabularyDataService } from './vocabulary.data.service';
|
||||
import { VocabularyEntryDetailsDataService } from './vocabulary-entry-details.data.service';
|
||||
import { ObjectCacheServiceStub } from '../../../shared/testing/object-cache-service.stub';
|
||||
|
||||
describe('VocabularyService', () => {
|
||||
let scheduler: TestScheduler;
|
||||
@@ -205,6 +206,7 @@ describe('VocabularyService', () => {
|
||||
|
||||
function initTestService() {
|
||||
hrefOnlyDataService = getMockHrefOnlyDataService();
|
||||
objectCache = new ObjectCacheServiceStub() as ObjectCacheService;
|
||||
|
||||
return new VocabularyService(
|
||||
requestService,
|
||||
|
@@ -17,13 +17,14 @@ import { RestResponse } from '../cache/response.models';
|
||||
import { RequestEntry } from '../data/request-entry.model';
|
||||
import { FindListOptions } from '../data/find-list-options.model';
|
||||
import { GroupDataService } from '../eperson/group-data.service';
|
||||
import { ObjectCacheServiceStub } from '../../shared/testing/object-cache-service.stub';
|
||||
|
||||
describe('SupervisionOrderService', () => {
|
||||
let scheduler: TestScheduler;
|
||||
let service: SupervisionOrderDataService;
|
||||
let requestService: RequestService;
|
||||
let rdbService: RemoteDataBuildService;
|
||||
let objectCache: ObjectCacheService;
|
||||
let objectCache: ObjectCacheServiceStub;
|
||||
let halService: HALEndpointService;
|
||||
let responseCacheEntry: RequestEntry;
|
||||
let groupService: GroupDataService;
|
||||
@@ -127,14 +128,14 @@ describe('SupervisionOrderService', () => {
|
||||
a: 'https://rest.api/rest/api/group/groups/' + groupUUID
|
||||
}),
|
||||
});
|
||||
objectCache = {} as ObjectCacheService;
|
||||
objectCache = new ObjectCacheServiceStub();
|
||||
const notificationsService = {} as NotificationsService;
|
||||
const comparator = {} as any;
|
||||
|
||||
service = new SupervisionOrderDataService(
|
||||
requestService,
|
||||
rdbService,
|
||||
objectCache,
|
||||
objectCache as ObjectCacheService,
|
||||
halService,
|
||||
notificationsService,
|
||||
comparator,
|
||||
|
31
src/app/shared/testing/object-cache-service.stub.ts
Normal file
31
src/app/shared/testing/object-cache-service.stub.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { CacheableObject } from '../../core/cache/cacheable-object.model';
|
||||
import { ObjectCacheEntry } from '../../core/cache/object-cache.reducer';
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/**
|
||||
* Stub class of {@link ObjectCacheService}
|
||||
*/
|
||||
export class ObjectCacheServiceStub {
|
||||
|
||||
add(_object: CacheableObject, _msToLive: number, _requestUUID: string, _alternativeLink?: string): void {
|
||||
}
|
||||
|
||||
remove(_href: string): void {
|
||||
}
|
||||
|
||||
getByHref(_href: string): Observable<ObjectCacheEntry> {
|
||||
return observableOf(undefined);
|
||||
}
|
||||
|
||||
hasByHref$(_href: string): Observable<boolean> {
|
||||
return observableOf(false);
|
||||
}
|
||||
|
||||
addDependency(_href$: string | Observable<string>, _dependsOnHref$: string | Observable<string>): void {
|
||||
}
|
||||
|
||||
removeDependents(_href: string): void {
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user