From d734ed108ef960283672ac27dd0a0fb7f2edf946 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 4 Jul 2019 17:52:14 +0200 Subject: [PATCH 01/15] 63469: Intermediate commit --- .../item-relationships.component.ts | 4 --- src/app/core/data/relationship.service.ts | 25 +++++++++++++++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.ts b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.ts index 087dea656b..e8f34bc70e 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.ts @@ -140,10 +140,6 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl this.notificationsService.error(this.getNotificationTitle('failed'), response.errorMessage); }); if (successfulResponses.length > 0) { - // Remove the item's cache to make sure the lists are reloaded with the newest values - this.objectCache.remove(this.item.self); - this.requestService.removeByHrefSubstring(this.item.self); - // Send a notification that the removal was successful this.notificationsService.success(this.getNotificationTitle('saved'), this.getNotificationContent('saved')); } } diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index fca5074a88..735674afc3 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -25,6 +25,7 @@ import { compareArraysUsingIds, filterRelationsByTypeLabel, relationsToItems } from '../../+item-page/simple/item-types/shared/item-relationships-utils'; +import { ObjectCacheService } from '../cache/object-cache.service'; /** * The service handling all relationship requests @@ -36,7 +37,8 @@ export class RelationshipService { constructor(protected requestService: RequestService, protected halService: HALEndpointService, protected rdbService: RemoteDataBuildService, - protected itemService: ItemDataService) { + protected itemService: ItemDataService, + protected objectCache: ObjectCacheService) { } /** @@ -49,6 +51,11 @@ export class RelationshipService { ); } + findById(uuid: string): Observable> { + const href$ = this.getRelationshipEndpoint(uuid); + return this.rdbService.buildSingle(href$); + } + /** * Send a delete request for a relationship by ID * @param uuid @@ -60,7 +67,8 @@ export class RelationshipService { map((endpointURL: string) => new DeleteRequest(this.requestService.generateRequestId(), endpointURL)), configureRequest(this.requestService), switchMap((restRequest: RestRequest) => this.requestService.getByUUID(restRequest.uuid)), - getResponseFromEntry() + getResponseFromEntry(), + tap(() => this.clearRelatedCache(uuid)) ); } @@ -203,4 +211,17 @@ export class RelationshipService { ); } + clearRelatedCache(uuid: string) { + this.findById(uuid).pipe( + getSucceededRemoteData(), + flatMap((rd: RemoteData) => observableCombineLatest(rd.payload.leftItem.pipe(getSucceededRemoteData()), rd.payload.rightItem.pipe(getSucceededRemoteData()))), + take(1) + ).subscribe(([leftItem, rightItem]) => { + this.objectCache.remove(leftItem.payload.self); + this.objectCache.remove(rightItem.payload.self); + this.requestService.removeByHrefSubstring(leftItem.payload.self); + this.requestService.removeByHrefSubstring(rightItem.payload.self); + }); + } + } From 8265942f18349bd8c6cb4d358f03a7209c9c968f Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 8 Jul 2019 13:41:20 +0200 Subject: [PATCH 02/15] 63469: Properly reload de-cached items --- .../+search-page/search-service/search.service.ts | 13 +++++++------ src/app/core/shared/operators.ts | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index 598657a1b2..79164b50c3 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -1,7 +1,7 @@ -import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; +import { combineLatest as observableCombineLatest, Observable, of as observableOf, zip as observableZip } from 'rxjs'; import { Injectable, OnDestroy } from '@angular/core'; import { NavigationExtras, PRIMARY_OUTLET, Router, UrlSegmentGroup } from '@angular/router'; -import { first, map, switchMap } from 'rxjs/operators'; +import { first, map, switchMap, tap } from 'rxjs/operators'; import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service'; import { FacetConfigSuccessResponse, @@ -23,7 +23,7 @@ import { getSucceededRemoteData } from '../../core/shared/operators'; import { URLCombiner } from '../../core/url-combiner/url-combiner'; -import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../shared/empty.util'; +import { hasValue, hasValueOperator, isEmpty, isNotEmpty, isNotUndefined } from '../../shared/empty.util'; import { NormalizedSearchResult } from '../normalized-search-result.model'; import { SearchOptions } from '../search-options.model'; import { SearchResult } from '../search-result.model'; @@ -137,10 +137,11 @@ export class SearchService implements OnDestroy { map((sqr: SearchQueryResponse) => { return sqr.objects .filter((nsr: NormalizedSearchResult) => isNotUndefined(nsr.indexableObject)) - .map((nsr: NormalizedSearchResult) => { - return this.rdb.buildSingle(nsr.indexableObject); - }) + .map((nsr: NormalizedSearchResult) => new GetRequest(this.requestService.generateRequestId(), nsr.indexableObject)) }), + // 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))), switchMap((input: Array>>) => this.rdb.aggregate(input)), ); diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index ae46691e39..d46c688e68 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -91,7 +91,7 @@ export const toDSpaceObjectListRD = () => source.pipe( filter((rd: RemoteData>>) => rd.hasSucceeded), map((rd: RemoteData>>) => { - const dsoPage: T[] = rd.payload.page.map((searchResult: SearchResult) => searchResult.indexableObject); + const dsoPage: T[] = rd.payload.page.filter((result) => hasValue(result)).map((searchResult: SearchResult) => searchResult.indexableObject); const payload = Object.assign(rd.payload, { page: dsoPage }) as PaginatedList; return Object.assign(rd, { payload: payload }); }) From 34e75a46e5919f4aaa436e5c5c49b9b6a8cfc889 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 8 Jul 2019 14:30:19 +0200 Subject: [PATCH 03/15] 63469: JSDocs + tests --- .../item-relationships.component.spec.ts | 4 +- .../core/data/relationship.service.spec.ts | 61 +++++++++++++------ src/app/core/data/relationship.service.ts | 8 +++ 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts index 1d3d59201b..51394ef9e5 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts @@ -227,10 +227,8 @@ describe('ItemRelationshipsComponent', () => { comp.submit(); }); - it('it should delete the correct relationship and de-cache the current item', () => { + it('it should delete the correct relationship', () => { expect(relationshipService.deleteRelationship).toHaveBeenCalledWith(relationships[1].uuid); - expect(objectCache.remove).toHaveBeenCalledWith(item.self); - expect(requestService.removeByHrefSubstring).toHaveBeenCalledWith(item.self); }); }); }); diff --git a/src/app/core/data/relationship.service.spec.ts b/src/app/core/data/relationship.service.spec.ts index ce2b169eef..88da4a5496 100644 --- a/src/app/core/data/relationship.service.spec.ts +++ b/src/app/core/data/relationship.service.spec.ts @@ -13,6 +13,8 @@ import { Item } from '../shared/item.model'; import { PaginatedList } from './paginated-list'; import { PageInfo } from '../shared/page-info.model'; import { DeleteRequest } from './request.models'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { Observable } from 'rxjs/internal/Observable'; describe('RelationshipService', () => { let service: RelationshipService; @@ -22,6 +24,11 @@ describe('RelationshipService', () => { const relationshipsEndpointURL = `${restEndpointURL}/relationships`; const halService: any = new HALEndpointServiceStub(restEndpointURL); const rdbService = getMockRemoteDataBuildService(); + const objectCache = Object.assign({ + /* tslint:disable:no-empty */ + remove: () => {} + /* tslint:enable:no-empty */ + }) as ObjectCacheService; const relationshipType = Object.assign(new RelationshipType(), { type: ResourceType.RelationshipType, @@ -31,24 +38,20 @@ describe('RelationshipService', () => { rightLabel: 'isPublicationOfAuthor' }); - const relationships = [ - Object.assign(new Relationship(), { - self: relationshipsEndpointURL + '/2', - id: '2', - uuid: '2', - leftId: 'author1', - rightId: 'publication', - relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType)) - }), - Object.assign(new Relationship(), { - self: relationshipsEndpointURL + '/3', - id: '3', - uuid: '3', - leftId: 'author2', - rightId: 'publication', - relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType)) - }) - ]; + const relationship1 = Object.assign(new Relationship(), { + self: relationshipsEndpointURL + '/2', + id: '2', + uuid: '2', + relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType)) + }); + const relationship2 = Object.assign(new Relationship(), { + self: relationshipsEndpointURL + '/3', + id: '3', + uuid: '3', + relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType)) + }); + + const relationships = [ relationship1, relationship2 ]; const item = Object.assign(new Item(), { self: 'fake-item-url/publication', @@ -65,6 +68,10 @@ describe('RelationshipService', () => { id: 'author2', uuid: 'author2' }); + relationship1.leftItem = getRemotedataObservable(relatedItem1); + relationship1.rightItem = getRemotedataObservable(item); + relationship2.leftItem = getRemotedataObservable(relatedItem2); + relationship2.rightItem = getRemotedataObservable(item); const relatedItems = [relatedItem1, relatedItem2]; const itemService = jasmine.createSpyObj('itemService', { @@ -76,7 +83,8 @@ describe('RelationshipService', () => { requestService, halService, rdbService, - itemService + itemService, + objectCache ); } @@ -93,13 +101,22 @@ describe('RelationshipService', () => { describe('deleteRelationship', () => { beforeEach(() => { + spyOn(service, 'findById').and.returnValue(getRemotedataObservable(relationship1)); + spyOn(objectCache, 'remove'); service.deleteRelationship(relationships[0].uuid).subscribe(); }); it('should send a DeleteRequest', () => { - const expected = new DeleteRequest(requestService.generateRequestId(), relationshipsEndpointURL + '/' + relationships[0].uuid); + const expected = new DeleteRequest(requestService.generateRequestId(), relationshipsEndpointURL + '/' + relationship1.uuid); expect(requestService.configure).toHaveBeenCalledWith(expected, undefined); }); + + it('should clear the related items their cache', () => { + expect(objectCache.remove).toHaveBeenCalledWith(relatedItem1.self); + expect(objectCache.remove).toHaveBeenCalledWith(item.self); + expect(requestService.removeByHrefSubstring).toHaveBeenCalledWith(relatedItem1.self); + expect(requestService.removeByHrefSubstring).toHaveBeenCalledWith(item.self); + }); }); describe('getItemRelationshipsArray', () => { @@ -135,3 +152,7 @@ describe('RelationshipService', () => { }) }); + +function getRemotedataObservable(obj: any): Observable> { + return observableOf(new RemoteData(false, false, true, undefined, obj)); +} diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index 735674afc3..1699b6a27d 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -51,6 +51,10 @@ export class RelationshipService { ); } + /** + * Find a relationship by its UUID + * @param uuid + */ findById(uuid: string): Observable> { const href$ = this.getRelationshipEndpoint(uuid); return this.rdbService.buildSingle(href$); @@ -211,6 +215,10 @@ export class RelationshipService { ); } + /** + * Clear object and request caches of the items related to a relationship (left and right items) + * @param uuid + */ clearRelatedCache(uuid: string) { this.findById(uuid).pipe( getSucceededRemoteData(), From e05f44d079f406cad7fbc31c5bc4e14bde143e72 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 13 Aug 2019 12:30:47 +0200 Subject: [PATCH 04/15] 64225: Import fix --- .../edit-item-page/item-metadata/item-metadata.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.ts b/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.ts index dbbcebfd00..be657d71dc 100644 --- a/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.ts +++ b/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.ts @@ -15,10 +15,10 @@ import { NotificationsService } from '../../../shared/notifications/notification import { GLOBAL_CONFIG, GlobalConfig } from '../../../../config'; import { TranslateService } from '@ngx-translate/core'; import { RegistryService } from '../../../core/registry/registry.service'; -import { MetadataField } from '../../../core/metadata/metadatafield.model'; import { MetadatumViewModel } from '../../../core/shared/metadata.models'; import { Metadata } from '../../../core/shared/metadata.utils'; import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component'; +import { MetadataField } from '../../../core/metadata/metadata-field.model'; @Component({ selector: 'ds-item-metadata', From fc21dc2019ee79f887b81b0006b1a1f238161e18 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 13 Aug 2019 14:46:10 +0200 Subject: [PATCH 05/15] 64225: Search results reloading correctly after object deletion --- src/app/+search-page/search-page.component.ts | 2 +- .../search-service/search.service.ts | 24 +++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 03433d1da1..c268c5b7f6 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -109,7 +109,7 @@ export class SearchPageComponent implements OnInit { ngOnInit(): void { this.searchOptions$ = this.getSearchOptions(); this.sub = this.searchOptions$.pipe( - switchMap((options) => this.service.search(options).pipe(getSucceededRemoteData(), startWith(observableOf(undefined))))) + switchMap((options) => this.service.search(options).pipe(getSucceededRemoteData(), startWith(undefined)))) .subscribe((results) => { this.resultsRD$.next(results); }); diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index 4e67fda864..be95ed096e 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -103,11 +103,18 @@ export class SearchService implements OnDestroy { * @returns {Observable>>>} Emits a paginated list with all search results found */ search(searchOptions?: PaginatedSearchOptions): Observable>>> { - const requestObs = this.halService.getEndpoint(this.searchLinkPath).pipe( + const hrefObs = this.halService.getEndpoint(this.searchLinkPath).pipe( map((url: string) => { if (hasValue(searchOptions)) { - url = (searchOptions as PaginatedSearchOptions).toRestUrl(url); + return (searchOptions as PaginatedSearchOptions).toRestUrl(url); + } else { + return url; } + }) + ); + + const requestObs = hrefObs.pipe( + map((url: string) => { const request = new this.request(this.requestService.generateRequestId(), url); const getResponseParserFn: () => GenericConstructor = () => { @@ -169,11 +176,20 @@ export class SearchService implements OnDestroy { const payloadObs = observableCombineLatest(tDomainListObs, pageInfoObs).pipe( map(([tDomainList, pageInfo]) => { - return new PaginatedList(pageInfo, tDomainList); + return new PaginatedList(pageInfo, tDomainList.filter((obj) => hasValue(obj))); }) ); - return this.rdb.toRemoteDataObservable(requestEntryObs, payloadObs); + return observableCombineLatest(hrefObs, tDomainListObs, requestEntryObs).pipe( + switchMap(([href, tDomainList, requestEntry]) => { + if (tDomainList.indexOf(undefined) > -1 && requestEntry && requestEntry.completed) { + this.requestService.removeByHrefSubstring(href); + return this.search(searchOptions) + } else { + return this.rdb.toRemoteDataObservable(requestEntryObs, payloadObs); + } + }) + ); } /** From 7d0439b006c259056f359f2ac111d2baa7534c06 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 14 Aug 2019 16:00:26 +0200 Subject: [PATCH 06/15] 64225: Fixed AoT build errors --- .../edit-relationship-list.component.spec.ts | 1 - .../edit-relationship/edit-relationship.component.spec.ts | 1 - .../item-relationships/item-relationships.component.spec.ts | 1 - src/app/core/data/relationship.service.spec.ts | 1 - 4 files changed, 4 deletions(-) diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts index bd5f5f2e5c..3748ebca9d 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts @@ -36,7 +36,6 @@ let relationshipType; describe('EditRelationshipListComponent', () => { beforeEach(async(() => { relationshipType = Object.assign(new RelationshipType(), { - type: ResourceType.RelationshipType, id: '1', uuid: '1', leftLabel: 'isAuthorOfPublication', diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts index fc6c999a1c..3306d8eb01 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts @@ -32,7 +32,6 @@ let el; describe('EditRelationshipComponent', () => { beforeEach(async(() => { relationshipType = Object.assign(new RelationshipType(), { - type: ResourceType.RelationshipType, id: '1', uuid: '1', leftLabel: 'isAuthorOfPublication', diff --git a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts index 51394ef9e5..b1a4e11371 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts @@ -66,7 +66,6 @@ describe('ItemRelationshipsComponent', () => { const date = new Date(); relationshipType = Object.assign(new RelationshipType(), { - type: ResourceType.RelationshipType, id: '1', uuid: '1', leftLabel: 'isAuthorOfPublication', diff --git a/src/app/core/data/relationship.service.spec.ts b/src/app/core/data/relationship.service.spec.ts index 88da4a5496..0ced517d74 100644 --- a/src/app/core/data/relationship.service.spec.ts +++ b/src/app/core/data/relationship.service.spec.ts @@ -31,7 +31,6 @@ describe('RelationshipService', () => { }) as ObjectCacheService; const relationshipType = Object.assign(new RelationshipType(), { - type: ResourceType.RelationshipType, id: '1', uuid: '1', leftLabel: 'isAuthorOfPublication', From 02cf98c75912c2de0ec3f977bedba8643cb17581 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 26 Aug 2019 17:51:42 +0200 Subject: [PATCH 07/15] 64574: relationship-service's getRelatedItemsByLabel returns paginated list using new REST API search endpoint + test cases on publication pages --- .../edit-relationship-list.component.ts | 8 +-- .../publication/publication.component.html | 6 +-- .../publication/publication.component.ts | 22 +++----- .../shared/item-relationships-utils.ts | 29 +++++++++++ .../item-types/shared/item.component.ts | 4 +- src/app/core/data/relationship.service.ts | 52 ++++++++++++++----- .../item-pages/person/person.component.ts | 4 +- 7 files changed, 91 insertions(+), 34 deletions(-) diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts index 765d6484d4..3a145c99e0 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts @@ -4,8 +4,10 @@ import { Observable } from 'rxjs/internal/Observable'; import { FieldUpdate, FieldUpdates } from '../../../../core/data/object-updates/object-updates.reducer'; import { RelationshipService } from '../../../../core/data/relationship.service'; import { Item } from '../../../../core/shared/item.model'; -import { switchMap } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { hasValue } from '../../../../shared/empty.util'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { PaginatedList } from '../../../../core/data/paginated-list'; @Component({ selector: 'ds-edit-relationship-list', @@ -63,7 +65,7 @@ export class EditRelationshipListComponent implements OnInit, OnChanges { * Transform the item's relationships of a specific type into related items * @param label The relationship type's label */ - public getRelatedItemsByLabel(label: string): Observable { + public getRelatedItemsByLabel(label: string): Observable>> { return this.relationshipService.getRelatedItemsByLabel(this.item, label); } @@ -73,7 +75,7 @@ export class EditRelationshipListComponent implements OnInit, OnChanges { */ public getUpdatesByLabel(label: string): Observable { return this.getRelatedItemsByLabel(label).pipe( - switchMap((items: Item[]) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, items)) + switchMap((itemsRD) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, itemsRD.payload.page)) ) } diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.html b/src/app/+item-page/simple/item-types/publication/publication.component.html index abf5225c27..aae269cdbe 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.html +++ b/src/app/+item-page/simple/item-types/publication/publication.component.html @@ -32,15 +32,15 @@ [representations]="authors$ | async"> diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.ts b/src/app/+item-page/simple/item-types/publication/publication.component.ts index 81e2726e0c..1d5a942559 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.ts +++ b/src/app/+item-page/simple/item-types/publication/publication.component.ts @@ -8,6 +8,8 @@ import { import { ItemComponent } from '../shared/item.component'; import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model'; import { getRelatedItemsByTypeLabel } from '../shared/item-relationships-utils'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { PaginatedList } from '../../../../core/data/paginated-list'; @rendersItemType('Publication', ItemViewMode.Full) @rendersItemType(DEFAULT_ITEM_TYPE, ItemViewMode.Full) @@ -26,17 +28,17 @@ export class PublicationComponent extends ItemComponent implements OnInit { /** * The projects related to this publication */ - projects$: Observable; + projects$: Observable>>; /** * The organisation units related to this publication */ - orgUnits$: Observable; + orgUnits$: Observable>>; /** * The journal issues related to this publication */ - journalIssues$: Observable; + journalIssues$: Observable>>; ngOnInit(): void { super.ngOnInit(); @@ -45,17 +47,9 @@ export class PublicationComponent extends ItemComponent implements OnInit { this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author'); - this.projects$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isProjectOfPublication') - ); - - this.orgUnits$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isOrgUnitOfPublication') - ); - - this.journalIssues$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isJournalIssueOfPublication') - ); + this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfPublication'); + this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfPublication'); + this.journalIssues$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalIssueOfPublication'); } } diff --git a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts index b4eda2abfb..c63900663d 100644 --- a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts +++ b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts @@ -13,6 +13,7 @@ import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { RelationshipService } from '../../../../core/data/relationship.service'; +import { PaginatedList } from '../../../../core/data/paginated-list'; /** * Operator for comparing arrays using a mapping function @@ -99,6 +100,34 @@ export const relationsToItems = (thisId: string) => distinctUntilChanged(compareArraysUsingIds()), ); +export const paginatedRelationsToItems = (thisId: string) => + (source: Observable>>): Observable>> => + source.pipe( + getSucceededRemoteData(), + switchMap((relationshipsRD: RemoteData>) => { + return observableZip( + ...relationshipsRD.payload.page.map((rel: Relationship) => observableCombineLatest(rel.leftItem, rel.rightItem)) + ).pipe( + map((arr) => + arr + .filter(([leftItem, rightItem]) => leftItem.hasSucceeded && rightItem.hasSucceeded) + .map(([leftItem, rightItem]) => { + if (leftItem.payload.id === thisId) { + return rightItem.payload; + } else if (rightItem.payload.id === thisId) { + return leftItem.payload; + } + }) + .filter((item: Item) => hasValue(item)) + ), + distinctUntilChanged(compareArraysUsingIds()), + map((relatedItems: Item[]) => + Object.assign(relationshipsRD, { payload: { page: relatedItems } }) + ) + ) + }) + ); + /** * Operator for turning a list of relationships and their relationship-types into a list of relevant items by relationship label * @param thisId The item's id of which the relations belong to diff --git a/src/app/+item-page/simple/item-types/shared/item.component.ts b/src/app/+item-page/simple/item-types/shared/item.component.ts index 556496fe49..e3b9ce59a8 100644 --- a/src/app/+item-page/simple/item-types/shared/item.component.ts +++ b/src/app/+item-page/simple/item-types/shared/item.component.ts @@ -15,6 +15,7 @@ import { MetadatumRepresentation } from '../../../../core/shared/metadata-repres import { of } from 'rxjs/internal/observable/of'; import { MetadataValue } from '../../../../core/shared/metadata.models'; import { compareArraysUsingIds } from './item-relationships-utils'; +import { RelationshipService } from '../../../../core/data/relationship.service'; /** * Operator for turning a list of relationships into a list of metadatarepresentations given the original metadata @@ -68,7 +69,8 @@ export class ItemComponent implements OnInit { resolvedRelsAndTypes$: Observable<[Relationship[], RelationshipType[]]>; constructor( - @Inject(ITEM) public item: Item + @Inject(ITEM) public item: Item, + public relationshipService: RelationshipService ) {} ngOnInit(): void { diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index 1699b6a27d..5791ab2165 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -10,7 +10,7 @@ import { getRemoteDataPayload, getResponseFromEntry, getSucceededRemoteData } from '../shared/operators'; -import { DeleteRequest, RestRequest } from './request.models'; +import { DeleteRequest, FindAllOptions, RestRequest } from './request.models'; import { Observable } from 'rxjs/internal/Observable'; import { RestResponse } from '../cache/response.models'; import { Item } from '../shared/item.model'; @@ -22,23 +22,42 @@ import { zip as observableZip } from 'rxjs'; import { PaginatedList } from './paginated-list'; import { ItemDataService } from './item-data.service'; import { - compareArraysUsingIds, filterRelationsByTypeLabel, + compareArraysUsingIds, filterRelationsByTypeLabel, paginatedRelationsToItems, relationsToItems } from '../../+item-page/simple/item-types/shared/item-relationships-utils'; import { ObjectCacheService } from '../cache/object-cache.service'; +import { DataService } from './data.service'; +import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; +import { Store } from '@ngrx/store'; +import { CoreState } from '../core.reducers'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { HttpClient } from '@angular/common/http'; +import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; +import { SearchParam } from '../cache/models/search-param.model'; /** * The service handling all relationship requests */ @Injectable() -export class RelationshipService { +export class RelationshipService extends DataService { protected linkPath = 'relationships'; + protected forceBypassCache = false; - constructor(protected requestService: RequestService, - protected halService: HALEndpointService, + constructor(protected itemService: ItemDataService, + protected requestService: RequestService, protected rdbService: RemoteDataBuildService, - protected itemService: ItemDataService, - protected objectCache: ObjectCacheService) { + protected dataBuildService: NormalizedObjectBuildService, + protected store: Store, + protected halService: HALEndpointService, + protected objectCache: ObjectCacheService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DefaultChangeAnalyzer) { + super(); + } + + getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable { + return this.halService.getEndpoint(linkPath); } /** @@ -208,11 +227,20 @@ export class RelationshipService { * @param item * @param label */ - getRelatedItemsByLabel(item: Item, label: string): Observable { - return this.getItemResolvedRelsAndTypes(item).pipe( - filterRelationsByTypeLabel(label), - relationsToItems(item.uuid) - ); + getRelatedItemsByLabel(item: Item, label: string): Observable>> { + return this.getItemRelationshipsByLabel(item, label).pipe(paginatedRelationsToItems(item.uuid)); + } + + /** + * Resolve a given item's relationships into related items, filtered by a relationship label + * and return the items as an array + * @param item + * @param label + */ + getItemRelationshipsByLabel(item: Item, label: string): Observable>> { + const options = new FindAllOptions(); + options.searchParams = [ new SearchParam('label', label), new SearchParam('dso', item.id) ]; + return this.searchBy('byLabel', options); } /** diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.ts b/src/app/entity-groups/research-entities/item-pages/person/person.component.ts index 8b36175b96..cd43c0b08b 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.ts +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.ts @@ -7,6 +7,7 @@ import { SearchFixedFilterService } from '../../../../+search-page/search-filter import { isNotEmpty } from '../../../../shared/empty.util'; import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component'; import { getRelatedItemsByTypeLabel } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils'; +import { RelationshipService } from '../../../../core/data/relationship.service'; @rendersItemType('Person', ItemViewMode.Full) @Component({ @@ -45,9 +46,10 @@ export class PersonComponent extends ItemComponent { constructor( @Inject(ITEM) public item: Item, + public relationshipService: RelationshipService, private fixedFilterService: SearchFixedFilterService ) { - super(item); + super(item, relationshipService); } ngOnInit(): void { super.ngOnInit(); From 4b55d7507cf376a593e46216fbec825b073c1902 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 27 Aug 2019 11:18:12 +0200 Subject: [PATCH 08/15] 64574: Refactor item-pages to use paginated-lists and relationship-service --- .../publication/publication.component.html | 2 +- .../publication/publication.component.ts | 17 ++---- .../item-types/shared/item.component.ts | 60 ++++--------------- .../journal-issue.component.html | 4 +- .../journal-issue/journal-issue.component.ts | 20 ++----- .../journal-volume.component.html | 4 +- .../journal-volume.component.ts | 20 ++----- .../item-pages/journal/journal.component.html | 2 +- .../item-pages/journal/journal.component.ts | 14 ++--- .../item-pages/orgunit/orgunit.component.html | 6 +- .../item-pages/orgunit/orgunit.component.ts | 31 ++++------ .../item-pages/person/person.component.html | 4 +- .../item-pages/person/person.component.ts | 55 ++++------------- .../item-pages/project/project.component.html | 8 +-- .../item-pages/project/project.component.ts | 32 ++++------ 15 files changed, 78 insertions(+), 201 deletions(-) diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.html b/src/app/+item-page/simple/item-types/publication/publication.component.html index aae269cdbe..b61fa6bbc1 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.html +++ b/src/app/+item-page/simple/item-types/publication/publication.component.html @@ -29,7 +29,7 @@
+ [representations]="(authors$ | async)?.payload?.page"> ; + authors$: Observable>>; /** * The projects related to this publication @@ -41,16 +40,10 @@ export class PublicationComponent extends ItemComponent implements OnInit { journalIssues$: Observable>>; ngOnInit(): void { - super.ngOnInit(); + this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author', 'isAuthorOfPublication'); - if (this.resolvedRelsAndTypes$) { - - this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author'); - - this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfPublication'); - this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfPublication'); - this.journalIssues$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalIssueOfPublication'); - - } + this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfPublication'); + this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfPublication'); + this.journalIssues$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalIssueOfPublication'); } } diff --git a/src/app/+item-page/simple/item-types/shared/item.component.ts b/src/app/+item-page/simple/item-types/shared/item.component.ts index e3b9ce59a8..18aa229611 100644 --- a/src/app/+item-page/simple/item-types/shared/item.component.ts +++ b/src/app/+item-page/simple/item-types/shared/item.component.ts @@ -1,20 +1,16 @@ -import { Component, Inject, OnInit } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { Observable , zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs'; -import { distinctUntilChanged, filter, flatMap, map } from 'rxjs/operators'; -import { ItemDataService } from '../../../../core/data/item-data.service'; +import { filter, flatMap, map } from 'rxjs/operators'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { RemoteData } from '../../../../core/data/remote-data'; -import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model'; import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; import { Item } from '../../../../core/shared/item.model'; -import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators'; import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model'; import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; import { of } from 'rxjs/internal/observable/of'; import { MetadataValue } from '../../../../core/shared/metadata.models'; -import { compareArraysUsingIds } from './item-relationships-utils'; import { RelationshipService } from '../../../../core/data/relationship.service'; /** @@ -24,15 +20,15 @@ import { RelationshipService } from '../../../../core/data/relationship.service' * @param metadata The list of original Metadatum objects */ export const relationsToRepresentations = (thisId: string, itemType: string, metadata: MetadataValue[]) => - (source: Observable): Observable => + (source: Observable>>): Observable>> => source.pipe( - flatMap((rels: Relationship[]) => + flatMap((relRD: RemoteData>) => observableZip( ...metadata .map((metadatum: any) => Object.assign(new MetadataValue(), metadatum)) .map((metadatum: MetadataValue) => { if (metadatum.isVirtual) { - const matchingRels = rels.filter((rel: Relationship) => ('' + rel.id) === metadatum.virtualValue); + const matchingRels = relRD.payload.page.filter((rel: Relationship) => ('' + rel.id) === metadatum.virtualValue); if (matchingRels.length > 0) { const matchingRel = matchingRels[0]; return observableCombineLatest(matchingRel.leftItem, matchingRel.rightItem).pipe( @@ -51,6 +47,8 @@ export const relationsToRepresentations = (thisId: string, itemType: string, met return of(Object.assign(new MetadatumRepresentation(itemType), metadatum)); } }) + ).pipe( + map((representations: MetadataRepresentation[]) => Object.assign(relRD, { payload: { page: representations } })) ) ) ); @@ -62,59 +60,25 @@ export const relationsToRepresentations = (thisId: string, itemType: string, met /** * A generic component for displaying metadata and relations of an item */ -export class ItemComponent implements OnInit { - /** - * Resolved relationships and types together in one observable - */ - resolvedRelsAndTypes$: Observable<[Relationship[], RelationshipType[]]>; +export class ItemComponent { constructor( @Inject(ITEM) public item: Item, public relationshipService: RelationshipService ) {} - ngOnInit(): void { - const relationships$ = this.item.relationships; - if (relationships$) { - const relsCurrentPage$ = relationships$.pipe( - filter((rd: RemoteData>) => rd.hasSucceeded), - getRemoteDataPayload(), - map((pl: PaginatedList) => pl.page), - distinctUntilChanged(compareArraysUsingIds()) - ); - - const relTypesCurrentPage$ = relsCurrentPage$.pipe( - flatMap((rels: Relationship[]) => - observableZip(...rels.map((rel: Relationship) => rel.relationshipType)).pipe( - map(([...arr]: Array>) => arr.map((d: RemoteData) => d.payload)) - ) - ), - distinctUntilChanged(compareArraysUsingIds()) - ); - - this.resolvedRelsAndTypes$ = observableCombineLatest( - relsCurrentPage$, - relTypesCurrentPage$ - ); - } - } - /** * Build a list of MetadataRepresentations for the current item. This combines all metadata and relationships of a * certain type. * @param itemType The type of item we're building representations of. Used for matching templates. * @param metadataField The metadata field that resembles the item type. + * @param relationshipLabel The label to use to fetch relationships to create MetadataRepresentations for. */ - buildRepresentations(itemType: string, metadataField: string): Observable { + buildRepresentations(itemType: string, metadataField: string, relationshipLabel: string): Observable>> { const metadata = this.item.findMetadataSortedByPlace(metadataField); - const relsCurrentPage$ = this.item.relationships.pipe( - getSucceededRemoteData(), - getRemoteDataPayload(), - map((pl: PaginatedList) => pl.page), - distinctUntilChanged(compareArraysUsingIds()) - ); + const relationships$ = this.relationshipService.getItemRelationshipsByLabel(this.item, relationshipLabel); - return relsCurrentPage$.pipe( + return relationships$.pipe( relationsToRepresentations(this.item.id, itemType, metadata) ); } diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html index eebd3e03c8..b69827cd96 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html @@ -29,12 +29,12 @@
; + volumes$: Observable>>; /** * The publications related to this journal issue */ - publications$: Observable; + publications$: Observable>>; ngOnInit(): void { - super.ngOnInit(); - - if (isNotEmpty(this.resolvedRelsAndTypes$)) { - this.volumes$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isJournalVolumeOfIssue') - ); - this.publications$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isPublicationOfJournalIssue') - ); - } + this.volumes$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalVolumeOfIssue'); + this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfJournalIssue'); } } diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html index 83626c7ae7..902b0e9d73 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html @@ -17,11 +17,11 @@
; + journals$: Observable>>; /** * The journal issues related to this journal volume */ - issues$: Observable; + issues$: Observable>>; ngOnInit(): void { - super.ngOnInit(); - - if (isNotEmpty(this.resolvedRelsAndTypes$)) { - this.journals$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isJournalOfVolume') - ); - this.issues$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isIssueOfJournalVolume') - ); - } + this.journals$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalOfVolume'); + this.issues$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isIssueOfJournalVolume'); } } diff --git a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html index a82d3c5df6..ee212d9be6 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html @@ -21,7 +21,7 @@
; + volumes$: Observable>>; ngOnInit(): void { - super.ngOnInit(); - - if (isNotEmpty(this.resolvedRelsAndTypes$)) { - this.volumes$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isVolumeOfJournal') - ); - } + this.volumes$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isVolumeOfJournal'); } } diff --git a/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html b/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html index 92ac3eac30..91e9fa0374 100644 --- a/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html +++ b/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html @@ -25,15 +25,15 @@
; + people$: Observable>>; /** * The projects related to this organisation unit */ - projects$: Observable; + projects$: Observable>>; /** * The publications related to this organisation unit */ - publications$: Observable; + publications$: Observable>>; ngOnInit(): void { - super.ngOnInit(); - - if (isNotEmpty(this.resolvedRelsAndTypes$)) { - this.people$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isPersonOfOrgUnit') - ); - - this.projects$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isProjectOfOrgUnit') - ); - - this.publications$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isPublicationOfOrgUnit') - ); - } - }} + this.people$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPersonOfOrgUnit'); + this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfOrgUnit'); + this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfOrgUnit'); + } +} diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.html b/src/app/entity-groups/research-entities/item-pages/person/person.component.html index 04d7b9e062..ba6b83bbf6 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.html +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.html @@ -25,11 +25,11 @@
; + publications$: Observable>>; /** * The projects related to this person */ - projects$: Observable; + projects$: Observable>>; /** * The organisation units related to this person */ - orgUnits$: Observable; + orgUnits$: Observable>>; - /** - * The applied fixed filter - */ - fixedFilter$: Observable; - - /** - * The query used for applying the fixed filter - */ - fixedFilterQuery: string; - - constructor( - @Inject(ITEM) public item: Item, - public relationshipService: RelationshipService, - private fixedFilterService: SearchFixedFilterService - ) { - super(item, relationshipService); - } ngOnInit(): void { - super.ngOnInit(); - - if (isNotEmpty(this.resolvedRelsAndTypes$)) { - this.publications$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isPublicationOfAuthor') - ); - - this.projects$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isProjectOfPerson') - ); - - this.orgUnits$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isOrgUnitOfPerson') - ); - - this.fixedFilterQuery = this.fixedFilterService.getQueryByRelations('isAuthorOfPublication', this.item.id); - this.fixedFilter$ = observableOf('publication'); - } + this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfAuthor'); + this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfPerson'); + this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfPerson'); } } diff --git a/src/app/entity-groups/research-entities/item-pages/project/project.component.html b/src/app/entity-groups/research-entities/item-pages/project/project.component.html index 4e9a130b8c..c6eb0690ce 100644 --- a/src/app/entity-groups/research-entities/item-pages/project/project.component.html +++ b/src/app/entity-groups/research-entities/item-pages/project/project.component.html @@ -12,7 +12,7 @@ + [representations]="(contributors$ | async)?.payload?.page">
; + contributors$: Observable>>; /** * The people related to this project */ - people$: Observable; + people$: Observable>>; /** * The publications related to this project */ - publications$: Observable; + publications$: Observable>>; /** * The organisation units related to this project */ - orgUnits$: Observable; + orgUnits$: Observable>>; ngOnInit(): void { - super.ngOnInit(); + this.contributors$ = this.buildRepresentations('OrgUnit', 'project.contributor.other', 'isOrgUnitOfProject'); - if (isNotEmpty(this.resolvedRelsAndTypes$)) { - this.contributors$ = this.buildRepresentations('OrgUnit', 'project.contributor.other'); - - this.people$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isPersonOfProject') - ); - - this.publications$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isPublicationOfProject') - ); - - this.orgUnits$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isOrgUnitOfProject') - ); - } + this.people$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPersonOfProject'); + this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfProject'); + this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfProject'); } } From bd54d47037c73f81c510dd25d3bb4d7ca26d1a06 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 27 Aug 2019 11:36:32 +0200 Subject: [PATCH 09/15] 64574: Further refactoring of item-component, mantis publication and JSDocs --- .../shared/item-relationships-utils.ts | 41 +++++++--------- .../item-types/shared/item.component.ts | 49 +------------------ .../publication/publication.component.html | 6 +-- 3 files changed, 24 insertions(+), 72 deletions(-) diff --git a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts index c63900663d..36c605fdc1 100644 --- a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts +++ b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts @@ -7,13 +7,13 @@ import { hasNoValue, hasValue } from '../../../../shared/empty.util'; import { Observable } from 'rxjs/internal/Observable'; import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model'; -import { distinctUntilChanged, filter, flatMap, map, switchMap, tap } from 'rxjs/operators'; -import { of as observableOf, zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs'; -import { ItemDataService } from '../../../../core/data/item-data.service'; +import { distinctUntilChanged, filter, flatMap, map, switchMap } from 'rxjs/operators'; +import { zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { RelationshipService } from '../../../../core/data/relationship.service'; import { PaginatedList } from '../../../../core/data/paginated-list'; +import { of } from 'rxjs/internal/observable/of'; /** * Operator for comparing arrays using a mapping function @@ -100,6 +100,12 @@ export const relationsToItems = (thisId: string) => distinctUntilChanged(compareArraysUsingIds()), ); +/** + * Operator for turning a paginated list of relationships into a paginated list of the relevant items + * The result is wrapped in the original RemoteData and PaginatedList + * @param {string} thisId The item's id of which the relations belong to + * @returns {(source: Observable) => Observable} + */ export const paginatedRelationsToItems = (thisId: string) => (source: Observable>>): Observable>> => source.pipe( @@ -128,39 +134,27 @@ export const paginatedRelationsToItems = (thisId: string) => }) ); -/** - * Operator for turning a list of relationships and their relationship-types into a list of relevant items by relationship label - * @param thisId The item's id of which the relations belong to - * @param label The label of the relationship-type to filter on - * @param side Filter only on one side of the relationship (for example: child-parent relationships) - */ -export const getRelatedItemsByTypeLabel = (thisId: string, label: string) => - (source: Observable<[Relationship[], RelationshipType[]]>): Observable => - source.pipe( - filterRelationsByTypeLabel(label, thisId), - relationsToItems(thisId) - ); - /** * Operator for turning a list of relationships into a list of metadatarepresentations given the original metadata + * The result is wrapped in the original RemoteData and PaginatedList * @param parentId The id of the parent item * @param itemType The type of relation this list resembles (for creating representations) * @param metadata The list of original Metadatum objects - * @param ids The ItemDataService to use for fetching Items from the Rest API */ -export const relationsToRepresentations = (parentId: string, itemType: string, metadata: MetadataValue[], ids: ItemDataService) => - (source: Observable): Observable => +export const relationsToRepresentations = (parentId: string, itemType: string, metadata: MetadataValue[]) => + (source: Observable>>): Observable>> => source.pipe( - flatMap((rels: Relationship[]) => + flatMap((relRD: RemoteData>) => observableZip( ...metadata .map((metadatum: any) => Object.assign(new MetadataValue(), metadatum)) .map((metadatum: MetadataValue) => { if (metadatum.isVirtual) { - const matchingRels = rels.filter((rel: Relationship) => ('' + rel.id) === metadatum.virtualValue); + const matchingRels = relRD.payload.page.filter((rel: Relationship) => ('' + rel.id) === metadatum.virtualValue); if (matchingRels.length > 0) { const matchingRel = matchingRels[0]; return observableCombineLatest(matchingRel.leftItem, matchingRel.rightItem).pipe( + filter(([leftItem, rightItem]) => leftItem.hasSucceeded && rightItem.hasSucceeded), map(([leftItem, rightItem]) => { if (leftItem.payload.id === parentId) { return rightItem.payload; @@ -172,9 +166,12 @@ export const relationsToRepresentations = (parentId: string, itemType: string, m ); } } else { - return observableOf(Object.assign(new MetadatumRepresentation(itemType), metadatum)); + return of(Object.assign(new MetadatumRepresentation(itemType), metadatum)); } }) + ).pipe( + distinctUntilChanged(compareArraysUsingIds()), + map((representations: MetadataRepresentation[]) => Object.assign(relRD, { payload: { page: representations } })) ) ) ); diff --git a/src/app/+item-page/simple/item-types/shared/item.component.ts b/src/app/+item-page/simple/item-types/shared/item.component.ts index 18aa229611..79255f6d7c 100644 --- a/src/app/+item-page/simple/item-types/shared/item.component.ts +++ b/src/app/+item-page/simple/item-types/shared/item.component.ts @@ -1,57 +1,12 @@ import { Component, Inject } from '@angular/core'; -import { Observable , zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs'; -import { filter, flatMap, map } from 'rxjs/operators'; +import { Observable } from 'rxjs'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { RemoteData } from '../../../../core/data/remote-data'; -import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; import { Item } from '../../../../core/shared/item.model'; import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model'; -import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; -import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; -import { of } from 'rxjs/internal/observable/of'; -import { MetadataValue } from '../../../../core/shared/metadata.models'; import { RelationshipService } from '../../../../core/data/relationship.service'; - -/** - * Operator for turning a list of relationships into a list of metadatarepresentations given the original metadata - * @param thisId The id of the parent item - * @param itemType The type of relation this list resembles (for creating representations) - * @param metadata The list of original Metadatum objects - */ -export const relationsToRepresentations = (thisId: string, itemType: string, metadata: MetadataValue[]) => - (source: Observable>>): Observable>> => - source.pipe( - flatMap((relRD: RemoteData>) => - observableZip( - ...metadata - .map((metadatum: any) => Object.assign(new MetadataValue(), metadatum)) - .map((metadatum: MetadataValue) => { - if (metadatum.isVirtual) { - const matchingRels = relRD.payload.page.filter((rel: Relationship) => ('' + rel.id) === metadatum.virtualValue); - if (matchingRels.length > 0) { - const matchingRel = matchingRels[0]; - return observableCombineLatest(matchingRel.leftItem, matchingRel.rightItem).pipe( - filter(([leftItem, rightItem]) => leftItem.hasSucceeded && rightItem.hasSucceeded), - map(([leftItem, rightItem]) => { - if (leftItem.payload.id === thisId) { - return rightItem.payload; - } else if (rightItem.payload.id === thisId) { - return leftItem.payload; - } - }), - map((item: Item) => Object.assign(new ItemMetadataRepresentation(), item)) - ); - } - } else { - return of(Object.assign(new MetadatumRepresentation(itemType), metadatum)); - } - }) - ).pipe( - map((representations: MetadataRepresentation[]) => Object.assign(relRD, { payload: { page: representations } })) - ) - ) - ); +import { relationsToRepresentations } from './item-relationships-utils'; @Component({ selector: 'ds-item', diff --git a/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html b/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html index 6b6f484183..bb3d6f8bd0 100644 --- a/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html +++ b/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html @@ -62,19 +62,19 @@
From 4afb35c53ef3e8ff9602e3a798f372fb0d00ca1c Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 27 Aug 2019 14:12:39 +0200 Subject: [PATCH 10/15] 64574: View-more/less for related-items + refactoring item pages --- resources/i18n/en.json | 2 + .../publication/publication.component.html | 9 ++- .../publication/publication.component.ts | 20 ------ .../shared/item-relationships-utils.ts | 4 +- .../related-items/related-items-component.ts | 65 +++++++++++++++++-- .../related-items.component.html | 10 ++- src/app/core/data/relationship.service.ts | 17 +++-- .../journal-issue.component.html | 6 +- .../journal-issue/journal-issue.component.ts | 18 ----- .../journal-volume.component.html | 6 +- .../journal-volume.component.ts | 18 ----- .../item-pages/journal/journal.component.html | 3 +- .../item-pages/journal/journal.component.ts | 12 ---- .../item-pages/orgunit/orgunit.component.html | 9 ++- .../item-pages/orgunit/orgunit.component.ts | 28 +------- .../item-pages/person/person.component.html | 6 +- .../item-pages/person/person.component.ts | 24 ------- .../item-pages/project/project.component.html | 9 ++- .../item-pages/project/project.component.ts | 20 ------ 19 files changed, 117 insertions(+), 169 deletions(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index e91b2776c1..e660189602 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -290,6 +290,8 @@ "item.page.link.full": "Full item page", "item.page.link.simple": "Simple item page", "item.page.person.search.title": "Articles by this author", + "item.page.related-items.view-more": "View more", + "item.page.related-items.view-less": "View less", "item.page.subject": "Keywords", "item.page.uri": "URI", "item.select.confirm": "Confirm selected", diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.html b/src/app/+item-page/simple/item-types/publication/publication.component.html index b61fa6bbc1..70e46014d0 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.html +++ b/src/app/+item-page/simple/item-types/publication/publication.component.html @@ -32,15 +32,18 @@ [representations]="(authors$ | async)?.payload?.page"> diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.ts b/src/app/+item-page/simple/item-types/publication/publication.component.ts index fa4b0988d5..16c81a1d14 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.ts +++ b/src/app/+item-page/simple/item-types/publication/publication.component.ts @@ -1,6 +1,5 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { Item } from '../../../../core/shared/item.model'; import { DEFAULT_ITEM_TYPE, ItemViewMode, rendersItemType @@ -24,26 +23,7 @@ export class PublicationComponent extends ItemComponent implements OnInit { */ authors$: Observable>>; - /** - * The projects related to this publication - */ - projects$: Observable>>; - - /** - * The organisation units related to this publication - */ - orgUnits$: Observable>>; - - /** - * The journal issues related to this publication - */ - journalIssues$: Observable>>; - ngOnInit(): void { this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author', 'isAuthorOfPublication'); - - this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfPublication'); - this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfPublication'); - this.journalIssues$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalIssueOfPublication'); } } diff --git a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts index 36c605fdc1..2a413f482d 100644 --- a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts +++ b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts @@ -128,7 +128,7 @@ export const paginatedRelationsToItems = (thisId: string) => ), distinctUntilChanged(compareArraysUsingIds()), map((relatedItems: Item[]) => - Object.assign(relationshipsRD, { payload: { page: relatedItems } }) + Object.assign(relationshipsRD, { payload: Object.assign(relationshipsRD.payload, { page: relatedItems } )}) ) ) }) @@ -171,7 +171,7 @@ export const relationsToRepresentations = (parentId: string, itemType: string, m }) ).pipe( distinctUntilChanged(compareArraysUsingIds()), - map((representations: MetadataRepresentation[]) => Object.assign(relRD, { payload: { page: representations } })) + map((representations: MetadataRepresentation[]) => Object.assign(relRD, { payload: Object.assign(relRD.payload, { page: representations }) })) ) ) ); diff --git a/src/app/+item-page/simple/related-items/related-items-component.ts b/src/app/+item-page/simple/related-items/related-items-component.ts index 7b54d7316a..838508524c 100644 --- a/src/app/+item-page/simple/related-items/related-items-component.ts +++ b/src/app/+item-page/simple/related-items/related-items-component.ts @@ -1,6 +1,11 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { Item } from '../../../core/shared/item.model'; import { ItemViewMode } from '../../../shared/items/item-type-decorator'; +import { Observable } from 'rxjs/internal/Observable'; +import { RemoteData } from '../../../core/data/remote-data'; +import { PaginatedList } from '../../../core/data/paginated-list'; +import { RelationshipService } from '../../../core/data/relationship.service'; +import { FindAllOptions } from '../../../core/data/request.models'; @Component({ selector: 'ds-related-items', @@ -9,22 +14,72 @@ import { ItemViewMode } from '../../../shared/items/item-type-decorator'; }) /** * This component is used for displaying relations between items - * It expects a list of items to display and a label to put on top + * It expects a parent item and relationship type, as well as a label to display on top */ -export class RelatedItemsComponent { +export class RelatedItemsComponent implements OnInit { /** - * A list of items to display + * The parent of the list of related items to display */ - @Input() items: Item[]; + @Input() parentItem: Item; + + /** + * The label of the relationship type to display + * Used in sending a search request to the REST API + */ + @Input() relationType: string; + + /** + * Default options to start a search request with + * Optional input, should you wish a different page size (or other options) + */ + @Input() options = Object.assign(new FindAllOptions(), { elementsPerPage: 5 }); /** * An i18n label to use as a title for the list (usually describes the relation) */ @Input() label: string; + /** + * The list of related items + */ + items$: Observable>>; + + /** + * Search options for displaying all elements in a list + */ + allOptions = Object.assign(new FindAllOptions(), { elementsPerPage: 9999 }); + /** * The view-mode we're currently on * @type {ElementViewMode} */ viewMode = ItemViewMode.Element; + + /** + * Whether or not the list is currently expanded to show all related items + */ + showingAll = false; + + constructor(public relationshipService: RelationshipService) { + } + + ngOnInit(): void { + this.items$ = this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, this.options); + } + + /** + * Expand the list to display all related items + */ + viewMore() { + this.items$ = this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, this.allOptions); + this.showingAll = true; + } + + /** + * Collapse the list to display the originally displayed items + */ + viewLess() { + this.items$ = this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, this.options); + this.showingAll = false; + } } diff --git a/src/app/+item-page/simple/related-items/related-items.component.html b/src/app/+item-page/simple/related-items/related-items.component.html index 4b284ad63c..20d4c6c38c 100644 --- a/src/app/+item-page/simple/related-items/related-items.component.html +++ b/src/app/+item-page/simple/related-items/related-items.component.html @@ -1,5 +1,11 @@ - - + + + diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index 5791ab2165..db4af49d8a 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -226,9 +226,10 @@ export class RelationshipService extends DataService { * and return the items as an array * @param item * @param label + * @param options */ - getRelatedItemsByLabel(item: Item, label: string): Observable>> { - return this.getItemRelationshipsByLabel(item, label).pipe(paginatedRelationsToItems(item.uuid)); + getRelatedItemsByLabel(item: Item, label: string, options?: FindAllOptions): Observable>> { + return this.getItemRelationshipsByLabel(item, label, options).pipe(paginatedRelationsToItems(item.uuid)); } /** @@ -236,11 +237,15 @@ export class RelationshipService extends DataService { * and return the items as an array * @param item * @param label + * @param options */ - getItemRelationshipsByLabel(item: Item, label: string): Observable>> { - const options = new FindAllOptions(); - options.searchParams = [ new SearchParam('label', label), new SearchParam('dso', item.id) ]; - return this.searchBy('byLabel', options); + getItemRelationshipsByLabel(item: Item, label: string, options?: FindAllOptions): Observable>> { + let findAllOptions = new FindAllOptions(); + if (options) { + findAllOptions = Object.assign(new FindAllOptions(), options); + } + findAllOptions.searchParams = [ new SearchParam('label', label), new SearchParam('dso', item.id) ]; + return this.searchBy('byLabel', findAllOptions); } /** diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html index b69827cd96..8db50e78c4 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html @@ -29,12 +29,14 @@
>>; - - /** - * The publications related to this journal issue - */ - publications$: Observable>>; - - ngOnInit(): void { - this.volumes$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalVolumeOfIssue'); - this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfJournalIssue'); - } } diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html index 902b0e9d73..150037eccb 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html @@ -17,11 +17,13 @@
>>; - - /** - * The journal issues related to this journal volume - */ - issues$: Observable>>; - - ngOnInit(): void { - this.journals$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalOfVolume'); - this.issues$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isIssueOfJournalVolume'); - } } diff --git a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html index ee212d9be6..d22933a657 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html @@ -21,7 +21,8 @@
>>; - - ngOnInit(): void { - this.volumes$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isVolumeOfJournal'); - } } diff --git a/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html b/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html index 91e9fa0374..a3d2fedb10 100644 --- a/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html +++ b/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html @@ -25,15 +25,18 @@
>>; - - /** - * The projects related to this organisation unit - */ - projects$: Observable>>; - - /** - * The publications related to this organisation unit - */ - publications$: Observable>>; - - ngOnInit(): void { - this.people$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPersonOfOrgUnit'); - this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfOrgUnit'); - this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfOrgUnit'); - } +export class OrgunitComponent extends ItemComponent { } diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.html b/src/app/entity-groups/research-entities/item-pages/person/person.component.html index ba6b83bbf6..3f0ca90368 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.html +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.html @@ -25,11 +25,13 @@
>>; - - /** - * The projects related to this person - */ - projects$: Observable>>; - - /** - * The organisation units related to this person - */ - orgUnits$: Observable>>; - - ngOnInit(): void { - this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfAuthor'); - this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfPerson'); - this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfPerson'); - } } diff --git a/src/app/entity-groups/research-entities/item-pages/project/project.component.html b/src/app/entity-groups/research-entities/item-pages/project/project.component.html index c6eb0690ce..3ca36b9afd 100644 --- a/src/app/entity-groups/research-entities/item-pages/project/project.component.html +++ b/src/app/entity-groups/research-entities/item-pages/project/project.component.html @@ -29,15 +29,18 @@
>>; - /** - * The people related to this project - */ - people$: Observable>>; - - /** - * The publications related to this project - */ - publications$: Observable>>; - - /** - * The organisation units related to this project - */ - orgUnits$: Observable>>; - ngOnInit(): void { this.contributors$ = this.buildRepresentations('OrgUnit', 'project.contributor.other', 'isOrgUnitOfProject'); - - this.people$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPersonOfProject'); - this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfProject'); - this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfProject'); } } From a641b20db7c583c19c46579d684249bd046b99b7 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 27 Aug 2019 15:58:23 +0200 Subject: [PATCH 11/15] 64574: View more/less for metadata-representations + refactoring item pages --- .../publication/publication.component.html | 6 +- .../publication/publication.component.ts | 16 +-- .../shared/item-relationships-utils.ts | 49 +------ .../item-types/shared/item.component.ts | 25 +--- ...etadata-representation-list.component.html | 10 +- .../metadata-representation-list.component.ts | 122 +++++++++++++++++- .../item-pages/project/project.component.html | 6 +- .../item-pages/project/project.component.ts | 16 +-- 8 files changed, 139 insertions(+), 111 deletions(-) diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.html b/src/app/+item-page/simple/item-types/publication/publication.component.html index 70e46014d0..1edc00f7eb 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.html +++ b/src/app/+item-page/simple/item-types/publication/publication.component.html @@ -28,8 +28,10 @@
+ [parentItem]="item" + [itemType]="'Person'" + [metadataField]="'dc.contributor.author'" + [label]="'relationships.isAuthorOf' | translate"> >>; - - ngOnInit(): void { - this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author', 'isAuthorOfPublication'); - } +export class PublicationComponent extends ItemComponent { } diff --git a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts index 2a413f482d..f9afa7981b 100644 --- a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts +++ b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts @@ -1,19 +1,14 @@ -import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; -import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model'; -import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; -import { MetadataValue } from '../../../../core/shared/metadata.models'; import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators'; import { hasNoValue, hasValue } from '../../../../shared/empty.util'; import { Observable } from 'rxjs/internal/Observable'; import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model'; -import { distinctUntilChanged, filter, flatMap, map, switchMap } from 'rxjs/operators'; +import { distinctUntilChanged, flatMap, map, switchMap } from 'rxjs/operators'; import { zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { RelationshipService } from '../../../../core/data/relationship.service'; import { PaginatedList } from '../../../../core/data/paginated-list'; -import { of } from 'rxjs/internal/observable/of'; /** * Operator for comparing arrays using a mapping function @@ -134,48 +129,6 @@ export const paginatedRelationsToItems = (thisId: string) => }) ); -/** - * Operator for turning a list of relationships into a list of metadatarepresentations given the original metadata - * The result is wrapped in the original RemoteData and PaginatedList - * @param parentId The id of the parent item - * @param itemType The type of relation this list resembles (for creating representations) - * @param metadata The list of original Metadatum objects - */ -export const relationsToRepresentations = (parentId: string, itemType: string, metadata: MetadataValue[]) => - (source: Observable>>): Observable>> => - source.pipe( - flatMap((relRD: RemoteData>) => - observableZip( - ...metadata - .map((metadatum: any) => Object.assign(new MetadataValue(), metadatum)) - .map((metadatum: MetadataValue) => { - if (metadatum.isVirtual) { - const matchingRels = relRD.payload.page.filter((rel: Relationship) => ('' + rel.id) === metadatum.virtualValue); - if (matchingRels.length > 0) { - const matchingRel = matchingRels[0]; - return observableCombineLatest(matchingRel.leftItem, matchingRel.rightItem).pipe( - filter(([leftItem, rightItem]) => leftItem.hasSucceeded && rightItem.hasSucceeded), - map(([leftItem, rightItem]) => { - if (leftItem.payload.id === parentId) { - return rightItem.payload; - } else if (rightItem.payload.id === parentId) { - return leftItem.payload; - } - }), - map((item: Item) => Object.assign(new ItemMetadataRepresentation(), item)) - ); - } - } else { - return of(Object.assign(new MetadatumRepresentation(itemType), metadatum)); - } - }) - ).pipe( - distinctUntilChanged(compareArraysUsingIds()), - map((representations: MetadataRepresentation[]) => Object.assign(relRD, { payload: Object.assign(relRD.payload, { page: representations }) })) - ) - ) - ); - /** * Operator for fetching an item's relationships, but filtered by related item IDs (essentially performing a reverse lookup) * Only relationships where leftItem or rightItem's ID is present in the list provided will be returned diff --git a/src/app/+item-page/simple/item-types/shared/item.component.ts b/src/app/+item-page/simple/item-types/shared/item.component.ts index 79255f6d7c..ae3d660a4f 100644 --- a/src/app/+item-page/simple/item-types/shared/item.component.ts +++ b/src/app/+item-page/simple/item-types/shared/item.component.ts @@ -1,12 +1,6 @@ import { Component, Inject } from '@angular/core'; -import { Observable } from 'rxjs'; -import { PaginatedList } from '../../../../core/data/paginated-list'; -import { RemoteData } from '../../../../core/data/remote-data'; import { Item } from '../../../../core/shared/item.model'; import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; -import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model'; -import { RelationshipService } from '../../../../core/data/relationship.service'; -import { relationsToRepresentations } from './item-relationships-utils'; @Component({ selector: 'ds-item', @@ -18,24 +12,7 @@ import { relationsToRepresentations } from './item-relationships-utils'; export class ItemComponent { constructor( - @Inject(ITEM) public item: Item, - public relationshipService: RelationshipService + @Inject(ITEM) public item: Item ) {} - /** - * Build a list of MetadataRepresentations for the current item. This combines all metadata and relationships of a - * certain type. - * @param itemType The type of item we're building representations of. Used for matching templates. - * @param metadataField The metadata field that resembles the item type. - * @param relationshipLabel The label to use to fetch relationships to create MetadataRepresentations for. - */ - buildRepresentations(itemType: string, metadataField: string, relationshipLabel: string): Observable>> { - const metadata = this.item.findMetadataSortedByPlace(metadataField); - const relationships$ = this.relationshipService.getItemRelationshipsByLabel(this.item, relationshipLabel); - - return relationships$.pipe( - relationsToRepresentations(this.item.id, itemType, metadata) - ); - } - } diff --git a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html index 48eabf8451..e9d2364ca6 100644 --- a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html +++ b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html @@ -1,5 +1,11 @@ - - + + + diff --git a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts index f0dc222bf1..4b5553e404 100644 --- a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts +++ b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts @@ -1,6 +1,17 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model'; import { ItemViewMode } from '../../../shared/items/item-type-decorator'; +import { Observable } from 'rxjs/internal/Observable'; +import { RemoteData } from '../../../core/data/remote-data'; +import { RelationshipService } from '../../../core/data/relationship.service'; +import { Item } from '../../../core/shared/item.model'; +import { zip as observableZip, combineLatest as observableCombineLatest, of as observableOf } from 'rxjs'; +import { MetadataValue } from '../../../core/shared/metadata.models'; +import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; +import { filter, map, switchMap } from 'rxjs/operators'; +import { getSucceededRemoteData } from '../../../core/shared/operators'; +import { Relationship } from '../../../core/shared/item-relationships/relationship.model'; +import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model'; @Component({ selector: 'ds-metadata-representation-list', @@ -8,22 +19,123 @@ import { ItemViewMode } from '../../../shared/items/item-type-decorator'; }) /** * This component is used for displaying metadata - * It expects a list of MetadataRepresentation objects and a label to put on top of the list + * It expects an item and a metadataField to fetch metadata + * It expects an itemType to resolve the metadata to a an item + * It expects a label to put on top of the list */ -export class MetadataRepresentationListComponent { +export class MetadataRepresentationListComponent implements OnInit { /** - * A list of metadata-representations to display + * The parent of the list of related items to display */ - @Input() representations: MetadataRepresentation[]; + @Input() parentItem: Item; + + /** + * The type of item to create a representation of + */ + @Input() itemType: string; + + /** + * The metadata field to use for fetching metadata from the item + */ + @Input() metadataField: string; /** * An i18n label to use as a title for the list */ @Input() label: string; + /** + * The max amount of representations to display + * Defaults to 10 + * The default can optionally be overridden by providing the limit as input to the component + */ + @Input() limit = 10; + + /** + * A list of metadata-representations to display + */ + representations$: Observable; + /** * The view-mode we're currently on * @type {ElementViewMode} */ viewMode = ItemViewMode.Metadata; + + /** + * The originally provided limit + * Used for resetting the limit to the original value when collapsing the list + */ + originalLimit: number; + + /** + * The total amount of metadata values available + */ + total: number; + + constructor(public relationshipService: RelationshipService) { + } + + ngOnInit(): void { + this.originalLimit = this.limit; + this.setRepresentations(); + } + + /** + * Initialize the metadata representations + */ + setRepresentations() { + const metadata = this.parentItem.findMetadataSortedByPlace(this.metadataField); + this.total = metadata.length; + this.representations$ = this.resolveMetadataRepresentations(metadata); + } + + /** + * Resolve a list of metadata values to a list of metadata representations + * @param metadata + */ + resolveMetadataRepresentations(metadata: MetadataValue[]): Observable { + return observableZip( + ...metadata + .slice(0, this.limit) + .map((metadatum: any) => Object.assign(new MetadataValue(), metadatum)) + .map((metadatum: MetadataValue) => { + if (metadatum.isVirtual) { + return this.relationshipService.findById(metadatum.virtualValue).pipe( + getSucceededRemoteData(), + switchMap((relRD: RemoteData) => + observableCombineLatest(relRD.payload.leftItem, relRD.payload.rightItem).pipe( + filter(([leftItem, rightItem]) => leftItem.hasSucceeded && rightItem.hasSucceeded), + map(([leftItem, rightItem]) => { + if (leftItem.payload.id === this.parentItem.id) { + return rightItem.payload; + } else if (rightItem.payload.id === this.parentItem.id) { + return leftItem.payload; + } + }), + map((item: Item) => Object.assign(new ItemMetadataRepresentation(), item)) + ) + )); + } else { + return observableOf(Object.assign(new MetadatumRepresentation(this.itemType), metadatum)); + } + }) + ); + } + + /** + * Expand the list to display all metadata representations + */ + viewMore() { + this.limit = 9999; + this.setRepresentations(); + } + + /** + * Collapse the list to display the originally displayed metadata representations + */ + viewLess() { + this.limit = this.originalLimit; + this.setRepresentations(); + } } diff --git a/src/app/entity-groups/research-entities/item-pages/project/project.component.html b/src/app/entity-groups/research-entities/item-pages/project/project.component.html index 3ca36b9afd..9ea0676f23 100644 --- a/src/app/entity-groups/research-entities/item-pages/project/project.component.html +++ b/src/app/entity-groups/research-entities/item-pages/project/project.component.html @@ -11,8 +11,10 @@ + [parentItem]="item" + [itemType]="'OrgUnit'" + [metadataField]="'project.contributor.other'" + [label]="'project.page.contributor' | translate"> >>; - - ngOnInit(): void { - this.contributors$ = this.buildRepresentations('OrgUnit', 'project.contributor.other', 'isOrgUnitOfProject'); - } +export class ProjectComponent extends ItemComponent { } From 8a1fd78811f3474a46926e2ac0dcb08745f3487b Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 27 Aug 2019 17:38:53 +0200 Subject: [PATCH 12/15] 64574: Tests intermediate commit --- .../edit-relationship-list.component.spec.ts | 2 +- .../related-items.component.html | 4 +- .../related-items.component.spec.ts | 48 ++++++++++++++++--- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts index 3748ebca9d..ca6c1fdd2e 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts @@ -97,7 +97,7 @@ describe('EditRelationshipListComponent', () => { relationshipService = jasmine.createSpyObj('relationshipService', { - getRelatedItemsByLabel: observableOf([author1, author2]), + getRelatedItemsByLabel: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), [author1, author2]))), } ); diff --git a/src/app/+item-page/simple/related-items/related-items.component.html b/src/app/+item-page/simple/related-items/related-items.component.html index 20d4c6c38c..a4baee9825 100644 --- a/src/app/+item-page/simple/related-items/related-items.component.html +++ b/src/app/+item-page/simple/related-items/related-items.component.html @@ -2,10 +2,10 @@ -
+ -
+ diff --git a/src/app/+item-page/simple/related-items/related-items.component.spec.ts b/src/app/+item-page/simple/related-items/related-items.component.spec.ts index 1896f46015..fa8891dce3 100644 --- a/src/app/+item-page/simple/related-items/related-items.component.spec.ts +++ b/src/app/+item-page/simple/related-items/related-items.component.spec.ts @@ -2,14 +2,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { RelatedItemsComponent } from './related-items-component'; import { Item } from '../../../core/shared/item.model'; -import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list'; import { PageInfo } from '../../../core/shared/page-info.model'; import { By } from '@angular/platform-browser'; import { createRelationshipsObservable } from '../item-types/shared/item.component.spec'; -import { of as observableOf } from 'rxjs'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils'; +import { RelationshipService } from '../../../core/data/relationship.service'; +import { TranslateModule } from '@ngx-translate/core'; +const parentItem: Item = Object.assign(new Item(), { + bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])), + metadata: [], + relationships: createRelationshipsObservable() +}); const mockItem1: Item = Object.assign(new Item(), { bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])), metadata: [], @@ -21,16 +26,26 @@ const mockItem2: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); const mockItems = [mockItem1, mockItem2]; +const relationType = 'isItemOfItem'; +let relationshipService: RelationshipService; -describe('RelatedItemsComponent', () => { +fdescribe('RelatedItemsComponent', () => { let comp: RelatedItemsComponent; let fixture: ComponentFixture; beforeEach(async(() => { + relationshipService = jasmine.createSpyObj('relationshipService', + { + getRelatedItemsByLabel: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), mockItems)), + } + ); + TestBed.configureTestingModule({ - imports: [], + imports: [TranslateModule.forRoot()], declarations: [RelatedItemsComponent], - providers: [], + providers: [ + { provide: RelationshipService, useValue: relationshipService } + ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(RelatedItemsComponent, { set: {changeDetection: ChangeDetectionStrategy.Default} @@ -40,7 +55,8 @@ describe('RelatedItemsComponent', () => { beforeEach(async(() => { fixture = TestBed.createComponent(RelatedItemsComponent); comp = fixture.componentInstance; - comp.items = mockItems; + comp.parentItem = parentItem; + comp.relationType = relationType; fixture.detectChanges(); })); @@ -49,4 +65,24 @@ describe('RelatedItemsComponent', () => { expect(fields.length).toBe(mockItems.length); }); + describe('when viewMore is called', () => { + beforeEach(() => { + comp.viewMore(); + }); + + it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments', () => { + expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, comp.allOptions); + }); + }); + + describe('when viewLess is called', () => { + beforeEach(() => { + comp.viewLess(); + }); + + it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments', () => { + expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, comp.options); + }); + }); + }); From f4f099995e80a6dd9f4d183b2bf57005f910ec6f Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 28 Aug 2019 11:13:54 +0200 Subject: [PATCH 13/15] 64574: Tests and small bugfix --- ...data-representation-list.component.spec.ts | 103 ++++++++++++++++-- .../related-items.component.spec.ts | 10 +- src/app/core/data/relationship.service.ts | 7 +- 3 files changed, 107 insertions(+), 13 deletions(-) diff --git a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts index f02625e8c7..120e846523 100644 --- a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts +++ b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts @@ -2,23 +2,72 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { MetadataRepresentationListComponent } from './metadata-representation-list.component'; -import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; -import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model'; +import { RelationshipService } from '../../../core/data/relationship.service'; +import { Item } from '../../../core/shared/item.model'; +import { Relationship } from '../../../core/shared/item-relationships/relationship.model'; +import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils'; +import { TranslateModule } from '@ngx-translate/core'; -const itemType = 'type'; -const metadataRepresentation1 = new MetadatumRepresentation(itemType); -const metadataRepresentation2 = new ItemMetadataRepresentation(); -const representations = [metadataRepresentation1, metadataRepresentation2]; +const itemType = 'Person'; +const metadataField = 'dc.contributor.author'; +const parentItem: Item = Object.assign(new Item(), { + id: 'parent-item', + metadata: { + 'dc.contributor.author': [ + { + language: null, + value: 'Related Author with authority', + authority: 'virtual::related-author', + place: 2 + }, + { + language: null, + value: 'Author without authority', + place: 1 + } + ], + 'dc.title': [ + { + language: null, + value: 'Parent Item' + } + ] + } +}); +const relatedAuthor: Item = Object.assign(new Item(), { + id: 'related-author', + metadata: { + 'dc.title': [ + { + language: null, + value: 'Related Author' + } + ] + } +}); +const relation: Relationship = Object.assign(new Relationship(), { + leftItem: createSuccessfulRemoteDataObject$(parentItem), + rightItem: createSuccessfulRemoteDataObject$(relatedAuthor) +}); +let relationshipService: RelationshipService; describe('MetadataRepresentationListComponent', () => { let comp: MetadataRepresentationListComponent; let fixture: ComponentFixture; + relationshipService = jasmine.createSpyObj('relationshipService', + { + findById: createSuccessfulRemoteDataObject$(relation) + } + ); + beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [], + imports: [TranslateModule.forRoot()], declarations: [MetadataRepresentationListComponent], - providers: [], + providers: [ + { provide: RelationshipService, useValue: relationshipService } + ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(MetadataRepresentationListComponent, { set: {changeDetection: ChangeDetectionStrategy.Default} @@ -28,13 +77,45 @@ describe('MetadataRepresentationListComponent', () => { beforeEach(async(() => { fixture = TestBed.createComponent(MetadataRepresentationListComponent); comp = fixture.componentInstance; - comp.representations = representations; + comp.parentItem = parentItem; + comp.itemType = itemType; + comp.metadataField = metadataField; fixture.detectChanges(); })); - it(`should load ${representations.length} item-type-switcher components`, () => { + it('should load 2 item-type-switcher components', () => { const fields = fixture.debugElement.queryAll(By.css('ds-item-type-switcher')); - expect(fields.length).toBe(representations.length); + expect(fields.length).toBe(2); + }); + + it('should initialize the original limit', () => { + expect(comp.originalLimit).toEqual(comp.limit); + }); + + describe('when viewMore is called', () => { + beforeEach(() => { + comp.viewMore(); + }); + + it('should set the limit to a high number in order to retrieve all metadata representations', () => { + expect(comp.limit).toBeGreaterThanOrEqual(999); + }); + }); + + describe('when viewLess is called', () => { + let originalLimit; + + beforeEach(() => { + // Store the original value of limit + originalLimit = comp.limit; + // Set limit to a random number + comp.limit = 458; + comp.viewLess(); + }); + + it('should reset the limit to the original value', () => { + expect(comp.limit).toEqual(originalLimit); + }); }); }); diff --git a/src/app/+item-page/simple/related-items/related-items.component.spec.ts b/src/app/+item-page/simple/related-items/related-items.component.spec.ts index fa8891dce3..6637091f02 100644 --- a/src/app/+item-page/simple/related-items/related-items.component.spec.ts +++ b/src/app/+item-page/simple/related-items/related-items.component.spec.ts @@ -29,7 +29,7 @@ const mockItems = [mockItem1, mockItem2]; const relationType = 'isItemOfItem'; let relationshipService: RelationshipService; -fdescribe('RelatedItemsComponent', () => { +describe('RelatedItemsComponent', () => { let comp: RelatedItemsComponent; let fixture: ComponentFixture; @@ -73,6 +73,10 @@ fdescribe('RelatedItemsComponent', () => { it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments', () => { expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, comp.allOptions); }); + + it('should set showingAll to true', () => { + expect(comp.showingAll).toEqual(true); + }); }); describe('when viewLess is called', () => { @@ -83,6 +87,10 @@ fdescribe('RelatedItemsComponent', () => { it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments', () => { expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, comp.options); }); + + it('should set showingAll to false', () => { + expect(comp.showingAll).toEqual(false); + }); }); }); diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index db4af49d8a..2638935028 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -244,7 +244,12 @@ export class RelationshipService extends DataService { if (options) { findAllOptions = Object.assign(new FindAllOptions(), options); } - findAllOptions.searchParams = [ new SearchParam('label', label), new SearchParam('dso', item.id) ]; + const searchParams = [ new SearchParam('label', label), new SearchParam('dso', item.id) ]; + if (findAllOptions.searchParams) { + findAllOptions.searchParams = [...findAllOptions.searchParams, ...searchParams]; + } else { + findAllOptions.searchParams = searchParams; + } return this.searchBy('byLabel', findAllOptions); } From 713c40451f5dc74a40b2dd43e4d78587430e3160 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 28 Aug 2019 11:32:04 +0200 Subject: [PATCH 14/15] 64574: Mantis publication template fix --- .../publication/publication.component.html | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html b/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html index bb3d6f8bd0..a6886cf1ac 100644 --- a/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html +++ b/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html @@ -18,13 +18,14 @@
+ [parentItem]="item" + [itemType]="'Person'" + [metadataField]="'dc.contributor.author'" + [label]="'relationships.isAuthorOf' | translate"> - + @@ -57,26 +58,29 @@
-
+
-
- - +
+ +
-
- - +
+ +
-
- - +
+ +
From 76636933e4d0910bd11322841f0130623e8e34f2 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 28 Aug 2019 12:01:24 +0200 Subject: [PATCH 15/15] 64574: Test fixes --- .../item-types/shared/item.component.spec.ts | 111 ------------------ .../core/data/relationship.service.spec.ts | 21 ++-- 2 files changed, 9 insertions(+), 123 deletions(-) diff --git a/src/app/+item-page/simple/item-types/shared/item.component.spec.ts b/src/app/+item-page/simple/item-types/shared/item.component.spec.ts index 3f525e6a25..41d05afd1b 100644 --- a/src/app/+item-page/simple/item-types/shared/item.component.spec.ts +++ b/src/app/+item-page/simple/item-types/shared/item.component.spec.ts @@ -317,115 +317,4 @@ describe('ItemComponent', () => { }); }); - describe('when calling buildRepresentations', () => { - let comp: ItemComponent; - let fixture: ComponentFixture; - - const metadataField = 'dc.contributor.author'; - const relatedItem = Object.assign(new Item(), { - id: '2', - metadata: Object.assign(new MetadataMap(), { - 'dc.title': [ - { - language: 'en_US', - value: 'related item' - } - ] - }) - }); - const mockItem = Object.assign(new Item(), { - id: '1', - uuid: '1', - metadata: new MetadataMap() - }); - mockItem.relationships = createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [ - Object.assign(new Relationship(), { - uuid: '123', - id: '123', - leftItem: createSuccessfulRemoteDataObject$(mockItem), - rightItem: createSuccessfulRemoteDataObject$(relatedItem), - relationshipType: createSuccessfulRemoteDataObject$(new RelationshipType()) - }) - ])); - mockItem.metadata[metadataField] = [ - { - value: 'Second value', - place: 1 - }, - { - value: 'Third value', - place: 2, - authority: 'virtual::123' - }, - { - value: 'First value', - place: 0 - }, - { - value: 'Fourth value', - place: 3, - authority: '123' - } - ] as MetadataValue[]; - const mockItemDataService = Object.assign({ - findById: (id) => { - if (id === relatedItem.id) { - return createSuccessfulRemoteDataObject$(relatedItem) - } - } - }) as ItemDataService; - - let representations: Observable; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: MockTranslateLoader - } - }), BrowserAnimationsModule], - declarations: [ItemComponent, VarDirective], - providers: [ - {provide: ITEM, useValue: mockItem} - ], - - schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(ItemComponent, { - set: {changeDetection: ChangeDetectionStrategy.Default} - }).compileComponents(); - })); - - beforeEach(async(() => { - fixture = TestBed.createComponent(ItemComponent); - comp = fixture.componentInstance; - fixture.detectChanges(); - representations = comp.buildRepresentations('bogus', metadataField); - })); - - it('should contain exactly 4 metadata-representations', () => { - representations.subscribe((reps: MetadataRepresentation[]) => { - expect(reps.length).toEqual(4); - }); - }); - - it('should have all the representations in the correct order', () => { - representations.subscribe((reps: MetadataRepresentation[]) => { - expect(reps[0].getValue()).toEqual('First value'); - expect(reps[1].getValue()).toEqual('Second value'); - expect(reps[2].getValue()).toEqual('related item'); - expect(reps[3].getValue()).toEqual('Fourth value'); - }); - }); - - it('should have created the correct MetadatumRepresentation and ItemMetadataRepresentation objects for the correct Metadata', () => { - representations.subscribe((reps: MetadataRepresentation[]) => { - expect(reps[0] instanceof MetadatumRepresentation).toEqual(true); - expect(reps[1] instanceof MetadatumRepresentation).toEqual(true); - expect(reps[2] instanceof ItemMetadataRepresentation).toEqual(true); - expect(reps[3] instanceof MetadatumRepresentation).toEqual(true); - }); - }); - }) - }); diff --git a/src/app/core/data/relationship.service.spec.ts b/src/app/core/data/relationship.service.spec.ts index 0ced517d74..bbd950ef5c 100644 --- a/src/app/core/data/relationship.service.spec.ts +++ b/src/app/core/data/relationship.service.spec.ts @@ -79,11 +79,16 @@ describe('RelationshipService', () => { function initTestService() { return new RelationshipService( - requestService, - halService, - rdbService, itemService, - objectCache + requestService, + rdbService, + null, + null, + halService, + objectCache, + null, + null, + null ); } @@ -142,14 +147,6 @@ describe('RelationshipService', () => { }); }); - describe('getRelatedItemsByLabel', () => { - it('should return the related items by label', () => { - service.getRelatedItemsByLabel(item, relationshipType.rightLabel).subscribe((result) => { - expect(result).toEqual(relatedItems); - }); - }); - }) - }); function getRemotedataObservable(obj: any): Observable> {