mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-08 18:44:14 +00:00
[CST-3088] fixed code
This commit is contained in:
@@ -251,7 +251,6 @@ export class AuthInterceptor implements HttpInterceptor {
|
||||
|
||||
// Pass on the new request instead of the original request.
|
||||
return next.handle(newReq).pipe(
|
||||
// tap((response) => console.log('next.handle: ', response)),
|
||||
map((response) => {
|
||||
// Intercept a Login/Logout response
|
||||
if (response instanceof HttpResponse && this.isSuccess(response) && this.isAuthRequest(response)) {
|
||||
|
@@ -144,6 +144,7 @@ import { Vocabulary } from './submission/vocabularies/models/vocabulary.model';
|
||||
import { VocabularyEntriesResponseParsingService } from './submission/vocabularies/vocabulary-entries-response-parsing.service';
|
||||
import { VocabularyEntryDetail } from './submission/vocabularies/models/vocabulary-entry-detail.model';
|
||||
import { VocabularyService } from './submission/vocabularies/vocabulary.service';
|
||||
import { VocabularyTreeviewService } from '../shared/vocabulary-treeview/vocabulary-treeview.service';
|
||||
|
||||
/**
|
||||
* When not in production, endpoint responses can be mocked for testing purposes
|
||||
@@ -268,7 +269,8 @@ const PROVIDERS = [
|
||||
FilteredDiscoveryPageResponseParsingService,
|
||||
{ provide: NativeWindowService, useFactory: NativeWindowFactory },
|
||||
VocabularyService,
|
||||
VocabularyEntriesResponseParsingService
|
||||
VocabularyEntriesResponseParsingService,
|
||||
VocabularyTreeviewService
|
||||
];
|
||||
|
||||
/**
|
||||
|
@@ -115,12 +115,6 @@ export abstract class DataService<T extends CacheableObject> {
|
||||
|
||||
result$ = this.getSearchEndpoint(searchMethod);
|
||||
|
||||
if (hasValue(options.searchParams)) {
|
||||
options.searchParams.forEach((param: RequestParam) => {
|
||||
args.push(`${param.fieldName}=${param.fieldValue}`);
|
||||
})
|
||||
}
|
||||
|
||||
return result$.pipe(map((result: string) => this.buildHrefFromFindOptions(result, options, args, ...linksToFollow)));
|
||||
}
|
||||
|
||||
@@ -351,7 +345,7 @@ export abstract class DataService<T extends CacheableObject> {
|
||||
return hrefObs.pipe(
|
||||
find((href: string) => hasValue(href)),
|
||||
tap((href: string) => {
|
||||
this.requestService.removeByHrefSubstring(href);
|
||||
this.requestService.removeByHrefSubstring(searchMethod);
|
||||
const request = new FindListRequest(this.requestService.generateRequestId(), href, options);
|
||||
if (hasValue(this.responseMsToLive)) {
|
||||
request.responseMsToLive = this.responseMsToLive;
|
||||
|
@@ -20,6 +20,7 @@ import { VocabularyService } from './vocabulary.service';
|
||||
import { getMockRequestService } from '../../../shared/mocks/request.service.mock';
|
||||
import { getMockRemoteDataBuildService } from '../../../shared/mocks/remote-data-build.service.mock';
|
||||
import { VocabularyOptions } from './models/vocabulary-options.model';
|
||||
import { VocabularyFindOptions } from './models/vocabulary-find-options.model';
|
||||
|
||||
describe('VocabularyService', () => {
|
||||
let scheduler: TestScheduler;
|
||||
@@ -208,9 +209,9 @@ describe('VocabularyService', () => {
|
||||
const endpointURL = `https://rest.api/rest/api/submission/vocabularies`;
|
||||
const requestURL = `https://rest.api/rest/api/submission/vocabularies/${vocabulary.id}`;
|
||||
const entryDetailEndpointURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails`;
|
||||
const entryDetailRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/srsc:SCB110/${hierarchicalVocabulary.id}:testValue`;
|
||||
const entryDetailParentRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/srsc:SCB110/${hierarchicalVocabulary.id}:testValue/parent`;
|
||||
const entryDetailChildrenRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/srsc:SCB110/${hierarchicalVocabulary.id}:testValue/children`;
|
||||
const entryDetailRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue`;
|
||||
const entryDetailParentRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue/parent`;
|
||||
const entryDetailChildrenRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue/children`;
|
||||
const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a';
|
||||
const vocabularyId = 'types';
|
||||
const metadata = 'dc.type';
|
||||
@@ -490,7 +491,7 @@ describe('VocabularyService', () => {
|
||||
scheduler = getTestScheduler();
|
||||
|
||||
halService = jasmine.createSpyObj('halService', {
|
||||
getEndpoint: cold('a', { a: endpointURL })
|
||||
getEndpoint: cold('a', { a: entryDetailEndpointURL })
|
||||
});
|
||||
|
||||
responseCacheEntry = new RequestEntry();
|
||||
@@ -517,9 +518,10 @@ describe('VocabularyService', () => {
|
||||
spyOn((service as any).vocabularyEntryDetailDataService, 'findById').and.callThrough();
|
||||
spyOn((service as any).vocabularyEntryDetailDataService, 'findAll').and.callThrough();
|
||||
spyOn((service as any).vocabularyEntryDetailDataService, 'findByHref').and.callThrough();
|
||||
spyOn((service as any).vocabularyEntryDetailDataService, 'findAllByHref').and.callThrough();
|
||||
spyOn((service as any).vocabularyEntryDetailDataService, 'searchBy').and.callThrough();
|
||||
spyOn((service as any).vocabularyEntryDetailDataService, 'getSearchByHref').and.returnValue(observableOf(searchRequestURL));
|
||||
spyOn((service as any).vocabularyEntryDetailDataService, 'getFindAllHref').and.returnValue(observableOf(entriesRequestURL));
|
||||
spyOn((service as any).vocabularyEntryDetailDataService, 'getFindAllHref').and.returnValue(observableOf(entryDetailChildrenRequestURL));
|
||||
spyOn((service as any).vocabularyEntryDetailDataService, 'getBrowseEndpoint').and.returnValue(observableOf(entryDetailEndpointURL));
|
||||
});
|
||||
|
||||
@@ -528,7 +530,7 @@ describe('VocabularyService', () => {
|
||||
});
|
||||
|
||||
describe('findEntryDetailByHref', () => {
|
||||
it('should proxy the call to vocabularyDataService.findVocabularyByHref', () => {
|
||||
it('should proxy the call to vocabularyDataService.findEntryDetailByHref', () => {
|
||||
scheduler.schedule(() => service.findEntryDetailByHref(entryDetailRequestURL));
|
||||
scheduler.flush();
|
||||
|
||||
@@ -563,7 +565,7 @@ describe('VocabularyService', () => {
|
||||
|
||||
describe('getEntryDetailParent', () => {
|
||||
it('should proxy the call to vocabularyDataService.getEntryDetailParent', () => {
|
||||
scheduler.schedule(() => service.getEntryDetailParent('testValue', hierarchicalVocabulary.id));
|
||||
scheduler.schedule(() => service.getEntryDetailParent('testValue', hierarchicalVocabulary.id).subscribe());
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).vocabularyEntryDetailDataService.findByHref).toHaveBeenCalledWith(entryDetailParentRequestURL);
|
||||
@@ -580,10 +582,20 @@ describe('VocabularyService', () => {
|
||||
|
||||
describe('getEntryDetailChildren', () => {
|
||||
it('should proxy the call to vocabularyDataService.getEntryDetailChildren', () => {
|
||||
scheduler.schedule(() => service.getEntryDetailChildren('testValue', hierarchicalVocabulary.id, new PageInfo()));
|
||||
const options: VocabularyFindOptions = new VocabularyFindOptions(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
pageInfo.elementsPerPage,
|
||||
pageInfo.currentPage
|
||||
);
|
||||
scheduler.schedule(() => service.getEntryDetailChildren('testValue', hierarchicalVocabulary.id, pageInfo).subscribe());
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).vocabularyEntryDetailDataService.findAllByHref).toHaveBeenCalledWith(entryDetailChildrenRequestURL);
|
||||
expect((service as any).vocabularyEntryDetailDataService.findAllByHref).toHaveBeenCalledWith(entryDetailChildrenRequestURL, options);
|
||||
});
|
||||
|
||||
it('should return a RemoteData<PaginatedList<ResourcePolicy>> for the object with the given URL', () => {
|
||||
@@ -595,9 +607,19 @@ describe('VocabularyService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('searchByTop', () => {
|
||||
it('should proxy the call to vocabularyEntryDetailDataService.searchBy', () => {
|
||||
const options = new FindListOptions();
|
||||
const options: VocabularyFindOptions = new VocabularyFindOptions(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
pageInfo.elementsPerPage,
|
||||
pageInfo.currentPage
|
||||
);
|
||||
options.searchParams = [new RequestParam('vocabulary', 'srsc')];
|
||||
scheduler.schedule(() => service.searchTopEntries('srsc', pageInfo));
|
||||
scheduler.flush();
|
||||
@@ -615,5 +637,14 @@ describe('VocabularyService', () => {
|
||||
|
||||
});
|
||||
|
||||
describe('clearSearchTopRequests', () => {
|
||||
it('should remove requests on the data service\'s endpoint', (done) => {
|
||||
service.clearSearchTopRequests();
|
||||
|
||||
expect(requestService.removeByHrefSubstring).toHaveBeenCalledWith(`search/${(service as any).searchTopMethod}`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@@ -353,10 +353,26 @@ export class VocabularyService {
|
||||
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
||||
*/
|
||||
searchTopEntries(name: string, pageInfo: PageInfo, ...linksToFollow: Array<FollowLinkConfig<VocabularyEntryDetail>>): Observable<RemoteData<PaginatedList<VocabularyEntryDetail>>> {
|
||||
const options = new FindListOptions();
|
||||
const options: VocabularyFindOptions = new VocabularyFindOptions(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
pageInfo.elementsPerPage,
|
||||
pageInfo.currentPage
|
||||
);
|
||||
options.searchParams = [new RequestParam('vocabulary', name)];
|
||||
return this.vocabularyEntryDetailDataService.searchBy(this.searchTopMethod, options, ...linksToFollow)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all search Top Requests
|
||||
*/
|
||||
clearSearchTopRequests(): void {
|
||||
this.requestService.removeByHrefSubstring(`search/${this.searchTopMethod}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -59,12 +59,13 @@ export abstract class DsDynamicVocabularyComponent extends DynamicFormControlCom
|
||||
}
|
||||
initValue$ = initEntry$.pipe(map((initEntry: VocabularyEntry) => {
|
||||
if (isNotEmpty(initEntry)) {
|
||||
return new FormFieldMetadataValueObject(
|
||||
initEntry.value,
|
||||
null,
|
||||
initEntry.authority,
|
||||
initEntry.display
|
||||
);
|
||||
// Integrate FormFieldMetadataValueObject with retrieved information
|
||||
return Object.assign(new FormFieldMetadataValueObject(), this.model.value, {
|
||||
value: initEntry.value,
|
||||
authority: initEntry.authority,
|
||||
display: initEntry.display,
|
||||
otherInformation: initEntry.otherInformation
|
||||
});
|
||||
} else {
|
||||
return this.model.value as any;
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ import { createTestComponent } from '../../../../../testing/utils.test';
|
||||
import { AuthorityConfidenceStateDirective } from '../../../../../authority-confidence/authority-confidence-state.directive';
|
||||
import { ObjNgFor } from '../../../../../utils/object-ngfor.pipe';
|
||||
import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../../../remote-data.utils';
|
||||
|
||||
export let TYPEAHEAD_TEST_GROUP;
|
||||
|
||||
@@ -34,7 +35,7 @@ function init() {
|
||||
vocabularyOptions: {
|
||||
closed: false,
|
||||
metadata: 'typeahead',
|
||||
name: 'EVENTAuthority',
|
||||
name: 'vocabulary',
|
||||
scope: 'c1c16450-d56f-41bc-bb81-27f1d1eb5c23'
|
||||
} as VocabularyOptions,
|
||||
disabled: false,
|
||||
@@ -50,13 +51,47 @@ function init() {
|
||||
};
|
||||
}
|
||||
|
||||
describe('DsDynamicTypeaheadComponent test suite', () => {
|
||||
fdescribe('DsDynamicTypeaheadComponent test suite', () => {
|
||||
|
||||
let testComp: TestComponent;
|
||||
let typeaheadComp: DsDynamicTypeaheadComponent;
|
||||
let testFixture: ComponentFixture<TestComponent>;
|
||||
let typeaheadFixture: ComponentFixture<DsDynamicTypeaheadComponent>;
|
||||
let service: any;
|
||||
let html;
|
||||
let vocabulary = {
|
||||
id: 'vocabulary',
|
||||
name: 'vocabulary',
|
||||
scrollable: true,
|
||||
hierarchical: false,
|
||||
preloadLevel: 0,
|
||||
type: 'vocabulary',
|
||||
_links: {
|
||||
self: {
|
||||
url: 'self'
|
||||
},
|
||||
entries: {
|
||||
url: 'entries'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let hierarchicalVocabulary = {
|
||||
id: 'hierarchicalVocabulary',
|
||||
name: 'hierarchicalVocabulary',
|
||||
scrollable: true,
|
||||
hierarchical: true,
|
||||
preloadLevel: 2,
|
||||
type: 'vocabulary',
|
||||
_links: {
|
||||
self: {
|
||||
url: 'self'
|
||||
},
|
||||
entries: {
|
||||
url: 'entries'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// async beforeEach
|
||||
beforeEach(async(() => {
|
||||
@@ -113,7 +148,7 @@ describe('DsDynamicTypeaheadComponent test suite', () => {
|
||||
}));
|
||||
});
|
||||
|
||||
describe('', () => {
|
||||
describe('not hiearchical', () => {
|
||||
describe('when init model value is empty', () => {
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -121,6 +156,8 @@ describe('DsDynamicTypeaheadComponent test suite', () => {
|
||||
typeaheadComp = typeaheadFixture.componentInstance; // FormComponent test instance
|
||||
typeaheadComp.group = TYPEAHEAD_TEST_GROUP;
|
||||
typeaheadComp.model = new DynamicTypeaheadModel(TYPEAHEAD_TEST_MODEL_CONFIG);
|
||||
service = (typeaheadComp as any).vocabularyService;
|
||||
spyOn(service, 'findVocabularyById').and.returnValue(createSuccessfulRemoteDataObject$(vocabulary));
|
||||
typeaheadFixture.detectChanges();
|
||||
});
|
||||
|
||||
|
@@ -119,7 +119,8 @@ export class DsDynamicTypeaheadComponent extends DsDynamicVocabularyComponent im
|
||||
}
|
||||
|
||||
this.vocabulary$ = this.vocabularyService.findVocabularyById(this.model.vocabularyOptions.name).pipe(
|
||||
getFirstSucceededRemoteDataPayload()
|
||||
getFirstSucceededRemoteDataPayload(),
|
||||
distinctUntilChanged()
|
||||
);
|
||||
|
||||
this.group.get(this.model.id).valueChanges.pipe(
|
||||
|
@@ -11,6 +11,7 @@ export interface OtherInformation {
|
||||
* A class representing a specific input-form field's value
|
||||
*/
|
||||
export class FormFieldMetadataValueObject implements MetadataValueInterface {
|
||||
metadata?: string;
|
||||
value: any;
|
||||
display: string;
|
||||
language: any;
|
||||
@@ -43,6 +44,9 @@ export class FormFieldMetadataValueObject implements MetadataValueInterface {
|
||||
}
|
||||
|
||||
this.place = place;
|
||||
if (isNotEmpty(metadata)) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
this.otherInformation = otherInformation;
|
||||
}
|
||||
|
@@ -182,8 +182,8 @@ export abstract class FieldParser {
|
||||
fieldIds.forEach((id) => {
|
||||
if (this.initFormValues.hasOwnProperty(id)) {
|
||||
const valueObj: FormFieldMetadataValueObject = Object.assign(new FormFieldMetadataValueObject(), this.initFormValues[id][innerIndex]);
|
||||
// valueObj.metadata = id;
|
||||
// valueObj.value = this.initFormValues[id][innerIndex];
|
||||
// Set metadata name, used for Qualdrop fields
|
||||
valueObj.metadata = id;
|
||||
values.push(valueObj);
|
||||
}
|
||||
});
|
||||
|
@@ -6,6 +6,7 @@ import { PaginatedList } from '../../core/data/paginated-list';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model';
|
||||
import { Vocabulary } from '../../core/submission/vocabularies/models/vocabulary.model';
|
||||
|
||||
export class VocabularyServiceStub {
|
||||
|
||||
@@ -37,4 +38,8 @@ export class VocabularyServiceStub {
|
||||
getVocabularyEntryByID(id: string, vocabularyOptions: VocabularyOptions): Observable<VocabularyEntry> {
|
||||
return observableOf(Object.assign(new VocabularyEntry(), { authority: 1, display: 'one', value: 1 }));
|
||||
}
|
||||
|
||||
findVocabularyById(id: string): Observable<RemoteData<Vocabulary>> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{'treeview.header' | translate}}</h4>
|
||||
<h4 class="modal-title">{{'vocabulary-treeview.header' | translate}}</h4>
|
||||
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
@@ -13,19 +13,19 @@
|
||||
<input type="text" class="form-control" [(ngModel)]="searchText" (keyup.enter)="search()">
|
||||
<div class="input-group-append" id="button-addon4">
|
||||
<button class="btn btn-outline-primary" type="button" (click)="search()" [disabled]="!isSearchEnabled()">
|
||||
{{'treeview.search.form.search' | translate}}
|
||||
{{'vocabulary-treeview.search.form.search' | translate}}
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary" type="button" (click)="reset()">
|
||||
{{'treeview.search.form.reset' | translate}}
|
||||
{{'vocabulary-treeview.search.form.reset' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="treeview-container">
|
||||
<ds-loading *ngIf="searching | async" [showMessage]="false"></ds-loading>
|
||||
<h4 *ngIf="!(searching | async) && dataSource.data.length === 0" class="text-center text-muted mt-4" >
|
||||
<span>{{'treeview.search.no-result' | translate}}</span>
|
||||
<ds-loading *ngIf="loading | async" [showMessage]="false"></ds-loading>
|
||||
<h4 *ngIf="!(loading | async) && dataSource.data.length === 0" class="text-center text-muted mt-4" >
|
||||
<span>{{'vocabulary-treeview.search.no-result' | translate}}</span>
|
||||
</h4>
|
||||
<cdk-tree [dataSource]="dataSource" [treeControl]="treeControl">
|
||||
<!-- Leaf node -->
|
||||
@@ -62,13 +62,13 @@
|
||||
|
||||
<cdk-tree-node *cdkTreeNodeDef="let node; when: isLoadMore" cdkTreeNodePadding>
|
||||
<button class="btn btn-outline-secondary btn-sm" (click)="loadMore(node.loadMoreParentItem)">
|
||||
{{'treeview.load-more' | translate}}...
|
||||
{{'vocabulary-treeview.load-more' | translate}}...
|
||||
</button>
|
||||
</cdk-tree-node>
|
||||
|
||||
<cdk-tree-node *cdkTreeNodeDef="let node; when: isLoadMoreRoot">
|
||||
<button class="btn btn-outline-secondary btn-sm" (click)="loadMoreRoot(node)">
|
||||
{{'treeview.load-more' | translate}}...
|
||||
{{'vocabulary-treeview.load-more' | translate}}...
|
||||
</button>
|
||||
</cdk-tree-node>
|
||||
</cdk-tree>
|
||||
|
@@ -16,6 +16,7 @@ import { TreeviewFlatNode } from './vocabulary-treeview-node.model';
|
||||
import { FormFieldMetadataValueObject } from '../form/builder/models/form-field-metadata-value.model';
|
||||
import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model';
|
||||
import { PageInfo } from '../../core/shared/page-info.model';
|
||||
import { VocabularyEntry } from '../../core/submission/vocabularies/models/vocabulary-entry.model';
|
||||
|
||||
describe('VocabularyTreeviewComponent test suite', () => {
|
||||
|
||||
@@ -37,7 +38,7 @@ describe('VocabularyTreeviewComponent test suite', () => {
|
||||
getData: jasmine.createSpy('getData'),
|
||||
loadMore: jasmine.createSpy('loadMore'),
|
||||
loadMoreRoot: jasmine.createSpy('loadMoreRoot'),
|
||||
isSearching: jasmine.createSpy('isSearching'),
|
||||
isLoading: jasmine.createSpy('isLoading'),
|
||||
searchByQuery: jasmine.createSpy('searchByQuery'),
|
||||
restoreNodes: jasmine.createSpy('restoreNodes'),
|
||||
cleanTree: jasmine.createSpy('cleanTree'),
|
||||
@@ -100,7 +101,7 @@ describe('VocabularyTreeviewComponent test suite', () => {
|
||||
comp = fixture.componentInstance;
|
||||
compAsAny = comp;
|
||||
vocabularyTreeviewServiceStub.getData.and.returnValue(observableOf([]));
|
||||
vocabularyTreeviewServiceStub.isSearching.and.returnValue(observableOf(false));
|
||||
vocabularyTreeviewServiceStub.isLoading.and.returnValue(observableOf(false));
|
||||
comp.vocabularyOptions = vocabularyOptions;
|
||||
comp.selectedItem = null;
|
||||
});
|
||||
@@ -118,19 +119,27 @@ describe('VocabularyTreeviewComponent test suite', () => {
|
||||
});
|
||||
|
||||
it('should should init component properly with init value as FormFieldMetadataValueObject', () => {
|
||||
comp.selectedItem = new FormFieldMetadataValueObject('test', null, 'auth001');
|
||||
const currentValue = new FormFieldMetadataValueObject();
|
||||
currentValue.value = 'testValue';
|
||||
currentValue.otherInformation = {
|
||||
id: 'entryID'
|
||||
};
|
||||
comp.selectedItem = currentValue;
|
||||
fixture.detectChanges();
|
||||
expect(comp.dataSource.data).toEqual([]);
|
||||
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), 'auth001');
|
||||
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), 'entryID');
|
||||
});
|
||||
|
||||
it('should should init component properly with init value as AuthorityEntry', () => {
|
||||
const authority = new VocabularyEntryDetail();
|
||||
authority.id = 'auth001';
|
||||
comp.selectedItem = authority;
|
||||
it('should should init component properly with init value as VocabularyEntry', () => {
|
||||
const currentValue = new VocabularyEntry();
|
||||
currentValue.value = 'testValue';
|
||||
currentValue.otherInformation = {
|
||||
id: 'entryID'
|
||||
};
|
||||
comp.selectedItem = currentValue;
|
||||
fixture.detectChanges();
|
||||
expect(comp.dataSource.data).toEqual([]);
|
||||
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), 'auth001');
|
||||
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), 'entryID');
|
||||
});
|
||||
|
||||
it('should call loadMore function', () => {
|
||||
|
@@ -16,6 +16,7 @@ import { VocabularyTreeviewService } from './vocabulary-treeview.service';
|
||||
import { LOAD_MORE, LOAD_MORE_ROOT, TreeviewFlatNode, TreeviewNode } from './vocabulary-treeview-node.model';
|
||||
import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model';
|
||||
import { PageInfo } from '../../core/shared/page-info.model';
|
||||
import { VocabularyEntry } from '../../core/submission/vocabularies/models/vocabulary-entry.model';
|
||||
|
||||
/**
|
||||
* Component that show a hierarchical vocabulary in a tree view
|
||||
@@ -78,9 +79,9 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit {
|
||||
searchText: string;
|
||||
|
||||
/**
|
||||
* A boolean representing if a search operation is pending
|
||||
* A boolean representing if tree is loading
|
||||
*/
|
||||
searching: Observable<boolean>;
|
||||
loading: Observable<boolean>;
|
||||
|
||||
/**
|
||||
* An event fired when a vocabulary entry is selected.
|
||||
@@ -198,7 +199,7 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit {
|
||||
})
|
||||
);
|
||||
|
||||
const descriptionLabel = 'tree.description.' + this.vocabularyOptions.name;
|
||||
const descriptionLabel = 'vocabulary-treeview.tree.description.' + this.vocabularyOptions.name;
|
||||
this.description = this.translate.get(descriptionLabel).pipe(
|
||||
filter((msg) => msg !== descriptionLabel),
|
||||
startWith('')
|
||||
@@ -207,13 +208,13 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit {
|
||||
// set isAuthenticated
|
||||
this.isAuthenticated = this.store.pipe(select(isAuthenticated));
|
||||
|
||||
this.searching = this.vocabularyTreeviewService.isSearching();
|
||||
this.loading = this.vocabularyTreeviewService.isLoading();
|
||||
|
||||
this.isAuthenticated.pipe(
|
||||
find((isAuth) => isAuth)
|
||||
).subscribe(() => {
|
||||
const valueId: string = (this.selectedItem) ? (this.selectedItem.authority || this.selectedItem.id) : null;
|
||||
this.vocabularyTreeviewService.initialize(this.vocabularyOptions, new PageInfo(), valueId);
|
||||
const entryId: string = (this.selectedItem) ? this.getEntryId(this.selectedItem) : null;
|
||||
this.vocabularyTreeviewService.initialize(this.vocabularyOptions, new PageInfo(), entryId);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -292,4 +293,11 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit {
|
||||
.filter((sub) => hasValue(sub))
|
||||
.forEach((sub) => sub.unsubscribe());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an id for a given {@link VocabularyEntry}
|
||||
*/
|
||||
private getEntryId(entry: VocabularyEntry): string {
|
||||
return entry.authority || entry.otherInformation.id || undefined;
|
||||
}
|
||||
}
|
||||
|
@@ -50,39 +50,47 @@ describe('VocabularyTreeviewService test suite', () => {
|
||||
let nodeMapWithChildren: Map<string, TreeviewNode>;
|
||||
let searchNodeMap: Map<string, TreeviewNode>;
|
||||
let vocabularyOptions;
|
||||
let pageInfo: PageInfo;
|
||||
|
||||
const vocabularyServiceStub = jasmine.createSpyObj('VocabularyService', {
|
||||
getVocabularyEntriesByValue: jasmine.createSpy('getVocabularyEntriesByValue'),
|
||||
getEntryDetailParent: jasmine.createSpy('getEntryDetailParent'),
|
||||
findEntryDetailByValue: jasmine.createSpy('findEntryDetailByValue'),
|
||||
searchTopEntries: jasmine.createSpy('searchTopEntries'),
|
||||
getEntryDetailChildren: jasmine.createSpy('getEntryDetailChildren')
|
||||
getEntryDetailChildren: jasmine.createSpy('getEntryDetailChildren'),
|
||||
clearSearchTopRequests: jasmine.createSpy('clearSearchTopRequests')
|
||||
});
|
||||
|
||||
function init() {
|
||||
|
||||
pageInfo = Object.assign(new PageInfo(), {
|
||||
elementsPerPage: 1,
|
||||
totalElements: 3,
|
||||
totalPages: 1,
|
||||
currentPage: 1
|
||||
});
|
||||
loadMoreNode = new TreeviewNode(LOAD_MORE_NODE, false, new PageInfo(), item);
|
||||
loadMoreRootNode = new TreeviewNode(LOAD_MORE_ROOT_NODE, false, new PageInfo(), null);
|
||||
loadMoreRootFlatNode = new TreeviewFlatNode(LOAD_MORE_ROOT_NODE, 1, false, new PageInfo(), null);
|
||||
item = new VocabularyEntryDetail();
|
||||
item.id = item.value = item.display = 'root1';
|
||||
item.otherInformation = { children: 'root1-child1::root1-child2', id: 'root1' };
|
||||
itemNode = new TreeviewNode(item, true);
|
||||
item.otherInformation = { hasChildren: 'true', id: 'root1' };
|
||||
itemNode = new TreeviewNode(item, true, pageInfo);
|
||||
searchItemNode = new TreeviewNode(item, true, new PageInfo(), null, true);
|
||||
|
||||
item2 = new VocabularyEntryDetail();
|
||||
item2.id = item2.value = item2.display = 'root2';
|
||||
item2.otherInformation = { id: 'root2' };
|
||||
itemNode2 = new TreeviewNode(item2);
|
||||
itemNode2 = new TreeviewNode(item2, false, pageInfo);
|
||||
|
||||
item3 = new VocabularyEntryDetail();
|
||||
item3.id = item3.value = item3.display = 'root3';
|
||||
item3.otherInformation = { id: 'root3' };
|
||||
itemNode3 = new TreeviewNode(item3);
|
||||
itemNode3 = new TreeviewNode(item3, false, pageInfo);
|
||||
|
||||
child = new VocabularyEntryDetail();
|
||||
child.id = child.value = child.display = 'root1-child1';
|
||||
child.otherInformation = { parent: 'root1', children: 'root1-child1-child1', id: 'root1-child1' };
|
||||
child.otherInformation = { parent: 'root1', hasChildren: 'true', id: 'root1-child1' };
|
||||
childNode = new TreeviewNode(child);
|
||||
searchChildNode = new TreeviewNode(child, true, new PageInfo(), item, true);
|
||||
|
||||
@@ -167,12 +175,6 @@ describe('VocabularyTreeviewService test suite', () => {
|
||||
|
||||
describe('initialize', () => {
|
||||
it('should set vocabularyName and call retrieveTopNodes method', () => {
|
||||
const pageInfo = Object.assign(new PageInfo(), {
|
||||
elementsPerPage: 1,
|
||||
totalElements: 3,
|
||||
totalPages: 1,
|
||||
currentPage: 1
|
||||
});
|
||||
serviceAsAny.vocabularyService.searchTopEntries.and.returnValue(hot('-a', {
|
||||
a: createSuccessfulRemoteDataObject(new PaginatedList(pageInfo, [item, item2, item3]))
|
||||
}));
|
||||
@@ -181,16 +183,12 @@ describe('VocabularyTreeviewService test suite', () => {
|
||||
scheduler.flush();
|
||||
|
||||
expect(serviceAsAny.vocabularyName).toEqual(vocabularyOptions.name);
|
||||
expect(serviceAsAny.pageInfo).toEqual(pageInfo);
|
||||
console.log(serviceAsAny.dataChange.value[0].pageInfo, itemNode.pageInfo);
|
||||
expect(serviceAsAny.dataChange.value).toEqual([itemNode, itemNode2, itemNode3]);
|
||||
});
|
||||
|
||||
it('should set initValueHierarchy', () => {
|
||||
const pageInfo = Object.assign(new PageInfo(), {
|
||||
elementsPerPage: 1,
|
||||
totalElements: 3,
|
||||
totalPages: 1,
|
||||
currentPage: 1
|
||||
});
|
||||
serviceAsAny.vocabularyService.searchTopEntries.and.returnValue(hot('-c', {
|
||||
a: createSuccessfulRemoteDataObject(new PaginatedList(pageInfo, [item, item2, item3]))
|
||||
}));
|
||||
@@ -239,7 +237,7 @@ describe('VocabularyTreeviewService test suite', () => {
|
||||
});
|
||||
|
||||
it('should add children nodes properly', () => {
|
||||
const pageInfo = Object.assign(new PageInfo(), {
|
||||
pageInfo = Object.assign(new PageInfo(), {
|
||||
elementsPerPage: 1,
|
||||
totalElements: 2,
|
||||
totalPages: 2,
|
||||
@@ -260,7 +258,7 @@ describe('VocabularyTreeviewService test suite', () => {
|
||||
});
|
||||
|
||||
it('should add loadMore node properly', () => {
|
||||
const pageInfo = Object.assign(new PageInfo(), {
|
||||
pageInfo = Object.assign(new PageInfo(), {
|
||||
elementsPerPage: 1,
|
||||
totalElements: 2,
|
||||
totalPages: 2,
|
||||
@@ -285,7 +283,7 @@ describe('VocabularyTreeviewService test suite', () => {
|
||||
|
||||
describe('searchByQuery', () => {
|
||||
it('should set tree data properly after a search', () => {
|
||||
const pageInfo = Object.assign(new PageInfo(), {
|
||||
pageInfo = Object.assign(new PageInfo(), {
|
||||
elementsPerPage: 1,
|
||||
totalElements: 1,
|
||||
totalPages: 1,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
|
||||
import { flatMap, map, merge, scan, take, tap } from 'rxjs/operators';
|
||||
import { flatMap, map, merge, scan } from 'rxjs/operators';
|
||||
import { findIndex } from 'lodash';
|
||||
|
||||
import { LOAD_MORE_NODE, LOAD_MORE_ROOT_NODE, TreeviewFlatNode, TreeviewNode } from './vocabulary-treeview-node.model';
|
||||
@@ -56,14 +56,18 @@ export class VocabularyTreeviewService {
|
||||
private initValueHierarchy: string[] = [];
|
||||
|
||||
/**
|
||||
* A boolean representing if a search operation is pending
|
||||
* A boolean representing if any operation is pending
|
||||
*/
|
||||
private searching = new BehaviorSubject<boolean>(false);
|
||||
private loading = new BehaviorSubject<boolean>(false);
|
||||
|
||||
/**
|
||||
* An observable to change the searching status
|
||||
* The {@link PageInfo} object
|
||||
*/
|
||||
private hideSearchingWhenUnsubscribed$ = new Observable(() => () => this.searching.next(false));
|
||||
private pageInfo: PageInfo;
|
||||
/**
|
||||
* An observable to change the loading status
|
||||
*/
|
||||
private hideSearchingWhenUnsubscribed$ = new Observable(() => () => this.loading.next(false));
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
@@ -92,8 +96,10 @@ export class VocabularyTreeviewService {
|
||||
* @param initValueId The entry id of the node to mark as selected, if any
|
||||
*/
|
||||
initialize(options: VocabularyOptions, pageInfo: PageInfo, initValueId?: string): void {
|
||||
this.loading.next(true);
|
||||
this.vocabularyOptions = options;
|
||||
this.vocabularyName = options.name;
|
||||
this.pageInfo = pageInfo;
|
||||
if (isNotEmpty(initValueId)) {
|
||||
this.getNodeHierarchyById(initValueId)
|
||||
.subscribe((hierarchy: string[]) => {
|
||||
@@ -160,17 +166,17 @@ export class VocabularyTreeviewService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a search operation is pending
|
||||
* Check if any operation is pending
|
||||
*/
|
||||
isSearching(): Observable<boolean> {
|
||||
return this.searching;
|
||||
isLoading(): Observable<boolean> {
|
||||
return this.loading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a search operation by query
|
||||
*/
|
||||
searchByQuery(query: string) {
|
||||
this.searching.next(true);
|
||||
this.loading.next(true);
|
||||
if (isEmpty(this.storedNodes)) {
|
||||
this.storedNodes = this.dataChange.value;
|
||||
this.storedNodeMap = this.nodeMap;
|
||||
@@ -192,7 +198,7 @@ export class VocabularyTreeviewService {
|
||||
merge(this.hideSearchingWhenUnsubscribed$)
|
||||
).subscribe((nodes: TreeviewNode[]) => {
|
||||
this.dataChange.next(nodes);
|
||||
this.searching.next(false);
|
||||
this.loading.next(false);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -200,7 +206,7 @@ export class VocabularyTreeviewService {
|
||||
* Reset tree state with the one before the search
|
||||
*/
|
||||
restoreNodes() {
|
||||
this.searching.next(false);
|
||||
this.loading.next(false);
|
||||
this.dataChange.next(this.storedNodes);
|
||||
this.nodeMap = this.storedNodeMap;
|
||||
|
||||
@@ -224,8 +230,8 @@ export class VocabularyTreeviewService {
|
||||
const entryDetail: VocabularyEntryDetail = Object.assign(new VocabularyEntryDetail(), entry, {
|
||||
id: entryId
|
||||
});
|
||||
const hasChildren = entry.hasOtherInformation() && isNotEmpty((entry.otherInformation as any).children);
|
||||
const pageInfo: PageInfo = new PageInfo();
|
||||
const hasChildren = entry.hasOtherInformation() && (entry.otherInformation as any)!.hasChildren == 'true';
|
||||
const pageInfo: PageInfo = this.pageInfo;
|
||||
const isInInitValueHierarchy = this.initValueHierarchy.includes(entryId);
|
||||
const result = new TreeviewNode(
|
||||
entryDetail,
|
||||
@@ -295,8 +301,10 @@ export class VocabularyTreeviewService {
|
||||
this.vocabularyService.searchTopEntries(this.vocabularyName, pageInfo).pipe(
|
||||
getFirstSucceededRemoteDataPayload()
|
||||
).subscribe((list: PaginatedList<VocabularyEntryDetail>) => {
|
||||
const newNodes: TreeviewNode[] = list.page.map((entry: VocabularyEntryDetail) => this._generateNode(entry))
|
||||
this.vocabularyService.clearSearchTopRequests();
|
||||
const newNodes: TreeviewNode[] = list.page.map((entry: VocabularyEntryDetail) => this._generateNode(entry));
|
||||
nodes.push(...newNodes);
|
||||
|
||||
if ((list.pageInfo.currentPage + 1) <= list.pageInfo.totalPages) {
|
||||
// Need a new load more node
|
||||
const newPageInfo: PageInfo = Object.assign(new PageInfo(), list.pageInfo, {
|
||||
@@ -306,6 +314,7 @@ export class VocabularyTreeviewService {
|
||||
loadMoreNode.updatePageInfo(newPageInfo);
|
||||
nodes.push(loadMoreNode);
|
||||
}
|
||||
this.loading.next(false);
|
||||
// Notify the change.
|
||||
this.dataChange.next(nodes);
|
||||
});
|
||||
@@ -328,8 +337,7 @@ export class VocabularyTreeviewService {
|
||||
if (isNotEmpty(children)) {
|
||||
const newChildren = children
|
||||
.filter((entry: TreeviewNode) => {
|
||||
const ii = findIndex(node.children, (nodeEntry) => nodeEntry.item.id === entry.item.id);
|
||||
return ii === -1;
|
||||
return findIndex(node.children, (nodeEntry) => nodeEntry.item.id === entry.item.id) === -1;
|
||||
});
|
||||
newChildren.forEach((entry: TreeviewNode) => {
|
||||
entry.loadMoreParentItem = node.item
|
||||
|
@@ -2712,6 +2712,24 @@
|
||||
"title": "DSpace",
|
||||
|
||||
|
||||
|
||||
|
||||
"vocabulary-treeview.header": "Hierarchical tree view",
|
||||
|
||||
"vocabulary-treeview.load-more": "Load more",
|
||||
|
||||
"vocabulary-treeview.search.form.reset": "Reset",
|
||||
|
||||
"vocabulary-treeview.search.form.search": "Search",
|
||||
|
||||
"vocabulary-treeview.search.no-result": "There were no items to show",
|
||||
|
||||
"vocabulary-treeview.tree.description.nsi": "The Norwegian Science Index",
|
||||
|
||||
"vocabulary-treeview.tree.description.srsc": "Research Subject Categories",
|
||||
|
||||
|
||||
|
||||
"administrativeView.search.results.head": "Administrative Search",
|
||||
|
||||
"menu.section.admin_search": "Admin Search",
|
||||
|
Reference in New Issue
Block a user