64574: relationship-service's getRelatedItemsByLabel returns paginated list using new REST API search endpoint + test cases on publication pages

This commit is contained in:
Kristof De Langhe
2019-08-26 17:51:42 +02:00
parent 7d0439b006
commit 02cf98c759
7 changed files with 91 additions and 34 deletions

View File

@@ -4,8 +4,10 @@ import { Observable } from 'rxjs/internal/Observable';
import { FieldUpdate, FieldUpdates } from '../../../../core/data/object-updates/object-updates.reducer'; import { FieldUpdate, FieldUpdates } from '../../../../core/data/object-updates/object-updates.reducer';
import { RelationshipService } from '../../../../core/data/relationship.service'; import { RelationshipService } from '../../../../core/data/relationship.service';
import { Item } from '../../../../core/shared/item.model'; 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 { hasValue } from '../../../../shared/empty.util';
import { RemoteData } from '../../../../core/data/remote-data';
import { PaginatedList } from '../../../../core/data/paginated-list';
@Component({ @Component({
selector: 'ds-edit-relationship-list', 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 * Transform the item's relationships of a specific type into related items
* @param label The relationship type's label * @param label The relationship type's label
*/ */
public getRelatedItemsByLabel(label: string): Observable<Item[]> { public getRelatedItemsByLabel(label: string): Observable<RemoteData<PaginatedList<Item>>> {
return this.relationshipService.getRelatedItemsByLabel(this.item, label); return this.relationshipService.getRelatedItemsByLabel(this.item, label);
} }
@@ -73,7 +75,7 @@ export class EditRelationshipListComponent implements OnInit, OnChanges {
*/ */
public getUpdatesByLabel(label: string): Observable<FieldUpdates> { public getUpdatesByLabel(label: string): Observable<FieldUpdates> {
return this.getRelatedItemsByLabel(label).pipe( return this.getRelatedItemsByLabel(label).pipe(
switchMap((items: Item[]) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, items)) switchMap((itemsRD) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, itemsRD.payload.page))
) )
} }

View File

@@ -32,15 +32,15 @@
[representations]="authors$ | async"> [representations]="authors$ | async">
</ds-metadata-representation-list> </ds-metadata-representation-list>
<ds-related-items <ds-related-items
[items]="projects$ | async" [items]="(projects$ | async)?.payload?.page"
[label]="'relationships.isProjectOf' | translate"> [label]="'relationships.isProjectOf' | translate">
</ds-related-items> </ds-related-items>
<ds-related-items <ds-related-items
[items]="orgUnits$ | async" [items]="(orgUnits$ | async)?.payload?.page"
[label]="'relationships.isOrgUnitOf' | translate"> [label]="'relationships.isOrgUnitOf' | translate">
</ds-related-items> </ds-related-items>
<ds-related-items <ds-related-items
[items]="journalIssues$ | async" [items]="(journalIssues$ | async)?.payload?.page"
[label]="'relationships.isJournalIssueOf' | translate"> [label]="'relationships.isJournalIssueOf' | translate">
</ds-related-items> </ds-related-items>
<ds-item-page-abstract-field [item]="item"></ds-item-page-abstract-field> <ds-item-page-abstract-field [item]="item"></ds-item-page-abstract-field>

View File

@@ -8,6 +8,8 @@ import {
import { ItemComponent } from '../shared/item.component'; import { ItemComponent } from '../shared/item.component';
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model'; import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
import { getRelatedItemsByTypeLabel } from '../shared/item-relationships-utils'; 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('Publication', ItemViewMode.Full)
@rendersItemType(DEFAULT_ITEM_TYPE, 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 * The projects related to this publication
*/ */
projects$: Observable<Item[]>; projects$: Observable<RemoteData<PaginatedList<Item>>>;
/** /**
* The organisation units related to this publication * The organisation units related to this publication
*/ */
orgUnits$: Observable<Item[]>; orgUnits$: Observable<RemoteData<PaginatedList<Item>>>;
/** /**
* The journal issues related to this publication * The journal issues related to this publication
*/ */
journalIssues$: Observable<Item[]>; journalIssues$: Observable<RemoteData<PaginatedList<Item>>>;
ngOnInit(): void { ngOnInit(): void {
super.ngOnInit(); super.ngOnInit();
@@ -45,17 +47,9 @@ export class PublicationComponent extends ItemComponent implements OnInit {
this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author'); this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author');
this.projects$ = this.resolvedRelsAndTypes$.pipe( this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfPublication');
getRelatedItemsByTypeLabel(this.item.id, 'isProjectOfPublication') this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfPublication');
); this.journalIssues$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalIssueOfPublication');
this.orgUnits$ = this.resolvedRelsAndTypes$.pipe(
getRelatedItemsByTypeLabel(this.item.id, 'isOrgUnitOfPublication')
);
this.journalIssues$ = this.resolvedRelsAndTypes$.pipe(
getRelatedItemsByTypeLabel(this.item.id, 'isJournalIssueOfPublication')
);
} }
} }

View File

@@ -13,6 +13,7 @@ import { ItemDataService } from '../../../../core/data/item-data.service';
import { Item } from '../../../../core/shared/item.model'; import { Item } from '../../../../core/shared/item.model';
import { RemoteData } from '../../../../core/data/remote-data'; import { RemoteData } from '../../../../core/data/remote-data';
import { RelationshipService } from '../../../../core/data/relationship.service'; import { RelationshipService } from '../../../../core/data/relationship.service';
import { PaginatedList } from '../../../../core/data/paginated-list';
/** /**
* Operator for comparing arrays using a mapping function * Operator for comparing arrays using a mapping function
@@ -99,6 +100,34 @@ export const relationsToItems = (thisId: string) =>
distinctUntilChanged(compareArraysUsingIds()), distinctUntilChanged(compareArraysUsingIds()),
); );
export const paginatedRelationsToItems = (thisId: string) =>
(source: Observable<RemoteData<PaginatedList<Relationship>>>): Observable<RemoteData<PaginatedList<Item>>> =>
source.pipe(
getSucceededRemoteData(),
switchMap((relationshipsRD: RemoteData<PaginatedList<Relationship>>) => {
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 * 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 thisId The item's id of which the relations belong to

View File

@@ -15,6 +15,7 @@ import { MetadatumRepresentation } from '../../../../core/shared/metadata-repres
import { of } from 'rxjs/internal/observable/of'; import { of } from 'rxjs/internal/observable/of';
import { MetadataValue } from '../../../../core/shared/metadata.models'; import { MetadataValue } from '../../../../core/shared/metadata.models';
import { compareArraysUsingIds } from './item-relationships-utils'; 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 * 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[]]>; resolvedRelsAndTypes$: Observable<[Relationship[], RelationshipType[]]>;
constructor( constructor(
@Inject(ITEM) public item: Item @Inject(ITEM) public item: Item,
public relationshipService: RelationshipService
) {} ) {}
ngOnInit(): void { ngOnInit(): void {

View File

@@ -10,7 +10,7 @@ import {
getRemoteDataPayload, getResponseFromEntry, getRemoteDataPayload, getResponseFromEntry,
getSucceededRemoteData getSucceededRemoteData
} from '../shared/operators'; } from '../shared/operators';
import { DeleteRequest, RestRequest } from './request.models'; import { DeleteRequest, FindAllOptions, RestRequest } from './request.models';
import { Observable } from 'rxjs/internal/Observable'; import { Observable } from 'rxjs/internal/Observable';
import { RestResponse } from '../cache/response.models'; import { RestResponse } from '../cache/response.models';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
@@ -22,23 +22,42 @@ import { zip as observableZip } from 'rxjs';
import { PaginatedList } from './paginated-list'; import { PaginatedList } from './paginated-list';
import { ItemDataService } from './item-data.service'; import { ItemDataService } from './item-data.service';
import { import {
compareArraysUsingIds, filterRelationsByTypeLabel, compareArraysUsingIds, filterRelationsByTypeLabel, paginatedRelationsToItems,
relationsToItems relationsToItems
} from '../../+item-page/simple/item-types/shared/item-relationships-utils'; } from '../../+item-page/simple/item-types/shared/item-relationships-utils';
import { ObjectCacheService } from '../cache/object-cache.service'; 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 * The service handling all relationship requests
*/ */
@Injectable() @Injectable()
export class RelationshipService { export class RelationshipService extends DataService<Relationship> {
protected linkPath = 'relationships'; protected linkPath = 'relationships';
protected forceBypassCache = false;
constructor(protected requestService: RequestService, constructor(protected itemService: ItemDataService,
protected halService: HALEndpointService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected itemService: ItemDataService, protected dataBuildService: NormalizedObjectBuildService,
protected objectCache: ObjectCacheService) { protected store: Store<CoreState>,
protected halService: HALEndpointService,
protected objectCache: ObjectCacheService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<Relationship>) {
super();
}
getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable<string> {
return this.halService.getEndpoint(linkPath);
} }
/** /**
@@ -208,11 +227,20 @@ export class RelationshipService {
* @param item * @param item
* @param label * @param label
*/ */
getRelatedItemsByLabel(item: Item, label: string): Observable<Item[]> { getRelatedItemsByLabel(item: Item, label: string): Observable<RemoteData<PaginatedList<Item>>> {
return this.getItemResolvedRelsAndTypes(item).pipe( return this.getItemRelationshipsByLabel(item, label).pipe(paginatedRelationsToItems(item.uuid));
filterRelationsByTypeLabel(label), }
relationsToItems(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<RemoteData<PaginatedList<Relationship>>> {
const options = new FindAllOptions();
options.searchParams = [ new SearchParam('label', label), new SearchParam('dso', item.id) ];
return this.searchBy('byLabel', options);
} }
/** /**

View File

@@ -7,6 +7,7 @@ import { SearchFixedFilterService } from '../../../../+search-page/search-filter
import { isNotEmpty } from '../../../../shared/empty.util'; import { isNotEmpty } from '../../../../shared/empty.util';
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component'; import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
import { getRelatedItemsByTypeLabel } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils'; import { getRelatedItemsByTypeLabel } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
import { RelationshipService } from '../../../../core/data/relationship.service';
@rendersItemType('Person', ItemViewMode.Full) @rendersItemType('Person', ItemViewMode.Full)
@Component({ @Component({
@@ -45,9 +46,10 @@ export class PersonComponent extends ItemComponent {
constructor( constructor(
@Inject(ITEM) public item: Item, @Inject(ITEM) public item: Item,
public relationshipService: RelationshipService,
private fixedFilterService: SearchFixedFilterService private fixedFilterService: SearchFixedFilterService
) { ) {
super(item); super(item, relationshipService);
} }
ngOnInit(): void { ngOnInit(): void {
super.ngOnInit(); super.ngOnInit();