mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
63184: Edit-Relationships left/right Item refactoring
This commit is contained in:
@@ -21,6 +21,7 @@ import { ObjectCacheService } from '../../../core/cache/object-cache.service';
|
||||
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
||||
import { RequestService } from '../../../core/data/request.service';
|
||||
import { Subscription } from 'rxjs/internal/Subscription';
|
||||
import { getRelationsByRelatedItemIds } from '../../simple/item-types/shared/item-relationships-utils';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-relationships',
|
||||
@@ -94,7 +95,7 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the currently selected related items back to relationships and send a delete request
|
||||
* Resolve the currently selected related items back to relationships and send a delete request for each of the relationships found
|
||||
* Make sure the lists are refreshed afterwards and notifications are sent for success and errors
|
||||
*/
|
||||
public submit(): void {
|
||||
@@ -105,42 +106,51 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
||||
map((fieldUpdates: FieldUpdate[]) => fieldUpdates.map((fieldUpdate: FieldUpdate) => fieldUpdate.field.uuid) as string[]),
|
||||
isNotEmptyOperator()
|
||||
);
|
||||
const allRelationshipsAndRemovedItemIds$ = observableCombineLatest(
|
||||
this.relationshipService.getItemRelationshipsArray(this.item),
|
||||
removedItemIds$
|
||||
);
|
||||
// Get all IDs of the relationships that should be removed
|
||||
const removedRelationshipIds$ = allRelationshipsAndRemovedItemIds$.pipe(
|
||||
map(([relationships, itemIds]) =>
|
||||
relationships
|
||||
.filter((relationship: Relationship) => itemIds.indexOf(relationship.leftId) > -1 || itemIds.indexOf(relationship.rightId) > -1)
|
||||
.map((relationship: Relationship) => relationship.id))
|
||||
// Get all the relationships that should be removed
|
||||
const removedRelationships$ = removedItemIds$.pipe(
|
||||
getRelationsByRelatedItemIds(this.item, this.relationshipService)
|
||||
);
|
||||
// Request a delete for every relationship found in the observable created above
|
||||
removedRelationshipIds$.pipe(
|
||||
removedRelationships$.pipe(
|
||||
take(1),
|
||||
map((removedRelationships: Relationship[]) => removedRelationships.map((rel: Relationship) => rel.id)),
|
||||
switchMap((removedIds: string[]) => observableZip(...removedIds.map((uuid: string) => this.relationshipService.deleteRelationship(uuid))))
|
||||
).subscribe((responses: RestResponse[]) => {
|
||||
const failedResponses = responses.filter((response: RestResponse) => !response.isSuccessful);
|
||||
const successfulResponses = responses.filter((response: RestResponse) => response.isSuccessful);
|
||||
|
||||
// Display an error notification for each failed request
|
||||
failedResponses.forEach((response: ErrorResponse) => {
|
||||
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'));
|
||||
}
|
||||
// Reset the state of editing relationships
|
||||
this.initializeOriginalFields();
|
||||
this.initializeUpdates();
|
||||
this.displayNotifications(responses);
|
||||
this.reset();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Display notifications
|
||||
* - Error notification for each failed response with their message
|
||||
* - Success notification in case there's at least one successful response
|
||||
* @param responses
|
||||
*/
|
||||
displayNotifications(responses: RestResponse[]) {
|
||||
const failedResponses = responses.filter((response: RestResponse) => !response.isSuccessful);
|
||||
const successfulResponses = responses.filter((response: RestResponse) => response.isSuccessful);
|
||||
|
||||
failedResponses.forEach((response: ErrorResponse) => {
|
||||
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'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the state of editing relationships
|
||||
*/
|
||||
reset() {
|
||||
this.initializeOriginalFields();
|
||||
this.initializeUpdates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends all initial values of this item to the object updates service
|
||||
*/
|
||||
|
@@ -7,11 +7,12 @@ import { 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, flatMap, map } from 'rxjs/operators';
|
||||
import { distinctUntilChanged, filter, flatMap, map, 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 { Item } from '../../../../core/shared/item.model';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||
|
||||
/**
|
||||
* Operator for comparing arrays using a mapping function
|
||||
@@ -120,3 +121,17 @@ export const relationsToRepresentations = (parentId: string, itemType: string, m
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param item
|
||||
* @param relationshipService
|
||||
*/
|
||||
export const getRelationsByRelatedItemIds = (item: Item, relationshipService: RelationshipService) =>
|
||||
(source: Observable<string[]>): Observable<Relationship[]> =>
|
||||
source.pipe(
|
||||
flatMap((relatedItemIds: string[]) => relationshipService.getItemResolvedRelatedItemsAndRelationships(item).pipe(
|
||||
map(([leftItems, rightItems, rels]) => rels.filter((rel: Relationship, index: number) => relatedItemIds.indexOf(leftItems[index].uuid) > -1 || relatedItemIds.indexOf(rightItems[index].uuid) > -1))
|
||||
))
|
||||
);
|
||||
|
@@ -3,7 +3,7 @@ import { RequestService } from './request.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { hasValue, hasValueOperator, isNotEmptyOperator } from '../../shared/empty.util';
|
||||
import { distinctUntilChanged, flatMap, map, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { distinctUntilChanged, filter, flatMap, map, switchMap, take, tap } from 'rxjs/operators';
|
||||
import {
|
||||
configureRequest,
|
||||
filterSuccessfulResponses,
|
||||
@@ -70,20 +70,35 @@ export class RelationshipService {
|
||||
* @param item
|
||||
*/
|
||||
getItemResolvedRelsAndTypes(item: Item): Observable<[Relationship[], RelationshipType[]]> {
|
||||
const relationships$ = this.getItemRelationshipsArray(item);
|
||||
|
||||
const relationshipTypes$ = relationships$.pipe(
|
||||
flatMap((rels: Relationship[]) =>
|
||||
observableZip(...rels.map((rel: Relationship) => rel.relationshipType)).pipe(
|
||||
map(([...arr]: Array<RemoteData<RelationshipType>>) => arr.map((d: RemoteData<RelationshipType>) => d.payload).filter((type) => hasValue(type)))
|
||||
)
|
||||
),
|
||||
distinctUntilChanged(compareArraysUsingIds())
|
||||
);
|
||||
|
||||
return observableCombineLatest(
|
||||
relationships$,
|
||||
relationshipTypes$
|
||||
this.getItemRelationshipsArray(item),
|
||||
this.getItemRelationshipTypesArray(item)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a combined observable containing an array of all the item's relationship's left- and right-side items, as well as an array of the relationships their types
|
||||
* This is used for easier access of a relationship's type and left and right items because they exist as observables
|
||||
* @param item
|
||||
*/
|
||||
getItemResolvedRelatedItemsAndTypes(item: Item): Observable<[Item[], Item[], RelationshipType[]]> {
|
||||
return observableCombineLatest(
|
||||
this.getItemLeftRelatedItemArray(item),
|
||||
this.getItemRightRelatedItemArray(item),
|
||||
this.getItemRelationshipTypesArray(item)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a combined observable containing an array of all the item's relationship's left- and right-side items, as well as an array of the relationships themselves
|
||||
* This is used for easier access of the relationship and their left and right items because they exist as observables
|
||||
* @param item
|
||||
*/
|
||||
getItemResolvedRelatedItemsAndRelationships(item: Item): Observable<[Item[], Item[], Relationship[]]> {
|
||||
return observableCombineLatest(
|
||||
this.getItemLeftRelatedItemArray(item),
|
||||
this.getItemRightRelatedItemArray(item),
|
||||
this.getItemRelationshipsArray(item)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -101,17 +116,60 @@ export class RelationshipService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item their relationship types in the form of an array
|
||||
* @param item
|
||||
*/
|
||||
getItemRelationshipTypesArray(item: Item): Observable<RelationshipType[]> {
|
||||
return this.getItemRelationshipsArray(item).pipe(
|
||||
flatMap((rels: Relationship[]) =>
|
||||
observableZip(...rels.map((rel: Relationship) => rel.relationshipType)).pipe(
|
||||
map(([...arr]: Array<RemoteData<RelationshipType>>) => arr.map((d: RemoteData<RelationshipType>) => d.payload).filter((type) => hasValue(type))),
|
||||
filter((arr) => arr.length === rels.length)
|
||||
)
|
||||
),
|
||||
distinctUntilChanged(compareArraysUsingIds())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item his relationship's left-side related items in the form of an array
|
||||
* @param item
|
||||
*/
|
||||
getItemLeftRelatedItemArray(item: Item): Observable<Item[]> {
|
||||
return this.getItemRelationshipsArray(item).pipe(
|
||||
flatMap((rels: Relationship[]) => observableZip(...rels.map((rel: Relationship) => rel.leftItem)).pipe(
|
||||
map(([...arr]: Array<RemoteData<Item>>) => arr.map((rd: RemoteData<Item>) => rd.payload).filter((i) => hasValue(i))),
|
||||
filter((arr) => arr.length === rels.length)
|
||||
)),
|
||||
distinctUntilChanged(compareArraysUsingIds())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item his relationship's right-side related items in the form of an array
|
||||
* @param item
|
||||
*/
|
||||
getItemRightRelatedItemArray(item: Item): Observable<Item[]> {
|
||||
return this.getItemRelationshipsArray(item).pipe(
|
||||
flatMap((rels: Relationship[]) => observableZip(...rels.map((rel: Relationship) => rel.rightItem)).pipe(
|
||||
map(([...arr]: Array<RemoteData<Item>>) => arr.map((rd: RemoteData<Item>) => rd.payload).filter((i) => hasValue(i))),
|
||||
filter((arr) => arr.length === rels.length)
|
||||
)),
|
||||
distinctUntilChanged(compareArraysUsingIds())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of an item their unique relationship type's labels
|
||||
* The array doesn't contain any duplicate labels
|
||||
* @param item
|
||||
*/
|
||||
getItemRelationshipLabels(item: Item): Observable<string[]> {
|
||||
return this.getItemResolvedRelsAndTypes(item).pipe(
|
||||
map(([relsCurrentPage, relTypesCurrentPage]) => {
|
||||
return this.getItemResolvedRelatedItemsAndTypes(item).pipe(
|
||||
map(([leftItems, rightItems, relTypesCurrentPage]) => {
|
||||
return relTypesCurrentPage.map((type, index) => {
|
||||
const relationship = relsCurrentPage[index];
|
||||
if (relationship.leftId === item.uuid) {
|
||||
if (leftItems[index].uuid === item.uuid) {
|
||||
return type.leftLabel;
|
||||
} else {
|
||||
return type.rightLabel;
|
||||
@@ -128,7 +186,7 @@ export class RelationshipService {
|
||||
*/
|
||||
getRelatedItems(item: Item): Observable<Item[]> {
|
||||
return this.getItemRelationshipsArray(item).pipe(
|
||||
relationsToItems(item.uuid, this.itemService)
|
||||
relationsToItems(item.uuid)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -141,7 +199,7 @@ export class RelationshipService {
|
||||
getRelatedItemsByLabel(item: Item, label: string): Observable<Item[]> {
|
||||
return this.getItemResolvedRelsAndTypes(item).pipe(
|
||||
filterRelationsByTypeLabel(label),
|
||||
relationsToItems(item.uuid, this.itemService)
|
||||
relationsToItems(item.uuid)
|
||||
);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user