[CST-3088] Added vocabulary service and models

This commit is contained in:
Giuseppe Digilio
2020-06-23 18:17:47 +02:00
parent e1b80bcbaf
commit 1156bd3934
9 changed files with 796 additions and 11 deletions

View File

@@ -16,8 +16,8 @@ import { MenuService } from '../shared/menu/menu.service';
import { EndpointMockingRestService } from '../shared/mocks/dspace-rest-v2/endpoint-mocking-rest.service'; import { EndpointMockingRestService } from '../shared/mocks/dspace-rest-v2/endpoint-mocking-rest.service';
import { import {
MOCK_RESPONSE_MAP, MOCK_RESPONSE_MAP,
ResponseMapMock, mockResponseMap,
mockResponseMap ResponseMapMock
} from '../shared/mocks/dspace-rest-v2/mocks/response-map.mock'; } from '../shared/mocks/dspace-rest-v2/mocks/response-map.mock';
import { NotificationsService } from '../shared/notifications/notifications.service'; import { NotificationsService } from '../shared/notifications/notifications.service';
import { SelectableListService } from '../shared/object-list/selectable-list/selectable-list.service'; import { SelectableListService } from '../shared/object-list/selectable-list/selectable-list.service';
@@ -145,6 +145,9 @@ import { Version } from './shared/version.model';
import { VersionHistory } from './shared/version-history.model'; import { VersionHistory } from './shared/version-history.model';
import { WorkflowActionDataService } from './data/workflow-action-data.service'; import { WorkflowActionDataService } from './data/workflow-action-data.service';
import { WorkflowAction } from './tasks/models/workflow-action-object.model'; import { WorkflowAction } from './tasks/models/workflow-action-object.model';
import { VocabularyEntry } from './submission/vocabularies/models/vocabulary-entry.model';
import { Vocabulary } from './submission/vocabularies/models/vocabulary.model';
import { VocabularyEntriesResponseParsingService } from './submission/vocabularies/vocabulary-entries-response-parsing.service';
/** /**
* When not in production, endpoint responses can be mocked for testing purposes * When not in production, endpoint responses can be mocked for testing purposes
@@ -178,7 +181,7 @@ const PROVIDERS = [
SiteDataService, SiteDataService,
DSOResponseParsingService, DSOResponseParsingService,
{ provide: MOCK_RESPONSE_MAP, useValue: mockResponseMap }, { provide: MOCK_RESPONSE_MAP, useValue: mockResponseMap },
{ provide: DSpaceRESTv2Service, useFactory: restServiceFactory, deps: [MOCK_RESPONSE_MAP, HttpClient]}, { provide: DSpaceRESTv2Service, useFactory: restServiceFactory, deps: [MOCK_RESPONSE_MAP, HttpClient] },
DynamicFormLayoutService, DynamicFormLayoutService,
DynamicFormService, DynamicFormService,
DynamicFormValidationService, DynamicFormValidationService,
@@ -272,7 +275,8 @@ const PROVIDERS = [
}, },
NotificationsService, NotificationsService,
FilteredDiscoveryPageResponseParsingService, FilteredDiscoveryPageResponseParsingService,
{ provide: NativeWindowService, useFactory: NativeWindowFactory } { provide: NativeWindowService, useFactory: NativeWindowFactory },
VocabularyEntriesResponseParsingService
]; ];
/** /**
@@ -314,7 +318,9 @@ export const models =
ExternalSourceEntry, ExternalSourceEntry,
Version, Version,
VersionHistory, VersionHistory,
WorkflowAction WorkflowAction,
Vocabulary,
VocabularyEntry
]; ];
@NgModule({ @NgModule({
@@ -333,6 +339,12 @@ export const models =
}) })
export class CoreModule { export class CoreModule {
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
if (isNotEmpty(parentModule)) {
throw new Error('CoreModule is already loaded. Import it in the AppModule only');
}
}
static forRoot(): ModuleWithProviders { static forRoot(): ModuleWithProviders {
return { return {
ngModule: CoreModule, ngModule: CoreModule,
@@ -341,10 +353,4 @@ export class CoreModule {
] ]
}; };
} }
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
if (isNotEmpty(parentModule)) {
throw new Error('CoreModule is already loaded. Import it in the AppModule only');
}
}
} }

View File

@@ -20,6 +20,7 @@ import { URLCombiner } from '../url-combiner/url-combiner';
import { TaskResponseParsingService } from '../tasks/task-response-parsing.service'; import { TaskResponseParsingService } from '../tasks/task-response-parsing.service';
import { ContentSourceResponseParsingService } from './content-source-response-parsing.service'; import { ContentSourceResponseParsingService } from './content-source-response-parsing.service';
import { MappedCollectionsReponseParsingService } from './mapped-collections-reponse-parsing.service'; import { MappedCollectionsReponseParsingService } from './mapped-collections-reponse-parsing.service';
import { VocabularyEntriesResponseParsingService } from '../submission/vocabularies/vocabulary-entries-response-parsing.service';
/* tslint:disable:max-classes-per-file */ /* tslint:disable:max-classes-per-file */
@@ -442,6 +443,15 @@ export class MyDSpaceRequest extends GetRequest {
public responseMsToLive = 10 * 1000; public responseMsToLive = 10 * 1000;
} }
/**
* Request to get vocabulary entries
*/
export class VocabularyEntriesRequest extends FindListRequest {
getResponseParser(): GenericConstructor<ResponseParsingService> {
return VocabularyEntriesResponseParsingService;
}
}
export class RequestError extends Error { export class RequestError extends Error {
statusCode: number; statusCode: number;
statusText: string; statusText: string;

View File

@@ -0,0 +1,11 @@
import { ResourceType } from '../../../shared/resource-type';
/**
* The resource type for vocabulary models
*
* Needs to be in a separate file to prevent circular
* dependencies in webpack.
*/
export const VOCABULARY = new ResourceType('vocabulary');
export const VOCABULARY_ENTRY = new ResourceType('vocabularyEntry');

View File

@@ -0,0 +1,103 @@
import { autoserialize, deserialize } from 'cerialize';
import { HALLink } from '../../../shared/hal-link.model';
import { VOCABULARY_ENTRY } from './vocabularies.resource-type';
import { typedObject } from '../../../cache/builders/build-decorators';
import { excludeFromEquals } from '../../../utilities/equals.decorators';
import { PLACEHOLDER_PARENT_METADATA } from '../../../../shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model';
import { OtherInformation } from '../../../../shared/form/builder/models/form-field-metadata-value.model';
import { isNotEmpty } from '../../../../shared/empty.util';
import { ListableObject } from '../../../../shared/object-collection/shared/listable-object.model';
import { GenericConstructor } from '../../../shared/generic-constructor';
/**
* Model class for a Vocabulary
*/
@typedObject
export class VocabularyEntry extends ListableObject {
static type = VOCABULARY_ENTRY;
/**
* The identifier of this vocabulary entry
*/
@autoserialize
authority: string;
/**
* The display value of this vocabulary entry
*/
@autoserialize
display: string;
/**
* The value of this vocabulary entry
*/
@autoserialize
value: string;
/**
* An object containing additional information related to this vocabulary entry
*/
@autoserialize
otherInformation: OtherInformation;
/**
* A string representing the kind of vocabulary entry
*/
@excludeFromEquals
@autoserialize
public type: any;
/**
* The {@link HALLink}s for this ExternalSourceEntry
*/
@deserialize
_links: {
self: HALLink;
vocabularyEntryDetail: HALLink;
};
/**
* This method checks if entry has an authority value
*
* @return boolean
*/
hasAuthority(): boolean {
return isNotEmpty(this.authority);
}
/**
* This method checks if entry has a value
*
* @return boolean
*/
hasValue(): boolean {
return isNotEmpty(this.value);
}
/**
* This method checks if entry has related information object
*
* @return boolean
*/
hasOtherInformation(): boolean {
return isNotEmpty(this.otherInformation);
}
/**
* This method checks if entry has a placeholder as value
*
* @return boolean
*/
hasPlaceholder(): boolean {
return this.hasValue() && this.value === PLACEHOLDER_PARENT_METADATA;
}
/**
* Method that returns as which type of object this object should be rendered
*/
getRenderTypes(): Array<string | GenericConstructor<ListableObject>> {
return [this.constructor as GenericConstructor<ListableObject>];
}
}

View File

@@ -0,0 +1,61 @@
import { autoserialize, deserialize } from 'cerialize';
import { HALLink } from '../../../shared/hal-link.model';
import { VOCABULARY } from './vocabularies.resource-type';
import { CacheableObject } from '../../../cache/object-cache.reducer';
import { typedObject } from '../../../cache/builders/build-decorators';
import { excludeFromEquals } from '../../../utilities/equals.decorators';
/**
* Model class for a Vocabulary
*/
@typedObject
export class Vocabulary implements CacheableObject {
static type = VOCABULARY;
/**
* The identifier of this Vocabulary
*/
@autoserialize
id: string;
/**
* The name of this Vocabulary
*/
@autoserialize
name: string;
/**
* True if it is possible to scroll all the entries in the vocabulary without providing a filter parameter
*/
@autoserialize
scrollable: boolean;
/**
* True if the vocabulary exposes a tree structure where some entries are parent of others
*/
@autoserialize
hierarchical: boolean;
/**
* For hierarchical vocabularies express the preference to preload the tree at a specific
* level of depth (0 only the top nodes are shown, 1 also their children are preloaded and so on)
*/
@autoserialize
preloadLevel: any;
/**
* A string representing the kind of Vocabulary model
*/
@excludeFromEquals
@autoserialize
public type: any;
/**
* The {@link HALLink}s for this Vocabulary
*/
@deserialize
_links: {
self: HALLink,
entries: HALLink
};
}

View File

@@ -0,0 +1,111 @@
import { getMockObjectCacheService } from '../../../shared/mocks/object-cache.service.mock';
import { ErrorResponse, GenericSuccessResponse } from '../../cache/response.models';
import { DSpaceRESTV2Response } from '../../dspace-rest-v2/dspace-rest-v2-response.model';
import { VocabularyEntriesResponseParsingService } from './vocabulary-entries-response-parsing.service';
import { VocabularyEntriesRequest } from '../../data/request.models';
fdescribe('VocabularyEntriesResponseParsingService', () => {
let service: VocabularyEntriesResponseParsingService;
const metadata = 'dc.type';
const collectionUUID = '8b39g7ya-5a4b-438b-851f-be1d5b4a1c5a';
const entriesRequestURL = `https://rest.api/rest/api/submission/vocabularies/types/entries?metadata=${metadata}&collection=${collectionUUID}`
beforeEach(() => {
service = new VocabularyEntriesResponseParsingService(getMockObjectCacheService());
});
describe('parse', () => {
const request = new VocabularyEntriesRequest('client/f5b4ccb8-fbb0-4548-b558-f234d9fdfad6', entriesRequestURL);
const validResponse = {
payload: {
_embedded: {
entries: [
{
display: 'testValue1',
value: 'testValue1',
otherInformation: {},
type: 'vocabularyEntry'
},
{
display: 'testValue2',
value: 'testValue2',
otherInformation: {},
type: 'vocabularyEntry'
},
{
display: 'testValue3',
value: 'testValue3',
otherInformation: {},
type: 'vocabularyEntry'
},
{
authority: 'authorityId1',
display: 'testValue1',
value: 'testValue1',
otherInformation: {
id: 'VR131402',
parent: 'Research Subject Categories::SOCIAL SCIENCES::Social sciences::Social work',
hasChildren: 'false',
note: 'Familjeforskning'
},
type: 'vocabularyEntry',
_links: {
vocabularyEntryDetail: {
href: 'https://rest.api/rest/api/submission/vocabularyEntryDetails/srsc:VR131402'
}
}
}
]
},
_links: {
first: {
href: 'https://rest.api/discover/browses/author/entries?page=0&size=5'
},
self: {
href: 'https://rest.api/discover/browses/author/entries'
},
next: {
href: 'https://rest.api/discover/browses/author/entries?page=1&size=5'
},
last: {
href: 'https://rest.api/discover/browses/author/entries?page=9&size=5'
}
},
page: {
size: 5,
totalElements: 50,
totalPages: 10,
number: 0
}
},
statusCode: 200,
statusText: 'OK'
} as DSpaceRESTV2Response;
const invalidResponseNotAList = {
statusCode: 200,
statusText: 'OK'
} as DSpaceRESTV2Response;
const invalidResponseStatusCode = {
payload: {}, statusCode: 500, statusText: 'Internal Server Error'
} as DSpaceRESTV2Response;
it('should return a GenericSuccessResponse if data contains a valid browse entries response', () => {
const response = service.parse(request, validResponse);
expect(response.constructor).toBe(GenericSuccessResponse);
});
it('should return an ErrorResponse if data contains an invalid browse entries response', () => {
const response = service.parse(request, invalidResponseNotAList);
expect(response.constructor).toBe(ErrorResponse);
});
it('should return an ErrorResponse if data contains a statuscode other than 200', () => {
const response = service.parse(request, invalidResponseStatusCode);
expect(response.constructor).toBe(ErrorResponse);
});
});
});

View File

@@ -0,0 +1,20 @@
import { Injectable } from '@angular/core';
import { ObjectCacheService } from '../../cache/object-cache.service';
import { BrowseEntriesResponseParsingService } from '../../data/browse-entries-response-parsing.service';
/**
* A service responsible for parsing data for a vocabulary entries response
*/
@Injectable()
export class VocabularyEntriesResponseParsingService extends BrowseEntriesResponseParsingService {
protected toCache = false;
constructor(
protected objectCache: ObjectCacheService,
) {
super(objectCache);
}
}

View File

@@ -0,0 +1,269 @@
import { HttpClient } from '@angular/common/http';
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
import { of as observableOf } from 'rxjs';
import { TestScheduler } from 'rxjs/testing';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../../cache/object-cache.service';
import { HALEndpointService } from '../../shared/hal-endpoint.service';
import { RequestService } from '../../data/request.service';
import { FindListOptions, VocabularyEntriesRequest } from '../../data/request.models';
import { RequestParam } from '../../cache/models/request-param.model';
import { PageInfo } from '../../shared/page-info.model';
import { PaginatedList } from '../../data/paginated-list';
import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils';
import { RequestEntry } from '../../data/request.reducer';
import { RestResponse } from '../../cache/response.models';
import { VocabularyService } from './vocabulary.service';
import { getMockRequestService } from '../../../shared/mocks/request.service.mock';
import { getMockRemoteDataBuildService } from '../../../shared/mocks/remote-data-build.service.mock';
describe('VocabularyService', () => {
let scheduler: TestScheduler;
let service: VocabularyService;
let requestService: RequestService;
let rdbService: RemoteDataBuildService;
let objectCache: ObjectCacheService;
let halService: HALEndpointService;
let responseCacheEntry: RequestEntry;
const vocabulary: any = {
id: 'types',
name: 'types',
scrollable: true,
hierarchical: false,
preloadLevel: 1,
type: 'vocabulary',
uuid: 'vocabulary-types',
_links: {
self: {
href: 'https://rest.api/rest/api/submission/vocabularies/types'
},
entries: {
href: 'https://rest.api/rest/api/submission/vocabularies/types/entries'
},
}
};
const anotherVocabulary: any = {
id: 'srsc',
name: 'srsc',
scrollable: false,
hierarchical: true,
preloadLevel: 2,
type: 'vocabulary',
uuid: 'vocabulary-srsc',
_links: {
self: {
href: 'https://rest.api/rest/api/submission/vocabularies/types'
},
entries: {
href: 'https://rest.api/rest/api/submission/vocabularies/types/entries'
},
}
};
const vocabularyEntry: any = {
display: 'testValue1',
value: 'testValue1',
otherInformation: {},
type: 'vocabularyEntry'
};
const vocabularyEntryWithAuthority: any = {
authority: 'authorityId1',
display: 'testValue1',
value: 'testValue1',
otherInformation: {
id: 'VR131402',
parent: 'Research Subject Categories::SOCIAL SCIENCES::Social sciences::Social work',
hasChildren: 'false',
note: 'Familjeforskning'
},
type: 'vocabularyEntry',
_links: {
vocabularyEntryDetail: {
href: 'https://rest.api/rest/api/submission/vocabularyEntryDetails/srsc:VR131402'
}
}
};
const endpointURL = `https://rest.api/rest/api/submission/vocabularies`;
const requestURL = `https://rest.api/rest/api/submission/vocabularies/${vocabulary.id}`;
const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a';
const vocabularyId = 'types';
const metadata = 'dc.type';
const collectionUUID = '8b39g7ya-5a4b-438b-851f-be1d5b4a1c5a';
const searchRequestURL = `https://rest.api/rest/api/submission/vocabularies/search/byMetadataAndCollection?metadata=${metadata}&collection=${collectionUUID}`;
const entriesRequestURL = `https://rest.api/rest/api/submission/vocabularies/${vocabulary.id}/entries?metadata=${metadata}&collection=${collectionUUID}`;
const pageInfo = new PageInfo();
const array = [vocabulary, anotherVocabulary];
const paginatedList = new PaginatedList(pageInfo, array);
const vocabularyRD = createSuccessfulRemoteDataObject(vocabulary);
const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList);
const getRequestEntry$ = (successful: boolean) => {
return observableOf({
response: { isSuccessful: successful, payload: vocabulary } as any
} as RequestEntry)
};
objectCache = {} as ObjectCacheService;
const notificationsService = {} as NotificationsService;
const http = {} as HttpClient;
const comparator = {} as any;
function initTestService() {
return new VocabularyService(
requestService,
rdbService,
objectCache,
halService,
notificationsService,
http,
comparator
);
}
beforeEach(() => {
scheduler = getTestScheduler();
halService = jasmine.createSpyObj('halService', {
getEndpoint: cold('a', { a: endpointURL })
});
responseCacheEntry = new RequestEntry();
responseCacheEntry.response = new RestResponse(true, 200, 'Success');
requestService = jasmine.createSpyObj('requestService', {
generateRequestId: requestUUID,
configure: true,
removeByHrefSubstring: {},
getByHref: observableOf(responseCacheEntry),
getByUUID: observableOf(responseCacheEntry),
});
rdbService = jasmine.createSpyObj('rdbService', {
buildSingle: hot('a|', {
a: vocabularyRD
}),
buildList: hot('a|', {
a: paginatedListRD
}),
});
service = initTestService();
spyOn((service as any).dataService, 'findById').and.callThrough();
spyOn((service as any).dataService, 'findAll').and.callThrough();
spyOn((service as any).dataService, 'findByHref').and.callThrough();
spyOn((service as any).dataService, 'searchBy').and.callThrough();
spyOn((service as any).dataService, 'getSearchByHref').and.returnValue(observableOf(searchRequestURL));
spyOn((service as any).dataService, 'getFindAllHref').and.returnValue(observableOf(entriesRequestURL));
});
afterEach(() => {
service = null;
});
describe('findById', () => {
it('should proxy the call to dataservice.findById', () => {
scheduler.schedule(() => service.findById(vocabularyId));
scheduler.flush();
expect((service as any).dataService.findById).toHaveBeenCalledWith(vocabularyId);
});
it('should return a RemoteData<Vocabulary> for the object with the given id', () => {
const result = service.findById(vocabularyId);
const expected = cold('a|', {
a: vocabularyRD
});
expect(result).toBeObservable(expected);
});
});
describe('findByHref', () => {
it('should proxy the call to dataservice.findByHref', () => {
scheduler.schedule(() => service.findByHref(requestURL));
scheduler.flush();
expect((service as any).dataService.findByHref).toHaveBeenCalledWith(requestURL);
});
it('should return a RemoteData<Vocabulary> for the object with the given URL', () => {
const result = service.findByHref(requestURL);
const expected = cold('a|', {
a: vocabularyRD
});
expect(result).toBeObservable(expected);
});
});
describe('findAll', () => {
it('should proxy the call to dataservice.findAll', () => {
scheduler.schedule(() => service.findAll());
scheduler.flush();
expect((service as any).dataService.findAll).toHaveBeenCalled();
});
it('should return a RemoteData<PaginatedList<Vocabulary>>', () => {
const result = service.findAll();
const expected = cold('a|', {
a: paginatedListRD
});
expect(result).toBeObservable(expected);
});
});
describe('searchByMetadataAndCollection', () => {
it('should proxy the call to dataservice.findByHref', () => {
const options = new FindListOptions();
options.searchParams = [
new RequestParam('metadata', metadata),
new RequestParam('collection', collectionUUID)
];
scheduler.schedule(() => service.searchByMetadataAndCollection(metadata, collectionUUID).subscribe());
scheduler.flush();
expect((service as any).dataService.findByHref).toHaveBeenCalledWith(searchRequestURL);
});
it('should return a RemoteData<Vocabulary> for the search', () => {
const result = service.searchByMetadataAndCollection(metadata, collectionUUID);
const expected = cold('a|', {
a: vocabularyRD
});
expect(result).toBeObservable(expected);
});
});
describe('getVocabularyEntries', () => {
beforeEach(() => {
requestService = getMockRequestService(getRequestEntry$(true));
rdbService = getMockRemoteDataBuildService();
spyOn(rdbService, 'toRemoteDataObservable').and.callThrough();
service = initTestService();
});
it('should configure a new VocabularyEntriesRequest', () => {
const expected = new VocabularyEntriesRequest(requestService.generateRequestId(), entriesRequestURL);
scheduler.schedule(() => service.getVocabularyEntries(vocabularyId, metadata, collectionUUID).subscribe());
scheduler.flush();
expect(requestService.configure).toHaveBeenCalledWith(expected);
});
it('should call RemoteDataBuildService to create the RemoteData Observable', () => {
service.getVocabularyEntries(vocabularyId, metadata, collectionUUID);
expect(rdbService.toRemoteDataObservable).toHaveBeenCalled();
});
});
});

View File

@@ -0,0 +1,194 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { distinctUntilChanged, first, flatMap, map } from 'rxjs/operators';
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { dataService } from '../../cache/builders/build-decorators';
import { DataService } from '../../data/data.service';
import { RequestService } from '../../data/request.service';
import { FindListOptions, RestRequest, VocabularyEntriesRequest } from '../../data/request.models';
import { HALEndpointService } from '../../shared/hal-endpoint.service';
import { RemoteData } from '../../data/remote-data';
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
import { CoreState } from '../../core.reducers';
import { ObjectCacheService } from '../../cache/object-cache.service';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { ChangeAnalyzer } from '../../data/change-analyzer';
import { DefaultChangeAnalyzer } from '../../data/default-change-analyzer.service';
import { PaginatedList } from '../../data/paginated-list';
import { RequestParam } from '../../cache/models/request-param.model';
import { Vocabulary } from './models/vocabulary.model';
import { VOCABULARY } from './models/vocabularies.resource-type';
import { VocabularyEntry } from './models/vocabulary-entry.model';
import { hasValue, isNotEmptyOperator } from '../../../shared/empty.util';
import { configureRequest, filterSuccessfulResponses, getRequestFromRequestHref } from '../../shared/operators';
import { GenericSuccessResponse } from '../../cache/response.models';
/* tslint:disable:max-classes-per-file */
/**
* A private DataService implementation to delegate specific methods to.
*/
class DataServiceImpl extends DataService<Vocabulary> {
protected linkPath = 'vocabularies';
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: ChangeAnalyzer<Vocabulary>) {
super();
}
}
/**
* A service responsible for fetching/sending data from/to the REST API on the vocabularies endpoint
*/
@Injectable({
providedIn: 'root'
})
@dataService(VOCABULARY)
export class VocabularyService {
protected searchByMetadataAndCollectionMethod = 'byMetadataAndCollection';
private dataService: DataServiceImpl;
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<Vocabulary>) {
this.dataService = new DataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator);
}
/**
* Returns an observable of {@link RemoteData} of a {@link Vocabulary}, based on an href, with a list of {@link FollowLinkConfig},
* to automatically resolve {@link HALLink}s of the {@link Vocabulary}
* @param href The url of {@link Vocabulary} we want to retrieve
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<Vocabulary>>}
* Return an observable that emits vocabulary object
*/
findByHref(href: string, ...linksToFollow: Array<FollowLinkConfig<Vocabulary>>): Observable<RemoteData<any>> {
return this.dataService.findByHref(href, ...linksToFollow);
}
/**
* Returns an observable of {@link RemoteData} of a {@link Vocabulary}, based on its ID, with a list of {@link FollowLinkConfig},
* to automatically resolve {@link HALLink}s of the object
* @param id ID of {@link Vocabulary} we want to retrieve
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<Vocabulary>>}
* Return an observable that emits vocabulary object
*/
findById(id: string, ...linksToFollow: Array<FollowLinkConfig<Vocabulary>>): Observable<RemoteData<Vocabulary>> {
return this.dataService.findById(id, ...linksToFollow);
}
/**
* Returns {@link RemoteData} of all object with a list of {@link FollowLinkConfig}, to indicate which embedded
* info should be added to the objects
*
* @param options Find list options object
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<Vocabulary>>>}
* Return an observable that emits object list
*/
findAll(options: FindListOptions = {}, ...linksToFollow: Array<FollowLinkConfig<Vocabulary>>): Observable<RemoteData<PaginatedList<Vocabulary>>> {
return this.dataService.findAll(options, ...linksToFollow);
}
/**
* Return the {@link VocabularyEntry} list for a given {@link Vocabulary}
*
* @param id The vocabulary id to retrieve the entries for
* @param metadata The metadata name
* @param collectionUUID The collection UUID
* @param options The {@link FindListOptions} for the request
* @return {Observable<RemoteData<PaginatedList<VocabularyEntry>>>}
* Return an observable that emits object list
*/
getVocabularyEntries(id: string, metadata: string, collectionUUID: string, options: FindListOptions = {}): Observable<RemoteData<PaginatedList<VocabularyEntry>>> {
options = Object.assign({}, options, {
searchParams: [
new RequestParam('metadata', metadata),
new RequestParam('collection', collectionUUID)
]
});
return this.dataService.getFindAllHref(options, `${id}/entries`).pipe(
isNotEmptyOperator(),
distinctUntilChanged(),
getVocabularyEntriesFor(this.requestService, this.rdbService)
);
}
/**
* Return the controlled {@link Vocabulary} configured for the specified metadata and collection if any.
*
* @param metadata The metadata name
* @param collectionUUID The collection UUID
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<Vocabulary>>>}
* Return an observable that emits object list
*/
searchByMetadataAndCollection(metadata: string, collectionUUID: string, ...linksToFollow: Array<FollowLinkConfig<Vocabulary>>): Observable<RemoteData<Vocabulary>> {
const options = new FindListOptions();
options.searchParams = [
new RequestParam('metadata', metadata),
new RequestParam('collection', collectionUUID)
];
return this.dataService.getSearchByHref(this.searchByMetadataAndCollectionMethod, options).pipe(
first((href: string) => hasValue(href)),
flatMap((href: string) => this.dataService.findByHref(href))
)
}
}
/**
* Operator for turning a href into a PaginatedList of VocabularyEntry
* @param requestService
* @param rdb
*/
export const getVocabularyEntriesFor = (requestService: RequestService, rdb: RemoteDataBuildService) =>
(source: Observable<string>): Observable<RemoteData<PaginatedList<VocabularyEntry>>> =>
source.pipe(
map((href: string) => new VocabularyEntriesRequest(requestService.generateRequestId(), href)),
configureRequest(requestService),
toRDPaginatedVocabularyEntries(requestService, rdb)
);
/**
* Operator for turning a RestRequest into a PaginatedList of VocabularyEntry
* @param requestService
* @param rdb
*/
export const toRDPaginatedVocabularyEntries = (requestService: RequestService, rdb: RemoteDataBuildService) =>
(source: Observable<RestRequest>): Observable<RemoteData<PaginatedList<VocabularyEntry>>> => {
const href$ = source.pipe(map((request: RestRequest) => request.href));
const requestEntry$ = href$.pipe(getRequestFromRequestHref(requestService));
const payload$ = requestEntry$.pipe(
filterSuccessfulResponses(),
map((response: GenericSuccessResponse<VocabularyEntry[]>) => new PaginatedList(response.pageInfo, response.payload)),
map((list: PaginatedList<VocabularyEntry>) => Object.assign(list, {
page: list.page ? list.page.map((entry: VocabularyEntry) => Object.assign(new VocabularyEntry(), entry)) : list.page
})),
distinctUntilChanged()
);
return rdb.toRemoteDataObservable(requestEntry$, payload$);
};