Skip to content

Commit 60d93e6

Browse files
113938: Added missing comcol structure to workspace/workflow item breadcrumbs
1 parent 692bb99 commit 60d93e6

14 files changed

Lines changed: 222 additions & 25 deletions

src/app/core/core.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ import { OrcidHistory } from './orcid/model/orcid-history.model';
180180
import { OrcidAuthService } from './orcid/orcid-auth.service';
181181
import { VocabularyDataService } from './submission/vocabularies/vocabulary.data.service';
182182
import { VocabularyEntryDetailsDataService } from './submission/vocabularies/vocabulary-entry-details.data.service';
183+
import { SubmissionParentBreadcrumbsService } from './submission/submission-parent-breadcrumb.service';
183184

184185
/**
185186
* When not in production, endpoint responses can be mocked for testing purposes
@@ -250,6 +251,7 @@ const PROVIDERS = [
250251
NotificationsService,
251252
WorkspaceitemDataService,
252253
WorkflowItemDataService,
254+
SubmissionParentBreadcrumbsService,
253255
UploaderService,
254256
DSpaceObjectDataService,
255257
ConfigurationDataService,
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
2+
import { WorkflowItem } from '../models/workflowitem.model';
3+
import { WorkspaceItem } from '../models/workspaceitem.model';
4+
5+
/**
6+
* The self links defined in this list are expected to be requested somewhere in the near future
7+
* Requesting them as embeds will limit the number of requests
8+
*
9+
* Needs to be in a separate file to prevent circular dependencies in webpack.
10+
*/
11+
export const SUBMISSION_LINKS_TO_FOLLOW: FollowLinkConfig<WorkflowItem | WorkspaceItem>[] = [
12+
followLink('item'),
13+
followLink('collection'),
14+
];

src/app/core/submission/resolver/submission-object.resolver.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import { followLink } from '../../../shared/utils/follow-link-config.model';
21
import { Injectable } from '@angular/core';
32
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
43
import { Observable } from 'rxjs';
5-
import { Store } from '@ngrx/store';
64
import { switchMap } from 'rxjs/operators';
75
import { RemoteData } from '../../data/remote-data';
86
import { getFirstCompletedRemoteData } from '../../shared/operators';
97
import { IdentifiableDataService } from '../../data/base/identifiable-data.service';
8+
import { SUBMISSION_LINKS_TO_FOLLOW } from './submission-links-to-follow';
109

1110
/**
1211
* This class represents a resolver that requests a specific item before the route is activated
@@ -15,7 +14,6 @@ import { IdentifiableDataService } from '../../data/base/identifiable-data.servi
1514
export class SubmissionObjectResolver<T> implements Resolve<RemoteData<T>> {
1615
constructor(
1716
protected dataService: IdentifiableDataService<any>,
18-
protected store: Store<any>,
1917
) {
2018
}
2119

@@ -30,7 +28,7 @@ export class SubmissionObjectResolver<T> implements Resolve<RemoteData<T>> {
3028
const itemRD$ = this.dataService.findById(route.params.id,
3129
true,
3230
false,
33-
followLink('item'),
31+
...SUBMISSION_LINKS_TO_FOLLOW,
3432
).pipe(
3533
getFirstCompletedRemoteData(),
3634
switchMap((wfiRD: RemoteData<any>) => wfiRD.payload.item as Observable<RemoteData<T>>),
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
2+
import { Observable } from 'rxjs';
3+
import { map } from 'rxjs/operators';
4+
import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../shared/operators';
5+
import { IdentifiableDataService } from '../../data/base/identifiable-data.service';
6+
import { BreadcrumbConfig } from '../../../breadcrumbs/breadcrumb/breadcrumb-config.model';
7+
import { SubmissionParentBreadcrumbsService } from '../submission-parent-breadcrumb.service';
8+
import { SUBMISSION_LINKS_TO_FOLLOW } from './submission-links-to-follow';
9+
import { SubmissionObject } from '../models/submission-object.model';
10+
11+
/**
12+
* This class represents a resolver that requests a specific item before the route is activated
13+
*/
14+
export abstract class SubmissionParentBreadcrumbResolver implements Resolve<BreadcrumbConfig<SubmissionObject>> {
15+
16+
protected constructor(
17+
protected dataService: IdentifiableDataService<any>,
18+
protected breadcrumbService: SubmissionParentBreadcrumbsService,
19+
) {
20+
}
21+
22+
/**
23+
* Method for resolving an item based on the parameters in the current route
24+
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
25+
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
26+
* @returns Observable<<RemoteData<Item>> Emits the found item based on the parameters in the current route,
27+
* or an error if something went wrong
28+
*/
29+
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<BreadcrumbConfig<SubmissionObject>> {
30+
return this.dataService.findById(route.params.id,
31+
true,
32+
false,
33+
...SUBMISSION_LINKS_TO_FOLLOW,
34+
).pipe(
35+
getFirstCompletedRemoteData(),
36+
getRemoteDataPayload(),
37+
map((submissionObject: SubmissionObject) => ({
38+
provider: this.breadcrumbService,
39+
key: submissionObject,
40+
} as BreadcrumbConfig<SubmissionObject>)),
41+
);
42+
}
43+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { BreadcrumbsProviderService } from '../breadcrumbs/breadcrumbsProviderService';
2+
import { Injectable } from '@angular/core';
3+
import { Observable, switchMap, combineLatest, of as observableOf } from 'rxjs';
4+
import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model';
5+
import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../shared/operators';
6+
import { Collection } from '../shared/collection.model';
7+
import { DSONameService } from '../breadcrumbs/dso-name.service';
8+
import { SubmissionObject } from './models/submission-object.model';
9+
import { RemoteData } from '../data/remote-data';
10+
import { DSOBreadcrumbsService } from '../breadcrumbs/dso-breadcrumbs.service';
11+
import { getDSORoute } from '../../app-routing-paths';
12+
import { SubmissionService } from '../../submission/submission.service';
13+
import { CollectionDataService } from '../data/collection-data.service';
14+
import { hasValue } from '../../shared/empty.util';
15+
16+
/**
17+
* Service to calculate the parent {@link DSpaceObject} breadcrumbs for a {@link SubmissionObject}
18+
*/
19+
@Injectable()
20+
export class SubmissionParentBreadcrumbsService implements BreadcrumbsProviderService<SubmissionObject> {
21+
22+
constructor(
23+
protected dsoNameService: DSONameService,
24+
protected dsoBreadcrumbsService: DSOBreadcrumbsService,
25+
protected submissionService: SubmissionService,
26+
protected collectionService: CollectionDataService,
27+
) {
28+
}
29+
30+
/**
31+
* Creates the parent breadcrumb structure for {@link SubmissionObject}s. It also automatically recreates the
32+
* parent breadcrumb structure when you change a {@link SubmissionObject}'s by dispatching a
33+
* {@link ChangeSubmissionCollectionAction}.
34+
*
35+
* @param submissionObject The {@link SubmissionObject} for which the parent breadcrumb structure needs to be created
36+
*/
37+
getBreadcrumbs(submissionObject: SubmissionObject): Observable<Breadcrumb[]> {
38+
return combineLatest([
39+
(submissionObject.collection as Observable<RemoteData<Collection>>).pipe(
40+
getFirstCompletedRemoteData(),
41+
getRemoteDataPayload(),
42+
),
43+
this.submissionService.getSubmissionCollectionId(submissionObject.id),
44+
]).pipe(
45+
switchMap(([collection, latestCollectionId]: [Collection, string]) => {
46+
if (hasValue(latestCollectionId)) {
47+
return this.collectionService.findById(latestCollectionId).pipe(
48+
getFirstCompletedRemoteData(),
49+
getRemoteDataPayload(),
50+
);
51+
} else {
52+
return observableOf(collection);
53+
}
54+
}),
55+
switchMap((collection: Collection) => this.dsoBreadcrumbsService.getBreadcrumbs(collection, getDSORoute(collection))),
56+
);
57+
}
58+
59+
}

src/app/submission/submission.service.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Router } from '@angular/router';
44

55
import { Observable, of as observableOf, Subscription, timer as observableTimer } from 'rxjs';
66
import { catchError, concatMap, distinctUntilChanged, filter, find, map, startWith, take, tap } from 'rxjs/operators';
7-
import { Store } from '@ngrx/store';
7+
import { Store, MemoizedSelector, createSelector, select } from '@ngrx/store';
88
import { TranslateService } from '@ngx-translate/core';
99

1010
import { submissionSelector, SubmissionState } from './submission.reducers';
@@ -47,6 +47,20 @@ import { SubmissionJsonPatchOperationsService } from '../core/submission/submiss
4747
import { SubmissionSectionObject } from './objects/submission-section-object.model';
4848
import { SubmissionError } from './objects/submission-error.model';
4949

50+
function getSubmissionSelector(submissionId: string): MemoizedSelector<SubmissionState, SubmissionObjectEntry> {
51+
return createSelector(
52+
submissionSelector,
53+
(state: SubmissionState) => state.objects[submissionId],
54+
);
55+
}
56+
57+
function getSubmissionCollectionIdSelector(submissionId: string): MemoizedSelector<SubmissionState, string> {
58+
return createSelector(
59+
getSubmissionSelector(submissionId),
60+
(submission: SubmissionObjectEntry) => submission?.collection,
61+
);
62+
}
63+
5064
/**
5165
* A service that provides methods used in submission process.
5266
*/
@@ -96,10 +110,19 @@ export class SubmissionService {
96110
* @param collectionId
97111
* The collection id
98112
*/
99-
changeSubmissionCollection(submissionId, collectionId) {
113+
changeSubmissionCollection(submissionId: string, collectionId: string): void {
100114
this.store.dispatch(new ChangeSubmissionCollectionAction(submissionId, collectionId));
101115
}
102116

117+
/**
118+
* Listen to collection changes for a certain {@link SubmissionObject}
119+
*
120+
* @param submissionId The submission id
121+
*/
122+
getSubmissionCollectionId(submissionId: string): Observable<string> {
123+
return this.store.pipe(select(getSubmissionCollectionIdSelector(submissionId)));
124+
}
125+
103126
/**
104127
* Perform a REST call to create a new workspaceitem and return response
105128
*
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Injectable } from '@angular/core';
2+
import { Resolve } from '@angular/router';
3+
import { WorkflowItemDataService } from '../core/submission/workflowitem-data.service';
4+
import { SubmissionParentBreadcrumbResolver } from '../core/submission/resolver/submission-parent-breadcrumb.resolver';
5+
import { BreadcrumbConfig } from '../breadcrumbs/breadcrumb/breadcrumb-config.model';
6+
import { SubmissionParentBreadcrumbsService } from '../core/submission/submission-parent-breadcrumb.service';
7+
import { SubmissionObject } from '../core/submission/models/submission-object.model';
8+
9+
/**
10+
* This class represents a resolver that retrieves the breadcrumbs of the workflow item
11+
*/
12+
@Injectable()
13+
export class ItemFromWorkflowBreadcrumbResolver extends SubmissionParentBreadcrumbResolver implements Resolve<BreadcrumbConfig<SubmissionObject>> {
14+
15+
constructor(
16+
protected dataService: WorkflowItemDataService,
17+
protected breadcrumbService: SubmissionParentBreadcrumbsService,
18+
) {
19+
super(dataService, breadcrumbService);
20+
}
21+
22+
}

src/app/workflowitems-edit-page/item-from-workflow.resolver.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ describe('ItemFromWorkflowResolver', () => {
1919
wfiService = {
2020
findById: (id: string) => createSuccessfulRemoteDataObject$(wfi)
2121
} as any;
22-
resolver = new ItemFromWorkflowResolver(wfiService, null);
22+
resolver = new ItemFromWorkflowResolver(wfiService);
2323
});
2424

2525
it('should resolve a an item from from the workflow item with the correct id', (done) => {

src/app/workflowitems-edit-page/item-from-workflow.resolver.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,19 @@ import { Injectable } from '@angular/core';
22
import { Resolve } from '@angular/router';
33
import { RemoteData } from '../core/data/remote-data';
44
import { Item } from '../core/shared/item.model';
5-
import { Store } from '@ngrx/store';
65
import { WorkflowItemDataService } from '../core/submission/workflowitem-data.service';
76
import { SubmissionObjectResolver } from '../core/submission/resolver/submission-object.resolver';
87

98
/**
109
* This class represents a resolver that requests a specific item before the route is activated
1110
*/
1211
@Injectable()
13-
export class ItemFromWorkflowResolver extends SubmissionObjectResolver<Item> implements Resolve<RemoteData<Item>> {
12+
export class ItemFromWorkflowResolver extends SubmissionObjectResolver<Item> implements Resolve<RemoteData<Item>> {
13+
1414
constructor(
15-
private workflowItemService: WorkflowItemDataService,
16-
protected store: Store<any>
15+
protected dataService: WorkflowItemDataService,
1716
) {
18-
super(workflowItemService, store);
17+
super(dataService);
1918
}
2019

2120
}

src/app/workflowitems-edit-page/workflowitems-edit-page-routing.module.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@ import { ThemedWorkflowItemSendBackComponent } from './workflow-item-send-back/t
1515
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
1616
import { ItemFromWorkflowResolver } from './item-from-workflow.resolver';
1717
import { ThemedFullItemPageComponent } from '../item-page/full/themed-full-item-page.component';
18+
import { ItemFromWorkflowBreadcrumbResolver } from './item-from-workflow-breadcrumb.resolver';
1819

1920
@NgModule({
2021
imports: [
2122
RouterModule.forChild([
2223
{
2324
path: ':id',
24-
resolve: { wfi: WorkflowItemPageResolver },
25+
resolve: {
26+
breadcrumb: ItemFromWorkflowBreadcrumbResolver,
27+
wfi: WorkflowItemPageResolver,
28+
},
2529
children: [
2630
{
2731
canActivate: [AuthenticatedGuard],
@@ -64,7 +68,11 @@ import { ThemedFullItemPageComponent } from '../item-page/full/themed-full-item-
6468
}]
6569
)
6670
],
67-
providers: [WorkflowItemPageResolver, ItemFromWorkflowResolver]
71+
providers: [
72+
ItemFromWorkflowBreadcrumbResolver,
73+
ItemFromWorkflowResolver,
74+
WorkflowItemPageResolver,
75+
],
6876
})
6977
/**
7078
* This module defines the default component to load when navigating to the workflowitems edit page path.

0 commit comments

Comments
 (0)