Skip to content

Commit ab87f42

Browse files
committed
Fix cache invalidation issues pt. 2
Old Bitstream UUID is not guaranteed to exist We also have to invalidate the parent Item in order to refresh derived Bundle lists Drop /replace URL from history after successful upload (won't be accessible anymore)
1 parent a7552ca commit ab87f42

2 files changed

Lines changed: 67 additions & 31 deletions

File tree

src/app/bitstream-page/replace-bitstream-page/replace-bitstream-page.component.spec.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ import {
1616
Router,
1717
} from '@angular/router';
1818
import { AuthService } from '@dspace/core/auth/auth.service';
19+
import { LinkService } from '@dspace/core/cache/builders/link.service';
1920
import { RequestService } from '@dspace/core/data/request.service';
2021
import { LocaleService } from '@dspace/core/locale/locale.service';
2122
import { NotificationsService } from '@dspace/core/notification-system/notifications.service';
2223
import { Bitstream } from '@dspace/core/shared/bitstream.model';
2324
import { Bundle } from '@dspace/core/shared/bundle.model';
25+
import { Item } from '@dspace/core/shared/item.model';
2426
import { AuthServiceStub } from '@dspace/core/testing/auth-service.stub';
27+
import { getMockLinkService } from '@dspace/core/testing/link-service.mock';
2528
import { NotificationsServiceStub } from '@dspace/core/testing/notifications-service.stub';
2629
import { RouterStub } from '@dspace/core/testing/router.stub';
2730
import {
@@ -42,22 +45,44 @@ describe('ReplaceBitstreamPageComponent', () => {
4245
let fixture: ComponentFixture<ReplaceBitstreamPageComponent>;
4346
const locationObject = jasmine.createSpyObj('location', ['back']);
4447
let notificationsService;
45-
const bitstreamSelfLink = 'bitstreams/123';
48+
const oldBitstreamSelfLink = 'bitstreams/123';
49+
const itemSelfLink = 'items/789';
50+
const item = Object.assign(new Item(), {
51+
_links: {
52+
self: { href: itemSelfLink },
53+
},
54+
});
4655
const bundleSelfLink = 'bundles/456';
4756
const bundle = Object.assign(new Bundle(), {
4857
_links: {
4958
self: { href: bundleSelfLink },
5059
},
60+
item: createSuccessfulRemoteDataObject$(item),
5161
});
52-
const bitstream = Object.assign(new Bitstream(), {
62+
const oldBitstream = Object.assign(new Bitstream(), {
5363
id: '123',
5464
_links: {
55-
self: { href: bitstreamSelfLink },
65+
self: { href: oldBitstreamSelfLink },
5666
bundle: { href: 'bitstreams/123/bundle' },
5767
},
5868
bundle: createSuccessfulRemoteDataObject$(bundle),
5969
});
60-
const route = { data: of({ bitstream: createSuccessfulRemoteDataObject(bitstream) }) };
70+
const newBitstream = Object.assign(new Bitstream(), {
71+
id: '124',
72+
metadata: {
73+
['dspace.bitstream.isReplacementOf']: [
74+
{
75+
authority: oldBitstream.id,
76+
},
77+
],
78+
},
79+
_links: {
80+
self: { href: oldBitstreamSelfLink },
81+
bundle: { href: 'bitstreams/124/bundle' },
82+
},
83+
bundle: createSuccessfulRemoteDataObject$(bundle),
84+
});
85+
const route = { data: of({ bitstream: createSuccessfulRemoteDataObject(oldBitstream) }) };
6186
const requestService = jasmine.createSpyObj('requestService', ['setStaleByHrefSubstring']);
6287
const router = new RouterStub();
6388
let localeService;
@@ -82,6 +107,7 @@ describe('ReplaceBitstreamPageComponent', () => {
82107
{ provide: Router, useValue: router },
83108
{ provide: RequestService, useValue: requestService },
84109
{ provide: LocaleService, useValue: mockLocaleService },
110+
{ provide: LinkService, useValue: getMockLinkService() },
85111
],
86112
}).overrideComponent(ReplaceBitstreamPageComponent, {
87113
remove: { imports: [UploaderComponent] },
@@ -133,12 +159,13 @@ describe('ReplaceBitstreamPageComponent', () => {
133159

134160
describe('calling onCompleteItem', () => {
135161
beforeEach(() => {
136-
component.onCompleteItem(bitstream);
162+
component.onCompleteItem(newBitstream);
137163
});
138164

139165
it('should set the bitstream and bundle to stale in the cache', () => {
140-
expect(requestService.setStaleByHrefSubstring).toHaveBeenCalledWith(bitstreamSelfLink);
166+
expect(requestService.setStaleByHrefSubstring).toHaveBeenCalledWith(oldBitstream.id);
141167
expect(requestService.setStaleByHrefSubstring).toHaveBeenCalledWith(bundleSelfLink);
168+
expect(requestService.setStaleByHrefSubstring).toHaveBeenCalledWith(itemSelfLink);
142169
});
143170

144171
it('should fire a success notification', () => {

src/app/bitstream-page/replace-bitstream-page/replace-bitstream-page.component.ts

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ import {
1212
Router,
1313
} from '@angular/router';
1414
import { RestRequestMethod } from '@dspace/config/rest-request-method';
15+
import { LinkService } from '@dspace/core/cache/builders/link.service';
1516
import { NotificationsService } from '@dspace/core/notification-system/notifications.service';
1617
import { getBitstreamModuleRoute } from '@dspace/core/router/core-routing-paths';
18+
import { Bundle } from '@dspace/core/shared/bundle.model';
19+
import { followLink } from '@dspace/core/shared/follow-link-config.model';
20+
import { Item } from '@dspace/core/shared/item.model';
1721
import {
1822
hasValue,
1923
isEmpty,
@@ -91,12 +95,6 @@ export class ReplaceBitstreamPageComponent implements OnInit {
9195
*/
9296
private uploadFilesUrlNoParam: string;
9397

94-
/**
95-
* The bundle's self link, resolved from the route-resolved bitstream for cache invalidation.
96-
* This is the bundle's own URL (e.g. bundles/{uuid}), not the bitstream-to-bundle link.
97-
*/
98-
private bundleSelfLink: string;
99-
10098
/**
10199
* The bitstream's remote data observable
102100
* Tracks changes and updates the view
@@ -108,19 +106,21 @@ export class ReplaceBitstreamPageComponent implements OnInit {
108106
*/
109107
protected shouldReplaceName = true;
110108

111-
constructor(private route: ActivatedRoute,
112-
private notificationService: NotificationsService,
113-
private location: Location,
114-
private translateService: TranslateService,
115-
private authService: AuthService,
116-
private requestService: RequestService,
117-
private router: Router) {
109+
constructor(
110+
private route: ActivatedRoute,
111+
private notificationService: NotificationsService,
112+
private location: Location,
113+
private translateService: TranslateService,
114+
private authService: AuthService,
115+
private requestService: RequestService,
116+
private router: Router,
117+
private linkService: LinkService,
118+
) {
118119
}
119120

120121
ngOnInit(): void {
121122
this.bitstreamRD$ = this.route.data.pipe(map((data) => data.bitstream));
122123
this.setUploadUrl();
123-
this.resolveBundleLink();
124124
}
125125

126126
back() {
@@ -137,10 +137,11 @@ export class ReplaceBitstreamPageComponent implements OnInit {
137137
* @param bitstream
138138
*/
139139
public onCompleteItem(bitstream: Bitstream) {
140-
this.requestService.setStaleByHrefSubstring(bitstream.self);
141-
this.requestService.setStaleByHrefSubstring(this.bundleSelfLink);
140+
this.invalidate(bitstream);
142141
this.notificationService.success(this.translateService.instant(this.saveNotificationKey));
143-
this.router.navigate([getBitstreamModuleRoute(), bitstream.id, 'edit']);
142+
this.router.navigate([getBitstreamModuleRoute(), bitstream.id, 'edit'], {
143+
replaceUrl: true,
144+
});
144145
}
145146

146147
/**
@@ -167,17 +168,25 @@ export class ReplaceBitstreamPageComponent implements OnInit {
167168
}
168169

169170
/**
170-
* Resolve the bundle from the route-resolved bitstream to store its self URL.
171-
* The bundle's self URL (bundles/{uuid}) is needed for cache invalidation because it is a
172-
* substring of the bundle's bitstreams list endpoint (bundles/{uuid}/bitstreams).
171+
* Invalidate the old Bitstream that has been replaced (and thus deleted), as well as our owning Bundle and Item
173172
*/
174-
private resolveBundleLink() {
175-
this.bitstreamRD$.pipe(
173+
private invalidate(newBitstream: Bitstream) {
174+
console.log(newBitstream); // todo: remove this
175+
// the Bitstream returned after upload is not an instance of Bitstream yet
176+
newBitstream = Object.assign(new Bitstream(), newBitstream)
177+
this.linkService.resolveLink<Bitstream>(newBitstream, followLink('bundle'));
178+
this.requestService.setStaleByHrefSubstring(newBitstream.firstMetadata('dspace.bitstream.isReplacementOf')?.authority);
179+
180+
newBitstream.bundle.pipe(
176181
getFirstSucceededRemoteDataPayload(),
177-
switchMap((bitstream: Bitstream) => bitstream.bundle),
182+
switchMap((bundle: Bundle) => {
183+
this.requestService.setStaleByHrefSubstring(bundle.self);
184+
this.linkService.resolveLink<Bundle>(bundle, followLink('item'));
185+
return bundle.item;
186+
}),
178187
getFirstSucceededRemoteDataPayload(),
179-
).subscribe((bundle) => {
180-
this.bundleSelfLink = bundle._links.self.href;
188+
).subscribe((item: Item) => {
189+
this.requestService.setStaleByHrefSubstring(item.self);
181190
});
182191
}
183192

0 commit comments

Comments
 (0)