Commits
Art Lowel authored 807500db41a
59 59 | const relationshipListsStateSelector = (state: AppState) => state.relationshipLists; |
60 60 | |
61 61 | const relationshipListStateSelector = (listID: string): MemoizedSelector<AppState, NameVariantListState> => { |
62 62 | return keySelector<NameVariantListState>(listID, relationshipListsStateSelector); |
63 63 | }; |
64 64 | |
65 65 | const relationshipStateSelector = (listID: string, itemID: string): MemoizedSelector<AppState, string> => { |
66 66 | return keySelector<string>(itemID, relationshipListStateSelector(listID)); |
67 67 | }; |
68 68 | |
69 + | /** |
70 + | * Return true if the Item in the payload of the source observable matches |
71 + | * the given Item by UUID |
72 + | * |
73 + | * @param itemCheck the Item to compare with |
74 + | */ |
75 + | const compareItemsByUUID = (itemCheck: Item) => |
76 + | (source: Observable<RemoteData<Item>>): Observable<boolean> => |
77 + | source.pipe( |
78 + | getFirstSucceededRemoteDataPayload(), |
79 + | map((item: Item) => item.uuid === itemCheck.uuid) |
80 + | ); |
81 + | |
69 82 | /** |
70 83 | * The service handling all relationship requests |
71 84 | */ |
72 85 | @Injectable() |
73 86 | @dataService(RELATIONSHIP) |
74 87 | export class RelationshipService extends DataService<Relationship> { |
75 88 | protected linkPath = 'relationships'; |
76 89 | |
77 90 | constructor(protected itemService: ItemDataService, |
78 91 | protected requestService: RequestService, |
118 131 | |
119 132 | /** |
120 133 | * Method to create a new relationship |
121 134 | * @param typeId The identifier of the relationship type |
122 135 | * @param item1 The first item of the relationship |
123 136 | * @param item2 The second item of the relationship |
124 137 | * @param leftwardValue The leftward value of the relationship |
125 138 | * @param rightwardValue The rightward value of the relationship |
126 139 | */ |
127 140 | addRelationship(typeId: string, item1: Item, item2: Item, leftwardValue?: string, rightwardValue?: string): Observable<RestResponse> { |
128 - | console.log('addRelationship', typeId, item1, item2, leftwardValue, rightwardValue); |
129 141 | const options: HttpOptions = Object.create({}); |
130 142 | let headers = new HttpHeaders(); |
131 143 | headers = headers.append('Content-Type', 'text/uri-list'); |
132 144 | options.headers = headers; |
133 145 | return this.halService.getEndpoint(this.linkPath).pipe( |
134 146 | isNotEmptyOperator(), |
135 147 | take(1), |
136 148 | map((endpointUrl: string) => `${endpointUrl}?relationshipType=${typeId}`), |
137 149 | map((endpointUrl: string) => isNotEmpty(leftwardValue) ? `${endpointUrl}&leftwardValue=${leftwardValue}` : endpointUrl), |
138 150 | map((endpointUrl: string) => isNotEmpty(rightwardValue) ? `${endpointUrl}&rightwardValue=${rightwardValue}` : endpointUrl), |
165 177 | }) |
166 178 | } |
167 179 | |
168 180 | /** |
169 181 | * Method to remove an item that's part of a relationship from the cache |
170 182 | * @param item The item to remove from the cache |
171 183 | */ |
172 184 | public refreshRelationshipItemsInCache(item) { |
173 185 | this.objectCache.remove(item._links.self.href); |
174 186 | this.requestService.removeByHrefSubstring(item.uuid); |
175 - | observableCombineLatest( |
187 + | observableCombineLatest([ |
176 188 | this.objectCache.hasBySelfLinkObservable(item._links.self.href), |
177 189 | this.requestService.hasByHrefObservable(item.self) |
178 - | ).pipe( |
190 + | ]).pipe( |
179 191 | filter(([existsInOC, existsInRC]) => !existsInOC && !existsInRC), |
180 192 | take(1), |
181 193 | switchMap(() => this.itemService.findByHref(item._links.self.href).pipe(take(1))) |
182 194 | ).subscribe(); |
183 195 | } |
184 196 | |
185 197 | /** |
186 198 | * Get an item's relationships in the form of an array |
187 - | * @param item |
199 + | * |
200 + | * @param item The {@link Item} to get {@link Relationship}s for |
201 + | * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s |
202 + | * should be automatically resolved |
188 203 | */ |
189 204 | getItemRelationshipsArray(item: Item, linksToFollow: Array<FollowLinkConfig<Relationship>>): Observable<Relationship[]> { |
190 205 | return this.findAllByHref(item._links.relationships.href, undefined, linksToFollow).pipe( |
191 206 | getSucceededRemoteData(), |
192 207 | getRemoteDataPayload(), |
193 208 | map((rels: PaginatedList<Relationship>) => rels.page), |
194 209 | hasValueOperator(), |
195 210 | distinctUntilChanged(compareArraysUsingIds()), |
196 211 | ); |
197 212 | } |
276 291 | |
277 292 | /** |
278 293 | * Method for fetching an item's relationships, but filtered by related item IDs (essentially performing a reverse lookup) |
279 294 | * Only relationships where leftItem or rightItem's ID is present in the list provided will be returned |
280 295 | * @param item |
281 296 | * @param uuids |
282 297 | */ |
283 298 | getRelationshipsByRelatedItemIds(item: Item, uuids: string[]): Observable<Relationship[]> { |
284 299 | return this.getItemRelationshipsArray(item, followLink('leftItem'), followLink('rightItem')).pipe( |
285 300 | switchMap((relationships: Relationship[]) => { |
286 - | return observableCombineLatest(relationships.map((relationship: Relationship) => { |
301 + | return observableCombineLatest(relationships.map((relationship: Relationship) => { |
287 302 | const isLeftItem$ = this.isItemInUUIDArray(relationship.leftItem, uuids); |
288 303 | const isRightItem$ = this.isItemInUUIDArray(relationship.rightItem, uuids); |
289 - | return observableCombineLatest(isLeftItem$, isRightItem$).pipe( |
304 + | return observableCombineLatest([isLeftItem$, isRightItem$]).pipe( |
290 305 | filter(([isLeftItem, isRightItem]) => isLeftItem || isRightItem), |
291 306 | map(() => relationship), |
292 307 | startWith(undefined) |
293 308 | ); |
294 309 | })) |
295 310 | }), |
296 311 | map((relationships: Relationship[]) => relationships.filter(((relationship) => hasValue(relationship)))), |
297 312 | ) |
298 313 | } |
299 314 | |
305 320 | ); |
306 321 | } |
307 322 | |
308 323 | /** |
309 324 | * Method to retrieve a relationship based on two items and a relationship type label |
310 325 | * @param item1 The first item in the relationship |
311 326 | * @param item2 The second item in the relationship |
312 327 | * @param label The rightward or leftward type of the relationship |
313 328 | */ |
314 329 | getRelationshipByItemsAndLabel(item1: Item, item2: Item, label: string, options?: FindListOptions): Observable<Relationship> { |
315 - | console.log('getRelationshipByItemsAndLabel', item1, item2, label, options); |
316 330 | return this.getItemRelationshipsByLabel( |
317 331 | item1, |
318 332 | label, |
319 333 | options, |
320 334 | followLink('relationshipType'), |
321 335 | followLink('leftItem'), |
322 336 | followLink('rightItem') |
323 337 | ).pipe( |
324 338 | getSucceededRemoteData(), |
325 339 | // the mergemap below will emit all elements of the list as separate events |
326 340 | mergeMap((relationshipListRD: RemoteData<PaginatedList<Relationship>>) => relationshipListRD.payload.page), |
327 341 | mergeMap((relationship: Relationship) => { |
328 342 | return observableCombineLatest([ |
329 - | this.isItemMatchWithItemRD(this.itemService.findByHref(relationship._links.leftItem.href), item2), |
330 - | this.isItemMatchWithItemRD(this.itemService.findByHref(relationship._links.rightItem.href), item2) |
343 + | this.itemService.findByHref(relationship._links.leftItem.href).pipe(compareItemsByUUID(item2)), |
344 + | this.itemService.findByHref(relationship._links.rightItem.href).pipe(compareItemsByUUID(item2)) |
331 345 | ]).pipe( |
332 346 | map(([isLeftItem, isRightItem]) => isLeftItem || isRightItem), |
333 347 | map((isMatch) => isMatch ? relationship : undefined) |
334 348 | ); |
335 349 | }), |
336 350 | filter((relationship) => hasValue(relationship)), |
337 351 | take(1) |
338 352 | ) |
339 353 | } |
340 354 | |
341 - | private isItemMatchWithItemRD(itemRD$: Observable<RemoteData<Item>>, itemCheck: Item): Observable<boolean> { |
342 - | return itemRD$.pipe( |
343 - | getFirstSucceededRemoteDataPayload(), |
344 - | map((item: Item) => item.uuid === itemCheck.uuid) |
345 - | ); |
346 - | } |
347 - | |
348 355 | /** |
349 356 | * Method to set the name variant for specific list and item |
350 357 | * @param listID The list for which to save the name variant |
351 358 | * @param itemID The item ID for which to save the name variant |
352 359 | * @param nameVariant The name variant to save |
353 360 | */ |
354 361 | public setNameVariant(listID: string, itemID: string, nameVariant: string) { |
355 362 | this.appStore.dispatch(new SetNameVariantAction(listID, itemID, nameVariant)); |
356 363 | } |
357 364 | |
434 441 | filter((relationshipRD: RemoteData<Relationship>) => relationshipRD.state === RemoteDataState.ResponsePending), |
435 442 | take(1), |
436 443 | ).subscribe((relationshipRD: RemoteData<Relationship>) => { |
437 444 | if (relationshipRD.state === RemoteDataState.ResponsePending) { |
438 445 | this.refreshRelationshipItemsInCacheByRelationship(reoRel.relationship.id); |
439 446 | } |
440 447 | }); |
441 448 | |
442 449 | return update$; |
443 450 | } |
451 + | |
452 + | |
453 + | /** |
454 + | * Patch isn't supported on the relationship endpoint, so use put instead. |
455 + | * |
456 + | * @param object the {@link Relationship} to update |
457 + | */ |
458 + | update(object: Relationship): Observable<RemoteData<Relationship>> { |
459 + | return this.put(object); |
460 + | } |
444 461 | } |