61142: RelationshipService

This commit is contained in:
Kristof De Langhe
2019-04-03 14:22:07 +02:00
parent a99fa4d4a2
commit 7a74c3355b
3 changed files with 149 additions and 70 deletions

View File

@@ -1,22 +1,16 @@
import { Component } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { Item } from '../../../core/shared/item.model'; import { Item } from '../../../core/shared/item.model';
import { FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer'; import { FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer';
import { Observable } from 'rxjs/internal/Observable'; import { Observable } from 'rxjs/internal/Observable';
import { distinctUntilChanged, first, flatMap, map, switchMap } from 'rxjs/operators'; import { switchMap, take } from 'rxjs/operators';
import { zip as observableZip } from 'rxjs';
import { RemoteData } from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list';
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
import { hasValue, hasValueOperator } from '../../../shared/empty.util';
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../core/shared/operators';
import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model';
import {
compareArraysUsingIds,
filterRelationsByTypeLabel,
relationsToItems
} from '../../simple/item-types/shared/item.component';
import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest';
import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component'; import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component';
import { ItemDataService } from '../../../core/data/item-data.service';
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
import { ActivatedRoute, Router } from '@angular/router';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
import { GLOBAL_CONFIG, GlobalConfig } from '../../../../config';
import { RelationshipService } from '../../../core/data/relationship.service';
@Component({ @Component({
selector: 'ds-item-relationships', selector: 'ds-item-relationships',
@@ -33,49 +27,32 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent {
*/ */
relationLabels$: Observable<string[]>; relationLabels$: Observable<string[]>;
/** constructor(
* Resolved relationships and types together in one observable protected itemService: ItemDataService,
*/ protected objectUpdatesService: ObjectUpdatesService,
resolvedRelsAndTypes$: Observable<[Relationship[], RelationshipType[]]>; protected router: Router,
protected notificationsService: NotificationsService,
protected translateService: TranslateService,
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
protected route: ActivatedRoute,
protected relationshipService: RelationshipService
) {
super(itemService, objectUpdatesService, router, notificationsService, translateService, EnvConfig, route);
}
/** /**
* Set up and initialize all fields * Set up and initialize all fields
*/ */
ngOnInit(): void { ngOnInit(): void {
super.ngOnInit(); super.ngOnInit();
this.initRelationshipObservables(); this.relationLabels$ = this.relationshipService.getItemRelationshipLabels(this.item);
}
/**
* Initialize the item's relationship observables for easier access across the component
*/
initRelationshipObservables() {
const relationships$ = this.getRelationships();
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())
);
this.resolvedRelsAndTypes$ = observableCombineLatest(
relationships$,
relationshipTypes$
);
this.relationLabels$ = relationshipTypes$.pipe(
map((types: RelationshipType[]) => Array.from(new Set(types.map((type) => type.leftLabel))))
);
} }
/** /**
* Initialize the values and updates of the current item's relationship fields * Initialize the values and updates of the current item's relationship fields
*/ */
public initializeUpdates(): void { public initializeUpdates(): void {
this.updates$ = this.getRelationships().pipe( this.updates$ = this.relationshipService.getRelatedItems(this.item).pipe(
relationsToItems(this.item.id, this.itemService),
switchMap((items: Item[]) => this.objectUpdatesService.getFieldUpdates(this.url, items)) switchMap((items: Item[]) => this.objectUpdatesService.getFieldUpdates(this.url, items))
); );
} }
@@ -88,9 +65,7 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent {
} }
public submit(): void { public submit(): void {
const updatedItems$ = this.getRelationships().pipe( const updatedItems$ = this.relationshipService.getRelatedItems(this.item).pipe(
first(),
relationsToItems(this.item.id, this.itemService),
switchMap((items: Item[]) => this.objectUpdatesService.getUpdatedFields(this.url, items) as Observable<Item[]>) switchMap((items: Item[]) => this.objectUpdatesService.getUpdatedFields(this.url, items) as Observable<Item[]>)
); );
// TODO: Delete relationships // TODO: Delete relationships
@@ -100,36 +75,17 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent {
* Sends all initial values of this item to the object updates service * Sends all initial values of this item to the object updates service
*/ */
public initializeOriginalFields() { public initializeOriginalFields() {
this.getRelationships().pipe( this.relationshipService.getRelatedItems(this.item).pipe(take(1)).subscribe((items: Item[]) => {
first(),
relationsToItems(this.item.id, this.itemService)
).subscribe((items: Item[]) => {
this.objectUpdatesService.initialize(this.url, items, this.item.lastModified); this.objectUpdatesService.initialize(this.url, items, this.item.lastModified);
}); });
} }
/**
* Fetch all the relationships of the item
*/
public getRelationships(): Observable<Relationship[]> {
return this.item.relationships.pipe(
getSucceededRemoteData(),
getRemoteDataPayload(),
map((rels: PaginatedList<Relationship>) => rels.page),
hasValueOperator(),
distinctUntilChanged(compareArraysUsingIds())
);
}
/** /**
* 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<Item[]> {
return this.resolvedRelsAndTypes$.pipe( return this.relationshipService.getRelatedItemsByLabel(this.item, label);
filterRelationsByTypeLabel(label),
relationsToItems(this.item.id, this.itemService)
);
} }
/** /**

View File

@@ -81,6 +81,7 @@ import { DSOChangeAnalyzer } from './data/dso-change-analyzer.service';
import { ObjectUpdatesService } from './data/object-updates/object-updates.service'; import { ObjectUpdatesService } from './data/object-updates/object-updates.service';
import { DefaultChangeAnalyzer } from './data/default-change-analyzer.service'; import { DefaultChangeAnalyzer } from './data/default-change-analyzer.service';
import { SearchService } from '../+search-page/search-service/search.service'; import { SearchService } from '../+search-page/search-service/search.service';
import { RelationshipService } from './data/relationship.service';
const IMPORTS = [ const IMPORTS = [
CommonModule, CommonModule,
@@ -163,6 +164,7 @@ const PROVIDERS = [
MenuService, MenuService,
ObjectUpdatesService, ObjectUpdatesService,
SearchService, SearchService,
RelationshipService,
// register AuthInterceptor as HttpInterceptor // register AuthInterceptor as HttpInterceptor
{ {
provide: HTTP_INTERCEPTORS, provide: HTTP_INTERCEPTORS,

View File

@@ -0,0 +1,121 @@
import { Injectable } from '@angular/core';
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, take } from 'rxjs/operators';
import {
configureRequest,
filterSuccessfulResponses,
getRemoteDataPayload,
getSucceededRemoteData
} from '../shared/operators';
import { DeleteRequest, RestRequest } from './request.models';
import { Observable } from 'rxjs/internal/Observable';
import { RestResponse } from '../cache/response.models';
import { Item } from '../shared/item.model';
import { Relationship } from '../shared/item-relationships/relationship.model';
import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
import { RemoteData } from './remote-data';
import {
compareArraysUsingIds,
filterRelationsByTypeLabel, relationsToItems
} from '../../+item-page/simple/item-types/shared/item.component';
import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest';
import { zip as observableZip } from 'rxjs';
import { PaginatedList } from './paginated-list';
import { ItemDataService } from './item-data.service';
/**
* The service handling all relationship requests
*/
@Injectable()
export class RelationshipService {
protected linkPath = 'relationships';
constructor(protected requestService: RequestService,
protected halService: HALEndpointService,
protected rdbService: RemoteDataBuildService,
protected itemService: ItemDataService) {
}
getRelationshipEndpoint(uuid: string) {
return this.halService.getEndpoint(this.linkPath).pipe(
map((href: string) => `${href}/${uuid}`)
);
}
deleteRelationship(uuid: string): Observable<RestResponse> {
const requestUuid = this.requestService.generateRequestId();
this.getRelationshipEndpoint(uuid).pipe(
isNotEmptyOperator(),
distinctUntilChanged(),
map((endpointURL: string) => new DeleteRequest(requestUuid, endpointURL)),
configureRequest(this.requestService),
take(1)
).subscribe();
return this.requestService.getByUUID(requestUuid).pipe(
filterSuccessfulResponses()
);
}
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$
);
}
getItemRelationshipsArray(item: Item): Observable<Relationship[]> {
return item.relationships.pipe(
getSucceededRemoteData(),
getRemoteDataPayload(),
map((rels: PaginatedList<Relationship>) => rels.page),
hasValueOperator(),
distinctUntilChanged(compareArraysUsingIds())
);
}
getItemRelationshipLabels(item: Item): Observable<string[]> {
return this.getItemResolvedRelsAndTypes(item).pipe(
map(([relsCurrentPage, relTypesCurrentPage]) => {
return relTypesCurrentPage.map((type, index) => {
const relationship = relsCurrentPage[index];
if (relationship.leftId === item.uuid) {
return type.leftLabel;
} else {
return type.rightLabel;
}
});
}),
map((labels: string[]) => Array.from(new Set(labels)))
)
}
getRelatedItems(item: Item): Observable<Item[]> {
return this.getItemRelationshipsArray(item).pipe(
relationsToItems(item.uuid, this.itemService)
);
}
getRelatedItemsByLabel(item: Item, label: string): Observable<Item[]> {
return this.getItemResolvedRelsAndTypes(item).pipe(
filterRelationsByTypeLabel(label),
relationsToItems(item.uuid, this.itemService)
);
}
}