Skip to content

Commit 4d89b6d

Browse files
author
Jens Vannerum
committed
140390: DO show replace button for new versions but not for (unversioned) new submission + cleanup code for nested subscriptions
1 parent b1bf764 commit 4d89b6d

2 files changed

Lines changed: 75 additions & 12 deletions

File tree

src/app/submission/sections/upload/file/section-upload-file.component.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@ import {
1313
waitForAsync,
1414
} from '@angular/core/testing';
1515
import { By } from '@angular/platform-browser';
16+
import { LinkService } from '@dspace/core/cache/builders/link.service';
1617
import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service';
18+
import { ItemDataService } from '@dspace/core/data/item-data.service';
1719
import { JsonPatchOperationPathCombiner } from '@dspace/core/json-patch/builder/json-patch-operation-path-combiner';
1820
import { JsonPatchOperationsBuilder } from '@dspace/core/json-patch/builder/json-patch-operations-builder';
1921
import { HALEndpointService } from '@dspace/core/shared/hal-endpoint.service';
2022
import { SubmissionJsonPatchOperationsService } from '@dspace/core/submission/submission-json-patch-operations.service';
2123
import { AuthorizationDataServiceStub } from '@dspace/core/testing/authorization-service.stub';
2224
import { HALEndpointServiceStub } from '@dspace/core/testing/hal-endpoint-service.stub';
25+
import { getMockLinkService } from '@dspace/core/testing/link-service.mock';
2326
import { getMockSectionUploadService } from '@dspace/core/testing/section-upload.service.mock';
2427
import { SubmissionJsonPatchOperationsServiceStub } from '@dspace/core/testing/submission-json-patch-operations-service.stub';
2528
import { SubmissionServiceStub } from '@dspace/core/testing/submission-service.stub';
@@ -29,6 +32,8 @@ import {
2932
NgbModal,
3033
NgbModule,
3134
} from '@ng-bootstrap/ng-bootstrap';
35+
import { Store } from '@ngrx/store';
36+
import { provideMockStore } from '@ngrx/store/testing';
3237
import { TranslateModule } from '@ngx-translate/core';
3338
import { of } from 'rxjs';
3439

@@ -112,12 +117,15 @@ describe('SubmissionSectionUploadFileComponent', () => {
112117
{ provide: SubmissionService, useValue: submissionServiceStub },
113118
{ provide: SectionUploadService, useValue: getMockSectionUploadService() },
114119
{ provide: ThemeService, useValue: getMockThemeService() },
120+
{ provide: Store, useValue: provideMockStore() },
121+
{ provide: ItemDataService, useValue: {} },
115122
ChangeDetectorRef,
116123
NgbModal,
117124
SubmissionSectionUploadFileComponent,
118125
SubmissionSectionUploadFileEditComponent,
119126
FormBuilderService,
120127
{ provide: AuthorizationDataService, useValue: new AuthorizationDataServiceStub() },
128+
{ provide: LinkService, useValue: getMockLinkService() },
121129
],
122130
})
123131
.overrideComponent(SubmissionSectionUploadFileComponent, {

src/app/submission/sections/upload/file/section-upload-file.component.ts

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,18 @@ import {
1111
import { SubmissionFormsModel } from '@dspace/core/config/models/config-submission-forms.model';
1212
import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service';
1313
import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id';
14+
import { ItemDataService } from '@dspace/core/data/item-data.service';
1415
import { JsonPatchOperationPathCombiner } from '@dspace/core/json-patch/builder/json-patch-operation-path-combiner';
1516
import { JsonPatchOperationsBuilder } from '@dspace/core/json-patch/builder/json-patch-operations-builder';
1617
import { Bitstream } from '@dspace/core/shared/bitstream.model';
18+
import { followLink } from '@dspace/core/shared/follow-link-config.model';
1719
import { HALEndpointService } from '@dspace/core/shared/hal-endpoint.service';
20+
import { Item } from '@dspace/core/shared/item.model';
21+
import {
22+
getAllSucceededRemoteData,
23+
getFirstSucceededRemoteDataPayload,
24+
getRemoteDataPayload,
25+
} from '@dspace/core/shared/operators';
1826
import { WorkspaceitemSectionUploadFileObject } from '@dspace/core/submission/models/workspaceitem-section-upload-file.model';
1927
import { SubmissionJsonPatchOperationsService } from '@dspace/core/submission/submission-json-patch-operations.service';
2028
import { SubmissionScopeType } from '@dspace/core/submission/submission-scope-type';
@@ -30,7 +38,9 @@ import { DynamicFormControlModel } from '@ng-dynamic-forms/core';
3038
import { TranslateModule } from '@ngx-translate/core';
3139
import {
3240
BehaviorSubject,
41+
combineLatest,
3342
Observable,
43+
of,
3444
Subscription,
3545
} from 'rxjs';
3646
import {
@@ -210,6 +220,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
210220
* @param {SectionUploadService} uploadService
211221
* @param {AuthorizationDataService} authorizationService
212222
* @param {HALEndpointService} halService
223+
* @param {ItemDataService} itemDataService
213224
*/
214225
constructor(
215226
private formService: FormService,
@@ -220,6 +231,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
220231
private uploadService: SectionUploadService,
221232
private authorizationService: AuthorizationDataService,
222233
private halService: HALEndpointService,
234+
private itemDataService: ItemDataService,
223235
) {
224236
this.readMode = true;
225237
}
@@ -250,23 +262,66 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
250262
this.processingSaveStatus$ = this.submissionService.getSubmissionSaveProcessingStatus(this.submissionId);
251263
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionId);
252264
this.loadFormMetadata();
265+
this.initReplaceButtonVisibility();
266+
}
267+
268+
/**
269+
* Sets up the subscription that drives {@link showReplaceButton$}. The version check
270+
* (`isVersionedSubmission`) is kept outside the per-file `switchMap` via `combineLatest`
271+
* so that repeated emissions from `getFileData` during form initialisation do not cancel
272+
* the in-flight version request before it has had a chance to resolve.
273+
*/
274+
private initReplaceButtonVisibility(): void {
275+
const scope = this.submissionService.getSubmissionScope();
276+
if (scope === SubmissionScopeType.WorkflowItem) {
277+
return;
278+
}
253279
this.subscriptions.push(
254-
this.uploadService.getFileData(this.submissionId, this.sectionId, this.fileId).pipe(
255-
filter(isNotUndefined),
256-
switchMap((fileData) => {
257-
// Replace is only meaningful for archived items being edited, not for fresh submissions.
258-
if (this.submissionService.getSubmissionScope() !== SubmissionScopeType.EditItem) {
259-
return [false];
260-
}
261-
return this.halService.getEndpoint('bitstreams').pipe(
262-
map(endpoint => `${endpoint}/${fileData.uuid}`),
263-
switchMap(bitstreamUrl => this.authorizationService.isAuthorized(FeatureID.CanReplaceBitstreamSubmitter, bitstreamUrl)),
264-
);
265-
}),
280+
combineLatest([
281+
this.isVersionedSubmission(scope),
282+
this.uploadService.getFileData(this.submissionId, this.sectionId, this.fileId).pipe(
283+
filter(isNotUndefined),
284+
switchMap((fileData) => this.isAuthorizedToReplace(fileData.uuid)),
285+
),
286+
]).pipe(
287+
map(([isVersioned, isAuthorized]) => isVersioned && isAuthorized),
266288
).subscribe((canReplace) => this.showReplaceButton$.next(canReplace)),
267289
);
268290
}
269291

292+
/**
293+
* Returns `true` when the current submission is a new-version workspace item (i.e. its item
294+
* already belongs to a version history), or unconditionally `true` for EditItem scope where
295+
* versioning does not apply.
296+
*/
297+
private isVersionedSubmission(scope: SubmissionScopeType): Observable<boolean> {
298+
if (scope !== SubmissionScopeType.WorkspaceItem) {
299+
return of(true);
300+
}
301+
return this.submissionService.retrieveSubmission(this.submissionId).pipe(
302+
getAllSucceededRemoteData(),
303+
getRemoteDataPayload(),
304+
switchMap((submissionObject) =>
305+
this.itemDataService.findByHref(submissionObject._links.item.href, true, true, followLink('version')).pipe(
306+
getFirstSucceededRemoteDataPayload(),
307+
switchMap((item: Item) => item.version),
308+
getFirstSucceededRemoteDataPayload(),
309+
map((version) => hasValue(version)),
310+
),
311+
),
312+
);
313+
}
314+
315+
/**
316+
* Returns whether the current user is authorized to replace the given bitstream.
317+
*/
318+
private isAuthorizedToReplace(bitstreamUuid: string): Observable<boolean> {
319+
return this.halService.getEndpoint('bitstreams').pipe(
320+
map((endpoint) => `${endpoint}/${bitstreamUuid}`),
321+
switchMap((bitstreamUrl) => this.authorizationService.isAuthorized(FeatureID.CanReplaceBitstreamSubmitter, bitstreamUrl)),
322+
);
323+
}
324+
270325
/**
271326
* Show confirmation dialog for delete
272327
*/

0 commit comments

Comments
 (0)