From e1f940668a909c21ce1494719df8e61fec0e8149 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 26 Feb 2020 17:50:44 +0100 Subject: [PATCH 1/7] 69115: MyDSpace ClaimedTask proper link resolving --- .../+my-dspace-page/my-dspace-page.component.ts | 7 ++++++- src/app/core/eperson/group-data.service.ts | 3 +++ src/app/core/shared/search/search.service.ts | 12 +++++++----- .../tasks/models/claimed-task-object.model.ts | 6 +++--- src/app/core/tasks/models/task-object.model.ts | 7 ++++--- ...item-metadata-list-element.component.spec.ts | 4 ++-- ...ed-search-result-list-element.component.html | 14 ++++++++------ ...search-result-list-element.component.spec.ts | 10 +++++++--- ...imed-search-result-list-element.component.ts | 17 ++--------------- 9 files changed, 42 insertions(+), 38 deletions(-) diff --git a/src/app/+my-dspace-page/my-dspace-page.component.ts b/src/app/+my-dspace-page/my-dspace-page.component.ts index 6556eaaf1f..592984d250 100644 --- a/src/app/+my-dspace-page/my-dspace-page.component.ts +++ b/src/app/+my-dspace-page/my-dspace-page.component.ts @@ -29,6 +29,8 @@ import { ViewMode } from '../core/shared/view-mode.model'; import { MyDSpaceRequest } from '../core/data/request.models'; import { SearchResult } from '../shared/search/search-result.model'; import { Context } from '../core/shared/context.model'; +import { followLink } from '../shared/utils/follow-link-config.model'; +import { ClaimedTask } from '../core/tasks/models/claimed-task-object.model'; export const MYDSPACE_ROUTE = '/mydspace'; export const SEARCH_CONFIG_SERVICE: InjectionToken = new InjectionToken('searchConfigurationService'); @@ -126,7 +128,10 @@ export class MyDSpacePageComponent implements OnInit { this.searchOptions$ = this.searchConfigService.paginatedSearchOptions; this.sub = this.searchOptions$.pipe( tap(() => this.resultsRD$.next(null)), - switchMap((options: PaginatedSearchOptions) => this.service.search(options).pipe(getSucceededRemoteData()))) + switchMap((options: PaginatedSearchOptions) => + this.service.search(options, undefined, + followLink('workflowitem', undefined, followLink('item'), followLink('submitter')), + followLink('owner')).pipe(getSucceededRemoteData()))) .subscribe((results) => { this.resultsRD$.next(results); }); diff --git a/src/app/core/eperson/group-data.service.ts b/src/app/core/eperson/group-data.service.ts index 2beeb588a9..532f42323a 100644 --- a/src/app/core/eperson/group-data.service.ts +++ b/src/app/core/eperson/group-data.service.ts @@ -18,6 +18,8 @@ import { RemoteData } from '../data/remote-data'; import { PaginatedList } from '../data/paginated-list'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; +import { dataService } from '../cache/builders/build-decorators'; +import { GROUP } from './models/group.resource-type'; /** * Provides methods to retrieve eperson group resources. @@ -25,6 +27,7 @@ import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; @Injectable({ providedIn: 'root' }) +@dataService(GROUP) export class GroupDataService extends DataService { protected linkPath = 'groups'; protected browseEndpoint = ''; diff --git a/src/app/core/shared/search/search.service.ts b/src/app/core/shared/search/search.service.ts index 1a016e64f8..06dfd6dba0 100644 --- a/src/app/core/shared/search/search.service.ts +++ b/src/app/core/shared/search/search.service.ts @@ -2,7 +2,7 @@ import { combineLatest as observableCombineLatest, Observable, of as observableO import { Injectable, OnDestroy } from '@angular/core'; import { NavigationExtras, Router } from '@angular/router'; import { first, map, switchMap, tap } from 'rxjs/operators'; -import { followLink } from '../../../shared/utils/follow-link-config.model'; +import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; import { LinkService } from '../../cache/builders/link.service'; import { FacetConfigSuccessResponse, FacetValueSuccessResponse, SearchSuccessResponse } from '../../cache/response.models'; import { PaginatedList } from '../../data/paginated-list'; @@ -107,10 +107,11 @@ export class SearchService implements OnDestroy { * Method to retrieve a paginated list of search results from the server * @param {PaginatedSearchOptions} searchOptions The configuration necessary to perform this search * @param responseMsToLive The amount of milliseconds for the response to live in cache + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved * @returns {Observable>>>} Emits a paginated list with all search results found */ - search(searchOptions?: PaginatedSearchOptions, responseMsToLive?: number): Observable>>> { - return this.getPaginatedResults(this.searchEntries(searchOptions)); + search(searchOptions?: PaginatedSearchOptions, responseMsToLive?: number, ...linksToFollow: Array>): Observable>>> { + return this.getPaginatedResults(this.searchEntries(searchOptions), ...linksToFollow); } /** @@ -151,9 +152,10 @@ export class SearchService implements OnDestroy { /** * Method to convert the parsed responses into a paginated list of search results * @param searchEntries: The request entries from the search method + * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved * @returns {Observable>>>} Emits a paginated list with all search results found */ - getPaginatedResults(searchEntries: Observable<{ searchOptions: PaginatedSearchOptions, requestEntry: RequestEntry }>): Observable>>> { + getPaginatedResults(searchEntries: Observable<{ searchOptions: PaginatedSearchOptions, requestEntry: RequestEntry }>, ...linksToFollow: Array>): Observable>>> { const requestEntryObs: Observable = searchEntries.pipe( map((entry) => entry.requestEntry), ); @@ -174,7 +176,7 @@ export class SearchService implements OnDestroy { }), // Send a request for each item to ensure fresh cache tap((reqs: RestRequest[]) => reqs.forEach((req: RestRequest) => this.requestService.configure(req))), - map((reqs: RestRequest[]) => reqs.map((req: RestRequest) => this.rdb.buildSingle(req.href))), + map((reqs: RestRequest[]) => reqs.map((req: RestRequest) => this.rdb.buildSingle(req.href, ...linksToFollow))), switchMap((input: Array>>) => this.rdb.aggregate(input)), ); diff --git a/src/app/core/tasks/models/claimed-task-object.model.ts b/src/app/core/tasks/models/claimed-task-object.model.ts index 9ec28bc2e0..3ea78595f2 100644 --- a/src/app/core/tasks/models/claimed-task-object.model.ts +++ b/src/app/core/tasks/models/claimed-task-object.model.ts @@ -1,6 +1,5 @@ import { inheritSerialization } from 'cerialize'; -import { typedObject } from '../../cache/builders/build-decorators'; -import { DSpaceObject } from '../../shared/dspace-object.model'; +import { inheritLinkAnnotations, typedObject } from '../../cache/builders/build-decorators'; import { CLAIMED_TASK } from './claimed-task-object.resource-type'; import { TaskObject } from './task-object.model'; @@ -8,7 +7,8 @@ import { TaskObject } from './task-object.model'; * A model class for a ClaimedTask. */ @typedObject -@inheritSerialization(DSpaceObject) +@inheritSerialization(TaskObject) +@inheritLinkAnnotations(TaskObject) export class ClaimedTask extends TaskObject { static type = CLAIMED_TASK; } diff --git a/src/app/core/tasks/models/task-object.model.ts b/src/app/core/tasks/models/task-object.model.ts index ac659ca9e5..b56cec3a7e 100644 --- a/src/app/core/tasks/models/task-object.model.ts +++ b/src/app/core/tasks/models/task-object.model.ts @@ -12,6 +12,7 @@ import { DSpaceObject } from '../../shared/dspace-object.model'; import { HALLink } from '../../shared/hal-link.model'; import { WorkflowItem } from '../../submission/models/workflowitem.model'; import { TASK_OBJECT } from './task-object.resource-type'; +import { WORKFLOWITEM } from '../../eperson/models/workflowitem.resource-type'; /** * An abstract model class for a TaskObject. @@ -45,7 +46,7 @@ export class TaskObject extends DSpaceObject implements CacheableObject { @deserialize _links: { self: HALLink; - eperson: HALLink; + owner: HALLink; group: HALLink; workflowitem: HALLink; }; @@ -54,7 +55,7 @@ export class TaskObject extends DSpaceObject implements CacheableObject { * The EPerson for this task * Will be undefined unless the eperson {@link HALLink} has been resolved. */ - @link(EPERSON) + @link(EPERSON, false, 'owner') eperson?: Observable>; /** @@ -68,7 +69,7 @@ export class TaskObject extends DSpaceObject implements CacheableObject { * The WorkflowItem for this task * Will be undefined unless the workflowitem {@link HALLink} has been resolved. */ - @link(WorkflowItem.type) + @link(WORKFLOWITEM) workflowitem?: Observable> | WorkflowItem; } diff --git a/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts b/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts index 37cbe47c72..4f899edaa8 100644 --- a/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts @@ -27,12 +27,12 @@ describe('OrgUnitItemMetadataListElementComponent', () => { }).compileComponents(); })); - beforeEach(async(() => { + beforeEach(() => { fixture = TestBed.createComponent(OrgUnitItemMetadataListElementComponent); comp = fixture.componentInstance; comp.metadataRepresentation = mockItemMetadataRepresentation; fixture.detectChanges(); - })); + }); it('should show the name of the organisation as a link', () => { const linkText = fixture.debugElement.query(By.css('a')).nativeElement.textContent; diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.html index 5ec4c9a5b5..5e988a5b7e 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.html @@ -1,7 +1,9 @@ - + + - + + diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.spec.ts index cce134f806..24b8adb5b4 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.spec.ts @@ -12,6 +12,7 @@ import { WorkflowItem } from '../../../../core/submission/models/workflowitem.mo import { createSuccessfulRemoteDataObject } from '../../../testing/utils'; import { ClaimedTaskSearchResult } from '../../../object-collection/shared/claimed-task-search-result.model'; import { TruncatableService } from '../../../truncatable/truncatable.service'; +import { VarDirective } from '../../../utils/var.directive'; let component: ClaimedSearchResultListElementComponent; let fixture: ComponentFixture; @@ -59,7 +60,7 @@ describe('ClaimedSearchResultListElementComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [NoopAnimationsModule], - declarations: [ClaimedSearchResultListElementComponent], + declarations: [ClaimedSearchResultListElementComponent, VarDirective], providers: [ { provide: TruncatableService, useValue: {} }, ], @@ -79,8 +80,11 @@ describe('ClaimedSearchResultListElementComponent', () => { fixture.detectChanges(); }); - it('should init item properly', () => { - expect(component.workflowitem).toEqual(workflowitem); + it('should init item properly', (done) => { + component.workflowitemRD$.subscribe((workflowitemRD) => { + expect(workflowitemRD.payload).toEqual(workflowitem); + done(); + }); }); it('should have properly status', () => { diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts index 35371f40aa..ea8bf30f04 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts @@ -2,11 +2,9 @@ import { Component } from '@angular/core'; import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common'; import { Observable } from 'rxjs'; -import { find } from 'rxjs/operators'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { RemoteData } from '../../../../core/data/remote-data'; -import { isNotUndefined } from '../../../empty.util'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; @@ -40,24 +38,13 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle /** * The workflowitem object that belonging to the result object */ - public workflowitem: WorkflowItem; + public workflowitemRD$: Observable>; /** * Initialize all instance variables */ ngOnInit() { super.ngOnInit(); - this.initWorkflowItem(this.dso.workflowitem as Observable>); - } - - /** - * Retrieve workflowitem from result object - */ - initWorkflowItem(wfi$: Observable>) { - wfi$.pipe( - find((rd: RemoteData) => (rd.hasSucceeded && isNotUndefined(rd.payload))) - ).subscribe((rd: RemoteData) => { - this.workflowitem = rd.payload; - }); + this.workflowitemRD$ = this.dso.workflowitem as Observable>; } } From 64c96c78f0bb9c154e5798cb8748f0889b462411 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 3 Mar 2020 12:09:33 +0100 Subject: [PATCH 2/7] Fixed issue while retrieve undefined item's bitstreams on ItemDetailPreviewComponent --- src/app/core/data/bitstream-data.service.ts | 6 +++--- .../item-detail-preview.component.html | 4 ++-- .../item-detail-preview.component.spec.ts | 20 ++++++++++++------ .../item-detail-preview.component.ts | 21 +++++-------------- 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/app/core/data/bitstream-data.service.ts b/src/app/core/data/bitstream-data.service.ts index 408dceb56e..c571c7f96c 100644 --- a/src/app/core/data/bitstream-data.service.ts +++ b/src/app/core/data/bitstream-data.service.ts @@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs/internal/Observable'; import { map, switchMap } from 'rxjs/operators'; -import { hasValue } from '../../shared/empty.util'; +import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { dataService } from '../cache/builders/build-decorators'; @@ -72,7 +72,7 @@ export class BitstreamDataService extends DataService { public getThumbnailFor(item: Item): Observable> { return this.bundleService.findByItemAndName(item, 'THUMBNAIL').pipe( switchMap((bundleRD: RemoteData) => { - if (hasValue(bundleRD.payload)) { + if (isNotEmpty(bundleRD.payload)) { return this.findAllByBundle(bundleRD.payload, { elementsPerPage: 1 }).pipe( map((bitstreamRD: RemoteData>) => { if (hasValue(bitstreamRD.payload) && hasValue(bitstreamRD.payload.page)) { @@ -108,7 +108,7 @@ export class BitstreamDataService extends DataService { public getMatchingThumbnail(item: Item, bitstreamInOriginal: Bitstream): Observable> { return this.bundleService.findByItemAndName(item, 'THUMBNAIL').pipe( switchMap((bundleRD: RemoteData) => { - if (hasValue(bundleRD.payload)) { + if (isNotEmpty(bundleRD.payload)) { return this.findAllByBundle(bundleRD.payload, { elementsPerPage: Number.MAX_SAFE_INTEGER }).pipe( map((bitstreamRD: RemoteData>) => { if (hasValue(bitstreamRD.payload) && hasValue(bitstreamRD.payload.page)) { diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html index ab2c24c435..2e6951c94d 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html @@ -10,9 +10,9 @@
- + - +