Skip to content
Snippets Groups Projects
Commit 14d7437d authored by Kristof De Langhe's avatar Kristof De Langhe
Browse files

63184: Edit-Relationships left/right Item refactoring

parent 74813aae
No related branches found
No related tags found
No related merge requests found
......@@ -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,40 +106,49 @@ 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);
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);
// 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();
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();
}
/**
......
......@@ -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);
return observableCombineLatest(
this.getItemRelationshipsArray(item),
this.getItemRelationshipTypesArray(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())
/**
* 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(
relationships$,
relationshipTypes$
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)
);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment