Skip to content

Commit 1621036

Browse files
authored
Merge pull request DSpace#3109 from alexandrevryghem/w2p-113560_edit-item-add-relationships-one-by-one_contribute-7_x
[Port dspace-7_x] Fixed caching & same entity type relationship with different left/rightwardtype bugs on edit item relationships
2 parents 2d28dfc + a0260f5 commit 1621036

23 files changed

Lines changed: 533 additions & 69 deletions

src/app/core/data/relationship-data.service.spec.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ describe('RelationshipDataService', () => {
125125

126126
const itemService = jasmine.createSpyObj('itemService', {
127127
findById: (uuid) => createSuccessfulRemoteDataObject(relatedItems.find((relatedItem) => relatedItem.id === uuid)),
128-
findByHref: createSuccessfulRemoteDataObject$(relatedItems[0])
128+
findByHref: createSuccessfulRemoteDataObject$(relatedItems[0]),
129+
getIDHrefObs: (uuid: string) => observableOf(`https://demo.dspace.org/server/api/core/items/${uuid}`),
129130
});
130131

131132
function initTestService() {
@@ -240,6 +241,16 @@ describe('RelationshipDataService', () => {
240241
});
241242
});
242243

244+
describe('searchByItemsAndType', () => {
245+
it('should call addDependency for each item to invalidate the request when one of the items is update', () => {
246+
spyOn(service as any, 'addDependency');
247+
248+
service.searchByItemsAndType(relationshipType.id, item.id, relationshipType.leftwardType, ['item-id-1', 'item-id-2']);
249+
250+
expect((service as any).addDependency).toHaveBeenCalledTimes(2);
251+
});
252+
});
253+
243254
describe('resolveMetadataRepresentation', () => {
244255
const parentItem: Item = Object.assign(new Item(), {
245256
id: 'parent-item',

src/app/core/data/relationship-data.service.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,13 +533,18 @@ export class RelationshipDataService extends IdentifiableDataService<Relationshi
533533
);
534534
});
535535

536-
return this.searchBy(
536+
const searchRD$: Observable<RemoteData<PaginatedList<Relationship>>> = this.searchBy(
537537
'byItemsAndType',
538538
{
539539
searchParams: searchParams
540540
},
541541
) as Observable<RemoteData<PaginatedList<Relationship>>>;
542542

543+
arrayOfItemIds.forEach((itemId: string) => {
544+
this.addDependency(searchRD$, this.itemService.getIDHrefObs(encodeURIComponent(itemId)));
545+
});
546+
547+
return searchRD$;
543548
}
544549

545550
/**

src/app/item-page/edit-item-page/edit-item-page.module.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ import { ResultsBackButtonModule } from '../../shared/results-back-button/result
4646
import {
4747
AccessControlFormModule
4848
} from '../../shared/access-control-form-container/access-control-form.module';
49+
import {
50+
EditRelationshipListWrapperComponent
51+
} from './item-relationships/edit-relationship-list-wrapper/edit-relationship-list-wrapper.component';
4952

5053
/**
5154
* Module that contains all components related to the Edit Item page administrator functionality
@@ -94,6 +97,7 @@ import {
9497
ItemRegisterDoiComponent,
9598
ItemCurateComponent,
9699
ItemAccessControlComponent,
100+
EditRelationshipListWrapperComponent,
97101
],
98102
providers: [
99103
BundleDataService,

src/app/item-page/edit-item-page/item-relationships/edit-item-relationships.service.spec.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ describe('EditItemRelationshipsService', () => {
185185

186186
expect(itemService.invalidateByHref).toHaveBeenCalledWith(currentItem.self);
187187
expect(itemService.invalidateByHref).toHaveBeenCalledWith(relationshipItem1.self);
188+
expect(itemService.invalidateByHref).toHaveBeenCalledWith(relationshipItem2.self);
188189

189190
expect(notificationsService.success).toHaveBeenCalledTimes(1);
190191
});
@@ -265,6 +266,116 @@ describe('EditItemRelationshipsService', () => {
265266
});
266267
});
267268

269+
describe('isProvidedItemTypeLeftType', () => {
270+
it('should return true if the provided item corresponds to the left type of the relationship', (done) => {
271+
const relationshipType = Object.assign(new RelationshipType(), {
272+
leftType: createSuccessfulRemoteDataObject$({id: 'leftType'}),
273+
rightType: createSuccessfulRemoteDataObject$({id: 'rightType'}),
274+
});
275+
const itemType = Object.assign(new ItemType(), {id: 'leftType'} );
276+
const item = Object.assign(new Item(), {uuid: 'item-uuid'});
277+
278+
const result = service.isProvidedItemTypeLeftType(relationshipType, itemType, item);
279+
result.subscribe((resultValue) => {
280+
expect(resultValue).toBeTrue();
281+
done();
282+
});
283+
});
284+
285+
it('should return false if the provided item corresponds to the right type of the relationship', (done) => {
286+
const relationshipType = Object.assign(new RelationshipType(), {
287+
leftType: createSuccessfulRemoteDataObject$({id: 'leftType'}),
288+
rightType: createSuccessfulRemoteDataObject$({id: 'rightType'}),
289+
});
290+
const itemType = Object.assign(new ItemType(), {id: 'rightType'} );
291+
const item = Object.assign(new Item(), {uuid: 'item-uuid'});
292+
293+
const result = service.isProvidedItemTypeLeftType(relationshipType, itemType, item);
294+
result.subscribe((resultValue) => {
295+
expect(resultValue).toBeFalse();
296+
done();
297+
});
298+
});
299+
300+
it('should return undefined if the provided item corresponds does not match any of the relationship types', (done) => {
301+
const relationshipType = Object.assign(new RelationshipType(), {
302+
leftType: createSuccessfulRemoteDataObject$({id: 'leftType'}),
303+
rightType: createSuccessfulRemoteDataObject$({id: 'rightType'}),
304+
});
305+
const itemType = Object.assign(new ItemType(), {id: 'something-else'} );
306+
const item = Object.assign(new Item(), {uuid: 'item-uuid'});
307+
308+
const result = service.isProvidedItemTypeLeftType(relationshipType, itemType, item);
309+
result.subscribe((resultValue) => {
310+
expect(resultValue).toBeUndefined();
311+
done();
312+
});
313+
});
314+
});
315+
316+
describe('relationshipMatchesBothSameTypes', () => {
317+
it('should return true if both left and right type of the relationship type are the same and match the provided itemtype', (done) => {
318+
const relationshipType = Object.assign(new RelationshipType(), {
319+
leftType: createSuccessfulRemoteDataObject$({id: 'sameType'}),
320+
rightType: createSuccessfulRemoteDataObject$({id:'sameType'}),
321+
leftwardType: 'isDepartmentOfDivision',
322+
rightwardType: 'isDivisionOfDepartment',
323+
});
324+
const itemType = Object.assign(new ItemType(), {id: 'sameType'} );
325+
326+
const result = service.shouldDisplayBothRelationshipSides(relationshipType, itemType);
327+
result.subscribe((resultValue) => {
328+
expect(resultValue).toBeTrue();
329+
done();
330+
});
331+
});
332+
it('should return false if both left and right type of the relationship type are the same and match the provided itemtype but the leftwardType & rightwardType is identical', (done) => {
333+
const relationshipType = Object.assign(new RelationshipType(), {
334+
leftType: createSuccessfulRemoteDataObject$({ id: 'sameType' }),
335+
rightType: createSuccessfulRemoteDataObject$({ id: 'sameType' }),
336+
leftwardType: 'isOrgUnitOfOrgUnit',
337+
rightwardType: 'isOrgUnitOfOrgUnit',
338+
});
339+
const itemType = Object.assign(new ItemType(), { id: 'sameType' });
340+
341+
const result = service.shouldDisplayBothRelationshipSides(relationshipType, itemType);
342+
result.subscribe((resultValue) => {
343+
expect(resultValue).toBeFalse();
344+
done();
345+
});
346+
});
347+
it('should return false if both left and right type of the relationship type are the same and do not match the provided itemtype', (done) => {
348+
const relationshipType = Object.assign(new RelationshipType(), {
349+
leftType: createSuccessfulRemoteDataObject$({id: 'sameType'}),
350+
rightType: createSuccessfulRemoteDataObject$({id: 'sameType'}),
351+
leftwardType: 'isDepartmentOfDivision',
352+
rightwardType: 'isDivisionOfDepartment',
353+
});
354+
const itemType = Object.assign(new ItemType(), {id: 'something-else'} );
355+
356+
const result = service.shouldDisplayBothRelationshipSides(relationshipType, itemType);
357+
result.subscribe((resultValue) => {
358+
expect(resultValue).toBeFalse();
359+
done();
360+
});
361+
});
362+
it('should return false if both left and right type of the relationship type are different', (done) => {
363+
const relationshipType = Object.assign(new RelationshipType(), {
364+
leftType: createSuccessfulRemoteDataObject$({id: 'leftType'}),
365+
rightType: createSuccessfulRemoteDataObject$({id: 'rightType'}),
366+
leftwardType: 'isAuthorOfPublication',
367+
rightwardType: 'isPublicationOfAuthor',
368+
});
369+
const itemType = Object.assign(new ItemType(), {id: 'leftType'} );
370+
371+
const result = service.shouldDisplayBothRelationshipSides(relationshipType, itemType);
372+
result.subscribe((resultValue) => {
373+
expect(resultValue).toBeFalse();
374+
done();
375+
});
376+
});
377+
});
378+
268379
describe('displayNotifications', () => {
269380
it('should show one success notification when multiple requests succeeded', () => {
270381
service.displayNotifications([

src/app/item-page/edit-item-page/item-relationships/edit-item-relationships.service.ts

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from '../../../core/data/object-updates/object-updates.reducer';
1111
import { RemoteData } from '../../../core/data/remote-data';
1212
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
13-
import { EMPTY, Observable, BehaviorSubject, Subscription } from 'rxjs';
13+
import { EMPTY, Observable, BehaviorSubject, Subscription, combineLatest as observableCombineLatest } from 'rxjs';
1414
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
1515
import { ItemDataService } from '../../../core/data/item-data.service';
1616
import { Item } from '../../../core/shared/item.model';
@@ -20,6 +20,9 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
2020
import { RelationshipDataService } from '../../../core/data/relationship-data.service';
2121
import { EntityTypeDataService } from '../../../core/data/entity-type-data.service';
2222
import { TranslateService } from '@ngx-translate/core';
23+
import { ItemType } from '../../../core/shared/item-relationships/item-type.model';
24+
import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../../../core/shared/operators';
25+
import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model';
2326

2427
@Injectable({
2528
providedIn: 'root'
@@ -58,7 +61,17 @@ export class EditItemRelationshipsService {
5861
// process each update one by one, while waiting for the previous to finish
5962
concatMap((update: FieldUpdate) => {
6063
if (update.changeType === FieldChangeType.REMOVE) {
61-
return this.deleteRelationship(update.field as DeleteRelationship).pipe(take(1));
64+
return this.deleteRelationship(update.field as DeleteRelationship).pipe(
65+
take(1),
66+
switchMap((deleteRD: RemoteData<NoContent>) => {
67+
if (deleteRD.hasSucceeded) {
68+
return this.itemService.invalidateByHref((update.field as DeleteRelationship).relatedItem._links.self.href).pipe(
69+
map(() => deleteRD),
70+
);
71+
}
72+
return [deleteRD];
73+
}),
74+
);
6275
} else if (update.changeType === FieldChangeType.ADD) {
6376
return this.addRelationship(update.field as RelationshipIdentifiable).pipe(
6477
take(1),
@@ -169,6 +182,55 @@ export class EditItemRelationshipsService {
169182
}
170183
}
171184

185+
isProvidedItemTypeLeftType(relationshipType: RelationshipType, itemType: ItemType, item: Item): Observable<boolean> {
186+
return this.getRelationshipLeftAndRightType(relationshipType).pipe(
187+
map(([leftType, rightType]: [ItemType, ItemType]) => {
188+
if (leftType.id === itemType.id) {
189+
return true;
190+
}
191+
192+
if (rightType.id === itemType.id) {
193+
return false;
194+
}
195+
196+
// should never happen...
197+
console.warn(`The item ${item.uuid} is not on the right or the left side of relationship type ${relationshipType.uuid}`);
198+
return undefined;
199+
})
200+
);
201+
}
202+
203+
/**
204+
* Whether both side of the relationship need to be displayed on the edit relationship page or not.
205+
*
206+
* @param relationshipType The relationship type
207+
* @param itemType The item type
208+
*/
209+
shouldDisplayBothRelationshipSides(relationshipType: RelationshipType, itemType: ItemType): Observable<boolean> {
210+
return this.getRelationshipLeftAndRightType(relationshipType).pipe(
211+
map(([leftType, rightType]: [ItemType, ItemType]) => {
212+
return leftType.id === itemType.id && rightType.id === itemType.id && relationshipType.leftwardType !== relationshipType.rightwardType;
213+
}),
214+
);
215+
}
216+
217+
protected getRelationshipLeftAndRightType(relationshipType: RelationshipType): Observable<[ItemType, ItemType]> {
218+
const leftType$: Observable<ItemType> = relationshipType.leftType.pipe(
219+
getFirstSucceededRemoteData(),
220+
getRemoteDataPayload(),
221+
);
222+
223+
const rightType$: Observable<ItemType> = relationshipType.rightType.pipe(
224+
getFirstSucceededRemoteData(),
225+
getRemoteDataPayload(),
226+
);
227+
228+
return observableCombineLatest([
229+
leftType$,
230+
rightType$,
231+
]);
232+
}
233+
172234

173235

174236
/**
@@ -185,6 +247,5 @@ export class EditItemRelationshipsService {
185247
*/
186248
getNotificationContent(key: string): string {
187249
return this.translateService.instant(this.notificationsPrefix + key + '.content');
188-
189250
}
190251
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<ng-container *ngIf="shouldDisplayBothRelationshipSides$ | async">
2+
<ds-edit-relationship-list
3+
[url]="url"
4+
[item]="item"
5+
[itemType]="itemType"
6+
[relationshipType]="relationshipType"
7+
[hasChanges]="hasChanges"
8+
[currentItemIsLeftItem$]="isLeftItem$"
9+
class="d-block mb-4"
10+
></ds-edit-relationship-list>
11+
<ds-edit-relationship-list
12+
[url]="url"
13+
[item]="item"
14+
[itemType]="itemType"
15+
[relationshipType]="relationshipType"
16+
[hasChanges]="hasChanges"
17+
[currentItemIsLeftItem$]="isRightItem$"
18+
></ds-edit-relationship-list>
19+
</ng-container>
20+
21+
<ng-container *ngIf="(shouldDisplayBothRelationshipSides$ | async) === false">
22+
<ds-edit-relationship-list
23+
[url]="url"
24+
[item]="item"
25+
[itemType]="itemType"
26+
[relationshipType]="relationshipType"
27+
[hasChanges]="hasChanges"
28+
[currentItemIsLeftItem$]="currentItemIsLeftItem$"
29+
></ds-edit-relationship-list>
30+
</ng-container>
31+

src/app/item-page/edit-item-page/item-relationships/edit-relationship-list-wrapper/edit-relationship-list-wrapper.component.scss

Whitespace-only changes.

0 commit comments

Comments
 (0)