Skip to content

Commit fb7f30c

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 fb7f30c

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,15 @@ 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';
1718
import { JsonPatchOperationPathCombiner } from '@dspace/core/json-patch/builder/json-patch-operation-path-combiner';
1819
import { JsonPatchOperationsBuilder } from '@dspace/core/json-patch/builder/json-patch-operations-builder';
1920
import { HALEndpointService } from '@dspace/core/shared/hal-endpoint.service';
2021
import { SubmissionJsonPatchOperationsService } from '@dspace/core/submission/submission-json-patch-operations.service';
2122
import { AuthorizationDataServiceStub } from '@dspace/core/testing/authorization-service.stub';
2223
import { HALEndpointServiceStub } from '@dspace/core/testing/hal-endpoint-service.stub';
24+
import { getMockLinkService } from '@dspace/core/testing/link-service.mock';
2325
import { getMockSectionUploadService } from '@dspace/core/testing/section-upload.service.mock';
2426
import { SubmissionJsonPatchOperationsServiceStub } from '@dspace/core/testing/submission-json-patch-operations-service.stub';
2527
import { SubmissionServiceStub } from '@dspace/core/testing/submission-service.stub';
@@ -51,6 +53,9 @@ import { POLICY_DEFAULT_WITH_LIST } from '../section-upload-constants';
5153
import { SubmissionSectionUploadFileEditComponent } from './edit/section-upload-file-edit.component';
5254
import { SubmissionSectionUploadFileComponent } from './section-upload-file.component';
5355
import { SubmissionSectionUploadFileViewComponent } from './view/section-upload-file-view.component';
56+
import { Store } from '@ngrx/store';
57+
import { provideMockStore } from '@ngrx/store/testing';
58+
import { ItemDataService } from '@dspace/core/data/item-data.service';
5459

5560
const configMetadataFormMock = {
5661
rows: [{
@@ -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)