mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
115284: Fix issue with same type relationships
This commit is contained in:

committed by
Alexandre Vryghem

parent
227d47154c
commit
33b59c739d
@@ -46,6 +46,9 @@ import { ResultsBackButtonModule } from '../../shared/results-back-button/result
|
|||||||
import {
|
import {
|
||||||
AccessControlFormModule
|
AccessControlFormModule
|
||||||
} from '../../shared/access-control-form-container/access-control-form.module';
|
} from '../../shared/access-control-form-container/access-control-form.module';
|
||||||
|
import {
|
||||||
|
EditRelationshipListWrapperComponent
|
||||||
|
} from './item-relationships/edit-relationship-list-wrapper/edit-relationship-list-wrapper.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module that contains all components related to the Edit Item page administrator functionality
|
* Module that contains all components related to the Edit Item page administrator functionality
|
||||||
@@ -94,6 +97,7 @@ import {
|
|||||||
ItemRegisterDoiComponent,
|
ItemRegisterDoiComponent,
|
||||||
ItemCurateComponent,
|
ItemCurateComponent,
|
||||||
ItemAccessControlComponent,
|
ItemAccessControlComponent,
|
||||||
|
EditRelationshipListWrapperComponent,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
BundleDataService,
|
BundleDataService,
|
||||||
|
@@ -265,6 +265,95 @@ describe('EditItemRelationshipsService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('isProvidedItemTypeLeftType', () => {
|
||||||
|
it('should return true if the provided item corresponds to the left type of the relationship', (done) => {
|
||||||
|
const relationshipType = Object.assign(new RelationshipType(), {
|
||||||
|
leftType: createSuccessfulRemoteDataObject$({id: 'leftType'}),
|
||||||
|
rightType: createSuccessfulRemoteDataObject$({id: 'rightType'}),
|
||||||
|
});
|
||||||
|
const itemType = Object.assign(new ItemType(), {id: 'leftType'} );
|
||||||
|
const item = Object.assign(new Item(), {uuid: 'item-uuid'});
|
||||||
|
|
||||||
|
const result = service.isProvidedItemTypeLeftType(relationshipType, itemType, item);
|
||||||
|
result.subscribe((resultValue) => {
|
||||||
|
expect(resultValue).toBeTrue();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if the provided item corresponds to the right type of the relationship', (done) => {
|
||||||
|
const relationshipType = Object.assign(new RelationshipType(), {
|
||||||
|
leftType: createSuccessfulRemoteDataObject$({id: 'leftType'}),
|
||||||
|
rightType: createSuccessfulRemoteDataObject$({id: 'rightType'}),
|
||||||
|
});
|
||||||
|
const itemType = Object.assign(new ItemType(), {id: 'rightType'} );
|
||||||
|
const item = Object.assign(new Item(), {uuid: 'item-uuid'});
|
||||||
|
|
||||||
|
const result = service.isProvidedItemTypeLeftType(relationshipType, itemType, item);
|
||||||
|
result.subscribe((resultValue) => {
|
||||||
|
expect(resultValue).toBeFalse();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return undefined if the provided item corresponds does not match any of the relationship types', (done) => {
|
||||||
|
const relationshipType = Object.assign(new RelationshipType(), {
|
||||||
|
leftType: createSuccessfulRemoteDataObject$({id: 'leftType'}),
|
||||||
|
rightType: createSuccessfulRemoteDataObject$({id: 'rightType'}),
|
||||||
|
});
|
||||||
|
const itemType = Object.assign(new ItemType(), {id: 'something-else'} );
|
||||||
|
const item = Object.assign(new Item(), {uuid: 'item-uuid'});
|
||||||
|
|
||||||
|
const result = service.isProvidedItemTypeLeftType(relationshipType, itemType, item);
|
||||||
|
result.subscribe((resultValue) => {
|
||||||
|
expect(resultValue).toBeUndefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('relationshipMatchesBothSameTypes', () => {
|
||||||
|
it('should return true if both left and right type of the relationship type are the same and match the provided itemtype', (done) => {
|
||||||
|
const relationshipType = Object.assign(new RelationshipType(), {
|
||||||
|
leftType: createSuccessfulRemoteDataObject$({id: 'sameType'}),
|
||||||
|
rightType: createSuccessfulRemoteDataObject$({id:'sameType'}),
|
||||||
|
});
|
||||||
|
const itemType = Object.assign(new ItemType(), {id: 'sameType'} );
|
||||||
|
|
||||||
|
const result = service.relationshipMatchesBothSameTypes(relationshipType, itemType);
|
||||||
|
result.subscribe((resultValue) => {
|
||||||
|
expect(resultValue).toBeTrue();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
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) => {
|
||||||
|
const relationshipType = Object.assign(new RelationshipType(), {
|
||||||
|
leftType: createSuccessfulRemoteDataObject$({id: 'sameType'}),
|
||||||
|
rightType: createSuccessfulRemoteDataObject$({id: 'sameType'}),
|
||||||
|
});
|
||||||
|
const itemType = Object.assign(new ItemType(), {id: 'something-else'} );
|
||||||
|
|
||||||
|
const result = service.relationshipMatchesBothSameTypes(relationshipType, itemType);
|
||||||
|
result.subscribe((resultValue) => {
|
||||||
|
expect(resultValue).toBeFalse();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should return false if both left and right type of the relationship type are different', (done) => {
|
||||||
|
const relationshipType = Object.assign(new RelationshipType(), {
|
||||||
|
leftType: createSuccessfulRemoteDataObject$({id: 'leftType'}),
|
||||||
|
rightType: createSuccessfulRemoteDataObject$({id: 'rightType'}),
|
||||||
|
});
|
||||||
|
const itemType = Object.assign(new ItemType(), {id: 'leftType'} );
|
||||||
|
|
||||||
|
const result = service.relationshipMatchesBothSameTypes(relationshipType, itemType);
|
||||||
|
result.subscribe((resultValue) => {
|
||||||
|
expect(resultValue).toBeFalse();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('displayNotifications', () => {
|
describe('displayNotifications', () => {
|
||||||
it('should show one success notification when multiple requests succeeded', () => {
|
it('should show one success notification when multiple requests succeeded', () => {
|
||||||
service.displayNotifications([
|
service.displayNotifications([
|
||||||
|
@@ -10,7 +10,7 @@ import {
|
|||||||
} from '../../../core/data/object-updates/object-updates.reducer';
|
} from '../../../core/data/object-updates/object-updates.reducer';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
|
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
|
||||||
import { EMPTY, Observable, BehaviorSubject, Subscription } from 'rxjs';
|
import { EMPTY, Observable, BehaviorSubject, Subscription, combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
|
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
|
||||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
@@ -20,6 +20,9 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
|||||||
import { RelationshipDataService } from '../../../core/data/relationship-data.service';
|
import { RelationshipDataService } from '../../../core/data/relationship-data.service';
|
||||||
import { EntityTypeDataService } from '../../../core/data/entity-type-data.service';
|
import { EntityTypeDataService } from '../../../core/data/entity-type-data.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { ItemType } from '../../../core/shared/item-relationships/item-type.model';
|
||||||
|
import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../../../core/shared/operators';
|
||||||
|
import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -169,6 +172,49 @@ export class EditItemRelationshipsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isProvidedItemTypeLeftType(relationshipType: RelationshipType, itemType: ItemType, item: Item): Observable<boolean> {
|
||||||
|
return this.getRelationshipLeftAndRightType(relationshipType).pipe(
|
||||||
|
map(([leftType, rightType]: [ItemType, ItemType]) => {
|
||||||
|
if (leftType.id === itemType.id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightType.id === itemType.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// should never happen...
|
||||||
|
console.warn(`The item ${item.uuid} is not on the right or the left side of relationship type ${relationshipType.uuid}`);
|
||||||
|
return undefined;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
relationshipMatchesBothSameTypes(relationshipType: RelationshipType, itemType: ItemType): Observable<boolean> {
|
||||||
|
return this.getRelationshipLeftAndRightType(relationshipType).pipe(
|
||||||
|
map(([leftType, rightType]: [ItemType, ItemType]) => {
|
||||||
|
return leftType.id === itemType.id && rightType.id === itemType.id;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getRelationshipLeftAndRightType(relationshipType: RelationshipType): Observable<[ItemType, ItemType]> {
|
||||||
|
const leftType$: Observable<ItemType> = relationshipType.leftType.pipe(
|
||||||
|
getFirstSucceededRemoteData(),
|
||||||
|
getRemoteDataPayload(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const rightType$: Observable<ItemType> = relationshipType.rightType.pipe(
|
||||||
|
getFirstSucceededRemoteData(),
|
||||||
|
getRemoteDataPayload(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return observableCombineLatest([
|
||||||
|
leftType$,
|
||||||
|
rightType$,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -185,6 +231,5 @@ export class EditItemRelationshipsService {
|
|||||||
*/
|
*/
|
||||||
getNotificationContent(key: string): string {
|
getNotificationContent(key: string): string {
|
||||||
return this.translateService.instant(this.notificationsPrefix + key + '.content');
|
return this.translateService.instant(this.notificationsPrefix + key + '.content');
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,30 @@
|
|||||||
|
<ng-container *ngIf="bothItemsMatchType$ | async">
|
||||||
|
<ds-edit-relationship-list
|
||||||
|
[url]="url"
|
||||||
|
[item]="item"
|
||||||
|
[itemType]="itemType"
|
||||||
|
[relationshipType]="relationshipType"
|
||||||
|
[hasChanges]="hasChanges"
|
||||||
|
[currentItemIsLeftItem$]="isLeftItem$"
|
||||||
|
></ds-edit-relationship-list>
|
||||||
|
<ds-edit-relationship-list
|
||||||
|
[url]="url"
|
||||||
|
[item]="item"
|
||||||
|
[itemType]="itemType"
|
||||||
|
[relationshipType]="relationshipType"
|
||||||
|
[hasChanges]="hasChanges"
|
||||||
|
[currentItemIsLeftItem$]="isRightItem$"
|
||||||
|
></ds-edit-relationship-list>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="!(bothItemsMatchType$ | async)">
|
||||||
|
<ds-edit-relationship-list
|
||||||
|
[url]="url"
|
||||||
|
[item]="item"
|
||||||
|
[itemType]="itemType"
|
||||||
|
[relationshipType]="relationshipType"
|
||||||
|
[hasChanges]="hasChanges"
|
||||||
|
[currentItemIsLeftItem$]="currentItemIsLeftItem$"
|
||||||
|
></ds-edit-relationship-list>
|
||||||
|
</ng-container>
|
||||||
|
|
@@ -0,0 +1,108 @@
|
|||||||
|
import { ChangeDetectorRef, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { EditRelationshipListWrapperComponent } from './edit-relationship-list-wrapper.component';
|
||||||
|
import { EditItemRelationshipsService } from '../edit-item-relationships.service';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
|
||||||
|
import { ItemType } from '../../../../core/shared/item-relationships/item-type.model';
|
||||||
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
|
||||||
|
describe('EditRelationshipListWrapperComponent', () => {
|
||||||
|
let editItemRelationshipsService: EditItemRelationshipsService;
|
||||||
|
let comp: EditRelationshipListWrapperComponent;
|
||||||
|
let fixture: ComponentFixture<EditRelationshipListWrapperComponent>;
|
||||||
|
|
||||||
|
const leftType = Object.assign(new ItemType(), {id: 'leftType', label: 'leftTypeString'});
|
||||||
|
const rightType = Object.assign(new ItemType(), {id: 'rightType', label: 'rightTypeString'});
|
||||||
|
|
||||||
|
const relationshipType = Object.assign(new RelationshipType(), {
|
||||||
|
id: '1',
|
||||||
|
leftMaxCardinality: null,
|
||||||
|
leftMinCardinality: 0,
|
||||||
|
leftType: createSuccessfulRemoteDataObject$(leftType),
|
||||||
|
leftwardType: 'isOrgUnitOfOrgUnit',
|
||||||
|
rightMaxCardinality: null,
|
||||||
|
rightMinCardinality: 0,
|
||||||
|
rightType: createSuccessfulRemoteDataObject$(rightType),
|
||||||
|
rightwardType: 'isOrgUnitOfOrgUnit',
|
||||||
|
uuid: 'relationshiptype-1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const item = Object.assign(new Item(), {uuid: 'item-uuid'});
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
|
||||||
|
editItemRelationshipsService = jasmine.createSpyObj('editItemRelationshipsService', {
|
||||||
|
isProvidedItemTypeLeftType: observableOf(true),
|
||||||
|
relationshipMatchesBothSameTypes: observableOf(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
// imports: [NoopAnimationsModule, SharedModule, TranslateModule.forRoot()],
|
||||||
|
declarations: [EditRelationshipListWrapperComponent],
|
||||||
|
providers: [
|
||||||
|
{provide: EditItemRelationshipsService, useValue: editItemRelationshipsService},
|
||||||
|
ChangeDetectorRef
|
||||||
|
], schemas: [
|
||||||
|
CUSTOM_ELEMENTS_SCHEMA
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(EditRelationshipListWrapperComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
comp.relationshipType = relationshipType;
|
||||||
|
comp.itemType = leftType;
|
||||||
|
comp.item = item;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onInit', () => {
|
||||||
|
it('should render the component', () => {
|
||||||
|
expect(comp).toBeTruthy();
|
||||||
|
});
|
||||||
|
it('should set currentItemIsLeftItem$ and bothItemsMatchType$ based on the provided relationshipType, itemType and item', () => {
|
||||||
|
expect(editItemRelationshipsService.isProvidedItemTypeLeftType).toHaveBeenCalledWith(relationshipType, leftType, item);
|
||||||
|
expect(editItemRelationshipsService.relationshipMatchesBothSameTypes).toHaveBeenCalledWith(relationshipType, leftType);
|
||||||
|
|
||||||
|
expect(comp.currentItemIsLeftItem$.getValue()).toEqual(true);
|
||||||
|
expect(comp.bothItemsMatchType$.getValue()).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the current item is left', () => {
|
||||||
|
it('should render one relationship list section', () => {
|
||||||
|
const relationshipLists = fixture.debugElement.queryAll(By.css('ds-edit-relationship-list'));
|
||||||
|
expect(relationshipLists.length).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the current item is right', () => {
|
||||||
|
it('should render one relationship list section', () => {
|
||||||
|
(editItemRelationshipsService.isProvidedItemTypeLeftType as jasmine.Spy).and.returnValue(observableOf(false));
|
||||||
|
comp.ngOnInit();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const relationshipLists = fixture.debugElement.queryAll(By.css('ds-edit-relationship-list'));
|
||||||
|
expect(relationshipLists.length).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the current item is both left and right', () => {
|
||||||
|
it('should render two relationship list sections', () => {
|
||||||
|
(editItemRelationshipsService.relationshipMatchesBothSameTypes as jasmine.Spy).and.returnValue(observableOf(true));
|
||||||
|
comp.ngOnInit();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const relationshipLists = fixture.debugElement.queryAll(By.css('ds-edit-relationship-list'));
|
||||||
|
expect(relationshipLists.length).toEqual(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,91 @@
|
|||||||
|
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
||||||
|
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||||
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
import { hasValue } from '../../../../shared/empty.util';
|
||||||
|
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||||
|
import { ItemType } from '../../../../core/shared/item-relationships/item-type.model';
|
||||||
|
import { EditItemRelationshipsService } from '../edit-item-relationships.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-edit-relationship-list-wrapper',
|
||||||
|
styleUrls: ['./edit-relationship-list-wrapper.component.scss'],
|
||||||
|
templateUrl: './edit-relationship-list-wrapper.component.html',
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* A component creating a list of editable relationships of a certain type
|
||||||
|
* The relationships are rendered as a list of related items
|
||||||
|
*/
|
||||||
|
export class EditRelationshipListWrapperComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The item to display related items for
|
||||||
|
*/
|
||||||
|
@Input() item: Item;
|
||||||
|
|
||||||
|
@Input() itemType: ItemType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URL to the current page
|
||||||
|
* Used to fetch updates for the current item from the store
|
||||||
|
*/
|
||||||
|
@Input() url: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The label of the relationship-type we're rendering a list for
|
||||||
|
*/
|
||||||
|
@Input() relationshipType: RelationshipType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If updated information has changed
|
||||||
|
*/
|
||||||
|
@Input() hasChanges!: Observable<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The event emmiter to submit the new information
|
||||||
|
*/
|
||||||
|
@Output() submitModal: EventEmitter<void> = new EventEmitter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observable that emits true if {@link itemType} is on the left-hand side of {@link relationshipType},
|
||||||
|
* false if it is on the right-hand side and undefined in the rare case that it is on neither side.
|
||||||
|
*/
|
||||||
|
currentItemIsLeftItem$: BehaviorSubject<boolean> = new BehaviorSubject(undefined);
|
||||||
|
|
||||||
|
|
||||||
|
isLeftItem$ = new BehaviorSubject(true);
|
||||||
|
|
||||||
|
isRightItem$ = new BehaviorSubject(false);
|
||||||
|
|
||||||
|
bothItemsMatchType$: BehaviorSubject<boolean> = new BehaviorSubject(undefined);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
private subs: Subscription[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected editItemRelationshipsService: EditItemRelationshipsService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.subs.push(this.editItemRelationshipsService.isProvidedItemTypeLeftType(this.relationshipType, this.itemType, this.item)
|
||||||
|
.subscribe((nextValue: boolean) => {
|
||||||
|
this.currentItemIsLeftItem$.next(nextValue);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.subs.push(this.editItemRelationshipsService.relationshipMatchesBothSameTypes(this.relationshipType, this.itemType)
|
||||||
|
.subscribe((nextValue: boolean) => {
|
||||||
|
this.bothItemsMatchType$.next(nextValue);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.subs
|
||||||
|
.filter((subscription) => hasValue(subscription))
|
||||||
|
.forEach((subscription) => subscription.unsubscribe());
|
||||||
|
}
|
||||||
|
}
|
@@ -2,7 +2,7 @@ import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
|||||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { BehaviorSubject, of as observableOf } from 'rxjs';
|
||||||
import { LinkService } from '../../../../core/cache/builders/link.service';
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||||
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
||||||
import { RelationshipDataService } from '../../../../core/data/relationship-data.service';
|
import { RelationshipDataService } from '../../../../core/data/relationship-data.service';
|
||||||
@@ -63,6 +63,7 @@ describe('EditRelationshipListComponent', () => {
|
|||||||
let relationships: Relationship[];
|
let relationships: Relationship[];
|
||||||
let relationshipType: RelationshipType;
|
let relationshipType: RelationshipType;
|
||||||
let paginationOptions: PaginationComponentOptions;
|
let paginationOptions: PaginationComponentOptions;
|
||||||
|
let currentItemIsLeftItem$ = new BehaviorSubject<boolean>(true);
|
||||||
|
|
||||||
const resetComponent = () => {
|
const resetComponent = () => {
|
||||||
fixture = TestBed.createComponent(EditRelationshipListComponent);
|
fixture = TestBed.createComponent(EditRelationshipListComponent);
|
||||||
@@ -73,6 +74,7 @@ describe('EditRelationshipListComponent', () => {
|
|||||||
comp.url = url;
|
comp.url = url;
|
||||||
comp.relationshipType = relationshipType;
|
comp.relationshipType = relationshipType;
|
||||||
comp.hasChanges = observableOf(false);
|
comp.hasChanges = observableOf(false);
|
||||||
|
comp.currentItemIsLeftItem$ = currentItemIsLeftItem$;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -293,6 +295,7 @@ describe('EditRelationshipListComponent', () => {
|
|||||||
leftwardType: 'isAuthorOfPublication',
|
leftwardType: 'isAuthorOfPublication',
|
||||||
rightwardType: 'isPublicationOfAuthor',
|
rightwardType: 'isPublicationOfAuthor',
|
||||||
});
|
});
|
||||||
|
currentItemIsLeftItem$ = new BehaviorSubject<boolean>(true);
|
||||||
relationshipService.getItemRelationshipsByLabel.calls.reset();
|
relationshipService.getItemRelationshipsByLabel.calls.reset();
|
||||||
resetComponent();
|
resetComponent();
|
||||||
});
|
});
|
||||||
@@ -317,6 +320,7 @@ describe('EditRelationshipListComponent', () => {
|
|||||||
leftwardType: 'isPublicationOfAuthor',
|
leftwardType: 'isPublicationOfAuthor',
|
||||||
rightwardType: 'isAuthorOfPublication',
|
rightwardType: 'isAuthorOfPublication',
|
||||||
});
|
});
|
||||||
|
currentItemIsLeftItem$ = new BehaviorSubject<boolean>(false);
|
||||||
relationshipService.getItemRelationshipsByLabel.calls.reset();
|
relationshipService.getItemRelationshipsByLabel.calls.reset();
|
||||||
resetComponent();
|
resetComponent();
|
||||||
});
|
});
|
||||||
|
@@ -113,7 +113,7 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy {
|
|||||||
* Observable that emits true if {@link itemType} is on the left-hand side of {@link relationshipType},
|
* Observable that emits true if {@link itemType} is on the left-hand side of {@link relationshipType},
|
||||||
* false if it is on the right-hand side and undefined in the rare case that it is on neither side.
|
* false if it is on the right-hand side and undefined in the rare case that it is on neither side.
|
||||||
*/
|
*/
|
||||||
private currentItemIsLeftItem$: BehaviorSubject<boolean> = new BehaviorSubject(undefined);
|
@Input() currentItemIsLeftItem$: BehaviorSubject<boolean> = new BehaviorSubject(undefined);
|
||||||
|
|
||||||
relatedEntityType$: Observable<ItemType>;
|
relatedEntityType$: Observable<ItemType>;
|
||||||
|
|
||||||
@@ -213,17 +213,14 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy {
|
|||||||
* Get the relevant label for this relationship type
|
* Get the relevant label for this relationship type
|
||||||
*/
|
*/
|
||||||
private getLabel(): Observable<string> {
|
private getLabel(): Observable<string> {
|
||||||
return observableCombineLatest([
|
return this.currentItemIsLeftItem$.pipe(
|
||||||
this.relationshipType.leftType,
|
map((currentItemIsLeftItem) => {
|
||||||
this.relationshipType.rightType,
|
if (currentItemIsLeftItem) {
|
||||||
].map((itemTypeRD) => itemTypeRD.pipe(
|
return this.relationshipType.leftwardType;
|
||||||
getFirstSucceededRemoteData(),
|
} else {
|
||||||
getRemoteDataPayload(),
|
return this.relationshipType.rightwardType;
|
||||||
))).pipe(
|
}
|
||||||
map((itemTypes: ItemType[]) => [
|
})
|
||||||
this.relationshipType.leftwardType,
|
|
||||||
this.relationshipType.rightwardType,
|
|
||||||
][itemTypes.findIndex((itemType) => itemType.id === this.itemType.id)]),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,6 +248,7 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy {
|
|||||||
modalComp.toAdd = [];
|
modalComp.toAdd = [];
|
||||||
modalComp.toRemove = [];
|
modalComp.toRemove = [];
|
||||||
modalComp.isPending = false;
|
modalComp.isPending = false;
|
||||||
|
modalComp.hiddenQuery = '-search.resourceid:' + this.item.uuid;
|
||||||
|
|
||||||
this.item.owningCollection.pipe(
|
this.item.owningCollection.pipe(
|
||||||
getFirstSucceededRemoteDataPayload()
|
getFirstSucceededRemoteDataPayload()
|
||||||
@@ -424,24 +422,6 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.relationshipMessageKey$ = this.getRelationshipMessageKey();
|
this.relationshipMessageKey$ = this.getRelationshipMessageKey();
|
||||||
|
|
||||||
this.subs.push(this.relationshipLeftAndRightType$.pipe(
|
|
||||||
map(([leftType, rightType]: [ItemType, ItemType]) => {
|
|
||||||
if (leftType.id === this.itemType.id) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rightType.id === this.itemType.id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// should never happen...
|
|
||||||
console.warn(`The item ${this.item.uuid} is not on the right or the left side of relationship type ${this.relationshipType.uuid}`);
|
|
||||||
return undefined;
|
|
||||||
})
|
|
||||||
).subscribe((nextValue: boolean) => {
|
|
||||||
this.currentItemIsLeftItem$.next(nextValue);
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
// initialize the pagination options
|
// initialize the pagination options
|
||||||
this.paginationConfig = new PaginationComponentOptions();
|
this.paginationConfig = new PaginationComponentOptions();
|
||||||
|
@@ -5,13 +5,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div *ngIf="relationshipTypes$ | async as relationshipTypes; else loading" class="mb-4">
|
<div *ngIf="relationshipTypes$ | async as relationshipTypes; else loading" class="mb-4">
|
||||||
<div *ngFor="let relationshipType of relationshipTypes; trackBy: trackById" class="mb-4">
|
<div *ngFor="let relationshipType of relationshipTypes; trackBy: trackById" class="mb-4">
|
||||||
<ds-edit-relationship-list
|
<ds-edit-relationship-list-wrapper
|
||||||
[url]="url"
|
[url]="url"
|
||||||
[item]="item"
|
[item]="item"
|
||||||
[itemType]="entityType"
|
[itemType]="entityType"
|
||||||
[relationshipType]="relationshipType"
|
[relationshipType]="relationshipType"
|
||||||
[hasChanges]="hasChanges$"
|
[hasChanges]="hasChanges$"
|
||||||
></ds-edit-relationship-list>
|
></ds-edit-relationship-list-wrapper>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-row bottom">
|
<div class="button-row bottom">
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
[repeatable]="repeatable"
|
[repeatable]="repeatable"
|
||||||
[context]="context"
|
[context]="context"
|
||||||
[query]="query"
|
[query]="query"
|
||||||
|
[hiddenQuery]="hiddenQuery"
|
||||||
[relationshipType]="relationshipType"
|
[relationshipType]="relationshipType"
|
||||||
[isLeft]="isLeft"
|
[isLeft]="isLeft"
|
||||||
[item]="item"
|
[item]="item"
|
||||||
|
@@ -97,6 +97,11 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
|
|||||||
|
|
||||||
query: string;
|
query: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A hidden query that will be used but not displayed in the url/searchbar
|
||||||
|
*/
|
||||||
|
hiddenQuery: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map of subscriptions within this component
|
* A map of subscriptions within this component
|
||||||
*/
|
*/
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
[configuration]="this.relationship.searchConfiguration"
|
[configuration]="this.relationship.searchConfiguration"
|
||||||
[context]="context"
|
[context]="context"
|
||||||
[fixedFilterQuery]="this.relationship.filter"
|
[fixedFilterQuery]="this.relationship.filter"
|
||||||
|
[hiddenQuery]="hiddenQuery"
|
||||||
[inPlaceSearch]="true"
|
[inPlaceSearch]="true"
|
||||||
[linkType]="linkTypes.ExternalLink"
|
[linkType]="linkTypes.ExternalLink"
|
||||||
[searchFormPlaceholder]="'submission.sections.describe.relationship-lookup.search-tab.search-form.placeholder'"
|
[searchFormPlaceholder]="'submission.sections.describe.relationship-lookup.search-tab.search-form.placeholder'"
|
||||||
|
@@ -93,6 +93,11 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest
|
|||||||
*/
|
*/
|
||||||
@Input() isEditRelationship: boolean;
|
@Input() isEditRelationship: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A hidden query that will be used but not displayed in the url/searchbar
|
||||||
|
*/
|
||||||
|
@Input() hiddenQuery: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send an event to deselect an object from the list
|
* Send an event to deselect an object from the list
|
||||||
*/
|
*/
|
||||||
|
@@ -18,7 +18,7 @@ import { DSpaceObject } from '../../../../../../core/shared/dspace-object.model'
|
|||||||
})
|
})
|
||||||
export class ThemedDynamicLookupRelationSearchTabComponent extends ThemedComponent<DsDynamicLookupRelationSearchTabComponent> {
|
export class ThemedDynamicLookupRelationSearchTabComponent extends ThemedComponent<DsDynamicLookupRelationSearchTabComponent> {
|
||||||
protected inAndOutputNames: (keyof DsDynamicLookupRelationSearchTabComponent & keyof this)[] = ['relationship', 'listId',
|
protected inAndOutputNames: (keyof DsDynamicLookupRelationSearchTabComponent & keyof this)[] = ['relationship', 'listId',
|
||||||
'query', 'repeatable', 'selection$', 'context', 'relationshipType', 'item', 'isLeft', 'toRemove', 'isEditRelationship',
|
'query', 'hiddenQuery', 'repeatable', 'selection$', 'context', 'relationshipType', 'item', 'isLeft', 'toRemove', 'isEditRelationship',
|
||||||
'deselectObject', 'selectObject', 'resultFound'];
|
'deselectObject', 'selectObject', 'resultFound'];
|
||||||
|
|
||||||
@Input() relationship: RelationshipOptions;
|
@Input() relationship: RelationshipOptions;
|
||||||
@@ -27,6 +27,8 @@ export class ThemedDynamicLookupRelationSearchTabComponent extends ThemedCompone
|
|||||||
|
|
||||||
@Input() query: string;
|
@Input() query: string;
|
||||||
|
|
||||||
|
@Input() hiddenQuery: string;
|
||||||
|
|
||||||
@Input() repeatable: boolean;
|
@Input() repeatable: boolean;
|
||||||
|
|
||||||
@Input() selection$: Observable<ListableObject[]>;
|
@Input() selection$: Observable<ListableObject[]>;
|
||||||
|
@@ -108,6 +108,7 @@ const searchServiceStub = jasmine.createSpyObj('SearchService', {
|
|||||||
}) as SearchService;
|
}) as SearchService;
|
||||||
const configurationParam = 'default';
|
const configurationParam = 'default';
|
||||||
const queryParam = 'test query';
|
const queryParam = 'test query';
|
||||||
|
const hiddenQuery = 'hidden query';
|
||||||
const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f';
|
const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f';
|
||||||
const fixedFilter = 'fixed filter';
|
const fixedFilter = 'fixed filter';
|
||||||
|
|
||||||
@@ -250,6 +251,7 @@ describe('SearchComponent', () => {
|
|||||||
comp = fixture.componentInstance; // SearchComponent test instance
|
comp = fixture.componentInstance; // SearchComponent test instance
|
||||||
comp.inPlaceSearch = false;
|
comp.inPlaceSearch = false;
|
||||||
comp.paginationId = paginationId;
|
comp.paginationId = paginationId;
|
||||||
|
comp.hiddenQuery = hiddenQuery;
|
||||||
|
|
||||||
spyOn((comp as any), 'getSearchOptions').and.returnValue(paginatedSearchOptions$.asObservable());
|
spyOn((comp as any), 'getSearchOptions').and.returnValue(paginatedSearchOptions$.asObservable());
|
||||||
|
|
||||||
|
@@ -75,6 +75,11 @@ export class SearchComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input() fixedFilterQuery: string;
|
@Input() fixedFilterQuery: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A hidden query that will be used but not displayed in the url/searchbar
|
||||||
|
*/
|
||||||
|
@Input() hiddenQuery: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If this is true, the request will only be sent if there's
|
* If this is true, the request will only be sent if there's
|
||||||
* no valid cached version. Defaults to true
|
* no valid cached version. Defaults to true
|
||||||
@@ -337,6 +342,7 @@ export class SearchComponent implements OnInit {
|
|||||||
if (combinedOptions.query === '') {
|
if (combinedOptions.query === '') {
|
||||||
combinedOptions.query = this.query;
|
combinedOptions.query = this.query;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newSearchOptions = new PaginatedSearchOptions(combinedOptions);
|
const newSearchOptions = new PaginatedSearchOptions(combinedOptions);
|
||||||
// check if search options are changed
|
// check if search options are changed
|
||||||
// if so retrieve new related results otherwise skip it
|
// if so retrieve new related results otherwise skip it
|
||||||
@@ -440,8 +446,18 @@ export class SearchComponent implements OnInit {
|
|||||||
if (this.configuration === 'supervision') {
|
if (this.configuration === 'supervision') {
|
||||||
followLinks.push(followLink<WorkspaceItem>('supervisionOrders', { isOptional: true }) as any);
|
followLinks.push(followLink<WorkspaceItem>('supervisionOrders', { isOptional: true }) as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const searchOptionsWithHidden = Object.assign (new PaginatedSearchOptions({}), searchOptions);
|
||||||
|
if (isNotEmpty(this.hiddenQuery)) {
|
||||||
|
if (isNotEmpty(searchOptionsWithHidden.query)) {
|
||||||
|
searchOptionsWithHidden.query = searchOptionsWithHidden.query + ' AND ' + this.hiddenQuery;
|
||||||
|
} else {
|
||||||
|
searchOptionsWithHidden.query = this.hiddenQuery;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.service.search(
|
this.service.search(
|
||||||
searchOptions,
|
searchOptionsWithHidden,
|
||||||
undefined,
|
undefined,
|
||||||
this.useCachedVersionIfAvailable,
|
this.useCachedVersionIfAvailable,
|
||||||
true,
|
true,
|
||||||
@@ -450,7 +466,7 @@ export class SearchComponent implements OnInit {
|
|||||||
.subscribe((results: RemoteData<SearchObjects<DSpaceObject>>) => {
|
.subscribe((results: RemoteData<SearchObjects<DSpaceObject>>) => {
|
||||||
if (results.hasSucceeded) {
|
if (results.hasSucceeded) {
|
||||||
if (this.trackStatistics) {
|
if (this.trackStatistics) {
|
||||||
this.service.trackSearch(searchOptions, results.payload);
|
this.service.trackSearch(searchOptionsWithHidden, results.payload);
|
||||||
}
|
}
|
||||||
if (results.payload?.page?.length > 0) {
|
if (results.payload?.page?.length > 0) {
|
||||||
this.resultFound.emit(results.payload);
|
this.resultFound.emit(results.payload);
|
||||||
|
@@ -19,7 +19,7 @@ import { ListableObject } from '../object-collection/shared/listable-object.mode
|
|||||||
templateUrl: '../theme-support/themed.component.html',
|
templateUrl: '../theme-support/themed.component.html',
|
||||||
})
|
})
|
||||||
export class ThemedSearchComponent extends ThemedComponent<SearchComponent> {
|
export class ThemedSearchComponent extends ThemedComponent<SearchComponent> {
|
||||||
protected inAndOutputNames: (keyof SearchComponent & keyof this)[] = ['configurationList', 'context', 'configuration', 'fixedFilterQuery', 'useCachedVersionIfAvailable', 'inPlaceSearch', 'linkType', 'paginationId', 'searchEnabled', 'sideBarWidth', 'searchFormPlaceholder', 'selectable', 'selectionConfig', 'showCsvExport', 'showSidebar', 'showThumbnails', 'showViewModes', 'useUniquePageId', 'viewModeList', 'showScopeSelector', 'resultFound', 'deselectObject', 'selectObject', 'trackStatistics', 'query'];
|
protected inAndOutputNames: (keyof SearchComponent & keyof this)[] = ['configurationList', 'context', 'configuration', 'fixedFilterQuery', 'hiddenQuery', 'useCachedVersionIfAvailable', 'inPlaceSearch', 'linkType', 'paginationId', 'searchEnabled', 'sideBarWidth', 'searchFormPlaceholder', 'selectable', 'selectionConfig', 'showCsvExport', 'showSidebar', 'showThumbnails', 'showViewModes', 'useUniquePageId', 'viewModeList', 'showScopeSelector', 'resultFound', 'deselectObject', 'selectObject', 'trackStatistics', 'query'];
|
||||||
|
|
||||||
@Input() configurationList: SearchConfigurationOption[];
|
@Input() configurationList: SearchConfigurationOption[];
|
||||||
|
|
||||||
@@ -29,6 +29,8 @@ export class ThemedSearchComponent extends ThemedComponent<SearchComponent> {
|
|||||||
|
|
||||||
@Input() fixedFilterQuery: string;
|
@Input() fixedFilterQuery: string;
|
||||||
|
|
||||||
|
@Input() hiddenQuery: string;
|
||||||
|
|
||||||
@Input() useCachedVersionIfAvailable: boolean;
|
@Input() useCachedVersionIfAvailable: boolean;
|
||||||
|
|
||||||
@Input() inPlaceSearch: boolean;
|
@Input() inPlaceSearch: boolean;
|
||||||
|
Reference in New Issue
Block a user